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