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