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