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