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 = DXGI_CREATE_FACTORY_DEBUG;
 978    #[cfg(not(debug_assertions))]
 979    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 980    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 981}
 982
 983fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 984    for adapter_index in 0.. {
 985        let adapter: IDXGIAdapter1 = unsafe {
 986            dxgi_factory
 987                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 988        }?;
 989        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
 990            let gpu_name = String::from_utf16_lossy(&desc.Description)
 991                .trim_matches(char::from(0))
 992                .to_string();
 993            log::info!("Using GPU: {}", gpu_name);
 994        }
 995        // Check to see whether the adapter supports Direct3D 11, but don't
 996        // create the actual device yet.
 997        if get_device(&adapter, None, None, None).log_err().is_some() {
 998            return Ok(adapter);
 999        }
1000    }
1001
1002    unreachable!()
1003}
1004
1005fn get_device(
1006    adapter: &IDXGIAdapter1,
1007    device: Option<*mut Option<ID3D11Device>>,
1008    context: Option<*mut Option<ID3D11DeviceContext>>,
1009    feature_level: Option<*mut D3D_FEATURE_LEVEL>,
1010) -> Result<()> {
1011    #[cfg(debug_assertions)]
1012    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
1013    #[cfg(not(debug_assertions))]
1014    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
1015    unsafe {
1016        D3D11CreateDevice(
1017            adapter,
1018            D3D_DRIVER_TYPE_UNKNOWN,
1019            HMODULE::default(),
1020            device_flags,
1021            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
1022            Some(&[
1023                D3D_FEATURE_LEVEL_11_1,
1024                D3D_FEATURE_LEVEL_11_0,
1025                D3D_FEATURE_LEVEL_10_1,
1026            ]),
1027            D3D11_SDK_VERSION,
1028            device,
1029            feature_level,
1030            context,
1031        )?;
1032    }
1033    Ok(())
1034}
1035
1036#[inline]
1037fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1038    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1039}
1040
1041fn create_swap_chain_for_composition(
1042    dxgi_factory: &IDXGIFactory6,
1043    device: &ID3D11Device,
1044    width: u32,
1045    height: u32,
1046) -> Result<IDXGISwapChain1> {
1047    let desc = DXGI_SWAP_CHAIN_DESC1 {
1048        Width: width,
1049        Height: height,
1050        Format: RENDER_TARGET_FORMAT,
1051        Stereo: false.into(),
1052        SampleDesc: DXGI_SAMPLE_DESC {
1053            Count: 1,
1054            Quality: 0,
1055        },
1056        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1057        BufferCount: BUFFER_COUNT as u32,
1058        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1059        Scaling: DXGI_SCALING_STRETCH,
1060        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1061        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1062        Flags: 0,
1063    };
1064    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1065}
1066
1067fn create_swap_chain(
1068    dxgi_factory: &IDXGIFactory6,
1069    device: &ID3D11Device,
1070    hwnd: HWND,
1071    width: u32,
1072    height: u32,
1073) -> Result<IDXGISwapChain1> {
1074    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1075
1076    let desc = DXGI_SWAP_CHAIN_DESC1 {
1077        Width: width,
1078        Height: height,
1079        Format: RENDER_TARGET_FORMAT,
1080        Stereo: false.into(),
1081        SampleDesc: DXGI_SAMPLE_DESC {
1082            Count: 1,
1083            Quality: 0,
1084        },
1085        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1086        BufferCount: BUFFER_COUNT as u32,
1087        Scaling: DXGI_SCALING_NONE,
1088        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1089        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1090        Flags: 0,
1091    };
1092    let swap_chain =
1093        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1094    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1095    Ok(swap_chain)
1096}
1097
1098#[inline]
1099fn create_resources(
1100    devices: &DirectXDevices,
1101    swap_chain: &IDXGISwapChain1,
1102    width: u32,
1103    height: u32,
1104) -> Result<(
1105    ManuallyDrop<ID3D11Texture2D>,
1106    [Option<ID3D11RenderTargetView>; 1],
1107    ID3D11Texture2D,
1108    [Option<ID3D11ShaderResourceView>; 1],
1109    ID3D11Texture2D,
1110    [Option<ID3D11RenderTargetView>; 1],
1111    [D3D11_VIEWPORT; 1],
1112)> {
1113    let (render_target, render_target_view) =
1114        create_render_target_and_its_view(&swap_chain, &devices.device)?;
1115    let (path_intermediate_texture, path_intermediate_srv) =
1116        create_path_intermediate_texture(&devices.device, width, height)?;
1117    let (path_intermediate_msaa_texture, path_intermediate_msaa_view) =
1118        create_path_intermediate_msaa_texture_and_view(&devices.device, width, height)?;
1119    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1120    Ok((
1121        render_target,
1122        render_target_view,
1123        path_intermediate_texture,
1124        path_intermediate_srv,
1125        path_intermediate_msaa_texture,
1126        path_intermediate_msaa_view,
1127        viewport,
1128    ))
1129}
1130
1131#[inline]
1132fn create_render_target_and_its_view(
1133    swap_chain: &IDXGISwapChain1,
1134    device: &ID3D11Device,
1135) -> Result<(
1136    ManuallyDrop<ID3D11Texture2D>,
1137    [Option<ID3D11RenderTargetView>; 1],
1138)> {
1139    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1140    let mut render_target_view = None;
1141    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1142    Ok((
1143        ManuallyDrop::new(render_target),
1144        [Some(render_target_view.unwrap())],
1145    ))
1146}
1147
1148#[inline]
1149fn create_path_intermediate_texture(
1150    device: &ID3D11Device,
1151    width: u32,
1152    height: u32,
1153) -> Result<(ID3D11Texture2D, [Option<ID3D11ShaderResourceView>; 1])> {
1154    let texture = unsafe {
1155        let mut output = None;
1156        let desc = D3D11_TEXTURE2D_DESC {
1157            Width: width,
1158            Height: height,
1159            MipLevels: 1,
1160            ArraySize: 1,
1161            Format: RENDER_TARGET_FORMAT,
1162            SampleDesc: DXGI_SAMPLE_DESC {
1163                Count: 1,
1164                Quality: 0,
1165            },
1166            Usage: D3D11_USAGE_DEFAULT,
1167            BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32,
1168            CPUAccessFlags: 0,
1169            MiscFlags: 0,
1170        };
1171        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1172        output.unwrap()
1173    };
1174
1175    let mut shader_resource_view = None;
1176    unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? };
1177
1178    Ok((texture, [Some(shader_resource_view.unwrap())]))
1179}
1180
1181#[inline]
1182fn create_path_intermediate_msaa_texture_and_view(
1183    device: &ID3D11Device,
1184    width: u32,
1185    height: u32,
1186) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1187    let msaa_texture = unsafe {
1188        let mut output = None;
1189        let desc = D3D11_TEXTURE2D_DESC {
1190            Width: width,
1191            Height: height,
1192            MipLevels: 1,
1193            ArraySize: 1,
1194            Format: RENDER_TARGET_FORMAT,
1195            SampleDesc: DXGI_SAMPLE_DESC {
1196                Count: PATH_MULTISAMPLE_COUNT,
1197                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1198            },
1199            Usage: D3D11_USAGE_DEFAULT,
1200            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1201            CPUAccessFlags: 0,
1202            MiscFlags: 0,
1203        };
1204        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1205        output.unwrap()
1206    };
1207    let mut msaa_view = None;
1208    unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? };
1209    Ok((msaa_texture, [Some(msaa_view.unwrap())]))
1210}
1211
1212#[inline]
1213fn set_viewport(
1214    device_context: &ID3D11DeviceContext,
1215    width: f32,
1216    height: f32,
1217) -> [D3D11_VIEWPORT; 1] {
1218    let viewport = [D3D11_VIEWPORT {
1219        TopLeftX: 0.0,
1220        TopLeftY: 0.0,
1221        Width: width,
1222        Height: height,
1223        MinDepth: 0.0,
1224        MaxDepth: 1.0,
1225    }];
1226    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1227    viewport
1228}
1229
1230#[inline]
1231fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1232    let desc = D3D11_RASTERIZER_DESC {
1233        FillMode: D3D11_FILL_SOLID,
1234        CullMode: D3D11_CULL_NONE,
1235        FrontCounterClockwise: false.into(),
1236        DepthBias: 0,
1237        DepthBiasClamp: 0.0,
1238        SlopeScaledDepthBias: 0.0,
1239        DepthClipEnable: true.into(),
1240        ScissorEnable: false.into(),
1241        MultisampleEnable: true.into(),
1242        AntialiasedLineEnable: false.into(),
1243    };
1244    let rasterizer_state = unsafe {
1245        let mut state = None;
1246        device.CreateRasterizerState(&desc, Some(&mut state))?;
1247        state.unwrap()
1248    };
1249    unsafe { device_context.RSSetState(&rasterizer_state) };
1250    Ok(())
1251}
1252
1253// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1254#[inline]
1255fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1256    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1257    // device performs the blend in linear space, which is ideal.
1258    let mut desc = D3D11_BLEND_DESC::default();
1259    desc.RenderTarget[0].BlendEnable = true.into();
1260    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1261    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1262    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1263    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1264    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1265    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1266    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1267    unsafe {
1268        let mut state = None;
1269        device.CreateBlendState(&desc, Some(&mut state))?;
1270        Ok(state.unwrap())
1271    }
1272}
1273
1274#[inline]
1275fn create_blend_state_for_path_rasterization(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1276    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1277    // device performs the blend in linear space, which is ideal.
1278    let mut desc = D3D11_BLEND_DESC::default();
1279    desc.RenderTarget[0].BlendEnable = true.into();
1280    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1281    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1282    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
1283    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1284    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1285    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
1286    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1287    unsafe {
1288        let mut state = None;
1289        device.CreateBlendState(&desc, Some(&mut state))?;
1290        Ok(state.unwrap())
1291    }
1292}
1293
1294#[inline]
1295fn create_blend_state_for_path_sprite(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1296    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1297    // device performs the blend in linear space, which is ideal.
1298    let mut desc = D3D11_BLEND_DESC::default();
1299    desc.RenderTarget[0].BlendEnable = true.into();
1300    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1301    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1302    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
1303    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1304    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1305    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1306    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1307    unsafe {
1308        let mut state = None;
1309        device.CreateBlendState(&desc, Some(&mut state))?;
1310        Ok(state.unwrap())
1311    }
1312}
1313
1314#[inline]
1315fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1316    unsafe {
1317        let mut shader = None;
1318        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1319        Ok(shader.unwrap())
1320    }
1321}
1322
1323#[inline]
1324fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1325    unsafe {
1326        let mut shader = None;
1327        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1328        Ok(shader.unwrap())
1329    }
1330}
1331
1332#[inline]
1333fn create_buffer(
1334    device: &ID3D11Device,
1335    element_size: usize,
1336    buffer_size: usize,
1337) -> Result<ID3D11Buffer> {
1338    let desc = D3D11_BUFFER_DESC {
1339        ByteWidth: (element_size * buffer_size) as u32,
1340        Usage: D3D11_USAGE_DYNAMIC,
1341        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1342        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1343        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1344        StructureByteStride: element_size as u32,
1345    };
1346    let mut buffer = None;
1347    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1348    Ok(buffer.unwrap())
1349}
1350
1351#[inline]
1352fn create_buffer_view(
1353    device: &ID3D11Device,
1354    buffer: &ID3D11Buffer,
1355) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1356    let mut view = None;
1357    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1358    Ok([view])
1359}
1360
1361#[inline]
1362fn update_buffer<T>(
1363    device_context: &ID3D11DeviceContext,
1364    buffer: &ID3D11Buffer,
1365    data: &[T],
1366) -> Result<()> {
1367    unsafe {
1368        let mut dest = std::mem::zeroed();
1369        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1370        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1371        device_context.Unmap(buffer, 0);
1372    }
1373    Ok(())
1374}
1375
1376#[inline]
1377fn set_pipeline_state(
1378    device_context: &ID3D11DeviceContext,
1379    buffer_view: &[Option<ID3D11ShaderResourceView>],
1380    topology: D3D_PRIMITIVE_TOPOLOGY,
1381    viewport: &[D3D11_VIEWPORT],
1382    vertex_shader: &ID3D11VertexShader,
1383    fragment_shader: &ID3D11PixelShader,
1384    global_params: &[Option<ID3D11Buffer>],
1385    blend_state: &ID3D11BlendState,
1386) {
1387    unsafe {
1388        device_context.VSSetShaderResources(1, Some(buffer_view));
1389        device_context.PSSetShaderResources(1, Some(buffer_view));
1390        device_context.IASetPrimitiveTopology(topology);
1391        device_context.RSSetViewports(Some(viewport));
1392        device_context.VSSetShader(vertex_shader, None);
1393        device_context.PSSetShader(fragment_shader, None);
1394        device_context.VSSetConstantBuffers(0, Some(global_params));
1395        device_context.PSSetConstantBuffers(0, Some(global_params));
1396        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1397    }
1398}
1399
1400#[cfg(debug_assertions)]
1401fn report_live_objects(device: &ID3D11Device) -> Result<()> {
1402    let debug_device: ID3D11Debug = device.cast()?;
1403    unsafe {
1404        debug_device.ReportLiveDeviceObjects(D3D11_RLDO_DETAIL)?;
1405    }
1406    Ok(())
1407}
1408
1409const BUFFER_COUNT: usize = 3;
1410
1411mod shader_resources {
1412    use anyhow::Result;
1413
1414    #[cfg(debug_assertions)]
1415    use windows::{
1416        Win32::Graphics::Direct3D::{
1417            Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1418            ID3DBlob,
1419        },
1420        core::{HSTRING, PCSTR},
1421    };
1422
1423    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1424    pub(super) enum ShaderModule {
1425        Quad,
1426        Shadow,
1427        Underline,
1428        PathRasterization,
1429        PathSprite,
1430        MonochromeSprite,
1431        PolychromeSprite,
1432    }
1433
1434    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1435    pub(super) enum ShaderTarget {
1436        Vertex,
1437        Fragment,
1438    }
1439
1440    pub(super) struct RawShaderBytes<'t> {
1441        inner: &'t [u8],
1442
1443        #[cfg(debug_assertions)]
1444        _blob: ID3DBlob,
1445    }
1446
1447    impl<'t> RawShaderBytes<'t> {
1448        pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result<Self> {
1449            #[cfg(not(debug_assertions))]
1450            {
1451                Ok(Self::from_bytes(module, target))
1452            }
1453            #[cfg(debug_assertions)]
1454            {
1455                let blob = build_shader_blob(module, target)?;
1456                let inner = unsafe {
1457                    std::slice::from_raw_parts(
1458                        blob.GetBufferPointer() as *const u8,
1459                        blob.GetBufferSize(),
1460                    )
1461                };
1462                Ok(Self { inner, _blob: blob })
1463            }
1464        }
1465
1466        pub(super) fn as_bytes(&'t self) -> &'t [u8] {
1467            self.inner
1468        }
1469
1470        #[cfg(not(debug_assertions))]
1471        fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self {
1472            let bytes = match module {
1473                ShaderModule::Quad => match target {
1474                    ShaderTarget::Vertex => QUAD_VERTEX_BYTES,
1475                    ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES,
1476                },
1477                ShaderModule::Shadow => match target {
1478                    ShaderTarget::Vertex => SHADOW_VERTEX_BYTES,
1479                    ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES,
1480                },
1481                ShaderModule::Underline => match target {
1482                    ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES,
1483                    ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES,
1484                },
1485                ShaderModule::PathRasterization => match target {
1486                    ShaderTarget::Vertex => PATH_RASTERIZATION_VERTEX_BYTES,
1487                    ShaderTarget::Fragment => PATH_RASTERIZATION_FRAGMENT_BYTES,
1488                },
1489                ShaderModule::PathSprite => match target {
1490                    ShaderTarget::Vertex => PATH_SPRITE_VERTEX_BYTES,
1491                    ShaderTarget::Fragment => PATH_SPRITE_FRAGMENT_BYTES,
1492                },
1493                ShaderModule::MonochromeSprite => match target {
1494                    ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES,
1495                    ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES,
1496                },
1497                ShaderModule::PolychromeSprite => match target {
1498                    ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES,
1499                    ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES,
1500                },
1501            };
1502            Self { inner: bytes }
1503        }
1504    }
1505
1506    #[cfg(debug_assertions)]
1507    pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> {
1508        unsafe {
1509            let entry = format!(
1510                "{}_{}\0",
1511                entry.as_str(),
1512                match target {
1513                    ShaderTarget::Vertex => "vertex",
1514                    ShaderTarget::Fragment => "fragment",
1515                }
1516            );
1517            let target = match target {
1518                ShaderTarget::Vertex => "vs_4_1\0",
1519                ShaderTarget::Fragment => "ps_4_1\0",
1520            };
1521
1522            let mut compile_blob = None;
1523            let mut error_blob = None;
1524            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1525                .join("src/platform/windows/shaders.hlsl")
1526                .canonicalize()?;
1527
1528            let entry_point = PCSTR::from_raw(entry.as_ptr());
1529            let target_cstr = PCSTR::from_raw(target.as_ptr());
1530
1531            let ret = D3DCompileFromFile(
1532                &HSTRING::from(shader_path.to_str().unwrap()),
1533                None,
1534                None,
1535                entry_point,
1536                target_cstr,
1537                D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
1538                0,
1539                &mut compile_blob,
1540                Some(&mut error_blob),
1541            );
1542            if ret.is_err() {
1543                let Some(error_blob) = error_blob else {
1544                    return Err(anyhow::anyhow!("{ret:?}"));
1545                };
1546
1547                let error_string =
1548                    std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8)
1549                        .to_string_lossy();
1550                log::error!("Shader compile error: {}", error_string);
1551                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1552            }
1553            Ok(compile_blob.unwrap())
1554        }
1555    }
1556
1557    #[cfg(not(debug_assertions))]
1558    include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs"));
1559
1560    #[cfg(debug_assertions)]
1561    impl ShaderModule {
1562        pub fn as_str(&self) -> &str {
1563            match self {
1564                ShaderModule::Quad => "quad",
1565                ShaderModule::Shadow => "shadow",
1566                ShaderModule::Underline => "underline",
1567                ShaderModule::PathRasterization => "path_rasterization",
1568                ShaderModule::PathSprite => "path_sprite",
1569                ShaderModule::MonochromeSprite => "monochrome_sprite",
1570                ShaderModule::PolychromeSprite => "polychrome_sprite",
1571            }
1572        }
1573    }
1574}
1575
1576mod nvidia {
1577    use std::{
1578        ffi::CStr,
1579        os::raw::{c_char, c_int, c_uint},
1580    };
1581
1582    use anyhow::{Context, Result};
1583    use windows::{
1584        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1585        core::s,
1586    };
1587
1588    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1589    const NVAPI_SHORT_STRING_MAX: usize = 64;
1590
1591    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1592    #[allow(non_camel_case_types)]
1593    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1594
1595    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
1596    #[allow(non_camel_case_types)]
1597    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
1598        driver_version: *mut c_uint,
1599        build_branch_string: *mut NvAPI_ShortString,
1600    ) -> c_int;
1601
1602    pub(super) fn get_driver_version() -> Result<String> {
1603        unsafe {
1604            // Try to load the NVIDIA driver DLL
1605            #[cfg(target_pointer_width = "64")]
1606            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
1607            #[cfg(target_pointer_width = "32")]
1608            let nvidia_dll = LoadLibraryA(s!("nvapi.dll")).context("Can't load nvapi.dll")?;
1609
1610            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
1611                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
1612            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
1613
1614            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1615            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1616            if nvapi_get_driver_version_ptr.is_null() {
1617                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1618            }
1619            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1620                std::mem::transmute(nvapi_get_driver_version_ptr);
1621
1622            let mut driver_version: c_uint = 0;
1623            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1624            let result = nvapi_get_driver_version(
1625                &mut driver_version as *mut c_uint,
1626                &mut build_branch_string as *mut NvAPI_ShortString,
1627            );
1628
1629            if result != 0 {
1630                anyhow::bail!(
1631                    "Failed to get NVIDIA driver version, error code: {}",
1632                    result
1633                );
1634            }
1635            let major = driver_version / 100;
1636            let minor = driver_version % 100;
1637            let branch_string = CStr::from_ptr(build_branch_string.as_ptr());
1638            Ok(format!(
1639                "{}.{} {}",
1640                major,
1641                minor,
1642                branch_string.to_string_lossy()
1643            ))
1644        }
1645    }
1646}
1647
1648mod amd {
1649    use std::os::raw::{c_char, c_int, c_void};
1650
1651    use anyhow::{Context, Result};
1652    use windows::{
1653        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1654        core::s,
1655    };
1656
1657    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145
1658    const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12);
1659
1660    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204
1661    // This is an opaque type, using struct to represent it properly for FFI
1662    #[repr(C)]
1663    struct AGSContext {
1664        _private: [u8; 0],
1665    }
1666
1667    #[repr(C)]
1668    pub struct AGSGPUInfo {
1669        pub driver_version: *const c_char,
1670        pub radeon_software_version: *const c_char,
1671        pub num_devices: c_int,
1672        pub devices: *mut c_void,
1673    }
1674
1675    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L429
1676    #[allow(non_camel_case_types)]
1677    type agsInitialize_t = unsafe extern "C" fn(
1678        version: c_int,
1679        config: *const c_void,
1680        context: *mut *mut AGSContext,
1681        gpu_info: *mut AGSGPUInfo,
1682    ) -> c_int;
1683
1684    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L436
1685    #[allow(non_camel_case_types)]
1686    type agsDeInitialize_t = unsafe extern "C" fn(context: *mut AGSContext) -> c_int;
1687
1688    pub(super) fn get_driver_version() -> Result<String> {
1689        unsafe {
1690            #[cfg(target_pointer_width = "64")]
1691            let amd_dll =
1692                LoadLibraryA(s!("amd_ags_x64.dll")).context("Failed to load AMD AGS library")?;
1693            #[cfg(target_pointer_width = "32")]
1694            let amd_dll =
1695                LoadLibraryA(s!("amd_ags_x86.dll")).context("Failed to load AMD AGS library")?;
1696
1697            let ags_initialize_addr = GetProcAddress(amd_dll, s!("agsInitialize"))
1698                .ok_or_else(|| anyhow::anyhow!("Failed to get agsInitialize address"))?;
1699            let ags_deinitialize_addr = GetProcAddress(amd_dll, s!("agsDeInitialize"))
1700                .ok_or_else(|| anyhow::anyhow!("Failed to get agsDeInitialize address"))?;
1701
1702            let ags_initialize: agsInitialize_t = std::mem::transmute(ags_initialize_addr);
1703            let ags_deinitialize: agsDeInitialize_t = std::mem::transmute(ags_deinitialize_addr);
1704
1705            let mut context: *mut AGSContext = std::ptr::null_mut();
1706            let mut gpu_info: AGSGPUInfo = AGSGPUInfo {
1707                driver_version: std::ptr::null(),
1708                radeon_software_version: std::ptr::null(),
1709                num_devices: 0,
1710                devices: std::ptr::null_mut(),
1711            };
1712
1713            let result = ags_initialize(
1714                AGS_CURRENT_VERSION,
1715                std::ptr::null(),
1716                &mut context,
1717                &mut gpu_info,
1718            );
1719            if result != 0 {
1720                anyhow::bail!("Failed to initialize AMD AGS, error code: {}", result);
1721            }
1722
1723            // Vulkan acctually returns this as the driver version
1724            let software_version = if !gpu_info.radeon_software_version.is_null() {
1725                std::ffi::CStr::from_ptr(gpu_info.radeon_software_version)
1726                    .to_string_lossy()
1727                    .into_owned()
1728            } else {
1729                "Unknown Radeon Software Version".to_string()
1730            };
1731
1732            let driver_version = if !gpu_info.driver_version.is_null() {
1733                std::ffi::CStr::from_ptr(gpu_info.driver_version)
1734                    .to_string_lossy()
1735                    .into_owned()
1736            } else {
1737                "Unknown Radeon Driver Version".to_string()
1738            };
1739
1740            ags_deinitialize(context);
1741            Ok(format!("{} ({})", software_version, driver_version))
1742        }
1743    }
1744}
1745
1746mod dxgi {
1747    use windows::{
1748        Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice},
1749        core::Interface,
1750    };
1751
1752    pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result<String> {
1753        let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?;
1754        Ok(format!(
1755            "{}.{}.{}.{}",
1756            number >> 48,
1757            (number >> 32) & 0xFFFF,
1758            (number >> 16) & 0xFFFF,
1759            number & 0xFFFF
1760        ))
1761    }
1762}