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