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
  18const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
  19// This configuration is used for MSAA rendering, and it's guaranteed to be supported by DirectX 11.
  20const MULTISAMPLE_COUNT: u32 = 4;
  21
  22pub(crate) struct DirectXRenderer {
  23    atlas: Arc<DirectXAtlas>,
  24    devices: DirectXDevices,
  25    resources: DirectXResources,
  26    globals: DirectXGlobalElements,
  27    pipelines: DirectXRenderPipelines,
  28    #[cfg(not(feature = "enable-renderdoc"))]
  29    _direct_composition: DirectComposition,
  30}
  31
  32/// Direct3D objects
  33#[derive(Clone)]
  34pub(crate) struct DirectXDevices {
  35    adapter: IDXGIAdapter1,
  36    dxgi_factory: IDXGIFactory6,
  37    #[cfg(not(feature = "enable-renderdoc"))]
  38    dxgi_device: IDXGIDevice,
  39    device: ID3D11Device,
  40    device_context: ID3D11DeviceContext,
  41}
  42
  43struct DirectXResources {
  44    // Direct3D rendering objects
  45    swap_chain: IDXGISwapChain1,
  46    render_target: ManuallyDrop<ID3D11Texture2D>,
  47    render_target_view: [Option<ID3D11RenderTargetView>; 1],
  48    msaa_target: ID3D11Texture2D,
  49    msaa_view: [Option<ID3D11RenderTargetView>; 1],
  50
  51    // Cached window size and viewport
  52    width: u32,
  53    height: u32,
  54    viewport: [D3D11_VIEWPORT; 1],
  55}
  56
  57struct DirectXRenderPipelines {
  58    shadow_pipeline: PipelineState<Shadow>,
  59    quad_pipeline: PipelineState<Quad>,
  60    paths_pipeline: PathsPipelineState,
  61    underline_pipeline: PipelineState<Underline>,
  62    mono_sprites: PipelineState<MonochromeSprite>,
  63    poly_sprites: PipelineState<PolychromeSprite>,
  64}
  65
  66struct DirectXGlobalElements {
  67    global_params_buffer: [Option<ID3D11Buffer>; 1],
  68    sampler: [Option<ID3D11SamplerState>; 1],
  69    blend_state: ID3D11BlendState,
  70}
  71
  72#[repr(C)]
  73struct DrawInstancedIndirectArgs {
  74    vertex_count_per_instance: u32,
  75    instance_count: u32,
  76    start_vertex_location: u32,
  77    start_instance_location: u32,
  78}
  79
  80#[cfg(not(feature = "enable-renderdoc"))]
  81struct DirectComposition {
  82    comp_device: IDCompositionDevice,
  83    comp_target: IDCompositionTarget,
  84    comp_visual: IDCompositionVisual,
  85}
  86
  87impl DirectXDevices {
  88    pub(crate) fn new() -> Result<Self> {
  89        let dxgi_factory = get_dxgi_factory()?;
  90        let adapter = get_adapter(&dxgi_factory)?;
  91        let (device, device_context) = {
  92            let mut device: Option<ID3D11Device> = None;
  93            let mut context: Option<ID3D11DeviceContext> = None;
  94            get_device(&adapter, Some(&mut device), Some(&mut context))?;
  95            (device.unwrap(), context.unwrap())
  96        };
  97        #[cfg(not(feature = "enable-renderdoc"))]
  98        let dxgi_device: IDXGIDevice = device.cast()?;
  99
 100        Ok(Self {
 101            adapter,
 102            dxgi_factory,
 103            #[cfg(not(feature = "enable-renderdoc"))]
 104            dxgi_device,
 105            device,
 106            device_context,
 107        })
 108    }
 109}
 110
 111impl DirectXRenderer {
 112    pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND) -> Result<Self> {
 113        let atlas = Arc::new(DirectXAtlas::new(
 114            devices.device.clone(),
 115            devices.device_context.clone(),
 116        ));
 117
 118        #[cfg(not(feature = "enable-renderdoc"))]
 119        let resources = DirectXResources::new(devices)?;
 120        #[cfg(feature = "enable-renderdoc")]
 121        let resources = DirectXResources::new(devices, hwnd)?;
 122
 123        let globals = DirectXGlobalElements::new(&devices.device)?;
 124        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
 125
 126        #[cfg(not(feature = "enable-renderdoc"))]
 127        let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
 128        #[cfg(not(feature = "enable-renderdoc"))]
 129        direct_composition.set_swap_chain(&resources.swap_chain)?;
 130
 131        Ok(DirectXRenderer {
 132            atlas,
 133            devices: devices.clone(),
 134            resources,
 135            globals,
 136            pipelines,
 137            #[cfg(not(feature = "enable-renderdoc"))]
 138            _direct_composition: direct_composition,
 139        })
 140    }
 141
 142    pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
 143        self.atlas.clone()
 144    }
 145
 146    fn pre_draw(&self) -> Result<()> {
 147        update_buffer(
 148            &self.devices.device_context,
 149            self.globals.global_params_buffer[0].as_ref().unwrap(),
 150            &[GlobalParams {
 151                viewport_size: [
 152                    self.resources.viewport[0].Width,
 153                    self.resources.viewport[0].Height,
 154                ],
 155                ..Default::default()
 156            }],
 157        )?;
 158        unsafe {
 159            self.devices
 160                .device_context
 161                .ClearRenderTargetView(self.resources.msaa_view[0].as_ref().unwrap(), &[0.0; 4]);
 162            self.devices
 163                .device_context
 164                .OMSetRenderTargets(Some(&self.resources.msaa_view), None);
 165            self.devices
 166                .device_context
 167                .RSSetViewports(Some(&self.resources.viewport));
 168            self.devices.device_context.OMSetBlendState(
 169                &self.globals.blend_state,
 170                None,
 171                0xFFFFFFFF,
 172            );
 173        }
 174        Ok(())
 175    }
 176
 177    fn present(&self) -> Result<()> {
 178        unsafe {
 179            self.devices.device_context.ResolveSubresource(
 180                &*self.resources.render_target,
 181                0,
 182                &self.resources.msaa_target,
 183                0,
 184                RENDER_TARGET_FORMAT,
 185            );
 186            self.devices
 187                .device_context
 188                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
 189            self.resources.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
 190        }
 191        Ok(())
 192    }
 193
 194    pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
 195        self.pre_draw()?;
 196        for batch in scene.batches() {
 197            match batch {
 198                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
 199                PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
 200                PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
 201                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
 202                PrimitiveBatch::MonochromeSprites {
 203                    texture_id,
 204                    sprites,
 205                } => self.draw_monochrome_sprites(texture_id, sprites),
 206                PrimitiveBatch::PolychromeSprites {
 207                    texture_id,
 208                    sprites,
 209                } => self.draw_polychrome_sprites(texture_id, sprites),
 210                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
 211            }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 212                    scene.paths.len(),
 213                    scene.shadows.len(),
 214                    scene.quads.len(),
 215                    scene.underlines.len(),
 216                    scene.monochrome_sprites.len(),
 217                    scene.polychrome_sprites.len(),
 218                    scene.surfaces.len(),))?;
 219        }
 220        self.present()
 221    }
 222
 223    pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
 224        let width = new_size.width.0.max(1) as u32;
 225        let height = new_size.height.0.max(1) as u32;
 226        if self.resources.width == width && self.resources.height == height {
 227            return Ok(());
 228        }
 229        unsafe {
 230            // Clear the render target before resizing
 231            self.devices.device_context.OMSetRenderTargets(None, None);
 232            ManuallyDrop::drop(&mut self.resources.render_target);
 233            drop(self.resources.render_target_view[0].take().unwrap());
 234
 235            self.resources.swap_chain.ResizeBuffers(
 236                BUFFER_COUNT as u32,
 237                width,
 238                height,
 239                RENDER_TARGET_FORMAT,
 240                DXGI_SWAP_CHAIN_FLAG(0),
 241            )?;
 242
 243            self.resources
 244                .recreate_resources(&self.devices, width, height)?;
 245            self.devices
 246                .device_context
 247                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
 248        }
 249        Ok(())
 250    }
 251
 252    fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
 253        if shadows.is_empty() {
 254            return Ok(());
 255        }
 256        self.pipelines.shadow_pipeline.update_buffer(
 257            &self.devices.device,
 258            &self.devices.device_context,
 259            shadows,
 260        )?;
 261        self.pipelines.shadow_pipeline.draw(
 262            &self.devices.device_context,
 263            &self.resources.viewport,
 264            &self.globals.global_params_buffer,
 265            shadows.len() as u32,
 266        )
 267    }
 268
 269    fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
 270        if quads.is_empty() {
 271            return Ok(());
 272        }
 273        self.pipelines.quad_pipeline.update_buffer(
 274            &self.devices.device,
 275            &self.devices.device_context,
 276            quads,
 277        )?;
 278        self.pipelines.quad_pipeline.draw(
 279            &self.devices.device_context,
 280            &self.resources.viewport,
 281            &self.globals.global_params_buffer,
 282            quads.len() as u32,
 283        )
 284    }
 285
 286    fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
 287        if paths.is_empty() {
 288            return Ok(());
 289        }
 290        let mut vertices = Vec::new();
 291        let mut sprites = Vec::with_capacity(paths.len());
 292        let mut draw_indirect_commands = Vec::with_capacity(paths.len());
 293        let mut start_vertex_location = 0;
 294        for (i, path) in paths.iter().enumerate() {
 295            draw_indirect_commands.push(DrawInstancedIndirectArgs {
 296                vertex_count_per_instance: path.vertices.len() as u32,
 297                instance_count: 1,
 298                start_vertex_location,
 299                start_instance_location: i as u32,
 300            });
 301            start_vertex_location += path.vertices.len() as u32;
 302
 303            vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex {
 304                xy_position: v.xy_position,
 305                content_mask: path.content_mask.bounds,
 306                sprite_index: i as u32,
 307            }));
 308
 309            sprites.push(PathSprite {
 310                bounds: path.bounds,
 311                color: path.color,
 312            });
 313        }
 314
 315        self.pipelines.paths_pipeline.update_buffer(
 316            &self.devices.device,
 317            &self.devices.device_context,
 318            &sprites,
 319            &vertices,
 320            &draw_indirect_commands,
 321        )?;
 322        self.pipelines.paths_pipeline.draw(
 323            &self.devices.device_context,
 324            paths.len(),
 325            &self.resources.viewport,
 326            &self.globals.global_params_buffer,
 327        )
 328    }
 329
 330    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
 331        if underlines.is_empty() {
 332            return Ok(());
 333        }
 334        self.pipelines.underline_pipeline.update_buffer(
 335            &self.devices.device,
 336            &self.devices.device_context,
 337            underlines,
 338        )?;
 339        self.pipelines.underline_pipeline.draw(
 340            &self.devices.device_context,
 341            &self.resources.viewport,
 342            &self.globals.global_params_buffer,
 343            underlines.len() as u32,
 344        )
 345    }
 346
 347    fn draw_monochrome_sprites(
 348        &mut self,
 349        texture_id: AtlasTextureId,
 350        sprites: &[MonochromeSprite],
 351    ) -> Result<()> {
 352        if sprites.is_empty() {
 353            return Ok(());
 354        }
 355        self.pipelines.mono_sprites.update_buffer(
 356            &self.devices.device,
 357            &self.devices.device_context,
 358            sprites,
 359        )?;
 360        let texture_view = self.atlas.get_texture_view(texture_id);
 361        self.pipelines.mono_sprites.draw_with_texture(
 362            &self.devices.device_context,
 363            &texture_view,
 364            &self.resources.viewport,
 365            &self.globals.global_params_buffer,
 366            &self.globals.sampler,
 367            sprites.len() as u32,
 368        )
 369    }
 370
 371    fn draw_polychrome_sprites(
 372        &mut self,
 373        texture_id: AtlasTextureId,
 374        sprites: &[PolychromeSprite],
 375    ) -> Result<()> {
 376        if sprites.is_empty() {
 377            return Ok(());
 378        }
 379        self.pipelines.poly_sprites.update_buffer(
 380            &self.devices.device,
 381            &self.devices.device_context,
 382            sprites,
 383        )?;
 384        let texture_view = self.atlas.get_texture_view(texture_id);
 385        self.pipelines.poly_sprites.draw_with_texture(
 386            &self.devices.device_context,
 387            &texture_view,
 388            &self.resources.viewport,
 389            &self.globals.global_params_buffer,
 390            &self.globals.sampler,
 391            sprites.len() as u32,
 392        )
 393    }
 394
 395    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
 396        if surfaces.is_empty() {
 397            return Ok(());
 398        }
 399        Ok(())
 400    }
 401
 402    pub(crate) fn gpu_specs(&self) -> Result<GpuSpecs> {
 403        let desc = unsafe { self.devices.adapter.GetDesc1() }?;
 404        let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0;
 405        let device_name = String::from_utf16_lossy(&desc.Description)
 406            .trim_matches(char::from(0))
 407            .to_string();
 408        let driver_name = match desc.VendorId {
 409            0x10DE => "NVIDIA Corporation".to_string(),
 410            0x1002 => "AMD Corporation".to_string(),
 411            0x8086 => "Intel Corporation".to_string(),
 412            _ => "Unknown Vendor".to_string(),
 413        };
 414        let driver_version = match desc.VendorId {
 415            0x10DE => nvidia::get_driver_version().context("Failed to get NVIDIA driver info"),
 416            0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")),
 417            0x8086 => Err(anyhow::anyhow!("Intel driver info not implemented yet")),
 418            _ => Err(anyhow::anyhow!("Unknown vendor detected.")),
 419        }
 420        .log_err()
 421        .unwrap_or("Unknown Driver".to_string());
 422        Ok(GpuSpecs {
 423            is_software_emulated,
 424            device_name,
 425            driver_name,
 426            driver_info: driver_version,
 427        })
 428    }
 429}
 430
 431impl DirectXResources {
 432    pub fn new(
 433        devices: &DirectXDevices,
 434        #[cfg(feature = "enable-renderdoc")] hwnd: HWND,
 435    ) -> Result<Self> {
 436        let width = 1;
 437        let height = 1;
 438
 439        #[cfg(not(feature = "enable-renderdoc"))]
 440        let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, width, height)?;
 441        #[cfg(feature = "enable-renderdoc")]
 442        let swap_chain =
 443            create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?;
 444
 445        let (render_target, render_target_view, msaa_target, msaa_view, viewport) =
 446            create_resources(devices, &swap_chain, width, height)?;
 447        set_rasterizer_state(&devices.device, &devices.device_context)?;
 448
 449        Ok(Self {
 450            swap_chain,
 451            render_target,
 452            render_target_view,
 453            msaa_target,
 454            msaa_view,
 455            width,
 456            height,
 457            viewport,
 458        })
 459    }
 460
 461    #[inline]
 462    fn recreate_resources(
 463        &mut self,
 464        devices: &DirectXDevices,
 465        width: u32,
 466        height: u32,
 467    ) -> Result<()> {
 468        let (render_target, render_target_view, msaa_target, msaa_view, viewport) =
 469            create_resources(devices, &self.swap_chain, width, height)?;
 470        self.render_target = render_target;
 471        self.render_target_view = render_target_view;
 472        self.msaa_target = msaa_target;
 473        self.msaa_view = msaa_view;
 474        self.viewport = viewport;
 475        self.width = width;
 476        self.height = height;
 477        Ok(())
 478    }
 479}
 480
 481impl DirectXRenderPipelines {
 482    pub fn new(device: &ID3D11Device) -> Result<Self> {
 483        let shadow_pipeline = PipelineState::new(
 484            device,
 485            "shadow_pipeline",
 486            "shadow_vertex",
 487            "shadow_fragment",
 488            4,
 489        )?;
 490        let quad_pipeline =
 491            PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?;
 492        let paths_pipeline = PathsPipelineState::new(device)?;
 493        let underline_pipeline = PipelineState::new(
 494            device,
 495            "underline_pipeline",
 496            "underline_vertex",
 497            "underline_fragment",
 498            4,
 499        )?;
 500        let mono_sprites = PipelineState::new(
 501            device,
 502            "monochrome_sprite_pipeline",
 503            "monochrome_sprite_vertex",
 504            "monochrome_sprite_fragment",
 505            512,
 506        )?;
 507        let poly_sprites = PipelineState::new(
 508            device,
 509            "polychrome_sprite_pipeline",
 510            "polychrome_sprite_vertex",
 511            "polychrome_sprite_fragment",
 512            16,
 513        )?;
 514
 515        Ok(Self {
 516            shadow_pipeline,
 517            quad_pipeline,
 518            paths_pipeline,
 519            underline_pipeline,
 520            mono_sprites,
 521            poly_sprites,
 522        })
 523    }
 524}
 525
 526#[cfg(not(feature = "enable-renderdoc"))]
 527impl DirectComposition {
 528    pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
 529        let comp_device = get_comp_device(&dxgi_device)?;
 530        let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
 531        let comp_visual = unsafe { comp_device.CreateVisual() }?;
 532
 533        Ok(Self {
 534            comp_device,
 535            comp_target,
 536            comp_visual,
 537        })
 538    }
 539
 540    pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
 541        unsafe {
 542            self.comp_visual.SetContent(swap_chain)?;
 543            self.comp_target.SetRoot(&self.comp_visual)?;
 544            self.comp_device.Commit()?;
 545        }
 546        Ok(())
 547    }
 548}
 549
 550impl DirectXGlobalElements {
 551    pub fn new(device: &ID3D11Device) -> Result<Self> {
 552        let global_params_buffer = unsafe {
 553            let desc = D3D11_BUFFER_DESC {
 554                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
 555                Usage: D3D11_USAGE_DYNAMIC,
 556                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
 557                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 558                ..Default::default()
 559            };
 560            let mut buffer = None;
 561            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
 562            [buffer]
 563        };
 564
 565        let sampler = unsafe {
 566            let desc = D3D11_SAMPLER_DESC {
 567                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
 568                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
 569                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
 570                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
 571                MipLODBias: 0.0,
 572                MaxAnisotropy: 1,
 573                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
 574                BorderColor: [0.0; 4],
 575                MinLOD: 0.0,
 576                MaxLOD: D3D11_FLOAT32_MAX,
 577            };
 578            let mut output = None;
 579            device.CreateSamplerState(&desc, Some(&mut output))?;
 580            [output]
 581        };
 582
 583        let blend_state = create_blend_state(device)?;
 584
 585        Ok(Self {
 586            global_params_buffer,
 587            sampler,
 588            blend_state,
 589        })
 590    }
 591}
 592
 593#[derive(Debug, Default)]
 594#[repr(C)]
 595struct GlobalParams {
 596    viewport_size: [f32; 2],
 597    _pad: u64,
 598}
 599
 600struct PipelineState<T> {
 601    label: &'static str,
 602    vertex: ID3D11VertexShader,
 603    fragment: ID3D11PixelShader,
 604    buffer: ID3D11Buffer,
 605    buffer_size: usize,
 606    view: [Option<ID3D11ShaderResourceView>; 1],
 607    _marker: std::marker::PhantomData<T>,
 608}
 609
 610struct PathsPipelineState {
 611    vertex: ID3D11VertexShader,
 612    fragment: ID3D11PixelShader,
 613    buffer: ID3D11Buffer,
 614    buffer_size: usize,
 615    vertex_buffer: Option<ID3D11Buffer>,
 616    vertex_buffer_size: usize,
 617    indirect_draw_buffer: ID3D11Buffer,
 618    indirect_buffer_size: usize,
 619    input_layout: ID3D11InputLayout,
 620    view: [Option<ID3D11ShaderResourceView>; 1],
 621}
 622
 623impl<T> PipelineState<T> {
 624    fn new(
 625        device: &ID3D11Device,
 626        label: &'static str,
 627        vertex_entry: &str,
 628        fragment_entry: &str,
 629        buffer_size: usize,
 630    ) -> Result<Self> {
 631        let vertex = {
 632            let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 633            let bytes = unsafe {
 634                std::slice::from_raw_parts(
 635                    shader_blob.GetBufferPointer() as *mut u8,
 636                    shader_blob.GetBufferSize(),
 637                )
 638            };
 639            create_vertex_shader(device, bytes)?
 640        };
 641        let fragment = {
 642            let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 643            let bytes = unsafe {
 644                std::slice::from_raw_parts(
 645                    shader_blob.GetBufferPointer() as *mut u8,
 646                    shader_blob.GetBufferSize(),
 647                )
 648            };
 649            create_fragment_shader(device, bytes)?
 650        };
 651        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
 652        let view = create_buffer_view(device, &buffer)?;
 653
 654        Ok(PipelineState {
 655            label,
 656            vertex,
 657            fragment,
 658            buffer,
 659            buffer_size,
 660            view,
 661            _marker: std::marker::PhantomData,
 662        })
 663    }
 664
 665    fn update_buffer(
 666        &mut self,
 667        device: &ID3D11Device,
 668        device_context: &ID3D11DeviceContext,
 669        data: &[T],
 670    ) -> Result<()> {
 671        if self.buffer_size < data.len() {
 672            let new_buffer_size = data.len().next_power_of_two();
 673            log::info!(
 674                "Updating {} buffer size from {} to {}",
 675                self.label,
 676                self.buffer_size,
 677                new_buffer_size
 678            );
 679            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
 680            let view = create_buffer_view(device, &buffer)?;
 681            self.buffer = buffer;
 682            self.view = view;
 683            self.buffer_size = new_buffer_size;
 684        }
 685        update_buffer(device_context, &self.buffer, data)
 686    }
 687
 688    fn draw(
 689        &self,
 690        device_context: &ID3D11DeviceContext,
 691        viewport: &[D3D11_VIEWPORT],
 692        global_params: &[Option<ID3D11Buffer>],
 693        instance_count: u32,
 694    ) -> Result<()> {
 695        set_pipeline_state(
 696            device_context,
 697            &self.view,
 698            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 699            viewport,
 700            &self.vertex,
 701            &self.fragment,
 702            global_params,
 703        );
 704        unsafe {
 705            device_context.DrawInstanced(4, instance_count, 0, 0);
 706        }
 707        Ok(())
 708    }
 709
 710    fn draw_with_texture(
 711        &self,
 712        device_context: &ID3D11DeviceContext,
 713        texture: &[Option<ID3D11ShaderResourceView>],
 714        viewport: &[D3D11_VIEWPORT],
 715        global_params: &[Option<ID3D11Buffer>],
 716        sampler: &[Option<ID3D11SamplerState>],
 717        instance_count: u32,
 718    ) -> Result<()> {
 719        set_pipeline_state(
 720            device_context,
 721            &self.view,
 722            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 723            viewport,
 724            &self.vertex,
 725            &self.fragment,
 726            global_params,
 727        );
 728        unsafe {
 729            device_context.PSSetSamplers(0, Some(sampler));
 730            device_context.VSSetShaderResources(0, Some(texture));
 731            device_context.PSSetShaderResources(0, Some(texture));
 732
 733            device_context.DrawInstanced(4, instance_count, 0, 0);
 734        }
 735        Ok(())
 736    }
 737}
 738
 739impl PathsPipelineState {
 740    fn new(device: &ID3D11Device) -> Result<Self> {
 741        let (vertex, vertex_shader) = {
 742            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 743            let bytes = unsafe {
 744                std::slice::from_raw_parts(
 745                    shader_blob.GetBufferPointer() as *mut u8,
 746                    shader_blob.GetBufferSize(),
 747                )
 748            };
 749            (create_vertex_shader(device, bytes)?, shader_blob)
 750        };
 751        let fragment = {
 752            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 753            let bytes = unsafe {
 754                std::slice::from_raw_parts(
 755                    shader_blob.GetBufferPointer() as *mut u8,
 756                    shader_blob.GetBufferSize(),
 757                )
 758            };
 759            create_fragment_shader(device, bytes)?
 760        };
 761        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 762        let view = create_buffer_view(device, &buffer)?;
 763        let vertex_buffer = Some(create_buffer(
 764            device,
 765            std::mem::size_of::<DirectXPathVertex>(),
 766            32,
 767        )?);
 768        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 769        // Create input layout
 770        let input_layout = unsafe {
 771            let shader_bytes = std::slice::from_raw_parts(
 772                vertex_shader.GetBufferPointer() as *const u8,
 773                vertex_shader.GetBufferSize(),
 774            );
 775            let mut layout = None;
 776            device.CreateInputLayout(
 777                &[
 778                    D3D11_INPUT_ELEMENT_DESC {
 779                        SemanticName: windows::core::s!("POSITION"),
 780                        SemanticIndex: 0,
 781                        Format: DXGI_FORMAT_R32G32_FLOAT,
 782                        InputSlot: 0,
 783                        AlignedByteOffset: 0,
 784                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 785                        InstanceDataStepRate: 0,
 786                    },
 787                    D3D11_INPUT_ELEMENT_DESC {
 788                        SemanticName: windows::core::s!("TEXCOORD"),
 789                        SemanticIndex: 0,
 790                        Format: DXGI_FORMAT_R32G32_FLOAT,
 791                        InputSlot: 0,
 792                        AlignedByteOffset: 8,
 793                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 794                        InstanceDataStepRate: 0,
 795                    },
 796                    D3D11_INPUT_ELEMENT_DESC {
 797                        SemanticName: windows::core::s!("TEXCOORD"),
 798                        SemanticIndex: 1,
 799                        Format: DXGI_FORMAT_R32G32_FLOAT,
 800                        InputSlot: 0,
 801                        AlignedByteOffset: 16,
 802                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 803                        InstanceDataStepRate: 0,
 804                    },
 805                    D3D11_INPUT_ELEMENT_DESC {
 806                        SemanticName: windows::core::s!("GLOBALIDX"),
 807                        SemanticIndex: 0,
 808                        Format: DXGI_FORMAT_R32_UINT,
 809                        InputSlot: 0,
 810                        AlignedByteOffset: 24,
 811                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 812                        InstanceDataStepRate: 0,
 813                    },
 814                ],
 815                shader_bytes,
 816                Some(&mut layout),
 817            )?;
 818            layout.unwrap()
 819        };
 820
 821        Ok(Self {
 822            vertex,
 823            fragment,
 824            buffer,
 825            buffer_size: 32,
 826            vertex_buffer,
 827            vertex_buffer_size: 32,
 828            indirect_draw_buffer,
 829            indirect_buffer_size: 32,
 830            input_layout,
 831            view,
 832        })
 833    }
 834
 835    fn update_buffer(
 836        &mut self,
 837        device: &ID3D11Device,
 838        device_context: &ID3D11DeviceContext,
 839        buffer_data: &[PathSprite],
 840        vertices_data: &[DirectXPathVertex],
 841        draw_commands: &[DrawInstancedIndirectArgs],
 842    ) -> Result<()> {
 843        if self.buffer_size < buffer_data.len() {
 844            let new_buffer_size = buffer_data.len().next_power_of_two();
 845            log::info!(
 846                "Updating Paths Pipeline buffer size from {} to {}",
 847                self.buffer_size,
 848                new_buffer_size
 849            );
 850            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 851            let view = create_buffer_view(device, &buffer)?;
 852            self.buffer = buffer;
 853            self.view = view;
 854            self.buffer_size = new_buffer_size;
 855        }
 856        update_buffer(device_context, &self.buffer, buffer_data)?;
 857        if self.vertex_buffer_size < vertices_data.len() {
 858            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 859            log::info!(
 860                "Updating Paths Pipeline vertex buffer size from {} to {}",
 861                self.vertex_buffer_size,
 862                new_vertex_buffer_size
 863            );
 864            let vertex_buffer = create_buffer(
 865                device,
 866                std::mem::size_of::<DirectXPathVertex>(),
 867                new_vertex_buffer_size,
 868            )?;
 869            self.vertex_buffer = Some(vertex_buffer);
 870            self.vertex_buffer_size = new_vertex_buffer_size;
 871        }
 872        update_buffer(
 873            device_context,
 874            self.vertex_buffer.as_ref().unwrap(),
 875            vertices_data,
 876        )?;
 877        if self.indirect_buffer_size < draw_commands.len() {
 878            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 879            log::info!(
 880                "Updating Paths Pipeline indirect buffer size from {} to {}",
 881                self.indirect_buffer_size,
 882                new_indirect_buffer_size
 883            );
 884            let indirect_draw_buffer =
 885                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 886            self.indirect_draw_buffer = indirect_draw_buffer;
 887            self.indirect_buffer_size = new_indirect_buffer_size;
 888        }
 889        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 890        Ok(())
 891    }
 892
 893    fn draw(
 894        &self,
 895        device_context: &ID3D11DeviceContext,
 896        count: usize,
 897        viewport: &[D3D11_VIEWPORT],
 898        global_params: &[Option<ID3D11Buffer>],
 899    ) -> Result<()> {
 900        set_pipeline_state(
 901            device_context,
 902            &self.view,
 903            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 904            viewport,
 905            &self.vertex,
 906            &self.fragment,
 907            global_params,
 908        );
 909        unsafe {
 910            const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
 911            device_context.IASetVertexBuffers(
 912                0,
 913                1,
 914                Some(&self.vertex_buffer),
 915                Some(&STRIDE),
 916                Some(&0),
 917            );
 918            device_context.IASetInputLayout(&self.input_layout);
 919        }
 920        for i in 0..count {
 921            unsafe {
 922                device_context.DrawInstancedIndirect(
 923                    &self.indirect_draw_buffer,
 924                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 925                );
 926            }
 927        }
 928        Ok(())
 929    }
 930}
 931
 932#[repr(C)]
 933struct DirectXPathVertex {
 934    xy_position: Point<ScaledPixels>,
 935    content_mask: Bounds<ScaledPixels>,
 936    sprite_index: u32,
 937}
 938
 939#[derive(Clone, Debug, Eq, PartialEq)]
 940#[repr(C)]
 941struct PathSprite {
 942    bounds: Bounds<ScaledPixels>,
 943    color: Background,
 944}
 945
 946impl Drop for DirectXResources {
 947    fn drop(&mut self) {
 948        unsafe {
 949            ManuallyDrop::drop(&mut self.render_target);
 950        }
 951    }
 952}
 953
 954#[inline]
 955fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 956    #[cfg(debug_assertions)]
 957    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 958    #[cfg(not(debug_assertions))]
 959    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 960    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 961}
 962
 963fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 964    for adapter_index in 0.. {
 965        let adapter: IDXGIAdapter1 = unsafe {
 966            dxgi_factory
 967                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 968        }?;
 969        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
 970            let gpu_name = String::from_utf16_lossy(&desc.Description)
 971                .trim_matches(char::from(0))
 972                .to_string();
 973            log::info!("Using GPU: {}", gpu_name);
 974        }
 975        // Check to see whether the adapter supports Direct3D 11, but don't
 976        // create the actual device yet.
 977        if get_device(&adapter, None, None).log_err().is_some() {
 978            return Ok(adapter);
 979        }
 980    }
 981
 982    unreachable!()
 983}
 984
 985fn get_device(
 986    adapter: &IDXGIAdapter1,
 987    device: Option<*mut Option<ID3D11Device>>,
 988    context: Option<*mut Option<ID3D11DeviceContext>>,
 989) -> Result<()> {
 990    #[cfg(debug_assertions)]
 991    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
 992    #[cfg(not(debug_assertions))]
 993    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 994    Ok(unsafe {
 995        D3D11CreateDevice(
 996            adapter,
 997            D3D_DRIVER_TYPE_UNKNOWN,
 998            HMODULE::default(),
 999            device_flags,
1000            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
1001            // 8x MSAA is required for Direct3D Feature Level 11.0 or better
1002            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1003            D3D11_SDK_VERSION,
1004            device,
1005            None,
1006            context,
1007        )?
1008    })
1009}
1010
1011#[cfg(not(feature = "enable-renderdoc"))]
1012fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1013    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1014}
1015
1016#[cfg(not(feature = "enable-renderdoc"))]
1017fn create_swap_chain(
1018    dxgi_factory: &IDXGIFactory6,
1019    device: &ID3D11Device,
1020    width: u32,
1021    height: u32,
1022) -> Result<IDXGISwapChain1> {
1023    let desc = DXGI_SWAP_CHAIN_DESC1 {
1024        Width: width,
1025        Height: height,
1026        Format: RENDER_TARGET_FORMAT,
1027        Stereo: false.into(),
1028        SampleDesc: DXGI_SAMPLE_DESC {
1029            Count: 1,
1030            Quality: 0,
1031        },
1032        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1033        BufferCount: BUFFER_COUNT as u32,
1034        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1035        Scaling: DXGI_SCALING_STRETCH,
1036        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1037        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
1038        Flags: 0,
1039    };
1040    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1041}
1042
1043#[cfg(feature = "enable-renderdoc")]
1044fn create_swap_chain(
1045    dxgi_factory: &IDXGIFactory6,
1046    device: &ID3D11Device,
1047    hwnd: HWND,
1048    width: u32,
1049    height: u32,
1050) -> Result<IDXGISwapChain1> {
1051    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1052
1053    let desc = DXGI_SWAP_CHAIN_DESC1 {
1054        Width: width,
1055        Height: height,
1056        Format: RENDER_TARGET_FORMAT,
1057        Stereo: false.into(),
1058        SampleDesc: DXGI_SAMPLE_DESC {
1059            Count: 1,
1060            Quality: 0,
1061        },
1062        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1063        BufferCount: BUFFER_COUNT as u32,
1064        Scaling: DXGI_SCALING_STRETCH,
1065        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1066        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1067        Flags: 0,
1068    };
1069    let swap_chain =
1070        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1071    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1072    Ok(swap_chain)
1073}
1074
1075#[inline]
1076fn create_resources(
1077    devices: &DirectXDevices,
1078    swap_chain: &IDXGISwapChain1,
1079    width: u32,
1080    height: u32,
1081) -> Result<(
1082    ManuallyDrop<ID3D11Texture2D>,
1083    [Option<ID3D11RenderTargetView>; 1],
1084    ID3D11Texture2D,
1085    [Option<ID3D11RenderTargetView>; 1],
1086    [D3D11_VIEWPORT; 1],
1087)> {
1088    let (render_target, render_target_view) =
1089        create_render_target_and_its_view(&swap_chain, &devices.device)?;
1090    let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?;
1091    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
1092    Ok((
1093        render_target,
1094        render_target_view,
1095        msaa_target,
1096        msaa_view,
1097        viewport,
1098    ))
1099}
1100
1101#[inline]
1102fn create_render_target_and_its_view(
1103    swap_chain: &IDXGISwapChain1,
1104    device: &ID3D11Device,
1105) -> Result<(
1106    ManuallyDrop<ID3D11Texture2D>,
1107    [Option<ID3D11RenderTargetView>; 1],
1108)> {
1109    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1110    let mut render_target_view = None;
1111    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
1112    Ok((
1113        ManuallyDrop::new(render_target),
1114        [Some(render_target_view.unwrap())],
1115    ))
1116}
1117
1118#[inline]
1119fn create_msaa_target_and_its_view(
1120    device: &ID3D11Device,
1121    width: u32,
1122    height: u32,
1123) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
1124    let msaa_target = unsafe {
1125        let mut output = None;
1126        let desc = D3D11_TEXTURE2D_DESC {
1127            Width: width,
1128            Height: height,
1129            MipLevels: 1,
1130            ArraySize: 1,
1131            Format: RENDER_TARGET_FORMAT,
1132            SampleDesc: DXGI_SAMPLE_DESC {
1133                Count: MULTISAMPLE_COUNT,
1134                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1135            },
1136            Usage: D3D11_USAGE_DEFAULT,
1137            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1138            CPUAccessFlags: 0,
1139            MiscFlags: 0,
1140        };
1141        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1142        output.unwrap()
1143    };
1144    let msaa_view = unsafe {
1145        let mut output = None;
1146        device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?;
1147        output.unwrap()
1148    };
1149    Ok((msaa_target, [Some(msaa_view)]))
1150}
1151
1152#[inline]
1153fn set_viewport(
1154    device_context: &ID3D11DeviceContext,
1155    width: f32,
1156    height: f32,
1157) -> [D3D11_VIEWPORT; 1] {
1158    let viewport = [D3D11_VIEWPORT {
1159        TopLeftX: 0.0,
1160        TopLeftY: 0.0,
1161        Width: width,
1162        Height: height,
1163        MinDepth: 0.0,
1164        MaxDepth: 1.0,
1165    }];
1166    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1167    viewport
1168}
1169
1170#[inline]
1171fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1172    let desc = D3D11_RASTERIZER_DESC {
1173        FillMode: D3D11_FILL_SOLID,
1174        CullMode: D3D11_CULL_NONE,
1175        FrontCounterClockwise: false.into(),
1176        DepthBias: 0,
1177        DepthBiasClamp: 0.0,
1178        SlopeScaledDepthBias: 0.0,
1179        DepthClipEnable: true.into(),
1180        ScissorEnable: false.into(),
1181        // MultisampleEnable: false.into(),
1182        MultisampleEnable: true.into(),
1183        AntialiasedLineEnable: false.into(),
1184    };
1185    let rasterizer_state = unsafe {
1186        let mut state = None;
1187        device.CreateRasterizerState(&desc, Some(&mut state))?;
1188        state.unwrap()
1189    };
1190    unsafe { device_context.RSSetState(&rasterizer_state) };
1191    Ok(())
1192}
1193
1194// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1195#[inline]
1196fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1197    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1198    // device performs the blend in linear space, which is ideal.
1199    let mut desc = D3D11_BLEND_DESC::default();
1200    desc.RenderTarget[0].BlendEnable = true.into();
1201    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1202    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1203    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1204    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1205    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1206    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1207    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1208    unsafe {
1209        let mut state = None;
1210        device.CreateBlendState(&desc, Some(&mut state))?;
1211        Ok(state.unwrap())
1212    }
1213}
1214
1215#[inline]
1216fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1217    unsafe {
1218        let mut shader = None;
1219        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1220        Ok(shader.unwrap())
1221    }
1222}
1223
1224#[inline]
1225fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1226    unsafe {
1227        let mut shader = None;
1228        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1229        Ok(shader.unwrap())
1230    }
1231}
1232
1233#[inline]
1234fn create_buffer(
1235    device: &ID3D11Device,
1236    element_size: usize,
1237    buffer_size: usize,
1238) -> Result<ID3D11Buffer> {
1239    let desc = D3D11_BUFFER_DESC {
1240        ByteWidth: (element_size * buffer_size) as u32,
1241        Usage: D3D11_USAGE_DYNAMIC,
1242        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1243        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1244        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1245        StructureByteStride: element_size as u32,
1246    };
1247    let mut buffer = None;
1248    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1249    Ok(buffer.unwrap())
1250}
1251
1252#[inline]
1253fn create_buffer_view(
1254    device: &ID3D11Device,
1255    buffer: &ID3D11Buffer,
1256) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1257    let mut view = None;
1258    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1259    Ok([view])
1260}
1261
1262#[inline]
1263fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1264    let desc = D3D11_BUFFER_DESC {
1265        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1266        Usage: D3D11_USAGE_DYNAMIC,
1267        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1268        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1269        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1270        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1271    };
1272    let mut buffer = None;
1273    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1274    Ok(buffer.unwrap())
1275}
1276
1277#[inline]
1278fn update_buffer<T>(
1279    device_context: &ID3D11DeviceContext,
1280    buffer: &ID3D11Buffer,
1281    data: &[T],
1282) -> Result<()> {
1283    unsafe {
1284        let mut dest = std::mem::zeroed();
1285        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1286        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1287        device_context.Unmap(buffer, 0);
1288    }
1289    Ok(())
1290}
1291
1292#[inline]
1293fn set_pipeline_state(
1294    device_context: &ID3D11DeviceContext,
1295    buffer_view: &[Option<ID3D11ShaderResourceView>],
1296    topology: D3D_PRIMITIVE_TOPOLOGY,
1297    viewport: &[D3D11_VIEWPORT],
1298    vertex_shader: &ID3D11VertexShader,
1299    fragment_shader: &ID3D11PixelShader,
1300    global_params: &[Option<ID3D11Buffer>],
1301) {
1302    unsafe {
1303        device_context.VSSetShaderResources(1, Some(buffer_view));
1304        device_context.PSSetShaderResources(1, Some(buffer_view));
1305        device_context.IASetPrimitiveTopology(topology);
1306        device_context.RSSetViewports(Some(viewport));
1307        device_context.VSSetShader(vertex_shader, None);
1308        device_context.PSSetShader(fragment_shader, None);
1309        device_context.VSSetConstantBuffers(0, Some(global_params));
1310        device_context.PSSetConstantBuffers(0, Some(global_params));
1311    }
1312}
1313
1314const BUFFER_COUNT: usize = 3;
1315
1316mod shader_resources {
1317    use anyhow::Result;
1318    use windows::Win32::Graphics::Direct3D::{
1319        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1320        ID3DBlob,
1321    };
1322    use windows_core::{HSTRING, PCSTR};
1323
1324    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1325        unsafe {
1326            let mut entry = entry.to_owned();
1327            let mut target = target.to_owned();
1328            let mut compile_blob = None;
1329            let mut error_blob = None;
1330            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1331                .join("src/platform/windows/shaders.hlsl")
1332                .canonicalize()
1333                .unwrap();
1334            entry.push_str("\0");
1335            target.push_str("\0");
1336            let entry_point = PCSTR::from_raw(entry.as_ptr());
1337            let target_cstr = PCSTR::from_raw(target.as_ptr());
1338            #[cfg(debug_assertions)]
1339            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1340            #[cfg(not(debug_assertions))]
1341            let compile_flag = 0;
1342            let ret = D3DCompileFromFile(
1343                &HSTRING::from(shader_path.to_str().unwrap()),
1344                None,
1345                None,
1346                entry_point,
1347                target_cstr,
1348                compile_flag,
1349                0,
1350                &mut compile_blob,
1351                Some(&mut error_blob),
1352            );
1353            if ret.is_err() {
1354                let Some(error_blob) = error_blob else {
1355                    return Err(anyhow::anyhow!("{ret:?}"));
1356                };
1357                let string_len = error_blob.GetBufferSize();
1358                let error_string_encode = Vec::from_raw_parts(
1359                    error_blob.GetBufferPointer() as *mut u8,
1360                    string_len,
1361                    string_len,
1362                );
1363                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1364                log::error!("Shader compile error: {}", error_string);
1365                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1366            }
1367            Ok(compile_blob.unwrap())
1368        }
1369    }
1370}
1371
1372mod nvidia {
1373    use std::os::raw::{c_char, c_int, c_uint};
1374
1375    use anyhow::{Context, Result};
1376    use windows::{
1377        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
1378        core::s,
1379    };
1380
1381    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
1382    const NVAPI_SHORT_STRING_MAX: usize = 64;
1383
1384    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
1385    #[allow(non_camel_case_types)]
1386    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
1387
1388    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi.h#L87
1389    #[allow(non_camel_case_types)]
1390    type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int;
1391
1392    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
1393    #[allow(non_camel_case_types)]
1394    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
1395        driver_version: *mut c_uint,
1396        build_branch_string: *mut NvAPI_ShortString,
1397    ) -> c_int;
1398
1399    pub(super) fn get_driver_version() -> Result<String> {
1400        unsafe {
1401            // Try to load the NVIDIA driver DLL
1402            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
1403            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
1404                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
1405            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
1406
1407            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L33
1408            let nvapi_init_ptr = nvapi_query(0x0150e828);
1409            if nvapi_init_ptr.is_null() {
1410                anyhow::bail!("Failed to get NVIDIA API function pointer");
1411            }
1412            let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr);
1413
1414            let result = nvapi_init();
1415            if result != 0 {
1416                anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result);
1417            }
1418
1419            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
1420            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
1421            if nvapi_get_driver_version_ptr.is_null() {
1422                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
1423            }
1424            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
1425                std::mem::transmute(nvapi_get_driver_version_ptr);
1426
1427            let mut driver_version: c_uint = 0;
1428            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
1429            let result = nvapi_get_driver_version(
1430                &mut driver_version as *mut c_uint,
1431                &mut build_branch_string as *mut NvAPI_ShortString,
1432            );
1433
1434            if result != 0 {
1435                anyhow::bail!(
1436                    "Failed to get NVIDIA driver version, error code: {}",
1437                    result
1438                );
1439            }
1440            let major = driver_version / 100;
1441            let minor = driver_version % 100;
1442            Ok(format!("{}.{}", major, minor))
1443        }
1444    }
1445}