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