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