diff --git a/Cargo.toml b/Cargo.toml index bf1eb9c3259a83e169eaaae7556f7d4b3022b4af..a57f29baa4b8766338d0ea815ecc848acee41237 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -666,6 +666,7 @@ features = [ "Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D11", "Win32_Graphics_Direct3D_Fxc", + "Win32_Graphics_DirectComposition", "Win32_Graphics_DirectWrite", "Win32_Graphics_Dwm", "Win32_Graphics_Dxgi", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 4e718b442867d2335715e2e963fa9330d9f04525..284664d02f3d8a1cbeff54b490b8010615578d32 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -71,6 +71,7 @@ screen-capture = [ "scap", ] windows-manifest = [] +enable-renderdoc = [] [lib] path = "src/gpui.rs" diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 0250e59a9bbc363a61377dc8c0ab01bccd820df3..8be6a0d5fabcae9abf3a97f5da9a8a71ebd4c4b5 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -13,7 +13,6 @@ mod mac; any(target_os = "linux", target_os = "freebsd"), any(feature = "x11", feature = "wayland") ), - target_os = "windows", feature = "macos-blade" ))] mod blade; diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index abd6e4ad40ba083183ebfdedb63d6d4f6b6e07f2..1abeab8928d28258c13a7a08a9da2466f599583d 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -12,7 +12,7 @@ use windows::Win32::Graphics::{ use crate::{ AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, - Size, platform::AtlasTextureList, + Point, Size, platform::AtlasTextureList, }; pub(crate) struct DirectXAtlas(Mutex); @@ -53,25 +53,6 @@ impl DirectXAtlas { let tex = lock.texture(id); tex.view.clone() } - - pub(crate) fn allocate( - &self, - size: Size, - texture_kind: AtlasTextureKind, - ) -> Option { - self.0.lock().allocate(size, texture_kind) - } - - pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { - let mut lock = self.0.lock(); - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, - AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, - }; - for texture in textures.iter_mut() { - texture.clear(); - } - } } impl PlatformAtlas for DirectXAtlas { @@ -249,10 +230,6 @@ impl DirectXAtlasState { } impl DirectXAtlasTexture { - fn clear(&mut self) { - self.allocator.clear(); - } - fn allocate(&mut self, size: Size) -> Option { let allocation = self.allocator.allocate(size.into())?; let tile = AtlasTile { @@ -301,3 +278,18 @@ impl DirectXAtlasTexture { self.live_atlas_keys == 0 } } + +impl From> for etagere::Size { + fn from(size: Size) -> Self { + etagere::Size::new(size.width.into(), size.height.into()) + } +} + +impl From for Point { + fn from(value: etagere::Point) -> Self { + Point { + x: DevicePixels::from(value.x), + y: DevicePixels::from(value.y), + } + } +} diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 36c68c8eb4668327c818fe313f4c0a8cc4a448c7..54478e9b02dc4a982e72d0fdd487cea158d7dae7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1,65 +1,72 @@ -use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc}; +use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -use collections::FxHasher; -// #[cfg(not(feature = "enable-renderdoc"))] -// use windows::Win32::Graphics::DirectComposition::*; -use windows::{ - Win32::{ - Foundation::{HMODULE, HWND}, - Graphics::{ - Direct3D::*, - Direct3D11::*, - Dxgi::{Common::*, *}, - }, +use windows::Win32::{ + Foundation::{HMODULE, HWND}, + Graphics::{ + Direct3D::*, + Direct3D11::*, + Dxgi::{Common::*, *}, }, - core::*, }; +#[cfg(not(feature = "enable-renderdoc"))] +use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; use crate::*; +const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; +// This configuration is used for MSAA rendering, and it's guaranteed to be supported by DirectX 11. +const MULTISAMPLE_COUNT: u32 = 4; + pub(crate) struct DirectXRenderer { atlas: Arc, devices: DirectXDevices, - context: DirectXContext, + resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - hwnd: HWND, - transparent: bool, + #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: DirectComposition, } +/// Direct3D objects #[derive(Clone)] pub(crate) struct DirectXDevices { + adapter: IDXGIAdapter1, dxgi_factory: IDXGIFactory6, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, device: ID3D11Device, device_context: ID3D11DeviceContext, } -struct DirectXContext { +struct DirectXResources { + // Direct3D rendering objects swap_chain: IDXGISwapChain1, - back_buffer: [Option; 1], + render_target: ManuallyDrop, + render_target_view: [Option; 1], + msaa_target: ID3D11Texture2D, + msaa_view: [Option; 1], + + // Cached window size and viewport + width: u32, + height: u32, viewport: [D3D11_VIEWPORT; 1], - // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition: DirectComposition, } struct DirectXRenderPipelines { - shadow_pipeline: PipelineState, - quad_pipeline: PipelineState, - paths_pipeline: PipelineState, - paths_indirect_draw_buffer: ID3D11Buffer, - underline_pipeline: PipelineState, - mono_sprites: PipelineState, - poly_sprites: PipelineState, + shadow_pipeline: PipelineState, + quad_pipeline: PipelineState, + paths_pipeline: PathsPipelineState, + underline_pipeline: PipelineState, + mono_sprites: PipelineState, + poly_sprites: PipelineState, } struct DirectXGlobalElements { global_params_buffer: [Option; 1], sampler: [Option; 1], blend_state: ID3D11BlendState, - blend_state_for_pr: ID3D11BlendState, } #[repr(C)] @@ -70,12 +77,12 @@ struct DrawInstancedIndirectArgs { start_instance_location: u32, } -// #[cfg(not(feature = "enable-renderdoc"))] -// struct DirectComposition { -// comp_device: IDCompositionDevice, -// comp_target: IDCompositionTarget, -// comp_visual: IDCompositionVisual, -// } +#[cfg(not(feature = "enable-renderdoc"))] +struct DirectComposition { + comp_device: IDCompositionDevice, + comp_target: IDCompositionTarget, + comp_visual: IDCompositionVisual, +} impl DirectXDevices { pub(crate) fn new() -> Result { @@ -87,10 +94,13 @@ impl DirectXDevices { get_device(&adapter, Some(&mut device), Some(&mut context))?; (device.unwrap(), context.unwrap()) }; + #[cfg(not(feature = "enable-renderdoc"))] let dxgi_device: IDXGIDevice = device.cast()?; Ok(Self { + adapter, dxgi_factory, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, device, device_context, @@ -99,22 +109,33 @@ impl DirectXDevices { } impl DirectXRenderer { - pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { let atlas = Arc::new(DirectXAtlas::new( devices.device.clone(), devices.device_context.clone(), )); - let context = DirectXContext::new(devices, hwnd, transparent)?; + + #[cfg(not(feature = "enable-renderdoc"))] + let resources = DirectXResources::new(devices)?; + #[cfg(feature = "enable-renderdoc")] + let resources = DirectXResources::new(devices, hwnd)?; + let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; + + #[cfg(not(feature = "enable-renderdoc"))] + let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition.set_swap_chain(&resources.swap_chain)?; + Ok(DirectXRenderer { atlas, devices: devices.clone(), - context, + resources, globals, pipelines, - hwnd, - transparent, + #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: direct_composition, }) } @@ -122,15 +143,56 @@ impl DirectXRenderer { self.atlas.clone() } - pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - pre_draw( + fn pre_draw(&self) -> Result<()> { + update_buffer( &self.devices.device_context, - &self.globals.global_params_buffer, - &self.context.viewport, - &self.context.back_buffer, - [0.0, 0.0, 0.0, 0.0], - &self.globals.blend_state, + self.globals.global_params_buffer[0].as_ref().unwrap(), + &[GlobalParams { + viewport_size: [ + self.resources.viewport[0].Width, + self.resources.viewport[0].Height, + ], + ..Default::default() + }], )?; + unsafe { + self.devices + .device_context + .ClearRenderTargetView(self.resources.msaa_view[0].as_ref().unwrap(), &[0.0; 4]); + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.msaa_view), None); + self.devices + .device_context + .RSSetViewports(Some(&self.resources.viewport)); + self.devices.device_context.OMSetBlendState( + &self.globals.blend_state, + None, + 0xFFFFFFFF, + ); + } + Ok(()) + } + + fn present(&self) -> Result<()> { + unsafe { + self.devices.device_context.ResolveSubresource( + &*self.resources.render_target, + 0, + &self.resources.msaa_target, + 0, + RENDER_TARGET_FORMAT, + ); + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); + self.resources.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + } + Ok(()) + } + + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + self.pre_draw()?; for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -155,98 +217,51 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?; - Ok(()) + self.present() } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { - unsafe { self.devices.device_context.OMSetRenderTargets(None, None) }; - drop(self.context.back_buffer[0].take().unwrap()); + let width = new_size.width.0.max(1) as u32; + let height = new_size.height.0.max(1) as u32; + if self.resources.width == width && self.resources.height == height { + return Ok(()); + } unsafe { - self.context.swap_chain.ResizeBuffers( + // Clear the render target before resizing + self.devices.device_context.OMSetRenderTargets(None, None); + ManuallyDrop::drop(&mut self.resources.render_target); + drop(self.resources.render_target_view[0].take().unwrap()); + + self.resources.swap_chain.ResizeBuffers( BUFFER_COUNT as u32, - new_size.width.0 as u32, - new_size.height.0 as u32, - DXGI_FORMAT_B8G8R8A8_UNORM, + width, + height, + RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), )?; - } - let backbuffer = set_render_target_view( - &self.context.swap_chain, - &self.devices.device, - &self.devices.device_context, - )?; - self.context.back_buffer[0] = Some(backbuffer); - self.context.viewport = set_viewport( - &self.devices.device_context, - new_size.width.0 as f32, - new_size.height.0 as f32, - ); - Ok(()) - } - // #[cfg(not(feature = "enable-renderdoc"))] - // pub(crate) fn update_transparency( - // &mut self, - // background_appearance: WindowBackgroundAppearance, - // ) -> Result<()> { - // // We only support setting `Transparent` and `Opaque` for now. - // match background_appearance { - // WindowBackgroundAppearance::Opaque => { - // if self.transparent { - // return Err(anyhow::anyhow!( - // "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window." - // )); - // } - // } - // WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => { - // if !self.transparent { - // return Err(anyhow::anyhow!( - // "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window." - // )); - // } - // } - // } - // Ok(()) - // } - - // #[cfg(feature = "enable-renderdoc")] - pub(crate) fn update_transparency( - &mut self, - background_appearance: WindowBackgroundAppearance, - ) -> Result<()> { - if background_appearance != WindowBackgroundAppearance::Opaque { - Err(anyhow::anyhow!( - "Set transparent background not supported when feature \"enable-renderdoc\" is enabled." - )) - } else { - Ok(()) + self.resources + .recreate_resources(&self.devices, width, height)?; + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); } + Ok(()) } fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { if shadows.is_empty() { return Ok(()); } - update_buffer_capacity( - &self.pipelines.shadow_pipeline, - std::mem::size_of::(), - shadows.len(), + self.pipelines.shadow_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.shadow_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.shadow_pipeline.buffer, shadows, )?; - draw_normal( + self.pipelines.shadow_pipeline.draw( &self.devices.device_context, - &self.pipelines.shadow_pipeline, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, shadows.len() as u32, ) } @@ -255,25 +270,15 @@ impl DirectXRenderer { if quads.is_empty() { return Ok(()); } - update_buffer_capacity( - &self.pipelines.quad_pipeline, - std::mem::size_of::(), - quads.len(), + self.pipelines.quad_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.quad_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.quad_pipeline.buffer, quads, )?; - draw_normal( + self.pipelines.quad_pipeline.draw( &self.devices.device_context, - &self.pipelines.quad_pipeline, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, quads.len() as u32, ) } @@ -282,7 +287,6 @@ impl DirectXRenderer { if paths.is_empty() { return Ok(()); } - println!("Drawing {} paths", paths.len()); let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); let mut draw_indirect_commands = Vec::with_capacity(paths.len()); @@ -296,11 +300,10 @@ impl DirectXRenderer { }); start_vertex_location += path.vertices.len() as u32; - vertices.extend(path.vertices.iter().map(|v| PathVertex { + vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex { xy_position: v.xy_position, - content_mask: ContentMask { - bounds: path.content_mask.bounds, - }, + content_mask: path.content_mask.bounds, + sprite_index: i as u32, })); sprites.push(PathSprite { @@ -309,64 +312,34 @@ impl DirectXRenderer { }); } - update_buffer_capacity( - &self.pipelines.paths_pipeline, - std::mem::size_of::(), - sprites.len(), + self.pipelines.paths_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, &sprites, - )?; - update_indirect_buffer( - &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, + &vertices, &draw_indirect_commands, )?; - prepare_indirect_draws( + self.pipelines.paths_pipeline.draw( &self.devices.device_context, - &self.pipelines.paths_pipeline, - &self.context.viewport, + paths.len(), + &self.resources.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, - )?; - - for i in 0..paths.len() { - draw_indirect( - &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, - (i * std::mem::size_of::()) as u32, - ); - } - Ok(()) + ) } fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> { if underlines.is_empty() { return Ok(()); } - update_buffer_capacity( - &self.pipelines.underline_pipeline, - std::mem::size_of::(), - underlines.len(), + self.pipelines.underline_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.underline_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.underline_pipeline.buffer, underlines, )?; - draw_normal( + self.pipelines.underline_pipeline.draw( &self.devices.device_context, - &self.pipelines.underline_pipeline, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, underlines.len() as u32, ) } @@ -379,24 +352,16 @@ impl DirectXRenderer { if sprites.is_empty() { return Ok(()); } - let texture_view = self.atlas.get_texture_view(texture_id); - update_buffer_capacity( - &self.pipelines.mono_sprites, - std::mem::size_of::(), - sprites.len(), + self.pipelines.mono_sprites.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.mono_sprites, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.mono_sprites.buffer, sprites, )?; - draw_with_texture( + let texture_view = self.atlas.get_texture_view(texture_id); + self.pipelines.mono_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.mono_sprites, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -411,24 +376,16 @@ impl DirectXRenderer { if sprites.is_empty() { return Ok(()); } - let texture_view = self.atlas.get_texture_view(texture_id); - update_buffer_capacity( - &self.pipelines.poly_sprites, - std::mem::size_of::(), - sprites.len(), + self.pipelines.poly_sprites.update_buffer( &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.poly_sprites, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.poly_sprites.buffer, sprites, )?; - draw_with_texture( + let texture_view = self.atlas.get_texture_view(texture_id); + self.pipelines.poly_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.poly_sprites, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -441,88 +398,125 @@ impl DirectXRenderer { } Ok(()) } + + pub(crate) fn gpu_specs(&self) -> Result { + let desc = unsafe { self.devices.adapter.GetDesc1() }?; + let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0; + let device_name = String::from_utf16_lossy(&desc.Description) + .trim_matches(char::from(0)) + .to_string(); + let driver_name = match desc.VendorId { + 0x10DE => "NVIDIA Corporation".to_string(), + 0x1002 => "AMD Corporation".to_string(), + 0x8086 => "Intel Corporation".to_string(), + _ => "Unknown Vendor".to_string(), + }; + let driver_version = match desc.VendorId { + 0x10DE => nvidia::get_driver_version(), + 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), + 0x8086 => intel::get_driver_version(&self.devices.adapter), + _ => Err(anyhow::anyhow!("Unknown vendor detected.")), + } + .context("Failed to get gpu driver info") + .log_err() + .unwrap_or("Unknown Driver".to_string()); + Ok(GpuSpecs { + is_software_emulated, + device_name, + driver_name, + driver_info: driver_version, + }) + } } -impl DirectXContext { - pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { - // #[cfg(not(feature = "enable-renderdoc"))] - // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?; - // #[cfg(feature = "enable-renderdoc")] +impl DirectXResources { + pub fn new( + devices: &DirectXDevices, + #[cfg(feature = "enable-renderdoc")] hwnd: HWND, + ) -> Result { + let width = 1; + let height = 1; + + #[cfg(not(feature = "enable-renderdoc"))] + let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, width, height)?; + #[cfg(feature = "enable-renderdoc")] let swap_chain = - create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; - // #[cfg(not(feature = "enable-renderdoc"))] - // let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; - // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition.set_swap_chain(&swap_chain)?; - let back_buffer = [Some(set_render_target_view( - &swap_chain, - &devices.device, - &devices.device_context, - )?)]; - let viewport = set_viewport(&devices.device_context, 1.0, 1.0); + create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?; + + let (render_target, render_target_view, msaa_target, msaa_view, viewport) = + create_resources(devices, &swap_chain, width, height)?; set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { swap_chain, - back_buffer, + render_target, + render_target_view, + msaa_target, + msaa_view, + width, + height, viewport, - // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition, }) } + + #[inline] + fn recreate_resources( + &mut self, + devices: &DirectXDevices, + width: u32, + height: u32, + ) -> Result<()> { + let (render_target, render_target_view, msaa_target, msaa_view, viewport) = + create_resources(devices, &self.swap_chain, width, height)?; + self.render_target = render_target; + self.render_target_view = render_target_view; + self.msaa_target = msaa_target; + self.msaa_view = msaa_view; + self.viewport = viewport; + self.width = width; + self.height = height; + Ok(()) + } } impl DirectXRenderPipelines { pub fn new(device: &ID3D11Device) -> Result { - let shadow_pipeline = create_pipieline( + let shadow_pipeline = PipelineState::new( device, + "shadow_pipeline", "shadow_vertex", "shadow_fragment", - std::mem::size_of::(), - 32, - )?; - let quad_pipeline = create_pipieline( - device, - "quad_vertex", - "quad_fragment", - std::mem::size_of::(), - 32, - )?; - let paths_pipeline = create_pipieline( - device, - "paths_vertex", - "paths_fragment", - std::mem::size_of::(), - 32, + 4, )?; - let underline_pipeline = create_pipieline( + let quad_pipeline = + PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?; + let paths_pipeline = PathsPipelineState::new(device)?; + let underline_pipeline = PipelineState::new( device, + "underline_pipeline", "underline_vertex", "underline_fragment", - std::mem::size_of::(), - 32, + 4, )?; - let mono_sprites = create_pipieline( + let mono_sprites = PipelineState::new( device, + "monochrome_sprite_pipeline", "monochrome_sprite_vertex", "monochrome_sprite_fragment", - std::mem::size_of::(), - 32, + 512, )?; - let poly_sprites = create_pipieline( + let poly_sprites = PipelineState::new( device, + "polychrome_sprite_pipeline", "polychrome_sprite_vertex", "polychrome_sprite_fragment", - std::mem::size_of::(), - 32, + 16, )?; - let paths_indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; Ok(Self { shadow_pipeline, quad_pipeline, paths_pipeline, - paths_indirect_draw_buffer, underline_pipeline, mono_sprites, poly_sprites, @@ -530,29 +524,29 @@ impl DirectXRenderPipelines { } } -// #[cfg(not(feature = "enable-renderdoc"))] -// impl DirectComposition { -// pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result { -// let comp_device = get_comp_device(&dxgi_device)?; -// let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?; -// let comp_visual = unsafe { comp_device.CreateVisual() }?; - -// Ok(Self { -// comp_device, -// comp_target, -// comp_visual, -// }) -// } - -// pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { -// unsafe { -// self.comp_visual.SetContent(swap_chain)?; -// self.comp_target.SetRoot(&self.comp_visual)?; -// self.comp_device.Commit()?; -// } -// Ok(()) -// } -// } +#[cfg(not(feature = "enable-renderdoc"))] +impl DirectComposition { + pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result { + let comp_device = get_comp_device(&dxgi_device)?; + let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?; + let comp_visual = unsafe { comp_device.CreateVisual() }?; + + Ok(Self { + comp_device, + comp_target, + comp_visual, + }) + } + + pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { + unsafe { + self.comp_visual.SetContent(swap_chain)?; + self.comp_target.SetRoot(&self.comp_visual)?; + self.comp_device.Commit()?; + } + Ok(()) + } +} impl DirectXGlobalElements { pub fn new(device: &ID3D11Device) -> Result { @@ -588,13 +582,11 @@ impl DirectXGlobalElements { }; let blend_state = create_blend_state(device)?; - let blend_state_for_pr = create_blend_state_for_path_raster(device)?; Ok(Self { global_params_buffer, sampler, blend_state, - blend_state_for_pr, }) } } @@ -606,14 +598,345 @@ struct GlobalParams { _pad: u64, } -struct PipelineState { +struct PipelineState { + label: &'static str, + vertex: ID3D11VertexShader, + fragment: ID3D11PixelShader, + buffer: ID3D11Buffer, + buffer_size: usize, + view: [Option; 1], + _marker: std::marker::PhantomData, +} + +struct PathsPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, buffer: ID3D11Buffer, buffer_size: usize, + vertex_buffer: Option, + vertex_buffer_size: usize, + indirect_draw_buffer: ID3D11Buffer, + indirect_buffer_size: usize, + input_layout: ID3D11InputLayout, view: [Option; 1], } +impl PipelineState { + fn new( + device: &ID3D11Device, + label: &'static str, + vertex_entry: &str, + fragment_entry: &str, + buffer_size: usize, + ) -> Result { + let vertex = { + let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *mut u8, + shader_blob.GetBufferSize(), + ) + }; + create_vertex_shader(device, bytes)? + }; + let fragment = { + let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *mut u8, + shader_blob.GetBufferSize(), + ) + }; + create_fragment_shader(device, bytes)? + }; + let buffer = create_buffer(device, std::mem::size_of::(), buffer_size)?; + let view = create_buffer_view(device, &buffer)?; + + Ok(PipelineState { + label, + vertex, + fragment, + buffer, + buffer_size, + view, + _marker: std::marker::PhantomData, + }) + } + + fn update_buffer( + &mut self, + device: &ID3D11Device, + device_context: &ID3D11DeviceContext, + data: &[T], + ) -> Result<()> { + if self.buffer_size < data.len() { + let new_buffer_size = data.len().next_power_of_two(); + log::info!( + "Updating {} buffer size from {} to {}", + self.label, + self.buffer_size, + new_buffer_size + ); + let buffer = create_buffer(device, std::mem::size_of::(), new_buffer_size)?; + let view = create_buffer_view(device, &buffer)?; + self.buffer = buffer; + self.view = view; + self.buffer_size = new_buffer_size; + } + update_buffer(device_context, &self.buffer, data) + } + + fn draw( + &self, + device_context: &ID3D11DeviceContext, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + instance_count: u32, + ) -> Result<()> { + set_pipeline_state( + device_context, + &self.view, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + viewport, + &self.vertex, + &self.fragment, + global_params, + ); + unsafe { + device_context.DrawInstanced(4, instance_count, 0, 0); + } + Ok(()) + } + + fn draw_with_texture( + &self, + device_context: &ID3D11DeviceContext, + texture: &[Option], + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + sampler: &[Option], + instance_count: u32, + ) -> Result<()> { + set_pipeline_state( + device_context, + &self.view, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + viewport, + &self.vertex, + &self.fragment, + global_params, + ); + unsafe { + device_context.PSSetSamplers(0, Some(sampler)); + device_context.VSSetShaderResources(0, Some(texture)); + device_context.PSSetShaderResources(0, Some(texture)); + + device_context.DrawInstanced(4, instance_count, 0, 0); + } + Ok(()) + } +} + +impl PathsPipelineState { + fn new(device: &ID3D11Device) -> Result { + let (vertex, vertex_shader) = { + let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *mut u8, + shader_blob.GetBufferSize(), + ) + }; + (create_vertex_shader(device, bytes)?, shader_blob) + }; + let fragment = { + let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *mut u8, + shader_blob.GetBufferSize(), + ) + }; + create_fragment_shader(device, bytes)? + }; + let buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let view = create_buffer_view(device, &buffer)?; + let vertex_buffer = Some(create_buffer( + device, + std::mem::size_of::(), + 32, + )?); + let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; + // Create input layout + let input_layout = unsafe { + let shader_bytes = std::slice::from_raw_parts( + vertex_shader.GetBufferPointer() as *const u8, + vertex_shader.GetBufferSize(), + ); + let mut layout = None; + device.CreateInputLayout( + &[ + D3D11_INPUT_ELEMENT_DESC { + SemanticName: windows::core::s!("POSITION"), + SemanticIndex: 0, + Format: DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: 0, + InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + D3D11_INPUT_ELEMENT_DESC { + SemanticName: windows::core::s!("TEXCOORD"), + SemanticIndex: 0, + Format: DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: 8, + InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + D3D11_INPUT_ELEMENT_DESC { + SemanticName: windows::core::s!("TEXCOORD"), + SemanticIndex: 1, + Format: DXGI_FORMAT_R32G32_FLOAT, + InputSlot: 0, + AlignedByteOffset: 16, + InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + D3D11_INPUT_ELEMENT_DESC { + SemanticName: windows::core::s!("GLOBALIDX"), + SemanticIndex: 0, + Format: DXGI_FORMAT_R32_UINT, + InputSlot: 0, + AlignedByteOffset: 24, + InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, + InstanceDataStepRate: 0, + }, + ], + shader_bytes, + Some(&mut layout), + )?; + layout.unwrap() + }; + + Ok(Self { + vertex, + fragment, + buffer, + buffer_size: 32, + vertex_buffer, + vertex_buffer_size: 32, + indirect_draw_buffer, + indirect_buffer_size: 32, + input_layout, + view, + }) + } + + fn update_buffer( + &mut self, + device: &ID3D11Device, + device_context: &ID3D11DeviceContext, + buffer_data: &[PathSprite], + vertices_data: &[DirectXPathVertex], + draw_commands: &[DrawInstancedIndirectArgs], + ) -> Result<()> { + if self.buffer_size < buffer_data.len() { + let new_buffer_size = buffer_data.len().next_power_of_two(); + log::info!( + "Updating Paths Pipeline buffer size from {} to {}", + self.buffer_size, + new_buffer_size + ); + let buffer = create_buffer(device, std::mem::size_of::(), new_buffer_size)?; + let view = create_buffer_view(device, &buffer)?; + self.buffer = buffer; + self.view = view; + self.buffer_size = new_buffer_size; + } + update_buffer(device_context, &self.buffer, buffer_data)?; + if self.vertex_buffer_size < vertices_data.len() { + let new_vertex_buffer_size = vertices_data.len().next_power_of_two(); + log::info!( + "Updating Paths Pipeline vertex buffer size from {} to {}", + self.vertex_buffer_size, + new_vertex_buffer_size + ); + let vertex_buffer = create_buffer( + device, + std::mem::size_of::(), + new_vertex_buffer_size, + )?; + self.vertex_buffer = Some(vertex_buffer); + self.vertex_buffer_size = new_vertex_buffer_size; + } + update_buffer( + device_context, + self.vertex_buffer.as_ref().unwrap(), + vertices_data, + )?; + if self.indirect_buffer_size < draw_commands.len() { + let new_indirect_buffer_size = draw_commands.len().next_power_of_two(); + log::info!( + "Updating Paths Pipeline indirect buffer size from {} to {}", + self.indirect_buffer_size, + new_indirect_buffer_size + ); + let indirect_draw_buffer = + create_indirect_draw_buffer(device, new_indirect_buffer_size)?; + self.indirect_draw_buffer = indirect_draw_buffer; + self.indirect_buffer_size = new_indirect_buffer_size; + } + update_buffer(device_context, &self.indirect_draw_buffer, draw_commands)?; + Ok(()) + } + + fn draw( + &self, + device_context: &ID3D11DeviceContext, + count: usize, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + ) -> Result<()> { + set_pipeline_state( + device_context, + &self.view, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + viewport, + &self.vertex, + &self.fragment, + global_params, + ); + unsafe { + const STRIDE: u32 = std::mem::size_of::() as u32; + device_context.IASetVertexBuffers( + 0, + 1, + Some(&self.vertex_buffer), + Some(&STRIDE), + Some(&0), + ); + device_context.IASetInputLayout(&self.input_layout); + } + for i in 0..count { + unsafe { + device_context.DrawInstancedIndirect( + &self.indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, + ); + } + } + Ok(()) + } +} + +#[repr(C)] +struct DirectXPathVertex { + xy_position: Point, + content_mask: Bounds, + sprite_index: u32, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -621,11 +944,20 @@ struct PathSprite { color: Background, } +impl Drop for DirectXResources { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self.render_target); + } + } +} + +#[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; #[cfg(not(debug_assertions))] - let factory_flag = 0u32; + let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default(); unsafe { Ok(CreateDXGIFactory2(factory_flag)?) } } @@ -635,12 +967,11 @@ fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result { dxgi_factory .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER) }?; - { - let desc = unsafe { adapter.GetDesc1() }?; - println!( - "Select GPU: {}", - String::from_utf16_lossy(&desc.Description) - ); + if let Ok(desc) = unsafe { adapter.GetDesc1() } { + let gpu_name = String::from_utf16_lossy(&desc.Description) + .trim_matches(char::from(0)) + .to_string(); + log::info!("Using GPU: {}", gpu_name); } // Check to see whether the adapter supports Direct3D 11, but don't // create the actual device yet. @@ -667,6 +998,8 @@ fn get_device( D3D_DRIVER_TYPE_UNKNOWN, HMODULE::default(), device_flags, + // 4x MSAA is required for Direct3D Feature Level 10.1 or better + // 8x MSAA is required for Direct3D Feature Level 11.0 or better Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]), D3D11_SDK_VERSION, device, @@ -676,54 +1009,52 @@ fn get_device( }) } -// #[cfg(not(feature = "enable-renderdoc"))] -// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { -// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) -// } - -// fn create_swap_chain( -// dxgi_factory: &IDXGIFactory6, -// device: &ID3D11Device, -// transparent: bool, -// ) -> Result { -// let alpha_mode = if transparent { -// DXGI_ALPHA_MODE_PREMULTIPLIED -// } else { -// DXGI_ALPHA_MODE_IGNORE -// }; -// let desc = DXGI_SWAP_CHAIN_DESC1 { -// Width: 1, -// Height: 1, -// Format: DXGI_FORMAT_B8G8R8A8_UNORM, -// Stereo: false.into(), -// SampleDesc: DXGI_SAMPLE_DESC { -// Count: 1, -// Quality: 0, -// }, -// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, -// BufferCount: BUFFER_COUNT as u32, -// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. -// Scaling: DXGI_SCALING_STRETCH, -// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, -// AlphaMode: alpha_mode, -// Flags: 0, -// }; -// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) -// } - -// #[cfg(feature = "enable-renderdoc")] -fn create_swap_chain_default( +#[cfg(not(feature = "enable-renderdoc"))] +fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { + Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) +} + +#[cfg(not(feature = "enable-renderdoc"))] +fn create_swap_chain( + dxgi_factory: &IDXGIFactory6, + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result { + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: RENDER_TARGET_FORMAT, + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: BUFFER_COUNT as u32, + // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, + Flags: 0, + }; + Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) +} + +#[cfg(feature = "enable-renderdoc")] +fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, hwnd: HWND, - _transparent: bool, + width: u32, + height: u32, ) -> Result { use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; let desc = DXGI_SWAP_CHAIN_DESC1 { - Width: 1, - Height: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, + Width: width, + Height: height, + Format: RENDER_TARGET_FORMAT, Stereo: false.into(), SampleDesc: DXGI_SAMPLE_DESC { Count: 1, @@ -742,23 +1073,84 @@ fn create_swap_chain_default( Ok(swap_chain) } -fn set_render_target_view( +#[inline] +fn create_resources( + devices: &DirectXDevices, + swap_chain: &IDXGISwapChain1, + width: u32, + height: u32, +) -> Result<( + ManuallyDrop, + [Option; 1], + ID3D11Texture2D, + [Option; 1], + [D3D11_VIEWPORT; 1], +)> { + let (render_target, render_target_view) = + create_render_target_and_its_view(&swap_chain, &devices.device)?; + let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, width, height)?; + let viewport = set_viewport(&devices.device_context, width as f32, height as f32); + Ok(( + render_target, + render_target_view, + msaa_target, + msaa_view, + viewport, + )) +} + +#[inline] +fn create_render_target_and_its_view( swap_chain: &IDXGISwapChain1, device: &ID3D11Device, - device_context: &ID3D11DeviceContext, -) -> Result { - // In dx11, ID3D11RenderTargetView is supposed to always point to the new back buffer. - // https://stackoverflow.com/questions/65246961/does-the-backbuffer-that-a-rendertargetview-points-to-automagically-change-after - let back_buffer = unsafe { - let resource: ID3D11Texture2D = swap_chain.GetBuffer(0)?; - let mut buffer: Option = None; - device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?; - buffer.unwrap() +) -> Result<( + ManuallyDrop, + [Option; 1], +)> { + let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; + let mut render_target_view = None; + unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? }; + Ok(( + ManuallyDrop::new(render_target), + [Some(render_target_view.unwrap())], + )) +} + +#[inline] +fn create_msaa_target_and_its_view( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result<(ID3D11Texture2D, [Option; 1])> { + let msaa_target = unsafe { + let mut output = None; + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: RENDER_TARGET_FORMAT, + SampleDesc: DXGI_SAMPLE_DESC { + Count: MULTISAMPLE_COUNT, + Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, + CPUAccessFlags: 0, + MiscFlags: 0, + }; + device.CreateTexture2D(&desc, None, Some(&mut output))?; + output.unwrap() }; - unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; - Ok(back_buffer) + let msaa_view = unsafe { + let mut output = None; + device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; + output.unwrap() + }; + Ok((msaa_target, [Some(msaa_view)])) } +#[inline] fn set_viewport( device_context: &ID3D11DeviceContext, width: f32, @@ -776,6 +1168,7 @@ fn set_viewport( viewport } +#[inline] fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> { let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, @@ -786,7 +1179,8 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont SlopeScaledDepthBias: 0.0, DepthClipEnable: true.into(), ScissorEnable: false.into(), - MultisampleEnable: false.into(), + // MultisampleEnable: false.into(), + MultisampleEnable: true.into(), AntialiasedLineEnable: false.into(), }; let rasterizer_state = unsafe { @@ -799,6 +1193,7 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont } // https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc +#[inline] fn create_blend_state(device: &ID3D11Device) -> Result { // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display // device performs the blend in linear space, which is ideal. @@ -818,63 +1213,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } -fn create_blend_state_for_path_raster(device: &ID3D11Device) -> Result { - // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display - // device performs the blend in linear space, which is ideal. - let mut desc = D3D11_BLEND_DESC::default(); - desc.RenderTarget[0].BlendEnable = true.into(); - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8; - unsafe { - let mut state = None; - device.CreateBlendState(&desc, Some(&mut state))?; - Ok(state.unwrap()) - } -} - -fn create_pipieline( - device: &ID3D11Device, - vertex_entry: &str, - fragment_entry: &str, - element_size: usize, - buffer_size: usize, -) -> Result { - let vertex = { - let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?; - let bytes = unsafe { - std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *mut u8, - shader_blob.GetBufferSize(), - ) - }; - create_vertex_shader(device, bytes)? - }; - let fragment = { - let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?; - let bytes = unsafe { - std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *mut u8, - shader_blob.GetBufferSize(), - ) - }; - create_fragment_shader(device, bytes)? - }; - let buffer = create_buffer(device, element_size, buffer_size)?; - let view = create_buffer_view(device, &buffer)?; - Ok(PipelineState { - vertex, - fragment, - buffer, - buffer_size, - view, - }) -} - +#[inline] fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { let mut shader = None; @@ -883,6 +1222,7 @@ fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { unsafe { let mut shader = None; @@ -891,6 +1231,7 @@ fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { - // let desc = D3D11_BUFFER_DESC { - // ByteWidth: std::mem::size_of::() as u32 * buffer_size, - // Usage: D3D11_USAGE_DYNAMIC, - // BindFlags: D3D11_BIND_INDIRECT_DRAW.0 as u32, - // MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32, - // ..Default::default() - // }; +#[inline] +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result { let desc = D3D11_BUFFER_DESC { - ByteWidth: std::mem::size_of::() as u32 * buffer_size, + ByteWidth: (std::mem::size_of::() * buffer_size) as u32, Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_INDEX_BUFFER.0 as u32, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, MiscFlags: D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS.0 as u32, StructureByteStride: std::mem::size_of::() as u32, @@ -939,72 +1275,7 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Resul Ok(buffer.unwrap()) } -fn update_global_params( - device_context: &ID3D11DeviceContext, - buffer: &[Option; 1], - globals: GlobalParams, -) -> Result<()> { - let buffer = buffer[0].as_ref().unwrap(); - unsafe { - let mut data = std::mem::zeroed(); - device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut data))?; - std::ptr::copy_nonoverlapping(&globals, data.pData as *mut _, 1); - device_context.Unmap(buffer, 0); - } - Ok(()) -} - -fn pre_draw( - device_context: &ID3D11DeviceContext, - global_params_buffer: &[Option; 1], - view_port: &[D3D11_VIEWPORT; 1], - render_target_view: &[Option; 1], - clear_color: [f32; 4], - blend_state: &ID3D11BlendState, -) -> Result<()> { - update_global_params( - device_context, - global_params_buffer, - GlobalParams { - viewport_size: [view_port[0].Width, view_port[0].Height], - ..Default::default() - }, - )?; - unsafe { - device_context.RSSetViewports(Some(view_port)); - device_context.OMSetRenderTargets(Some(render_target_view), None); - device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color); - device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF); - } - Ok(()) -} - -fn update_buffer_capacity( - pipeline: &PipelineState, - element_size: usize, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { - if pipeline.buffer_size >= data_size { - return None; - } - println!("buffer too small: {} < {}", pipeline.buffer_size, data_size); - let buffer_size = data_size.next_power_of_two(); - println!("New size: {}", buffer_size); - let buffer = create_buffer(device, element_size, buffer_size).unwrap(); - let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, view)) -} - -fn update_pipeline( - pipeline: &mut PipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; -} - +#[inline] fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1019,99 +1290,26 @@ fn update_buffer( Ok(()) } -fn update_indirect_buffer( - device_context: &ID3D11DeviceContext, - buffer: &ID3D11Buffer, - data: &[DrawInstancedIndirectArgs], -) -> Result<()> { - unsafe { - let mut dest = std::mem::zeroed(); - device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?; - std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len()); - device_context.Unmap(buffer, 0); - } - Ok(()) -} - -fn prepare_indirect_draws( +#[inline] +fn set_pipeline_state( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, - viewport: &[D3D11_VIEWPORT], - global_params: &[Option], + buffer_view: &[Option], topology: D3D_PRIMITIVE_TOPOLOGY, -) -> Result<()> { - unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetShaderResources(1, Some(&pipeline.view)); - device_context.IASetPrimitiveTopology(topology); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&pipeline.vertex, None); - device_context.PSSetShader(&pipeline.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - } - Ok(()) -} - -fn draw_indirect( - device_context: &ID3D11DeviceContext, - indirect_draw_buffer: &ID3D11Buffer, - offset: u32, -) { - unsafe { - device_context.DrawInstancedIndirect(indirect_draw_buffer, offset); - } -} - -fn draw_normal( - device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, viewport: &[D3D11_VIEWPORT], + vertex_shader: &ID3D11VertexShader, + fragment_shader: &ID3D11PixelShader, global_params: &[Option], - topology: D3D_PRIMITIVE_TOPOLOGY, - vertex_count: u32, - instance_count: u32, -) -> Result<()> { +) { unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetShaderResources(1, Some(&pipeline.view)); + device_context.VSSetShaderResources(1, Some(buffer_view)); + device_context.PSSetShaderResources(1, Some(buffer_view)); device_context.IASetPrimitiveTopology(topology); device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&pipeline.vertex, None); - device_context.PSSetShader(&pipeline.fragment, None); + device_context.VSSetShader(vertex_shader, None); + device_context.PSSetShader(fragment_shader, None); device_context.VSSetConstantBuffers(0, Some(global_params)); device_context.PSSetConstantBuffers(0, Some(global_params)); - - device_context.DrawInstanced(vertex_count, instance_count, 0, 0); } - Ok(()) -} - -fn draw_with_texture( - device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, - texture: &[Option], - viewport: &[D3D11_VIEWPORT], - global_params: &[Option], - sampler: &[Option], - instance_count: u32, -) -> Result<()> { - unsafe { - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&pipeline.vertex, None); - device_context.PSSetShader(&pipeline.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - device_context.VSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetSamplers(0, Some(sampler)); - device_context.VSSetShaderResources(0, Some(texture)); - device_context.PSSetShaderResources(0, Some(texture)); - - device_context.DrawInstanced(4, instance_count, 0, 0); - } - Ok(()) } const BUFFER_COUNT: usize = 3; @@ -1125,7 +1323,6 @@ mod shader_resources { use windows_core::{HSTRING, PCSTR}; pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { - println!("Building shader: {}", entry); unsafe { let mut entry = entry.to_owned(); let mut target = target.to_owned(); @@ -1139,11 +1336,6 @@ mod shader_resources { target.push_str("\0"); let entry_point = PCSTR::from_raw(entry.as_ptr()); let target_cstr = PCSTR::from_raw(target.as_ptr()); - println!( - "Compiling shader: {} with target: {}", - entry_point.display(), - target_cstr.display() - ); #[cfg(debug_assertions)] let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #[cfg(not(debug_assertions))] @@ -1159,7 +1351,6 @@ mod shader_resources { &mut compile_blob, Some(&mut error_blob), ); - println!("Shader compile result: {:?}", ret); if ret.is_err() { let Some(error_blob) = error_blob else { return Err(anyhow::anyhow!("{ret:?}")); @@ -1170,11 +1361,97 @@ mod shader_resources { string_len, string_len, ); - let error_string = String::from_utf8_lossy(&error_string_encode); - println!("Shader compile error: {}", error_string); + let error_string = String::from_utf8_lossy(&error_string_encode).to_string(); + log::error!("Shader compile error: {}", error_string); return Err(anyhow::anyhow!("Compile error: {}", error_string)); } Ok(compile_blob.unwrap()) } } } + +mod nvidia { + use std::{ + ffi::CStr, + os::raw::{c_char, c_int, c_uint}, + }; + + use anyhow::{Context, Result}; + use windows::{ + Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}, + core::s, + }; + + // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180 + const NVAPI_SHORT_STRING_MAX: usize = 64; + + // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235 + #[allow(non_camel_case_types)] + type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX]; + + // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447 + #[allow(non_camel_case_types)] + type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn( + driver_version: *mut c_uint, + build_branch_string: *mut NvAPI_ShortString, + ) -> c_int; + + pub(super) fn get_driver_version() -> Result { + unsafe { + // Try to load the NVIDIA driver DLL + let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?; + let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface")) + .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?; + let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr); + + // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41 + let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad); + if nvapi_get_driver_version_ptr.is_null() { + anyhow::bail!("Failed to get NVIDIA driver version function pointer"); + } + let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t = + std::mem::transmute(nvapi_get_driver_version_ptr); + + let mut driver_version: c_uint = 0; + let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX]; + let result = nvapi_get_driver_version( + &mut driver_version as *mut c_uint, + &mut build_branch_string as *mut NvAPI_ShortString, + ); + + if result != 0 { + anyhow::bail!( + "Failed to get NVIDIA driver version, error code: {}", + result + ); + } + let major = driver_version / 100; + let minor = driver_version % 100; + let branch_string = CStr::from_ptr(build_branch_string.as_ptr()); + Ok(format!( + "{}.{} {}", + major, + minor, + branch_string.to_string_lossy() + )) + } + } +} + +mod intel { + use windows::{ + Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice}, + core::Interface, + }; + + pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result { + let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?; + Ok(format!( + "{}.{}.{}.{}", + number >> 48, + (number >> 32) & 0xFFFF, + (number >> 16) & 0xFFFF, + number & 0xFFFF + )) + } +} diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 461aae5e25e5b1ccbbf3d9e174bf5c6cadd0e7b3..6fd899cbef864a5ff019e4496d3768345208dad5 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -181,12 +181,8 @@ fn handle_size_msg( let new_size = size(DevicePixels(width), DevicePixels(height)); let scale_factor = lock.scale_factor; if lock.restore_from_minimized.is_some() { - // lock.renderer - // .update_drawable_size_even_if_unchanged(new_size); - lock.renderer.resize(new_size).log_err(); lock.callbacks.request_frame = lock.restore_from_minimized.take(); } else { - // lock.renderer.update_drawable_size(new_size); lock.renderer.resize(new_size).log_err(); } let new_size = new_size.to_pixels(scale_factor); diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index a14e9ecb54bc543f02353eac1fc1c5a66a20cad7..ae6c4cc73d460b2c00fcbe3a09392fc02d85e2ad 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -28,13 +28,12 @@ use windows::{ core::*, }; -use crate::{platform::blade::BladeContext, *}; +use crate::*; pub(crate) struct WindowsPlatform { state: RefCell, raw_window_handles: RwLock>, // The below members will never change throughout the entire lifecycle of the app. - // gpu_context: BladeContext, directx_devices: DirectXDevices, icon: HICON, main_receiver: flume::Receiver, @@ -112,7 +111,6 @@ impl WindowsPlatform { let icon = load_icon().unwrap_or_default(); let state = RefCell::new(WindowsPlatformState::new()); let raw_window_handles = RwLock::new(SmallVec::new()); - // let gpu_context = BladeContext::new().context("Unable to init GPU context")?; let directx_devices = DirectXDevices::new().context("Unable to init directx devices.")?; let windows_version = WindowsVersion::new().context("Error retrieve windows version")?; @@ -462,7 +460,8 @@ impl Platform for WindowsPlatform { options, self.generate_creation_info(), &self.directx_devices, - )?; + ) + .inspect_err(|err| show_error("Failed to open new window", err.to_string()))?; let handle = window.get_raw_handle(); self.raw_window_handles.write().push(handle); diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index cf2fd4ca01709dcfa27a9dbb97dd9ee140ef5cc1..f0c773a6732306e07bc072fa9648a3bf4043cf80 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -93,10 +93,9 @@ float4 to_device_position(float2 unit_vertex, Bounds bounds) { } float4 distance_from_clip_rect_impl(float2 position, Bounds clip_bounds) { - return float4(position.x - clip_bounds.origin.x, - clip_bounds.origin.x + clip_bounds.size.x - position.x, - position.y - clip_bounds.origin.y, - clip_bounds.origin.y + clip_bounds.size.y - position.y); + float2 tl = position - clip_bounds.origin; + float2 br = clip_bounds.origin + clip_bounds.size - position; + return float4(tl.x, br.x, tl.y, br.y); } float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { @@ -240,6 +239,23 @@ float2 to_tile_position(float2 unit_vertex, AtlasTile tile) { return (float2(tile.bounds.origin) + unit_vertex * float2(tile.bounds.size)) / atlas_size; } +// Selects corner radius based on quadrant. +float pick_corner_radius(float2 center_to_point, Corners corner_radii) { + if (center_to_point.x < 0.) { + if (center_to_point.y < 0.) { + return corner_radii.top_left; + } else { + return corner_radii.bottom_left; + } + } else { + if (center_to_point.y < 0.) { + return corner_radii.top_right; + } else { + return corner_radii.bottom_right; + } + } +} + float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, TransformationMatrix transformation) { float2 position = unit_vertex * bounds.size + bounds.origin; @@ -248,48 +264,48 @@ float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, return float4(device_position, 0.0, 1.0); } +// Implementation of quad signed distance field +float quad_sdf_impl(float2 corner_center_to_point, float corner_radius) { + if (corner_radius == 0.0) { + // Fast path for unrounded corners + return max(corner_center_to_point.x, corner_center_to_point.y); + } else { + // Signed distance of the point from a quad that is inset by corner_radius + // It is negative inside this quad, and positive outside + float signed_distance_to_inset_quad = + // 0 inside the inset quad, and positive outside + length(max(float2(0.0, 0.0), corner_center_to_point)) + + // 0 outside the inset quad, and negative inside + min(0.0, max(corner_center_to_point.x, corner_center_to_point.y)); + + return signed_distance_to_inset_quad - corner_radius; + } +} + float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { float2 half_size = bounds.size / 2.; float2 center = bounds.origin + half_size; float2 center_to_point = pt - center; - float corner_radius; - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_left; - } else { - corner_radius = corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_right; - } else { - corner_radius = corner_radii.bottom_right; - } - } - - float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; - float distance = - length(max(0., rounded_edge_to_point)) + - min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - - corner_radius; - - return distance; + float corner_radius = pick_corner_radius(center_to_point, corner_radii); + float2 corner_to_point = abs(center_to_point) - half_size; + float2 corner_center_to_point = corner_to_point + corner_radius; + return quad_sdf_impl(corner_center_to_point, corner_radius); } -GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { +GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, LinearColorStop colors[2]) { GradientColor output; - if (tag == 0) { + if (tag == 0 || tag == 2) { output.solid = hsla_to_rgba(solid); } else if (tag == 1) { - output.color0 = hsla_to_rgba(color0); - output.color1 = hsla_to_rgba(color1); + output.color0 = hsla_to_rgba(colors[0].color); + output.color1 = hsla_to_rgba(colors[1].color); // Prepare color space in vertex for avoid conversion // in fragment shader for performance reasons if (color_space == 1) { - // Oklab - output.color0 = srgb_to_oklab(output.color0); - output.color1 = srgb_to_oklab(output.color1); + // Oklab + output.color0 = srgb_to_oklab(output.color0); + output.color1 = srgb_to_oklab(output.color1); } } @@ -326,8 +342,8 @@ float4 gradient_color(Background background, } // Get the t value for the linear gradient with the color stop percentages. - float2 half_size = float2(bounds.size.x, bounds.size.y) / 2.; - float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; + float2 half_size = bounds.size * 0.5; + float2 center = bounds.origin + half_size; float2 center_to_point = position - center; float t = dot(center_to_point, direction) / length(direction); // Check the direct to determine the use x or y @@ -376,97 +392,55 @@ float4 gradient_color(Background background, return color; } -/* -** -** Shadows -** -*/ - -struct ShadowVertexOutput { - float4 position: SV_Position; - float4 color: COLOR; - uint shadow_id: FLAT; - float4 clip_distance: SV_ClipDistance; -}; - -struct ShadowFragmentInput { - float4 position: SV_Position; - float4 color: COLOR; - uint shadow_id: FLAT; -}; - -struct Shadow { - uint order; - float blur_radius; - Bounds bounds; - Corners corner_radii; - Bounds content_mask; - Hsla color; -}; - -StructuredBuffer shadows: register(t1); - -ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV_InstanceID) { - float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); - Shadow shadow = shadows[shadow_id]; - - float margin = 3.0 * shadow.blur_radius; - Bounds bounds = shadow.bounds; - bounds.origin -= margin; - bounds.size += 2.0 * margin; - - float4 device_position = to_device_position(unit_vertex, bounds); - float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask); - float4 color = hsla_to_rgba(shadow.color); - - ShadowVertexOutput output; - output.position = device_position; - output.color = color; - output.shadow_id = shadow_id; - output.clip_distance = clip_distance; - - return output; -} - -float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { - Shadow shadow = shadows[input.shadow_id]; - - float2 half_size = shadow.bounds.size / 2.; - float2 center = shadow.bounds.origin + half_size; - float2 point0 = input.position.xy - center; - float corner_radius; - if (point0.x < 0.) { - if (point0.y < 0.) { - corner_radius = shadow.corner_radii.top_left; - } else { - corner_radius = shadow.corner_radii.bottom_left; - } +// Returns the dash velocity of a corner given the dash velocity of the two +// sides, by returning the slower velocity (larger dashes). +// +// Since 0 is used for dash velocity when the border width is 0 (instead of +// +inf), this returns the other dash velocity in that case. +// +// An alternative to this might be to appropriately interpolate the dash +// velocity around the corner, but that seems overcomplicated. +float corner_dash_velocity(float dv1, float dv2) { + if (dv1 == 0.0) { + return dv2; + } else if (dv2 == 0.0) { + return dv1; } else { - if (point0.y < 0.) { - corner_radius = shadow.corner_radii.top_right; - } else { - corner_radius = shadow.corner_radii.bottom_right; - } + return min(dv1, dv2); } +} - // The signal is only non-zero in a limited range, so don't waste samples - float low = point0.y - half_size.y; - float high = point0.y + half_size.y; - float start = clamp(-3. * shadow.blur_radius, low, high); - float end = clamp(3. * shadow.blur_radius, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - float step = (end - start) / 4.; - float y = start + step * 0.5; - float alpha = 0.; - for (int i = 0; i < 4; i++) { - alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius, - corner_radius, half_size) * - gaussian(y, shadow.blur_radius) * step; - y += step; - } +// Returns alpha used to render antialiased dashes. +// `t` is within the dash when `fmod(t, period) < length`. +float dash_alpha( + float t, float period, float length, float dash_velocity, + float antialias_threshold +) { + float half_period = period / 2.0; + float half_length = length / 2.0; + // Value in [-half_period, half_period] + // The dash is in [-half_length, half_length] + float centered = fmod(t + half_period - half_length, period) - half_period; + // Signed distance for the dash, negative values are inside the dash + float signed_distance = abs(centered) - half_length; + // Antialiased alpha based on the signed distance + return saturate(antialias_threshold - signed_distance / dash_velocity); +} - return input.color * float4(1., 1., 1., alpha); +// This approximates distance to the nearest point to a quarter ellipse in a way +// that is sufficient for anti-aliasing when the ellipse is not very eccentric. +// The components of `point` are expected to be positive. +// +// Negative on the outside and positive on the inside. +float quarter_ellipse_sdf(float2 pt, float2 radii) { + // Scale the space to treat the ellipse like a unit circle + float2 circle_vec = pt / radii; + float unit_circle_sdf = length(circle_vec) - 1.0; + // Approximate up-scaling of the length by using the average of the radii. + // + // TODO: A better solution would be to use the gradient of the implicit + // function for an ellipse to approximate a scaling factor. + return unit_circle_sdf * (radii.x + radii.y) * -0.5; } /* @@ -477,7 +451,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct Quad { uint order; - uint pad; + uint border_style; Bounds bounds; Bounds content_mask; Background background; @@ -487,9 +461,9 @@ struct Quad { }; struct QuadVertexOutput { + nointerpolation uint quad_id: TEXCOORD0; float4 position: SV_Position; nointerpolation float4 border_color: COLOR0; - nointerpolation uint quad_id: TEXCOORD0; nointerpolation float4 background_solid: COLOR1; nointerpolation float4 background_color0: COLOR2; nointerpolation float4 background_color1: COLOR3; @@ -511,16 +485,15 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); Quad quad = quads[quad_id]; float4 device_position = to_device_position(unit_vertex, quad.bounds); - float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask); - float4 border_color = hsla_to_rgba(quad.border_color); GradientColor gradient = prepare_gradient_color( quad.background.tag, quad.background.color_space, quad.background.solid, - quad.background.colors[0].color, - quad.background.colors[1].color + quad.background.colors ); + float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask); + float4 border_color = hsla_to_rgba(quad.border_color); QuadVertexOutput output; output.position = device_position; @@ -535,79 +508,380 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta float4 quad_fragment(QuadFragmentInput input): SV_Target { Quad quad = quads[input.quad_id]; - float2 half_size = quad.bounds.size / 2.; - float2 center = quad.bounds.origin + half_size; - float2 center_to_point = input.position.xy - center; - float4 color = gradient_color(quad.background, input.position.xy, quad.bounds, + float4 background_color = gradient_color(quad.background, input.position.xy, quad.bounds, input.background_solid, input.background_color0, input.background_color1); - // Fast path when the quad is not rounded and doesn't have any border. - if (quad.corner_radii.top_left == 0. && quad.corner_radii.bottom_left == 0. && - quad.corner_radii.top_right == 0. && - quad.corner_radii.bottom_right == 0. && quad.border_widths.top == 0. && - quad.border_widths.left == 0. && quad.border_widths.right == 0. && - quad.border_widths.bottom == 0.) { - return color; + bool unrounded = quad.corner_radii.top_left == 0.0 && + quad.corner_radii.top_right == 0.0 && + quad.corner_radii.bottom_left == 0.0 && + quad.corner_radii.bottom_right == 0.0; + + // Fast path when the quad is not rounded and doesn't have any border + if (quad.border_widths.top == 0.0 && + quad.border_widths.left == 0.0 && + quad.border_widths.right == 0.0 && + quad.border_widths.bottom == 0.0 && + unrounded) { + return background_color; } - float corner_radius; - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_left; - } else { - corner_radius = quad.corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_right; - } else { - corner_radius = quad.corner_radii.bottom_right; - } + float2 size = quad.bounds.size; + float2 half_size = size / 2.; + float2 the_point = input.position.xy - quad.bounds.origin; + float2 center_to_point = the_point - half_size; + + // Signed distance field threshold for inclusion of pixels. 0.5 is the + // minimum distance between the center of the pixel and the edge. + const float antialias_threshold = 0.5; + + // Radius of the nearest corner + float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii); + + float2 border = float2( + center_to_point.x < 0.0 ? quad.border_widths.left : quad.border_widths.right, + center_to_point.y < 0.0 ? quad.border_widths.top : quad.border_widths.bottom + ); + + // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`. + // The purpose of this is to not draw antialiasing pixels in this case. + float2 reduced_border = float2( + border.x == 0.0 ? -antialias_threshold : border.x, + border.y == 0.0 ? -antialias_threshold : border.y + ); + + // Vector from the corner of the quad bounds to the point, after mirroring + // the point into the bottom right quadrant. Both components are <= 0. + float2 corner_to_point = abs(center_to_point) - half_size; + + // Vector from the point to the center of the rounded corner's circle, also + // mirrored into bottom right quadrant. + float2 corner_center_to_point = corner_to_point + corner_radius; + + // Whether the nearest point on the border is rounded + bool is_near_rounded_corner = + corner_center_to_point.x >= 0.0 && + corner_center_to_point.y >= 0.0; + + // Vector from straight border inner corner to point. + // + // 0-width borders are turned into width -1 so that inner_sdf is > 1.0 near + // the border. Without this, antialiasing pixels would be drawn. + float2 straight_border_inner_corner_to_point = corner_to_point + reduced_border; + + // Whether the point is beyond the inner edge of the straight border + bool is_beyond_inner_straight_border = + straight_border_inner_corner_to_point.x > 0.0 || + straight_border_inner_corner_to_point.y > 0.0; + + // Whether the point is far enough inside the quad, such that the pixels are + // not affected by the straight border. + bool is_within_inner_straight_border = + straight_border_inner_corner_to_point.x < -antialias_threshold && + straight_border_inner_corner_to_point.y < -antialias_threshold; + + // Fast path for points that must be part of the background + if (is_within_inner_straight_border && !is_near_rounded_corner) { + return background_color; } - float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; - float distance = - length(max(0., rounded_edge_to_point)) + - min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - - corner_radius; - - float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left - : quad.border_widths.right; - float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top - : quad.border_widths.bottom; - float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border); - float2 point_to_inset_corner = abs(center_to_point) - inset_size; - float border_width; - if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) { - border_width = 0.; - } else if (point_to_inset_corner.y > point_to_inset_corner.x) { - border_width = horizontal_border; + // Signed distance of the point to the outside edge of the quad's border + float outer_sdf = quad_sdf_impl(corner_center_to_point, corner_radius); + + // Approximate signed distance of the point to the inside edge of the quad's + // border. It is negative outside this edge (within the border), and + // positive inside. + // + // This is not always an accurate signed distance: + // * The rounded portions with varying border width use an approximation of + // nearest-point-on-ellipse. + // * When it is quickly known to be outside the edge, -1.0 is used. + float inner_sdf = 0.0; + if (corner_center_to_point.x <= 0.0 || corner_center_to_point.y <= 0.0) { + // Fast paths for straight borders + inner_sdf = -max(straight_border_inner_corner_to_point.x, + straight_border_inner_corner_to_point.y); + } else if (is_beyond_inner_straight_border) { + // Fast path for points that must be outside the inner edge + inner_sdf = -1.0; + } else if (reduced_border.x == reduced_border.y) { + // Fast path for circular inner edge. + inner_sdf = -(outer_sdf + reduced_border.x); } else { - border_width = vertical_border; + float2 ellipse_radii = max(float2(0.0, 0.0), float2(corner_radius, corner_radius) - reduced_border); + inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii); } - if (border_width != 0.) { - float inset_distance = distance + border_width; + // Negative when inside the border + float border_sdf = max(inner_sdf, outer_sdf); + + float4 color = background_color; + if (border_sdf < antialias_threshold) { + float4 border_color = input.border_color; + // Dashed border logic when border_style == 1 + if (quad.border_style == 1) { + // Position along the perimeter in "dash space", where each dash + // period has length 1 + float t = 0.0; + + // Total number of dash periods, so that the dash spacing can be + // adjusted to evenly divide it + float max_t = 0.0; + + // Border width is proportional to dash size. This is the behavior + // used by browsers, but also avoids dashes from different segments + // overlapping when dash size is smaller than the border width. + // + // Dash pattern: (2 * border width) dash, (1 * border width) gap + const float dash_length_per_width = 2.0; + const float dash_gap_per_width = 1.0; + const float dash_period_per_width = dash_length_per_width + dash_gap_per_width; + + // Since the dash size is determined by border width, the density of + // dashes varies. Multiplying a pixel distance by this returns a + // position in dash space - it has units (dash period / pixels). So + // a dash velocity of (1 / 10) is 1 dash every 10 pixels. + float dash_velocity = 0.0; + + // Dividing this by the border width gives the dash velocity + const float dv_numerator = 1.0 / dash_period_per_width; + + if (unrounded) { + // When corners aren't rounded, the dashes are separately laid + // out on each straight line, rather than around the whole + // perimeter. This way each line starts and ends with a dash. + bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; + float border_width = is_horizontal ? border.x : border.y; + dash_velocity = dv_numerator / border_width; + t = is_horizontal ? the_point.x : the_point.y; + t *= dash_velocity; + max_t = is_horizontal ? size.x : size.y; + max_t *= dash_velocity; + } else { + // When corners are rounded, the dashes are laid out clockwise + // around the whole perimeter. + + float r_tr = quad.corner_radii.top_right; + float r_br = quad.corner_radii.bottom_right; + float r_bl = quad.corner_radii.bottom_left; + float r_tl = quad.corner_radii.top_left; + + float w_t = quad.border_widths.top; + float w_r = quad.border_widths.right; + float w_b = quad.border_widths.bottom; + float w_l = quad.border_widths.left; + + // Straight side dash velocities + float dv_t = w_t <= 0.0 ? 0.0 : dv_numerator / w_t; + float dv_r = w_r <= 0.0 ? 0.0 : dv_numerator / w_r; + float dv_b = w_b <= 0.0 ? 0.0 : dv_numerator / w_b; + float dv_l = w_l <= 0.0 ? 0.0 : dv_numerator / w_l; + + // Straight side lengths in dash space + float s_t = (size.x - r_tl - r_tr) * dv_t; + float s_r = (size.y - r_tr - r_br) * dv_r; + float s_b = (size.x - r_br - r_bl) * dv_b; + float s_l = (size.y - r_bl - r_tl) * dv_l; + + float corner_dash_velocity_tr = corner_dash_velocity(dv_t, dv_r); + float corner_dash_velocity_br = corner_dash_velocity(dv_b, dv_r); + float corner_dash_velocity_bl = corner_dash_velocity(dv_b, dv_l); + float corner_dash_velocity_tl = corner_dash_velocity(dv_t, dv_l); + + // Corner lengths in dash space + float c_tr = r_tr * (M_PI_F / 2.0) * corner_dash_velocity_tr; + float c_br = r_br * (M_PI_F / 2.0) * corner_dash_velocity_br; + float c_bl = r_bl * (M_PI_F / 2.0) * corner_dash_velocity_bl; + float c_tl = r_tl * (M_PI_F / 2.0) * corner_dash_velocity_tl; + + // Cumulative dash space upto each segment + float upto_tr = s_t; + float upto_r = upto_tr + c_tr; + float upto_br = upto_r + s_r; + float upto_b = upto_br + c_br; + float upto_bl = upto_b + s_b; + float upto_l = upto_bl + c_bl; + float upto_tl = upto_l + s_l; + max_t = upto_tl + c_tl; + + if (is_near_rounded_corner) { + float radians = atan2(corner_center_to_point.y, corner_center_to_point.x); + float corner_t = radians * corner_radius; + + if (center_to_point.x >= 0.0) { + if (center_to_point.y < 0.0) { + dash_velocity = corner_dash_velocity_tr; + // Subtracted because radians is pi/2 to 0 when + // going clockwise around the top right corner, + // since the y axis has been flipped + t = upto_r - corner_t * dash_velocity; + } else { + dash_velocity = corner_dash_velocity_br; + // Added because radians is 0 to pi/2 when going + // clockwise around the bottom-right corner + t = upto_br + corner_t * dash_velocity; + } + } else { + if (center_to_point.y >= 0.0) { + dash_velocity = corner_dash_velocity_bl; + // Subtracted because radians is pi/1 to 0 when + // going clockwise around the bottom-left corner, + // since the x axis has been flipped + t = upto_l - corner_t * dash_velocity; + } else { + dash_velocity = corner_dash_velocity_tl; + // Added because radians is 0 to pi/2 when going + // clockwise around the top-left corner, since both + // axis were flipped + t = upto_tl + corner_t * dash_velocity; + } + } + } else { + // Straight borders + bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y; + if (is_horizontal) { + if (center_to_point.y < 0.0) { + dash_velocity = dv_t; + t = (the_point.x - r_tl) * dash_velocity; + } else { + dash_velocity = dv_b; + t = upto_bl - (the_point.x - r_bl) * dash_velocity; + } + } else { + if (center_to_point.x < 0.0) { + dash_velocity = dv_l; + t = upto_tl - (the_point.y - r_tl) * dash_velocity; + } else { + dash_velocity = dv_r; + t = upto_r + (the_point.y - r_tr) * dash_velocity; + } + } + } + } + float dash_length = dash_length_per_width / dash_period_per_width; + float desired_dash_gap = dash_gap_per_width / dash_period_per_width; + + // Straight borders should start and end with a dash, so max_t is + // reduced to cause this. + max_t -= unrounded ? dash_length : 0.0; + if (max_t >= 1.0) { + // Adjust dash gap to evenly divide max_t + float dash_count = floor(max_t); + float dash_period = max_t / dash_count; + border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold); + } else if (unrounded) { + // When there isn't enough space for the full gap between the + // two start / end dashes of a straight border, reduce gap to + // make them fit. + float dash_gap = max_t - dash_length; + if (dash_gap > 0.0) { + float dash_period = dash_length + dash_gap; + border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold); + } + } + } + // Blend the border on top of the background and then linearly interpolate // between the two as we slide inside the background. - float4 blended_border = over(color, input.border_color); - color = lerp(blended_border, color, saturate(0.5 - inset_distance)); + float4 blended_border = over(background_color, border_color); + color = lerp(background_color, blended_border, + saturate(antialias_threshold - inner_sdf)); } - return color * float4(1., 1., 1., saturate(0.5 - distance)); + return color * float4(1.0, 1.0, 1.0, saturate(antialias_threshold - outer_sdf)); } -struct PathVertex { - float2 xy_position; +/* +** +** Shadows +** +*/ + +struct Shadow { + uint order; + float blur_radius; + Bounds bounds; + Corners corner_radii; Bounds content_mask; + Hsla color; }; +struct ShadowVertexOutput { + nointerpolation uint shadow_id: TEXCOORD0; + float4 position: SV_Position; + nointerpolation float4 color: COLOR; + float4 clip_distance: SV_ClipDistance; +}; + +struct ShadowFragmentInput { + nointerpolation uint shadow_id: TEXCOORD0; + float4 position: SV_Position; + nointerpolation float4 color: COLOR; +}; + +StructuredBuffer shadows: register(t1); + +ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV_InstanceID) { + float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + Shadow shadow = shadows[shadow_id]; + + float margin = 3.0 * shadow.blur_radius; + Bounds bounds = shadow.bounds; + bounds.origin -= margin; + bounds.size += 2.0 * margin; + + float4 device_position = to_device_position(unit_vertex, bounds); + float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask); + float4 color = hsla_to_rgba(shadow.color); + + ShadowVertexOutput output; + output.position = device_position; + output.color = color; + output.shadow_id = shadow_id; + output.clip_distance = clip_distance; + + return output; +} + +float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { + Shadow shadow = shadows[input.shadow_id]; + + float2 half_size = shadow.bounds.size / 2.; + float2 center = shadow.bounds.origin + half_size; + float2 point0 = input.position.xy - center; + float corner_radius = pick_corner_radius(point0, shadow.corner_radii); + + // The signal is only non-zero in a limited range, so don't waste samples + float low = point0.y - half_size.y; + float high = point0.y + half_size.y; + float start = clamp(-3. * shadow.blur_radius, low, high); + float end = clamp(3. * shadow.blur_radius, low, high); + + // Accumulate samples (we can get away with surprisingly few samples) + float step = (end - start) / 4.; + float y = start + step * 0.5; + float alpha = 0.; + for (int i = 0; i < 4; i++) { + alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius, + corner_radius, half_size) * + gaussian(y, shadow.blur_radius) * step; + y += step; + } + + return input.color * float4(1., 1., 1., alpha); +} + /* ** ** Paths ** */ +struct PathVertex { + float2 xy_position: POSITION; + Bounds content_mask: TEXCOORD; + uint idx: GLOBALIDX; +}; + struct PathSprite { Bounds bounds; Background color; @@ -615,31 +889,36 @@ struct PathSprite { struct PathVertexOutput { float4 position: SV_Position; + nointerpolation uint sprite_id: TEXCOORD0; + nointerpolation float4 solid_color: COLOR0; + nointerpolation float4 color0: COLOR1; + nointerpolation float4 color1: COLOR2; float4 clip_distance: SV_ClipDistance; +}; + +struct PathFragmentInput { + float4 position: SV_Position; nointerpolation uint sprite_id: TEXCOORD0; nointerpolation float4 solid_color: COLOR0; nointerpolation float4 color0: COLOR1; nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_vertices: register(t1); -StructuredBuffer path_sprites: register(t2); +StructuredBuffer path_sprites: register(t1); -PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { - PathVertex v = path_vertices[vertex_id]; - PathSprite sprite = path_sprites[instance_id]; +PathVertexOutput paths_vertex(PathVertex input) { + PathSprite sprite = path_sprites[input.idx]; PathVertexOutput output; - output.position = to_device_position_impl(v.xy_position); - output.clip_distance = distance_from_clip_rect_impl(v.xy_position, v.content_mask); - output.sprite_id = instance_id; + output.position = to_device_position_impl(input.xy_position); + output.clip_distance = distance_from_clip_rect_impl(input.xy_position, input.content_mask); + output.sprite_id = input.idx; GradientColor gradient = prepare_gradient_color( sprite.color.tag, sprite.color.color_space, sprite.color.solid, - sprite.color.colors[0].color, - sprite.color.colors[1].color + sprite.color.colors ); output.solid_color = gradient.solid; @@ -648,12 +927,7 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ return output; } -float4 paths_fragment(PathVertexOutput input): SV_Target { - float4 zero = 0.0; - if (any(input.clip_distance < zero)) { - return zero; - } - +float4 paths_fragment(PathFragmentInput input): SV_Target { PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, @@ -678,16 +952,16 @@ struct Underline { }; struct UnderlineVertexOutput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; float4 clip_distance: SV_ClipDistance; }; struct UnderlineFragmentInput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; }; StructuredBuffer underlines: register(t1); @@ -712,10 +986,8 @@ float4 underline_fragment(UnderlineFragmentInput input): SV_Target { Underline underline = underlines[input.underline_id]; if (underline.wavy) { float half_thickness = underline.thickness * 0.5; - float2 origin = - float2(underline.bounds.origin.x, underline.bounds.origin.y); - float2 st = ((input.position.xy - origin) / underline.bounds.size.y) - - float2(0., 0.5); + float2 origin = underline.bounds.origin; + float2 st = ((input.position.xy - origin) / underline.bounds.size.y) - float2(0., 0.5); float frequency = (M_PI_F * (3. * underline.thickness)) / 8.; float amplitude = 1. / (2. * underline.thickness); float sine = sin(st.x * frequency) * amplitude; @@ -751,14 +1023,14 @@ struct MonochromeSprite { struct MonochromeSpriteVertexOutput { float4 position: SV_Position; float2 tile_position: POSITION; - float4 color: COLOR; + nointerpolation float4 color: COLOR; float4 clip_distance: SV_ClipDistance; }; struct MonochromeSpriteFragmentInput { float4 position: SV_Position; float2 tile_position: POSITION; - float4 color: COLOR; + nointerpolation float4 color: COLOR; }; StructuredBuffer mono_sprites: register(t1); @@ -795,7 +1067,9 @@ float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Targe struct PolychromeSprite { uint order; + uint pad; uint grayscale; + float opacity; Bounds bounds; Bounds content_mask; Corners corner_radii; @@ -803,16 +1077,16 @@ struct PolychromeSprite { }; struct PolychromeSpriteVertexOutput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; float4 clip_distance: SV_ClipDistance; }; struct PolychromeSpriteFragmentInput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; }; StructuredBuffer poly_sprites: register(t1); @@ -843,6 +1117,6 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } - color.a *= saturate(0.5 - distance); + color.a *= sprite.opacity * saturate(0.5 - distance); return color; } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 2801674a649896d33cb6b47736cd9f4117d80c80..eda75956b64e337aef2fba9efd1f6a1a176d25d5 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -26,7 +26,6 @@ use windows::{ core::*, }; -use crate::platform::blade::{BladeContext, BladeRenderer}; use crate::*; pub(crate) struct WindowsWindow(pub Rc); @@ -80,7 +79,6 @@ pub(crate) struct WindowsWindowStatePtr { impl WindowsWindowState { fn new( hwnd: HWND, - transparent: bool, cs: &CREATESTRUCTW, current_cursor: Option, display: WindowsDisplay, @@ -103,8 +101,7 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; - // let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; - let renderer = DirectXRenderer::new(gpu_context, hwnd, transparent)?; + let renderer = DirectXRenderer::new(gpu_context, hwnd)?; let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -207,7 +204,6 @@ impl WindowsWindowStatePtr { fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result> { let state = RefCell::new(WindowsWindowState::new( hwnd, - context.transparent, cs, context.current_cursor, context.display, @@ -335,7 +331,6 @@ struct WindowCreateContext<'a> { handle: AnyWindowHandle, hide_title_bar: bool, display: WindowsDisplay, - transparent: bool, is_movable: bool, min_size: Option>, executor: ForegroundExecutor, @@ -381,10 +376,13 @@ impl WindowsWindow { .unwrap_or(""), ); let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp { - (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) + ( + WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP, + WINDOW_STYLE(0x0), + ) } else { ( - WS_EX_APPWINDOW | WS_EX_LAYERED, + WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -402,7 +400,6 @@ impl WindowsWindow { handle, hide_title_bar, display, - transparent: true, is_movable: params.is_movable, min_size: params.window_min_size, executor, @@ -454,14 +451,6 @@ impl WindowsWindow { state: WindowOpenState::Windowed, }); } - // The render pipeline will perform compositing on the GPU when the - // swapchain is configured correctly (see downstream of - // update_transparency). - // The following configuration is a one-time setup to ensure that the - // window is going to be composited with per-pixel alpha, but the render - // pipeline is responsible for effectively calling UpdateLayeredWindow - // at the appropriate time. - unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } @@ -486,7 +475,6 @@ impl rwh::HasDisplayHandle for WindowsWindow { impl Drop for WindowsWindow { fn drop(&mut self) { - // self.0.state.borrow_mut().renderer.destroy(); // clone this `Rc` to prevent early release of the pointer let this = self.0.clone(); self.0 @@ -706,25 +694,21 @@ impl PlatformWindow for WindowsWindow { } fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - let mut window_state = self.0.state.borrow_mut(); - // todo(zjk) - // window_state - // .renderer - // .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque); + let hwnd = self.0.hwnd; match background_appearance { WindowBackgroundAppearance::Opaque => { // ACCENT_DISABLED - set_window_composition_attribute(window_state.hwnd, None, 0); + set_window_composition_attribute(hwnd, None, 0); } WindowBackgroundAppearance::Transparent => { // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background - set_window_composition_attribute(window_state.hwnd, None, 2); + set_window_composition_attribute(hwnd, None, 2); } WindowBackgroundAppearance::Blurred => { // Enable acrylic blur // ACCENT_ENABLE_ACRYLICBLURBEHIND - set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); + set_window_composition_attribute(hwnd, Some((0, 0, 0, 0)), 4); } } } @@ -808,13 +792,11 @@ impl PlatformWindow for WindowsWindow { } fn gpu_specs(&self) -> Option { - // todo(zjk) - // Some(self.0.state.borrow().renderer.gpu_specs()) - None + self.0.state.borrow().renderer.gpu_specs().log_err() } fn update_ime_position(&self, _bounds: Bounds) { - // todo(windows) + // There is no such thing on Windows. } } @@ -1310,52 +1292,6 @@ fn set_window_composition_attribute(hwnd: HWND, color: Option, state: u32 } } -mod windows_renderer { - use crate::platform::blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}; - use raw_window_handle as rwh; - use std::num::NonZeroIsize; - use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::GWLP_HINSTANCE}; - - use crate::{get_window_long, show_error}; - - pub(super) fn init( - context: &BladeContext, - hwnd: HWND, - transparent: bool, - ) -> anyhow::Result { - let raw = RawWindow { hwnd }; - let config = BladeSurfaceConfig { - size: Default::default(), - transparent, - }; - BladeRenderer::new(context, &raw, config) - .inspect_err(|err| show_error("Failed to initialize BladeRenderer", err.to_string())) - } - - struct RawWindow { - hwnd: HWND, - } - - impl rwh::HasWindowHandle for RawWindow { - fn window_handle(&self) -> Result, rwh::HandleError> { - Ok(unsafe { - let hwnd = NonZeroIsize::new_unchecked(self.hwnd.0 as isize); - let mut handle = rwh::Win32WindowHandle::new(hwnd); - let hinstance = get_window_long(self.hwnd, GWLP_HINSTANCE); - handle.hinstance = NonZeroIsize::new(hinstance); - rwh::WindowHandle::borrow_raw(handle.into()) - }) - } - } - - impl rwh::HasDisplayHandle for RawWindow { - fn display_handle(&self) -> Result, rwh::HandleError> { - let handle = rwh::WindowsDisplayHandle::new(); - Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) }) - } - } -} - #[cfg(test)] mod tests { use super::ClickState;