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