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