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        set_pipeline_state(
 620            device_context,
 621            &self.view,
 622            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 623            viewport,
 624            &self.vertex,
 625            &self.fragment,
 626            global_params,
 627        );
 628        unsafe {
 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        set_pipeline_state(
 644            device_context,
 645            &self.view,
 646            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 647            viewport,
 648            &self.vertex,
 649            &self.fragment,
 650            global_params,
 651        );
 652        unsafe {
 653            device_context.PSSetSamplers(0, Some(sampler));
 654            device_context.VSSetShaderResources(0, Some(texture));
 655            device_context.PSSetShaderResources(0, Some(texture));
 656
 657            device_context.DrawInstanced(4, instance_count, 0, 0);
 658        }
 659        Ok(())
 660    }
 661}
 662
 663impl PathsPipelineState {
 664    fn new(device: &ID3D11Device) -> Result<Self> {
 665        let (vertex, vertex_shader) = {
 666            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 667            let bytes = unsafe {
 668                std::slice::from_raw_parts(
 669                    shader_blob.GetBufferPointer() as *mut u8,
 670                    shader_blob.GetBufferSize(),
 671                )
 672            };
 673            (create_vertex_shader(device, bytes)?, shader_blob)
 674        };
 675        let fragment = {
 676            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 677            let bytes = unsafe {
 678                std::slice::from_raw_parts(
 679                    shader_blob.GetBufferPointer() as *mut u8,
 680                    shader_blob.GetBufferSize(),
 681                )
 682            };
 683            create_fragment_shader(device, bytes)?
 684        };
 685        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 686        let view = create_buffer_view(device, &buffer)?;
 687        let vertex_buffer = Some(create_buffer(
 688            device,
 689            std::mem::size_of::<PathVertex<ScaledPixels>>(),
 690            32,
 691        )?);
 692        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 693        // Create input layout
 694        let input_layout = unsafe {
 695            let shader_bytes = std::slice::from_raw_parts(
 696                vertex_shader.GetBufferPointer() as *const u8,
 697                vertex_shader.GetBufferSize(),
 698            );
 699            let mut layout = None;
 700            device.CreateInputLayout(
 701                &[
 702                    D3D11_INPUT_ELEMENT_DESC {
 703                        SemanticName: windows::core::s!("POSITION"),
 704                        SemanticIndex: 0,
 705                        Format: DXGI_FORMAT_R32G32_FLOAT,
 706                        InputSlot: 0,
 707                        AlignedByteOffset: 0,
 708                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 709                        InstanceDataStepRate: 0,
 710                    },
 711                    D3D11_INPUT_ELEMENT_DESC {
 712                        SemanticName: windows::core::s!("TEXCOORD"),
 713                        SemanticIndex: 0,
 714                        Format: DXGI_FORMAT_R32G32_FLOAT,
 715                        InputSlot: 0,
 716                        AlignedByteOffset: 8,
 717                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 718                        InstanceDataStepRate: 0,
 719                    },
 720                    D3D11_INPUT_ELEMENT_DESC {
 721                        SemanticName: windows::core::s!("TEXCOORD"),
 722                        SemanticIndex: 1,
 723                        Format: DXGI_FORMAT_R32G32_FLOAT,
 724                        InputSlot: 0,
 725                        AlignedByteOffset: 16,
 726                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 727                        InstanceDataStepRate: 0,
 728                    },
 729                ],
 730                shader_bytes,
 731                Some(&mut layout),
 732            )?;
 733            layout.unwrap()
 734        };
 735
 736        Ok(Self {
 737            vertex,
 738            fragment,
 739            buffer,
 740            buffer_size: 32,
 741            vertex_buffer,
 742            vertex_buffer_size: 32,
 743            indirect_draw_buffer,
 744            indirect_buffer_size: 32,
 745            input_layout,
 746            view,
 747        })
 748    }
 749
 750    fn update_buffer(
 751        &mut self,
 752        device: &ID3D11Device,
 753        device_context: &ID3D11DeviceContext,
 754        buffer_data: &[PathSprite],
 755        vertices_data: &[PathVertex<ScaledPixels>],
 756        draw_commands: &[DrawInstancedIndirectArgs],
 757    ) -> Result<()> {
 758        if self.buffer_size < buffer_data.len() {
 759            let new_buffer_size = buffer_data.len().next_power_of_two();
 760            log::info!(
 761                "Updating Paths Pipeline buffer size from {} to {}",
 762                self.buffer_size,
 763                new_buffer_size
 764            );
 765            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 766            let view = create_buffer_view(device, &buffer)?;
 767            self.buffer = buffer;
 768            self.view = view;
 769            self.buffer_size = new_buffer_size;
 770        }
 771        update_buffer(device_context, &self.buffer, buffer_data)?;
 772        if self.vertex_buffer_size < vertices_data.len() {
 773            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 774            log::info!(
 775                "Updating Paths Pipeline vertex buffer size from {} to {}",
 776                self.vertex_buffer_size,
 777                new_vertex_buffer_size
 778            );
 779            let vertex_buffer = create_buffer(
 780                device,
 781                std::mem::size_of::<PathVertex<ScaledPixels>>(),
 782                new_vertex_buffer_size,
 783            )?;
 784            self.vertex_buffer = Some(vertex_buffer);
 785            self.vertex_buffer_size = new_vertex_buffer_size;
 786        }
 787        update_buffer(
 788            device_context,
 789            self.vertex_buffer.as_ref().unwrap(),
 790            vertices_data,
 791        )?;
 792        if self.indirect_buffer_size < draw_commands.len() {
 793            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 794            log::info!(
 795                "Updating Paths Pipeline indirect buffer size from {} to {}",
 796                self.indirect_buffer_size,
 797                new_indirect_buffer_size
 798            );
 799            let indirect_draw_buffer =
 800                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 801            self.indirect_draw_buffer = indirect_draw_buffer;
 802            self.indirect_buffer_size = new_indirect_buffer_size;
 803        }
 804        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 805        Ok(())
 806    }
 807
 808    fn draw(
 809        &self,
 810        device_context: &ID3D11DeviceContext,
 811        count: usize,
 812        viewport: &[D3D11_VIEWPORT],
 813        global_params: &[Option<ID3D11Buffer>],
 814    ) -> Result<()> {
 815        set_pipeline_state(
 816            device_context,
 817            &self.view,
 818            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 819            viewport,
 820            &self.vertex,
 821            &self.fragment,
 822            global_params,
 823        );
 824        unsafe {
 825            const STRIDE: u32 = std::mem::size_of::<PathVertex<ScaledPixels>>() as u32;
 826            device_context.IASetVertexBuffers(
 827                0,
 828                1,
 829                Some(&self.vertex_buffer),
 830                Some(&STRIDE),
 831                Some(&0),
 832            );
 833            device_context.IASetInputLayout(&self.input_layout);
 834        }
 835        for i in 0..count {
 836            unsafe {
 837                device_context.DrawInstancedIndirect(
 838                    &self.indirect_draw_buffer,
 839                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 840                );
 841            }
 842        }
 843        Ok(())
 844    }
 845}
 846
 847#[derive(Clone, Debug, Eq, PartialEq)]
 848#[repr(C)]
 849struct PathSprite {
 850    bounds: Bounds<ScaledPixels>,
 851    color: Background,
 852}
 853
 854#[inline]
 855fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 856    #[cfg(debug_assertions)]
 857    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 858    #[cfg(not(debug_assertions))]
 859    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 860    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 861}
 862
 863fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 864    for adapter_index in 0.. {
 865        let adapter: IDXGIAdapter1 = unsafe {
 866            dxgi_factory
 867                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 868        }?;
 869        {
 870            let desc = unsafe { adapter.GetDesc1() }?;
 871            println!(
 872                "Select GPU: {}",
 873                String::from_utf16_lossy(&desc.Description)
 874            );
 875        }
 876        // Check to see whether the adapter supports Direct3D 11, but don't
 877        // create the actual device yet.
 878        if get_device(&adapter, None, None).log_err().is_some() {
 879            return Ok(adapter);
 880        }
 881    }
 882
 883    unreachable!()
 884}
 885
 886fn get_device(
 887    adapter: &IDXGIAdapter1,
 888    device: Option<*mut Option<ID3D11Device>>,
 889    context: Option<*mut Option<ID3D11DeviceContext>>,
 890) -> Result<()> {
 891    #[cfg(debug_assertions)]
 892    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
 893    #[cfg(not(debug_assertions))]
 894    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 895    Ok(unsafe {
 896        D3D11CreateDevice(
 897            adapter,
 898            D3D_DRIVER_TYPE_UNKNOWN,
 899            HMODULE::default(),
 900            device_flags,
 901            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
 902            D3D11_SDK_VERSION,
 903            device,
 904            None,
 905            context,
 906        )?
 907    })
 908}
 909
 910// #[cfg(not(feature = "enable-renderdoc"))]
 911// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
 912//     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
 913// }
 914
 915// fn create_swap_chain(
 916//     dxgi_factory: &IDXGIFactory6,
 917//     device: &ID3D11Device,
 918//     transparent: bool,
 919// ) -> Result<IDXGISwapChain1> {
 920//     let alpha_mode = if transparent {
 921//         DXGI_ALPHA_MODE_PREMULTIPLIED
 922//     } else {
 923//         DXGI_ALPHA_MODE_IGNORE
 924//     };
 925//     let desc = DXGI_SWAP_CHAIN_DESC1 {
 926//         Width: 1,
 927//         Height: 1,
 928//         Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 929//         Stereo: false.into(),
 930//         SampleDesc: DXGI_SAMPLE_DESC {
 931//             Count: 1,
 932//             Quality: 0,
 933//         },
 934//         BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 935//         BufferCount: BUFFER_COUNT as u32,
 936//         // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
 937//         Scaling: DXGI_SCALING_STRETCH,
 938//         SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 939//         AlphaMode: alpha_mode,
 940//         Flags: 0,
 941//     };
 942//     Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
 943// }
 944
 945// #[cfg(feature = "enable-renderdoc")]
 946fn create_swap_chain_default(
 947    dxgi_factory: &IDXGIFactory6,
 948    device: &ID3D11Device,
 949    hwnd: HWND,
 950    _transparent: bool,
 951) -> Result<IDXGISwapChain1> {
 952    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
 953
 954    let desc = DXGI_SWAP_CHAIN_DESC1 {
 955        Width: 1,
 956        Height: 1,
 957        Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 958        Stereo: false.into(),
 959        SampleDesc: DXGI_SAMPLE_DESC {
 960            Count: 1,
 961            Quality: 0,
 962        },
 963        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 964        BufferCount: BUFFER_COUNT as u32,
 965        Scaling: DXGI_SCALING_STRETCH,
 966        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 967        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
 968        Flags: 0,
 969    };
 970    let swap_chain =
 971        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
 972    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
 973    Ok(swap_chain)
 974}
 975
 976#[inline]
 977fn set_render_target_view(
 978    swap_chain: &IDXGISwapChain1,
 979    device: &ID3D11Device,
 980    device_context: &ID3D11DeviceContext,
 981) -> Result<ID3D11RenderTargetView> {
 982    // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
 983    // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
 984    let back_buffer = unsafe {
 985        let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
 986        let mut buffer: Option<ID3D11RenderTargetView> = None;
 987        device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?;
 988        buffer.unwrap()
 989    };
 990    unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
 991    Ok(back_buffer)
 992}
 993
 994#[inline]
 995fn set_viewport(
 996    device_context: &ID3D11DeviceContext,
 997    width: f32,
 998    height: f32,
 999) -> [D3D11_VIEWPORT; 1] {
1000    let viewport = [D3D11_VIEWPORT {
1001        TopLeftX: 0.0,
1002        TopLeftY: 0.0,
1003        Width: width,
1004        Height: height,
1005        MinDepth: 0.0,
1006        MaxDepth: 1.0,
1007    }];
1008    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1009    viewport
1010}
1011
1012#[inline]
1013fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1014    let desc = D3D11_RASTERIZER_DESC {
1015        FillMode: D3D11_FILL_SOLID,
1016        CullMode: D3D11_CULL_NONE,
1017        // FrontCounterClockwise: true.into(),
1018        FrontCounterClockwise: false.into(),
1019        DepthBias: 0,
1020        DepthBiasClamp: 0.0,
1021        SlopeScaledDepthBias: 0.0,
1022        DepthClipEnable: true.into(),
1023        ScissorEnable: false.into(),
1024        MultisampleEnable: false.into(),
1025        AntialiasedLineEnable: false.into(),
1026    };
1027    let rasterizer_state = unsafe {
1028        let mut state = None;
1029        device.CreateRasterizerState(&desc, Some(&mut state))?;
1030        state.unwrap()
1031    };
1032    unsafe { device_context.RSSetState(&rasterizer_state) };
1033    Ok(())
1034}
1035
1036// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1037#[inline]
1038fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1039    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1040    // device performs the blend in linear space, which is ideal.
1041    let mut desc = D3D11_BLEND_DESC::default();
1042    desc.RenderTarget[0].BlendEnable = true.into();
1043    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1044    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1045    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1046    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1047    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1048    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1049    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1050    unsafe {
1051        let mut state = None;
1052        device.CreateBlendState(&desc, Some(&mut state))?;
1053        Ok(state.unwrap())
1054    }
1055}
1056
1057#[inline]
1058fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1059    unsafe {
1060        let mut shader = None;
1061        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1062        Ok(shader.unwrap())
1063    }
1064}
1065
1066#[inline]
1067fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1068    unsafe {
1069        let mut shader = None;
1070        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1071        Ok(shader.unwrap())
1072    }
1073}
1074
1075#[inline]
1076fn create_buffer(
1077    device: &ID3D11Device,
1078    element_size: usize,
1079    buffer_size: usize,
1080) -> Result<ID3D11Buffer> {
1081    let desc = D3D11_BUFFER_DESC {
1082        ByteWidth: (element_size * buffer_size) as u32,
1083        Usage: D3D11_USAGE_DYNAMIC,
1084        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1085        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1086        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1087        StructureByteStride: element_size as u32,
1088    };
1089    let mut buffer = None;
1090    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1091    Ok(buffer.unwrap())
1092}
1093
1094#[inline]
1095fn create_buffer_view(
1096    device: &ID3D11Device,
1097    buffer: &ID3D11Buffer,
1098) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1099    let mut view = None;
1100    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1101    Ok([view])
1102}
1103
1104#[inline]
1105fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1106    let desc = D3D11_BUFFER_DESC {
1107        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1108        Usage: D3D11_USAGE_DYNAMIC,
1109        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1110        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1111        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1112        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1113    };
1114    let mut buffer = None;
1115    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1116    Ok(buffer.unwrap())
1117}
1118
1119#[inline]
1120fn pre_draw(
1121    device_context: &ID3D11DeviceContext,
1122    global_params_buffer: &[Option<ID3D11Buffer>; 1],
1123    view_port: &[D3D11_VIEWPORT; 1],
1124    render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1125    clear_color: [f32; 4],
1126    blend_state: &ID3D11BlendState,
1127) -> Result<()> {
1128    let global_params = global_params_buffer[0].as_ref().unwrap();
1129    update_buffer(
1130        device_context,
1131        global_params,
1132        &[GlobalParams {
1133            viewport_size: [view_port[0].Width, view_port[0].Height],
1134            ..Default::default()
1135        }],
1136    )?;
1137    unsafe {
1138        device_context.RSSetViewports(Some(view_port));
1139        device_context.OMSetRenderTargets(Some(render_target_view), None);
1140        device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1141        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1142    }
1143    Ok(())
1144}
1145
1146#[inline]
1147fn update_buffer<T>(
1148    device_context: &ID3D11DeviceContext,
1149    buffer: &ID3D11Buffer,
1150    data: &[T],
1151) -> Result<()> {
1152    unsafe {
1153        let mut dest = std::mem::zeroed();
1154        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1155        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1156        device_context.Unmap(buffer, 0);
1157    }
1158    Ok(())
1159}
1160
1161#[inline]
1162fn set_pipeline_state(
1163    device_context: &ID3D11DeviceContext,
1164    buffer_view: &[Option<ID3D11ShaderResourceView>],
1165    topology: D3D_PRIMITIVE_TOPOLOGY,
1166    viewport: &[D3D11_VIEWPORT],
1167    vertex_shader: &ID3D11VertexShader,
1168    fragment_shader: &ID3D11PixelShader,
1169    global_params: &[Option<ID3D11Buffer>],
1170) {
1171    unsafe {
1172        device_context.VSSetShaderResources(1, Some(buffer_view));
1173        device_context.PSSetShaderResources(1, Some(buffer_view));
1174        device_context.IASetPrimitiveTopology(topology);
1175        device_context.RSSetViewports(Some(viewport));
1176        device_context.VSSetShader(vertex_shader, None);
1177        device_context.PSSetShader(fragment_shader, None);
1178        device_context.VSSetConstantBuffers(0, Some(global_params));
1179        device_context.PSSetConstantBuffers(0, Some(global_params));
1180    }
1181}
1182
1183const BUFFER_COUNT: usize = 3;
1184
1185mod shader_resources {
1186    use anyhow::Result;
1187    use windows::Win32::Graphics::Direct3D::{
1188        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1189        ID3DBlob,
1190    };
1191    use windows_core::{HSTRING, PCSTR};
1192
1193    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1194        unsafe {
1195            let mut entry = entry.to_owned();
1196            let mut target = target.to_owned();
1197            let mut compile_blob = None;
1198            let mut error_blob = None;
1199            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1200                .join("src/platform/windows/shaders.hlsl")
1201                .canonicalize()
1202                .unwrap();
1203            entry.push_str("\0");
1204            target.push_str("\0");
1205            let entry_point = PCSTR::from_raw(entry.as_ptr());
1206            let target_cstr = PCSTR::from_raw(target.as_ptr());
1207            #[cfg(debug_assertions)]
1208            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1209            #[cfg(not(debug_assertions))]
1210            let compile_flag = 0;
1211            let ret = D3DCompileFromFile(
1212                &HSTRING::from(shader_path.to_str().unwrap()),
1213                None,
1214                None,
1215                entry_point,
1216                target_cstr,
1217                compile_flag,
1218                0,
1219                &mut compile_blob,
1220                Some(&mut error_blob),
1221            );
1222            if ret.is_err() {
1223                let Some(error_blob) = error_blob else {
1224                    return Err(anyhow::anyhow!("{ret:?}"));
1225                };
1226                let string_len = error_blob.GetBufferSize();
1227                let error_string_encode = Vec::from_raw_parts(
1228                    error_blob.GetBufferPointer() as *mut u8,
1229                    string_len,
1230                    string_len,
1231                );
1232                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1233                log::error!("Shader compile error: {}", error_string);
1234                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1235            }
1236            Ok(compile_blob.unwrap())
1237        }
1238    }
1239}