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        // unsafe {
 406        //     devices
 407        //         .device_context
 408        //         .OMSetRenderTargets(Some(&render_target_view), None);
 409        // }
 410        set_rasterizer_state(&devices.device, &devices.device_context)?;
 411
 412        Ok(Self {
 413            swap_chain,
 414            render_target,
 415            render_target_view,
 416            msaa_target,
 417            msaa_view,
 418            width,
 419            height,
 420            viewport,
 421        })
 422    }
 423
 424    #[inline]
 425    fn recreate_resources(
 426        &mut self,
 427        devices: &DirectXDevices,
 428        width: u32,
 429        height: u32,
 430    ) -> Result<()> {
 431        let (render_target, render_target_view, msaa_target, msaa_view, viewport) =
 432            create_resources(devices, &self.swap_chain, width, height)?;
 433        self.render_target = render_target;
 434        self.render_target_view = render_target_view;
 435        self.msaa_target = msaa_target;
 436        self.msaa_view = msaa_view;
 437        self.viewport = viewport;
 438        self.width = width;
 439        self.height = height;
 440        Ok(())
 441    }
 442}
 443
 444impl DirectXRenderPipelines {
 445    pub fn new(device: &ID3D11Device) -> Result<Self> {
 446        let shadow_pipeline = PipelineState::new(
 447            device,
 448            "shadow_pipeline",
 449            "shadow_vertex",
 450            "shadow_fragment",
 451            4,
 452        )?;
 453        let quad_pipeline =
 454            PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?;
 455        let paths_pipeline = PathsPipelineState::new(device)?;
 456        let underline_pipeline = PipelineState::new(
 457            device,
 458            "underline_pipeline",
 459            "underline_vertex",
 460            "underline_fragment",
 461            4,
 462        )?;
 463        let mono_sprites = PipelineState::new(
 464            device,
 465            "monochrome_sprite_pipeline",
 466            "monochrome_sprite_vertex",
 467            "monochrome_sprite_fragment",
 468            512,
 469        )?;
 470        let poly_sprites = PipelineState::new(
 471            device,
 472            "polychrome_sprite_pipeline",
 473            "polychrome_sprite_vertex",
 474            "polychrome_sprite_fragment",
 475            16,
 476        )?;
 477
 478        Ok(Self {
 479            shadow_pipeline,
 480            quad_pipeline,
 481            paths_pipeline,
 482            underline_pipeline,
 483            mono_sprites,
 484            poly_sprites,
 485        })
 486    }
 487}
 488
 489// #[cfg(not(feature = "enable-renderdoc"))]
 490impl DirectComposition {
 491    pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
 492        let comp_device = get_comp_device(&dxgi_device)?;
 493        let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
 494        let comp_visual = unsafe { comp_device.CreateVisual() }?;
 495
 496        Ok(Self {
 497            comp_device,
 498            comp_target,
 499            comp_visual,
 500        })
 501    }
 502
 503    pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
 504        unsafe {
 505            self.comp_visual.SetContent(swap_chain)?;
 506            self.comp_target.SetRoot(&self.comp_visual)?;
 507            self.comp_device.Commit()?;
 508        }
 509        Ok(())
 510    }
 511}
 512
 513impl DirectXGlobalElements {
 514    pub fn new(device: &ID3D11Device) -> Result<Self> {
 515        let global_params_buffer = unsafe {
 516            let desc = D3D11_BUFFER_DESC {
 517                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
 518                Usage: D3D11_USAGE_DYNAMIC,
 519                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
 520                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 521                ..Default::default()
 522            };
 523            let mut buffer = None;
 524            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
 525            [buffer]
 526        };
 527
 528        let sampler = unsafe {
 529            let desc = D3D11_SAMPLER_DESC {
 530                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
 531                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
 532                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
 533                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
 534                MipLODBias: 0.0,
 535                MaxAnisotropy: 1,
 536                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
 537                BorderColor: [0.0; 4],
 538                MinLOD: 0.0,
 539                MaxLOD: D3D11_FLOAT32_MAX,
 540            };
 541            let mut output = None;
 542            device.CreateSamplerState(&desc, Some(&mut output))?;
 543            [output]
 544        };
 545
 546        let blend_state = create_blend_state(device)?;
 547
 548        Ok(Self {
 549            global_params_buffer,
 550            sampler,
 551            blend_state,
 552        })
 553    }
 554}
 555
 556#[derive(Debug, Default)]
 557#[repr(C)]
 558struct GlobalParams {
 559    viewport_size: [f32; 2],
 560    _pad: u64,
 561}
 562
 563struct PipelineState<T> {
 564    label: &'static str,
 565    vertex: ID3D11VertexShader,
 566    fragment: ID3D11PixelShader,
 567    buffer: ID3D11Buffer,
 568    buffer_size: usize,
 569    view: [Option<ID3D11ShaderResourceView>; 1],
 570    _marker: std::marker::PhantomData<T>,
 571}
 572
 573struct PathsPipelineState {
 574    vertex: ID3D11VertexShader,
 575    fragment: ID3D11PixelShader,
 576    buffer: ID3D11Buffer,
 577    buffer_size: usize,
 578    vertex_buffer: Option<ID3D11Buffer>,
 579    vertex_buffer_size: usize,
 580    indirect_draw_buffer: ID3D11Buffer,
 581    indirect_buffer_size: usize,
 582    input_layout: ID3D11InputLayout,
 583    view: [Option<ID3D11ShaderResourceView>; 1],
 584}
 585
 586impl<T> PipelineState<T> {
 587    fn new(
 588        device: &ID3D11Device,
 589        label: &'static str,
 590        vertex_entry: &str,
 591        fragment_entry: &str,
 592        buffer_size: usize,
 593    ) -> Result<Self> {
 594        let vertex = {
 595            let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 596            let bytes = unsafe {
 597                std::slice::from_raw_parts(
 598                    shader_blob.GetBufferPointer() as *mut u8,
 599                    shader_blob.GetBufferSize(),
 600                )
 601            };
 602            create_vertex_shader(device, bytes)?
 603        };
 604        let fragment = {
 605            let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 606            let bytes = unsafe {
 607                std::slice::from_raw_parts(
 608                    shader_blob.GetBufferPointer() as *mut u8,
 609                    shader_blob.GetBufferSize(),
 610                )
 611            };
 612            create_fragment_shader(device, bytes)?
 613        };
 614        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
 615        let view = create_buffer_view(device, &buffer)?;
 616
 617        Ok(PipelineState {
 618            label,
 619            vertex,
 620            fragment,
 621            buffer,
 622            buffer_size,
 623            view,
 624            _marker: std::marker::PhantomData,
 625        })
 626    }
 627
 628    fn update_buffer(
 629        &mut self,
 630        device: &ID3D11Device,
 631        device_context: &ID3D11DeviceContext,
 632        data: &[T],
 633    ) -> Result<()> {
 634        if self.buffer_size < data.len() {
 635            let new_buffer_size = data.len().next_power_of_two();
 636            log::info!(
 637                "Updating {} buffer size from {} to {}",
 638                self.label,
 639                self.buffer_size,
 640                new_buffer_size
 641            );
 642            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
 643            let view = create_buffer_view(device, &buffer)?;
 644            self.buffer = buffer;
 645            self.view = view;
 646            self.buffer_size = new_buffer_size;
 647        }
 648        update_buffer(device_context, &self.buffer, data)
 649    }
 650
 651    fn draw(
 652        &self,
 653        device_context: &ID3D11DeviceContext,
 654        viewport: &[D3D11_VIEWPORT],
 655        global_params: &[Option<ID3D11Buffer>],
 656        instance_count: u32,
 657    ) -> Result<()> {
 658        set_pipeline_state(
 659            device_context,
 660            &self.view,
 661            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 662            viewport,
 663            &self.vertex,
 664            &self.fragment,
 665            global_params,
 666        );
 667        unsafe {
 668            device_context.DrawInstanced(4, instance_count, 0, 0);
 669        }
 670        Ok(())
 671    }
 672
 673    fn draw_with_texture(
 674        &self,
 675        device_context: &ID3D11DeviceContext,
 676        texture: &[Option<ID3D11ShaderResourceView>],
 677        viewport: &[D3D11_VIEWPORT],
 678        global_params: &[Option<ID3D11Buffer>],
 679        sampler: &[Option<ID3D11SamplerState>],
 680        instance_count: u32,
 681    ) -> Result<()> {
 682        set_pipeline_state(
 683            device_context,
 684            &self.view,
 685            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 686            viewport,
 687            &self.vertex,
 688            &self.fragment,
 689            global_params,
 690        );
 691        unsafe {
 692            device_context.PSSetSamplers(0, Some(sampler));
 693            device_context.VSSetShaderResources(0, Some(texture));
 694            device_context.PSSetShaderResources(0, Some(texture));
 695
 696            device_context.DrawInstanced(4, instance_count, 0, 0);
 697        }
 698        Ok(())
 699    }
 700}
 701
 702impl PathsPipelineState {
 703    fn new(device: &ID3D11Device) -> Result<Self> {
 704        let (vertex, vertex_shader) = {
 705            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 706            let bytes = unsafe {
 707                std::slice::from_raw_parts(
 708                    shader_blob.GetBufferPointer() as *mut u8,
 709                    shader_blob.GetBufferSize(),
 710                )
 711            };
 712            (create_vertex_shader(device, bytes)?, shader_blob)
 713        };
 714        let fragment = {
 715            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 716            let bytes = unsafe {
 717                std::slice::from_raw_parts(
 718                    shader_blob.GetBufferPointer() as *mut u8,
 719                    shader_blob.GetBufferSize(),
 720                )
 721            };
 722            create_fragment_shader(device, bytes)?
 723        };
 724        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 725        let view = create_buffer_view(device, &buffer)?;
 726        let vertex_buffer = Some(create_buffer(
 727            device,
 728            std::mem::size_of::<DirectXPathVertex>(),
 729            32,
 730        )?);
 731        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 732        // Create input layout
 733        let input_layout = unsafe {
 734            let shader_bytes = std::slice::from_raw_parts(
 735                vertex_shader.GetBufferPointer() as *const u8,
 736                vertex_shader.GetBufferSize(),
 737            );
 738            let mut layout = None;
 739            device.CreateInputLayout(
 740                &[
 741                    D3D11_INPUT_ELEMENT_DESC {
 742                        SemanticName: windows::core::s!("POSITION"),
 743                        SemanticIndex: 0,
 744                        Format: DXGI_FORMAT_R32G32_FLOAT,
 745                        InputSlot: 0,
 746                        AlignedByteOffset: 0,
 747                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 748                        InstanceDataStepRate: 0,
 749                    },
 750                    D3D11_INPUT_ELEMENT_DESC {
 751                        SemanticName: windows::core::s!("TEXCOORD"),
 752                        SemanticIndex: 0,
 753                        Format: DXGI_FORMAT_R32G32_FLOAT,
 754                        InputSlot: 0,
 755                        AlignedByteOffset: 8,
 756                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 757                        InstanceDataStepRate: 0,
 758                    },
 759                    D3D11_INPUT_ELEMENT_DESC {
 760                        SemanticName: windows::core::s!("TEXCOORD"),
 761                        SemanticIndex: 1,
 762                        Format: DXGI_FORMAT_R32G32_FLOAT,
 763                        InputSlot: 0,
 764                        AlignedByteOffset: 16,
 765                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 766                        InstanceDataStepRate: 0,
 767                    },
 768                    D3D11_INPUT_ELEMENT_DESC {
 769                        SemanticName: windows::core::s!("GLOBALIDX"),
 770                        SemanticIndex: 0,
 771                        Format: DXGI_FORMAT_R32_UINT,
 772                        InputSlot: 0,
 773                        AlignedByteOffset: 24,
 774                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 775                        InstanceDataStepRate: 0,
 776                    },
 777                ],
 778                shader_bytes,
 779                Some(&mut layout),
 780            )?;
 781            layout.unwrap()
 782        };
 783
 784        Ok(Self {
 785            vertex,
 786            fragment,
 787            buffer,
 788            buffer_size: 32,
 789            vertex_buffer,
 790            vertex_buffer_size: 32,
 791            indirect_draw_buffer,
 792            indirect_buffer_size: 32,
 793            input_layout,
 794            view,
 795        })
 796    }
 797
 798    fn update_buffer(
 799        &mut self,
 800        device: &ID3D11Device,
 801        device_context: &ID3D11DeviceContext,
 802        buffer_data: &[PathSprite],
 803        vertices_data: &[DirectXPathVertex],
 804        draw_commands: &[DrawInstancedIndirectArgs],
 805    ) -> Result<()> {
 806        if self.buffer_size < buffer_data.len() {
 807            let new_buffer_size = buffer_data.len().next_power_of_two();
 808            log::info!(
 809                "Updating Paths Pipeline buffer size from {} to {}",
 810                self.buffer_size,
 811                new_buffer_size
 812            );
 813            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 814            let view = create_buffer_view(device, &buffer)?;
 815            self.buffer = buffer;
 816            self.view = view;
 817            self.buffer_size = new_buffer_size;
 818        }
 819        update_buffer(device_context, &self.buffer, buffer_data)?;
 820        if self.vertex_buffer_size < vertices_data.len() {
 821            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 822            log::info!(
 823                "Updating Paths Pipeline vertex buffer size from {} to {}",
 824                self.vertex_buffer_size,
 825                new_vertex_buffer_size
 826            );
 827            let vertex_buffer = create_buffer(
 828                device,
 829                std::mem::size_of::<DirectXPathVertex>(),
 830                new_vertex_buffer_size,
 831            )?;
 832            self.vertex_buffer = Some(vertex_buffer);
 833            self.vertex_buffer_size = new_vertex_buffer_size;
 834        }
 835        update_buffer(
 836            device_context,
 837            self.vertex_buffer.as_ref().unwrap(),
 838            vertices_data,
 839        )?;
 840        if self.indirect_buffer_size < draw_commands.len() {
 841            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 842            log::info!(
 843                "Updating Paths Pipeline indirect buffer size from {} to {}",
 844                self.indirect_buffer_size,
 845                new_indirect_buffer_size
 846            );
 847            let indirect_draw_buffer =
 848                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 849            self.indirect_draw_buffer = indirect_draw_buffer;
 850            self.indirect_buffer_size = new_indirect_buffer_size;
 851        }
 852        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 853        Ok(())
 854    }
 855
 856    fn draw(
 857        &self,
 858        device_context: &ID3D11DeviceContext,
 859        count: usize,
 860        viewport: &[D3D11_VIEWPORT],
 861        global_params: &[Option<ID3D11Buffer>],
 862    ) -> Result<()> {
 863        set_pipeline_state(
 864            device_context,
 865            &self.view,
 866            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 867            viewport,
 868            &self.vertex,
 869            &self.fragment,
 870            global_params,
 871        );
 872        unsafe {
 873            const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
 874            device_context.IASetVertexBuffers(
 875                0,
 876                1,
 877                Some(&self.vertex_buffer),
 878                Some(&STRIDE),
 879                Some(&0),
 880            );
 881            device_context.IASetInputLayout(&self.input_layout);
 882        }
 883        for i in 0..count {
 884            unsafe {
 885                device_context.DrawInstancedIndirect(
 886                    &self.indirect_draw_buffer,
 887                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 888                );
 889            }
 890        }
 891        Ok(())
 892    }
 893}
 894
 895#[repr(C)]
 896struct DirectXPathVertex {
 897    xy_position: Point<ScaledPixels>,
 898    content_mask: Bounds<ScaledPixels>,
 899    sprite_index: u32,
 900}
 901
 902#[derive(Clone, Debug, Eq, PartialEq)]
 903#[repr(C)]
 904struct PathSprite {
 905    bounds: Bounds<ScaledPixels>,
 906    color: Background,
 907}
 908
 909impl Drop for DirectXResources {
 910    fn drop(&mut self) {
 911        unsafe {
 912            ManuallyDrop::drop(&mut self.render_target);
 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            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
 965            // 8x MSAA is required for Direct3D Feature Level 11.0 or better
 966            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
 967            D3D11_SDK_VERSION,
 968            device,
 969            None,
 970            context,
 971        )?
 972    })
 973}
 974
 975// #[cfg(not(feature = "enable-renderdoc"))]
 976fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
 977    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
 978}
 979
 980fn create_swap_chain(
 981    dxgi_factory: &IDXGIFactory6,
 982    device: &ID3D11Device,
 983    width: u32,
 984    height: u32,
 985) -> Result<IDXGISwapChain1> {
 986    let desc = DXGI_SWAP_CHAIN_DESC1 {
 987        Width: width,
 988        Height: height,
 989        Format: RENDER_TARGET_FORMAT,
 990        Stereo: false.into(),
 991        SampleDesc: DXGI_SAMPLE_DESC {
 992            Count: 1,
 993            Quality: 0,
 994        },
 995        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 996        BufferCount: BUFFER_COUNT as u32,
 997        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
 998        Scaling: DXGI_SCALING_STRETCH,
 999        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1000        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1001        Flags: 0,
1002    };
1003    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1004}
1005
1006// #[cfg(feature = "enable-renderdoc")]
1007fn create_swap_chain_default(
1008    dxgi_factory: &IDXGIFactory6,
1009    device: &ID3D11Device,
1010    hwnd: HWND,
1011    width: u32,
1012    height: u32,
1013) -> Result<IDXGISwapChain1> {
1014    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1015
1016    let desc = DXGI_SWAP_CHAIN_DESC1 {
1017        Width: width,
1018        Height: height,
1019        Format: RENDER_TARGET_FORMAT,
1020        Stereo: false.into(),
1021        SampleDesc: DXGI_SAMPLE_DESC {
1022            Count: 1,
1023            Quality: 0,
1024        },
1025        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1026        BufferCount: BUFFER_COUNT as u32,
1027        Scaling: DXGI_SCALING_STRETCH,
1028        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1029        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1030        Flags: 0,
1031    };
1032    let swap_chain =
1033        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1034    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1035    Ok(swap_chain)
1036}
1037
1038#[inline]
1039fn create_resources(
1040    devices: &DirectXDevices,
1041    swap_chain: &IDXGISwapChain1,
1042    width: u32,
1043    height: u32,
1044) -> Result<(
1045    ManuallyDrop<ID3D11Texture2D>,
1046    [Option<ID3D11RenderTargetView>; 1],
1047    ID3D11Texture2D,
1048    [Option<ID3D11RenderTargetView>; 1],
1049    [D3D11_VIEWPORT; 1],
1050)> {
1051    let (render_target, render_target_view) =
1052        create_render_target_and_its_view(&swap_chain, &devices.device)?;
1053    let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1054    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1055    Ok((
1056        render_target,
1057        render_target_view,
1058        msaa_target,
1059        msaa_view,
1060        viewport,
1061    ))
1062}
1063
1064#[inline]
1065fn create_render_target_and_its_view(
1066    swap_chain: &IDXGISwapChain1,
1067    device: &ID3D11Device,
1068) -> Result<(
1069    ManuallyDrop<ID3D11Texture2D>,
1070    [Option<ID3D11RenderTargetView>; 1],
1071)> {
1072    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1073    let mut render_target_view = None;
1074    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1075    Ok((
1076        ManuallyDrop::new(render_target),
1077        [Some(render_target_view.unwrap())],
1078    ))
1079}
1080
1081#[inline]
1082fn create_msaa_target_and_its_view(
1083    device: &ID3D11Device,
1084    width: u32,
1085    height: u32,
1086) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1087    let msaa_target = unsafe {
1088        let mut output = None;
1089        let desc = D3D11_TEXTURE2D_DESC {
1090            Width: width,
1091            Height: height,
1092            MipLevels: 1,
1093            ArraySize: 1,
1094            Format: RENDER_TARGET_FORMAT,
1095            SampleDesc: DXGI_SAMPLE_DESC {
1096                Count: MULTISAMPLE_COUNT,
1097                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1098            },
1099            Usage: D3D11_USAGE_DEFAULT,
1100            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1101            CPUAccessFlags: 0,
1102            MiscFlags: 0,
1103        };
1104        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1105        output.unwrap()
1106    };
1107    let msaa_view = unsafe {
1108        let mut output = None;
1109        device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1110        output.unwrap()
1111    };
1112    Ok((msaa_target, [Some(msaa_view)]))
1113}
1114
1115#[inline]
1116fn set_viewport(
1117    device_context: &ID3D11DeviceContext,
1118    width: f32,
1119    height: f32,
1120) -> [D3D11_VIEWPORT; 1] {
1121    let viewport = [D3D11_VIEWPORT {
1122        TopLeftX: 0.0,
1123        TopLeftY: 0.0,
1124        Width: width,
1125        Height: height,
1126        MinDepth: 0.0,
1127        MaxDepth: 1.0,
1128    }];
1129    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1130    viewport
1131}
1132
1133#[inline]
1134fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1135    let desc = D3D11_RASTERIZER_DESC {
1136        FillMode: D3D11_FILL_SOLID,
1137        CullMode: D3D11_CULL_NONE,
1138        FrontCounterClockwise: false.into(),
1139        DepthBias: 0,
1140        DepthBiasClamp: 0.0,
1141        SlopeScaledDepthBias: 0.0,
1142        DepthClipEnable: true.into(),
1143        ScissorEnable: false.into(),
1144        // MultisampleEnable: false.into(),
1145        MultisampleEnable: true.into(),
1146        AntialiasedLineEnable: false.into(),
1147    };
1148    let rasterizer_state = unsafe {
1149        let mut state = None;
1150        device.CreateRasterizerState(&desc, Some(&mut state))?;
1151        state.unwrap()
1152    };
1153    unsafe { device_context.RSSetState(&rasterizer_state) };
1154    Ok(())
1155}
1156
1157// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1158#[inline]
1159fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1160    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1161    // device performs the blend in linear space, which is ideal.
1162    let mut desc = D3D11_BLEND_DESC::default();
1163    desc.RenderTarget[0].BlendEnable = true.into();
1164    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1165    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1166    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1167    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1168    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1169    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1170    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1171    unsafe {
1172        let mut state = None;
1173        device.CreateBlendState(&desc, Some(&mut state))?;
1174        Ok(state.unwrap())
1175    }
1176}
1177
1178#[inline]
1179fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1180    unsafe {
1181        let mut shader = None;
1182        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1183        Ok(shader.unwrap())
1184    }
1185}
1186
1187#[inline]
1188fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1189    unsafe {
1190        let mut shader = None;
1191        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1192        Ok(shader.unwrap())
1193    }
1194}
1195
1196#[inline]
1197fn create_buffer(
1198    device: &ID3D11Device,
1199    element_size: usize,
1200    buffer_size: usize,
1201) -> Result<ID3D11Buffer> {
1202    let desc = D3D11_BUFFER_DESC {
1203        ByteWidth: (element_size * buffer_size) as u32,
1204        Usage: D3D11_USAGE_DYNAMIC,
1205        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1206        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1207        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1208        StructureByteStride: element_size as u32,
1209    };
1210    let mut buffer = None;
1211    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1212    Ok(buffer.unwrap())
1213}
1214
1215#[inline]
1216fn create_buffer_view(
1217    device: &ID3D11Device,
1218    buffer: &ID3D11Buffer,
1219) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1220    let mut view = None;
1221    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1222    Ok([view])
1223}
1224
1225#[inline]
1226fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1227    let desc = D3D11_BUFFER_DESC {
1228        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1229        Usage: D3D11_USAGE_DYNAMIC,
1230        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1231        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1232        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1233        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1234    };
1235    let mut buffer = None;
1236    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1237    Ok(buffer.unwrap())
1238}
1239
1240#[inline]
1241fn update_buffer<T>(
1242    device_context: &ID3D11DeviceContext,
1243    buffer: &ID3D11Buffer,
1244    data: &[T],
1245) -> Result<()> {
1246    unsafe {
1247        let mut dest = std::mem::zeroed();
1248        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1249        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1250        device_context.Unmap(buffer, 0);
1251    }
1252    Ok(())
1253}
1254
1255#[inline]
1256fn set_pipeline_state(
1257    device_context: &ID3D11DeviceContext,
1258    buffer_view: &[Option<ID3D11ShaderResourceView>],
1259    topology: D3D_PRIMITIVE_TOPOLOGY,
1260    viewport: &[D3D11_VIEWPORT],
1261    vertex_shader: &ID3D11VertexShader,
1262    fragment_shader: &ID3D11PixelShader,
1263    global_params: &[Option<ID3D11Buffer>],
1264) {
1265    unsafe {
1266        device_context.VSSetShaderResources(1, Some(buffer_view));
1267        device_context.PSSetShaderResources(1, Some(buffer_view));
1268        device_context.IASetPrimitiveTopology(topology);
1269        device_context.RSSetViewports(Some(viewport));
1270        device_context.VSSetShader(vertex_shader, None);
1271        device_context.PSSetShader(fragment_shader, None);
1272        device_context.VSSetConstantBuffers(0, Some(global_params));
1273        device_context.PSSetConstantBuffers(0, Some(global_params));
1274    }
1275}
1276
1277const BUFFER_COUNT: usize = 3;
1278
1279mod shader_resources {
1280    use anyhow::Result;
1281    use windows::Win32::Graphics::Direct3D::{
1282        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1283        ID3DBlob,
1284    };
1285    use windows_core::{HSTRING, PCSTR};
1286
1287    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1288        unsafe {
1289            let mut entry = entry.to_owned();
1290            let mut target = target.to_owned();
1291            let mut compile_blob = None;
1292            let mut error_blob = None;
1293            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1294                .join("src/platform/windows/shaders.hlsl")
1295                .canonicalize()
1296                .unwrap();
1297            entry.push_str("\0");
1298            target.push_str("\0");
1299            let entry_point = PCSTR::from_raw(entry.as_ptr());
1300            let target_cstr = PCSTR::from_raw(target.as_ptr());
1301            #[cfg(debug_assertions)]
1302            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1303            #[cfg(not(debug_assertions))]
1304            let compile_flag = 0;
1305            let ret = D3DCompileFromFile(
1306                &HSTRING::from(shader_path.to_str().unwrap()),
1307                None,
1308                None,
1309                entry_point,
1310                target_cstr,
1311                compile_flag,
1312                0,
1313                &mut compile_blob,
1314                Some(&mut error_blob),
1315            );
1316            if ret.is_err() {
1317                let Some(error_blob) = error_blob else {
1318                    return Err(anyhow::anyhow!("{ret:?}"));
1319                };
1320                let string_len = error_blob.GetBufferSize();
1321                let error_string_encode = Vec::from_raw_parts(
1322                    error_blob.GetBufferPointer() as *mut u8,
1323                    string_len,
1324                    string_len,
1325                );
1326                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1327                log::error!("Shader compile error: {}", error_string);
1328                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1329            }
1330            Ok(compile_blob.unwrap())
1331        }
1332    }
1333}