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