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;
  22const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
  23
  24pub(crate) struct DirectXRenderer {
  25    atlas: Arc<DirectXAtlas>,
  26    devices: DirectXDevices,
  27    context: DirectXContext,
  28    globals: DirectXGlobalElements,
  29    pipelines: DirectXRenderPipelines,
  30    hwnd: HWND,
  31    transparent: bool,
  32}
  33
  34#[derive(Clone)]
  35pub(crate) struct DirectXDevices {
  36    dxgi_factory: IDXGIFactory6,
  37    dxgi_device: IDXGIDevice,
  38    device: ID3D11Device,
  39    device_context: ID3D11DeviceContext,
  40}
  41
  42struct DirectXContext {
  43    swap_chain: ManuallyDrop<IDXGISwapChain1>,
  44    render_target: ManuallyDrop<ID3D11Texture2D>,
  45    render_target_view: [Option<ID3D11RenderTargetView>; 1],
  46    msaa_target: ID3D11Texture2D,
  47    msaa_view: ID3D11RenderTargetView,
  48    viewport: [D3D11_VIEWPORT; 1],
  49    // #[cfg(not(feature = "enable-renderdoc"))]
  50    // direct_composition: DirectComposition,
  51}
  52
  53struct DirectXRenderPipelines {
  54    shadow_pipeline: PipelineState<Shadow>,
  55    quad_pipeline: PipelineState<Quad>,
  56    paths_pipeline: PathsPipelineState,
  57    underline_pipeline: PipelineState<Underline>,
  58    mono_sprites: PipelineState<MonochromeSprite>,
  59    poly_sprites: PipelineState<PolychromeSprite>,
  60}
  61
  62struct DirectXGlobalElements {
  63    global_params_buffer: [Option<ID3D11Buffer>; 1],
  64    sampler: [Option<ID3D11SamplerState>; 1],
  65    blend_state: ID3D11BlendState,
  66}
  67
  68#[repr(C)]
  69struct DrawInstancedIndirectArgs {
  70    vertex_count_per_instance: u32,
  71    instance_count: u32,
  72    start_vertex_location: u32,
  73    start_instance_location: u32,
  74}
  75
  76// #[cfg(not(feature = "enable-renderdoc"))]
  77// struct DirectComposition {
  78//     comp_device: IDCompositionDevice,
  79//     comp_target: IDCompositionTarget,
  80//     comp_visual: IDCompositionVisual,
  81// }
  82
  83impl DirectXDevices {
  84    pub(crate) fn new() -> Result<Self> {
  85        let dxgi_factory = get_dxgi_factory()?;
  86        let adapter = get_adapter(&dxgi_factory)?;
  87        let (device, device_context) = {
  88            let mut device: Option<ID3D11Device> = None;
  89            let mut context: Option<ID3D11DeviceContext> = None;
  90            get_device(&adapter, Some(&mut device), Some(&mut context))?;
  91            (device.unwrap(), context.unwrap())
  92        };
  93        let dxgi_device: IDXGIDevice = device.cast()?;
  94
  95        Ok(Self {
  96            dxgi_factory,
  97            dxgi_device,
  98            device,
  99            device_context,
 100        })
 101    }
 102}
 103
 104impl DirectXRenderer {
 105    pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
 106        let atlas = Arc::new(DirectXAtlas::new(
 107            devices.device.clone(),
 108            devices.device_context.clone(),
 109        ));
 110        let context = DirectXContext::new(devices, hwnd, transparent)?;
 111        let globals = DirectXGlobalElements::new(&devices.device)?;
 112        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
 113        Ok(DirectXRenderer {
 114            atlas,
 115            devices: devices.clone(),
 116            context,
 117            globals,
 118            pipelines,
 119            hwnd,
 120            transparent,
 121        })
 122    }
 123
 124    pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
 125        self.atlas.clone()
 126    }
 127
 128    fn pre_draw(&self) -> Result<()> {
 129        update_buffer(
 130            &self.devices.device_context,
 131            self.globals.global_params_buffer[0].as_ref().unwrap(),
 132            &[GlobalParams {
 133                viewport_size: [
 134                    self.context.viewport[0].Width,
 135                    self.context.viewport[0].Height,
 136                ],
 137                ..Default::default()
 138            }],
 139        )?;
 140        unsafe {
 141            self.devices
 142                .device_context
 143                .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]);
 144            self.devices
 145                .device_context
 146                .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None);
 147            self.devices
 148                .device_context
 149                .RSSetViewports(Some(&self.context.viewport));
 150            self.devices.device_context.OMSetBlendState(
 151                &self.globals.blend_state,
 152                None,
 153                0xFFFFFFFF,
 154            );
 155        }
 156        Ok(())
 157    }
 158
 159    pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
 160        // pre_draw(
 161        //     &self.devices.device_context,
 162        //     &self.globals.global_params_buffer,
 163        //     &self.context.viewport,
 164        //     &self.context.back_buffer,
 165        //     [0.0, 0.0, 0.0, 0.0],
 166        //     &self.globals.blend_state,
 167        // )?;
 168        println!("Pre-draw: {:?}", self.context.render_target_view);
 169        self.pre_draw()?;
 170        for batch in scene.batches() {
 171            match batch {
 172                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
 173                PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
 174                PrimitiveBatch::Paths(paths) => self.draw_paths(paths),
 175                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
 176                PrimitiveBatch::MonochromeSprites {
 177                    texture_id,
 178                    sprites,
 179                } => self.draw_monochrome_sprites(texture_id, sprites),
 180                PrimitiveBatch::PolychromeSprites {
 181                    texture_id,
 182                    sprites,
 183                } => self.draw_polychrome_sprites(texture_id, sprites),
 184                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
 185            }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 186                    scene.paths.len(),
 187                    scene.shadows.len(),
 188                    scene.quads.len(),
 189                    scene.underlines.len(),
 190                    scene.monochrome_sprites.len(),
 191                    scene.polychrome_sprites.len(),
 192                    scene.surfaces.len(),))?;
 193        }
 194        unsafe {
 195            self.devices.device_context.ResolveSubresource(
 196                &*self.context.render_target,
 197                0,
 198                &self.context.msaa_target,
 199                0,
 200                BACK_BUFFER_FORMAT,
 201            );
 202            self.devices
 203                .device_context
 204                .OMSetRenderTargets(Some(&self.context.render_target_view), None);
 205            self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
 206        }
 207        Ok(())
 208    }
 209
 210    pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
 211        println!("Resize: {:?}", self.context.render_target_view);
 212        unsafe {
 213            self.devices.device_context.OMSetRenderTargets(None, None);
 214            ManuallyDrop::drop(&mut self.context.render_target);
 215        }
 216        drop(self.context.render_target_view[0].take().unwrap());
 217        unsafe {
 218            self.context
 219                .swap_chain
 220                .ResizeBuffers(
 221                    BUFFER_COUNT as u32,
 222                    new_size.width.0 as u32,
 223                    new_size.height.0 as u32,
 224                    RENDER_TARGET_FORMAT,
 225                    DXGI_SWAP_CHAIN_FLAG(0),
 226                )
 227                .unwrap();
 228        }
 229        // let backbuffer = set_render_target_view(
 230        //     &self.context.swap_chain,
 231        //     &self.devices.device,
 232        //     &self.devices.device_context,
 233        // )?;
 234        let (render_target, render_target_view) =
 235            create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
 236                .unwrap();
 237        self.context.render_target = render_target;
 238        self.context.render_target_view = render_target_view;
 239        unsafe {
 240            self.devices
 241                .device_context
 242                .OMSetRenderTargets(Some(&self.context.render_target_view), None);
 243        }
 244
 245        let (msaa_target, msaa_view) = create_msaa_target_and_its_view(
 246            &self.devices.device,
 247            new_size.width.0 as u32,
 248            new_size.height.0 as u32,
 249        )?;
 250        self.context.msaa_target = msaa_target;
 251        self.context.msaa_view = msaa_view;
 252
 253        self.context.viewport = set_viewport(
 254            &self.devices.device_context,
 255            new_size.width.0 as f32,
 256            new_size.height.0 as f32,
 257        );
 258        Ok(())
 259    }
 260
 261    // #[cfg(not(feature = "enable-renderdoc"))]
 262    // pub(crate) fn update_transparency(
 263    //     &mut self,
 264    //     background_appearance: WindowBackgroundAppearance,
 265    // ) -> Result<()> {
 266    //     // We only support setting `Transparent` and `Opaque` for now.
 267    //     match background_appearance {
 268    //         WindowBackgroundAppearance::Opaque => {
 269    //             if self.transparent {
 270    //                 return Err(anyhow::anyhow!(
 271    //                     "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
 272    //                 ));
 273    //             }
 274    //         }
 275    //         WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
 276    //             if !self.transparent {
 277    //                 return Err(anyhow::anyhow!(
 278    //                     "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
 279    //                 ));
 280    //             }
 281    //         }
 282    //     }
 283    //     Ok(())
 284    // }
 285
 286    // #[cfg(feature = "enable-renderdoc")]
 287    pub(crate) fn update_transparency(
 288        &mut self,
 289        background_appearance: WindowBackgroundAppearance,
 290    ) -> Result<()> {
 291        let transparent = background_appearance != WindowBackgroundAppearance::Opaque;
 292        if self.transparent == transparent {
 293            return Ok(());
 294        }
 295        self.transparent = transparent;
 296        // unsafe {
 297        //     // recreate the swapchain
 298        //     self.devices.device_context.OMSetRenderTargets(None, None);
 299        //     drop(self.context.back_buffer[0].take().unwrap());
 300        //     ManuallyDrop::drop(&mut self.context.swap_chain);
 301        //     self.context.swap_chain = create_swap_chain_default(
 302        //         &self.devices.dxgi_factory,
 303        //         &self.devices.device,
 304        //         self.hwnd,
 305        //         transparent,
 306        //     )?;
 307        //     self.context.back_buffer = [Some(set_render_target_view(
 308        //         &self.context.swap_chain,
 309        //         &self.devices.device,
 310        //         &self.devices.device_context,
 311        //     )?)];
 312        //     self.context.viewport = set_viewport(
 313        //         &self.devices.device_context,
 314        //         self.context.viewport[0].Width,
 315        //         self.context.viewport[0].Height,
 316        //     );
 317        //     set_rasterizer_state(&self.devices.device, &self.devices.device_context)?;
 318        // }
 319        Ok(())
 320    }
 321
 322    fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
 323        if shadows.is_empty() {
 324            return Ok(());
 325        }
 326        self.pipelines.shadow_pipeline.update_buffer(
 327            &self.devices.device,
 328            &self.devices.device_context,
 329            shadows,
 330        )?;
 331        self.pipelines.shadow_pipeline.draw(
 332            &self.devices.device_context,
 333            &self.context.viewport,
 334            &self.globals.global_params_buffer,
 335            shadows.len() as u32,
 336        )
 337    }
 338
 339    fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
 340        if quads.is_empty() {
 341            return Ok(());
 342        }
 343        self.pipelines.quad_pipeline.update_buffer(
 344            &self.devices.device,
 345            &self.devices.device_context,
 346            quads,
 347        )?;
 348        self.pipelines.quad_pipeline.draw(
 349            &self.devices.device_context,
 350            &self.context.viewport,
 351            &self.globals.global_params_buffer,
 352            quads.len() as u32,
 353        )
 354    }
 355
 356    fn draw_paths(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
 357        if paths.is_empty() {
 358            return Ok(());
 359        }
 360        let mut vertices = Vec::new();
 361        let mut sprites = Vec::with_capacity(paths.len());
 362        let mut draw_indirect_commands = Vec::with_capacity(paths.len());
 363        let mut start_vertex_location = 0;
 364        for (i, path) in paths.iter().enumerate() {
 365            draw_indirect_commands.push(DrawInstancedIndirectArgs {
 366                vertex_count_per_instance: path.vertices.len() as u32,
 367                instance_count: 1,
 368                start_vertex_location,
 369                start_instance_location: i as u32,
 370            });
 371            start_vertex_location += path.vertices.len() as u32;
 372
 373            vertices.extend(path.vertices.iter().map(|v| PathVertex {
 374                xy_position: v.xy_position,
 375                content_mask: ContentMask {
 376                    bounds: path.content_mask.bounds,
 377                },
 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::<PathVertex<ScaledPixels>>(),
 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                ],
 839                shader_bytes,
 840                Some(&mut layout),
 841            )?;
 842            layout.unwrap()
 843        };
 844
 845        Ok(Self {
 846            vertex,
 847            fragment,
 848            buffer,
 849            buffer_size: 32,
 850            vertex_buffer,
 851            vertex_buffer_size: 32,
 852            indirect_draw_buffer,
 853            indirect_buffer_size: 32,
 854            input_layout,
 855            view,
 856        })
 857    }
 858
 859    fn update_buffer(
 860        &mut self,
 861        device: &ID3D11Device,
 862        device_context: &ID3D11DeviceContext,
 863        buffer_data: &[PathSprite],
 864        vertices_data: &[PathVertex<ScaledPixels>],
 865        draw_commands: &[DrawInstancedIndirectArgs],
 866    ) -> Result<()> {
 867        if self.buffer_size < buffer_data.len() {
 868            let new_buffer_size = buffer_data.len().next_power_of_two();
 869            log::info!(
 870                "Updating Paths Pipeline buffer size from {} to {}",
 871                self.buffer_size,
 872                new_buffer_size
 873            );
 874            let buffer = create_buffer(device, std::mem::size_of::<PathSprite>(), new_buffer_size)?;
 875            let view = create_buffer_view(device, &buffer)?;
 876            self.buffer = buffer;
 877            self.view = view;
 878            self.buffer_size = new_buffer_size;
 879        }
 880        update_buffer(device_context, &self.buffer, buffer_data)?;
 881        if self.vertex_buffer_size < vertices_data.len() {
 882            let new_vertex_buffer_size = vertices_data.len().next_power_of_two();
 883            log::info!(
 884                "Updating Paths Pipeline vertex buffer size from {} to {}",
 885                self.vertex_buffer_size,
 886                new_vertex_buffer_size
 887            );
 888            let vertex_buffer = create_buffer(
 889                device,
 890                std::mem::size_of::<PathVertex<ScaledPixels>>(),
 891                new_vertex_buffer_size,
 892            )?;
 893            self.vertex_buffer = Some(vertex_buffer);
 894            self.vertex_buffer_size = new_vertex_buffer_size;
 895        }
 896        update_buffer(
 897            device_context,
 898            self.vertex_buffer.as_ref().unwrap(),
 899            vertices_data,
 900        )?;
 901        if self.indirect_buffer_size < draw_commands.len() {
 902            let new_indirect_buffer_size = draw_commands.len().next_power_of_two();
 903            log::info!(
 904                "Updating Paths Pipeline indirect buffer size from {} to {}",
 905                self.indirect_buffer_size,
 906                new_indirect_buffer_size
 907            );
 908            let indirect_draw_buffer =
 909                create_indirect_draw_buffer(device, new_indirect_buffer_size)?;
 910            self.indirect_draw_buffer = indirect_draw_buffer;
 911            self.indirect_buffer_size = new_indirect_buffer_size;
 912        }
 913        update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?;
 914        Ok(())
 915    }
 916
 917    fn draw(
 918        &self,
 919        device_context: &ID3D11DeviceContext,
 920        count: usize,
 921        viewport: &[D3D11_VIEWPORT],
 922        global_params: &[Option<ID3D11Buffer>],
 923    ) -> Result<()> {
 924        set_pipeline_state(
 925            device_context,
 926            &self.view,
 927            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
 928            viewport,
 929            &self.vertex,
 930            &self.fragment,
 931            global_params,
 932        );
 933        unsafe {
 934            const STRIDE: u32 = std::mem::size_of::<PathVertex<ScaledPixels>>() as u32;
 935            device_context.IASetVertexBuffers(
 936                0,
 937                1,
 938                Some(&self.vertex_buffer),
 939                Some(&STRIDE),
 940                Some(&0),
 941            );
 942            device_context.IASetInputLayout(&self.input_layout);
 943        }
 944        for i in 0..count {
 945            unsafe {
 946                device_context.DrawInstancedIndirect(
 947                    &self.indirect_draw_buffer,
 948                    (i * std::mem::size_of::<DrawInstancedIndirectArgs>()) as u32,
 949                );
 950            }
 951        }
 952        Ok(())
 953    }
 954}
 955
 956#[derive(Clone, Debug, Eq, PartialEq)]
 957#[repr(C)]
 958struct PathSprite {
 959    bounds: Bounds<ScaledPixels>,
 960    color: Background,
 961}
 962
 963impl Drop for DirectXContext {
 964    fn drop(&mut self) {
 965        unsafe {
 966            ManuallyDrop::drop(&mut self.render_target);
 967            ManuallyDrop::drop(&mut self.swap_chain);
 968        }
 969    }
 970}
 971
 972#[inline]
 973fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 974    #[cfg(debug_assertions)]
 975    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 976    #[cfg(not(debug_assertions))]
 977    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
 978    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 979}
 980
 981fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 982    for adapter_index in 0.. {
 983        let adapter: IDXGIAdapter1 = unsafe {
 984            dxgi_factory
 985                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 986        }?;
 987        {
 988            let desc = unsafe { adapter.GetDesc1() }?;
 989            println!(
 990                "Select GPU: {}",
 991                String::from_utf16_lossy(&desc.Description)
 992            );
 993        }
 994        // Check to see whether the adapter supports Direct3D 11, but don't
 995        // create the actual device yet.
 996        if get_device(&adapter, None, None).log_err().is_some() {
 997            return Ok(adapter);
 998        }
 999    }
1000
1001    unreachable!()
1002}
1003
1004fn get_device(
1005    adapter: &IDXGIAdapter1,
1006    device: Option<*mut Option<ID3D11Device>>,
1007    context: Option<*mut Option<ID3D11DeviceContext>>,
1008) -> Result<()> {
1009    #[cfg(debug_assertions)]
1010    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
1011    #[cfg(not(debug_assertions))]
1012    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
1013    Ok(unsafe {
1014        D3D11CreateDevice(
1015            adapter,
1016            D3D_DRIVER_TYPE_UNKNOWN,
1017            HMODULE::default(),
1018            device_flags,
1019            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
1020            D3D11_SDK_VERSION,
1021            device,
1022            None,
1023            context,
1024        )?
1025    })
1026}
1027
1028// #[cfg(not(feature = "enable-renderdoc"))]
1029// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
1030//     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
1031// }
1032
1033// fn create_swap_chain(
1034//     dxgi_factory: &IDXGIFactory6,
1035//     device: &ID3D11Device,
1036//     transparent: bool,
1037// ) -> Result<IDXGISwapChain1> {
1038//     let alpha_mode = if transparent {
1039//         DXGI_ALPHA_MODE_PREMULTIPLIED
1040//     } else {
1041//         DXGI_ALPHA_MODE_IGNORE
1042//     };
1043//     let desc = DXGI_SWAP_CHAIN_DESC1 {
1044//         Width: 1,
1045//         Height: 1,
1046//         Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1047//         Stereo: false.into(),
1048//         SampleDesc: DXGI_SAMPLE_DESC {
1049//             Count: 1,
1050//             Quality: 0,
1051//         },
1052//         BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1053//         BufferCount: BUFFER_COUNT as u32,
1054//         // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
1055//         Scaling: DXGI_SCALING_STRETCH,
1056//         SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1057//         AlphaMode: alpha_mode,
1058//         Flags: 0,
1059//     };
1060//     Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
1061// }
1062
1063// #[cfg(feature = "enable-renderdoc")]
1064fn create_swap_chain_default(
1065    dxgi_factory: &IDXGIFactory6,
1066    device: &ID3D11Device,
1067    hwnd: HWND,
1068    _transparent: bool,
1069) -> Result<ManuallyDrop<IDXGISwapChain1>> {
1070    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
1071
1072    let desc = DXGI_SWAP_CHAIN_DESC1 {
1073        Width: 1,
1074        Height: 1,
1075        Format: RENDER_TARGET_FORMAT,
1076        Stereo: false.into(),
1077        SampleDesc: DXGI_SAMPLE_DESC {
1078            Count: 1,
1079            Quality: 0,
1080        },
1081        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
1082        BufferCount: BUFFER_COUNT as u32,
1083        Scaling: DXGI_SCALING_STRETCH,
1084        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
1085        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
1086        Flags: 0,
1087    };
1088    let swap_chain =
1089        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
1090    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
1091    Ok(ManuallyDrop::new(swap_chain))
1092}
1093
1094#[inline]
1095fn create_render_target_and_its_view(
1096    swap_chain: &IDXGISwapChain1,
1097    device: &ID3D11Device,
1098) -> Result<(
1099    ManuallyDrop<ID3D11Texture2D>,
1100    [Option<ID3D11RenderTargetView>; 1],
1101)> {
1102    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
1103    let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1104        Format: BACK_BUFFER_FORMAT,
1105        ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1106        ..Default::default()
1107    };
1108    let mut render_target_view = None;
1109    unsafe {
1110        device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))?
1111    };
1112    Ok((
1113        ManuallyDrop::new(render_target),
1114        [Some(render_target_view.unwrap())],
1115    ))
1116}
1117
1118#[inline]
1119fn set_render_target_view(
1120    swap_chain: &IDXGISwapChain1,
1121    device: &ID3D11Device,
1122    device_context: &ID3D11DeviceContext,
1123) -> Result<ID3D11RenderTargetView> {
1124    // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
1125    // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
1126    let back_buffer = unsafe {
1127        let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
1128        let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1129            Format: BACK_BUFFER_FORMAT,
1130            ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1131            ..Default::default()
1132        };
1133        let mut buffer: Option<ID3D11RenderTargetView> = None;
1134        device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?;
1135        buffer.unwrap()
1136    };
1137    unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
1138    Ok(back_buffer)
1139}
1140
1141#[inline]
1142fn create_msaa_target_and_its_view(
1143    device: &ID3D11Device,
1144    width: u32,
1145    height: u32,
1146) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> {
1147    let msaa_target = unsafe {
1148        let mut output = None;
1149        let desc = D3D11_TEXTURE2D_DESC {
1150            Width: width,
1151            Height: height,
1152            MipLevels: 1,
1153            ArraySize: 1,
1154            Format: BACK_BUFFER_FORMAT,
1155            SampleDesc: DXGI_SAMPLE_DESC {
1156                Count: 4,
1157                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
1158            },
1159            Usage: D3D11_USAGE_DEFAULT,
1160            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1161            CPUAccessFlags: 0,
1162            MiscFlags: 0,
1163        };
1164        device.CreateTexture2D(&desc, None, Some(&mut output))?;
1165        output.unwrap()
1166    };
1167    let msaa_view = unsafe {
1168        let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1169            Format: BACK_BUFFER_FORMAT,
1170            ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS,
1171            ..Default::default()
1172        };
1173        let mut output = None;
1174        device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?;
1175        output.unwrap()
1176    };
1177    Ok((msaa_target, msaa_view))
1178}
1179
1180#[inline]
1181fn set_viewport(
1182    device_context: &ID3D11DeviceContext,
1183    width: f32,
1184    height: f32,
1185) -> [D3D11_VIEWPORT; 1] {
1186    let viewport = [D3D11_VIEWPORT {
1187        TopLeftX: 0.0,
1188        TopLeftY: 0.0,
1189        Width: width,
1190        Height: height,
1191        MinDepth: 0.0,
1192        MaxDepth: 1.0,
1193    }];
1194    unsafe { device_context.RSSetViewports(Some(&viewport)) };
1195    viewport
1196}
1197
1198#[inline]
1199fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
1200    let desc = D3D11_RASTERIZER_DESC {
1201        FillMode: D3D11_FILL_SOLID,
1202        CullMode: D3D11_CULL_NONE,
1203        // FrontCounterClockwise: true.into(),
1204        FrontCounterClockwise: false.into(),
1205        DepthBias: 0,
1206        DepthBiasClamp: 0.0,
1207        SlopeScaledDepthBias: 0.0,
1208        DepthClipEnable: true.into(),
1209        ScissorEnable: false.into(),
1210        // MultisampleEnable: false.into(),
1211        MultisampleEnable: true.into(),
1212        AntialiasedLineEnable: false.into(),
1213    };
1214    let rasterizer_state = unsafe {
1215        let mut state = None;
1216        device.CreateRasterizerState(&desc, Some(&mut state))?;
1217        state.unwrap()
1218    };
1219    unsafe { device_context.RSSetState(&rasterizer_state) };
1220    Ok(())
1221}
1222
1223// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
1224#[inline]
1225fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
1226    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
1227    // device performs the blend in linear space, which is ideal.
1228    let mut desc = D3D11_BLEND_DESC::default();
1229    desc.RenderTarget[0].BlendEnable = true.into();
1230    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1231    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1232    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1233    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1234    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1235    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
1236    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
1237    unsafe {
1238        let mut state = None;
1239        device.CreateBlendState(&desc, Some(&mut state))?;
1240        Ok(state.unwrap())
1241    }
1242}
1243
1244#[inline]
1245fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
1246    unsafe {
1247        let mut shader = None;
1248        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
1249        Ok(shader.unwrap())
1250    }
1251}
1252
1253#[inline]
1254fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
1255    unsafe {
1256        let mut shader = None;
1257        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
1258        Ok(shader.unwrap())
1259    }
1260}
1261
1262#[inline]
1263fn create_buffer(
1264    device: &ID3D11Device,
1265    element_size: usize,
1266    buffer_size: usize,
1267) -> Result<ID3D11Buffer> {
1268    let desc = D3D11_BUFFER_DESC {
1269        ByteWidth: (element_size * buffer_size) as u32,
1270        Usage: D3D11_USAGE_DYNAMIC,
1271        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1272        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1273        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
1274        StructureByteStride: element_size as u32,
1275    };
1276    let mut buffer = None;
1277    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1278    Ok(buffer.unwrap())
1279}
1280
1281#[inline]
1282fn create_buffer_view(
1283    device: &ID3D11Device,
1284    buffer: &ID3D11Buffer,
1285) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
1286    let mut view = None;
1287    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
1288    Ok([view])
1289}
1290
1291#[inline]
1292fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result<ID3D11Buffer> {
1293    let desc = D3D11_BUFFER_DESC {
1294        ByteWidth: (std::mem::size_of::<DrawInstancedIndirectArgs>() * buffer_size) as u32,
1295        Usage: D3D11_USAGE_DYNAMIC,
1296        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1297        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1298        MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32,
1299        StructureByteStride: std::mem::size_of::<DrawInstancedIndirectArgs>() as u32,
1300    };
1301    let mut buffer = None;
1302    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
1303    Ok(buffer.unwrap())
1304}
1305
1306#[inline]
1307fn pre_draw(
1308    device_context: &ID3D11DeviceContext,
1309    global_params_buffer: &[Option<ID3D11Buffer>; 1],
1310    view_port: &[D3D11_VIEWPORT; 1],
1311    render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1312    clear_color: [f32; 4],
1313    blend_state: &ID3D11BlendState,
1314) -> Result<()> {
1315    let global_params = global_params_buffer[0].as_ref().unwrap();
1316    update_buffer(
1317        device_context,
1318        global_params,
1319        &[GlobalParams {
1320            viewport_size: [view_port[0].Width, view_port[0].Height],
1321            ..Default::default()
1322        }],
1323    )?;
1324    unsafe {
1325        device_context.RSSetViewports(Some(view_port));
1326        device_context.OMSetRenderTargets(Some(render_target_view), None);
1327        device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1328        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1329    }
1330    Ok(())
1331}
1332
1333#[inline]
1334fn update_buffer<T>(
1335    device_context: &ID3D11DeviceContext,
1336    buffer: &ID3D11Buffer,
1337    data: &[T],
1338) -> Result<()> {
1339    unsafe {
1340        let mut dest = std::mem::zeroed();
1341        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1342        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1343        device_context.Unmap(buffer, 0);
1344    }
1345    Ok(())
1346}
1347
1348#[inline]
1349fn set_pipeline_state(
1350    device_context: &ID3D11DeviceContext,
1351    buffer_view: &[Option<ID3D11ShaderResourceView>],
1352    topology: D3D_PRIMITIVE_TOPOLOGY,
1353    viewport: &[D3D11_VIEWPORT],
1354    vertex_shader: &ID3D11VertexShader,
1355    fragment_shader: &ID3D11PixelShader,
1356    global_params: &[Option<ID3D11Buffer>],
1357) {
1358    unsafe {
1359        device_context.VSSetShaderResources(1, Some(buffer_view));
1360        device_context.PSSetShaderResources(1, Some(buffer_view));
1361        device_context.IASetPrimitiveTopology(topology);
1362        device_context.RSSetViewports(Some(viewport));
1363        device_context.VSSetShader(vertex_shader, None);
1364        device_context.PSSetShader(fragment_shader, None);
1365        device_context.VSSetConstantBuffers(0, Some(global_params));
1366        device_context.PSSetConstantBuffers(0, Some(global_params));
1367    }
1368}
1369
1370const BUFFER_COUNT: usize = 3;
1371
1372mod shader_resources {
1373    use anyhow::Result;
1374    use windows::Win32::Graphics::Direct3D::{
1375        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1376        ID3DBlob,
1377    };
1378    use windows_core::{HSTRING, PCSTR};
1379
1380    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1381        unsafe {
1382            let mut entry = entry.to_owned();
1383            let mut target = target.to_owned();
1384            let mut compile_blob = None;
1385            let mut error_blob = None;
1386            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1387                .join("src/platform/windows/shaders.hlsl")
1388                .canonicalize()
1389                .unwrap();
1390            entry.push_str("\0");
1391            target.push_str("\0");
1392            let entry_point = PCSTR::from_raw(entry.as_ptr());
1393            let target_cstr = PCSTR::from_raw(target.as_ptr());
1394            #[cfg(debug_assertions)]
1395            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1396            #[cfg(not(debug_assertions))]
1397            let compile_flag = 0;
1398            let ret = D3DCompileFromFile(
1399                &HSTRING::from(shader_path.to_str().unwrap()),
1400                None,
1401                None,
1402                entry_point,
1403                target_cstr,
1404                compile_flag,
1405                0,
1406                &mut compile_blob,
1407                Some(&mut error_blob),
1408            );
1409            if ret.is_err() {
1410                let Some(error_blob) = error_blob else {
1411                    return Err(anyhow::anyhow!("{ret:?}"));
1412                };
1413                let string_len = error_blob.GetBufferSize();
1414                let error_string_encode = Vec::from_raw_parts(
1415                    error_blob.GetBufferPointer() as *mut u8,
1416                    string_len,
1417                    string_len,
1418                );
1419                let error_string = String::from_utf8_lossy(&error_string_encode).to_string();
1420                log::error!("Shader compile error: {}", error_string);
1421                return Err(anyhow::anyhow!("Compile error: {}", error_string));
1422            }
1423            Ok(compile_blob.unwrap())
1424        }
1425    }
1426}