directx_renderer.rs

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