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