directx_renderer.rs

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