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().context("Failed to get NVIDIA driver info"),
416 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")),
417 0x8086 => Err(anyhow::anyhow!("Intel driver info not implemented yet")),
418 _ => Err(anyhow::anyhow!("Unknown vendor detected.")),
419 }
420 .log_err()
421 .unwrap_or("Unknown Driver".to_string());
422 Ok(GpuSpecs {
423 is_software_emulated,
424 device_name,
425 driver_name,
426 driver_info: driver_version,
427 })
428 }
429}
430
431impl DirectXResources {
432 pub fn new(
433 devices: &DirectXDevices,
434 #[cfg(feature = "enable-renderdoc")] hwnd: HWND,
435 ) -> Result<Self> {
436 let width = 1;
437 let height = 1;
438
439 #[cfg(not(feature = "enable-renderdoc"))]
440 let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, width, height)?;
441 #[cfg(feature = "enable-renderdoc")]
442 let swap_chain =
443 create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?;
444
445 let (render_target, render_target_view, msaa_target, msaa_view, viewport) =
446 create_resources(devices, &swap_chain, width, height)?;
447 set_rasterizer_state(&devices.device, &devices.device_context)?;
448
449 Ok(Self {
450 swap_chain,
451 render_target,
452 render_target_view,
453 msaa_target,
454 msaa_view,
455 width,
456 height,
457 viewport,
458 })
459 }
460
461 #[inline]
462 fn recreate_resources(
463 &mut self,
464 devices: &DirectXDevices,
465 width: u32,
466 height: u32,
467 ) -> Result<()> {
468 let (render_target, render_target_view, msaa_target, msaa_view, viewport) =
469 create_resources(devices, &self.swap_chain, width, height)?;
470 self.render_target = render_target;
471 self.render_target_view = render_target_view;
472 self.msaa_target = msaa_target;
473 self.msaa_view = msaa_view;
474 self.viewport = viewport;
475 self.width = width;
476 self.height = height;
477 Ok(())
478 }
479}
480
481impl DirectXRenderPipelines {
482 pub fn new(device: &ID3D11Device) -> Result<Self> {
483 let shadow_pipeline = PipelineState::new(
484 device,
485 "shadow_pipeline",
486 "shadow_vertex",
487 "shadow_fragment",
488 4,
489 )?;
490 let quad_pipeline =
491 PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?;
492 let paths_pipeline = PathsPipelineState::new(device)?;
493 let underline_pipeline = PipelineState::new(
494 device,
495 "underline_pipeline",
496 "underline_vertex",
497 "underline_fragment",
498 4,
499 )?;
500 let mono_sprites = PipelineState::new(
501 device,
502 "monochrome_sprite_pipeline",
503 "monochrome_sprite_vertex",
504 "monochrome_sprite_fragment",
505 512,
506 )?;
507 let poly_sprites = PipelineState::new(
508 device,
509 "polychrome_sprite_pipeline",
510 "polychrome_sprite_vertex",
511 "polychrome_sprite_fragment",
512 16,
513 )?;
514
515 Ok(Self {
516 shadow_pipeline,
517 quad_pipeline,
518 paths_pipeline,
519 underline_pipeline,
520 mono_sprites,
521 poly_sprites,
522 })
523 }
524}
525
526#[cfg(not(feature = "enable-renderdoc"))]
527impl DirectComposition {
528 pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
529 let comp_device = get_comp_device(&dxgi_device)?;
530 let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
531 let comp_visual = unsafe { comp_device.CreateVisual() }?;
532
533 Ok(Self {
534 comp_device,
535 comp_target,
536 comp_visual,
537 })
538 }
539
540 pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
541 unsafe {
542 self.comp_visual.SetContent(swap_chain)?;
543 self.comp_target.SetRoot(&self.comp_visual)?;
544 self.comp_device.Commit()?;
545 }
546 Ok(())
547 }
548}
549
550impl DirectXGlobalElements {
551 pub fn new(device: &ID3D11Device) -> Result<Self> {
552 let global_params_buffer = unsafe {
553 let desc = D3D11_BUFFER_DESC {
554 ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
555 Usage: D3D11_USAGE_DYNAMIC,
556 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
557 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
558 ..Default::default()
559 };
560 let mut buffer = None;
561 device.CreateBuffer(&desc, None, Some(&mut buffer))?;
562 [buffer]
563 };
564
565 let sampler = unsafe {
566 let desc = D3D11_SAMPLER_DESC {
567 Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
568 AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
569 AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
570 AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
571 MipLODBias: 0.0,
572 MaxAnisotropy: 1,
573 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
574 BorderColor: [0.0; 4],
575 MinLOD: 0.0,
576 MaxLOD: D3D11_FLOAT32_MAX,
577 };
578 let mut output = None;
579 device.CreateSamplerState(&desc, Some(&mut output))?;
580 [output]
581 };
582
583 let blend_state = create_blend_state(device)?;
584
585 Ok(Self {
586 global_params_buffer,
587 sampler,
588 blend_state,
589 })
590 }
591}
592
593#[derive(Debug, Default)]
594#[repr(C)]
595struct GlobalParams {
596 viewport_size: [f32; 2],
597 _pad: u64,
598}
599
600struct PipelineState<T> {
601 label: &'static str,
602 vertex: ID3D11VertexShader,
603 fragment: ID3D11PixelShader,
604 buffer: ID3D11Buffer,
605 buffer_size: usize,
606 view: [Option<ID3D11ShaderResourceView>; 1],
607 _marker: std::marker::PhantomData<T>,
608}
609
610struct PathsPipelineState {
611 vertex: ID3D11VertexShader,
612 fragment: ID3D11PixelShader,
613 buffer: ID3D11Buffer,
614 buffer_size: usize,
615 vertex_buffer: Option<ID3D11Buffer>,
616 vertex_buffer_size: usize,
617 indirect_draw_buffer: ID3D11Buffer,
618 indirect_buffer_size: usize,
619 input_layout: ID3D11InputLayout,
620 view: [Option<ID3D11ShaderResourceView>; 1],
621}
622
623impl<T> PipelineState<T> {
624 fn new(
625 device: &ID3D11Device,
626 label: &'static str,
627 vertex_entry: &str,
628 fragment_entry: &str,
629 buffer_size: usize,
630 ) -> Result<Self> {
631 let vertex = {
632 let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
633 let bytes = unsafe {
634 std::slice::from_raw_parts(
635 shader_blob.GetBufferPointer() as *mut u8,
636 shader_blob.GetBufferSize(),
637 )
638 };
639 create_vertex_shader(device, bytes)?
640 };
641 let fragment = {
642 let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_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_fragment_shader(device, bytes)?
650 };
651 let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
652 let view = create_buffer_view(device, &buffer)?;
653
654 Ok(PipelineState {
655 label,
656 vertex,
657 fragment,
658 buffer,
659 buffer_size,
660 view,
661 _marker: std::marker::PhantomData,
662 })
663 }
664
665 fn update_buffer(
666 &mut self,
667 device: &ID3D11Device,
668 device_context: &ID3D11DeviceContext,
669 data: &[T],
670 ) -> Result<()> {
671 if self.buffer_size < data.len() {
672 let new_buffer_size = data.len().next_power_of_two();
673 log::info!(
674 "Updating {} buffer size from {} to {}",
675 self.label,
676 self.buffer_size,
677 new_buffer_size
678 );
679 let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
680 let view = create_buffer_view(device, &buffer)?;
681 self.buffer = buffer;
682 self.view = view;
683 self.buffer_size = new_buffer_size;
684 }
685 update_buffer(device_context, &self.buffer, data)
686 }
687
688 fn draw(
689 &self,
690 device_context: &ID3D11DeviceContext,
691 viewport: &[D3D11_VIEWPORT],
692 global_params: &[Option<ID3D11Buffer>],
693 instance_count: u32,
694 ) -> Result<()> {
695 set_pipeline_state(
696 device_context,
697 &self.view,
698 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
699 viewport,
700 &self.vertex,
701 &self.fragment,
702 global_params,
703 );
704 unsafe {
705 device_context.DrawInstanced(4, instance_count, 0, 0);
706 }
707 Ok(())
708 }
709
710 fn draw_with_texture(
711 &self,
712 device_context: &ID3D11DeviceContext,
713 texture: &[Option<ID3D11ShaderResourceView>],
714 viewport: &[D3D11_VIEWPORT],
715 global_params: &[Option<ID3D11Buffer>],
716 sampler: &[Option<ID3D11SamplerState>],
717 instance_count: u32,
718 ) -> Result<()> {
719 set_pipeline_state(
720 device_context,
721 &self.view,
722 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
723 viewport,
724 &self.vertex,
725 &self.fragment,
726 global_params,
727 );
728 unsafe {
729 device_context.PSSetSamplers(0, Some(sampler));
730 device_context.VSSetShaderResources(0, Some(texture));
731 device_context.PSSetShaderResources(0, Some(texture));
732
733 device_context.DrawInstanced(4, instance_count, 0, 0);
734 }
735 Ok(())
736 }
737}
738
739impl PathsPipelineState {
740 fn new(device: &ID3D11Device) -> Result<Self> {
741 let (vertex, vertex_shader) = {
742 let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
743 let bytes = unsafe {
744 std::slice::from_raw_parts(
745 shader_blob.GetBufferPointer() as *mut u8,
746 shader_blob.GetBufferSize(),
747 )
748 };
749 (create_vertex_shader(device, bytes)?, shader_blob)
750 };
751 let fragment = {
752 let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
753 let bytes = unsafe {
754 std::slice::from_raw_parts(
755 shader_blob.GetBufferPointer() as *mut u8,
756 shader_blob.GetBufferSize(),
757 )
758 };
759 create_fragment_shader(device, bytes)?
760 };
761 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
762 let view = create_buffer_view(device, &buffer)?;
763 let vertex_buffer = Some(create_buffer(
764 device,
765 std::mem::size_of::<DirectXPathVertex>(),
766 32,
767 )?);
768 let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
769 // Create input layout
770 let input_layout = unsafe {
771 let shader_bytes = std::slice::from_raw_parts(
772 vertex_shader.GetBufferPointer() as *const u8,
773 vertex_shader.GetBufferSize(),
774 );
775 let mut layout = None;
776 device.CreateInputLayout(
777 &[
778 D3D11_INPUT_ELEMENT_DESC {
779 SemanticName: windows::core::s!("POSITION"),
780 SemanticIndex: 0,
781 Format: DXGI_FORMAT_R32G32_FLOAT,
782 InputSlot: 0,
783 AlignedByteOffset: 0,
784 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
785 InstanceDataStepRate: 0,
786 },
787 D3D11_INPUT_ELEMENT_DESC {
788 SemanticName: windows::core::s!("TEXCOORD"),
789 SemanticIndex: 0,
790 Format: DXGI_FORMAT_R32G32_FLOAT,
791 InputSlot: 0,
792 AlignedByteOffset: 8,
793 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
794 InstanceDataStepRate: 0,
795 },
796 D3D11_INPUT_ELEMENT_DESC {
797 SemanticName: windows::core::s!("TEXCOORD"),
798 SemanticIndex: 1,
799 Format: DXGI_FORMAT_R32G32_FLOAT,
800 InputSlot: 0,
801 AlignedByteOffset: 16,
802 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
803 InstanceDataStepRate: 0,
804 },
805 D3D11_INPUT_ELEMENT_DESC {
806 SemanticName: windows::core::s!("GLOBALIDX"),
807 SemanticIndex: 0,
808 Format: DXGI_FORMAT_R32_UINT,
809 InputSlot: 0,
810 AlignedByteOffset: 24,
811 InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
812 InstanceDataStepRate: 0,
813 },
814 ],
815 shader_bytes,
816 Some(&mut layout),
817 )?;
818 layout.unwrap()
819 };
820
821 Ok(Self {
822 vertex,
823 fragment,
824 buffer,
825 buffer_size: 32,
826 vertex_buffer,
827 vertex_buffer_size: 32,
828 indirect_draw_buffer,
829 indirect_buffer_size: 32,
830 input_layout,
831 view,
832 })
833 }
834
835 fn update_buffer(
836 &mut self,
837 device: &ID3D11Device,
838 device_context: &ID3D11DeviceContext,
839 buffer_data: &[PathSprite],
840 vertices_data: &[DirectXPathVertex],
841 draw_commands: &[DrawInstancedIndirectArgs],
842 ) -> Result<()> {
843 if self.buffer_size < buffer_data.len() {
844 let new_buffer_size = buffer_data.len().next_power_of_two();
845 log::info!(
846 "Updating Paths Pipeline buffer size from {} to {}",
847 self.buffer_size,
848 new_buffer_size
849 );
850 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
851 let view = create_buffer_view(device, &buffer)?;
852 self.buffer = buffer;
853 self.view = view;
854 self.buffer_size = new_buffer_size;
855 }
856 update_buffer(device_context, &self.buffer, buffer_data)?;
857 if self.vertex_buffer_size < vertices_data.len() {
858 let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
859 log::info!(
860 "Updating Paths Pipeline vertex buffer size from {} to {}",
861 self.vertex_buffer_size,
862 new_vertex_buffer_size
863 );
864 let vertex_buffer = create_buffer(
865 device,
866 std::mem::size_of::<DirectXPathVertex>(),
867 new_vertex_buffer_size,
868 )?;
869 self.vertex_buffer = Some(vertex_buffer);
870 self.vertex_buffer_size = new_vertex_buffer_size;
871 }
872 update_buffer(
873 device_context,
874 self.vertex_buffer.as_ref().unwrap(),
875 vertices_data,
876 )?;
877 if self.indirect_buffer_size < draw_commands.len() {
878 let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
879 log::info!(
880 "Updating Paths Pipeline indirect buffer size from {} to {}",
881 self.indirect_buffer_size,
882 new_indirect_buffer_size
883 );
884 let indirect_draw_buffer =
885 create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
886 self.indirect_draw_buffer = indirect_draw_buffer;
887 self.indirect_buffer_size = new_indirect_buffer_size;
888 }
889 update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
890 Ok(())
891 }
892
893 fn draw(
894 &self,
895 device_context: &ID3D11DeviceContext,
896 count: usize,
897 viewport: &[D3D11_VIEWPORT],
898 global_params: &[Option<ID3D11Buffer>],
899 ) -> Result<()> {
900 set_pipeline_state(
901 device_context,
902 &self.view,
903 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
904 viewport,
905 &self.vertex,
906 &self.fragment,
907 global_params,
908 );
909 unsafe {
910 const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
911 device_context.IASetVertexBuffers(
912 0,
913 1,
914 Some(&self.vertex_buffer),
915 Some(&STRIDE),
916 Some(&0),
917 );
918 device_context.IASetInputLayout(&self.input_layout);
919 }
920 for i in 0..count {
921 unsafe {
922 device_context.DrawInstancedIndirect(
923 &self.indirect_draw_buffer,
924 (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
925 );
926 }
927 }
928 Ok(())
929 }
930}
931
932#[repr(C)]
933struct DirectXPathVertex {
934 xy_position: Point<ScaledPixels>,
935 content_mask: Bounds<ScaledPixels>,
936 sprite_index: u32,
937}
938
939#[derive(Clone, Debug, Eq, PartialEq)]
940#[repr(C)]
941struct PathSprite {
942 bounds: Bounds<ScaledPixels>,
943 color: Background,
944}
945
946impl Drop for DirectXResources {
947 fn drop(&mut self) {
948 unsafe {
949 ManuallyDrop::drop(&mut self.render_target);
950 }
951 }
952}
953
954#[inline]
955fn get_dxgi_factory() -> Result<IDXGIFactory6> {
956 #[cfg(debug_assertions)]
957 let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
958 #[cfg(not(debug_assertions))]
959 let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
960 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
961}
962
963fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
964 for adapter_index in 0.. {
965 let adapter: IDXGIAdapter1 = unsafe {
966 dxgi_factory
967 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
968 }?;
969 if let Ok(desc) = unsafe { adapter.GetDesc1() } {
970 let gpu_name = String::from_utf16_lossy(&desc.Description)
971 .trim_matches(char::from(0))
972 .to_string();
973 log::info!("Using GPU: {}", gpu_name);
974 }
975 // Check to see whether the adapter supports Direct3D 11, but don't
976 // create the actual device yet.
977 if get_device(&adapter, None, None).log_err().is_some() {
978 return Ok(adapter);
979 }
980 }
981
982 unreachable!()
983}
984
985fn get_device(
986 adapter: &IDXGIAdapter1,
987 device: Option<*mut Option<ID3D11Device>>,
988 context: Option<*mut Option<ID3D11DeviceContext>>,
989) -> Result<()> {
990 #[cfg(debug_assertions)]
991 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
992 #[cfg(not(debug_assertions))]
993 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
994 Ok(unsafe {
995 D3D11CreateDevice(
996 adapter,
997 D3D_DRIVER_TYPE_UNKNOWN,
998 HMODULE::default(),
999 device_flags,
1000 // 4x MSAA is required for Direct3D Feature Level 10.1 or better
1001 // 8x MSAA is required for Direct3D Feature Level 11.0 or better
1002 Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1003 D3D11_SDK_VERSION,
1004 device,
1005 None,
1006 context,
1007 )?
1008 })
1009}
1010
1011#[cfg(not(feature = "enable-renderdoc"))]
1012fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1013 Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1014}
1015
1016#[cfg(not(feature = "enable-renderdoc"))]
1017fn create_swap_chain(
1018 dxgi_factory: &IDXGIFactory6,
1019 device: &ID3D11Device,
1020 width: u32,
1021 height: u32,
1022) -> Result<IDXGISwapChain1> {
1023 let desc = DXGI_SWAP_CHAIN_DESC1 {
1024 Width: width,
1025 Height: height,
1026 Format: RENDER_TARGET_FORMAT,
1027 Stereo: false.into(),
1028 SampleDesc: DXGI_SAMPLE_DESC {
1029 Count: 1,
1030 Quality: 0,
1031 },
1032 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1033 BufferCount: BUFFER_COUNT as u32,
1034 // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1035 Scaling: DXGI_SCALING_STRETCH,
1036 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1037 AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1038 Flags: 0,
1039 };
1040 Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1041}
1042
1043#[cfg(feature = "enable-renderdoc")]
1044fn create_swap_chain(
1045 dxgi_factory: &IDXGIFactory6,
1046 device: &ID3D11Device,
1047 hwnd: HWND,
1048 width: u32,
1049 height: u32,
1050) -> Result<IDXGISwapChain1> {
1051 use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1052
1053 let desc = DXGI_SWAP_CHAIN_DESC1 {
1054 Width: width,
1055 Height: height,
1056 Format: RENDER_TARGET_FORMAT,
1057 Stereo: false.into(),
1058 SampleDesc: DXGI_SAMPLE_DESC {
1059 Count: 1,
1060 Quality: 0,
1061 },
1062 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1063 BufferCount: BUFFER_COUNT as u32,
1064 Scaling: DXGI_SCALING_STRETCH,
1065 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1066 AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1067 Flags: 0,
1068 };
1069 let swap_chain =
1070 unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1071 unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1072 Ok(swap_chain)
1073}
1074
1075#[inline]
1076fn create_resources(
1077 devices: &DirectXDevices,
1078 swap_chain: &IDXGISwapChain1,
1079 width: u32,
1080 height: u32,
1081) -> Result<(
1082 ManuallyDrop<ID3D11Texture2D>,
1083 [Option<ID3D11RenderTargetView>; 1],
1084 ID3D11Texture2D,
1085 [Option<ID3D11RenderTargetView>; 1],
1086 [D3D11_VIEWPORT; 1],
1087)> {
1088 let (render_target, render_target_view) =
1089 create_render_target_and_its_view(&swap_chain, &devices.device)?;
1090 let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1091 let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1092 Ok((
1093 render_target,
1094 render_target_view,
1095 msaa_target,
1096 msaa_view,
1097 viewport,
1098 ))
1099}
1100
1101#[inline]
1102fn create_render_target_and_its_view(
1103 swap_chain: &IDXGISwapChain1,
1104 device: &ID3D11Device,
1105) -> Result<(
1106 ManuallyDrop<ID3D11Texture2D>,
1107 [Option<ID3D11RenderTargetView>; 1],
1108)> {
1109 let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1110 let mut render_target_view = None;
1111 unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1112 Ok((
1113 ManuallyDrop::new(render_target),
1114 [Some(render_target_view.unwrap())],
1115 ))
1116}
1117
1118#[inline]
1119fn create_msaa_target_and_its_view(
1120 device: &ID3D11Device,
1121 width: u32,
1122 height: u32,
1123) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1124 let msaa_target = unsafe {
1125 let mut output = None;
1126 let desc = D3D11_TEXTURE2D_DESC {
1127 Width: width,
1128 Height: height,
1129 MipLevels: 1,
1130 ArraySize: 1,
1131 Format: RENDER_TARGET_FORMAT,
1132 SampleDesc: DXGI_SAMPLE_DESC {
1133 Count: MULTISAMPLE_COUNT,
1134 Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1135 },
1136 Usage: D3D11_USAGE_DEFAULT,
1137 BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1138 CPUAccessFlags: 0,
1139 MiscFlags: 0,
1140 };
1141 device.CreateTexture2D(&desc, None, Some(&mut output))?;
1142 output.unwrap()
1143 };
1144 let msaa_view = unsafe {
1145 let mut output = None;
1146 device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1147 output.unwrap()
1148 };
1149 Ok((msaa_target, [Some(msaa_view)]))
1150}
1151
1152#[inline]
1153fn set_viewport(
1154 device_context: &ID3D11DeviceContext,
1155 width: f32,
1156 height: f32,
1157) -> [D3D11_VIEWPORT; 1] {
1158 let viewport = [D3D11_VIEWPORT {
1159 TopLeftX: 0.0,
1160 TopLeftY: 0.0,
1161 Width: width,
1162 Height: height,
1163 MinDepth: 0.0,
1164 MaxDepth: 1.0,
1165 }];
1166 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1167 viewport
1168}
1169
1170#[inline]
1171fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1172 let desc = D3D11_RASTERIZER_DESC {
1173 FillMode: D3D11_FILL_SOLID,
1174 CullMode: D3D11_CULL_NONE,
1175 FrontCounterClockwise: false.into(),
1176 DepthBias: 0,
1177 DepthBiasClamp: 0.0,
1178 SlopeScaledDepthBias: 0.0,
1179 DepthClipEnable: true.into(),
1180 ScissorEnable: false.into(),
1181 // MultisampleEnable: false.into(),
1182 MultisampleEnable: true.into(),
1183 AntialiasedLineEnable: false.into(),
1184 };
1185 let rasterizer_state = unsafe {
1186 let mut state = None;
1187 device.CreateRasterizerState(&desc, Some(&mut state))?;
1188 state.unwrap()
1189 };
1190 unsafe { device_context.RSSetState(&rasterizer_state) };
1191 Ok(())
1192}
1193
1194// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1195#[inline]
1196fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1197 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1198 // device performs the blend in linear space, which is ideal.
1199 let mut desc = D3D11_BLEND_DESC::default();
1200 desc.RenderTarget[0].BlendEnable = true.into();
1201 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1202 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1203 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1204 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1205 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1206 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1207 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1208 unsafe {
1209 let mut state = None;
1210 device.CreateBlendState(&desc, Some(&mut state))?;
1211 Ok(state.unwrap())
1212 }
1213}
1214
1215#[inline]
1216fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1217 unsafe {
1218 let mut shader = None;
1219 device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1220 Ok(shader.unwrap())
1221 }
1222}
1223
1224#[inline]
1225fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1226 unsafe {
1227 let mut shader = None;
1228 device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1229 Ok(shader.unwrap())
1230 }
1231}
1232
1233#[inline]
1234fn create_buffer(
1235 device: &ID3D11Device,
1236 element_size: usize,
1237 buffer_size: usize,
1238) -> Result<ID3D11Buffer> {
1239 let desc = D3D11_BUFFER_DESC {
1240 ByteWidth: (element_size * buffer_size) as u32,
1241 Usage: D3D11_USAGE_DYNAMIC,
1242 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1243 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1244 MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1245 StructureByteStride: element_size as u32,
1246 };
1247 let mut buffer = None;
1248 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1249 Ok(buffer.unwrap())
1250}
1251
1252#[inline]
1253fn create_buffer_view(
1254 device: &ID3D11Device,
1255 buffer: &ID3D11Buffer,
1256) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1257 let mut view = None;
1258 unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1259 Ok([view])
1260}
1261
1262#[inline]
1263fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1264 let desc = D3D11_BUFFER_DESC {
1265 ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1266 Usage: D3D11_USAGE_DYNAMIC,
1267 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1268 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1269 MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1270 StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1271 };
1272 let mut buffer = None;
1273 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1274 Ok(buffer.unwrap())
1275}
1276
1277#[inline]
1278fn update_buffer<T>(
1279 device_context: &ID3D11DeviceContext,
1280 buffer: &ID3D11Buffer,
1281 data: &[T],
1282) -> Result<()> {
1283 unsafe {
1284 let mut dest = std::mem::zeroed();
1285 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1286 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1287 device_context.Unmap(buffer, 0);
1288 }
1289 Ok(())
1290}
1291
1292#[inline]
1293fn set_pipeline_state(
1294 device_context: &ID3D11DeviceContext,
1295 buffer_view: &[Option<ID3D11ShaderResourceView>],
1296 topology: D3D_PRIMITIVE_TOPOLOGY,
1297 viewport: &[D3D11_VIEWPORT],
1298 vertex_shader: &ID3D11VertexShader,
1299 fragment_shader: &ID3D11PixelShader,
1300 global_params: &[Option<ID3D11Buffer>],
1301) {
1302 unsafe {
1303 device_context.VSSetShaderResources(1, Some(buffer_view));
1304 device_context.PSSetShaderResources(1, Some(buffer_view));
1305 device_context.IASetPrimitiveTopology(topology);
1306 device_context.RSSetViewports(Some(viewport));
1307 device_context.VSSetShader(vertex_shader, None);
1308 device_context.PSSetShader(fragment_shader, None);
1309 device_context.VSSetConstantBuffers(0, Some(global_params));
1310 device_context.PSSetConstantBuffers(0, Some(global_params));
1311 }
1312}
1313
1314const BUFFER_COUNT: usize = 3;
1315
1316mod shader_resources {
1317 use anyhow::Result;
1318 use windows::Win32::Graphics::Direct3D::{
1319 Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1320 ID3DBlob,
1321 };
1322 use windows_core::{HSTRING, PCSTR};
1323
1324 pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1325 unsafe {
1326 let mut entry = entry.to_owned();
1327 let mut target = target.to_owned();
1328 let mut compile_blob = None;
1329 let mut error_blob = None;
1330 let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1331 .join("src/platform/windows/shaders.hlsl")
1332 .canonicalize()
1333 .unwrap();
1334 entry.push_str("\0");
1335 target.push_str("\0");
1336 let entry_point = PCSTR::from_raw(entry.as_ptr());
1337 let target_cstr = PCSTR::from_raw(target.as_ptr());
1338 #[cfg(debug_assertions)]
1339 let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1340 #[cfg(not(debug_assertions))]
1341 let compile_flag = 0;
1342 let ret = D3DCompileFromFile(
1343 &HSTRING::from(shader_path.to_str().unwrap()),
1344 None,
1345 None,
1346 entry_point,
1347 target_cstr,
1348 compile_flag,
1349 0,
1350 &mut compile_blob,
1351 Some(&mut error_blob),
1352 );
1353 if ret.is_err() {
1354 let Some(error_blob) = error_blob else {
1355 return Err(anyhow::anyhow!("{ret:?}"));
1356 };
1357 let string_len = error_blob.GetBufferSize();
1358 let error_string_encode = Vec::from_raw_parts(
1359 error_blob.GetBufferPointer() as *mut u8,
1360 string_len,
1361 string_len,
1362 );
1363 let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1364 log::error!("Shader compile error: {}", error_string);
1365 return Err(anyhow::anyhow!("Compile error: {}", error_string));
1366 }
1367 Ok(compile_blob.unwrap())
1368 }
1369 }
1370}
1371
1372mod nvidia {
1373 use std::os::raw::{c_char, c_int, c_uint};
1374
1375 use anyhow::{Context, Result};
1376 use windows::{
1377 Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1378 core::s,
1379 };
1380
1381 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1382 const NVAPI_SHORT_STRING_MAX: usize = 64;
1383
1384 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1385 #[allow(non_camel_case_types)]
1386 type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1387
1388 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi.h#L87
1389 #[allow(non_camel_case_types)]
1390 type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int;
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#L33
1408 let nvapi_init_ptr = nvapi_query(0x0150e828);
1409 if nvapi_init_ptr.is_null() {
1410 anyhow::bail!("Failed to get NVIDIA API function pointer");
1411 }
1412 let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr);
1413
1414 let result = nvapi_init();
1415 if result != 0 {
1416 anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result);
1417 }
1418
1419 // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1420 let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1421 if nvapi_get_driver_version_ptr.is_null() {
1422 anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1423 }
1424 let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1425 std::mem::transmute(nvapi_get_driver_version_ptr);
1426
1427 let mut driver_version: c_uint = 0;
1428 let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1429 let result = nvapi_get_driver_version(
1430 &mut driver_version as *mut c_uint,
1431 &mut build_branch_string as *mut NvAPI_ShortString,
1432 );
1433
1434 if result != 0 {
1435 anyhow::bail!(
1436 "Failed to get NVIDIA driver version, error code: {}",
1437 result
1438 );
1439 }
1440 let major = driver_version / 100;
1441 let minor = driver_version % 100;
1442 Ok(format!("{}.{}", major, minor))
1443 }
1444 }
1445}