directx_renderer.rs

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