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| DirectXPathVertex {
 375                xy_position: v.xy_position,
 376                content_mask: path.content_mask.bounds,
 377                sprite_index: i as u32,
 378            }));
 379
 380            sprites.push(PathSprite {
 381                bounds: path.bounds,
 382                color: path.color,
 383            });
 384        }
 385
 386        self.pipelines.paths_pipeline.update_buffer(
 387            &self.devices.device,
 388            &self.devices.device_context,
 389            &sprites,
 390            &vertices,
 391            &draw_indirect_commands,
 392        )?;
 393        self.pipelines.paths_pipeline.draw(
 394            &self.devices.device_context,
 395            paths.len(),
 396            &self.context.viewport,
 397            &self.globals.global_params_buffer,
 398        )
 399    }
 400
 401    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
 402        if underlines.is_empty() {
 403            return Ok(());
 404        }
 405        self.pipelines.underline_pipeline.update_buffer(
 406            &self.devices.device,
 407            &self.devices.device_context,
 408            underlines,
 409        )?;
 410        self.pipelines.underline_pipeline.draw(
 411            &self.devices.device_context,
 412            &self.context.viewport,
 413            &self.globals.global_params_buffer,
 414            underlines.len() as u32,
 415        )
 416    }
 417
 418    fn draw_monochrome_sprites(
 419        &mut self,
 420        texture_id: AtlasTextureId,
 421        sprites: &[MonochromeSprite],
 422    ) -> Result<()> {
 423        if sprites.is_empty() {
 424            return Ok(());
 425        }
 426        self.pipelines.mono_sprites.update_buffer(
 427            &self.devices.device,
 428            &self.devices.device_context,
 429            sprites,
 430        )?;
 431        let texture_view = self.atlas.get_texture_view(texture_id);
 432        self.pipelines.mono_sprites.draw_with_texture(
 433            &self.devices.device_context,
 434            &texture_view,
 435            &self.context.viewport,
 436            &self.globals.global_params_buffer,
 437            &self.globals.sampler,
 438            sprites.len() as u32,
 439        )
 440    }
 441
 442    fn draw_polychrome_sprites(
 443        &mut self,
 444        texture_id: AtlasTextureId,
 445        sprites: &[PolychromeSprite],
 446    ) -> Result<()> {
 447        if sprites.is_empty() {
 448            return Ok(());
 449        }
 450        self.pipelines.poly_sprites.update_buffer(
 451            &self.devices.device,
 452            &self.devices.device_context,
 453            sprites,
 454        )?;
 455        let texture_view = self.atlas.get_texture_view(texture_id);
 456        self.pipelines.poly_sprites.draw_with_texture(
 457            &self.devices.device_context,
 458            &texture_view,
 459            &self.context.viewport,
 460            &self.globals.global_params_buffer,
 461            &self.globals.sampler,
 462            sprites.len() as u32,
 463        )
 464    }
 465
 466    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
 467        if surfaces.is_empty() {
 468            return Ok(());
 469        }
 470        Ok(())
 471    }
 472}
 473
 474impl DirectXContext {
 475    pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
 476        // #[cfg(not(feature = "enable-renderdoc"))]
 477        // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
 478        // #[cfg(feature = "enable-renderdoc")]
 479        let swap_chain =
 480            create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
 481        // #[cfg(not(feature = "enable-renderdoc"))]
 482        // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
 483        // #[cfg(not(feature = "enable-renderdoc"))]
 484        // direct_composition.set_swap_chain(&swap_chain)?;
 485        let (render_target, render_target_view) =
 486            create_render_target_and_its_view(&swap_chain, &devices.device)?;
 487        // let back_buffer = [Some(set_render_target_view(
 488        //     &swap_chain,
 489        //     &devices.device,
 490        //     &devices.device_context,
 491        // )?)];
 492        let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?;
 493        let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
 494        unsafe {
 495            devices
 496                .device_context
 497                .OMSetRenderTargets(Some(&render_target_view), None);
 498        }
 499        set_rasterizer_state(&devices.device, &devices.device_context)?;
 500
 501        Ok(Self {
 502            swap_chain,
 503            render_target,
 504            render_target_view,
 505            msaa_target,
 506            msaa_view,
 507            viewport,
 508            // #[cfg(not(feature = "enable-renderdoc"))]
 509            // direct_composition,
 510        })
 511    }
 512}
 513
 514impl DirectXRenderPipelines {
 515    pub fn new(device: &ID3D11Device) -> Result<Self> {
 516        let shadow_pipeline = PipelineState::new(
 517            device,
 518            "shadow_pipeline",
 519            "shadow_vertex",
 520            "shadow_fragment",
 521            4,
 522        )?;
 523        let quad_pipeline =
 524            PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?;
 525        let paths_pipeline = PathsPipelineState::new(device)?;
 526        let underline_pipeline = PipelineState::new(
 527            device,
 528            "underline_pipeline",
 529            "underline_vertex",
 530            "underline_fragment",
 531            4,
 532        )?;
 533        let mono_sprites = PipelineState::new(
 534            device,
 535            "monochrome_sprite_pipeline",
 536            "monochrome_sprite_vertex",
 537            "monochrome_sprite_fragment",
 538            512,
 539        )?;
 540        let poly_sprites = PipelineState::new(
 541            device,
 542            "polychrome_sprite_pipeline",
 543            "polychrome_sprite_vertex",
 544            "polychrome_sprite_fragment",
 545            16,
 546        )?;
 547
 548        Ok(Self {
 549            shadow_pipeline,
 550            quad_pipeline,
 551            paths_pipeline,
 552            underline_pipeline,
 553            mono_sprites,
 554            poly_sprites,
 555        })
 556    }
 557}
 558
 559// #[cfg(not(feature = "enable-renderdoc"))]
 560// impl DirectComposition {
 561//     pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
 562//         let comp_device = get_comp_device(&dxgi_device)?;
 563//         let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
 564//         let comp_visual = unsafe { comp_device.CreateVisual() }?;
 565
 566//         Ok(Self {
 567//             comp_device,
 568//             comp_target,
 569//             comp_visual,
 570//         })
 571//     }
 572
 573//     pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
 574//         unsafe {
 575//             self.comp_visual.SetContent(swap_chain)?;
 576//             self.comp_target.SetRoot(&self.comp_visual)?;
 577//             self.comp_device.Commit()?;
 578//         }
 579//         Ok(())
 580//     }
 581// }
 582
 583impl DirectXGlobalElements {
 584    pub fn new(device: &ID3D11Device) -> Result<Self> {
 585        let global_params_buffer = unsafe {
 586            let desc = D3D11_BUFFER_DESC {
 587                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
 588                Usage: D3D11_USAGE_DYNAMIC,
 589                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
 590                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 591                ..Default::default()
 592            };
 593            let mut buffer = None;
 594            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
 595            [buffer]
 596        };
 597
 598        let sampler = unsafe {
 599            let desc = D3D11_SAMPLER_DESC {
 600                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
 601                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
 602                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
 603                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
 604                MipLODBias: 0.0,
 605                MaxAnisotropy: 1,
 606                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
 607                BorderColor: [0.0; 4],
 608                MinLOD: 0.0,
 609                MaxLOD: D3D11_FLOAT32_MAX,
 610            };
 611            let mut output = None;
 612            device.CreateSamplerState(&desc, Some(&mut output))?;
 613            [output]
 614        };
 615
 616        let blend_state = create_blend_state(device)?;
 617
 618        Ok(Self {
 619            global_params_buffer,
 620            sampler,
 621            blend_state,
 622        })
 623    }
 624}
 625
 626#[derive(Debug, Default)]
 627#[repr(C)]
 628struct GlobalParams {
 629    viewport_size: [f32; 2],
 630    _pad: u64,
 631}
 632
 633struct PipelineState<T> {
 634    label: &'static str,
 635    vertex: ID3D11VertexShader,
 636    fragment: ID3D11PixelShader,
 637    buffer: ID3D11Buffer,
 638    buffer_size: usize,
 639    view: [Option<ID3D11ShaderResourceView>; 1],
 640    _marker: std::marker::PhantomData<T>,
 641}
 642
 643struct PathsPipelineState {
 644    vertex: ID3D11VertexShader,
 645    fragment: ID3D11PixelShader,
 646    buffer: ID3D11Buffer,
 647    buffer_size: usize,
 648    vertex_buffer: Option<ID3D11Buffer>,
 649    vertex_buffer_size: usize,
 650    indirect_draw_buffer: ID3D11Buffer,
 651    indirect_buffer_size: usize,
 652    input_layout: ID3D11InputLayout,
 653    view: [Option<ID3D11ShaderResourceView>; 1],
 654}
 655
 656impl<T> PipelineState<T> {
 657    fn new(
 658        device: &ID3D11Device,
 659        label: &'static str,
 660        vertex_entry: &str,
 661        fragment_entry: &str,
 662        buffer_size: usize,
 663    ) -> Result<Self> {
 664        let vertex = {
 665            let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 666            let bytes = unsafe {
 667                std::slice::from_raw_parts(
 668                    shader_blob.GetBufferPointer() as *mut u8,
 669                    shader_blob.GetBufferSize(),
 670                )
 671            };
 672            create_vertex_shader(device, bytes)?
 673        };
 674        let fragment = {
 675            let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 676            let bytes = unsafe {
 677                std::slice::from_raw_parts(
 678                    shader_blob.GetBufferPointer() as *mut u8,
 679                    shader_blob.GetBufferSize(),
 680                )
 681            };
 682            create_fragment_shader(device, bytes)?
 683        };
 684        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
 685        let view = create_buffer_view(device, &buffer)?;
 686
 687        Ok(PipelineState {
 688            label,
 689            vertex,
 690            fragment,
 691            buffer,
 692            buffer_size,
 693            view,
 694            _marker: std::marker::PhantomData,
 695        })
 696    }
 697
 698    fn update_buffer(
 699        &mut self,
 700        device: &ID3D11Device,
 701        device_context: &ID3D11DeviceContext,
 702        data: &[T],
 703    ) -> Result<()> {
 704        if self.buffer_size < data.len() {
 705            let new_buffer_size = data.len().next_power_of_two();
 706            log::info!(
 707                "Updating {} buffer size from {} to {}",
 708                self.label,
 709                self.buffer_size,
 710                new_buffer_size
 711            );
 712            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
 713            let view = create_buffer_view(device, &buffer)?;
 714            self.buffer = buffer;
 715            self.view = view;
 716            self.buffer_size = new_buffer_size;
 717        }
 718        update_buffer(device_context, &self.buffer, data)
 719    }
 720
 721    fn draw(
 722        &self,
 723        device_context: &ID3D11DeviceContext,
 724        viewport: &[D3D11_VIEWPORT],
 725        global_params: &[Option<ID3D11Buffer>],
 726        instance_count: u32,
 727    ) -> Result<()> {
 728        set_pipeline_state(
 729            device_context,
 730            &self.view,
 731            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 732            viewport,
 733            &self.vertex,
 734            &self.fragment,
 735            global_params,
 736        );
 737        unsafe {
 738            device_context.DrawInstanced(4, instance_count, 0, 0);
 739        }
 740        Ok(())
 741    }
 742
 743    fn draw_with_texture(
 744        &self,
 745        device_context: &ID3D11DeviceContext,
 746        texture: &[Option<ID3D11ShaderResourceView>],
 747        viewport: &[D3D11_VIEWPORT],
 748        global_params: &[Option<ID3D11Buffer>],
 749        sampler: &[Option<ID3D11SamplerState>],
 750        instance_count: u32,
 751    ) -> Result<()> {
 752        set_pipeline_state(
 753            device_context,
 754            &self.view,
 755            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 756            viewport,
 757            &self.vertex,
 758            &self.fragment,
 759            global_params,
 760        );
 761        unsafe {
 762            device_context.PSSetSamplers(0, Some(sampler));
 763            device_context.VSSetShaderResources(0, Some(texture));
 764            device_context.PSSetShaderResources(0, Some(texture));
 765
 766            device_context.DrawInstanced(4, instance_count, 0, 0);
 767        }
 768        Ok(())
 769    }
 770}
 771
 772impl PathsPipelineState {
 773    fn new(device: &ID3D11Device) -> Result<Self> {
 774        let (vertex, vertex_shader) = {
 775            let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?;
 776            let bytes = unsafe {
 777                std::slice::from_raw_parts(
 778                    shader_blob.GetBufferPointer() as *mut u8,
 779                    shader_blob.GetBufferSize(),
 780                )
 781            };
 782            (create_vertex_shader(device, bytes)?, shader_blob)
 783        };
 784        let fragment = {
 785            let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?;
 786            let bytes = unsafe {
 787                std::slice::from_raw_parts(
 788                    shader_blob.GetBufferPointer() as *mut u8,
 789                    shader_blob.GetBufferSize(),
 790                )
 791            };
 792            create_fragment_shader(device, bytes)?
 793        };
 794        let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), 32)?;
 795        let view = create_buffer_view(device, &buffer)?;
 796        let vertex_buffer = Some(create_buffer(
 797            device,
 798            std::mem::size_of::<DirectXPathVertex>(),
 799            32,
 800        )?);
 801        let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?;
 802        // Create input layout
 803        let input_layout = unsafe {
 804            let shader_bytes = std::slice::from_raw_parts(
 805                vertex_shader.GetBufferPointer() as *const u8,
 806                vertex_shader.GetBufferSize(),
 807            );
 808            let mut layout = None;
 809            device.CreateInputLayout(
 810                &[
 811                    D3D11_INPUT_ELEMENT_DESC {
 812                        SemanticName: windows::core::s!("POSITION"),
 813                        SemanticIndex: 0,
 814                        Format: DXGI_FORMAT_R32G32_FLOAT,
 815                        InputSlot: 0,
 816                        AlignedByteOffset: 0,
 817                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 818                        InstanceDataStepRate: 0,
 819                    },
 820                    D3D11_INPUT_ELEMENT_DESC {
 821                        SemanticName: windows::core::s!("TEXCOORD"),
 822                        SemanticIndex: 0,
 823                        Format: DXGI_FORMAT_R32G32_FLOAT,
 824                        InputSlot: 0,
 825                        AlignedByteOffset: 8,
 826                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 827                        InstanceDataStepRate: 0,
 828                    },
 829                    D3D11_INPUT_ELEMENT_DESC {
 830                        SemanticName: windows::core::s!("TEXCOORD"),
 831                        SemanticIndex: 1,
 832                        Format: DXGI_FORMAT_R32G32_FLOAT,
 833                        InputSlot: 0,
 834                        AlignedByteOffset: 16,
 835                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 836                        InstanceDataStepRate: 0,
 837                    },
 838                    D3D11_INPUT_ELEMENT_DESC {
 839                        SemanticName: windows::core::s!("GLOBALIDX"),
 840                        SemanticIndex: 0,
 841                        Format: DXGI_FORMAT_R32_UINT,
 842                        InputSlot: 0,
 843                        AlignedByteOffset: 24,
 844                        InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA,
 845                        InstanceDataStepRate: 0,
 846                    },
 847                ],
 848                shader_bytes,
 849                Some(&mut layout),
 850            )?;
 851            layout.unwrap()
 852        };
 853
 854        Ok(Self {
 855            vertex,
 856            fragment,
 857            buffer,
 858            buffer_size: 32,
 859            vertex_buffer,
 860            vertex_buffer_size: 32,
 861            indirect_draw_buffer,
 862            indirect_buffer_size: 32,
 863            input_layout,
 864            view,
 865        })
 866    }
 867
 868    fn update_buffer(
 869        &mut self,
 870        device: &ID3D11Device,
 871        device_context: &ID3D11DeviceContext,
 872        buffer_data: &[PathSprite],
 873        vertices_data: &[DirectXPathVertex],
 874        draw_commands: &[DrawInstancedIndirectArgs],
 875    ) -> Result<()> {
 876        if self.buffer_size < buffer_data.len() {
 877            let new_buffer_size = buffer_data.len().next_power_of_two();
 878            log::info!(
 879                "Updating Paths Pipeline buffer size from {} to {}",
 880                self.buffer_size,
 881                new_buffer_size
 882            );
 883            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 884            let view = create_buffer_view(device, &buffer)?;
 885            self.buffer = buffer;
 886            self.view = view;
 887            self.buffer_size = new_buffer_size;
 888        }
 889        update_buffer(device_context, &self.buffer, buffer_data)?;
 890        if self.vertex_buffer_size < vertices_data.len() {
 891            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 892            log::info!(
 893                "Updating Paths Pipeline vertex buffer size from {} to {}",
 894                self.vertex_buffer_size,
 895                new_vertex_buffer_size
 896            );
 897            let vertex_buffer = create_buffer(
 898                device,
 899                std::mem::size_of::<DirectXPathVertex>(),
 900                new_vertex_buffer_size,
 901            )?;
 902            self.vertex_buffer = Some(vertex_buffer);
 903            self.vertex_buffer_size = new_vertex_buffer_size;
 904        }
 905        update_buffer(
 906            device_context,
 907            self.vertex_buffer.as_ref().unwrap(),
 908            vertices_data,
 909        )?;
 910        if self.indirect_buffer_size < draw_commands.len() {
 911            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 912            log::info!(
 913                "Updating Paths Pipeline indirect buffer size from {} to {}",
 914                self.indirect_buffer_size,
 915                new_indirect_buffer_size
 916            );
 917            let indirect_draw_buffer =
 918                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 919            self.indirect_draw_buffer = indirect_draw_buffer;
 920            self.indirect_buffer_size = new_indirect_buffer_size;
 921        }
 922        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 923        Ok(())
 924    }
 925
 926    fn draw(
 927        &self,
 928        device_context: &ID3D11DeviceContext,
 929        count: usize,
 930        viewport: &[D3D11_VIEWPORT],
 931        global_params: &[Option<ID3D11Buffer>],
 932    ) -> Result<()> {
 933        set_pipeline_state(
 934            device_context,
 935            &self.view,
 936            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 937            viewport,
 938            &self.vertex,
 939            &self.fragment,
 940            global_params,
 941        );
 942        unsafe {
 943            const STRIDE: u32 = std::mem::size_of::<DirectXPathVertex>() as u32;
 944            device_context.IASetVertexBuffers(
 945                0,
 946                1,
 947                Some(&self.vertex_buffer),
 948                Some(&STRIDE),
 949                Some(&0),
 950            );
 951            device_context.IASetInputLayout(&self.input_layout);
 952        }
 953        for i in 0..count {
 954            unsafe {
 955                device_context.DrawInstancedIndirect(
 956                    &self.indirect_draw_buffer,
 957                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 958                );
 959            }
 960        }
 961        Ok(())
 962    }
 963}
 964
 965#[repr(C)]
 966struct DirectXPathVertex {
 967    xy_position: Point<ScaledPixels>,
 968    content_mask: Bounds<ScaledPixels>,
 969    sprite_index: u32,
 970}
 971
 972#[derive(Clone, Debug, Eq, PartialEq)]
 973#[repr(C)]
 974struct PathSprite {
 975    bounds: Bounds<ScaledPixels>,
 976    color: Background,
 977}
 978
 979impl Drop for DirectXContext {
 980    fn drop(&mut self) {
 981        unsafe {
 982            ManuallyDrop::drop(&mut self.render_target);
 983            ManuallyDrop::drop(&mut self.swap_chain);
 984        }
 985    }
 986}
 987
 988#[inline]
 989fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 990    #[cfg(debug_assertions)]
 991    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 992    #[cfg(not(debug_assertions))]
 993    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 994    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 995}
 996
 997fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 998    for adapter_index in 0.. {
 999        let adapter: IDXGIAdapter1 = unsafe {
1000            dxgi_factory
1001                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
1002        }?;
1003        {
1004            let desc = unsafe { adapter.GetDesc1() }?;
1005            println!(
1006                "Select GPU: {}",
1007                String::from_utf16_lossy(&desc.Description)
1008            );
1009        }
1010        // Check to see whether the adapter supports Direct3D 11, but don't
1011        // create the actual device yet.
1012        if get_device(&adapter, None, None).log_err().is_some() {
1013            return Ok(adapter);
1014        }
1015    }
1016
1017    unreachable!()
1018}
1019
1020fn get_device(
1021    adapter: &IDXGIAdapter1,
1022    device: Option<*mut Option<ID3D11Device>>,
1023    context: Option<*mut Option<ID3D11DeviceContext>>,
1024) -> Result<()> {
1025    #[cfg(debug_assertions)]
1026    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
1027    #[cfg(not(debug_assertions))]
1028    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
1029    Ok(unsafe {
1030        D3D11CreateDevice(
1031            adapter,
1032            D3D_DRIVER_TYPE_UNKNOWN,
1033            HMODULE::default(),
1034            device_flags,
1035            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1036            D3D11_SDK_VERSION,
1037            device,
1038            None,
1039            context,
1040        )?
1041    })
1042}
1043
1044// #[cfg(not(feature = "enable-renderdoc"))]
1045// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1046//     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1047// }
1048
1049// fn create_swap_chain(
1050//     dxgi_factory: &IDXGIFactory6,
1051//     device: &ID3D11Device,
1052//     transparent: bool,
1053// ) -> Result<IDXGISwapChain1> {
1054//     let alpha_mode = if transparent {
1055//         DXGI_ALPHA_MODE_PREMULTIPLIED
1056//     } else {
1057//         DXGI_ALPHA_MODE_IGNORE
1058//     };
1059//     let desc = DXGI_SWAP_CHAIN_DESC1 {
1060//         Width: 1,
1061//         Height: 1,
1062//         Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1063//         Stereo: false.into(),
1064//         SampleDesc: DXGI_SAMPLE_DESC {
1065//             Count: 1,
1066//             Quality: 0,
1067//         },
1068//         BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1069//         BufferCount: BUFFER_COUNT as u32,
1070//         // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1071//         Scaling: DXGI_SCALING_STRETCH,
1072//         SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1073//         AlphaMode: alpha_mode,
1074//         Flags: 0,
1075//     };
1076//     Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1077// }
1078
1079// #[cfg(feature = "enable-renderdoc")]
1080fn create_swap_chain_default(
1081    dxgi_factory: &IDXGIFactory6,
1082    device: &ID3D11Device,
1083    hwnd: HWND,
1084    _transparent: bool,
1085) -> Result<ManuallyDrop<IDXGISwapChain1>> {
1086    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1087
1088    let desc = DXGI_SWAP_CHAIN_DESC1 {
1089        Width: 1,
1090        Height: 1,
1091        Format: RENDER_TARGET_FORMAT,
1092        Stereo: false.into(),
1093        SampleDesc: DXGI_SAMPLE_DESC {
1094            Count: 1,
1095            Quality: 0,
1096        },
1097        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1098        BufferCount: BUFFER_COUNT as u32,
1099        Scaling: DXGI_SCALING_STRETCH,
1100        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1101        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1102        Flags: 0,
1103    };
1104    let swap_chain =
1105        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1106    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1107    Ok(ManuallyDrop::new(swap_chain))
1108}
1109
1110#[inline]
1111fn create_render_target_and_its_view(
1112    swap_chain: &IDXGISwapChain1,
1113    device: &ID3D11Device,
1114) -> Result<(
1115    ManuallyDrop<ID3D11Texture2D>,
1116    [Option<ID3D11RenderTargetView>; 1],
1117)> {
1118    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1119    let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1120        Format: BACK_BUFFER_FORMAT,
1121        ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1122        ..Default::default()
1123    };
1124    let mut render_target_view = None;
1125    unsafe {
1126        device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))?
1127    };
1128    Ok((
1129        ManuallyDrop::new(render_target),
1130        [Some(render_target_view.unwrap())],
1131    ))
1132}
1133
1134#[inline]
1135fn set_render_target_view(
1136    swap_chain: &IDXGISwapChain1,
1137    device: &ID3D11Device,
1138    device_context: &ID3D11DeviceContext,
1139) -> Result<ID3D11RenderTargetView> {
1140    // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
1141    // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
1142    let back_buffer = unsafe {
1143        let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
1144        let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1145            Format: BACK_BUFFER_FORMAT,
1146            ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1147            ..Default::default()
1148        };
1149        let mut buffer: Option<ID3D11RenderTargetView> = None;
1150        device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?;
1151        buffer.unwrap()
1152    };
1153    unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
1154    Ok(back_buffer)
1155}
1156
1157#[inline]
1158fn create_msaa_target_and_its_view(
1159    device: &ID3D11Device,
1160    width: u32,
1161    height: u32,
1162) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
1163    let msaa_target = unsafe {
1164        let mut output = None;
1165        let desc = D3D11_TEXTURE2D_DESC {
1166            Width: width,
1167            Height: height,
1168            MipLevels: 1,
1169            ArraySize: 1,
1170            Format: BACK_BUFFER_FORMAT,
1171            SampleDesc: DXGI_SAMPLE_DESC {
1172                Count: 4,
1173                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1174            },
1175            Usage: D3D11_USAGE_DEFAULT,
1176            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1177            CPUAccessFlags: 0,
1178            MiscFlags: 0,
1179        };
1180        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1181        output.unwrap()
1182    };
1183    let msaa_view = unsafe {
1184        let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1185            Format: BACK_BUFFER_FORMAT,
1186            ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS,
1187            ..Default::default()
1188        };
1189        let mut output = None;
1190        device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?;
1191        output.unwrap()
1192    };
1193    Ok((msaa_target, msaa_view))
1194}
1195
1196#[inline]
1197fn set_viewport(
1198    device_context: &ID3D11DeviceContext,
1199    width: f32,
1200    height: f32,
1201) -> [D3D11_VIEWPORT; 1] {
1202    let viewport = [D3D11_VIEWPORT {
1203        TopLeftX: 0.0,
1204        TopLeftY: 0.0,
1205        Width: width,
1206        Height: height,
1207        MinDepth: 0.0,
1208        MaxDepth: 1.0,
1209    }];
1210    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1211    viewport
1212}
1213
1214#[inline]
1215fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1216    let desc = D3D11_RASTERIZER_DESC {
1217        FillMode: D3D11_FILL_SOLID,
1218        CullMode: D3D11_CULL_NONE,
1219        FrontCounterClockwise: false.into(),
1220        DepthBias: 0,
1221        DepthBiasClamp: 0.0,
1222        SlopeScaledDepthBias: 0.0,
1223        DepthClipEnable: true.into(),
1224        ScissorEnable: false.into(),
1225        // MultisampleEnable: false.into(),
1226        MultisampleEnable: true.into(),
1227        AntialiasedLineEnable: false.into(),
1228    };
1229    let rasterizer_state = unsafe {
1230        let mut state = None;
1231        device.CreateRasterizerState(&desc, Some(&mut state))?;
1232        state.unwrap()
1233    };
1234    unsafe { device_context.RSSetState(&rasterizer_state) };
1235    Ok(())
1236}
1237
1238// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1239#[inline]
1240fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1241    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1242    // device performs the blend in linear space, which is ideal.
1243    let mut desc = D3D11_BLEND_DESC::default();
1244    desc.RenderTarget[0].BlendEnable = true.into();
1245    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1246    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1247    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1248    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1249    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1250    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1251    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1252    unsafe {
1253        let mut state = None;
1254        device.CreateBlendState(&desc, Some(&mut state))?;
1255        Ok(state.unwrap())
1256    }
1257}
1258
1259#[inline]
1260fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1261    unsafe {
1262        let mut shader = None;
1263        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1264        Ok(shader.unwrap())
1265    }
1266}
1267
1268#[inline]
1269fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1270    unsafe {
1271        let mut shader = None;
1272        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1273        Ok(shader.unwrap())
1274    }
1275}
1276
1277#[inline]
1278fn create_buffer(
1279    device: &ID3D11Device,
1280    element_size: usize,
1281    buffer_size: usize,
1282) -> Result<ID3D11Buffer> {
1283    let desc = D3D11_BUFFER_DESC {
1284        ByteWidth: (element_size * buffer_size) as u32,
1285        Usage: D3D11_USAGE_DYNAMIC,
1286        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1287        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1288        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1289        StructureByteStride: element_size as u32,
1290    };
1291    let mut buffer = None;
1292    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1293    Ok(buffer.unwrap())
1294}
1295
1296#[inline]
1297fn create_buffer_view(
1298    device: &ID3D11Device,
1299    buffer: &ID3D11Buffer,
1300) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1301    let mut view = None;
1302    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1303    Ok([view])
1304}
1305
1306#[inline]
1307fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1308    let desc = D3D11_BUFFER_DESC {
1309        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1310        Usage: D3D11_USAGE_DYNAMIC,
1311        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1312        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1313        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1314        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1315    };
1316    let mut buffer = None;
1317    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1318    Ok(buffer.unwrap())
1319}
1320
1321#[inline]
1322fn pre_draw(
1323    device_context: &ID3D11DeviceContext,
1324    global_params_buffer: &[Option<ID3D11Buffer>; 1],
1325    view_port: &[D3D11_VIEWPORT; 1],
1326    render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1327    clear_color: [f32; 4],
1328    blend_state: &ID3D11BlendState,
1329) -> Result<()> {
1330    let global_params = global_params_buffer[0].as_ref().unwrap();
1331    update_buffer(
1332        device_context,
1333        global_params,
1334        &[GlobalParams {
1335            viewport_size: [view_port[0].Width, view_port[0].Height],
1336            ..Default::default()
1337        }],
1338    )?;
1339    unsafe {
1340        device_context.RSSetViewports(Some(view_port));
1341        device_context.OMSetRenderTargets(Some(render_target_view), None);
1342        device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1343        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1344    }
1345    Ok(())
1346}
1347
1348#[inline]
1349fn update_buffer<T>(
1350    device_context: &ID3D11DeviceContext,
1351    buffer: &ID3D11Buffer,
1352    data: &[T],
1353) -> Result<()> {
1354    unsafe {
1355        let mut dest = std::mem::zeroed();
1356        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1357        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1358        device_context.Unmap(buffer, 0);
1359    }
1360    Ok(())
1361}
1362
1363#[inline]
1364fn set_pipeline_state(
1365    device_context: &ID3D11DeviceContext,
1366    buffer_view: &[Option<ID3D11ShaderResourceView>],
1367    topology: D3D_PRIMITIVE_TOPOLOGY,
1368    viewport: &[D3D11_VIEWPORT],
1369    vertex_shader: &ID3D11VertexShader,
1370    fragment_shader: &ID3D11PixelShader,
1371    global_params: &[Option<ID3D11Buffer>],
1372) {
1373    unsafe {
1374        device_context.VSSetShaderResources(1, Some(buffer_view));
1375        device_context.PSSetShaderResources(1, Some(buffer_view));
1376        device_context.IASetPrimitiveTopology(topology);
1377        device_context.RSSetViewports(Some(viewport));
1378        device_context.VSSetShader(vertex_shader, None);
1379        device_context.PSSetShader(fragment_shader, None);
1380        device_context.VSSetConstantBuffers(0, Some(global_params));
1381        device_context.PSSetConstantBuffers(0, Some(global_params));
1382    }
1383}
1384
1385const BUFFER_COUNT: usize = 3;
1386
1387mod shader_resources {
1388    use anyhow::Result;
1389    use windows::Win32::Graphics::Direct3D::{
1390        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1391        ID3DBlob,
1392    };
1393    use windows_core::{HSTRING, PCSTR};
1394
1395    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1396        unsafe {
1397            let mut entry = entry.to_owned();
1398            let mut target = target.to_owned();
1399            let mut compile_blob = None;
1400            let mut error_blob = None;
1401            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1402                .join("src/platform/windows/shaders.hlsl")
1403                .canonicalize()
1404                .unwrap();
1405            entry.push_str("\0");
1406            target.push_str("\0");
1407            let entry_point = PCSTR::from_raw(entry.as_ptr());
1408            let target_cstr = PCSTR::from_raw(target.as_ptr());
1409            #[cfg(debug_assertions)]
1410            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1411            #[cfg(not(debug_assertions))]
1412            let compile_flag = 0;
1413            let ret = D3DCompileFromFile(
1414                &HSTRING::from(shader_path.to_str().unwrap()),
1415                None,
1416                None,
1417                entry_point,
1418                target_cstr,
1419                compile_flag,
1420                0,
1421                &mut compile_blob,
1422                Some(&mut error_blob),
1423            );
1424            if ret.is_err() {
1425                let Some(error_blob) = error_blob else {
1426                    return Err(anyhow::anyhow!("{ret:?}"));
1427                };
1428                let string_len = error_blob.GetBufferSize();
1429                let error_string_encode = Vec::from_raw_parts(
1430                    error_blob.GetBufferPointer() as *mut u8,
1431                    string_len,
1432                    string_len,
1433                );
1434                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1435                log::error!("Shader compile error: {}", error_string);
1436                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1437            }
1438            Ok(compile_blob.unwrap())
1439        }
1440    }
1441}