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
  24pub(crate) struct DirectXRenderer {
  25    atlas: Arc<DirectXAtlas>,
  26    devices: DirectXDevices,
  27    context: DirectXContext,
  28    globals: DirectXGlobalElements,
  29    pipelines: DirectXRenderPipelines,
  30}
  31
  32#[derive(Clone)]
  33pub(crate) struct DirectXDevices {
  34    dxgi_factory: IDXGIFactory6,
  35    dxgi_device: IDXGIDevice,
  36    device: ID3D11Device,
  37    device_context: ID3D11DeviceContext,
  38}
  39
  40struct DirectXContext {
  41    swap_chain: ManuallyDrop<IDXGISwapChain1>,
  42    render_target: ManuallyDrop<ID3D11Texture2D>,
  43    render_target_view: [Option<ID3D11RenderTargetView>; 1],
  44    msaa_target: ID3D11Texture2D,
  45    msaa_view: ID3D11RenderTargetView,
  46    viewport: [D3D11_VIEWPORT; 1],
  47    // #[cfg(not(feature = "enable-renderdoc"))]
  48    _direct_composition: DirectComposition,
  49}
  50
  51struct DirectXRenderPipelines {
  52    shadow_pipeline: PipelineState<Shadow>,
  53    quad_pipeline: PipelineState<Quad>,
  54    paths_pipeline: PathsPipelineState,
  55    underline_pipeline: PipelineState<Underline>,
  56    mono_sprites: PipelineState<MonochromeSprite>,
  57    poly_sprites: PipelineState<PolychromeSprite>,
  58}
  59
  60struct DirectXGlobalElements {
  61    global_params_buffer: [Option<ID3D11Buffer>; 1],
  62    sampler: [Option<ID3D11SamplerState>; 1],
  63    blend_state: ID3D11BlendState,
  64}
  65
  66#[repr(C)]
  67struct DrawInstancedIndirectArgs {
  68    vertex_count_per_instance: u32,
  69    instance_count: u32,
  70    start_vertex_location: u32,
  71    start_instance_location: u32,
  72}
  73
  74// #[cfg(not(feature = "enable-renderdoc"))]
  75struct DirectComposition {
  76    comp_device: IDCompositionDevice,
  77    comp_target: IDCompositionTarget,
  78    comp_visual: IDCompositionVisual,
  79}
  80
  81impl DirectXDevices {
  82    pub(crate) fn new() -> Result<Self> {
  83        let dxgi_factory = get_dxgi_factory()?;
  84        let adapter = get_adapter(&dxgi_factory)?;
  85        let (device, device_context) = {
  86            let mut device: Option<ID3D11Device> = None;
  87            let mut context: Option<ID3D11DeviceContext> = None;
  88            get_device(&adapter, Some(&mut device), Some(&mut context))?;
  89            (device.unwrap(), context.unwrap())
  90        };
  91        let dxgi_device: IDXGIDevice = device.cast()?;
  92
  93        Ok(Self {
  94            dxgi_factory,
  95            dxgi_device,
  96            device,
  97            device_context,
  98        })
  99    }
 100}
 101
 102impl DirectXRenderer {
 103    pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND) -> Result<Self> {
 104        let atlas = Arc::new(DirectXAtlas::new(
 105            devices.device.clone(),
 106            devices.device_context.clone(),
 107        ));
 108        let context = DirectXContext::new(devices, hwnd)?;
 109        let globals = DirectXGlobalElements::new(&devices.device)?;
 110        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
 111        Ok(DirectXRenderer {
 112            atlas,
 113            devices: devices.clone(),
 114            context,
 115            globals,
 116            pipelines,
 117        })
 118    }
 119
 120    pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
 121        self.atlas.clone()
 122    }
 123
 124    fn pre_draw(&self) -> Result<()> {
 125        update_buffer(
 126            &self.devices.device_context,
 127            self.globals.global_params_buffer[0].as_ref().unwrap(),
 128            &[GlobalParams {
 129                viewport_size: [
 130                    self.context.viewport[0].Width,
 131                    self.context.viewport[0].Height,
 132                ],
 133                ..Default::default()
 134            }],
 135        )?;
 136        unsafe {
 137            self.devices
 138                .device_context
 139                .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]);
 140            self.devices
 141                .device_context
 142                .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None);
 143            self.devices
 144                .device_context
 145                .RSSetViewports(Some(&self.context.viewport));
 146            self.devices.device_context.OMSetBlendState(
 147                &self.globals.blend_state,
 148                None,
 149                0xFFFFFFFF,
 150            );
 151        }
 152        Ok(())
 153    }
 154
 155    pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
 156        self.pre_draw()?;
 157        for batch in scene.batches() {
 158            match batch {
 159                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
 160                PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
 161                PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
 162                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
 163                PrimitiveBatch::MonochromeSprites {
 164                    texture_id,
 165                    sprites,
 166                } => self.draw_monochrome_sprites(texture_id, sprites),
 167                PrimitiveBatch::PolychromeSprites {
 168                    texture_id,
 169                    sprites,
 170                } => self.draw_polychrome_sprites(texture_id, sprites),
 171                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
 172            }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 173                    scene.paths.len(),
 174                    scene.shadows.len(),
 175                    scene.quads.len(),
 176                    scene.underlines.len(),
 177                    scene.monochrome_sprites.len(),
 178                    scene.polychrome_sprites.len(),
 179                    scene.surfaces.len(),))?;
 180        }
 181        unsafe {
 182            self.devices.device_context.ResolveSubresource(
 183                &*self.context.render_target,
 184                0,
 185                &self.context.msaa_target,
 186                0,
 187                RENDER_TARGET_FORMAT,
 188            );
 189            self.devices
 190                .device_context
 191                .OMSetRenderTargets(Some(&self.context.render_target_view), None);
 192            self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
 193        }
 194        Ok(())
 195    }
 196
 197    pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
 198        unsafe {
 199            self.devices.device_context.OMSetRenderTargets(None, None);
 200            ManuallyDrop::drop(&mut self.context.render_target);
 201        }
 202        drop(self.context.render_target_view[0].take().unwrap());
 203        unsafe {
 204            self.context
 205                .swap_chain
 206                .ResizeBuffers(
 207                    BUFFER_COUNT as u32,
 208                    new_size.width.0 as u32,
 209                    new_size.height.0 as u32,
 210                    RENDER_TARGET_FORMAT,
 211                    DXGI_SWAP_CHAIN_FLAG(0),
 212                )
 213                .unwrap();
 214        }
 215        let (render_target, render_target_view) =
 216            create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
 217                .unwrap();
 218        self.context.render_target = render_target;
 219        self.context.render_target_view = render_target_view;
 220        unsafe {
 221            self.devices
 222                .device_context
 223                .OMSetRenderTargets(Some(&self.context.render_target_view), None);
 224        }
 225
 226        let (msaa_target, msaa_view) = create_msaa_target_and_its_view(
 227            &self.devices.device,
 228            new_size.width.0 as u32,
 229            new_size.height.0 as u32,
 230        )?;
 231        self.context.msaa_target = msaa_target;
 232        self.context.msaa_view = msaa_view;
 233
 234        self.context.viewport = set_viewport(
 235            &self.devices.device_context,
 236            new_size.width.0 as f32,
 237            new_size.height.0 as f32,
 238        );
 239        Ok(())
 240    }
 241
 242    fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
 243        if shadows.is_empty() {
 244            return Ok(());
 245        }
 246        self.pipelines.shadow_pipeline.update_buffer(
 247            &self.devices.device,
 248            &self.devices.device_context,
 249            shadows,
 250        )?;
 251        self.pipelines.shadow_pipeline.draw(
 252            &self.devices.device_context,
 253            &self.context.viewport,
 254            &self.globals.global_params_buffer,
 255            shadows.len() as u32,
 256        )
 257    }
 258
 259    fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
 260        if quads.is_empty() {
 261            return Ok(());
 262        }
 263        self.pipelines.quad_pipeline.update_buffer(
 264            &self.devices.device,
 265            &self.devices.device_context,
 266            quads,
 267        )?;
 268        self.pipelines.quad_pipeline.draw(
 269            &self.devices.device_context,
 270            &self.context.viewport,
 271            &self.globals.global_params_buffer,
 272            quads.len() as u32,
 273        )
 274    }
 275
 276    fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
 277        if paths.is_empty() {
 278            return Ok(());
 279        }
 280        let mut vertices = Vec::new();
 281        let mut sprites = Vec::with_capacity(paths.len());
 282        let mut draw_indirect_commands = Vec::with_capacity(paths.len());
 283        let mut start_vertex_location = 0;
 284        for (i, path) in paths.iter().enumerate() {
 285            draw_indirect_commands.push(DrawInstancedIndirectArgs {
 286                vertex_count_per_instance: path.vertices.len() as u32,
 287                instance_count: 1,
 288                start_vertex_location,
 289                start_instance_location: i as u32,
 290            });
 291            start_vertex_location += path.vertices.len() as u32;
 292
 293            vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex {
 294                xy_position: v.xy_position,
 295                content_mask: path.content_mask.bounds,
 296                sprite_index: i as u32,
 297            }));
 298
 299            sprites.push(PathSprite {
 300                bounds: path.bounds,
 301                color: path.color,
 302            });
 303        }
 304
 305        self.pipelines.paths_pipeline.update_buffer(
 306            &self.devices.device,
 307            &self.devices.device_context,
 308            &sprites,
 309            &vertices,
 310            &draw_indirect_commands,
 311        )?;
 312        self.pipelines.paths_pipeline.draw(
 313            &self.devices.device_context,
 314            paths.len(),
 315            &self.context.viewport,
 316            &self.globals.global_params_buffer,
 317        )
 318    }
 319
 320    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
 321        if underlines.is_empty() {
 322            return Ok(());
 323        }
 324        self.pipelines.underline_pipeline.update_buffer(
 325            &self.devices.device,
 326            &self.devices.device_context,
 327            underlines,
 328        )?;
 329        self.pipelines.underline_pipeline.draw(
 330            &self.devices.device_context,
 331            &self.context.viewport,
 332            &self.globals.global_params_buffer,
 333            underlines.len() as u32,
 334        )
 335    }
 336
 337    fn draw_monochrome_sprites(
 338        &mut self,
 339        texture_id: AtlasTextureId,
 340        sprites: &[MonochromeSprite],
 341    ) -> Result<()> {
 342        if sprites.is_empty() {
 343            return Ok(());
 344        }
 345        self.pipelines.mono_sprites.update_buffer(
 346            &self.devices.device,
 347            &self.devices.device_context,
 348            sprites,
 349        )?;
 350        let texture_view = self.atlas.get_texture_view(texture_id);
 351        self.pipelines.mono_sprites.draw_with_texture(
 352            &self.devices.device_context,
 353            &texture_view,
 354            &self.context.viewport,
 355            &self.globals.global_params_buffer,
 356            &self.globals.sampler,
 357            sprites.len() as u32,
 358        )
 359    }
 360
 361    fn draw_polychrome_sprites(
 362        &mut self,
 363        texture_id: AtlasTextureId,
 364        sprites: &[PolychromeSprite],
 365    ) -> Result<()> {
 366        if sprites.is_empty() {
 367            return Ok(());
 368        }
 369        self.pipelines.poly_sprites.update_buffer(
 370            &self.devices.device,
 371            &self.devices.device_context,
 372            sprites,
 373        )?;
 374        let texture_view = self.atlas.get_texture_view(texture_id);
 375        self.pipelines.poly_sprites.draw_with_texture(
 376            &self.devices.device_context,
 377            &texture_view,
 378            &self.context.viewport,
 379            &self.globals.global_params_buffer,
 380            &self.globals.sampler,
 381            sprites.len() as u32,
 382        )
 383    }
 384
 385    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
 386        if surfaces.is_empty() {
 387            return Ok(());
 388        }
 389        Ok(())
 390    }
 391}
 392
 393impl DirectXContext {
 394    pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result<Self> {
 395        let (width, height) = unsafe {
 396            let mut rect = std::mem::zeroed();
 397            GetWindowRect(hwnd, &mut rect)?;
 398            (rect.right - rect.left, rect.bottom - rect.top)
 399        };
 400        assert!(
 401            width > 0 && height > 0,
 402            "Window size must be greater than zero"
 403        );
 404        // #[cfg(not(feature = "enable-renderdoc"))]
 405        let swap_chain = create_swap_chain(
 406            &devices.dxgi_factory,
 407            &devices.device,
 408            width as u32,
 409            height as u32,
 410        )?;
 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 as u32, height as u32)?;
 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 DirectXContext {
 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    _transparent: bool,
1012) -> Result<ManuallyDrop<IDXGISwapChain1>> {
1013    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1014
1015    let desc = DXGI_SWAP_CHAIN_DESC1 {
1016        Width: 1,
1017        Height: 1,
1018        Format: RENDER_TARGET_FORMAT,
1019        Stereo: false.into(),
1020        SampleDesc: DXGI_SAMPLE_DESC {
1021            Count: 1,
1022            Quality: 0,
1023        },
1024        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1025        BufferCount: BUFFER_COUNT as u32,
1026        Scaling: DXGI_SCALING_STRETCH,
1027        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1028        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1029        Flags: 0,
1030    };
1031    let swap_chain =
1032        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1033    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1034    Ok(ManuallyDrop::new(swap_chain))
1035}
1036
1037#[inline]
1038fn create_render_target_and_its_view(
1039    swap_chain: &IDXGISwapChain1,
1040    device: &ID3D11Device,
1041) -> Result<(
1042    ManuallyDrop<ID3D11Texture2D>,
1043    [Option<ID3D11RenderTargetView>; 1],
1044)> {
1045    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1046    let mut render_target_view = None;
1047    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1048    Ok((
1049        ManuallyDrop::new(render_target),
1050        [Some(render_target_view.unwrap())],
1051    ))
1052}
1053
1054#[inline]
1055fn create_msaa_target_and_its_view(
1056    device: &ID3D11Device,
1057    width: u32,
1058    height: u32,
1059) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
1060    let msaa_target = unsafe {
1061        let mut output = None;
1062        let desc = D3D11_TEXTURE2D_DESC {
1063            Width: width,
1064            Height: height,
1065            MipLevels: 1,
1066            ArraySize: 1,
1067            Format: RENDER_TARGET_FORMAT,
1068            SampleDesc: DXGI_SAMPLE_DESC {
1069                Count: 4,
1070                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1071            },
1072            Usage: D3D11_USAGE_DEFAULT,
1073            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1074            CPUAccessFlags: 0,
1075            MiscFlags: 0,
1076        };
1077        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1078        output.unwrap()
1079    };
1080    let msaa_view = unsafe {
1081        let mut output = None;
1082        device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1083        output.unwrap()
1084    };
1085    Ok((msaa_target, msaa_view))
1086}
1087
1088#[inline]
1089fn set_viewport(
1090    device_context: &ID3D11DeviceContext,
1091    width: f32,
1092    height: f32,
1093) -> [D3D11_VIEWPORT; 1] {
1094    let viewport = [D3D11_VIEWPORT {
1095        TopLeftX: 0.0,
1096        TopLeftY: 0.0,
1097        Width: width,
1098        Height: height,
1099        MinDepth: 0.0,
1100        MaxDepth: 1.0,
1101    }];
1102    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1103    viewport
1104}
1105
1106#[inline]
1107fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1108    let desc = D3D11_RASTERIZER_DESC {
1109        FillMode: D3D11_FILL_SOLID,
1110        CullMode: D3D11_CULL_NONE,
1111        FrontCounterClockwise: false.into(),
1112        DepthBias: 0,
1113        DepthBiasClamp: 0.0,
1114        SlopeScaledDepthBias: 0.0,
1115        DepthClipEnable: true.into(),
1116        ScissorEnable: false.into(),
1117        // MultisampleEnable: false.into(),
1118        MultisampleEnable: true.into(),
1119        AntialiasedLineEnable: false.into(),
1120    };
1121    let rasterizer_state = unsafe {
1122        let mut state = None;
1123        device.CreateRasterizerState(&desc, Some(&mut state))?;
1124        state.unwrap()
1125    };
1126    unsafe { device_context.RSSetState(&rasterizer_state) };
1127    Ok(())
1128}
1129
1130// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1131#[inline]
1132fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1133    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1134    // device performs the blend in linear space, which is ideal.
1135    let mut desc = D3D11_BLEND_DESC::default();
1136    desc.RenderTarget[0].BlendEnable = true.into();
1137    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1138    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1139    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1140    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1141    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1142    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1143    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1144    unsafe {
1145        let mut state = None;
1146        device.CreateBlendState(&desc, Some(&mut state))?;
1147        Ok(state.unwrap())
1148    }
1149}
1150
1151#[inline]
1152fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1153    unsafe {
1154        let mut shader = None;
1155        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1156        Ok(shader.unwrap())
1157    }
1158}
1159
1160#[inline]
1161fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1162    unsafe {
1163        let mut shader = None;
1164        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1165        Ok(shader.unwrap())
1166    }
1167}
1168
1169#[inline]
1170fn create_buffer(
1171    device: &ID3D11Device,
1172    element_size: usize,
1173    buffer_size: usize,
1174) -> Result<ID3D11Buffer> {
1175    let desc = D3D11_BUFFER_DESC {
1176        ByteWidth: (element_size * buffer_size) as u32,
1177        Usage: D3D11_USAGE_DYNAMIC,
1178        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1179        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1180        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1181        StructureByteStride: element_size as u32,
1182    };
1183    let mut buffer = None;
1184    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1185    Ok(buffer.unwrap())
1186}
1187
1188#[inline]
1189fn create_buffer_view(
1190    device: &ID3D11Device,
1191    buffer: &ID3D11Buffer,
1192) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1193    let mut view = None;
1194    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1195    Ok([view])
1196}
1197
1198#[inline]
1199fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1200    let desc = D3D11_BUFFER_DESC {
1201        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1202        Usage: D3D11_USAGE_DYNAMIC,
1203        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1204        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1205        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1206        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1207    };
1208    let mut buffer = None;
1209    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1210    Ok(buffer.unwrap())
1211}
1212
1213#[inline]
1214fn update_buffer<T>(
1215    device_context: &ID3D11DeviceContext,
1216    buffer: &ID3D11Buffer,
1217    data: &[T],
1218) -> Result<()> {
1219    unsafe {
1220        let mut dest = std::mem::zeroed();
1221        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1222        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1223        device_context.Unmap(buffer, 0);
1224    }
1225    Ok(())
1226}
1227
1228#[inline]
1229fn set_pipeline_state(
1230    device_context: &ID3D11DeviceContext,
1231    buffer_view: &[Option<ID3D11ShaderResourceView>],
1232    topology: D3D_PRIMITIVE_TOPOLOGY,
1233    viewport: &[D3D11_VIEWPORT],
1234    vertex_shader: &ID3D11VertexShader,
1235    fragment_shader: &ID3D11PixelShader,
1236    global_params: &[Option<ID3D11Buffer>],
1237) {
1238    unsafe {
1239        device_context.VSSetShaderResources(1, Some(buffer_view));
1240        device_context.PSSetShaderResources(1, Some(buffer_view));
1241        device_context.IASetPrimitiveTopology(topology);
1242        device_context.RSSetViewports(Some(viewport));
1243        device_context.VSSetShader(vertex_shader, None);
1244        device_context.PSSetShader(fragment_shader, None);
1245        device_context.VSSetConstantBuffers(0, Some(global_params));
1246        device_context.PSSetConstantBuffers(0, Some(global_params));
1247    }
1248}
1249
1250const BUFFER_COUNT: usize = 3;
1251
1252mod shader_resources {
1253    use anyhow::Result;
1254    use windows::Win32::Graphics::Direct3D::{
1255        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1256        ID3DBlob,
1257    };
1258    use windows_core::{HSTRING, PCSTR};
1259
1260    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1261        unsafe {
1262            let mut entry = entry.to_owned();
1263            let mut target = target.to_owned();
1264            let mut compile_blob = None;
1265            let mut error_blob = None;
1266            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1267                .join("src/platform/windows/shaders.hlsl")
1268                .canonicalize()
1269                .unwrap();
1270            entry.push_str("\0");
1271            target.push_str("\0");
1272            let entry_point = PCSTR::from_raw(entry.as_ptr());
1273            let target_cstr = PCSTR::from_raw(target.as_ptr());
1274            #[cfg(debug_assertions)]
1275            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1276            #[cfg(not(debug_assertions))]
1277            let compile_flag = 0;
1278            let ret = D3DCompileFromFile(
1279                &HSTRING::from(shader_path.to_str().unwrap()),
1280                None,
1281                None,
1282                entry_point,
1283                target_cstr,
1284                compile_flag,
1285                0,
1286                &mut compile_blob,
1287                Some(&mut error_blob),
1288            );
1289            if ret.is_err() {
1290                let Some(error_blob) = error_blob else {
1291                    return Err(anyhow::anyhow!("{ret:?}"));
1292                };
1293                let string_len = error_blob.GetBufferSize();
1294                let error_string_encode = Vec::from_raw_parts(
1295                    error_blob.GetBufferPointer() as *mut u8,
1296                    string_len,
1297                    string_len,
1298                );
1299                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1300                log::error!("Shader compile error: {}", error_string);
1301                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1302            }
1303            Ok(compile_blob.unwrap())
1304        }
1305    }
1306}