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 device: ID3D11Device,
40 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 => amd::get_driver_version(),
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 = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
634 let bytes = unsafe {
635 std::slice::from_raw_parts(
636 shader_blob.GetBufferPointer() as *mut u8,
637 shader_blob.GetBufferSize(),
638 )
639 };
640 create_vertex_shader(device, bytes)?
641 };
642 let fragment = {
643 let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
644 let bytes = unsafe {
645 std::slice::from_raw_parts(
646 shader_blob.GetBufferPointer() as *mut u8,
647 shader_blob.GetBufferSize(),
648 )
649 };
650 create_fragment_shader(device, bytes)?
651 };
652 let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
653 let view = create_buffer_view(device, &buffer)?;
654
655 Ok(PipelineState {
656 label,
657 vertex,
658 fragment,
659 buffer,
660 buffer_size,
661 view,
662 _marker: std::marker::PhantomData,
663 })
664 }
665
666 fn update_buffer(
667 &mut self,
668 device: &ID3D11Device,
669 device_context: &ID3D11DeviceContext,
670 data: &[T],
671 ) -> Result<()> {
672 if self.buffer_size < data.len() {
673 let new_buffer_size = data.len().next_power_of_two();
674 log::info!(
675 "Updating {} buffer size from {} to {}",
676 self.label,
677 self.buffer_size,
678 new_buffer_size
679 );
680 let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
681 let view = create_buffer_view(device, &buffer)?;
682 self.buffer = buffer;
683 self.view = view;
684 self.buffer_size = new_buffer_size;
685 }
686 update_buffer(device_context, &self.buffer, data)
687 }
688
689 fn draw(
690 &self,
691 device_context: &ID3D11DeviceContext,
692 viewport: &[D3D11_VIEWPORT],
693 global_params: &[Option<ID3D11Buffer>],
694 instance_count: u32,
695 ) -> Result<()> {
696 set_pipeline_state(
697 device_context,
698 &self.view,
699 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
700 viewport,
701 &self.vertex,
702 &self.fragment,
703 global_params,
704 );
705 unsafe {
706 device_context.DrawInstanced(4, instance_count, 0, 0);
707 }
708 Ok(())
709 }
710
711 fn draw_with_texture(
712 &self,
713 device_context: &ID3D11DeviceContext,
714 texture: &[Option<ID3D11ShaderResourceView>],
715 viewport: &[D3D11_VIEWPORT],
716 global_params: &[Option<ID3D11Buffer>],
717 sampler: &[Option<ID3D11SamplerState>],
718 instance_count: u32,
719 ) -> Result<()> {
720 set_pipeline_state(
721 device_context,
722 &self.view,
723 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
724 viewport,
725 &self.vertex,
726 &self.fragment,
727 global_params,
728 );
729 unsafe {
730 device_context.PSSetSamplers(0, Some(sampler));
731 device_context.VSSetShaderResources(0, Some(texture));
732 device_context.PSSetShaderResources(0, Some(texture));
733
734 device_context.DrawInstanced(4, instance_count, 0, 0);
735 }
736 Ok(())
737 }
738}
739
740impl PathsPipelineState {
741 fn new(device: &ID3D11Device) -> Result<Self> {
742 let (vertex, vertex_shader) = {
743 let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
744 let bytes = unsafe {
745 std::slice::from_raw_parts(
746 shader_blob.GetBufferPointer() as *mut u8,
747 shader_blob.GetBufferSize(),
748 )
749 };
750 (create_vertex_shader(device, bytes)?, shader_blob)
751 };
752 let fragment = {
753 let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
754 let bytes = unsafe {
755 std::slice::from_raw_parts(
756 shader_blob.GetBufferPointer() as *mut u8,
757 shader_blob.GetBufferSize(),
758 )
759 };
760 create_fragment_shader(device, bytes)?
761 };
762 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
763 let view = create_buffer_view(device, &buffer)?;
764 let vertex_buffer = Some(create_buffer(
765 device,
766 std::mem::size_of::<DirectXPathVertex>(),
767 32,
768 )?);
769 let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
770 // Create input layout
771 let input_layout = unsafe {
772 let shader_bytes = std::slice::from_raw_parts(
773 vertex_shader.GetBufferPointer() as *const u8,
774 vertex_shader.GetBufferSize(),
775 );
776 let mut layout = None;
777 device.CreateInputLayout(
778 &[
779 D3D11_INPUT_ELEMENT_DESC {
780 SemanticName: windows::core::s!("POSITION"),
781 SemanticIndex: 0,
782 Format: DXGI_FORMAT_R32G32_FLOAT,
783 InputSlot: 0,
784 AlignedByteOffset: 0,
785 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
786 InstanceDataStepRate: 0,
787 },
788 D3D11_INPUT_ELEMENT_DESC {
789 SemanticName: windows::core::s!("TEXCOORD"),
790 SemanticIndex: 0,
791 Format: DXGI_FORMAT_R32G32_FLOAT,
792 InputSlot: 0,
793 AlignedByteOffset: 8,
794 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
795 InstanceDataStepRate: 0,
796 },
797 D3D11_INPUT_ELEMENT_DESC {
798 SemanticName: windows::core::s!("TEXCOORD"),
799 SemanticIndex: 1,
800 Format: DXGI_FORMAT_R32G32_FLOAT,
801 InputSlot: 0,
802 AlignedByteOffset: 16,
803 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
804 InstanceDataStepRate: 0,
805 },
806 D3D11_INPUT_ELEMENT_DESC {
807 SemanticName: windows::core::s!("GLOBALIDX"),
808 SemanticIndex: 0,
809 Format: DXGI_FORMAT_R32_UINT,
810 InputSlot: 0,
811 AlignedByteOffset: 24,
812 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
813 InstanceDataStepRate: 0,
814 },
815 ],
816 shader_bytes,
817 Some(&mut layout),
818 )?;
819 layout.unwrap()
820 };
821
822 Ok(Self {
823 vertex,
824 fragment,
825 buffer,
826 buffer_size: 32,
827 vertex_buffer,
828 vertex_buffer_size: 32,
829 indirect_draw_buffer,
830 indirect_buffer_size: 32,
831 input_layout,
832 view,
833 })
834 }
835
836 fn update_buffer(
837 &mut self,
838 device: &ID3D11Device,
839 device_context: &ID3D11DeviceContext,
840 buffer_data: &[PathSprite],
841 vertices_data: &[DirectXPathVertex],
842 draw_commands: &[DrawInstancedIndirectArgs],
843 ) -> Result<()> {
844 if self.buffer_size < buffer_data.len() {
845 let new_buffer_size = buffer_data.len().next_power_of_two();
846 log::info!(
847 "Updating Paths Pipeline buffer size from {} to {}",
848 self.buffer_size,
849 new_buffer_size
850 );
851 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
852 let view = create_buffer_view(device, &buffer)?;
853 self.buffer = buffer;
854 self.view = view;
855 self.buffer_size = new_buffer_size;
856 }
857 update_buffer(device_context, &self.buffer, buffer_data)?;
858 if self.vertex_buffer_size < vertices_data.len() {
859 let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
860 log::info!(
861 "Updating Paths Pipeline vertex buffer size from {} to {}",
862 self.vertex_buffer_size,
863 new_vertex_buffer_size
864 );
865 let vertex_buffer = create_buffer(
866 device,
867 std::mem::size_of::<DirectXPathVertex>(),
868 new_vertex_buffer_size,
869 )?;
870 self.vertex_buffer = Some(vertex_buffer);
871 self.vertex_buffer_size = new_vertex_buffer_size;
872 }
873 update_buffer(
874 device_context,
875 self.vertex_buffer.as_ref().unwrap(),
876 vertices_data,
877 )?;
878 if self.indirect_buffer_size < draw_commands.len() {
879 let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
880 log::info!(
881 "Updating Paths Pipeline indirect buffer size from {} to {}",
882 self.indirect_buffer_size,
883 new_indirect_buffer_size
884 );
885 let indirect_draw_buffer =
886 create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
887 self.indirect_draw_buffer = indirect_draw_buffer;
888 self.indirect_buffer_size = new_indirect_buffer_size;
889 }
890 update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
891 Ok(())
892 }
893
894 fn draw(
895 &self,
896 device_context: &ID3D11DeviceContext,
897 count: usize,
898 viewport: &[D3D11_VIEWPORT],
899 global_params: &[Option<ID3D11Buffer>],
900 ) -> Result<()> {
901 set_pipeline_state(
902 device_context,
903 &self.view,
904 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
905 viewport,
906 &self.vertex,
907 &self.fragment,
908 global_params,
909 );
910 unsafe {
911 const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
912 device_context.IASetVertexBuffers(
913 0,
914 1,
915 Some(&self.vertex_buffer),
916 Some(&STRIDE),
917 Some(&0),
918 );
919 device_context.IASetInputLayout(&self.input_layout);
920 }
921 for i in 0..count {
922 unsafe {
923 device_context.DrawInstancedIndirect(
924 &self.indirect_draw_buffer,
925 (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
926 );
927 }
928 }
929 Ok(())
930 }
931}
932
933#[repr(C)]
934struct DirectXPathVertex {
935 xy_position: Point<ScaledPixels>,
936 content_mask: Bounds<ScaledPixels>,
937 sprite_index: u32,
938}
939
940#[derive(Clone, Debug, Eq, PartialEq)]
941#[repr(C)]
942struct PathSprite {
943 bounds: Bounds<ScaledPixels>,
944 color: Background,
945}
946
947impl Drop for DirectXResources {
948 fn drop(&mut self) {
949 unsafe {
950 ManuallyDrop::drop(&mut self.render_target);
951 }
952 }
953}
954
955#[inline]
956fn get_dxgi_factory() -> Result<IDXGIFactory6> {
957 #[cfg(debug_assertions)]
958 let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
959 #[cfg(not(debug_assertions))]
960 let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
961 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
962}
963
964fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
965 for adapter_index in 0.. {
966 let adapter: IDXGIAdapter1 = unsafe {
967 dxgi_factory
968 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
969 }?;
970 if let Ok(desc) = unsafe { adapter.GetDesc1() } {
971 let gpu_name = String::from_utf16_lossy(&desc.Description)
972 .trim_matches(char::from(0))
973 .to_string();
974 log::info!("Using GPU: {}", gpu_name);
975 }
976 // Check to see whether the adapter supports Direct3D 11, but don't
977 // create the actual device yet.
978 if get_device(&adapter, None, None).log_err().is_some() {
979 return Ok(adapter);
980 }
981 }
982
983 unreachable!()
984}
985
986fn get_device(
987 adapter: &IDXGIAdapter1,
988 device: Option<*mut Option<ID3D11Device>>,
989 context: Option<*mut Option<ID3D11DeviceContext>>,
990) -> Result<()> {
991 #[cfg(debug_assertions)]
992 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
993 #[cfg(not(debug_assertions))]
994 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
995 Ok(unsafe {
996 D3D11CreateDevice(
997 adapter,
998 D3D_DRIVER_TYPE_UNKNOWN,
999 HMODULE::default(),
1000 device_flags,
1001 // 4x MSAA is required for Direct3D Feature Level 10.1 or better
1002 // 8x MSAA is required for Direct3D Feature Level 11.0 or better
1003 Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1004 D3D11_SDK_VERSION,
1005 device,
1006 None,
1007 context,
1008 )?
1009 })
1010}
1011
1012#[cfg(not(feature = "enable-renderdoc"))]
1013fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1014 Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1015}
1016
1017#[cfg(not(feature = "enable-renderdoc"))]
1018fn create_swap_chain(
1019 dxgi_factory: &IDXGIFactory6,
1020 device: &ID3D11Device,
1021 width: u32,
1022 height: u32,
1023) -> Result<IDXGISwapChain1> {
1024 let desc = DXGI_SWAP_CHAIN_DESC1 {
1025 Width: width,
1026 Height: height,
1027 Format: RENDER_TARGET_FORMAT,
1028 Stereo: false.into(),
1029 SampleDesc: DXGI_SAMPLE_DESC {
1030 Count: 1,
1031 Quality: 0,
1032 },
1033 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1034 BufferCount: BUFFER_COUNT as u32,
1035 // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1036 Scaling: DXGI_SCALING_STRETCH,
1037 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1038 AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1039 Flags: 0,
1040 };
1041 Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1042}
1043
1044#[cfg(feature = "enable-renderdoc")]
1045fn create_swap_chain(
1046 dxgi_factory: &IDXGIFactory6,
1047 device: &ID3D11Device,
1048 hwnd: HWND,
1049 width: u32,
1050 height: u32,
1051) -> Result<IDXGISwapChain1> {
1052 use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1053
1054 let desc = DXGI_SWAP_CHAIN_DESC1 {
1055 Width: width,
1056 Height: height,
1057 Format: RENDER_TARGET_FORMAT,
1058 Stereo: false.into(),
1059 SampleDesc: DXGI_SAMPLE_DESC {
1060 Count: 1,
1061 Quality: 0,
1062 },
1063 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1064 BufferCount: BUFFER_COUNT as u32,
1065 Scaling: DXGI_SCALING_STRETCH,
1066 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1067 AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1068 Flags: 0,
1069 };
1070 let swap_chain =
1071 unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1072 unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1073 Ok(swap_chain)
1074}
1075
1076#[inline]
1077fn create_resources(
1078 devices: &DirectXDevices,
1079 swap_chain: &IDXGISwapChain1,
1080 width: u32,
1081 height: u32,
1082) -> Result<(
1083 ManuallyDrop<ID3D11Texture2D>,
1084 [Option<ID3D11RenderTargetView>; 1],
1085 ID3D11Texture2D,
1086 [Option<ID3D11RenderTargetView>; 1],
1087 [D3D11_VIEWPORT; 1],
1088)> {
1089 let (render_target, render_target_view) =
1090 create_render_target_and_its_view(&swap_chain, &devices.device)?;
1091 let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1092 let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1093 Ok((
1094 render_target,
1095 render_target_view,
1096 msaa_target,
1097 msaa_view,
1098 viewport,
1099 ))
1100}
1101
1102#[inline]
1103fn create_render_target_and_its_view(
1104 swap_chain: &IDXGISwapChain1,
1105 device: &ID3D11Device,
1106) -> Result<(
1107 ManuallyDrop<ID3D11Texture2D>,
1108 [Option<ID3D11RenderTargetView>; 1],
1109)> {
1110 let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1111 let mut render_target_view = None;
1112 unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1113 Ok((
1114 ManuallyDrop::new(render_target),
1115 [Some(render_target_view.unwrap())],
1116 ))
1117}
1118
1119#[inline]
1120fn create_msaa_target_and_its_view(
1121 device: &ID3D11Device,
1122 width: u32,
1123 height: u32,
1124) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1125 let msaa_target = unsafe {
1126 let mut output = None;
1127 let desc = D3D11_TEXTURE2D_DESC {
1128 Width: width,
1129 Height: height,
1130 MipLevels: 1,
1131 ArraySize: 1,
1132 Format: RENDER_TARGET_FORMAT,
1133 SampleDesc: DXGI_SAMPLE_DESC {
1134 Count: MULTISAMPLE_COUNT,
1135 Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1136 },
1137 Usage: D3D11_USAGE_DEFAULT,
1138 BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1139 CPUAccessFlags: 0,
1140 MiscFlags: 0,
1141 };
1142 device.CreateTexture2D(&desc, None, Some(&mut output))?;
1143 output.unwrap()
1144 };
1145 let msaa_view = unsafe {
1146 let mut output = None;
1147 device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1148 output.unwrap()
1149 };
1150 Ok((msaa_target, [Some(msaa_view)]))
1151}
1152
1153#[inline]
1154fn set_viewport(
1155 device_context: &ID3D11DeviceContext,
1156 width: f32,
1157 height: f32,
1158) -> [D3D11_VIEWPORT; 1] {
1159 let viewport = [D3D11_VIEWPORT {
1160 TopLeftX: 0.0,
1161 TopLeftY: 0.0,
1162 Width: width,
1163 Height: height,
1164 MinDepth: 0.0,
1165 MaxDepth: 1.0,
1166 }];
1167 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1168 viewport
1169}
1170
1171#[inline]
1172fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1173 let desc = D3D11_RASTERIZER_DESC {
1174 FillMode: D3D11_FILL_SOLID,
1175 CullMode: D3D11_CULL_NONE,
1176 FrontCounterClockwise: false.into(),
1177 DepthBias: 0,
1178 DepthBiasClamp: 0.0,
1179 SlopeScaledDepthBias: 0.0,
1180 DepthClipEnable: true.into(),
1181 ScissorEnable: false.into(),
1182 // MultisampleEnable: false.into(),
1183 MultisampleEnable: true.into(),
1184 AntialiasedLineEnable: false.into(),
1185 };
1186 let rasterizer_state = unsafe {
1187 let mut state = None;
1188 device.CreateRasterizerState(&desc, Some(&mut state))?;
1189 state.unwrap()
1190 };
1191 unsafe { device_context.RSSetState(&rasterizer_state) };
1192 Ok(())
1193}
1194
1195// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1196#[inline]
1197fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1198 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1199 // device performs the blend in linear space, which is ideal.
1200 let mut desc = D3D11_BLEND_DESC::default();
1201 desc.RenderTarget[0].BlendEnable = true.into();
1202 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1203 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1204 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1205 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1206 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1207 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1208 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1209 unsafe {
1210 let mut state = None;
1211 device.CreateBlendState(&desc, Some(&mut state))?;
1212 Ok(state.unwrap())
1213 }
1214}
1215
1216#[inline]
1217fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1218 unsafe {
1219 let mut shader = None;
1220 device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1221 Ok(shader.unwrap())
1222 }
1223}
1224
1225#[inline]
1226fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1227 unsafe {
1228 let mut shader = None;
1229 device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1230 Ok(shader.unwrap())
1231 }
1232}
1233
1234#[inline]
1235fn create_buffer(
1236 device: &ID3D11Device,
1237 element_size: usize,
1238 buffer_size: usize,
1239) -> Result<ID3D11Buffer> {
1240 let desc = D3D11_BUFFER_DESC {
1241 ByteWidth: (element_size * buffer_size) as u32,
1242 Usage: D3D11_USAGE_DYNAMIC,
1243 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1244 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1245 MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1246 StructureByteStride: element_size as u32,
1247 };
1248 let mut buffer = None;
1249 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1250 Ok(buffer.unwrap())
1251}
1252
1253#[inline]
1254fn create_buffer_view(
1255 device: &ID3D11Device,
1256 buffer: &ID3D11Buffer,
1257) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1258 let mut view = None;
1259 unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1260 Ok([view])
1261}
1262
1263#[inline]
1264fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1265 let desc = D3D11_BUFFER_DESC {
1266 ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1267 Usage: D3D11_USAGE_DYNAMIC,
1268 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1269 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1270 MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1271 StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1272 };
1273 let mut buffer = None;
1274 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1275 Ok(buffer.unwrap())
1276}
1277
1278#[inline]
1279fn update_buffer<T>(
1280 device_context: &ID3D11DeviceContext,
1281 buffer: &ID3D11Buffer,
1282 data: &[T],
1283) -> Result<()> {
1284 unsafe {
1285 let mut dest = std::mem::zeroed();
1286 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1287 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1288 device_context.Unmap(buffer, 0);
1289 }
1290 Ok(())
1291}
1292
1293#[inline]
1294fn set_pipeline_state(
1295 device_context: &ID3D11DeviceContext,
1296 buffer_view: &[Option<ID3D11ShaderResourceView>],
1297 topology: D3D_PRIMITIVE_TOPOLOGY,
1298 viewport: &[D3D11_VIEWPORT],
1299 vertex_shader: &ID3D11VertexShader,
1300 fragment_shader: &ID3D11PixelShader,
1301 global_params: &[Option<ID3D11Buffer>],
1302) {
1303 unsafe {
1304 device_context.VSSetShaderResources(1, Some(buffer_view));
1305 device_context.PSSetShaderResources(1, Some(buffer_view));
1306 device_context.IASetPrimitiveTopology(topology);
1307 device_context.RSSetViewports(Some(viewport));
1308 device_context.VSSetShader(vertex_shader, None);
1309 device_context.PSSetShader(fragment_shader, None);
1310 device_context.VSSetConstantBuffers(0, Some(global_params));
1311 device_context.PSSetConstantBuffers(0, Some(global_params));
1312 }
1313}
1314
1315const BUFFER_COUNT: usize = 3;
1316
1317mod shader_resources {
1318 use anyhow::Result;
1319 use windows::Win32::Graphics::Direct3D::{
1320 Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1321 ID3DBlob,
1322 };
1323 use windows_core::{HSTRING, PCSTR};
1324
1325 pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1326 unsafe {
1327 let mut entry = entry.to_owned();
1328 let mut target = target.to_owned();
1329 let mut compile_blob = None;
1330 let mut error_blob = None;
1331 let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1332 .join("src/platform/windows/shaders.hlsl")
1333 .canonicalize()
1334 .unwrap();
1335 entry.push_str("\0");
1336 target.push_str("\0");
1337 let entry_point = PCSTR::from_raw(entry.as_ptr());
1338 let target_cstr = PCSTR::from_raw(target.as_ptr());
1339 #[cfg(debug_assertions)]
1340 let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1341 #[cfg(not(debug_assertions))]
1342 let compile_flag = 0;
1343 let ret = D3DCompileFromFile(
1344 &HSTRING::from(shader_path.to_str().unwrap()),
1345 None,
1346 None,
1347 entry_point,
1348 target_cstr,
1349 compile_flag,
1350 0,
1351 &mut compile_blob,
1352 Some(&mut error_blob),
1353 );
1354 if ret.is_err() {
1355 let Some(error_blob) = error_blob else {
1356 return Err(anyhow::anyhow!("{ret:?}"));
1357 };
1358 let string_len = error_blob.GetBufferSize();
1359 let error_string_encode = Vec::from_raw_parts(
1360 error_blob.GetBufferPointer() as *mut u8,
1361 string_len,
1362 string_len,
1363 );
1364 let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1365 log::error!("Shader compile error: {}", error_string);
1366 return Err(anyhow::anyhow!("Compile error: {}", error_string));
1367 }
1368 Ok(compile_blob.unwrap())
1369 }
1370 }
1371}
1372
1373mod nvidia {
1374 use std::{
1375 ffi::CStr,
1376 os::raw::{c_char, c_int, c_uint},
1377 };
1378
1379 use anyhow::{Context, Result};
1380 use windows::{
1381 Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1382 core::s,
1383 };
1384
1385 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1386 const NVAPI_SHORT_STRING_MAX: usize = 64;
1387
1388 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1389 #[allow(non_camel_case_types)]
1390 type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1391
1392 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
1393 #[allow(non_camel_case_types)]
1394 type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
1395 driver_version: *mut c_uint,
1396 build_branch_string: *mut NvAPI_ShortString,
1397 ) -> c_int;
1398
1399 pub(super) fn get_driver_version() -> Result<String> {
1400 unsafe {
1401 // Try to load the NVIDIA driver DLL
1402 let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
1403 let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
1404 .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
1405 let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
1406
1407 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1408 let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1409 if nvapi_get_driver_version_ptr.is_null() {
1410 anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1411 }
1412 let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1413 std::mem::transmute(nvapi_get_driver_version_ptr);
1414
1415 let mut driver_version: c_uint = 0;
1416 let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1417 let result = nvapi_get_driver_version(
1418 &mut driver_version as *mut c_uint,
1419 &mut build_branch_string as *mut NvAPI_ShortString,
1420 );
1421
1422 if result != 0 {
1423 anyhow::bail!(
1424 "Failed to get NVIDIA driver version, error code: {}",
1425 result
1426 );
1427 }
1428 let major = driver_version / 100;
1429 let minor = driver_version % 100;
1430 let branch_string = CStr::from_ptr(build_branch_string.as_ptr());
1431 Ok(format!(
1432 "{}.{} {}",
1433 major,
1434 minor,
1435 branch_string.to_string_lossy()
1436 ))
1437 }
1438 }
1439}
1440
1441mod amd {
1442 use std::os::raw::{c_char, c_int, c_void};
1443
1444 // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145
1445 const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0;
1446
1447 // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204
1448 // This is an opaque type, using struct to represent it properly for FFI
1449 #[repr(C)]
1450 struct AGSContext {
1451 _private: [u8; 0],
1452 }
1453
1454 #[repr(C)]
1455 pub struct AGSGPUInfo {
1456 pub driver_version: *const c_char,
1457 pub radeon_software_version: *const c_char,
1458 pub num_devices: c_int,
1459 pub devices: *mut c_void,
1460 }
1461
1462 unsafe extern "C" {
1463 fn agsInitialize(
1464 version: c_int,
1465 config: *const c_void,
1466 context: *mut *mut AGSContext,
1467 gpu_info: *mut AGSGPUInfo,
1468 ) -> c_int;
1469
1470 fn agsDeInitialize(context: *mut AGSContext) -> c_int;
1471 }
1472
1473 pub(super) fn get_driver_version() -> anyhow::Result<String> {
1474 unsafe {
1475 let mut context: *mut AGSContext = std::ptr::null_mut();
1476 let mut gpu_info: AGSGPUInfo = AGSGPUInfo {
1477 driver_version: std::ptr::null(),
1478 radeon_software_version: std::ptr::null(),
1479 num_devices: 0,
1480 devices: std::ptr::null_mut(),
1481 };
1482
1483 let result = agsInitialize(
1484 AGS_CURRENT_VERSION,
1485 std::ptr::null(),
1486 &mut context,
1487 &mut gpu_info,
1488 );
1489 if result != 0 {
1490 return Err(anyhow::anyhow!(
1491 "Failed to initialize AGS, error code: {}",
1492 result
1493 ));
1494 }
1495
1496 // Vulkan acctually returns this as the driver version
1497 let software_version = if !gpu_info.radeon_software_version.is_null() {
1498 std::ffi::CStr::from_ptr(gpu_info.radeon_software_version)
1499 .to_string_lossy()
1500 .into_owned()
1501 } else {
1502 "Unknown Radeon Software Version".to_string()
1503 };
1504
1505 let driver_version = if !gpu_info.driver_version.is_null() {
1506 std::ffi::CStr::from_ptr(gpu_info.driver_version)
1507 .to_string_lossy()
1508 .into_owned()
1509 } else {
1510 "Unknown Radeon Driver Version".to_string()
1511 };
1512
1513 agsDeInitialize(context);
1514 Ok(format!("{} ({})", software_version, driver_version))
1515 }
1516 }
1517}
1518
1519mod intel {
1520 use windows::{
1521 Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice},
1522 core::Interface,
1523 };
1524
1525 pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result<String> {
1526 let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?;
1527 Ok(format!(
1528 "{}.{}.{}.{}",
1529 number >> 48,
1530 (number >> 32) & 0xFFFF,
1531 (number >> 16) & 0xFFFF,
1532 number & 0xFFFF
1533 ))
1534 }
1535}