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