directx_renderer.rs

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