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