1use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc};
2
3use ::util::ResultExt;
4use anyhow::{Context, Result};
5use collections::FxHasher;
6// #[cfg(not(feature = "enable-renderdoc"))]
7// use windows::Win32::Graphics::DirectComposition::*;
8use windows::{
9 Win32::{
10 Foundation::{HMODULE, HWND},
11 Graphics::{
12 Direct3D::*,
13 Direct3D11::*,
14 Dxgi::{Common::*, *},
15 },
16 },
17 core::*,
18};
19
20use crate::*;
21
22pub(crate) struct DirectXRenderer {
23 atlas: Arc<DirectXAtlas>,
24 devices: DirectXDevices,
25 context: DirectXContext,
26 globals: DirectXGlobalElements,
27 pipelines: DirectXRenderPipelines,
28 hwnd: HWND,
29 transparent: bool,
30}
31
32#[derive(Clone)]
33pub(crate) struct DirectXDevices {
34 dxgi_factory: IDXGIFactory6,
35 dxgi_device: IDXGIDevice,
36 device: ID3D11Device,
37 device_context: ID3D11DeviceContext,
38}
39
40struct DirectXContext {
41 swap_chain: IDXGISwapChain1,
42 back_buffer: [Option<ID3D11RenderTargetView>; 1],
43 viewport: [D3D11_VIEWPORT; 1],
44 // #[cfg(not(feature = "enable-renderdoc"))]
45 // direct_composition: DirectComposition,
46}
47
48struct DirectXRenderPipelines {
49 shadow_pipeline: PipelineState,
50 quad_pipeline: PipelineState,
51 paths_pipeline: PipelineState,
52 paths_indirect_draw_buffer: ID3D11Buffer,
53 underline_pipeline: PipelineState,
54 mono_sprites: PipelineState,
55 poly_sprites: PipelineState,
56}
57
58struct DirectXGlobalElements {
59 global_params_buffer: [Option<ID3D11Buffer>; 1],
60 sampler: [Option<ID3D11SamplerState>; 1],
61 blend_state: ID3D11BlendState,
62 blend_state_for_pr: ID3D11BlendState,
63}
64
65#[repr(C)]
66struct DrawInstancedIndirectArgs {
67 vertex_count_per_instance: u32,
68 instance_count: u32,
69 start_vertex_location: u32,
70 start_instance_location: u32,
71}
72
73// #[cfg(not(feature = "enable-renderdoc"))]
74// struct DirectComposition {
75// comp_device: IDCompositionDevice,
76// comp_target: IDCompositionTarget,
77// comp_visual: IDCompositionVisual,
78// }
79
80impl DirectXDevices {
81 pub(crate) fn new() -> Result<Self> {
82 let dxgi_factory = get_dxgi_factory()?;
83 let adapter = get_adapter(&dxgi_factory)?;
84 let (device, device_context) = {
85 let mut device: Option<ID3D11Device> = None;
86 let mut context: Option<ID3D11DeviceContext> = None;
87 get_device(&adapter, Some(&mut device), Some(&mut context))?;
88 (device.unwrap(), context.unwrap())
89 };
90 let dxgi_device: IDXGIDevice = device.cast()?;
91
92 Ok(Self {
93 dxgi_factory,
94 dxgi_device,
95 device,
96 device_context,
97 })
98 }
99}
100
101impl DirectXRenderer {
102 pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
103 let atlas = Arc::new(DirectXAtlas::new(
104 devices.device.clone(),
105 devices.device_context.clone(),
106 ));
107 let context = DirectXContext::new(devices, hwnd, transparent)?;
108 let globals = DirectXGlobalElements::new(&devices.device)?;
109 let pipelines = DirectXRenderPipelines::new(&devices.device)?;
110 Ok(DirectXRenderer {
111 atlas,
112 devices: devices.clone(),
113 context,
114 globals,
115 pipelines,
116 hwnd,
117 transparent,
118 })
119 }
120
121 pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
122 self.atlas.clone()
123 }
124
125 pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
126 pre_draw(
127 &self.devices.device_context,
128 &self.globals.global_params_buffer,
129 &self.context.viewport,
130 &self.context.back_buffer,
131 [0.0, 0.0, 0.0, 0.0],
132 &self.globals.blend_state,
133 )?;
134 for batch in scene.batches() {
135 match batch {
136 PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
137 PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
138 PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
139 PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
140 PrimitiveBatch::MonochromeSprites {
141 texture_id,
142 sprites,
143 } => self.draw_monochrome_sprites(texture_id, sprites),
144 PrimitiveBatch::PolychromeSprites {
145 texture_id,
146 sprites,
147 } => self.draw_polychrome_sprites(texture_id, sprites),
148 PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
149 }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
150 scene.paths.len(),
151 scene.shadows.len(),
152 scene.quads.len(),
153 scene.underlines.len(),
154 scene.monochrome_sprites.len(),
155 scene.polychrome_sprites.len(),
156 scene.surfaces.len(),))?;
157 }
158 unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?;
159 Ok(())
160 }
161
162 pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
163 unsafe { self.devices.device_context.OMSetRenderTargets(None, None) };
164 drop(self.context.back_buffer[0].take().unwrap());
165 unsafe {
166 self.context.swap_chain.ResizeBuffers(
167 BUFFER_COUNT as u32,
168 new_size.width.0 as u32,
169 new_size.height.0 as u32,
170 DXGI_FORMAT_B8G8R8A8_UNORM,
171 DXGI_SWAP_CHAIN_FLAG(0),
172 )?;
173 }
174 let backbuffer = set_render_target_view(
175 &self.context.swap_chain,
176 &self.devices.device,
177 &self.devices.device_context,
178 )?;
179 self.context.back_buffer[0] = Some(backbuffer);
180 self.context.viewport = set_viewport(
181 &self.devices.device_context,
182 new_size.width.0 as f32,
183 new_size.height.0 as f32,
184 );
185 Ok(())
186 }
187
188 // #[cfg(not(feature = "enable-renderdoc"))]
189 // pub(crate) fn update_transparency(
190 // &mut self,
191 // background_appearance: WindowBackgroundAppearance,
192 // ) -> Result<()> {
193 // // We only support setting `Transparent` and `Opaque` for now.
194 // match background_appearance {
195 // WindowBackgroundAppearance::Opaque => {
196 // if self.transparent {
197 // return Err(anyhow::anyhow!(
198 // "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
199 // ));
200 // }
201 // }
202 // WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
203 // if !self.transparent {
204 // return Err(anyhow::anyhow!(
205 // "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
206 // ));
207 // }
208 // }
209 // }
210 // Ok(())
211 // }
212
213 // #[cfg(feature = "enable-renderdoc")]
214 pub(crate) fn update_transparency(
215 &mut self,
216 background_appearance: WindowBackgroundAppearance,
217 ) -> Result<()> {
218 if background_appearance != WindowBackgroundAppearance::Opaque {
219 Err(anyhow::anyhow!(
220 "Set transparent background not supported when feature \"enable-renderdoc\" is enabled."
221 ))
222 } else {
223 Ok(())
224 }
225 }
226
227 fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
228 if shadows.is_empty() {
229 return Ok(());
230 }
231 update_buffer_capacity(
232 &self.pipelines.shadow_pipeline,
233 std::mem::size_of::<Shadow>(),
234 shadows.len(),
235 &self.devices.device,
236 )
237 .map(|input| update_pipeline(&mut self.pipelines.shadow_pipeline, input));
238 update_buffer(
239 &self.devices.device_context,
240 &self.pipelines.shadow_pipeline.buffer,
241 shadows,
242 )?;
243 draw_normal(
244 &self.devices.device_context,
245 &self.pipelines.shadow_pipeline,
246 &self.context.viewport,
247 &self.globals.global_params_buffer,
248 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
249 4,
250 shadows.len() as u32,
251 )
252 }
253
254 fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
255 if quads.is_empty() {
256 return Ok(());
257 }
258 update_buffer_capacity(
259 &self.pipelines.quad_pipeline,
260 std::mem::size_of::<Quad>(),
261 quads.len(),
262 &self.devices.device,
263 )
264 .map(|input| update_pipeline(&mut self.pipelines.quad_pipeline, input));
265 update_buffer(
266 &self.devices.device_context,
267 &self.pipelines.quad_pipeline.buffer,
268 quads,
269 )?;
270 draw_normal(
271 &self.devices.device_context,
272 &self.pipelines.quad_pipeline,
273 &self.context.viewport,
274 &self.globals.global_params_buffer,
275 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
276 4,
277 quads.len() as u32,
278 )
279 }
280
281 fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
282 if paths.is_empty() {
283 return Ok(());
284 }
285 println!("Drawing {} paths", paths.len());
286 let mut vertices = Vec::new();
287 let mut sprites = Vec::with_capacity(paths.len());
288 let mut draw_indirect_commands = Vec::with_capacity(paths.len());
289 let mut start_vertex_location = 0;
290 for (i, path) in paths.iter().enumerate() {
291 draw_indirect_commands.push(DrawInstancedIndirectArgs {
292 vertex_count_per_instance: path.vertices.len() as u32,
293 instance_count: 1,
294 start_vertex_location,
295 start_instance_location: i as u32,
296 });
297 start_vertex_location += path.vertices.len() as u32;
298
299 vertices.extend(path.vertices.iter().map(|v| PathVertex {
300 xy_position: v.xy_position,
301 content_mask: ContentMask {
302 bounds: path.content_mask.bounds,
303 },
304 }));
305
306 sprites.push(PathSprite {
307 bounds: path.bounds,
308 color: path.color,
309 });
310 }
311
312 update_buffer_capacity(
313 &self.pipelines.paths_pipeline,
314 std::mem::size_of::<PathSprite>(),
315 sprites.len(),
316 &self.devices.device,
317 )
318 .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input));
319 update_buffer(
320 &self.devices.device_context,
321 &self.pipelines.paths_pipeline.buffer,
322 &sprites,
323 )?;
324 update_indirect_buffer(
325 &self.devices.device_context,
326 &self.pipelines.paths_indirect_draw_buffer,
327 &draw_indirect_commands,
328 )?;
329 prepare_indirect_draws(
330 &self.devices.device_context,
331 &self.pipelines.paths_pipeline,
332 &self.context.viewport,
333 &self.globals.global_params_buffer,
334 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
335 )?;
336
337 for i in 0..paths.len() {
338 draw_indirect(
339 &self.devices.device_context,
340 &self.pipelines.paths_indirect_draw_buffer,
341 (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
342 );
343 }
344 Ok(())
345 }
346
347 fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
348 if underlines.is_empty() {
349 return Ok(());
350 }
351 update_buffer_capacity(
352 &self.pipelines.underline_pipeline,
353 std::mem::size_of::<Underline>(),
354 underlines.len(),
355 &self.devices.device,
356 )
357 .map(|input| update_pipeline(&mut self.pipelines.underline_pipeline, input));
358 update_buffer(
359 &self.devices.device_context,
360 &self.pipelines.underline_pipeline.buffer,
361 underlines,
362 )?;
363 draw_normal(
364 &self.devices.device_context,
365 &self.pipelines.underline_pipeline,
366 &self.context.viewport,
367 &self.globals.global_params_buffer,
368 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
369 4,
370 underlines.len() as u32,
371 )
372 }
373
374 fn draw_monochrome_sprites(
375 &mut self,
376 texture_id: AtlasTextureId,
377 sprites: &[MonochromeSprite],
378 ) -> Result<()> {
379 if sprites.is_empty() {
380 return Ok(());
381 }
382 let texture_view = self.atlas.get_texture_view(texture_id);
383 update_buffer_capacity(
384 &self.pipelines.mono_sprites,
385 std::mem::size_of::<MonochromeSprite>(),
386 sprites.len(),
387 &self.devices.device,
388 )
389 .map(|input| update_pipeline(&mut self.pipelines.mono_sprites, input));
390 update_buffer(
391 &self.devices.device_context,
392 &self.pipelines.mono_sprites.buffer,
393 sprites,
394 )?;
395 draw_with_texture(
396 &self.devices.device_context,
397 &self.pipelines.mono_sprites,
398 &texture_view,
399 &self.context.viewport,
400 &self.globals.global_params_buffer,
401 &self.globals.sampler,
402 sprites.len() as u32,
403 )
404 }
405
406 fn draw_polychrome_sprites(
407 &mut self,
408 texture_id: AtlasTextureId,
409 sprites: &[PolychromeSprite],
410 ) -> Result<()> {
411 if sprites.is_empty() {
412 return Ok(());
413 }
414 let texture_view = self.atlas.get_texture_view(texture_id);
415 update_buffer_capacity(
416 &self.pipelines.poly_sprites,
417 std::mem::size_of::<PolychromeSprite>(),
418 sprites.len(),
419 &self.devices.device,
420 )
421 .map(|input| update_pipeline(&mut self.pipelines.poly_sprites, input));
422 update_buffer(
423 &self.devices.device_context,
424 &self.pipelines.poly_sprites.buffer,
425 sprites,
426 )?;
427 draw_with_texture(
428 &self.devices.device_context,
429 &self.pipelines.poly_sprites,
430 &texture_view,
431 &self.context.viewport,
432 &self.globals.global_params_buffer,
433 &self.globals.sampler,
434 sprites.len() as u32,
435 )
436 }
437
438 fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
439 if surfaces.is_empty() {
440 return Ok(());
441 }
442 Ok(())
443 }
444}
445
446impl DirectXContext {
447 pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
448 // #[cfg(not(feature = "enable-renderdoc"))]
449 // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
450 // #[cfg(feature = "enable-renderdoc")]
451 let swap_chain =
452 create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
453 // #[cfg(not(feature = "enable-renderdoc"))]
454 // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
455 // #[cfg(not(feature = "enable-renderdoc"))]
456 // direct_composition.set_swap_chain(&swap_chain)?;
457 let back_buffer = [Some(set_render_target_view(
458 &swap_chain,
459 &devices.device,
460 &devices.device_context,
461 )?)];
462 let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
463 set_rasterizer_state(&devices.device, &devices.device_context)?;
464
465 Ok(Self {
466 swap_chain,
467 back_buffer,
468 viewport,
469 // #[cfg(not(feature = "enable-renderdoc"))]
470 // direct_composition,
471 })
472 }
473}
474
475impl DirectXRenderPipelines {
476 pub fn new(device: &ID3D11Device) -> Result<Self> {
477 let shadow_pipeline = create_pipieline(
478 device,
479 "shadow_vertex",
480 "shadow_fragment",
481 std::mem::size_of::<Shadow>(),
482 32,
483 )?;
484 let quad_pipeline = create_pipieline(
485 device,
486 "quad_vertex",
487 "quad_fragment",
488 std::mem::size_of::<Quad>(),
489 32,
490 )?;
491 let paths_pipeline = create_pipieline(
492 device,
493 "paths_vertex",
494 "paths_fragment",
495 std::mem::size_of::<PathSprite>(),
496 32,
497 )?;
498 let underline_pipeline = create_pipieline(
499 device,
500 "underline_vertex",
501 "underline_fragment",
502 std::mem::size_of::<Underline>(),
503 32,
504 )?;
505 let mono_sprites = create_pipieline(
506 device,
507 "monochrome_sprite_vertex",
508 "monochrome_sprite_fragment",
509 std::mem::size_of::<MonochromeSprite>(),
510 32,
511 )?;
512 let poly_sprites = create_pipieline(
513 device,
514 "polychrome_sprite_vertex",
515 "polychrome_sprite_fragment",
516 std::mem::size_of::<PolychromeSprite>(),
517 32,
518 )?;
519 let paths_indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
520
521 Ok(Self {
522 shadow_pipeline,
523 quad_pipeline,
524 paths_pipeline,
525 paths_indirect_draw_buffer,
526 underline_pipeline,
527 mono_sprites,
528 poly_sprites,
529 })
530 }
531}
532
533// #[cfg(not(feature = "enable-renderdoc"))]
534// impl DirectComposition {
535// pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
536// let comp_device = get_comp_device(&dxgi_device)?;
537// let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
538// let comp_visual = unsafe { comp_device.CreateVisual() }?;
539
540// Ok(Self {
541// comp_device,
542// comp_target,
543// comp_visual,
544// })
545// }
546
547// pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
548// unsafe {
549// self.comp_visual.SetContent(swap_chain)?;
550// self.comp_target.SetRoot(&self.comp_visual)?;
551// self.comp_device.Commit()?;
552// }
553// Ok(())
554// }
555// }
556
557impl DirectXGlobalElements {
558 pub fn new(device: &ID3D11Device) -> Result<Self> {
559 let global_params_buffer = unsafe {
560 let desc = D3D11_BUFFER_DESC {
561 ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
562 Usage: D3D11_USAGE_DYNAMIC,
563 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
564 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
565 ..Default::default()
566 };
567 let mut buffer = None;
568 device.CreateBuffer(&desc, None, Some(&mut buffer))?;
569 [buffer]
570 };
571
572 let sampler = unsafe {
573 let desc = D3D11_SAMPLER_DESC {
574 Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
575 AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
576 AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
577 AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
578 MipLODBias: 0.0,
579 MaxAnisotropy: 1,
580 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
581 BorderColor: [0.0; 4],
582 MinLOD: 0.0,
583 MaxLOD: D3D11_FLOAT32_MAX,
584 };
585 let mut output = None;
586 device.CreateSamplerState(&desc, Some(&mut output))?;
587 [output]
588 };
589
590 let blend_state = create_blend_state(device)?;
591 let blend_state_for_pr = create_blend_state_for_path_raster(device)?;
592
593 Ok(Self {
594 global_params_buffer,
595 sampler,
596 blend_state,
597 blend_state_for_pr,
598 })
599 }
600}
601
602#[derive(Debug, Default)]
603#[repr(C)]
604struct GlobalParams {
605 viewport_size: [f32; 2],
606 _pad: u64,
607}
608
609struct PipelineState {
610 vertex: ID3D11VertexShader,
611 fragment: ID3D11PixelShader,
612 buffer: ID3D11Buffer,
613 buffer_size: usize,
614 view: [Option<ID3D11ShaderResourceView>; 1],
615}
616
617#[derive(Clone, Debug, Eq, PartialEq)]
618#[repr(C)]
619struct PathSprite {
620 bounds: Bounds<ScaledPixels>,
621 color: Background,
622}
623
624fn get_dxgi_factory() -> Result<IDXGIFactory6> {
625 #[cfg(debug_assertions)]
626 let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
627 #[cfg(not(debug_assertions))]
628 let factory_flag = 0u32;
629 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
630}
631
632fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
633 for adapter_index in 0.. {
634 let adapter: IDXGIAdapter1 = unsafe {
635 dxgi_factory
636 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
637 }?;
638 {
639 let desc = unsafe { adapter.GetDesc1() }?;
640 println!(
641 "Select GPU: {}",
642 String::from_utf16_lossy(&desc.Description)
643 );
644 }
645 // Check to see whether the adapter supports Direct3D 11, but don't
646 // create the actual device yet.
647 if get_device(&adapter, None, None).log_err().is_some() {
648 return Ok(adapter);
649 }
650 }
651
652 unreachable!()
653}
654
655fn get_device(
656 adapter: &IDXGIAdapter1,
657 device: Option<*mut Option<ID3D11Device>>,
658 context: Option<*mut Option<ID3D11DeviceContext>>,
659) -> Result<()> {
660 #[cfg(debug_assertions)]
661 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
662 #[cfg(not(debug_assertions))]
663 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
664 Ok(unsafe {
665 D3D11CreateDevice(
666 adapter,
667 D3D_DRIVER_TYPE_UNKNOWN,
668 HMODULE::default(),
669 device_flags,
670 Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
671 D3D11_SDK_VERSION,
672 device,
673 None,
674 context,
675 )?
676 })
677}
678
679// #[cfg(not(feature = "enable-renderdoc"))]
680// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
681// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
682// }
683
684// fn create_swap_chain(
685// dxgi_factory: &IDXGIFactory6,
686// device: &ID3D11Device,
687// transparent: bool,
688// ) -> Result<IDXGISwapChain1> {
689// let alpha_mode = if transparent {
690// DXGI_ALPHA_MODE_PREMULTIPLIED
691// } else {
692// DXGI_ALPHA_MODE_IGNORE
693// };
694// let desc = DXGI_SWAP_CHAIN_DESC1 {
695// Width: 1,
696// Height: 1,
697// Format: DXGI_FORMAT_B8G8R8A8_UNORM,
698// Stereo: false.into(),
699// SampleDesc: DXGI_SAMPLE_DESC {
700// Count: 1,
701// Quality: 0,
702// },
703// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
704// BufferCount: BUFFER_COUNT as u32,
705// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
706// Scaling: DXGI_SCALING_STRETCH,
707// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
708// AlphaMode: alpha_mode,
709// Flags: 0,
710// };
711// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
712// }
713
714// #[cfg(feature = "enable-renderdoc")]
715fn create_swap_chain_default(
716 dxgi_factory: &IDXGIFactory6,
717 device: &ID3D11Device,
718 hwnd: HWND,
719 _transparent: bool,
720) -> Result<IDXGISwapChain1> {
721 use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
722
723 let desc = DXGI_SWAP_CHAIN_DESC1 {
724 Width: 1,
725 Height: 1,
726 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
727 Stereo: false.into(),
728 SampleDesc: DXGI_SAMPLE_DESC {
729 Count: 1,
730 Quality: 0,
731 },
732 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
733 BufferCount: BUFFER_COUNT as u32,
734 Scaling: DXGI_SCALING_STRETCH,
735 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
736 AlphaMode: DXGI_ALPHA_MODE_IGNORE,
737 Flags: 0,
738 };
739 let swap_chain =
740 unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
741 unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
742 Ok(swap_chain)
743}
744
745fn set_render_target_view(
746 swap_chain: &IDXGISwapChain1,
747 device: &ID3D11Device,
748 device_context: &ID3D11DeviceContext,
749) -> Result<ID3D11RenderTargetView> {
750 // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
751 // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
752 let back_buffer = unsafe {
753 let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
754 let mut buffer: Option<ID3D11RenderTargetView> = None;
755 device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?;
756 buffer.unwrap()
757 };
758 unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
759 Ok(back_buffer)
760}
761
762fn set_viewport(
763 device_context: &ID3D11DeviceContext,
764 width: f32,
765 height: f32,
766) -> [D3D11_VIEWPORT; 1] {
767 let viewport = [D3D11_VIEWPORT {
768 TopLeftX: 0.0,
769 TopLeftY: 0.0,
770 Width: width,
771 Height: height,
772 MinDepth: 0.0,
773 MaxDepth: 1.0,
774 }];
775 unsafe { device_context.RSSetViewports(Some(&viewport)) };
776 viewport
777}
778
779fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
780 let desc = D3D11_RASTERIZER_DESC {
781 FillMode: D3D11_FILL_SOLID,
782 CullMode: D3D11_CULL_NONE,
783 FrontCounterClockwise: false.into(),
784 DepthBias: 0,
785 DepthBiasClamp: 0.0,
786 SlopeScaledDepthBias: 0.0,
787 DepthClipEnable: true.into(),
788 ScissorEnable: false.into(),
789 MultisampleEnable: false.into(),
790 AntialiasedLineEnable: false.into(),
791 };
792 let rasterizer_state = unsafe {
793 let mut state = None;
794 device.CreateRasterizerState(&desc, Some(&mut state))?;
795 state.unwrap()
796 };
797 unsafe { device_context.RSSetState(&rasterizer_state) };
798 Ok(())
799}
800
801// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
802fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
803 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
804 // device performs the blend in linear space, which is ideal.
805 let mut desc = D3D11_BLEND_DESC::default();
806 desc.RenderTarget[0].BlendEnable = true.into();
807 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
808 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
809 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
810 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
811 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
812 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
813 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
814 unsafe {
815 let mut state = None;
816 device.CreateBlendState(&desc, Some(&mut state))?;
817 Ok(state.unwrap())
818 }
819}
820
821fn create_blend_state_for_path_raster(device: &ID3D11Device) -> Result<ID3D11BlendState> {
822 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
823 // device performs the blend in linear space, which is ideal.
824 let mut desc = D3D11_BLEND_DESC::default();
825 desc.RenderTarget[0].BlendEnable = true.into();
826 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
827 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
828 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
829 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
830 desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
831 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
832 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
833 unsafe {
834 let mut state = None;
835 device.CreateBlendState(&desc, Some(&mut state))?;
836 Ok(state.unwrap())
837 }
838}
839
840fn create_pipieline(
841 device: &ID3D11Device,
842 vertex_entry: &str,
843 fragment_entry: &str,
844 element_size: usize,
845 buffer_size: usize,
846) -> Result<PipelineState> {
847 let vertex = {
848 let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
849 let bytes = unsafe {
850 std::slice::from_raw_parts(
851 shader_blob.GetBufferPointer() as *mut u8,
852 shader_blob.GetBufferSize(),
853 )
854 };
855 create_vertex_shader(device, bytes)?
856 };
857 let fragment = {
858 let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
859 let bytes = unsafe {
860 std::slice::from_raw_parts(
861 shader_blob.GetBufferPointer() as *mut u8,
862 shader_blob.GetBufferSize(),
863 )
864 };
865 create_fragment_shader(device, bytes)?
866 };
867 let buffer = create_buffer(device, element_size, buffer_size)?;
868 let view = create_buffer_view(device, &buffer)?;
869 Ok(PipelineState {
870 vertex,
871 fragment,
872 buffer,
873 buffer_size,
874 view,
875 })
876}
877
878fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
879 unsafe {
880 let mut shader = None;
881 device.CreateVertexShader(bytes, None, Some(&mut shader))?;
882 Ok(shader.unwrap())
883 }
884}
885
886fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
887 unsafe {
888 let mut shader = None;
889 device.CreatePixelShader(bytes, None, Some(&mut shader))?;
890 Ok(shader.unwrap())
891 }
892}
893
894fn create_buffer(
895 device: &ID3D11Device,
896 element_size: usize,
897 buffer_size: usize,
898) -> Result<ID3D11Buffer> {
899 let desc = D3D11_BUFFER_DESC {
900 ByteWidth: (element_size * buffer_size) as u32,
901 Usage: D3D11_USAGE_DYNAMIC,
902 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
903 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
904 MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
905 StructureByteStride: element_size as u32,
906 };
907 let mut buffer = None;
908 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
909 Ok(buffer.unwrap())
910}
911
912fn create_buffer_view(
913 device: &ID3D11Device,
914 buffer: &ID3D11Buffer,
915) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
916 let mut view = None;
917 unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
918 Ok([view])
919}
920
921fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result<ID3D11Buffer> {
922 // let desc = D3D11_BUFFER_DESC {
923 // ByteWidth: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32 * buffer_size,
924 // Usage: D3D11_USAGE_DYNAMIC,
925 // BindFlags: D3D11_BIND_INDIRECT_DRAW.0 as u32,
926 // MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
927 // ..Default::default()
928 // };
929 let desc = D3D11_BUFFER_DESC {
930 ByteWidth: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32 * buffer_size,
931 Usage: D3D11_USAGE_DYNAMIC,
932 BindFlags: D3D11_BIND_INDEX_BUFFER.0 as u32,
933 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
934 MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
935 StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
936 };
937 let mut buffer = None;
938 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
939 Ok(buffer.unwrap())
940}
941
942fn update_global_params(
943 device_context: &ID3D11DeviceContext,
944 buffer: &[Option<ID3D11Buffer>; 1],
945 globals: GlobalParams,
946) -> Result<()> {
947 let buffer = buffer[0].as_ref().unwrap();
948 unsafe {
949 let mut data = std::mem::zeroed();
950 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut data))?;
951 std::ptr::copy_nonoverlapping(&globals, data.pData as *mut _, 1);
952 device_context.Unmap(buffer, 0);
953 }
954 Ok(())
955}
956
957fn pre_draw(
958 device_context: &ID3D11DeviceContext,
959 global_params_buffer: &[Option<ID3D11Buffer>; 1],
960 view_port: &[D3D11_VIEWPORT; 1],
961 render_target_view: &[Option<ID3D11RenderTargetView>; 1],
962 clear_color: [f32; 4],
963 blend_state: &ID3D11BlendState,
964) -> Result<()> {
965 update_global_params(
966 device_context,
967 global_params_buffer,
968 GlobalParams {
969 viewport_size: [view_port[0].Width, view_port[0].Height],
970 ..Default::default()
971 },
972 )?;
973 unsafe {
974 device_context.RSSetViewports(Some(view_port));
975 device_context.OMSetRenderTargets(Some(render_target_view), None);
976 device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
977 device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
978 }
979 Ok(())
980}
981
982fn update_buffer_capacity(
983 pipeline: &PipelineState,
984 element_size: usize,
985 data_size: usize,
986 device: &ID3D11Device,
987) -> Option<(ID3D11Buffer, usize, [Option<ID3D11ShaderResourceView>; 1])> {
988 if pipeline.buffer_size >= data_size {
989 return None;
990 }
991 println!("buffer too small: {} < {}", pipeline.buffer_size, data_size);
992 let buffer_size = data_size.next_power_of_two();
993 println!("New size: {}", buffer_size);
994 let buffer = create_buffer(device, element_size, buffer_size).unwrap();
995 let view = create_buffer_view(device, &buffer).unwrap();
996 Some((buffer, buffer_size, view))
997}
998
999fn update_pipeline(
1000 pipeline: &mut PipelineState,
1001 input: (ID3D11Buffer, usize, [Option<ID3D11ShaderResourceView>; 1]),
1002) {
1003 pipeline.buffer = input.0;
1004 pipeline.buffer_size = input.1;
1005 pipeline.view = input.2;
1006}
1007
1008fn update_buffer<T>(
1009 device_context: &ID3D11DeviceContext,
1010 buffer: &ID3D11Buffer,
1011 data: &[T],
1012) -> Result<()> {
1013 unsafe {
1014 let mut dest = std::mem::zeroed();
1015 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1016 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1017 device_context.Unmap(buffer, 0);
1018 }
1019 Ok(())
1020}
1021
1022fn update_indirect_buffer(
1023 device_context: &ID3D11DeviceContext,
1024 buffer: &ID3D11Buffer,
1025 data: &[DrawInstancedIndirectArgs],
1026) -> Result<()> {
1027 unsafe {
1028 let mut dest = std::mem::zeroed();
1029 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1030 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1031 device_context.Unmap(buffer, 0);
1032 }
1033 Ok(())
1034}
1035
1036fn prepare_indirect_draws(
1037 device_context: &ID3D11DeviceContext,
1038 pipeline: &PipelineState,
1039 viewport: &[D3D11_VIEWPORT],
1040 global_params: &[Option<ID3D11Buffer>],
1041 topology: D3D_PRIMITIVE_TOPOLOGY,
1042) -> Result<()> {
1043 unsafe {
1044 device_context.VSSetShaderResources(1, Some(&pipeline.view));
1045 device_context.PSSetShaderResources(1, Some(&pipeline.view));
1046 device_context.IASetPrimitiveTopology(topology);
1047 device_context.RSSetViewports(Some(viewport));
1048 device_context.VSSetShader(&pipeline.vertex, None);
1049 device_context.PSSetShader(&pipeline.fragment, None);
1050 device_context.VSSetConstantBuffers(0, Some(global_params));
1051 device_context.PSSetConstantBuffers(0, Some(global_params));
1052 }
1053 Ok(())
1054}
1055
1056fn draw_indirect(
1057 device_context: &ID3D11DeviceContext,
1058 indirect_draw_buffer: &ID3D11Buffer,
1059 offset: u32,
1060) {
1061 unsafe {
1062 device_context.DrawInstancedIndirect(indirect_draw_buffer, offset);
1063 }
1064}
1065
1066fn draw_normal(
1067 device_context: &ID3D11DeviceContext,
1068 pipeline: &PipelineState,
1069 viewport: &[D3D11_VIEWPORT],
1070 global_params: &[Option<ID3D11Buffer>],
1071 topology: D3D_PRIMITIVE_TOPOLOGY,
1072 vertex_count: u32,
1073 instance_count: u32,
1074) -> Result<()> {
1075 unsafe {
1076 device_context.VSSetShaderResources(1, Some(&pipeline.view));
1077 device_context.PSSetShaderResources(1, Some(&pipeline.view));
1078 device_context.IASetPrimitiveTopology(topology);
1079 device_context.RSSetViewports(Some(viewport));
1080 device_context.VSSetShader(&pipeline.vertex, None);
1081 device_context.PSSetShader(&pipeline.fragment, None);
1082 device_context.VSSetConstantBuffers(0, Some(global_params));
1083 device_context.PSSetConstantBuffers(0, Some(global_params));
1084
1085 device_context.DrawInstanced(vertex_count, instance_count, 0, 0);
1086 }
1087 Ok(())
1088}
1089
1090fn draw_with_texture(
1091 device_context: &ID3D11DeviceContext,
1092 pipeline: &PipelineState,
1093 texture: &[Option<ID3D11ShaderResourceView>],
1094 viewport: &[D3D11_VIEWPORT],
1095 global_params: &[Option<ID3D11Buffer>],
1096 sampler: &[Option<ID3D11SamplerState>],
1097 instance_count: u32,
1098) -> Result<()> {
1099 unsafe {
1100 device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
1101 device_context.RSSetViewports(Some(viewport));
1102 device_context.VSSetShader(&pipeline.vertex, None);
1103 device_context.PSSetShader(&pipeline.fragment, None);
1104 device_context.VSSetConstantBuffers(0, Some(global_params));
1105 device_context.PSSetConstantBuffers(0, Some(global_params));
1106 device_context.VSSetShaderResources(1, Some(&pipeline.view));
1107 device_context.PSSetShaderResources(1, Some(&pipeline.view));
1108 device_context.PSSetSamplers(0, Some(sampler));
1109 device_context.VSSetShaderResources(0, Some(texture));
1110 device_context.PSSetShaderResources(0, Some(texture));
1111
1112 device_context.DrawInstanced(4, instance_count, 0, 0);
1113 }
1114 Ok(())
1115}
1116
1117const BUFFER_COUNT: usize = 3;
1118
1119mod shader_resources {
1120 use anyhow::Result;
1121 use windows::Win32::Graphics::Direct3D::{
1122 Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1123 ID3DBlob,
1124 };
1125 use windows_core::{HSTRING, PCSTR};
1126
1127 pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1128 println!("Building shader: {}", entry);
1129 unsafe {
1130 let mut entry = entry.to_owned();
1131 let mut target = target.to_owned();
1132 let mut compile_blob = None;
1133 let mut error_blob = None;
1134 let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1135 .join("src/platform/windows/shaders.hlsl")
1136 .canonicalize()
1137 .unwrap();
1138 entry.push_str("\0");
1139 target.push_str("\0");
1140 let entry_point = PCSTR::from_raw(entry.as_ptr());
1141 let target_cstr = PCSTR::from_raw(target.as_ptr());
1142 println!(
1143 "Compiling shader: {} with target: {}",
1144 entry_point.display(),
1145 target_cstr.display()
1146 );
1147 #[cfg(debug_assertions)]
1148 let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1149 #[cfg(not(debug_assertions))]
1150 let compile_flag = 0;
1151 let ret = D3DCompileFromFile(
1152 &HSTRING::from(shader_path.to_str().unwrap()),
1153 None,
1154 None,
1155 entry_point,
1156 target_cstr,
1157 compile_flag,
1158 0,
1159 &mut compile_blob,
1160 Some(&mut error_blob),
1161 );
1162 println!("Shader compile result: {:?}", ret);
1163 if ret.is_err() {
1164 let Some(error_blob) = error_blob else {
1165 return Err(anyhow::anyhow!("{ret:?}"));
1166 };
1167 let string_len = error_blob.GetBufferSize();
1168 let error_string_encode = Vec::from_raw_parts(
1169 error_blob.GetBufferPointer() as *mut u8,
1170 string_len,
1171 string_len,
1172 );
1173 let error_string = String::from_utf8_lossy(&error_string_encode);
1174 println!("Shader compile error: {}", error_string);
1175 return Err(anyhow::anyhow!("Compile error: {}", error_string));
1176 }
1177 Ok(compile_blob.unwrap())
1178 }
1179 }
1180}