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