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