directx_renderer.rs

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