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