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(1, 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    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    Ok(())
1068}
1069
1070#[cfg(not(feature = "enable-renderdoc"))]
1071fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1072    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1073}
1074
1075#[cfg(not(feature = "enable-renderdoc"))]
1076fn create_swap_chain(
1077    dxgi_factory: &IDXGIFactory6,
1078    device: &ID3D11Device,
1079    width: u32,
1080    height: u32,
1081) -> Result<IDXGISwapChain1> {
1082    let desc = DXGI_SWAP_CHAIN_DESC1 {
1083        Width: width,
1084        Height: height,
1085        Format: RENDER_TARGET_FORMAT,
1086        Stereo: false.into(),
1087        SampleDesc: DXGI_SAMPLE_DESC {
1088            Count: 1,
1089            Quality: 0,
1090        },
1091        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1092        BufferCount: BUFFER_COUNT as u32,
1093        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1094        Scaling: DXGI_SCALING_STRETCH,
1095        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1096        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1097        Flags: 0,
1098    };
1099    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1100}
1101
1102#[cfg(feature = "enable-renderdoc")]
1103fn create_swap_chain(
1104    dxgi_factory: &IDXGIFactory6,
1105    device: &ID3D11Device,
1106    hwnd: HWND,
1107    width: u32,
1108    height: u32,
1109) -> Result<IDXGISwapChain1> {
1110    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1111
1112    let desc = DXGI_SWAP_CHAIN_DESC1 {
1113        Width: width,
1114        Height: height,
1115        Format: RENDER_TARGET_FORMAT,
1116        Stereo: false.into(),
1117        SampleDesc: DXGI_SAMPLE_DESC {
1118            Count: 1,
1119            Quality: 0,
1120        },
1121        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1122        BufferCount: BUFFER_COUNT as u32,
1123        Scaling: DXGI_SCALING_STRETCH,
1124        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1125        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1126        Flags: 0,
1127    };
1128    let swap_chain =
1129        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1130    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1131    Ok(swap_chain)
1132}
1133
1134#[inline]
1135fn create_resources(
1136    devices: &DirectXDevices,
1137    swap_chain: &IDXGISwapChain1,
1138    width: u32,
1139    height: u32,
1140) -> Result<(
1141    ManuallyDrop<ID3D11Texture2D>,
1142    [Option<ID3D11RenderTargetView>; 1],
1143    ID3D11Texture2D,
1144    [Option<ID3D11RenderTargetView>; 1],
1145    [D3D11_VIEWPORT; 1],
1146)> {
1147    let (render_target, render_target_view) =
1148        create_render_target_and_its_view(&swap_chain, &devices.device)?;
1149    let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1150    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1151    Ok((
1152        render_target,
1153        render_target_view,
1154        msaa_target,
1155        msaa_view,
1156        viewport,
1157    ))
1158}
1159
1160#[inline]
1161fn create_render_target_and_its_view(
1162    swap_chain: &IDXGISwapChain1,
1163    device: &ID3D11Device,
1164) -> Result<(
1165    ManuallyDrop<ID3D11Texture2D>,
1166    [Option<ID3D11RenderTargetView>; 1],
1167)> {
1168    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1169    let mut render_target_view = None;
1170    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1171    Ok((
1172        ManuallyDrop::new(render_target),
1173        [Some(render_target_view.unwrap())],
1174    ))
1175}
1176
1177#[inline]
1178fn create_msaa_target_and_its_view(
1179    device: &ID3D11Device,
1180    width: u32,
1181    height: u32,
1182) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1183    let msaa_target = unsafe {
1184        let mut output = None;
1185        let desc = D3D11_TEXTURE2D_DESC {
1186            Width: width,
1187            Height: height,
1188            MipLevels: 1,
1189            ArraySize: 1,
1190            Format: RENDER_TARGET_FORMAT,
1191            SampleDesc: DXGI_SAMPLE_DESC {
1192                Count: MULTISAMPLE_COUNT,
1193                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1194            },
1195            Usage: D3D11_USAGE_DEFAULT,
1196            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1197            CPUAccessFlags: 0,
1198            MiscFlags: 0,
1199        };
1200        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1201        output.unwrap()
1202    };
1203    let msaa_view = unsafe {
1204        let mut output = None;
1205        device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1206        output.unwrap()
1207    };
1208    Ok((msaa_target, [Some(msaa_view)]))
1209}
1210
1211#[inline]
1212fn set_viewport(
1213    device_context: &ID3D11DeviceContext,
1214    width: f32,
1215    height: f32,
1216) -> [D3D11_VIEWPORT; 1] {
1217    let viewport = [D3D11_VIEWPORT {
1218        TopLeftX: 0.0,
1219        TopLeftY: 0.0,
1220        Width: width,
1221        Height: height,
1222        MinDepth: 0.0,
1223        MaxDepth: 1.0,
1224    }];
1225    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1226    viewport
1227}
1228
1229#[inline]
1230fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1231    let desc = D3D11_RASTERIZER_DESC {
1232        FillMode: D3D11_FILL_SOLID,
1233        CullMode: D3D11_CULL_NONE,
1234        FrontCounterClockwise: false.into(),
1235        DepthBias: 0,
1236        DepthBiasClamp: 0.0,
1237        SlopeScaledDepthBias: 0.0,
1238        DepthClipEnable: true.into(),
1239        ScissorEnable: false.into(),
1240        // MultisampleEnable: false.into(),
1241        MultisampleEnable: true.into(),
1242        AntialiasedLineEnable: false.into(),
1243    };
1244    let rasterizer_state = unsafe {
1245        let mut state = None;
1246        device.CreateRasterizerState(&desc, Some(&mut state))?;
1247        state.unwrap()
1248    };
1249    unsafe { device_context.RSSetState(&rasterizer_state) };
1250    Ok(())
1251}
1252
1253// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1254#[inline]
1255fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1256    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1257    // device performs the blend in linear space, which is ideal.
1258    let mut desc = D3D11_BLEND_DESC::default();
1259    desc.RenderTarget[0].BlendEnable = true.into();
1260    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1261    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1262    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1263    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1264    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1265    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1266    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1267    unsafe {
1268        let mut state = None;
1269        device.CreateBlendState(&desc, Some(&mut state))?;
1270        Ok(state.unwrap())
1271    }
1272}
1273
1274#[inline]
1275fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1276    unsafe {
1277        let mut shader = None;
1278        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1279        Ok(shader.unwrap())
1280    }
1281}
1282
1283#[inline]
1284fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1285    unsafe {
1286        let mut shader = None;
1287        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1288        Ok(shader.unwrap())
1289    }
1290}
1291
1292#[inline]
1293fn create_buffer(
1294    device: &ID3D11Device,
1295    element_size: usize,
1296    buffer_size: usize,
1297) -> Result<ID3D11Buffer> {
1298    let desc = D3D11_BUFFER_DESC {
1299        ByteWidth: (element_size * buffer_size) as u32,
1300        Usage: D3D11_USAGE_DYNAMIC,
1301        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1302        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1303        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1304        StructureByteStride: element_size as u32,
1305    };
1306    let mut buffer = None;
1307    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1308    Ok(buffer.unwrap())
1309}
1310
1311#[inline]
1312fn create_buffer_view(
1313    device: &ID3D11Device,
1314    buffer: &ID3D11Buffer,
1315) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1316    let mut view = None;
1317    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1318    Ok([view])
1319}
1320
1321#[inline]
1322fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1323    let desc = D3D11_BUFFER_DESC {
1324        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1325        Usage: D3D11_USAGE_DYNAMIC,
1326        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1327        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1328        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1329        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1330    };
1331    let mut buffer = None;
1332    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1333    Ok(buffer.unwrap())
1334}
1335
1336#[inline]
1337fn update_buffer<T>(
1338    device_context: &ID3D11DeviceContext,
1339    buffer: &ID3D11Buffer,
1340    data: &[T],
1341) -> Result<()> {
1342    unsafe {
1343        let mut dest = std::mem::zeroed();
1344        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1345        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1346        device_context.Unmap(buffer, 0);
1347    }
1348    Ok(())
1349}
1350
1351#[inline]
1352fn set_pipeline_state(
1353    device_context: &ID3D11DeviceContext,
1354    buffer_view: &[Option<ID3D11ShaderResourceView>],
1355    topology: D3D_PRIMITIVE_TOPOLOGY,
1356    viewport: &[D3D11_VIEWPORT],
1357    vertex_shader: &ID3D11VertexShader,
1358    fragment_shader: &ID3D11PixelShader,
1359    global_params: &[Option<ID3D11Buffer>],
1360) {
1361    unsafe {
1362        device_context.VSSetShaderResources(1, Some(buffer_view));
1363        device_context.PSSetShaderResources(1, Some(buffer_view));
1364        device_context.IASetPrimitiveTopology(topology);
1365        device_context.RSSetViewports(Some(viewport));
1366        device_context.VSSetShader(vertex_shader, None);
1367        device_context.PSSetShader(fragment_shader, None);
1368        device_context.VSSetConstantBuffers(0, Some(global_params));
1369        device_context.PSSetConstantBuffers(0, Some(global_params));
1370    }
1371}
1372
1373const BUFFER_COUNT: usize = 3;
1374
1375mod shader_resources {
1376    use anyhow::Result;
1377
1378    #[cfg(debug_assertions)]
1379    use windows::{
1380        Win32::Graphics::Direct3D::{
1381            Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1382            ID3DBlob,
1383        },
1384        core::{HSTRING, PCSTR},
1385    };
1386
1387    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1388    pub(super) enum ShaderModule {
1389        Quad,
1390        Shadow,
1391        Underline,
1392        Paths,
1393        MonochromeSprite,
1394        PolychromeSprite,
1395    }
1396
1397    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1398    pub(super) enum ShaderTarget {
1399        Vertex,
1400        Fragment,
1401    }
1402
1403    pub(super) struct RawShaderBytes<'t> {
1404        inner: &'t [u8],
1405
1406        #[cfg(debug_assertions)]
1407        _blob: ID3DBlob,
1408    }
1409
1410    impl<'t> RawShaderBytes<'t> {
1411        pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result<Self> {
1412            #[cfg(not(debug_assertions))]
1413            {
1414                Ok(Self::from_bytes(module, target))
1415            }
1416            #[cfg(debug_assertions)]
1417            {
1418                let blob = build_shader_blob(module, target)?;
1419                let inner = unsafe {
1420                    std::slice::from_raw_parts(
1421                        blob.GetBufferPointer() as *const u8,
1422                        blob.GetBufferSize(),
1423                    )
1424                };
1425                Ok(Self { inner, _blob: blob })
1426            }
1427        }
1428
1429        pub(super) fn as_bytes(&'t self) -> &'t [u8] {
1430            self.inner
1431        }
1432
1433        #[cfg(not(debug_assertions))]
1434        fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self {
1435            let bytes = match module {
1436                ShaderModule::Quad => match target {
1437                    ShaderTarget::Vertex => QUAD_VERTEX_BYTES,
1438                    ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES,
1439                },
1440                ShaderModule::Shadow => match target {
1441                    ShaderTarget::Vertex => SHADOW_VERTEX_BYTES,
1442                    ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES,
1443                },
1444                ShaderModule::Underline => match target {
1445                    ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES,
1446                    ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES,
1447                },
1448                ShaderModule::Paths => match target {
1449                    ShaderTarget::Vertex => PATHS_VERTEX_BYTES,
1450                    ShaderTarget::Fragment => PATHS_FRAGMENT_BYTES,
1451                },
1452                ShaderModule::MonochromeSprite => match target {
1453                    ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES,
1454                    ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES,
1455                },
1456                ShaderModule::PolychromeSprite => match target {
1457                    ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES,
1458                    ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES,
1459                },
1460            };
1461            Self { inner: bytes }
1462        }
1463    }
1464
1465    #[cfg(debug_assertions)]
1466    pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> {
1467        unsafe {
1468            let entry = format!(
1469                "{}_{}\0",
1470                entry.as_str(),
1471                match target {
1472                    ShaderTarget::Vertex => "vertex",
1473                    ShaderTarget::Fragment => "fragment",
1474                }
1475            );
1476            let target = match target {
1477                ShaderTarget::Vertex => "vs_5_0\0",
1478                ShaderTarget::Fragment => "ps_5_0\0",
1479            };
1480
1481            let mut compile_blob = None;
1482            let mut error_blob = None;
1483            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1484                .join("src/platform/windows/shaders.hlsl")
1485                .canonicalize()?;
1486
1487            let entry_point = PCSTR::from_raw(entry.as_ptr());
1488            let target_cstr = PCSTR::from_raw(target.as_ptr());
1489
1490            let ret = D3DCompileFromFile(
1491                &HSTRING::from(shader_path.to_str().unwrap()),
1492                None,
1493                None,
1494                entry_point,
1495                target_cstr,
1496                D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
1497                0,
1498                &mut compile_blob,
1499                Some(&mut error_blob),
1500            );
1501            if ret.is_err() {
1502                let Some(error_blob) = error_blob else {
1503                    return Err(anyhow::anyhow!("{ret:?}"));
1504                };
1505
1506                let error_string =
1507                    std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8)
1508                        .to_string_lossy();
1509                log::error!("Shader compile error: {}", error_string);
1510                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1511            }
1512            Ok(compile_blob.unwrap())
1513        }
1514    }
1515
1516    #[cfg(not(debug_assertions))]
1517    include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs"));
1518
1519    #[cfg(debug_assertions)]
1520    impl ShaderModule {
1521        pub fn as_str(&self) -> &str {
1522            match self {
1523                ShaderModule::Quad => "quad",
1524                ShaderModule::Shadow => "shadow",
1525                ShaderModule::Underline => "underline",
1526                ShaderModule::Paths => "paths",
1527                ShaderModule::MonochromeSprite => "monochrome_sprite",
1528                ShaderModule::PolychromeSprite => "polychrome_sprite",
1529            }
1530        }
1531    }
1532}
1533
1534mod nvidia {
1535    use std::{
1536        ffi::CStr,
1537        os::raw::{c_char, c_int, c_uint},
1538    };
1539
1540    use anyhow::{Context, Result};
1541    use windows::{
1542        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1543        core::s,
1544    };
1545
1546    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1547    const NVAPI_SHORT_STRING_MAX: usize = 64;
1548
1549    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1550    #[allow(non_camel_case_types)]
1551    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1552
1553    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
1554    #[allow(non_camel_case_types)]
1555    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
1556        driver_version: *mut c_uint,
1557        build_branch_string: *mut NvAPI_ShortString,
1558    ) -> c_int;
1559
1560    pub(super) fn get_driver_version() -> Result<String> {
1561        unsafe {
1562            // Try to load the NVIDIA driver DLL
1563            #[cfg(target_pointer_width = "64")]
1564            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
1565            #[cfg(target_pointer_width = "32")]
1566            let nvidia_dll = LoadLibraryA(s!("nvapi.dll")).context("Can't load nvapi.dll")?;
1567
1568            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
1569                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
1570            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
1571
1572            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1573            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1574            if nvapi_get_driver_version_ptr.is_null() {
1575                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1576            }
1577            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1578                std::mem::transmute(nvapi_get_driver_version_ptr);
1579
1580            let mut driver_version: c_uint = 0;
1581            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1582            let result = nvapi_get_driver_version(
1583                &mut driver_version as *mut c_uint,
1584                &mut build_branch_string as *mut NvAPI_ShortString,
1585            );
1586
1587            if result != 0 {
1588                anyhow::bail!(
1589                    "Failed to get NVIDIA driver version, error code: {}",
1590                    result
1591                );
1592            }
1593            let major = driver_version / 100;
1594            let minor = driver_version % 100;
1595            let branch_string = CStr::from_ptr(build_branch_string.as_ptr());
1596            Ok(format!(
1597                "{}.{} {}",
1598                major,
1599                minor,
1600                branch_string.to_string_lossy()
1601            ))
1602        }
1603    }
1604}
1605
1606mod amd {
1607    use std::os::raw::{c_char, c_int, c_void};
1608
1609    use anyhow::{Context, Result};
1610    use windows::{
1611        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1612        core::s,
1613    };
1614
1615    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145
1616    const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12);
1617
1618    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204
1619    // This is an opaque type, using struct to represent it properly for FFI
1620    #[repr(C)]
1621    struct AGSContext {
1622        _private: [u8; 0],
1623    }
1624
1625    #[repr(C)]
1626    pub struct AGSGPUInfo {
1627        pub driver_version: *const c_char,
1628        pub radeon_software_version: *const c_char,
1629        pub num_devices: c_int,
1630        pub devices: *mut c_void,
1631    }
1632
1633    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L429
1634    #[allow(non_camel_case_types)]
1635    type agsInitialize_t = unsafe extern "C" fn(
1636        version: c_int,
1637        config: *const c_void,
1638        context: *mut *mut AGSContext,
1639        gpu_info: *mut AGSGPUInfo,
1640    ) -> c_int;
1641
1642    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L436
1643    #[allow(non_camel_case_types)]
1644    type agsDeInitialize_t = unsafe extern "C" fn(context: *mut AGSContext) -> c_int;
1645
1646    pub(super) fn get_driver_version() -> Result<String> {
1647        unsafe {
1648            #[cfg(target_pointer_width = "64")]
1649            let amd_dll =
1650                LoadLibraryA(s!("amd_ags_x64.dll")).context("Failed to load AMD AGS library")?;
1651            #[cfg(target_pointer_width = "32")]
1652            let amd_dll =
1653                LoadLibraryA(s!("amd_ags_x86.dll")).context("Failed to load AMD AGS library")?;
1654
1655            let ags_initialize_addr = GetProcAddress(amd_dll, s!("agsInitialize"))
1656                .ok_or_else(|| anyhow::anyhow!("Failed to get agsInitialize address"))?;
1657            let ags_deinitialize_addr = GetProcAddress(amd_dll, s!("agsDeInitialize"))
1658                .ok_or_else(|| anyhow::anyhow!("Failed to get agsDeInitialize address"))?;
1659
1660            let ags_initialize: agsInitialize_t = std::mem::transmute(ags_initialize_addr);
1661            let ags_deinitialize: agsDeInitialize_t = std::mem::transmute(ags_deinitialize_addr);
1662
1663            let mut context: *mut AGSContext = std::ptr::null_mut();
1664            let mut gpu_info: AGSGPUInfo = AGSGPUInfo {
1665                driver_version: std::ptr::null(),
1666                radeon_software_version: std::ptr::null(),
1667                num_devices: 0,
1668                devices: std::ptr::null_mut(),
1669            };
1670
1671            let result = ags_initialize(
1672                AGS_CURRENT_VERSION,
1673                std::ptr::null(),
1674                &mut context,
1675                &mut gpu_info,
1676            );
1677            if result != 0 {
1678                anyhow::bail!("Failed to initialize AMD AGS, error code: {}", result);
1679            }
1680
1681            // Vulkan acctually returns this as the driver version
1682            let software_version = if !gpu_info.radeon_software_version.is_null() {
1683                std::ffi::CStr::from_ptr(gpu_info.radeon_software_version)
1684                    .to_string_lossy()
1685                    .into_owned()
1686            } else {
1687                "Unknown Radeon Software Version".to_string()
1688            };
1689
1690            let driver_version = if !gpu_info.driver_version.is_null() {
1691                std::ffi::CStr::from_ptr(gpu_info.driver_version)
1692                    .to_string_lossy()
1693                    .into_owned()
1694            } else {
1695                "Unknown Radeon Driver Version".to_string()
1696            };
1697
1698            ags_deinitialize(context);
1699            Ok(format!("{} ({})", software_version, driver_version))
1700        }
1701    }
1702}
1703
1704mod intel {
1705    use windows::{
1706        Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice},
1707        core::Interface,
1708    };
1709
1710    pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result<String> {
1711        let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?;
1712        Ok(format!(
1713            "{}.{}.{}.{}",
1714            number >> 48,
1715            (number >> 32) & 0xFFFF,
1716            (number >> 16) & 0xFFFF,
1717            number & 0xFFFF
1718        ))
1719    }
1720}