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