directx_renderer.rs

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