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