directx_renderer.rs

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