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