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