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