1use std::{mem::ManuallyDrop, sync::Arc};
2
3use ::util::ResultExt;
4use anyhow::{Context, Result};
5// #[cfg(not(feature = "enable-renderdoc"))]
6// use windows::Win32::Graphics::DirectComposition::*;
7use windows::{
8 Win32::{
9 Foundation::{HMODULE, HWND},
10 Graphics::{
11 Direct3D::*,
12 Direct3D11::*,
13 Dxgi::{Common::*, *},
14 },
15 },
16 core::*,
17};
18
19use crate::*;
20
21const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
22const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
23
24pub(crate) struct DirectXRenderer {
25 atlas: Arc<DirectXAtlas>,
26 devices: DirectXDevices,
27 context: DirectXContext,
28 globals: DirectXGlobalElements,
29 pipelines: DirectXRenderPipelines,
30 hwnd: HWND,
31 transparent: bool,
32}
33
34#[derive(Clone)]
35pub(crate) struct DirectXDevices {
36 dxgi_factory: IDXGIFactory6,
37 dxgi_device: IDXGIDevice,
38 device: ID3D11Device,
39 device_context: ID3D11DeviceContext,
40}
41
42struct DirectXContext {
43 swap_chain: ManuallyDrop<IDXGISwapChain1>,
44 render_target: ManuallyDrop<ID3D11Texture2D>,
45 render_target_view: [Option<ID3D11RenderTargetView>; 1],
46 msaa_target: ID3D11Texture2D,
47 msaa_view: ID3D11RenderTargetView,
48 viewport: [D3D11_VIEWPORT; 1],
49 // #[cfg(not(feature = "enable-renderdoc"))]
50 // direct_composition: DirectComposition,
51}
52
53struct DirectXRenderPipelines {
54 shadow_pipeline: PipelineState<Shadow>,
55 quad_pipeline: PipelineState<Quad>,
56 paths_pipeline: PathsPipelineState,
57 underline_pipeline: PipelineState<Underline>,
58 mono_sprites: PipelineState<MonochromeSprite>,
59 poly_sprites: PipelineState<PolychromeSprite>,
60}
61
62struct DirectXGlobalElements {
63 global_params_buffer: [Option<ID3D11Buffer>; 1],
64 sampler: [Option<ID3D11SamplerState>; 1],
65 blend_state: ID3D11BlendState,
66}
67
68#[repr(C)]
69struct DrawInstancedIndirectArgs {
70 vertex_count_per_instance: u32,
71 instance_count: u32,
72 start_vertex_location: u32,
73 start_instance_location: u32,
74}
75
76// #[cfg(not(feature = "enable-renderdoc"))]
77// struct DirectComposition {
78// comp_device: IDCompositionDevice,
79// comp_target: IDCompositionTarget,
80// comp_visual: IDCompositionVisual,
81// }
82
83impl DirectXDevices {
84 pub(crate) fn new() -> Result<Self> {
85 let dxgi_factory = get_dxgi_factory()?;
86 let adapter = get_adapter(&dxgi_factory)?;
87 let (device, device_context) = {
88 let mut device: Option<ID3D11Device> = None;
89 let mut context: Option<ID3D11DeviceContext> = None;
90 get_device(&adapter, Some(&mut device), Some(&mut context))?;
91 (device.unwrap(), context.unwrap())
92 };
93 let dxgi_device: IDXGIDevice = device.cast()?;
94
95 Ok(Self {
96 dxgi_factory,
97 dxgi_device,
98 device,
99 device_context,
100 })
101 }
102}
103
104impl DirectXRenderer {
105 pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
106 let atlas = Arc::new(DirectXAtlas::new(
107 devices.device.clone(),
108 devices.device_context.clone(),
109 ));
110 let context = DirectXContext::new(devices, hwnd, transparent)?;
111 let globals = DirectXGlobalElements::new(&devices.device)?;
112 let pipelines = DirectXRenderPipelines::new(&devices.device)?;
113 Ok(DirectXRenderer {
114 atlas,
115 devices: devices.clone(),
116 context,
117 globals,
118 pipelines,
119 hwnd,
120 transparent,
121 })
122 }
123
124 pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
125 self.atlas.clone()
126 }
127
128 fn pre_draw(&self) -> Result<()> {
129 update_buffer(
130 &self.devices.device_context,
131 self.globals.global_params_buffer[0].as_ref().unwrap(),
132 &[GlobalParams {
133 viewport_size: [
134 self.context.viewport[0].Width,
135 self.context.viewport[0].Height,
136 ],
137 ..Default::default()
138 }],
139 )?;
140 unsafe {
141 self.devices
142 .device_context
143 .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]);
144 self.devices
145 .device_context
146 .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None);
147 self.devices
148 .device_context
149 .RSSetViewports(Some(&self.context.viewport));
150 self.devices.device_context.OMSetBlendState(
151 &self.globals.blend_state,
152 None,
153 0xFFFFFFFF,
154 );
155 }
156 Ok(())
157 }
158
159 pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
160 // pre_draw(
161 // &self.devices.device_context,
162 // &self.globals.global_params_buffer,
163 // &self.context.viewport,
164 // &self.context.back_buffer,
165 // [0.0, 0.0, 0.0, 0.0],
166 // &self.globals.blend_state,
167 // )?;
168 println!("Pre-draw: {:?}", self.context.render_target_view);
169 self.pre_draw()?;
170 for batch in scene.batches() {
171 match batch {
172 PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
173 PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
174 PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
175 PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
176 PrimitiveBatch::MonochromeSprites {
177 texture_id,
178 sprites,
179 } => self.draw_monochrome_sprites(texture_id, sprites),
180 PrimitiveBatch::PolychromeSprites {
181 texture_id,
182 sprites,
183 } => self.draw_polychrome_sprites(texture_id, sprites),
184 PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
185 }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
186 scene.paths.len(),
187 scene.shadows.len(),
188 scene.quads.len(),
189 scene.underlines.len(),
190 scene.monochrome_sprites.len(),
191 scene.polychrome_sprites.len(),
192 scene.surfaces.len(),))?;
193 }
194 unsafe {
195 self.devices.device_context.ResolveSubresource(
196 &*self.context.render_target,
197 0,
198 &self.context.msaa_target,
199 0,
200 BACK_BUFFER_FORMAT,
201 );
202 self.devices
203 .device_context
204 .OMSetRenderTargets(Some(&self.context.render_target_view), None);
205 self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
206 }
207 Ok(())
208 }
209
210 pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
211 println!("Resize: {:?}", self.context.render_target_view);
212 unsafe {
213 self.devices.device_context.OMSetRenderTargets(None, None);
214 ManuallyDrop::drop(&mut self.context.render_target);
215 }
216 drop(self.context.render_target_view[0].take().unwrap());
217 unsafe {
218 self.context
219 .swap_chain
220 .ResizeBuffers(
221 BUFFER_COUNT as u32,
222 new_size.width.0 as u32,
223 new_size.height.0 as u32,
224 RENDER_TARGET_FORMAT,
225 DXGI_SWAP_CHAIN_FLAG(0),
226 )
227 .unwrap();
228 }
229 // let backbuffer = set_render_target_view(
230 // &self.context.swap_chain,
231 // &self.devices.device,
232 // &self.devices.device_context,
233 // )?;
234 let (render_target, render_target_view) =
235 create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
236 .unwrap();
237 self.context.render_target = render_target;
238 self.context.render_target_view = render_target_view;
239 unsafe {
240 self.devices
241 .device_context
242 .OMSetRenderTargets(Some(&self.context.render_target_view), None);
243 }
244
245 let (msaa_target, msaa_view) = create_msaa_target_and_its_view(
246 &self.devices.device,
247 new_size.width.0 as u32,
248 new_size.height.0 as u32,
249 )?;
250 self.context.msaa_target = msaa_target;
251 self.context.msaa_view = msaa_view;
252
253 self.context.viewport = set_viewport(
254 &self.devices.device_context,
255 new_size.width.0 as f32,
256 new_size.height.0 as f32,
257 );
258 Ok(())
259 }
260
261 // #[cfg(not(feature = "enable-renderdoc"))]
262 // pub(crate) fn update_transparency(
263 // &mut self,
264 // background_appearance: WindowBackgroundAppearance,
265 // ) -> Result<()> {
266 // // We only support setting `Transparent` and `Opaque` for now.
267 // match background_appearance {
268 // WindowBackgroundAppearance::Opaque => {
269 // if self.transparent {
270 // return Err(anyhow::anyhow!(
271 // "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
272 // ));
273 // }
274 // }
275 // WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
276 // if !self.transparent {
277 // return Err(anyhow::anyhow!(
278 // "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
279 // ));
280 // }
281 // }
282 // }
283 // Ok(())
284 // }
285
286 // #[cfg(feature = "enable-renderdoc")]
287 pub(crate) fn update_transparency(
288 &mut self,
289 background_appearance: WindowBackgroundAppearance,
290 ) -> Result<()> {
291 let transparent = background_appearance != WindowBackgroundAppearance::Opaque;
292 if self.transparent == transparent {
293 return Ok(());
294 }
295 self.transparent = transparent;
296 // unsafe {
297 // // recreate the swapchain
298 // self.devices.device_context.OMSetRenderTargets(None, None);
299 // drop(self.context.back_buffer[0].take().unwrap());
300 // ManuallyDrop::drop(&mut self.context.swap_chain);
301 // self.context.swap_chain = create_swap_chain_default(
302 // &self.devices.dxgi_factory,
303 // &self.devices.device,
304 // self.hwnd,
305 // transparent,
306 // )?;
307 // self.context.back_buffer = [Some(set_render_target_view(
308 // &self.context.swap_chain,
309 // &self.devices.device,
310 // &self.devices.device_context,
311 // )?)];
312 // self.context.viewport = set_viewport(
313 // &self.devices.device_context,
314 // self.context.viewport[0].Width,
315 // self.context.viewport[0].Height,
316 // );
317 // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?;
318 // }
319 Ok(())
320 }
321
322 fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
323 if shadows.is_empty() {
324 return Ok(());
325 }
326 self.pipelines.shadow_pipeline.update_buffer(
327 &self.devices.device,
328 &self.devices.device_context,
329 shadows,
330 )?;
331 self.pipelines.shadow_pipeline.draw(
332 &self.devices.device_context,
333 &self.context.viewport,
334 &self.globals.global_params_buffer,
335 shadows.len() as u32,
336 )
337 }
338
339 fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
340 if quads.is_empty() {
341 return Ok(());
342 }
343 self.pipelines.quad_pipeline.update_buffer(
344 &self.devices.device,
345 &self.devices.device_context,
346 quads,
347 )?;
348 self.pipelines.quad_pipeline.draw(
349 &self.devices.device_context,
350 &self.context.viewport,
351 &self.globals.global_params_buffer,
352 quads.len() as u32,
353 )
354 }
355
356 fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
357 if paths.is_empty() {
358 return Ok(());
359 }
360 let mut vertices = Vec::new();
361 let mut sprites = Vec::with_capacity(paths.len());
362 let mut draw_indirect_commands = Vec::with_capacity(paths.len());
363 let mut start_vertex_location = 0;
364 for (i, path) in paths.iter().enumerate() {
365 draw_indirect_commands.push(DrawInstancedIndirectArgs {
366 vertex_count_per_instance: path.vertices.len() as u32,
367 instance_count: 1,
368 start_vertex_location,
369 start_instance_location: i as u32,
370 });
371 start_vertex_location += path.vertices.len() as u32;
372
373 vertices.extend(path.vertices.iter().map(|v| PathVertex {
374 xy_position: v.xy_position,
375 content_mask: ContentMask {
376 bounds: path.content_mask.bounds,
377 },
378 }));
379
380 sprites.push(PathSprite {
381 bounds: path.bounds,
382 color: path.color,
383 });
384 }
385
386 self.pipelines.paths_pipeline.update_buffer(
387 &self.devices.device,
388 &self.devices.device_context,
389 &sprites,
390 &vertices,
391 &draw_indirect_commands,
392 )?;
393 self.pipelines.paths_pipeline.draw(
394 &self.devices.device_context,
395 paths.len(),
396 &self.context.viewport,
397 &self.globals.global_params_buffer,
398 )
399 }
400
401 fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
402 if underlines.is_empty() {
403 return Ok(());
404 }
405 self.pipelines.underline_pipeline.update_buffer(
406 &self.devices.device,
407 &self.devices.device_context,
408 underlines,
409 )?;
410 self.pipelines.underline_pipeline.draw(
411 &self.devices.device_context,
412 &self.context.viewport,
413 &self.globals.global_params_buffer,
414 underlines.len() as u32,
415 )
416 }
417
418 fn draw_monochrome_sprites(
419 &mut self,
420 texture_id: AtlasTextureId,
421 sprites: &[MonochromeSprite],
422 ) -> Result<()> {
423 if sprites.is_empty() {
424 return Ok(());
425 }
426 self.pipelines.mono_sprites.update_buffer(
427 &self.devices.device,
428 &self.devices.device_context,
429 sprites,
430 )?;
431 let texture_view = self.atlas.get_texture_view(texture_id);
432 self.pipelines.mono_sprites.draw_with_texture(
433 &self.devices.device_context,
434 &texture_view,
435 &self.context.viewport,
436 &self.globals.global_params_buffer,
437 &self.globals.sampler,
438 sprites.len() as u32,
439 )
440 }
441
442 fn draw_polychrome_sprites(
443 &mut self,
444 texture_id: AtlasTextureId,
445 sprites: &[PolychromeSprite],
446 ) -> Result<()> {
447 if sprites.is_empty() {
448 return Ok(());
449 }
450 self.pipelines.poly_sprites.update_buffer(
451 &self.devices.device,
452 &self.devices.device_context,
453 sprites,
454 )?;
455 let texture_view = self.atlas.get_texture_view(texture_id);
456 self.pipelines.poly_sprites.draw_with_texture(
457 &self.devices.device_context,
458 &texture_view,
459 &self.context.viewport,
460 &self.globals.global_params_buffer,
461 &self.globals.sampler,
462 sprites.len() as u32,
463 )
464 }
465
466 fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
467 if surfaces.is_empty() {
468 return Ok(());
469 }
470 Ok(())
471 }
472}
473
474impl DirectXContext {
475 pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
476 // #[cfg(not(feature = "enable-renderdoc"))]
477 // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
478 // #[cfg(feature = "enable-renderdoc")]
479 let swap_chain =
480 create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
481 // #[cfg(not(feature = "enable-renderdoc"))]
482 // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
483 // #[cfg(not(feature = "enable-renderdoc"))]
484 // direct_composition.set_swap_chain(&swap_chain)?;
485 let (render_target, render_target_view) =
486 create_render_target_and_its_view(&swap_chain, &devices.device)?;
487 // let back_buffer = [Some(set_render_target_view(
488 // &swap_chain,
489 // &devices.device,
490 // &devices.device_context,
491 // )?)];
492 let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?;
493 let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
494 unsafe {
495 devices
496 .device_context
497 .OMSetRenderTargets(Some(&render_target_view), None);
498 }
499 set_rasterizer_state(&devices.device, &devices.device_context)?;
500
501 Ok(Self {
502 swap_chain,
503 render_target,
504 render_target_view,
505 msaa_target,
506 msaa_view,
507 viewport,
508 // #[cfg(not(feature = "enable-renderdoc"))]
509 // direct_composition,
510 })
511 }
512}
513
514impl DirectXRenderPipelines {
515 pub fn new(device: &ID3D11Device) -> Result<Self> {
516 let shadow_pipeline = PipelineState::new(
517 device,
518 "shadow_pipeline",
519 "shadow_vertex",
520 "shadow_fragment",
521 4,
522 )?;
523 let quad_pipeline =
524 PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?;
525 let paths_pipeline = PathsPipelineState::new(device)?;
526 let underline_pipeline = PipelineState::new(
527 device,
528 "underline_pipeline",
529 "underline_vertex",
530 "underline_fragment",
531 4,
532 )?;
533 let mono_sprites = PipelineState::new(
534 device,
535 "monochrome_sprite_pipeline",
536 "monochrome_sprite_vertex",
537 "monochrome_sprite_fragment",
538 512,
539 )?;
540 let poly_sprites = PipelineState::new(
541 device,
542 "polychrome_sprite_pipeline",
543 "polychrome_sprite_vertex",
544 "polychrome_sprite_fragment",
545 16,
546 )?;
547
548 Ok(Self {
549 shadow_pipeline,
550 quad_pipeline,
551 paths_pipeline,
552 underline_pipeline,
553 mono_sprites,
554 poly_sprites,
555 })
556 }
557}
558
559// #[cfg(not(feature = "enable-renderdoc"))]
560// impl DirectComposition {
561// pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
562// let comp_device = get_comp_device(&dxgi_device)?;
563// let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
564// let comp_visual = unsafe { comp_device.CreateVisual() }?;
565
566// Ok(Self {
567// comp_device,
568// comp_target,
569// comp_visual,
570// })
571// }
572
573// pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
574// unsafe {
575// self.comp_visual.SetContent(swap_chain)?;
576// self.comp_target.SetRoot(&self.comp_visual)?;
577// self.comp_device.Commit()?;
578// }
579// Ok(())
580// }
581// }
582
583impl DirectXGlobalElements {
584 pub fn new(device: &ID3D11Device) -> Result<Self> {
585 let global_params_buffer = unsafe {
586 let desc = D3D11_BUFFER_DESC {
587 ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
588 Usage: D3D11_USAGE_DYNAMIC,
589 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
590 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
591 ..Default::default()
592 };
593 let mut buffer = None;
594 device.CreateBuffer(&desc, None, Some(&mut buffer))?;
595 [buffer]
596 };
597
598 let sampler = unsafe {
599 let desc = D3D11_SAMPLER_DESC {
600 Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
601 AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
602 AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
603 AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
604 MipLODBias: 0.0,
605 MaxAnisotropy: 1,
606 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
607 BorderColor: [0.0; 4],
608 MinLOD: 0.0,
609 MaxLOD: D3D11_FLOAT32_MAX,
610 };
611 let mut output = None;
612 device.CreateSamplerState(&desc, Some(&mut output))?;
613 [output]
614 };
615
616 let blend_state = create_blend_state(device)?;
617
618 Ok(Self {
619 global_params_buffer,
620 sampler,
621 blend_state,
622 })
623 }
624}
625
626#[derive(Debug, Default)]
627#[repr(C)]
628struct GlobalParams {
629 viewport_size: [f32; 2],
630 _pad: u64,
631}
632
633struct PipelineState<T> {
634 label: &'static str,
635 vertex: ID3D11VertexShader,
636 fragment: ID3D11PixelShader,
637 buffer: ID3D11Buffer,
638 buffer_size: usize,
639 view: [Option<ID3D11ShaderResourceView>; 1],
640 _marker: std::marker::PhantomData<T>,
641}
642
643struct PathsPipelineState {
644 vertex: ID3D11VertexShader,
645 fragment: ID3D11PixelShader,
646 buffer: ID3D11Buffer,
647 buffer_size: usize,
648 vertex_buffer: Option<ID3D11Buffer>,
649 vertex_buffer_size: usize,
650 indirect_draw_buffer: ID3D11Buffer,
651 indirect_buffer_size: usize,
652 input_layout: ID3D11InputLayout,
653 view: [Option<ID3D11ShaderResourceView>; 1],
654}
655
656impl<T> PipelineState<T> {
657 fn new(
658 device: &ID3D11Device,
659 label: &'static str,
660 vertex_entry: &str,
661 fragment_entry: &str,
662 buffer_size: usize,
663 ) -> Result<Self> {
664 let vertex = {
665 let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
666 let bytes = unsafe {
667 std::slice::from_raw_parts(
668 shader_blob.GetBufferPointer() as *mut u8,
669 shader_blob.GetBufferSize(),
670 )
671 };
672 create_vertex_shader(device, bytes)?
673 };
674 let fragment = {
675 let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
676 let bytes = unsafe {
677 std::slice::from_raw_parts(
678 shader_blob.GetBufferPointer() as *mut u8,
679 shader_blob.GetBufferSize(),
680 )
681 };
682 create_fragment_shader(device, bytes)?
683 };
684 let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
685 let view = create_buffer_view(device, &buffer)?;
686
687 Ok(PipelineState {
688 label,
689 vertex,
690 fragment,
691 buffer,
692 buffer_size,
693 view,
694 _marker: std::marker::PhantomData,
695 })
696 }
697
698 fn update_buffer(
699 &mut self,
700 device: &ID3D11Device,
701 device_context: &ID3D11DeviceContext,
702 data: &[T],
703 ) -> Result<()> {
704 if self.buffer_size < data.len() {
705 let new_buffer_size = data.len().next_power_of_two();
706 log::info!(
707 "Updating {} buffer size from {} to {}",
708 self.label,
709 self.buffer_size,
710 new_buffer_size
711 );
712 let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
713 let view = create_buffer_view(device, &buffer)?;
714 self.buffer = buffer;
715 self.view = view;
716 self.buffer_size = new_buffer_size;
717 }
718 update_buffer(device_context, &self.buffer, data)
719 }
720
721 fn draw(
722 &self,
723 device_context: &ID3D11DeviceContext,
724 viewport: &[D3D11_VIEWPORT],
725 global_params: &[Option<ID3D11Buffer>],
726 instance_count: u32,
727 ) -> Result<()> {
728 set_pipeline_state(
729 device_context,
730 &self.view,
731 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
732 viewport,
733 &self.vertex,
734 &self.fragment,
735 global_params,
736 );
737 unsafe {
738 device_context.DrawInstanced(4, instance_count, 0, 0);
739 }
740 Ok(())
741 }
742
743 fn draw_with_texture(
744 &self,
745 device_context: &ID3D11DeviceContext,
746 texture: &[Option<ID3D11ShaderResourceView>],
747 viewport: &[D3D11_VIEWPORT],
748 global_params: &[Option<ID3D11Buffer>],
749 sampler: &[Option<ID3D11SamplerState>],
750 instance_count: u32,
751 ) -> Result<()> {
752 set_pipeline_state(
753 device_context,
754 &self.view,
755 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
756 viewport,
757 &self.vertex,
758 &self.fragment,
759 global_params,
760 );
761 unsafe {
762 device_context.PSSetSamplers(0, Some(sampler));
763 device_context.VSSetShaderResources(0, Some(texture));
764 device_context.PSSetShaderResources(0, Some(texture));
765
766 device_context.DrawInstanced(4, instance_count, 0, 0);
767 }
768 Ok(())
769 }
770}
771
772impl PathsPipelineState {
773 fn new(device: &ID3D11Device) -> Result<Self> {
774 let (vertex, vertex_shader) = {
775 let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
776 let bytes = unsafe {
777 std::slice::from_raw_parts(
778 shader_blob.GetBufferPointer() as *mut u8,
779 shader_blob.GetBufferSize(),
780 )
781 };
782 (create_vertex_shader(device, bytes)?, shader_blob)
783 };
784 let fragment = {
785 let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
786 let bytes = unsafe {
787 std::slice::from_raw_parts(
788 shader_blob.GetBufferPointer() as *mut u8,
789 shader_blob.GetBufferSize(),
790 )
791 };
792 create_fragment_shader(device, bytes)?
793 };
794 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
795 let view = create_buffer_view(device, &buffer)?;
796 let vertex_buffer = Some(create_buffer(
797 device,
798 std::mem::size_of::<PathVertex<ScaledPixels>>(),
799 32,
800 )?);
801 let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
802 // Create input layout
803 let input_layout = unsafe {
804 let shader_bytes = std::slice::from_raw_parts(
805 vertex_shader.GetBufferPointer() as *const u8,
806 vertex_shader.GetBufferSize(),
807 );
808 let mut layout = None;
809 device.CreateInputLayout(
810 &[
811 D3D11_INPUT_ELEMENT_DESC {
812 SemanticName: windows::core::s!("POSITION"),
813 SemanticIndex: 0,
814 Format: DXGI_FORMAT_R32G32_FLOAT,
815 InputSlot: 0,
816 AlignedByteOffset: 0,
817 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
818 InstanceDataStepRate: 0,
819 },
820 D3D11_INPUT_ELEMENT_DESC {
821 SemanticName: windows::core::s!("TEXCOORD"),
822 SemanticIndex: 0,
823 Format: DXGI_FORMAT_R32G32_FLOAT,
824 InputSlot: 0,
825 AlignedByteOffset: 8,
826 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
827 InstanceDataStepRate: 0,
828 },
829 D3D11_INPUT_ELEMENT_DESC {
830 SemanticName: windows::core::s!("TEXCOORD"),
831 SemanticIndex: 1,
832 Format: DXGI_FORMAT_R32G32_FLOAT,
833 InputSlot: 0,
834 AlignedByteOffset: 16,
835 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
836 InstanceDataStepRate: 0,
837 },
838 ],
839 shader_bytes,
840 Some(&mut layout),
841 )?;
842 layout.unwrap()
843 };
844
845 Ok(Self {
846 vertex,
847 fragment,
848 buffer,
849 buffer_size: 32,
850 vertex_buffer,
851 vertex_buffer_size: 32,
852 indirect_draw_buffer,
853 indirect_buffer_size: 32,
854 input_layout,
855 view,
856 })
857 }
858
859 fn update_buffer(
860 &mut self,
861 device: &ID3D11Device,
862 device_context: &ID3D11DeviceContext,
863 buffer_data: &[PathSprite],
864 vertices_data: &[PathVertex<ScaledPixels>],
865 draw_commands: &[DrawInstancedIndirectArgs],
866 ) -> Result<()> {
867 if self.buffer_size < buffer_data.len() {
868 let new_buffer_size = buffer_data.len().next_power_of_two();
869 log::info!(
870 "Updating Paths Pipeline buffer size from {} to {}",
871 self.buffer_size,
872 new_buffer_size
873 );
874 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
875 let view = create_buffer_view(device, &buffer)?;
876 self.buffer = buffer;
877 self.view = view;
878 self.buffer_size = new_buffer_size;
879 }
880 update_buffer(device_context, &self.buffer, buffer_data)?;
881 if self.vertex_buffer_size < vertices_data.len() {
882 let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
883 log::info!(
884 "Updating Paths Pipeline vertex buffer size from {} to {}",
885 self.vertex_buffer_size,
886 new_vertex_buffer_size
887 );
888 let vertex_buffer = create_buffer(
889 device,
890 std::mem::size_of::<PathVertex<ScaledPixels>>(),
891 new_vertex_buffer_size,
892 )?;
893 self.vertex_buffer = Some(vertex_buffer);
894 self.vertex_buffer_size = new_vertex_buffer_size;
895 }
896 update_buffer(
897 device_context,
898 self.vertex_buffer.as_ref().unwrap(),
899 vertices_data,
900 )?;
901 if self.indirect_buffer_size < draw_commands.len() {
902 let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
903 log::info!(
904 "Updating Paths Pipeline indirect buffer size from {} to {}",
905 self.indirect_buffer_size,
906 new_indirect_buffer_size
907 );
908 let indirect_draw_buffer =
909 create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
910 self.indirect_draw_buffer = indirect_draw_buffer;
911 self.indirect_buffer_size = new_indirect_buffer_size;
912 }
913 update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
914 Ok(())
915 }
916
917 fn draw(
918 &self,
919 device_context: &ID3D11DeviceContext,
920 count: usize,
921 viewport: &[D3D11_VIEWPORT],
922 global_params: &[Option<ID3D11Buffer>],
923 ) -> Result<()> {
924 set_pipeline_state(
925 device_context,
926 &self.view,
927 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
928 viewport,
929 &self.vertex,
930 &self.fragment,
931 global_params,
932 );
933 unsafe {
934 const STRIDE: u32 = std::mem::size_of::<PathVertex<ScaledPixels>>() as u32;
935 device_context.IASetVertexBuffers(
936 0,
937 1,
938 Some(&self.vertex_buffer),
939 Some(&STRIDE),
940 Some(&0),
941 );
942 device_context.IASetInputLayout(&self.input_layout);
943 }
944 for i in 0..count {
945 unsafe {
946 device_context.DrawInstancedIndirect(
947 &self.indirect_draw_buffer,
948 (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
949 );
950 }
951 }
952 Ok(())
953 }
954}
955
956#[derive(Clone, Debug, Eq, PartialEq)]
957#[repr(C)]
958struct PathSprite {
959 bounds: Bounds<ScaledPixels>,
960 color: Background,
961}
962
963impl Drop for DirectXContext {
964 fn drop(&mut self) {
965 unsafe {
966 ManuallyDrop::drop(&mut self.render_target);
967 ManuallyDrop::drop(&mut self.swap_chain);
968 }
969 }
970}
971
972#[inline]
973fn get_dxgi_factory() -> Result<IDXGIFactory6> {
974 #[cfg(debug_assertions)]
975 let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
976 #[cfg(not(debug_assertions))]
977 let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
978 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
979}
980
981fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
982 for adapter_index in 0.. {
983 let adapter: IDXGIAdapter1 = unsafe {
984 dxgi_factory
985 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
986 }?;
987 {
988 let desc = unsafe { adapter.GetDesc1() }?;
989 println!(
990 "Select GPU: {}",
991 String::from_utf16_lossy(&desc.Description)
992 );
993 }
994 // Check to see whether the adapter supports Direct3D 11, but don't
995 // create the actual device yet.
996 if get_device(&adapter, None, None).log_err().is_some() {
997 return Ok(adapter);
998 }
999 }
1000
1001 unreachable!()
1002}
1003
1004fn get_device(
1005 adapter: &IDXGIAdapter1,
1006 device: Option<*mut Option<ID3D11Device>>,
1007 context: Option<*mut Option<ID3D11DeviceContext>>,
1008) -> Result<()> {
1009 #[cfg(debug_assertions)]
1010 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
1011 #[cfg(not(debug_assertions))]
1012 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
1013 Ok(unsafe {
1014 D3D11CreateDevice(
1015 adapter,
1016 D3D_DRIVER_TYPE_UNKNOWN,
1017 HMODULE::default(),
1018 device_flags,
1019 Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1020 D3D11_SDK_VERSION,
1021 device,
1022 None,
1023 context,
1024 )?
1025 })
1026}
1027
1028// #[cfg(not(feature = "enable-renderdoc"))]
1029// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1030// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1031// }
1032
1033// fn create_swap_chain(
1034// dxgi_factory: &IDXGIFactory6,
1035// device: &ID3D11Device,
1036// transparent: bool,
1037// ) -> Result<IDXGISwapChain1> {
1038// let alpha_mode = if transparent {
1039// DXGI_ALPHA_MODE_PREMULTIPLIED
1040// } else {
1041// DXGI_ALPHA_MODE_IGNORE
1042// };
1043// let desc = DXGI_SWAP_CHAIN_DESC1 {
1044// Width: 1,
1045// Height: 1,
1046// Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1047// Stereo: false.into(),
1048// SampleDesc: DXGI_SAMPLE_DESC {
1049// Count: 1,
1050// Quality: 0,
1051// },
1052// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1053// BufferCount: BUFFER_COUNT as u32,
1054// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1055// Scaling: DXGI_SCALING_STRETCH,
1056// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1057// AlphaMode: alpha_mode,
1058// Flags: 0,
1059// };
1060// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1061// }
1062
1063// #[cfg(feature = "enable-renderdoc")]
1064fn create_swap_chain_default(
1065 dxgi_factory: &IDXGIFactory6,
1066 device: &ID3D11Device,
1067 hwnd: HWND,
1068 _transparent: bool,
1069) -> Result<ManuallyDrop<IDXGISwapChain1>> {
1070 use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1071
1072 let desc = DXGI_SWAP_CHAIN_DESC1 {
1073 Width: 1,
1074 Height: 1,
1075 Format: RENDER_TARGET_FORMAT,
1076 Stereo: false.into(),
1077 SampleDesc: DXGI_SAMPLE_DESC {
1078 Count: 1,
1079 Quality: 0,
1080 },
1081 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1082 BufferCount: BUFFER_COUNT as u32,
1083 Scaling: DXGI_SCALING_STRETCH,
1084 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1085 AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1086 Flags: 0,
1087 };
1088 let swap_chain =
1089 unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1090 unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1091 Ok(ManuallyDrop::new(swap_chain))
1092}
1093
1094#[inline]
1095fn create_render_target_and_its_view(
1096 swap_chain: &IDXGISwapChain1,
1097 device: &ID3D11Device,
1098) -> Result<(
1099 ManuallyDrop<ID3D11Texture2D>,
1100 [Option<ID3D11RenderTargetView>; 1],
1101)> {
1102 let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1103 let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1104 Format: BACK_BUFFER_FORMAT,
1105 ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1106 ..Default::default()
1107 };
1108 let mut render_target_view = None;
1109 unsafe {
1110 device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))?
1111 };
1112 Ok((
1113 ManuallyDrop::new(render_target),
1114 [Some(render_target_view.unwrap())],
1115 ))
1116}
1117
1118#[inline]
1119fn set_render_target_view(
1120 swap_chain: &IDXGISwapChain1,
1121 device: &ID3D11Device,
1122 device_context: &ID3D11DeviceContext,
1123) -> Result<ID3D11RenderTargetView> {
1124 // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
1125 // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
1126 let back_buffer = unsafe {
1127 let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
1128 let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1129 Format: BACK_BUFFER_FORMAT,
1130 ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1131 ..Default::default()
1132 };
1133 let mut buffer: Option<ID3D11RenderTargetView> = None;
1134 device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?;
1135 buffer.unwrap()
1136 };
1137 unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
1138 Ok(back_buffer)
1139}
1140
1141#[inline]
1142fn create_msaa_target_and_its_view(
1143 device: &ID3D11Device,
1144 width: u32,
1145 height: u32,
1146) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
1147 let msaa_target = unsafe {
1148 let mut output = None;
1149 let desc = D3D11_TEXTURE2D_DESC {
1150 Width: width,
1151 Height: height,
1152 MipLevels: 1,
1153 ArraySize: 1,
1154 Format: BACK_BUFFER_FORMAT,
1155 SampleDesc: DXGI_SAMPLE_DESC {
1156 Count: 4,
1157 Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1158 },
1159 Usage: D3D11_USAGE_DEFAULT,
1160 BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1161 CPUAccessFlags: 0,
1162 MiscFlags: 0,
1163 };
1164 device.CreateTexture2D(&desc, None, Some(&mut output))?;
1165 output.unwrap()
1166 };
1167 let msaa_view = unsafe {
1168 let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1169 Format: BACK_BUFFER_FORMAT,
1170 ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS,
1171 ..Default::default()
1172 };
1173 let mut output = None;
1174 device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?;
1175 output.unwrap()
1176 };
1177 Ok((msaa_target, msaa_view))
1178}
1179
1180#[inline]
1181fn set_viewport(
1182 device_context: &ID3D11DeviceContext,
1183 width: f32,
1184 height: f32,
1185) -> [D3D11_VIEWPORT; 1] {
1186 let viewport = [D3D11_VIEWPORT {
1187 TopLeftX: 0.0,
1188 TopLeftY: 0.0,
1189 Width: width,
1190 Height: height,
1191 MinDepth: 0.0,
1192 MaxDepth: 1.0,
1193 }];
1194 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1195 viewport
1196}
1197
1198#[inline]
1199fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1200 let desc = D3D11_RASTERIZER_DESC {
1201 FillMode: D3D11_FILL_SOLID,
1202 CullMode: D3D11_CULL_NONE,
1203 // FrontCounterClockwise: true.into(),
1204 FrontCounterClockwise: false.into(),
1205 DepthBias: 0,
1206 DepthBiasClamp: 0.0,
1207 SlopeScaledDepthBias: 0.0,
1208 DepthClipEnable: true.into(),
1209 ScissorEnable: false.into(),
1210 // MultisampleEnable: false.into(),
1211 MultisampleEnable: true.into(),
1212 AntialiasedLineEnable: false.into(),
1213 };
1214 let rasterizer_state = unsafe {
1215 let mut state = None;
1216 device.CreateRasterizerState(&desc, Some(&mut state))?;
1217 state.unwrap()
1218 };
1219 unsafe { device_context.RSSetState(&rasterizer_state) };
1220 Ok(())
1221}
1222
1223// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1224#[inline]
1225fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1226 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1227 // device performs the blend in linear space, which is ideal.
1228 let mut desc = D3D11_BLEND_DESC::default();
1229 desc.RenderTarget[0].BlendEnable = true.into();
1230 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1231 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1232 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1233 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1234 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1235 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1236 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1237 unsafe {
1238 let mut state = None;
1239 device.CreateBlendState(&desc, Some(&mut state))?;
1240 Ok(state.unwrap())
1241 }
1242}
1243
1244#[inline]
1245fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1246 unsafe {
1247 let mut shader = None;
1248 device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1249 Ok(shader.unwrap())
1250 }
1251}
1252
1253#[inline]
1254fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1255 unsafe {
1256 let mut shader = None;
1257 device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1258 Ok(shader.unwrap())
1259 }
1260}
1261
1262#[inline]
1263fn create_buffer(
1264 device: &ID3D11Device,
1265 element_size: usize,
1266 buffer_size: usize,
1267) -> Result<ID3D11Buffer> {
1268 let desc = D3D11_BUFFER_DESC {
1269 ByteWidth: (element_size * buffer_size) as u32,
1270 Usage: D3D11_USAGE_DYNAMIC,
1271 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1272 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1273 MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1274 StructureByteStride: element_size as u32,
1275 };
1276 let mut buffer = None;
1277 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1278 Ok(buffer.unwrap())
1279}
1280
1281#[inline]
1282fn create_buffer_view(
1283 device: &ID3D11Device,
1284 buffer: &ID3D11Buffer,
1285) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1286 let mut view = None;
1287 unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1288 Ok([view])
1289}
1290
1291#[inline]
1292fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1293 let desc = D3D11_BUFFER_DESC {
1294 ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1295 Usage: D3D11_USAGE_DYNAMIC,
1296 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1297 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1298 MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1299 StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1300 };
1301 let mut buffer = None;
1302 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1303 Ok(buffer.unwrap())
1304}
1305
1306#[inline]
1307fn pre_draw(
1308 device_context: &ID3D11DeviceContext,
1309 global_params_buffer: &[Option<ID3D11Buffer>; 1],
1310 view_port: &[D3D11_VIEWPORT; 1],
1311 render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1312 clear_color: [f32; 4],
1313 blend_state: &ID3D11BlendState,
1314) -> Result<()> {
1315 let global_params = global_params_buffer[0].as_ref().unwrap();
1316 update_buffer(
1317 device_context,
1318 global_params,
1319 &[GlobalParams {
1320 viewport_size: [view_port[0].Width, view_port[0].Height],
1321 ..Default::default()
1322 }],
1323 )?;
1324 unsafe {
1325 device_context.RSSetViewports(Some(view_port));
1326 device_context.OMSetRenderTargets(Some(render_target_view), None);
1327 device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1328 device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1329 }
1330 Ok(())
1331}
1332
1333#[inline]
1334fn update_buffer<T>(
1335 device_context: &ID3D11DeviceContext,
1336 buffer: &ID3D11Buffer,
1337 data: &[T],
1338) -> Result<()> {
1339 unsafe {
1340 let mut dest = std::mem::zeroed();
1341 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1342 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1343 device_context.Unmap(buffer, 0);
1344 }
1345 Ok(())
1346}
1347
1348#[inline]
1349fn set_pipeline_state(
1350 device_context: &ID3D11DeviceContext,
1351 buffer_view: &[Option<ID3D11ShaderResourceView>],
1352 topology: D3D_PRIMITIVE_TOPOLOGY,
1353 viewport: &[D3D11_VIEWPORT],
1354 vertex_shader: &ID3D11VertexShader,
1355 fragment_shader: &ID3D11PixelShader,
1356 global_params: &[Option<ID3D11Buffer>],
1357) {
1358 unsafe {
1359 device_context.VSSetShaderResources(1, Some(buffer_view));
1360 device_context.PSSetShaderResources(1, Some(buffer_view));
1361 device_context.IASetPrimitiveTopology(topology);
1362 device_context.RSSetViewports(Some(viewport));
1363 device_context.VSSetShader(vertex_shader, None);
1364 device_context.PSSetShader(fragment_shader, None);
1365 device_context.VSSetConstantBuffers(0, Some(global_params));
1366 device_context.PSSetConstantBuffers(0, Some(global_params));
1367 }
1368}
1369
1370const BUFFER_COUNT: usize = 3;
1371
1372mod shader_resources {
1373 use anyhow::Result;
1374 use windows::Win32::Graphics::Direct3D::{
1375 Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1376 ID3DBlob,
1377 };
1378 use windows_core::{HSTRING, PCSTR};
1379
1380 pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1381 unsafe {
1382 let mut entry = entry.to_owned();
1383 let mut target = target.to_owned();
1384 let mut compile_blob = None;
1385 let mut error_blob = None;
1386 let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1387 .join("src/platform/windows/shaders.hlsl")
1388 .canonicalize()
1389 .unwrap();
1390 entry.push_str("\0");
1391 target.push_str("\0");
1392 let entry_point = PCSTR::from_raw(entry.as_ptr());
1393 let target_cstr = PCSTR::from_raw(target.as_ptr());
1394 #[cfg(debug_assertions)]
1395 let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1396 #[cfg(not(debug_assertions))]
1397 let compile_flag = 0;
1398 let ret = D3DCompileFromFile(
1399 &HSTRING::from(shader_path.to_str().unwrap()),
1400 None,
1401 None,
1402 entry_point,
1403 target_cstr,
1404 compile_flag,
1405 0,
1406 &mut compile_blob,
1407 Some(&mut error_blob),
1408 );
1409 if ret.is_err() {
1410 let Some(error_blob) = error_blob else {
1411 return Err(anyhow::anyhow!("{ret:?}"));
1412 };
1413 let string_len = error_blob.GetBufferSize();
1414 let error_string_encode = Vec::from_raw_parts(
1415 error_blob.GetBufferPointer() as *mut u8,
1416 string_len,
1417 string_len,
1418 );
1419 let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1420 log::error!("Shader compile error: {}", error_string);
1421 return Err(anyhow::anyhow!("Compile error: {}", error_string));
1422 }
1423 Ok(compile_blob.unwrap())
1424 }
1425 }
1426}