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