directx_renderer.rs

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