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