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 self.pipelines.shadow_pipeline.draw(
234 &self.devices.device_context,
235 &self.context.viewport,
236 &self.globals.global_params_buffer,
237 shadows.len() as u32,
238 )
239 }
240
241 fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
242 if quads.is_empty() {
243 return Ok(());
244 }
245 self.pipelines.quad_pipeline.update_buffer(
246 &self.devices.device,
247 &self.devices.device_context,
248 quads,
249 )?;
250 self.pipelines.quad_pipeline.draw(
251 &self.devices.device_context,
252 &self.context.viewport,
253 &self.globals.global_params_buffer,
254 quads.len() as u32,
255 )
256 }
257
258 fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
259 if paths.is_empty() {
260 return Ok(());
261 }
262 let mut vertices = Vec::new();
263 let mut sprites = Vec::with_capacity(paths.len());
264 let mut draw_indirect_commands = Vec::with_capacity(paths.len());
265 let mut start_vertex_location = 0;
266 for (i, path) in paths.iter().enumerate() {
267 draw_indirect_commands.push(DrawInstancedIndirectArgs {
268 vertex_count_per_instance: path.vertices.len() as u32,
269 instance_count: 1,
270 start_vertex_location,
271 start_instance_location: i as u32,
272 });
273 start_vertex_location += path.vertices.len() as u32;
274
275 vertices.extend(path.vertices.iter().map(|v| PathVertex {
276 xy_position: v.xy_position,
277 content_mask: ContentMask {
278 bounds: path.content_mask.bounds,
279 },
280 }));
281
282 sprites.push(PathSprite {
283 bounds: path.bounds,
284 color: path.color,
285 });
286 }
287
288 self.pipelines.paths_pipeline.update_buffer(
289 &self.devices.device,
290 &self.devices.device_context,
291 &sprites,
292 &vertices,
293 &draw_indirect_commands,
294 )?;
295 self.pipelines.paths_pipeline.draw(
296 &self.devices.device_context,
297 paths.len(),
298 &self.context.viewport,
299 &self.globals.global_params_buffer,
300 )
301 }
302
303 fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
304 if underlines.is_empty() {
305 return Ok(());
306 }
307 self.pipelines.underline_pipeline.update_buffer(
308 &self.devices.device,
309 &self.devices.device_context,
310 underlines,
311 )?;
312 self.pipelines.underline_pipeline.draw(
313 &self.devices.device_context,
314 &self.context.viewport,
315 &self.globals.global_params_buffer,
316 underlines.len() as u32,
317 )
318 }
319
320 fn draw_monochrome_sprites(
321 &mut self,
322 texture_id: AtlasTextureId,
323 sprites: &[MonochromeSprite],
324 ) -> Result<()> {
325 if sprites.is_empty() {
326 return Ok(());
327 }
328 self.pipelines.mono_sprites.update_buffer(
329 &self.devices.device,
330 &self.devices.device_context,
331 sprites,
332 )?;
333 let texture_view = self.atlas.get_texture_view(texture_id);
334 self.pipelines.mono_sprites.draw_with_texture(
335 &self.devices.device_context,
336 &texture_view,
337 &self.context.viewport,
338 &self.globals.global_params_buffer,
339 &self.globals.sampler,
340 sprites.len() as u32,
341 )
342 }
343
344 fn draw_polychrome_sprites(
345 &mut self,
346 texture_id: AtlasTextureId,
347 sprites: &[PolychromeSprite],
348 ) -> Result<()> {
349 if sprites.is_empty() {
350 return Ok(());
351 }
352 self.pipelines.poly_sprites.update_buffer(
353 &self.devices.device,
354 &self.devices.device_context,
355 sprites,
356 )?;
357 let texture_view = self.atlas.get_texture_view(texture_id);
358 self.pipelines.poly_sprites.draw_with_texture(
359 &self.devices.device_context,
360 &texture_view,
361 &self.context.viewport,
362 &self.globals.global_params_buffer,
363 &self.globals.sampler,
364 sprites.len() as u32,
365 )
366 }
367
368 fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
369 if surfaces.is_empty() {
370 return Ok(());
371 }
372 Ok(())
373 }
374}
375
376impl DirectXContext {
377 pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
378 // #[cfg(not(feature = "enable-renderdoc"))]
379 // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
380 // #[cfg(feature = "enable-renderdoc")]
381 let swap_chain =
382 create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
383 // #[cfg(not(feature = "enable-renderdoc"))]
384 // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
385 // #[cfg(not(feature = "enable-renderdoc"))]
386 // direct_composition.set_swap_chain(&swap_chain)?;
387 let back_buffer = [Some(set_render_target_view(
388 &swap_chain,
389 &devices.device,
390 &devices.device_context,
391 )?)];
392 let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
393 set_rasterizer_state(&devices.device, &devices.device_context)?;
394
395 Ok(Self {
396 swap_chain,
397 back_buffer,
398 viewport,
399 // #[cfg(not(feature = "enable-renderdoc"))]
400 // direct_composition,
401 })
402 }
403}
404
405impl DirectXRenderPipelines {
406 pub fn new(device: &ID3D11Device) -> Result<Self> {
407 let shadow_pipeline = PipelineState::new(
408 device,
409 "shadow_pipeline",
410 "shadow_vertex",
411 "shadow_fragment",
412 32,
413 )?;
414 let quad_pipeline =
415 PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?;
416 let paths_pipeline = PathsPipelineState::new(device)?;
417 let underline_pipeline = PipelineState::new(
418 device,
419 "underline_pipeline",
420 "underline_vertex",
421 "underline_fragment",
422 32,
423 )?;
424 let mono_sprites = PipelineState::new(
425 device,
426 "monochrome_sprite_pipeline",
427 "monochrome_sprite_vertex",
428 "monochrome_sprite_fragment",
429 32,
430 )?;
431 let poly_sprites = PipelineState::new(
432 device,
433 "polychrome_sprite_pipeline",
434 "polychrome_sprite_vertex",
435 "polychrome_sprite_fragment",
436 32,
437 )?;
438
439 Ok(Self {
440 shadow_pipeline,
441 quad_pipeline,
442 paths_pipeline,
443 underline_pipeline,
444 mono_sprites,
445 poly_sprites,
446 })
447 }
448}
449
450// #[cfg(not(feature = "enable-renderdoc"))]
451// impl DirectComposition {
452// pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
453// let comp_device = get_comp_device(&dxgi_device)?;
454// let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
455// let comp_visual = unsafe { comp_device.CreateVisual() }?;
456
457// Ok(Self {
458// comp_device,
459// comp_target,
460// comp_visual,
461// })
462// }
463
464// pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
465// unsafe {
466// self.comp_visual.SetContent(swap_chain)?;
467// self.comp_target.SetRoot(&self.comp_visual)?;
468// self.comp_device.Commit()?;
469// }
470// Ok(())
471// }
472// }
473
474impl DirectXGlobalElements {
475 pub fn new(device: &ID3D11Device) -> Result<Self> {
476 let global_params_buffer = unsafe {
477 let desc = D3D11_BUFFER_DESC {
478 ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
479 Usage: D3D11_USAGE_DYNAMIC,
480 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
481 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
482 ..Default::default()
483 };
484 let mut buffer = None;
485 device.CreateBuffer(&desc, None, Some(&mut buffer))?;
486 [buffer]
487 };
488
489 let sampler = unsafe {
490 let desc = D3D11_SAMPLER_DESC {
491 Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
492 AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
493 AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
494 AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
495 MipLODBias: 0.0,
496 MaxAnisotropy: 1,
497 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
498 BorderColor: [0.0; 4],
499 MinLOD: 0.0,
500 MaxLOD: D3D11_FLOAT32_MAX,
501 };
502 let mut output = None;
503 device.CreateSamplerState(&desc, Some(&mut output))?;
504 [output]
505 };
506
507 let blend_state = create_blend_state(device)?;
508
509 Ok(Self {
510 global_params_buffer,
511 sampler,
512 blend_state,
513 })
514 }
515}
516
517#[derive(Debug, Default)]
518#[repr(C)]
519struct GlobalParams {
520 viewport_size: [f32; 2],
521 _pad: u64,
522}
523
524struct PipelineState<T> {
525 label: &'static str,
526 vertex: ID3D11VertexShader,
527 fragment: ID3D11PixelShader,
528 buffer: ID3D11Buffer,
529 buffer_size: usize,
530 view: [Option<ID3D11ShaderResourceView>; 1],
531 _marker: std::marker::PhantomData<T>,
532}
533
534struct PathsPipelineState {
535 vertex: ID3D11VertexShader,
536 fragment: ID3D11PixelShader,
537 buffer: ID3D11Buffer,
538 buffer_size: usize,
539 vertex_buffer: Option<ID3D11Buffer>,
540 vertex_buffer_size: usize,
541 indirect_draw_buffer: ID3D11Buffer,
542 indirect_buffer_size: usize,
543 input_layout: ID3D11InputLayout,
544 view: [Option<ID3D11ShaderResourceView>; 1],
545}
546
547impl<T> PipelineState<T> {
548 fn new(
549 device: &ID3D11Device,
550 label: &'static str,
551 vertex_entry: &str,
552 fragment_entry: &str,
553 buffer_size: usize,
554 ) -> Result<Self> {
555 let vertex = {
556 let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
557 let bytes = unsafe {
558 std::slice::from_raw_parts(
559 shader_blob.GetBufferPointer() as *mut u8,
560 shader_blob.GetBufferSize(),
561 )
562 };
563 create_vertex_shader(device, bytes)?
564 };
565 let fragment = {
566 let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
567 let bytes = unsafe {
568 std::slice::from_raw_parts(
569 shader_blob.GetBufferPointer() as *mut u8,
570 shader_blob.GetBufferSize(),
571 )
572 };
573 create_fragment_shader(device, bytes)?
574 };
575 let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
576 let view = create_buffer_view(device, &buffer)?;
577
578 Ok(PipelineState {
579 label,
580 vertex,
581 fragment,
582 buffer,
583 buffer_size,
584 view,
585 _marker: std::marker::PhantomData,
586 })
587 }
588
589 fn update_buffer(
590 &mut self,
591 device: &ID3D11Device,
592 device_context: &ID3D11DeviceContext,
593 data: &[T],
594 ) -> Result<()> {
595 if self.buffer_size < data.len() {
596 let new_buffer_size = data.len().next_power_of_two();
597 log::info!(
598 "Updating {} buffer size from {} to {}",
599 self.label,
600 self.buffer_size,
601 new_buffer_size
602 );
603 let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
604 let view = create_buffer_view(device, &buffer)?;
605 self.buffer = buffer;
606 self.view = view;
607 self.buffer_size = new_buffer_size;
608 }
609 update_buffer(device_context, &self.buffer, data)
610 }
611
612 fn draw(
613 &self,
614 device_context: &ID3D11DeviceContext,
615 viewport: &[D3D11_VIEWPORT],
616 global_params: &[Option<ID3D11Buffer>],
617 instance_count: u32,
618 ) -> Result<()> {
619 set_pipeline_state(
620 device_context,
621 &self.view,
622 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
623 viewport,
624 &self.vertex,
625 &self.fragment,
626 global_params,
627 );
628 unsafe {
629 device_context.DrawInstanced(4, instance_count, 0, 0);
630 }
631 Ok(())
632 }
633
634 fn draw_with_texture(
635 &self,
636 device_context: &ID3D11DeviceContext,
637 texture: &[Option<ID3D11ShaderResourceView>],
638 viewport: &[D3D11_VIEWPORT],
639 global_params: &[Option<ID3D11Buffer>],
640 sampler: &[Option<ID3D11SamplerState>],
641 instance_count: u32,
642 ) -> Result<()> {
643 set_pipeline_state(
644 device_context,
645 &self.view,
646 D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
647 viewport,
648 &self.vertex,
649 &self.fragment,
650 global_params,
651 );
652 unsafe {
653 device_context.PSSetSamplers(0, Some(sampler));
654 device_context.VSSetShaderResources(0, Some(texture));
655 device_context.PSSetShaderResources(0, Some(texture));
656
657 device_context.DrawInstanced(4, instance_count, 0, 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 fn update_buffer(
751 &mut self,
752 device: &ID3D11Device,
753 device_context: &ID3D11DeviceContext,
754 buffer_data: &[PathSprite],
755 vertices_data: &[PathVertex<ScaledPixels>],
756 draw_commands: &[DrawInstancedIndirectArgs],
757 ) -> Result<()> {
758 if self.buffer_size < buffer_data.len() {
759 let new_buffer_size = buffer_data.len().next_power_of_two();
760 log::info!(
761 "Updating Paths Pipeline buffer size from {} to {}",
762 self.buffer_size,
763 new_buffer_size
764 );
765 let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
766 let view = create_buffer_view(device, &buffer)?;
767 self.buffer = buffer;
768 self.view = view;
769 self.buffer_size = new_buffer_size;
770 }
771 update_buffer(device_context, &self.buffer, buffer_data)?;
772 if self.vertex_buffer_size < vertices_data.len() {
773 let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
774 log::info!(
775 "Updating Paths Pipeline vertex buffer size from {} to {}",
776 self.vertex_buffer_size,
777 new_vertex_buffer_size
778 );
779 let vertex_buffer = create_buffer(
780 device,
781 std::mem::size_of::<PathVertex<ScaledPixels>>(),
782 new_vertex_buffer_size,
783 )?;
784 self.vertex_buffer = Some(vertex_buffer);
785 self.vertex_buffer_size = new_vertex_buffer_size;
786 }
787 update_buffer(
788 device_context,
789 self.vertex_buffer.as_ref().unwrap(),
790 vertices_data,
791 )?;
792 if self.indirect_buffer_size < draw_commands.len() {
793 let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
794 log::info!(
795 "Updating Paths Pipeline indirect buffer size from {} to {}",
796 self.indirect_buffer_size,
797 new_indirect_buffer_size
798 );
799 let indirect_draw_buffer =
800 create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
801 self.indirect_draw_buffer = indirect_draw_buffer;
802 self.indirect_buffer_size = new_indirect_buffer_size;
803 }
804 update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
805 Ok(())
806 }
807
808 fn draw(
809 &self,
810 device_context: &ID3D11DeviceContext,
811 count: usize,
812 viewport: &[D3D11_VIEWPORT],
813 global_params: &[Option<ID3D11Buffer>],
814 ) -> Result<()> {
815 set_pipeline_state(
816 device_context,
817 &self.view,
818 D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
819 viewport,
820 &self.vertex,
821 &self.fragment,
822 global_params,
823 );
824 unsafe {
825 const STRIDE: u32 = std::mem::size_of::<PathVertex<ScaledPixels>>() as u32;
826 device_context.IASetVertexBuffers(
827 0,
828 1,
829 Some(&self.vertex_buffer),
830 Some(&STRIDE),
831 Some(&0),
832 );
833 device_context.IASetInputLayout(&self.input_layout);
834 }
835 for i in 0..count {
836 unsafe {
837 device_context.DrawInstancedIndirect(
838 &self.indirect_draw_buffer,
839 (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
840 );
841 }
842 }
843 Ok(())
844 }
845}
846
847#[derive(Clone, Debug, Eq, PartialEq)]
848#[repr(C)]
849struct PathSprite {
850 bounds: Bounds<ScaledPixels>,
851 color: Background,
852}
853
854#[inline]
855fn get_dxgi_factory() -> Result<IDXGIFactory6> {
856 #[cfg(debug_assertions)]
857 let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
858 #[cfg(not(debug_assertions))]
859 let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
860 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
861}
862
863fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
864 for adapter_index in 0.. {
865 let adapter: IDXGIAdapter1 = unsafe {
866 dxgi_factory
867 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
868 }?;
869 {
870 let desc = unsafe { adapter.GetDesc1() }?;
871 println!(
872 "Select GPU: {}",
873 String::from_utf16_lossy(&desc.Description)
874 );
875 }
876 // Check to see whether the adapter supports Direct3D 11, but don't
877 // create the actual device yet.
878 if get_device(&adapter, None, None).log_err().is_some() {
879 return Ok(adapter);
880 }
881 }
882
883 unreachable!()
884}
885
886fn get_device(
887 adapter: &IDXGIAdapter1,
888 device: Option<*mut Option<ID3D11Device>>,
889 context: Option<*mut Option<ID3D11DeviceContext>>,
890) -> Result<()> {
891 #[cfg(debug_assertions)]
892 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
893 #[cfg(not(debug_assertions))]
894 let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
895 Ok(unsafe {
896 D3D11CreateDevice(
897 adapter,
898 D3D_DRIVER_TYPE_UNKNOWN,
899 HMODULE::default(),
900 device_flags,
901 Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
902 D3D11_SDK_VERSION,
903 device,
904 None,
905 context,
906 )?
907 })
908}
909
910// #[cfg(not(feature = "enable-renderdoc"))]
911// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
912// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
913// }
914
915// fn create_swap_chain(
916// dxgi_factory: &IDXGIFactory6,
917// device: &ID3D11Device,
918// transparent: bool,
919// ) -> Result<IDXGISwapChain1> {
920// let alpha_mode = if transparent {
921// DXGI_ALPHA_MODE_PREMULTIPLIED
922// } else {
923// DXGI_ALPHA_MODE_IGNORE
924// };
925// let desc = DXGI_SWAP_CHAIN_DESC1 {
926// Width: 1,
927// Height: 1,
928// Format: DXGI_FORMAT_B8G8R8A8_UNORM,
929// Stereo: false.into(),
930// SampleDesc: DXGI_SAMPLE_DESC {
931// Count: 1,
932// Quality: 0,
933// },
934// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
935// BufferCount: BUFFER_COUNT as u32,
936// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
937// Scaling: DXGI_SCALING_STRETCH,
938// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
939// AlphaMode: alpha_mode,
940// Flags: 0,
941// };
942// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
943// }
944
945// #[cfg(feature = "enable-renderdoc")]
946fn create_swap_chain_default(
947 dxgi_factory: &IDXGIFactory6,
948 device: &ID3D11Device,
949 hwnd: HWND,
950 _transparent: bool,
951) -> Result<IDXGISwapChain1> {
952 use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
953
954 let desc = DXGI_SWAP_CHAIN_DESC1 {
955 Width: 1,
956 Height: 1,
957 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
958 Stereo: false.into(),
959 SampleDesc: DXGI_SAMPLE_DESC {
960 Count: 1,
961 Quality: 0,
962 },
963 BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
964 BufferCount: BUFFER_COUNT as u32,
965 Scaling: DXGI_SCALING_STRETCH,
966 SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
967 AlphaMode: DXGI_ALPHA_MODE_IGNORE,
968 Flags: 0,
969 };
970 let swap_chain =
971 unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
972 unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
973 Ok(swap_chain)
974}
975
976#[inline]
977fn set_render_target_view(
978 swap_chain: &IDXGISwapChain1,
979 device: &ID3D11Device,
980 device_context: &ID3D11DeviceContext,
981) -> Result<ID3D11RenderTargetView> {
982 // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
983 // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
984 let back_buffer = unsafe {
985 let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
986 let mut buffer: Option<ID3D11RenderTargetView> = None;
987 device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?;
988 buffer.unwrap()
989 };
990 unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
991 Ok(back_buffer)
992}
993
994#[inline]
995fn set_viewport(
996 device_context: &ID3D11DeviceContext,
997 width: f32,
998 height: f32,
999) -> [D3D11_VIEWPORT; 1] {
1000 let viewport = [D3D11_VIEWPORT {
1001 TopLeftX: 0.0,
1002 TopLeftY: 0.0,
1003 Width: width,
1004 Height: height,
1005 MinDepth: 0.0,
1006 MaxDepth: 1.0,
1007 }];
1008 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1009 viewport
1010}
1011
1012#[inline]
1013fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1014 let desc = D3D11_RASTERIZER_DESC {
1015 FillMode: D3D11_FILL_SOLID,
1016 CullMode: D3D11_CULL_NONE,
1017 // FrontCounterClockwise: true.into(),
1018 FrontCounterClockwise: false.into(),
1019 DepthBias: 0,
1020 DepthBiasClamp: 0.0,
1021 SlopeScaledDepthBias: 0.0,
1022 DepthClipEnable: true.into(),
1023 ScissorEnable: false.into(),
1024 MultisampleEnable: false.into(),
1025 AntialiasedLineEnable: false.into(),
1026 };
1027 let rasterizer_state = unsafe {
1028 let mut state = None;
1029 device.CreateRasterizerState(&desc, Some(&mut state))?;
1030 state.unwrap()
1031 };
1032 unsafe { device_context.RSSetState(&rasterizer_state) };
1033 Ok(())
1034}
1035
1036// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1037#[inline]
1038fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1039 // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1040 // device performs the blend in linear space, which is ideal.
1041 let mut desc = D3D11_BLEND_DESC::default();
1042 desc.RenderTarget[0].BlendEnable = true.into();
1043 desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1044 desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1045 desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1046 desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1047 desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1048 desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1049 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1050 unsafe {
1051 let mut state = None;
1052 device.CreateBlendState(&desc, Some(&mut state))?;
1053 Ok(state.unwrap())
1054 }
1055}
1056
1057#[inline]
1058fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1059 unsafe {
1060 let mut shader = None;
1061 device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1062 Ok(shader.unwrap())
1063 }
1064}
1065
1066#[inline]
1067fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1068 unsafe {
1069 let mut shader = None;
1070 device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1071 Ok(shader.unwrap())
1072 }
1073}
1074
1075#[inline]
1076fn create_buffer(
1077 device: &ID3D11Device,
1078 element_size: usize,
1079 buffer_size: usize,
1080) -> Result<ID3D11Buffer> {
1081 let desc = D3D11_BUFFER_DESC {
1082 ByteWidth: (element_size * buffer_size) as u32,
1083 Usage: D3D11_USAGE_DYNAMIC,
1084 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1085 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1086 MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1087 StructureByteStride: element_size as u32,
1088 };
1089 let mut buffer = None;
1090 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1091 Ok(buffer.unwrap())
1092}
1093
1094#[inline]
1095fn create_buffer_view(
1096 device: &ID3D11Device,
1097 buffer: &ID3D11Buffer,
1098) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1099 let mut view = None;
1100 unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1101 Ok([view])
1102}
1103
1104#[inline]
1105fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1106 let desc = D3D11_BUFFER_DESC {
1107 ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1108 Usage: D3D11_USAGE_DYNAMIC,
1109 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1110 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1111 MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1112 StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1113 };
1114 let mut buffer = None;
1115 unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1116 Ok(buffer.unwrap())
1117}
1118
1119#[inline]
1120fn pre_draw(
1121 device_context: &ID3D11DeviceContext,
1122 global_params_buffer: &[Option<ID3D11Buffer>; 1],
1123 view_port: &[D3D11_VIEWPORT; 1],
1124 render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1125 clear_color: [f32; 4],
1126 blend_state: &ID3D11BlendState,
1127) -> Result<()> {
1128 let global_params = global_params_buffer[0].as_ref().unwrap();
1129 update_buffer(
1130 device_context,
1131 global_params,
1132 &[GlobalParams {
1133 viewport_size: [view_port[0].Width, view_port[0].Height],
1134 ..Default::default()
1135 }],
1136 )?;
1137 unsafe {
1138 device_context.RSSetViewports(Some(view_port));
1139 device_context.OMSetRenderTargets(Some(render_target_view), None);
1140 device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1141 device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1142 }
1143 Ok(())
1144}
1145
1146#[inline]
1147fn update_buffer<T>(
1148 device_context: &ID3D11DeviceContext,
1149 buffer: &ID3D11Buffer,
1150 data: &[T],
1151) -> Result<()> {
1152 unsafe {
1153 let mut dest = std::mem::zeroed();
1154 device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1155 std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1156 device_context.Unmap(buffer, 0);
1157 }
1158 Ok(())
1159}
1160
1161#[inline]
1162fn set_pipeline_state(
1163 device_context: &ID3D11DeviceContext,
1164 buffer_view: &[Option<ID3D11ShaderResourceView>],
1165 topology: D3D_PRIMITIVE_TOPOLOGY,
1166 viewport: &[D3D11_VIEWPORT],
1167 vertex_shader: &ID3D11VertexShader,
1168 fragment_shader: &ID3D11PixelShader,
1169 global_params: &[Option<ID3D11Buffer>],
1170) {
1171 unsafe {
1172 device_context.VSSetShaderResources(1, Some(buffer_view));
1173 device_context.PSSetShaderResources(1, Some(buffer_view));
1174 device_context.IASetPrimitiveTopology(topology);
1175 device_context.RSSetViewports(Some(viewport));
1176 device_context.VSSetShader(vertex_shader, None);
1177 device_context.PSSetShader(fragment_shader, None);
1178 device_context.VSSetConstantBuffers(0, Some(global_params));
1179 device_context.PSSetConstantBuffers(0, Some(global_params));
1180 }
1181}
1182
1183const BUFFER_COUNT: usize = 3;
1184
1185mod shader_resources {
1186 use anyhow::Result;
1187 use windows::Win32::Graphics::Direct3D::{
1188 Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1189 ID3DBlob,
1190 };
1191 use windows_core::{HSTRING, PCSTR};
1192
1193 pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1194 unsafe {
1195 let mut entry = entry.to_owned();
1196 let mut target = target.to_owned();
1197 let mut compile_blob = None;
1198 let mut error_blob = None;
1199 let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1200 .join("src/platform/windows/shaders.hlsl")
1201 .canonicalize()
1202 .unwrap();
1203 entry.push_str("\0");
1204 target.push_str("\0");
1205 let entry_point = PCSTR::from_raw(entry.as_ptr());
1206 let target_cstr = PCSTR::from_raw(target.as_ptr());
1207 #[cfg(debug_assertions)]
1208 let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1209 #[cfg(not(debug_assertions))]
1210 let compile_flag = 0;
1211 let ret = D3DCompileFromFile(
1212 &HSTRING::from(shader_path.to_str().unwrap()),
1213 None,
1214 None,
1215 entry_point,
1216 target_cstr,
1217 compile_flag,
1218 0,
1219 &mut compile_blob,
1220 Some(&mut error_blob),
1221 );
1222 if ret.is_err() {
1223 let Some(error_blob) = error_blob else {
1224 return Err(anyhow::anyhow!("{ret:?}"));
1225 };
1226 let string_len = error_blob.GetBufferSize();
1227 let error_string_encode = Vec::from_raw_parts(
1228 error_blob.GetBufferPointer() as *mut u8,
1229 string_len,
1230 string_len,
1231 );
1232 let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1233 log::error!("Shader compile error: {}", error_string);
1234 return Err(anyhow::anyhow!("Compile error: {}", error_string));
1235 }
1236 Ok(compile_blob.unwrap())
1237 }
1238 }
1239}