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