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"))]
   7// use windows::Win32::Graphics::DirectComposition::*;
   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
  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"))]
  66// struct 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: devices.clone(),
 105            context,
 106            globals,
 107            pipelines,
 108            hwnd,
 109            transparent,
 110        })
 111    }
 112
 113    pub(crate) fn sprite_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, DXGI_PRESENT(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                DXGI_SWAP_CHAIN_FLAG(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        None
 355    }
 356
 357    fn draw_paths(
 358        &mut self,
 359        paths: &[Path<ScaledPixels>],
 360        path_tiles: &HashMap<PathId, AtlasTile>,
 361    ) -> Result<()> {
 362        // if paths.is_empty() {
 363        //     return Ok(());
 364        // }
 365        // for path in paths {
 366        //     let tile = &path_tiles[&path.id];
 367        //     let texture_view = self.atlas.get_texture_view(tile.texture_id);
 368        //     let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
 369        //     let sprites = [PathSprite {
 370        //         bounds: Bounds {
 371        //             origin: origin.map(|p| p.floor()),
 372        //             size: tile.bounds.size.map(Into::into),
 373        //         },
 374        //         color: path.color,
 375        //         tile: (*tile).clone(),
 376        //     }];
 377        //     update_buffer_capacity(
 378        //         &self.pipelines.paths_pipeline,
 379        //         std::mem::size_of::<PathSprite>(),
 380        //         1,
 381        //         &self.devices.device,
 382        //     )
 383        //     .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input));
 384        //     update_buffer(
 385        //         &self.devices.device_context,
 386        //         &self.pipelines.paths_pipeline.buffer,
 387        //         &sprites,
 388        //     )?;
 389        //     draw_with_texture(
 390        //         &self.devices.device_context,
 391        //         &self.pipelines.paths_pipeline,
 392        //         &texture_view,
 393        //         &self.context.viewport,
 394        //         &self.globals.global_params_buffer,
 395        //         &self.globals.sampler,
 396        //         1,
 397        //     )?;
 398        // }
 399        Ok(())
 400    }
 401
 402    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
 403        if underlines.is_empty() {
 404            return Ok(());
 405        }
 406        update_buffer_capacity(
 407            &self.pipelines.underline_pipeline,
 408            std::mem::size_of::<Underline>(),
 409            underlines.len(),
 410            &self.devices.device,
 411        )
 412        .map(|input| update_pipeline(&mut self.pipelines.underline_pipeline, input));
 413        update_buffer(
 414            &self.devices.device_context,
 415            &self.pipelines.underline_pipeline.buffer,
 416            underlines,
 417        )?;
 418        draw_normal(
 419            &self.devices.device_context,
 420            &self.pipelines.underline_pipeline,
 421            &self.context.viewport,
 422            &self.globals.global_params_buffer,
 423            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
 424            4,
 425            underlines.len() as u32,
 426        )
 427    }
 428
 429    fn draw_monochrome_sprites(
 430        &mut self,
 431        texture_id: AtlasTextureId,
 432        sprites: &[MonochromeSprite],
 433    ) -> Result<()> {
 434        if sprites.is_empty() {
 435            return Ok(());
 436        }
 437        let texture_view = self.atlas.get_texture_view(texture_id);
 438        update_buffer_capacity(
 439            &self.pipelines.mono_sprites,
 440            std::mem::size_of::<MonochromeSprite>(),
 441            sprites.len(),
 442            &self.devices.device,
 443        )
 444        .map(|input| update_pipeline(&mut self.pipelines.mono_sprites, input));
 445        update_buffer(
 446            &self.devices.device_context,
 447            &self.pipelines.mono_sprites.buffer,
 448            sprites,
 449        )?;
 450        draw_with_texture(
 451            &self.devices.device_context,
 452            &self.pipelines.mono_sprites,
 453            &texture_view,
 454            &self.context.viewport,
 455            &self.globals.global_params_buffer,
 456            &self.globals.sampler,
 457            sprites.len() as u32,
 458        )
 459    }
 460
 461    fn draw_polychrome_sprites(
 462        &mut self,
 463        texture_id: AtlasTextureId,
 464        sprites: &[PolychromeSprite],
 465    ) -> Result<()> {
 466        if sprites.is_empty() {
 467            return Ok(());
 468        }
 469        let texture_view = self.atlas.get_texture_view(texture_id);
 470        update_buffer_capacity(
 471            &self.pipelines.poly_sprites,
 472            std::mem::size_of::<PolychromeSprite>(),
 473            sprites.len(),
 474            &self.devices.device,
 475        )
 476        .map(|input| update_pipeline(&mut self.pipelines.poly_sprites, input));
 477        update_buffer(
 478            &self.devices.device_context,
 479            &self.pipelines.poly_sprites.buffer,
 480            sprites,
 481        )?;
 482        draw_with_texture(
 483            &self.devices.device_context,
 484            &self.pipelines.poly_sprites,
 485            &texture_view,
 486            &self.context.viewport,
 487            &self.globals.global_params_buffer,
 488            &self.globals.sampler,
 489            sprites.len() as u32,
 490        )
 491    }
 492
 493    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
 494        if surfaces.is_empty() {
 495            return Ok(());
 496        }
 497        Ok(())
 498    }
 499}
 500
 501impl DirectXContext {
 502    pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
 503        // #[cfg(not(feature = "enable-renderdoc"))]
 504        // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
 505        // #[cfg(feature = "enable-renderdoc")]
 506        let swap_chain =
 507            create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
 508        // #[cfg(not(feature = "enable-renderdoc"))]
 509        // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
 510        // #[cfg(not(feature = "enable-renderdoc"))]
 511        // direct_composition.set_swap_chain(&swap_chain)?;
 512        let back_buffer = [Some(set_render_target_view(
 513            &swap_chain,
 514            &devices.device,
 515            &devices.device_context,
 516        )?)];
 517        let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
 518        set_rasterizer_state(&devices.device, &devices.device_context)?;
 519
 520        Ok(Self {
 521            swap_chain,
 522            back_buffer,
 523            viewport,
 524            // #[cfg(not(feature = "enable-renderdoc"))]
 525            // direct_composition,
 526        })
 527    }
 528}
 529
 530impl DirectXRenderPipelines {
 531    pub fn new(device: &ID3D11Device) -> Result<Self> {
 532        let shadow_pipeline = create_pipieline(
 533            device,
 534            "shadow_vertex",
 535            "shadow_fragment",
 536            std::mem::size_of::<Shadow>(),
 537            32,
 538        )?;
 539        let quad_pipeline = create_pipieline(
 540            device,
 541            "quad_vertex",
 542            "quad_fragment",
 543            std::mem::size_of::<Quad>(),
 544            32,
 545        )?;
 546        let path_raster_pipeline = create_pipieline(
 547            device,
 548            "path_rasterization_vertex",
 549            "path_rasterization_fragment",
 550            std::mem::size_of::<PathVertex<ScaledPixels>>(),
 551            32,
 552        )?;
 553        let paths_pipeline = create_pipieline(
 554            device,
 555            "paths_vertex",
 556            "paths_fragment",
 557            std::mem::size_of::<PathSprite>(),
 558            1,
 559        )?;
 560        let underline_pipeline = create_pipieline(
 561            device,
 562            "underline_vertex",
 563            "underline_fragment",
 564            std::mem::size_of::<Underline>(),
 565            32,
 566        )?;
 567        let mono_sprites = create_pipieline(
 568            device,
 569            "monochrome_sprite_vertex",
 570            "monochrome_sprite_fragment",
 571            std::mem::size_of::<MonochromeSprite>(),
 572            32,
 573        )?;
 574        let poly_sprites = create_pipieline(
 575            device,
 576            "polychrome_sprite_vertex",
 577            "polychrome_sprite_fragment",
 578            std::mem::size_of::<PolychromeSprite>(),
 579            32,
 580        )?;
 581
 582        Ok(Self {
 583            shadow_pipeline,
 584            quad_pipeline,
 585            path_raster_pipeline,
 586            paths_pipeline,
 587            underline_pipeline,
 588            mono_sprites,
 589            poly_sprites,
 590        })
 591    }
 592}
 593
 594// #[cfg(not(feature = "enable-renderdoc"))]
 595// impl DirectComposition {
 596//     pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
 597//         let comp_device = get_comp_device(&dxgi_device)?;
 598//         let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
 599//         let comp_visual = unsafe { comp_device.CreateVisual() }?;
 600
 601//         Ok(Self {
 602//             comp_device,
 603//             comp_target,
 604//             comp_visual,
 605//         })
 606//     }
 607
 608//     pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
 609//         unsafe {
 610//             self.comp_visual.SetContent(swap_chain)?;
 611//             self.comp_target.SetRoot(&self.comp_visual)?;
 612//             self.comp_device.Commit()?;
 613//         }
 614//         Ok(())
 615//     }
 616// }
 617
 618impl DirectXGlobalElements {
 619    pub fn new(device: &ID3D11Device) -> Result<Self> {
 620        let global_params_buffer = unsafe {
 621            let desc = D3D11_BUFFER_DESC {
 622                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
 623                Usage: D3D11_USAGE_DYNAMIC,
 624                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
 625                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 626                ..Default::default()
 627            };
 628            let mut buffer = None;
 629            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
 630            [buffer]
 631        };
 632
 633        let sampler = unsafe {
 634            let desc = D3D11_SAMPLER_DESC {
 635                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
 636                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
 637                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
 638                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
 639                MipLODBias: 0.0,
 640                MaxAnisotropy: 1,
 641                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
 642                BorderColor: [0.0; 4],
 643                MinLOD: 0.0,
 644                MaxLOD: D3D11_FLOAT32_MAX,
 645            };
 646            let mut output = None;
 647            device.CreateSamplerState(&desc, Some(&mut output))?;
 648            [output]
 649        };
 650
 651        let blend_state = create_blend_state(device)?;
 652        let blend_state_for_pr = create_blend_state_for_path_raster(device)?;
 653
 654        Ok(Self {
 655            global_params_buffer,
 656            sampler,
 657            blend_state,
 658            blend_state_for_pr,
 659        })
 660    }
 661}
 662
 663#[derive(Debug, Default)]
 664#[repr(C)]
 665struct GlobalParams {
 666    viewport_size: [f32; 2],
 667    _pad: u64,
 668}
 669
 670struct PipelineState {
 671    vertex: ID3D11VertexShader,
 672    fragment: ID3D11PixelShader,
 673    buffer: ID3D11Buffer,
 674    buffer_size: usize,
 675    view: [Option<ID3D11ShaderResourceView>; 1],
 676}
 677
 678#[derive(Clone, Debug, Eq, PartialEq)]
 679#[repr(C)]
 680struct PathSprite {
 681    bounds: Bounds<ScaledPixels>,
 682    color: Hsla,
 683    tile: AtlasTile,
 684}
 685
 686fn get_dxgi_factory() -> Result<IDXGIFactory6> {
 687    #[cfg(debug_assertions)]
 688    let factory_flag = DXGI_CREATE_FACTORY_DEBUG;
 689    #[cfg(not(debug_assertions))]
 690    let factory_flag = 0u32;
 691    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
 692}
 693
 694fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
 695    for adapter_index in 0.. {
 696        let adapter: IDXGIAdapter1 = unsafe {
 697            dxgi_factory
 698                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
 699        }?;
 700        {
 701            let desc = unsafe { adapter.GetDesc1() }?;
 702            println!(
 703                "Select GPU: {}",
 704                String::from_utf16_lossy(&desc.Description)
 705            );
 706        }
 707        // Check to see whether the adapter supports Direct3D 11, but don't
 708        // create the actual device yet.
 709        if get_device(&adapter, None, None).log_err().is_some() {
 710            return Ok(adapter);
 711        }
 712    }
 713
 714    unreachable!()
 715}
 716
 717fn get_device(
 718    adapter: &IDXGIAdapter1,
 719    device: Option<*mut Option<ID3D11Device>>,
 720    context: Option<*mut Option<ID3D11DeviceContext>>,
 721) -> Result<()> {
 722    #[cfg(debug_assertions)]
 723    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
 724    #[cfg(not(debug_assertions))]
 725    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
 726    Ok(unsafe {
 727        D3D11CreateDevice(
 728            adapter,
 729            D3D_DRIVER_TYPE_UNKNOWN,
 730            HMODULE::default(),
 731            device_flags,
 732            Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
 733            D3D11_SDK_VERSION,
 734            device,
 735            None,
 736            context,
 737        )?
 738    })
 739}
 740
 741// #[cfg(not(feature = "enable-renderdoc"))]
 742// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
 743//     Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
 744// }
 745
 746fn create_swap_chain(
 747    dxgi_factory: &IDXGIFactory6,
 748    device: &ID3D11Device,
 749    transparent: bool,
 750) -> Result<IDXGISwapChain1> {
 751    let alpha_mode = if transparent {
 752        DXGI_ALPHA_MODE_PREMULTIPLIED
 753    } else {
 754        DXGI_ALPHA_MODE_IGNORE
 755    };
 756    let desc = DXGI_SWAP_CHAIN_DESC1 {
 757        Width: 1,
 758        Height: 1,
 759        Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 760        Stereo: false.into(),
 761        SampleDesc: DXGI_SAMPLE_DESC {
 762            Count: 1,
 763            Quality: 0,
 764        },
 765        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 766        BufferCount: BUFFER_COUNT as u32,
 767        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
 768        Scaling: DXGI_SCALING_STRETCH,
 769        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 770        AlphaMode: alpha_mode,
 771        Flags: 0,
 772    };
 773    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
 774}
 775
 776// #[cfg(feature = "enable-renderdoc")]
 777fn create_swap_chain_default(
 778    dxgi_factory: &IDXGIFactory6,
 779    device: &ID3D11Device,
 780    hwnd: HWND,
 781    _transparent: bool,
 782) -> Result<IDXGISwapChain1> {
 783    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
 784
 785    let desc = DXGI_SWAP_CHAIN_DESC1 {
 786        Width: 1,
 787        Height: 1,
 788        Format: DXGI_FORMAT_B8G8R8A8_UNORM,
 789        Stereo: false.into(),
 790        SampleDesc: DXGI_SAMPLE_DESC {
 791            Count: 1,
 792            Quality: 0,
 793        },
 794        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
 795        BufferCount: BUFFER_COUNT as u32,
 796        Scaling: DXGI_SCALING_STRETCH,
 797        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
 798        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
 799        Flags: 0,
 800    };
 801    let swap_chain =
 802        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
 803    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
 804    Ok(swap_chain)
 805}
 806
 807fn set_render_target_view(
 808    swap_chain: &IDXGISwapChain1,
 809    device: &ID3D11Device,
 810    device_context: &ID3D11DeviceContext,
 811) -> Result<ID3D11RenderTargetView> {
 812    // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer.
 813    // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after
 814    let back_buffer = unsafe {
 815        let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?;
 816        let mut buffer: Option<ID3D11RenderTargetView> = None;
 817        device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?;
 818        buffer.unwrap()
 819    };
 820    unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) };
 821    Ok(back_buffer)
 822}
 823
 824fn set_viewport(
 825    device_context: &ID3D11DeviceContext,
 826    width: f32,
 827    height: f32,
 828) -> [D3D11_VIEWPORT; 1] {
 829    let viewport = [D3D11_VIEWPORT {
 830        TopLeftX: 0.0,
 831        TopLeftY: 0.0,
 832        Width: width,
 833        Height: height,
 834        MinDepth: 0.0,
 835        MaxDepth: 1.0,
 836    }];
 837    unsafe { device_context.RSSetViewports(Some(&viewport)) };
 838    viewport
 839}
 840
 841fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
 842    let desc = D3D11_RASTERIZER_DESC {
 843        FillMode: D3D11_FILL_SOLID,
 844        CullMode: D3D11_CULL_NONE,
 845        FrontCounterClockwise: false.into(),
 846        DepthBias: 0,
 847        DepthBiasClamp: 0.0,
 848        SlopeScaledDepthBias: 0.0,
 849        DepthClipEnable: true.into(),
 850        ScissorEnable: false.into(),
 851        MultisampleEnable: false.into(),
 852        AntialiasedLineEnable: false.into(),
 853    };
 854    let rasterizer_state = unsafe {
 855        let mut state = None;
 856        device.CreateRasterizerState(&desc, Some(&mut state))?;
 857        state.unwrap()
 858    };
 859    unsafe { device_context.RSSetState(&rasterizer_state) };
 860    Ok(())
 861}
 862
 863// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
 864fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
 865    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
 866    // device performs the blend in linear space, which is ideal.
 867    let mut desc = D3D11_BLEND_DESC::default();
 868    desc.RenderTarget[0].BlendEnable = true.into();
 869    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
 870    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
 871    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
 872    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
 873    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
 874    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
 875    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
 876    unsafe {
 877        let mut state = None;
 878        device.CreateBlendState(&desc, Some(&mut state))?;
 879        Ok(state.unwrap())
 880    }
 881}
 882
 883fn create_blend_state_for_path_raster(device: &ID3D11Device) -> Result<ID3D11BlendState> {
 884    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
 885    // device performs the blend in linear space, which is ideal.
 886    let mut desc = D3D11_BLEND_DESC::default();
 887    desc.RenderTarget[0].BlendEnable = true.into();
 888    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
 889    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
 890    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
 891    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
 892    desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
 893    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
 894    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
 895    unsafe {
 896        let mut state = None;
 897        device.CreateBlendState(&desc, Some(&mut state))?;
 898        Ok(state.unwrap())
 899    }
 900}
 901
 902fn create_pipieline(
 903    device: &ID3D11Device,
 904    vertex_entry: &str,
 905    fragment_entry: &str,
 906    element_size: usize,
 907    buffer_size: usize,
 908) -> Result<PipelineState> {
 909    let vertex = {
 910        let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?;
 911        let bytes = unsafe {
 912            std::slice::from_raw_parts(
 913                shader_blob.GetBufferPointer() as *mut u8,
 914                shader_blob.GetBufferSize(),
 915            )
 916        };
 917        create_vertex_shader(device, bytes)?
 918    };
 919    let fragment = {
 920        let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?;
 921        let bytes = unsafe {
 922            std::slice::from_raw_parts(
 923                shader_blob.GetBufferPointer() as *mut u8,
 924                shader_blob.GetBufferSize(),
 925            )
 926        };
 927        create_fragment_shader(device, bytes)?
 928    };
 929    let buffer = create_buffer(device, element_size, buffer_size)?;
 930    let view = create_buffer_view(device, &buffer)?;
 931    Ok(PipelineState {
 932        vertex,
 933        fragment,
 934        buffer,
 935        buffer_size,
 936        view,
 937    })
 938}
 939
 940fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
 941    unsafe {
 942        let mut shader = None;
 943        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
 944        Ok(shader.unwrap())
 945    }
 946}
 947
 948fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
 949    unsafe {
 950        let mut shader = None;
 951        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
 952        Ok(shader.unwrap())
 953    }
 954}
 955
 956fn create_buffer(
 957    device: &ID3D11Device,
 958    element_size: usize,
 959    buffer_size: usize,
 960) -> Result<ID3D11Buffer> {
 961    let desc = D3D11_BUFFER_DESC {
 962        ByteWidth: (element_size * buffer_size) as u32,
 963        Usage: D3D11_USAGE_DYNAMIC,
 964        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
 965        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
 966        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
 967        StructureByteStride: element_size as u32,
 968    };
 969    let mut buffer = None;
 970    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
 971    Ok(buffer.unwrap())
 972}
 973
 974fn create_buffer_view(
 975    device: &ID3D11Device,
 976    buffer: &ID3D11Buffer,
 977) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
 978    let mut view = None;
 979    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
 980    Ok([view])
 981}
 982
 983fn update_global_params(
 984    device_context: &ID3D11DeviceContext,
 985    buffer: &[Option<ID3D11Buffer>; 1],
 986    globals: GlobalParams,
 987) -> Result<()> {
 988    let buffer = buffer[0].as_ref().unwrap();
 989    unsafe {
 990        let mut data = std::mem::zeroed();
 991        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut data))?;
 992        std::ptr::copy_nonoverlapping(&globals, data.pData as *mut _, 1);
 993        device_context.Unmap(buffer, 0);
 994    }
 995    Ok(())
 996}
 997
 998fn pre_draw(
 999    device_context: &ID3D11DeviceContext,
1000    global_params_buffer: &[Option<ID3D11Buffer>; 1],
1001    view_port: &[D3D11_VIEWPORT; 1],
1002    render_target_view: &[Option<ID3D11RenderTargetView>; 1],
1003    clear_color: [f32; 4],
1004    blend_state: &ID3D11BlendState,
1005) -> Result<()> {
1006    update_global_params(
1007        device_context,
1008        global_params_buffer,
1009        GlobalParams {
1010            viewport_size: [view_port[0].Width, view_port[0].Height],
1011            ..Default::default()
1012        },
1013    )?;
1014    unsafe {
1015        device_context.RSSetViewports(Some(view_port));
1016        device_context.OMSetRenderTargets(Some(render_target_view), None);
1017        device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
1018        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
1019    }
1020    Ok(())
1021}
1022
1023fn update_buffer_capacity(
1024    pipeline: &PipelineState,
1025    element_size: usize,
1026    data_size: usize,
1027    device: &ID3D11Device,
1028) -> Option<(ID3D11Buffer, usize, [Option<ID3D11ShaderResourceView>; 1])> {
1029    if pipeline.buffer_size >= data_size {
1030        return None;
1031    }
1032    println!("buffer too small: {} < {}", pipeline.buffer_size, data_size);
1033    let buffer_size = data_size.next_power_of_two();
1034    println!("New size: {}", buffer_size);
1035    let buffer = create_buffer(device, element_size, buffer_size).unwrap();
1036    let view = create_buffer_view(device, &buffer).unwrap();
1037    Some((buffer, buffer_size, view))
1038}
1039
1040fn update_pipeline(
1041    pipeline: &mut PipelineState,
1042    input: (ID3D11Buffer, usize, [Option<ID3D11ShaderResourceView>; 1]),
1043) {
1044    pipeline.buffer = input.0;
1045    pipeline.buffer_size = input.1;
1046    pipeline.view = input.2;
1047}
1048
1049fn update_buffer<T>(
1050    device_context: &ID3D11DeviceContext,
1051    buffer: &ID3D11Buffer,
1052    data: &[T],
1053) -> Result<()> {
1054    unsafe {
1055        let mut dest = std::mem::zeroed();
1056        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
1057        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
1058        device_context.Unmap(buffer, 0);
1059    }
1060    Ok(())
1061}
1062
1063fn draw_normal(
1064    device_context: &ID3D11DeviceContext,
1065    pipeline: &PipelineState,
1066    viewport: &[D3D11_VIEWPORT],
1067    global_params: &[Option<ID3D11Buffer>],
1068    topology: D3D_PRIMITIVE_TOPOLOGY,
1069    vertex_count: u32,
1070    instance_count: u32,
1071) -> Result<()> {
1072    unsafe {
1073        device_context.VSSetShaderResources(1, Some(&pipeline.view));
1074        device_context.PSSetShaderResources(1, Some(&pipeline.view));
1075        device_context.IASetPrimitiveTopology(topology);
1076        device_context.RSSetViewports(Some(viewport));
1077        device_context.VSSetShader(&pipeline.vertex, None);
1078        device_context.PSSetShader(&pipeline.fragment, None);
1079        device_context.VSSetConstantBuffers(0, Some(global_params));
1080        device_context.PSSetConstantBuffers(0, Some(global_params));
1081
1082        device_context.DrawInstanced(vertex_count, instance_count, 0, 0);
1083    }
1084    Ok(())
1085}
1086
1087fn draw_with_texture(
1088    device_context: &ID3D11DeviceContext,
1089    pipeline: &PipelineState,
1090    texture: &[Option<ID3D11ShaderResourceView>],
1091    viewport: &[D3D11_VIEWPORT],
1092    global_params: &[Option<ID3D11Buffer>],
1093    sampler: &[Option<ID3D11SamplerState>],
1094    instance_count: u32,
1095) -> Result<()> {
1096    unsafe {
1097        device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
1098        device_context.RSSetViewports(Some(viewport));
1099        device_context.VSSetShader(&pipeline.vertex, None);
1100        device_context.PSSetShader(&pipeline.fragment, None);
1101        device_context.VSSetConstantBuffers(0, Some(global_params));
1102        device_context.PSSetConstantBuffers(0, Some(global_params));
1103        device_context.VSSetShaderResources(1, Some(&pipeline.view));
1104        device_context.PSSetShaderResources(1, Some(&pipeline.view));
1105        device_context.PSSetSamplers(0, Some(sampler));
1106        device_context.VSSetShaderResources(0, Some(texture));
1107        device_context.PSSetShaderResources(0, Some(texture));
1108
1109        device_context.DrawInstanced(4, instance_count, 0, 0);
1110    }
1111    Ok(())
1112}
1113
1114const BUFFER_COUNT: usize = 3;
1115
1116mod shader_resources {
1117    use anyhow::Result;
1118    use windows::Win32::Graphics::Direct3D::{
1119        Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
1120        ID3DBlob,
1121    };
1122    use windows_core::{HSTRING, PCSTR};
1123
1124    pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result<ID3DBlob> {
1125        unsafe {
1126            let mut entry = entry.to_owned();
1127            let mut target = target.to_owned();
1128            let mut compile_blob = None;
1129            let mut error_blob = None;
1130            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1131                .join("src/platform/windows/shaders.hlsl")
1132                .canonicalize()
1133                .unwrap();
1134            entry.push_str("\0");
1135            target.push_str("\0");
1136            let entry_point = PCSTR::from_raw(entry.as_ptr());
1137            let target_cstr = PCSTR::from_raw(target.as_ptr());
1138            #[cfg(debug_assertions)]
1139            let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
1140            #[cfg(not(debug_assertions))]
1141            let compile_flag = 0;
1142            let ret = D3DCompileFromFile(
1143                &HSTRING::from(shader_path.to_str().unwrap()),
1144                None,
1145                None,
1146                entry_point,
1147                target_cstr,
1148                compile_flag,
1149                0,
1150                &mut compile_blob,
1151                Some(&mut error_blob),
1152            );
1153            if ret.is_err() {
1154                let Some(error_blob) = error_blob else {
1155                    return Err(anyhow::anyhow!("{ret:?}"));
1156                };
1157                let string_len = error_blob.GetBufferSize();
1158                let error_string_encode = Vec::from_raw_parts(
1159                    error_blob.GetBufferPointer() as *mut u8,
1160                    string_len,
1161                    string_len,
1162                );
1163                return Err(anyhow::anyhow!(
1164                    "Compile error: {}",
1165                    String::from_utf8_lossy(&error_string_encode)
1166                ));
1167            }
1168            Ok(compile_blob.unwrap())
1169        }
1170    }
1171}