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