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