directx_renderer.rs

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