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