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