From ca3d55ee4daf8d940cb8498ba08ee6449a663fb2 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 13:15:24 +0800 Subject: [PATCH] wip --- .../src/platform/windows/directx_atlas.rs | 33 +- .../src/platform/windows/directx_renderer.rs | 381 +++++----- crates/gpui/src/platform/windows/events.rs | 8 +- crates/gpui/src/platform/windows/platform.rs | 10 +- crates/gpui/src/platform/windows/shaders.hlsl | 676 ++++++++++++++++++ crates/gpui/src/platform/windows/window.rs | 28 +- 6 files changed, 910 insertions(+), 226 deletions(-) create mode 100644 crates/gpui/src/platform/windows/shaders.hlsl diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 4bb903a463993328fd61803104bd055c34c859f2..03d33f5cbc400f670645368e35af99e41bc9b3b9 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -84,7 +84,7 @@ impl DirectXAtlas { let textures = match texture_kind { AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, - AtlasTextureKind::Path => &mut lock.path_textures, + // AtlasTextureKind::Path => &mut lock.path_textures, }; for texture in textures { texture.clear(); @@ -131,7 +131,7 @@ impl DirectXAtlasState { let textures = match texture_kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Path => &mut self.path_textures, + // AtlasTextureKind::Path => &mut self.path_textures, }; textures @@ -173,12 +173,11 @@ impl DirectXAtlasState { pixel_format = DXGI_FORMAT_B8G8R8A8_UNORM; bind_flag = D3D11_BIND_SHADER_RESOURCE; bytes_per_pixel = 4; - } - AtlasTextureKind::Path => { - pixel_format = DXGI_FORMAT_R16_FLOAT; - bind_flag = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - bytes_per_pixel = 2; - } + } // AtlasTextureKind::Path => { + // pixel_format = DXGI_FORMAT_R16_FLOAT; + // bind_flag = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + // bytes_per_pixel = 2; + // } } let texture_desc = D3D11_TEXTURE2D_DESC { Width: size.width.0 as u32, @@ -206,16 +205,16 @@ impl DirectXAtlasState { let textures = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Path => &mut self.path_textures, + // AtlasTextureKind::Path => &mut self.path_textures, }; let rtv = match kind { - AtlasTextureKind::Path => unsafe { - let mut view: Option = None; - self.device - .CreateRenderTargetView(&texture, None, Some(&mut view)) - .unwrap(); - [view] - }, + // AtlasTextureKind::Path => unsafe { + // let mut view: Option = None; + // self.device + // .CreateRenderTargetView(&texture, None, Some(&mut view)) + // .unwrap(); + // [view] + // }, _ => [None], }; let view = unsafe { @@ -244,7 +243,7 @@ impl DirectXAtlasState { let textures = match id.kind { crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, - crate::AtlasTextureKind::Path => &self.path_textures, + // crate::AtlasTextureKind::Path => &self.path_textures, }; &textures[id.index as usize] } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index d135bd4f3359c1e1915d8910d4dd9067b9547424..4a7f3796d79f8be4dec0be7a0931555364c15f48 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -3,11 +3,11 @@ use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; use collections::FxHasher; -#[cfg(not(feature = "enable-renderdoc"))] -use windows::Win32::Graphics::DirectComposition::*; +// #[cfg(not(feature = "enable-renderdoc"))] +// use windows::Win32::Graphics::DirectComposition::*; use windows::{ Win32::{ - Foundation::HWND, + Foundation::{HMODULE, HWND}, Graphics::{ Direct3D::*, Direct3D11::*, @@ -41,8 +41,8 @@ struct DirectXContext { swap_chain: IDXGISwapChain1, back_buffer: [Option; 1], viewport: [D3D11_VIEWPORT; 1], - #[cfg(not(feature = "enable-renderdoc"))] - direct_composition: DirectComposition, + // #[cfg(not(feature = "enable-renderdoc"))] + // direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -62,12 +62,12 @@ struct DirectXGlobalElements { blend_state_for_pr: ID3D11BlendState, } -#[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 { @@ -91,17 +91,17 @@ impl DirectXDevices { } impl DirectXRenderer { - pub(crate) fn new(devices: DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { let atlas = Arc::new(DirectXAtlas::new( devices.device.clone(), devices.device_context.clone(), )); - let context = DirectXContext::new(&devices, hwnd, transparent)?; + let context = DirectXContext::new(devices, hwnd, transparent)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; Ok(DirectXRenderer { atlas, - devices, + devices: devices.clone(), context, globals, pipelines, @@ -110,7 +110,7 @@ impl DirectXRenderer { }) } - pub(crate) fn spirite_atlas(&self) -> Arc { + pub(crate) fn sprite_atlas(&self) -> Arc { self.atlas.clone() } @@ -153,7 +153,7 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - unsafe { self.context.swap_chain.Present(0, 0) }.ok()?; + unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?; Ok(()) } @@ -166,7 +166,7 @@ impl DirectXRenderer { new_size.width.0 as u32, new_size.height.0 as u32, DXGI_FORMAT_B8G8R8A8_UNORM, - 0, + DXGI_SWAP_CHAIN_FLAG(0), )?; } let backbuffer = set_render_target_view( @@ -183,32 +183,32 @@ impl DirectXRenderer { 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")] + // #[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, @@ -280,77 +280,78 @@ impl DirectXRenderer { &mut self, paths: &[Path], ) -> Option> { - self.atlas.clear_textures(AtlasTextureKind::Path); - - let mut tiles = HashMap::default(); - let mut vertices_by_texture_id: HashMap< - AtlasTextureId, - Vec>, - BuildHasherDefault, - > = HashMap::default(); - for path in paths { - let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds); - - let tile = self - .atlas - .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path)?; - vertices_by_texture_id - .entry(tile.texture_id) - .or_insert(Vec::new()) - .extend(path.vertices.iter().map(|vertex| PathVertex { - xy_position: vertex.xy_position - clipped_bounds.origin - + tile.bounds.origin.map(Into::into), - content_mask: ContentMask { - bounds: tile.bounds.map(Into::into), - }, - })); - tiles.insert(path.id, tile); - } - - for (texture_id, vertices) in vertices_by_texture_id { - let (texture_size, rtv) = self.atlas.get_texture_drawing_info(texture_id); - let viewport = [D3D11_VIEWPORT { - TopLeftX: 0.0, - TopLeftY: 0.0, - Width: texture_size.width, - Height: texture_size.height, - MinDepth: 0.0, - MaxDepth: 1.0, - }]; - pre_draw( - &self.devices.device_context, - &self.globals.global_params_buffer, - &viewport, - &rtv, - [0.0, 0.0, 0.0, 1.0], - &self.globals.blend_state_for_pr, - ) - .log_err()?; - update_buffer_capacity( - &self.pipelines.path_raster_pipeline, - std::mem::size_of::>(), - vertices.len(), - &self.devices.device, - ) - .map(|input| update_pipeline(&mut self.pipelines.path_raster_pipeline, input)); - update_buffer( - &self.devices.device_context, - &self.pipelines.path_raster_pipeline.buffer, - &vertices, - ) - .log_err()?; - draw_normal( - &self.devices.device_context, - &self.pipelines.path_raster_pipeline, - &viewport, - &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, - vertices.len() as u32, - 1, - ) - .log_err()?; - } - Some(tiles) + // self.atlas.clear_textures(AtlasTextureKind::Path); + + // let mut tiles = HashMap::default(); + // let mut vertices_by_texture_id: HashMap< + // AtlasTextureId, + // Vec>, + // BuildHasherDefault, + // > = HashMap::default(); + // for path in paths { + // let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds); + + // let tile = self + // .atlas + // .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path)?; + // vertices_by_texture_id + // .entry(tile.texture_id) + // .or_insert(Vec::new()) + // .extend(path.vertices.iter().map(|vertex| PathVertex { + // xy_position: vertex.xy_position - clipped_bounds.origin + // + tile.bounds.origin.map(Into::into), + // content_mask: ContentMask { + // bounds: tile.bounds.map(Into::into), + // }, + // })); + // tiles.insert(path.id, tile); + // } + + // for (texture_id, vertices) in vertices_by_texture_id { + // let (texture_size, rtv) = self.atlas.get_texture_drawing_info(texture_id); + // let viewport = [D3D11_VIEWPORT { + // TopLeftX: 0.0, + // TopLeftY: 0.0, + // Width: texture_size.width, + // Height: texture_size.height, + // MinDepth: 0.0, + // MaxDepth: 1.0, + // }]; + // pre_draw( + // &self.devices.device_context, + // &self.globals.global_params_buffer, + // &viewport, + // &rtv, + // [0.0, 0.0, 0.0, 1.0], + // &self.globals.blend_state_for_pr, + // ) + // .log_err()?; + // update_buffer_capacity( + // &self.pipelines.path_raster_pipeline, + // std::mem::size_of::>(), + // vertices.len(), + // &self.devices.device, + // ) + // .map(|input| update_pipeline(&mut self.pipelines.path_raster_pipeline, input)); + // update_buffer( + // &self.devices.device_context, + // &self.pipelines.path_raster_pipeline.buffer, + // &vertices, + // ) + // .log_err()?; + // draw_normal( + // &self.devices.device_context, + // &self.pipelines.path_raster_pipeline, + // &viewport, + // &self.globals.global_params_buffer, + // D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + // vertices.len() as u32, + // 1, + // ) + // .log_err()?; + // } + // Some(tiles) + None } fn draw_paths( @@ -358,43 +359,43 @@ impl DirectXRenderer { paths: &[Path], path_tiles: &HashMap, ) -> Result<()> { - if paths.is_empty() { - return Ok(()); - } - for path in paths { - let tile = &path_tiles[&path.id]; - let texture_view = self.atlas.get_texture_view(tile.texture_id); - let origin = path.bounds.intersect(&path.content_mask.bounds).origin; - let sprites = [PathSprite { - bounds: Bounds { - origin: origin.map(|p| p.floor()), - size: tile.bounds.size.map(Into::into), - }, - color: path.color, - tile: (*tile).clone(), - }]; - update_buffer_capacity( - &self.pipelines.paths_pipeline, - std::mem::size_of::(), - 1, - &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, - )?; - draw_with_texture( - &self.devices.device_context, - &self.pipelines.paths_pipeline, - &texture_view, - &self.context.viewport, - &self.globals.global_params_buffer, - &self.globals.sampler, - 1, - )?; - } + // if paths.is_empty() { + // return Ok(()); + // } + // for path in paths { + // let tile = &path_tiles[&path.id]; + // let texture_view = self.atlas.get_texture_view(tile.texture_id); + // let origin = path.bounds.intersect(&path.content_mask.bounds).origin; + // let sprites = [PathSprite { + // bounds: Bounds { + // origin: origin.map(|p| p.floor()), + // size: tile.bounds.size.map(Into::into), + // }, + // color: path.color, + // tile: (*tile).clone(), + // }]; + // update_buffer_capacity( + // &self.pipelines.paths_pipeline, + // std::mem::size_of::(), + // 1, + // &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, + // )?; + // draw_with_texture( + // &self.devices.device_context, + // &self.pipelines.paths_pipeline, + // &texture_view, + // &self.context.viewport, + // &self.globals.global_params_buffer, + // &self.globals.sampler, + // 1, + // )?; + // } Ok(()) } @@ -489,7 +490,7 @@ impl DirectXRenderer { ) } - fn draw_surfaces(&mut self, surfaces: &[Surface]) -> Result<()> { + fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> { if surfaces.is_empty() { return Ok(()); } @@ -499,15 +500,15 @@ impl DirectXRenderer { 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")] + // #[cfg(not(feature = "enable-renderdoc"))] + // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?; + // #[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)?; + // #[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, @@ -520,8 +521,8 @@ impl DirectXContext { swap_chain, back_buffer, viewport, - #[cfg(not(feature = "enable-renderdoc"))] - direct_composition, + // #[cfg(not(feature = "enable-renderdoc"))] + // direct_composition, }) } } @@ -590,29 +591,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 { @@ -726,7 +727,7 @@ fn get_device( D3D11CreateDevice( adapter, D3D_DRIVER_TYPE_UNKNOWN, - None, + HMODULE::default(), device_flags, Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]), D3D11_SDK_VERSION, @@ -737,10 +738,10 @@ fn get_device( }) } -#[cfg(not(feature = "enable-renderdoc"))] -fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { - Ok(unsafe { DCompositionCreateDevice(dxgi_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, @@ -772,7 +773,7 @@ fn create_swap_chain( Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } -#[cfg(feature = "enable-renderdoc")] +// #[cfg(feature = "enable-renderdoc")] fn create_swap_chain_default( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 839fd10375b04180b5699e0bd6ab5fa3441f8b2b..461aae5e25e5b1ccbbf3d9e174bf5c6cadd0e7b3 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -181,11 +181,13 @@ 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 + // .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.update_drawable_size(new_size); + lock.renderer.resize(new_size).log_err(); } let new_size = new_size.to_pixels(scale_factor); lock.logical_size = new_size; diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 401ecdeffecc1aefdab85ec1728aa6918a0e0857..a0bdb6dedf474eaf92e12fe56df92db5eca0ed58 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -34,7 +34,8 @@ 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, + // gpu_context: BladeContext, + directx_devices: DirectXDevices, icon: HICON, main_receiver: flume::Receiver, background_executor: BackgroundExecutor, @@ -111,13 +112,14 @@ 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 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")?; Ok(Self { state, raw_window_handles, - gpu_context, + directx_devices, icon, main_receiver, background_executor, @@ -459,7 +461,7 @@ impl Platform for WindowsPlatform { handle, options, self.generate_creation_info(), - &self.gpu_context, + &self.directx_devices, )?; 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 new file mode 100644 index 0000000000000000000000000000000000000000..bb6342d6fb13daac198113b7fa5cada2d24329b1 --- /dev/null +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -0,0 +1,676 @@ +cbuffer GlobalParams: register(b0) { + float2 global_viewport_size; + uint2 _global_pad; +}; + +Texture2D t_sprite: register(t0); +SamplerState s_sprite: register(s0); + +struct Bounds { + float2 origin; + float2 size; +}; + +struct Corners { + float top_left; + float top_right; + float bottom_right; + float bottom_left; +}; + +struct Edges { + float top; + float right; + float bottom; + float left; +}; + +struct Hsla { + float h; + float s; + float l; + float a; +}; + +struct AtlasTextureId { + uint index; + uint kind; +}; + +struct AtlasBounds { + int2 origin; + int2 size; +}; + +struct AtlasTile { + AtlasTextureId texture_id; + uint tile_id; + uint padding; + AtlasBounds bounds; +}; + +struct TransformationMatrix { + float2x2 rotation_scale; + float2 translation; +}; + +static const float M_PI_F = 3.141592653f; +static const float3 GRAYSCALE_FACTORS = float3(0.2126f, 0.7152f, 0.0722f); + +float4 to_device_position(float2 unit_vertex, Bounds bounds) { + float2 position = unit_vertex * bounds.size + bounds.origin; + float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); + return float4(device_position, 0., 1.); +} + +float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { + float2 position = unit_vertex * bounds.size + bounds.origin; + 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); +} + +float4 hsla_to_rgba(Hsla hsla) { + float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range + float s = hsla.s; + float l = hsla.l; + float a = hsla.a; + + float c = (1.0 - abs(2.0 * l - 1.0)) * s; + float x = c * (1.0 - abs(fmod(h, 2.0) - 1.0)); + float m = l - c / 2.0; + + float r = 0.0; + float g = 0.0; + float b = 0.0; + + if (h >= 0.0 && h < 1.0) { + r = c; + g = x; + b = 0.0; + } else if (h >= 1.0 && h < 2.0) { + r = x; + g = c; + b = 0.0; + } else if (h >= 2.0 && h < 3.0) { + r = 0.0; + g = c; + b = x; + } else if (h >= 3.0 && h < 4.0) { + r = 0.0; + g = x; + b = c; + } else if (h >= 4.0 && h < 5.0) { + r = x; + g = 0.0; + b = c; + } else { + r = c; + g = 0.0; + b = x; + } + + float4 rgba; + rgba.x = (r + m); + rgba.y = (g + m); + rgba.z = (b + m); + rgba.w = a; + return rgba; +} + +// This approximates the error function, needed for the gaussian integral +float2 erf(float2 x) { + float2 s = sign(x); + float2 a = abs(x); + x = 1. + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a; + x *= x; + return s - s / (x * x); +} + +float blur_along_x(float x, float y, float sigma, float corner, float2 half_size) { + float delta = min(half_size.y - corner - abs(y), 0.); + float curved = half_size.x - corner + sqrt(max(0., corner * corner - delta * delta)); + float2 integral = 0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5) / sigma)); + return integral.y - integral.x; +} + +// A standard gaussian function, used for weighting samples +float gaussian(float x, float sigma) { + return exp(-(x * x) / (2. * sigma * sigma)) / (sqrt(2. * M_PI_F) * sigma); +} + +float4 over(float4 below, float4 above) { + float4 result; + float alpha = above.a + below.a * (1.0 - above.a); + result.rgb = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; + result.a = alpha; + return result; +} + +float2 to_tile_position(float2 unit_vertex, AtlasTile tile) { + float2 atlas_size; + t_sprite.GetDimensions(atlas_size.x, atlas_size.y); + return (float2(tile.bounds.origin) + unit_vertex * float2(tile.bounds.size)) / atlas_size; +} + +float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, + TransformationMatrix transformation) { + float2 position = unit_vertex * bounds.size + bounds.origin; + float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation; + float2 device_position = transformed / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); + return float4(device_position, 0.0, 1.0); +} + +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; +} + +/* +** +** 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; + } + } else { + if (point0.y < 0.) { + corner_radius = shadow.corner_radii.top_right; + } else { + corner_radius = shadow.corner_radii.bottom_right; + } + } + + // 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); +} + +/* +** +** Quads +** +*/ + +struct Quad { + uint order; + uint pad; + Bounds bounds; + Bounds content_mask; + Hsla background; + Hsla border_color; + Corners corner_radii; + Edges border_widths; +}; + +struct QuadVertexOutput { + float4 position: SV_Position; + float4 background_color: COLOR0; + float4 border_color: COLOR1; + uint quad_id: FLAT; + float4 clip_distance: SV_ClipDistance; +}; + +struct QuadFragmentInput { + float4 position: SV_Position; + float4 background_color: COLOR0; + float4 border_color: COLOR1; + uint quad_id: FLAT; +}; + +StructuredBuffer quads: register(t1); + +QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_InstanceID) { + 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 background_color = hsla_to_rgba(quad.background); + float4 border_color = hsla_to_rgba(quad.border_color); + + QuadVertexOutput output; + output.position = device_position; + output.background_color = background_color; + output.border_color = border_color; + output.quad_id = quad_id; + output.clip_distance = clip_distance; + return output; +} + +float4 quad_fragment(QuadFragmentInput input): SV_Target { + Quad quad = quads[input.quad_id]; + + // 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 input.background_color; + } + + float2 half_size = quad.bounds.size / 2.; + float2 center = quad.bounds.origin + half_size; + float2 center_to_point = input.position.xy - center; + 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 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; + } else { + border_width = vertical_border; + } + + float4 color; + if (border_width == 0.) { + color = input.background_color; + } else { + float inset_distance = distance + border_width; + // 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(input.background_color, input.border_color); + color = lerp(blended_border, input.background_color, + saturate(0.5 - inset_distance)); + } + + return color * float4(1., 1., 1., saturate(0.5 - distance)); +} + +/* +** +** Path raster +** +*/ + +struct PathVertex { + float2 xy_position; + float2 st_position; + Bounds content_mask; +}; + +struct PathRasterizationOutput { + float4 position: SV_Position; + float2 st_position: TEXCOORD0; + float4 clip_distances: SV_ClipDistance; +}; + +struct PathRasterizationInput { + float4 position: SV_Position; + float2 st_position: TEXCOORD0; +}; + +StructuredBuffer path_vertices: register(t1); + +PathRasterizationOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) { + PathVertex vertex = path_vertices[vertex_id]; + PathRasterizationOutput output; + float2 device_position = vertex.xy_position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0); + float2 tl = vertex.xy_position - vertex.content_mask.origin; + float2 br = vertex.content_mask.origin + vertex.content_mask.size - vertex.xy_position; + + output.position = float4(device_position, 0.0, 1.0); + output.st_position = vertex.st_position; + output.clip_distances = float4(tl.x, br.x, tl.y, br.y); + return output; +} + +float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { + float2 dx = ddx(input.st_position); + float2 dy = ddy(input.st_position); + float2 gradient = float2((2. * input.st_position.x) * dx.x - dx.y, + (2. * input.st_position.x) * dy.x - dy.y); + float f = (input.st_position.x * input.st_position.x) - input.st_position.y; + float distance = f / length(gradient); + float alpha = saturate(0.5 - distance); + return float4(alpha, 0., 0., 1.); +} + +/* +** +** Paths +** +*/ + +struct PathSprite { + Bounds bounds; + Hsla color; + AtlasTile tile; +}; + +struct PathVertexOutput { + float4 position: SV_Position; + float2 tile_position: POSITION1; + float4 color: COLOR; +}; + +StructuredBuffer path_sprites: register(t1); + +PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) { + float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + PathSprite sprite = path_sprites[instance_id]; + // Don't apply content mask because it was already accounted for when rasterizing the path. + + PathVertexOutput output; + output.position = to_device_position(unit_vertex, sprite.bounds); + output.tile_position = to_tile_position(unit_vertex, sprite.tile); + output.color = hsla_to_rgba(sprite.color); + return output; +} + +float4 paths_fragment(PathVertexOutput input): SV_Target { + float sample = t_sprite.Sample(s_sprite, input.tile_position).r; + float mask = 1.0 - abs(1.0 - sample % 2.0); + float4 color = input.color; + color.a *= mask; + return color; +} + +/* +** +** Underlines +** +*/ + +struct Underline { + uint order; + uint pad; + Bounds bounds; + Bounds content_mask; + Hsla color; + float thickness; + uint wavy; +}; + +struct UnderlineVertexOutput { + float4 position: SV_Position; + float4 color: COLOR; + uint underline_id: FLAT; + float4 clip_distance: SV_ClipDistance; +}; + +struct UnderlineFragmentInput { + float4 position: SV_Position; + float4 color: COLOR; + uint underline_id: FLAT; +}; + +StructuredBuffer underlines: register(t1); + +UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underline_id: SV_InstanceID) { + float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + Underline underline = underlines[underline_id]; + float4 device_position = to_device_position(unit_vertex, underline.bounds); + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, + underline.content_mask); + float4 color = hsla_to_rgba(underline.color); + + UnderlineVertexOutput output; + output.position = device_position; + output.color = color; + output.underline_id = underline_id; + output.clip_distance = clip_distance; + return output; +} + +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); + float frequency = (M_PI_F * (3. * underline.thickness)) / 8.; + float amplitude = 1. / (2. * underline.thickness); + float sine = sin(st.x * frequency) * amplitude; + float dSine = cos(st.x * frequency) * amplitude * frequency; + float distance = (st.y - sine) / sqrt(1. + dSine * dSine); + float distance_in_pixels = distance * underline.bounds.size.y; + float distance_from_top_border = distance_in_pixels - half_thickness; + float distance_from_bottom_border = distance_in_pixels + half_thickness; + float alpha = saturate( + 0.5 - max(-distance_from_bottom_border, distance_from_top_border)); + return input.color * float4(1., 1., 1., alpha); + } else { + return input.color; + } +} + +/* +** +** Monochrome sprites +** +*/ + +struct MonochromeSprite { + uint order; + uint pad; + Bounds bounds; + Bounds content_mask; + Hsla color; + AtlasTile tile; + TransformationMatrix transformation; +}; + +struct MonochromeSpriteVertexOutput { + float4 position: SV_Position; + float2 tile_position: POSITION; + float4 color: COLOR; + float4 clip_distance: SV_ClipDistance; +}; + +struct MonochromeSpriteFragmentInput { + float4 position: SV_Position; + float2 tile_position: POSITION; + float4 color: COLOR; +}; + +StructuredBuffer mono_sprites: register(t1); + +MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) { + float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + MonochromeSprite sprite = mono_sprites[sprite_id]; + float4 device_position = + to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation); + float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); + float2 tile_position = to_tile_position(unit_vertex, sprite.tile); + float4 color = hsla_to_rgba(sprite.color); + + MonochromeSpriteVertexOutput output; + output.position = device_position; + output.tile_position = tile_position; + output.color = color; + output.clip_distance = clip_distance; + return output; +} + +float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target { + float4 sample = t_sprite.Sample(s_sprite, input.tile_position); + float4 color = input.color; + color.a *= sample.a; + return color; +} + +/* +** +** Polychrome sprites +** +*/ + +struct PolychromeSprite { + uint order; + uint grayscale; + Bounds bounds; + Bounds content_mask; + Corners corner_radii; + AtlasTile tile; +}; + +struct PolychromeSpriteVertexOutput { + float4 position: SV_Position; + float2 tile_position: POSITION; + uint sprite_id: FLAT; + float4 clip_distance: SV_ClipDistance; +}; + +struct PolychromeSpriteFragmentInput { + float4 position: SV_Position; + float2 tile_position: POSITION; + uint sprite_id: FLAT; +}; + +StructuredBuffer poly_sprites: register(t1); + +PolychromeSpriteVertexOutput polychrome_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) { + float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u)); + PolychromeSprite sprite = poly_sprites[sprite_id]; + float4 device_position = to_device_position(unit_vertex, sprite.bounds); + float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, + sprite.content_mask); + float2 tile_position = to_tile_position(unit_vertex, sprite.tile); + + PolychromeSpriteVertexOutput output; + output.position = device_position; + output.tile_position = tile_position; + output.sprite_id = sprite_id; + output.clip_distance = clip_distance; + return output; +} + +float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Target { + PolychromeSprite sprite = poly_sprites[input.sprite_id]; + float4 sample = t_sprite.Sample(s_sprite, input.tile_position); + float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); + + float4 color = sample; + if ((sprite.grayscale & 0xFFu) != 0u) { + float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); + color = float4(grayscale, sample.a); + } + color.a *= 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 5703a82815eb0679ca3668a13c08f3e9affa3696..2801674a649896d33cb6b47736cd9f4117d80c80 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -49,7 +49,7 @@ pub struct WindowsWindowState { pub system_key_handled: bool, pub hovered: bool, - pub renderer: BladeRenderer, + pub renderer: DirectXRenderer, pub click_state: ClickState, pub system_settings: WindowsSystemSettings, @@ -84,7 +84,7 @@ impl WindowsWindowState { cs: &CREATESTRUCTW, current_cursor: Option, display: WindowsDisplay, - gpu_context: &BladeContext, + gpu_context: &DirectXDevices, min_size: Option>, appearance: WindowAppearance, ) -> Result { @@ -103,7 +103,8 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; - let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; + // let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; + let renderer = DirectXRenderer::new(gpu_context, hwnd, transparent)?; let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -343,7 +344,7 @@ struct WindowCreateContext<'a> { drop_target_helper: IDropTargetHelper, validation_number: usize, main_receiver: flume::Receiver, - gpu_context: &'a BladeContext, + gpu_context: &'a DirectXDevices, main_thread_id_win32: u32, appearance: WindowAppearance, } @@ -353,7 +354,7 @@ impl WindowsWindow { handle: AnyWindowHandle, params: WindowParams, creation_info: WindowCreationInfo, - gpu_context: &BladeContext, + gpu_context: &DirectXDevices, ) -> Result { let WindowCreationInfo { icon, @@ -485,7 +486,7 @@ impl rwh::HasDisplayHandle for WindowsWindow { impl Drop for WindowsWindow { fn drop(&mut self) { - self.0.state.borrow_mut().renderer.destroy(); + // 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,9 +707,10 @@ impl PlatformWindow for WindowsWindow { fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { let mut window_state = self.0.state.borrow_mut(); - window_state - .renderer - .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque); + // todo(zjk) + // window_state + // .renderer + // .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque); match background_appearance { WindowBackgroundAppearance::Opaque => { @@ -794,11 +796,11 @@ impl PlatformWindow for WindowsWindow { } fn draw(&self, scene: &Scene) { - self.0.state.borrow_mut().renderer.draw(scene) + self.0.state.borrow_mut().renderer.draw(scene).log_err(); } fn sprite_atlas(&self) -> Arc { - self.0.state.borrow().renderer.sprite_atlas().clone() + self.0.state.borrow().renderer.sprite_atlas() } fn get_raw_handle(&self) -> HWND { @@ -806,7 +808,9 @@ impl PlatformWindow for WindowsWindow { } fn gpu_specs(&self) -> Option { - Some(self.0.state.borrow().renderer.gpu_specs()) + // todo(zjk) + // Some(self.0.state.borrow().renderer.gpu_specs()) + None } fn update_ime_position(&self, _bounds: Bounds) {