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