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