directx_renderer.rs

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