directx_renderer.rs

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