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