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