directx_renderer.rs

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