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