directx_renderer.rs

   1use std::sync::Arc;
   2
   3use ::util::ResultExt;
   4use anyhow::{Context, Result};
   5// #[cfg(not(feature = "enable-renderdoc"))]
   6// use windows::Win32::Graphics::DirectComposition::*;
   7use windows::{
   8    Win32::{
   9        Foundation::{HMODULE, HWND},
  10        Graphics::{
  11            Direct3D::*,
  12            Direct3D11::*,
  13            Dxgi::{Common::*, *},
  14        },
  15    },
  16    core::*,
  17};
  18
  19use crate::*;
  20
  21pub(crate) struct DirectXRenderer {
  22    atlas: Arc<DirectXAtlas>,
  23    devices: DirectXDevices,
  24    context: DirectXContext,
  25    globals: DirectXGlobalElements,
  26    pipelines: DirectXRenderPipelines,
  27    hwnd: HWND,
  28    transparent: bool,
  29}
  30
  31#[derive(Clone)]
  32pub(crate) struct DirectXDevices {
  33    dxgi_factory: IDXGIFactory6,
  34    dxgi_device: IDXGIDevice,
  35    device: ID3D11Device,
  36    device_context: ID3D11DeviceContext,
  37}
  38
  39struct DirectXContext {
  40    swap_chain: IDXGISwapChain1,
  41    back_buffer: [Option<ID3D11RenderTargetView>; 1],
  42    viewport: [D3D11_VIEWPORT; 1],
  43    // #[cfg(not(feature = "enable-renderdoc"))]
  44    // direct_composition: DirectComposition,
  45}
  46
  47struct DirectXRenderPipelines {
  48    shadow_pipeline: PipelineState<Shadow>,
  49    quad_pipeline: PipelineState<Quad>,
  50    paths_pipeline: PathsPipelineState,
  51    underline_pipeline: PipelineState<Underline>,
  52    mono_sprites: PipelineState<MonochromeSprite>,
  53    poly_sprites: PipelineState<PolychromeSprite>,
  54}
  55
  56struct DirectXGlobalElements {
  57    global_params_buffer: [Option<ID3D11Buffer>; 1],
  58    sampler: [Option<ID3D11SamplerState>; 1],
  59    blend_state: ID3D11BlendState,
  60}
  61
  62#[repr(C)]
  63struct DrawInstancedIndirectArgs {
  64    vertex_count_per_instance: u32,
  65    instance_count: u32,
  66    start_vertex_location: u32,
  67    start_instance_location: u32,
  68}
  69
  70// #[cfg(not(feature = "enable-renderdoc"))]
  71// struct DirectComposition {
  72//     comp_device: IDCompositionDevice,
  73//     comp_target: IDCompositionTarget,
  74//     comp_visual: IDCompositionVisual,
  75// }
  76
  77impl DirectXDevices {
  78    pub(crate) fn new() -> Result<Self> {
  79        let dxgi_factory = get_dxgi_factory()?;
  80        let adapter = get_adapter(&dxgi_factory)?;
  81        let (device, device_context) = {
  82            let mut device: Option<ID3D11Device> = None;
  83            let mut context: Option<ID3D11DeviceContext> = None;
  84            get_device(&adapter, Some(&mut device), Some(&mut context))?;
  85            (device.unwrap(), context.unwrap())
  86        };
  87        let dxgi_device: IDXGIDevice = device.cast()?;
  88
  89        Ok(Self {
  90            dxgi_factory,
  91            dxgi_device,
  92            device,
  93            device_context,
  94        })
  95    }
  96}
  97
  98impl DirectXRenderer {
  99    pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
 100        let atlas = Arc::new(DirectXAtlas::new(
 101            devices.device.clone(),
 102            devices.device_context.clone(),
 103        ));
 104        let context = DirectXContext::new(devices, hwnd, transparent)?;
 105        let globals = DirectXGlobalElements::new(&devices.device)?;
 106        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
 107        Ok(DirectXRenderer {
 108            atlas,
 109            devices: devices.clone(),
 110            context,
 111            globals,
 112            pipelines,
 113            hwnd,
 114            transparent,
 115        })
 116    }
 117
 118    pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
 119        self.atlas.clone()
 120    }
 121
 122    pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
 123        pre_draw(
 124            &self.devices.device_context,
 125            &self.globals.global_params_buffer,
 126            &self.context.viewport,
 127            &self.context.back_buffer,
 128            [0.0, 0.0, 0.0, 0.0],
 129            &self.globals.blend_state,
 130        )?;
 131        for batch in scene.batches() {
 132            match batch {
 133                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
 134                PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
 135                PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
 136                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
 137                PrimitiveBatch::MonochromeSprites {
 138                    texture_id,
 139                    sprites,
 140                } => self.draw_monochrome_sprites(texture_id, sprites),
 141                PrimitiveBatch::PolychromeSprites {
 142                    texture_id,
 143                    sprites,
 144                } => self.draw_polychrome_sprites(texture_id, sprites),
 145                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
 146            }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 147                    scene.paths.len(),
 148                    scene.shadows.len(),
 149                    scene.quads.len(),
 150                    scene.underlines.len(),
 151                    scene.monochrome_sprites.len(),
 152                    scene.polychrome_sprites.len(),
 153                    scene.surfaces.len(),))?;
 154        }
 155        unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?;
 156        Ok(())
 157    }
 158
 159    pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
 160        unsafe { self.devices.device_context.OMSetRenderTargets(None, None) };
 161        drop(self.context.back_buffer[0].take().unwrap());
 162        unsafe {
 163            self.context.swap_chain.ResizeBuffers(
 164                BUFFER_COUNT as u32,
 165                new_size.width.0 as u32,
 166                new_size.height.0 as u32,
 167                DXGI_FORMAT_B8G8R8A8_UNORM,
 168                DXGI_SWAP_CHAIN_FLAG(0),
 169            )?;
 170        }
 171        let backbuffer = set_render_target_view(
 172            &self.context.swap_chain,
 173            &self.devices.device,
 174            &self.devices.device_context,
 175        )?;
 176        self.context.back_buffer[0] = Some(backbuffer);
 177        self.context.viewport = set_viewport(
 178            &self.devices.device_context,
 179            new_size.width.0 as f32,
 180            new_size.height.0 as f32,
 181        );
 182        Ok(())
 183    }
 184
 185    // #[cfg(not(feature = "enable-renderdoc"))]
 186    // pub(crate) fn update_transparency(
 187    //     &mut self,
 188    //     background_appearance: WindowBackgroundAppearance,
 189    // ) -> Result<()> {
 190    //     // We only support setting `Transparent` and `Opaque` for now.
 191    //     match background_appearance {
 192    //         WindowBackgroundAppearance::Opaque => {
 193    //             if self.transparent {
 194    //                 return Err(anyhow::anyhow!(
 195    //                     "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
 196    //                 ));
 197    //             }
 198    //         }
 199    //         WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
 200    //             if !self.transparent {
 201    //                 return Err(anyhow::anyhow!(
 202    //                     "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
 203    //                 ));
 204    //             }
 205    //         }
 206    //     }
 207    //     Ok(())
 208    // }
 209
 210    // #[cfg(feature = "enable-renderdoc")]
 211    pub(crate) fn update_transparency(
 212        &mut self,
 213        background_appearance: WindowBackgroundAppearance,
 214    ) -> Result<()> {
 215        if background_appearance != WindowBackgroundAppearance::Opaque {
 216            Err(anyhow::anyhow!(
 217                "Set transparent background not supported when feature \"enable-renderdoc\" is enabled."
 218            ))
 219        } else {
 220            Ok(())
 221        }
 222    }
 223
 224    fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
 225        if shadows.is_empty() {
 226            return Ok(());
 227        }
 228        self.pipelines.shadow_pipeline.update_buffer(
 229            &self.devices.device,
 230            &self.devices.device_context,
 231            shadows,
 232        )?;
 233        self.pipelines.shadow_pipeline.draw(
 234            &self.devices.device_context,
 235            &self.context.viewport,
 236            &self.globals.global_params_buffer,
 237            shadows.len() as u32,
 238        )
 239    }
 240
 241    fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
 242        if quads.is_empty() {
 243            return Ok(());
 244        }
 245        self.pipelines.quad_pipeline.update_buffer(
 246            &self.devices.device,
 247            &self.devices.device_context,
 248            quads,
 249        )?;
 250        self.pipelines.quad_pipeline.draw(
 251            &self.devices.device_context,
 252            &self.context.viewport,
 253            &self.globals.global_params_buffer,
 254            quads.len() as u32,
 255        )
 256    }
 257
 258    fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
 259        if paths.is_empty() {
 260            return Ok(());
 261        }
 262        let mut vertices = Vec::new();
 263        let mut sprites = Vec::with_capacity(paths.len());
 264        let mut draw_indirect_commands = Vec::with_capacity(paths.len());
 265        let mut start_vertex_location = 0;
 266        for (i, path) in paths.iter().enumerate() {
 267            draw_indirect_commands.push(DrawInstancedIndirectArgs {
 268                vertex_count_per_instance: path.vertices.len() as u32,
 269                instance_count: 1,
 270                start_vertex_location,
 271                start_instance_location: i as u32,
 272            });
 273            start_vertex_location += path.vertices.len() as u32;
 274
 275            vertices.extend(path.vertices.iter().map(|v| PathVertex {
 276                xy_position: v.xy_position,
 277                content_mask: ContentMask {
 278                    bounds: path.content_mask.bounds,
 279                },
 280            }));
 281
 282            sprites.push(PathSprite {
 283                bounds: path.bounds,
 284                color: path.color,
 285            });
 286        }
 287
 288        self.pipelines.paths_pipeline.update_buffer(
 289            &self.devices.device,
 290            &self.devices.device_context,
 291            &sprites,
 292            &vertices,
 293            &draw_indirect_commands,
 294        )?;
 295        self.pipelines.paths_pipeline.draw(
 296            &self.devices.device_context,
 297            paths.len(),
 298            &self.context.viewport,
 299            &self.globals.global_params_buffer,
 300        )
 301    }
 302
 303    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
 304        if underlines.is_empty() {
 305            return Ok(());
 306        }
 307        self.pipelines.underline_pipeline.update_buffer(
 308            &self.devices.device,
 309            &self.devices.device_context,
 310            underlines,
 311        )?;
 312        self.pipelines.underline_pipeline.draw(
 313            &self.devices.device_context,
 314            &self.context.viewport,
 315            &self.globals.global_params_buffer,
 316            underlines.len() as u32,
 317        )
 318    }
 319
 320    fn draw_monochrome_sprites(
 321        &mut self,
 322        texture_id: AtlasTextureId,
 323        sprites: &[MonochromeSprite],
 324    ) -> Result<()> {
 325        if sprites.is_empty() {
 326            return Ok(());
 327        }
 328        self.pipelines.mono_sprites.update_buffer(
 329            &self.devices.device,
 330            &self.devices.device_context,
 331            sprites,
 332        )?;
 333        let texture_view = self.atlas.get_texture_view(texture_id);
 334        self.pipelines.mono_sprites.draw_with_texture(
 335            &self.devices.device_context,
 336            &texture_view,
 337            &self.context.viewport,
 338            &self.globals.global_params_buffer,
 339            &self.globals.sampler,
 340            sprites.len() as u32,
 341        )
 342    }
 343
 344    fn draw_polychrome_sprites(
 345        &mut self,
 346        texture_id: AtlasTextureId,
 347        sprites: &[PolychromeSprite],
 348    ) -> Result<()> {
 349        if sprites.is_empty() {
 350            return Ok(());
 351        }
 352        self.pipelines.poly_sprites.update_buffer(
 353            &self.devices.device,
 354            &self.devices.device_context,
 355            sprites,
 356        )?;
 357        let texture_view = self.atlas.get_texture_view(texture_id);
 358        self.pipelines.poly_sprites.draw_with_texture(
 359            &self.devices.device_context,
 360            &texture_view,
 361            &self.context.viewport,
 362            &self.globals.global_params_buffer,
 363            &self.globals.sampler,
 364            sprites.len() as u32,
 365        )
 366    }
 367
 368    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
 369        if surfaces.is_empty() {
 370            return Ok(());
 371        }
 372        Ok(())
 373    }
 374}
 375
 376impl DirectXContext {
 377    pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
 378        // #[cfg(not(feature = "enable-renderdoc"))]
 379        // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
 380        // #[cfg(feature = "enable-renderdoc")]
 381        let swap_chain =
 382            create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
 383        // #[cfg(not(feature = "enable-renderdoc"))]
 384        // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
 385        // #[cfg(not(feature = "enable-renderdoc"))]
 386        // direct_composition.set_swap_chain(&swap_chain)?;
 387        let back_buffer = [Some(set_render_target_view(
 388            &swap_chain,
 389            &devices.device,
 390            &devices.device_context,
 391        )?)];
 392        let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
 393        set_rasterizer_state(&devices.device, &devices.device_context)?;
 394
 395        Ok(Self {
 396            swap_chain,
 397            back_buffer,
 398            viewport,
 399            // #[cfg(not(feature = "enable-renderdoc"))]
 400            // direct_composition,
 401        })
 402    }
 403}
 404
 405impl DirectXRenderPipelines {
 406    pub fn new(device: &ID3D11Device) -> Result<Self> {
 407        let shadow_pipeline = PipelineState::new(
 408            device,
 409            "shadow_pipeline",
 410            "shadow_vertex",
 411            "shadow_fragment",
 412            32,
 413        )?;
 414        let quad_pipeline =
 415            PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?;
 416        let paths_pipeline = PathsPipelineState::new(device)?;
 417        let underline_pipeline = PipelineState::new(
 418            device,
 419            "underline_pipeline",
 420            "underline_vertex",
 421            "underline_fragment",
 422            32,
 423        )?;
 424        let mono_sprites = PipelineState::new(
 425            device,
 426            "monochrome_sprite_pipeline",
 427            "monochrome_sprite_vertex",
 428            "monochrome_sprite_fragment",
 429            32,
 430        )?;
 431        let poly_sprites = PipelineState::new(
 432            device,
 433            "polychrome_sprite_pipeline",
 434            "polychrome_sprite_vertex",
 435            "polychrome_sprite_fragment",
 436            32,
 437        )?;
 438
 439        Ok(Self {
 440            shadow_pipeline,
 441            quad_pipeline,
 442            paths_pipeline,
 443            underline_pipeline,
 444            mono_sprites,
 445            poly_sprites,
 446        })
 447    }
 448}
 449
 450// #[cfg(not(feature = "enable-renderdoc"))]
 451// impl DirectComposition {
 452//     pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
 453//         let comp_device = get_comp_device(&dxgi_device)?;
 454//         let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
 455//         let comp_visual = unsafe { comp_device.CreateVisual() }?;
 456
 457//         Ok(Self {
 458//             comp_device,
 459//             comp_target,
 460//             comp_visual,
 461//         })
 462//     }
 463
 464//     pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
 465//         unsafe {
 466//             self.comp_visual.SetContent(swap_chain)?;
 467//             self.comp_target.SetRoot(&self.comp_visual)?;
 468//             self.comp_device.Commit()?;
 469//         }
 470//         Ok(())
 471//     }
 472// }
 473
 474impl DirectXGlobalElements {
 475    pub fn new(device: &ID3D11Device) -> Result<Self> {
 476        let global_params_buffer = unsafe {
 477            let desc = D3D11_BUFFER_DESC {
 478                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
 479                Usage: D3D11_USAGE_DYNAMIC,
 480                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
 481                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 482                ..Default::default()
 483            };
 484            let mut buffer = None;
 485            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
 486            [buffer]
 487        };
 488
 489        let sampler = unsafe {
 490            let desc = D3D11_SAMPLER_DESC {
 491                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
 492                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
 493                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
 494                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
 495                MipLODBias: 0.0,
 496                MaxAnisotropy: 1,
 497                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
 498                BorderColor: [0.0; 4],
 499                MinLOD: 0.0,
 500                MaxLOD: D3D11_FLOAT32_MAX,
 501            };
 502            let mut output = None;
 503            device.CreateSamplerState(&desc, Some(&mut output))?;
 504            [output]
 505        };
 506
 507        let blend_state = create_blend_state(device)?;
 508
 509        Ok(Self {
 510            global_params_buffer,
 511            sampler,
 512            blend_state,
 513        })
 514    }
 515}
 516
 517#[derive(Debug, Default)]
 518#[repr(C)]
 519struct GlobalParams {
 520    viewport_size: [f32; 2],
 521    _pad: u64,
 522}
 523
 524struct PipelineState<T> {
 525    label: &'static str,
 526    vertex: ID3D11VertexShader,
 527    fragment: ID3D11PixelShader,
 528    buffer: ID3D11Buffer,
 529    buffer_size: usize,
 530    view: [Option<ID3D11ShaderResourceView>; 1],
 531    _marker: std::marker::PhantomData<T>,
 532}
 533
 534struct PathsPipelineState {
 535    vertex: ID3D11VertexShader,
 536    fragment: ID3D11PixelShader,
 537    buffer: ID3D11Buffer,
 538    buffer_size: usize,
 539    vertex_buffer: Option<ID3D11Buffer>,
 540    vertex_buffer_size: usize,
 541    indirect_draw_buffer: ID3D11Buffer,
 542    indirect_buffer_size: usize,
 543    input_layout: ID3D11InputLayout,
 544    view: [Option<ID3D11ShaderResourceView>; 1],
 545}
 546
 547impl<T> PipelineState<T> {
 548    fn new(
 549        device: &ID3D11Device,
 550        label: &'static str,
 551        vertex_entry: &str,
 552        fragment_entry: &str,
 553        buffer_size: usize,
 554    ) -> Result<Self> {
 555        let vertex = {
 556            let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 557            let bytes = unsafe {
 558                std::slice::from_raw_parts(
 559                    shader_blob.GetBufferPointer() as *mut u8,
 560                    shader_blob.GetBufferSize(),
 561                )
 562            };
 563            create_vertex_shader(device, bytes)?
 564        };
 565        let fragment = {
 566            let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 567            let bytes = unsafe {
 568                std::slice::from_raw_parts(
 569                    shader_blob.GetBufferPointer() as *mut u8,
 570                    shader_blob.GetBufferSize(),
 571                )
 572            };
 573            create_fragment_shader(device, bytes)?
 574        };
 575        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
 576        let view = create_buffer_view(device, &buffer)?;
 577
 578        Ok(PipelineState {
 579            label,
 580            vertex,
 581            fragment,
 582            buffer,
 583            buffer_size,
 584            view,
 585            _marker: std::marker::PhantomData,
 586        })
 587    }
 588
 589    fn update_buffer(
 590        &mut self,
 591        device: &ID3D11Device,
 592        device_context: &ID3D11DeviceContext,
 593        data: &[T],
 594    ) -> Result<()> {
 595        if self.buffer_size < data.len() {
 596            let new_buffer_size = data.len().next_power_of_two();
 597            log::info!(
 598                "Updating {} buffer size from {} to {}",
 599                self.label,
 600                self.buffer_size,
 601                new_buffer_size
 602            );
 603            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
 604            let view = create_buffer_view(device, &buffer)?;
 605            self.buffer = buffer;
 606            self.view = view;
 607            self.buffer_size = new_buffer_size;
 608        }
 609        update_buffer(device_context, &self.buffer, data)
 610    }
 611
 612    fn draw(
 613        &self,
 614        device_context: &ID3D11DeviceContext,
 615        viewport: &[D3D11_VIEWPORT],
 616        global_params: &[Option<ID3D11Buffer>],
 617        instance_count: u32,
 618    ) -> Result<()> {
 619        unsafe {
 620            device_context.VSSetShaderResources(1, Some(&self.view));
 621            device_context.PSSetShaderResources(1, Some(&self.view));
 622            device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
 623            device_context.RSSetViewports(Some(viewport));
 624            device_context.VSSetShader(&self.vertex, None);
 625            device_context.PSSetShader(&self.fragment, None);
 626            device_context.VSSetConstantBuffers(0, Some(global_params));
 627            device_context.PSSetConstantBuffers(0, Some(global_params));
 628
 629            device_context.DrawInstanced(4, instance_count, 0, 0);
 630        }
 631        Ok(())
 632    }
 633
 634    fn draw_with_texture(
 635        &self,
 636        device_context: &ID3D11DeviceContext,
 637        texture: &[Option<ID3D11ShaderResourceView>],
 638        viewport: &[D3D11_VIEWPORT],
 639        global_params: &[Option<ID3D11Buffer>],
 640        sampler: &[Option<ID3D11SamplerState>],
 641        instance_count: u32,
 642    ) -> Result<()> {
 643        unsafe {
 644            device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
 645            device_context.RSSetViewports(Some(viewport));
 646            device_context.VSSetShader(&self.vertex, None);
 647            device_context.PSSetShader(&self.fragment, None);
 648            device_context.VSSetConstantBuffers(0, Some(global_params));
 649            device_context.PSSetConstantBuffers(0, Some(global_params));
 650            device_context.VSSetShaderResources(1, Some(&self.view));
 651            device_context.PSSetShaderResources(1, Some(&self.view));
 652            device_context.PSSetSamplers(0, Some(sampler));
 653            device_context.VSSetShaderResources(0, Some(texture));
 654            device_context.PSSetShaderResources(0, Some(texture));
 655
 656            device_context.DrawInstanced(4, instance_count, 0, 0);
 657        }
 658        Ok(())
 659    }
 660}
 661
 662impl PathsPipelineState {
 663    fn new(device: &ID3D11Device) -> Result<Self> {
 664        let (vertex, vertex_shader) = {
 665            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 666            let bytes = unsafe {
 667                std::slice::from_raw_parts(
 668                    shader_blob.GetBufferPointer() as *mut u8,
 669                    shader_blob.GetBufferSize(),
 670                )
 671            };
 672            (create_vertex_shader(device, bytes)?, shader_blob)
 673        };
 674        let fragment = {
 675            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 676            let bytes = unsafe {
 677                std::slice::from_raw_parts(
 678                    shader_blob.GetBufferPointer() as *mut u8,
 679                    shader_blob.GetBufferSize(),
 680                )
 681            };
 682            create_fragment_shader(device, bytes)?
 683        };
 684        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 685        let view = create_buffer_view(device, &buffer)?;
 686        let vertex_buffer = Some(create_buffer(
 687            device,
 688            std::mem::size_of::<PathVertex<ScaledPixels>>(),
 689            32,
 690        )?);
 691        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 692        // Create input layout
 693        let input_layout = unsafe {
 694            let shader_bytes = std::slice::from_raw_parts(
 695                vertex_shader.GetBufferPointer() as *const u8,
 696                vertex_shader.GetBufferSize(),
 697            );
 698            let mut layout = None;
 699            device.CreateInputLayout(
 700                &[
 701                    D3D11_INPUT_ELEMENT_DESC {
 702                        SemanticName: windows::core::s!("POSITION"),
 703                        SemanticIndex: 0,
 704                        Format: DXGI_FORMAT_R32G32_FLOAT,
 705                        InputSlot: 0,
 706                        AlignedByteOffset: 0,
 707                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 708                        InstanceDataStepRate: 0,
 709                    },
 710                    D3D11_INPUT_ELEMENT_DESC {
 711                        SemanticName: windows::core::s!("TEXCOORD"),
 712                        SemanticIndex: 0,
 713                        Format: DXGI_FORMAT_R32G32_FLOAT,
 714                        InputSlot: 0,
 715                        AlignedByteOffset: 8,
 716                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 717                        InstanceDataStepRate: 0,
 718                    },
 719                    D3D11_INPUT_ELEMENT_DESC {
 720                        SemanticName: windows::core::s!("TEXCOORD"),
 721                        SemanticIndex: 1,
 722                        Format: DXGI_FORMAT_R32G32_FLOAT,
 723                        InputSlot: 0,
 724                        AlignedByteOffset: 16,
 725                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 726                        InstanceDataStepRate: 0,
 727                    },
 728                ],
 729                shader_bytes,
 730                Some(&mut layout),
 731            )?;
 732            layout.unwrap()
 733        };
 734
 735        Ok(Self {
 736            vertex,
 737            fragment,
 738            buffer,
 739            buffer_size: 32,
 740            vertex_buffer,
 741            vertex_buffer_size: 32,
 742            indirect_draw_buffer,
 743            indirect_buffer_size: 32,
 744            input_layout,
 745            view,
 746        })
 747    }
 748
 749    fn update_buffer(
 750        &mut self,
 751        device: &ID3D11Device,
 752        device_context: &ID3D11DeviceContext,
 753        buffer_data: &[PathSprite],
 754        vertices_data: &[PathVertex<ScaledPixels>],
 755        draw_commands: &[DrawInstancedIndirectArgs],
 756    ) -> Result<()> {
 757        if self.buffer_size < buffer_data.len() {
 758            let new_buffer_size = buffer_data.len().next_power_of_two();
 759            log::info!(
 760                "Updating Paths Pipeline buffer size from {} to {}",
 761                self.buffer_size,
 762                new_buffer_size
 763            );
 764            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 765            let view = create_buffer_view(device, &buffer)?;
 766            self.buffer = buffer;
 767            self.view = view;
 768            self.buffer_size = new_buffer_size;
 769        }
 770        update_buffer(device_context, &self.buffer, buffer_data)?;
 771        if self.vertex_buffer_size < vertices_data.len() {
 772            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 773            log::info!(
 774                "Updating Paths Pipeline vertex buffer size from {} to {}",
 775                self.vertex_buffer_size,
 776                new_vertex_buffer_size
 777            );
 778            let vertex_buffer = create_buffer(
 779                device,
 780                std::mem::size_of::<PathVertex<ScaledPixels>>(),
 781                new_vertex_buffer_size,
 782            )?;
 783            self.vertex_buffer = Some(vertex_buffer);
 784            self.vertex_buffer_size = new_vertex_buffer_size;
 785        }
 786        update_buffer(
 787            device_context,
 788            self.vertex_buffer.as_ref().unwrap(),
 789            vertices_data,
 790        )?;
 791        if self.indirect_buffer_size < draw_commands.len() {
 792            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 793            log::info!(
 794                "Updating Paths Pipeline indirect buffer size from {} to {}",
 795                self.indirect_buffer_size,
 796                new_indirect_buffer_size
 797            );
 798            let indirect_draw_buffer =
 799                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 800            self.indirect_draw_buffer = indirect_draw_buffer;
 801            self.indirect_buffer_size = new_indirect_buffer_size;
 802        }
 803        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 804        Ok(())
 805    }
 806
 807    fn draw(
 808        &self,
 809        device_context: &ID3D11DeviceContext,
 810        count: usize,
 811        viewport: &[D3D11_VIEWPORT],
 812        global_params: &[Option<ID3D11Buffer>],
 813    ) -> Result<()> {
 814        unsafe {
 815            device_context.VSSetShaderResources(1, Some(&self.view));
 816            device_context.PSSetShaderResources(1, Some(&self.view));
 817            device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
 818            device_context.RSSetViewports(Some(viewport));
 819            device_context.VSSetShader(&self.vertex, None);
 820            device_context.PSSetShader(&self.fragment, None);
 821            device_context.VSSetConstantBuffers(0, Some(global_params));
 822            device_context.PSSetConstantBuffers(0, Some(global_params));
 823            const STRIDE: u32 = std::mem::size_of::<PathVertex<ScaledPixels>>() as u32;
 824            device_context.IASetVertexBuffers(
 825                0,
 826                1,
 827                Some(&self.vertex_buffer),
 828                Some(&STRIDE),
 829                Some(&0),
 830            );
 831            device_context.IASetInputLayout(&self.input_layout);
 832        }
 833        for i in 0..count {
 834            unsafe {
 835                device_context.DrawInstancedIndirect(
 836                    &self.indirect_draw_buffer,
 837                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 838                );
 839            }
 840        }
 841        Ok(())
 842    }
 843}
 844
 845#[derive(Clone, Debug, Eq, PartialEq)]
 846#[repr(C)]
 847struct PathSprite {
 848    bounds: Bounds<ScaledPixels>,
 849    color: Background,
 850}
 851
 852fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 853    #[cfg(debug_assertions)]
 854    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 855    #[cfg(not(debug_assertions))]
 856    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 857    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 858}
 859
 860fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 861    for adapter_index in 0.. {
 862        let adapter: IDXGIAdapter1 = unsafe {
 863            dxgi_factory
 864                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 865        }?;
 866        {
 867            let desc = unsafe { adapter.GetDesc1() }?;
 868            println!(
 869                "Select GPU: {}",
 870                String::from_utf16_lossy(&desc.Description)
 871            );
 872        }
 873        // Check to see whether the adapter supports Direct3D 11, but don't
 874        // create the actual device yet.
 875        if get_device(&adapter, None, None).log_err().is_some() {
 876            return Ok(adapter);
 877        }
 878    }
 879
 880    unreachable!()
 881}
 882
 883fn get_device(
 884    adapter: &IDXGIAdapter1,
 885    device: Option<*mut Option<ID3D11Device>>,
 886    context: Option<*mut Option<ID3D11DeviceContext>>,
 887) -> Result<()> {
 888    #[cfg(debug_assertions)]
 889    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
 890    #[cfg(not(debug_assertions))]
 891    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 892    Ok(unsafe {
 893        D3D11CreateDevice(
 894            adapter,
 895            D3D_DRIVER_TYPE_UNKNOWN,
 896            HMODULE::default(),
 897            device_flags,
 898            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
 899            D3D11_SDK_VERSION,
 900            device,
 901            None,
 902            context,
 903        )?
 904    })
 905}
 906
 907// #[cfg(not(feature = "enable-renderdoc"))]
 908// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
 909//     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
 910// }
 911
 912// fn create_swap_chain(
 913//     dxgi_factory: &IDXGIFactory6,
 914//     device: &ID3D11Device,
 915//     transparent: bool,
 916// ) -> Result<IDXGISwapChain1> {
 917//     let alpha_mode = if transparent {
 918//         DXGI_ALPHA_MODE_PREMULTIPLIED
 919//     } else {
 920//         DXGI_ALPHA_MODE_IGNORE
 921//     };
 922//     let desc = DXGI_SWAP_CHAIN_DESC1 {
 923//         Width: 1,
 924//         Height: 1,
 925//         Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 926//         Stereo: false.into(),
 927//         SampleDesc: DXGI_SAMPLE_DESC {
 928//             Count: 1,
 929//             Quality: 0,
 930//         },
 931//         BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 932//         BufferCount: BUFFER_COUNT as u32,
 933//         // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
 934//         Scaling: DXGI_SCALING_STRETCH,
 935//         SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 936//         AlphaMode: alpha_mode,
 937//         Flags: 0,
 938//     };
 939//     Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
 940// }
 941
 942// #[cfg(feature = "enable-renderdoc")]
 943fn create_swap_chain_default(
 944    dxgi_factory: &IDXGIFactory6,
 945    device: &ID3D11Device,
 946    hwnd: HWND,
 947    _transparent: bool,
 948) -> Result<IDXGISwapChain1> {
 949    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
 950
 951    let desc = DXGI_SWAP_CHAIN_DESC1 {
 952        Width: 1,
 953        Height: 1,
 954        Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 955        Stereo: false.into(),
 956        SampleDesc: DXGI_SAMPLE_DESC {
 957            Count: 1,
 958            Quality: 0,
 959        },
 960        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 961        BufferCount: BUFFER_COUNT as u32,
 962        Scaling: DXGI_SCALING_STRETCH,
 963        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 964        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
 965        Flags: 0,
 966    };
 967    let swap_chain =
 968        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
 969    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
 970    Ok(swap_chain)
 971}
 972
 973fn set_render_target_view(
 974    swap_chain: &IDXGISwapChain1,
 975    device: &ID3D11Device,
 976    device_context: &ID3D11DeviceContext,
 977) -> Result<ID3D11RenderTargetView> {
 978    // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
 979    // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
 980    let back_buffer = unsafe {
 981        let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
 982        let mut buffer: Option<ID3D11RenderTargetView> = None;
 983        device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?;
 984        buffer.unwrap()
 985    };
 986    unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
 987    Ok(back_buffer)
 988}
 989
 990fn set_viewport(
 991    device_context: &ID3D11DeviceContext,
 992    width: f32,
 993    height: f32,
 994) -> [D3D11_VIEWPORT; 1] {
 995    let viewport = [D3D11_VIEWPORT {
 996        TopLeftX: 0.0,
 997        TopLeftY: 0.0,
 998        Width: width,
 999        Height: height,
1000        MinDepth: 0.0,
1001        MaxDepth: 1.0,
1002    }];
1003    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1004    viewport
1005}
1006
1007fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1008    let desc = D3D11_RASTERIZER_DESC {
1009        FillMode: D3D11_FILL_SOLID,
1010        CullMode: D3D11_CULL_NONE,
1011        // FrontCounterClockwise: true.into(),
1012        FrontCounterClockwise: false.into(),
1013        DepthBias: 0,
1014        DepthBiasClamp: 0.0,
1015        SlopeScaledDepthBias: 0.0,
1016        DepthClipEnable: true.into(),
1017        ScissorEnable: false.into(),
1018        MultisampleEnable: false.into(),
1019        AntialiasedLineEnable: false.into(),
1020    };
1021    let rasterizer_state = unsafe {
1022        let mut state = None;
1023        device.CreateRasterizerState(&desc, Some(&mut state))?;
1024        state.unwrap()
1025    };
1026    unsafe { device_context.RSSetState(&rasterizer_state) };
1027    Ok(())
1028}
1029
1030// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1031fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1032    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1033    // device performs the blend in linear space, which is ideal.
1034    let mut desc = D3D11_BLEND_DESC::default();
1035    desc.RenderTarget[0].BlendEnable = true.into();
1036    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1037    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1038    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1039    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1040    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1041    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1042    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1043    unsafe {
1044        let mut state = None;
1045        device.CreateBlendState(&desc, Some(&mut state))?;
1046        Ok(state.unwrap())
1047    }
1048}
1049
1050fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1051    unsafe {
1052        let mut shader = None;
1053        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1054        Ok(shader.unwrap())
1055    }
1056}
1057
1058fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1059    unsafe {
1060        let mut shader = None;
1061        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1062        Ok(shader.unwrap())
1063    }
1064}
1065
1066fn create_buffer(
1067    device: &ID3D11Device,
1068    element_size: usize,
1069    buffer_size: usize,
1070) -> Result<ID3D11Buffer> {
1071    let desc = D3D11_BUFFER_DESC {
1072        ByteWidth: (element_size * buffer_size) as u32,
1073        Usage: D3D11_USAGE_DYNAMIC,
1074        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1075        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1076        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1077        StructureByteStride: element_size as u32,
1078    };
1079    let mut buffer = None;
1080    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1081    Ok(buffer.unwrap())
1082}
1083
1084fn create_buffer_view(
1085    device: &ID3D11Device,
1086    buffer: &ID3D11Buffer,
1087) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1088    let mut view = None;
1089    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1090    Ok([view])
1091}
1092
1093fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1094    let desc = D3D11_BUFFER_DESC {
1095        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1096        Usage: D3D11_USAGE_DYNAMIC,
1097        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1098        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1099        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1100        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1101    };
1102    let mut buffer = None;
1103    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1104    Ok(buffer.unwrap())
1105}
1106
1107fn pre_draw(
1108    device_context: &ID3D11DeviceContext,
1109    global_params_buffer: &[Option<ID3D11Buffer>; 1],
1110    view_port: &[D3D11_VIEWPORT; 1],
1111    render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1112    clear_color: [f32; 4],
1113    blend_state: &ID3D11BlendState,
1114) -> Result<()> {
1115    let global_params = global_params_buffer[0].as_ref().unwrap();
1116    update_buffer(
1117        device_context,
1118        global_params,
1119        &[GlobalParams {
1120            viewport_size: [view_port[0].Width, view_port[0].Height],
1121            ..Default::default()
1122        }],
1123    )?;
1124    unsafe {
1125        device_context.RSSetViewports(Some(view_port));
1126        device_context.OMSetRenderTargets(Some(render_target_view), None);
1127        device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1128        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1129    }
1130    Ok(())
1131}
1132
1133fn update_buffer<T>(
1134    device_context: &ID3D11DeviceContext,
1135    buffer: &ID3D11Buffer,
1136    data: &[T],
1137) -> Result<()> {
1138    unsafe {
1139        let mut dest = std::mem::zeroed();
1140        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1141        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1142        device_context.Unmap(buffer, 0);
1143    }
1144    Ok(())
1145}
1146
1147const BUFFER_COUNT: usize = 3;
1148
1149mod shader_resources {
1150    use anyhow::Result;
1151    use windows::Win32::Graphics::Direct3D::{
1152        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1153        ID3DBlob,
1154    };
1155    use windows_core::{HSTRING, PCSTR};
1156
1157    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1158        unsafe {
1159            let mut entry = entry.to_owned();
1160            let mut target = target.to_owned();
1161            let mut compile_blob = None;
1162            let mut error_blob = None;
1163            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1164                .join("src/platform/windows/shaders.hlsl")
1165                .canonicalize()
1166                .unwrap();
1167            entry.push_str("\0");
1168            target.push_str("\0");
1169            let entry_point = PCSTR::from_raw(entry.as_ptr());
1170            let target_cstr = PCSTR::from_raw(target.as_ptr());
1171            #[cfg(debug_assertions)]
1172            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1173            #[cfg(not(debug_assertions))]
1174            let compile_flag = 0;
1175            let ret = D3DCompileFromFile(
1176                &HSTRING::from(shader_path.to_str().unwrap()),
1177                None,
1178                None,
1179                entry_point,
1180                target_cstr,
1181                compile_flag,
1182                0,
1183                &mut compile_blob,
1184                Some(&mut error_blob),
1185            );
1186            if ret.is_err() {
1187                let Some(error_blob) = error_blob else {
1188                    return Err(anyhow::anyhow!("{ret:?}"));
1189                };
1190                let string_len = error_blob.GetBufferSize();
1191                let error_string_encode = Vec::from_raw_parts(
1192                    error_blob.GetBufferPointer() as *mut u8,
1193                    string_len,
1194                    string_len,
1195                );
1196                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1197                log::error!("Shader compile error: {}", error_string);
1198                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1199            }
1200            Ok(compile_blob.unwrap())
1201        }
1202    }
1203}