directx_renderer.rs

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