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