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