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    device: ID3D11Device,
  40    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 => amd::get_driver_version(),
 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 = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 634            let bytes = unsafe {
 635                std::slice::from_raw_parts(
 636                    shader_blob.GetBufferPointer() as *mut u8,
 637                    shader_blob.GetBufferSize(),
 638                )
 639            };
 640            create_vertex_shader(device, bytes)?
 641        };
 642        let fragment = {
 643            let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 644            let bytes = unsafe {
 645                std::slice::from_raw_parts(
 646                    shader_blob.GetBufferPointer() as *mut u8,
 647                    shader_blob.GetBufferSize(),
 648                )
 649            };
 650            create_fragment_shader(device, bytes)?
 651        };
 652        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
 653        let view = create_buffer_view(device, &buffer)?;
 654
 655        Ok(PipelineState {
 656            label,
 657            vertex,
 658            fragment,
 659            buffer,
 660            buffer_size,
 661            view,
 662            _marker: std::marker::PhantomData,
 663        })
 664    }
 665
 666    fn update_buffer(
 667        &mut self,
 668        device: &ID3D11Device,
 669        device_context: &ID3D11DeviceContext,
 670        data: &[T],
 671    ) -> Result<()> {
 672        if self.buffer_size < data.len() {
 673            let new_buffer_size = data.len().next_power_of_two();
 674            log::info!(
 675                "Updating {} buffer size from {} to {}",
 676                self.label,
 677                self.buffer_size,
 678                new_buffer_size
 679            );
 680            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
 681            let view = create_buffer_view(device, &buffer)?;
 682            self.buffer = buffer;
 683            self.view = view;
 684            self.buffer_size = new_buffer_size;
 685        }
 686        update_buffer(device_context, &self.buffer, data)
 687    }
 688
 689    fn draw(
 690        &self,
 691        device_context: &ID3D11DeviceContext,
 692        viewport: &[D3D11_VIEWPORT],
 693        global_params: &[Option<ID3D11Buffer>],
 694        instance_count: u32,
 695    ) -> Result<()> {
 696        set_pipeline_state(
 697            device_context,
 698            &self.view,
 699            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 700            viewport,
 701            &self.vertex,
 702            &self.fragment,
 703            global_params,
 704        );
 705        unsafe {
 706            device_context.DrawInstanced(4, instance_count, 0, 0);
 707        }
 708        Ok(())
 709    }
 710
 711    fn draw_with_texture(
 712        &self,
 713        device_context: &ID3D11DeviceContext,
 714        texture: &[Option<ID3D11ShaderResourceView>],
 715        viewport: &[D3D11_VIEWPORT],
 716        global_params: &[Option<ID3D11Buffer>],
 717        sampler: &[Option<ID3D11SamplerState>],
 718        instance_count: u32,
 719    ) -> Result<()> {
 720        set_pipeline_state(
 721            device_context,
 722            &self.view,
 723            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 724            viewport,
 725            &self.vertex,
 726            &self.fragment,
 727            global_params,
 728        );
 729        unsafe {
 730            device_context.PSSetSamplers(0, Some(sampler));
 731            device_context.VSSetShaderResources(0, Some(texture));
 732            device_context.PSSetShaderResources(0, Some(texture));
 733
 734            device_context.DrawInstanced(4, instance_count, 0, 0);
 735        }
 736        Ok(())
 737    }
 738}
 739
 740impl PathsPipelineState {
 741    fn new(device: &ID3D11Device) -> Result<Self> {
 742        let (vertex, vertex_shader) = {
 743            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 744            let bytes = unsafe {
 745                std::slice::from_raw_parts(
 746                    shader_blob.GetBufferPointer() as *mut u8,
 747                    shader_blob.GetBufferSize(),
 748                )
 749            };
 750            (create_vertex_shader(device, bytes)?, shader_blob)
 751        };
 752        let fragment = {
 753            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 754            let bytes = unsafe {
 755                std::slice::from_raw_parts(
 756                    shader_blob.GetBufferPointer() as *mut u8,
 757                    shader_blob.GetBufferSize(),
 758                )
 759            };
 760            create_fragment_shader(device, bytes)?
 761        };
 762        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 763        let view = create_buffer_view(device, &buffer)?;
 764        let vertex_buffer = Some(create_buffer(
 765            device,
 766            std::mem::size_of::<DirectXPathVertex>(),
 767            32,
 768        )?);
 769        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 770        // Create input layout
 771        let input_layout = unsafe {
 772            let shader_bytes = std::slice::from_raw_parts(
 773                vertex_shader.GetBufferPointer() as *const u8,
 774                vertex_shader.GetBufferSize(),
 775            );
 776            let mut layout = None;
 777            device.CreateInputLayout(
 778                &[
 779                    D3D11_INPUT_ELEMENT_DESC {
 780                        SemanticName: windows::core::s!("POSITION"),
 781                        SemanticIndex: 0,
 782                        Format: DXGI_FORMAT_R32G32_FLOAT,
 783                        InputSlot: 0,
 784                        AlignedByteOffset: 0,
 785                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 786                        InstanceDataStepRate: 0,
 787                    },
 788                    D3D11_INPUT_ELEMENT_DESC {
 789                        SemanticName: windows::core::s!("TEXCOORD"),
 790                        SemanticIndex: 0,
 791                        Format: DXGI_FORMAT_R32G32_FLOAT,
 792                        InputSlot: 0,
 793                        AlignedByteOffset: 8,
 794                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 795                        InstanceDataStepRate: 0,
 796                    },
 797                    D3D11_INPUT_ELEMENT_DESC {
 798                        SemanticName: windows::core::s!("TEXCOORD"),
 799                        SemanticIndex: 1,
 800                        Format: DXGI_FORMAT_R32G32_FLOAT,
 801                        InputSlot: 0,
 802                        AlignedByteOffset: 16,
 803                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 804                        InstanceDataStepRate: 0,
 805                    },
 806                    D3D11_INPUT_ELEMENT_DESC {
 807                        SemanticName: windows::core::s!("GLOBALIDX"),
 808                        SemanticIndex: 0,
 809                        Format: DXGI_FORMAT_R32_UINT,
 810                        InputSlot: 0,
 811                        AlignedByteOffset: 24,
 812                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 813                        InstanceDataStepRate: 0,
 814                    },
 815                ],
 816                shader_bytes,
 817                Some(&mut layout),
 818            )?;
 819            layout.unwrap()
 820        };
 821
 822        Ok(Self {
 823            vertex,
 824            fragment,
 825            buffer,
 826            buffer_size: 32,
 827            vertex_buffer,
 828            vertex_buffer_size: 32,
 829            indirect_draw_buffer,
 830            indirect_buffer_size: 32,
 831            input_layout,
 832            view,
 833        })
 834    }
 835
 836    fn update_buffer(
 837        &mut self,
 838        device: &ID3D11Device,
 839        device_context: &ID3D11DeviceContext,
 840        buffer_data: &[PathSprite],
 841        vertices_data: &[DirectXPathVertex],
 842        draw_commands: &[DrawInstancedIndirectArgs],
 843    ) -> Result<()> {
 844        if self.buffer_size < buffer_data.len() {
 845            let new_buffer_size = buffer_data.len().next_power_of_two();
 846            log::info!(
 847                "Updating Paths Pipeline buffer size from {} to {}",
 848                self.buffer_size,
 849                new_buffer_size
 850            );
 851            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 852            let view = create_buffer_view(device, &buffer)?;
 853            self.buffer = buffer;
 854            self.view = view;
 855            self.buffer_size = new_buffer_size;
 856        }
 857        update_buffer(device_context, &self.buffer, buffer_data)?;
 858        if self.vertex_buffer_size < vertices_data.len() {
 859            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 860            log::info!(
 861                "Updating Paths Pipeline vertex buffer size from {} to {}",
 862                self.vertex_buffer_size,
 863                new_vertex_buffer_size
 864            );
 865            let vertex_buffer = create_buffer(
 866                device,
 867                std::mem::size_of::<DirectXPathVertex>(),
 868                new_vertex_buffer_size,
 869            )?;
 870            self.vertex_buffer = Some(vertex_buffer);
 871            self.vertex_buffer_size = new_vertex_buffer_size;
 872        }
 873        update_buffer(
 874            device_context,
 875            self.vertex_buffer.as_ref().unwrap(),
 876            vertices_data,
 877        )?;
 878        if self.indirect_buffer_size < draw_commands.len() {
 879            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 880            log::info!(
 881                "Updating Paths Pipeline indirect buffer size from {} to {}",
 882                self.indirect_buffer_size,
 883                new_indirect_buffer_size
 884            );
 885            let indirect_draw_buffer =
 886                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 887            self.indirect_draw_buffer = indirect_draw_buffer;
 888            self.indirect_buffer_size = new_indirect_buffer_size;
 889        }
 890        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 891        Ok(())
 892    }
 893
 894    fn draw(
 895        &self,
 896        device_context: &ID3D11DeviceContext,
 897        count: usize,
 898        viewport: &[D3D11_VIEWPORT],
 899        global_params: &[Option<ID3D11Buffer>],
 900    ) -> Result<()> {
 901        set_pipeline_state(
 902            device_context,
 903            &self.view,
 904            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 905            viewport,
 906            &self.vertex,
 907            &self.fragment,
 908            global_params,
 909        );
 910        unsafe {
 911            const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
 912            device_context.IASetVertexBuffers(
 913                0,
 914                1,
 915                Some(&self.vertex_buffer),
 916                Some(&STRIDE),
 917                Some(&0),
 918            );
 919            device_context.IASetInputLayout(&self.input_layout);
 920        }
 921        for i in 0..count {
 922            unsafe {
 923                device_context.DrawInstancedIndirect(
 924                    &self.indirect_draw_buffer,
 925                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 926                );
 927            }
 928        }
 929        Ok(())
 930    }
 931}
 932
 933#[repr(C)]
 934struct DirectXPathVertex {
 935    xy_position: Point<ScaledPixels>,
 936    content_mask: Bounds<ScaledPixels>,
 937    sprite_index: u32,
 938}
 939
 940#[derive(Clone, Debug, Eq, PartialEq)]
 941#[repr(C)]
 942struct PathSprite {
 943    bounds: Bounds<ScaledPixels>,
 944    color: Background,
 945}
 946
 947impl Drop for DirectXResources {
 948    fn drop(&mut self) {
 949        unsafe {
 950            ManuallyDrop::drop(&mut self.render_target);
 951        }
 952    }
 953}
 954
 955#[inline]
 956fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 957    #[cfg(debug_assertions)]
 958    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 959    #[cfg(not(debug_assertions))]
 960    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 961    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 962}
 963
 964fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 965    for adapter_index in 0.. {
 966        let adapter: IDXGIAdapter1 = unsafe {
 967            dxgi_factory
 968                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 969        }?;
 970        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
 971            let gpu_name = String::from_utf16_lossy(&desc.Description)
 972                .trim_matches(char::from(0))
 973                .to_string();
 974            log::info!("Using GPU: {}", gpu_name);
 975        }
 976        // Check to see whether the adapter supports Direct3D 11, but don't
 977        // create the actual device yet.
 978        if get_device(&adapter, None, None).log_err().is_some() {
 979            return Ok(adapter);
 980        }
 981    }
 982
 983    unreachable!()
 984}
 985
 986fn get_device(
 987    adapter: &IDXGIAdapter1,
 988    device: Option<*mut Option<ID3D11Device>>,
 989    context: Option<*mut Option<ID3D11DeviceContext>>,
 990) -> Result<()> {
 991    #[cfg(debug_assertions)]
 992    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
 993    #[cfg(not(debug_assertions))]
 994    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 995    Ok(unsafe {
 996        D3D11CreateDevice(
 997            adapter,
 998            D3D_DRIVER_TYPE_UNKNOWN,
 999            HMODULE::default(),
1000            device_flags,
1001            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
1002            // 8x MSAA is required for Direct3D Feature Level 11.0 or better
1003            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1004            D3D11_SDK_VERSION,
1005            device,
1006            None,
1007            context,
1008        )?
1009    })
1010}
1011
1012#[cfg(not(feature = "enable-renderdoc"))]
1013fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1014    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1015}
1016
1017#[cfg(not(feature = "enable-renderdoc"))]
1018fn create_swap_chain(
1019    dxgi_factory: &IDXGIFactory6,
1020    device: &ID3D11Device,
1021    width: u32,
1022    height: u32,
1023) -> Result<IDXGISwapChain1> {
1024    let desc = DXGI_SWAP_CHAIN_DESC1 {
1025        Width: width,
1026        Height: height,
1027        Format: RENDER_TARGET_FORMAT,
1028        Stereo: false.into(),
1029        SampleDesc: DXGI_SAMPLE_DESC {
1030            Count: 1,
1031            Quality: 0,
1032        },
1033        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1034        BufferCount: BUFFER_COUNT as u32,
1035        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1036        Scaling: DXGI_SCALING_STRETCH,
1037        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1038        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1039        Flags: 0,
1040    };
1041    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1042}
1043
1044#[cfg(feature = "enable-renderdoc")]
1045fn create_swap_chain(
1046    dxgi_factory: &IDXGIFactory6,
1047    device: &ID3D11Device,
1048    hwnd: HWND,
1049    width: u32,
1050    height: u32,
1051) -> Result<IDXGISwapChain1> {
1052    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1053
1054    let desc = DXGI_SWAP_CHAIN_DESC1 {
1055        Width: width,
1056        Height: height,
1057        Format: RENDER_TARGET_FORMAT,
1058        Stereo: false.into(),
1059        SampleDesc: DXGI_SAMPLE_DESC {
1060            Count: 1,
1061            Quality: 0,
1062        },
1063        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1064        BufferCount: BUFFER_COUNT as u32,
1065        Scaling: DXGI_SCALING_STRETCH,
1066        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1067        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1068        Flags: 0,
1069    };
1070    let swap_chain =
1071        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1072    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1073    Ok(swap_chain)
1074}
1075
1076#[inline]
1077fn create_resources(
1078    devices: &DirectXDevices,
1079    swap_chain: &IDXGISwapChain1,
1080    width: u32,
1081    height: u32,
1082) -> Result<(
1083    ManuallyDrop<ID3D11Texture2D>,
1084    [Option<ID3D11RenderTargetView>; 1],
1085    ID3D11Texture2D,
1086    [Option<ID3D11RenderTargetView>; 1],
1087    [D3D11_VIEWPORT; 1],
1088)> {
1089    let (render_target, render_target_view) =
1090        create_render_target_and_its_view(&swap_chain, &devices.device)?;
1091    let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1092    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1093    Ok((
1094        render_target,
1095        render_target_view,
1096        msaa_target,
1097        msaa_view,
1098        viewport,
1099    ))
1100}
1101
1102#[inline]
1103fn create_render_target_and_its_view(
1104    swap_chain: &IDXGISwapChain1,
1105    device: &ID3D11Device,
1106) -> Result<(
1107    ManuallyDrop<ID3D11Texture2D>,
1108    [Option<ID3D11RenderTargetView>; 1],
1109)> {
1110    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1111    let mut render_target_view = None;
1112    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1113    Ok((
1114        ManuallyDrop::new(render_target),
1115        [Some(render_target_view.unwrap())],
1116    ))
1117}
1118
1119#[inline]
1120fn create_msaa_target_and_its_view(
1121    device: &ID3D11Device,
1122    width: u32,
1123    height: u32,
1124) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1125    let msaa_target = unsafe {
1126        let mut output = None;
1127        let desc = D3D11_TEXTURE2D_DESC {
1128            Width: width,
1129            Height: height,
1130            MipLevels: 1,
1131            ArraySize: 1,
1132            Format: RENDER_TARGET_FORMAT,
1133            SampleDesc: DXGI_SAMPLE_DESC {
1134                Count: MULTISAMPLE_COUNT,
1135                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1136            },
1137            Usage: D3D11_USAGE_DEFAULT,
1138            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1139            CPUAccessFlags: 0,
1140            MiscFlags: 0,
1141        };
1142        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1143        output.unwrap()
1144    };
1145    let msaa_view = unsafe {
1146        let mut output = None;
1147        device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1148        output.unwrap()
1149    };
1150    Ok((msaa_target, [Some(msaa_view)]))
1151}
1152
1153#[inline]
1154fn set_viewport(
1155    device_context: &ID3D11DeviceContext,
1156    width: f32,
1157    height: f32,
1158) -> [D3D11_VIEWPORT; 1] {
1159    let viewport = [D3D11_VIEWPORT {
1160        TopLeftX: 0.0,
1161        TopLeftY: 0.0,
1162        Width: width,
1163        Height: height,
1164        MinDepth: 0.0,
1165        MaxDepth: 1.0,
1166    }];
1167    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1168    viewport
1169}
1170
1171#[inline]
1172fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1173    let desc = D3D11_RASTERIZER_DESC {
1174        FillMode: D3D11_FILL_SOLID,
1175        CullMode: D3D11_CULL_NONE,
1176        FrontCounterClockwise: false.into(),
1177        DepthBias: 0,
1178        DepthBiasClamp: 0.0,
1179        SlopeScaledDepthBias: 0.0,
1180        DepthClipEnable: true.into(),
1181        ScissorEnable: false.into(),
1182        // MultisampleEnable: false.into(),
1183        MultisampleEnable: true.into(),
1184        AntialiasedLineEnable: false.into(),
1185    };
1186    let rasterizer_state = unsafe {
1187        let mut state = None;
1188        device.CreateRasterizerState(&desc, Some(&mut state))?;
1189        state.unwrap()
1190    };
1191    unsafe { device_context.RSSetState(&rasterizer_state) };
1192    Ok(())
1193}
1194
1195// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1196#[inline]
1197fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1198    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1199    // device performs the blend in linear space, which is ideal.
1200    let mut desc = D3D11_BLEND_DESC::default();
1201    desc.RenderTarget[0].BlendEnable = true.into();
1202    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1203    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1204    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1205    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1206    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1207    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1208    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1209    unsafe {
1210        let mut state = None;
1211        device.CreateBlendState(&desc, Some(&mut state))?;
1212        Ok(state.unwrap())
1213    }
1214}
1215
1216#[inline]
1217fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1218    unsafe {
1219        let mut shader = None;
1220        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1221        Ok(shader.unwrap())
1222    }
1223}
1224
1225#[inline]
1226fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1227    unsafe {
1228        let mut shader = None;
1229        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1230        Ok(shader.unwrap())
1231    }
1232}
1233
1234#[inline]
1235fn create_buffer(
1236    device: &ID3D11Device,
1237    element_size: usize,
1238    buffer_size: usize,
1239) -> Result<ID3D11Buffer> {
1240    let desc = D3D11_BUFFER_DESC {
1241        ByteWidth: (element_size * buffer_size) as u32,
1242        Usage: D3D11_USAGE_DYNAMIC,
1243        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1244        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1245        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1246        StructureByteStride: element_size as u32,
1247    };
1248    let mut buffer = None;
1249    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1250    Ok(buffer.unwrap())
1251}
1252
1253#[inline]
1254fn create_buffer_view(
1255    device: &ID3D11Device,
1256    buffer: &ID3D11Buffer,
1257) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1258    let mut view = None;
1259    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1260    Ok([view])
1261}
1262
1263#[inline]
1264fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1265    let desc = D3D11_BUFFER_DESC {
1266        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1267        Usage: D3D11_USAGE_DYNAMIC,
1268        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1269        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1270        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1271        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1272    };
1273    let mut buffer = None;
1274    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1275    Ok(buffer.unwrap())
1276}
1277
1278#[inline]
1279fn update_buffer<T>(
1280    device_context: &ID3D11DeviceContext,
1281    buffer: &ID3D11Buffer,
1282    data: &[T],
1283) -> Result<()> {
1284    unsafe {
1285        let mut dest = std::mem::zeroed();
1286        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1287        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1288        device_context.Unmap(buffer, 0);
1289    }
1290    Ok(())
1291}
1292
1293#[inline]
1294fn set_pipeline_state(
1295    device_context: &ID3D11DeviceContext,
1296    buffer_view: &[Option<ID3D11ShaderResourceView>],
1297    topology: D3D_PRIMITIVE_TOPOLOGY,
1298    viewport: &[D3D11_VIEWPORT],
1299    vertex_shader: &ID3D11VertexShader,
1300    fragment_shader: &ID3D11PixelShader,
1301    global_params: &[Option<ID3D11Buffer>],
1302) {
1303    unsafe {
1304        device_context.VSSetShaderResources(1, Some(buffer_view));
1305        device_context.PSSetShaderResources(1, Some(buffer_view));
1306        device_context.IASetPrimitiveTopology(topology);
1307        device_context.RSSetViewports(Some(viewport));
1308        device_context.VSSetShader(vertex_shader, None);
1309        device_context.PSSetShader(fragment_shader, None);
1310        device_context.VSSetConstantBuffers(0, Some(global_params));
1311        device_context.PSSetConstantBuffers(0, Some(global_params));
1312    }
1313}
1314
1315const BUFFER_COUNT: usize = 3;
1316
1317mod shader_resources {
1318    use anyhow::Result;
1319    use windows::Win32::Graphics::Direct3D::{
1320        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1321        ID3DBlob,
1322    };
1323    use windows_core::{HSTRING, PCSTR};
1324
1325    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1326        unsafe {
1327            let mut entry = entry.to_owned();
1328            let mut target = target.to_owned();
1329            let mut compile_blob = None;
1330            let mut error_blob = None;
1331            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1332                .join("src/platform/windows/shaders.hlsl")
1333                .canonicalize()
1334                .unwrap();
1335            entry.push_str("\0");
1336            target.push_str("\0");
1337            let entry_point = PCSTR::from_raw(entry.as_ptr());
1338            let target_cstr = PCSTR::from_raw(target.as_ptr());
1339            #[cfg(debug_assertions)]
1340            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1341            #[cfg(not(debug_assertions))]
1342            let compile_flag = 0;
1343            let ret = D3DCompileFromFile(
1344                &HSTRING::from(shader_path.to_str().unwrap()),
1345                None,
1346                None,
1347                entry_point,
1348                target_cstr,
1349                compile_flag,
1350                0,
1351                &mut compile_blob,
1352                Some(&mut error_blob),
1353            );
1354            if ret.is_err() {
1355                let Some(error_blob) = error_blob else {
1356                    return Err(anyhow::anyhow!("{ret:?}"));
1357                };
1358                let string_len = error_blob.GetBufferSize();
1359                let error_string_encode = Vec::from_raw_parts(
1360                    error_blob.GetBufferPointer() as *mut u8,
1361                    string_len,
1362                    string_len,
1363                );
1364                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1365                log::error!("Shader compile error: {}", error_string);
1366                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1367            }
1368            Ok(compile_blob.unwrap())
1369        }
1370    }
1371}
1372
1373mod nvidia {
1374    use std::{
1375        ffi::CStr,
1376        os::raw::{c_char, c_int, c_uint},
1377    };
1378
1379    use anyhow::{Context, Result};
1380    use windows::{
1381        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1382        core::s,
1383    };
1384
1385    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1386    const NVAPI_SHORT_STRING_MAX: usize = 64;
1387
1388    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1389    #[allow(non_camel_case_types)]
1390    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1391
1392    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
1393    #[allow(non_camel_case_types)]
1394    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
1395        driver_version: *mut c_uint,
1396        build_branch_string: *mut NvAPI_ShortString,
1397    ) -> c_int;
1398
1399    pub(super) fn get_driver_version() -> Result<String> {
1400        unsafe {
1401            // Try to load the NVIDIA driver DLL
1402            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
1403            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
1404                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
1405            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
1406
1407            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1408            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1409            if nvapi_get_driver_version_ptr.is_null() {
1410                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1411            }
1412            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1413                std::mem::transmute(nvapi_get_driver_version_ptr);
1414
1415            let mut driver_version: c_uint = 0;
1416            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1417            let result = nvapi_get_driver_version(
1418                &mut driver_version as *mut c_uint,
1419                &mut build_branch_string as *mut NvAPI_ShortString,
1420            );
1421
1422            if result != 0 {
1423                anyhow::bail!(
1424                    "Failed to get NVIDIA driver version, error code: {}",
1425                    result
1426                );
1427            }
1428            let major = driver_version / 100;
1429            let minor = driver_version % 100;
1430            let branch_string = CStr::from_ptr(build_branch_string.as_ptr());
1431            Ok(format!(
1432                "{}.{} {}",
1433                major,
1434                minor,
1435                branch_string.to_string_lossy()
1436            ))
1437        }
1438    }
1439}
1440
1441mod amd {
1442    use std::os::raw::{c_char, c_int, c_void};
1443
1444    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145
1445    const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0;
1446
1447    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204
1448    // This is an opaque type, using struct to represent it properly for FFI
1449    #[repr(C)]
1450    struct AGSContext {
1451        _private: [u8; 0],
1452    }
1453
1454    #[repr(C)]
1455    pub struct AGSGPUInfo {
1456        pub driver_version: *const c_char,
1457        pub radeon_software_version: *const c_char,
1458        pub num_devices: c_int,
1459        pub devices: *mut c_void,
1460    }
1461
1462    unsafe extern "C" {
1463        fn agsInitialize(
1464            version: c_int,
1465            config: *const c_void,
1466            context: *mut *mut AGSContext,
1467            gpu_info: *mut AGSGPUInfo,
1468        ) -> c_int;
1469
1470        fn agsDeInitialize(context: *mut AGSContext) -> c_int;
1471    }
1472
1473    pub(super) fn get_driver_version() -> anyhow::Result<String> {
1474        unsafe {
1475            let mut context: *mut AGSContext = std::ptr::null_mut();
1476            let mut gpu_info: AGSGPUInfo = AGSGPUInfo {
1477                driver_version: std::ptr::null(),
1478                radeon_software_version: std::ptr::null(),
1479                num_devices: 0,
1480                devices: std::ptr::null_mut(),
1481            };
1482
1483            let result = agsInitialize(
1484                AGS_CURRENT_VERSION,
1485                std::ptr::null(),
1486                &mut context,
1487                &mut gpu_info,
1488            );
1489            if result != 0 {
1490                return Err(anyhow::anyhow!(
1491                    "Failed to initialize AGS, error code: {}",
1492                    result
1493                ));
1494            }
1495
1496            // Vulkan acctually returns this as the driver version
1497            let software_version = if !gpu_info.radeon_software_version.is_null() {
1498                std::ffi::CStr::from_ptr(gpu_info.radeon_software_version)
1499                    .to_string_lossy()
1500                    .into_owned()
1501            } else {
1502                "Unknown Radeon Software Version".to_string()
1503            };
1504
1505            let driver_version = if !gpu_info.driver_version.is_null() {
1506                std::ffi::CStr::from_ptr(gpu_info.driver_version)
1507                    .to_string_lossy()
1508                    .into_owned()
1509            } else {
1510                "Unknown Radeon Driver Version".to_string()
1511            };
1512
1513            agsDeInitialize(context);
1514            Ok(format!("{} ({})", software_version, driver_version))
1515        }
1516    }
1517}
1518
1519mod intel {
1520    use windows::{
1521        Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice},
1522        core::Interface,
1523    };
1524
1525    pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result<String> {
1526        let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?;
1527        Ok(format!(
1528            "{}.{}.{}.{}",
1529            number >> 48,
1530            (number >> 32) & 0xFFFF,
1531            (number >> 16) & 0xFFFF,
1532            number & 0xFFFF
1533        ))
1534    }
1535}