directx_renderer.rs

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