From 68192052fd251876816a3c5d88719196db169ce6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 12:32:59 +0800 Subject: [PATCH 001/202] init --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7e3b43e58a..bf1eb9c325 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -663,8 +663,12 @@ features = [ "Win32_Globalization", "Win32_Graphics_Direct2D", "Win32_Graphics_Direct2D_Common", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Direct3D_Fxc", "Win32_Graphics_DirectWrite", "Win32_Graphics_Dwm", + "Win32_Graphics_Dxgi", "Win32_Graphics_Dxgi_Common", "Win32_Graphics_Gdi", "Win32_Graphics_Imaging", From b4771bc4f813a05865ca50f9f965ad960f1bfe03 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 12:49:05 +0800 Subject: [PATCH 002/202] wip --- crates/gpui/src/platform/windows.rs | 4 + .../src/platform/windows/directx_atlas.rs | 296 +++++ .../src/platform/windows/directx_renderer.rs | 1170 +++++++++++++++++ 3 files changed, 1470 insertions(+) create mode 100644 crates/gpui/src/platform/windows/directx_atlas.rs create mode 100644 crates/gpui/src/platform/windows/directx_renderer.rs diff --git a/crates/gpui/src/platform/windows.rs b/crates/gpui/src/platform/windows.rs index 4bdf42080d..5268d3ccba 100644 --- a/crates/gpui/src/platform/windows.rs +++ b/crates/gpui/src/platform/windows.rs @@ -1,6 +1,8 @@ mod clipboard; mod destination_list; mod direct_write; +mod directx_atlas; +mod directx_renderer; mod dispatcher; mod display; mod events; @@ -14,6 +16,8 @@ mod wrapper; pub(crate) use clipboard::*; pub(crate) use destination_list::*; pub(crate) use direct_write::*; +pub(crate) use directx_atlas::*; +pub(crate) use directx_renderer::*; pub(crate) use dispatcher::*; pub(crate) use display::*; pub(crate) use events::*; diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs new file mode 100644 index 0000000000..4bb903a463 --- /dev/null +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -0,0 +1,296 @@ +use collections::FxHashMap; +use etagere::BucketedAtlasAllocator; +use parking_lot::Mutex; +use windows::Win32::Graphics::{ + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, + ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Texture2D, + }, + Dxgi::Common::{ + DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R16_FLOAT, DXGI_SAMPLE_DESC, + }, +}; + +use crate::*; + +pub(crate) struct DirectXAtlas(Mutex); + +struct DirectXAtlasState { + device: ID3D11Device, + device_context: ID3D11DeviceContext, + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, + tiles_by_key: FxHashMap, +} + +struct DirectXAtlasTexture { + id: AtlasTextureId, + bytes_per_pixel: u32, + allocator: BucketedAtlasAllocator, + texture: ID3D11Texture2D, + rtv: [Option; 1], + view: [Option; 1], +} + +impl DirectXAtlas { + pub(crate) fn new(device: ID3D11Device, device_context: ID3D11DeviceContext) -> Self { + DirectXAtlas(Mutex::new(DirectXAtlasState { + device, + device_context, + monochrome_textures: Default::default(), + polychrome_textures: Default::default(), + path_textures: Default::default(), + tiles_by_key: Default::default(), + })) + } + + pub(crate) fn get_texture_drawing_info( + &self, + id: AtlasTextureId, + ) -> (Size, [Option; 1]) { + let lock = self.0.lock(); + let tex = lock.texture(id); + let size = tex.allocator.size(); + ( + Size { + width: size.width as f32, + height: size.height as f32, + }, + tex.rtv.clone(), + ) + } + + pub(crate) fn get_texture_view( + &self, + id: AtlasTextureId, + ) -> [Option; 1] { + let lock = self.0.lock(); + 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, + AtlasTextureKind::Path => &mut lock.path_textures, + }; + for texture in textures { + texture.clear(); + } + } +} + +impl PlatformAtlas for DirectXAtlas { + fn get_or_insert_with<'a>( + &self, + key: &AtlasKey, + build: &mut dyn FnMut() -> anyhow::Result< + Option<(Size, std::borrow::Cow<'a, [u8]>)>, + >, + ) -> anyhow::Result> { + let mut lock = self.0.lock(); + if let Some(tile) = lock.tiles_by_key.get(key) { + Ok(Some(tile.clone())) + } else { + let Some((size, bytes)) = build()? else { + return Ok(None); + }; + let tile = lock + .allocate(size, key.texture_kind()) + .ok_or_else(|| anyhow::anyhow!("failed to allocate"))?; + let texture = lock.texture(tile.texture_id); + texture.upload(&lock.device_context, tile.bounds, &bytes); + lock.tiles_by_key.insert(key.clone(), tile.clone()); + Ok(Some(tile)) + } + } + + fn remove(&self, key: &AtlasKey) { + todo!() + } +} + +impl DirectXAtlasState { + fn allocate( + &mut self, + size: Size, + texture_kind: AtlasTextureKind, + ) -> Option { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + + textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + .or_else(|| { + let texture = self.push_texture(size, texture_kind); + texture.allocate(size) + }) + } + + fn push_texture( + &mut self, + min_size: Size, + kind: AtlasTextureKind, + ) -> &mut DirectXAtlasTexture { + const DEFAULT_ATLAS_SIZE: Size = Size { + width: DevicePixels(1024), + height: DevicePixels(1024), + }; + // Max texture size for DirectX. See: + // https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits + const MAX_ATLAS_SIZE: Size = Size { + width: DevicePixels(16384), + height: DevicePixels(16384), + }; + let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE); + let pixel_format; + let bind_flag; + let bytes_per_pixel; + match kind { + AtlasTextureKind::Monochrome => { + pixel_format = DXGI_FORMAT_A8_UNORM; + bind_flag = D3D11_BIND_SHADER_RESOURCE; + bytes_per_pixel = 1; + } + AtlasTextureKind::Polychrome => { + 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; + } + } + let texture_desc = D3D11_TEXTURE2D_DESC { + Width: size.width.0 as u32, + Height: size.height.0 as u32, + MipLevels: 1, + ArraySize: 1, + Format: pixel_format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: bind_flag.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + let mut texture: Option = None; + unsafe { + self.device + .CreateTexture2D(&texture_desc, None, Some(&mut texture)) + .unwrap(); + } + let texture = texture.unwrap(); + + let textures = match kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_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] + }, + _ => [None], + }; + let view = unsafe { + let mut view = None; + self.device + .CreateShaderResourceView(&texture, None, Some(&mut view)) + .unwrap(); + [view] + }; + let atlas_texture = DirectXAtlasTexture { + id: AtlasTextureId { + index: textures.len() as u32, + kind, + }, + bytes_per_pixel, + allocator: etagere::BucketedAtlasAllocator::new(size.into()), + texture, + rtv, + view, + }; + textures.push(atlas_texture); + textures.last_mut().unwrap() + } + + fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + &textures[id.index as usize] + } +} + +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 { + texture_id: self.id, + tile_id: allocation.id.into(), + bounds: Bounds { + origin: allocation.rectangle.min.into(), + size, + }, + padding: 0, + }; + Some(tile) + } + + fn upload( + &self, + device_context: &ID3D11DeviceContext, + bounds: Bounds, + bytes: &[u8], + ) { + unsafe { + device_context.UpdateSubresource( + &self.texture, + 0, + Some(&D3D11_BOX { + left: bounds.left().0 as u32, + top: bounds.top().0 as u32, + front: 0, + right: bounds.right().0 as u32, + bottom: bounds.bottom().0 as u32, + back: 1, + }), + bytes.as_ptr() as _, + bounds.size.width.to_bytes(self.bytes_per_pixel as u8), + 0, + ); + } + } +} diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs new file mode 100644 index 0000000000..d135bd4f33 --- /dev/null +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -0,0 +1,1170 @@ +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::*; +use windows::{ + Win32::{ + Foundation::HWND, + Graphics::{ + Direct3D::*, + Direct3D11::*, + Dxgi::{Common::*, *}, + }, + }, + core::*, +}; + +use crate::*; + +pub(crate) struct DirectXRenderer { + atlas: Arc, + devices: DirectXDevices, + context: DirectXContext, + globals: DirectXGlobalElements, + pipelines: DirectXRenderPipelines, + hwnd: HWND, + transparent: bool, +} + +#[derive(Clone)] +pub(crate) struct DirectXDevices { + dxgi_factory: IDXGIFactory6, + dxgi_device: IDXGIDevice, + device: ID3D11Device, + device_context: ID3D11DeviceContext, +} + +struct DirectXContext { + swap_chain: IDXGISwapChain1, + back_buffer: [Option; 1], + viewport: [D3D11_VIEWPORT; 1], + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition: DirectComposition, +} + +struct DirectXRenderPipelines { + shadow_pipeline: PipelineState, + quad_pipeline: PipelineState, + path_raster_pipeline: PipelineState, + paths_pipeline: PipelineState, + 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, +} + +#[cfg(not(feature = "enable-renderdoc"))] +struct DirectComposition { + comp_device: IDCompositionDevice, + comp_target: IDCompositionTarget, + comp_visual: IDCompositionVisual, +} + +impl DirectXDevices { + pub(crate) fn new() -> Result { + let dxgi_factory = get_dxgi_factory()?; + let adapter = get_adapter(&dxgi_factory)?; + let (device, device_context) = { + let mut device: Option = None; + let mut context: Option = None; + get_device(&adapter, Some(&mut device), Some(&mut context))?; + (device.unwrap(), context.unwrap()) + }; + let dxgi_device: IDXGIDevice = device.cast()?; + + Ok(Self { + dxgi_factory, + dxgi_device, + device, + device_context, + }) + } +} + +impl DirectXRenderer { + 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 globals = DirectXGlobalElements::new(&devices.device)?; + let pipelines = DirectXRenderPipelines::new(&devices.device)?; + Ok(DirectXRenderer { + atlas, + devices, + context, + globals, + pipelines, + hwnd, + transparent, + }) + } + + pub(crate) fn spirite_atlas(&self) -> Arc { + self.atlas.clone() + } + + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { + return Err(anyhow::anyhow!( + "failed to rasterize {} paths", + scene.paths().len() + )); + }; + pre_draw( + &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, + )?; + for batch in scene.batches() { + match batch { + PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), + PrimitiveBatch::Quads(quads) => self.draw_quads(quads), + PrimitiveBatch::Paths(paths) => self.draw_paths(paths, &path_tiles), + PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines), + PrimitiveBatch::MonochromeSprites { + texture_id, + sprites, + } => self.draw_monochrome_sprites(texture_id, sprites), + PrimitiveBatch::PolychromeSprites { + texture_id, + sprites, + } => self.draw_polychrome_sprites(texture_id, sprites), + PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces), + }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", + scene.paths.len(), + scene.shadows.len(), + scene.quads.len(), + scene.underlines.len(), + scene.monochrome_sprites.len(), + scene.polychrome_sprites.len(), + scene.surfaces.len(),))?; + } + unsafe { self.context.swap_chain.Present(0, 0) }.ok()?; + Ok(()) + } + + 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()); + unsafe { + self.context.swap_chain.ResizeBuffers( + BUFFER_COUNT as u32, + new_size.width.0 as u32, + new_size.height.0 as u32, + DXGI_FORMAT_B8G8R8A8_UNORM, + 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(()) + } + } + + 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.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.devices.device_context, + &self.pipelines.shadow_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + shadows.len() as u32, + ) + } + + fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> { + if quads.is_empty() { + return Ok(()); + } + update_buffer_capacity( + &self.pipelines.quad_pipeline, + std::mem::size_of::(), + quads.len(), + &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.devices.device_context, + &self.pipelines.quad_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + quads.len() as u32, + ) + } + + fn rasterize_paths( + &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) + } + + fn draw_paths( + &mut self, + 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, + )?; + } + 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.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.devices.device_context, + &self.pipelines.underline_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + underlines.len() as u32, + ) + } + + fn draw_monochrome_sprites( + &mut self, + texture_id: AtlasTextureId, + sprites: &[MonochromeSprite], + ) -> Result<()> { + 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.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( + &self.devices.device_context, + &self.pipelines.mono_sprites, + &texture_view, + &self.context.viewport, + &self.globals.global_params_buffer, + &self.globals.sampler, + sprites.len() as u32, + ) + } + + fn draw_polychrome_sprites( + &mut self, + texture_id: AtlasTextureId, + sprites: &[PolychromeSprite], + ) -> Result<()> { + 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.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( + &self.devices.device_context, + &self.pipelines.poly_sprites, + &texture_view, + &self.context.viewport, + &self.globals.global_params_buffer, + &self.globals.sampler, + sprites.len() as u32, + ) + } + + fn draw_surfaces(&mut self, surfaces: &[Surface]) -> Result<()> { + if surfaces.is_empty() { + return Ok(()); + } + Ok(()) + } +} + +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")] + 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); + set_rasterizer_state(&devices.device, &devices.device_context)?; + + Ok(Self { + swap_chain, + back_buffer, + viewport, + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition, + }) + } +} + +impl DirectXRenderPipelines { + pub fn new(device: &ID3D11Device) -> Result { + let shadow_pipeline = create_pipieline( + device, + "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 path_raster_pipeline = create_pipieline( + device, + "path_rasterization_vertex", + "path_rasterization_fragment", + std::mem::size_of::>(), + 32, + )?; + let paths_pipeline = create_pipieline( + device, + "paths_vertex", + "paths_fragment", + std::mem::size_of::(), + 1, + )?; + let underline_pipeline = create_pipieline( + device, + "underline_vertex", + "underline_fragment", + std::mem::size_of::(), + 32, + )?; + let mono_sprites = create_pipieline( + device, + "monochrome_sprite_vertex", + "monochrome_sprite_fragment", + std::mem::size_of::(), + 32, + )?; + let poly_sprites = create_pipieline( + device, + "polychrome_sprite_vertex", + "polychrome_sprite_fragment", + std::mem::size_of::(), + 32, + )?; + + Ok(Self { + shadow_pipeline, + quad_pipeline, + path_raster_pipeline, + paths_pipeline, + underline_pipeline, + mono_sprites, + poly_sprites, + }) + } +} + +#[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 { + let global_params_buffer = unsafe { + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + ..Default::default() + }; + let mut buffer = None; + device.CreateBuffer(&desc, None, Some(&mut buffer))?; + [buffer] + }; + + let sampler = unsafe { + let desc = D3D11_SAMPLER_DESC { + Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR, + AddressU: D3D11_TEXTURE_ADDRESS_WRAP, + AddressV: D3D11_TEXTURE_ADDRESS_WRAP, + AddressW: D3D11_TEXTURE_ADDRESS_WRAP, + MipLODBias: 0.0, + MaxAnisotropy: 1, + ComparisonFunc: D3D11_COMPARISON_ALWAYS, + BorderColor: [0.0; 4], + MinLOD: 0.0, + MaxLOD: D3D11_FLOAT32_MAX, + }; + let mut output = None; + device.CreateSamplerState(&desc, Some(&mut output))?; + [output] + }; + + 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, + }) + } +} + +#[derive(Debug, Default)] +#[repr(C)] +struct GlobalParams { + viewport_size: [f32; 2], + _pad: u64, +} + +struct PipelineState { + vertex: ID3D11VertexShader, + fragment: ID3D11PixelShader, + buffer: ID3D11Buffer, + buffer_size: usize, + view: [Option; 1], +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(C)] +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} + +fn get_dxgi_factory() -> Result { + #[cfg(debug_assertions)] + let factory_flag = DXGI_CREATE_FACTORY_DEBUG; + #[cfg(not(debug_assertions))] + let factory_flag = 0u32; + unsafe { Ok(CreateDXGIFactory2(factory_flag)?) } +} + +fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result { + for adapter_index in 0.. { + let adapter: IDXGIAdapter1 = unsafe { + dxgi_factory + .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER) + }?; + { + let desc = unsafe { adapter.GetDesc1() }?; + println!( + "Select GPU: {}", + String::from_utf16_lossy(&desc.Description) + ); + } + // Check to see whether the adapter supports Direct3D 11, but don't + // create the actual device yet. + if get_device(&adapter, None, None).log_err().is_some() { + return Ok(adapter); + } + } + + unreachable!() +} + +fn get_device( + adapter: &IDXGIAdapter1, + device: Option<*mut Option>, + context: Option<*mut Option>, +) -> Result<()> { + #[cfg(debug_assertions)] + let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG; + #[cfg(not(debug_assertions))] + let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + Ok(unsafe { + D3D11CreateDevice( + adapter, + D3D_DRIVER_TYPE_UNKNOWN, + None, + device_flags, + Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]), + D3D11_SDK_VERSION, + device, + None, + context, + )? + }) +} + +#[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( + dxgi_factory: &IDXGIFactory6, + device: &ID3D11Device, + hwnd: HWND, + _transparent: bool, +) -> 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, + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: BUFFER_COUNT as u32, + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, + Flags: 0, + }; + let swap_chain = + unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; + unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; + Ok(swap_chain) +} + +fn set_render_target_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() + }; + unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; + Ok(back_buffer) +} + +fn set_viewport( + device_context: &ID3D11DeviceContext, + width: f32, + height: f32, +) -> [D3D11_VIEWPORT; 1] { + let viewport = [D3D11_VIEWPORT { + TopLeftX: 0.0, + TopLeftY: 0.0, + Width: width, + Height: height, + MinDepth: 0.0, + MaxDepth: 1.0, + }]; + unsafe { device_context.RSSetViewports(Some(&viewport)) }; + viewport +} + +fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> { + let desc = D3D11_RASTERIZER_DESC { + FillMode: D3D11_FILL_SOLID, + CullMode: D3D11_CULL_NONE, + FrontCounterClockwise: false.into(), + DepthBias: 0, + DepthBiasClamp: 0.0, + SlopeScaledDepthBias: 0.0, + DepthClipEnable: true.into(), + ScissorEnable: false.into(), + MultisampleEnable: false.into(), + AntialiasedLineEnable: false.into(), + }; + let rasterizer_state = unsafe { + let mut state = None; + device.CreateRasterizerState(&desc, Some(&mut state))?; + state.unwrap() + }; + unsafe { device_context.RSSetState(&rasterizer_state) }; + Ok(()) +} + +// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc +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. + 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_SRC_ALPHA; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + 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_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, + }) +} + +fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { + unsafe { + let mut shader = None; + device.CreateVertexShader(bytes, None, Some(&mut shader))?; + Ok(shader.unwrap()) + } +} + +fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { + unsafe { + let mut shader = None; + device.CreatePixelShader(bytes, None, Some(&mut shader))?; + Ok(shader.unwrap()) + } +} + +fn create_buffer( + device: &ID3D11Device, + element_size: usize, + buffer_size: usize, +) -> Result { + let desc = D3D11_BUFFER_DESC { + ByteWidth: (element_size * buffer_size) as u32, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32, + StructureByteStride: element_size as u32, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) +} + +fn create_buffer_view( + device: &ID3D11Device, + buffer: &ID3D11Buffer, +) -> Result<[Option; 1]> { + let mut view = None; + unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; + Ok([view]) +} + +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; +} + +fn update_buffer( + device_context: &ID3D11DeviceContext, + buffer: &ID3D11Buffer, + data: &[T], +) -> 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 draw_normal( + device_context: &ID3D11DeviceContext, + pipeline: &PipelineState, + viewport: &[D3D11_VIEWPORT], + 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.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)); + + 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; + +mod shader_resources { + use anyhow::Result; + use windows::Win32::Graphics::Direct3D::{ + Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + ID3DBlob, + }; + use windows_core::{HSTRING, PCSTR}; + + pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + unsafe { + let mut entry = entry.to_owned(); + let mut target = target.to_owned(); + let mut compile_blob = None; + let mut error_blob = None; + let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("src/platform/windows/shaders.hlsl") + .canonicalize() + .unwrap(); + entry.push_str("\0"); + target.push_str("\0"); + let entry_point = PCSTR::from_raw(entry.as_ptr()); + let target_cstr = PCSTR::from_raw(target.as_ptr()); + #[cfg(debug_assertions)] + let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + #[cfg(not(debug_assertions))] + let compile_flag = 0; + let ret = D3DCompileFromFile( + &HSTRING::from(shader_path.to_str().unwrap()), + None, + None, + entry_point, + target_cstr, + compile_flag, + 0, + &mut compile_blob, + Some(&mut error_blob), + ); + if ret.is_err() { + let Some(error_blob) = error_blob else { + return Err(anyhow::anyhow!("{ret:?}")); + }; + let string_len = error_blob.GetBufferSize(); + let error_string_encode = Vec::from_raw_parts( + error_blob.GetBufferPointer() as *mut u8, + string_len, + string_len, + ); + return Err(anyhow::anyhow!( + "Compile error: {}", + String::from_utf8_lossy(&error_string_encode) + )); + } + Ok(compile_blob.unwrap()) + } + } +} From 5d03296dc2a58b9a72289bae4ef8a6439a716da3 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 13:15:24 +0800 Subject: [PATCH 003/202] wip --- .../src/platform/windows/directx_atlas.rs | 33 +- .../src/platform/windows/directx_renderer.rs | 369 +++++----- 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, 904 insertions(+), 220 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 4bb903a463..03d33f5cbc 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 d135bd4f33..4a7f3796d7 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(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(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); + // 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 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); - } + // 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) + // 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() }?; +// #[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, - }) - } +// 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(()) - } -} +// 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 839fd10375..461aae5e25 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 f69a802da0..a14e9ecb54 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 0000000000..bb6342d6fb --- /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 5703a82815..2801674a64 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) { From f2c847a1b0ac2416f4ec08ef7683b80c74aedea1 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 13:28:52 +0800 Subject: [PATCH 004/202] apply #15782 --- crates/gpui/src/platform/windows/directx_renderer.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4a7f3796d7..6f2797d182 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -115,12 +115,12 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { - return Err(anyhow::anyhow!( - "failed to rasterize {} paths", - scene.paths().len() - )); - }; + // let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { + // return Err(anyhow::anyhow!( + // "failed to rasterize {} paths", + // scene.paths().len() + // )); + // }; pre_draw( &self.devices.device_context, &self.globals.global_params_buffer, From 83ea328be5a0cd3e800e147bf5d5cae93bd49102 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 16:34:16 +0800 Subject: [PATCH 005/202] apply #20812 --- crates/gpui/src/platform/windows/shaders.hlsl | 231 ++++++++++++++++-- 1 file changed, 207 insertions(+), 24 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index bb6342d6fb..0234f6bdc7 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -32,6 +32,30 @@ struct Hsla { float a; }; +struct LinearColorStop { + Hsla color; + float percentage; +}; + +struct Background { + // 0u is Solid + // 1u is LinearGradient + uint tag; + // 0u is sRGB linear color + // 1u is Oklab color + uint color_space; + Hsla solid; + float angle; + LinearColorStop colors[2]; + uint pad; +}; + +struct GradientColor { + float4 solid; + float4 color0; + float4 color1; +}; + struct AtlasTextureId { uint index; uint kind; @@ -71,6 +95,17 @@ float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bo clip_bounds.origin.y + clip_bounds.size.y - position.y); } +// Convert linear RGB to sRGB +float3 linear_to_srgb(float3 color) { + return pow(color, float3(2.2)); +} + +// Convert sRGB to linear RGB +float3 srgb_to_linear(float3 color) { + return pow(color, float3(1.0 / 2.2)); +} + +/// Hsla to linear RGBA conversion. 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; @@ -119,6 +154,48 @@ float4 hsla_to_rgba(Hsla hsla) { return rgba; } +// Converts a sRGB color to the Oklab color space. +// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab +float4 srgb_to_oklab(float4 color) { + // Convert non-linear sRGB to linear sRGB + color = float4(srgb_to_linear(color.rgb), color.a); + + float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b; + float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b; + float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b; + + float l_ = pow(l, 1.0/3.0); + float m_ = pow(m, 1.0/3.0); + float s_ = pow(s, 1.0/3.0); + + return float4( + 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, + 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, + 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, + color.a + ); +} + +// Converts an Oklab color to the sRGB color space. +float4 oklab_to_srgb(float4 color) { + float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b; + float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b; + float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b; + + float l = l_ * l_ * l_; + float m = m_ * m_ * m_; + float s = s_ * s_ * s_; + + float3 linear_rgb = float3( + 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, + -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, + -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s + ); + + // Convert linear sRGB to non-linear sRGB + return float4(linear_to_srgb(linear_rgb), color.a); +} + // This approximates the error function, needed for the gaussian integral float2 erf(float2 x) { float2 s = sign(x); @@ -190,6 +267,83 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { return distance; } +GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { + GradientColor res; + if (tag == 0) { + res.solid = hsla_to_rgba(solid); + } else if (tag == 1) { + res.color0 = hsla_to_rgba(color0); + res.color1 = hsla_to_rgba(color1); + + // Prepare color space in vertex for avoid conversion + // in fragment shader for performance reasons + if (color_space == 1) { + // Oklab + res.color0 = srgb_to_oklab(res.color0); + res.color1 = srgb_to_oklab(res.color1); + } + } + + return res; +} + +float4 gradient_color(Background background, + float2 position, + Bounds bounds, + float4 solid_color, float4 color0, float4 color1) { + float4 color; + + switch (background.tag) { + case 0: + color = solid_color; + break; + case 1: { + // -90 degrees to match the CSS gradient angle. + float radians = (fmod(background.angle, 360.0) - 90.0) * (M_PI_F / 180.0); + float2 direction = float2(cos(radians), sin(radians)); + + // Expand the short side to be the same as the long side + if (bounds.size.x > bounds.size.y) { + direction.y *= bounds.size.y / bounds.size.x; + } else { + direction.x *= bounds.size.x / bounds.size.y; + } + + // 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 center_to_point = position - center; + float t = dot(center_to_point, direction) / length(direction); + // Check the direct to determine the use x or y + if (abs(direction.x) > abs(direction.y)) { + t = (t + half_size.x) / bounds.size.x; + } else { + t = (t + half_size.y) / bounds.size.y; + } + + // Adjust t based on the stop percentages + t = (t - background.colors[0].percentage) + / (background.colors[1].percentage + - background.colors[0].percentage); + t = clamp(t, 0.0, 1.0); + + switch (background.color_space) { + case 0: + color = lerp(color0, color1, t); + break; + case 1: { + float4 oklab_color = lerp(color0, color1, t); + color = oklab_to_srgb(oklab_color); + break; + } + } + break; + } + } + + return color; +} + /* ** ** Shadows @@ -294,7 +448,7 @@ struct Quad { uint pad; Bounds bounds; Bounds content_mask; - Hsla background; + Background background; Hsla border_color; Corners corner_radii; Edges border_widths; @@ -302,17 +456,22 @@ struct Quad { struct QuadVertexOutput { float4 position: SV_Position; - float4 background_color: COLOR0; - float4 border_color: COLOR1; + // float4 border_color: COLOR0; + float4 border_color: FLAT; uint quad_id: FLAT; + float4 background_solid: FLAT; + float4 background_color0: FLAT; + float4 background_color1: FLAT; float4 clip_distance: SV_ClipDistance; }; struct QuadFragmentInput { - float4 position: SV_Position; - float4 background_color: COLOR0; - float4 border_color: COLOR1; uint quad_id: FLAT; + float4 position: SV_Position; + float4 border_color: FLAT; + float4 background_solid: FLAT; + float4 background_color0: FLAT; + float4 background_color1: FLAT; }; StructuredBuffer quads: register(t1); @@ -322,20 +481,34 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta 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); + 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 + ); + QuadVertexOutput output; output.position = device_position; - output.background_color = background_color; output.border_color = border_color; output.quad_id = quad_id; + output.background_solid = gradient.solid; + output.background_color0 = gradient.color0; + output.background_color1 = gradient.color1; output.clip_distance = clip_distance; return output; } 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, + 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. && @@ -343,12 +516,9 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { 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; + return 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.) { @@ -385,16 +555,12 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { border_width = vertical_border; } - float4 color; - if (border_width == 0.) { - color = input.background_color; - } else { + if (border_width != 0.) { 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)); + float4 blended_border = over(color, input.border_color); + color = lerp(blended_border, color, saturate(0.5 - inset_distance)); } return color * float4(1., 1., 1., saturate(0.5 - distance)); @@ -457,14 +623,17 @@ float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { struct PathSprite { Bounds bounds; - Hsla color; + Background color; AtlasTile tile; }; struct PathVertexOutput { float4 position: SV_Position; float2 tile_position: POSITION1; - float4 color: COLOR; + uint sprite_id: FLAT; + float4 solid_color: FLAT; + float4 color0: FLAT; + float4 color1: FLAT; }; StructuredBuffer path_sprites: register(t1); @@ -473,18 +642,32 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ 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); + output.sprite_id = instance_id; + + 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 + ); + + output.solid_color = gradient.solid; + output.color0 = gradient.color0; + output.color1 = gradient.color1; 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; + PathSprite sprite = path_sprites[input.sprite_id]; + Background background = sprite.color; + float4 color = gradient_color(background, input.position.xy, sprite.bounds, + input.solid_color, input.color0, input.color1); color.a *= mask; return color; } From eec406bb36c8d38c984fcc301f6b87670231dfca Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 17:13:53 +0800 Subject: [PATCH 006/202] wip --- .../src/platform/windows/directx_atlas.rs | 4 +- .../src/platform/windows/directx_renderer.rs | 238 ++++++------------ 2 files changed, 73 insertions(+), 169 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 03d33f5cbc..3a5da33ae6 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -21,7 +21,7 @@ struct DirectXAtlasState { device_context: ID3D11DeviceContext, monochrome_textures: Vec, polychrome_textures: Vec, - path_textures: Vec, + // path_textures: Vec, tiles_by_key: FxHashMap, } @@ -41,7 +41,7 @@ impl DirectXAtlas { device_context, monochrome_textures: Default::default(), polychrome_textures: Default::default(), - path_textures: Default::default(), + // path_textures: Default::default(), tiles_by_key: Default::default(), })) } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 6f2797d182..c8f3bcbda7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -48,7 +48,6 @@ struct DirectXContext { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - path_raster_pipeline: PipelineState, paths_pipeline: PipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, @@ -115,12 +114,6 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - // let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { - // return Err(anyhow::anyhow!( - // "failed to rasterize {} paths", - // scene.paths().len() - // )); - // }; pre_draw( &self.devices.device_context, &self.globals.global_params_buffer, @@ -133,7 +126,7 @@ impl DirectXRenderer { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), PrimitiveBatch::Quads(quads) => self.draw_quads(quads), - PrimitiveBatch::Paths(paths) => self.draw_paths(paths, &path_tiles), + PrimitiveBatch::Paths(paths) => self.draw_paths(paths), PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines), PrimitiveBatch::MonochromeSprites { texture_id, @@ -276,126 +269,46 @@ impl DirectXRenderer { ) } - fn rasterize_paths( - &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) - None - } - - fn draw_paths( - &mut self, - 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, - // )?; - // } + fn draw_paths(&mut self, paths: &[Path]) -> Result<()> { + if paths.is_empty() { + return Ok(()); + } + let mut vertices = Vec::new(); + let mut sprites = Vec::with_capacity(paths.len()); + 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(()) } @@ -543,13 +456,6 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - let path_raster_pipeline = create_pipieline( - device, - "path_rasterization_vertex", - "path_rasterization_fragment", - std::mem::size_of::>(), - 32, - )?; let paths_pipeline = create_pipieline( device, "paths_vertex", @@ -582,7 +488,6 @@ impl DirectXRenderPipelines { Ok(Self { shadow_pipeline, quad_pipeline, - path_raster_pipeline, paths_pipeline, underline_pipeline, mono_sprites, @@ -679,8 +584,7 @@ struct PipelineState { #[repr(C)] struct PathSprite { bounds: Bounds, - color: Hsla, - tile: AtlasTile, + color: Background, } fn get_dxgi_factory() -> Result { @@ -743,35 +647,35 @@ fn get_device( // 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)? }) -} +// 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( From af2009710a6667bfe1a0e1f6a44521bc76c54228 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 20:17:01 +0800 Subject: [PATCH 007/202] fix all --- .../src/platform/windows/directx_renderer.rs | 174 ++++++++++++++---- crates/gpui/src/platform/windows/shaders.hlsl | 127 +++++-------- 2 files changed, 186 insertions(+), 115 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c8f3bcbda7..e4abdd6c3d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -49,6 +49,7 @@ 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, @@ -61,6 +62,14 @@ struct DirectXGlobalElements { blend_state_for_pr: ID3D11BlendState, } +#[repr(C)] +struct DrawInstancedIndirectArgs { + vertex_count_per_instance: u32, + instance_count: u32, + start_vertex_location: u32, + start_instance_location: u32, +} + // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -122,6 +131,7 @@ impl DirectXRenderer { [0.0, 0.0, 0.0, 0.0], &self.globals.blend_state, )?; + println!("--> Drawing scene"); for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -273,41 +283,64 @@ 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()); - 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), + let mut draw_indirect_commands = Vec::with_capacity(paths.len()); + let mut start_vertex_location = 0; + for (i, path) in paths.iter().enumerate() { + draw_indirect_commands.push(DrawInstancedIndirectArgs { + vertex_count_per_instance: path.vertices.len() as u32, + instance_count: 1, + start_vertex_location, + start_instance_location: i as u32, + }); + start_vertex_location += path.vertices.len() as u32; + + vertices.extend(path.vertices.iter().map(|v| PathVertex { + xy_position: v.xy_position, + content_mask: ContentMask { + bounds: path.content_mask.bounds, }, + })); + + sprites.push(PathSprite { + bounds: path.bounds, 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( + }); + } + + update_buffer_capacity( + &self.pipelines.paths_pipeline, + std::mem::size_of::(), + sprites.len(), + &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, + &draw_indirect_commands, + )?; + prepare_indirect_draws( + &self.devices.device_context, + &self.pipelines.paths_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + )?; + + for i in 0..paths.len() { + draw_indirect( &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, - )?; + &self.pipelines.paths_indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, + ); } Ok(()) } @@ -484,11 +517,13 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; + 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, @@ -884,6 +919,27 @@ fn create_buffer_view( Ok([view]) } +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> 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() + // }; + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32 * buffer_size, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_INDEX_BUFFER.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, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) +} + fn update_global_params( device_context: &ID3D11DeviceContext, buffer: &[Option; 1], @@ -964,6 +1020,50 @@ 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( + device_context: &ID3D11DeviceContext, + pipeline: &PipelineState, + viewport: &[D3D11_VIEWPORT], + global_params: &[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, @@ -1026,6 +1126,7 @@ 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(); @@ -1039,6 +1140,11 @@ 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))] @@ -1054,6 +1160,7 @@ 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:?}")); @@ -1064,10 +1171,9 @@ mod shader_resources { string_len, string_len, ); - return Err(anyhow::anyhow!( - "Compile error: {}", - String::from_utf8_lossy(&error_string_encode) - )); + let error_string = String::from_utf8_lossy(&error_string_encode); + println!("Shader compile error: {}", error_string); + return Err(anyhow::anyhow!("Compile error: {}", error_string)); } Ok(compile_blob.unwrap()) } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 0234f6bdc7..fbb5414321 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -81,28 +81,36 @@ struct TransformationMatrix { 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; +float4 to_device_position_impl(float2 position) { 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) { +float4 to_device_position(float2 unit_vertex, Bounds bounds) { float2 position = unit_vertex * bounds.size + bounds.origin; + return to_device_position_impl(position); +} + +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); } +float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { + float2 position = unit_vertex * bounds.size + bounds.origin; + return distance_from_clip_rect_impl(position, clip_bounds); +} + // Convert linear RGB to sRGB float3 linear_to_srgb(float3 color) { - return pow(color, float3(2.2)); + return pow(color, float3(2.2, 2.2, 2.2)); } // Convert sRGB to linear RGB float3 srgb_to_linear(float3 color) { - return pow(color, float3(1.0 / 2.2)); + return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2)); } /// Hsla to linear RGBA conversion. @@ -268,23 +276,23 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { } GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { - GradientColor res; + GradientColor output; if (tag == 0) { - res.solid = hsla_to_rgba(solid); + output.solid = hsla_to_rgba(solid); } else if (tag == 1) { - res.color0 = hsla_to_rgba(color0); - res.color1 = hsla_to_rgba(color1); + output.color0 = hsla_to_rgba(color0); + output.color1 = hsla_to_rgba(color1); // Prepare color space in vertex for avoid conversion // in fragment shader for performance reasons if (color_space == 1) { // Oklab - res.color0 = srgb_to_oklab(res.color0); - res.color1 = srgb_to_oklab(res.color1); + output.color0 = srgb_to_oklab(output.color0); + output.color1 = srgb_to_oklab(output.color1); } } - return res; + return output; } float4 gradient_color(Background background, @@ -456,22 +464,21 @@ struct Quad { struct QuadVertexOutput { float4 position: SV_Position; - // float4 border_color: COLOR0; - float4 border_color: FLAT; - uint quad_id: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + 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; float4 clip_distance: SV_ClipDistance; }; struct QuadFragmentInput { - uint quad_id: FLAT; + nointerpolation uint quad_id: TEXCOORD0; float4 position: SV_Position; - float4 border_color: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + nointerpolation float4 border_color: COLOR0; + nointerpolation float4 background_solid: COLOR1; + nointerpolation float4 background_color0: COLOR2; + nointerpolation float4 background_color1: COLOR3; }; StructuredBuffer quads: register(t1); @@ -566,55 +573,11 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { 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 @@ -624,27 +587,27 @@ float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { struct PathSprite { Bounds bounds; Background color; - AtlasTile tile; }; struct PathVertexOutput { float4 position: SV_Position; - float2 tile_position: POSITION1; - uint sprite_id: FLAT; - float4 solid_color: FLAT; - float4 color0: FLAT; - float4 color1: FLAT; + float4 clip_distance: SV_ClipDistance; + nointerpolation uint sprite_id: TEXCOORD0; + nointerpolation float4 solid_color: COLOR0; + nointerpolation float4 color0: COLOR1; + nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_sprites: register(t1); +StructuredBuffer path_vertices: register(t1); +StructuredBuffer path_sprites: register(t2); 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)); + PathVertex v = path_vertices[vertex_id]; 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.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; GradientColor gradient = prepare_gradient_color( @@ -662,13 +625,15 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ } 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 zero = 0.0; + if (any(input.clip_distance < zero)) { + return zero; + } + PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, input.solid_color, input.color0, input.color1); - color.a *= mask; return color; } From d0a2257472ee01da498f084f3d9e0ecf39f5de5b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 20:31:39 +0800 Subject: [PATCH 008/202] fix --- crates/gpui/src/platform/windows/directx_renderer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index e4abdd6c3d..36c68c8eb4 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -131,7 +131,6 @@ impl DirectXRenderer { [0.0, 0.0, 0.0, 0.0], &self.globals.blend_state, )?; - println!("--> Drawing scene"); for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -332,7 +331,7 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, )?; for i in 0..paths.len() { @@ -494,7 +493,7 @@ impl DirectXRenderPipelines { "paths_vertex", "paths_fragment", std::mem::size_of::(), - 1, + 32, )?; let underline_pipeline = create_pipieline( device, From 2319cd8211edfb4a1edda4ffad9ce0e379db225b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 14:24:47 +0800 Subject: [PATCH 009/202] apply #19772 --- .../src/platform/windows/directx_atlas.rs | 140 +++++++++--------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 3a5da33ae6..abd6e4ad40 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -3,25 +3,25 @@ use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use windows::Win32::Graphics::{ Direct3D11::{ - D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, - D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, - ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Texture2D, - }, - Dxgi::Common::{ - DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R16_FLOAT, DXGI_SAMPLE_DESC, + D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, D3D11_TEXTURE2D_DESC, + D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView, + ID3D11Texture2D, }, + Dxgi::Common::{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}, }; -use crate::*; +use crate::{ + AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, + Size, platform::AtlasTextureList, +}; pub(crate) struct DirectXAtlas(Mutex); struct DirectXAtlasState { device: ID3D11Device, device_context: ID3D11DeviceContext, - monochrome_textures: Vec, - polychrome_textures: Vec, - // path_textures: Vec, + monochrome_textures: AtlasTextureList, + polychrome_textures: AtlasTextureList, tiles_by_key: FxHashMap, } @@ -30,8 +30,8 @@ struct DirectXAtlasTexture { bytes_per_pixel: u32, allocator: BucketedAtlasAllocator, texture: ID3D11Texture2D, - rtv: [Option; 1], view: [Option; 1], + live_atlas_keys: u32, } impl DirectXAtlas { @@ -41,27 +41,10 @@ impl DirectXAtlas { device_context, monochrome_textures: Default::default(), polychrome_textures: Default::default(), - // path_textures: Default::default(), tiles_by_key: Default::default(), })) } - pub(crate) fn get_texture_drawing_info( - &self, - id: AtlasTextureId, - ) -> (Size, [Option; 1]) { - let lock = self.0.lock(); - let tex = lock.texture(id); - let size = tex.allocator.size(); - ( - Size { - width: size.width as f32, - height: size.height as f32, - }, - tex.rtv.clone(), - ) - } - pub(crate) fn get_texture_view( &self, id: AtlasTextureId, @@ -84,9 +67,8 @@ 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, }; - for texture in textures { + for texture in textures.iter_mut() { texture.clear(); } } @@ -118,7 +100,30 @@ impl PlatformAtlas for DirectXAtlas { } fn remove(&self, key: &AtlasKey) { - todo!() + let mut lock = self.0.lock(); + + let Some(id) = lock.tiles_by_key.remove(key).map(|tile| tile.texture_id) else { + return; + }; + + let textures = match id.kind { + AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, + AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, + }; + + let Some(texture_slot) = textures.textures.get_mut(id.index as usize) else { + return; + }; + + if let Some(mut texture) = texture_slot.take() { + texture.decrement_ref_count(); + if texture.is_unreferenced() { + textures.free_list.push(texture.id.index as usize); + lock.tiles_by_key.remove(key); + } else { + *texture_slot = Some(texture); + } + } } } @@ -128,20 +133,23 @@ impl DirectXAtlasState { size: Size, texture_kind: AtlasTextureKind, ) -> Option { - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - // AtlasTextureKind::Path => &mut self.path_textures, - }; + { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + }; - textures - .iter_mut() - .rev() - .find_map(|texture| texture.allocate(size)) - .or_else(|| { - let texture = self.push_texture(size, texture_kind); - texture.allocate(size) - }) + if let Some(tile) = textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + { + return Some(tile); + } + } + + let texture = self.push_texture(size, texture_kind); + texture.allocate(size) } fn push_texture( @@ -173,11 +181,7 @@ 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; - // } + } } let texture_desc = D3D11_TEXTURE2D_DESC { Width: size.width.0 as u32, @@ -202,21 +206,11 @@ impl DirectXAtlasState { } let texture = texture.unwrap(); - let textures = match kind { + let texture_list = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Polychrome => &mut self.polychrome_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] - // }, - _ => [None], }; + let index = texture_list.free_list.pop(); let view = unsafe { let mut view = None; self.device @@ -226,17 +220,22 @@ impl DirectXAtlasState { }; let atlas_texture = DirectXAtlasTexture { id: AtlasTextureId { - index: textures.len() as u32, + index: index.unwrap_or(texture_list.textures.len()) as u32, kind, }, bytes_per_pixel, allocator: etagere::BucketedAtlasAllocator::new(size.into()), texture, - rtv, view, + live_atlas_keys: 0, }; - textures.push(atlas_texture); - textures.last_mut().unwrap() + if let Some(ix) = index { + texture_list.textures[ix] = Some(atlas_texture); + texture_list.textures.get_mut(ix).unwrap().as_mut().unwrap() + } else { + texture_list.textures.push(Some(atlas_texture)); + texture_list.textures.last_mut().unwrap().as_mut().unwrap() + } } fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { @@ -245,7 +244,7 @@ impl DirectXAtlasState { crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, // crate::AtlasTextureKind::Path => &self.path_textures, }; - &textures[id.index as usize] + textures[id.index as usize].as_ref().unwrap() } } @@ -265,6 +264,7 @@ impl DirectXAtlasTexture { }, padding: 0, }; + self.live_atlas_keys += 1; Some(tile) } @@ -292,4 +292,12 @@ impl DirectXAtlasTexture { ); } } + + fn decrement_ref_count(&mut self) { + self.live_atlas_keys -= 1; + } + + fn is_unreferenced(&mut self) -> bool { + self.live_atlas_keys == 0 + } } From 52c181328cf1fbf265621c1907b6fd996d9ea76a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 14:50:21 +0800 Subject: [PATCH 010/202] apply #23576 --- crates/gpui/src/platform/windows/shaders.hlsl | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index fbb5414321..cf2fd4ca01 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -40,12 +40,13 @@ struct LinearColorStop { struct Background { // 0u is Solid // 1u is LinearGradient + // 2u is PatternSlash uint tag; // 0u is sRGB linear color // 1u is Oklab color uint color_space; Hsla solid; - float angle; + float gradient_angle_or_pattern_height; LinearColorStop colors[2]; uint pad; }; @@ -295,6 +296,12 @@ GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsl return output; } +float2x2 rotate2d(float angle) { + float s = sin(angle); + float c = cos(angle); + return float2x2(c, -s, s, c); +} + float4 gradient_color(Background background, float2 position, Bounds bounds, @@ -307,7 +314,8 @@ float4 gradient_color(Background background, break; case 1: { // -90 degrees to match the CSS gradient angle. - float radians = (fmod(background.angle, 360.0) - 90.0) * (M_PI_F / 180.0); + float gradient_angle = background.gradient_angle_or_pattern_height; + float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0); float2 direction = float2(cos(radians), sin(radians)); // Expand the short side to be the same as the long side @@ -347,6 +355,22 @@ float4 gradient_color(Background background, } break; } + case 2: { + float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height; + float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f; + float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f; + float pattern_height = pattern_width + pattern_interval; + float stripe_angle = M_PI_F / 4.0; + float pattern_period = pattern_height * sin(stripe_angle); + float2x2 rotation = rotate2d(stripe_angle); + float2 relative_position = position - bounds.origin; + float2 rotated_point = mul(rotation, relative_position); + float pattern = fmod(rotated_point.x, pattern_period); + float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) / 2.0f; + color = solid_color; + color.a *= saturate(0.5 - distance); + break; + } } return color; From dcdd7404e46d97f1a4e7ed7b74c7caa018227772 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 16:47:45 +0800 Subject: [PATCH 011/202] wip --- .../src/platform/windows/directx_renderer.rs | 195 ++++++++++++++++-- 1 file changed, 175 insertions(+), 20 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 36c68c8eb4..ed2ec0a8c9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -48,8 +48,7 @@ struct DirectXContext { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - paths_pipeline: PipelineState, - paths_indirect_draw_buffer: ID3D11Buffer, + paths_pipeline: PathsPipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, poly_sprites: PipelineState, @@ -309,21 +308,37 @@ impl DirectXRenderer { }); } - update_buffer_capacity( + update_paths_buffer_capacity( &self.pipelines.paths_pipeline, - std::mem::size_of::(), sprites.len(), &self.devices.device, ) - .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input)); + .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, &self.pipelines.paths_pipeline.buffer, &sprites, )?; - update_indirect_buffer( + update_paths_vertex_capacity( + &mut self.pipelines.paths_pipeline, + vertices.len(), + &self.devices.device, + ) + .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); + update_buffer( &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, + &self.pipelines.paths_pipeline.vertex_buffer, + &vertices, + )?; + update_indirect_buffer_capacity( + &self.pipelines.paths_pipeline, + draw_indirect_commands.len(), + &self.devices.device, + ) + .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); + update_buffer( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, &draw_indirect_commands, )?; prepare_indirect_draws( @@ -337,7 +352,7 @@ impl DirectXRenderer { for i in 0..paths.len() { draw_indirect( &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, + &self.pipelines.paths_pipeline.indirect_draw_buffer, (i * std::mem::size_of::()) as u32, ); } @@ -488,13 +503,14 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - let paths_pipeline = create_pipieline( - device, - "paths_vertex", - "paths_fragment", - std::mem::size_of::(), - 32, - )?; + // let paths_pipeline = create_pipieline( + // device, + // "paths_vertex", + // "paths_fragment", + // std::mem::size_of::(), + // 32, + // )?; + let paths_pipeline = PathsPipelineState::new(device)?; let underline_pipeline = create_pipieline( device, "underline_vertex", @@ -516,13 +532,11 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - 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, @@ -614,6 +628,62 @@ struct PipelineState { view: [Option; 1], } +struct PathsPipelineState { + vertex: ID3D11VertexShader, + fragment: ID3D11PixelShader, + buffer: ID3D11Buffer, + buffer_size: usize, + vertex_buffer: ID3D11Buffer, + vertex_buffer_size: usize, + indirect_draw_buffer: ID3D11Buffer, + indirect_buffer_size: usize, + view: [Option; 1], + vertex_view: [Option; 1], +} + +impl PathsPipelineState { + fn new(device: &ID3D11Device) -> Result { + let vertex = { + 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)? + }; + 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 = + create_buffer(device, std::mem::size_of::>(), 32)?; + let vertex_view = create_buffer_view(device, &vertex_buffer)?; + let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; + Ok(Self { + vertex, + fragment, + buffer, + buffer_size: 32, + vertex_buffer, + vertex_buffer_size: 32, + indirect_draw_buffer, + indirect_buffer_size: 32, + view, + vertex_view, + }) + } +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -996,6 +1066,67 @@ fn update_buffer_capacity( Some((buffer, buffer_size, view)) } +fn update_paths_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.buffer_size >= data_size { + return None; + } + println!( + "Paths buffer too small: {} < {}", + pipeline.buffer_size, data_size + ); + let buffer_size = data_size.next_power_of_two(); + println!("Paths New size: {}", buffer_size); + let buffer = create_buffer(device, std::mem::align_of::(), buffer_size).unwrap(); + let view = create_buffer_view(device, &buffer).unwrap(); + Some((buffer, buffer_size, view)) +} + +fn update_paths_vertex_capacity( + pipeline: &PathsPipelineState, + vertex_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.vertex_buffer_size >= vertex_size { + return None; + } + println!( + "Paths vertex buffer too small: {} < {}", + pipeline.vertex_buffer_size, vertex_size + ); + let vertex_size = vertex_size.next_power_of_two(); + println!("Paths vertex New size: {}", vertex_size); + let buffer = create_buffer( + device, + std::mem::size_of::>(), + vertex_size, + ) + .unwrap(); + let view = create_buffer_view(device, &buffer).unwrap(); + Some((buffer, vertex_size, view)) +} + +fn update_indirect_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize)> { + if pipeline.indirect_buffer_size >= data_size { + return None; + } + println!( + "Indirect buffer too small: {} < {}", + pipeline.indirect_buffer_size, data_size + ); + let buffer_size = data_size.next_power_of_two(); + println!("Indirect New size: {}", buffer_size); + let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + Some((buffer, buffer_size)) +} + fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1005,6 +1136,29 @@ fn update_pipeline( pipeline.view = input.2; } +fn update_paths_pipeline_buffer( + pipeline: &mut PathsPipelineState, + input: (ID3D11Buffer, usize, [Option; 1]), +) { + pipeline.buffer = input.0; + pipeline.buffer_size = input.1; + pipeline.view = input.2; +} + +fn update_paths_pipeline_vertex( + pipeline: &mut PathsPipelineState, + input: (ID3D11Buffer, usize, [Option; 1]), +) { + pipeline.vertex_buffer = input.0; + pipeline.vertex_buffer_size = input.1; + pipeline.vertex_view = input.2; +} + +fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { + pipeline.indirect_draw_buffer = input.0; + pipeline.indirect_buffer_size = input.1; +} + fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1035,14 +1189,15 @@ fn update_indirect_buffer( fn prepare_indirect_draws( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PathsPipelineState, viewport: &[D3D11_VIEWPORT], global_params: &[Option], topology: D3D_PRIMITIVE_TOPOLOGY, ) -> Result<()> { unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetShaderResources(1, Some(&pipeline.view)); + device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); + device_context.VSSetShaderResources(2, Some(&pipeline.view)); + device_context.PSSetShaderResources(2, Some(&pipeline.view)); device_context.IASetPrimitiveTopology(topology); device_context.RSSetViewports(Some(viewport)); device_context.VSSetShader(&pipeline.vertex, None); From 622a42e3aaee406560922f2239d56b1d1074485a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 17:49:44 +0800 Subject: [PATCH 012/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 443 +++++++++++++++--- 1 file changed, 368 insertions(+), 75 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index cf2fd4ca01..f7e371b1b7 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -240,6 +240,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,32 +265,32 @@ 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) { @@ -376,6 +393,57 @@ float4 gradient_color(Background background, return color; } +// 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 { + return min(dv1, dv2); + } +} + +// 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); +} + +// 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; +} + /* ** ** Shadows @@ -477,7 +545,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct Quad { uint order; - uint pad; + uint border_style; Bounds bounds; Bounds content_mask; Background background; @@ -535,66 +603,286 @@ 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.bottom_left == 0.0 && + quad.corner_radii.top_right == 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; - } + 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; + } + + // 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 { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_right; - } else { - corner_radius = quad.corner_radii.bottom_right; + 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); + } + + // 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); + } + } } - } - 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; - } - - if (border_width != 0.) { - 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(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 { @@ -795,7 +1083,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; @@ -805,14 +1095,14 @@ struct PolychromeSprite { struct PolychromeSpriteVertexOutput { float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; + nointerpolation uint sprite_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct PolychromeSpriteFragmentInput { float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; + nointerpolation uint sprite_id: TEXCOORD0; }; StructuredBuffer poly_sprites: register(t1); @@ -839,10 +1129,13 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); float4 color = sample; + if (sprite.grayscale) { + + } if ((sprite.grayscale & 0xFFu) != 0u) { 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; } From c82edc38a9dccc924e655e1e5358504ee91d48ca Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 17:55:50 +0800 Subject: [PATCH 013/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f7e371b1b7..fcd13ddb31 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1130,12 +1130,13 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float4 color = sample; if (sprite.grayscale) { - - } - if ((sprite.grayscale & 0xFFu) != 0u) { float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } + // if ((sprite.grayscale & 0xFFu) != 0u) { + // float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); + // color = float4(grayscale, sample.a); + // } color.a *= sprite.opacity * saturate(0.5 - distance); return color; } From 6a918b64bf54c72ae3fd1e852a5a6298dc28de63 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 18:35:52 +0800 Subject: [PATCH 014/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 228 ++++++++---------- 1 file changed, 105 insertions(+), 123 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index fcd13ddb31..f93ef426dd 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) { @@ -293,20 +292,20 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { 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); } } @@ -444,99 +443,6 @@ float quarter_ellipse_sdf(float2 pt, float2 radii) { return unit_circle_sdf * (radii.x + radii.y) * -0.5; } -/* -** -** 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 @@ -579,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; @@ -885,17 +790,97 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { return color * float4(1.0, 1.0, 1.0, saturate(antialias_threshold - outer_sdf)); } -struct PathVertex { - float2 xy_position; - Bounds content_mask; +/* +** +** Shadows +** +*/ + +struct ShadowVertexOutput { + float4 position: SV_Position; + nointerpolation float4 color: COLOR; + nointerpolation uint shadow_id: TEXCOORD0; + float4 clip_distance: SV_ClipDistance; }; +struct ShadowFragmentInput { + float4 position: SV_Position; + float4 color: COLOR; + nointerpolation uint shadow_id: TEXCOORD0; +}; + +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 = 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; + Bounds content_mask; +}; + struct PathSprite { Bounds bounds; Background color; @@ -926,8 +911,7 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ 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; @@ -967,15 +951,15 @@ struct Underline { struct UnderlineVertexOutput { float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; + nointerpolation uint underline_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct UnderlineFragmentInput { float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; + nointerpolation uint underline_id: TEXCOORD0; }; StructuredBuffer underlines: register(t1); @@ -1000,10 +984,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; @@ -1039,14 +1021,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); From e8bd47f66844d388de6fb47bdb77d9bca6615350 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 19:59:26 +0800 Subject: [PATCH 015/202] wip --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ed2ec0a8c9..132ab81320 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -281,7 +281,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()); @@ -850,6 +849,7 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, + // CullMode: D3D11_CULL_BACK, FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, @@ -1080,7 +1080,7 @@ fn update_paths_buffer_capacity( ); let buffer_size = data_size.next_power_of_two(); println!("Paths New size: {}", buffer_size); - let buffer = create_buffer(device, std::mem::align_of::(), buffer_size).unwrap(); + let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); Some((buffer, buffer_size, view)) } From b130346edeb4dbab3f91900aec192359d7f9861a Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 20:55:16 +0200 Subject: [PATCH 016/202] initial removal attempt --- .../gpui/src/platform/windows/direct_write.rs | 336 +++++++----------- 1 file changed, 129 insertions(+), 207 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index ada306c15c..3fc143fdcb 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::Result; @@ -9,13 +9,7 @@ use windows::{ Win32::{ Foundation::*, Globalization::GetUserDefaultLocaleName, - Graphics::{ - Direct2D::{Common::*, *}, - DirectWrite::*, - Dxgi::Common::*, - Gdi::LOGFONTW, - Imaging::*, - }, + Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*}, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, }, @@ -40,7 +34,6 @@ struct DirectWriteComponent { locale: String, factory: IDWriteFactory5, bitmap_factory: AgileReference, - d2d1_factory: ID2D1Factory, in_memory_loader: IDWriteInMemoryFontFileLoader, builder: IDWriteFontSetBuilder1, text_renderer: Arc, @@ -49,7 +42,6 @@ struct DirectWriteComponent { struct GlyphRenderContext { params: IDWriteRenderingParams3, - dc_target: ID2D1DeviceContext4, } struct DirectWriteState { @@ -74,8 +66,6 @@ impl DirectWriteComponent { unsafe { let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let bitmap_factory = AgileReference::new(bitmap_factory)?; - let d2d1_factory: ID2D1Factory = - D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?; // The `IDWriteInMemoryFontFileLoader` here is supported starting from // Windows 10 Creators Update, which consequently requires the entire // `DirectWriteTextSystem` to run on `win10 1703`+. @@ -86,13 +76,12 @@ impl DirectWriteComponent { GetUserDefaultLocaleName(&mut locale_vec); let locale = String::from_utf16_lossy(&locale_vec); let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); - let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?; + let render_context = GlyphRenderContext::new(&factory)?; Ok(DirectWriteComponent { locale, factory, bitmap_factory, - d2d1_factory, in_memory_loader, builder, text_renderer, @@ -103,7 +92,7 @@ impl DirectWriteComponent { } impl GlyphRenderContext { - pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result { + pub fn new(factory: &IDWriteFactory5) -> Result { unsafe { let default_params: IDWriteRenderingParams3 = factory.CreateRenderingParams()?.cast()?; @@ -122,17 +111,8 @@ impl GlyphRenderContext { DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, grid_fit_mode, )?; - let dc_target = { - let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ))?; - let target = target.cast::()?; - target.SetTextRenderingParams(¶ms); - target - }; - Ok(Self { params, dc_target }) + Ok(Self { params }) } } } @@ -649,17 +629,12 @@ impl DirectWriteState { } fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - let render_target = &self.components.render_context.dc_target; - unsafe { - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor); - } let font = &self.fonts[params.font_id.0]; let glyph_id = [params.glyph_id.0 as u16]; let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, + fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -668,13 +643,29 @@ impl DirectWriteState { isSideways: BOOL(0), bidiLevel: 0, }; - let bounds = unsafe { - render_target.GetGlyphRunWorldBounds( - Vector2 { X: 0.0, Y: 0.0 }, + + let transform = DWRITE_MATRIX::default(); + let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + let baseline_origin_x = 0.0; + let baseline_origin_y = 0.0; + + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - DWRITE_MEASURING_MODE_NATURAL, + Some(&transform as *const _), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, )? }; + + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + // todo(windows) // This is a walkaround, deleted when figured out. let y_offset; @@ -696,12 +687,13 @@ impl DirectWriteState { } else { Ok(Bounds { origin: point( - ((bounds.left * params.scale_factor).ceil() as i32).into(), - ((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(), + ((bounds.left as f32 * params.scale_factor).ceil() as i32).into(), + ((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(), ), size: size( - (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(), - (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32 + (((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32) + .into(), + (((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32 + extra_height) .into(), ), @@ -739,7 +731,7 @@ impl DirectWriteState { ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, }]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) }, + fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -759,149 +751,108 @@ impl DirectWriteState { } let bitmap_size = bitmap_size; - let total_bytes; - let bitmap_format; - let render_target_property; - let bitmap_width; - let bitmap_height; - let bitmap_stride; - let bitmap_dpi; - if params.is_emoji { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4; - bitmap_format = &GUID_WICPixelFormat32bppPBGRA; - render_target_property = get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ); - bitmap_width = bitmap_size.width.0 as u32; - bitmap_height = bitmap_size.height.0 as u32; - bitmap_stride = bitmap_size.width.0 as u32 * 4; - bitmap_dpi = 96.0; + let subpixel_shift = params + .subpixel_variant + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); + let baseline_origin_x = subpixel_shift.x / params.scale_factor; + let baseline_origin_y = subpixel_shift.y / params.scale_factor; + + let transform = DWRITE_MATRIX { + m11: params.scale_factor, + m12: 0.0, + m21: 0.0, + m22: params.scale_factor, + dx: 0.0, + dy: 0.0, + }; + + let rendering_mode = if params.is_emoji { + DWRITE_RENDERING_MODE1_NATURAL } else { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize; - bitmap_format = &GUID_WICPixelFormat8bppAlpha; - render_target_property = - get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT); - bitmap_width = bitmap_size.width.0 as u32 * 2; - bitmap_height = bitmap_size.height.0 as u32 * 2; - bitmap_stride = bitmap_size.width.0 as u32; - bitmap_dpi = 192.0; - } + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC + }; - let bitmap_factory = self.components.bitmap_factory.resolve()?; - unsafe { - let bitmap = bitmap_factory.CreateBitmap( - bitmap_width, - bitmap_height, - bitmap_format, - WICBitmapCacheOnLoad, - )?; - let render_target = self - .components - .d2d1_factory - .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?; - let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?; - let subpixel_shift = params - .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); - let baseline_origin = Vector2 { - X: subpixel_shift.x / params.scale_factor, - Y: subpixel_shift.y / params.scale_factor, - }; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - // This `cast()` action here should never fail since we are running on Win10+, and - // ID2D1DeviceContext4 requires Win8+ - let render_target = render_target.cast::().unwrap(); - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi( - bitmap_dpi * params.scale_factor, - bitmap_dpi * params.scale_factor, - ); - render_target.SetTextRenderingParams(&self.components.render_context.params); - render_target.BeginDraw(); + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( + &glyph_run, + Some(&transform), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, + )? + }; - if params.is_emoji { - // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested - let enumerator = self.components.factory.TranslateColorGlyphRun( - baseline_origin, - &glyph_run as _, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR - | DWRITE_GLYPH_IMAGE_FORMATS_SVG - | DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - DWRITE_MEASURING_MODE_NATURAL, - None, - 0, - )?; - while enumerator.MoveNext().is_ok() { - let Ok(color_glyph) = enumerator.GetCurrentRun() else { - break; - }; - let color_glyph = &*color_glyph; - let brush_color = translate_color(&color_glyph.Base.runColor); - brush.SetColor(&brush_color); - match color_glyph.glyphImageFormat { - DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target - .DrawColorBitmapGlyphRun( - color_glyph.glyphImageFormat, - baseline_origin, - &color_glyph.Base.glyphRun, - color_glyph.measuringMode, - D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT, - ), - DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - &brush, - None, - color_glyph.Base.paletteIndex as u32, - color_glyph.measuringMode, - ), - _ => render_target.DrawGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - Some(color_glyph.Base.glyphRunDescription as *const _), - &brush, - color_glyph.measuringMode, - ), - } - } - } else { - render_target.DrawGlyphRun( - baseline_origin, - &glyph_run, - None, - &brush, - DWRITE_MEASURING_MODE_NATURAL, - ); + if params.is_emoji { + // For emoji, we need to handle color glyphs differently + // This is a simplified approach - in a full implementation you'd want to + // properly handle color glyph runs using TranslateColorGlyphRun + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], + )); } - render_target.EndDraw(None, None)?; - let mut raw_data = vec![0u8; total_bytes]; - if params.is_emoji { - bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; - // Convert from BGRA with premultiplied alpha to BGRA with straight alpha. - for pixel in raw_data.chunks_exact_mut(4) { - let a = pixel[3] as f32 / 255.; - pixel[0] = (pixel[0] as f32 / a) as u8; - pixel[1] = (pixel[1] as f32 / a) as u8; - pixel[2] = (pixel[2] as f32 / a) as u8; - } - } else { - let scaler = bitmap_factory.CreateBitmapScaler()?; - scaler.Initialize( - &bitmap, - bitmap_size.width.0 as u32, - bitmap_size.height.0 as u32, - WICBitmapInterpolationModeHighQualityCubic, - )?; - scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; + let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - Ok((bitmap_size, raw_data)) + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; + rgba_data.resize(expected_size, 0); + + Ok((bitmap_size, rgba_data)) + } else { + // For regular text, use grayscale or cleartype + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], + )); + } + + let mut alpha_data = vec![0u8; (width * height) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture( + texture_type, + &texture_bounds, + &mut alpha_data, + )?; + } + + // For cleartype, we need to convert the 3x1 subpixel data to grayscale + // This is a simplified conversion - you might want to do proper subpixel rendering + let mut grayscale_data = Vec::new(); + for chunk in alpha_data.chunks_exact(3) { + let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; + grayscale_data.push(avg as u8); + } + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + grayscale_data.resize(expected_size, 0); + + Ok((bitmap_size, grayscale_data)) } } @@ -1471,13 +1422,8 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result { } #[inline] -fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F { - D2D1_COLOR_F { - r: color.r, - g: color.g, - b: color.b, - a: color.a, - } +fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] { + [color.r, color.g, color.b, color.a] } fn get_system_ui_font_name() -> SharedString { @@ -1504,24 +1450,6 @@ fn get_system_ui_font_name() -> SharedString { } } -#[inline] -fn get_render_target_property( - pixel_format: DXGI_FORMAT, - alpha_mode: D2D1_ALPHA_MODE, -) -> D2D1_RENDER_TARGET_PROPERTIES { - D2D1_RENDER_TARGET_PROPERTIES { - r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT, - pixelFormat: D2D1_PIXEL_FORMAT { - format: pixel_format, - alphaMode: alpha_mode, - }, - dpiX: 96.0, - dpiY: 96.0, - usage: D2D1_RENDER_TARGET_USAGE_NONE, - minLevel: D2D1_FEATURE_LEVEL_DEFAULT, - } -} - // One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats // but that doesn't seem to work for some glyphs, say ❤ fn is_color_glyph( @@ -1561,12 +1489,6 @@ fn is_color_glyph( } const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); -const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, -}; #[cfg(test)] mod tests { From b9eb18eb7f22cc434cd5cea1745bd585306f2d9d Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 21:44:20 +0200 Subject: [PATCH 017/202] make it not crash --- crates/gpui/src/platform/windows/direct_write.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 3fc143fdcb..4ee94e6155 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -634,7 +634,7 @@ impl DirectWriteState { let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), + fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -644,7 +644,6 @@ impl DirectWriteState { bidiLevel: 0, }; - let transform = DWRITE_MATRIX::default(); let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; let baseline_origin_x = 0.0; @@ -653,7 +652,7 @@ impl DirectWriteState { let glyph_analysis = unsafe { self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - Some(&transform as *const _), + None, rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, @@ -830,7 +829,7 @@ impl DirectWriteState { )); } - let mut alpha_data = vec![0u8; (width * height) as usize]; + let mut alpha_data = vec![0u8; (width * height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( From f5aa88ca6abcc55c3fa691c09c7c0779fd3244ff Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 22:34:36 +0200 Subject: [PATCH 018/202] more fixes and debugging --- crates/gpui/examples/text.rs | 36 +++++++++---------- .../gpui/src/platform/windows/direct_write.rs | 7 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..6f1b98e81c 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - .child(div().child(CharacterGrid::new().scale(base))) + // .child(div().child(CharacterGrid::new().scale(base))) .child( div() - .child(Specimen::new(self.next_id()).scale(step_down_2)) - .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_down_1)) - .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_2)) + // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_1)) + // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - .child(Specimen::new(self.next_id()).scale(base).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_1)) - .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_2)) - .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_3)) - .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_4)) - .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_5)) - .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_6)) - .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + // .child(Specimen::new(self.next_id()).scale(base).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_1)) + // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_2)) + // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_3)) + // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_4)) + // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_5)) + // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_6)) + // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 4ee94e6155..8d191facbe 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -810,14 +810,13 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; - rgba_data.resize(expected_size, 0); Ok((bitmap_size, rgba_data)) } else { // For regular text, use grayscale or cleartype let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); let width = (texture_bounds.right - texture_bounds.left) as u32; let height = (texture_bounds.bottom - texture_bounds.top) as u32; @@ -848,10 +847,10 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + let expected_size = width as usize * height as usize; grayscale_data.resize(expected_size, 0); - Ok((bitmap_size, grayscale_data)) + Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } } From 8d538fad0c86b38d083d702425ee13bb81522248 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 16:58:13 -0700 Subject: [PATCH 019/202] Translate rasterized glyphs from texture to bitmap --- crates/gpui/examples/text.rs | 36 +++--- .../gpui/src/platform/windows/direct_write.rs | 114 +++++++++++------- 2 files changed, 86 insertions(+), 64 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 6f1b98e81c..19214aebde 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - // .child(div().child(CharacterGrid::new().scale(base))) + .child(div().child(CharacterGrid::new().scale(base))) .child( div() - // .child(Specimen::new(self.next_id()).scale(step_down_2)) - // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_down_1)) - // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_2)) + .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_1)) + .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - // .child(Specimen::new(self.next_id()).scale(base).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_1)) - // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_2)) - // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_3)) - // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_4)) - // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_5)) - // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_6)) - // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + .child(Specimen::new(self.next_id()).scale(base).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_1)) + .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_2)) + .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_3)) + .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_4)) + .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_5)) + .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_6)) + .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 8d191facbe..bd7bacd4fc 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -786,50 +786,58 @@ impl DirectWriteState { )? }; + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + let texture_width = (texture_bounds.right - texture_bounds.left) as u32; + let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if texture_width == 0 || texture_height == 0 { + return Ok(( + bitmap_size, + vec![ + 0u8; + bitmap_size.width.0 as usize + * bitmap_size.height.0 as usize + * if params.is_emoji { 4 } else { 1 } + ], + )); + } + + let mut bitmap_data; if params.is_emoji { - // For emoji, we need to handle color glyphs differently - // This is a simplified approach - in a full implementation you'd want to - // properly handle color glyph runs using TranslateColorGlyphRun - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], - )); - } - - let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + bitmap_data = + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4]; + let mut rgba_data = vec![0u8; (texture_width * texture_height * 4) as usize]; unsafe { glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - // Resize to match expected bitmap_size if needed + // Copy texture data into bitmap at correct position + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; - Ok((bitmap_size, rgba_data)) - } else { - // For regular text, use grayscale or cleartype - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 4; + let bitmap_idx = (bitmap_y * bitmap_size.width.0 as usize + bitmap_x) * 4; - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], - )); + if texture_idx + 3 < rgba_data.len() && bitmap_idx + 3 < bitmap_data.len() { + bitmap_data[bitmap_idx..bitmap_idx + 4] + .copy_from_slice(&rgba_data[texture_idx..texture_idx + 4]); + } + } + } } + } else { + bitmap_data = vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; - let mut alpha_data = vec![0u8; (width * height * 3) as usize]; - + let mut alpha_data = vec![0u8; (texture_width * texture_height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( texture_type, @@ -838,20 +846,34 @@ impl DirectWriteState { )?; } - // For cleartype, we need to convert the 3x1 subpixel data to grayscale - // This is a simplified conversion - you might want to do proper subpixel rendering - let mut grayscale_data = Vec::new(); - for chunk in alpha_data.chunks_exact(3) { - let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; - grayscale_data.push(avg as u8); + // Convert ClearType RGB data to grayscale and place in bitmap + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; + + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 3; + let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; + + if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { + let avg = (alpha_data[texture_idx] as u32 + + alpha_data[texture_idx + 1] as u32 + + alpha_data[texture_idx + 2] as u32) + / 3; + bitmap_data[bitmap_idx] = avg as u8; + } + } + } } - - // Resize to match expected bitmap_size if needed - let expected_size = width as usize * height as usize; - grayscale_data.resize(expected_size, 0); - - Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } + + Ok((bitmap_size, bitmap_data)) } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From 231c38aa41a62fb2baeae3350cad8bf63a0122ec Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 17:57:52 -0700 Subject: [PATCH 020/202] Add emojis to text example --- crates/gpui/examples/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..1166bb2795 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid { "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ", - "❮", "<=", "!=", "==", "--", "++", "=>", "->", + "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", ]; let columns = 11; From 8514850ad4afddc44d03fec8be08e4b5e6b489ef Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 12:35:33 +0800 Subject: [PATCH 021/202] temporarily disable transparancy --- crates/gpui/src/platform/windows/window.rs | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 2801674a64..5d1c91b5e3 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,7 +384,8 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - WS_EX_APPWINDOW | WS_EX_LAYERED, + // WS_EX_APPWINDOW | WS_EX_LAYERED, + WS_EX_APPWINDOW, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -461,7 +462,7 @@ impl WindowsWindow { // 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)? }; + // unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } @@ -706,27 +707,27 @@ impl PlatformWindow for WindowsWindow { } fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - let mut window_state = self.0.state.borrow_mut(); + // let mut window_state = self.0.state.borrow_mut(); // todo(zjk) // window_state // .renderer // .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque); - match background_appearance { - WindowBackgroundAppearance::Opaque => { - // ACCENT_DISABLED - set_window_composition_attribute(window_state.hwnd, None, 0); - } - WindowBackgroundAppearance::Transparent => { - // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background - set_window_composition_attribute(window_state.hwnd, None, 2); - } - WindowBackgroundAppearance::Blurred => { - // Enable acrylic blur - // ACCENT_ENABLE_ACRYLICBLURBEHIND - set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); - } - } + // match background_appearance { + // WindowBackgroundAppearance::Opaque => { + // // ACCENT_DISABLED + // set_window_composition_attribute(window_state.hwnd, None, 0); + // } + // WindowBackgroundAppearance::Transparent => { + // // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background + // set_window_composition_attribute(window_state.hwnd, None, 2); + // } + // WindowBackgroundAppearance::Blurred => { + // // Enable acrylic blur + // // ACCENT_ENABLE_ACRYLICBLURBEHIND + // set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); + // } + // } } fn minimize(&self) { From 6928488aad1617a5a7c1c2de2391c1a92468e8f8 Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 20:55:16 +0200 Subject: [PATCH 022/202] initial removal attempt --- .../gpui/src/platform/windows/direct_write.rs | 336 +++++++----------- 1 file changed, 129 insertions(+), 207 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index ada306c15c..3fc143fdcb 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::Result; @@ -9,13 +9,7 @@ use windows::{ Win32::{ Foundation::*, Globalization::GetUserDefaultLocaleName, - Graphics::{ - Direct2D::{Common::*, *}, - DirectWrite::*, - Dxgi::Common::*, - Gdi::LOGFONTW, - Imaging::*, - }, + Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*}, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, }, @@ -40,7 +34,6 @@ struct DirectWriteComponent { locale: String, factory: IDWriteFactory5, bitmap_factory: AgileReference, - d2d1_factory: ID2D1Factory, in_memory_loader: IDWriteInMemoryFontFileLoader, builder: IDWriteFontSetBuilder1, text_renderer: Arc, @@ -49,7 +42,6 @@ struct DirectWriteComponent { struct GlyphRenderContext { params: IDWriteRenderingParams3, - dc_target: ID2D1DeviceContext4, } struct DirectWriteState { @@ -74,8 +66,6 @@ impl DirectWriteComponent { unsafe { let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let bitmap_factory = AgileReference::new(bitmap_factory)?; - let d2d1_factory: ID2D1Factory = - D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?; // The `IDWriteInMemoryFontFileLoader` here is supported starting from // Windows 10 Creators Update, which consequently requires the entire // `DirectWriteTextSystem` to run on `win10 1703`+. @@ -86,13 +76,12 @@ impl DirectWriteComponent { GetUserDefaultLocaleName(&mut locale_vec); let locale = String::from_utf16_lossy(&locale_vec); let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); - let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?; + let render_context = GlyphRenderContext::new(&factory)?; Ok(DirectWriteComponent { locale, factory, bitmap_factory, - d2d1_factory, in_memory_loader, builder, text_renderer, @@ -103,7 +92,7 @@ impl DirectWriteComponent { } impl GlyphRenderContext { - pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result { + pub fn new(factory: &IDWriteFactory5) -> Result { unsafe { let default_params: IDWriteRenderingParams3 = factory.CreateRenderingParams()?.cast()?; @@ -122,17 +111,8 @@ impl GlyphRenderContext { DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, grid_fit_mode, )?; - let dc_target = { - let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ))?; - let target = target.cast::()?; - target.SetTextRenderingParams(¶ms); - target - }; - Ok(Self { params, dc_target }) + Ok(Self { params }) } } } @@ -649,17 +629,12 @@ impl DirectWriteState { } fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - let render_target = &self.components.render_context.dc_target; - unsafe { - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor); - } let font = &self.fonts[params.font_id.0]; let glyph_id = [params.glyph_id.0 as u16]; let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, + fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -668,13 +643,29 @@ impl DirectWriteState { isSideways: BOOL(0), bidiLevel: 0, }; - let bounds = unsafe { - render_target.GetGlyphRunWorldBounds( - Vector2 { X: 0.0, Y: 0.0 }, + + let transform = DWRITE_MATRIX::default(); + let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + let baseline_origin_x = 0.0; + let baseline_origin_y = 0.0; + + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - DWRITE_MEASURING_MODE_NATURAL, + Some(&transform as *const _), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, )? }; + + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + // todo(windows) // This is a walkaround, deleted when figured out. let y_offset; @@ -696,12 +687,13 @@ impl DirectWriteState { } else { Ok(Bounds { origin: point( - ((bounds.left * params.scale_factor).ceil() as i32).into(), - ((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(), + ((bounds.left as f32 * params.scale_factor).ceil() as i32).into(), + ((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(), ), size: size( - (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(), - (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32 + (((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32) + .into(), + (((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32 + extra_height) .into(), ), @@ -739,7 +731,7 @@ impl DirectWriteState { ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, }]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) }, + fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -759,149 +751,108 @@ impl DirectWriteState { } let bitmap_size = bitmap_size; - let total_bytes; - let bitmap_format; - let render_target_property; - let bitmap_width; - let bitmap_height; - let bitmap_stride; - let bitmap_dpi; - if params.is_emoji { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4; - bitmap_format = &GUID_WICPixelFormat32bppPBGRA; - render_target_property = get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ); - bitmap_width = bitmap_size.width.0 as u32; - bitmap_height = bitmap_size.height.0 as u32; - bitmap_stride = bitmap_size.width.0 as u32 * 4; - bitmap_dpi = 96.0; + let subpixel_shift = params + .subpixel_variant + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); + let baseline_origin_x = subpixel_shift.x / params.scale_factor; + let baseline_origin_y = subpixel_shift.y / params.scale_factor; + + let transform = DWRITE_MATRIX { + m11: params.scale_factor, + m12: 0.0, + m21: 0.0, + m22: params.scale_factor, + dx: 0.0, + dy: 0.0, + }; + + let rendering_mode = if params.is_emoji { + DWRITE_RENDERING_MODE1_NATURAL } else { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize; - bitmap_format = &GUID_WICPixelFormat8bppAlpha; - render_target_property = - get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT); - bitmap_width = bitmap_size.width.0 as u32 * 2; - bitmap_height = bitmap_size.height.0 as u32 * 2; - bitmap_stride = bitmap_size.width.0 as u32; - bitmap_dpi = 192.0; - } + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC + }; - let bitmap_factory = self.components.bitmap_factory.resolve()?; - unsafe { - let bitmap = bitmap_factory.CreateBitmap( - bitmap_width, - bitmap_height, - bitmap_format, - WICBitmapCacheOnLoad, - )?; - let render_target = self - .components - .d2d1_factory - .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?; - let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?; - let subpixel_shift = params - .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); - let baseline_origin = Vector2 { - X: subpixel_shift.x / params.scale_factor, - Y: subpixel_shift.y / params.scale_factor, - }; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - // This `cast()` action here should never fail since we are running on Win10+, and - // ID2D1DeviceContext4 requires Win8+ - let render_target = render_target.cast::().unwrap(); - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi( - bitmap_dpi * params.scale_factor, - bitmap_dpi * params.scale_factor, - ); - render_target.SetTextRenderingParams(&self.components.render_context.params); - render_target.BeginDraw(); + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( + &glyph_run, + Some(&transform), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, + )? + }; - if params.is_emoji { - // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested - let enumerator = self.components.factory.TranslateColorGlyphRun( - baseline_origin, - &glyph_run as _, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR - | DWRITE_GLYPH_IMAGE_FORMATS_SVG - | DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - DWRITE_MEASURING_MODE_NATURAL, - None, - 0, - )?; - while enumerator.MoveNext().is_ok() { - let Ok(color_glyph) = enumerator.GetCurrentRun() else { - break; - }; - let color_glyph = &*color_glyph; - let brush_color = translate_color(&color_glyph.Base.runColor); - brush.SetColor(&brush_color); - match color_glyph.glyphImageFormat { - DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target - .DrawColorBitmapGlyphRun( - color_glyph.glyphImageFormat, - baseline_origin, - &color_glyph.Base.glyphRun, - color_glyph.measuringMode, - D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT, - ), - DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - &brush, - None, - color_glyph.Base.paletteIndex as u32, - color_glyph.measuringMode, - ), - _ => render_target.DrawGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - Some(color_glyph.Base.glyphRunDescription as *const _), - &brush, - color_glyph.measuringMode, - ), - } - } - } else { - render_target.DrawGlyphRun( - baseline_origin, - &glyph_run, - None, - &brush, - DWRITE_MEASURING_MODE_NATURAL, - ); + if params.is_emoji { + // For emoji, we need to handle color glyphs differently + // This is a simplified approach - in a full implementation you'd want to + // properly handle color glyph runs using TranslateColorGlyphRun + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], + )); } - render_target.EndDraw(None, None)?; - let mut raw_data = vec![0u8; total_bytes]; - if params.is_emoji { - bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; - // Convert from BGRA with premultiplied alpha to BGRA with straight alpha. - for pixel in raw_data.chunks_exact_mut(4) { - let a = pixel[3] as f32 / 255.; - pixel[0] = (pixel[0] as f32 / a) as u8; - pixel[1] = (pixel[1] as f32 / a) as u8; - pixel[2] = (pixel[2] as f32 / a) as u8; - } - } else { - let scaler = bitmap_factory.CreateBitmapScaler()?; - scaler.Initialize( - &bitmap, - bitmap_size.width.0 as u32, - bitmap_size.height.0 as u32, - WICBitmapInterpolationModeHighQualityCubic, - )?; - scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; + let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - Ok((bitmap_size, raw_data)) + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; + rgba_data.resize(expected_size, 0); + + Ok((bitmap_size, rgba_data)) + } else { + // For regular text, use grayscale or cleartype + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], + )); + } + + let mut alpha_data = vec![0u8; (width * height) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture( + texture_type, + &texture_bounds, + &mut alpha_data, + )?; + } + + // For cleartype, we need to convert the 3x1 subpixel data to grayscale + // This is a simplified conversion - you might want to do proper subpixel rendering + let mut grayscale_data = Vec::new(); + for chunk in alpha_data.chunks_exact(3) { + let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; + grayscale_data.push(avg as u8); + } + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + grayscale_data.resize(expected_size, 0); + + Ok((bitmap_size, grayscale_data)) } } @@ -1471,13 +1422,8 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result { } #[inline] -fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F { - D2D1_COLOR_F { - r: color.r, - g: color.g, - b: color.b, - a: color.a, - } +fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] { + [color.r, color.g, color.b, color.a] } fn get_system_ui_font_name() -> SharedString { @@ -1504,24 +1450,6 @@ fn get_system_ui_font_name() -> SharedString { } } -#[inline] -fn get_render_target_property( - pixel_format: DXGI_FORMAT, - alpha_mode: D2D1_ALPHA_MODE, -) -> D2D1_RENDER_TARGET_PROPERTIES { - D2D1_RENDER_TARGET_PROPERTIES { - r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT, - pixelFormat: D2D1_PIXEL_FORMAT { - format: pixel_format, - alphaMode: alpha_mode, - }, - dpiX: 96.0, - dpiY: 96.0, - usage: D2D1_RENDER_TARGET_USAGE_NONE, - minLevel: D2D1_FEATURE_LEVEL_DEFAULT, - } -} - // One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats // but that doesn't seem to work for some glyphs, say ❤ fn is_color_glyph( @@ -1561,12 +1489,6 @@ fn is_color_glyph( } const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); -const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, -}; #[cfg(test)] mod tests { From a7e34ab0bc1540f7110fdf1ffa7d62a8fcd08aff Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 21:44:20 +0200 Subject: [PATCH 023/202] make it not crash --- crates/gpui/src/platform/windows/direct_write.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 3fc143fdcb..4ee94e6155 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -634,7 +634,7 @@ impl DirectWriteState { let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), + fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -644,7 +644,6 @@ impl DirectWriteState { bidiLevel: 0, }; - let transform = DWRITE_MATRIX::default(); let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; let baseline_origin_x = 0.0; @@ -653,7 +652,7 @@ impl DirectWriteState { let glyph_analysis = unsafe { self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - Some(&transform as *const _), + None, rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, @@ -830,7 +829,7 @@ impl DirectWriteState { )); } - let mut alpha_data = vec![0u8; (width * height) as usize]; + let mut alpha_data = vec![0u8; (width * height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( From 2fb31a9157844a868c77d12f15d7481d1a9bb048 Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 22:34:36 +0200 Subject: [PATCH 024/202] more fixes and debugging --- crates/gpui/examples/text.rs | 36 +++++++++---------- .../gpui/src/platform/windows/direct_write.rs | 7 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..6f1b98e81c 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - .child(div().child(CharacterGrid::new().scale(base))) + // .child(div().child(CharacterGrid::new().scale(base))) .child( div() - .child(Specimen::new(self.next_id()).scale(step_down_2)) - .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_down_1)) - .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_2)) + // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_1)) + // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - .child(Specimen::new(self.next_id()).scale(base).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_1)) - .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_2)) - .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_3)) - .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_4)) - .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_5)) - .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_6)) - .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + // .child(Specimen::new(self.next_id()).scale(base).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_1)) + // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_2)) + // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_3)) + // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_4)) + // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_5)) + // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_6)) + // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 4ee94e6155..8d191facbe 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -810,14 +810,13 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; - rgba_data.resize(expected_size, 0); Ok((bitmap_size, rgba_data)) } else { // For regular text, use grayscale or cleartype let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); let width = (texture_bounds.right - texture_bounds.left) as u32; let height = (texture_bounds.bottom - texture_bounds.top) as u32; @@ -848,10 +847,10 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + let expected_size = width as usize * height as usize; grayscale_data.resize(expected_size, 0); - Ok((bitmap_size, grayscale_data)) + Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } } From 6fc8d7746f4918881514301c6937a93d4ccd747c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 16:58:13 -0700 Subject: [PATCH 025/202] Translate rasterized glyphs from texture to bitmap --- crates/gpui/examples/text.rs | 36 +++--- .../gpui/src/platform/windows/direct_write.rs | 114 +++++++++++------- 2 files changed, 86 insertions(+), 64 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 6f1b98e81c..19214aebde 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - // .child(div().child(CharacterGrid::new().scale(base))) + .child(div().child(CharacterGrid::new().scale(base))) .child( div() - // .child(Specimen::new(self.next_id()).scale(step_down_2)) - // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_down_1)) - // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_2)) + .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_1)) + .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - // .child(Specimen::new(self.next_id()).scale(base).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_1)) - // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_2)) - // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_3)) - // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_4)) - // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_5)) - // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_6)) - // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + .child(Specimen::new(self.next_id()).scale(base).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_1)) + .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_2)) + .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_3)) + .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_4)) + .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_5)) + .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_6)) + .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 8d191facbe..bd7bacd4fc 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -786,50 +786,58 @@ impl DirectWriteState { )? }; + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + let texture_width = (texture_bounds.right - texture_bounds.left) as u32; + let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if texture_width == 0 || texture_height == 0 { + return Ok(( + bitmap_size, + vec![ + 0u8; + bitmap_size.width.0 as usize + * bitmap_size.height.0 as usize + * if params.is_emoji { 4 } else { 1 } + ], + )); + } + + let mut bitmap_data; if params.is_emoji { - // For emoji, we need to handle color glyphs differently - // This is a simplified approach - in a full implementation you'd want to - // properly handle color glyph runs using TranslateColorGlyphRun - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], - )); - } - - let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + bitmap_data = + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4]; + let mut rgba_data = vec![0u8; (texture_width * texture_height * 4) as usize]; unsafe { glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - // Resize to match expected bitmap_size if needed + // Copy texture data into bitmap at correct position + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; - Ok((bitmap_size, rgba_data)) - } else { - // For regular text, use grayscale or cleartype - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 4; + let bitmap_idx = (bitmap_y * bitmap_size.width.0 as usize + bitmap_x) * 4; - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], - )); + if texture_idx + 3 < rgba_data.len() && bitmap_idx + 3 < bitmap_data.len() { + bitmap_data[bitmap_idx..bitmap_idx + 4] + .copy_from_slice(&rgba_data[texture_idx..texture_idx + 4]); + } + } + } } + } else { + bitmap_data = vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; - let mut alpha_data = vec![0u8; (width * height * 3) as usize]; - + let mut alpha_data = vec![0u8; (texture_width * texture_height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( texture_type, @@ -838,20 +846,34 @@ impl DirectWriteState { )?; } - // For cleartype, we need to convert the 3x1 subpixel data to grayscale - // This is a simplified conversion - you might want to do proper subpixel rendering - let mut grayscale_data = Vec::new(); - for chunk in alpha_data.chunks_exact(3) { - let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; - grayscale_data.push(avg as u8); + // Convert ClearType RGB data to grayscale and place in bitmap + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; + + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 3; + let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; + + if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { + let avg = (alpha_data[texture_idx] as u32 + + alpha_data[texture_idx + 1] as u32 + + alpha_data[texture_idx + 2] as u32) + / 3; + bitmap_data[bitmap_idx] = avg as u8; + } + } + } } - - // Resize to match expected bitmap_size if needed - let expected_size = width as usize * height as usize; - grayscale_data.resize(expected_size, 0); - - Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } + + Ok((bitmap_size, bitmap_data)) } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From 34d5926ebd37e58897390734a9224635a823b954 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 17:57:52 -0700 Subject: [PATCH 026/202] Add emojis to text example --- crates/gpui/examples/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..1166bb2795 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid { "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ", - "❮", "<=", "!=", "==", "--", "++", "=>", "->", + "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", ]; let columns = 11; From a1c00ed87f6ec69dbe6dee7b827a735253fce999 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:28:25 +0800 Subject: [PATCH 027/202] fix all --- .../src/platform/windows/directx_renderer.rs | 50 ++----------------- crates/gpui/src/platform/windows/shaders.hlsl | 46 ++++++++--------- 2 files changed, 24 insertions(+), 72 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 132ab81320..3853e506c7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -58,7 +58,6 @@ struct DirectXGlobalElements { global_params_buffer: [Option; 1], sampler: [Option; 1], blend_state: ID3D11BlendState, - blend_state_for_pr: ID3D11BlendState, } #[repr(C)] @@ -601,13 +600,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, }) } } @@ -849,7 +846,7 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, - // CullMode: D3D11_CULL_BACK, + // FrontCounterClockwise: true.into(), FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, @@ -888,25 +885,6 @@ 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, @@ -989,13 +967,6 @@ fn create_buffer_view( } fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> 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() - // }; let desc = D3D11_BUFFER_DESC { ByteWidth: std::mem::size_of::() as u32 * buffer_size, Usage: D3D11_USAGE_DYNAMIC, @@ -1173,20 +1144,6 @@ 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( device_context: &ID3D11DeviceContext, pipeline: &PathsPipelineState, @@ -1314,7 +1271,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:?}")); @@ -1325,8 +1281,8 @@ 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()) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f93ef426dd..06a65e909f 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -461,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; @@ -512,9 +512,9 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { input.background_solid, input.background_color0, input.background_color1); bool unrounded = quad.corner_radii.top_left == 0.0 && - quad.corner_radii.bottom_left == 0.0 && - quad.corner_radii.top_right == 0.0 && - quad.corner_radii.bottom_right == 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 && @@ -796,19 +796,6 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { ** */ -struct ShadowVertexOutput { - float4 position: SV_Position; - nointerpolation float4 color: COLOR; - nointerpolation uint shadow_id: TEXCOORD0; - float4 clip_distance: SV_ClipDistance; -}; - -struct ShadowFragmentInput { - float4 position: SV_Position; - float4 color: COLOR; - nointerpolation uint shadow_id: TEXCOORD0; -}; - struct Shadow { uint order; float blur_radius; @@ -818,6 +805,19 @@ struct Shadow { 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) { @@ -950,16 +950,16 @@ struct Underline { }; struct UnderlineVertexOutput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; nointerpolation float4 color: COLOR; - nointerpolation uint underline_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct UnderlineFragmentInput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; nointerpolation float4 color: COLOR; - nointerpolation uint underline_id: TEXCOORD0; }; StructuredBuffer underlines: register(t1); @@ -1075,16 +1075,16 @@ struct PolychromeSprite { }; struct PolychromeSpriteVertexOutput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - nointerpolation uint sprite_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct PolychromeSpriteFragmentInput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - nointerpolation uint sprite_id: TEXCOORD0; }; StructuredBuffer poly_sprites: register(t1); @@ -1115,10 +1115,6 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } - // if ((sprite.grayscale & 0xFFu) != 0u) { - // float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); - // color = float4(grayscale, sample.a); - // } color.a *= sprite.opacity * saturate(0.5 - distance); return color; } From 85cf9e405ea8b87cdebd7cd291974aff13f0069a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:29:32 +0800 Subject: [PATCH 028/202] remove debug print --- .../src/platform/windows/directx_renderer.rs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 3853e506c7..609872714b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1029,9 +1029,7 @@ fn update_buffer_capacity( 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)) @@ -1045,12 +1043,7 @@ fn update_paths_buffer_capacity( if pipeline.buffer_size >= data_size { return None; } - println!( - "Paths buffer too small: {} < {}", - pipeline.buffer_size, data_size - ); let buffer_size = data_size.next_power_of_two(); - println!("Paths New size: {}", buffer_size); let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); Some((buffer, buffer_size, view)) @@ -1064,12 +1057,7 @@ fn update_paths_vertex_capacity( if pipeline.vertex_buffer_size >= vertex_size { return None; } - println!( - "Paths vertex buffer too small: {} < {}", - pipeline.vertex_buffer_size, vertex_size - ); let vertex_size = vertex_size.next_power_of_two(); - println!("Paths vertex New size: {}", vertex_size); let buffer = create_buffer( device, std::mem::size_of::>(), @@ -1088,12 +1076,7 @@ fn update_indirect_buffer_capacity( if pipeline.indirect_buffer_size >= data_size { return None; } - println!( - "Indirect buffer too small: {} < {}", - pipeline.indirect_buffer_size, data_size - ); let buffer_size = data_size.next_power_of_two(); - println!("Indirect New size: {}", buffer_size); let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); Some((buffer, buffer_size)) } @@ -1237,7 +1220,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(); @@ -1251,11 +1233,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))] From f16f07b36fa94ec9e5775b3e54ebac362a16eaed Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:38:18 +0800 Subject: [PATCH 029/202] bringback our colorful avatar --- crates/gpui/src/platform/windows/shaders.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 06a65e909f..1ed2de2240 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1111,7 +1111,7 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); float4 color = sample; - if (sprite.grayscale) { + if ((sprite.grayscale & 0xFFu) != 0u) { float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } From 83d942611f5eda5245eaddcc35e052d13b58b925 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 15 Jul 2025 15:49:30 -0700 Subject: [PATCH 030/202] Fix path rendering - draw all paths w/ one regular draw call --- .../src/platform/windows/directx_renderer.rs | 255 ++++++++---------- crates/gpui/src/platform/windows/shaders.hlsl | 29 +- 2 files changed, 131 insertions(+), 153 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 609872714b..3614641ced 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -60,14 +60,6 @@ struct DirectXGlobalElements { blend_state: ID3D11BlendState, } -#[repr(C)] -struct DrawInstancedIndirectArgs { - vertex_count_per_instance: u32, - instance_count: u32, - start_vertex_location: u32, - start_instance_location: u32, -} - // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -282,22 +274,12 @@ impl DirectXRenderer { } let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); - let mut draw_indirect_commands = Vec::with_capacity(paths.len()); - let mut start_vertex_location = 0; - for (i, path) in paths.iter().enumerate() { - draw_indirect_commands.push(DrawInstancedIndirectArgs { - vertex_count_per_instance: path.vertices.len() as u32, - instance_count: 1, - start_vertex_location, - start_instance_location: i as u32, - }); - start_vertex_location += path.vertices.len() as u32; - vertices.extend(path.vertices.iter().map(|v| PathVertex { + for (i, path) in paths.iter().enumerate() { + 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 { @@ -314,7 +296,7 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, + &self.pipelines.paths_pipeline.instance_buffer, &sprites, )?; update_paths_vertex_capacity( @@ -328,32 +310,40 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline.vertex_buffer, &vertices, )?; - update_indirect_buffer_capacity( - &self.pipelines.paths_pipeline, - draw_indirect_commands.len(), - &self.devices.device, - ) - .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - &self.pipelines.paths_pipeline.indirect_draw_buffer, - &draw_indirect_commands, - )?; - prepare_indirect_draws( - &self.devices.device_context, - &self.pipelines.paths_pipeline, - &self.context.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_pipeline.indirect_draw_buffer, - (i * std::mem::size_of::()) as u32, + let device_context = &self.devices.device_context; + unsafe { + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_context.VSSetShader(&self.pipelines.paths_pipeline.vertex, None); + device_context.PSSetShader(&self.pipelines.paths_pipeline.fragment, None); + device_context.VSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); + device_context.PSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); + device_context.VSSetShaderResources( + 2, + Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), ); + device_context.PSSetShaderResources( + 2, + Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), + ); + device_context.PSSetSamplers(0, Some(&self.globals.sampler)); + device_context.OMSetBlendState(&self.globals.blend_state, None, 0xffffffff); + let stride = std::mem::size_of::() as u32; + let offset = 0u32; + device_context.IASetVertexBuffers( + 0, + 1, + Some([Some(self.pipelines.paths_pipeline.vertex_buffer.clone())].as_ptr()), + Some(&stride), + Some(&offset), + ); + device_context.IASetInputLayout(&self.pipelines.paths_pipeline.input_layout); } + + unsafe { + device_context.Draw(vertices.len() as u32, 0); + } + Ok(()) } @@ -627,19 +617,18 @@ struct PipelineState { struct PathsPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, - buffer: ID3D11Buffer, - buffer_size: usize, + instance_buffer: ID3D11Buffer, + instance_buffer_size: usize, vertex_buffer: ID3D11Buffer, vertex_buffer_size: usize, - indirect_draw_buffer: ID3D11Buffer, - indirect_buffer_size: usize, - view: [Option; 1], - vertex_view: [Option; 1], + instance_view: ID3D11ShaderResourceView, + vertex_view: ID3D11ShaderResourceView, + input_layout: ID3D11InputLayout, } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let vertex = { + let (vertex, shader_blob) = { let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( @@ -647,7 +636,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - create_vertex_shader(device, bytes)? + (create_vertex_shader(device, bytes)?, shader_blob) }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -659,23 +648,64 @@ impl PathsPipelineState { }; 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 = - create_buffer(device, std::mem::size_of::>(), 32)?; + let instance_buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let instance_view = create_buffer_view(device, &instance_buffer)?; + let vertex_buffer = create_buffer(device, std::mem::size_of::(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; - 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( + shader_blob.GetBufferPointer() as *const u8, + shader_blob.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_R32G32B32A32_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_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, + instance_buffer, + instance_buffer_size: 32, vertex_buffer, vertex_buffer_size: 32, - indirect_draw_buffer, - indirect_buffer_size: 32, - view, + instance_view, vertex_view, + input_layout, }) } } @@ -687,6 +717,14 @@ struct PathSprite { color: Background, } +#[derive(Clone, Debug)] +#[repr(C)] +struct DirectXPathVertex { + xy_position: Point, + content_mask: Bounds, + sprite_index: u32, +} + fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -919,7 +957,7 @@ fn create_pipieline( fragment, buffer, buffer_size, - view, + view: [Some(view)], }) } @@ -960,24 +998,10 @@ fn create_buffer( fn create_buffer_view( device: &ID3D11Device, buffer: &ID3D11Buffer, -) -> Result<[Option; 1]> { +) -> Result { let mut view = None; unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; - Ok([view]) -} - -fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { - let desc = D3D11_BUFFER_DESC { - ByteWidth: std::mem::size_of::() as u32 * buffer_size, - Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_INDEX_BUFFER.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, - }; - let mut buffer = None; - unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; - Ok(buffer.unwrap()) + Ok(view.unwrap()) } fn update_global_params( @@ -1032,15 +1056,15 @@ fn update_buffer_capacity( let buffer_size = data_size.next_power_of_two(); let buffer = create_buffer(device, element_size, buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, view)) + Some((buffer, buffer_size, [Some(view)])) } fn update_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { - if pipeline.buffer_size >= data_size { +) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { + if pipeline.instance_buffer_size >= data_size { return None; } let buffer_size = data_size.next_power_of_two(); @@ -1053,14 +1077,14 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { +) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { if pipeline.vertex_buffer_size >= vertex_size { return None; } let vertex_size = vertex_size.next_power_of_two(); let buffer = create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), vertex_size, ) .unwrap(); @@ -1068,19 +1092,6 @@ fn update_paths_vertex_capacity( Some((buffer, vertex_size, view)) } -fn update_indirect_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.indirect_buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); - Some((buffer, buffer_size)) -} - fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1092,27 +1103,22 @@ fn update_pipeline( fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), + input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), ) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; + pipeline.instance_buffer = input.0; + pipeline.instance_buffer_size = input.1; + pipeline.instance_view = input.2; } fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), + input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), ) { pipeline.vertex_buffer = input.0; pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } -fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.indirect_draw_buffer = input.0; - pipeline.indirect_buffer_size = input.1; -} - fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1127,37 +1133,6 @@ fn update_buffer( Ok(()) } -fn prepare_indirect_draws( - device_context: &ID3D11DeviceContext, - pipeline: &PathsPipelineState, - viewport: &[D3D11_VIEWPORT], - global_params: &[Option], - topology: D3D_PRIMITIVE_TOPOLOGY, -) -> Result<()> { - unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); - device_context.VSSetShaderResources(2, Some(&pipeline.view)); - device_context.PSSetShaderResources(2, 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, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 1ed2de2240..b926847cbf 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -256,7 +256,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -876,9 +876,10 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { ** */ -struct PathVertex { - float2 xy_position; - Bounds content_mask; +struct PathVertexInput { + float2 xy_position: POSITION; + float4 content_mask: TEXCOORD0; + uint sprite_index: TEXCOORD1; }; struct PathSprite { @@ -895,17 +896,19 @@ struct PathVertexOutput { nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_vertices: register(t1); StructuredBuffer path_sprites: register(t2); -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(PathVertexInput input) { + PathSprite sprite = path_sprites[input.sprite_index]; + + Bounds content_mask; + content_mask.origin = input.content_mask.xy; + content_mask.size = input.content_mask.zw; 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, content_mask); + output.sprite_id = input.sprite_index; GradientColor gradient = prepare_gradient_color( sprite.color.tag, @@ -925,7 +928,7 @@ float4 paths_fragment(PathVertexOutput input): SV_Target { if (any(input.clip_distance < zero)) { return zero; } - + PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, @@ -968,7 +971,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); From c014dbae8ce7112e73a68bb922e3f86de4b72493 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 15 Jul 2025 16:48:19 -0700 Subject: [PATCH 031/202] Fix dxgi_factory type error in release mode --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 3614641ced..a1f4dbeb58 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -729,7 +729,7 @@ 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)?) } } From cdbaff8375fcb9ec04dedb1444c20fcf02b9bd46 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:56:55 +0800 Subject: [PATCH 032/202] wip --- .../gpui/src/platform/windows/directx_renderer.rs | 3 +-- crates/gpui/src/platform/windows/shaders.hlsl | 15 +++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index a1f4dbeb58..542c90444d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1,8 +1,7 @@ -use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc}; +use std::sync::Arc; use ::util::ResultExt; use anyhow::{Context, Result}; -use collections::FxHasher; // #[cfg(not(feature = "enable-renderdoc"))] // use windows::Win32::Graphics::DirectComposition::*; use windows::{ diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index b926847cbf..18760dcf78 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -889,7 +889,15 @@ 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; @@ -923,12 +931,7 @@ PathVertexOutput paths_vertex(PathVertexInput input) { 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, From 158732eb179b2469e03fab77a58b76d87d293dcb Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:08:30 +0800 Subject: [PATCH 033/202] Revert "Fix path rendering - draw all paths w/ one regular draw call" This reverts commit 83d942611f5eda5245eaddcc35e052d13b58b925. --- .../src/platform/windows/directx_renderer.rs | 255 ++++++++++-------- crates/gpui/src/platform/windows/shaders.hlsl | 27 +- 2 files changed, 152 insertions(+), 130 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 542c90444d..4c8642e7c2 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -59,6 +59,14 @@ struct DirectXGlobalElements { blend_state: ID3D11BlendState, } +#[repr(C)] +struct DrawInstancedIndirectArgs { + vertex_count_per_instance: u32, + instance_count: u32, + start_vertex_location: u32, + start_instance_location: u32, +} + // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -273,12 +281,22 @@ impl DirectXRenderer { } let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); - + let mut draw_indirect_commands = Vec::with_capacity(paths.len()); + let mut start_vertex_location = 0; for (i, path) in paths.iter().enumerate() { - vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex { + draw_indirect_commands.push(DrawInstancedIndirectArgs { + vertex_count_per_instance: path.vertices.len() as u32, + instance_count: 1, + start_vertex_location, + start_instance_location: i as u32, + }); + start_vertex_location += path.vertices.len() as u32; + + vertices.extend(path.vertices.iter().map(|v| PathVertex { xy_position: v.xy_position, - content_mask: path.content_mask.bounds, - sprite_index: i as u32, + content_mask: ContentMask { + bounds: path.content_mask.bounds, + }, })); sprites.push(PathSprite { @@ -295,7 +313,7 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.instance_buffer, + &self.pipelines.paths_pipeline.buffer, &sprites, )?; update_paths_vertex_capacity( @@ -309,40 +327,32 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline.vertex_buffer, &vertices, )?; + update_indirect_buffer_capacity( + &self.pipelines.paths_pipeline, + draw_indirect_commands.len(), + &self.devices.device, + ) + .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); + update_buffer( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, + &draw_indirect_commands, + )?; + prepare_indirect_draws( + &self.devices.device_context, + &self.pipelines.paths_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + )?; - let device_context = &self.devices.device_context; - unsafe { - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - device_context.VSSetShader(&self.pipelines.paths_pipeline.vertex, None); - device_context.PSSetShader(&self.pipelines.paths_pipeline.fragment, None); - device_context.VSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); - device_context.PSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); - device_context.VSSetShaderResources( - 2, - Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), + for i in 0..paths.len() { + draw_indirect( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, ); - device_context.PSSetShaderResources( - 2, - Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), - ); - device_context.PSSetSamplers(0, Some(&self.globals.sampler)); - device_context.OMSetBlendState(&self.globals.blend_state, None, 0xffffffff); - let stride = std::mem::size_of::() as u32; - let offset = 0u32; - device_context.IASetVertexBuffers( - 0, - 1, - Some([Some(self.pipelines.paths_pipeline.vertex_buffer.clone())].as_ptr()), - Some(&stride), - Some(&offset), - ); - device_context.IASetInputLayout(&self.pipelines.paths_pipeline.input_layout); } - - unsafe { - device_context.Draw(vertices.len() as u32, 0); - } - Ok(()) } @@ -616,18 +626,19 @@ struct PipelineState { struct PathsPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, - instance_buffer: ID3D11Buffer, - instance_buffer_size: usize, + buffer: ID3D11Buffer, + buffer_size: usize, vertex_buffer: ID3D11Buffer, vertex_buffer_size: usize, - instance_view: ID3D11ShaderResourceView, - vertex_view: ID3D11ShaderResourceView, - input_layout: ID3D11InputLayout, + indirect_draw_buffer: ID3D11Buffer, + indirect_buffer_size: usize, + view: [Option; 1], + vertex_view: [Option; 1], } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let (vertex, shader_blob) = { + let vertex = { let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( @@ -635,7 +646,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - (create_vertex_shader(device, bytes)?, shader_blob) + create_vertex_shader(device, bytes)? }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -647,64 +658,23 @@ impl PathsPipelineState { }; create_fragment_shader(device, bytes)? }; - let instance_buffer = create_buffer(device, std::mem::size_of::(), 32)?; - let instance_view = create_buffer_view(device, &instance_buffer)?; - let vertex_buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let view = create_buffer_view(device, &buffer)?; + let vertex_buffer = + create_buffer(device, std::mem::size_of::>(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; - - // Create input layout - let input_layout = unsafe { - let shader_bytes = std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *const u8, - shader_blob.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_R32G32B32A32_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_R32_UINT, - InputSlot: 0, - AlignedByteOffset: 24, - InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - ], - shader_bytes, - Some(&mut layout), - )?; - layout.unwrap() - }; - + let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; Ok(Self { vertex, fragment, - instance_buffer, - instance_buffer_size: 32, + buffer, + buffer_size: 32, vertex_buffer, vertex_buffer_size: 32, - instance_view, + indirect_draw_buffer, + indirect_buffer_size: 32, + view, vertex_view, - input_layout, }) } } @@ -716,14 +686,6 @@ struct PathSprite { color: Background, } -#[derive(Clone, Debug)] -#[repr(C)] -struct DirectXPathVertex { - xy_position: Point, - content_mask: Bounds, - sprite_index: u32, -} - fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -956,7 +918,7 @@ fn create_pipieline( fragment, buffer, buffer_size, - view: [Some(view)], + view, }) } @@ -997,10 +959,24 @@ fn create_buffer( fn create_buffer_view( device: &ID3D11Device, buffer: &ID3D11Buffer, -) -> Result { +) -> Result<[Option; 1]> { let mut view = None; unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; - Ok(view.unwrap()) + Ok([view]) +} + +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32 * buffer_size, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_INDEX_BUFFER.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, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) } fn update_global_params( @@ -1055,15 +1031,15 @@ fn update_buffer_capacity( let buffer_size = data_size.next_power_of_two(); let buffer = create_buffer(device, element_size, buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, [Some(view)])) + Some((buffer, buffer_size, view)) } fn update_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { - if pipeline.instance_buffer_size >= data_size { +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.buffer_size >= data_size { return None; } let buffer_size = data_size.next_power_of_two(); @@ -1076,14 +1052,14 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { if pipeline.vertex_buffer_size >= vertex_size { return None; } let vertex_size = vertex_size.next_power_of_two(); let buffer = create_buffer( device, - std::mem::size_of::(), + std::mem::size_of::>(), vertex_size, ) .unwrap(); @@ -1091,6 +1067,19 @@ fn update_paths_vertex_capacity( Some((buffer, vertex_size, view)) } +fn update_indirect_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize)> { + if pipeline.indirect_buffer_size >= data_size { + return None; + } + let buffer_size = data_size.next_power_of_two(); + let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + Some((buffer, buffer_size)) +} + fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1102,22 +1091,27 @@ fn update_pipeline( fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), + input: (ID3D11Buffer, usize, [Option; 1]), ) { - pipeline.instance_buffer = input.0; - pipeline.instance_buffer_size = input.1; - pipeline.instance_view = input.2; + pipeline.buffer = input.0; + pipeline.buffer_size = input.1; + pipeline.view = input.2; } fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), + input: (ID3D11Buffer, usize, [Option; 1]), ) { pipeline.vertex_buffer = input.0; pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } +fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { + pipeline.indirect_draw_buffer = input.0; + pipeline.indirect_buffer_size = input.1; +} + fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1132,6 +1126,37 @@ fn update_buffer( Ok(()) } +fn prepare_indirect_draws( + device_context: &ID3D11DeviceContext, + pipeline: &PathsPipelineState, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, +) -> Result<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); + device_context.VSSetShaderResources(2, Some(&pipeline.view)); + device_context.PSSetShaderResources(2, 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, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 18760dcf78..3438e708a3 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -256,7 +256,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -876,10 +876,9 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { ** */ -struct PathVertexInput { - float2 xy_position: POSITION; - float4 content_mask: TEXCOORD0; - uint sprite_index: TEXCOORD1; +struct PathVertex { + float2 xy_position; + Bounds content_mask; }; struct PathSprite { @@ -904,19 +903,17 @@ struct PathFragmentInput { nointerpolation float4 color1: COLOR2; }; +StructuredBuffer path_vertices: register(t1); StructuredBuffer path_sprites: register(t2); -PathVertexOutput paths_vertex(PathVertexInput input) { - PathSprite sprite = path_sprites[input.sprite_index]; - - Bounds content_mask; - content_mask.origin = input.content_mask.xy; - content_mask.size = input.content_mask.zw; +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 output; - output.position = to_device_position_impl(input.xy_position); - output.clip_distance = distance_from_clip_rect_impl(input.xy_position, content_mask); - output.sprite_id = input.sprite_index; + 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; GradientColor gradient = prepare_gradient_color( sprite.color.tag, @@ -974,7 +971,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); From 291691ca0eb1b235fc9e100590de457f3060114d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:24:55 +0800 Subject: [PATCH 034/202] fix paths rendering --- .../src/platform/windows/directx_renderer.rs | 99 +++++++++++++++++-- crates/gpui/src/platform/windows/shaders.hlsl | 10 +- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4c8642e7c2..9a457420aa 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -324,7 +324,11 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.vertex_buffer, + self.pipelines + .paths_pipeline + .vertex_buffer + .as_ref() + .unwrap(), &vertices, )?; update_indirect_buffer_capacity( @@ -628,10 +632,11 @@ struct PathsPipelineState { fragment: ID3D11PixelShader, buffer: ID3D11Buffer, buffer_size: usize, - vertex_buffer: ID3D11Buffer, + vertex_buffer: Option, vertex_buffer_size: usize, indirect_draw_buffer: ID3D11Buffer, indirect_buffer_size: usize, + input_layout: ID3D11InputLayout, view: [Option; 1], vertex_view: [Option; 1], } @@ -663,7 +668,71 @@ impl PathsPipelineState { let vertex_buffer = create_buffer(device, std::mem::size_of::>(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; + let vertex_buffer = Some(vertex_buffer); let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; + // Create input layout + let input_layout = unsafe { + let (vertex, shader_blob) = { + 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 shader_bytes = std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *const u8, + shader_blob.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, + }, + ], + shader_bytes, + Some(&mut layout), + )?; + layout.unwrap() + }; + Ok(Self { vertex, fragment, @@ -673,6 +742,7 @@ impl PathsPipelineState { vertex_buffer_size: 32, indirect_draw_buffer, indirect_buffer_size: 32, + input_layout, view, vertex_view, }) @@ -965,11 +1035,11 @@ fn create_buffer_view( Ok([view]) } -fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { +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, @@ -1076,7 +1146,7 @@ fn update_indirect_buffer_capacity( return None; } let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + let buffer = create_indirect_draw_buffer(device, data_size).unwrap(); Some((buffer, buffer_size)) } @@ -1102,7 +1172,7 @@ fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize, [Option; 1]), ) { - pipeline.vertex_buffer = input.0; + pipeline.vertex_buffer = Some(input.0); pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } @@ -1134,15 +1204,24 @@ fn prepare_indirect_draws( topology: D3D_PRIMITIVE_TOPOLOGY, ) -> Result<()> { unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); - device_context.VSSetShaderResources(2, Some(&pipeline.view)); - device_context.PSSetShaderResources(2, Some(&pipeline.view)); + 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)); + let stride = std::mem::size_of::>() as u32; + let offset = 0u32; + device_context.IASetVertexBuffers( + 0, + 1, + Some(&pipeline.vertex_buffer), + Some(&stride), + Some(&offset), + ); + device_context.IASetInputLayout(&pipeline.input_layout); } Ok(()) } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 3438e708a3..7e59bb714e 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -877,8 +877,8 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { */ struct PathVertex { - float2 xy_position; - Bounds content_mask; + float2 xy_position: POSITION; + Bounds content_mask: TEXCOORD; }; struct PathSprite { @@ -903,11 +903,9 @@ struct PathFragmentInput { 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]; +PathVertexOutput paths_vertex(PathVertex v, uint instance_id: SV_InstanceID) { PathSprite sprite = path_sprites[instance_id]; PathVertexOutput output; From f715acc92a3bb4662c79cf7bf8b87ff4dcc0eb7e Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:37:51 +0800 Subject: [PATCH 035/202] remove unused --- .../src/platform/windows/directx_renderer.rs | 57 +++++-------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 9a457420aa..a3931519f9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -638,12 +638,11 @@ struct PathsPipelineState { indirect_buffer_size: usize, input_layout: ID3D11InputLayout, view: [Option; 1], - vertex_view: [Option; 1], } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let vertex = { + 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( @@ -651,7 +650,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - create_vertex_shader(device, bytes)? + (create_vertex_shader(device, bytes)?, shader_blob) }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -665,36 +664,17 @@ impl PathsPipelineState { }; let buffer = create_buffer(device, std::mem::size_of::(), 32)?; let view = create_buffer_view(device, &buffer)?; - let vertex_buffer = - create_buffer(device, std::mem::size_of::>(), 32)?; - let vertex_view = create_buffer_view(device, &vertex_buffer)?; - let vertex_buffer = Some(vertex_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 (vertex, shader_blob) = { - 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 shader_bytes = std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *const u8, - shader_blob.GetBufferSize(), + vertex_shader.GetBufferPointer() as *const u8, + vertex_shader.GetBufferSize(), ); let mut layout = None; device.CreateInputLayout( @@ -744,7 +724,6 @@ impl PathsPipelineState { indirect_buffer_size: 32, input_layout, view, - vertex_view, }) } } @@ -1122,7 +1101,7 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { +) -> Option<(ID3D11Buffer, usize)> { if pipeline.vertex_buffer_size >= vertex_size { return None; } @@ -1133,8 +1112,7 @@ fn update_paths_vertex_capacity( vertex_size, ) .unwrap(); - let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, vertex_size, view)) + Some((buffer, vertex_size)) } fn update_indirect_buffer_capacity( @@ -1168,13 +1146,9 @@ fn update_paths_pipeline_buffer( pipeline.view = input.2; } -fn update_paths_pipeline_vertex( - pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { +fn update_paths_pipeline_vertex(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { pipeline.vertex_buffer = Some(input.0); pipeline.vertex_buffer_size = input.1; - pipeline.vertex_view = input.2; } fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { @@ -1212,14 +1186,13 @@ fn prepare_indirect_draws( device_context.PSSetShader(&pipeline.fragment, None); device_context.VSSetConstantBuffers(0, Some(global_params)); device_context.PSSetConstantBuffers(0, Some(global_params)); - let stride = std::mem::size_of::>() as u32; - let offset = 0u32; + const STRIDE: u32 = std::mem::size_of::>() as u32; device_context.IASetVertexBuffers( 0, 1, Some(&pipeline.vertex_buffer), - Some(&stride), - Some(&offset), + Some(&STRIDE), + Some(&0), ); device_context.IASetInputLayout(&pipeline.input_layout); } From 2017ce3699e9e7afa746b907956f4fea1810ad2c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 14:04:49 +0800 Subject: [PATCH 036/202] refactor --- .../src/platform/windows/directx_renderer.rs | 230 +++++++----------- 1 file changed, 94 insertions(+), 136 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index a3931519f9..dd5e1a5b32 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -45,12 +45,12 @@ struct DirectXContext { } struct DirectXRenderPipelines { - shadow_pipeline: PipelineState, - quad_pipeline: PipelineState, + shadow_pipeline: PipelineState, + quad_pipeline: PipelineState, paths_pipeline: PathsPipelineState, - underline_pipeline: PipelineState, - mono_sprites: PipelineState, - poly_sprites: PipelineState, + underline_pipeline: PipelineState, + mono_sprites: PipelineState, + poly_sprites: PipelineState, } struct DirectXGlobalElements { @@ -225,16 +225,9 @@ impl DirectXRenderer { 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( @@ -252,16 +245,9 @@ 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( @@ -364,16 +350,9 @@ impl DirectXRenderer { 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( @@ -395,19 +374,12 @@ 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, )?; + let texture_view = self.atlas.get_texture_view(texture_id); draw_with_texture( &self.devices.device_context, &self.pipelines.mono_sprites, @@ -427,19 +399,12 @@ 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, )?; + let texture_view = self.atlas.get_texture_view(texture_id); draw_with_texture( &self.devices.device_context, &self.pipelines.poly_sprites, @@ -490,47 +455,35 @@ impl DirectXContext { 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, - // )?; + let quad_pipeline = + PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?; let paths_pipeline = PathsPipelineState::new(device)?; - let underline_pipeline = create_pipieline( + let underline_pipeline = PipelineState::new( device, + "underline_pipeline", "underline_vertex", "underline_fragment", - std::mem::size_of::(), 32, )?; - 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, )?; - 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, )?; @@ -619,12 +572,14 @@ 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 { @@ -640,6 +595,71 @@ struct PathsPipelineState { 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(); + 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; + } + unsafe { + let mut dest = std::mem::zeroed(); + device_context.Map(&self.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(&self.buffer, 0); + } + Ok(()) + } +} + impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { let (vertex, vertex_shader) = { @@ -933,44 +953,6 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } -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, - }) -} - fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { let mut shader = None; @@ -1068,21 +1050,6 @@ fn pre_draw( 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; - } - let buffer_size = data_size.next_power_of_two(); - 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_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, @@ -1128,15 +1095,6 @@ fn update_indirect_buffer_capacity( Some((buffer, buffer_size)) } -fn update_pipeline( - pipeline: &mut PipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; -} - fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1209,9 +1167,9 @@ fn draw_indirect( } } -fn draw_normal( +fn draw_normal( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PipelineState, viewport: &[D3D11_VIEWPORT], global_params: &[Option], topology: D3D_PRIMITIVE_TOPOLOGY, @@ -1233,9 +1191,9 @@ fn draw_normal( Ok(()) } -fn draw_with_texture( +fn draw_with_texture( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PipelineState, texture: &[Option], viewport: &[D3D11_VIEWPORT], global_params: &[Option], From 1baafae3f7b4b3e175b9e52cff778b698902d0f9 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 14:14:31 +0800 Subject: [PATCH 037/202] refactor --- .../src/platform/windows/directx_renderer.rs | 121 ++++++++---------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index dd5e1a5b32..273d5a1a2b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -230,13 +230,10 @@ impl DirectXRenderer { &self.devices.device_context, shadows, )?; - draw_normal( + self.pipelines.shadow_pipeline.draw( &self.devices.device_context, - &self.pipelines.shadow_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, shadows.len() as u32, ) } @@ -250,13 +247,10 @@ impl DirectXRenderer { &self.devices.device_context, quads, )?; - draw_normal( + self.pipelines.quad_pipeline.draw( &self.devices.device_context, - &self.pipelines.quad_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, quads.len() as u32, ) } @@ -355,13 +349,10 @@ impl DirectXRenderer { &self.devices.device_context, underlines, )?; - draw_normal( + self.pipelines.underline_pipeline.draw( &self.devices.device_context, - &self.pipelines.underline_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, underlines.len() as u32, ) } @@ -380,9 +371,8 @@ impl DirectXRenderer { sprites, )?; let texture_view = self.atlas.get_texture_view(texture_id); - draw_with_texture( + self.pipelines.mono_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.mono_sprites, &texture_view, &self.context.viewport, &self.globals.global_params_buffer, @@ -405,9 +395,8 @@ impl DirectXRenderer { sprites, )?; let texture_view = self.atlas.get_texture_view(texture_id); - draw_with_texture( + self.pipelines.poly_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.poly_sprites, &texture_view, &self.context.viewport, &self.globals.global_params_buffer, @@ -658,6 +647,55 @@ impl PipelineState { } Ok(()) } + + fn draw( + &self, + device_context: &ID3D11DeviceContext, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + instance_count: u32, + ) -> Result<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.view)); + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + + 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<()> { + unsafe { + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.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(()) + } } impl PathsPipelineState { @@ -1167,57 +1205,6 @@ fn draw_indirect( } } -fn draw_normal( - device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, - viewport: &[D3D11_VIEWPORT], - 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.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)); - - 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; mod shader_resources { From 5ed8b13e4aca23e9bc410935b6ed75cd00f9620b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 15:36:24 +0800 Subject: [PATCH 038/202] refactor --- .../src/platform/windows/directx_renderer.rs | 285 +++++++----------- 1 file changed, 112 insertions(+), 173 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 273d5a1a2b..623383946a 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -285,59 +285,19 @@ impl DirectXRenderer { }); } - update_paths_buffer_capacity( - &self.pipelines.paths_pipeline, - sprites.len(), + self.pipelines.paths_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, &sprites, - )?; - update_paths_vertex_capacity( - &mut self.pipelines.paths_pipeline, - vertices.len(), - &self.devices.device, - ) - .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - self.pipelines - .paths_pipeline - .vertex_buffer - .as_ref() - .unwrap(), &vertices, - )?; - update_indirect_buffer_capacity( - &self.pipelines.paths_pipeline, - draw_indirect_commands.len(), - &self.devices.device, - ) - .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - &self.pipelines.paths_pipeline.indirect_draw_buffer, &draw_indirect_commands, )?; - prepare_indirect_draws( + self.pipelines.paths_pipeline.draw( &self.devices.device_context, - &self.pipelines.paths_pipeline, + paths.len(), &self.context.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_pipeline.indirect_draw_buffer, - (i * std::mem::size_of::()) as u32, - ); - } - Ok(()) + ) } fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> { @@ -634,18 +594,19 @@ impl PipelineState { ) -> 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; } - unsafe { - let mut dest = std::mem::zeroed(); - device_context.Map(&self.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(&self.buffer, 0); - } - Ok(()) + update_buffer(device_context, &self.buffer, data) } fn draw( @@ -784,6 +745,101 @@ impl PathsPipelineState { view, }) } + + fn update_buffer( + &mut self, + device: &ID3D11Device, + device_context: &ID3D11DeviceContext, + buffer_data: &[PathSprite], + vertices_data: &[PathVertex], + 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<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.view)); + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + 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(()) + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1048,21 +1104,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res 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], @@ -1071,13 +1112,14 @@ fn pre_draw( clear_color: [f32; 4], blend_state: &ID3D11BlendState, ) -> Result<()> { - update_global_params( + let global_params = global_params_buffer[0].as_ref().unwrap(); + update_buffer( device_context, - global_params_buffer, - GlobalParams { + global_params, + &[GlobalParams { viewport_size: [view_port[0].Width, view_port[0].Height], ..Default::default() - }, + }], )?; unsafe { device_context.RSSetViewports(Some(view_port)); @@ -1088,70 +1130,6 @@ fn pre_draw( Ok(()) } -fn update_paths_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { - if pipeline.buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); - let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, view)) -} - -fn update_paths_vertex_capacity( - pipeline: &PathsPipelineState, - vertex_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.vertex_buffer_size >= vertex_size { - return None; - } - let vertex_size = vertex_size.next_power_of_two(); - let buffer = create_buffer( - device, - std::mem::size_of::>(), - vertex_size, - ) - .unwrap(); - Some((buffer, vertex_size)) -} - -fn update_indirect_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.indirect_buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size).unwrap(); - Some((buffer, buffer_size)) -} - -fn update_paths_pipeline_buffer( - pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; -} - -fn update_paths_pipeline_vertex(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.vertex_buffer = Some(input.0); - pipeline.vertex_buffer_size = input.1; -} - -fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.indirect_draw_buffer = input.0; - pipeline.indirect_buffer_size = input.1; -} - fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1166,45 +1144,6 @@ fn update_buffer( Ok(()) } -fn prepare_indirect_draws( - device_context: &ID3D11DeviceContext, - pipeline: &PathsPipelineState, - viewport: &[D3D11_VIEWPORT], - global_params: &[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)); - const STRIDE: u32 = std::mem::size_of::>() as u32; - device_context.IASetVertexBuffers( - 0, - 1, - Some(&pipeline.vertex_buffer), - Some(&STRIDE), - Some(&0), - ); - device_context.IASetInputLayout(&pipeline.input_layout); - } - Ok(()) -} - -fn draw_indirect( - device_context: &ID3D11DeviceContext, - indirect_draw_buffer: &ID3D11Buffer, - offset: u32, -) { - unsafe { - device_context.DrawInstancedIndirect(indirect_draw_buffer, offset); - } -} - const BUFFER_COUNT: usize = 3; mod shader_resources { From ffbb47452d85eb40cf9556242f35d3b12a56a43d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 15:48:55 +0800 Subject: [PATCH 039/202] introduce `set_pipeline_state` --- .../src/platform/windows/directx_renderer.rs | 86 +++++++++++++------ 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 623383946a..ee38632f9b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -616,16 +616,16 @@ impl PipelineState { 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.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - device_context.DrawInstanced(4, instance_count, 0, 0); } Ok(()) @@ -640,15 +640,16 @@ impl PipelineState { 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.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - device_context.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); device_context.PSSetSamplers(0, Some(sampler)); device_context.VSSetShaderResources(0, Some(texture)); device_context.PSSetShaderResources(0, Some(texture)); @@ -811,15 +812,16 @@ impl PathsPipelineState { 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 { - device_context.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); const STRIDE: u32 = std::mem::size_of::>() as u32; device_context.IASetVertexBuffers( 0, @@ -849,6 +851,7 @@ struct PathSprite { color: Background, } +#[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -970,6 +973,7 @@ fn create_swap_chain_default( Ok(swap_chain) } +#[inline] fn set_render_target_view( swap_chain: &IDXGISwapChain1, device: &ID3D11Device, @@ -987,6 +991,7 @@ fn set_render_target_view( Ok(back_buffer) } +#[inline] fn set_viewport( device_context: &ID3D11DeviceContext, width: f32, @@ -1004,6 +1009,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, @@ -1028,6 +1034,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. @@ -1047,6 +1054,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } +#[inline] fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { let mut shader = None; @@ -1055,6 +1063,7 @@ fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { unsafe { let mut shader = None; @@ -1063,6 +1072,7 @@ fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { let desc = D3D11_BUFFER_DESC { ByteWidth: (std::mem::size_of::() * buffer_size) as u32, @@ -1104,6 +1116,7 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res Ok(buffer.unwrap()) } +#[inline] fn pre_draw( device_context: &ID3D11DeviceContext, global_params_buffer: &[Option; 1], @@ -1130,6 +1143,7 @@ fn pre_draw( Ok(()) } +#[inline] fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1144,6 +1158,28 @@ fn update_buffer( Ok(()) } +#[inline] +fn set_pipeline_state( + device_context: &ID3D11DeviceContext, + buffer_view: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, + viewport: &[D3D11_VIEWPORT], + vertex_shader: &ID3D11VertexShader, + fragment_shader: &ID3D11PixelShader, + global_params: &[Option], +) { + unsafe { + 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(vertex_shader, None); + device_context.PSSetShader(fragment_shader, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + } +} + const BUFFER_COUNT: usize = 3; mod shader_resources { From 46fc76fdf8092225853bd8c5a832eee51fa8f017 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 17:14:24 +0800 Subject: [PATCH 040/202] reenable transparency --- .../src/platform/windows/directx_renderer.rs | 50 +++++++++++++++---- crates/gpui/src/platform/windows/window.rs | 48 +++++++++--------- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ee38632f9b..428464876a 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; @@ -37,7 +37,7 @@ pub(crate) struct DirectXDevices { } struct DirectXContext { - swap_chain: IDXGISwapChain1, + swap_chain: ManuallyDrop, back_buffer: [Option; 1], viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] @@ -212,13 +212,35 @@ impl DirectXRenderer { &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(()) + let transparent = background_appearance != WindowBackgroundAppearance::Opaque; + if self.transparent == transparent { + return Ok(()); } + self.transparent = transparent; + // unsafe { + // // recreate the swapchain + // self.devices.device_context.OMSetRenderTargets(None, None); + // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.swap_chain); + // self.context.swap_chain = create_swap_chain_default( + // &self.devices.dxgi_factory, + // &self.devices.device, + // self.hwnd, + // transparent, + // )?; + // self.context.back_buffer = [Some(set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?)]; + // self.context.viewport = set_viewport( + // &self.devices.device_context, + // self.context.viewport[0].Width, + // self.context.viewport[0].Height, + // ); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // } + Ok(()) } fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { @@ -851,6 +873,14 @@ struct PathSprite { color: Background, } +impl Drop for DirectXContext { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self.swap_chain); + } + } +} + #[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] @@ -948,7 +978,7 @@ fn create_swap_chain_default( device: &ID3D11Device, hwnd: HWND, _transparent: bool, -) -> Result { +) -> Result> { use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; let desc = DXGI_SWAP_CHAIN_DESC1 { @@ -970,7 +1000,7 @@ fn create_swap_chain_default( let swap_chain = unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; - Ok(swap_chain) + Ok(ManuallyDrop::new(swap_chain)) } #[inline] diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 5d1c91b5e3..74001c84e9 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,8 +384,7 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - // WS_EX_APPWINDOW | WS_EX_LAYERED, - WS_EX_APPWINDOW, + WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -403,7 +402,7 @@ impl WindowsWindow { handle, hide_title_bar, display, - transparent: true, + transparent: false, is_movable: params.is_movable, min_size: params.window_min_size, executor, @@ -462,7 +461,7 @@ impl WindowsWindow { // 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)? }; + unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } @@ -707,27 +706,28 @@ 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 mut window_state = self.0.state.borrow_mut(); + window_state + .renderer + .update_transparency(background_appearance) + .context("Updating window transparency") + .log_err(); - // match background_appearance { - // WindowBackgroundAppearance::Opaque => { - // // ACCENT_DISABLED - // set_window_composition_attribute(window_state.hwnd, None, 0); - // } - // WindowBackgroundAppearance::Transparent => { - // // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background - // set_window_composition_attribute(window_state.hwnd, None, 2); - // } - // WindowBackgroundAppearance::Blurred => { - // // Enable acrylic blur - // // ACCENT_ENABLE_ACRYLICBLURBEHIND - // set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); - // } - // } + match background_appearance { + WindowBackgroundAppearance::Opaque => { + // ACCENT_DISABLED + set_window_composition_attribute(window_state.hwnd, None, 0); + } + WindowBackgroundAppearance::Transparent => { + // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background + set_window_composition_attribute(window_state.hwnd, None, 2); + } + WindowBackgroundAppearance::Blurred => { + // Enable acrylic blur + // ACCENT_ENABLE_ACRYLICBLURBEHIND + set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); + } + } } fn minimize(&self) { From 4b2ff5e251b04484e68dd712fccdbd51006487de Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 19:56:56 +0800 Subject: [PATCH 041/202] update default buffer size --- crates/gpui/src/platform/windows/directx_renderer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 428464876a..27d00eeb63 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -431,31 +431,31 @@ impl DirectXRenderPipelines { "shadow_pipeline", "shadow_vertex", "shadow_fragment", - 32, + 4, )?; let quad_pipeline = - PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?; + 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", - 32, + 4, )?; let mono_sprites = PipelineState::new( device, "monochrome_sprite_pipeline", "monochrome_sprite_vertex", "monochrome_sprite_fragment", - 32, + 512, )?; let poly_sprites = PipelineState::new( device, "polychrome_sprite_pipeline", "polychrome_sprite_vertex", "polychrome_sprite_fragment", - 32, + 16, )?; Ok(Self { From da3736bd5f8344467c49f61d024bebfe81cae4e6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 21:26:11 +0800 Subject: [PATCH 042/202] add msaa --- .../src/platform/windows/directx_renderer.rs | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 27d00eeb63..410da3162d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -18,6 +18,9 @@ use windows::{ use crate::*; +const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + pub(crate) struct DirectXRenderer { atlas: Arc, devices: DirectXDevices, @@ -39,6 +42,8 @@ pub(crate) struct DirectXDevices { struct DirectXContext { swap_chain: ManuallyDrop, back_buffer: [Option; 1], + msaa_target: ID3D11Texture2D, + msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] // direct_composition: DirectComposition, @@ -164,7 +169,7 @@ impl DirectXRenderer { BUFFER_COUNT as u32, new_size.width.0 as u32, new_size.height.0 as u32, - DXGI_FORMAT_B8G8R8A8_UNORM, + RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), )?; } @@ -174,6 +179,15 @@ impl DirectXRenderer { &self.devices.device_context, )?; self.context.back_buffer[0] = Some(backbuffer); + + let (msaa_target, msaa_view) = create_msaa_target_and_its_view( + &self.devices.device, + new_size.width.0 as u32, + new_size.height.0 as u32, + )?; + self.context.msaa_target = msaa_target; + self.context.msaa_view = msaa_view; + self.context.viewport = set_viewport( &self.devices.device_context, new_size.width.0 as f32, @@ -411,12 +425,15 @@ impl DirectXContext { &devices.device, &devices.device_context, )?)]; + let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; let viewport = set_viewport(&devices.device_context, 1.0, 1.0); set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { swap_chain, back_buffer, + msaa_target, + msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] // direct_composition, @@ -984,7 +1001,7 @@ fn create_swap_chain_default( let desc = DXGI_SWAP_CHAIN_DESC1 { Width: 1, Height: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, + Format: RENDER_TARGET_FORMAT, Stereo: false.into(), SampleDesc: DXGI_SAMPLE_DESC { Count: 1, @@ -1013,14 +1030,57 @@ fn set_render_target_view( // 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 desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, + ..Default::default() + }; let mut buffer: Option = None; - device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?; + device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?; buffer.unwrap() }; unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; Ok(back_buffer) } +fn create_msaa_target_and_its_view( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> { + let msaa_target = unsafe { + let mut output = None; + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: BACK_BUFFER_FORMAT, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 4, + 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() + }; + let msaa_view = unsafe { + let desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS, + ..Default::default() + }; + let mut output = None; + device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?; + output.unwrap() + }; + Ok((msaa_target, msaa_view)) +} + #[inline] fn set_viewport( device_context: &ID3D11DeviceContext, From 55edee58fb939608043eebe70b55854290bc84e2 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 22:20:38 +0800 Subject: [PATCH 043/202] checkpoint msaa --- .../src/platform/windows/directx_renderer.rs | 161 ++++++++++++++---- 1 file changed, 129 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 410da3162d..74c1c68286 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -18,7 +18,7 @@ use windows::{ use crate::*; -const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; pub(crate) struct DirectXRenderer { @@ -41,7 +41,8 @@ pub(crate) struct DirectXDevices { struct DirectXContext { swap_chain: ManuallyDrop, - back_buffer: [Option; 1], + render_target: ManuallyDrop, + render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], @@ -124,15 +125,48 @@ 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.context.viewport[0].Width, + self.context.viewport[0].Height, + ], + ..Default::default() + }], )?; + unsafe { + self.devices + .device_context + .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]); + self.devices + .device_context + .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None); + self.devices + .device_context + .RSSetViewports(Some(&self.context.viewport)); + self.devices.device_context.OMSetBlendState( + &self.globals.blend_state, + None, + 0xFFFFFFFF, + ); + } + Ok(()) + } + + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + // pre_draw( + // &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, + // )?; + println!("Pre-draw: {:?}", self.context.render_target_view); + self.pre_draw()?; for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -157,28 +191,56 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?; + unsafe { + self.devices.device_context.ResolveSubresource( + &*self.context.render_target, + 0, + &self.context.msaa_target, + 0, + BACK_BUFFER_FORMAT, + ); + self.devices + .device_context + .OMSetRenderTargets(Some(&self.context.render_target_view), None); + self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + } Ok(()) } 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()); + println!("Resize: {:?}", self.context.render_target_view); unsafe { - self.context.swap_chain.ResizeBuffers( - BUFFER_COUNT as u32, - new_size.width.0 as u32, - new_size.height.0 as u32, - RENDER_TARGET_FORMAT, - DXGI_SWAP_CHAIN_FLAG(0), - )?; + self.devices.device_context.OMSetRenderTargets(None, None); + ManuallyDrop::drop(&mut self.context.render_target); + } + drop(self.context.render_target_view[0].take().unwrap()); + unsafe { + self.context + .swap_chain + .ResizeBuffers( + BUFFER_COUNT as u32, + new_size.width.0 as u32, + new_size.height.0 as u32, + RENDER_TARGET_FORMAT, + DXGI_SWAP_CHAIN_FLAG(0), + ) + .unwrap(); + } + // let backbuffer = set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?; + let (render_target, render_target_view) = + create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + .unwrap(); + self.context.render_target = render_target; + self.context.render_target_view = render_target_view; + unsafe { + self.devices + .device_context + .OMSetRenderTargets(Some(&self.context.render_target_view), None); } - let backbuffer = set_render_target_view( - &self.context.swap_chain, - &self.devices.device, - &self.devices.device_context, - )?; - self.context.back_buffer[0] = Some(backbuffer); let (msaa_target, msaa_view) = create_msaa_target_and_its_view( &self.devices.device, @@ -420,18 +482,26 @@ impl DirectXContext { // 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 (render_target, render_target_view) = + create_render_target_and_its_view(&swap_chain, &devices.device)?; + // let back_buffer = [Some(set_render_target_view( + // &swap_chain, + // &devices.device, + // &devices.device_context, + // )?)]; let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; let viewport = set_viewport(&devices.device_context, 1.0, 1.0); + unsafe { + devices + .device_context + .OMSetRenderTargets(Some(&render_target_view), None); + } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { swap_chain, - back_buffer, + render_target, + render_target_view, msaa_target, msaa_view, viewport, @@ -893,6 +963,7 @@ struct PathSprite { impl Drop for DirectXContext { fn drop(&mut self) { unsafe { + ManuallyDrop::drop(&mut self.render_target); ManuallyDrop::drop(&mut self.swap_chain); } } @@ -1020,6 +1091,30 @@ fn create_swap_chain_default( Ok(ManuallyDrop::new(swap_chain)) } +#[inline] +fn create_render_target_and_its_view( + swap_chain: &IDXGISwapChain1, + device: &ID3D11Device, +) -> Result<( + ManuallyDrop, + [Option; 1], +)> { + let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; + let desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, + ..Default::default() + }; + let mut render_target_view = None; + unsafe { + device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))? + }; + Ok(( + ManuallyDrop::new(render_target), + [Some(render_target_view.unwrap())], + )) +} + #[inline] fn set_render_target_view( swap_chain: &IDXGISwapChain1, @@ -1043,6 +1138,7 @@ fn set_render_target_view( Ok(back_buffer) } +#[inline] fn create_msaa_target_and_its_view( device: &ID3D11Device, width: u32, @@ -1111,7 +1207,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 { From 398d492f85b27302ff5b39b6d13dbacba511cc80 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 22:58:51 +0800 Subject: [PATCH 044/202] wip --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- crates/gpui/src/platform/windows/shaders.hlsl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 74c1c68286..47f6f3945d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -19,7 +19,8 @@ use windows::{ use crate::*; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; -const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; +// const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; +const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; pub(crate) struct DirectXRenderer { atlas: Arc, @@ -1200,7 +1201,6 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, - // FrontCounterClockwise: true.into(), FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 7e59bb714e..a67e2cde60 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -342,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 From b0fe5fd56fa1ed29d2f5d1b0b6ff0e0ab69fa748 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 23:55:32 +0800 Subject: [PATCH 045/202] wip --- .../src/platform/windows/directx_renderer.rs | 31 ++++++++++++++----- crates/gpui/src/platform/windows/shaders.hlsl | 11 ++++--- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 47f6f3945d..53b22a05e3 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -371,11 +371,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 { @@ -796,7 +795,7 @@ impl PathsPipelineState { let view = create_buffer_view(device, &buffer)?; let vertex_buffer = Some(create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), 32, )?); let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; @@ -836,6 +835,15 @@ impl PathsPipelineState { 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), @@ -862,7 +870,7 @@ impl PathsPipelineState { device: &ID3D11Device, device_context: &ID3D11DeviceContext, buffer_data: &[PathSprite], - vertices_data: &[PathVertex], + vertices_data: &[DirectXPathVertex], draw_commands: &[DrawInstancedIndirectArgs], ) -> Result<()> { if self.buffer_size < buffer_data.len() { @@ -888,7 +896,7 @@ impl PathsPipelineState { ); let vertex_buffer = create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), new_vertex_buffer_size, )?; self.vertex_buffer = Some(vertex_buffer); @@ -932,7 +940,7 @@ impl PathsPipelineState { global_params, ); unsafe { - const STRIDE: u32 = std::mem::size_of::>() as u32; + const STRIDE: u32 = std::mem::size_of::() as u32; device_context.IASetVertexBuffers( 0, 1, @@ -954,6 +962,13 @@ impl PathsPipelineState { } } +#[repr(C)] +struct DirectXPathVertex { + xy_position: Point, + content_mask: Bounds, + sprite_index: u32, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index a67e2cde60..f0c773a673 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -879,6 +879,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct PathVertex { float2 xy_position: POSITION; Bounds content_mask: TEXCOORD; + uint idx: GLOBALIDX; }; struct PathSprite { @@ -905,13 +906,13 @@ struct PathFragmentInput { StructuredBuffer path_sprites: register(t1); -PathVertexOutput paths_vertex(PathVertex v, uint instance_id: SV_InstanceID) { - 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, From b1b5a383e0fb154a80a59bf6782631a743064fcc Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 00:05:46 +0800 Subject: [PATCH 046/202] remove unused --- .../src/platform/windows/directx_renderer.rs | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 53b22a05e3..99126186c7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -19,8 +19,6 @@ use windows::{ use crate::*; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; -// const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; -const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; pub(crate) struct DirectXRenderer { atlas: Arc, @@ -158,15 +156,6 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - // pre_draw( - // &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, - // )?; - println!("Pre-draw: {:?}", self.context.render_target_view); self.pre_draw()?; for batch in scene.batches() { match batch { @@ -198,7 +187,7 @@ impl DirectXRenderer { 0, &self.context.msaa_target, 0, - BACK_BUFFER_FORMAT, + RENDER_TARGET_FORMAT, ); self.devices .device_context @@ -209,7 +198,6 @@ impl DirectXRenderer { } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { - println!("Resize: {:?}", self.context.render_target_view); unsafe { self.devices.device_context.OMSetRenderTargets(None, None); ManuallyDrop::drop(&mut self.context.render_target); @@ -1116,15 +1104,8 @@ fn create_render_target_and_its_view( [Option; 1], )> { let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - ..Default::default() - }; let mut render_target_view = None; - unsafe { - device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))? - }; + unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? }; Ok(( ManuallyDrop::new(render_target), [Some(render_target_view.unwrap())], @@ -1141,13 +1122,8 @@ fn set_render_target_view( // 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 desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - ..Default::default() - }; let mut buffer: Option = None; - device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?; + device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?; buffer.unwrap() }; unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; @@ -1167,7 +1143,7 @@ fn create_msaa_target_and_its_view( Height: height, MipLevels: 1, ArraySize: 1, - Format: BACK_BUFFER_FORMAT, + Format: RENDER_TARGET_FORMAT, SampleDesc: DXGI_SAMPLE_DESC { Count: 4, Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, @@ -1181,13 +1157,8 @@ fn create_msaa_target_and_its_view( output.unwrap() }; let msaa_view = unsafe { - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS, - ..Default::default() - }; let mut output = None; - device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?; + device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; output.unwrap() }; Ok((msaa_target, msaa_view)) From a486bb28f66fce27792da002214daa045cb878ee Mon Sep 17 00:00:00 2001 From: Kate Date: Wed, 16 Jul 2025 23:22:08 +0200 Subject: [PATCH 047/202] initial color emoji implementation, currently only monochrome, still figuring out why it doesn't render even though it rasterizes to the bitmap correctly --- .../gpui/src/platform/windows/direct_write.rs | 257 ++++++++++++++---- 1 file changed, 203 insertions(+), 54 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index bd7bacd4fc..cbe4f50ba7 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -805,75 +805,224 @@ impl DirectWriteState { let mut bitmap_data; if params.is_emoji { - bitmap_data = - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4]; + // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering + let color_enumerator = unsafe { + self.components.factory.TranslateColorGlyphRun( + Vector2::new(baseline_origin_x, baseline_origin_y), + &glyph_run, + None, + DWRITE_GLYPH_IMAGE_FORMATS_COLR + | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, + measuring_mode, + Some(&transform), + 0, + ) + }; - let mut rgba_data = vec![0u8; (texture_width * texture_height * 4) as usize]; - unsafe { - glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; - } + // if let Ok(color_enumerator) = color_enumerator { + // loop { + // let color_run = unsafe { color_enumerator.GetCurrentRun() }; + // if let Ok(color_run) = color_run { + // let color_glyph_run = unsafe { &*color_run }; + // let color_value = color_glyph_run.Base.runColor; - // Copy texture data into bitmap at correct position - let offset_x = texture_bounds.left.max(0) as usize; - let offset_y = texture_bounds.top.max(0) as usize; - for y in 0..texture_height as usize { - for x in 0..texture_width as usize { - let bitmap_x = offset_x + x; - let bitmap_y = offset_y + y; + // // Create analysis for this color layer + // let color_analysis = unsafe { + // self.components.factory.CreateGlyphRunAnalysis( + // &color_glyph_run.Base.glyphRun as *const _, + // Some(&transform), + // rendering_mode, + // measuring_mode, + // DWRITE_GRID_FIT_MODE_DEFAULT, + // DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + // baseline_origin_x, + // baseline_origin_y, + // ) + // }; - if bitmap_x < bitmap_size.width.0 as usize - && bitmap_y < bitmap_size.height.0 as usize - { - let texture_idx = (y * texture_width as usize + x) * 4; - let bitmap_idx = (bitmap_y * bitmap_size.width.0 as usize + bitmap_x) * 4; + // // todo: move this block completely to the gpu + // // this is important because fonts can bundle quite large icons + // // and compositing them on the cpu is quite expensive + // // also the code is ugly + // if let Ok(color_analysis) = color_analysis { + // let color_bounds = + // unsafe { color_analysis.GetAlphaTextureBounds(texture_type) }; + // if let Ok(color_bounds) = color_bounds { + // let color_width = (color_bounds.right - color_bounds.left) as u32; + // let color_height = (color_bounds.bottom - color_bounds.top) as u32; - if texture_idx + 3 < rgba_data.len() && bitmap_idx + 3 < bitmap_data.len() { - bitmap_data[bitmap_idx..bitmap_idx + 4] - .copy_from_slice(&rgba_data[texture_idx..texture_idx + 4]); - } - } - } - } + // if color_width > 0 && color_height > 0 { + // let mut alpha_data = + // vec![0u8; (color_width * color_height * 3) as usize]; + // if unsafe { + // color_analysis.CreateAlphaTexture( + // texture_type, + // &color_bounds, + // &mut alpha_data, + // ) + // } + // .is_ok() + // { + // let r = (color_value.r * 255.0) as u8; + // let g = (color_value.g * 255.0) as u8; + // let b = (color_value.b * 255.0) as u8; + // let a = (color_value.a * 255.0) as u8; + + // let offset_x = color_bounds.left.max(0) as usize; + // let offset_y = color_bounds.top.max(0) as usize; + + // for y in 0..color_height as usize { + // for x in 0..color_width as usize { + // let bitmap_x = offset_x + x; + // let bitmap_y = offset_y + y; + + // if bitmap_x < bitmap_size.width.0 as usize + // && bitmap_y < bitmap_size.height.0 as usize + // { + // let alpha_idx = + // (y * color_width as usize + x) * 3; + // let bitmap_idx = (bitmap_y + // * bitmap_size.width.0 as usize + // + bitmap_x) + // * 4; + + // if alpha_idx + 2 < alpha_data.len() + // && bitmap_idx + 3 < bitmap_data.len() + // { + // let alpha_value = (alpha_data[alpha_idx] + // as u32 + // + alpha_data[alpha_idx + 1] as u32 + // + alpha_data[alpha_idx + 2] as u32) + // / 3; + // let final_alpha = + // ((alpha_value * a as u32) / 255) as u8; + + // if final_alpha > 0 { + // let existing_r = + // bitmap_data[bitmap_idx]; + // let existing_g = + // bitmap_data[bitmap_idx + 1]; + // let existing_b = + // bitmap_data[bitmap_idx + 2]; + // let existing_a = + // bitmap_data[bitmap_idx + 3]; + + // let src_alpha = + // final_alpha as f32 / 255.0; + // let dst_alpha = + // existing_a as f32 / 255.0; + // let out_alpha = src_alpha + // + dst_alpha * (1.0 - src_alpha); + + // if out_alpha > 0.0 { + // bitmap_data[bitmap_idx] = + // ((r as f32 * src_alpha + // + existing_r as f32 + // * dst_alpha + // * (1.0 - src_alpha)) + // / out_alpha) + // as u8; + // bitmap_data[bitmap_idx + 1] = + // ((g as f32 * src_alpha + // + existing_g as f32 + // * dst_alpha + // * (1.0 - src_alpha)) + // / out_alpha) + // as u8; + // bitmap_data[bitmap_idx + 2] = + // ((b as f32 * src_alpha + // + existing_b as f32 + // * dst_alpha + // * (1.0 - src_alpha)) + // / out_alpha) + // as u8; + // bitmap_data[bitmap_idx + 3] = + // (out_alpha * 255.0) as u8; + // } + // } + // } + // } + // } + // } + // } + // } + // } + // } + // } + + // if !unsafe { color_enumerator.MoveNext() }?.as_bool() { + // break; + // } + // } + // } else { + // } + let monochrome_data = Self::rasterize_monochrome( + &glyph_analysis, + bitmap_size, + size(texture_width, texture_height), + &texture_bounds, + )?; + bitmap_data = monochrome_data + .into_iter() + .flat_map(|e| [e, e, e, 255]) + .collect::>(); } else { - bitmap_data = vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; + bitmap_data = Self::rasterize_monochrome( + &glyph_analysis, + bitmap_size, + size(texture_width, texture_height), + &texture_bounds, + )?; + } - let mut alpha_data = vec![0u8; (texture_width * texture_height * 3) as usize]; - unsafe { - glyph_analysis.CreateAlphaTexture( - texture_type, - &texture_bounds, - &mut alpha_data, - )?; - } + Ok((bitmap_size, bitmap_data)) + } - // Convert ClearType RGB data to grayscale and place in bitmap - let offset_x = texture_bounds.left.max(0) as usize; - let offset_y = texture_bounds.top.max(0) as usize; + fn rasterize_monochrome( + glyph_analysis: &IDWriteGlyphRunAnalysis, + bitmap_size: Size, + texture_size: Size, + texture_bounds: &RECT, + ) -> Result> { + let mut bitmap_data = + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; - for y in 0..texture_height as usize { - for x in 0..texture_width as usize { - let bitmap_x = offset_x + x; - let bitmap_y = offset_y + y; + let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize]; + unsafe { + glyph_analysis.CreateAlphaTexture( + DWRITE_TEXTURE_CLEARTYPE_3x1, + texture_bounds, + &mut alpha_data, + )?; + } - if bitmap_x < bitmap_size.width.0 as usize - && bitmap_y < bitmap_size.height.0 as usize - { - let texture_idx = (y * texture_width as usize + x) * 3; - let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; + // Convert ClearType RGB data to grayscale and place in bitmap + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; - if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { - let avg = (alpha_data[texture_idx] as u32 - + alpha_data[texture_idx + 1] as u32 - + alpha_data[texture_idx + 2] as u32) - / 3; - bitmap_data[bitmap_idx] = avg as u8; - } + for y in 0..texture_size.height as usize { + for x in 0..texture_size.width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; + + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_size.width as usize + x) * 3; + let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; + + if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { + let avg = (alpha_data[texture_idx] as u32 + + alpha_data[texture_idx + 1] as u32 + + alpha_data[texture_idx + 2] as u32) + / 3; + bitmap_data[bitmap_idx] = avg as u8; } } } } - Ok((bitmap_size, bitmap_data)) + Ok(bitmap_data) } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From b8314e74db13e2ac6c9deaecf4a1717961fc71b2 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 11:29:44 +0800 Subject: [PATCH 048/202] add transparency --- Cargo.toml | 1 + .../src/platform/windows/directx_renderer.rs | 314 +++++++++--------- crates/gpui/src/platform/windows/window.rs | 5 +- 3 files changed, 166 insertions(+), 154 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bf1eb9c325..a57f29baa4 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/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 99126186c7..ed39f3bedf 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -3,7 +3,8 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; // #[cfg(not(feature = "enable-renderdoc"))] -// use windows::Win32::Graphics::DirectComposition::*; +use windows::Win32::Graphics::DirectComposition::*; +use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; use windows::{ Win32::{ Foundation::{HMODULE, HWND}, @@ -26,7 +27,6 @@ pub(crate) struct DirectXRenderer { context: DirectXContext, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - hwnd: HWND, transparent: bool, } @@ -46,7 +46,7 @@ struct DirectXContext { msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition: DirectComposition, + direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -73,11 +73,11 @@ struct DrawInstancedIndirectArgs { } // #[cfg(not(feature = "enable-renderdoc"))] -// struct DirectComposition { -// comp_device: IDCompositionDevice, -// comp_target: IDCompositionTarget, -// comp_visual: IDCompositionVisual, -// } +struct DirectComposition { + comp_device: IDCompositionDevice, + comp_target: IDCompositionTarget, + comp_visual: IDCompositionVisual, +} impl DirectXDevices { pub(crate) fn new() -> Result { @@ -115,7 +115,6 @@ impl DirectXRenderer { context, globals, pipelines, - hwnd, transparent, }) } @@ -215,11 +214,6 @@ impl DirectXRenderer { ) .unwrap(); } - // let backbuffer = set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?; let (render_target, render_target_view) = create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) .unwrap(); @@ -248,31 +242,6 @@ impl DirectXRenderer { } // #[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, @@ -281,33 +250,84 @@ impl DirectXRenderer { if self.transparent == transparent { return Ok(()); } - self.transparent = transparent; - // unsafe { - // // recreate the swapchain + // self.transparent = transparent; + // let (width, height) = unsafe { // self.devices.device_context.OMSetRenderTargets(None, None); - // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.render_target); + // drop(self.context.render_target_view[0].take().unwrap()); + // let desc = self.context.swap_chain.GetDesc1().unwrap(); // ManuallyDrop::drop(&mut self.context.swap_chain); - // self.context.swap_chain = create_swap_chain_default( - // &self.devices.dxgi_factory, - // &self.devices.device, - // self.hwnd, - // transparent, - // )?; - // self.context.back_buffer = [Some(set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?)]; - // self.context.viewport = set_viewport( - // &self.devices.device_context, - // self.context.viewport[0].Width, - // self.context.viewport[0].Height, - // ); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // (desc.Width, desc.Height) + // }; + // self.context.swap_chain = create_swap_chain( + // &self.devices.dxgi_factory, + // &self.devices.device, + // transparent, + // width, + // height, + // ) + // .unwrap(); + // self.context + // .direct_composition + // .set_swap_chain(&self.context.swap_chain) + // .context("Failed to set swap chain for DirectComposition")?; + // let (render_target, render_target_view) = + // create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + // .unwrap(); + // self.context.render_target = render_target; + // self.context.render_target_view = render_target_view; + // unsafe { + // self.devices + // .device_context + // .OMSetRenderTargets(Some(&self.context.render_target_view), None); // } + + // let (msaa_target, msaa_view) = + // create_msaa_target_and_its_view(&self.devices.device, width, height)?; + // self.context.msaa_target = msaa_target; + // self.context.msaa_view = msaa_view; + + // self.context.viewport = set_viewport(&self.devices.device_context, width as _, height as _); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; Ok(()) } + // #[cfg(feature = "enable-renderdoc")] + // pub(crate) fn update_transparency( + // &mut self, + // background_appearance: WindowBackgroundAppearance, + // ) -> Result<()> { + // let transparent = background_appearance != WindowBackgroundAppearance::Opaque; + // if self.transparent == transparent { + // return Ok(()); + // } + // self.transparent = transparent; + // unsafe { + // // recreate the swapchain + // self.devices.device_context.OMSetRenderTargets(None, None); + // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.swap_chain); + // self.context.swap_chain = create_swap_chain_default( + // &self.devices.dxgi_factory, + // &self.devices.device, + // self.hwnd, + // transparent, + // )?; + // self.context.back_buffer = [Some(set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?)]; + // self.context.viewport = set_viewport( + // &self.devices.device_context, + // self.context.viewport[0].Width, + // self.context.viewport[0].Height, + // ); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // } + // Ok(()) + // } + fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { if shadows.is_empty() { return Ok(()); @@ -461,24 +481,35 @@ impl DirectXRenderer { impl DirectXContext { pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + let (width, height) = unsafe { + let mut rect = std::mem::zeroed(); + GetWindowRect(hwnd, &mut rect)?; + (rect.right - rect.left, rect.bottom - rect.top) + }; + assert!( + width > 0 && height > 0, + "Window size must be greater than zero" + ); // #[cfg(not(feature = "enable-renderdoc"))] - // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?; + let swap_chain = create_swap_chain( + &devices.dxgi_factory, + &devices.device, + transparent, + width as u32, + height as u32, + )?; // #[cfg(feature = "enable-renderdoc")] - let swap_chain = - create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; + // 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)?; + let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition.set_swap_chain(&swap_chain)?; + direct_composition.set_swap_chain(&swap_chain)?; let (render_target, render_target_view) = create_render_target_and_its_view(&swap_chain, &devices.device)?; - // let back_buffer = [Some(set_render_target_view( - // &swap_chain, - // &devices.device, - // &devices.device_context, - // )?)]; - let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; - let viewport = set_viewport(&devices.device_context, 1.0, 1.0); + let (msaa_target, msaa_view) = + create_msaa_target_and_its_view(&devices.device, width as u32, height as u32)?; + let viewport = set_viewport(&devices.device_context, width as f32, height as f32); unsafe { devices .device_context @@ -494,7 +525,7 @@ impl DirectXContext { msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition, + direct_composition, }) } } @@ -545,28 +576,28 @@ 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() }?; +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, -// }) -// } + 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(()) -// } -// } + 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 { @@ -1030,39 +1061,45 @@ fn get_device( } // #[cfg(not(feature = "enable-renderdoc"))] -// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { -// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) -// } +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)? }) -// } +fn create_swap_chain( + dxgi_factory: &IDXGIFactory6, + device: &ID3D11Device, + transparent: bool, + width: u32, + height: u32, +) -> Result> { + println!("Creating swap chain for DirectComposition: {}", transparent); + let transparent = true; + let alpha_mode = if transparent { + DXGI_ALPHA_MODE_PREMULTIPLIED + } else { + DXGI_ALPHA_MODE_IGNORE + }; + 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: alpha_mode, + Flags: 0, + }; + Ok(ManuallyDrop::new(unsafe { + dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? + })) +} // #[cfg(feature = "enable-renderdoc")] fn create_swap_chain_default( @@ -1289,33 +1326,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res Ok(buffer.unwrap()) } -#[inline] -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<()> { - let global_params = global_params_buffer[0].as_ref().unwrap(); - update_buffer( - device_context, - global_params, - &[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(()) -} - #[inline] fn update_buffer( device_context: &ID3D11DeviceContext, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 74001c84e9..077ba82e92 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,7 +384,8 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - WS_EX_APPWINDOW | WS_EX_LAYERED, + WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP, + // WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -461,7 +462,7 @@ impl WindowsWindow { // 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)? }; + // unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } From 602bd189f6d5a10234cb79f5c18f4b7b23307537 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 14:36:41 +0800 Subject: [PATCH 049/202] finetune transpanrency --- .../src/platform/windows/directx_renderer.rs | 128 +----------------- crates/gpui/src/platform/windows/window.rs | 33 ++--- 2 files changed, 15 insertions(+), 146 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ed39f3bedf..ebd4c4730b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -27,7 +27,6 @@ pub(crate) struct DirectXRenderer { context: DirectXContext, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - transparent: bool, } #[derive(Clone)] @@ -46,7 +45,7 @@ struct DirectXContext { msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] - direct_composition: DirectComposition, + _direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -101,12 +100,12 @@ 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)?; + let context = DirectXContext::new(devices, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; Ok(DirectXRenderer { @@ -115,7 +114,6 @@ impl DirectXRenderer { context, globals, pipelines, - transparent, }) } @@ -241,93 +239,6 @@ impl DirectXRenderer { Ok(()) } - // #[cfg(not(feature = "enable-renderdoc"))] - pub(crate) fn update_transparency( - &mut self, - background_appearance: WindowBackgroundAppearance, - ) -> Result<()> { - let transparent = background_appearance != WindowBackgroundAppearance::Opaque; - if self.transparent == transparent { - return Ok(()); - } - // self.transparent = transparent; - // let (width, height) = unsafe { - // self.devices.device_context.OMSetRenderTargets(None, None); - // ManuallyDrop::drop(&mut self.context.render_target); - // drop(self.context.render_target_view[0].take().unwrap()); - // let desc = self.context.swap_chain.GetDesc1().unwrap(); - // ManuallyDrop::drop(&mut self.context.swap_chain); - // (desc.Width, desc.Height) - // }; - // self.context.swap_chain = create_swap_chain( - // &self.devices.dxgi_factory, - // &self.devices.device, - // transparent, - // width, - // height, - // ) - // .unwrap(); - // self.context - // .direct_composition - // .set_swap_chain(&self.context.swap_chain) - // .context("Failed to set swap chain for DirectComposition")?; - // let (render_target, render_target_view) = - // create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) - // .unwrap(); - // self.context.render_target = render_target; - // self.context.render_target_view = render_target_view; - // unsafe { - // self.devices - // .device_context - // .OMSetRenderTargets(Some(&self.context.render_target_view), None); - // } - - // let (msaa_target, msaa_view) = - // create_msaa_target_and_its_view(&self.devices.device, width, height)?; - // self.context.msaa_target = msaa_target; - // self.context.msaa_view = msaa_view; - - // self.context.viewport = set_viewport(&self.devices.device_context, width as _, height as _); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; - Ok(()) - } - - // #[cfg(feature = "enable-renderdoc")] - // pub(crate) fn update_transparency( - // &mut self, - // background_appearance: WindowBackgroundAppearance, - // ) -> Result<()> { - // let transparent = background_appearance != WindowBackgroundAppearance::Opaque; - // if self.transparent == transparent { - // return Ok(()); - // } - // self.transparent = transparent; - // unsafe { - // // recreate the swapchain - // self.devices.device_context.OMSetRenderTargets(None, None); - // drop(self.context.back_buffer[0].take().unwrap()); - // ManuallyDrop::drop(&mut self.context.swap_chain); - // self.context.swap_chain = create_swap_chain_default( - // &self.devices.dxgi_factory, - // &self.devices.device, - // self.hwnd, - // transparent, - // )?; - // self.context.back_buffer = [Some(set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?)]; - // self.context.viewport = set_viewport( - // &self.devices.device_context, - // self.context.viewport[0].Width, - // self.context.viewport[0].Height, - // ); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; - // } - // Ok(()) - // } - fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { if shadows.is_empty() { return Ok(()); @@ -480,7 +391,7 @@ impl DirectXRenderer { } impl DirectXContext { - pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { let (width, height) = unsafe { let mut rect = std::mem::zeroed(); GetWindowRect(hwnd, &mut rect)?; @@ -494,7 +405,6 @@ impl DirectXContext { let swap_chain = create_swap_chain( &devices.dxgi_factory, &devices.device, - transparent, width as u32, height as u32, )?; @@ -525,7 +435,7 @@ impl DirectXContext { msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] - direct_composition, + _direct_composition: direct_composition, }) } } @@ -1068,17 +978,9 @@ fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, - transparent: bool, width: u32, height: u32, ) -> Result> { - println!("Creating swap chain for DirectComposition: {}", transparent); - let transparent = true; - let alpha_mode = if transparent { - DXGI_ALPHA_MODE_PREMULTIPLIED - } else { - DXGI_ALPHA_MODE_IGNORE - }; let desc = DXGI_SWAP_CHAIN_DESC1 { Width: width, Height: height, @@ -1093,7 +995,7 @@ fn create_swap_chain( // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. Scaling: DXGI_SCALING_STRETCH, SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, - AlphaMode: alpha_mode, + AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, Flags: 0, }; Ok(ManuallyDrop::new(unsafe { @@ -1149,24 +1051,6 @@ fn create_render_target_and_its_view( )) } -#[inline] -fn set_render_target_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() - }; - unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; - Ok(back_buffer) -} - #[inline] fn create_msaa_target_and_its_view( device: &ID3D11Device, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 077ba82e92..e712d939c8 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -80,7 +80,6 @@ pub(crate) struct WindowsWindowStatePtr { impl WindowsWindowState { fn new( hwnd: HWND, - transparent: bool, cs: &CREATESTRUCTW, current_cursor: Option, display: WindowsDisplay, @@ -104,7 +103,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 +206,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 +333,6 @@ struct WindowCreateContext<'a> { handle: AnyWindowHandle, hide_title_bar: bool, display: WindowsDisplay, - transparent: bool, is_movable: bool, min_size: Option>, executor: ForegroundExecutor, @@ -381,11 +378,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_NOREDIRECTIONBITMAP, - // WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -403,7 +402,6 @@ impl WindowsWindow { handle, hide_title_bar, display, - transparent: false, is_movable: params.is_movable, min_size: params.window_min_size, executor, @@ -455,14 +453,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)) } @@ -707,26 +697,21 @@ 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) - .context("Updating window transparency") - .log_err(); + 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); } } } From 63727f99da1e922797d24a886086524e7c1a82fa Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 14:53:28 +0800 Subject: [PATCH 050/202] rename to `DirectXResources` --- .../src/platform/windows/directx_renderer.rs | 95 ++++++++++--------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ebd4c4730b..bc74ad7782 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -20,15 +20,18 @@ use windows::{ 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, } +/// Direct3D objects #[derive(Clone)] pub(crate) struct DirectXDevices { dxgi_factory: IDXGIFactory6, @@ -37,13 +40,17 @@ pub(crate) struct DirectXDevices { device_context: ID3D11DeviceContext, } -struct DirectXContext { +struct DirectXResources { + // Direct3D rendering objects swap_chain: ManuallyDrop, render_target: ManuallyDrop, render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, msaa_view: ID3D11RenderTargetView, + + // Cached viewport viewport: [D3D11_VIEWPORT; 1], + // #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: DirectComposition, } @@ -105,13 +112,13 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); - let context = DirectXContext::new(devices, hwnd)?; + let resources = DirectXResources::new(devices, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; Ok(DirectXRenderer { atlas, devices: devices.clone(), - context, + resources, globals, pipelines, }) @@ -127,8 +134,8 @@ impl DirectXRenderer { self.globals.global_params_buffer[0].as_ref().unwrap(), &[GlobalParams { viewport_size: [ - self.context.viewport[0].Width, - self.context.viewport[0].Height, + self.resources.viewport[0].Width, + self.resources.viewport[0].Height, ], ..Default::default() }], @@ -136,13 +143,13 @@ impl DirectXRenderer { unsafe { self.devices .device_context - .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]); + .ClearRenderTargetView(&self.resources.msaa_view, &[0.0; 4]); self.devices .device_context - .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None); + .OMSetRenderTargets(Some(&[Some(self.resources.msaa_view.clone())]), None); self.devices .device_context - .RSSetViewports(Some(&self.context.viewport)); + .RSSetViewports(Some(&self.resources.viewport)); self.devices.device_context.OMSetBlendState( &self.globals.blend_state, None, @@ -180,16 +187,16 @@ impl DirectXRenderer { } unsafe { self.devices.device_context.ResolveSubresource( - &*self.context.render_target, + &*self.resources.render_target, 0, - &self.context.msaa_target, + &self.resources.msaa_target, 0, RENDER_TARGET_FORMAT, ); self.devices .device_context - .OMSetRenderTargets(Some(&self.context.render_target_view), None); - self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); + self.resources.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; } Ok(()) } @@ -197,11 +204,11 @@ impl DirectXRenderer { pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { unsafe { self.devices.device_context.OMSetRenderTargets(None, None); - ManuallyDrop::drop(&mut self.context.render_target); + ManuallyDrop::drop(&mut self.resources.render_target); } - drop(self.context.render_target_view[0].take().unwrap()); + drop(self.resources.render_target_view[0].take().unwrap()); unsafe { - self.context + self.resources .swap_chain .ResizeBuffers( BUFFER_COUNT as u32, @@ -213,14 +220,14 @@ impl DirectXRenderer { .unwrap(); } let (render_target, render_target_view) = - create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + create_render_target_and_its_view(&self.resources.swap_chain, &self.devices.device) .unwrap(); - self.context.render_target = render_target; - self.context.render_target_view = render_target_view; + self.resources.render_target = render_target; + self.resources.render_target_view = render_target_view; unsafe { self.devices .device_context - .OMSetRenderTargets(Some(&self.context.render_target_view), None); + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); } let (msaa_target, msaa_view) = create_msaa_target_and_its_view( @@ -228,10 +235,10 @@ impl DirectXRenderer { new_size.width.0 as u32, new_size.height.0 as u32, )?; - self.context.msaa_target = msaa_target; - self.context.msaa_view = msaa_view; + self.resources.msaa_target = msaa_target; + self.resources.msaa_view = msaa_view; - self.context.viewport = set_viewport( + self.resources.viewport = set_viewport( &self.devices.device_context, new_size.width.0 as f32, new_size.height.0 as f32, @@ -250,7 +257,7 @@ impl DirectXRenderer { )?; self.pipelines.shadow_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, shadows.len() as u32, ) @@ -267,7 +274,7 @@ impl DirectXRenderer { )?; self.pipelines.quad_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, quads.len() as u32, ) @@ -312,7 +319,7 @@ impl DirectXRenderer { self.pipelines.paths_pipeline.draw( &self.devices.device_context, paths.len(), - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, ) } @@ -328,7 +335,7 @@ impl DirectXRenderer { )?; self.pipelines.underline_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, underlines.len() as u32, ) @@ -351,7 +358,7 @@ impl DirectXRenderer { self.pipelines.mono_sprites.draw_with_texture( &self.devices.device_context, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -375,7 +382,7 @@ impl DirectXRenderer { self.pipelines.poly_sprites.draw_with_texture( &self.devices.device_context, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -390,24 +397,17 @@ impl DirectXRenderer { } } -impl DirectXContext { +impl DirectXResources { pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { let (width, height) = unsafe { let mut rect = std::mem::zeroed(); GetWindowRect(hwnd, &mut rect)?; - (rect.right - rect.left, rect.bottom - rect.top) + let width = (rect.right - rect.left).max(1) as u32; + let height = (rect.bottom - rect.top).max(1) as u32; + (width, height) }; - assert!( - width > 0 && height > 0, - "Window size must be greater than zero" - ); // #[cfg(not(feature = "enable-renderdoc"))] - let swap_chain = create_swap_chain( - &devices.dxgi_factory, - &devices.device, - width as u32, - height as u32, - )?; + 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)?; @@ -418,7 +418,7 @@ impl DirectXContext { 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 as u32, height as u32)?; + create_msaa_target_and_its_view(&devices.device, width, height)?; let viewport = set_viewport(&devices.device_context, width as f32, height as f32); unsafe { devices @@ -905,7 +905,7 @@ struct PathSprite { color: Background, } -impl Drop for DirectXContext { +impl Drop for DirectXResources { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.render_target); @@ -1008,13 +1008,14 @@ fn create_swap_chain_default( 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, + Width: width, + Height: height, Format: RENDER_TARGET_FORMAT, Stereo: false.into(), SampleDesc: DXGI_SAMPLE_DESC { @@ -1066,7 +1067,7 @@ fn create_msaa_target_and_its_view( ArraySize: 1, Format: RENDER_TARGET_FORMAT, SampleDesc: DXGI_SAMPLE_DESC { - Count: 4, + Count: MULTISAMPLE_COUNT, Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, }, Usage: D3D11_USAGE_DEFAULT, From 1888f21a1455dc611f54dc8c4065a181ec43d56e Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 15:52:51 +0800 Subject: [PATCH 051/202] refactor --- .../src/platform/windows/directx_renderer.rs | 178 ++++++++++-------- 1 file changed, 99 insertions(+), 79 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index bc74ad7782..87c98512b9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -29,6 +29,8 @@ pub(crate) struct DirectXRenderer { resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, + // #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: DirectComposition, } /// Direct3D objects @@ -42,17 +44,14 @@ pub(crate) struct DirectXDevices { struct DirectXResources { // Direct3D rendering objects - swap_chain: ManuallyDrop, + swap_chain: IDXGISwapChain1, render_target: ManuallyDrop, render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, - msaa_view: ID3D11RenderTargetView, + msaa_view: [Option; 1], // Cached viewport viewport: [D3D11_VIEWPORT; 1], - - // #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -115,12 +114,18 @@ impl DirectXRenderer { 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(), resources, globals, pipelines, + // #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: direct_composition, }) } @@ -143,10 +148,10 @@ impl DirectXRenderer { unsafe { self.devices .device_context - .ClearRenderTargetView(&self.resources.msaa_view, &[0.0; 4]); + .ClearRenderTargetView(self.resources.msaa_view[0].as_ref().unwrap(), &[0.0; 4]); self.devices .device_context - .OMSetRenderTargets(Some(&[Some(self.resources.msaa_view.clone())]), None); + .OMSetRenderTargets(Some(&self.resources.msaa_view), None); self.devices .device_context .RSSetViewports(Some(&self.resources.viewport)); @@ -159,6 +164,23 @@ impl DirectXRenderer { 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() { @@ -185,64 +207,29 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - 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(()) + self.present() } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { unsafe { + let width = new_size.width.0 as u32; + let height = new_size.height.0 as u32; self.devices.device_context.OMSetRenderTargets(None, None); ManuallyDrop::drop(&mut self.resources.render_target); - } - drop(self.resources.render_target_view[0].take().unwrap()); - unsafe { + drop(self.resources.render_target_view[0].take().unwrap()); + self.resources.swap_chain.ResizeBuffers( + BUFFER_COUNT as u32, + width, + height, + RENDER_TARGET_FORMAT, + DXGI_SWAP_CHAIN_FLAG(0), + )?; self.resources - .swap_chain - .ResizeBuffers( - BUFFER_COUNT as u32, - new_size.width.0 as u32, - new_size.height.0 as u32, - RENDER_TARGET_FORMAT, - DXGI_SWAP_CHAIN_FLAG(0), - ) - .unwrap(); - } - let (render_target, render_target_view) = - create_render_target_and_its_view(&self.resources.swap_chain, &self.devices.device) - .unwrap(); - self.resources.render_target = render_target; - self.resources.render_target_view = render_target_view; - unsafe { + .recreate_resources(&self.devices, width, height)?; self.devices .device_context .OMSetRenderTargets(Some(&self.resources.render_target_view), None); } - - let (msaa_target, msaa_view) = create_msaa_target_and_its_view( - &self.devices.device, - new_size.width.0 as u32, - new_size.height.0 as u32, - )?; - self.resources.msaa_target = msaa_target; - self.resources.msaa_view = msaa_view; - - self.resources.viewport = set_viewport( - &self.devices.device_context, - new_size.width.0 as f32, - new_size.height.0 as f32, - ); Ok(()) } @@ -411,20 +398,13 @@ impl DirectXResources { // #[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 (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); - unsafe { - devices - .device_context - .OMSetRenderTargets(Some(&render_target_view), None); - } + let (render_target, render_target_view, msaa_target, msaa_view, viewport) = + create_resources(devices, &swap_chain, width, height)?; + // unsafe { + // devices + // .device_context + // .OMSetRenderTargets(Some(&render_target_view), None); + // } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { @@ -434,10 +414,25 @@ impl DirectXResources { msaa_target, msaa_view, viewport, - // #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: 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; + Ok(()) + } } impl DirectXRenderPipelines { @@ -909,7 +904,6 @@ impl Drop for DirectXResources { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.render_target); - ManuallyDrop::drop(&mut self.swap_chain); } } } @@ -961,6 +955,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, @@ -980,7 +976,7 @@ fn create_swap_chain( device: &ID3D11Device, width: u32, height: u32, -) -> Result> { +) -> Result { let desc = DXGI_SWAP_CHAIN_DESC1 { Width: width, Height: height, @@ -998,9 +994,7 @@ fn create_swap_chain( AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, Flags: 0, }; - Ok(ManuallyDrop::new(unsafe { - dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? - })) + Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } // #[cfg(feature = "enable-renderdoc")] @@ -1010,7 +1004,7 @@ fn create_swap_chain_default( hwnd: HWND, width: u32, height: u32, -) -> Result> { +) -> Result { use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; let desc = DXGI_SWAP_CHAIN_DESC1 { @@ -1032,7 +1026,33 @@ fn create_swap_chain_default( let swap_chain = unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; - Ok(ManuallyDrop::new(swap_chain)) + Ok(swap_chain) +} + +#[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] @@ -1057,7 +1077,7 @@ fn create_msaa_target_and_its_view( device: &ID3D11Device, width: u32, height: u32, -) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> { +) -> Result<(ID3D11Texture2D, [Option; 1])> { let msaa_target = unsafe { let mut output = None; let desc = D3D11_TEXTURE2D_DESC { @@ -1083,7 +1103,7 @@ fn create_msaa_target_and_its_view( device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; output.unwrap() }; - Ok((msaa_target, msaa_view)) + Ok((msaa_target, [Some(msaa_view)])) } #[inline] From 7cf10d110c4dd865740e8f97a5292c189d9d728a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:34:09 +0800 Subject: [PATCH 052/202] wip --- .../src/platform/windows/directx_renderer.rs | 32 +++++++++++-------- crates/gpui/src/platform/windows/events.rs | 4 --- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 87c98512b9..d6d5d442af 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -4,7 +4,6 @@ use ::util::ResultExt; use anyhow::{Context, Result}; // #[cfg(not(feature = "enable-renderdoc"))] use windows::Win32::Graphics::DirectComposition::*; -use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; use windows::{ Win32::{ Foundation::{HMODULE, HWND}, @@ -50,7 +49,9 @@ struct DirectXResources { msaa_target: ID3D11Texture2D, msaa_view: [Option; 1], - // Cached viewport + // Cached window size and viewport + width: u32, + height: u32, viewport: [D3D11_VIEWPORT; 1], } @@ -111,7 +112,7 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); - let resources = DirectXResources::new(devices, hwnd)?; + let resources = DirectXResources::new(devices)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; // #[cfg(not(feature = "enable-renderdoc"))] @@ -211,12 +212,17 @@ impl DirectXRenderer { } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { + 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 { - let width = new_size.width.0 as u32; - let height = new_size.height.0 as u32; + // 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, width, @@ -224,6 +230,7 @@ impl DirectXRenderer { RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), )?; + self.resources .recreate_resources(&self.devices, width, height)?; self.devices @@ -385,14 +392,9 @@ impl DirectXRenderer { } impl DirectXResources { - pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { - let (width, height) = unsafe { - let mut rect = std::mem::zeroed(); - GetWindowRect(hwnd, &mut rect)?; - let width = (rect.right - rect.left).max(1) as u32; - let height = (rect.bottom - rect.top).max(1) as u32; - (width, height) - }; + pub fn new(devices: &DirectXDevices) -> 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")] @@ -413,6 +415,8 @@ impl DirectXResources { render_target_view, msaa_target, msaa_view, + width, + height, viewport, }) } @@ -431,6 +435,8 @@ impl DirectXResources { self.msaa_target = msaa_target; self.msaa_view = msaa_view; self.viewport = viewport; + self.width = width; + self.height = height; Ok(()) } } diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 461aae5e25..6fd899cbef 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); From a57cbe46361d6d8bf27c7324f1f57a74a7f217ef Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:34:34 +0800 Subject: [PATCH 053/202] remove unused --- crates/gpui/src/platform/windows/directx_renderer.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index d6d5d442af..02469043b5 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -402,11 +402,6 @@ impl DirectXResources { // create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; let (render_target, render_target_view, msaa_target, msaa_view, viewport) = create_resources(devices, &swap_chain, width, height)?; - // unsafe { - // devices - // .device_context - // .OMSetRenderTargets(Some(&render_target_view), None); - // } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { From e560c6813fe63af5bb0586c516c3b900a1197908 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:57:23 +0800 Subject: [PATCH 054/202] add new feature `enable-renderdoc` --- crates/gpui/Cargo.toml | 1 + .../src/platform/windows/directx_renderer.rs | 61 +++++++++++-------- crates/zed/Cargo.toml | 1 + 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 4e718b4428..284664d02f 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/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 02469043b5..062f0a4315 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -2,18 +2,15 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -// #[cfg(not(feature = "enable-renderdoc"))] +#[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::*, }; use crate::*; @@ -28,7 +25,7 @@ pub(crate) struct DirectXRenderer { resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: DirectComposition, } @@ -36,6 +33,7 @@ pub(crate) struct DirectXRenderer { #[derive(Clone)] pub(crate) struct DirectXDevices { dxgi_factory: IDXGIFactory6, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, device: ID3D11Device, device_context: ID3D11DeviceContext, @@ -78,7 +76,7 @@ struct DrawInstancedIndirectArgs { start_instance_location: u32, } -// #[cfg(not(feature = "enable-renderdoc"))] +#[cfg(not(feature = "enable-renderdoc"))] struct DirectComposition { comp_device: IDCompositionDevice, comp_target: IDCompositionTarget, @@ -95,10 +93,12 @@ 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 { dxgi_factory, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, device, device_context, @@ -112,20 +112,27 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); + + #[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"))] + + #[cfg(not(feature = "enable-renderdoc"))] let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] direct_composition.set_swap_chain(&resources.swap_chain)?; + Ok(DirectXRenderer { atlas, devices: devices.clone(), resources, globals, pipelines, - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: direct_composition, }) } @@ -392,14 +399,19 @@ impl DirectXRenderer { } impl DirectXResources { - pub fn new(devices: &DirectXDevices) -> Result { + pub fn new( + devices: &DirectXDevices, + #[cfg(feature = "enable-renderdoc")] hwnd: HWND, + ) -> Result { let width = 1; let height = 1; - // #[cfg(not(feature = "enable-renderdoc"))] + + #[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(feature = "enable-renderdoc")] + let swap_chain = + 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)?; @@ -481,7 +493,7 @@ impl DirectXRenderPipelines { } } -// #[cfg(not(feature = "enable-renderdoc"))] +#[cfg(not(feature = "enable-renderdoc"))] impl DirectComposition { pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result { let comp_device = get_comp_device(&dxgi_device)?; @@ -967,11 +979,12 @@ fn get_device( }) } -// #[cfg(not(feature = "enable-renderdoc"))] +#[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, @@ -998,8 +1011,8 @@ fn create_swap_chain( Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } -// #[cfg(feature = "enable-renderdoc")] -fn create_swap_chain_default( +#[cfg(feature = "enable-renderdoc")] +fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, hwnd: HWND, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 3af1709b74..9b170a84f3 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -69,6 +69,7 @@ gpui = { workspace = true, features = [ "x11", "font-kit", "windows-manifest", + "enable-renderdoc", ] } gpui_tokio.workspace = true From 29b5acf27b313c5e6e58eb98a5c622713e147a93 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 17:04:36 +0800 Subject: [PATCH 055/202] show err if failed to create new window --- crates/gpui/src/platform/windows/platform.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index a14e9ecb54..ca205cc61a 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -462,7 +462,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); From 76fb80eaeb24a3cb3334c29d1cd3ea01731ed13a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 17:12:27 +0800 Subject: [PATCH 056/202] fix --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- crates/zed/Cargo.toml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 062f0a4315..4bc80851be 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -2,8 +2,6 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -#[cfg(not(feature = "enable-renderdoc"))] -use windows::Win32::Graphics::DirectComposition::*; use windows::Win32::{ Foundation::{HMODULE, HWND}, Graphics::{ @@ -12,6 +10,8 @@ use windows::Win32::{ Dxgi::{Common::*, *}, }, }; +#[cfg(not(feature = "enable-renderdoc"))] +use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; use crate::*; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 9b170a84f3..3af1709b74 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -69,7 +69,6 @@ gpui = { workspace = true, features = [ "x11", "font-kit", "windows-manifest", - "enable-renderdoc", ] } gpui_tokio.workspace = true From 4d936845f35435aa9a823921b992c758df36e054 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 19:30:49 +0800 Subject: [PATCH 057/202] implement gpu driver version for nvidia --- .../src/platform/windows/directx_renderer.rs | 116 +++++++++++++++++- crates/gpui/src/platform/windows/window.rs | 3 +- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4bc80851be..22bd5b0b95 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -32,6 +32,7 @@ pub(crate) struct DirectXRenderer { /// Direct3D objects #[derive(Clone)] pub(crate) struct DirectXDevices { + adapter: IDXGIAdapter1, dxgi_factory: IDXGIFactory6, #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, @@ -97,6 +98,7 @@ impl DirectXDevices { let dxgi_device: IDXGIDevice = device.cast()?; Ok(Self { + adapter, dxgi_factory, #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, @@ -396,6 +398,34 @@ 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().context("Failed to get NVIDIA driver info"), + 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), + 0x8086 => Err(anyhow::anyhow!("Intel driver info not implemented yet")), + _ => Err(anyhow::anyhow!("Unknown vendor detected.")), + } + .log_err() + .unwrap_or("Unknown Driver".to_string()); + Ok(GpuSpecs { + is_software_emulated, + device_name, + driver_name, + driver_info: driver_version, + }) + } } impl DirectXResources { @@ -936,12 +966,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. @@ -1339,3 +1368,78 @@ mod shader_resources { } } } + +mod nvidia { + use std::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.h#L87 + #[allow(non_camel_case_types)] + type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int; + + // 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#L33 + let nvapi_init_ptr = nvapi_query(0x0150e828); + if nvapi_init_ptr.is_null() { + anyhow::bail!("Failed to get NVIDIA API function pointer"); + } + let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr); + + let result = nvapi_init(); + if result != 0 { + anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result); + } + + // 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; + Ok(format!("{}.{}", major, minor)) + } + } +} diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index e712d939c8..a2bc854484 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -104,6 +104,7 @@ impl WindowsWindowState { let restore_from_minimized = None; // let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; let renderer = DirectXRenderer::new(gpu_context, hwnd)?; + println!("GPU specs: {:#?}", renderer.gpu_specs()); let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -801,7 +802,7 @@ impl PlatformWindow for WindowsWindow { } fn update_ime_position(&self, _bounds: Bounds) { - // todo(windows) + // There is no such thing on Windows. } } From 78824390d0e73a3340eca7f53b75002d06c2c774 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 19:48:42 +0800 Subject: [PATCH 058/202] cleanup --- crates/gpui/src/platform.rs | 1 - .../src/platform/windows/directx_atlas.rs | 40 ++++++-------- crates/gpui/src/platform/windows/platform.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 53 +------------------ 4 files changed, 18 insertions(+), 78 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 0250e59a9b..8be6a0d5fa 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 abd6e4ad40..1abeab8928 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/platform.rs b/crates/gpui/src/platform/windows/platform.rs index ca205cc61a..3123f943e1 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -28,7 +28,7 @@ use windows::{ core::*, }; -use crate::{platform::blade::BladeContext, *}; +use crate::*; pub(crate) struct WindowsPlatform { state: RefCell, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index a2bc854484..a972cead2b 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); @@ -102,9 +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)?; - println!("GPU specs: {:#?}", renderer.gpu_specs()); let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -796,9 +793,7 @@ 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) { @@ -1298,52 +1293,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; From 7627097875ca3909e628d4d8a15a90494444f770 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:10:33 +0800 Subject: [PATCH 059/202] impl intel driver version --- .../src/platform/windows/directx_renderer.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 22bd5b0b95..8b549c2ae4 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -412,11 +412,12 @@ impl DirectXRenderer { _ => "Unknown Vendor".to_string(), }; let driver_version = match desc.VendorId { - 0x10DE => nvidia::get_driver_version().context("Failed to get NVIDIA driver info"), + 0x10DE => nvidia::get_driver_version(), 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), - 0x8086 => Err(anyhow::anyhow!("Intel 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 { @@ -1443,3 +1444,21 @@ mod nvidia { } } } + +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 + )) + } +} From 84f75fe6839fee381e09bf16e68829cb046c7207 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:31:00 +0800 Subject: [PATCH 060/202] better output for nvidia --- .../src/platform/windows/directx_renderer.rs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 8b549c2ae4..54478e9b02 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1371,7 +1371,10 @@ mod shader_resources { } mod nvidia { - use std::os::raw::{c_char, c_int, c_uint}; + use std::{ + ffi::CStr, + os::raw::{c_char, c_int, c_uint}, + }; use anyhow::{Context, Result}; use windows::{ @@ -1386,10 +1389,6 @@ mod nvidia { #[allow(non_camel_case_types)] type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX]; - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi.h#L87 - #[allow(non_camel_case_types)] - type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int; - // 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( @@ -1405,18 +1404,6 @@ mod nvidia { .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#L33 - let nvapi_init_ptr = nvapi_query(0x0150e828); - if nvapi_init_ptr.is_null() { - anyhow::bail!("Failed to get NVIDIA API function pointer"); - } - let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr); - - let result = nvapi_init(); - if result != 0 { - anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result); - } - // 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() { @@ -1440,7 +1427,13 @@ mod nvidia { } let major = driver_version / 100; let minor = driver_version % 100; - Ok(format!("{}.{}", major, minor)) + let branch_string = CStr::from_ptr(build_branch_string.as_ptr()); + Ok(format!( + "{}.{} {}", + major, + minor, + branch_string.to_string_lossy() + )) } } } From 6477a9b0567a25c73d8f088c9ce678305eacdef9 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:35:42 +0800 Subject: [PATCH 061/202] remove unused --- crates/gpui/src/platform/windows/platform.rs | 2 -- crates/gpui/src/platform/windows/window.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 3123f943e1..ae6c4cc73d 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -34,7 +34,6 @@ 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")?; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index a972cead2b..eda75956b6 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -475,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 From 6907064be6450682c264e1b5148c7b4841994195 Mon Sep 17 00:00:00 2001 From: Kate Date: Thu, 17 Jul 2025 19:56:55 +0200 Subject: [PATCH 062/202] prepare for gpu rasterization --- crates/gpui/Cargo.toml | 2 + crates/gpui/src/color.rs | 1 + .../platform/windows/color_text_raster.hlsl | 22 + .../gpui/src/platform/windows/direct_write.rs | 401 ++++++++++++++++-- .../src/platform/windows/directx_renderer.rs | 22 +- crates/gpui/src/platform/windows/platform.rs | 4 +- 6 files changed, 415 insertions(+), 37 deletions(-) create mode 100644 crates/gpui/src/platform/windows/color_text_raster.hlsl diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 284664d02f..1b687a2b36 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -130,6 +130,8 @@ lyon = "1.0" workspace-hack.workspace = true libc.workspace = true +renderdoc = "0.12.1" + [target.'cfg(target_os = "macos")'.dependencies] block = "0.1" cocoa.workspace = true diff --git a/crates/gpui/src/color.rs b/crates/gpui/src/color.rs index 7fc9c24393..d603986909 100644 --- a/crates/gpui/src/color.rs +++ b/crates/gpui/src/color.rs @@ -40,6 +40,7 @@ pub(crate) fn swap_rgba_pa_to_bgra(color: &mut [u8]) { /// An RGBA color #[derive(PartialEq, Clone, Copy, Default)] +#[repr(C)] pub struct Rgba { /// The red component of the color, in the range 0.0 to 1.0 pub r: f32, diff --git a/crates/gpui/src/platform/windows/color_text_raster.hlsl b/crates/gpui/src/platform/windows/color_text_raster.hlsl new file mode 100644 index 0000000000..b19bc05ac9 --- /dev/null +++ b/crates/gpui/src/platform/windows/color_text_raster.hlsl @@ -0,0 +1,22 @@ + +struct RasterVertexInput { + float2 position : POSITION; +}; + +struct RasterVertexOutput { + float4 position : SV_Position; +}; + +RasterVertexOutput vertex(RasterVertexInput input) { + RasterVertexOutput output; + output.position = float4(input.position, 0.0, 1.0); + return output; +} + +struct PixelInput { + float4 position: SV_Position; +}; + +float4 pixel(PixelInput input): SV_Target { + return float4(input.position.xy, 0.0, 1.0); +} diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index cbe4f50ba7..655a883938 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -9,7 +9,18 @@ use windows::{ Win32::{ Foundation::*, Globalization::GetUserDefaultLocaleName, - Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*}, + Graphics::{ + Direct3D11::{ + D3D11_BIND_SHADER_RESOURCE, D3D11_BUFFER_DESC, D3D11_CPU_ACCESS_WRITE, + D3D11_MAP_WRITE_DISCARD, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC, ID3D11Device, + ID3D11DeviceContext, ID3D11ShaderResourceView, ID3D11Texture2D, + }, + DirectWrite::*, + Dxgi::Common::*, + Gdi::LOGFONTW, + Imaging::*, + }, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, }, @@ -44,7 +55,19 @@ struct GlyphRenderContext { params: IDWriteRenderingParams3, } +struct GPUState { + device: ID3D11Device, + device_context: ID3D11DeviceContext, +} + +struct Syncer(T); +unsafe impl Send for Syncer {} +unsafe impl Sync for Syncer {} + struct DirectWriteState { + gpu_state: GPUState, + #[cfg(feature = "enable-renderdoc")] + renderdoc: Syncer>>>, components: DirectWriteComponent, system_ui_font_name: SharedString, system_font_collection: IDWriteFontCollection1, @@ -118,7 +141,10 @@ impl GlyphRenderContext { } impl DirectWriteTextSystem { - pub(crate) fn new(bitmap_factory: &IWICImagingFactory) -> Result { + pub(crate) fn new( + gpu_context: &DirectXDevices, + bitmap_factory: &IWICImagingFactory, + ) -> Result { let components = DirectWriteComponent::new(bitmap_factory)?; let system_font_collection = unsafe { let mut result = std::mem::zeroed(); @@ -135,7 +161,15 @@ impl DirectWriteTextSystem { }; let system_ui_font_name = get_system_ui_font_name(); + let gpu_state = GPUState { + device: gpu_context.device.clone(), + device_context: gpu_context.device_context.clone(), + }; + Ok(Self(RwLock::new(DirectWriteState { + gpu_state, + #[cfg(feature = "enable-renderdoc")] + renderdoc: Syncer(Arc::new(RwLock::new(renderdoc::RenderDoc::new().unwrap()))), components, system_ui_font_name, system_font_collection, @@ -803,21 +837,32 @@ impl DirectWriteState { )); } - let mut bitmap_data; + let mut bitmap_data: Vec; if params.is_emoji { - // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering - let color_enumerator = unsafe { - self.components.factory.TranslateColorGlyphRun( - Vector2::new(baseline_origin_x, baseline_origin_y), - &glyph_run, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - measuring_mode, - Some(&transform), - 0, - ) - }; + bitmap_data = vec![0u8; texture_width as usize * texture_height as usize * 4]; + + self.rasterize_color( + &glyph_run, + rendering_mode, + measuring_mode, + &transform, + point(baseline_origin_x, baseline_origin_y), + size(DevicePixels(0), DevicePixels(0)), + size(0, 0), + ); + // // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering + // let color_enumerator = unsafe { + // self.components.factory.TranslateColorGlyphRun( + // Vector2::new(baseline_origin_x, baseline_origin_y), + // &glyph_run, + // None, + // DWRITE_GLYPH_IMAGE_FORMATS_COLR + // | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, + // measuring_mode, + // Some(&transform), + // 0, + // ) + // }; // if let Ok(color_enumerator) = color_enumerator { // loop { @@ -954,18 +999,36 @@ impl DirectWriteState { // break; // } // } + + // // bitmap_data.chunks_mut(4).for_each(|chunk| { + // // let tmp = chunk[2]; + // // chunk[2] = chunk[0]; + // // chunk[0] = tmp; + // // }); + + // std::fs::write( + // &format!( + // "{}x{}_{}_color.raw", + // texture_width, texture_height, params.glyph_id.0 + // ), + // &bitmap_data, + // ) + // .unwrap(); // } else { + // let monochrome_data = Self::rasterize_monochrome( + // &glyph_analysis, + // bitmap_size, + // size(texture_width, texture_height), + // &texture_bounds, + // )?; + // // todo: monochrome emojis should be handled gracefully by the renderer + // // currently they just get drawn as their reported color because it assumes they are always colored + // // but in reality monochrome emojis should be colored the same as text is + // bitmap_data = monochrome_data + // .into_iter() + // .flat_map(|e| [0, 0, 0, e]) + // .collect::>(); // } - let monochrome_data = Self::rasterize_monochrome( - &glyph_analysis, - bitmap_size, - size(texture_width, texture_height), - &texture_bounds, - )?; - bitmap_data = monochrome_data - .into_iter() - .flat_map(|e| [e, e, e, 255]) - .collect::>(); } else { bitmap_data = Self::rasterize_monochrome( &glyph_analysis, @@ -1025,6 +1088,214 @@ impl DirectWriteState { Ok(bitmap_data) } + fn rasterize_color( + &self, + glyph_run: &DWRITE_GLYPH_RUN, + rendering_mode: DWRITE_RENDERING_MODE1, + measuring_mode: DWRITE_MEASURING_MODE, + transform: &DWRITE_MATRIX, + baseline_origin: Point, + bitmap_size: Size, + texture_size: Size, + ) -> Result> { + let color_enumerator = unsafe { + self.components.factory.TranslateColorGlyphRun( + Vector2::new(baseline_origin.x, baseline_origin.y), + glyph_run, + None, + DWRITE_GLYPH_IMAGE_FORMATS_COLR | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, + measuring_mode, + Some(transform), + 0, + ) + }?; + + let mut glyph_layers = Vec::new(); + loop { + let color_run = unsafe { color_enumerator.GetCurrentRun() }?; + let color_run = unsafe { &*color_run }; + + let color_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( + &color_run.Base.glyphRun as *const _, + Some(transform), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin.x, + baseline_origin.y, + ) + }?; + + let color_bounds = + unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?; + + let color_size = size( + color_bounds.right - color_bounds.left, + color_bounds.bottom - color_bounds.top, + ); + if color_size.width > 0 && color_size.height > 0 { + let mut alpha_data = vec![0u8; (color_size.width * color_size.height * 3) as usize]; + unsafe { + color_analysis.CreateAlphaTexture( + DWRITE_TEXTURE_CLEARTYPE_3x1, + &color_bounds, + &mut alpha_data, + ) + }?; + + let run_color = { + let run_color = color_run.Base.runColor; + Rgba { + r: run_color.r, + g: run_color.g, + b: run_color.b, + a: run_color.a, + } + }; + let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size); + let alpha_data = alpha_data + .chunks_exact(3) + .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255]) + .collect::>(); + glyph_layers.push(GlyphLayerTexture::new( + &self.gpu_state, + run_color, + bounds, + &alpha_data, + )?); + } + + let has_next = unsafe { color_enumerator.MoveNext() } + .map(|e| e.as_bool()) + .unwrap_or(false); + if !has_next { + break; + } + } + + let params = glyph_layers + .iter() + .enumerate() + .map(|(index, e)| GlyphLayerTextureParams { + run_color: e.run_color, + bounds: e.bounds, + alpha_texture_index: index as u32, + }) + .collect::>(); + + let params_buffer = { + let desc = D3D11_BUFFER_DESC { + ByteWidth: (std::mem::size_of::() * params.len()) as u32, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32, + StructureByteStride: std::mem::size_of::() as u32, + }; + + let mut buffer = None; + unsafe { + self.gpu_state + .device + .CreateBuffer(&desc, None, Some(&mut buffer)) + }?; + buffer.unwrap() + }; + let params_buffer_view = { + let mut view = None; + unsafe { + self.gpu_state.device.CreateShaderResourceView( + ¶ms_buffer, + None, + Some(&mut view), + ) + }?; + [view] + }; + + unsafe { + let mut dest = std::mem::zeroed(); + self.gpu_state.device_context.Map( + ¶ms_buffer, + 0, + D3D11_MAP_WRITE_DISCARD, + 0, + Some(&mut dest), + )?; + std::ptr::copy_nonoverlapping(params.as_ptr(), dest.pData as *mut _, params.len()); + self.gpu_state.device_context.Unmap(¶ms_buffer, 0); + }; + + let textures = glyph_layers + .iter() + .map(|layer| Some(layer.texture_view.clone())) + .collect::>(); + + let vertex_shader = { + let source = + shader_resources::build_shader_blob("color_text_raster", "vertex", "vs_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + source.GetBufferPointer() as *mut u8, + source.GetBufferSize(), + ) + }; + let mut shader = None; + unsafe { + self.gpu_state + .device + .CreateVertexShader(bytes, None, Some(&mut shader)) + }?; + shader.unwrap() + }; + + let pixel_shader = { + let source = + shader_resources::build_shader_blob("color_text_raster", "pixel", "ps_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + source.GetBufferPointer() as *mut u8, + source.GetBufferSize(), + ) + }; + let mut shader = None; + unsafe { + self.gpu_state + .device + .CreatePixelShader(bytes, None, Some(&mut shader)) + }?; + shader.unwrap() + }; + + #[cfg(feature = "enable-renderdoc")] + self.renderdoc + .0 + .write() + .start_frame_capture(std::ptr::null(), std::ptr::null()); + + let device_context = &self.gpu_state.device_context; + unsafe { device_context.VSSetShaderResources(0, Some(textures.as_slice())) }; + unsafe { device_context.PSSetShaderResources(0, Some(textures.as_slice())) }; + unsafe { device_context.VSSetShaderResources(1, Some(¶ms_buffer_view)) }; + unsafe { device_context.PSSetShaderResources(1, Some(¶ms_buffer_view)) }; + unsafe { device_context.VSSetShader(&vertex_shader, None) }; + unsafe { device_context.PSSetShader(&pixel_shader, None) }; + + unsafe { device_context.DrawInstanced(4, params.len() as u32, 0, 0) }; + + #[cfg(feature = "enable-renderdoc")] + self.renderdoc + .0 + .write() + .end_frame_capture(std::ptr::null(), std::ptr::null()); + + println!("render finished"); + + Ok(Vec::new()) + } + fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { unsafe { let font = &self.fonts[font_id.0].font_face; @@ -1096,6 +1367,84 @@ impl Drop for DirectWriteState { } } +struct GlyphLayerTexture { + run_color: Rgba, + bounds: Bounds, + texture: ID3D11Texture2D, + texture_view: ID3D11ShaderResourceView, +} + +impl GlyphLayerTexture { + pub fn new( + gpu_state: &GPUState, + run_color: Rgba, + bounds: Bounds, + alpha_data: &[u8], + ) -> Result { + let texture_size = bounds.size; + + let desc = D3D11_TEXTURE2D_DESC { + Width: texture_size.width as u32, + Height: texture_size.height as u32, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_R8G8B8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + + let texture = { + let mut texture: Option = None; + unsafe { + gpu_state + .device + .CreateTexture2D(&desc, None, Some(&mut texture))? + }; + texture.unwrap() + }; + let texture_view = { + let mut view: Option = None; + unsafe { + gpu_state + .device + .CreateShaderResourceView(&texture, None, Some(&mut view))? + }; + view.unwrap() + }; + + unsafe { + gpu_state.device_context.UpdateSubresource( + &texture, + 0, + None, + alpha_data.as_ptr() as _, + (texture_size.width * 4) as u32, + 0, + ) + }; + + Ok(GlyphLayerTexture { + run_color, + bounds, + texture, + texture_view, + }) + } +} + +#[repr(C)] +struct GlyphLayerTextureParams { + run_color: Rgba, + bounds: Bounds, + alpha_texture_index: u32, +} + struct TextRendererWrapper(pub IDWriteTextRenderer); impl TextRendererWrapper { diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 54478e9b02..a857b350ec 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -36,8 +36,8 @@ pub(crate) struct DirectXDevices { dxgi_factory: IDXGIFactory6, #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, - device: ID3D11Device, - device_context: ID3D11DeviceContext, + pub(crate) device: ID3D11Device, + pub(crate) device_context: ID3D11DeviceContext, } struct DirectXResources { @@ -630,7 +630,8 @@ impl PipelineState { buffer_size: usize, ) -> Result { let vertex = { - let shader_blob = shader_resources::build_shader_blob(vertex_entry, "vs_5_0")?; + let shader_blob = + shader_resources::build_shader_blob("shaders", vertex_entry, "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( shader_blob.GetBufferPointer() as *mut u8, @@ -640,7 +641,8 @@ impl PipelineState { create_vertex_shader(device, bytes)? }; let fragment = { - let shader_blob = shader_resources::build_shader_blob(fragment_entry, "ps_5_0")?; + let shader_blob = + shader_resources::build_shader_blob("shaders", fragment_entry, "ps_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( shader_blob.GetBufferPointer() as *mut u8, @@ -740,7 +742,8 @@ impl PipelineState { 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 shader_blob = + shader_resources::build_shader_blob("shaders", "paths_vertex", "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( shader_blob.GetBufferPointer() as *mut u8, @@ -750,7 +753,8 @@ impl PathsPipelineState { (create_vertex_shader(device, bytes)?, shader_blob) }; let fragment = { - let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; + let shader_blob = + shader_resources::build_shader_blob("shaders", "paths_fragment", "ps_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( shader_blob.GetBufferPointer() as *mut u8, @@ -1314,7 +1318,7 @@ fn set_pipeline_state( const BUFFER_COUNT: usize = 3; -mod shader_resources { +pub(crate) mod shader_resources { use anyhow::Result; use windows::Win32::Graphics::Direct3D::{ Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, @@ -1322,14 +1326,14 @@ mod shader_resources { }; use windows_core::{HSTRING, PCSTR}; - pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + pub(crate) fn build_shader_blob(filename: &str, entry: &str, target: &str) -> Result { unsafe { let mut entry = entry.to_owned(); let mut target = target.to_owned(); let mut compile_blob = None; let mut error_blob = None; let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("src/platform/windows/shaders.hlsl") + .join(&format!("src/platform/windows/{}.hlsl", filename)) .canonicalize() .unwrap(); entry.push_str("\0"); diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index ae6c4cc73d..d8415a6501 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -96,12 +96,13 @@ impl WindowsPlatform { )); let background_executor = BackgroundExecutor::new(dispatcher.clone()); let foreground_executor = ForegroundExecutor::new(dispatcher); + let directx_devices = DirectXDevices::new().context("Unable to init directx devices.")?; let bitmap_factory = ManuallyDrop::new(unsafe { CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER) .context("Error creating bitmap factory.")? }); let text_system = Arc::new( - DirectWriteTextSystem::new(&bitmap_factory) + DirectWriteTextSystem::new(&directx_devices, &bitmap_factory) .context("Error creating DirectWriteTextSystem")?, ); let drop_target_helper: IDropTargetHelper = unsafe { @@ -111,7 +112,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 directx_devices = DirectXDevices::new().context("Unable to init directx devices.")?; let windows_version = WindowsVersion::new().context("Error retrieve windows version")?; Ok(Self { From df1f62477c778c64a2a4dfbe1b3fdc49787b9522 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 15:11:49 +0800 Subject: [PATCH 063/202] add amd gpu version support --- crates/gpui/build.rs | 57 +++++++++++-- crates/gpui/libs/README.md | 10 +++ crates/gpui/libs/amd_ags_x64_2022_MT.lib | Bin 0 -> 153528 bytes crates/gpui/libs/amd_ags_x86_2022_MT.lib | Bin 0 -> 81484 bytes .../src/platform/windows/directx_renderer.rs | 80 +++++++++++++++++- 5 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 crates/gpui/libs/README.md create mode 100644 crates/gpui/libs/amd_ags_x64_2022_MT.lib create mode 100644 crates/gpui/libs/amd_ags_x86_2022_MT.lib diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index b9496cc014..6bb63ee67f 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -9,7 +9,10 @@ fn main() { let target = env::var("CARGO_CFG_TARGET_OS"); println!("cargo::rustc-check-cfg=cfg(gles)"); - #[cfg(any(not(target_os = "macos"), feature = "macos-blade"))] + #[cfg(any( + not(any(target_os = "macos", target_os = "windows")), + feature = "macos-blade" + ))] check_wgsl_shaders(); match target.as_deref() { @@ -17,15 +20,9 @@ fn main() { #[cfg(target_os = "macos")] macos::build(); } - #[cfg(all(target_os = "windows", feature = "windows-manifest"))] Ok("windows") => { - let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); - let rc_file = std::path::Path::new("resources/windows/gpui.rc"); - println!("cargo:rerun-if-changed={}", manifest.display()); - println!("cargo:rerun-if-changed={}", rc_file.display()); - embed_resource::compile(rc_file, embed_resource::NONE) - .manifest_required() - .unwrap(); + #[cfg(target_os = "windows")] + windows::build(); } _ => (), }; @@ -242,3 +239,45 @@ mod macos { } } } + +#[cfg(target_os = "windows")] +mod windows { + use std::path::PathBuf; + + pub(super) fn build() { + // Link the AMD AGS library + link_amd_ags(); + + // Embed the Windows manifest and resource file + #[cfg(feature = "windows-manifest")] + embed_resource(); + } + + fn link_amd_ags() { + // We can not use relative paths in `cargo:rustc-link-search`, so we need to use the absolute path. + // See: https://stackoverflow.com/questions/41917096/how-do-i-make-rustc-link-search-relative-to-the-project-location + let lib_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("libs"); + #[cfg(target_pointer_width = "64")] + let lib_name = "amd_ags_x64_2022_MT"; + #[cfg(target_pointer_width = "32")] + let lib_name = "amd_ags_x86_2022_MT"; + println!("cargo:rustc-link-lib=static={}", lib_name); + println!("cargo:rustc-link-search=native={}", lib_dir.display()); + println!( + "cargo:rerun-if-changed={}/{}.lib", + lib_dir.display(), + lib_name + ); + } + + #[cfg(feature = "windows-manifest")] + fn embed_resource() { + let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); + let rc_file = std::path::Path::new("resources/windows/gpui.rc"); + println!("cargo:rerun-if-changed={}", manifest.display()); + println!("cargo:rerun-if-changed={}", rc_file.display()); + embed_resource::compile(rc_file, embed_resource::NONE) + .manifest_required() + .unwrap(); + } +} diff --git a/crates/gpui/libs/README.md b/crates/gpui/libs/README.md new file mode 100644 index 0000000000..2be286ee53 --- /dev/null +++ b/crates/gpui/libs/README.md @@ -0,0 +1,10 @@ +The files in this folder are required for the Windows platform support in the gpui library. + + +#### amd_ags_x64.lib & amd_ags_x86.lib + +These libraries are used for AMD GPU support, currently only used on Windows, mainly for retrieving GPU information and capabilities. They are linked against the AMD AGS (AMD GPU Services) library. + +The official AMD AGS documentation can be found [here](https://gpuopen.com/amd-gpu-services-ags-library). And these two files are grabbed from the [official AMD AGS repository](https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK), currently at version 6.3.0. + +If you want to update these files, don't forget to update the value of `AGS_CURRENT_VERSION` in `gpui/src/platform/windows/directx_renderer.rs` as well. diff --git a/crates/gpui/libs/amd_ags_x64_2022_MT.lib b/crates/gpui/libs/amd_ags_x64_2022_MT.lib new file mode 100644 index 0000000000000000000000000000000000000000..a25d772160ddadcc98ce777c0e73a57e9f05a59a GIT binary patch literal 153528 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 CUx%mw literal 0 HcmV?d00001 diff --git a/crates/gpui/libs/amd_ags_x86_2022_MT.lib b/crates/gpui/libs/amd_ags_x86_2022_MT.lib new file mode 100644 index 0000000000000000000000000000000000000000..cdb1305378fa058b0e3ffcdeeca8d4ee4e28318b GIT binary patch literal 81484 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 J1FHcs7y#_=bGHBh literal 0 HcmV?d00001 diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 54478e9b02..4e804e74dc 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -413,7 +413,7 @@ impl DirectXRenderer { }; let driver_version = match desc.VendorId { 0x10DE => nvidia::get_driver_version(), - 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), + 0x1002 => amd::get_driver_version(), 0x8086 => intel::get_driver_version(&self.devices.adapter), _ => Err(anyhow::anyhow!("Unknown vendor detected.")), } @@ -1438,6 +1438,84 @@ mod nvidia { } } +mod amd { + use std::os::raw::{c_char, c_int, c_void}; + + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145 + const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0; + + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204 + // This is an opaque type, using struct to represent it properly for FFI + #[repr(C)] + struct AGSContext { + _private: [u8; 0], + } + + #[repr(C)] + pub struct AGSGPUInfo { + pub driver_version: *const c_char, + pub radeon_software_version: *const c_char, + pub num_devices: c_int, + pub devices: *mut c_void, + } + + unsafe extern "C" { + fn agsInitialize( + version: c_int, + config: *const c_void, + context: *mut *mut AGSContext, + gpu_info: *mut AGSGPUInfo, + ) -> c_int; + + fn agsDeInitialize(context: *mut AGSContext) -> c_int; + } + + pub(super) fn get_driver_version() -> anyhow::Result { + unsafe { + let mut context: *mut AGSContext = std::ptr::null_mut(); + let mut gpu_info: AGSGPUInfo = AGSGPUInfo { + driver_version: std::ptr::null(), + radeon_software_version: std::ptr::null(), + num_devices: 0, + devices: std::ptr::null_mut(), + }; + + let result = agsInitialize( + AGS_CURRENT_VERSION, + std::ptr::null(), + &mut context, + &mut gpu_info, + ); + if result != 0 { + return Err(anyhow::anyhow!( + "Failed to initialize AGS, error code: {}", + result + )); + } + + // Vulkan acctually returns this as the driver version + let software_version = if !gpu_info.radeon_software_version.is_null() { + std::ffi::CStr::from_ptr(gpu_info.radeon_software_version) + .to_string_lossy() + .into_owned() + } else { + "Unknown Radeon Software Version".to_string() + }; + + let driver_version = if !gpu_info.driver_version.is_null() { + std::ffi::CStr::from_ptr(gpu_info.driver_version) + .to_string_lossy() + .into_owned() + } else { + "Unknown Radeon Driver Version".to_string() + }; + + agsDeInitialize(context); + Ok(format!("{} ({})", software_version, driver_version)) + } + } +} + mod intel { use windows::{ Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice}, From 636a0573733ba4f3525b25499edb5a64ff0cd8d0 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 16:46:34 +0800 Subject: [PATCH 064/202] allow to compile shader at building --- crates/gpui/build.rs | 179 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 6bb63ee67f..077be5e683 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -242,12 +242,19 @@ mod macos { #[cfg(target_os = "windows")] mod windows { - use std::path::PathBuf; + use std::{ + io::Write, + path::{Path, PathBuf}, + process::{self, Command}, + }; pub(super) fn build() { // Link the AMD AGS library link_amd_ags(); + // Compile HLSL shaders + compile_shaders(); + // Embed the Windows manifest and resource file #[cfg(feature = "windows-manifest")] embed_resource(); @@ -280,4 +287,174 @@ mod windows { .manifest_required() .unwrap(); } + + fn compile_shaders() { + use std::fs; + use std::process::{self, Command}; + + let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("src/platform/windows/shaders.hlsl"); + let out_dir = std::env::var("OUT_DIR").unwrap(); + + println!("cargo:rerun-if-changed={}", shader_path.display()); + + // Check if fxc.exe is available + let fxc_path = find_fxc_compiler(); + + // Define all modules + let modules = [ + "quad", + "shadow", + "paths", + "underline", + "monochrome_sprite", + "polychrome_sprite", + ]; + + let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir); + if Path::new(&rust_binding_path).exists() { + fs::remove_file(&rust_binding_path) + .expect("Failed to remove existing Rust binding file"); + } + for module in &modules { + compile_shader_for_module( + module, + &out_dir, + &fxc_path, + shader_path.to_str().unwrap(), + &rust_binding_path, + ); + } + println!( + "cargo:warning=Successfully compiled shaders. Output written to: {}", + rust_binding_path + ); + } + + fn find_fxc_compiler() -> String { + // Try to find in PATH + if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { + if output.status.success() { + let path = String::from_utf8_lossy(&output.stdout); + return path.trim().to_string(); + } + } + + // Check the default path + if Path::new(r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe") + .exists() + { + return r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe" + .to_string(); + } + + // Check environment variable + if let Ok(path) = std::env::var("GPUI_FXC_PATH") { + if Path::new(&path).exists() { + return path; + } + } + + panic!("Failed to find fxc.exe"); + } + + fn compile_shader_for_module( + module: &str, + out_dir: &str, + fxc_path: &str, + shader_path: &str, + rust_binding_path: &str, + ) { + // Compile vertex shader + let output_file = format!("{}/{}_vs.h", out_dir, module); + let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase()); + compile_shader_impl( + fxc_path, + &format!("{module}_vertex"), + &output_file, + &const_name, + shader_path, + "vs_5_0", + ); + generate_rust_binding(&const_name, &output_file, &rust_binding_path); + + // Compile fragment shader + let output_file = format!("{}/{}_ps.h", out_dir, module); + let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase()); + compile_shader_impl( + fxc_path, + &format!("{module}_fragment"), + &output_file, + &const_name, + shader_path, + "ps_5_0", + ); + generate_rust_binding(&const_name, &output_file, &rust_binding_path); + } + + fn compile_shader_impl( + fxc_path: &str, + entry_point: &str, + output_path: &str, + var_name: &str, + shader_path: &str, + target: &str, + ) { + let output = Command::new(fxc_path) + .args([ + "/T", + target, + "/E", + entry_point, + "/Fh", + output_path, + "/Vn", + var_name, + shader_path, + ]) + .output(); + + match output { + Ok(result) => { + if result.status.success() { + return; + } + eprintln!( + "Pixel shader compilation failed for {}:\n{}", + entry_point, + String::from_utf8_lossy(&result.stderr) + ); + process::exit(1); + } + Err(e) => { + eprintln!("Failed to run fxc for {}: {}", entry_point, e); + process::exit(1); + } + } + } + + fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) { + let header_content = + std::fs::read_to_string(head_file).expect("Failed to read header file"); + let const_definition = { + let global_var_start = header_content.find("const BYTE").unwrap(); + let global_var = &header_content[global_var_start..]; + let equal = global_var.find('=').unwrap(); + global_var[equal + 1..].trim() + }; + let rust_binding = format!( + "const {}: &[u8] = &{}\n", + const_name, + const_definition.replace('{', "[").replace('}', "]") + ); + let mut options = std::fs::OpenOptions::new() + .write(true) + .create(true) + .append(true) + .open(output_path) + .expect("Failed to open Rust binding file"); + options + .write_all(rust_binding.as_bytes()) + .expect("Failed to write Rust binding file"); + } } From 154705e72966fa28dcbaf1be39cfe0cb84de7a2c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:08:44 +0800 Subject: [PATCH 065/202] checkpoint --- crates/gpui/build.rs | 1 + .../src/platform/windows/directx_renderer.rs | 177 +++++++++++------- 2 files changed, 113 insertions(+), 65 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 077be5e683..c593c2ff45 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -288,6 +288,7 @@ mod windows { .unwrap(); } + /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { use std::fs; use std::process::{self, Command}; diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4e804e74dc..7959e2d90f 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -13,7 +13,12 @@ use windows::Win32::{ #[cfg(not(feature = "enable-renderdoc"))] use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; -use crate::*; +use crate::{ + platform::windows::directx_renderer::shader_resources::{ + RawShaderBytes, ShaderModule, ShaderTarget, + }, + *, +}; 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. @@ -481,35 +486,22 @@ impl DirectXResources { impl DirectXRenderPipelines { pub fn new(device: &ID3D11Device) -> Result { - let shadow_pipeline = PipelineState::new( - device, - "shadow_pipeline", - "shadow_vertex", - "shadow_fragment", - 4, - )?; - let quad_pipeline = - PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?; + let shadow_pipeline = + PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; + let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; let paths_pipeline = PathsPipelineState::new(device)?; - let underline_pipeline = PipelineState::new( - device, - "underline_pipeline", - "underline_vertex", - "underline_fragment", - 4, - )?; + let underline_pipeline = + PipelineState::new(device, "underline_pipeline", ShaderModule::Underline, 4)?; let mono_sprites = PipelineState::new( device, "monochrome_sprite_pipeline", - "monochrome_sprite_vertex", - "monochrome_sprite_fragment", + ShaderModule::MonochromeSprite, 512, )?; let poly_sprites = PipelineState::new( device, "polychrome_sprite_pipeline", - "polychrome_sprite_vertex", - "polychrome_sprite_fragment", + ShaderModule::PolychromeSprite, 16, )?; @@ -625,29 +617,16 @@ impl PipelineState { fn new( device: &ID3D11Device, label: &'static str, - vertex_entry: &str, - fragment_entry: &str, + shader_module: ShaderModule, 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 raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Vertex)?; + create_vertex_shader(device, raw_shader.as_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 raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Fragment)?; + create_fragment_shader(device, raw_shader.as_bytes())? }; let buffer = create_buffer(device, std::mem::size_of::(), buffer_size)?; let view = create_buffer_view(device, &buffer)?; @@ -740,24 +719,15 @@ impl PipelineState { 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 raw_vertex_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Vertex)?; + ( + create_vertex_shader(device, raw_vertex_shader.as_bytes())?, + raw_vertex_shader, + ) }; 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 raw_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Fragment)?; + create_fragment_shader(device, raw_shader.as_bytes())? }; let buffer = create_buffer(device, std::mem::size_of::(), 32)?; let view = create_buffer_view(device, &buffer)?; @@ -769,10 +739,6 @@ impl PathsPipelineState { 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( &[ @@ -813,7 +779,7 @@ impl PathsPipelineState { InstanceDataStepRate: 0, }, ], - shader_bytes, + vertex_shader.as_bytes(), Some(&mut layout), )?; layout.unwrap() @@ -1316,12 +1282,73 @@ const BUFFER_COUNT: usize = 3; mod shader_resources { use anyhow::Result; - use windows::Win32::Graphics::Direct3D::{ - Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, - ID3DBlob, - }; - use windows_core::{HSTRING, PCSTR}; + // use windows::Win32::Graphics::Direct3D::{ + // Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + // ID3DBlob, + // }; + // use windows_core::{HSTRING, PCSTR}; + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub(super) enum ShaderModule { + Quad, + Shadow, + Underline, + Paths, + MonochromeSprite, + PolychromeSprite, + } + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub(super) enum ShaderTarget { + Vertex, + Fragment, + } + + pub(super) struct RawShaderBytes<'t> { + inner: &'t [u8], + } + + impl<'t> RawShaderBytes<'t> { + pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result { + Ok(Self::from_bytes(module, target)) + } + + pub(super) fn as_bytes(&self) -> &'t [u8] { + self.inner + } + + fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self { + let bytes = match module { + ShaderModule::Quad => match target { + ShaderTarget::Vertex => QUAD_VERTEX_BYTES, + ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES, + }, + ShaderModule::Shadow => match target { + ShaderTarget::Vertex => SHADOW_VERTEX_BYTES, + ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES, + }, + ShaderModule::Underline => match target { + ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES, + ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES, + }, + ShaderModule::Paths => match target { + ShaderTarget::Vertex => PATHS_VERTEX_BYTES, + ShaderTarget::Fragment => PATHS_FRAGMENT_BYTES, + }, + ShaderModule::MonochromeSprite => match target { + ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES, + ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES, + }, + ShaderModule::PolychromeSprite => match target { + ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES, + ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES, + }, + }; + Self { inner: bytes } + } + } + + #[cfg(not(debug_assertions))] pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { unsafe { let mut entry = entry.to_owned(); @@ -1368,6 +1395,26 @@ mod shader_resources { Ok(compile_blob.unwrap()) } } + + include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs")); + + impl ShaderModule { + pub fn as_str(&self) -> &'static str { + match self { + ShaderModule::Quad => "quad", + ShaderModule::Shadow => "shadow", + ShaderModule::Underline => "underline", + ShaderModule::Paths => "paths", + ShaderModule::MonochromeSprite => "monochrome_sprite", + ShaderModule::PolychromeSprite => "polychrome_sprite", + } + } + } + + // pub fn quad_vertex_shader() -> &'static [u8] { + // unsafe { + // std::slice::from_raw_parts(g_quad_vertex.as_ptr() as *const u8, g_quad_vertex.len()) + // } } mod nvidia { From 825ee6233ba112a09adbb5971203115d22a40df4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:43:01 +0800 Subject: [PATCH 066/202] add runtime shader --- crates/gpui/build.rs | 1 + .../src/platform/windows/directx_renderer.rs | 88 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index c593c2ff45..dbbb8687fd 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -253,6 +253,7 @@ mod windows { link_amd_ags(); // Compile HLSL shaders + #[cfg(not(debug_assertions))] compile_shaders(); // Embed the Windows manifest and resource file diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 7959e2d90f..acc3faa91b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1282,11 +1282,15 @@ const BUFFER_COUNT: usize = 3; mod shader_resources { use anyhow::Result; - // use windows::Win32::Graphics::Direct3D::{ - // Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, - // ID3DBlob, - // }; - // use windows_core::{HSTRING, PCSTR}; + + #[cfg(debug_assertions)] + use windows::{ + Win32::Graphics::Direct3D::{ + Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + ID3DBlob, + }, + core::{HSTRING, PCSTR}, + }; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(super) enum ShaderModule { @@ -1306,17 +1310,35 @@ mod shader_resources { pub(super) struct RawShaderBytes<'t> { inner: &'t [u8], + + #[cfg(debug_assertions)] + _blob: ID3DBlob, } impl<'t> RawShaderBytes<'t> { pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result { - Ok(Self::from_bytes(module, target)) + #[cfg(not(debug_assertions))] + { + Ok(Self::from_bytes(module, target)) + } + #[cfg(debug_assertions)] + { + let blob = build_shader_blob(module, target)?; + let inner = unsafe { + std::slice::from_raw_parts( + blob.GetBufferPointer() as *const u8, + blob.GetBufferSize(), + ) + }; + Ok(Self { inner, _blob: blob }) + } } - pub(super) fn as_bytes(&self) -> &'t [u8] { + pub(super) fn as_bytes(&'t self) -> &'t [u8] { self.inner } + #[cfg(not(debug_assertions))] fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self { let bytes = match module { ShaderModule::Quad => match target { @@ -1348,32 +1370,38 @@ mod shader_resources { } } - #[cfg(not(debug_assertions))] - pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + #[cfg(debug_assertions)] + pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result { unsafe { - let mut entry = entry.to_owned(); - let mut target = target.to_owned(); + let entry = format!( + "{}_{}\0", + entry.as_str(), + match target { + ShaderTarget::Vertex => "vertex", + ShaderTarget::Fragment => "fragment", + } + ); + let target = match target { + ShaderTarget::Vertex => "vs_5_0\0", + ShaderTarget::Fragment => "ps_5_0\0", + }; + let mut compile_blob = None; let mut error_blob = None; let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("src/platform/windows/shaders.hlsl") - .canonicalize() - .unwrap(); - entry.push_str("\0"); - target.push_str("\0"); + .canonicalize()?; + let entry_point = PCSTR::from_raw(entry.as_ptr()); let target_cstr = PCSTR::from_raw(target.as_ptr()); - #[cfg(debug_assertions)] - let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; - #[cfg(not(debug_assertions))] - let compile_flag = 0; + let ret = D3DCompileFromFile( &HSTRING::from(shader_path.to_str().unwrap()), None, None, entry_point, target_cstr, - compile_flag, + D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &mut compile_blob, Some(&mut error_blob), @@ -1382,13 +1410,10 @@ mod shader_resources { let Some(error_blob) = error_blob else { return Err(anyhow::anyhow!("{ret:?}")); }; - let string_len = error_blob.GetBufferSize(); - let error_string_encode = Vec::from_raw_parts( - error_blob.GetBufferPointer() as *mut u8, - string_len, - string_len, - ); - let error_string = String::from_utf8_lossy(&error_string_encode).to_string(); + + let error_string = + std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8) + .to_string_lossy(); log::error!("Shader compile error: {}", error_string); return Err(anyhow::anyhow!("Compile error: {}", error_string)); } @@ -1396,10 +1421,12 @@ mod shader_resources { } } + #[cfg(not(debug_assertions))] include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs")); + #[cfg(debug_assertions)] impl ShaderModule { - pub fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &str { match self { ShaderModule::Quad => "quad", ShaderModule::Shadow => "shadow", @@ -1410,11 +1437,6 @@ mod shader_resources { } } } - - // pub fn quad_vertex_shader() -> &'static [u8] { - // unsafe { - // std::slice::from_raw_parts(g_quad_vertex.as_ptr() as *const u8, g_quad_vertex.len()) - // } } mod nvidia { From b0e48d01cec6e8261f6ffd758b9f723dc4d64904 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:48:05 +0800 Subject: [PATCH 067/202] add x86 support for nvidia --- crates/gpui/src/platform/windows/directx_renderer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index acc3faa91b..2cad807ae1 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1468,7 +1468,13 @@ mod nvidia { 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")?; + #[cfg(target_pointer_width = "64")] + let nvidia_dll = + LoadLibraryA(s!("nvapi64.dll")).context(format!("Can't load nvapi64.dll"))?; + #[cfg(target_pointer_width = "32")] + let nvidia_dll = + LoadLibraryA(s!("nvapi.dll")).context(format!("Can't load nvapi.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); From e87ee91d8ec32cbae1e61d3ac1ce47ef99754d1d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:53:45 +0800 Subject: [PATCH 068/202] enable `O3` optimization for fxc --- crates/gpui/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index dbbb8687fd..46dbdb0199 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -292,7 +292,6 @@ mod windows { /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { use std::fs; - use std::process::{self, Command}; let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) .join("src/platform/windows/shaders.hlsl"); @@ -412,6 +411,7 @@ mod windows { output_path, "/Vn", var_name, + "/O3", shader_path, ]) .output(); From 788865e892c7d10e4886600bd67e864e8271c929 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:54:26 +0800 Subject: [PATCH 069/202] remove debug print --- crates/gpui/build.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 46dbdb0199..3c958528ff 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -326,10 +326,6 @@ mod windows { &rust_binding_path, ); } - println!( - "cargo:warning=Successfully compiled shaders. Output written to: {}", - rust_binding_path - ); } fn find_fxc_compiler() -> String { From 9f200ebf5a242b1f73280791d6cc0c5488b241fb Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:56:46 +0800 Subject: [PATCH 070/202] fix --- crates/gpui/build.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 3c958528ff..54409177c4 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -243,6 +243,7 @@ mod macos { #[cfg(target_os = "windows")] mod windows { use std::{ + fs, io::Write, path::{Path, PathBuf}, process::{self, Command}, @@ -269,6 +270,7 @@ mod windows { let lib_name = "amd_ags_x64_2022_MT"; #[cfg(target_pointer_width = "32")] let lib_name = "amd_ags_x86_2022_MT"; + println!("cargo:rustc-link-lib=static={}", lib_name); println!("cargo:rustc-link-search=native={}", lib_dir.display()); println!( @@ -291,8 +293,6 @@ mod windows { /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { - use std::fs; - let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) .join("src/platform/windows/shaders.hlsl"); let out_dir = std::env::var("OUT_DIR").unwrap(); @@ -317,7 +317,7 @@ mod windows { fs::remove_file(&rust_binding_path) .expect("Failed to remove existing Rust binding file"); } - for module in &modules { + for module in modules { compile_shader_for_module( module, &out_dir, @@ -328,6 +328,7 @@ mod windows { } } + /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn find_fxc_compiler() -> String { // Try to find in PATH if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { @@ -432,8 +433,7 @@ mod windows { } fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) { - let header_content = - std::fs::read_to_string(head_file).expect("Failed to read header file"); + let header_content = fs::read_to_string(head_file).expect("Failed to read header file"); let const_definition = { let global_var_start = header_content.find("const BYTE").unwrap(); let global_var = &header_content[global_var_start..]; @@ -445,7 +445,7 @@ mod windows { const_name, const_definition.replace('{', "[").replace('}', "]") ); - let mut options = std::fs::OpenOptions::new() + let mut options = fs::OpenOptions::new() .write(true) .create(true) .append(true) From 11dc14ad4d7c941f7cf234c61828730de1ffbb53 Mon Sep 17 00:00:00 2001 From: Kate Date: Fri, 18 Jul 2025 16:15:53 +0200 Subject: [PATCH 071/202] gpu rasterization works now --- .../platform/windows/color_text_raster.hlsl | 33 ++- .../gpui/src/platform/windows/direct_write.rs | 213 ++++++++++++------ 2 files changed, 175 insertions(+), 71 deletions(-) diff --git a/crates/gpui/src/platform/windows/color_text_raster.hlsl b/crates/gpui/src/platform/windows/color_text_raster.hlsl index b19bc05ac9..b0307aee42 100644 --- a/crates/gpui/src/platform/windows/color_text_raster.hlsl +++ b/crates/gpui/src/platform/windows/color_text_raster.hlsl @@ -1,22 +1,39 @@ - -struct RasterVertexInput { - float2 position : POSITION; -}; - struct RasterVertexOutput { float4 position : SV_Position; + float2 texcoord : TEXCOORD0; }; -RasterVertexOutput vertex(RasterVertexInput input) { +RasterVertexOutput vertex(uint vertexID : SV_VERTEXID) +{ RasterVertexOutput output; - output.position = float4(input.position, 0.0, 1.0); + output.texcoord = float2((vertexID << 1) & 2, vertexID & 2); + output.position = float4(output.texcoord * 2.0f - 1.0f, 0.0f, 1.0f); + output.position.y = -output.position.y; + return output; } struct PixelInput { float4 position: SV_Position; + float2 texcoord : TEXCOORD0; +}; + +struct Bounds { + int2 origin; + int2 size; +}; + +Texture2D t_layer : register(t0); +SamplerState s_layer : register(s0); + +cbuffer GlyphLayerTextureParams : register(b0) { + Bounds bounds; + float4 run_color; }; float4 pixel(PixelInput input): SV_Target { - return float4(input.position.xy, 0.0, 1.0); + float3 sampled = t_layer.Sample(s_layer, input.texcoord.xy).rgb; + float alpha = (sampled.r + sampled.g + sampled.b) / 3; + + return float4(run_color.rgb, alpha); } diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 655a883938..a15732c7e6 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -10,16 +10,8 @@ use windows::{ Foundation::*, Globalization::GetUserDefaultLocaleName, Graphics::{ - Direct3D11::{ - D3D11_BIND_SHADER_RESOURCE, D3D11_BUFFER_DESC, D3D11_CPU_ACCESS_WRITE, - D3D11_MAP_WRITE_DISCARD, D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, - D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC, ID3D11Device, - ID3D11DeviceContext, ID3D11ShaderResourceView, ID3D11Texture2D, - }, - DirectWrite::*, - Dxgi::Common::*, - Gdi::LOGFONTW, - Imaging::*, + Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*, + Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*, }, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, @@ -847,8 +839,8 @@ impl DirectWriteState { measuring_mode, &transform, point(baseline_origin_x, baseline_origin_y), - size(DevicePixels(0), DevicePixels(0)), - size(0, 0), + bitmap_size, + size(texture_width, texture_height), ); // // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering // let color_enumerator = unsafe { @@ -1175,24 +1167,14 @@ impl DirectWriteState { } } - let params = glyph_layers - .iter() - .enumerate() - .map(|(index, e)| GlyphLayerTextureParams { - run_color: e.run_color, - bounds: e.bounds, - alpha_texture_index: index as u32, - }) - .collect::>(); - let params_buffer = { let desc = D3D11_BUFFER_DESC { - ByteWidth: (std::mem::size_of::() * params.len()) as u32, + ByteWidth: std::mem::size_of::() as u32, Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32, CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, - MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32, - StructureByteStride: std::mem::size_of::() as u32, + MiscFlags: 0, + StructureByteStride: 0, }; let mut buffer = None; @@ -1201,37 +1183,8 @@ impl DirectWriteState { .device .CreateBuffer(&desc, None, Some(&mut buffer)) }?; - buffer.unwrap() + [buffer] }; - let params_buffer_view = { - let mut view = None; - unsafe { - self.gpu_state.device.CreateShaderResourceView( - ¶ms_buffer, - None, - Some(&mut view), - ) - }?; - [view] - }; - - unsafe { - let mut dest = std::mem::zeroed(); - self.gpu_state.device_context.Map( - ¶ms_buffer, - 0, - D3D11_MAP_WRITE_DISCARD, - 0, - Some(&mut dest), - )?; - std::ptr::copy_nonoverlapping(params.as_ptr(), dest.pData as *mut _, params.len()); - self.gpu_state.device_context.Unmap(¶ms_buffer, 0); - }; - - let textures = glyph_layers - .iter() - .map(|layer| Some(layer.texture_view.clone())) - .collect::>(); let vertex_shader = { let source = @@ -1251,6 +1204,105 @@ impl DirectWriteState { shader.unwrap() }; + let render_target_texture = { + let mut texture = None; + let desc = D3D11_TEXTURE2D_DESC { + Width: bitmap_size.width.0 as u32, + Height: bitmap_size.height.0 as u32, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32, + MiscFlags: 0, + }; + unsafe { + self.gpu_state + .device + .CreateTexture2D(&desc, None, Some(&mut texture)) + }?; + texture.unwrap() + }; + + let render_target_view = { + let desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, + Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 { + Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 }, + }, + }; + let mut rtv = None; + unsafe { + self.gpu_state.device.CreateRenderTargetView( + &render_target_texture, + Some(&desc), + Some(&mut rtv), + ) + }?; + [rtv] + }; + + let blend_state = { + let mut blend_state = None; + let desc = D3D11_BLEND_DESC { + AlphaToCoverageEnable: false.into(), + IndependentBlendEnable: false.into(), + RenderTarget: [ + D3D11_RENDER_TARGET_BLEND_DESC { + BlendEnable: true.into(), + SrcBlend: D3D11_BLEND_SRC_ALPHA, + DestBlend: D3D11_BLEND_INV_SRC_ALPHA, + BlendOp: D3D11_BLEND_OP_ADD, + SrcBlendAlpha: D3D11_BLEND_ONE, + DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA, + BlendOpAlpha: D3D11_BLEND_OP_ADD, + RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8, + }, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ], + }; + unsafe { + self.gpu_state + .device + .CreateBlendState(&desc, Some(&mut blend_state)) + }?; + blend_state.unwrap() + }; + + let sampler = { + let mut sampler = None; + let desc = D3D11_SAMPLER_DESC { + Filter: D3D11_FILTER_MIN_MAG_MIP_POINT, + AddressU: D3D11_TEXTURE_ADDRESS_BORDER, + AddressV: D3D11_TEXTURE_ADDRESS_BORDER, + AddressW: D3D11_TEXTURE_ADDRESS_BORDER, + MipLODBias: 0.0, + MaxAnisotropy: 1, + ComparisonFunc: D3D11_COMPARISON_ALWAYS, + BorderColor: [0.0, 0.0, 0.0, 0.0], + MinLOD: 0.0, + MaxLOD: 0.0, + }; + unsafe { + self.gpu_state + .device + .CreateSamplerState(&desc, Some(&mut sampler)) + }?; + [sampler] + }; + let pixel_shader = { let source = shader_resources::build_shader_blob("color_text_raster", "pixel", "ps_5_0")?; @@ -1276,14 +1328,50 @@ impl DirectWriteState { .start_frame_capture(std::ptr::null(), std::ptr::null()); let device_context = &self.gpu_state.device_context; - unsafe { device_context.VSSetShaderResources(0, Some(textures.as_slice())) }; - unsafe { device_context.PSSetShaderResources(0, Some(textures.as_slice())) }; - unsafe { device_context.VSSetShaderResources(1, Some(¶ms_buffer_view)) }; - unsafe { device_context.PSSetShaderResources(1, Some(¶ms_buffer_view)) }; + unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) }; unsafe { device_context.VSSetShader(&vertex_shader, None) }; unsafe { device_context.PSSetShader(&pixel_shader, None) }; + unsafe { device_context.VSSetConstantBuffers(0, Some(¶ms_buffer)) }; + unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_buffer)) }; + unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) }; + unsafe { device_context.PSSetSamplers(0, Some(&sampler)) }; + unsafe { device_context.OMSetBlendState(&blend_state, None, 0xffffffff) }; - unsafe { device_context.DrawInstanced(4, params.len() as u32, 0, 0) }; + for layer in glyph_layers { + let params = GlyphLayerTextureParams { + run_color: layer.run_color, + bounds: layer.bounds, + }; + unsafe { + let mut dest = std::mem::zeroed(); + self.gpu_state.device_context.Map( + params_buffer[0].as_ref().unwrap(), + 0, + D3D11_MAP_WRITE_DISCARD, + 0, + Some(&mut dest), + )?; + std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1); + self.gpu_state + .device_context + .Unmap(params_buffer[0].as_ref().unwrap(), 0); + }; + + let texture = [Some(layer.texture_view)]; + unsafe { device_context.PSSetShaderResources(0, Some(&texture)) }; + + let viewport = [D3D11_VIEWPORT { + TopLeftX: layer.bounds.origin.x as f32, + TopLeftY: layer.bounds.origin.y as f32, + Width: layer.bounds.size.width as f32, + Height: layer.bounds.size.height as f32, + MinDepth: 0.0, + MaxDepth: 1.0, + }]; + unsafe { device_context.RSSetViewports(Some(&viewport)) }; + + unsafe { device_context.Draw(4, 0) }; + } #[cfg(feature = "enable-renderdoc")] self.renderdoc @@ -1440,9 +1528,8 @@ impl GlyphLayerTexture { #[repr(C)] struct GlyphLayerTextureParams { - run_color: Rgba, bounds: Bounds, - alpha_texture_index: u32, + run_color: Rgba, } struct TextRendererWrapper(pub IDWriteTextRenderer); From ba80e163394ba5e63c3b5673844160d62b70da00 Mon Sep 17 00:00:00 2001 From: Kate Date: Fri, 18 Jul 2025 21:12:19 +0200 Subject: [PATCH 072/202] color rasterization works now --- .../gpui/src/platform/windows/direct_write.rs | 167 ++++++++++++------ 1 file changed, 114 insertions(+), 53 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index a15732c7e6..d39b00280a 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -831,9 +831,10 @@ impl DirectWriteState { let mut bitmap_data: Vec; if params.is_emoji { - bitmap_data = vec![0u8; texture_width as usize * texture_height as usize * 4]; + // bitmap_data = vec![0u8; texture_width as usize * texture_height as usize * 4]; - self.rasterize_color( + println!("trying to rasterize"); + let res = self.rasterize_color( &glyph_run, rendering_mode, measuring_mode, @@ -1090,12 +1091,13 @@ impl DirectWriteState { bitmap_size: Size, texture_size: Size, ) -> Result> { + // todo: support formats other than COLR let color_enumerator = unsafe { self.components.factory.TranslateColorGlyphRun( Vector2::new(baseline_origin.x, baseline_origin.y), glyph_run, None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, + DWRITE_GLYPH_IMAGE_FORMATS_COLR, measuring_mode, Some(transform), 0, @@ -1106,57 +1108,60 @@ impl DirectWriteState { loop { let color_run = unsafe { color_enumerator.GetCurrentRun() }?; let color_run = unsafe { &*color_run }; - - let color_analysis = unsafe { - self.components.factory.CreateGlyphRunAnalysis( - &color_run.Base.glyphRun as *const _, - Some(transform), - rendering_mode, - measuring_mode, - DWRITE_GRID_FIT_MODE_DEFAULT, - DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, - baseline_origin.x, - baseline_origin.y, - ) - }?; - - let color_bounds = - unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?; - - let color_size = size( - color_bounds.right - color_bounds.left, - color_bounds.bottom - color_bounds.top, - ); - if color_size.width > 0 && color_size.height > 0 { - let mut alpha_data = vec![0u8; (color_size.width * color_size.height * 3) as usize]; - unsafe { - color_analysis.CreateAlphaTexture( - DWRITE_TEXTURE_CLEARTYPE_3x1, - &color_bounds, - &mut alpha_data, + let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE; + if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR { + let color_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( + &color_run.Base.glyphRun as *const _, + Some(transform), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin.x, + baseline_origin.y, ) }?; - let run_color = { - let run_color = color_run.Base.runColor; - Rgba { - r: run_color.r, - g: run_color.g, - b: run_color.b, - a: run_color.a, - } - }; - let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size); - let alpha_data = alpha_data - .chunks_exact(3) - .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255]) - .collect::>(); - glyph_layers.push(GlyphLayerTexture::new( - &self.gpu_state, - run_color, - bounds, - &alpha_data, - )?); + let color_bounds = + unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?; + + let color_size = size( + color_bounds.right - color_bounds.left, + color_bounds.bottom - color_bounds.top, + ); + if color_size.width > 0 && color_size.height > 0 { + let mut alpha_data = + vec![0u8; (color_size.width * color_size.height * 3) as usize]; + unsafe { + color_analysis.CreateAlphaTexture( + DWRITE_TEXTURE_CLEARTYPE_3x1, + &color_bounds, + &mut alpha_data, + ) + }?; + + let run_color = { + let run_color = color_run.Base.runColor; + Rgba { + r: run_color.r, + g: run_color.g, + b: run_color.b, + a: run_color.a, + } + }; + let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size); + let alpha_data = alpha_data + .chunks_exact(3) + .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255]) + .collect::>(); + glyph_layers.push(GlyphLayerTexture::new( + &self.gpu_state, + run_color, + bounds, + &alpha_data, + )?); + } } let has_next = unsafe { color_enumerator.MoveNext() } @@ -1218,7 +1223,7 @@ impl DirectWriteState { }, Usage: D3D11_USAGE_DEFAULT, BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, - CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32, + CPUAccessFlags: 0, MiscFlags: 0, }; unsafe { @@ -1248,6 +1253,31 @@ impl DirectWriteState { [rtv] }; + let staging_texture = { + let mut texture = None; + let desc = D3D11_TEXTURE2D_DESC { + Width: bitmap_size.width.0 as u32, + Height: bitmap_size.height.0 as u32, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_STAGING, + BindFlags: 0, + CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32, + MiscFlags: 0, + }; + unsafe { + self.gpu_state + .device + .CreateTexture2D(&desc, None, Some(&mut texture)) + }?; + texture.unwrap() + }; + let blend_state = { let mut blend_state = None; let desc = D3D11_BLEND_DESC { @@ -1373,6 +1403,37 @@ impl DirectWriteState { unsafe { device_context.Draw(4, 0) }; } + unsafe { device_context.CopyResource(&staging_texture, &render_target_texture) }; + + let mapped_data = { + let mut mapped_data = D3D11_MAPPED_SUBRESOURCE::default(); + unsafe { + device_context.Map( + &staging_texture, + 0, + D3D11_MAP_READ, + 0, + Some(&mut mapped_data), + ) + }?; + mapped_data + }; + let mut rasterized = + vec![0u8; (bitmap_size.width.0 as u32 * bitmap_size.height.0 as u32 * 4) as usize]; + + for y in 0..bitmap_size.height.0 as usize { + let width = bitmap_size.width.0 as usize; + unsafe { + std::ptr::copy_nonoverlapping::( + (mapped_data.pData as *const u8).byte_add(mapped_data.RowPitch as usize * y), + rasterized + .as_mut_ptr() + .byte_add(width * y * std::mem::size_of::()), + width * std::mem::size_of::(), + ) + }; + } + #[cfg(feature = "enable-renderdoc")] self.renderdoc .0 @@ -1381,7 +1442,7 @@ impl DirectWriteState { println!("render finished"); - Ok(Vec::new()) + Ok(rasterized) } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From 1f55a0a35852d309e797835382ecfd71366aaa9a Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 21 Jul 2025 17:17:42 +0200 Subject: [PATCH 073/202] cleanup code a bit --- .../gpui/src/platform/windows/direct_write.rs | 474 ++++++------------ 1 file changed, 151 insertions(+), 323 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index d39b00280a..82b9058b80 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -40,16 +40,18 @@ struct DirectWriteComponent { in_memory_loader: IDWriteInMemoryFontFileLoader, builder: IDWriteFontSetBuilder1, text_renderer: Arc, - render_context: GlyphRenderContext, -} -struct GlyphRenderContext { - params: IDWriteRenderingParams3, + render_params: IDWriteRenderingParams3, + gpu_state: GPUState, } struct GPUState { device: ID3D11Device, device_context: ID3D11DeviceContext, + sampler: [Option; 1], + blend_state: ID3D11BlendState, + vertex_shader: ID3D11VertexShader, + pixel_shader: ID3D11PixelShader, } struct Syncer(T); @@ -57,7 +59,6 @@ unsafe impl Send for Syncer {} unsafe impl Sync for Syncer {} struct DirectWriteState { - gpu_state: GPUState, #[cfg(feature = "enable-renderdoc")] renderdoc: Syncer>>>, components: DirectWriteComponent, @@ -77,7 +78,8 @@ struct FontIdentifier { } impl DirectWriteComponent { - pub fn new(bitmap_factory: &IWICImagingFactory) -> Result { + pub fn new(bitmap_factory: &IWICImagingFactory, gpu_context: &DirectXDevices) -> Result { + // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing unsafe { let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let bitmap_factory = AgileReference::new(bitmap_factory)?; @@ -91,7 +93,28 @@ impl DirectWriteComponent { GetUserDefaultLocaleName(&mut locale_vec); let locale = String::from_utf16_lossy(&locale_vec); let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); - let render_context = GlyphRenderContext::new(&factory)?; + + let render_params = { + let default_params: IDWriteRenderingParams3 = + factory.CreateRenderingParams()?.cast()?; + let gamma = default_params.GetGamma(); + let enhanced_contrast = default_params.GetEnhancedContrast(); + let gray_contrast = default_params.GetGrayscaleEnhancedContrast(); + let cleartype_level = default_params.GetClearTypeLevel(); + let grid_fit_mode = default_params.GetGridFitMode(); + + factory.CreateCustomRenderingParams( + gamma, + enhanced_contrast, + gray_contrast, + cleartype_level, + DWRITE_PIXEL_GEOMETRY_RGB, + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, + grid_fit_mode, + )? + }; + + let gpu_state = GPUState::new(gpu_context)?; Ok(DirectWriteComponent { locale, @@ -100,35 +123,102 @@ impl DirectWriteComponent { in_memory_loader, builder, text_renderer, - render_context, + render_params, + gpu_state, }) } } } -impl GlyphRenderContext { - pub fn new(factory: &IDWriteFactory5) -> Result { - unsafe { - let default_params: IDWriteRenderingParams3 = - factory.CreateRenderingParams()?.cast()?; - let gamma = default_params.GetGamma(); - let enhanced_contrast = default_params.GetEnhancedContrast(); - let gray_contrast = default_params.GetGrayscaleEnhancedContrast(); - let cleartype_level = default_params.GetClearTypeLevel(); - let grid_fit_mode = default_params.GetGridFitMode(); +impl GPUState { + fn new(gpu_context: &DirectXDevices) -> Result { + // todo: safety comments + let device = gpu_context.device.clone(); + let device_context = gpu_context.device_context.clone(); - let params = factory.CreateCustomRenderingParams( - gamma, - enhanced_contrast, - gray_contrast, - cleartype_level, - DWRITE_PIXEL_GEOMETRY_RGB, - DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, - grid_fit_mode, - )?; + let blend_state = { + let mut blend_state = None; + let desc = D3D11_BLEND_DESC { + AlphaToCoverageEnable: false.into(), + IndependentBlendEnable: false.into(), + RenderTarget: [ + D3D11_RENDER_TARGET_BLEND_DESC { + BlendEnable: true.into(), + SrcBlend: D3D11_BLEND_SRC_ALPHA, + DestBlend: D3D11_BLEND_INV_SRC_ALPHA, + BlendOp: D3D11_BLEND_OP_ADD, + SrcBlendAlpha: D3D11_BLEND_SRC_ALPHA, + DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA, + BlendOpAlpha: D3D11_BLEND_OP_ADD, + RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8, + }, + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ], + }; + unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?; + blend_state.unwrap() + }; - Ok(Self { params }) - } + let sampler = { + let mut sampler = None; + let desc = D3D11_SAMPLER_DESC { + Filter: D3D11_FILTER_MIN_MAG_MIP_POINT, + AddressU: D3D11_TEXTURE_ADDRESS_BORDER, + AddressV: D3D11_TEXTURE_ADDRESS_BORDER, + AddressW: D3D11_TEXTURE_ADDRESS_BORDER, + MipLODBias: 0.0, + MaxAnisotropy: 1, + ComparisonFunc: D3D11_COMPARISON_ALWAYS, + BorderColor: [0.0, 0.0, 0.0, 0.0], + MinLOD: 0.0, + MaxLOD: 0.0, + }; + unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?; + [sampler] + }; + + let vertex_shader = { + let source = + shader_resources::build_shader_blob("color_text_raster", "vertex", "vs_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + source.GetBufferPointer() as *mut u8, + source.GetBufferSize(), + ) + }; + let mut shader = None; + unsafe { device.CreateVertexShader(bytes, None, Some(&mut shader)) }?; + shader.unwrap() + }; + + let pixel_shader = { + let source = + shader_resources::build_shader_blob("color_text_raster", "pixel", "ps_5_0")?; + let bytes = unsafe { + std::slice::from_raw_parts( + source.GetBufferPointer() as *mut u8, + source.GetBufferSize(), + ) + }; + let mut shader = None; + unsafe { device.CreatePixelShader(bytes, None, Some(&mut shader)) }?; + shader.unwrap() + }; + + Ok(Self { + device, + device_context, + sampler, + blend_state, + vertex_shader, + pixel_shader, + }) } } @@ -137,7 +227,7 @@ impl DirectWriteTextSystem { gpu_context: &DirectXDevices, bitmap_factory: &IWICImagingFactory, ) -> Result { - let components = DirectWriteComponent::new(bitmap_factory)?; + let components = DirectWriteComponent::new(bitmap_factory, gpu_context)?; let system_font_collection = unsafe { let mut result = std::mem::zeroed(); components @@ -153,13 +243,7 @@ impl DirectWriteTextSystem { }; let system_ui_font_name = get_system_ui_font_name(); - let gpu_state = GPUState { - device: gpu_context.device.clone(), - device_context: gpu_context.device_context.clone(), - }; - Ok(Self(RwLock::new(DirectWriteState { - gpu_state, #[cfg(feature = "enable-renderdoc")] renderdoc: Syncer(Arc::new(RwLock::new(renderdoc::RenderDoc::new().unwrap()))), components, @@ -831,10 +915,7 @@ impl DirectWriteState { let mut bitmap_data: Vec; if params.is_emoji { - // bitmap_data = vec![0u8; texture_width as usize * texture_height as usize * 4]; - - println!("trying to rasterize"); - let res = self.rasterize_color( + if let Ok(color) = self.rasterize_color( &glyph_run, rendering_mode, measuring_mode, @@ -842,186 +923,20 @@ impl DirectWriteState { point(baseline_origin_x, baseline_origin_y), bitmap_size, size(texture_width, texture_height), - ); - // // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering - // let color_enumerator = unsafe { - // self.components.factory.TranslateColorGlyphRun( - // Vector2::new(baseline_origin_x, baseline_origin_y), - // &glyph_run, - // None, - // DWRITE_GLYPH_IMAGE_FORMATS_COLR - // | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - // measuring_mode, - // Some(&transform), - // 0, - // ) - // }; - - // if let Ok(color_enumerator) = color_enumerator { - // loop { - // let color_run = unsafe { color_enumerator.GetCurrentRun() }; - // if let Ok(color_run) = color_run { - // let color_glyph_run = unsafe { &*color_run }; - // let color_value = color_glyph_run.Base.runColor; - - // // Create analysis for this color layer - // let color_analysis = unsafe { - // self.components.factory.CreateGlyphRunAnalysis( - // &color_glyph_run.Base.glyphRun as *const _, - // Some(&transform), - // rendering_mode, - // measuring_mode, - // DWRITE_GRID_FIT_MODE_DEFAULT, - // DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, - // baseline_origin_x, - // baseline_origin_y, - // ) - // }; - - // // todo: move this block completely to the gpu - // // this is important because fonts can bundle quite large icons - // // and compositing them on the cpu is quite expensive - // // also the code is ugly - // if let Ok(color_analysis) = color_analysis { - // let color_bounds = - // unsafe { color_analysis.GetAlphaTextureBounds(texture_type) }; - // if let Ok(color_bounds) = color_bounds { - // let color_width = (color_bounds.right - color_bounds.left) as u32; - // let color_height = (color_bounds.bottom - color_bounds.top) as u32; - - // if color_width > 0 && color_height > 0 { - // let mut alpha_data = - // vec![0u8; (color_width * color_height * 3) as usize]; - // if unsafe { - // color_analysis.CreateAlphaTexture( - // texture_type, - // &color_bounds, - // &mut alpha_data, - // ) - // } - // .is_ok() - // { - // let r = (color_value.r * 255.0) as u8; - // let g = (color_value.g * 255.0) as u8; - // let b = (color_value.b * 255.0) as u8; - // let a = (color_value.a * 255.0) as u8; - - // let offset_x = color_bounds.left.max(0) as usize; - // let offset_y = color_bounds.top.max(0) as usize; - - // for y in 0..color_height as usize { - // for x in 0..color_width as usize { - // let bitmap_x = offset_x + x; - // let bitmap_y = offset_y + y; - - // if bitmap_x < bitmap_size.width.0 as usize - // && bitmap_y < bitmap_size.height.0 as usize - // { - // let alpha_idx = - // (y * color_width as usize + x) * 3; - // let bitmap_idx = (bitmap_y - // * bitmap_size.width.0 as usize - // + bitmap_x) - // * 4; - - // if alpha_idx + 2 < alpha_data.len() - // && bitmap_idx + 3 < bitmap_data.len() - // { - // let alpha_value = (alpha_data[alpha_idx] - // as u32 - // + alpha_data[alpha_idx + 1] as u32 - // + alpha_data[alpha_idx + 2] as u32) - // / 3; - // let final_alpha = - // ((alpha_value * a as u32) / 255) as u8; - - // if final_alpha > 0 { - // let existing_r = - // bitmap_data[bitmap_idx]; - // let existing_g = - // bitmap_data[bitmap_idx + 1]; - // let existing_b = - // bitmap_data[bitmap_idx + 2]; - // let existing_a = - // bitmap_data[bitmap_idx + 3]; - - // let src_alpha = - // final_alpha as f32 / 255.0; - // let dst_alpha = - // existing_a as f32 / 255.0; - // let out_alpha = src_alpha - // + dst_alpha * (1.0 - src_alpha); - - // if out_alpha > 0.0 { - // bitmap_data[bitmap_idx] = - // ((r as f32 * src_alpha - // + existing_r as f32 - // * dst_alpha - // * (1.0 - src_alpha)) - // / out_alpha) - // as u8; - // bitmap_data[bitmap_idx + 1] = - // ((g as f32 * src_alpha - // + existing_g as f32 - // * dst_alpha - // * (1.0 - src_alpha)) - // / out_alpha) - // as u8; - // bitmap_data[bitmap_idx + 2] = - // ((b as f32 * src_alpha - // + existing_b as f32 - // * dst_alpha - // * (1.0 - src_alpha)) - // / out_alpha) - // as u8; - // bitmap_data[bitmap_idx + 3] = - // (out_alpha * 255.0) as u8; - // } - // } - // } - // } - // } - // } - // } - // } - // } - // } - // } - - // if !unsafe { color_enumerator.MoveNext() }?.as_bool() { - // break; - // } - // } - - // // bitmap_data.chunks_mut(4).for_each(|chunk| { - // // let tmp = chunk[2]; - // // chunk[2] = chunk[0]; - // // chunk[0] = tmp; - // // }); - - // std::fs::write( - // &format!( - // "{}x{}_{}_color.raw", - // texture_width, texture_height, params.glyph_id.0 - // ), - // &bitmap_data, - // ) - // .unwrap(); - // } else { - // let monochrome_data = Self::rasterize_monochrome( - // &glyph_analysis, - // bitmap_size, - // size(texture_width, texture_height), - // &texture_bounds, - // )?; - // // todo: monochrome emojis should be handled gracefully by the renderer - // // currently they just get drawn as their reported color because it assumes they are always colored - // // but in reality monochrome emojis should be colored the same as text is - // bitmap_data = monochrome_data - // .into_iter() - // .flat_map(|e| [0, 0, 0, e]) - // .collect::>(); - // } + ) { + bitmap_data = color; + } else { + let monochrome = Self::rasterize_monochrome( + &glyph_analysis, + bitmap_size, + size(texture_width, texture_height), + &texture_bounds, + )?; + bitmap_data = monochrome + .into_iter() + .flat_map(|pixel| [0, 0, 0, pixel]) + .collect::>(); + } } else { bitmap_data = Self::rasterize_monochrome( &glyph_analysis, @@ -1044,6 +959,8 @@ impl DirectWriteState { vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize]; + + // todo: this should just use the 1x1 format without manual converison as it's kinda useless here unsafe { glyph_analysis.CreateAlphaTexture( DWRITE_TEXTURE_CLEARTYPE_3x1, @@ -1156,7 +1073,7 @@ impl DirectWriteState { .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255]) .collect::>(); glyph_layers.push(GlyphLayerTexture::new( - &self.gpu_state, + &self.components.gpu_state, run_color, bounds, &alpha_data, @@ -1172,6 +1089,7 @@ impl DirectWriteState { } } + let gpu_state = &self.components.gpu_state; let params_buffer = { let desc = D3D11_BUFFER_DESC { ByteWidth: std::mem::size_of::() as u32, @@ -1184,31 +1102,13 @@ impl DirectWriteState { let mut buffer = None; unsafe { - self.gpu_state + gpu_state .device .CreateBuffer(&desc, None, Some(&mut buffer)) }?; [buffer] }; - let vertex_shader = { - let source = - shader_resources::build_shader_blob("color_text_raster", "vertex", "vs_5_0")?; - let bytes = unsafe { - std::slice::from_raw_parts( - source.GetBufferPointer() as *mut u8, - source.GetBufferSize(), - ) - }; - let mut shader = None; - unsafe { - self.gpu_state - .device - .CreateVertexShader(bytes, None, Some(&mut shader)) - }?; - shader.unwrap() - }; - let render_target_texture = { let mut texture = None; let desc = D3D11_TEXTURE2D_DESC { @@ -1227,7 +1127,7 @@ impl DirectWriteState { MiscFlags: 0, }; unsafe { - self.gpu_state + gpu_state .device .CreateTexture2D(&desc, None, Some(&mut texture)) }?; @@ -1244,7 +1144,7 @@ impl DirectWriteState { }; let mut rtv = None; unsafe { - self.gpu_state.device.CreateRenderTargetView( + gpu_state.device.CreateRenderTargetView( &render_target_texture, Some(&desc), Some(&mut rtv), @@ -1271,101 +1171,28 @@ impl DirectWriteState { MiscFlags: 0, }; unsafe { - self.gpu_state + gpu_state .device .CreateTexture2D(&desc, None, Some(&mut texture)) }?; texture.unwrap() }; - let blend_state = { - let mut blend_state = None; - let desc = D3D11_BLEND_DESC { - AlphaToCoverageEnable: false.into(), - IndependentBlendEnable: false.into(), - RenderTarget: [ - D3D11_RENDER_TARGET_BLEND_DESC { - BlendEnable: true.into(), - SrcBlend: D3D11_BLEND_SRC_ALPHA, - DestBlend: D3D11_BLEND_INV_SRC_ALPHA, - BlendOp: D3D11_BLEND_OP_ADD, - SrcBlendAlpha: D3D11_BLEND_ONE, - DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA, - BlendOpAlpha: D3D11_BLEND_OP_ADD, - RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8, - }, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ], - }; - unsafe { - self.gpu_state - .device - .CreateBlendState(&desc, Some(&mut blend_state)) - }?; - blend_state.unwrap() - }; - - let sampler = { - let mut sampler = None; - let desc = D3D11_SAMPLER_DESC { - Filter: D3D11_FILTER_MIN_MAG_MIP_POINT, - AddressU: D3D11_TEXTURE_ADDRESS_BORDER, - AddressV: D3D11_TEXTURE_ADDRESS_BORDER, - AddressW: D3D11_TEXTURE_ADDRESS_BORDER, - MipLODBias: 0.0, - MaxAnisotropy: 1, - ComparisonFunc: D3D11_COMPARISON_ALWAYS, - BorderColor: [0.0, 0.0, 0.0, 0.0], - MinLOD: 0.0, - MaxLOD: 0.0, - }; - unsafe { - self.gpu_state - .device - .CreateSamplerState(&desc, Some(&mut sampler)) - }?; - [sampler] - }; - - let pixel_shader = { - let source = - shader_resources::build_shader_blob("color_text_raster", "pixel", "ps_5_0")?; - let bytes = unsafe { - std::slice::from_raw_parts( - source.GetBufferPointer() as *mut u8, - source.GetBufferSize(), - ) - }; - let mut shader = None; - unsafe { - self.gpu_state - .device - .CreatePixelShader(bytes, None, Some(&mut shader)) - }?; - shader.unwrap() - }; - #[cfg(feature = "enable-renderdoc")] self.renderdoc .0 .write() .start_frame_capture(std::ptr::null(), std::ptr::null()); - let device_context = &self.gpu_state.device_context; + let device_context = &gpu_state.device_context; unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) }; - unsafe { device_context.VSSetShader(&vertex_shader, None) }; - unsafe { device_context.PSSetShader(&pixel_shader, None) }; + unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) }; + unsafe { device_context.PSSetShader(&gpu_state.pixel_shader, None) }; unsafe { device_context.VSSetConstantBuffers(0, Some(¶ms_buffer)) }; unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_buffer)) }; unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) }; - unsafe { device_context.PSSetSamplers(0, Some(&sampler)) }; - unsafe { device_context.OMSetBlendState(&blend_state, None, 0xffffffff) }; + unsafe { device_context.PSSetSamplers(0, Some(&gpu_state.sampler)) }; + unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) }; for layer in glyph_layers { let params = GlyphLayerTextureParams { @@ -1374,7 +1201,7 @@ impl DirectWriteState { }; unsafe { let mut dest = std::mem::zeroed(); - self.gpu_state.device_context.Map( + gpu_state.device_context.Map( params_buffer[0].as_ref().unwrap(), 0, D3D11_MAP_WRITE_DISCARD, @@ -1382,7 +1209,7 @@ impl DirectWriteState { Some(&mut dest), )?; std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1); - self.gpu_state + gpu_state .device_context .Unmap(params_buffer[0].as_ref().unwrap(), 0); }; @@ -1530,6 +1357,7 @@ impl GlyphLayerTexture { bounds: Bounds, alpha_data: &[u8], ) -> Result { + // todo: ideally we would have a nice texture wrapper let texture_size = bounds.size; let desc = D3D11_TEXTURE2D_DESC { From 68780da673e7b5971245c15a1837ef661ecd67cf Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 21 Jul 2025 13:35:21 -0700 Subject: [PATCH 074/202] Pass scale factor transform to glyph analysis when computing bounds rather than simply multiplying every rect field by the scale factor. This fixes clipping of glyphs and removes the need for magic numbers expanding the bounds vertically. Co-authored-by: Julia Ryan --- .../gpui/src/platform/windows/direct_write.rs | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 82b9058b80..a86c6d5d68 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -759,10 +759,19 @@ impl DirectWriteState { let baseline_origin_x = 0.0; let baseline_origin_y = 0.0; + let transform = DWRITE_MATRIX { + m11: params.scale_factor, + m12: 0.0, + m21: 0.0, + m22: params.scale_factor, + dx: 0.0, + dy: 0.0, + }; + let glyph_analysis = unsafe { self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - None, + Some(&transform), rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, @@ -775,19 +784,6 @@ impl DirectWriteState { let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - // todo(windows) - // This is a walkaround, deleted when figured out. - let y_offset; - let extra_height; - if params.is_emoji { - y_offset = 0; - extra_height = 0; - } else { - // make some room for scaler. - y_offset = -1; - extra_height = 2; - } - if bounds.right < bounds.left { Ok(Bounds { origin: point(0.into(), 0.into()), @@ -795,16 +791,10 @@ impl DirectWriteState { }) } else { Ok(Bounds { - origin: point( - ((bounds.left as f32 * params.scale_factor).ceil() as i32).into(), - ((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(), - ), + origin: point((bounds.left as i32).into(), (bounds.top as i32).into()), size: size( - (((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32) - .into(), - (((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32 - + extra_height) - .into(), + (bounds.right - bounds.left).into(), + (bounds.bottom - bounds.top).into(), ), }) } @@ -922,7 +912,6 @@ impl DirectWriteState { &transform, point(baseline_origin_x, baseline_origin_y), bitmap_size, - size(texture_width, texture_height), ) { bitmap_data = color; } else { @@ -960,7 +949,6 @@ impl DirectWriteState { let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize]; - // todo: this should just use the 1x1 format without manual converison as it's kinda useless here unsafe { glyph_analysis.CreateAlphaTexture( DWRITE_TEXTURE_CLEARTYPE_3x1, @@ -1006,7 +994,6 @@ impl DirectWriteState { transform: &DWRITE_MATRIX, baseline_origin: Point, bitmap_size: Size, - texture_size: Size, ) -> Result> { // todo: support formats other than COLR let color_enumerator = unsafe { From 7d84014ad24bc9179a7d52f0eab02c27b16a133c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 21 Jul 2025 13:58:59 -0700 Subject: [PATCH 075/202] Update cargo lock --- Cargo.lock | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c07d311db9..e2b65dfbf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5641,6 +5641,9 @@ name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] [[package]] name = "float-ord" @@ -7271,6 +7274,7 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "refineable", + "renderdoc", "reqwest_client", "resvg", "scap", @@ -13330,6 +13334,27 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "renderdoc" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac633a08f39bf3268714799bd8c4a1a19c19c203d817d3448bb8b91c97817cf0" +dependencies = [ + "bitflags 2.9.0", + "float-cmp", + "libloading", + "once_cell", + "renderdoc-sys", + "winapi", + "wio", +] + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + [[package]] name = "repl" version = "0.1.0" From 7186f1322efd5245166e896e586256fe9e186c6d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 12:32:59 +0800 Subject: [PATCH 076/202] init --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ea01003f36..e090721e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -674,8 +674,12 @@ features = [ "Win32_Globalization", "Win32_Graphics_Direct2D", "Win32_Graphics_Direct2D_Common", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Direct3D_Fxc", "Win32_Graphics_DirectWrite", "Win32_Graphics_Dwm", + "Win32_Graphics_Dxgi", "Win32_Graphics_Dxgi_Common", "Win32_Graphics_Gdi", "Win32_Graphics_Imaging", From c0bad42968eb72a70db7771d562b42e4422086c7 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 12:49:05 +0800 Subject: [PATCH 077/202] wip --- crates/gpui/src/platform/windows.rs | 4 + .../src/platform/windows/directx_atlas.rs | 296 +++++ .../src/platform/windows/directx_renderer.rs | 1170 +++++++++++++++++ 3 files changed, 1470 insertions(+) create mode 100644 crates/gpui/src/platform/windows/directx_atlas.rs create mode 100644 crates/gpui/src/platform/windows/directx_renderer.rs diff --git a/crates/gpui/src/platform/windows.rs b/crates/gpui/src/platform/windows.rs index 4bdf42080d..5268d3ccba 100644 --- a/crates/gpui/src/platform/windows.rs +++ b/crates/gpui/src/platform/windows.rs @@ -1,6 +1,8 @@ mod clipboard; mod destination_list; mod direct_write; +mod directx_atlas; +mod directx_renderer; mod dispatcher; mod display; mod events; @@ -14,6 +16,8 @@ mod wrapper; pub(crate) use clipboard::*; pub(crate) use destination_list::*; pub(crate) use direct_write::*; +pub(crate) use directx_atlas::*; +pub(crate) use directx_renderer::*; pub(crate) use dispatcher::*; pub(crate) use display::*; pub(crate) use events::*; diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs new file mode 100644 index 0000000000..4bb903a463 --- /dev/null +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -0,0 +1,296 @@ +use collections::FxHashMap; +use etagere::BucketedAtlasAllocator; +use parking_lot::Mutex; +use windows::Win32::Graphics::{ + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, + ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Texture2D, + }, + Dxgi::Common::{ + DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R16_FLOAT, DXGI_SAMPLE_DESC, + }, +}; + +use crate::*; + +pub(crate) struct DirectXAtlas(Mutex); + +struct DirectXAtlasState { + device: ID3D11Device, + device_context: ID3D11DeviceContext, + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, + tiles_by_key: FxHashMap, +} + +struct DirectXAtlasTexture { + id: AtlasTextureId, + bytes_per_pixel: u32, + allocator: BucketedAtlasAllocator, + texture: ID3D11Texture2D, + rtv: [Option; 1], + view: [Option; 1], +} + +impl DirectXAtlas { + pub(crate) fn new(device: ID3D11Device, device_context: ID3D11DeviceContext) -> Self { + DirectXAtlas(Mutex::new(DirectXAtlasState { + device, + device_context, + monochrome_textures: Default::default(), + polychrome_textures: Default::default(), + path_textures: Default::default(), + tiles_by_key: Default::default(), + })) + } + + pub(crate) fn get_texture_drawing_info( + &self, + id: AtlasTextureId, + ) -> (Size, [Option; 1]) { + let lock = self.0.lock(); + let tex = lock.texture(id); + let size = tex.allocator.size(); + ( + Size { + width: size.width as f32, + height: size.height as f32, + }, + tex.rtv.clone(), + ) + } + + pub(crate) fn get_texture_view( + &self, + id: AtlasTextureId, + ) -> [Option; 1] { + let lock = self.0.lock(); + 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, + AtlasTextureKind::Path => &mut lock.path_textures, + }; + for texture in textures { + texture.clear(); + } + } +} + +impl PlatformAtlas for DirectXAtlas { + fn get_or_insert_with<'a>( + &self, + key: &AtlasKey, + build: &mut dyn FnMut() -> anyhow::Result< + Option<(Size, std::borrow::Cow<'a, [u8]>)>, + >, + ) -> anyhow::Result> { + let mut lock = self.0.lock(); + if let Some(tile) = lock.tiles_by_key.get(key) { + Ok(Some(tile.clone())) + } else { + let Some((size, bytes)) = build()? else { + return Ok(None); + }; + let tile = lock + .allocate(size, key.texture_kind()) + .ok_or_else(|| anyhow::anyhow!("failed to allocate"))?; + let texture = lock.texture(tile.texture_id); + texture.upload(&lock.device_context, tile.bounds, &bytes); + lock.tiles_by_key.insert(key.clone(), tile.clone()); + Ok(Some(tile)) + } + } + + fn remove(&self, key: &AtlasKey) { + todo!() + } +} + +impl DirectXAtlasState { + fn allocate( + &mut self, + size: Size, + texture_kind: AtlasTextureKind, + ) -> Option { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + + textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + .or_else(|| { + let texture = self.push_texture(size, texture_kind); + texture.allocate(size) + }) + } + + fn push_texture( + &mut self, + min_size: Size, + kind: AtlasTextureKind, + ) -> &mut DirectXAtlasTexture { + const DEFAULT_ATLAS_SIZE: Size = Size { + width: DevicePixels(1024), + height: DevicePixels(1024), + }; + // Max texture size for DirectX. See: + // https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits + const MAX_ATLAS_SIZE: Size = Size { + width: DevicePixels(16384), + height: DevicePixels(16384), + }; + let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE); + let pixel_format; + let bind_flag; + let bytes_per_pixel; + match kind { + AtlasTextureKind::Monochrome => { + pixel_format = DXGI_FORMAT_A8_UNORM; + bind_flag = D3D11_BIND_SHADER_RESOURCE; + bytes_per_pixel = 1; + } + AtlasTextureKind::Polychrome => { + 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; + } + } + let texture_desc = D3D11_TEXTURE2D_DESC { + Width: size.width.0 as u32, + Height: size.height.0 as u32, + MipLevels: 1, + ArraySize: 1, + Format: pixel_format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: bind_flag.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + let mut texture: Option = None; + unsafe { + self.device + .CreateTexture2D(&texture_desc, None, Some(&mut texture)) + .unwrap(); + } + let texture = texture.unwrap(); + + let textures = match kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_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] + }, + _ => [None], + }; + let view = unsafe { + let mut view = None; + self.device + .CreateShaderResourceView(&texture, None, Some(&mut view)) + .unwrap(); + [view] + }; + let atlas_texture = DirectXAtlasTexture { + id: AtlasTextureId { + index: textures.len() as u32, + kind, + }, + bytes_per_pixel, + allocator: etagere::BucketedAtlasAllocator::new(size.into()), + texture, + rtv, + view, + }; + textures.push(atlas_texture); + textures.last_mut().unwrap() + } + + fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + &textures[id.index as usize] + } +} + +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 { + texture_id: self.id, + tile_id: allocation.id.into(), + bounds: Bounds { + origin: allocation.rectangle.min.into(), + size, + }, + padding: 0, + }; + Some(tile) + } + + fn upload( + &self, + device_context: &ID3D11DeviceContext, + bounds: Bounds, + bytes: &[u8], + ) { + unsafe { + device_context.UpdateSubresource( + &self.texture, + 0, + Some(&D3D11_BOX { + left: bounds.left().0 as u32, + top: bounds.top().0 as u32, + front: 0, + right: bounds.right().0 as u32, + bottom: bounds.bottom().0 as u32, + back: 1, + }), + bytes.as_ptr() as _, + bounds.size.width.to_bytes(self.bytes_per_pixel as u8), + 0, + ); + } + } +} diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs new file mode 100644 index 0000000000..d135bd4f33 --- /dev/null +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -0,0 +1,1170 @@ +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::*; +use windows::{ + Win32::{ + Foundation::HWND, + Graphics::{ + Direct3D::*, + Direct3D11::*, + Dxgi::{Common::*, *}, + }, + }, + core::*, +}; + +use crate::*; + +pub(crate) struct DirectXRenderer { + atlas: Arc, + devices: DirectXDevices, + context: DirectXContext, + globals: DirectXGlobalElements, + pipelines: DirectXRenderPipelines, + hwnd: HWND, + transparent: bool, +} + +#[derive(Clone)] +pub(crate) struct DirectXDevices { + dxgi_factory: IDXGIFactory6, + dxgi_device: IDXGIDevice, + device: ID3D11Device, + device_context: ID3D11DeviceContext, +} + +struct DirectXContext { + swap_chain: IDXGISwapChain1, + back_buffer: [Option; 1], + viewport: [D3D11_VIEWPORT; 1], + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition: DirectComposition, +} + +struct DirectXRenderPipelines { + shadow_pipeline: PipelineState, + quad_pipeline: PipelineState, + path_raster_pipeline: PipelineState, + paths_pipeline: PipelineState, + 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, +} + +#[cfg(not(feature = "enable-renderdoc"))] +struct DirectComposition { + comp_device: IDCompositionDevice, + comp_target: IDCompositionTarget, + comp_visual: IDCompositionVisual, +} + +impl DirectXDevices { + pub(crate) fn new() -> Result { + let dxgi_factory = get_dxgi_factory()?; + let adapter = get_adapter(&dxgi_factory)?; + let (device, device_context) = { + let mut device: Option = None; + let mut context: Option = None; + get_device(&adapter, Some(&mut device), Some(&mut context))?; + (device.unwrap(), context.unwrap()) + }; + let dxgi_device: IDXGIDevice = device.cast()?; + + Ok(Self { + dxgi_factory, + dxgi_device, + device, + device_context, + }) + } +} + +impl DirectXRenderer { + 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 globals = DirectXGlobalElements::new(&devices.device)?; + let pipelines = DirectXRenderPipelines::new(&devices.device)?; + Ok(DirectXRenderer { + atlas, + devices, + context, + globals, + pipelines, + hwnd, + transparent, + }) + } + + pub(crate) fn spirite_atlas(&self) -> Arc { + self.atlas.clone() + } + + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { + return Err(anyhow::anyhow!( + "failed to rasterize {} paths", + scene.paths().len() + )); + }; + pre_draw( + &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, + )?; + for batch in scene.batches() { + match batch { + PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), + PrimitiveBatch::Quads(quads) => self.draw_quads(quads), + PrimitiveBatch::Paths(paths) => self.draw_paths(paths, &path_tiles), + PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines), + PrimitiveBatch::MonochromeSprites { + texture_id, + sprites, + } => self.draw_monochrome_sprites(texture_id, sprites), + PrimitiveBatch::PolychromeSprites { + texture_id, + sprites, + } => self.draw_polychrome_sprites(texture_id, sprites), + PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces), + }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces", + scene.paths.len(), + scene.shadows.len(), + scene.quads.len(), + scene.underlines.len(), + scene.monochrome_sprites.len(), + scene.polychrome_sprites.len(), + scene.surfaces.len(),))?; + } + unsafe { self.context.swap_chain.Present(0, 0) }.ok()?; + Ok(()) + } + + 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()); + unsafe { + self.context.swap_chain.ResizeBuffers( + BUFFER_COUNT as u32, + new_size.width.0 as u32, + new_size.height.0 as u32, + DXGI_FORMAT_B8G8R8A8_UNORM, + 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(()) + } + } + + 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.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.devices.device_context, + &self.pipelines.shadow_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + shadows.len() as u32, + ) + } + + fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> { + if quads.is_empty() { + return Ok(()); + } + update_buffer_capacity( + &self.pipelines.quad_pipeline, + std::mem::size_of::(), + quads.len(), + &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.devices.device_context, + &self.pipelines.quad_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + quads.len() as u32, + ) + } + + fn rasterize_paths( + &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) + } + + fn draw_paths( + &mut self, + 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, + )?; + } + 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.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.devices.device_context, + &self.pipelines.underline_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, + underlines.len() as u32, + ) + } + + fn draw_monochrome_sprites( + &mut self, + texture_id: AtlasTextureId, + sprites: &[MonochromeSprite], + ) -> Result<()> { + 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.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( + &self.devices.device_context, + &self.pipelines.mono_sprites, + &texture_view, + &self.context.viewport, + &self.globals.global_params_buffer, + &self.globals.sampler, + sprites.len() as u32, + ) + } + + fn draw_polychrome_sprites( + &mut self, + texture_id: AtlasTextureId, + sprites: &[PolychromeSprite], + ) -> Result<()> { + 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.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( + &self.devices.device_context, + &self.pipelines.poly_sprites, + &texture_view, + &self.context.viewport, + &self.globals.global_params_buffer, + &self.globals.sampler, + sprites.len() as u32, + ) + } + + fn draw_surfaces(&mut self, surfaces: &[Surface]) -> Result<()> { + if surfaces.is_empty() { + return Ok(()); + } + Ok(()) + } +} + +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")] + 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); + set_rasterizer_state(&devices.device, &devices.device_context)?; + + Ok(Self { + swap_chain, + back_buffer, + viewport, + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition, + }) + } +} + +impl DirectXRenderPipelines { + pub fn new(device: &ID3D11Device) -> Result { + let shadow_pipeline = create_pipieline( + device, + "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 path_raster_pipeline = create_pipieline( + device, + "path_rasterization_vertex", + "path_rasterization_fragment", + std::mem::size_of::>(), + 32, + )?; + let paths_pipeline = create_pipieline( + device, + "paths_vertex", + "paths_fragment", + std::mem::size_of::(), + 1, + )?; + let underline_pipeline = create_pipieline( + device, + "underline_vertex", + "underline_fragment", + std::mem::size_of::(), + 32, + )?; + let mono_sprites = create_pipieline( + device, + "monochrome_sprite_vertex", + "monochrome_sprite_fragment", + std::mem::size_of::(), + 32, + )?; + let poly_sprites = create_pipieline( + device, + "polychrome_sprite_vertex", + "polychrome_sprite_fragment", + std::mem::size_of::(), + 32, + )?; + + Ok(Self { + shadow_pipeline, + quad_pipeline, + path_raster_pipeline, + paths_pipeline, + underline_pipeline, + mono_sprites, + poly_sprites, + }) + } +} + +#[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 { + let global_params_buffer = unsafe { + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + ..Default::default() + }; + let mut buffer = None; + device.CreateBuffer(&desc, None, Some(&mut buffer))?; + [buffer] + }; + + let sampler = unsafe { + let desc = D3D11_SAMPLER_DESC { + Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR, + AddressU: D3D11_TEXTURE_ADDRESS_WRAP, + AddressV: D3D11_TEXTURE_ADDRESS_WRAP, + AddressW: D3D11_TEXTURE_ADDRESS_WRAP, + MipLODBias: 0.0, + MaxAnisotropy: 1, + ComparisonFunc: D3D11_COMPARISON_ALWAYS, + BorderColor: [0.0; 4], + MinLOD: 0.0, + MaxLOD: D3D11_FLOAT32_MAX, + }; + let mut output = None; + device.CreateSamplerState(&desc, Some(&mut output))?; + [output] + }; + + 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, + }) + } +} + +#[derive(Debug, Default)] +#[repr(C)] +struct GlobalParams { + viewport_size: [f32; 2], + _pad: u64, +} + +struct PipelineState { + vertex: ID3D11VertexShader, + fragment: ID3D11PixelShader, + buffer: ID3D11Buffer, + buffer_size: usize, + view: [Option; 1], +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(C)] +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} + +fn get_dxgi_factory() -> Result { + #[cfg(debug_assertions)] + let factory_flag = DXGI_CREATE_FACTORY_DEBUG; + #[cfg(not(debug_assertions))] + let factory_flag = 0u32; + unsafe { Ok(CreateDXGIFactory2(factory_flag)?) } +} + +fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result { + for adapter_index in 0.. { + let adapter: IDXGIAdapter1 = unsafe { + dxgi_factory + .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER) + }?; + { + let desc = unsafe { adapter.GetDesc1() }?; + println!( + "Select GPU: {}", + String::from_utf16_lossy(&desc.Description) + ); + } + // Check to see whether the adapter supports Direct3D 11, but don't + // create the actual device yet. + if get_device(&adapter, None, None).log_err().is_some() { + return Ok(adapter); + } + } + + unreachable!() +} + +fn get_device( + adapter: &IDXGIAdapter1, + device: Option<*mut Option>, + context: Option<*mut Option>, +) -> Result<()> { + #[cfg(debug_assertions)] + let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG; + #[cfg(not(debug_assertions))] + let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + Ok(unsafe { + D3D11CreateDevice( + adapter, + D3D_DRIVER_TYPE_UNKNOWN, + None, + device_flags, + Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]), + D3D11_SDK_VERSION, + device, + None, + context, + )? + }) +} + +#[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( + dxgi_factory: &IDXGIFactory6, + device: &ID3D11Device, + hwnd: HWND, + _transparent: bool, +) -> 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, + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: BUFFER_COUNT as u32, + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, + Flags: 0, + }; + let swap_chain = + unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; + unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; + Ok(swap_chain) +} + +fn set_render_target_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() + }; + unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; + Ok(back_buffer) +} + +fn set_viewport( + device_context: &ID3D11DeviceContext, + width: f32, + height: f32, +) -> [D3D11_VIEWPORT; 1] { + let viewport = [D3D11_VIEWPORT { + TopLeftX: 0.0, + TopLeftY: 0.0, + Width: width, + Height: height, + MinDepth: 0.0, + MaxDepth: 1.0, + }]; + unsafe { device_context.RSSetViewports(Some(&viewport)) }; + viewport +} + +fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> { + let desc = D3D11_RASTERIZER_DESC { + FillMode: D3D11_FILL_SOLID, + CullMode: D3D11_CULL_NONE, + FrontCounterClockwise: false.into(), + DepthBias: 0, + DepthBiasClamp: 0.0, + SlopeScaledDepthBias: 0.0, + DepthClipEnable: true.into(), + ScissorEnable: false.into(), + MultisampleEnable: false.into(), + AntialiasedLineEnable: false.into(), + }; + let rasterizer_state = unsafe { + let mut state = None; + device.CreateRasterizerState(&desc, Some(&mut state))?; + state.unwrap() + }; + unsafe { device_context.RSSetState(&rasterizer_state) }; + Ok(()) +} + +// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc +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. + 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_SRC_ALPHA; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + 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_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, + }) +} + +fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { + unsafe { + let mut shader = None; + device.CreateVertexShader(bytes, None, Some(&mut shader))?; + Ok(shader.unwrap()) + } +} + +fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { + unsafe { + let mut shader = None; + device.CreatePixelShader(bytes, None, Some(&mut shader))?; + Ok(shader.unwrap()) + } +} + +fn create_buffer( + device: &ID3D11Device, + element_size: usize, + buffer_size: usize, +) -> Result { + let desc = D3D11_BUFFER_DESC { + ByteWidth: (element_size * buffer_size) as u32, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32, + StructureByteStride: element_size as u32, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) +} + +fn create_buffer_view( + device: &ID3D11Device, + buffer: &ID3D11Buffer, +) -> Result<[Option; 1]> { + let mut view = None; + unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; + Ok([view]) +} + +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; +} + +fn update_buffer( + device_context: &ID3D11DeviceContext, + buffer: &ID3D11Buffer, + data: &[T], +) -> 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 draw_normal( + device_context: &ID3D11DeviceContext, + pipeline: &PipelineState, + viewport: &[D3D11_VIEWPORT], + 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.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)); + + 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; + +mod shader_resources { + use anyhow::Result; + use windows::Win32::Graphics::Direct3D::{ + Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + ID3DBlob, + }; + use windows_core::{HSTRING, PCSTR}; + + pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + unsafe { + let mut entry = entry.to_owned(); + let mut target = target.to_owned(); + let mut compile_blob = None; + let mut error_blob = None; + let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("src/platform/windows/shaders.hlsl") + .canonicalize() + .unwrap(); + entry.push_str("\0"); + target.push_str("\0"); + let entry_point = PCSTR::from_raw(entry.as_ptr()); + let target_cstr = PCSTR::from_raw(target.as_ptr()); + #[cfg(debug_assertions)] + let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; + #[cfg(not(debug_assertions))] + let compile_flag = 0; + let ret = D3DCompileFromFile( + &HSTRING::from(shader_path.to_str().unwrap()), + None, + None, + entry_point, + target_cstr, + compile_flag, + 0, + &mut compile_blob, + Some(&mut error_blob), + ); + if ret.is_err() { + let Some(error_blob) = error_blob else { + return Err(anyhow::anyhow!("{ret:?}")); + }; + let string_len = error_blob.GetBufferSize(); + let error_string_encode = Vec::from_raw_parts( + error_blob.GetBufferPointer() as *mut u8, + string_len, + string_len, + ); + return Err(anyhow::anyhow!( + "Compile error: {}", + String::from_utf8_lossy(&error_string_encode) + )); + } + Ok(compile_blob.unwrap()) + } + } +} 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 078/202] wip --- .../src/platform/windows/directx_atlas.rs | 33 +- .../src/platform/windows/directx_renderer.rs | 369 +++++----- 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, 904 insertions(+), 220 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 4bb903a463..03d33f5cbc 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 d135bd4f33..4a7f3796d7 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(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(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); + // 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 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); - } + // 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) + // 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() }?; +// #[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, - }) - } +// 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(()) - } -} +// 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 839fd10375..461aae5e25 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 401ecdeffe..a0bdb6dedf 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 0000000000..bb6342d6fb --- /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 5703a82815..2801674a64 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) { From a48ae50e1aa518cf44730648143ac763bccfdbc6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 13:28:52 +0800 Subject: [PATCH 079/202] apply #15782 --- crates/gpui/src/platform/windows/directx_renderer.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4a7f3796d7..6f2797d182 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -115,12 +115,12 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { - return Err(anyhow::anyhow!( - "failed to rasterize {} paths", - scene.paths().len() - )); - }; + // let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { + // return Err(anyhow::anyhow!( + // "failed to rasterize {} paths", + // scene.paths().len() + // )); + // }; pre_draw( &self.devices.device_context, &self.globals.global_params_buffer, From 528718366775a51647f2397678f9c7eef1dad342 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 16:34:16 +0800 Subject: [PATCH 080/202] apply #20812 --- crates/gpui/src/platform/windows/shaders.hlsl | 231 ++++++++++++++++-- 1 file changed, 207 insertions(+), 24 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index bb6342d6fb..0234f6bdc7 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -32,6 +32,30 @@ struct Hsla { float a; }; +struct LinearColorStop { + Hsla color; + float percentage; +}; + +struct Background { + // 0u is Solid + // 1u is LinearGradient + uint tag; + // 0u is sRGB linear color + // 1u is Oklab color + uint color_space; + Hsla solid; + float angle; + LinearColorStop colors[2]; + uint pad; +}; + +struct GradientColor { + float4 solid; + float4 color0; + float4 color1; +}; + struct AtlasTextureId { uint index; uint kind; @@ -71,6 +95,17 @@ float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bo clip_bounds.origin.y + clip_bounds.size.y - position.y); } +// Convert linear RGB to sRGB +float3 linear_to_srgb(float3 color) { + return pow(color, float3(2.2)); +} + +// Convert sRGB to linear RGB +float3 srgb_to_linear(float3 color) { + return pow(color, float3(1.0 / 2.2)); +} + +/// Hsla to linear RGBA conversion. 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; @@ -119,6 +154,48 @@ float4 hsla_to_rgba(Hsla hsla) { return rgba; } +// Converts a sRGB color to the Oklab color space. +// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab +float4 srgb_to_oklab(float4 color) { + // Convert non-linear sRGB to linear sRGB + color = float4(srgb_to_linear(color.rgb), color.a); + + float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b; + float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b; + float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b; + + float l_ = pow(l, 1.0/3.0); + float m_ = pow(m, 1.0/3.0); + float s_ = pow(s, 1.0/3.0); + + return float4( + 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_, + 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_, + 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_, + color.a + ); +} + +// Converts an Oklab color to the sRGB color space. +float4 oklab_to_srgb(float4 color) { + float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b; + float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b; + float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b; + + float l = l_ * l_ * l_; + float m = m_ * m_ * m_; + float s = s_ * s_ * s_; + + float3 linear_rgb = float3( + 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, + -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, + -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s + ); + + // Convert linear sRGB to non-linear sRGB + return float4(linear_to_srgb(linear_rgb), color.a); +} + // This approximates the error function, needed for the gaussian integral float2 erf(float2 x) { float2 s = sign(x); @@ -190,6 +267,83 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { return distance; } +GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { + GradientColor res; + if (tag == 0) { + res.solid = hsla_to_rgba(solid); + } else if (tag == 1) { + res.color0 = hsla_to_rgba(color0); + res.color1 = hsla_to_rgba(color1); + + // Prepare color space in vertex for avoid conversion + // in fragment shader for performance reasons + if (color_space == 1) { + // Oklab + res.color0 = srgb_to_oklab(res.color0); + res.color1 = srgb_to_oklab(res.color1); + } + } + + return res; +} + +float4 gradient_color(Background background, + float2 position, + Bounds bounds, + float4 solid_color, float4 color0, float4 color1) { + float4 color; + + switch (background.tag) { + case 0: + color = solid_color; + break; + case 1: { + // -90 degrees to match the CSS gradient angle. + float radians = (fmod(background.angle, 360.0) - 90.0) * (M_PI_F / 180.0); + float2 direction = float2(cos(radians), sin(radians)); + + // Expand the short side to be the same as the long side + if (bounds.size.x > bounds.size.y) { + direction.y *= bounds.size.y / bounds.size.x; + } else { + direction.x *= bounds.size.x / bounds.size.y; + } + + // 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 center_to_point = position - center; + float t = dot(center_to_point, direction) / length(direction); + // Check the direct to determine the use x or y + if (abs(direction.x) > abs(direction.y)) { + t = (t + half_size.x) / bounds.size.x; + } else { + t = (t + half_size.y) / bounds.size.y; + } + + // Adjust t based on the stop percentages + t = (t - background.colors[0].percentage) + / (background.colors[1].percentage + - background.colors[0].percentage); + t = clamp(t, 0.0, 1.0); + + switch (background.color_space) { + case 0: + color = lerp(color0, color1, t); + break; + case 1: { + float4 oklab_color = lerp(color0, color1, t); + color = oklab_to_srgb(oklab_color); + break; + } + } + break; + } + } + + return color; +} + /* ** ** Shadows @@ -294,7 +448,7 @@ struct Quad { uint pad; Bounds bounds; Bounds content_mask; - Hsla background; + Background background; Hsla border_color; Corners corner_radii; Edges border_widths; @@ -302,17 +456,22 @@ struct Quad { struct QuadVertexOutput { float4 position: SV_Position; - float4 background_color: COLOR0; - float4 border_color: COLOR1; + // float4 border_color: COLOR0; + float4 border_color: FLAT; uint quad_id: FLAT; + float4 background_solid: FLAT; + float4 background_color0: FLAT; + float4 background_color1: FLAT; float4 clip_distance: SV_ClipDistance; }; struct QuadFragmentInput { - float4 position: SV_Position; - float4 background_color: COLOR0; - float4 border_color: COLOR1; uint quad_id: FLAT; + float4 position: SV_Position; + float4 border_color: FLAT; + float4 background_solid: FLAT; + float4 background_color0: FLAT; + float4 background_color1: FLAT; }; StructuredBuffer quads: register(t1); @@ -322,20 +481,34 @@ QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_Insta 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); + 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 + ); + QuadVertexOutput output; output.position = device_position; - output.background_color = background_color; output.border_color = border_color; output.quad_id = quad_id; + output.background_solid = gradient.solid; + output.background_color0 = gradient.color0; + output.background_color1 = gradient.color1; output.clip_distance = clip_distance; return output; } 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, + 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. && @@ -343,12 +516,9 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { 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; + return 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.) { @@ -385,16 +555,12 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { border_width = vertical_border; } - float4 color; - if (border_width == 0.) { - color = input.background_color; - } else { + if (border_width != 0.) { 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)); + float4 blended_border = over(color, input.border_color); + color = lerp(blended_border, color, saturate(0.5 - inset_distance)); } return color * float4(1., 1., 1., saturate(0.5 - distance)); @@ -457,14 +623,17 @@ float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { struct PathSprite { Bounds bounds; - Hsla color; + Background color; AtlasTile tile; }; struct PathVertexOutput { float4 position: SV_Position; float2 tile_position: POSITION1; - float4 color: COLOR; + uint sprite_id: FLAT; + float4 solid_color: FLAT; + float4 color0: FLAT; + float4 color1: FLAT; }; StructuredBuffer path_sprites: register(t1); @@ -473,18 +642,32 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ 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); + output.sprite_id = instance_id; + + 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 + ); + + output.solid_color = gradient.solid; + output.color0 = gradient.color0; + output.color1 = gradient.color1; 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; + PathSprite sprite = path_sprites[input.sprite_id]; + Background background = sprite.color; + float4 color = gradient_color(background, input.position.xy, sprite.bounds, + input.solid_color, input.color0, input.color1); color.a *= mask; return color; } From dbe2ce2464874a3d53de9fd8b7a9bacb6dae4c87 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 17:13:53 +0800 Subject: [PATCH 081/202] wip --- .../src/platform/windows/directx_atlas.rs | 4 +- .../src/platform/windows/directx_renderer.rs | 238 ++++++------------ 2 files changed, 73 insertions(+), 169 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 03d33f5cbc..3a5da33ae6 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -21,7 +21,7 @@ struct DirectXAtlasState { device_context: ID3D11DeviceContext, monochrome_textures: Vec, polychrome_textures: Vec, - path_textures: Vec, + // path_textures: Vec, tiles_by_key: FxHashMap, } @@ -41,7 +41,7 @@ impl DirectXAtlas { device_context, monochrome_textures: Default::default(), polychrome_textures: Default::default(), - path_textures: Default::default(), + // path_textures: Default::default(), tiles_by_key: Default::default(), })) } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 6f2797d182..c8f3bcbda7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -48,7 +48,6 @@ struct DirectXContext { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - path_raster_pipeline: PipelineState, paths_pipeline: PipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, @@ -115,12 +114,6 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - // let Some(path_tiles) = self.rasterize_paths(scene.paths()) else { - // return Err(anyhow::anyhow!( - // "failed to rasterize {} paths", - // scene.paths().len() - // )); - // }; pre_draw( &self.devices.device_context, &self.globals.global_params_buffer, @@ -133,7 +126,7 @@ impl DirectXRenderer { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), PrimitiveBatch::Quads(quads) => self.draw_quads(quads), - PrimitiveBatch::Paths(paths) => self.draw_paths(paths, &path_tiles), + PrimitiveBatch::Paths(paths) => self.draw_paths(paths), PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines), PrimitiveBatch::MonochromeSprites { texture_id, @@ -276,126 +269,46 @@ impl DirectXRenderer { ) } - fn rasterize_paths( - &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) - None - } - - fn draw_paths( - &mut self, - 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, - // )?; - // } + fn draw_paths(&mut self, paths: &[Path]) -> Result<()> { + if paths.is_empty() { + return Ok(()); + } + let mut vertices = Vec::new(); + let mut sprites = Vec::with_capacity(paths.len()); + 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(()) } @@ -543,13 +456,6 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - let path_raster_pipeline = create_pipieline( - device, - "path_rasterization_vertex", - "path_rasterization_fragment", - std::mem::size_of::>(), - 32, - )?; let paths_pipeline = create_pipieline( device, "paths_vertex", @@ -582,7 +488,6 @@ impl DirectXRenderPipelines { Ok(Self { shadow_pipeline, quad_pipeline, - path_raster_pipeline, paths_pipeline, underline_pipeline, mono_sprites, @@ -679,8 +584,7 @@ struct PipelineState { #[repr(C)] struct PathSprite { bounds: Bounds, - color: Hsla, - tile: AtlasTile, + color: Background, } fn get_dxgi_factory() -> Result { @@ -743,35 +647,35 @@ fn get_device( // 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)? }) -} +// 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( From c8ae5a3b11d2eeaea0ab880dc1a70f6d7e1fcd7d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 20:17:01 +0800 Subject: [PATCH 082/202] fix all --- .../src/platform/windows/directx_renderer.rs | 174 ++++++++++++++---- crates/gpui/src/platform/windows/shaders.hlsl | 127 +++++-------- 2 files changed, 186 insertions(+), 115 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c8f3bcbda7..e4abdd6c3d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -49,6 +49,7 @@ 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, @@ -61,6 +62,14 @@ struct DirectXGlobalElements { blend_state_for_pr: ID3D11BlendState, } +#[repr(C)] +struct DrawInstancedIndirectArgs { + vertex_count_per_instance: u32, + instance_count: u32, + start_vertex_location: u32, + start_instance_location: u32, +} + // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -122,6 +131,7 @@ impl DirectXRenderer { [0.0, 0.0, 0.0, 0.0], &self.globals.blend_state, )?; + println!("--> Drawing scene"); for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -273,41 +283,64 @@ 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()); - 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), + let mut draw_indirect_commands = Vec::with_capacity(paths.len()); + let mut start_vertex_location = 0; + for (i, path) in paths.iter().enumerate() { + draw_indirect_commands.push(DrawInstancedIndirectArgs { + vertex_count_per_instance: path.vertices.len() as u32, + instance_count: 1, + start_vertex_location, + start_instance_location: i as u32, + }); + start_vertex_location += path.vertices.len() as u32; + + vertices.extend(path.vertices.iter().map(|v| PathVertex { + xy_position: v.xy_position, + content_mask: ContentMask { + bounds: path.content_mask.bounds, }, + })); + + sprites.push(PathSprite { + bounds: path.bounds, 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( + }); + } + + update_buffer_capacity( + &self.pipelines.paths_pipeline, + std::mem::size_of::(), + sprites.len(), + &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, + &draw_indirect_commands, + )?; + prepare_indirect_draws( + &self.devices.device_context, + &self.pipelines.paths_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + )?; + + for i in 0..paths.len() { + draw_indirect( &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, - )?; + &self.pipelines.paths_indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, + ); } Ok(()) } @@ -484,11 +517,13 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; + 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, @@ -884,6 +919,27 @@ fn create_buffer_view( Ok([view]) } +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> 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() + // }; + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32 * buffer_size, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_INDEX_BUFFER.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, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) +} + fn update_global_params( device_context: &ID3D11DeviceContext, buffer: &[Option; 1], @@ -964,6 +1020,50 @@ 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( + device_context: &ID3D11DeviceContext, + pipeline: &PipelineState, + viewport: &[D3D11_VIEWPORT], + global_params: &[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, @@ -1026,6 +1126,7 @@ 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(); @@ -1039,6 +1140,11 @@ 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))] @@ -1054,6 +1160,7 @@ 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:?}")); @@ -1064,10 +1171,9 @@ mod shader_resources { string_len, string_len, ); - return Err(anyhow::anyhow!( - "Compile error: {}", - String::from_utf8_lossy(&error_string_encode) - )); + let error_string = String::from_utf8_lossy(&error_string_encode); + println!("Shader compile error: {}", error_string); + return Err(anyhow::anyhow!("Compile error: {}", error_string)); } Ok(compile_blob.unwrap()) } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 0234f6bdc7..fbb5414321 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -81,28 +81,36 @@ struct TransformationMatrix { 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; +float4 to_device_position_impl(float2 position) { 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) { +float4 to_device_position(float2 unit_vertex, Bounds bounds) { float2 position = unit_vertex * bounds.size + bounds.origin; + return to_device_position_impl(position); +} + +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); } +float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) { + float2 position = unit_vertex * bounds.size + bounds.origin; + return distance_from_clip_rect_impl(position, clip_bounds); +} + // Convert linear RGB to sRGB float3 linear_to_srgb(float3 color) { - return pow(color, float3(2.2)); + return pow(color, float3(2.2, 2.2, 2.2)); } // Convert sRGB to linear RGB float3 srgb_to_linear(float3 color) { - return pow(color, float3(1.0 / 2.2)); + return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2)); } /// Hsla to linear RGBA conversion. @@ -268,23 +276,23 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { } GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsla color0, Hsla color1) { - GradientColor res; + GradientColor output; if (tag == 0) { - res.solid = hsla_to_rgba(solid); + output.solid = hsla_to_rgba(solid); } else if (tag == 1) { - res.color0 = hsla_to_rgba(color0); - res.color1 = hsla_to_rgba(color1); + output.color0 = hsla_to_rgba(color0); + output.color1 = hsla_to_rgba(color1); // Prepare color space in vertex for avoid conversion // in fragment shader for performance reasons if (color_space == 1) { // Oklab - res.color0 = srgb_to_oklab(res.color0); - res.color1 = srgb_to_oklab(res.color1); + output.color0 = srgb_to_oklab(output.color0); + output.color1 = srgb_to_oklab(output.color1); } } - return res; + return output; } float4 gradient_color(Background background, @@ -456,22 +464,21 @@ struct Quad { struct QuadVertexOutput { float4 position: SV_Position; - // float4 border_color: COLOR0; - float4 border_color: FLAT; - uint quad_id: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + 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; float4 clip_distance: SV_ClipDistance; }; struct QuadFragmentInput { - uint quad_id: FLAT; + nointerpolation uint quad_id: TEXCOORD0; float4 position: SV_Position; - float4 border_color: FLAT; - float4 background_solid: FLAT; - float4 background_color0: FLAT; - float4 background_color1: FLAT; + nointerpolation float4 border_color: COLOR0; + nointerpolation float4 background_solid: COLOR1; + nointerpolation float4 background_color0: COLOR2; + nointerpolation float4 background_color1: COLOR3; }; StructuredBuffer quads: register(t1); @@ -566,55 +573,11 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { 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 @@ -624,27 +587,27 @@ float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target { struct PathSprite { Bounds bounds; Background color; - AtlasTile tile; }; struct PathVertexOutput { float4 position: SV_Position; - float2 tile_position: POSITION1; - uint sprite_id: FLAT; - float4 solid_color: FLAT; - float4 color0: FLAT; - float4 color1: FLAT; + float4 clip_distance: SV_ClipDistance; + nointerpolation uint sprite_id: TEXCOORD0; + nointerpolation float4 solid_color: COLOR0; + nointerpolation float4 color0: COLOR1; + nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_sprites: register(t1); +StructuredBuffer path_vertices: register(t1); +StructuredBuffer path_sprites: register(t2); 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)); + PathVertex v = path_vertices[vertex_id]; 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.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; GradientColor gradient = prepare_gradient_color( @@ -662,13 +625,15 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ } 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 zero = 0.0; + if (any(input.clip_distance < zero)) { + return zero; + } + PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, input.solid_color, input.color0, input.color1); - color.a *= mask; return color; } From 238ccec5eeb71f1d11c97b6d239a1cbb5e2a7045 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Sun, 13 Jul 2025 20:31:39 +0800 Subject: [PATCH 083/202] fix --- crates/gpui/src/platform/windows/directx_renderer.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index e4abdd6c3d..36c68c8eb4 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -131,7 +131,6 @@ impl DirectXRenderer { [0.0, 0.0, 0.0, 0.0], &self.globals.blend_state, )?; - println!("--> Drawing scene"); for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -332,7 +331,7 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, )?; for i in 0..paths.len() { @@ -494,7 +493,7 @@ impl DirectXRenderPipelines { "paths_vertex", "paths_fragment", std::mem::size_of::(), - 1, + 32, )?; let underline_pipeline = create_pipieline( device, From 1fb689bad347ad4042a91f2d746f419b99c8febb Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 14:24:47 +0800 Subject: [PATCH 084/202] apply #19772 --- .../src/platform/windows/directx_atlas.rs | 140 +++++++++--------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 3a5da33ae6..abd6e4ad40 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -3,25 +3,25 @@ use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use windows::Win32::Graphics::{ Direct3D11::{ - D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, - D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, - ID3D11RenderTargetView, ID3D11ShaderResourceView, ID3D11Texture2D, - }, - Dxgi::Common::{ - DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R16_FLOAT, DXGI_SAMPLE_DESC, + D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, D3D11_TEXTURE2D_DESC, + D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView, + ID3D11Texture2D, }, + Dxgi::Common::{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}, }; -use crate::*; +use crate::{ + AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, + Size, platform::AtlasTextureList, +}; pub(crate) struct DirectXAtlas(Mutex); struct DirectXAtlasState { device: ID3D11Device, device_context: ID3D11DeviceContext, - monochrome_textures: Vec, - polychrome_textures: Vec, - // path_textures: Vec, + monochrome_textures: AtlasTextureList, + polychrome_textures: AtlasTextureList, tiles_by_key: FxHashMap, } @@ -30,8 +30,8 @@ struct DirectXAtlasTexture { bytes_per_pixel: u32, allocator: BucketedAtlasAllocator, texture: ID3D11Texture2D, - rtv: [Option; 1], view: [Option; 1], + live_atlas_keys: u32, } impl DirectXAtlas { @@ -41,27 +41,10 @@ impl DirectXAtlas { device_context, monochrome_textures: Default::default(), polychrome_textures: Default::default(), - // path_textures: Default::default(), tiles_by_key: Default::default(), })) } - pub(crate) fn get_texture_drawing_info( - &self, - id: AtlasTextureId, - ) -> (Size, [Option; 1]) { - let lock = self.0.lock(); - let tex = lock.texture(id); - let size = tex.allocator.size(); - ( - Size { - width: size.width as f32, - height: size.height as f32, - }, - tex.rtv.clone(), - ) - } - pub(crate) fn get_texture_view( &self, id: AtlasTextureId, @@ -84,9 +67,8 @@ 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, }; - for texture in textures { + for texture in textures.iter_mut() { texture.clear(); } } @@ -118,7 +100,30 @@ impl PlatformAtlas for DirectXAtlas { } fn remove(&self, key: &AtlasKey) { - todo!() + let mut lock = self.0.lock(); + + let Some(id) = lock.tiles_by_key.remove(key).map(|tile| tile.texture_id) else { + return; + }; + + let textures = match id.kind { + AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, + AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, + }; + + let Some(texture_slot) = textures.textures.get_mut(id.index as usize) else { + return; + }; + + if let Some(mut texture) = texture_slot.take() { + texture.decrement_ref_count(); + if texture.is_unreferenced() { + textures.free_list.push(texture.id.index as usize); + lock.tiles_by_key.remove(key); + } else { + *texture_slot = Some(texture); + } + } } } @@ -128,20 +133,23 @@ impl DirectXAtlasState { size: Size, texture_kind: AtlasTextureKind, ) -> Option { - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - // AtlasTextureKind::Path => &mut self.path_textures, - }; + { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + }; - textures - .iter_mut() - .rev() - .find_map(|texture| texture.allocate(size)) - .or_else(|| { - let texture = self.push_texture(size, texture_kind); - texture.allocate(size) - }) + if let Some(tile) = textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + { + return Some(tile); + } + } + + let texture = self.push_texture(size, texture_kind); + texture.allocate(size) } fn push_texture( @@ -173,11 +181,7 @@ 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; - // } + } } let texture_desc = D3D11_TEXTURE2D_DESC { Width: size.width.0 as u32, @@ -202,21 +206,11 @@ impl DirectXAtlasState { } let texture = texture.unwrap(); - let textures = match kind { + let texture_list = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Polychrome => &mut self.polychrome_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] - // }, - _ => [None], }; + let index = texture_list.free_list.pop(); let view = unsafe { let mut view = None; self.device @@ -226,17 +220,22 @@ impl DirectXAtlasState { }; let atlas_texture = DirectXAtlasTexture { id: AtlasTextureId { - index: textures.len() as u32, + index: index.unwrap_or(texture_list.textures.len()) as u32, kind, }, bytes_per_pixel, allocator: etagere::BucketedAtlasAllocator::new(size.into()), texture, - rtv, view, + live_atlas_keys: 0, }; - textures.push(atlas_texture); - textures.last_mut().unwrap() + if let Some(ix) = index { + texture_list.textures[ix] = Some(atlas_texture); + texture_list.textures.get_mut(ix).unwrap().as_mut().unwrap() + } else { + texture_list.textures.push(Some(atlas_texture)); + texture_list.textures.last_mut().unwrap().as_mut().unwrap() + } } fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture { @@ -245,7 +244,7 @@ impl DirectXAtlasState { crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, // crate::AtlasTextureKind::Path => &self.path_textures, }; - &textures[id.index as usize] + textures[id.index as usize].as_ref().unwrap() } } @@ -265,6 +264,7 @@ impl DirectXAtlasTexture { }, padding: 0, }; + self.live_atlas_keys += 1; Some(tile) } @@ -292,4 +292,12 @@ impl DirectXAtlasTexture { ); } } + + fn decrement_ref_count(&mut self) { + self.live_atlas_keys -= 1; + } + + fn is_unreferenced(&mut self) -> bool { + self.live_atlas_keys == 0 + } } From 9c3cfca8357360abd8a2b7775eb1686770867800 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 14:50:21 +0800 Subject: [PATCH 085/202] apply #23576 --- crates/gpui/src/platform/windows/shaders.hlsl | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index fbb5414321..cf2fd4ca01 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -40,12 +40,13 @@ struct LinearColorStop { struct Background { // 0u is Solid // 1u is LinearGradient + // 2u is PatternSlash uint tag; // 0u is sRGB linear color // 1u is Oklab color uint color_space; Hsla solid; - float angle; + float gradient_angle_or_pattern_height; LinearColorStop colors[2]; uint pad; }; @@ -295,6 +296,12 @@ GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, Hsl return output; } +float2x2 rotate2d(float angle) { + float s = sin(angle); + float c = cos(angle); + return float2x2(c, -s, s, c); +} + float4 gradient_color(Background background, float2 position, Bounds bounds, @@ -307,7 +314,8 @@ float4 gradient_color(Background background, break; case 1: { // -90 degrees to match the CSS gradient angle. - float radians = (fmod(background.angle, 360.0) - 90.0) * (M_PI_F / 180.0); + float gradient_angle = background.gradient_angle_or_pattern_height; + float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0); float2 direction = float2(cos(radians), sin(radians)); // Expand the short side to be the same as the long side @@ -347,6 +355,22 @@ float4 gradient_color(Background background, } break; } + case 2: { + float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height; + float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f; + float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f; + float pattern_height = pattern_width + pattern_interval; + float stripe_angle = M_PI_F / 4.0; + float pattern_period = pattern_height * sin(stripe_angle); + float2x2 rotation = rotate2d(stripe_angle); + float2 relative_position = position - bounds.origin; + float2 rotated_point = mul(rotation, relative_position); + float pattern = fmod(rotated_point.x, pattern_period); + float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) / 2.0f; + color = solid_color; + color.a *= saturate(0.5 - distance); + break; + } } return color; From fda3d56d87463b35769be0ace6cc2b0cb29032f8 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 16:47:45 +0800 Subject: [PATCH 086/202] wip --- .../src/platform/windows/directx_renderer.rs | 195 ++++++++++++++++-- 1 file changed, 175 insertions(+), 20 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 36c68c8eb4..ed2ec0a8c9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -48,8 +48,7 @@ struct DirectXContext { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - paths_pipeline: PipelineState, - paths_indirect_draw_buffer: ID3D11Buffer, + paths_pipeline: PathsPipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, poly_sprites: PipelineState, @@ -309,21 +308,37 @@ impl DirectXRenderer { }); } - update_buffer_capacity( + update_paths_buffer_capacity( &self.pipelines.paths_pipeline, - std::mem::size_of::(), sprites.len(), &self.devices.device, ) - .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input)); + .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, &self.pipelines.paths_pipeline.buffer, &sprites, )?; - update_indirect_buffer( + update_paths_vertex_capacity( + &mut self.pipelines.paths_pipeline, + vertices.len(), + &self.devices.device, + ) + .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); + update_buffer( &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, + &self.pipelines.paths_pipeline.vertex_buffer, + &vertices, + )?; + update_indirect_buffer_capacity( + &self.pipelines.paths_pipeline, + draw_indirect_commands.len(), + &self.devices.device, + ) + .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); + update_buffer( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, &draw_indirect_commands, )?; prepare_indirect_draws( @@ -337,7 +352,7 @@ impl DirectXRenderer { for i in 0..paths.len() { draw_indirect( &self.devices.device_context, - &self.pipelines.paths_indirect_draw_buffer, + &self.pipelines.paths_pipeline.indirect_draw_buffer, (i * std::mem::size_of::()) as u32, ); } @@ -488,13 +503,14 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - let paths_pipeline = create_pipieline( - device, - "paths_vertex", - "paths_fragment", - std::mem::size_of::(), - 32, - )?; + // let paths_pipeline = create_pipieline( + // device, + // "paths_vertex", + // "paths_fragment", + // std::mem::size_of::(), + // 32, + // )?; + let paths_pipeline = PathsPipelineState::new(device)?; let underline_pipeline = create_pipieline( device, "underline_vertex", @@ -516,13 +532,11 @@ impl DirectXRenderPipelines { std::mem::size_of::(), 32, )?; - 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, @@ -614,6 +628,62 @@ struct PipelineState { view: [Option; 1], } +struct PathsPipelineState { + vertex: ID3D11VertexShader, + fragment: ID3D11PixelShader, + buffer: ID3D11Buffer, + buffer_size: usize, + vertex_buffer: ID3D11Buffer, + vertex_buffer_size: usize, + indirect_draw_buffer: ID3D11Buffer, + indirect_buffer_size: usize, + view: [Option; 1], + vertex_view: [Option; 1], +} + +impl PathsPipelineState { + fn new(device: &ID3D11Device) -> Result { + let vertex = { + 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)? + }; + 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 = + create_buffer(device, std::mem::size_of::>(), 32)?; + let vertex_view = create_buffer_view(device, &vertex_buffer)?; + let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; + Ok(Self { + vertex, + fragment, + buffer, + buffer_size: 32, + vertex_buffer, + vertex_buffer_size: 32, + indirect_draw_buffer, + indirect_buffer_size: 32, + view, + vertex_view, + }) + } +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -996,6 +1066,67 @@ fn update_buffer_capacity( Some((buffer, buffer_size, view)) } +fn update_paths_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.buffer_size >= data_size { + return None; + } + println!( + "Paths buffer too small: {} < {}", + pipeline.buffer_size, data_size + ); + let buffer_size = data_size.next_power_of_two(); + println!("Paths New size: {}", buffer_size); + let buffer = create_buffer(device, std::mem::align_of::(), buffer_size).unwrap(); + let view = create_buffer_view(device, &buffer).unwrap(); + Some((buffer, buffer_size, view)) +} + +fn update_paths_vertex_capacity( + pipeline: &PathsPipelineState, + vertex_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.vertex_buffer_size >= vertex_size { + return None; + } + println!( + "Paths vertex buffer too small: {} < {}", + pipeline.vertex_buffer_size, vertex_size + ); + let vertex_size = vertex_size.next_power_of_two(); + println!("Paths vertex New size: {}", vertex_size); + let buffer = create_buffer( + device, + std::mem::size_of::>(), + vertex_size, + ) + .unwrap(); + let view = create_buffer_view(device, &buffer).unwrap(); + Some((buffer, vertex_size, view)) +} + +fn update_indirect_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize)> { + if pipeline.indirect_buffer_size >= data_size { + return None; + } + println!( + "Indirect buffer too small: {} < {}", + pipeline.indirect_buffer_size, data_size + ); + let buffer_size = data_size.next_power_of_two(); + println!("Indirect New size: {}", buffer_size); + let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + Some((buffer, buffer_size)) +} + fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1005,6 +1136,29 @@ fn update_pipeline( pipeline.view = input.2; } +fn update_paths_pipeline_buffer( + pipeline: &mut PathsPipelineState, + input: (ID3D11Buffer, usize, [Option; 1]), +) { + pipeline.buffer = input.0; + pipeline.buffer_size = input.1; + pipeline.view = input.2; +} + +fn update_paths_pipeline_vertex( + pipeline: &mut PathsPipelineState, + input: (ID3D11Buffer, usize, [Option; 1]), +) { + pipeline.vertex_buffer = input.0; + pipeline.vertex_buffer_size = input.1; + pipeline.vertex_view = input.2; +} + +fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { + pipeline.indirect_draw_buffer = input.0; + pipeline.indirect_buffer_size = input.1; +} + fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1035,14 +1189,15 @@ fn update_indirect_buffer( fn prepare_indirect_draws( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PathsPipelineState, viewport: &[D3D11_VIEWPORT], global_params: &[Option], topology: D3D_PRIMITIVE_TOPOLOGY, ) -> Result<()> { unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.view)); - device_context.PSSetShaderResources(1, Some(&pipeline.view)); + device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); + device_context.VSSetShaderResources(2, Some(&pipeline.view)); + device_context.PSSetShaderResources(2, Some(&pipeline.view)); device_context.IASetPrimitiveTopology(topology); device_context.RSSetViewports(Some(viewport)); device_context.VSSetShader(&pipeline.vertex, None); From 4a78ce7cfd4cdb0408fcd77af6b7a047ae1143d9 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 17:49:44 +0800 Subject: [PATCH 087/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 443 +++++++++++++++--- 1 file changed, 368 insertions(+), 75 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index cf2fd4ca01..f7e371b1b7 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -240,6 +240,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,32 +265,32 @@ 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) { @@ -376,6 +393,57 @@ float4 gradient_color(Background background, return color; } +// 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 { + return min(dv1, dv2); + } +} + +// 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); +} + +// 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; +} + /* ** ** Shadows @@ -477,7 +545,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct Quad { uint order; - uint pad; + uint border_style; Bounds bounds; Bounds content_mask; Background background; @@ -535,66 +603,286 @@ 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.bottom_left == 0.0 && + quad.corner_radii.top_right == 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; - } + 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; + } + + // 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 { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_right; - } else { - corner_radius = quad.corner_radii.bottom_right; + 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); + } + + // 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); + } + } } - } - 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; - } - - if (border_width != 0.) { - 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(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 { @@ -795,7 +1083,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; @@ -805,14 +1095,14 @@ struct PolychromeSprite { struct PolychromeSpriteVertexOutput { float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; + nointerpolation uint sprite_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct PolychromeSpriteFragmentInput { float4 position: SV_Position; float2 tile_position: POSITION; - uint sprite_id: FLAT; + nointerpolation uint sprite_id: TEXCOORD0; }; StructuredBuffer poly_sprites: register(t1); @@ -839,10 +1129,13 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); float4 color = sample; + if (sprite.grayscale) { + + } if ((sprite.grayscale & 0xFFu) != 0u) { 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; } From ecde968a0c8c595409b1eb70fa4013a4bed66184 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 17:55:50 +0800 Subject: [PATCH 088/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f7e371b1b7..fcd13ddb31 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1130,12 +1130,13 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float4 color = sample; if (sprite.grayscale) { - - } - if ((sprite.grayscale & 0xFFu) != 0u) { float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } + // if ((sprite.grayscale & 0xFFu) != 0u) { + // float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); + // color = float4(grayscale, sample.a); + // } color.a *= sprite.opacity * saturate(0.5 - distance); return color; } From 201c274c4b03144fb669a683ba2b64f394927418 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 18:35:52 +0800 Subject: [PATCH 089/202] wip --- crates/gpui/src/platform/windows/shaders.hlsl | 228 ++++++++---------- 1 file changed, 105 insertions(+), 123 deletions(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index fcd13ddb31..f93ef426dd 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) { @@ -293,20 +292,20 @@ float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) { 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); } } @@ -444,99 +443,6 @@ float quarter_ellipse_sdf(float2 pt, float2 radii) { return unit_circle_sdf * (radii.x + radii.y) * -0.5; } -/* -** -** 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 @@ -579,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; @@ -885,17 +790,97 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { return color * float4(1.0, 1.0, 1.0, saturate(antialias_threshold - outer_sdf)); } -struct PathVertex { - float2 xy_position; - Bounds content_mask; +/* +** +** Shadows +** +*/ + +struct ShadowVertexOutput { + float4 position: SV_Position; + nointerpolation float4 color: COLOR; + nointerpolation uint shadow_id: TEXCOORD0; + float4 clip_distance: SV_ClipDistance; }; +struct ShadowFragmentInput { + float4 position: SV_Position; + float4 color: COLOR; + nointerpolation uint shadow_id: TEXCOORD0; +}; + +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 = 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; + Bounds content_mask; +}; + struct PathSprite { Bounds bounds; Background color; @@ -926,8 +911,7 @@ PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_ 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; @@ -967,15 +951,15 @@ struct Underline { struct UnderlineVertexOutput { float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; + nointerpolation uint underline_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct UnderlineFragmentInput { float4 position: SV_Position; - float4 color: COLOR; - uint underline_id: FLAT; + nointerpolation float4 color: COLOR; + nointerpolation uint underline_id: TEXCOORD0; }; StructuredBuffer underlines: register(t1); @@ -1000,10 +984,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; @@ -1039,14 +1021,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); From 401e0e6f41167bce4339e43cfc2ab74a45201438 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 14 Jul 2025 19:59:26 +0800 Subject: [PATCH 090/202] wip --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ed2ec0a8c9..132ab81320 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -281,7 +281,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()); @@ -850,6 +849,7 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, + // CullMode: D3D11_CULL_BACK, FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, @@ -1080,7 +1080,7 @@ fn update_paths_buffer_capacity( ); let buffer_size = data_size.next_power_of_two(); println!("Paths New size: {}", buffer_size); - let buffer = create_buffer(device, std::mem::align_of::(), buffer_size).unwrap(); + let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); Some((buffer, buffer_size, view)) } From 7fde34f85eb2fb6176f0f8da6a6176ea9b7cc4c7 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 12:35:33 +0800 Subject: [PATCH 091/202] temporarily disable transparancy --- crates/gpui/src/platform/windows/window.rs | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 2801674a64..5d1c91b5e3 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,7 +384,8 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - WS_EX_APPWINDOW | WS_EX_LAYERED, + // WS_EX_APPWINDOW | WS_EX_LAYERED, + WS_EX_APPWINDOW, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -461,7 +462,7 @@ impl WindowsWindow { // 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)? }; + // unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } @@ -706,27 +707,27 @@ impl PlatformWindow for WindowsWindow { } fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) { - let mut window_state = self.0.state.borrow_mut(); + // let mut window_state = self.0.state.borrow_mut(); // todo(zjk) // window_state // .renderer // .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque); - match background_appearance { - WindowBackgroundAppearance::Opaque => { - // ACCENT_DISABLED - set_window_composition_attribute(window_state.hwnd, None, 0); - } - WindowBackgroundAppearance::Transparent => { - // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background - set_window_composition_attribute(window_state.hwnd, None, 2); - } - WindowBackgroundAppearance::Blurred => { - // Enable acrylic blur - // ACCENT_ENABLE_ACRYLICBLURBEHIND - set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); - } - } + // match background_appearance { + // WindowBackgroundAppearance::Opaque => { + // // ACCENT_DISABLED + // set_window_composition_attribute(window_state.hwnd, None, 0); + // } + // WindowBackgroundAppearance::Transparent => { + // // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background + // set_window_composition_attribute(window_state.hwnd, None, 2); + // } + // WindowBackgroundAppearance::Blurred => { + // // Enable acrylic blur + // // ACCENT_ENABLE_ACRYLICBLURBEHIND + // set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); + // } + // } } fn minimize(&self) { From 96d847b6d114b86bcf0d7514f1f0dae1834b7546 Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 20:55:16 +0200 Subject: [PATCH 092/202] initial removal attempt --- .../gpui/src/platform/windows/direct_write.rs | 336 +++++++----------- 1 file changed, 129 insertions(+), 207 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index ada306c15c..3fc143fdcb 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::Result; @@ -9,13 +9,7 @@ use windows::{ Win32::{ Foundation::*, Globalization::GetUserDefaultLocaleName, - Graphics::{ - Direct2D::{Common::*, *}, - DirectWrite::*, - Dxgi::Common::*, - Gdi::LOGFONTW, - Imaging::*, - }, + Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*}, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, }, @@ -40,7 +34,6 @@ struct DirectWriteComponent { locale: String, factory: IDWriteFactory5, bitmap_factory: AgileReference, - d2d1_factory: ID2D1Factory, in_memory_loader: IDWriteInMemoryFontFileLoader, builder: IDWriteFontSetBuilder1, text_renderer: Arc, @@ -49,7 +42,6 @@ struct DirectWriteComponent { struct GlyphRenderContext { params: IDWriteRenderingParams3, - dc_target: ID2D1DeviceContext4, } struct DirectWriteState { @@ -74,8 +66,6 @@ impl DirectWriteComponent { unsafe { let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let bitmap_factory = AgileReference::new(bitmap_factory)?; - let d2d1_factory: ID2D1Factory = - D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?; // The `IDWriteInMemoryFontFileLoader` here is supported starting from // Windows 10 Creators Update, which consequently requires the entire // `DirectWriteTextSystem` to run on `win10 1703`+. @@ -86,13 +76,12 @@ impl DirectWriteComponent { GetUserDefaultLocaleName(&mut locale_vec); let locale = String::from_utf16_lossy(&locale_vec); let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); - let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?; + let render_context = GlyphRenderContext::new(&factory)?; Ok(DirectWriteComponent { locale, factory, bitmap_factory, - d2d1_factory, in_memory_loader, builder, text_renderer, @@ -103,7 +92,7 @@ impl DirectWriteComponent { } impl GlyphRenderContext { - pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result { + pub fn new(factory: &IDWriteFactory5) -> Result { unsafe { let default_params: IDWriteRenderingParams3 = factory.CreateRenderingParams()?.cast()?; @@ -122,17 +111,8 @@ impl GlyphRenderContext { DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, grid_fit_mode, )?; - let dc_target = { - let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ))?; - let target = target.cast::()?; - target.SetTextRenderingParams(¶ms); - target - }; - Ok(Self { params, dc_target }) + Ok(Self { params }) } } } @@ -649,17 +629,12 @@ impl DirectWriteState { } fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { - let render_target = &self.components.render_context.dc_target; - unsafe { - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor); - } let font = &self.fonts[params.font_id.0]; let glyph_id = [params.glyph_id.0 as u16]; let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, + fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -668,13 +643,29 @@ impl DirectWriteState { isSideways: BOOL(0), bidiLevel: 0, }; - let bounds = unsafe { - render_target.GetGlyphRunWorldBounds( - Vector2 { X: 0.0, Y: 0.0 }, + + let transform = DWRITE_MATRIX::default(); + let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + let baseline_origin_x = 0.0; + let baseline_origin_y = 0.0; + + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - DWRITE_MEASURING_MODE_NATURAL, + Some(&transform as *const _), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, )? }; + + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + // todo(windows) // This is a walkaround, deleted when figured out. let y_offset; @@ -696,12 +687,13 @@ impl DirectWriteState { } else { Ok(Bounds { origin: point( - ((bounds.left * params.scale_factor).ceil() as i32).into(), - ((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(), + ((bounds.left as f32 * params.scale_factor).ceil() as i32).into(), + ((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(), ), size: size( - (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(), - (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32 + (((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32) + .into(), + (((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32 + extra_height) .into(), ), @@ -739,7 +731,7 @@ impl DirectWriteState { ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, }]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) }, + fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -759,149 +751,108 @@ impl DirectWriteState { } let bitmap_size = bitmap_size; - let total_bytes; - let bitmap_format; - let render_target_property; - let bitmap_width; - let bitmap_height; - let bitmap_stride; - let bitmap_dpi; - if params.is_emoji { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4; - bitmap_format = &GUID_WICPixelFormat32bppPBGRA; - render_target_property = get_render_target_property( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_PREMULTIPLIED, - ); - bitmap_width = bitmap_size.width.0 as u32; - bitmap_height = bitmap_size.height.0 as u32; - bitmap_stride = bitmap_size.width.0 as u32 * 4; - bitmap_dpi = 96.0; + let subpixel_shift = params + .subpixel_variant + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); + let baseline_origin_x = subpixel_shift.x / params.scale_factor; + let baseline_origin_y = subpixel_shift.y / params.scale_factor; + + let transform = DWRITE_MATRIX { + m11: params.scale_factor, + m12: 0.0, + m21: 0.0, + m22: params.scale_factor, + dx: 0.0, + dy: 0.0, + }; + + let rendering_mode = if params.is_emoji { + DWRITE_RENDERING_MODE1_NATURAL } else { - total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize; - bitmap_format = &GUID_WICPixelFormat8bppAlpha; - render_target_property = - get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT); - bitmap_width = bitmap_size.width.0 as u32 * 2; - bitmap_height = bitmap_size.height.0 as u32 * 2; - bitmap_stride = bitmap_size.width.0 as u32; - bitmap_dpi = 192.0; - } + DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC + }; - let bitmap_factory = self.components.bitmap_factory.resolve()?; - unsafe { - let bitmap = bitmap_factory.CreateBitmap( - bitmap_width, - bitmap_height, - bitmap_format, - WICBitmapCacheOnLoad, - )?; - let render_target = self - .components - .d2d1_factory - .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?; - let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?; - let subpixel_shift = params - .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); - let baseline_origin = Vector2 { - X: subpixel_shift.x / params.scale_factor, - Y: subpixel_shift.y / params.scale_factor, - }; + let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - // This `cast()` action here should never fail since we are running on Win10+, and - // ID2D1DeviceContext4 requires Win8+ - let render_target = render_target.cast::().unwrap(); - render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); - render_target.SetDpi( - bitmap_dpi * params.scale_factor, - bitmap_dpi * params.scale_factor, - ); - render_target.SetTextRenderingParams(&self.components.render_context.params); - render_target.BeginDraw(); + let glyph_analysis = unsafe { + self.components.factory.CreateGlyphRunAnalysis( + &glyph_run, + Some(&transform), + rendering_mode, + measuring_mode, + DWRITE_GRID_FIT_MODE_DEFAULT, + DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, + baseline_origin_x, + baseline_origin_y, + )? + }; - if params.is_emoji { - // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested - let enumerator = self.components.factory.TranslateColorGlyphRun( - baseline_origin, - &glyph_run as _, - None, - DWRITE_GLYPH_IMAGE_FORMATS_COLR - | DWRITE_GLYPH_IMAGE_FORMATS_SVG - | DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, - DWRITE_MEASURING_MODE_NATURAL, - None, - 0, - )?; - while enumerator.MoveNext().is_ok() { - let Ok(color_glyph) = enumerator.GetCurrentRun() else { - break; - }; - let color_glyph = &*color_glyph; - let brush_color = translate_color(&color_glyph.Base.runColor); - brush.SetColor(&brush_color); - match color_glyph.glyphImageFormat { - DWRITE_GLYPH_IMAGE_FORMATS_PNG - | DWRITE_GLYPH_IMAGE_FORMATS_JPEG - | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target - .DrawColorBitmapGlyphRun( - color_glyph.glyphImageFormat, - baseline_origin, - &color_glyph.Base.glyphRun, - color_glyph.measuringMode, - D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT, - ), - DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - &brush, - None, - color_glyph.Base.paletteIndex as u32, - color_glyph.measuringMode, - ), - _ => render_target.DrawGlyphRun( - baseline_origin, - &color_glyph.Base.glyphRun, - Some(color_glyph.Base.glyphRunDescription as *const _), - &brush, - color_glyph.measuringMode, - ), - } - } - } else { - render_target.DrawGlyphRun( - baseline_origin, - &glyph_run, - None, - &brush, - DWRITE_MEASURING_MODE_NATURAL, - ); + if params.is_emoji { + // For emoji, we need to handle color glyphs differently + // This is a simplified approach - in a full implementation you'd want to + // properly handle color glyph runs using TranslateColorGlyphRun + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], + )); } - render_target.EndDraw(None, None)?; - let mut raw_data = vec![0u8; total_bytes]; - if params.is_emoji { - bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; - // Convert from BGRA with premultiplied alpha to BGRA with straight alpha. - for pixel in raw_data.chunks_exact_mut(4) { - let a = pixel[3] as f32 / 255.; - pixel[0] = (pixel[0] as f32 / a) as u8; - pixel[1] = (pixel[1] as f32 / a) as u8; - pixel[2] = (pixel[2] as f32 / a) as u8; - } - } else { - let scaler = bitmap_factory.CreateBitmapScaler()?; - scaler.Initialize( - &bitmap, - bitmap_size.width.0 as u32, - bitmap_size.height.0 as u32, - WICBitmapInterpolationModeHighQualityCubic, - )?; - scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; + let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - Ok((bitmap_size, raw_data)) + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; + rgba_data.resize(expected_size, 0); + + Ok((bitmap_size, rgba_data)) + } else { + // For regular text, use grayscale or cleartype + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], + )); + } + + let mut alpha_data = vec![0u8; (width * height) as usize]; + + unsafe { + glyph_analysis.CreateAlphaTexture( + texture_type, + &texture_bounds, + &mut alpha_data, + )?; + } + + // For cleartype, we need to convert the 3x1 subpixel data to grayscale + // This is a simplified conversion - you might want to do proper subpixel rendering + let mut grayscale_data = Vec::new(); + for chunk in alpha_data.chunks_exact(3) { + let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; + grayscale_data.push(avg as u8); + } + + // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + grayscale_data.resize(expected_size, 0); + + Ok((bitmap_size, grayscale_data)) } } @@ -1471,13 +1422,8 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result { } #[inline] -fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F { - D2D1_COLOR_F { - r: color.r, - g: color.g, - b: color.b, - a: color.a, - } +fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] { + [color.r, color.g, color.b, color.a] } fn get_system_ui_font_name() -> SharedString { @@ -1504,24 +1450,6 @@ fn get_system_ui_font_name() -> SharedString { } } -#[inline] -fn get_render_target_property( - pixel_format: DXGI_FORMAT, - alpha_mode: D2D1_ALPHA_MODE, -) -> D2D1_RENDER_TARGET_PROPERTIES { - D2D1_RENDER_TARGET_PROPERTIES { - r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT, - pixelFormat: D2D1_PIXEL_FORMAT { - format: pixel_format, - alphaMode: alpha_mode, - }, - dpiX: 96.0, - dpiY: 96.0, - usage: D2D1_RENDER_TARGET_USAGE_NONE, - minLevel: D2D1_FEATURE_LEVEL_DEFAULT, - } -} - // One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats // but that doesn't seem to work for some glyphs, say ❤ fn is_color_glyph( @@ -1561,12 +1489,6 @@ fn is_color_glyph( } const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); -const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, -}; #[cfg(test)] mod tests { From 2b53a2cb125cf15e6f5d784487c969e35389883a Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 21:44:20 +0200 Subject: [PATCH 093/202] make it not crash --- crates/gpui/src/platform/windows/direct_write.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 3fc143fdcb..4ee94e6155 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -634,7 +634,7 @@ impl DirectWriteState { let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), + fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -644,7 +644,6 @@ impl DirectWriteState { bidiLevel: 0, }; - let transform = DWRITE_MATRIX::default(); let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; let baseline_origin_x = 0.0; @@ -653,7 +652,7 @@ impl DirectWriteState { let glyph_analysis = unsafe { self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - Some(&transform as *const _), + None, rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, @@ -830,7 +829,7 @@ impl DirectWriteState { )); } - let mut alpha_data = vec![0u8; (width * height) as usize]; + let mut alpha_data = vec![0u8; (width * height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( From 3b6105b71341bb9f7006d6539c30cb996c6287a3 Mon Sep 17 00:00:00 2001 From: Kate Date: Mon, 14 Jul 2025 22:34:36 +0200 Subject: [PATCH 094/202] more fixes and debugging --- crates/gpui/examples/text.rs | 36 +++++++++---------- .../gpui/src/platform/windows/direct_write.rs | 7 ++-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..6f1b98e81c 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - .child(div().child(CharacterGrid::new().scale(base))) + // .child(div().child(CharacterGrid::new().scale(base))) .child( div() - .child(Specimen::new(self.next_id()).scale(step_down_2)) - .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_down_1)) - .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_2)) + // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_1)) + // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - .child(Specimen::new(self.next_id()).scale(base).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_1)) - .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_2)) - .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_3)) - .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_4)) - .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_5)) - .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_6)) - .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + // .child(Specimen::new(self.next_id()).scale(base).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_1)) + // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_2)) + // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_3)) + // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_4)) + // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_5)) + // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_6)) + // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 4ee94e6155..8d191facbe 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -810,14 +810,13 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; - rgba_data.resize(expected_size, 0); Ok((bitmap_size, rgba_data)) } else { // For regular text, use grayscale or cleartype let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); let width = (texture_bounds.right - texture_bounds.left) as u32; let height = (texture_bounds.bottom - texture_bounds.top) as u32; @@ -848,10 +847,10 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; + let expected_size = width as usize * height as usize; grayscale_data.resize(expected_size, 0); - Ok((bitmap_size, grayscale_data)) + Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } } From 8075998c0998b8e020bc1d038cdd38f4261758fa Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 16:58:13 -0700 Subject: [PATCH 095/202] Translate rasterized glyphs from texture to bitmap --- crates/gpui/examples/text.rs | 36 +++--- .../gpui/src/platform/windows/direct_write.rs | 114 +++++++++++------- 2 files changed, 86 insertions(+), 64 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 6f1b98e81c..19214aebde 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - // .child(div().child(CharacterGrid::new().scale(base))) + .child(div().child(CharacterGrid::new().scale(base))) .child( div() - // .child(Specimen::new(self.next_id()).scale(step_down_2)) - // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_down_1)) - // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_2)) + .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_1)) + .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - // .child(Specimen::new(self.next_id()).scale(base).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_1)) - // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_2)) - // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_3)) - // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_4)) - // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_5)) - // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_6)) - // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + .child(Specimen::new(self.next_id()).scale(base).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_1)) + .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_2)) + .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_3)) + .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_4)) + .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_5)) + .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_6)) + .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 8d191facbe..bd7bacd4fc 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -786,50 +786,58 @@ impl DirectWriteState { )? }; + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + let texture_width = (texture_bounds.right - texture_bounds.left) as u32; + let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if texture_width == 0 || texture_height == 0 { + return Ok(( + bitmap_size, + vec![ + 0u8; + bitmap_size.width.0 as usize + * bitmap_size.height.0 as usize + * if params.is_emoji { 4 } else { 1 } + ], + )); + } + + let mut bitmap_data; if params.is_emoji { - // For emoji, we need to handle color glyphs differently - // This is a simplified approach - in a full implementation you'd want to - // properly handle color glyph runs using TranslateColorGlyphRun - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], - )); - } - - let mut rgba_data = vec![0u8; (width * height * 4) as usize]; + bitmap_data = + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4]; + let mut rgba_data = vec![0u8; (texture_width * texture_height * 4) as usize]; unsafe { glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - // Resize to match expected bitmap_size if needed + // Copy texture data into bitmap at correct position + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; - Ok((bitmap_size, rgba_data)) - } else { - // For regular text, use grayscale or cleartype - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 4; + let bitmap_idx = (bitmap_y * bitmap_size.width.0 as usize + bitmap_x) * 4; - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], - )); + if texture_idx + 3 < rgba_data.len() && bitmap_idx + 3 < bitmap_data.len() { + bitmap_data[bitmap_idx..bitmap_idx + 4] + .copy_from_slice(&rgba_data[texture_idx..texture_idx + 4]); + } + } + } } + } else { + bitmap_data = vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; - let mut alpha_data = vec![0u8; (width * height * 3) as usize]; - + let mut alpha_data = vec![0u8; (texture_width * texture_height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( texture_type, @@ -838,20 +846,34 @@ impl DirectWriteState { )?; } - // For cleartype, we need to convert the 3x1 subpixel data to grayscale - // This is a simplified conversion - you might want to do proper subpixel rendering - let mut grayscale_data = Vec::new(); - for chunk in alpha_data.chunks_exact(3) { - let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; - grayscale_data.push(avg as u8); + // Convert ClearType RGB data to grayscale and place in bitmap + let offset_x = texture_bounds.left.max(0) as usize; + let offset_y = texture_bounds.top.max(0) as usize; + + for y in 0..texture_height as usize { + for x in 0..texture_width as usize { + let bitmap_x = offset_x + x; + let bitmap_y = offset_y + y; + + if bitmap_x < bitmap_size.width.0 as usize + && bitmap_y < bitmap_size.height.0 as usize + { + let texture_idx = (y * texture_width as usize + x) * 3; + let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; + + if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { + let avg = (alpha_data[texture_idx] as u32 + + alpha_data[texture_idx + 1] as u32 + + alpha_data[texture_idx + 2] as u32) + / 3; + bitmap_data[bitmap_idx] = avg as u8; + } + } + } } - - // Resize to match expected bitmap_size if needed - let expected_size = width as usize * height as usize; - grayscale_data.resize(expected_size, 0); - - Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) } + + Ok((bitmap_size, bitmap_data)) } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From 9162583bace276f0a0532cf6df43fae10b8a7a8e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 14 Jul 2025 17:57:52 -0700 Subject: [PATCH 096/202] Add emojis to text example --- crates/gpui/examples/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..1166bb2795 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid { "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ", - "❮", "<=", "!=", "==", "--", "++", "=>", "->", + "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", ]; let columns = 11; From 1b12dd39ccede372a8c2cdb3e9f8a9e65435d45d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:28:25 +0800 Subject: [PATCH 097/202] fix all --- .../src/platform/windows/directx_renderer.rs | 50 ++----------------- crates/gpui/src/platform/windows/shaders.hlsl | 46 ++++++++--------- 2 files changed, 24 insertions(+), 72 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 132ab81320..3853e506c7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -58,7 +58,6 @@ struct DirectXGlobalElements { global_params_buffer: [Option; 1], sampler: [Option; 1], blend_state: ID3D11BlendState, - blend_state_for_pr: ID3D11BlendState, } #[repr(C)] @@ -601,13 +600,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, }) } } @@ -849,7 +846,7 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, - // CullMode: D3D11_CULL_BACK, + // FrontCounterClockwise: true.into(), FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, @@ -888,25 +885,6 @@ 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, @@ -989,13 +967,6 @@ fn create_buffer_view( } fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> 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() - // }; let desc = D3D11_BUFFER_DESC { ByteWidth: std::mem::size_of::() as u32 * buffer_size, Usage: D3D11_USAGE_DYNAMIC, @@ -1173,20 +1144,6 @@ 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( device_context: &ID3D11DeviceContext, pipeline: &PathsPipelineState, @@ -1314,7 +1271,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:?}")); @@ -1325,8 +1281,8 @@ 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()) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f93ef426dd..06a65e909f 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -461,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; @@ -512,9 +512,9 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { input.background_solid, input.background_color0, input.background_color1); bool unrounded = quad.corner_radii.top_left == 0.0 && - quad.corner_radii.bottom_left == 0.0 && - quad.corner_radii.top_right == 0.0 && - quad.corner_radii.bottom_right == 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 && @@ -796,19 +796,6 @@ float4 quad_fragment(QuadFragmentInput input): SV_Target { ** */ -struct ShadowVertexOutput { - float4 position: SV_Position; - nointerpolation float4 color: COLOR; - nointerpolation uint shadow_id: TEXCOORD0; - float4 clip_distance: SV_ClipDistance; -}; - -struct ShadowFragmentInput { - float4 position: SV_Position; - float4 color: COLOR; - nointerpolation uint shadow_id: TEXCOORD0; -}; - struct Shadow { uint order; float blur_radius; @@ -818,6 +805,19 @@ struct Shadow { 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) { @@ -950,16 +950,16 @@ struct Underline { }; struct UnderlineVertexOutput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; nointerpolation float4 color: COLOR; - nointerpolation uint underline_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct UnderlineFragmentInput { + nointerpolation uint underline_id: TEXCOORD0; float4 position: SV_Position; nointerpolation float4 color: COLOR; - nointerpolation uint underline_id: TEXCOORD0; }; StructuredBuffer underlines: register(t1); @@ -1075,16 +1075,16 @@ struct PolychromeSprite { }; struct PolychromeSpriteVertexOutput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - nointerpolation uint sprite_id: TEXCOORD0; float4 clip_distance: SV_ClipDistance; }; struct PolychromeSpriteFragmentInput { + nointerpolation uint sprite_id: TEXCOORD0; float4 position: SV_Position; float2 tile_position: POSITION; - nointerpolation uint sprite_id: TEXCOORD0; }; StructuredBuffer poly_sprites: register(t1); @@ -1115,10 +1115,6 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } - // if ((sprite.grayscale & 0xFFu) != 0u) { - // float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); - // color = float4(grayscale, sample.a); - // } color.a *= sprite.opacity * saturate(0.5 - distance); return color; } From 2a6b83f19071601d77c9299716c47b661dc81af7 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:29:32 +0800 Subject: [PATCH 098/202] remove debug print --- .../src/platform/windows/directx_renderer.rs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 3853e506c7..609872714b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1029,9 +1029,7 @@ fn update_buffer_capacity( 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)) @@ -1045,12 +1043,7 @@ fn update_paths_buffer_capacity( if pipeline.buffer_size >= data_size { return None; } - println!( - "Paths buffer too small: {} < {}", - pipeline.buffer_size, data_size - ); let buffer_size = data_size.next_power_of_two(); - println!("Paths New size: {}", buffer_size); let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); Some((buffer, buffer_size, view)) @@ -1064,12 +1057,7 @@ fn update_paths_vertex_capacity( if pipeline.vertex_buffer_size >= vertex_size { return None; } - println!( - "Paths vertex buffer too small: {} < {}", - pipeline.vertex_buffer_size, vertex_size - ); let vertex_size = vertex_size.next_power_of_two(); - println!("Paths vertex New size: {}", vertex_size); let buffer = create_buffer( device, std::mem::size_of::>(), @@ -1088,12 +1076,7 @@ fn update_indirect_buffer_capacity( if pipeline.indirect_buffer_size >= data_size { return None; } - println!( - "Indirect buffer too small: {} < {}", - pipeline.indirect_buffer_size, data_size - ); let buffer_size = data_size.next_power_of_two(); - println!("Indirect New size: {}", buffer_size); let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); Some((buffer, buffer_size)) } @@ -1237,7 +1220,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(); @@ -1251,11 +1233,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))] From ffef9fd25a017a0a213683755e52d9faf2487e31 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:38:18 +0800 Subject: [PATCH 099/202] bringback our colorful avatar --- crates/gpui/src/platform/windows/shaders.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 06a65e909f..1ed2de2240 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1111,7 +1111,7 @@ float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Targe float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); float4 color = sample; - if (sprite.grayscale) { + if ((sprite.grayscale & 0xFFu) != 0u) { float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS); color = float4(grayscale, sample.a); } From 4f416d3818746607cb96f6a41f85b14b29156ea0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 15 Jul 2025 15:49:30 -0700 Subject: [PATCH 100/202] Fix path rendering - draw all paths w/ one regular draw call --- .../src/platform/windows/directx_renderer.rs | 255 ++++++++---------- crates/gpui/src/platform/windows/shaders.hlsl | 29 +- 2 files changed, 131 insertions(+), 153 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 609872714b..3614641ced 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -60,14 +60,6 @@ struct DirectXGlobalElements { blend_state: ID3D11BlendState, } -#[repr(C)] -struct DrawInstancedIndirectArgs { - vertex_count_per_instance: u32, - instance_count: u32, - start_vertex_location: u32, - start_instance_location: u32, -} - // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -282,22 +274,12 @@ impl DirectXRenderer { } let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); - let mut draw_indirect_commands = Vec::with_capacity(paths.len()); - let mut start_vertex_location = 0; - for (i, path) in paths.iter().enumerate() { - draw_indirect_commands.push(DrawInstancedIndirectArgs { - vertex_count_per_instance: path.vertices.len() as u32, - instance_count: 1, - start_vertex_location, - start_instance_location: i as u32, - }); - start_vertex_location += path.vertices.len() as u32; - vertices.extend(path.vertices.iter().map(|v| PathVertex { + for (i, path) in paths.iter().enumerate() { + 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 { @@ -314,7 +296,7 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, + &self.pipelines.paths_pipeline.instance_buffer, &sprites, )?; update_paths_vertex_capacity( @@ -328,32 +310,40 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline.vertex_buffer, &vertices, )?; - update_indirect_buffer_capacity( - &self.pipelines.paths_pipeline, - draw_indirect_commands.len(), - &self.devices.device, - ) - .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - &self.pipelines.paths_pipeline.indirect_draw_buffer, - &draw_indirect_commands, - )?; - prepare_indirect_draws( - &self.devices.device_context, - &self.pipelines.paths_pipeline, - &self.context.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_pipeline.indirect_draw_buffer, - (i * std::mem::size_of::()) as u32, + let device_context = &self.devices.device_context; + unsafe { + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_context.VSSetShader(&self.pipelines.paths_pipeline.vertex, None); + device_context.PSSetShader(&self.pipelines.paths_pipeline.fragment, None); + device_context.VSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); + device_context.PSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); + device_context.VSSetShaderResources( + 2, + Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), ); + device_context.PSSetShaderResources( + 2, + Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), + ); + device_context.PSSetSamplers(0, Some(&self.globals.sampler)); + device_context.OMSetBlendState(&self.globals.blend_state, None, 0xffffffff); + let stride = std::mem::size_of::() as u32; + let offset = 0u32; + device_context.IASetVertexBuffers( + 0, + 1, + Some([Some(self.pipelines.paths_pipeline.vertex_buffer.clone())].as_ptr()), + Some(&stride), + Some(&offset), + ); + device_context.IASetInputLayout(&self.pipelines.paths_pipeline.input_layout); } + + unsafe { + device_context.Draw(vertices.len() as u32, 0); + } + Ok(()) } @@ -627,19 +617,18 @@ struct PipelineState { struct PathsPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, - buffer: ID3D11Buffer, - buffer_size: usize, + instance_buffer: ID3D11Buffer, + instance_buffer_size: usize, vertex_buffer: ID3D11Buffer, vertex_buffer_size: usize, - indirect_draw_buffer: ID3D11Buffer, - indirect_buffer_size: usize, - view: [Option; 1], - vertex_view: [Option; 1], + instance_view: ID3D11ShaderResourceView, + vertex_view: ID3D11ShaderResourceView, + input_layout: ID3D11InputLayout, } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let vertex = { + let (vertex, shader_blob) = { let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( @@ -647,7 +636,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - create_vertex_shader(device, bytes)? + (create_vertex_shader(device, bytes)?, shader_blob) }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -659,23 +648,64 @@ impl PathsPipelineState { }; 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 = - create_buffer(device, std::mem::size_of::>(), 32)?; + let instance_buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let instance_view = create_buffer_view(device, &instance_buffer)?; + let vertex_buffer = create_buffer(device, std::mem::size_of::(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; - 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( + shader_blob.GetBufferPointer() as *const u8, + shader_blob.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_R32G32B32A32_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_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, + instance_buffer, + instance_buffer_size: 32, vertex_buffer, vertex_buffer_size: 32, - indirect_draw_buffer, - indirect_buffer_size: 32, - view, + instance_view, vertex_view, + input_layout, }) } } @@ -687,6 +717,14 @@ struct PathSprite { color: Background, } +#[derive(Clone, Debug)] +#[repr(C)] +struct DirectXPathVertex { + xy_position: Point, + content_mask: Bounds, + sprite_index: u32, +} + fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -919,7 +957,7 @@ fn create_pipieline( fragment, buffer, buffer_size, - view, + view: [Some(view)], }) } @@ -960,24 +998,10 @@ fn create_buffer( fn create_buffer_view( device: &ID3D11Device, buffer: &ID3D11Buffer, -) -> Result<[Option; 1]> { +) -> Result { let mut view = None; unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; - Ok([view]) -} - -fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { - let desc = D3D11_BUFFER_DESC { - ByteWidth: std::mem::size_of::() as u32 * buffer_size, - Usage: D3D11_USAGE_DYNAMIC, - BindFlags: D3D11_BIND_INDEX_BUFFER.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, - }; - let mut buffer = None; - unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; - Ok(buffer.unwrap()) + Ok(view.unwrap()) } fn update_global_params( @@ -1032,15 +1056,15 @@ fn update_buffer_capacity( let buffer_size = data_size.next_power_of_two(); let buffer = create_buffer(device, element_size, buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, view)) + Some((buffer, buffer_size, [Some(view)])) } fn update_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { - if pipeline.buffer_size >= data_size { +) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { + if pipeline.instance_buffer_size >= data_size { return None; } let buffer_size = data_size.next_power_of_two(); @@ -1053,14 +1077,14 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { +) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { if pipeline.vertex_buffer_size >= vertex_size { return None; } let vertex_size = vertex_size.next_power_of_two(); let buffer = create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), vertex_size, ) .unwrap(); @@ -1068,19 +1092,6 @@ fn update_paths_vertex_capacity( Some((buffer, vertex_size, view)) } -fn update_indirect_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.indirect_buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); - Some((buffer, buffer_size)) -} - fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1092,27 +1103,22 @@ fn update_pipeline( fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), + input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), ) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; + pipeline.instance_buffer = input.0; + pipeline.instance_buffer_size = input.1; + pipeline.instance_view = input.2; } fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), + input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), ) { pipeline.vertex_buffer = input.0; pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } -fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.indirect_draw_buffer = input.0; - pipeline.indirect_buffer_size = input.1; -} - fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1127,37 +1133,6 @@ fn update_buffer( Ok(()) } -fn prepare_indirect_draws( - device_context: &ID3D11DeviceContext, - pipeline: &PathsPipelineState, - viewport: &[D3D11_VIEWPORT], - global_params: &[Option], - topology: D3D_PRIMITIVE_TOPOLOGY, -) -> Result<()> { - unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); - device_context.VSSetShaderResources(2, Some(&pipeline.view)); - device_context.PSSetShaderResources(2, 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, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 1ed2de2240..b926847cbf 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -256,7 +256,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -876,9 +876,10 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { ** */ -struct PathVertex { - float2 xy_position; - Bounds content_mask; +struct PathVertexInput { + float2 xy_position: POSITION; + float4 content_mask: TEXCOORD0; + uint sprite_index: TEXCOORD1; }; struct PathSprite { @@ -895,17 +896,19 @@ struct PathVertexOutput { nointerpolation float4 color1: COLOR2; }; -StructuredBuffer path_vertices: register(t1); StructuredBuffer path_sprites: register(t2); -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(PathVertexInput input) { + PathSprite sprite = path_sprites[input.sprite_index]; + + Bounds content_mask; + content_mask.origin = input.content_mask.xy; + content_mask.size = input.content_mask.zw; 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, content_mask); + output.sprite_id = input.sprite_index; GradientColor gradient = prepare_gradient_color( sprite.color.tag, @@ -925,7 +928,7 @@ float4 paths_fragment(PathVertexOutput input): SV_Target { if (any(input.clip_distance < zero)) { return zero; } - + PathSprite sprite = path_sprites[input.sprite_id]; Background background = sprite.color; float4 color = gradient_color(background, input.position.xy, sprite.bounds, @@ -968,7 +971,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); From 31fab3a37acf7c2106a1e3ef729d821661396e57 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 15 Jul 2025 16:48:19 -0700 Subject: [PATCH 101/202] Fix dxgi_factory type error in release mode --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 3614641ced..a1f4dbeb58 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -729,7 +729,7 @@ 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)?) } } From 0c274370c31e844b7f1f0cd0da36a65f2cad17a1 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 15 Jul 2025 23:56:55 +0800 Subject: [PATCH 102/202] wip --- .../gpui/src/platform/windows/directx_renderer.rs | 3 +-- crates/gpui/src/platform/windows/shaders.hlsl | 15 +++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index a1f4dbeb58..542c90444d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1,8 +1,7 @@ -use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc}; +use std::sync::Arc; use ::util::ResultExt; use anyhow::{Context, Result}; -use collections::FxHasher; // #[cfg(not(feature = "enable-renderdoc"))] // use windows::Win32::Graphics::DirectComposition::*; use windows::{ diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index b926847cbf..18760dcf78 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -889,7 +889,15 @@ 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; @@ -923,12 +931,7 @@ PathVertexOutput paths_vertex(PathVertexInput input) { 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, From 723712e3cf99fe1547e3a63522f3eba0b6fa5ac3 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:08:30 +0800 Subject: [PATCH 103/202] Revert "Fix path rendering - draw all paths w/ one regular draw call" This reverts commit 83d942611f5eda5245eaddcc35e052d13b58b925. --- .../src/platform/windows/directx_renderer.rs | 255 ++++++++++-------- crates/gpui/src/platform/windows/shaders.hlsl | 27 +- 2 files changed, 152 insertions(+), 130 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 542c90444d..4c8642e7c2 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -59,6 +59,14 @@ struct DirectXGlobalElements { blend_state: ID3D11BlendState, } +#[repr(C)] +struct DrawInstancedIndirectArgs { + vertex_count_per_instance: u32, + instance_count: u32, + start_vertex_location: u32, + start_instance_location: u32, +} + // #[cfg(not(feature = "enable-renderdoc"))] // struct DirectComposition { // comp_device: IDCompositionDevice, @@ -273,12 +281,22 @@ impl DirectXRenderer { } let mut vertices = Vec::new(); let mut sprites = Vec::with_capacity(paths.len()); - + let mut draw_indirect_commands = Vec::with_capacity(paths.len()); + let mut start_vertex_location = 0; for (i, path) in paths.iter().enumerate() { - vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex { + draw_indirect_commands.push(DrawInstancedIndirectArgs { + vertex_count_per_instance: path.vertices.len() as u32, + instance_count: 1, + start_vertex_location, + start_instance_location: i as u32, + }); + start_vertex_location += path.vertices.len() as u32; + + vertices.extend(path.vertices.iter().map(|v| PathVertex { xy_position: v.xy_position, - content_mask: path.content_mask.bounds, - sprite_index: i as u32, + content_mask: ContentMask { + bounds: path.content_mask.bounds, + }, })); sprites.push(PathSprite { @@ -295,7 +313,7 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.instance_buffer, + &self.pipelines.paths_pipeline.buffer, &sprites, )?; update_paths_vertex_capacity( @@ -309,40 +327,32 @@ impl DirectXRenderer { &self.pipelines.paths_pipeline.vertex_buffer, &vertices, )?; + update_indirect_buffer_capacity( + &self.pipelines.paths_pipeline, + draw_indirect_commands.len(), + &self.devices.device, + ) + .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); + update_buffer( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, + &draw_indirect_commands, + )?; + prepare_indirect_draws( + &self.devices.device_context, + &self.pipelines.paths_pipeline, + &self.context.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + )?; - let device_context = &self.devices.device_context; - unsafe { - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - device_context.VSSetShader(&self.pipelines.paths_pipeline.vertex, None); - device_context.PSSetShader(&self.pipelines.paths_pipeline.fragment, None); - device_context.VSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); - device_context.PSSetConstantBuffers(0, Some(&self.globals.global_params_buffer)); - device_context.VSSetShaderResources( - 2, - Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), + for i in 0..paths.len() { + draw_indirect( + &self.devices.device_context, + &self.pipelines.paths_pipeline.indirect_draw_buffer, + (i * std::mem::size_of::()) as u32, ); - device_context.PSSetShaderResources( - 2, - Some(&[Some(self.pipelines.paths_pipeline.instance_view.clone())]), - ); - device_context.PSSetSamplers(0, Some(&self.globals.sampler)); - device_context.OMSetBlendState(&self.globals.blend_state, None, 0xffffffff); - let stride = std::mem::size_of::() as u32; - let offset = 0u32; - device_context.IASetVertexBuffers( - 0, - 1, - Some([Some(self.pipelines.paths_pipeline.vertex_buffer.clone())].as_ptr()), - Some(&stride), - Some(&offset), - ); - device_context.IASetInputLayout(&self.pipelines.paths_pipeline.input_layout); } - - unsafe { - device_context.Draw(vertices.len() as u32, 0); - } - Ok(()) } @@ -616,18 +626,19 @@ struct PipelineState { struct PathsPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, - instance_buffer: ID3D11Buffer, - instance_buffer_size: usize, + buffer: ID3D11Buffer, + buffer_size: usize, vertex_buffer: ID3D11Buffer, vertex_buffer_size: usize, - instance_view: ID3D11ShaderResourceView, - vertex_view: ID3D11ShaderResourceView, - input_layout: ID3D11InputLayout, + indirect_draw_buffer: ID3D11Buffer, + indirect_buffer_size: usize, + view: [Option; 1], + vertex_view: [Option; 1], } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let (vertex, shader_blob) = { + let vertex = { let shader_blob = shader_resources::build_shader_blob("paths_vertex", "vs_5_0")?; let bytes = unsafe { std::slice::from_raw_parts( @@ -635,7 +646,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - (create_vertex_shader(device, bytes)?, shader_blob) + create_vertex_shader(device, bytes)? }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -647,64 +658,23 @@ impl PathsPipelineState { }; create_fragment_shader(device, bytes)? }; - let instance_buffer = create_buffer(device, std::mem::size_of::(), 32)?; - let instance_view = create_buffer_view(device, &instance_buffer)?; - let vertex_buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let buffer = create_buffer(device, std::mem::size_of::(), 32)?; + let view = create_buffer_view(device, &buffer)?; + let vertex_buffer = + create_buffer(device, std::mem::size_of::>(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; - - // Create input layout - let input_layout = unsafe { - let shader_bytes = std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *const u8, - shader_blob.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_R32G32B32A32_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_R32_UINT, - InputSlot: 0, - AlignedByteOffset: 24, - InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - ], - shader_bytes, - Some(&mut layout), - )?; - layout.unwrap() - }; - + let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; Ok(Self { vertex, fragment, - instance_buffer, - instance_buffer_size: 32, + buffer, + buffer_size: 32, vertex_buffer, vertex_buffer_size: 32, - instance_view, + indirect_draw_buffer, + indirect_buffer_size: 32, + view, vertex_view, - input_layout, }) } } @@ -716,14 +686,6 @@ struct PathSprite { color: Background, } -#[derive(Clone, Debug)] -#[repr(C)] -struct DirectXPathVertex { - xy_position: Point, - content_mask: Bounds, - sprite_index: u32, -} - fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -956,7 +918,7 @@ fn create_pipieline( fragment, buffer, buffer_size, - view: [Some(view)], + view, }) } @@ -997,10 +959,24 @@ fn create_buffer( fn create_buffer_view( device: &ID3D11Device, buffer: &ID3D11Buffer, -) -> Result { +) -> Result<[Option; 1]> { let mut view = None; unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?; - Ok(view.unwrap()) + Ok([view]) +} + +fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { + let desc = D3D11_BUFFER_DESC { + ByteWidth: std::mem::size_of::() as u32 * buffer_size, + Usage: D3D11_USAGE_DYNAMIC, + BindFlags: D3D11_BIND_INDEX_BUFFER.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, + }; + let mut buffer = None; + unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; + Ok(buffer.unwrap()) } fn update_global_params( @@ -1055,15 +1031,15 @@ fn update_buffer_capacity( let buffer_size = data_size.next_power_of_two(); let buffer = create_buffer(device, element_size, buffer_size).unwrap(); let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, [Some(view)])) + Some((buffer, buffer_size, view)) } fn update_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { - if pipeline.instance_buffer_size >= data_size { +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { + if pipeline.buffer_size >= data_size { return None; } let buffer_size = data_size.next_power_of_two(); @@ -1076,14 +1052,14 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, ID3D11ShaderResourceView)> { +) -> Option<(ID3D11Buffer, usize, [Option; 1])> { if pipeline.vertex_buffer_size >= vertex_size { return None; } let vertex_size = vertex_size.next_power_of_two(); let buffer = create_buffer( device, - std::mem::size_of::(), + std::mem::size_of::>(), vertex_size, ) .unwrap(); @@ -1091,6 +1067,19 @@ fn update_paths_vertex_capacity( Some((buffer, vertex_size, view)) } +fn update_indirect_buffer_capacity( + pipeline: &PathsPipelineState, + data_size: usize, + device: &ID3D11Device, +) -> Option<(ID3D11Buffer, usize)> { + if pipeline.indirect_buffer_size >= data_size { + return None; + } + let buffer_size = data_size.next_power_of_two(); + let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + Some((buffer, buffer_size)) +} + fn update_pipeline( pipeline: &mut PipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1102,22 +1091,27 @@ fn update_pipeline( fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), + input: (ID3D11Buffer, usize, [Option; 1]), ) { - pipeline.instance_buffer = input.0; - pipeline.instance_buffer_size = input.1; - pipeline.instance_view = input.2; + pipeline.buffer = input.0; + pipeline.buffer_size = input.1; + pipeline.view = input.2; } fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, ID3D11ShaderResourceView), + input: (ID3D11Buffer, usize, [Option; 1]), ) { pipeline.vertex_buffer = input.0; pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } +fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { + pipeline.indirect_draw_buffer = input.0; + pipeline.indirect_buffer_size = input.1; +} + fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1132,6 +1126,37 @@ fn update_buffer( Ok(()) } +fn prepare_indirect_draws( + device_context: &ID3D11DeviceContext, + pipeline: &PathsPipelineState, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, +) -> Result<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); + device_context.VSSetShaderResources(2, Some(&pipeline.view)); + device_context.PSSetShaderResources(2, 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, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 18760dcf78..3438e708a3 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -256,7 +256,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -876,10 +876,9 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { ** */ -struct PathVertexInput { - float2 xy_position: POSITION; - float4 content_mask: TEXCOORD0; - uint sprite_index: TEXCOORD1; +struct PathVertex { + float2 xy_position; + Bounds content_mask; }; struct PathSprite { @@ -904,19 +903,17 @@ struct PathFragmentInput { nointerpolation float4 color1: COLOR2; }; +StructuredBuffer path_vertices: register(t1); StructuredBuffer path_sprites: register(t2); -PathVertexOutput paths_vertex(PathVertexInput input) { - PathSprite sprite = path_sprites[input.sprite_index]; - - Bounds content_mask; - content_mask.origin = input.content_mask.xy; - content_mask.size = input.content_mask.zw; +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 output; - output.position = to_device_position_impl(input.xy_position); - output.clip_distance = distance_from_clip_rect_impl(input.xy_position, content_mask); - output.sprite_id = input.sprite_index; + 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; GradientColor gradient = prepare_gradient_color( sprite.color.tag, @@ -974,7 +971,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); From 5472c71f1a9a9650931cba92a656dac7578e0be0 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:24:55 +0800 Subject: [PATCH 104/202] fix paths rendering --- .../src/platform/windows/directx_renderer.rs | 99 +++++++++++++++++-- crates/gpui/src/platform/windows/shaders.hlsl | 10 +- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4c8642e7c2..9a457420aa 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -324,7 +324,11 @@ impl DirectXRenderer { .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.vertex_buffer, + self.pipelines + .paths_pipeline + .vertex_buffer + .as_ref() + .unwrap(), &vertices, )?; update_indirect_buffer_capacity( @@ -628,10 +632,11 @@ struct PathsPipelineState { fragment: ID3D11PixelShader, buffer: ID3D11Buffer, buffer_size: usize, - vertex_buffer: ID3D11Buffer, + vertex_buffer: Option, vertex_buffer_size: usize, indirect_draw_buffer: ID3D11Buffer, indirect_buffer_size: usize, + input_layout: ID3D11InputLayout, view: [Option; 1], vertex_view: [Option; 1], } @@ -663,7 +668,71 @@ impl PathsPipelineState { let vertex_buffer = create_buffer(device, std::mem::size_of::>(), 32)?; let vertex_view = create_buffer_view(device, &vertex_buffer)?; + let vertex_buffer = Some(vertex_buffer); let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; + // Create input layout + let input_layout = unsafe { + let (vertex, shader_blob) = { + 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 shader_bytes = std::slice::from_raw_parts( + shader_blob.GetBufferPointer() as *const u8, + shader_blob.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, + }, + ], + shader_bytes, + Some(&mut layout), + )?; + layout.unwrap() + }; + Ok(Self { vertex, fragment, @@ -673,6 +742,7 @@ impl PathsPipelineState { vertex_buffer_size: 32, indirect_draw_buffer, indirect_buffer_size: 32, + input_layout, view, vertex_view, }) @@ -965,11 +1035,11 @@ fn create_buffer_view( Ok([view]) } -fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: u32) -> Result { +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, @@ -1076,7 +1146,7 @@ fn update_indirect_buffer_capacity( return None; } let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size as u32).unwrap(); + let buffer = create_indirect_draw_buffer(device, data_size).unwrap(); Some((buffer, buffer_size)) } @@ -1102,7 +1172,7 @@ fn update_paths_pipeline_vertex( pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize, [Option; 1]), ) { - pipeline.vertex_buffer = input.0; + pipeline.vertex_buffer = Some(input.0); pipeline.vertex_buffer_size = input.1; pipeline.vertex_view = input.2; } @@ -1134,15 +1204,24 @@ fn prepare_indirect_draws( topology: D3D_PRIMITIVE_TOPOLOGY, ) -> Result<()> { unsafe { - device_context.VSSetShaderResources(1, Some(&pipeline.vertex_view)); - device_context.VSSetShaderResources(2, Some(&pipeline.view)); - device_context.PSSetShaderResources(2, Some(&pipeline.view)); + 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)); + let stride = std::mem::size_of::>() as u32; + let offset = 0u32; + device_context.IASetVertexBuffers( + 0, + 1, + Some(&pipeline.vertex_buffer), + Some(&stride), + Some(&offset), + ); + device_context.IASetInputLayout(&pipeline.input_layout); } Ok(()) } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 3438e708a3..7e59bb714e 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -877,8 +877,8 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { */ struct PathVertex { - float2 xy_position; - Bounds content_mask; + float2 xy_position: POSITION; + Bounds content_mask: TEXCOORD; }; struct PathSprite { @@ -903,11 +903,9 @@ struct PathFragmentInput { 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]; +PathVertexOutput paths_vertex(PathVertex v, uint instance_id: SV_InstanceID) { PathSprite sprite = path_sprites[instance_id]; PathVertexOutput output; From 204071e6bf9f1651db86fd00c46b6b941b075dcc Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 11:37:51 +0800 Subject: [PATCH 105/202] remove unused --- .../src/platform/windows/directx_renderer.rs | 57 +++++-------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 9a457420aa..a3931519f9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -638,12 +638,11 @@ struct PathsPipelineState { indirect_buffer_size: usize, input_layout: ID3D11InputLayout, view: [Option; 1], - vertex_view: [Option; 1], } impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { - let vertex = { + 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( @@ -651,7 +650,7 @@ impl PathsPipelineState { shader_blob.GetBufferSize(), ) }; - create_vertex_shader(device, bytes)? + (create_vertex_shader(device, bytes)?, shader_blob) }; let fragment = { let shader_blob = shader_resources::build_shader_blob("paths_fragment", "ps_5_0")?; @@ -665,36 +664,17 @@ impl PathsPipelineState { }; let buffer = create_buffer(device, std::mem::size_of::(), 32)?; let view = create_buffer_view(device, &buffer)?; - let vertex_buffer = - create_buffer(device, std::mem::size_of::>(), 32)?; - let vertex_view = create_buffer_view(device, &vertex_buffer)?; - let vertex_buffer = Some(vertex_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 (vertex, shader_blob) = { - 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 shader_bytes = std::slice::from_raw_parts( - shader_blob.GetBufferPointer() as *const u8, - shader_blob.GetBufferSize(), + vertex_shader.GetBufferPointer() as *const u8, + vertex_shader.GetBufferSize(), ); let mut layout = None; device.CreateInputLayout( @@ -744,7 +724,6 @@ impl PathsPipelineState { indirect_buffer_size: 32, input_layout, view, - vertex_view, }) } } @@ -1122,7 +1101,7 @@ fn update_paths_vertex_capacity( pipeline: &PathsPipelineState, vertex_size: usize, device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { +) -> Option<(ID3D11Buffer, usize)> { if pipeline.vertex_buffer_size >= vertex_size { return None; } @@ -1133,8 +1112,7 @@ fn update_paths_vertex_capacity( vertex_size, ) .unwrap(); - let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, vertex_size, view)) + Some((buffer, vertex_size)) } fn update_indirect_buffer_capacity( @@ -1168,13 +1146,9 @@ fn update_paths_pipeline_buffer( pipeline.view = input.2; } -fn update_paths_pipeline_vertex( - pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { +fn update_paths_pipeline_vertex(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { pipeline.vertex_buffer = Some(input.0); pipeline.vertex_buffer_size = input.1; - pipeline.vertex_view = input.2; } fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { @@ -1212,14 +1186,13 @@ fn prepare_indirect_draws( device_context.PSSetShader(&pipeline.fragment, None); device_context.VSSetConstantBuffers(0, Some(global_params)); device_context.PSSetConstantBuffers(0, Some(global_params)); - let stride = std::mem::size_of::>() as u32; - let offset = 0u32; + const STRIDE: u32 = std::mem::size_of::>() as u32; device_context.IASetVertexBuffers( 0, 1, Some(&pipeline.vertex_buffer), - Some(&stride), - Some(&offset), + Some(&STRIDE), + Some(&0), ); device_context.IASetInputLayout(&pipeline.input_layout); } From 5261c02d1835c8759e78df731b6c37fb4462701d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 14:04:49 +0800 Subject: [PATCH 106/202] refactor --- .../src/platform/windows/directx_renderer.rs | 230 +++++++----------- 1 file changed, 94 insertions(+), 136 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index a3931519f9..dd5e1a5b32 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -45,12 +45,12 @@ struct DirectXContext { } struct DirectXRenderPipelines { - shadow_pipeline: PipelineState, - quad_pipeline: PipelineState, + shadow_pipeline: PipelineState, + quad_pipeline: PipelineState, paths_pipeline: PathsPipelineState, - underline_pipeline: PipelineState, - mono_sprites: PipelineState, - poly_sprites: PipelineState, + underline_pipeline: PipelineState, + mono_sprites: PipelineState, + poly_sprites: PipelineState, } struct DirectXGlobalElements { @@ -225,16 +225,9 @@ impl DirectXRenderer { 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( @@ -252,16 +245,9 @@ 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( @@ -364,16 +350,9 @@ impl DirectXRenderer { 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( @@ -395,19 +374,12 @@ 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, )?; + let texture_view = self.atlas.get_texture_view(texture_id); draw_with_texture( &self.devices.device_context, &self.pipelines.mono_sprites, @@ -427,19 +399,12 @@ 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, )?; + let texture_view = self.atlas.get_texture_view(texture_id); draw_with_texture( &self.devices.device_context, &self.pipelines.poly_sprites, @@ -490,47 +455,35 @@ impl DirectXContext { 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, - // )?; + let quad_pipeline = + PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?; let paths_pipeline = PathsPipelineState::new(device)?; - let underline_pipeline = create_pipieline( + let underline_pipeline = PipelineState::new( device, + "underline_pipeline", "underline_vertex", "underline_fragment", - std::mem::size_of::(), 32, )?; - 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, )?; - 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, )?; @@ -619,12 +572,14 @@ 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 { @@ -640,6 +595,71 @@ struct PathsPipelineState { 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(); + 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; + } + unsafe { + let mut dest = std::mem::zeroed(); + device_context.Map(&self.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(&self.buffer, 0); + } + Ok(()) + } +} + impl PathsPipelineState { fn new(device: &ID3D11Device) -> Result { let (vertex, vertex_shader) = { @@ -933,44 +953,6 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } -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, - }) -} - fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { let mut shader = None; @@ -1068,21 +1050,6 @@ fn pre_draw( 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; - } - let buffer_size = data_size.next_power_of_two(); - 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_paths_buffer_capacity( pipeline: &PathsPipelineState, data_size: usize, @@ -1128,15 +1095,6 @@ fn update_indirect_buffer_capacity( Some((buffer, buffer_size)) } -fn update_pipeline( - pipeline: &mut PipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; -} - fn update_paths_pipeline_buffer( pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize, [Option; 1]), @@ -1209,9 +1167,9 @@ fn draw_indirect( } } -fn draw_normal( +fn draw_normal( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PipelineState, viewport: &[D3D11_VIEWPORT], global_params: &[Option], topology: D3D_PRIMITIVE_TOPOLOGY, @@ -1233,9 +1191,9 @@ fn draw_normal( Ok(()) } -fn draw_with_texture( +fn draw_with_texture( device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, + pipeline: &PipelineState, texture: &[Option], viewport: &[D3D11_VIEWPORT], global_params: &[Option], From 667c19907a54248a78b6a7113d894ed308954cc4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 14:14:31 +0800 Subject: [PATCH 107/202] refactor --- .../src/platform/windows/directx_renderer.rs | 121 ++++++++---------- 1 file changed, 54 insertions(+), 67 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index dd5e1a5b32..273d5a1a2b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -230,13 +230,10 @@ impl DirectXRenderer { &self.devices.device_context, shadows, )?; - draw_normal( + self.pipelines.shadow_pipeline.draw( &self.devices.device_context, - &self.pipelines.shadow_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, shadows.len() as u32, ) } @@ -250,13 +247,10 @@ impl DirectXRenderer { &self.devices.device_context, quads, )?; - draw_normal( + self.pipelines.quad_pipeline.draw( &self.devices.device_context, - &self.pipelines.quad_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, quads.len() as u32, ) } @@ -355,13 +349,10 @@ impl DirectXRenderer { &self.devices.device_context, underlines, )?; - draw_normal( + self.pipelines.underline_pipeline.draw( &self.devices.device_context, - &self.pipelines.underline_pipeline, &self.context.viewport, &self.globals.global_params_buffer, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, - 4, underlines.len() as u32, ) } @@ -380,9 +371,8 @@ impl DirectXRenderer { sprites, )?; let texture_view = self.atlas.get_texture_view(texture_id); - draw_with_texture( + self.pipelines.mono_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.mono_sprites, &texture_view, &self.context.viewport, &self.globals.global_params_buffer, @@ -405,9 +395,8 @@ impl DirectXRenderer { sprites, )?; let texture_view = self.atlas.get_texture_view(texture_id); - draw_with_texture( + self.pipelines.poly_sprites.draw_with_texture( &self.devices.device_context, - &self.pipelines.poly_sprites, &texture_view, &self.context.viewport, &self.globals.global_params_buffer, @@ -658,6 +647,55 @@ impl PipelineState { } Ok(()) } + + fn draw( + &self, + device_context: &ID3D11DeviceContext, + viewport: &[D3D11_VIEWPORT], + global_params: &[Option], + instance_count: u32, + ) -> Result<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.view)); + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + + 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<()> { + unsafe { + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.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(()) + } } impl PathsPipelineState { @@ -1167,57 +1205,6 @@ fn draw_indirect( } } -fn draw_normal( - device_context: &ID3D11DeviceContext, - pipeline: &PipelineState, - viewport: &[D3D11_VIEWPORT], - 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.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)); - - 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; mod shader_resources { From b012246d2b62ec6e292aa99c4b060d04ab75f1f0 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 15:36:24 +0800 Subject: [PATCH 108/202] refactor --- .../src/platform/windows/directx_renderer.rs | 285 +++++++----------- 1 file changed, 112 insertions(+), 173 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 273d5a1a2b..623383946a 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -285,59 +285,19 @@ impl DirectXRenderer { }); } - update_paths_buffer_capacity( - &self.pipelines.paths_pipeline, - sprites.len(), + self.pipelines.paths_pipeline.update_buffer( &self.devices.device, - ) - .map(|input| update_paths_pipeline_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( &self.devices.device_context, - &self.pipelines.paths_pipeline.buffer, &sprites, - )?; - update_paths_vertex_capacity( - &mut self.pipelines.paths_pipeline, - vertices.len(), - &self.devices.device, - ) - .map(|input| update_paths_pipeline_vertex(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - self.pipelines - .paths_pipeline - .vertex_buffer - .as_ref() - .unwrap(), &vertices, - )?; - update_indirect_buffer_capacity( - &self.pipelines.paths_pipeline, - draw_indirect_commands.len(), - &self.devices.device, - ) - .map(|input| update_paths_indirect_buffer(&mut self.pipelines.paths_pipeline, input)); - update_buffer( - &self.devices.device_context, - &self.pipelines.paths_pipeline.indirect_draw_buffer, &draw_indirect_commands, )?; - prepare_indirect_draws( + self.pipelines.paths_pipeline.draw( &self.devices.device_context, - &self.pipelines.paths_pipeline, + paths.len(), &self.context.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_pipeline.indirect_draw_buffer, - (i * std::mem::size_of::()) as u32, - ); - } - Ok(()) + ) } fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> { @@ -634,18 +594,19 @@ impl PipelineState { ) -> 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; } - unsafe { - let mut dest = std::mem::zeroed(); - device_context.Map(&self.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(&self.buffer, 0); - } - Ok(()) + update_buffer(device_context, &self.buffer, data) } fn draw( @@ -784,6 +745,101 @@ impl PathsPipelineState { view, }) } + + fn update_buffer( + &mut self, + device: &ID3D11Device, + device_context: &ID3D11DeviceContext, + buffer_data: &[PathSprite], + vertices_data: &[PathVertex], + 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<()> { + unsafe { + device_context.VSSetShaderResources(1, Some(&self.view)); + device_context.PSSetShaderResources(1, Some(&self.view)); + device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + device_context.RSSetViewports(Some(viewport)); + device_context.VSSetShader(&self.vertex, None); + device_context.PSSetShader(&self.fragment, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + 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(()) + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1048,21 +1104,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res 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], @@ -1071,13 +1112,14 @@ fn pre_draw( clear_color: [f32; 4], blend_state: &ID3D11BlendState, ) -> Result<()> { - update_global_params( + let global_params = global_params_buffer[0].as_ref().unwrap(); + update_buffer( device_context, - global_params_buffer, - GlobalParams { + global_params, + &[GlobalParams { viewport_size: [view_port[0].Width, view_port[0].Height], ..Default::default() - }, + }], )?; unsafe { device_context.RSSetViewports(Some(view_port)); @@ -1088,70 +1130,6 @@ fn pre_draw( Ok(()) } -fn update_paths_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize, [Option; 1])> { - if pipeline.buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_buffer(device, std::mem::size_of::(), buffer_size).unwrap(); - let view = create_buffer_view(device, &buffer).unwrap(); - Some((buffer, buffer_size, view)) -} - -fn update_paths_vertex_capacity( - pipeline: &PathsPipelineState, - vertex_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.vertex_buffer_size >= vertex_size { - return None; - } - let vertex_size = vertex_size.next_power_of_two(); - let buffer = create_buffer( - device, - std::mem::size_of::>(), - vertex_size, - ) - .unwrap(); - Some((buffer, vertex_size)) -} - -fn update_indirect_buffer_capacity( - pipeline: &PathsPipelineState, - data_size: usize, - device: &ID3D11Device, -) -> Option<(ID3D11Buffer, usize)> { - if pipeline.indirect_buffer_size >= data_size { - return None; - } - let buffer_size = data_size.next_power_of_two(); - let buffer = create_indirect_draw_buffer(device, data_size).unwrap(); - Some((buffer, buffer_size)) -} - -fn update_paths_pipeline_buffer( - pipeline: &mut PathsPipelineState, - input: (ID3D11Buffer, usize, [Option; 1]), -) { - pipeline.buffer = input.0; - pipeline.buffer_size = input.1; - pipeline.view = input.2; -} - -fn update_paths_pipeline_vertex(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.vertex_buffer = Some(input.0); - pipeline.vertex_buffer_size = input.1; -} - -fn update_paths_indirect_buffer(pipeline: &mut PathsPipelineState, input: (ID3D11Buffer, usize)) { - pipeline.indirect_draw_buffer = input.0; - pipeline.indirect_buffer_size = input.1; -} - fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1166,45 +1144,6 @@ fn update_buffer( Ok(()) } -fn prepare_indirect_draws( - device_context: &ID3D11DeviceContext, - pipeline: &PathsPipelineState, - viewport: &[D3D11_VIEWPORT], - global_params: &[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)); - const STRIDE: u32 = std::mem::size_of::>() as u32; - device_context.IASetVertexBuffers( - 0, - 1, - Some(&pipeline.vertex_buffer), - Some(&STRIDE), - Some(&0), - ); - device_context.IASetInputLayout(&pipeline.input_layout); - } - Ok(()) -} - -fn draw_indirect( - device_context: &ID3D11DeviceContext, - indirect_draw_buffer: &ID3D11Buffer, - offset: u32, -) { - unsafe { - device_context.DrawInstancedIndirect(indirect_draw_buffer, offset); - } -} - const BUFFER_COUNT: usize = 3; mod shader_resources { From 54e2420405ee65b31bfc78e64b754ddf0dbb13e1 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 15:48:55 +0800 Subject: [PATCH 109/202] introduce `set_pipeline_state` --- .../src/platform/windows/directx_renderer.rs | 86 +++++++++++++------ 1 file changed, 61 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 623383946a..ee38632f9b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -616,16 +616,16 @@ impl PipelineState { 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.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - device_context.DrawInstanced(4, instance_count, 0, 0); } Ok(()) @@ -640,15 +640,16 @@ impl PipelineState { 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.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); - device_context.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); device_context.PSSetSamplers(0, Some(sampler)); device_context.VSSetShaderResources(0, Some(texture)); device_context.PSSetShaderResources(0, Some(texture)); @@ -811,15 +812,16 @@ impl PathsPipelineState { 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 { - device_context.VSSetShaderResources(1, Some(&self.view)); - device_context.PSSetShaderResources(1, Some(&self.view)); - device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - device_context.RSSetViewports(Some(viewport)); - device_context.VSSetShader(&self.vertex, None); - device_context.PSSetShader(&self.fragment, None); - device_context.VSSetConstantBuffers(0, Some(global_params)); - device_context.PSSetConstantBuffers(0, Some(global_params)); const STRIDE: u32 = std::mem::size_of::>() as u32; device_context.IASetVertexBuffers( 0, @@ -849,6 +851,7 @@ struct PathSprite { color: Background, } +#[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] let factory_flag = DXGI_CREATE_FACTORY_DEBUG; @@ -970,6 +973,7 @@ fn create_swap_chain_default( Ok(swap_chain) } +#[inline] fn set_render_target_view( swap_chain: &IDXGISwapChain1, device: &ID3D11Device, @@ -987,6 +991,7 @@ fn set_render_target_view( Ok(back_buffer) } +#[inline] fn set_viewport( device_context: &ID3D11DeviceContext, width: f32, @@ -1004,6 +1009,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, @@ -1028,6 +1034,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. @@ -1047,6 +1054,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } +#[inline] fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { let mut shader = None; @@ -1055,6 +1063,7 @@ fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { unsafe { let mut shader = None; @@ -1063,6 +1072,7 @@ fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result Result { let desc = D3D11_BUFFER_DESC { ByteWidth: (std::mem::size_of::() * buffer_size) as u32, @@ -1104,6 +1116,7 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res Ok(buffer.unwrap()) } +#[inline] fn pre_draw( device_context: &ID3D11DeviceContext, global_params_buffer: &[Option; 1], @@ -1130,6 +1143,7 @@ fn pre_draw( Ok(()) } +#[inline] fn update_buffer( device_context: &ID3D11DeviceContext, buffer: &ID3D11Buffer, @@ -1144,6 +1158,28 @@ fn update_buffer( Ok(()) } +#[inline] +fn set_pipeline_state( + device_context: &ID3D11DeviceContext, + buffer_view: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, + viewport: &[D3D11_VIEWPORT], + vertex_shader: &ID3D11VertexShader, + fragment_shader: &ID3D11PixelShader, + global_params: &[Option], +) { + unsafe { + 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(vertex_shader, None); + device_context.PSSetShader(fragment_shader, None); + device_context.VSSetConstantBuffers(0, Some(global_params)); + device_context.PSSetConstantBuffers(0, Some(global_params)); + } +} + const BUFFER_COUNT: usize = 3; mod shader_resources { From bfdcc65801bfae7a4768c9d404aee6e3b1f24275 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 17:14:24 +0800 Subject: [PATCH 110/202] reenable transparency --- .../src/platform/windows/directx_renderer.rs | 50 +++++++++++++++---- crates/gpui/src/platform/windows/window.rs | 48 +++++++++--------- 2 files changed, 64 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ee38632f9b..428464876a 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; @@ -37,7 +37,7 @@ pub(crate) struct DirectXDevices { } struct DirectXContext { - swap_chain: IDXGISwapChain1, + swap_chain: ManuallyDrop, back_buffer: [Option; 1], viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] @@ -212,13 +212,35 @@ impl DirectXRenderer { &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(()) + let transparent = background_appearance != WindowBackgroundAppearance::Opaque; + if self.transparent == transparent { + return Ok(()); } + self.transparent = transparent; + // unsafe { + // // recreate the swapchain + // self.devices.device_context.OMSetRenderTargets(None, None); + // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.swap_chain); + // self.context.swap_chain = create_swap_chain_default( + // &self.devices.dxgi_factory, + // &self.devices.device, + // self.hwnd, + // transparent, + // )?; + // self.context.back_buffer = [Some(set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?)]; + // self.context.viewport = set_viewport( + // &self.devices.device_context, + // self.context.viewport[0].Width, + // self.context.viewport[0].Height, + // ); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // } + Ok(()) } fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { @@ -851,6 +873,14 @@ struct PathSprite { color: Background, } +impl Drop for DirectXContext { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self.swap_chain); + } + } +} + #[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] @@ -948,7 +978,7 @@ fn create_swap_chain_default( device: &ID3D11Device, hwnd: HWND, _transparent: bool, -) -> Result { +) -> Result> { use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; let desc = DXGI_SWAP_CHAIN_DESC1 { @@ -970,7 +1000,7 @@ fn create_swap_chain_default( let swap_chain = unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; - Ok(swap_chain) + Ok(ManuallyDrop::new(swap_chain)) } #[inline] diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 5d1c91b5e3..74001c84e9 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,8 +384,7 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - // WS_EX_APPWINDOW | WS_EX_LAYERED, - WS_EX_APPWINDOW, + WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -403,7 +402,7 @@ impl WindowsWindow { handle, hide_title_bar, display, - transparent: true, + transparent: false, is_movable: params.is_movable, min_size: params.window_min_size, executor, @@ -462,7 +461,7 @@ impl WindowsWindow { // 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)? }; + unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } @@ -707,27 +706,28 @@ 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 mut window_state = self.0.state.borrow_mut(); + window_state + .renderer + .update_transparency(background_appearance) + .context("Updating window transparency") + .log_err(); - // match background_appearance { - // WindowBackgroundAppearance::Opaque => { - // // ACCENT_DISABLED - // set_window_composition_attribute(window_state.hwnd, None, 0); - // } - // WindowBackgroundAppearance::Transparent => { - // // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background - // set_window_composition_attribute(window_state.hwnd, None, 2); - // } - // WindowBackgroundAppearance::Blurred => { - // // Enable acrylic blur - // // ACCENT_ENABLE_ACRYLICBLURBEHIND - // set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); - // } - // } + match background_appearance { + WindowBackgroundAppearance::Opaque => { + // ACCENT_DISABLED + set_window_composition_attribute(window_state.hwnd, None, 0); + } + WindowBackgroundAppearance::Transparent => { + // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background + set_window_composition_attribute(window_state.hwnd, None, 2); + } + WindowBackgroundAppearance::Blurred => { + // Enable acrylic blur + // ACCENT_ENABLE_ACRYLICBLURBEHIND + set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4); + } + } } fn minimize(&self) { From 642d769502b32541b520bfc785e4a42f18b7af5b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 19:56:56 +0800 Subject: [PATCH 111/202] update default buffer size --- crates/gpui/src/platform/windows/directx_renderer.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 428464876a..27d00eeb63 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -431,31 +431,31 @@ impl DirectXRenderPipelines { "shadow_pipeline", "shadow_vertex", "shadow_fragment", - 32, + 4, )?; let quad_pipeline = - PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 32)?; + 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", - 32, + 4, )?; let mono_sprites = PipelineState::new( device, "monochrome_sprite_pipeline", "monochrome_sprite_vertex", "monochrome_sprite_fragment", - 32, + 512, )?; let poly_sprites = PipelineState::new( device, "polychrome_sprite_pipeline", "polychrome_sprite_vertex", "polychrome_sprite_fragment", - 32, + 16, )?; Ok(Self { From 22cba07072b45fad648dc62ba0899c8e313129b5 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 21:26:11 +0800 Subject: [PATCH 112/202] add msaa --- .../src/platform/windows/directx_renderer.rs | 66 ++++++++++++++++++- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 27d00eeb63..410da3162d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -18,6 +18,9 @@ use windows::{ use crate::*; +const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + pub(crate) struct DirectXRenderer { atlas: Arc, devices: DirectXDevices, @@ -39,6 +42,8 @@ pub(crate) struct DirectXDevices { struct DirectXContext { swap_chain: ManuallyDrop, back_buffer: [Option; 1], + msaa_target: ID3D11Texture2D, + msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] // direct_composition: DirectComposition, @@ -164,7 +169,7 @@ impl DirectXRenderer { BUFFER_COUNT as u32, new_size.width.0 as u32, new_size.height.0 as u32, - DXGI_FORMAT_B8G8R8A8_UNORM, + RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), )?; } @@ -174,6 +179,15 @@ impl DirectXRenderer { &self.devices.device_context, )?; self.context.back_buffer[0] = Some(backbuffer); + + let (msaa_target, msaa_view) = create_msaa_target_and_its_view( + &self.devices.device, + new_size.width.0 as u32, + new_size.height.0 as u32, + )?; + self.context.msaa_target = msaa_target; + self.context.msaa_view = msaa_view; + self.context.viewport = set_viewport( &self.devices.device_context, new_size.width.0 as f32, @@ -411,12 +425,15 @@ impl DirectXContext { &devices.device, &devices.device_context, )?)]; + let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; let viewport = set_viewport(&devices.device_context, 1.0, 1.0); set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { swap_chain, back_buffer, + msaa_target, + msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] // direct_composition, @@ -984,7 +1001,7 @@ fn create_swap_chain_default( let desc = DXGI_SWAP_CHAIN_DESC1 { Width: 1, Height: 1, - Format: DXGI_FORMAT_B8G8R8A8_UNORM, + Format: RENDER_TARGET_FORMAT, Stereo: false.into(), SampleDesc: DXGI_SAMPLE_DESC { Count: 1, @@ -1013,14 +1030,57 @@ fn set_render_target_view( // 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 desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, + ..Default::default() + }; let mut buffer: Option = None; - device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?; + device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?; buffer.unwrap() }; unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; Ok(back_buffer) } +fn create_msaa_target_and_its_view( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> { + let msaa_target = unsafe { + let mut output = None; + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: BACK_BUFFER_FORMAT, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 4, + 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() + }; + let msaa_view = unsafe { + let desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS, + ..Default::default() + }; + let mut output = None; + device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?; + output.unwrap() + }; + Ok((msaa_target, msaa_view)) +} + #[inline] fn set_viewport( device_context: &ID3D11DeviceContext, From 0d8600bf1ee537bc9534b3380cb76ecfa8719e97 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 22:20:38 +0800 Subject: [PATCH 113/202] checkpoint msaa --- .../src/platform/windows/directx_renderer.rs | 161 ++++++++++++++---- 1 file changed, 129 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 410da3162d..74c1c68286 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -18,7 +18,7 @@ use windows::{ use crate::*; -const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; pub(crate) struct DirectXRenderer { @@ -41,7 +41,8 @@ pub(crate) struct DirectXDevices { struct DirectXContext { swap_chain: ManuallyDrop, - back_buffer: [Option; 1], + render_target: ManuallyDrop, + render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], @@ -124,15 +125,48 @@ 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.context.viewport[0].Width, + self.context.viewport[0].Height, + ], + ..Default::default() + }], )?; + unsafe { + self.devices + .device_context + .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]); + self.devices + .device_context + .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None); + self.devices + .device_context + .RSSetViewports(Some(&self.context.viewport)); + self.devices.device_context.OMSetBlendState( + &self.globals.blend_state, + None, + 0xFFFFFFFF, + ); + } + Ok(()) + } + + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { + // pre_draw( + // &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, + // )?; + println!("Pre-draw: {:?}", self.context.render_target_view); + self.pre_draw()?; for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), @@ -157,28 +191,56 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?; + unsafe { + self.devices.device_context.ResolveSubresource( + &*self.context.render_target, + 0, + &self.context.msaa_target, + 0, + BACK_BUFFER_FORMAT, + ); + self.devices + .device_context + .OMSetRenderTargets(Some(&self.context.render_target_view), None); + self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + } Ok(()) } 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()); + println!("Resize: {:?}", self.context.render_target_view); unsafe { - self.context.swap_chain.ResizeBuffers( - BUFFER_COUNT as u32, - new_size.width.0 as u32, - new_size.height.0 as u32, - RENDER_TARGET_FORMAT, - DXGI_SWAP_CHAIN_FLAG(0), - )?; + self.devices.device_context.OMSetRenderTargets(None, None); + ManuallyDrop::drop(&mut self.context.render_target); + } + drop(self.context.render_target_view[0].take().unwrap()); + unsafe { + self.context + .swap_chain + .ResizeBuffers( + BUFFER_COUNT as u32, + new_size.width.0 as u32, + new_size.height.0 as u32, + RENDER_TARGET_FORMAT, + DXGI_SWAP_CHAIN_FLAG(0), + ) + .unwrap(); + } + // let backbuffer = set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?; + let (render_target, render_target_view) = + create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + .unwrap(); + self.context.render_target = render_target; + self.context.render_target_view = render_target_view; + unsafe { + self.devices + .device_context + .OMSetRenderTargets(Some(&self.context.render_target_view), None); } - let backbuffer = set_render_target_view( - &self.context.swap_chain, - &self.devices.device, - &self.devices.device_context, - )?; - self.context.back_buffer[0] = Some(backbuffer); let (msaa_target, msaa_view) = create_msaa_target_and_its_view( &self.devices.device, @@ -420,18 +482,26 @@ impl DirectXContext { // 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 (render_target, render_target_view) = + create_render_target_and_its_view(&swap_chain, &devices.device)?; + // let back_buffer = [Some(set_render_target_view( + // &swap_chain, + // &devices.device, + // &devices.device_context, + // )?)]; let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; let viewport = set_viewport(&devices.device_context, 1.0, 1.0); + unsafe { + devices + .device_context + .OMSetRenderTargets(Some(&render_target_view), None); + } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { swap_chain, - back_buffer, + render_target, + render_target_view, msaa_target, msaa_view, viewport, @@ -893,6 +963,7 @@ struct PathSprite { impl Drop for DirectXContext { fn drop(&mut self) { unsafe { + ManuallyDrop::drop(&mut self.render_target); ManuallyDrop::drop(&mut self.swap_chain); } } @@ -1020,6 +1091,30 @@ fn create_swap_chain_default( Ok(ManuallyDrop::new(swap_chain)) } +#[inline] +fn create_render_target_and_its_view( + swap_chain: &IDXGISwapChain1, + device: &ID3D11Device, +) -> Result<( + ManuallyDrop, + [Option; 1], +)> { + let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; + let desc = D3D11_RENDER_TARGET_VIEW_DESC { + Format: BACK_BUFFER_FORMAT, + ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, + ..Default::default() + }; + let mut render_target_view = None; + unsafe { + device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))? + }; + Ok(( + ManuallyDrop::new(render_target), + [Some(render_target_view.unwrap())], + )) +} + #[inline] fn set_render_target_view( swap_chain: &IDXGISwapChain1, @@ -1043,6 +1138,7 @@ fn set_render_target_view( Ok(back_buffer) } +#[inline] fn create_msaa_target_and_its_view( device: &ID3D11Device, width: u32, @@ -1111,7 +1207,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 { From 32758022df9f929d1fae4299f268d4e1b567f263 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 22:58:51 +0800 Subject: [PATCH 114/202] wip --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- crates/gpui/src/platform/windows/shaders.hlsl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 74c1c68286..47f6f3945d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -19,7 +19,8 @@ use windows::{ use crate::*; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; -const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; +// const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; +const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; pub(crate) struct DirectXRenderer { atlas: Arc, @@ -1200,7 +1201,6 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont let desc = D3D11_RASTERIZER_DESC { FillMode: D3D11_FILL_SOLID, CullMode: D3D11_CULL_NONE, - // FrontCounterClockwise: true.into(), FrontCounterClockwise: false.into(), DepthBias: 0, DepthBiasClamp: 0.0, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 7e59bb714e..a67e2cde60 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -342,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 From 22c9d133bd32748a71166865a26c3b26c971428a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 16 Jul 2025 23:55:32 +0800 Subject: [PATCH 115/202] wip --- .../src/platform/windows/directx_renderer.rs | 31 ++++++++++++++----- crates/gpui/src/platform/windows/shaders.hlsl | 11 ++++--- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 47f6f3945d..53b22a05e3 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -371,11 +371,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 { @@ -796,7 +795,7 @@ impl PathsPipelineState { let view = create_buffer_view(device, &buffer)?; let vertex_buffer = Some(create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), 32, )?); let indirect_draw_buffer = create_indirect_draw_buffer(device, 32)?; @@ -836,6 +835,15 @@ impl PathsPipelineState { 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), @@ -862,7 +870,7 @@ impl PathsPipelineState { device: &ID3D11Device, device_context: &ID3D11DeviceContext, buffer_data: &[PathSprite], - vertices_data: &[PathVertex], + vertices_data: &[DirectXPathVertex], draw_commands: &[DrawInstancedIndirectArgs], ) -> Result<()> { if self.buffer_size < buffer_data.len() { @@ -888,7 +896,7 @@ impl PathsPipelineState { ); let vertex_buffer = create_buffer( device, - std::mem::size_of::>(), + std::mem::size_of::(), new_vertex_buffer_size, )?; self.vertex_buffer = Some(vertex_buffer); @@ -932,7 +940,7 @@ impl PathsPipelineState { global_params, ); unsafe { - const STRIDE: u32 = std::mem::size_of::>() as u32; + const STRIDE: u32 = std::mem::size_of::() as u32; device_context.IASetVertexBuffers( 0, 1, @@ -954,6 +962,13 @@ impl PathsPipelineState { } } +#[repr(C)] +struct DirectXPathVertex { + xy_position: Point, + content_mask: Bounds, + sprite_index: u32, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index a67e2cde60..f0c773a673 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -879,6 +879,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct PathVertex { float2 xy_position: POSITION; Bounds content_mask: TEXCOORD; + uint idx: GLOBALIDX; }; struct PathSprite { @@ -905,13 +906,13 @@ struct PathFragmentInput { StructuredBuffer path_sprites: register(t1); -PathVertexOutput paths_vertex(PathVertex v, uint instance_id: SV_InstanceID) { - 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, From c007121b412aef8732ac8598466c08f0f4015c8b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 00:05:46 +0800 Subject: [PATCH 116/202] remove unused --- .../src/platform/windows/directx_renderer.rs | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 53b22a05e3..99126186c7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -19,8 +19,6 @@ use windows::{ use crate::*; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; -// const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; -const BACK_BUFFER_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; pub(crate) struct DirectXRenderer { atlas: Arc, @@ -158,15 +156,6 @@ impl DirectXRenderer { } pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { - // pre_draw( - // &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, - // )?; - println!("Pre-draw: {:?}", self.context.render_target_view); self.pre_draw()?; for batch in scene.batches() { match batch { @@ -198,7 +187,7 @@ impl DirectXRenderer { 0, &self.context.msaa_target, 0, - BACK_BUFFER_FORMAT, + RENDER_TARGET_FORMAT, ); self.devices .device_context @@ -209,7 +198,6 @@ impl DirectXRenderer { } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { - println!("Resize: {:?}", self.context.render_target_view); unsafe { self.devices.device_context.OMSetRenderTargets(None, None); ManuallyDrop::drop(&mut self.context.render_target); @@ -1116,15 +1104,8 @@ fn create_render_target_and_its_view( [Option; 1], )> { let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?; - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - ..Default::default() - }; let mut render_target_view = None; - unsafe { - device.CreateRenderTargetView(&render_target, Some(&desc), Some(&mut render_target_view))? - }; + unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? }; Ok(( ManuallyDrop::new(render_target), [Some(render_target_view.unwrap())], @@ -1141,13 +1122,8 @@ fn set_render_target_view( // 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 desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D, - ..Default::default() - }; let mut buffer: Option = None; - device.CreateRenderTargetView(&resource, Some(&desc), Some(&mut buffer))?; + device.CreateRenderTargetView(&resource, None, Some(&mut buffer))?; buffer.unwrap() }; unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; @@ -1167,7 +1143,7 @@ fn create_msaa_target_and_its_view( Height: height, MipLevels: 1, ArraySize: 1, - Format: BACK_BUFFER_FORMAT, + Format: RENDER_TARGET_FORMAT, SampleDesc: DXGI_SAMPLE_DESC { Count: 4, Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, @@ -1181,13 +1157,8 @@ fn create_msaa_target_and_its_view( output.unwrap() }; let msaa_view = unsafe { - let desc = D3D11_RENDER_TARGET_VIEW_DESC { - Format: BACK_BUFFER_FORMAT, - ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2DMS, - ..Default::default() - }; let mut output = None; - device.CreateRenderTargetView(&msaa_target, Some(&desc), Some(&mut output))?; + device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; output.unwrap() }; Ok((msaa_target, msaa_view)) From 7ab2d0d80051ef9d2f8a370a899fb438be9416b6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 11:29:44 +0800 Subject: [PATCH 117/202] add transparency --- Cargo.toml | 1 + .../src/platform/windows/directx_renderer.rs | 314 +++++++++--------- crates/gpui/src/platform/windows/window.rs | 5 +- 3 files changed, 166 insertions(+), 154 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e090721e1a..1957f8b4cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -677,6 +677,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/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 99126186c7..ed39f3bedf 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -3,7 +3,8 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; // #[cfg(not(feature = "enable-renderdoc"))] -// use windows::Win32::Graphics::DirectComposition::*; +use windows::Win32::Graphics::DirectComposition::*; +use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; use windows::{ Win32::{ Foundation::{HMODULE, HWND}, @@ -26,7 +27,6 @@ pub(crate) struct DirectXRenderer { context: DirectXContext, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - hwnd: HWND, transparent: bool, } @@ -46,7 +46,7 @@ struct DirectXContext { msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition: DirectComposition, + direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -73,11 +73,11 @@ struct DrawInstancedIndirectArgs { } // #[cfg(not(feature = "enable-renderdoc"))] -// struct DirectComposition { -// comp_device: IDCompositionDevice, -// comp_target: IDCompositionTarget, -// comp_visual: IDCompositionVisual, -// } +struct DirectComposition { + comp_device: IDCompositionDevice, + comp_target: IDCompositionTarget, + comp_visual: IDCompositionVisual, +} impl DirectXDevices { pub(crate) fn new() -> Result { @@ -115,7 +115,6 @@ impl DirectXRenderer { context, globals, pipelines, - hwnd, transparent, }) } @@ -215,11 +214,6 @@ impl DirectXRenderer { ) .unwrap(); } - // let backbuffer = set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?; let (render_target, render_target_view) = create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) .unwrap(); @@ -248,31 +242,6 @@ impl DirectXRenderer { } // #[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, @@ -281,33 +250,84 @@ impl DirectXRenderer { if self.transparent == transparent { return Ok(()); } - self.transparent = transparent; - // unsafe { - // // recreate the swapchain + // self.transparent = transparent; + // let (width, height) = unsafe { // self.devices.device_context.OMSetRenderTargets(None, None); - // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.render_target); + // drop(self.context.render_target_view[0].take().unwrap()); + // let desc = self.context.swap_chain.GetDesc1().unwrap(); // ManuallyDrop::drop(&mut self.context.swap_chain); - // self.context.swap_chain = create_swap_chain_default( - // &self.devices.dxgi_factory, - // &self.devices.device, - // self.hwnd, - // transparent, - // )?; - // self.context.back_buffer = [Some(set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?)]; - // self.context.viewport = set_viewport( - // &self.devices.device_context, - // self.context.viewport[0].Width, - // self.context.viewport[0].Height, - // ); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // (desc.Width, desc.Height) + // }; + // self.context.swap_chain = create_swap_chain( + // &self.devices.dxgi_factory, + // &self.devices.device, + // transparent, + // width, + // height, + // ) + // .unwrap(); + // self.context + // .direct_composition + // .set_swap_chain(&self.context.swap_chain) + // .context("Failed to set swap chain for DirectComposition")?; + // let (render_target, render_target_view) = + // create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + // .unwrap(); + // self.context.render_target = render_target; + // self.context.render_target_view = render_target_view; + // unsafe { + // self.devices + // .device_context + // .OMSetRenderTargets(Some(&self.context.render_target_view), None); // } + + // let (msaa_target, msaa_view) = + // create_msaa_target_and_its_view(&self.devices.device, width, height)?; + // self.context.msaa_target = msaa_target; + // self.context.msaa_view = msaa_view; + + // self.context.viewport = set_viewport(&self.devices.device_context, width as _, height as _); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; Ok(()) } + // #[cfg(feature = "enable-renderdoc")] + // pub(crate) fn update_transparency( + // &mut self, + // background_appearance: WindowBackgroundAppearance, + // ) -> Result<()> { + // let transparent = background_appearance != WindowBackgroundAppearance::Opaque; + // if self.transparent == transparent { + // return Ok(()); + // } + // self.transparent = transparent; + // unsafe { + // // recreate the swapchain + // self.devices.device_context.OMSetRenderTargets(None, None); + // drop(self.context.back_buffer[0].take().unwrap()); + // ManuallyDrop::drop(&mut self.context.swap_chain); + // self.context.swap_chain = create_swap_chain_default( + // &self.devices.dxgi_factory, + // &self.devices.device, + // self.hwnd, + // transparent, + // )?; + // self.context.back_buffer = [Some(set_render_target_view( + // &self.context.swap_chain, + // &self.devices.device, + // &self.devices.device_context, + // )?)]; + // self.context.viewport = set_viewport( + // &self.devices.device_context, + // self.context.viewport[0].Width, + // self.context.viewport[0].Height, + // ); + // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; + // } + // Ok(()) + // } + fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { if shadows.is_empty() { return Ok(()); @@ -461,24 +481,35 @@ impl DirectXRenderer { impl DirectXContext { pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + let (width, height) = unsafe { + let mut rect = std::mem::zeroed(); + GetWindowRect(hwnd, &mut rect)?; + (rect.right - rect.left, rect.bottom - rect.top) + }; + assert!( + width > 0 && height > 0, + "Window size must be greater than zero" + ); // #[cfg(not(feature = "enable-renderdoc"))] - // let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?; + let swap_chain = create_swap_chain( + &devices.dxgi_factory, + &devices.device, + transparent, + width as u32, + height as u32, + )?; // #[cfg(feature = "enable-renderdoc")] - let swap_chain = - create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; + // 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)?; + let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition.set_swap_chain(&swap_chain)?; + direct_composition.set_swap_chain(&swap_chain)?; let (render_target, render_target_view) = create_render_target_and_its_view(&swap_chain, &devices.device)?; - // let back_buffer = [Some(set_render_target_view( - // &swap_chain, - // &devices.device, - // &devices.device_context, - // )?)]; - let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?; - let viewport = set_viewport(&devices.device_context, 1.0, 1.0); + let (msaa_target, msaa_view) = + create_msaa_target_and_its_view(&devices.device, width as u32, height as u32)?; + let viewport = set_viewport(&devices.device_context, width as f32, height as f32); unsafe { devices .device_context @@ -494,7 +525,7 @@ impl DirectXContext { msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] - // direct_composition, + direct_composition, }) } } @@ -545,28 +576,28 @@ 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() }?; +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, -// }) -// } + 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(()) -// } -// } + 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 { @@ -1030,39 +1061,45 @@ fn get_device( } // #[cfg(not(feature = "enable-renderdoc"))] -// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { -// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) -// } +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)? }) -// } +fn create_swap_chain( + dxgi_factory: &IDXGIFactory6, + device: &ID3D11Device, + transparent: bool, + width: u32, + height: u32, +) -> Result> { + println!("Creating swap chain for DirectComposition: {}", transparent); + let transparent = true; + let alpha_mode = if transparent { + DXGI_ALPHA_MODE_PREMULTIPLIED + } else { + DXGI_ALPHA_MODE_IGNORE + }; + 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: alpha_mode, + Flags: 0, + }; + Ok(ManuallyDrop::new(unsafe { + dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? + })) +} // #[cfg(feature = "enable-renderdoc")] fn create_swap_chain_default( @@ -1289,33 +1326,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res Ok(buffer.unwrap()) } -#[inline] -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<()> { - let global_params = global_params_buffer[0].as_ref().unwrap(); - update_buffer( - device_context, - global_params, - &[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(()) -} - #[inline] fn update_buffer( device_context: &ID3D11DeviceContext, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 74001c84e9..077ba82e92 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -384,7 +384,8 @@ impl WindowsWindow { (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) } else { ( - WS_EX_APPWINDOW | WS_EX_LAYERED, + WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP, + // WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -461,7 +462,7 @@ impl WindowsWindow { // 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)? }; + // unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; Ok(Self(state_ptr)) } From c6e020f60f9b2d52b0bba3b9c991e87dee85c0be Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 14:36:41 +0800 Subject: [PATCH 118/202] finetune transpanrency --- .../src/platform/windows/directx_renderer.rs | 128 +----------------- crates/gpui/src/platform/windows/window.rs | 33 ++--- 2 files changed, 15 insertions(+), 146 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ed39f3bedf..ebd4c4730b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -27,7 +27,6 @@ pub(crate) struct DirectXRenderer { context: DirectXContext, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - transparent: bool, } #[derive(Clone)] @@ -46,7 +45,7 @@ struct DirectXContext { msaa_view: ID3D11RenderTargetView, viewport: [D3D11_VIEWPORT; 1], // #[cfg(not(feature = "enable-renderdoc"))] - direct_composition: DirectComposition, + _direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -101,12 +100,12 @@ 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)?; + let context = DirectXContext::new(devices, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; Ok(DirectXRenderer { @@ -115,7 +114,6 @@ impl DirectXRenderer { context, globals, pipelines, - transparent, }) } @@ -241,93 +239,6 @@ impl DirectXRenderer { Ok(()) } - // #[cfg(not(feature = "enable-renderdoc"))] - pub(crate) fn update_transparency( - &mut self, - background_appearance: WindowBackgroundAppearance, - ) -> Result<()> { - let transparent = background_appearance != WindowBackgroundAppearance::Opaque; - if self.transparent == transparent { - return Ok(()); - } - // self.transparent = transparent; - // let (width, height) = unsafe { - // self.devices.device_context.OMSetRenderTargets(None, None); - // ManuallyDrop::drop(&mut self.context.render_target); - // drop(self.context.render_target_view[0].take().unwrap()); - // let desc = self.context.swap_chain.GetDesc1().unwrap(); - // ManuallyDrop::drop(&mut self.context.swap_chain); - // (desc.Width, desc.Height) - // }; - // self.context.swap_chain = create_swap_chain( - // &self.devices.dxgi_factory, - // &self.devices.device, - // transparent, - // width, - // height, - // ) - // .unwrap(); - // self.context - // .direct_composition - // .set_swap_chain(&self.context.swap_chain) - // .context("Failed to set swap chain for DirectComposition")?; - // let (render_target, render_target_view) = - // create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) - // .unwrap(); - // self.context.render_target = render_target; - // self.context.render_target_view = render_target_view; - // unsafe { - // self.devices - // .device_context - // .OMSetRenderTargets(Some(&self.context.render_target_view), None); - // } - - // let (msaa_target, msaa_view) = - // create_msaa_target_and_its_view(&self.devices.device, width, height)?; - // self.context.msaa_target = msaa_target; - // self.context.msaa_view = msaa_view; - - // self.context.viewport = set_viewport(&self.devices.device_context, width as _, height as _); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; - Ok(()) - } - - // #[cfg(feature = "enable-renderdoc")] - // pub(crate) fn update_transparency( - // &mut self, - // background_appearance: WindowBackgroundAppearance, - // ) -> Result<()> { - // let transparent = background_appearance != WindowBackgroundAppearance::Opaque; - // if self.transparent == transparent { - // return Ok(()); - // } - // self.transparent = transparent; - // unsafe { - // // recreate the swapchain - // self.devices.device_context.OMSetRenderTargets(None, None); - // drop(self.context.back_buffer[0].take().unwrap()); - // ManuallyDrop::drop(&mut self.context.swap_chain); - // self.context.swap_chain = create_swap_chain_default( - // &self.devices.dxgi_factory, - // &self.devices.device, - // self.hwnd, - // transparent, - // )?; - // self.context.back_buffer = [Some(set_render_target_view( - // &self.context.swap_chain, - // &self.devices.device, - // &self.devices.device_context, - // )?)]; - // self.context.viewport = set_viewport( - // &self.devices.device_context, - // self.context.viewport[0].Width, - // self.context.viewport[0].Height, - // ); - // set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; - // } - // Ok(()) - // } - fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { if shadows.is_empty() { return Ok(()); @@ -480,7 +391,7 @@ impl DirectXRenderer { } impl DirectXContext { - pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result { + pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { let (width, height) = unsafe { let mut rect = std::mem::zeroed(); GetWindowRect(hwnd, &mut rect)?; @@ -494,7 +405,6 @@ impl DirectXContext { let swap_chain = create_swap_chain( &devices.dxgi_factory, &devices.device, - transparent, width as u32, height as u32, )?; @@ -525,7 +435,7 @@ impl DirectXContext { msaa_view, viewport, // #[cfg(not(feature = "enable-renderdoc"))] - direct_composition, + _direct_composition: direct_composition, }) } } @@ -1068,17 +978,9 @@ fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, - transparent: bool, width: u32, height: u32, ) -> Result> { - println!("Creating swap chain for DirectComposition: {}", transparent); - let transparent = true; - let alpha_mode = if transparent { - DXGI_ALPHA_MODE_PREMULTIPLIED - } else { - DXGI_ALPHA_MODE_IGNORE - }; let desc = DXGI_SWAP_CHAIN_DESC1 { Width: width, Height: height, @@ -1093,7 +995,7 @@ fn create_swap_chain( // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. Scaling: DXGI_SCALING_STRETCH, SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, - AlphaMode: alpha_mode, + AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, Flags: 0, }; Ok(ManuallyDrop::new(unsafe { @@ -1149,24 +1051,6 @@ fn create_render_target_and_its_view( )) } -#[inline] -fn set_render_target_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() - }; - unsafe { device_context.OMSetRenderTargets(Some(&[Some(back_buffer.clone())]), None) }; - Ok(back_buffer) -} - #[inline] fn create_msaa_target_and_its_view( device: &ID3D11Device, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 077ba82e92..e712d939c8 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -80,7 +80,6 @@ pub(crate) struct WindowsWindowStatePtr { impl WindowsWindowState { fn new( hwnd: HWND, - transparent: bool, cs: &CREATESTRUCTW, current_cursor: Option, display: WindowsDisplay, @@ -104,7 +103,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 +206,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 +333,6 @@ struct WindowCreateContext<'a> { handle: AnyWindowHandle, hide_title_bar: bool, display: WindowsDisplay, - transparent: bool, is_movable: bool, min_size: Option>, executor: ForegroundExecutor, @@ -381,11 +378,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_NOREDIRECTIONBITMAP, - // WS_EX_APPWINDOW | WS_EX_LAYERED, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; @@ -403,7 +402,6 @@ impl WindowsWindow { handle, hide_title_bar, display, - transparent: false, is_movable: params.is_movable, min_size: params.window_min_size, executor, @@ -455,14 +453,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)) } @@ -707,26 +697,21 @@ 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) - .context("Updating window transparency") - .log_err(); + 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); } } } From 499b3b6b50251d54a9a1af540ec225b5d01dff64 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 14:53:28 +0800 Subject: [PATCH 119/202] rename to `DirectXResources` --- .../src/platform/windows/directx_renderer.rs | 95 ++++++++++--------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ebd4c4730b..bc74ad7782 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -20,15 +20,18 @@ use windows::{ 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, } +/// Direct3D objects #[derive(Clone)] pub(crate) struct DirectXDevices { dxgi_factory: IDXGIFactory6, @@ -37,13 +40,17 @@ pub(crate) struct DirectXDevices { device_context: ID3D11DeviceContext, } -struct DirectXContext { +struct DirectXResources { + // Direct3D rendering objects swap_chain: ManuallyDrop, render_target: ManuallyDrop, render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, msaa_view: ID3D11RenderTargetView, + + // Cached viewport viewport: [D3D11_VIEWPORT; 1], + // #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: DirectComposition, } @@ -105,13 +112,13 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); - let context = DirectXContext::new(devices, hwnd)?; + let resources = DirectXResources::new(devices, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; Ok(DirectXRenderer { atlas, devices: devices.clone(), - context, + resources, globals, pipelines, }) @@ -127,8 +134,8 @@ impl DirectXRenderer { self.globals.global_params_buffer[0].as_ref().unwrap(), &[GlobalParams { viewport_size: [ - self.context.viewport[0].Width, - self.context.viewport[0].Height, + self.resources.viewport[0].Width, + self.resources.viewport[0].Height, ], ..Default::default() }], @@ -136,13 +143,13 @@ impl DirectXRenderer { unsafe { self.devices .device_context - .ClearRenderTargetView(&self.context.msaa_view, &[0.0; 4]); + .ClearRenderTargetView(&self.resources.msaa_view, &[0.0; 4]); self.devices .device_context - .OMSetRenderTargets(Some(&[Some(self.context.msaa_view.clone())]), None); + .OMSetRenderTargets(Some(&[Some(self.resources.msaa_view.clone())]), None); self.devices .device_context - .RSSetViewports(Some(&self.context.viewport)); + .RSSetViewports(Some(&self.resources.viewport)); self.devices.device_context.OMSetBlendState( &self.globals.blend_state, None, @@ -180,16 +187,16 @@ impl DirectXRenderer { } unsafe { self.devices.device_context.ResolveSubresource( - &*self.context.render_target, + &*self.resources.render_target, 0, - &self.context.msaa_target, + &self.resources.msaa_target, 0, RENDER_TARGET_FORMAT, ); self.devices .device_context - .OMSetRenderTargets(Some(&self.context.render_target_view), None); - self.context.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); + self.resources.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; } Ok(()) } @@ -197,11 +204,11 @@ impl DirectXRenderer { pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { unsafe { self.devices.device_context.OMSetRenderTargets(None, None); - ManuallyDrop::drop(&mut self.context.render_target); + ManuallyDrop::drop(&mut self.resources.render_target); } - drop(self.context.render_target_view[0].take().unwrap()); + drop(self.resources.render_target_view[0].take().unwrap()); unsafe { - self.context + self.resources .swap_chain .ResizeBuffers( BUFFER_COUNT as u32, @@ -213,14 +220,14 @@ impl DirectXRenderer { .unwrap(); } let (render_target, render_target_view) = - create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) + create_render_target_and_its_view(&self.resources.swap_chain, &self.devices.device) .unwrap(); - self.context.render_target = render_target; - self.context.render_target_view = render_target_view; + self.resources.render_target = render_target; + self.resources.render_target_view = render_target_view; unsafe { self.devices .device_context - .OMSetRenderTargets(Some(&self.context.render_target_view), None); + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); } let (msaa_target, msaa_view) = create_msaa_target_and_its_view( @@ -228,10 +235,10 @@ impl DirectXRenderer { new_size.width.0 as u32, new_size.height.0 as u32, )?; - self.context.msaa_target = msaa_target; - self.context.msaa_view = msaa_view; + self.resources.msaa_target = msaa_target; + self.resources.msaa_view = msaa_view; - self.context.viewport = set_viewport( + self.resources.viewport = set_viewport( &self.devices.device_context, new_size.width.0 as f32, new_size.height.0 as f32, @@ -250,7 +257,7 @@ impl DirectXRenderer { )?; self.pipelines.shadow_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, shadows.len() as u32, ) @@ -267,7 +274,7 @@ impl DirectXRenderer { )?; self.pipelines.quad_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, quads.len() as u32, ) @@ -312,7 +319,7 @@ impl DirectXRenderer { self.pipelines.paths_pipeline.draw( &self.devices.device_context, paths.len(), - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, ) } @@ -328,7 +335,7 @@ impl DirectXRenderer { )?; self.pipelines.underline_pipeline.draw( &self.devices.device_context, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, underlines.len() as u32, ) @@ -351,7 +358,7 @@ impl DirectXRenderer { self.pipelines.mono_sprites.draw_with_texture( &self.devices.device_context, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -375,7 +382,7 @@ impl DirectXRenderer { self.pipelines.poly_sprites.draw_with_texture( &self.devices.device_context, &texture_view, - &self.context.viewport, + &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, sprites.len() as u32, @@ -390,24 +397,17 @@ impl DirectXRenderer { } } -impl DirectXContext { +impl DirectXResources { pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { let (width, height) = unsafe { let mut rect = std::mem::zeroed(); GetWindowRect(hwnd, &mut rect)?; - (rect.right - rect.left, rect.bottom - rect.top) + let width = (rect.right - rect.left).max(1) as u32; + let height = (rect.bottom - rect.top).max(1) as u32; + (width, height) }; - assert!( - width > 0 && height > 0, - "Window size must be greater than zero" - ); // #[cfg(not(feature = "enable-renderdoc"))] - let swap_chain = create_swap_chain( - &devices.dxgi_factory, - &devices.device, - width as u32, - height as u32, - )?; + 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)?; @@ -418,7 +418,7 @@ impl DirectXContext { 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 as u32, height as u32)?; + create_msaa_target_and_its_view(&devices.device, width, height)?; let viewport = set_viewport(&devices.device_context, width as f32, height as f32); unsafe { devices @@ -905,7 +905,7 @@ struct PathSprite { color: Background, } -impl Drop for DirectXContext { +impl Drop for DirectXResources { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.render_target); @@ -1008,13 +1008,14 @@ fn create_swap_chain_default( 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, + Width: width, + Height: height, Format: RENDER_TARGET_FORMAT, Stereo: false.into(), SampleDesc: DXGI_SAMPLE_DESC { @@ -1066,7 +1067,7 @@ fn create_msaa_target_and_its_view( ArraySize: 1, Format: RENDER_TARGET_FORMAT, SampleDesc: DXGI_SAMPLE_DESC { - Count: 4, + Count: MULTISAMPLE_COUNT, Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, }, Usage: D3D11_USAGE_DEFAULT, From 8c1d9f75d10b32c64696979eee2f10df489331d7 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 15:52:51 +0800 Subject: [PATCH 120/202] refactor --- .../src/platform/windows/directx_renderer.rs | 178 ++++++++++-------- 1 file changed, 99 insertions(+), 79 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index bc74ad7782..87c98512b9 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -29,6 +29,8 @@ pub(crate) struct DirectXRenderer { resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, + // #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: DirectComposition, } /// Direct3D objects @@ -42,17 +44,14 @@ pub(crate) struct DirectXDevices { struct DirectXResources { // Direct3D rendering objects - swap_chain: ManuallyDrop, + swap_chain: IDXGISwapChain1, render_target: ManuallyDrop, render_target_view: [Option; 1], msaa_target: ID3D11Texture2D, - msaa_view: ID3D11RenderTargetView, + msaa_view: [Option; 1], // Cached viewport viewport: [D3D11_VIEWPORT; 1], - - // #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: DirectComposition, } struct DirectXRenderPipelines { @@ -115,12 +114,18 @@ impl DirectXRenderer { 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(), resources, globals, pipelines, + // #[cfg(not(feature = "enable-renderdoc"))] + _direct_composition: direct_composition, }) } @@ -143,10 +148,10 @@ impl DirectXRenderer { unsafe { self.devices .device_context - .ClearRenderTargetView(&self.resources.msaa_view, &[0.0; 4]); + .ClearRenderTargetView(self.resources.msaa_view[0].as_ref().unwrap(), &[0.0; 4]); self.devices .device_context - .OMSetRenderTargets(Some(&[Some(self.resources.msaa_view.clone())]), None); + .OMSetRenderTargets(Some(&self.resources.msaa_view), None); self.devices .device_context .RSSetViewports(Some(&self.resources.viewport)); @@ -159,6 +164,23 @@ impl DirectXRenderer { 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() { @@ -185,64 +207,29 @@ impl DirectXRenderer { scene.polychrome_sprites.len(), scene.surfaces.len(),))?; } - 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(()) + self.present() } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { unsafe { + let width = new_size.width.0 as u32; + let height = new_size.height.0 as u32; self.devices.device_context.OMSetRenderTargets(None, None); ManuallyDrop::drop(&mut self.resources.render_target); - } - drop(self.resources.render_target_view[0].take().unwrap()); - unsafe { + drop(self.resources.render_target_view[0].take().unwrap()); + self.resources.swap_chain.ResizeBuffers( + BUFFER_COUNT as u32, + width, + height, + RENDER_TARGET_FORMAT, + DXGI_SWAP_CHAIN_FLAG(0), + )?; self.resources - .swap_chain - .ResizeBuffers( - BUFFER_COUNT as u32, - new_size.width.0 as u32, - new_size.height.0 as u32, - RENDER_TARGET_FORMAT, - DXGI_SWAP_CHAIN_FLAG(0), - ) - .unwrap(); - } - let (render_target, render_target_view) = - create_render_target_and_its_view(&self.resources.swap_chain, &self.devices.device) - .unwrap(); - self.resources.render_target = render_target; - self.resources.render_target_view = render_target_view; - unsafe { + .recreate_resources(&self.devices, width, height)?; self.devices .device_context .OMSetRenderTargets(Some(&self.resources.render_target_view), None); } - - let (msaa_target, msaa_view) = create_msaa_target_and_its_view( - &self.devices.device, - new_size.width.0 as u32, - new_size.height.0 as u32, - )?; - self.resources.msaa_target = msaa_target; - self.resources.msaa_view = msaa_view; - - self.resources.viewport = set_viewport( - &self.devices.device_context, - new_size.width.0 as f32, - new_size.height.0 as f32, - ); Ok(()) } @@ -411,20 +398,13 @@ impl DirectXResources { // #[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 (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); - unsafe { - devices - .device_context - .OMSetRenderTargets(Some(&render_target_view), None); - } + let (render_target, render_target_view, msaa_target, msaa_view, viewport) = + create_resources(devices, &swap_chain, width, height)?; + // unsafe { + // devices + // .device_context + // .OMSetRenderTargets(Some(&render_target_view), None); + // } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { @@ -434,10 +414,25 @@ impl DirectXResources { msaa_target, msaa_view, viewport, - // #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: 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; + Ok(()) + } } impl DirectXRenderPipelines { @@ -909,7 +904,6 @@ impl Drop for DirectXResources { fn drop(&mut self) { unsafe { ManuallyDrop::drop(&mut self.render_target); - ManuallyDrop::drop(&mut self.swap_chain); } } } @@ -961,6 +955,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, @@ -980,7 +976,7 @@ fn create_swap_chain( device: &ID3D11Device, width: u32, height: u32, -) -> Result> { +) -> Result { let desc = DXGI_SWAP_CHAIN_DESC1 { Width: width, Height: height, @@ -998,9 +994,7 @@ fn create_swap_chain( AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, Flags: 0, }; - Ok(ManuallyDrop::new(unsafe { - dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? - })) + Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } // #[cfg(feature = "enable-renderdoc")] @@ -1010,7 +1004,7 @@ fn create_swap_chain_default( hwnd: HWND, width: u32, height: u32, -) -> Result> { +) -> Result { use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER; let desc = DXGI_SWAP_CHAIN_DESC1 { @@ -1032,7 +1026,33 @@ fn create_swap_chain_default( let swap_chain = unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?; unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?; - Ok(ManuallyDrop::new(swap_chain)) + Ok(swap_chain) +} + +#[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] @@ -1057,7 +1077,7 @@ fn create_msaa_target_and_its_view( device: &ID3D11Device, width: u32, height: u32, -) -> Result<(ID3D11Texture2D, ID3D11RenderTargetView)> { +) -> Result<(ID3D11Texture2D, [Option; 1])> { let msaa_target = unsafe { let mut output = None; let desc = D3D11_TEXTURE2D_DESC { @@ -1083,7 +1103,7 @@ fn create_msaa_target_and_its_view( device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; output.unwrap() }; - Ok((msaa_target, msaa_view)) + Ok((msaa_target, [Some(msaa_view)])) } #[inline] From eb310bcf7dc51b6ca660ac6c6125423198ac82da Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:34:09 +0800 Subject: [PATCH 121/202] wip --- .../src/platform/windows/directx_renderer.rs | 32 +++++++++++-------- crates/gpui/src/platform/windows/events.rs | 4 --- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 87c98512b9..d6d5d442af 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -4,7 +4,6 @@ use ::util::ResultExt; use anyhow::{Context, Result}; // #[cfg(not(feature = "enable-renderdoc"))] use windows::Win32::Graphics::DirectComposition::*; -use windows::Win32::UI::WindowsAndMessaging::GetWindowRect; use windows::{ Win32::{ Foundation::{HMODULE, HWND}, @@ -50,7 +49,9 @@ struct DirectXResources { msaa_target: ID3D11Texture2D, msaa_view: [Option; 1], - // Cached viewport + // Cached window size and viewport + width: u32, + height: u32, viewport: [D3D11_VIEWPORT; 1], } @@ -111,7 +112,7 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); - let resources = DirectXResources::new(devices, hwnd)?; + let resources = DirectXResources::new(devices)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; // #[cfg(not(feature = "enable-renderdoc"))] @@ -211,12 +212,17 @@ impl DirectXRenderer { } pub(crate) fn resize(&mut self, new_size: Size) -> Result<()> { + 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 { - let width = new_size.width.0 as u32; - let height = new_size.height.0 as u32; + // 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, width, @@ -224,6 +230,7 @@ impl DirectXRenderer { RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), )?; + self.resources .recreate_resources(&self.devices, width, height)?; self.devices @@ -385,14 +392,9 @@ impl DirectXRenderer { } impl DirectXResources { - pub fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { - let (width, height) = unsafe { - let mut rect = std::mem::zeroed(); - GetWindowRect(hwnd, &mut rect)?; - let width = (rect.right - rect.left).max(1) as u32; - let height = (rect.bottom - rect.top).max(1) as u32; - (width, height) - }; + pub fn new(devices: &DirectXDevices) -> 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")] @@ -413,6 +415,8 @@ impl DirectXResources { render_target_view, msaa_target, msaa_view, + width, + height, viewport, }) } @@ -431,6 +435,8 @@ impl DirectXResources { self.msaa_target = msaa_target; self.msaa_view = msaa_view; self.viewport = viewport; + self.width = width; + self.height = height; Ok(()) } } diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 461aae5e25..6fd899cbef 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); From fa1320d9aa5eaae519b4740118c03db75e1ee357 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:34:34 +0800 Subject: [PATCH 122/202] remove unused --- crates/gpui/src/platform/windows/directx_renderer.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index d6d5d442af..02469043b5 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -402,11 +402,6 @@ impl DirectXResources { // create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; let (render_target, render_target_view, msaa_target, msaa_view, viewport) = create_resources(devices, &swap_chain, width, height)?; - // unsafe { - // devices - // .device_context - // .OMSetRenderTargets(Some(&render_target_view), None); - // } set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(Self { From 72c55b46536c6f5c3e052b752b9ee4719c7ce6ee Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 16:57:23 +0800 Subject: [PATCH 123/202] add new feature `enable-renderdoc` --- crates/gpui/Cargo.toml | 1 + .../src/platform/windows/directx_renderer.rs | 61 +++++++++++-------- crates/zed/Cargo.toml | 1 + 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 29e81269e3..e926bdfcc5 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -72,6 +72,7 @@ screen-capture = [ "scap", ] windows-manifest = [] +enable-renderdoc = [] [lib] path = "src/gpui.rs" diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 02469043b5..062f0a4315 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -2,18 +2,15 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -// #[cfg(not(feature = "enable-renderdoc"))] +#[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::*, }; use crate::*; @@ -28,7 +25,7 @@ pub(crate) struct DirectXRenderer { resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: DirectComposition, } @@ -36,6 +33,7 @@ pub(crate) struct DirectXRenderer { #[derive(Clone)] pub(crate) struct DirectXDevices { dxgi_factory: IDXGIFactory6, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, device: ID3D11Device, device_context: ID3D11DeviceContext, @@ -78,7 +76,7 @@ struct DrawInstancedIndirectArgs { start_instance_location: u32, } -// #[cfg(not(feature = "enable-renderdoc"))] +#[cfg(not(feature = "enable-renderdoc"))] struct DirectComposition { comp_device: IDCompositionDevice, comp_target: IDCompositionTarget, @@ -95,10 +93,12 @@ 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 { dxgi_factory, + #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, device, device_context, @@ -112,20 +112,27 @@ impl DirectXRenderer { devices.device.clone(), devices.device_context.clone(), )); + + #[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"))] + + #[cfg(not(feature = "enable-renderdoc"))] let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] direct_composition.set_swap_chain(&resources.swap_chain)?; + Ok(DirectXRenderer { atlas, devices: devices.clone(), resources, globals, pipelines, - // #[cfg(not(feature = "enable-renderdoc"))] + #[cfg(not(feature = "enable-renderdoc"))] _direct_composition: direct_composition, }) } @@ -392,14 +399,19 @@ impl DirectXRenderer { } impl DirectXResources { - pub fn new(devices: &DirectXDevices) -> Result { + pub fn new( + devices: &DirectXDevices, + #[cfg(feature = "enable-renderdoc")] hwnd: HWND, + ) -> Result { let width = 1; let height = 1; - // #[cfg(not(feature = "enable-renderdoc"))] + + #[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(feature = "enable-renderdoc")] + let swap_chain = + 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)?; @@ -481,7 +493,7 @@ impl DirectXRenderPipelines { } } -// #[cfg(not(feature = "enable-renderdoc"))] +#[cfg(not(feature = "enable-renderdoc"))] impl DirectComposition { pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result { let comp_device = get_comp_device(&dxgi_device)?; @@ -967,11 +979,12 @@ fn get_device( }) } -// #[cfg(not(feature = "enable-renderdoc"))] +#[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, @@ -998,8 +1011,8 @@ fn create_swap_chain( Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } -// #[cfg(feature = "enable-renderdoc")] -fn create_swap_chain_default( +#[cfg(feature = "enable-renderdoc")] +fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, hwnd: HWND, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index a864ece683..dd72260f52 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -70,6 +70,7 @@ gpui = { workspace = true, features = [ "x11", "font-kit", "windows-manifest", + "enable-renderdoc", ] } gpui_tokio.workspace = true From 9acee42c38f604a97902116d57359a3e48ad7f13 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 17:04:36 +0800 Subject: [PATCH 124/202] show err if failed to create new window --- crates/gpui/src/platform/windows/platform.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index a0bdb6dedf..68813b833e 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -462,7 +462,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); From 32488e1e2d8ea8957c23305238bcc95b57f15b06 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 17:12:27 +0800 Subject: [PATCH 125/202] fix --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- crates/zed/Cargo.toml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 062f0a4315..4bc80851be 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -2,8 +2,6 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -#[cfg(not(feature = "enable-renderdoc"))] -use windows::Win32::Graphics::DirectComposition::*; use windows::Win32::{ Foundation::{HMODULE, HWND}, Graphics::{ @@ -12,6 +10,8 @@ use windows::Win32::{ Dxgi::{Common::*, *}, }, }; +#[cfg(not(feature = "enable-renderdoc"))] +use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; use crate::*; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index dd72260f52..a864ece683 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -70,7 +70,6 @@ gpui = { workspace = true, features = [ "x11", "font-kit", "windows-manifest", - "enable-renderdoc", ] } gpui_tokio.workspace = true From 432d11f57b295056bb3ad828b88ed4d4846fd150 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 19:30:49 +0800 Subject: [PATCH 126/202] implement gpu driver version for nvidia --- .../src/platform/windows/directx_renderer.rs | 116 +++++++++++++++++- crates/gpui/src/platform/windows/window.rs | 3 +- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4bc80851be..22bd5b0b95 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -32,6 +32,7 @@ pub(crate) struct DirectXRenderer { /// Direct3D objects #[derive(Clone)] pub(crate) struct DirectXDevices { + adapter: IDXGIAdapter1, dxgi_factory: IDXGIFactory6, #[cfg(not(feature = "enable-renderdoc"))] dxgi_device: IDXGIDevice, @@ -97,6 +98,7 @@ impl DirectXDevices { let dxgi_device: IDXGIDevice = device.cast()?; Ok(Self { + adapter, dxgi_factory, #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, @@ -396,6 +398,34 @@ 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().context("Failed to get NVIDIA driver info"), + 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), + 0x8086 => Err(anyhow::anyhow!("Intel driver info not implemented yet")), + _ => Err(anyhow::anyhow!("Unknown vendor detected.")), + } + .log_err() + .unwrap_or("Unknown Driver".to_string()); + Ok(GpuSpecs { + is_software_emulated, + device_name, + driver_name, + driver_info: driver_version, + }) + } } impl DirectXResources { @@ -936,12 +966,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. @@ -1339,3 +1368,78 @@ mod shader_resources { } } } + +mod nvidia { + use std::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.h#L87 + #[allow(non_camel_case_types)] + type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int; + + // 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#L33 + let nvapi_init_ptr = nvapi_query(0x0150e828); + if nvapi_init_ptr.is_null() { + anyhow::bail!("Failed to get NVIDIA API function pointer"); + } + let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr); + + let result = nvapi_init(); + if result != 0 { + anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result); + } + + // 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; + Ok(format!("{}.{}", major, minor)) + } + } +} diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index e712d939c8..a2bc854484 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -104,6 +104,7 @@ impl WindowsWindowState { let restore_from_minimized = None; // let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; let renderer = DirectXRenderer::new(gpu_context, hwnd)?; + println!("GPU specs: {:#?}", renderer.gpu_specs()); let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -801,7 +802,7 @@ impl PlatformWindow for WindowsWindow { } fn update_ime_position(&self, _bounds: Bounds) { - // todo(windows) + // There is no such thing on Windows. } } From 5058752f2d85ab64e592c42be5cdaf366b11cd08 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 19:48:42 +0800 Subject: [PATCH 127/202] cleanup --- crates/gpui/src/platform.rs | 1 - .../src/platform/windows/directx_atlas.rs | 40 ++++++-------- crates/gpui/src/platform/windows/platform.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 53 +------------------ 4 files changed, 18 insertions(+), 78 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 1e72d23868..f0b9be68bb 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 abd6e4ad40..1abeab8928 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/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 68813b833e..0d72e3c359 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -28,7 +28,7 @@ use windows::{ core::*, }; -use crate::{platform::blade::BladeContext, *}; +use crate::*; pub(crate) struct WindowsPlatform { state: RefCell, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index a2bc854484..a972cead2b 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); @@ -102,9 +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)?; - println!("GPU specs: {:#?}", renderer.gpu_specs()); let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -796,9 +793,7 @@ 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) { @@ -1298,52 +1293,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; From 0c40bb9b5f6ec165b0d1836a233778bd3b3e4d81 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:10:33 +0800 Subject: [PATCH 128/202] impl intel driver version --- .../src/platform/windows/directx_renderer.rs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 22bd5b0b95..8b549c2ae4 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -412,11 +412,12 @@ impl DirectXRenderer { _ => "Unknown Vendor".to_string(), }; let driver_version = match desc.VendorId { - 0x10DE => nvidia::get_driver_version().context("Failed to get NVIDIA driver info"), + 0x10DE => nvidia::get_driver_version(), 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), - 0x8086 => Err(anyhow::anyhow!("Intel 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 { @@ -1443,3 +1444,21 @@ mod nvidia { } } } + +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 + )) + } +} From 0e45ef7e43e1708a3f1beac99de44dddea04ea06 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:31:00 +0800 Subject: [PATCH 129/202] better output for nvidia --- .../src/platform/windows/directx_renderer.rs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 8b549c2ae4..54478e9b02 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1371,7 +1371,10 @@ mod shader_resources { } mod nvidia { - use std::os::raw::{c_char, c_int, c_uint}; + use std::{ + ffi::CStr, + os::raw::{c_char, c_int, c_uint}, + }; use anyhow::{Context, Result}; use windows::{ @@ -1386,10 +1389,6 @@ mod nvidia { #[allow(non_camel_case_types)] type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX]; - // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi.h#L87 - #[allow(non_camel_case_types)] - type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int; - // 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( @@ -1405,18 +1404,6 @@ mod nvidia { .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#L33 - let nvapi_init_ptr = nvapi_query(0x0150e828); - if nvapi_init_ptr.is_null() { - anyhow::bail!("Failed to get NVIDIA API function pointer"); - } - let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr); - - let result = nvapi_init(); - if result != 0 { - anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result); - } - // 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() { @@ -1440,7 +1427,13 @@ mod nvidia { } let major = driver_version / 100; let minor = driver_version % 100; - Ok(format!("{}.{}", major, minor)) + let branch_string = CStr::from_ptr(build_branch_string.as_ptr()); + Ok(format!( + "{}.{} {}", + major, + minor, + branch_string.to_string_lossy() + )) } } } From c7342a9df5cb02db9bedeeef81561c738d62b7aa Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 17 Jul 2025 21:35:42 +0800 Subject: [PATCH 130/202] remove unused --- crates/gpui/src/platform/windows/platform.rs | 2 -- crates/gpui/src/platform/windows/window.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 0d72e3c359..0a6f73dae4 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -34,7 +34,6 @@ 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")?; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index a972cead2b..eda75956b6 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -475,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 From 0b57c86e07f242b223df583621ac504d076f2f21 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 15:11:49 +0800 Subject: [PATCH 131/202] add amd gpu version support --- crates/gpui/build.rs | 57 +++++++++++-- crates/gpui/libs/README.md | 10 +++ crates/gpui/libs/amd_ags_x64_2022_MT.lib | Bin 0 -> 153528 bytes crates/gpui/libs/amd_ags_x86_2022_MT.lib | Bin 0 -> 81484 bytes .../src/platform/windows/directx_renderer.rs | 80 +++++++++++++++++- 5 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 crates/gpui/libs/README.md create mode 100644 crates/gpui/libs/amd_ags_x64_2022_MT.lib create mode 100644 crates/gpui/libs/amd_ags_x86_2022_MT.lib diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 7ab44a73f5..8f0c0da6dd 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -9,7 +9,10 @@ fn main() { let target = env::var("CARGO_CFG_TARGET_OS"); println!("cargo::rustc-check-cfg=cfg(gles)"); - #[cfg(any(not(target_os = "macos"), feature = "macos-blade"))] + #[cfg(any( + not(any(target_os = "macos", target_os = "windows")), + feature = "macos-blade" + ))] check_wgsl_shaders(); match target.as_deref() { @@ -17,15 +20,9 @@ fn main() { #[cfg(target_os = "macos")] macos::build(); } - #[cfg(all(target_os = "windows", feature = "windows-manifest"))] Ok("windows") => { - let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); - let rc_file = std::path::Path::new("resources/windows/gpui.rc"); - println!("cargo:rerun-if-changed={}", manifest.display()); - println!("cargo:rerun-if-changed={}", rc_file.display()); - embed_resource::compile(rc_file, embed_resource::NONE) - .manifest_required() - .unwrap(); + #[cfg(target_os = "windows")] + windows::build(); } _ => (), }; @@ -243,3 +240,45 @@ mod macos { } } } + +#[cfg(target_os = "windows")] +mod windows { + use std::path::PathBuf; + + pub(super) fn build() { + // Link the AMD AGS library + link_amd_ags(); + + // Embed the Windows manifest and resource file + #[cfg(feature = "windows-manifest")] + embed_resource(); + } + + fn link_amd_ags() { + // We can not use relative paths in `cargo:rustc-link-search`, so we need to use the absolute path. + // See: https://stackoverflow.com/questions/41917096/how-do-i-make-rustc-link-search-relative-to-the-project-location + let lib_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("libs"); + #[cfg(target_pointer_width = "64")] + let lib_name = "amd_ags_x64_2022_MT"; + #[cfg(target_pointer_width = "32")] + let lib_name = "amd_ags_x86_2022_MT"; + println!("cargo:rustc-link-lib=static={}", lib_name); + println!("cargo:rustc-link-search=native={}", lib_dir.display()); + println!( + "cargo:rerun-if-changed={}/{}.lib", + lib_dir.display(), + lib_name + ); + } + + #[cfg(feature = "windows-manifest")] + fn embed_resource() { + let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); + let rc_file = std::path::Path::new("resources/windows/gpui.rc"); + println!("cargo:rerun-if-changed={}", manifest.display()); + println!("cargo:rerun-if-changed={}", rc_file.display()); + embed_resource::compile(rc_file, embed_resource::NONE) + .manifest_required() + .unwrap(); + } +} diff --git a/crates/gpui/libs/README.md b/crates/gpui/libs/README.md new file mode 100644 index 0000000000..2be286ee53 --- /dev/null +++ b/crates/gpui/libs/README.md @@ -0,0 +1,10 @@ +The files in this folder are required for the Windows platform support in the gpui library. + + +#### amd_ags_x64.lib & amd_ags_x86.lib + +These libraries are used for AMD GPU support, currently only used on Windows, mainly for retrieving GPU information and capabilities. They are linked against the AMD AGS (AMD GPU Services) library. + +The official AMD AGS documentation can be found [here](https://gpuopen.com/amd-gpu-services-ags-library). And these two files are grabbed from the [official AMD AGS repository](https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK), currently at version 6.3.0. + +If you want to update these files, don't forget to update the value of `AGS_CURRENT_VERSION` in `gpui/src/platform/windows/directx_renderer.rs` as well. diff --git a/crates/gpui/libs/amd_ags_x64_2022_MT.lib b/crates/gpui/libs/amd_ags_x64_2022_MT.lib new file mode 100644 index 0000000000000000000000000000000000000000..a25d772160ddadcc98ce777c0e73a57e9f05a59a GIT binary patch literal 153528 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 CUx%mw literal 0 HcmV?d00001 diff --git a/crates/gpui/libs/amd_ags_x86_2022_MT.lib b/crates/gpui/libs/amd_ags_x86_2022_MT.lib new file mode 100644 index 0000000000000000000000000000000000000000..cdb1305378fa058b0e3ffcdeeca8d4ee4e28318b GIT binary patch literal 81484 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 J1FHcs7y#_=bGHBh literal 0 HcmV?d00001 diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 54478e9b02..4e804e74dc 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -413,7 +413,7 @@ impl DirectXRenderer { }; let driver_version = match desc.VendorId { 0x10DE => nvidia::get_driver_version(), - 0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")), + 0x1002 => amd::get_driver_version(), 0x8086 => intel::get_driver_version(&self.devices.adapter), _ => Err(anyhow::anyhow!("Unknown vendor detected.")), } @@ -1438,6 +1438,84 @@ mod nvidia { } } +mod amd { + use std::os::raw::{c_char, c_int, c_void}; + + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145 + const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0; + + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204 + // This is an opaque type, using struct to represent it properly for FFI + #[repr(C)] + struct AGSContext { + _private: [u8; 0], + } + + #[repr(C)] + pub struct AGSGPUInfo { + pub driver_version: *const c_char, + pub radeon_software_version: *const c_char, + pub num_devices: c_int, + pub devices: *mut c_void, + } + + unsafe extern "C" { + fn agsInitialize( + version: c_int, + config: *const c_void, + context: *mut *mut AGSContext, + gpu_info: *mut AGSGPUInfo, + ) -> c_int; + + fn agsDeInitialize(context: *mut AGSContext) -> c_int; + } + + pub(super) fn get_driver_version() -> anyhow::Result { + unsafe { + let mut context: *mut AGSContext = std::ptr::null_mut(); + let mut gpu_info: AGSGPUInfo = AGSGPUInfo { + driver_version: std::ptr::null(), + radeon_software_version: std::ptr::null(), + num_devices: 0, + devices: std::ptr::null_mut(), + }; + + let result = agsInitialize( + AGS_CURRENT_VERSION, + std::ptr::null(), + &mut context, + &mut gpu_info, + ); + if result != 0 { + return Err(anyhow::anyhow!( + "Failed to initialize AGS, error code: {}", + result + )); + } + + // Vulkan acctually returns this as the driver version + let software_version = if !gpu_info.radeon_software_version.is_null() { + std::ffi::CStr::from_ptr(gpu_info.radeon_software_version) + .to_string_lossy() + .into_owned() + } else { + "Unknown Radeon Software Version".to_string() + }; + + let driver_version = if !gpu_info.driver_version.is_null() { + std::ffi::CStr::from_ptr(gpu_info.driver_version) + .to_string_lossy() + .into_owned() + } else { + "Unknown Radeon Driver Version".to_string() + }; + + agsDeInitialize(context); + Ok(format!("{} ({})", software_version, driver_version)) + } + } +} + mod intel { use windows::{ Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice}, From 61981aabb522703f1f1b01f98bf2053092e66810 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 16:46:34 +0800 Subject: [PATCH 132/202] allow to compile shader at building --- crates/gpui/build.rs | 179 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 8f0c0da6dd..5749280d3c 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -243,12 +243,19 @@ mod macos { #[cfg(target_os = "windows")] mod windows { - use std::path::PathBuf; + use std::{ + io::Write, + path::{Path, PathBuf}, + process::{self, Command}, + }; pub(super) fn build() { // Link the AMD AGS library link_amd_ags(); + // Compile HLSL shaders + compile_shaders(); + // Embed the Windows manifest and resource file #[cfg(feature = "windows-manifest")] embed_resource(); @@ -281,4 +288,174 @@ mod windows { .manifest_required() .unwrap(); } + + fn compile_shaders() { + use std::fs; + use std::process::{self, Command}; + + let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("src/platform/windows/shaders.hlsl"); + let out_dir = std::env::var("OUT_DIR").unwrap(); + + println!("cargo:rerun-if-changed={}", shader_path.display()); + + // Check if fxc.exe is available + let fxc_path = find_fxc_compiler(); + + // Define all modules + let modules = [ + "quad", + "shadow", + "paths", + "underline", + "monochrome_sprite", + "polychrome_sprite", + ]; + + let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir); + if Path::new(&rust_binding_path).exists() { + fs::remove_file(&rust_binding_path) + .expect("Failed to remove existing Rust binding file"); + } + for module in &modules { + compile_shader_for_module( + module, + &out_dir, + &fxc_path, + shader_path.to_str().unwrap(), + &rust_binding_path, + ); + } + println!( + "cargo:warning=Successfully compiled shaders. Output written to: {}", + rust_binding_path + ); + } + + fn find_fxc_compiler() -> String { + // Try to find in PATH + if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { + if output.status.success() { + let path = String::from_utf8_lossy(&output.stdout); + return path.trim().to_string(); + } + } + + // Check the default path + if Path::new(r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe") + .exists() + { + return r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe" + .to_string(); + } + + // Check environment variable + if let Ok(path) = std::env::var("GPUI_FXC_PATH") { + if Path::new(&path).exists() { + return path; + } + } + + panic!("Failed to find fxc.exe"); + } + + fn compile_shader_for_module( + module: &str, + out_dir: &str, + fxc_path: &str, + shader_path: &str, + rust_binding_path: &str, + ) { + // Compile vertex shader + let output_file = format!("{}/{}_vs.h", out_dir, module); + let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase()); + compile_shader_impl( + fxc_path, + &format!("{module}_vertex"), + &output_file, + &const_name, + shader_path, + "vs_5_0", + ); + generate_rust_binding(&const_name, &output_file, &rust_binding_path); + + // Compile fragment shader + let output_file = format!("{}/{}_ps.h", out_dir, module); + let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase()); + compile_shader_impl( + fxc_path, + &format!("{module}_fragment"), + &output_file, + &const_name, + shader_path, + "ps_5_0", + ); + generate_rust_binding(&const_name, &output_file, &rust_binding_path); + } + + fn compile_shader_impl( + fxc_path: &str, + entry_point: &str, + output_path: &str, + var_name: &str, + shader_path: &str, + target: &str, + ) { + let output = Command::new(fxc_path) + .args([ + "/T", + target, + "/E", + entry_point, + "/Fh", + output_path, + "/Vn", + var_name, + shader_path, + ]) + .output(); + + match output { + Ok(result) => { + if result.status.success() { + return; + } + eprintln!( + "Pixel shader compilation failed for {}:\n{}", + entry_point, + String::from_utf8_lossy(&result.stderr) + ); + process::exit(1); + } + Err(e) => { + eprintln!("Failed to run fxc for {}: {}", entry_point, e); + process::exit(1); + } + } + } + + fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) { + let header_content = + std::fs::read_to_string(head_file).expect("Failed to read header file"); + let const_definition = { + let global_var_start = header_content.find("const BYTE").unwrap(); + let global_var = &header_content[global_var_start..]; + let equal = global_var.find('=').unwrap(); + global_var[equal + 1..].trim() + }; + let rust_binding = format!( + "const {}: &[u8] = &{}\n", + const_name, + const_definition.replace('{', "[").replace('}', "]") + ); + let mut options = std::fs::OpenOptions::new() + .write(true) + .create(true) + .append(true) + .open(output_path) + .expect("Failed to open Rust binding file"); + options + .write_all(rust_binding.as_bytes()) + .expect("Failed to write Rust binding file"); + } } From d9c6d09545a24bad2013feda5249b1cd2be559da Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:08:44 +0800 Subject: [PATCH 133/202] checkpoint --- crates/gpui/build.rs | 1 + .../src/platform/windows/directx_renderer.rs | 177 +++++++++++------- 2 files changed, 113 insertions(+), 65 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 5749280d3c..f8f756cc53 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -289,6 +289,7 @@ mod windows { .unwrap(); } + /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { use std::fs; use std::process::{self, Command}; diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 4e804e74dc..7959e2d90f 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -13,7 +13,12 @@ use windows::Win32::{ #[cfg(not(feature = "enable-renderdoc"))] use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; -use crate::*; +use crate::{ + platform::windows::directx_renderer::shader_resources::{ + RawShaderBytes, ShaderModule, ShaderTarget, + }, + *, +}; 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. @@ -481,35 +486,22 @@ impl DirectXResources { impl DirectXRenderPipelines { pub fn new(device: &ID3D11Device) -> Result { - let shadow_pipeline = PipelineState::new( - device, - "shadow_pipeline", - "shadow_vertex", - "shadow_fragment", - 4, - )?; - let quad_pipeline = - PipelineState::new(device, "quad_pipeline", "quad_vertex", "quad_fragment", 64)?; + let shadow_pipeline = + PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; + let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; let paths_pipeline = PathsPipelineState::new(device)?; - let underline_pipeline = PipelineState::new( - device, - "underline_pipeline", - "underline_vertex", - "underline_fragment", - 4, - )?; + let underline_pipeline = + PipelineState::new(device, "underline_pipeline", ShaderModule::Underline, 4)?; let mono_sprites = PipelineState::new( device, "monochrome_sprite_pipeline", - "monochrome_sprite_vertex", - "monochrome_sprite_fragment", + ShaderModule::MonochromeSprite, 512, )?; let poly_sprites = PipelineState::new( device, "polychrome_sprite_pipeline", - "polychrome_sprite_vertex", - "polychrome_sprite_fragment", + ShaderModule::PolychromeSprite, 16, )?; @@ -625,29 +617,16 @@ impl PipelineState { fn new( device: &ID3D11Device, label: &'static str, - vertex_entry: &str, - fragment_entry: &str, + shader_module: ShaderModule, 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 raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Vertex)?; + create_vertex_shader(device, raw_shader.as_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 raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Fragment)?; + create_fragment_shader(device, raw_shader.as_bytes())? }; let buffer = create_buffer(device, std::mem::size_of::(), buffer_size)?; let view = create_buffer_view(device, &buffer)?; @@ -740,24 +719,15 @@ impl PipelineState { 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 raw_vertex_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Vertex)?; + ( + create_vertex_shader(device, raw_vertex_shader.as_bytes())?, + raw_vertex_shader, + ) }; 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 raw_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Fragment)?; + create_fragment_shader(device, raw_shader.as_bytes())? }; let buffer = create_buffer(device, std::mem::size_of::(), 32)?; let view = create_buffer_view(device, &buffer)?; @@ -769,10 +739,6 @@ impl PathsPipelineState { 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( &[ @@ -813,7 +779,7 @@ impl PathsPipelineState { InstanceDataStepRate: 0, }, ], - shader_bytes, + vertex_shader.as_bytes(), Some(&mut layout), )?; layout.unwrap() @@ -1316,12 +1282,73 @@ const BUFFER_COUNT: usize = 3; mod shader_resources { use anyhow::Result; - use windows::Win32::Graphics::Direct3D::{ - Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, - ID3DBlob, - }; - use windows_core::{HSTRING, PCSTR}; + // use windows::Win32::Graphics::Direct3D::{ + // Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + // ID3DBlob, + // }; + // use windows_core::{HSTRING, PCSTR}; + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub(super) enum ShaderModule { + Quad, + Shadow, + Underline, + Paths, + MonochromeSprite, + PolychromeSprite, + } + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + pub(super) enum ShaderTarget { + Vertex, + Fragment, + } + + pub(super) struct RawShaderBytes<'t> { + inner: &'t [u8], + } + + impl<'t> RawShaderBytes<'t> { + pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result { + Ok(Self::from_bytes(module, target)) + } + + pub(super) fn as_bytes(&self) -> &'t [u8] { + self.inner + } + + fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self { + let bytes = match module { + ShaderModule::Quad => match target { + ShaderTarget::Vertex => QUAD_VERTEX_BYTES, + ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES, + }, + ShaderModule::Shadow => match target { + ShaderTarget::Vertex => SHADOW_VERTEX_BYTES, + ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES, + }, + ShaderModule::Underline => match target { + ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES, + ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES, + }, + ShaderModule::Paths => match target { + ShaderTarget::Vertex => PATHS_VERTEX_BYTES, + ShaderTarget::Fragment => PATHS_FRAGMENT_BYTES, + }, + ShaderModule::MonochromeSprite => match target { + ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES, + ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES, + }, + ShaderModule::PolychromeSprite => match target { + ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES, + ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES, + }, + }; + Self { inner: bytes } + } + } + + #[cfg(not(debug_assertions))] pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { unsafe { let mut entry = entry.to_owned(); @@ -1368,6 +1395,26 @@ mod shader_resources { Ok(compile_blob.unwrap()) } } + + include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs")); + + impl ShaderModule { + pub fn as_str(&self) -> &'static str { + match self { + ShaderModule::Quad => "quad", + ShaderModule::Shadow => "shadow", + ShaderModule::Underline => "underline", + ShaderModule::Paths => "paths", + ShaderModule::MonochromeSprite => "monochrome_sprite", + ShaderModule::PolychromeSprite => "polychrome_sprite", + } + } + } + + // pub fn quad_vertex_shader() -> &'static [u8] { + // unsafe { + // std::slice::from_raw_parts(g_quad_vertex.as_ptr() as *const u8, g_quad_vertex.len()) + // } } mod nvidia { From 5ff9114b18d342129581d5d62f59c7100f5bdadd Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:43:01 +0800 Subject: [PATCH 134/202] add runtime shader --- crates/gpui/build.rs | 1 + .../src/platform/windows/directx_renderer.rs | 88 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index f8f756cc53..a7f323277a 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -254,6 +254,7 @@ mod windows { link_amd_ags(); // Compile HLSL shaders + #[cfg(not(debug_assertions))] compile_shaders(); // Embed the Windows manifest and resource file diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 7959e2d90f..acc3faa91b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1282,11 +1282,15 @@ const BUFFER_COUNT: usize = 3; mod shader_resources { use anyhow::Result; - // use windows::Win32::Graphics::Direct3D::{ - // Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, - // ID3DBlob, - // }; - // use windows_core::{HSTRING, PCSTR}; + + #[cfg(debug_assertions)] + use windows::{ + Win32::Graphics::Direct3D::{ + Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile}, + ID3DBlob, + }, + core::{HSTRING, PCSTR}, + }; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(super) enum ShaderModule { @@ -1306,17 +1310,35 @@ mod shader_resources { pub(super) struct RawShaderBytes<'t> { inner: &'t [u8], + + #[cfg(debug_assertions)] + _blob: ID3DBlob, } impl<'t> RawShaderBytes<'t> { pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result { - Ok(Self::from_bytes(module, target)) + #[cfg(not(debug_assertions))] + { + Ok(Self::from_bytes(module, target)) + } + #[cfg(debug_assertions)] + { + let blob = build_shader_blob(module, target)?; + let inner = unsafe { + std::slice::from_raw_parts( + blob.GetBufferPointer() as *const u8, + blob.GetBufferSize(), + ) + }; + Ok(Self { inner, _blob: blob }) + } } - pub(super) fn as_bytes(&self) -> &'t [u8] { + pub(super) fn as_bytes(&'t self) -> &'t [u8] { self.inner } + #[cfg(not(debug_assertions))] fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self { let bytes = match module { ShaderModule::Quad => match target { @@ -1348,32 +1370,38 @@ mod shader_resources { } } - #[cfg(not(debug_assertions))] - pub(super) fn build_shader_blob(entry: &str, target: &str) -> Result { + #[cfg(debug_assertions)] + pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result { unsafe { - let mut entry = entry.to_owned(); - let mut target = target.to_owned(); + let entry = format!( + "{}_{}\0", + entry.as_str(), + match target { + ShaderTarget::Vertex => "vertex", + ShaderTarget::Fragment => "fragment", + } + ); + let target = match target { + ShaderTarget::Vertex => "vs_5_0\0", + ShaderTarget::Fragment => "ps_5_0\0", + }; + let mut compile_blob = None; let mut error_blob = None; let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("src/platform/windows/shaders.hlsl") - .canonicalize() - .unwrap(); - entry.push_str("\0"); - target.push_str("\0"); + .canonicalize()?; + let entry_point = PCSTR::from_raw(entry.as_ptr()); let target_cstr = PCSTR::from_raw(target.as_ptr()); - #[cfg(debug_assertions)] - let compile_flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; - #[cfg(not(debug_assertions))] - let compile_flag = 0; + let ret = D3DCompileFromFile( &HSTRING::from(shader_path.to_str().unwrap()), None, None, entry_point, target_cstr, - compile_flag, + D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION, 0, &mut compile_blob, Some(&mut error_blob), @@ -1382,13 +1410,10 @@ mod shader_resources { let Some(error_blob) = error_blob else { return Err(anyhow::anyhow!("{ret:?}")); }; - let string_len = error_blob.GetBufferSize(); - let error_string_encode = Vec::from_raw_parts( - error_blob.GetBufferPointer() as *mut u8, - string_len, - string_len, - ); - let error_string = String::from_utf8_lossy(&error_string_encode).to_string(); + + let error_string = + std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8) + .to_string_lossy(); log::error!("Shader compile error: {}", error_string); return Err(anyhow::anyhow!("Compile error: {}", error_string)); } @@ -1396,10 +1421,12 @@ mod shader_resources { } } + #[cfg(not(debug_assertions))] include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs")); + #[cfg(debug_assertions)] impl ShaderModule { - pub fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &str { match self { ShaderModule::Quad => "quad", ShaderModule::Shadow => "shadow", @@ -1410,11 +1437,6 @@ mod shader_resources { } } } - - // pub fn quad_vertex_shader() -> &'static [u8] { - // unsafe { - // std::slice::from_raw_parts(g_quad_vertex.as_ptr() as *const u8, g_quad_vertex.len()) - // } } mod nvidia { From 1b0a0aa58e2fbaa4e2e437d91a43a653d7aaea2d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:48:05 +0800 Subject: [PATCH 135/202] add x86 support for nvidia --- crates/gpui/src/platform/windows/directx_renderer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index acc3faa91b..2cad807ae1 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1468,7 +1468,13 @@ mod nvidia { 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")?; + #[cfg(target_pointer_width = "64")] + let nvidia_dll = + LoadLibraryA(s!("nvapi64.dll")).context(format!("Can't load nvapi64.dll"))?; + #[cfg(target_pointer_width = "32")] + let nvidia_dll = + LoadLibraryA(s!("nvapi.dll")).context(format!("Can't load nvapi.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); From d208f75f46072871cee7b7b755a09ea10d797235 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:53:45 +0800 Subject: [PATCH 136/202] enable `O3` optimization for fxc --- crates/gpui/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index a7f323277a..bba311aff2 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -293,7 +293,6 @@ mod windows { /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { use std::fs; - use std::process::{self, Command}; let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) .join("src/platform/windows/shaders.hlsl"); @@ -413,6 +412,7 @@ mod windows { output_path, "/Vn", var_name, + "/O3", shader_path, ]) .output(); From 52c0fa5ce9f86816080219aa32fa1061eead7887 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:54:26 +0800 Subject: [PATCH 137/202] remove debug print --- crates/gpui/build.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index bba311aff2..c5eb293a2d 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -327,10 +327,6 @@ mod windows { &rust_binding_path, ); } - println!( - "cargo:warning=Successfully compiled shaders. Output written to: {}", - rust_binding_path - ); } fn find_fxc_compiler() -> String { From 3978bba5a7d2ba5be2db20f938e1848d7b217784 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Fri, 18 Jul 2025 17:56:46 +0800 Subject: [PATCH 138/202] fix --- crates/gpui/build.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index c5eb293a2d..36186c87ed 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -244,6 +244,7 @@ mod macos { #[cfg(target_os = "windows")] mod windows { use std::{ + fs, io::Write, path::{Path, PathBuf}, process::{self, Command}, @@ -270,6 +271,7 @@ mod windows { let lib_name = "amd_ags_x64_2022_MT"; #[cfg(target_pointer_width = "32")] let lib_name = "amd_ags_x86_2022_MT"; + println!("cargo:rustc-link-lib=static={}", lib_name); println!("cargo:rustc-link-search=native={}", lib_dir.display()); println!( @@ -292,8 +294,6 @@ mod windows { /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn compile_shaders() { - use std::fs; - let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()) .join("src/platform/windows/shaders.hlsl"); let out_dir = std::env::var("OUT_DIR").unwrap(); @@ -318,7 +318,7 @@ mod windows { fs::remove_file(&rust_binding_path) .expect("Failed to remove existing Rust binding file"); } - for module in &modules { + for module in modules { compile_shader_for_module( module, &out_dir, @@ -329,6 +329,7 @@ mod windows { } } + /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn find_fxc_compiler() -> String { // Try to find in PATH if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { @@ -433,8 +434,7 @@ mod windows { } fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) { - let header_content = - std::fs::read_to_string(head_file).expect("Failed to read header file"); + let header_content = fs::read_to_string(head_file).expect("Failed to read header file"); let const_definition = { let global_var_start = header_content.find("const BYTE").unwrap(); let global_var = &header_content[global_var_start..]; @@ -446,7 +446,7 @@ mod windows { const_name, const_definition.replace('{', "[").replace('}', "]") ); - let mut options = std::fs::OpenOptions::new() + let mut options = fs::OpenOptions::new() .write(true) .create(true) .append(true) From 275d84d56632236a222980c872c8ef2a798e5229 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 15:35:27 +0800 Subject: [PATCH 139/202] init `handle_device_lost` --- .../src/platform/windows/directx_renderer.rs | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 2cad807ae1..91f85458d0 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -179,7 +179,7 @@ impl DirectXRenderer { Ok(()) } - fn present(&self) -> Result<()> { + fn present(&mut self) -> Result<()> { unsafe { self.devices.device_context.ResolveSubresource( &*self.resources.render_target, @@ -191,11 +191,26 @@ impl DirectXRenderer { self.devices .device_context .OMSetRenderTargets(Some(&self.resources.render_target_view), None); - self.resources.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?; + let result = self.resources.swap_chain.Present(0, DXGI_PRESENT(0)); + // Presenting the swap chain can fail if the DirectX device was removed or reset. + if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET { + let reason = self.devices.device.GetDeviceRemovedReason(); + log::error!( + "DirectX device removed or reset when drawing. Reason: {:?}", + reason + ); + self.handle_device_lost()?; + } else { + result.ok()?; + } } Ok(()) } + fn handle_device_lost(&mut self) -> Result<()> { + Ok(()) + } + pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> { self.pre_draw()?; for batch in scene.batches() { @@ -237,13 +252,33 @@ impl DirectXRenderer { ManuallyDrop::drop(&mut self.resources.render_target); drop(self.resources.render_target_view[0].take().unwrap()); - self.resources.swap_chain.ResizeBuffers( + let result = self.resources.swap_chain.ResizeBuffers( BUFFER_COUNT as u32, width, height, RENDER_TARGET_FORMAT, DXGI_SWAP_CHAIN_FLAG(0), - )?; + ); + // Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error. + // The app might have moved to a monitor that's attached to a different graphics device. + // When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change. + match result { + Ok(_) => {} + Err(e) => { + if e.code() == DXGI_ERROR_DEVICE_REMOVED || e.code() == DXGI_ERROR_DEVICE_RESET + { + let reason = self.devices.device.GetDeviceRemovedReason(); + log::error!( + "DirectX device removed or reset when resizing. Reason: {:?}", + reason + ); + self.handle_device_lost()?; + return Ok(()); + } + log::error!("Failed to resize swap chain: {:?}", e); + return Err(e.into()); + } + } self.resources .recreate_resources(&self.devices, width, height)?; From b5377c56f283daf80996ad6f983e135ad82ab2a0 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 16:07:47 +0800 Subject: [PATCH 140/202] remove noise when device lost --- crates/gpui/src/platform/windows/platform.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 0a6f73dae4..c7ba58b3af 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -851,8 +851,9 @@ fn begin_vsync(vsync_event: HANDLE) { let event: SafeHandle = vsync_event.into(); std::thread::spawn(move || unsafe { loop { - windows::Win32::Graphics::Dwm::DwmFlush().log_err(); - SetEvent(*event).log_err(); + if windows::Win32::Graphics::Dwm::DwmFlush().is_ok() { + SetEvent(*event).log_err(); + } } }); } From 74d953d02452d5f691e183fa08e03694fb13823c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 16:49:41 +0800 Subject: [PATCH 141/202] checkpoint --- .../src/platform/windows/directx_atlas.rs | 14 ++++ .../src/platform/windows/directx_renderer.rs | 64 +++++++++++++++---- 2 files changed, 66 insertions(+), 12 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 1abeab8928..ca6d38de77 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -53,6 +53,20 @@ impl DirectXAtlas { let tex = lock.texture(id); tex.view.clone() } + + pub(crate) fn handle_device_lost( + &self, + device: ID3D11Device, + device_context: ID3D11DeviceContext, + ) -> anyhow::Result<()> { + let mut lock = self.0.lock(); + lock.device = device; + lock.device_context = device_context; + lock.monochrome_textures = AtlasTextureList::default(); + lock.polychrome_textures = AtlasTextureList::default(); + lock.tiles_by_key.clear(); + Ok(()) + } } impl PlatformAtlas for DirectXAtlas { diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 91f85458d0..bbb0f5119f 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -25,13 +25,14 @@ const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; const MULTISAMPLE_COUNT: u32 = 4; pub(crate) struct DirectXRenderer { + hwnd: HWND, atlas: Arc, devices: DirectXDevices, resources: DirectXResources, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: DirectComposition, + _direct_composition: ManuallyDrop, } /// Direct3D objects @@ -121,19 +122,22 @@ impl DirectXRenderer { )); #[cfg(not(feature = "enable-renderdoc"))] - let resources = DirectXResources::new(devices)?; + let resources = DirectXResources::new(devices).unwrap(); #[cfg(feature = "enable-renderdoc")] let resources = DirectXResources::new(devices, hwnd)?; - let globals = DirectXGlobalElements::new(&devices.device)?; - let pipelines = DirectXRenderPipelines::new(&devices.device)?; + let globals = DirectXGlobalElements::new(&devices.device).unwrap(); + let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); #[cfg(not(feature = "enable-renderdoc"))] - let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; + let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd).unwrap(); #[cfg(not(feature = "enable-renderdoc"))] - direct_composition.set_swap_chain(&resources.swap_chain)?; + direct_composition + .set_swap_chain(&resources.swap_chain) + .unwrap(); Ok(DirectXRenderer { + hwnd, atlas, devices: devices.clone(), resources, @@ -208,6 +212,34 @@ impl DirectXRenderer { } fn handle_device_lost(&mut self) -> Result<()> { + let devices = DirectXDevices::new().context("Recreating DirectX devices")?; + unsafe { + ManuallyDrop::drop(&mut self._direct_composition); + } + self.atlas + .handle_device_lost(devices.device.clone(), devices.device_context.clone()); + #[cfg(not(feature = "enable-renderdoc"))] + let resources = DirectXResources::new(&devices).unwrap(); + #[cfg(feature = "enable-renderdoc")] + let resources = DirectXResources::new(devices, hwnd)?; + let globals = DirectXGlobalElements::new(&devices.device).unwrap(); + let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); + + #[cfg(not(feature = "enable-renderdoc"))] + let direct_composition = DirectComposition::new(&devices.dxgi_device, self.hwnd).unwrap(); + #[cfg(not(feature = "enable-renderdoc"))] + direct_composition + .set_swap_chain(&resources.swap_chain) + .unwrap(); + + self.devices = devices; + self.resources = resources; + self.globals = globals; + self.pipelines = pipelines; + #[cfg(not(feature = "enable-renderdoc"))] + { + self._direct_composition = direct_composition; + } Ok(()) } @@ -553,16 +585,16 @@ 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() }?; + pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result> { + let comp_device = get_comp_device(&dxgi_device).unwrap(); + let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }.unwrap(); + let comp_visual = unsafe { comp_device.CreateVisual() }.unwrap(); - Ok(Self { + Ok(ManuallyDrop::new(Self { comp_device, comp_target, comp_visual, - }) + })) } pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { @@ -945,6 +977,14 @@ struct PathSprite { color: Background, } +impl Drop for DirectXRenderer { + fn drop(&mut self) { + unsafe { + ManuallyDrop::drop(&mut self._direct_composition); + } + } +} + impl Drop for DirectXResources { fn drop(&mut self) { unsafe { From 787c6382f95ea60462d9c06723ef17d6e6e59626 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 18:36:29 +0800 Subject: [PATCH 142/202] remove unused --- crates/gpui/src/platform/windows/directx_atlas.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index ca6d38de77..d915a93cfa 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -58,14 +58,13 @@ impl DirectXAtlas { &self, device: ID3D11Device, device_context: ID3D11DeviceContext, - ) -> anyhow::Result<()> { + ) { let mut lock = self.0.lock(); lock.device = device; lock.device_context = device_context; lock.monochrome_textures = AtlasTextureList::default(); lock.polychrome_textures = AtlasTextureList::default(); lock.tiles_by_key.clear(); - Ok(()) } } From fcb77979f369ab7c6f55fe7a14e58835bdddf16c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 18:38:52 +0800 Subject: [PATCH 143/202] fix build --- crates/gpui/src/platform/windows/directx_renderer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index bbb0f5119f..c9f2add7a2 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -213,6 +213,7 @@ impl DirectXRenderer { fn handle_device_lost(&mut self) -> Result<()> { let devices = DirectXDevices::new().context("Recreating DirectX devices")?; + #[cfg(not(feature = "enable-renderdoc"))] unsafe { ManuallyDrop::drop(&mut self._direct_composition); } @@ -221,7 +222,7 @@ impl DirectXRenderer { #[cfg(not(feature = "enable-renderdoc"))] let resources = DirectXResources::new(&devices).unwrap(); #[cfg(feature = "enable-renderdoc")] - let resources = DirectXResources::new(devices, hwnd)?; + let resources = DirectXResources::new(&devices, self.hwnd)?; let globals = DirectXGlobalElements::new(&devices.device).unwrap(); let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); @@ -977,6 +978,7 @@ struct PathSprite { color: Background, } +#[cfg(not(feature = "enable-renderdoc"))] impl Drop for DirectXRenderer { fn drop(&mut self) { unsafe { From be7d56e11be7cf81b1f8cbc1cbb0ef360c1d2355 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 20:51:24 +0800 Subject: [PATCH 144/202] fix --- .../src/platform/windows/directx_renderer.rs | 59 +++++++++++-------- crates/gpui/src/platform/windows/platform.rs | 12 +--- crates/gpui/src/platform/windows/window.rs | 9 +-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c9f2add7a2..72c2cb5d42 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -27,8 +27,8 @@ const MULTISAMPLE_COUNT: u32 = 4; pub(crate) struct DirectXRenderer { hwnd: HWND, atlas: Arc, - devices: DirectXDevices, - resources: DirectXResources, + devices: ManuallyDrop, + resources: ManuallyDrop, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, #[cfg(not(feature = "enable-renderdoc"))] @@ -115,16 +115,14 @@ impl DirectXDevices { } impl DirectXRenderer { - pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND) -> Result { - let atlas = Arc::new(DirectXAtlas::new( - devices.device.clone(), - devices.device_context.clone(), - )); + pub(crate) fn new(hwnd: HWND) -> Result { + let devices = ManuallyDrop::new(DirectXDevices::new().context("Creating DirectX devices")?); + let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context)); #[cfg(not(feature = "enable-renderdoc"))] - let resources = DirectXResources::new(devices).unwrap(); + let resources = DirectXResources::new(&devices, 1, 1).unwrap(); #[cfg(feature = "enable-renderdoc")] - let resources = DirectXResources::new(devices, hwnd)?; + let resources = DirectXResources::new(&devices, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device).unwrap(); let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); @@ -139,7 +137,7 @@ impl DirectXRenderer { Ok(DirectXRenderer { hwnd, atlas, - devices: devices.clone(), + devices, resources, globals, pipelines, @@ -212,17 +210,29 @@ impl DirectXRenderer { } fn handle_device_lost(&mut self) -> Result<()> { - let devices = DirectXDevices::new().context("Recreating DirectX devices")?; - #[cfg(not(feature = "enable-renderdoc"))] unsafe { + ManuallyDrop::drop(&mut self.devices); + ManuallyDrop::drop(&mut self.resources); + #[cfg(not(feature = "enable-renderdoc"))] ManuallyDrop::drop(&mut self._direct_composition); } - self.atlas - .handle_device_lost(devices.device.clone(), devices.device_context.clone()); + let devices = + ManuallyDrop::new(DirectXDevices::new().context("Recreating DirectX devices")?); + unsafe { + devices.device_context.OMSetRenderTargets(None, None); + devices.device_context.ClearState(); + devices.device_context.Flush(); + } #[cfg(not(feature = "enable-renderdoc"))] - let resources = DirectXResources::new(&devices).unwrap(); + let resources = + DirectXResources::new(&devices, self.resources.width, self.resources.height).unwrap(); #[cfg(feature = "enable-renderdoc")] - let resources = DirectXResources::new(&devices, self.hwnd)?; + let resources = DirectXResources::new( + &devices, + self.resources.width, + self.resources.height, + self.hwnd, + )?; let globals = DirectXGlobalElements::new(&devices.device).unwrap(); let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); @@ -233,6 +243,8 @@ impl DirectXRenderer { .set_swap_chain(&resources.swap_chain) .unwrap(); + self.atlas + .handle_device_lost(&devices.device, &devices.device_context); self.devices = devices; self.resources = resources; self.globals = globals; @@ -505,11 +517,10 @@ impl DirectXRenderer { impl DirectXResources { pub fn new( devices: &DirectXDevices, + width: u32, + height: u32, #[cfg(feature = "enable-renderdoc")] hwnd: HWND, - ) -> Result { - let width = 1; - let height = 1; - + ) -> Result> { #[cfg(not(feature = "enable-renderdoc"))] let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, width, height)?; #[cfg(feature = "enable-renderdoc")] @@ -520,7 +531,7 @@ impl DirectXResources { create_resources(devices, &swap_chain, width, height)?; set_rasterizer_state(&devices.device, &devices.device_context)?; - Ok(Self { + Ok(ManuallyDrop::new(Self { swap_chain, render_target, render_target_view, @@ -529,7 +540,7 @@ impl DirectXResources { width, height, viewport, - }) + })) } #[inline] @@ -978,10 +989,12 @@ struct PathSprite { color: Background, } -#[cfg(not(feature = "enable-renderdoc"))] impl Drop for DirectXRenderer { fn drop(&mut self) { unsafe { + ManuallyDrop::drop(&mut self.devices); + ManuallyDrop::drop(&mut self.resources); + #[cfg(not(feature = "enable-renderdoc"))] ManuallyDrop::drop(&mut self._direct_composition); } } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index c7ba58b3af..d86305bd30 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -34,7 +34,6 @@ pub(crate) struct WindowsPlatform { state: RefCell, raw_window_handles: RwLock>, // The below members will never change throughout the entire lifecycle of the app. - directx_devices: DirectXDevices, icon: HICON, main_receiver: flume::Receiver, background_executor: BackgroundExecutor, @@ -111,13 +110,11 @@ impl WindowsPlatform { let icon = load_icon().unwrap_or_default(); let state = RefCell::new(WindowsPlatformState::new()); let raw_window_handles = RwLock::new(SmallVec::new()); - 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, - directx_devices, icon, main_receiver, background_executor, @@ -455,13 +452,8 @@ impl Platform for WindowsPlatform { handle: AnyWindowHandle, options: WindowParams, ) -> Result> { - let window = WindowsWindow::new( - handle, - options, - self.generate_creation_info(), - &self.directx_devices, - ) - .inspect_err(|err| show_error("Failed to open new window", err.to_string()))?; + let window = WindowsWindow::new(handle, options, self.generate_creation_info()) + .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/window.rs b/crates/gpui/src/platform/windows/window.rs index eda75956b6..159cb6d95f 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -82,7 +82,6 @@ impl WindowsWindowState { cs: &CREATESTRUCTW, current_cursor: Option, display: WindowsDisplay, - gpu_context: &DirectXDevices, min_size: Option>, appearance: WindowAppearance, ) -> Result { @@ -101,7 +100,7 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; - let renderer = DirectXRenderer::new(gpu_context, hwnd)?; + let renderer = DirectXRenderer::new(hwnd)?; let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -207,7 +206,6 @@ impl WindowsWindowStatePtr { cs, context.current_cursor, context.display, - context.gpu_context, context.min_size, context.appearance, )?); @@ -326,7 +324,7 @@ pub(crate) struct Callbacks { pub(crate) appearance_changed: Option>, } -struct WindowCreateContext<'a> { +struct WindowCreateContext { inner: Option>>, handle: AnyWindowHandle, hide_title_bar: bool, @@ -339,7 +337,6 @@ struct WindowCreateContext<'a> { drop_target_helper: IDropTargetHelper, validation_number: usize, main_receiver: flume::Receiver, - gpu_context: &'a DirectXDevices, main_thread_id_win32: u32, appearance: WindowAppearance, } @@ -349,7 +346,6 @@ impl WindowsWindow { handle: AnyWindowHandle, params: WindowParams, creation_info: WindowCreationInfo, - gpu_context: &DirectXDevices, ) -> Result { let WindowCreationInfo { icon, @@ -408,7 +404,6 @@ impl WindowsWindow { drop_target_helper, validation_number, main_receiver, - gpu_context, main_thread_id_win32, appearance, }; From 787fee8a1a319bf5671282a4c8f445783f9482e9 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:08:55 +0800 Subject: [PATCH 145/202] fix --- crates/gpui/src/platform/windows/directx_atlas.rs | 14 +++++++------- .../gpui/src/platform/windows/directx_renderer.rs | 5 +++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index d915a93cfa..8ab4092efd 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -35,10 +35,10 @@ struct DirectXAtlasTexture { } impl DirectXAtlas { - pub(crate) fn new(device: ID3D11Device, device_context: ID3D11DeviceContext) -> Self { + pub(crate) fn new(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Self { DirectXAtlas(Mutex::new(DirectXAtlasState { - device, - device_context, + device: device.clone(), + device_context: device_context.clone(), monochrome_textures: Default::default(), polychrome_textures: Default::default(), tiles_by_key: Default::default(), @@ -56,12 +56,12 @@ impl DirectXAtlas { pub(crate) fn handle_device_lost( &self, - device: ID3D11Device, - device_context: ID3D11DeviceContext, + device: &ID3D11Device, + device_context: &ID3D11DeviceContext, ) { let mut lock = self.0.lock(); - lock.device = device; - lock.device_context = device_context; + lock.device = device.clone(); + lock.device_context = device_context.clone(); lock.monochrome_textures = AtlasTextureList::default(); lock.polychrome_textures = AtlasTextureList::default(); lock.tiles_by_key.clear(); diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 72c2cb5d42..8d8e4f3e3b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -253,6 +253,11 @@ impl DirectXRenderer { { self._direct_composition = direct_composition; } + unsafe { + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); + } Ok(()) } From 2711d8823cbb956e6f9a0c50ae9aab279da3e9b2 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:09:07 +0800 Subject: [PATCH 146/202] use vsync --- crates/gpui/src/platform/windows/platform.rs | 35 +++----------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index d86305bd30..a887c716d8 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -340,27 +340,11 @@ impl Platform for WindowsPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - let vsync_event = unsafe { Owned::new(CreateEventW(None, false, false, None).unwrap()) }; - begin_vsync(*vsync_event); - 'a: loop { - let wait_result = unsafe { - MsgWaitForMultipleObjects(Some(&[*vsync_event]), false, INFINITE, QS_ALLINPUT) - }; - - match wait_result { - // compositor clock ticked so we should draw a frame - WAIT_EVENT(0) => self.redraw_all(), - // Windows thread messages are posted - WAIT_EVENT(1) => { - if self.handle_events() { - break 'a; - } - } - _ => { - log::error!("Something went wrong while waiting {:?}", wait_result); - break; - } + loop { + if self.handle_events() { + break; } + self.redraw_all(); } if let Some(ref mut callback) = self.state.borrow_mut().callbacks.quit { @@ -839,17 +823,6 @@ fn file_save_dialog(directory: PathBuf, window: Option) -> Result Result { let module = unsafe { GetModuleHandleW(None).context("unable to get module handle")? }; let handle = unsafe { From 32f2505fbf079d75cc9c4478fb0f4dc56a1c471f Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:14:32 +0800 Subject: [PATCH 147/202] use `?` --- .../src/platform/windows/directx_renderer.rs | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 8d8e4f3e3b..0fbd053f22 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -120,19 +120,17 @@ impl DirectXRenderer { let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context)); #[cfg(not(feature = "enable-renderdoc"))] - let resources = DirectXResources::new(&devices, 1, 1).unwrap(); + let resources = DirectXResources::new(&devices, 1, 1)?; #[cfg(feature = "enable-renderdoc")] let resources = DirectXResources::new(&devices, hwnd)?; - let globals = DirectXGlobalElements::new(&devices.device).unwrap(); - let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); + 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).unwrap(); + let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; #[cfg(not(feature = "enable-renderdoc"))] - direct_composition - .set_swap_chain(&resources.swap_chain) - .unwrap(); + direct_composition.set_swap_chain(&resources.swap_chain)?; Ok(DirectXRenderer { hwnd, @@ -225,7 +223,7 @@ impl DirectXRenderer { } #[cfg(not(feature = "enable-renderdoc"))] let resources = - DirectXResources::new(&devices, self.resources.width, self.resources.height).unwrap(); + DirectXResources::new(&devices, self.resources.width, self.resources.height)?; #[cfg(feature = "enable-renderdoc")] let resources = DirectXResources::new( &devices, @@ -233,15 +231,13 @@ impl DirectXRenderer { self.resources.height, self.hwnd, )?; - let globals = DirectXGlobalElements::new(&devices.device).unwrap(); - let pipelines = DirectXRenderPipelines::new(&devices.device).unwrap(); + 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, self.hwnd).unwrap(); + let direct_composition = DirectComposition::new(&devices.dxgi_device, self.hwnd)?; #[cfg(not(feature = "enable-renderdoc"))] - direct_composition - .set_swap_chain(&resources.swap_chain) - .unwrap(); + direct_composition.set_swap_chain(&resources.swap_chain)?; self.atlas .handle_device_lost(&devices.device, &devices.device_context); @@ -603,9 +599,9 @@ 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).unwrap(); - let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }.unwrap(); - let comp_visual = unsafe { comp_device.CreateVisual() }.unwrap(); + 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(ManuallyDrop::new(Self { comp_device, From 9dbbee0334312c942f7f4c2f7f3219b5cfdb8ac6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:18:11 +0800 Subject: [PATCH 148/202] Revert "Add emojis to text example" This reverts commit 34d5926ebd37e58897390734a9224635a823b954. --- crates/gpui/examples/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 1166bb2795..19214aebde 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid { "χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ", - "❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎", + "❮", "<=", "!=", "==", "--", "++", "=>", "->", ]; let columns = 11; From 9d8ef8156d6d0d580260428169240dd4d09e4eb3 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:18:15 +0800 Subject: [PATCH 149/202] Revert "Translate rasterized glyphs from texture to bitmap" This reverts commit 6fc8d7746f4918881514301c6937a93d4ccd747c. --- crates/gpui/examples/text.rs | 36 +++--- .../gpui/src/platform/windows/direct_write.rs | 116 +++++++----------- 2 files changed, 65 insertions(+), 87 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 19214aebde..6f1b98e81c 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - .child(div().child(CharacterGrid::new().scale(base))) + // .child(div().child(CharacterGrid::new().scale(base))) .child( div() - .child(Specimen::new(self.next_id()).scale(step_down_2)) - .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_down_1)) - .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_2)) + // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_down_1)) + // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - .child(Specimen::new(self.next_id()).scale(base).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_1)) - .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_2)) - .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_3)) - .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_4)) - .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_5)) - .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - .child(Specimen::new(self.next_id()).scale(step_up_6)) - .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + // .child(Specimen::new(self.next_id()).scale(base).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_1)) + // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_2)) + // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_3)) + // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_4)) + // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_5)) + // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + // .child(Specimen::new(self.next_id()).scale(step_up_6)) + // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index bd7bacd4fc..8d191facbe 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -786,58 +786,50 @@ impl DirectWriteState { )? }; - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - let texture_width = (texture_bounds.right - texture_bounds.left) as u32; - let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if texture_width == 0 || texture_height == 0 { - return Ok(( - bitmap_size, - vec![ - 0u8; - bitmap_size.width.0 as usize - * bitmap_size.height.0 as usize - * if params.is_emoji { 4 } else { 1 } - ], - )); - } - - let mut bitmap_data; if params.is_emoji { - bitmap_data = - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4]; + // For emoji, we need to handle color glyphs differently + // This is a simplified approach - in a full implementation you'd want to + // properly handle color glyph runs using TranslateColorGlyphRun + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], + )); + } + + let mut rgba_data = vec![0u8; (width * height * 4) as usize]; - let mut rgba_data = vec![0u8; (texture_width * texture_height * 4) as usize]; unsafe { glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; } - // Copy texture data into bitmap at correct position - let offset_x = texture_bounds.left.max(0) as usize; - let offset_y = texture_bounds.top.max(0) as usize; - for y in 0..texture_height as usize { - for x in 0..texture_width as usize { - let bitmap_x = offset_x + x; - let bitmap_y = offset_y + y; + // Resize to match expected bitmap_size if needed - if bitmap_x < bitmap_size.width.0 as usize - && bitmap_y < bitmap_size.height.0 as usize - { - let texture_idx = (y * texture_width as usize + x) * 4; - let bitmap_idx = (bitmap_y * bitmap_size.width.0 as usize + bitmap_x) * 4; - - if texture_idx + 3 < rgba_data.len() && bitmap_idx + 3 < bitmap_data.len() { - bitmap_data[bitmap_idx..bitmap_idx + 4] - .copy_from_slice(&rgba_data[texture_idx..texture_idx + 4]); - } - } - } - } + Ok((bitmap_size, rgba_data)) } else { - bitmap_data = vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; + // For regular text, use grayscale or cleartype + let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); + + let width = (texture_bounds.right - texture_bounds.left) as u32; + let height = (texture_bounds.bottom - texture_bounds.top) as u32; + + if width == 0 || height == 0 { + return Ok(( + bitmap_size, + vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], + )); + } + + let mut alpha_data = vec![0u8; (width * height * 3) as usize]; - let mut alpha_data = vec![0u8; (texture_width * texture_height * 3) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( texture_type, @@ -846,34 +838,20 @@ impl DirectWriteState { )?; } - // Convert ClearType RGB data to grayscale and place in bitmap - let offset_x = texture_bounds.left.max(0) as usize; - let offset_y = texture_bounds.top.max(0) as usize; - - for y in 0..texture_height as usize { - for x in 0..texture_width as usize { - let bitmap_x = offset_x + x; - let bitmap_y = offset_y + y; - - if bitmap_x < bitmap_size.width.0 as usize - && bitmap_y < bitmap_size.height.0 as usize - { - let texture_idx = (y * texture_width as usize + x) * 3; - let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; - - if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { - let avg = (alpha_data[texture_idx] as u32 - + alpha_data[texture_idx + 1] as u32 - + alpha_data[texture_idx + 2] as u32) - / 3; - bitmap_data[bitmap_idx] = avg as u8; - } - } - } + // For cleartype, we need to convert the 3x1 subpixel data to grayscale + // This is a simplified conversion - you might want to do proper subpixel rendering + let mut grayscale_data = Vec::new(); + for chunk in alpha_data.chunks_exact(3) { + let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; + grayscale_data.push(avg as u8); } - } - Ok((bitmap_size, bitmap_data)) + // Resize to match expected bitmap_size if needed + let expected_size = width as usize * height as usize; + grayscale_data.resize(expected_size, 0); + + Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) + } } fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { From 347b863ac63a706140187c898cd6bdce9b6acf2c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:18:17 +0800 Subject: [PATCH 150/202] Revert "more fixes and debugging" This reverts commit 2fb31a9157844a868c77d12f15d7481d1a9bb048. --- crates/gpui/examples/text.rs | 36 +++++++++---------- .../gpui/src/platform/windows/direct_write.rs | 7 ++-- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 6f1b98e81c..19214aebde 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -270,27 +270,27 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - // .child(div().child(CharacterGrid::new().scale(base))) + .child(div().child(CharacterGrid::new().scale(base))) .child( div() - // .child(Specimen::new(self.next_id()).scale(step_down_2)) - // .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_down_1)) - // .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_2)) + .child(Specimen::new(self.next_id()).scale(step_down_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_down_1)) + .child(Specimen::new(self.next_id()).scale(step_down_1).invert()) .child(Specimen::new(self.next_id()).scale(base)) - // .child(Specimen::new(self.next_id()).scale(base).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_1)) - // .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_2)) - // .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_3)) - // .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_4)) - // .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_5)) - // .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) - // .child(Specimen::new(self.next_id()).scale(step_up_6)) - // .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), + .child(Specimen::new(self.next_id()).scale(base).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_1)) + .child(Specimen::new(self.next_id()).scale(step_up_1).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_2)) + .child(Specimen::new(self.next_id()).scale(step_up_2).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_3)) + .child(Specimen::new(self.next_id()).scale(step_up_3).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_4)) + .child(Specimen::new(self.next_id()).scale(step_up_4).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_5)) + .child(Specimen::new(self.next_id()).scale(step_up_5).invert()) + .child(Specimen::new(self.next_id()).scale(step_up_6)) + .child(Specimen::new(self.next_id()).scale(step_up_6).invert()), ), ) .child(div().w(px(240.)).h_full().bg(colors.container)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 8d191facbe..4ee94e6155 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -810,13 +810,14 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; + rgba_data.resize(expected_size, 0); Ok((bitmap_size, rgba_data)) } else { // For regular text, use grayscale or cleartype let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - println!("glyph id: {:?}, variant: {:?}, size: {:?}, texture_bounds: {:?}", glyph_id, params.subpixel_variant, bitmap_size, texture_bounds); let width = (texture_bounds.right - texture_bounds.left) as u32; let height = (texture_bounds.bottom - texture_bounds.top) as u32; @@ -847,10 +848,10 @@ impl DirectWriteState { } // Resize to match expected bitmap_size if needed - let expected_size = width as usize * height as usize; + let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; grayscale_data.resize(expected_size, 0); - Ok((size(DevicePixels(width as i32), DevicePixels(height as i32)), grayscale_data)) + Ok((bitmap_size, grayscale_data)) } } From 18937f57561d5ead156787c3cba8ac3470186746 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:18:18 +0800 Subject: [PATCH 151/202] Revert "make it not crash" This reverts commit a7e34ab0bc1540f7110fdf1ffa7d62a8fcd08aff. --- crates/gpui/src/platform/windows/direct_write.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 4ee94e6155..3fc143fdcb 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -634,7 +634,7 @@ impl DirectWriteState { let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, + fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -644,6 +644,7 @@ impl DirectWriteState { bidiLevel: 0, }; + let transform = DWRITE_MATRIX::default(); let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; let baseline_origin_x = 0.0; @@ -652,7 +653,7 @@ impl DirectWriteState { let glyph_analysis = unsafe { self.components.factory.CreateGlyphRunAnalysis( &glyph_run, - None, + Some(&transform as *const _), rendering_mode, measuring_mode, DWRITE_GRID_FIT_MODE_DEFAULT, @@ -829,7 +830,7 @@ impl DirectWriteState { )); } - let mut alpha_data = vec![0u8; (width * height * 3) as usize]; + let mut alpha_data = vec![0u8; (width * height) as usize]; unsafe { glyph_analysis.CreateAlphaTexture( From 2744e6cb6530d0b38bcc7fdc0d64defc21e52c6a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:18:19 +0800 Subject: [PATCH 152/202] Revert "initial removal attempt" This reverts commit 6928488aad1617a5a7c1c2de2391c1a92468e8f8. --- .../gpui/src/platform/windows/direct_write.rs | 332 +++++++++++------- 1 file changed, 205 insertions(+), 127 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 3fc143fdcb..ada306c15c 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc}; +use std::{borrow::Cow, sync::Arc}; use ::util::ResultExt; use anyhow::Result; @@ -9,7 +9,13 @@ use windows::{ Win32::{ Foundation::*, Globalization::GetUserDefaultLocaleName, - Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*}, + Graphics::{ + Direct2D::{Common::*, *}, + DirectWrite::*, + Dxgi::Common::*, + Gdi::LOGFONTW, + Imaging::*, + }, System::SystemServices::LOCALE_NAME_MAX_LENGTH, UI::WindowsAndMessaging::*, }, @@ -34,6 +40,7 @@ struct DirectWriteComponent { locale: String, factory: IDWriteFactory5, bitmap_factory: AgileReference, + d2d1_factory: ID2D1Factory, in_memory_loader: IDWriteInMemoryFontFileLoader, builder: IDWriteFontSetBuilder1, text_renderer: Arc, @@ -42,6 +49,7 @@ struct DirectWriteComponent { struct GlyphRenderContext { params: IDWriteRenderingParams3, + dc_target: ID2D1DeviceContext4, } struct DirectWriteState { @@ -66,6 +74,8 @@ impl DirectWriteComponent { unsafe { let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let bitmap_factory = AgileReference::new(bitmap_factory)?; + let d2d1_factory: ID2D1Factory = + D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?; // The `IDWriteInMemoryFontFileLoader` here is supported starting from // Windows 10 Creators Update, which consequently requires the entire // `DirectWriteTextSystem` to run on `win10 1703`+. @@ -76,12 +86,13 @@ impl DirectWriteComponent { GetUserDefaultLocaleName(&mut locale_vec); let locale = String::from_utf16_lossy(&locale_vec); let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); - let render_context = GlyphRenderContext::new(&factory)?; + let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?; Ok(DirectWriteComponent { locale, factory, bitmap_factory, + d2d1_factory, in_memory_loader, builder, text_renderer, @@ -92,7 +103,7 @@ impl DirectWriteComponent { } impl GlyphRenderContext { - pub fn new(factory: &IDWriteFactory5) -> Result { + pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result { unsafe { let default_params: IDWriteRenderingParams3 = factory.CreateRenderingParams()?.cast()?; @@ -111,8 +122,17 @@ impl GlyphRenderContext { DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, grid_fit_mode, )?; + let dc_target = { + let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED, + ))?; + let target = target.cast::()?; + target.SetTextRenderingParams(¶ms); + target + }; - Ok(Self { params }) + Ok(Self { params, dc_target }) } } } @@ -629,12 +649,17 @@ impl DirectWriteState { } fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { + let render_target = &self.components.render_context.dc_target; + unsafe { + render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); + render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor); + } let font = &self.fonts[params.font_id.0]; let glyph_id = [params.glyph_id.0 as u16]; let advance = [0.0f32]; let offset = [DWRITE_GLYPH_OFFSET::default()]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)), + fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -643,29 +668,13 @@ impl DirectWriteState { isSideways: BOOL(0), bidiLevel: 0, }; - - let transform = DWRITE_MATRIX::default(); - let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; - let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - let baseline_origin_x = 0.0; - let baseline_origin_y = 0.0; - - let glyph_analysis = unsafe { - self.components.factory.CreateGlyphRunAnalysis( + let bounds = unsafe { + render_target.GetGlyphRunWorldBounds( + Vector2 { X: 0.0, Y: 0.0 }, &glyph_run, - Some(&transform as *const _), - rendering_mode, - measuring_mode, - DWRITE_GRID_FIT_MODE_DEFAULT, - DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, - baseline_origin_x, - baseline_origin_y, + DWRITE_MEASURING_MODE_NATURAL, )? }; - - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - // todo(windows) // This is a walkaround, deleted when figured out. let y_offset; @@ -687,13 +696,12 @@ impl DirectWriteState { } else { Ok(Bounds { origin: point( - ((bounds.left as f32 * params.scale_factor).ceil() as i32).into(), - ((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(), + ((bounds.left * params.scale_factor).ceil() as i32).into(), + ((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(), ), size: size( - (((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32) - .into(), - (((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32 + (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(), + (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32 + extra_height) .into(), ), @@ -731,7 +739,7 @@ impl DirectWriteState { ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, }]; let glyph_run = DWRITE_GLYPH_RUN { - fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)), + fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) }, fontEmSize: params.font_size.0, glyphCount: 1, glyphIndices: glyph_id.as_ptr(), @@ -751,108 +759,149 @@ impl DirectWriteState { } let bitmap_size = bitmap_size; - let subpixel_shift = params - .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); - let baseline_origin_x = subpixel_shift.x / params.scale_factor; - let baseline_origin_y = subpixel_shift.y / params.scale_factor; - - let transform = DWRITE_MATRIX { - m11: params.scale_factor, - m12: 0.0, - m21: 0.0, - m22: params.scale_factor, - dx: 0.0, - dy: 0.0, - }; - - let rendering_mode = if params.is_emoji { - DWRITE_RENDERING_MODE1_NATURAL - } else { - DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC - }; - - let measuring_mode = DWRITE_MEASURING_MODE_NATURAL; - - let glyph_analysis = unsafe { - self.components.factory.CreateGlyphRunAnalysis( - &glyph_run, - Some(&transform), - rendering_mode, - measuring_mode, - DWRITE_GRID_FIT_MODE_DEFAULT, - DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE, - baseline_origin_x, - baseline_origin_y, - )? - }; - + let total_bytes; + let bitmap_format; + let render_target_property; + let bitmap_width; + let bitmap_height; + let bitmap_stride; + let bitmap_dpi; if params.is_emoji { - // For emoji, we need to handle color glyphs differently - // This is a simplified approach - in a full implementation you'd want to - // properly handle color glyph runs using TranslateColorGlyphRun - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; - - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; - - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4], - )); - } - - let mut rgba_data = vec![0u8; (width * height * 4) as usize]; - - unsafe { - glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?; - } - - // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4; - rgba_data.resize(expected_size, 0); - - Ok((bitmap_size, rgba_data)) + total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4; + bitmap_format = &GUID_WICPixelFormat32bppPBGRA; + render_target_property = get_render_target_property( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED, + ); + bitmap_width = bitmap_size.width.0 as u32; + bitmap_height = bitmap_size.height.0 as u32; + bitmap_stride = bitmap_size.width.0 as u32 * 4; + bitmap_dpi = 96.0; } else { - // For regular text, use grayscale or cleartype - let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; - let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? }; + total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize; + bitmap_format = &GUID_WICPixelFormat8bppAlpha; + render_target_property = + get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT); + bitmap_width = bitmap_size.width.0 as u32 * 2; + bitmap_height = bitmap_size.height.0 as u32 * 2; + bitmap_stride = bitmap_size.width.0 as u32; + bitmap_dpi = 192.0; + } - let width = (texture_bounds.right - texture_bounds.left) as u32; - let height = (texture_bounds.bottom - texture_bounds.top) as u32; + let bitmap_factory = self.components.bitmap_factory.resolve()?; + unsafe { + let bitmap = bitmap_factory.CreateBitmap( + bitmap_width, + bitmap_height, + bitmap_format, + WICBitmapCacheOnLoad, + )?; + let render_target = self + .components + .d2d1_factory + .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?; + let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?; + let subpixel_shift = params + .subpixel_variant + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); + let baseline_origin = Vector2 { + X: subpixel_shift.x / params.scale_factor, + Y: subpixel_shift.y / params.scale_factor, + }; - if width == 0 || height == 0 { - return Ok(( - bitmap_size, - vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize], - )); - } + // This `cast()` action here should never fail since we are running on Win10+, and + // ID2D1DeviceContext4 requires Win8+ + let render_target = render_target.cast::().unwrap(); + render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); + render_target.SetDpi( + bitmap_dpi * params.scale_factor, + bitmap_dpi * params.scale_factor, + ); + render_target.SetTextRenderingParams(&self.components.render_context.params); + render_target.BeginDraw(); - let mut alpha_data = vec![0u8; (width * height) as usize]; - - unsafe { - glyph_analysis.CreateAlphaTexture( - texture_type, - &texture_bounds, - &mut alpha_data, + if params.is_emoji { + // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested + let enumerator = self.components.factory.TranslateColorGlyphRun( + baseline_origin, + &glyph_run as _, + None, + DWRITE_GLYPH_IMAGE_FORMATS_COLR + | DWRITE_GLYPH_IMAGE_FORMATS_SVG + | DWRITE_GLYPH_IMAGE_FORMATS_PNG + | DWRITE_GLYPH_IMAGE_FORMATS_JPEG + | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, + DWRITE_MEASURING_MODE_NATURAL, + None, + 0, )?; + while enumerator.MoveNext().is_ok() { + let Ok(color_glyph) = enumerator.GetCurrentRun() else { + break; + }; + let color_glyph = &*color_glyph; + let brush_color = translate_color(&color_glyph.Base.runColor); + brush.SetColor(&brush_color); + match color_glyph.glyphImageFormat { + DWRITE_GLYPH_IMAGE_FORMATS_PNG + | DWRITE_GLYPH_IMAGE_FORMATS_JPEG + | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target + .DrawColorBitmapGlyphRun( + color_glyph.glyphImageFormat, + baseline_origin, + &color_glyph.Base.glyphRun, + color_glyph.measuringMode, + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT, + ), + DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun( + baseline_origin, + &color_glyph.Base.glyphRun, + &brush, + None, + color_glyph.Base.paletteIndex as u32, + color_glyph.measuringMode, + ), + _ => render_target.DrawGlyphRun( + baseline_origin, + &color_glyph.Base.glyphRun, + Some(color_glyph.Base.glyphRunDescription as *const _), + &brush, + color_glyph.measuringMode, + ), + } + } + } else { + render_target.DrawGlyphRun( + baseline_origin, + &glyph_run, + None, + &brush, + DWRITE_MEASURING_MODE_NATURAL, + ); } + render_target.EndDraw(None, None)?; - // For cleartype, we need to convert the 3x1 subpixel data to grayscale - // This is a simplified conversion - you might want to do proper subpixel rendering - let mut grayscale_data = Vec::new(); - for chunk in alpha_data.chunks_exact(3) { - let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3; - grayscale_data.push(avg as u8); + let mut raw_data = vec![0u8; total_bytes]; + if params.is_emoji { + bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; + // Convert from BGRA with premultiplied alpha to BGRA with straight alpha. + for pixel in raw_data.chunks_exact_mut(4) { + let a = pixel[3] as f32 / 255.; + pixel[0] = (pixel[0] as f32 / a) as u8; + pixel[1] = (pixel[1] as f32 / a) as u8; + pixel[2] = (pixel[2] as f32 / a) as u8; + } + } else { + let scaler = bitmap_factory.CreateBitmapScaler()?; + scaler.Initialize( + &bitmap, + bitmap_size.width.0 as u32, + bitmap_size.height.0 as u32, + WICBitmapInterpolationModeHighQualityCubic, + )?; + scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?; } - - // Resize to match expected bitmap_size if needed - let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize; - grayscale_data.resize(expected_size, 0); - - Ok((bitmap_size, grayscale_data)) + Ok((bitmap_size, raw_data)) } } @@ -1422,8 +1471,13 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result { } #[inline] -fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] { - [color.r, color.g, color.b, color.a] +fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F { + D2D1_COLOR_F { + r: color.r, + g: color.g, + b: color.b, + a: color.a, + } } fn get_system_ui_font_name() -> SharedString { @@ -1450,6 +1504,24 @@ fn get_system_ui_font_name() -> SharedString { } } +#[inline] +fn get_render_target_property( + pixel_format: DXGI_FORMAT, + alpha_mode: D2D1_ALPHA_MODE, +) -> D2D1_RENDER_TARGET_PROPERTIES { + D2D1_RENDER_TARGET_PROPERTIES { + r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT, + pixelFormat: D2D1_PIXEL_FORMAT { + format: pixel_format, + alphaMode: alpha_mode, + }, + dpiX: 96.0, + dpiY: 96.0, + usage: D2D1_RENDER_TARGET_USAGE_NONE, + minLevel: D2D1_FEATURE_LEVEL_DEFAULT, + } +} + // One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats // but that doesn't seem to work for some glyphs, say ❤ fn is_color_glyph( @@ -1489,6 +1561,12 @@ fn is_color_glyph( } const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); +const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F { + r: 1.0, + g: 1.0, + b: 1.0, + a: 1.0, +}; #[cfg(test)] mod tests { From b54eaecbbcd0eb3e0efcaf54363ddf7b9d95b189 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:37:19 +0800 Subject: [PATCH 153/202] clippy --- crates/gpui/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 36186c87ed..dfe7aa09df 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -447,7 +447,6 @@ mod windows { const_definition.replace('{', "[").replace('}', "]") ); let mut options = fs::OpenOptions::new() - .write(true) .create(true) .append(true) .open(output_path) From c67ddd7572db12e31f8bd6680e06ee330fa4d9e8 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:42:36 +0800 Subject: [PATCH 154/202] `AGS` is not typo --- typos.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/typos.toml b/typos.toml index 7f1c6e04f1..94d3a8ffbc 100644 --- a/typos.toml +++ b/typos.toml @@ -71,6 +71,8 @@ extend-ignore-re = [ # Not an actual typo but an intentionally invalid color, in `color_extractor` "#fof", # Stripped version of reserved keyword `type` - "typ" + "typ", + # AMD GPU Services + "ags" ] check-filename = true From d192ac6b7f40d28c9b9cdbb28014d7ae5f7c330a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:45:51 +0800 Subject: [PATCH 155/202] `ags` is not typo --- typos.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/typos.toml b/typos.toml index 94d3a8ffbc..336a829a44 100644 --- a/typos.toml +++ b/typos.toml @@ -73,6 +73,8 @@ extend-ignore-re = [ # Stripped version of reserved keyword `type` "typ", # AMD GPU Services - "ags" + "ags", + # AMD GPU Services + "AGS" ] check-filename = true From 3277640f554514aad53e2c55a1584b819c1cedff Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 22 Jul 2025 22:50:49 +0800 Subject: [PATCH 156/202] fix --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 0fbd053f22..ba7333acd8 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -122,7 +122,7 @@ impl DirectXRenderer { #[cfg(not(feature = "enable-renderdoc"))] let resources = DirectXResources::new(&devices, 1, 1)?; #[cfg(feature = "enable-renderdoc")] - let resources = DirectXResources::new(&devices, hwnd)?; + let resources = DirectXResources::new(&devices, 1, 1, hwnd)?; let globals = DirectXGlobalElements::new(&devices.device)?; let pipelines = DirectXRenderPipelines::new(&devices.device)?; From 4de2ebf95437e36f127ce01237bb122ee5998bbc Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 23 Jul 2025 17:16:42 +0800 Subject: [PATCH 157/202] acctually enable vsync --- crates/gpui/src/platform/windows/directx_renderer.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ba7333acd8..b12ecebb5e 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -191,7 +191,7 @@ impl DirectXRenderer { self.devices .device_context .OMSetRenderTargets(Some(&self.resources.render_target_view), None); - let result = self.resources.swap_chain.Present(0, DXGI_PRESENT(0)); + let result = self.resources.swap_chain.Present(1, DXGI_PRESENT(0)); // Presenting the swap chain can fail if the DirectX device was removed or reset. if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET { let reason = self.devices.device.GetDeviceRemovedReason(); @@ -506,6 +506,14 @@ impl DirectXRenderer { .context("Failed to get gpu driver info") .log_err() .unwrap_or("Unknown Driver".to_string()); + match unsafe { + self.devices + .device + .CheckMultisampleQualityLevels(RENDER_TARGET_FORMAT, MULTISAMPLE_COUNT) + } { + Ok(level) => println!("=> Multisample quality levels: {}", level), + Err(err) => println!("Failed to check multisample quality levels: {:?}", err), + } Ok(GpuSpecs { is_software_emulated, device_name, From 63daf446931ea97e8e56273f33187f1e5eee1220 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 23 Jul 2025 20:50:51 +0800 Subject: [PATCH 158/202] remove debug print --- crates/gpui/src/platform/windows/directx_renderer.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index b12ecebb5e..ab75e66bae 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -506,14 +506,6 @@ impl DirectXRenderer { .context("Failed to get gpu driver info") .log_err() .unwrap_or("Unknown Driver".to_string()); - match unsafe { - self.devices - .device - .CheckMultisampleQualityLevels(RENDER_TARGET_FORMAT, MULTISAMPLE_COUNT) - } { - Ok(level) => println!("=> Multisample quality levels: {}", level), - Err(err) => println!("Failed to check multisample quality levels: {:?}", err), - } Ok(GpuSpecs { is_software_emulated, device_name, From 6964cecc149e14eb9f000ca66412b1b9af09ca81 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 23 Jul 2025 23:28:05 +0800 Subject: [PATCH 159/202] ensure app is idle --- crates/gpui/src/platform.rs | 2 +- crates/gpui/src/platform/windows/events.rs | 17 ++++++++++++++--- crates/gpui/src/platform/windows/window.rs | 6 +++--- crates/gpui/src/window.rs | 6 +++++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index f0b9be68bb..633a62fb44 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -479,7 +479,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn zoom(&self); fn toggle_fullscreen(&self); fn is_fullscreen(&self) -> bool; - fn on_request_frame(&self, callback: Box); + fn on_request_frame(&self, callback: Box bool>); fn on_input(&self, callback: Box DispatchEventResult>); fn on_active_status_change(&self, callback: Box); fn on_hover_status_change(&self, callback: Box); diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 6fd899cbef..ebacf8e965 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -237,12 +237,23 @@ fn handle_timer_msg( fn handle_paint_msg(handle: HWND, state_ptr: Rc) -> Option { let mut lock = state_ptr.state.borrow_mut(); - if let Some(mut request_frame) = lock.callbacks.request_frame.take() { + let is_idle = if let Some(mut request_frame) = lock.callbacks.request_frame.take() { drop(lock); - request_frame(Default::default()); + // request_frame(RequestFrameOptions { + // require_presentation: true, + // }); + let is_idle = request_frame(Default::default()); state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); - } + is_idle + } else { + false + }; unsafe { ValidateRect(Some(handle), None).ok().log_err() }; + if is_idle { + unsafe { + MsgWaitForMultipleObjects(None, false, 100, QS_ALLINPUT); + } + } Some(0) } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 159cb6d95f..8e073a0a69 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -38,7 +38,7 @@ pub struct WindowsWindowState { pub border_offset: WindowBorderOffset, pub appearance: WindowAppearance, pub scale_factor: f32, - pub restore_from_minimized: Option>, + pub restore_from_minimized: Option bool>>, pub callbacks: Callbacks, pub input_handler: Option, @@ -312,7 +312,7 @@ impl WindowsWindowStatePtr { #[derive(Default)] pub(crate) struct Callbacks { - pub(crate) request_frame: Option>, + pub(crate) request_frame: Option bool>>, pub(crate) input: Option DispatchEventResult>>, pub(crate) active_status_change: Option>, pub(crate) hovered_status_change: Option>, @@ -734,7 +734,7 @@ impl PlatformWindow for WindowsWindow { self.0.state.borrow().is_fullscreen() } - fn on_request_frame(&self, callback: Box) { + fn on_request_frame(&self, callback: Box bool>) { self.0.state.borrow_mut().callbacks.request_frame = Some(callback); } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 963d2bb45c..af873b12d8 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1012,6 +1012,7 @@ impl Window { .log_err(); } + let invalidator_is_dirty = invalidator.is_dirty(); // Keep presenting the current scene for 1 extra second since the // last input to prevent the display from underclocking the refresh rate. let needs_present = request_frame_options.require_presentation @@ -1019,7 +1020,7 @@ impl Window { || (active.get() && last_input_timestamp.get().elapsed() < Duration::from_secs(1)); - if invalidator.is_dirty() { + if invalidator_is_dirty { measure("frame duration", || { handle .update(&mut cx, |_, window, cx| { @@ -1041,6 +1042,9 @@ impl Window { window.complete_frame(); }) .log_err(); + + // Return true if app is idle + !(invalidator_is_dirty || needs_present) } })); platform_window.on_resize(Box::new({ From ca6aa25d1ee3da7b80b9b2d2dd1953f564dfb32d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 14:59:30 +0800 Subject: [PATCH 160/202] remove walkaround for close animation --- crates/gpui/src/platform/windows/events.rs | 31 +++++----------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index ebacf8e965..1949f40ff1 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -48,7 +48,7 @@ pub(crate) fn handle_msg( WM_DISPLAYCHANGE => handle_display_change_msg(handle, state_ptr), WM_NCHITTEST => handle_hit_test_msg(handle, msg, wparam, lparam, state_ptr), WM_PAINT => handle_paint_msg(handle, state_ptr), - WM_CLOSE => handle_close_msg(handle, state_ptr), + WM_CLOSE => handle_close_msg(state_ptr), WM_DESTROY => handle_destroy_msg(handle, state_ptr), WM_MOUSEMOVE => handle_mouse_move_msg(handle, lparam, wparam, state_ptr), WM_MOUSELEAVE | WM_NCMOUSELEAVE => handle_mouse_leave_msg(state_ptr), @@ -257,30 +257,11 @@ fn handle_paint_msg(handle: HWND, state_ptr: Rc) -> Optio Some(0) } -fn handle_close_msg(handle: HWND, state_ptr: Rc) -> Option { - let mut lock = state_ptr.state.borrow_mut(); - let output = if let Some(mut callback) = lock.callbacks.should_close.take() { - drop(lock); - let should_close = callback(); - state_ptr.state.borrow_mut().callbacks.should_close = Some(callback); - if should_close { None } else { Some(0) } - } else { - None - }; - - // Workaround as window close animation is not played with `WS_EX_LAYERED` enabled. - if output.is_none() { - unsafe { - let current_style = get_window_long(handle, GWL_EXSTYLE); - set_window_long( - handle, - GWL_EXSTYLE, - current_style & !WS_EX_LAYERED.0 as isize, - ); - } - } - - output +fn handle_close_msg(state_ptr: Rc) -> Option { + let mut callback = state_ptr.state.borrow_mut().callbacks.should_close.take()?; + let should_close = callback(); + state_ptr.state.borrow_mut().callbacks.should_close = Some(callback); + if should_close { None } else { Some(0) } } fn handle_destroy_msg(handle: HWND, state_ptr: Rc) -> Option { From 0065e5fd764f11981c9da43da619e9c732a9f796 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 17:04:28 +0800 Subject: [PATCH 161/202] handle `WM_DEVICECHANGE` --- crates/gpui/src/platform/windows/events.rs | 54 ++++++++++++++-------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 1949f40ff1..4b905302af 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -37,6 +37,7 @@ pub(crate) fn handle_msg( let handled = match msg { WM_ACTIVATE => handle_activate_msg(wparam, state_ptr), WM_CREATE => handle_create_msg(handle, state_ptr), + WM_DEVICECHANGE => handle_device_change_msg(handle, wparam, state_ptr), WM_MOVE => handle_move_msg(handle, lparam, state_ptr), WM_SIZE => handle_size_msg(wparam, lparam, state_ptr), WM_GETMINMAXINFO => handle_get_min_max_info_msg(lparam, state_ptr), @@ -236,25 +237,7 @@ fn handle_timer_msg( } fn handle_paint_msg(handle: HWND, state_ptr: Rc) -> Option { - let mut lock = state_ptr.state.borrow_mut(); - let is_idle = if let Some(mut request_frame) = lock.callbacks.request_frame.take() { - drop(lock); - // request_frame(RequestFrameOptions { - // require_presentation: true, - // }); - let is_idle = request_frame(Default::default()); - state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); - is_idle - } else { - false - }; - unsafe { ValidateRect(Some(handle), None).ok().log_err() }; - if is_idle { - unsafe { - MsgWaitForMultipleObjects(None, false, 100, QS_ALLINPUT); - } - } - Some(0) + draw_window(handle, false, state_ptr) } fn handle_close_msg(state_ptr: Rc) -> Option { @@ -1213,6 +1196,39 @@ fn handle_input_language_changed( Some(0) } +fn handle_device_change_msg( + handle: HWND, + wparam: WPARAM, + state_ptr: Rc, +) -> Option { + if wparam.0 == DBT_DEVNODES_CHANGED as usize { + draw_window(handle, true, state_ptr) + } else { + // Other device change messages are not handled. + None + } +} + +#[inline] +fn draw_window( + handle: HWND, + force_draw: bool, + state_ptr: Rc, +) -> Option { + let mut request_frame = state_ptr + .state + .borrow_mut() + .callbacks + .request_frame + .take()?; + request_frame(RequestFrameOptions { + require_presentation: force_draw, + }); + state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); + unsafe { ValidateRect(Some(handle), None).ok().log_err() }; + Some(0) +} + #[inline] fn parse_char_message(wparam: WPARAM, state_ptr: &Rc) -> Option { let code_point = wparam.loword(); From ac1ea0f96dbebe3fffa6d17d1ea8fbe8a9497ee8 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 17:10:17 +0800 Subject: [PATCH 162/202] revert idle --- crates/gpui/src/platform.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 6 +++--- crates/gpui/src/window.rs | 6 +----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 633a62fb44..f0b9be68bb 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -479,7 +479,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn zoom(&self); fn toggle_fullscreen(&self); fn is_fullscreen(&self) -> bool; - fn on_request_frame(&self, callback: Box bool>); + fn on_request_frame(&self, callback: Box); fn on_input(&self, callback: Box DispatchEventResult>); fn on_active_status_change(&self, callback: Box); fn on_hover_status_change(&self, callback: Box); diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 8e073a0a69..159cb6d95f 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -38,7 +38,7 @@ pub struct WindowsWindowState { pub border_offset: WindowBorderOffset, pub appearance: WindowAppearance, pub scale_factor: f32, - pub restore_from_minimized: Option bool>>, + pub restore_from_minimized: Option>, pub callbacks: Callbacks, pub input_handler: Option, @@ -312,7 +312,7 @@ impl WindowsWindowStatePtr { #[derive(Default)] pub(crate) struct Callbacks { - pub(crate) request_frame: Option bool>>, + pub(crate) request_frame: Option>, pub(crate) input: Option DispatchEventResult>>, pub(crate) active_status_change: Option>, pub(crate) hovered_status_change: Option>, @@ -734,7 +734,7 @@ impl PlatformWindow for WindowsWindow { self.0.state.borrow().is_fullscreen() } - fn on_request_frame(&self, callback: Box bool>) { + fn on_request_frame(&self, callback: Box) { self.0.state.borrow_mut().callbacks.request_frame = Some(callback); } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index af873b12d8..963d2bb45c 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1012,7 +1012,6 @@ impl Window { .log_err(); } - let invalidator_is_dirty = invalidator.is_dirty(); // Keep presenting the current scene for 1 extra second since the // last input to prevent the display from underclocking the refresh rate. let needs_present = request_frame_options.require_presentation @@ -1020,7 +1019,7 @@ impl Window { || (active.get() && last_input_timestamp.get().elapsed() < Duration::from_secs(1)); - if invalidator_is_dirty { + if invalidator.is_dirty() { measure("frame duration", || { handle .update(&mut cx, |_, window, cx| { @@ -1042,9 +1041,6 @@ impl Window { window.complete_frame(); }) .log_err(); - - // Return true if app is idle - !(invalidator_is_dirty || needs_present) } })); platform_window.on_resize(Box::new({ From a9058346bfff7e39dd71df67bd8d293e4f6f2552 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 17:28:58 +0800 Subject: [PATCH 163/202] remove static linking of ags --- crates/gpui/build.rs | 35 +++--------- crates/gpui/libs/README.md | 10 ---- crates/gpui/libs/amd_ags_x64_2022_MT.lib | Bin 153528 -> 0 bytes crates/gpui/libs/amd_ags_x86_2022_MT.lib | Bin 81484 -> 0 bytes .../src/platform/windows/directx_renderer.rs | 52 ++++++++++++------ 5 files changed, 43 insertions(+), 54 deletions(-) delete mode 100644 crates/gpui/libs/README.md delete mode 100644 crates/gpui/libs/amd_ags_x64_2022_MT.lib delete mode 100644 crates/gpui/libs/amd_ags_x86_2022_MT.lib diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index dfe7aa09df..5f24d88f5b 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -251,9 +251,6 @@ mod windows { }; pub(super) fn build() { - // Link the AMD AGS library - link_amd_ags(); - // Compile HLSL shaders #[cfg(not(debug_assertions))] compile_shaders(); @@ -263,24 +260,6 @@ mod windows { embed_resource(); } - fn link_amd_ags() { - // We can not use relative paths in `cargo:rustc-link-search`, so we need to use the absolute path. - // See: https://stackoverflow.com/questions/41917096/how-do-i-make-rustc-link-search-relative-to-the-project-location - let lib_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("libs"); - #[cfg(target_pointer_width = "64")] - let lib_name = "amd_ags_x64_2022_MT"; - #[cfg(target_pointer_width = "32")] - let lib_name = "amd_ags_x86_2022_MT"; - - println!("cargo:rustc-link-lib=static={}", lib_name); - println!("cargo:rustc-link-search=native={}", lib_dir.display()); - println!( - "cargo:rerun-if-changed={}/{}.lib", - lib_dir.display(), - lib_name - ); - } - #[cfg(feature = "windows-manifest")] fn embed_resource() { let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml"); @@ -331,6 +310,13 @@ mod windows { /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. fn find_fxc_compiler() -> String { + // Check environment variable + if let Ok(path) = std::env::var("GPUI_FXC_PATH") { + if Path::new(&path).exists() { + return path; + } + } + // Try to find in PATH if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { if output.status.success() { @@ -347,13 +333,6 @@ mod windows { .to_string(); } - // Check environment variable - if let Ok(path) = std::env::var("GPUI_FXC_PATH") { - if Path::new(&path).exists() { - return path; - } - } - panic!("Failed to find fxc.exe"); } diff --git a/crates/gpui/libs/README.md b/crates/gpui/libs/README.md deleted file mode 100644 index 2be286ee53..0000000000 --- a/crates/gpui/libs/README.md +++ /dev/null @@ -1,10 +0,0 @@ -The files in this folder are required for the Windows platform support in the gpui library. - - -#### amd_ags_x64.lib & amd_ags_x86.lib - -These libraries are used for AMD GPU support, currently only used on Windows, mainly for retrieving GPU information and capabilities. They are linked against the AMD AGS (AMD GPU Services) library. - -The official AMD AGS documentation can be found [here](https://gpuopen.com/amd-gpu-services-ags-library). And these two files are grabbed from the [official AMD AGS repository](https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK), currently at version 6.3.0. - -If you want to update these files, don't forget to update the value of `AGS_CURRENT_VERSION` in `gpui/src/platform/windows/directx_renderer.rs` as well. diff --git a/crates/gpui/libs/amd_ags_x64_2022_MT.lib b/crates/gpui/libs/amd_ags_x64_2022_MT.lib deleted file mode 100644 index a25d772160ddadcc98ce777c0e73a57e9f05a59a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153528 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 CUx%mw diff --git a/crates/gpui/libs/amd_ags_x86_2022_MT.lib b/crates/gpui/libs/amd_ags_x86_2022_MT.lib deleted file mode 100644 index cdb1305378fa058b0e3ffcdeeca8d4ee4e28318b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81484 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 J1FHcs7y#_=bGHBh diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index ab75e66bae..d261bf1b4c 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1607,6 +1607,12 @@ mod nvidia { mod amd { use std::os::raw::{c_char, c_int, c_void}; + use anyhow::{Context, Result}; + use windows::{ + Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}, + core::s, + }; + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145 const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0; @@ -1625,19 +1631,36 @@ mod amd { pub devices: *mut c_void, } - unsafe extern "C" { - fn agsInitialize( - version: c_int, - config: *const c_void, - context: *mut *mut AGSContext, - gpu_info: *mut AGSGPUInfo, - ) -> c_int; + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L429 + #[allow(non_camel_case_types)] + type agsInitialize_t = unsafe extern "C" fn( + version: c_int, + config: *const c_void, + context: *mut *mut AGSContext, + gpu_info: *mut AGSGPUInfo, + ) -> c_int; - fn agsDeInitialize(context: *mut AGSContext) -> c_int; - } + // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L436 + #[allow(non_camel_case_types)] + type agsDeInitialize_t = unsafe extern "C" fn(context: *mut AGSContext) -> c_int; - pub(super) fn get_driver_version() -> anyhow::Result { + pub(super) fn get_driver_version() -> Result { unsafe { + #[cfg(target_pointer_width = "64")] + let amd_dll = + LoadLibraryA(s!("amd_ags_x64.dll")).context("Failed to load AMD AGS library")?; + #[cfg(target_pointer_width = "32")] + let amd_dll = + LoadLibraryA(s!("amd_ags_x86.dll")).context("Failed to load AMD AGS library")?; + + let ags_initialize_addr = GetProcAddress(amd_dll, s!("agsInitialize")) + .ok_or_else(|| anyhow::anyhow!("Failed to get agsInitialize address"))?; + let ags_deinitialize_addr = GetProcAddress(amd_dll, s!("agsDeInitialize")) + .ok_or_else(|| anyhow::anyhow!("Failed to get agsDeInitialize address"))?; + + let ags_initialize: agsInitialize_t = std::mem::transmute(ags_initialize_addr); + let ags_deinitialize: agsDeInitialize_t = std::mem::transmute(ags_deinitialize_addr); + let mut context: *mut AGSContext = std::ptr::null_mut(); let mut gpu_info: AGSGPUInfo = AGSGPUInfo { driver_version: std::ptr::null(), @@ -1646,17 +1669,14 @@ mod amd { devices: std::ptr::null_mut(), }; - let result = agsInitialize( + let result = ags_initialize( AGS_CURRENT_VERSION, std::ptr::null(), &mut context, &mut gpu_info, ); if result != 0 { - return Err(anyhow::anyhow!( - "Failed to initialize AGS, error code: {}", - result - )); + anyhow::bail!("Failed to initialize AMD AGS, error code: {}", result); } // Vulkan acctually returns this as the driver version @@ -1676,7 +1696,7 @@ mod amd { "Unknown Radeon Driver Version".to_string() }; - agsDeInitialize(context); + ags_deinitialize(context); Ok(format!("{} ({})", software_version, driver_version)) } } From 89d34e15134ef33f421cc77559bcc5de4d6c890d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 17:44:40 +0800 Subject: [PATCH 164/202] clippy --- crates/gpui/src/platform.rs | 2 +- .../gpui/src/platform/windows/directx_renderer.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index f0b9be68bb..76a5eb4c02 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -13,7 +13,7 @@ mod mac; any(target_os = "linux", target_os = "freebsd"), any(feature = "x11", feature = "wayland") ), - feature = "macos-blade" + all(target_os = "macos", feature = "macos-blade") ))] mod blade; diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index d261bf1b4c..6e1ba13de0 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1049,7 +1049,7 @@ fn get_device( let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG; #[cfg(not(debug_assertions))] let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; - Ok(unsafe { + unsafe { D3D11CreateDevice( adapter, D3D_DRIVER_TYPE_UNKNOWN, @@ -1062,8 +1062,9 @@ fn get_device( device, None, context, - )? - }) + )?; + } + Ok(()) } #[cfg(not(feature = "enable-renderdoc"))] @@ -1560,11 +1561,9 @@ mod nvidia { unsafe { // Try to load the NVIDIA driver DLL #[cfg(target_pointer_width = "64")] - let nvidia_dll = - LoadLibraryA(s!("nvapi64.dll")).context(format!("Can't load nvapi64.dll"))?; + let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?; #[cfg(target_pointer_width = "32")] - let nvidia_dll = - LoadLibraryA(s!("nvapi.dll")).context(format!("Can't load nvapi.dll"))?; + let nvidia_dll = LoadLibraryA(s!("nvapi.dll")).context("Can't load nvapi.dll")?; let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface")) .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?; @@ -1614,7 +1613,7 @@ mod amd { }; // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145 - const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12) | 0; + const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12); // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204 // This is an opaque type, using struct to represent it properly for FFI From 1ee81a507bfc80f6321971d3c6ec0037c842b47b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 18:50:25 +0800 Subject: [PATCH 165/202] bundle ags.dll --- crates/zed/resources/windows/zed.iss | 1 + script/bundle-windows.ps1 | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/zed/resources/windows/zed.iss b/crates/zed/resources/windows/zed.iss index 9d104d1f15..66a2fbb4b7 100644 --- a/crates/zed/resources/windows/zed.iss +++ b/crates/zed/resources/windows/zed.iss @@ -62,6 +62,7 @@ Source: "{#ResourcesDir}\Zed.exe"; DestDir: "{code:GetInstallDir}"; Flags: ignor Source: "{#ResourcesDir}\bin\*"; DestDir: "{code:GetInstallDir}\bin"; Flags: ignoreversion Source: "{#ResourcesDir}\tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion Source: "{#ResourcesDir}\appx\*"; DestDir: "{app}\appx"; BeforeInstall: RemoveAppxPackage; AfterInstall: AddAppxPackage; Flags: ignoreversion; Check: IsWindows11OrLater +Source: "{#ResourcesDir}\amd_ags_x64.dll"; DestDir: "{app}"; Flags: ignoreversion [Icons] Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}.exe"; AppUserModelID: "{#AppUserId}" diff --git a/script/bundle-windows.ps1 b/script/bundle-windows.ps1 index 01a1114c26..0018d7c9cb 100644 --- a/script/bundle-windows.ps1 +++ b/script/bundle-windows.ps1 @@ -120,11 +120,22 @@ function SignZedAndItsFriends { & "$innoDir\sign.ps1" $files } +function DownloadAMDGpuServices { + # If you update the AGS SDK version, please also update the version in `crates/gpui/src/platform/windows/directx_renderer.rs` + $url = "https://codeload.github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/zip/refs/tags/v6.3.0" + $zipPath = ".\AGS_SDK_v6.3.0.zip" + # Download the AGS SDK zip file + Invoke-WebRequest -Uri $url -OutFile $zipPath + # Extract the AGS SDK zip file + Expand-Archive -Path $zipPath -DestinationPath "." -Force +} + function CollectFiles { Move-Item -Path "$innoDir\zed_explorer_command_injector.appx" -Destination "$innoDir\appx\zed_explorer_command_injector.appx" -Force Move-Item -Path "$innoDir\zed_explorer_command_injector.dll" -Destination "$innoDir\appx\zed_explorer_command_injector.dll" -Force Move-Item -Path "$innoDir\cli.exe" -Destination "$innoDir\bin\zed.exe" -Force Move-Item -Path "$innoDir\auto_update_helper.exe" -Destination "$innoDir\tools\auto_update_helper.exe" -Force + Move-Item -Path ".\AGS_SDK-6.3.0\ags_lib\lib\amd_ags_x64.dll" -Destination "$innoDir\amd_ags_x64.dll" -Force } function BuildInstaller { @@ -195,7 +206,6 @@ function BuildInstaller { # Windows runner 2022 default has iscc in PATH, https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md # Currently, we are using Windows 2022 runner. # Windows runner 2025 doesn't have iscc in PATH for now, https://github.com/actions/runner-images/issues/11228 - # $innoSetupPath = "iscc.exe" $innoSetupPath = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" $definitions = @{ @@ -250,6 +260,7 @@ BuildZedAndItsFriends MakeAppx SignZedAndItsFriends ZipZedAndItsFriendsDebug +DownloadAMDGpuServices CollectFiles BuildInstaller From 5f3a1bdbd127e887a963b9e048bb5374f1925355 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Mon, 28 Jul 2025 19:34:15 +0800 Subject: [PATCH 166/202] trigger ci --- .github/workflows/ci.yml | 22 +++++++++++----------- crates/zed/RELEASE_CHANNEL | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66a2237c29..8bd43b2e74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -771,7 +771,7 @@ jobs: timeout-minutes: 120 name: Create a Windows installer runs-on: [self-hosted, Windows, X64] - if: false && (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling')) + if: true && (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling')) needs: [windows_tests] env: AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }} @@ -807,16 +807,16 @@ jobs: name: ZedEditorUserSetup-x64-${{ github.event.pull_request.head.sha || github.sha }}.exe path: ${{ env.SETUP_PATH }} - - name: Upload Artifacts to release - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 - # Re-enable when we are ready to publish windows preview releases - if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview - with: - draft: true - prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }} - files: ${{ env.SETUP_PATH }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # - name: Upload Artifacts to release + # uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 + # # Re-enable when we are ready to publish windows preview releases + # if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview + # with: + # draft: true + # prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }} + # files: ${{ env.SETUP_PATH }} + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} auto-release-preview: name: Auto release preview diff --git a/crates/zed/RELEASE_CHANNEL b/crates/zed/RELEASE_CHANNEL index 38f8e886e1..bf867e0ae5 100644 --- a/crates/zed/RELEASE_CHANNEL +++ b/crates/zed/RELEASE_CHANNEL @@ -1 +1 @@ -dev +nightly From e9697e46391e885ddc50aa5f95cf69e5a28f3abd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 28 Jul 2025 17:45:35 -0700 Subject: [PATCH 167/202] Start work on doing path MSAA using intermediate texture --- .../src/platform/windows/directx_renderer.rs | 387 +++++++++++------- crates/gpui/src/platform/windows/shaders.hlsl | 88 +++- 2 files changed, 317 insertions(+), 158 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 6e1ba13de0..837cd7ef1e 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -21,8 +21,8 @@ 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; +// This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11. +const PATH_MULTISAMPLE_COUNT: u32 = 4; pub(crate) struct DirectXRenderer { hwnd: HWND, @@ -51,8 +51,13 @@ struct DirectXResources { swap_chain: IDXGISwapChain1, render_target: ManuallyDrop, render_target_view: [Option; 1], - msaa_target: ID3D11Texture2D, - msaa_view: [Option; 1], + + // Path intermediate textures (with MSAA) + path_intermediate_texture: ID3D11Texture2D, + path_intermediate_view: [Option; 1], + path_intermediate_msaa_texture: ID3D11Texture2D, + path_intermediate_msaa_view: [Option; 1], + path_intermediate_srv: Option, // Cached window size and viewport width: u32, @@ -63,7 +68,8 @@ struct DirectXResources { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - paths_pipeline: PathsPipelineState, + paths_rasterization_pipeline: PathsPipelineState, + paths_sprite_pipeline: PipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, poly_sprites: PipelineState, @@ -76,13 +82,6 @@ struct DirectXGlobalElements { } #[repr(C)] -struct DrawInstancedIndirectArgs { - vertex_count_per_instance: u32, - instance_count: u32, - start_vertex_location: u32, - start_instance_location: u32, -} - #[cfg(not(feature = "enable-renderdoc"))] struct DirectComposition { comp_device: IDCompositionDevice, @@ -161,12 +160,13 @@ impl DirectXRenderer { }], )?; unsafe { + self.devices.device_context.ClearRenderTargetView( + self.resources.render_target_view[0].as_ref().unwrap(), + &[0.0; 4], + ); 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); + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); self.devices .device_context .RSSetViewports(Some(&self.resources.viewport)); @@ -181,16 +181,6 @@ impl DirectXRenderer { fn present(&mut 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); let result = self.resources.swap_chain.Present(1, DXGI_PRESENT(0)); // Presenting the swap chain can fail if the DirectX device was removed or reset. if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET { @@ -263,7 +253,10 @@ impl DirectXRenderer { match batch { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows), PrimitiveBatch::Quads(quads) => self.draw_quads(quads), - PrimitiveBatch::Paths(paths) => self.draw_paths(paths), + PrimitiveBatch::Paths(paths) => { + self.draw_paths_to_intermediate(paths)?; + self.draw_paths_from_intermediate(paths) + } PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines), PrimitiveBatch::MonochromeSprites { texture_id, @@ -369,47 +362,118 @@ impl DirectXRenderer { ) } - fn draw_paths(&mut self, paths: &[Path]) -> Result<()> { + fn draw_paths_to_intermediate(&mut self, paths: &[Path]) -> Result<()> { if paths.is_empty() { return Ok(()); } - let mut vertices = Vec::new(); - let mut sprites = Vec::with_capacity(paths.len()); - let mut draw_indirect_commands = Vec::with_capacity(paths.len()); - let mut start_vertex_location = 0; - for (i, path) in paths.iter().enumerate() { - draw_indirect_commands.push(DrawInstancedIndirectArgs { - vertex_count_per_instance: path.vertices.len() as u32, - instance_count: 1, - start_vertex_location, - start_instance_location: i as u32, - }); - start_vertex_location += path.vertices.len() as u32; + // Clear intermediate MSAA texture + unsafe { + self.devices.device_context.ClearRenderTargetView( + self.resources.path_intermediate_msaa_view[0] + .as_ref() + .unwrap(), + &[0.0; 4], + ); + // Set intermediate MSAA texture as render target + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.path_intermediate_msaa_view), None); + } + + // Collect all vertices and sprites for a single draw call + let mut vertices = Vec::new(); + let mut sprites = Vec::new(); + + for (path_index, path) in paths.iter().enumerate() { vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex { xy_position: v.xy_position, - content_mask: path.content_mask.bounds, - sprite_index: i as u32, + st_position: v.st_position, + path_index: path_index as u32, })); - sprites.push(PathSprite { - bounds: path.bounds, + sprites.push(PathRasterizationSprite { + bounds: path.bounds.intersect(&path.content_mask.bounds), color: path.color, }); } - self.pipelines.paths_pipeline.update_buffer( + if !vertices.is_empty() { + self.pipelines.paths_rasterization_pipeline.update_buffer( + &self.devices.device, + &self.devices.device_context, + &sprites, + &vertices, + )?; + self.pipelines.paths_rasterization_pipeline.draw( + &self.devices.device_context, + vertices.len() as u32, + &self.resources.viewport, + &self.globals.global_params_buffer, + )?; + } + + // Resolve MSAA to non-MSAA intermediate texture + unsafe { + self.devices.device_context.ResolveSubresource( + &self.resources.path_intermediate_texture, + 0, + &self.resources.path_intermediate_msaa_texture, + 0, + RENDER_TARGET_FORMAT, + ); + // Flush to ensure the resolve operation is complete before using the texture + self.devices.device_context.Flush(); + // Restore main render target + self.devices + .device_context + .OMSetRenderTargets(Some(&self.resources.render_target_view), None); + } + + Ok(()) + } + + fn draw_paths_from_intermediate(&mut self, paths: &[Path]) -> Result<()> { + let Some(first_path) = paths.first() else { + return Ok(()); + }; + + // When copying paths from the intermediate texture to the drawable, + // each pixel must only be copied once, in case of transparent paths. + // + // If all paths have the same draw order, then their bounds are all + // disjoint, so we can copy each path's bounds individually. If this + // batch combines different draw orders, we perform a single copy + // for a minimal spanning rect. + let sprites = if paths.last().unwrap().order == first_path.order { + paths + .iter() + .map(|path| PathSprite { + bounds: path.bounds, + }) + .collect::>() + } else { + let mut bounds = first_path.bounds; + for path in paths.iter().skip(1) { + bounds = bounds.union(&path.bounds); + } + vec![PathSprite { bounds }] + }; + + self.pipelines.paths_sprite_pipeline.update_buffer( &self.devices.device, &self.devices.device_context, &sprites, - &vertices, - &draw_indirect_commands, )?; - self.pipelines.paths_pipeline.draw( + + // Draw the sprites with the path texture + self.pipelines.paths_sprite_pipeline.draw_with_texture( &self.devices.device_context, - paths.len(), + &[self.resources.path_intermediate_srv.clone()], &self.resources.viewport, &self.globals.global_params_buffer, + &self.globals.sampler, + sprites.len() as u32, ) } @@ -528,19 +592,30 @@ impl DirectXResources { let swap_chain = 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)?; + let ( + render_target, + render_target_view, + path_intermediate_texture, + path_intermediate_view, + path_intermediate_msaa_texture, + path_intermediate_msaa_view, + path_intermediate_srv, + viewport, + ) = create_resources(devices, &swap_chain, width, height)?; set_rasterizer_state(&devices.device, &devices.device_context)?; Ok(ManuallyDrop::new(Self { swap_chain, render_target, render_target_view, - msaa_target, - msaa_view, + path_intermediate_texture, + path_intermediate_view, + path_intermediate_msaa_texture, + path_intermediate_msaa_view, + path_intermediate_srv, + viewport, width, height, - viewport, })) } @@ -551,12 +626,23 @@ impl DirectXResources { width: u32, height: u32, ) -> Result<()> { - let (render_target, render_target_view, msaa_target, msaa_view, viewport) = - create_resources(devices, &self.swap_chain, width, height)?; + let ( + render_target, + render_target_view, + path_intermediate_texture, + path_intermediate_view, + path_intermediate_msaa_texture, + path_intermediate_msaa_view, + path_intermediate_srv, + 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.path_intermediate_texture = path_intermediate_texture; + self.path_intermediate_view = path_intermediate_view; + self.path_intermediate_msaa_texture = path_intermediate_msaa_texture; + self.path_intermediate_msaa_view = path_intermediate_msaa_view; + self.path_intermediate_srv = path_intermediate_srv; self.viewport = viewport; self.width = width; self.height = height; @@ -569,7 +655,9 @@ impl DirectXRenderPipelines { let shadow_pipeline = PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; - let paths_pipeline = PathsPipelineState::new(device)?; + let paths_rasterization_pipeline = PathsPipelineState::new(device)?; + let paths_sprite_pipeline = + PipelineState::new(device, "paths_sprite_pipeline", ShaderModule::PathSprite, 1)?; let underline_pipeline = PipelineState::new(device, "underline_pipeline", ShaderModule::Underline, 4)?; let mono_sprites = PipelineState::new( @@ -588,7 +676,8 @@ impl DirectXRenderPipelines { Ok(Self { shadow_pipeline, quad_pipeline, - paths_pipeline, + paths_rasterization_pipeline, + paths_sprite_pipeline, underline_pipeline, mono_sprites, poly_sprites, @@ -685,12 +774,10 @@ struct PathsPipelineState { fragment: ID3D11PixelShader, buffer: ID3D11Buffer, buffer_size: usize, + view: [Option; 1], vertex_buffer: Option, vertex_buffer_size: usize, - indirect_draw_buffer: ID3D11Buffer, - indirect_buffer_size: usize, input_layout: ID3D11InputLayout, - view: [Option; 1], } impl PipelineState { @@ -809,15 +896,14 @@ impl PathsPipelineState { let raw_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Fragment)?; create_fragment_shader(device, raw_shader.as_bytes())? }; - let buffer = create_buffer(device, std::mem::size_of::(), 32)?; + 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 mut layout = None; device.CreateInputLayout( @@ -843,18 +929,9 @@ impl PathsPipelineState { 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, + AlignedByteOffset: 16, InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, InstanceDataStepRate: 0, }, @@ -870,12 +947,10 @@ impl PathsPipelineState { fragment, buffer, buffer_size: 32, + view, vertex_buffer, vertex_buffer_size: 32, - indirect_draw_buffer, - indirect_buffer_size: 32, input_layout, - view, }) } @@ -883,24 +958,28 @@ impl PathsPipelineState { &mut self, device: &ID3D11Device, device_context: &ID3D11DeviceContext, - buffer_data: &[PathSprite], + sprites: &[PathRasterizationSprite], 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(); + if self.buffer_size < sprites.len() { + let new_buffer_size = sprites.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 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)?; + update_buffer(device_context, &self.buffer, sprites)?; + if self.vertex_buffer_size < vertices_data.len() { let new_vertex_buffer_size = vertices_data.len().next_power_of_two(); log::info!( @@ -921,26 +1000,14 @@ impl PathsPipelineState { 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, + vertex_count: u32, viewport: &[D3D11_VIEWPORT], global_params: &[Option], ) -> Result<()> { @@ -955,39 +1022,40 @@ impl PathsPipelineState { ); unsafe { const STRIDE: u32 = std::mem::size_of::() as u32; + const OFFSET: u32 = 0; + device_context.IASetInputLayout(&self.input_layout); device_context.IASetVertexBuffers( 0, 1, Some(&self.vertex_buffer), Some(&STRIDE), - Some(&0), + Some(&OFFSET), ); - 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, - ); - } + device_context.Draw(vertex_count, 0); } Ok(()) } } +#[derive(Clone, Copy)] #[repr(C)] struct DirectXPathVertex { xy_position: Point, - content_mask: Bounds, - sprite_index: u32, + st_position: Point, + path_index: u32, } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Copy)] +#[repr(C)] +struct PathRasterizationSprite { + bounds: Bounds, + color: Background, +} + +#[derive(Clone, Copy)] #[repr(C)] struct PathSprite { bounds: Bounds, - color: Background, } impl Drop for DirectXRenderer { @@ -1142,17 +1210,26 @@ fn create_resources( [Option; 1], ID3D11Texture2D, [Option; 1], + ID3D11Texture2D, + [Option; 1], + Option, [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 (path_intermediate_texture, path_intermediate_view, path_intermediate_srv) = + create_path_intermediate_texture_and_view(&devices.device, width, height)?; + let (path_intermediate_msaa_texture, path_intermediate_msaa_view) = + create_path_intermediate_msaa_texture_and_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, + path_intermediate_texture, + path_intermediate_view, + path_intermediate_msaa_texture, + path_intermediate_msaa_view, + path_intermediate_srv, viewport, )) } @@ -1175,12 +1252,16 @@ fn create_render_target_and_its_view( } #[inline] -fn create_msaa_target_and_its_view( +fn create_path_intermediate_texture_and_view( device: &ID3D11Device, width: u32, height: u32, -) -> Result<(ID3D11Texture2D, [Option; 1])> { - let msaa_target = unsafe { +) -> Result<( + ID3D11Texture2D, + [Option; 1], + Option, +)> { + let texture = unsafe { let mut output = None; let desc = D3D11_TEXTURE2D_DESC { Width: width, @@ -1189,7 +1270,47 @@ fn create_msaa_target_and_its_view( ArraySize: 1, Format: RENDER_TARGET_FORMAT, SampleDesc: DXGI_SAMPLE_DESC { - Count: MULTISAMPLE_COUNT, + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: 0, + }; + device.CreateTexture2D(&desc, None, Some(&mut output))?; + output.unwrap() + }; + + let mut render_target_view = None; + unsafe { device.CreateRenderTargetView(&texture, None, Some(&mut render_target_view))? }; + + let mut shader_resource_view = None; + unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? }; + + Ok(( + texture, + [Some(render_target_view.unwrap())], + shader_resource_view, + )) +} + +#[inline] +fn create_path_intermediate_msaa_texture_and_view( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result<(ID3D11Texture2D, [Option; 1])> { + let msaa_texture = 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: PATH_MULTISAMPLE_COUNT, Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32, }, Usage: D3D11_USAGE_DEFAULT, @@ -1200,12 +1321,9 @@ fn create_msaa_target_and_its_view( device.CreateTexture2D(&desc, None, Some(&mut output))?; output.unwrap() }; - let msaa_view = unsafe { - let mut output = None; - device.CreateRenderTargetView(&msaa_target, None, Some(&mut output))?; - output.unwrap() - }; - Ok((msaa_target, [Some(msaa_view)])) + let mut msaa_view = None; + unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? }; + Ok((msaa_texture, [Some(msaa_view.unwrap())])) } #[inline] @@ -1318,21 +1436,6 @@ fn create_buffer_view( Ok([view]) } -#[inline] -fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Result { - let desc = D3D11_BUFFER_DESC { - ByteWidth: (std::mem::size_of::() * buffer_size) as u32, - Usage: D3D11_USAGE_DYNAMIC, - 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, - }; - let mut buffer = None; - unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?; - Ok(buffer.unwrap()) -} - #[inline] fn update_buffer( device_context: &ID3D11DeviceContext, @@ -1390,6 +1493,7 @@ mod shader_resources { Shadow, Underline, Paths, + PathSprite, MonochromeSprite, PolychromeSprite, } @@ -1524,6 +1628,7 @@ mod shader_resources { ShaderModule::Shadow => "shadow", ShaderModule::Underline => "underline", ShaderModule::Paths => "paths", + ShaderModule::PathSprite => "path_sprite", ShaderModule::MonochromeSprite => "monochrome_sprite", ShaderModule::PolychromeSprite => "polychrome_sprite", } diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f0c773a673..a934102ce9 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -256,7 +256,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -878,41 +878,58 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { struct PathVertex { float2 xy_position: POSITION; - Bounds content_mask: TEXCOORD; - uint idx: GLOBALIDX; + float2 st_position: TEXCOORD0; + uint path_index: TEXCOORD1; }; -struct PathSprite { +struct PathRasterizationSprite { Bounds bounds; Background color; }; +struct PathSprite { + Bounds bounds; +}; + +StructuredBuffer path_rasterization_sprites: register(t1); + struct PathVertexOutput { float4 position: SV_Position; - nointerpolation uint sprite_id: TEXCOORD0; + float2 st_position: TEXCOORD0; + nointerpolation uint tag: TEXCOORD1; + nointerpolation uint color_space: TEXCOORD2; + nointerpolation float gradient_angle: TEXCOORD3; nointerpolation float4 solid_color: COLOR0; nointerpolation float4 color0: COLOR1; nointerpolation float4 color1: COLOR2; - float4 clip_distance: SV_ClipDistance; + nointerpolation float2 stop_percentages: COLOR3; + nointerpolation Bounds bounds: BOUNDS; }; struct PathFragmentInput { float4 position: SV_Position; - nointerpolation uint sprite_id: TEXCOORD0; + float2 st_position: TEXCOORD0; + nointerpolation uint tag: TEXCOORD1; + nointerpolation uint color_space: TEXCOORD2; + nointerpolation float gradient_angle: TEXCOORD3; nointerpolation float4 solid_color: COLOR0; nointerpolation float4 color0: COLOR1; nointerpolation float4 color1: COLOR2; + nointerpolation float2 stop_percentages: COLOR3; + nointerpolation Bounds bounds: BOUNDS; }; -StructuredBuffer path_sprites: register(t1); - PathVertexOutput paths_vertex(PathVertex input) { - PathSprite sprite = path_sprites[input.idx]; + PathRasterizationSprite sprite = path_rasterization_sprites[input.path_index]; PathVertexOutput output; 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; + output.st_position = input.st_position; + output.bounds = sprite.bounds; + output.tag = sprite.color.tag; + output.color_space = sprite.color.color_space; + output.gradient_angle = sprite.color.gradient_angle_or_pattern_height; + output.stop_percentages = float2(sprite.color.colors[0].percentage, sprite.color.colors[1].percentage); GradientColor gradient = prepare_gradient_color( sprite.color.tag, @@ -920,21 +937,58 @@ PathVertexOutput paths_vertex(PathVertex input) { sprite.color.solid, sprite.color.colors ); - output.solid_color = gradient.solid; output.color0 = gradient.color0; output.color1 = gradient.color1; + return output; } 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, + Background background; + background.tag = input.tag; + background.color_space = input.color_space; + background.solid = (Hsla)0; // Not used when tag != 0 + background.gradient_angle_or_pattern_height = input.gradient_angle; + background.colors[0].color = (Hsla)0; // Not used when colors are pre-computed + background.colors[0].percentage = input.stop_percentages.x; + background.colors[1].color = (Hsla)0; // Not used when colors are pre-computed + background.colors[1].percentage = input.stop_percentages.y; + + float4 color = gradient_color(background, input.position.xy, input.bounds, input.solid_color, input.color0, input.color1); return color; } +// --- path sprite --- // + +struct PathSpriteVertexOutput { + float4 position: SV_Position; + float2 texture_coords: TEXCOORD0; +}; + +StructuredBuffer path_sprites: register(t1); + +PathSpriteVertexOutput path_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)); + PathSprite sprite = path_sprites[sprite_id]; + + // Don't apply content mask because it was already accounted for when rasterizing the path + float4 device_position = to_device_position(unit_vertex, sprite.bounds); + + float2 screen_position = sprite.bounds.origin + unit_vertex * sprite.bounds.size; + float2 texture_coords = screen_position / global_viewport_size; + + PathSpriteVertexOutput output; + output.position = device_position; + output.texture_coords = texture_coords; + return output; +} + +float4 path_sprite_fragment(PathSpriteVertexOutput input): SV_Target { + return t_sprite.Sample(s_sprite, input.texture_coords); +} + /* ** ** Underlines @@ -970,7 +1024,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); From 8eea9aad40061d80f7f8e83c21671887336c0f2d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 28 Jul 2025 17:52:55 -0700 Subject: [PATCH 168/202] Use pre-multiplied alpha for path rasterization --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- crates/gpui/src/platform/windows/shaders.hlsl | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 837cd7ef1e..6680bc3887 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1377,7 +1377,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { 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_SRC_ALPHA; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index a934102ce9..58145c82c3 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -957,7 +957,8 @@ float4 paths_fragment(PathFragmentInput input): SV_Target { float4 color = gradient_color(background, input.position.xy, input.bounds, input.solid_color, input.color0, input.color1); - return color; + // Return premultiplied alpha for correct blending + return float4(color.rgb * color.a, color.a); } // --- path sprite --- // From ce67ce148259bbc50831745007bfd718a192f470 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 13:07:00 +0800 Subject: [PATCH 169/202] Revert "Use pre-multiplied alpha for path rasterization" This reverts commit 8eea9aad40061d80f7f8e83c21671887336c0f2d. --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- crates/gpui/src/platform/windows/shaders.hlsl | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 6680bc3887..837cd7ef1e 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1377,7 +1377,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { 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].SrcBlend = D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 58145c82c3..a934102ce9 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -957,8 +957,7 @@ float4 paths_fragment(PathFragmentInput input): SV_Target { float4 color = gradient_color(background, input.position.xy, input.bounds, input.solid_color, input.color0, input.color1); - // Return premultiplied alpha for correct blending - return float4(color.rgb * color.a, color.a); + return color; } // --- path sprite --- // From d7b14d8dc50d6d07963029477678813b9bd9566d Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 13:24:56 +0800 Subject: [PATCH 170/202] rename pipeline --- crates/gpui/build.rs | 3 +- .../src/platform/windows/directx_renderer.rs | 46 +++++++++++-------- crates/gpui/src/platform/windows/shaders.hlsl | 12 +++-- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 5f24d88f5b..0a67262bcc 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -286,7 +286,8 @@ mod windows { let modules = [ "quad", "shadow", - "paths", + "path_rasterization", + "path_sprite", "underline", "monochrome_sprite", "polychrome_sprite", diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 837cd7ef1e..e731a74553 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -68,8 +68,8 @@ struct DirectXResources { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - paths_rasterization_pipeline: PathsPipelineState, - paths_sprite_pipeline: PipelineState, + path_rasterization_pipeline: PathRasterizationPipelineState, + path_sprite_pipeline: PipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, poly_sprites: PipelineState, @@ -399,13 +399,13 @@ impl DirectXRenderer { } if !vertices.is_empty() { - self.pipelines.paths_rasterization_pipeline.update_buffer( + self.pipelines.path_rasterization_pipeline.update_buffer( &self.devices.device, &self.devices.device_context, &sprites, &vertices, )?; - self.pipelines.paths_rasterization_pipeline.draw( + self.pipelines.path_rasterization_pipeline.draw( &self.devices.device_context, vertices.len() as u32, &self.resources.viewport, @@ -460,14 +460,14 @@ impl DirectXRenderer { vec![PathSprite { bounds }] }; - self.pipelines.paths_sprite_pipeline.update_buffer( + self.pipelines.path_sprite_pipeline.update_buffer( &self.devices.device, &self.devices.device_context, &sprites, )?; // Draw the sprites with the path texture - self.pipelines.paths_sprite_pipeline.draw_with_texture( + self.pipelines.path_sprite_pipeline.draw_with_texture( &self.devices.device_context, &[self.resources.path_intermediate_srv.clone()], &self.resources.viewport, @@ -655,9 +655,9 @@ impl DirectXRenderPipelines { let shadow_pipeline = PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; - let paths_rasterization_pipeline = PathsPipelineState::new(device)?; - let paths_sprite_pipeline = - PipelineState::new(device, "paths_sprite_pipeline", ShaderModule::PathSprite, 1)?; + let path_rasterization_pipeline = PathRasterizationPipelineState::new(device)?; + let path_sprite_pipeline = + PipelineState::new(device, "path_sprite_pipeline", ShaderModule::PathSprite, 1)?; let underline_pipeline = PipelineState::new(device, "underline_pipeline", ShaderModule::Underline, 4)?; let mono_sprites = PipelineState::new( @@ -676,8 +676,8 @@ impl DirectXRenderPipelines { Ok(Self { shadow_pipeline, quad_pipeline, - paths_rasterization_pipeline, - paths_sprite_pipeline, + path_rasterization_pipeline, + path_sprite_pipeline, underline_pipeline, mono_sprites, poly_sprites, @@ -769,7 +769,7 @@ struct PipelineState { _marker: std::marker::PhantomData, } -struct PathsPipelineState { +struct PathRasterizationPipelineState { vertex: ID3D11VertexShader, fragment: ID3D11PixelShader, buffer: ID3D11Buffer, @@ -883,17 +883,19 @@ impl PipelineState { } } -impl PathsPipelineState { +impl PathRasterizationPipelineState { fn new(device: &ID3D11Device) -> Result { let (vertex, vertex_shader) = { - let raw_vertex_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Vertex)?; + let raw_vertex_shader = + RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Vertex)?; ( create_vertex_shader(device, raw_vertex_shader.as_bytes())?, raw_vertex_shader, ) }; let fragment = { - let raw_shader = RawShaderBytes::new(ShaderModule::Paths, ShaderTarget::Fragment)?; + let raw_shader = + RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Fragment)?; create_fragment_shader(device, raw_shader.as_bytes())? }; let buffer = create_buffer(device, std::mem::size_of::(), 32)?; @@ -1492,7 +1494,7 @@ mod shader_resources { Quad, Shadow, Underline, - Paths, + PathRasterization, PathSprite, MonochromeSprite, PolychromeSprite, @@ -1549,9 +1551,13 @@ mod shader_resources { ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES, ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES, }, - ShaderModule::Paths => match target { - ShaderTarget::Vertex => PATHS_VERTEX_BYTES, - ShaderTarget::Fragment => PATHS_FRAGMENT_BYTES, + ShaderModule::PathRasterization => match target { + ShaderTarget::Vertex => PATH_RASTERIZATION_VERTEX_BYTES, + ShaderTarget::Fragment => PATH_RASTERIZATION_FRAGMENT_BYTES, + }, + ShaderModule::PathSprite => match target { + ShaderTarget::Vertex => PATH_SPRITE_VERTEX_BYTES, + ShaderTarget::Fragment => PATH_SPRITE_FRAGMENT_BYTES, }, ShaderModule::MonochromeSprite => match target { ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES, @@ -1627,7 +1633,7 @@ mod shader_resources { ShaderModule::Quad => "quad", ShaderModule::Shadow => "shadow", ShaderModule::Underline => "underline", - ShaderModule::Paths => "paths", + ShaderModule::PathRasterization => "path_rasterization", ShaderModule::PathSprite => "path_sprite", ShaderModule::MonochromeSprite => "monochrome_sprite", ShaderModule::PolychromeSprite => "polychrome_sprite", diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index a934102ce9..5e42201f1c 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -872,7 +872,7 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { /* ** -** Paths +** Path Rasterization ** */ @@ -919,7 +919,7 @@ struct PathFragmentInput { nointerpolation Bounds bounds: BOUNDS; }; -PathVertexOutput paths_vertex(PathVertex input) { +PathVertexOutput path_rasterization_vertex(PathVertex input) { PathRasterizationSprite sprite = path_rasterization_sprites[input.path_index]; PathVertexOutput output; @@ -944,7 +944,7 @@ PathVertexOutput paths_vertex(PathVertex input) { return output; } -float4 paths_fragment(PathFragmentInput input): SV_Target { +float4 path_rasterization_fragment(PathFragmentInput input): SV_Target { Background background; background.tag = input.tag; background.color_space = input.color_space; @@ -960,7 +960,11 @@ float4 paths_fragment(PathFragmentInput input): SV_Target { return color; } -// --- path sprite --- // +/* +** +** Path Sprites +** +*/ struct PathSpriteVertexOutput { float4 position: SV_Position; From 62d1b7e36f2af320f1de4061123ab6360bcb7821 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 14:05:38 +0800 Subject: [PATCH 171/202] fix `PathRasterization` pipeline --- .../src/platform/windows/directx_renderer.rs | 355 ++++++++---------- crates/gpui/src/platform/windows/shaders.hlsl | 94 ++--- 2 files changed, 196 insertions(+), 253 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index e731a74553..80195bf93e 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -68,7 +68,8 @@ struct DirectXResources { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - path_rasterization_pipeline: PathRasterizationPipelineState, + // path_rasterization_pipeline: PathRasterizationPipelineState, + path_rasterization_pipeline: PipelineState, path_sprite_pipeline: PipelineState, underline_pipeline: PipelineState, mono_sprites: PipelineState, @@ -341,6 +342,8 @@ impl DirectXRenderer { &self.devices.device_context, &self.resources.viewport, &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, shadows.len() as u32, ) } @@ -358,6 +361,8 @@ impl DirectXRenderer { &self.devices.device_context, &self.resources.viewport, &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, quads.len() as u32, ) } @@ -383,35 +388,29 @@ impl DirectXRenderer { // Collect all vertices and sprites for a single draw call let mut vertices = Vec::new(); - let mut sprites = Vec::new(); - for (path_index, path) in paths.iter().enumerate() { - vertices.extend(path.vertices.iter().map(|v| DirectXPathVertex { + for path in paths { + vertices.extend(path.vertices.iter().map(|v| PathRasterizationSprite { xy_position: v.xy_position, st_position: v.st_position, - path_index: path_index as u32, - })); - - sprites.push(PathRasterizationSprite { - bounds: path.bounds.intersect(&path.content_mask.bounds), color: path.color, - }); + bounds: path.bounds.intersect(&path.content_mask.bounds), + })); } - if !vertices.is_empty() { - self.pipelines.path_rasterization_pipeline.update_buffer( - &self.devices.device, - &self.devices.device_context, - &sprites, - &vertices, - )?; - self.pipelines.path_rasterization_pipeline.draw( - &self.devices.device_context, - vertices.len() as u32, - &self.resources.viewport, - &self.globals.global_params_buffer, - )?; - } + self.pipelines.path_rasterization_pipeline.update_buffer( + &self.devices.device, + &self.devices.device_context, + &vertices, + )?; + self.pipelines.path_rasterization_pipeline.draw( + &self.devices.device_context, + &self.resources.viewport, + &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, + vertices.len() as u32, + 1, + )?; // Resolve MSAA to non-MSAA intermediate texture unsafe { @@ -490,6 +489,8 @@ impl DirectXRenderer { &self.devices.device_context, &self.resources.viewport, &self.globals.global_params_buffer, + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + 4, underlines.len() as u32, ) } @@ -655,7 +656,12 @@ impl DirectXRenderPipelines { let shadow_pipeline = PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; - let path_rasterization_pipeline = PathRasterizationPipelineState::new(device)?; + let path_rasterization_pipeline = PipelineState::new( + device, + "path_rasterization_pipeline", + ShaderModule::PathRasterization, + 32, + )?; let path_sprite_pipeline = PipelineState::new(device, "path_sprite_pipeline", ShaderModule::PathSprite, 1)?; let underline_pipeline = @@ -769,16 +775,16 @@ struct PipelineState { _marker: std::marker::PhantomData, } -struct PathRasterizationPipelineState { - vertex: ID3D11VertexShader, - fragment: ID3D11PixelShader, - buffer: ID3D11Buffer, - buffer_size: usize, - view: [Option; 1], - vertex_buffer: Option, - vertex_buffer_size: usize, - input_layout: ID3D11InputLayout, -} +// struct PathRasterizationPipelineState { +// vertex: ID3D11VertexShader, +// fragment: ID3D11PixelShader, +// buffer: ID3D11Buffer, +// buffer_size: usize, +// view: [Option; 1], +// vertex_buffer: Option, +// vertex_buffer_size: usize, +// input_layout: ID3D11InputLayout, +// } impl PipelineState { fn new( @@ -837,19 +843,21 @@ impl PipelineState { device_context: &ID3D11DeviceContext, viewport: &[D3D11_VIEWPORT], global_params: &[Option], + topology: D3D_PRIMITIVE_TOPOLOGY, + vertex_count: u32, instance_count: u32, ) -> Result<()> { set_pipeline_state( device_context, &self.view, - D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, + topology, viewport, &self.vertex, &self.fragment, global_params, ); unsafe { - device_context.DrawInstanced(4, instance_count, 0, 0); + device_context.DrawInstanced(vertex_count, instance_count, 0, 0); } Ok(()) } @@ -883,175 +891,130 @@ impl PipelineState { } } -impl PathRasterizationPipelineState { - fn new(device: &ID3D11Device) -> Result { - let (vertex, vertex_shader) = { - let raw_vertex_shader = - RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Vertex)?; - ( - create_vertex_shader(device, raw_vertex_shader.as_bytes())?, - raw_vertex_shader, - ) - }; - let fragment = { - let raw_shader = - RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Fragment)?; - create_fragment_shader(device, raw_shader.as_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, - )?); +// impl PathRasterizationPipelineState { +// fn new(device: &ID3D11Device) -> Result { +// let (vertex, vertex_shader) = { +// let raw_vertex_shader = +// RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Vertex)?; +// ( +// create_vertex_shader(device, raw_vertex_shader.as_bytes())?, +// raw_vertex_shader, +// ) +// }; +// let fragment = { +// let raw_shader = +// RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Fragment)?; +// create_fragment_shader(device, raw_shader.as_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 input_layout = unsafe { - 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_R32_UINT, - InputSlot: 0, - AlignedByteOffset: 16, - InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA, - InstanceDataStepRate: 0, - }, - ], - vertex_shader.as_bytes(), - Some(&mut layout), - )?; - layout.unwrap() - }; +// Ok(Self { +// vertex, +// fragment, +// buffer, +// buffer_size: 32, +// view, +// vertex_buffer, +// vertex_buffer_size: 32, +// }) +// } - Ok(Self { - vertex, - fragment, - buffer, - buffer_size: 32, - view, - vertex_buffer, - vertex_buffer_size: 32, - input_layout, - }) - } +// fn update_buffer( +// &mut self, +// device: &ID3D11Device, +// device_context: &ID3D11DeviceContext, +// sprites: &[PathRasterizationSprite], +// vertices_data: &[DirectXPathVertex], +// ) -> Result<()> { +// if self.buffer_size < sprites.len() { +// let new_buffer_size = sprites.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, sprites)?; - fn update_buffer( - &mut self, - device: &ID3D11Device, - device_context: &ID3D11DeviceContext, - sprites: &[PathRasterizationSprite], - vertices_data: &[DirectXPathVertex], - ) -> Result<()> { - if self.buffer_size < sprites.len() { - let new_buffer_size = sprites.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, sprites)?; +// 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.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, - )?; +// Ok(()) +// } - Ok(()) - } - - fn draw( - &self, - device_context: &ID3D11DeviceContext, - vertex_count: u32, - 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; - const OFFSET: u32 = 0; - device_context.IASetInputLayout(&self.input_layout); - device_context.IASetVertexBuffers( - 0, - 1, - Some(&self.vertex_buffer), - Some(&STRIDE), - Some(&OFFSET), - ); - device_context.Draw(vertex_count, 0); - } - Ok(()) - } -} - -#[derive(Clone, Copy)] -#[repr(C)] -struct DirectXPathVertex { - xy_position: Point, - st_position: Point, - path_index: u32, -} +// fn draw( +// &self, +// device_context: &ID3D11DeviceContext, +// vertex_count: u32, +// 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; +// const OFFSET: u32 = 0; +// device_context.IASetInputLayout(&self.input_layout); +// device_context.IASetVertexBuffers( +// 0, +// 1, +// Some(&self.vertex_buffer), +// Some(&STRIDE), +// Some(&OFFSET), +// ); +// device_context.Draw(vertex_count, 0); +// } +// Ok(()) +// } +// } #[derive(Clone, Copy)] #[repr(C)] struct PathRasterizationSprite { - bounds: Bounds, + xy_position: Point, + st_position: Point, color: Background, + bounds: Bounds, } #[derive(Clone, Copy)] diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 5e42201f1c..954040c4c3 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -876,18 +876,10 @@ float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET { ** */ -struct PathVertex { - float2 xy_position: POSITION; - float2 st_position: TEXCOORD0; - uint path_index: TEXCOORD1; -}; - struct PathRasterizationSprite { - Bounds bounds; + float2 xy_position; + float2 st_position; Background color; -}; - -struct PathSprite { Bounds bounds; }; @@ -896,68 +888,52 @@ StructuredBuffer path_rasterization_sprites: register(t struct PathVertexOutput { float4 position: SV_Position; float2 st_position: TEXCOORD0; - nointerpolation uint tag: TEXCOORD1; - nointerpolation uint color_space: TEXCOORD2; - nointerpolation float gradient_angle: TEXCOORD3; - nointerpolation float4 solid_color: COLOR0; - nointerpolation float4 color0: COLOR1; - nointerpolation float4 color1: COLOR2; - nointerpolation float2 stop_percentages: COLOR3; - nointerpolation Bounds bounds: BOUNDS; + nointerpolation uint vertex_id: TEXCOORD1; + float4 clip_distance: SV_ClipDistance; }; struct PathFragmentInput { float4 position: SV_Position; float2 st_position: TEXCOORD0; - nointerpolation uint tag: TEXCOORD1; - nointerpolation uint color_space: TEXCOORD2; - nointerpolation float gradient_angle: TEXCOORD3; - nointerpolation float4 solid_color: COLOR0; - nointerpolation float4 color0: COLOR1; - nointerpolation float4 color1: COLOR2; - nointerpolation float2 stop_percentages: COLOR3; - nointerpolation Bounds bounds: BOUNDS; + nointerpolation uint vertex_id: TEXCOORD1; }; -PathVertexOutput path_rasterization_vertex(PathVertex input) { - PathRasterizationSprite sprite = path_rasterization_sprites[input.path_index]; +PathVertexOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) { + PathRasterizationSprite sprite = path_rasterization_sprites[vertex_id]; PathVertexOutput output; - output.position = to_device_position_impl(input.xy_position); - output.st_position = input.st_position; - output.bounds = sprite.bounds; - output.tag = sprite.color.tag; - output.color_space = sprite.color.color_space; - output.gradient_angle = sprite.color.gradient_angle_or_pattern_height; - output.stop_percentages = float2(sprite.color.colors[0].percentage, sprite.color.colors[1].percentage); - - GradientColor gradient = prepare_gradient_color( - sprite.color.tag, - sprite.color.color_space, - sprite.color.solid, - sprite.color.colors - ); - output.solid_color = gradient.solid; - output.color0 = gradient.color0; - output.color1 = gradient.color1; + output.position = to_device_position_impl(sprite.xy_position); + output.st_position = sprite.st_position; + output.vertex_id = vertex_id; + output.clip_distance = distance_from_clip_rect_impl(sprite.xy_position, sprite.bounds); return output; } float4 path_rasterization_fragment(PathFragmentInput input): SV_Target { - Background background; - background.tag = input.tag; - background.color_space = input.color_space; - background.solid = (Hsla)0; // Not used when tag != 0 - background.gradient_angle_or_pattern_height = input.gradient_angle; - background.colors[0].color = (Hsla)0; // Not used when colors are pre-computed - background.colors[0].percentage = input.stop_percentages.x; - background.colors[1].color = (Hsla)0; // Not used when colors are pre-computed - background.colors[1].percentage = input.stop_percentages.y; + float2 dx = ddx(input.st_position); + float2 dy = ddy(input.st_position); + PathRasterizationSprite sprite = path_rasterization_sprites[input.vertex_id]; + + Background background = sprite.color; + Bounds bounds = sprite.bounds; - float4 color = gradient_color(background, input.position.xy, input.bounds, - input.solid_color, input.color0, input.color1); - return color; + float alpha; + if (length(float2(dx.x, dy.x))) { + alpha = 1.0; + } else { + float2 gradient = 2.0 * input.st_position.xx * float2(dx.x, dy.x) - float2(dx.y, dy.y); + float f = input.st_position.x * input.st_position.x - input.st_position.y; + float distance = f / length(gradient); + alpha = saturate(0.5 - distance); + } + + GradientColor gradient = prepare_gradient_color( + background.tag, background.color_space, background.solid, background.colors); + + float4 color = gradient_color(background, input.position.xy, bounds, + gradient.solid, gradient.color0, gradient.color1); + return float4(color.rgb * color.a * alpha, alpha * color.a); } /* @@ -966,6 +942,10 @@ float4 path_rasterization_fragment(PathFragmentInput input): SV_Target { ** */ +struct PathSprite { + Bounds bounds; +}; + struct PathSpriteVertexOutput { float4 position: SV_Position; float2 texture_coords: TEXCOORD0; From 64b3b050e3a50a9ae2f6950a19d8be8b2b7bb0ef Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 14:24:43 +0800 Subject: [PATCH 172/202] fix --- .../src/platform/windows/directx_renderer.rs | 222 ++++++------------ 1 file changed, 78 insertions(+), 144 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 80195bf93e..73ecd7cfca 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -79,7 +79,6 @@ struct DirectXRenderPipelines { struct DirectXGlobalElements { global_params_buffer: [Option; 1], sampler: [Option; 1], - blend_state: ID3D11BlendState, } #[repr(C)] @@ -171,11 +170,6 @@ impl DirectXRenderer { self.devices .device_context .RSSetViewports(Some(&self.resources.viewport)); - self.devices.device_context.OMSetBlendState( - &self.globals.blend_state, - None, - 0xFFFFFFFF, - ); } Ok(()) } @@ -653,30 +647,54 @@ impl DirectXResources { impl DirectXRenderPipelines { pub fn new(device: &ID3D11Device) -> Result { - let shadow_pipeline = - PipelineState::new(device, "shadow_pipeline", ShaderModule::Shadow, 4)?; - let quad_pipeline = PipelineState::new(device, "quad_pipeline", ShaderModule::Quad, 64)?; + let shadow_pipeline = PipelineState::new( + device, + "shadow_pipeline", + ShaderModule::Shadow, + 4, + create_blend_state(device)?, + )?; + let quad_pipeline = PipelineState::new( + device, + "quad_pipeline", + ShaderModule::Quad, + 64, + create_blend_state(device)?, + )?; let path_rasterization_pipeline = PipelineState::new( device, "path_rasterization_pipeline", ShaderModule::PathRasterization, 32, + create_blend_state_for_path_rasterization(device)?, + )?; + let path_sprite_pipeline = PipelineState::new( + device, + "path_sprite_pipeline", + ShaderModule::PathSprite, + 4, + create_blend_state_for_path_sprite(device)?, + )?; + let underline_pipeline = PipelineState::new( + device, + "underline_pipeline", + ShaderModule::Underline, + 4, + create_blend_state(device)?, )?; - let path_sprite_pipeline = - PipelineState::new(device, "path_sprite_pipeline", ShaderModule::PathSprite, 1)?; - let underline_pipeline = - PipelineState::new(device, "underline_pipeline", ShaderModule::Underline, 4)?; let mono_sprites = PipelineState::new( device, "monochrome_sprite_pipeline", ShaderModule::MonochromeSprite, 512, + create_blend_state(device)?, )?; let poly_sprites = PipelineState::new( device, "polychrome_sprite_pipeline", ShaderModule::PolychromeSprite, 16, + create_blend_state(device)?, )?; Ok(Self { @@ -748,12 +766,9 @@ impl DirectXGlobalElements { [output] }; - let blend_state = create_blend_state(device)?; - Ok(Self { global_params_buffer, sampler, - blend_state, }) } } @@ -772,26 +787,17 @@ struct PipelineState { buffer: ID3D11Buffer, buffer_size: usize, view: [Option; 1], + blend_state: ID3D11BlendState, _marker: std::marker::PhantomData, } -// struct PathRasterizationPipelineState { -// vertex: ID3D11VertexShader, -// fragment: ID3D11PixelShader, -// buffer: ID3D11Buffer, -// buffer_size: usize, -// view: [Option; 1], -// vertex_buffer: Option, -// vertex_buffer_size: usize, -// input_layout: ID3D11InputLayout, -// } - impl PipelineState { fn new( device: &ID3D11Device, label: &'static str, shader_module: ShaderModule, buffer_size: usize, + blend_state: ID3D11BlendState, ) -> Result { let vertex = { let raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Vertex)?; @@ -811,6 +817,7 @@ impl PipelineState { buffer, buffer_size, view, + blend_state, _marker: std::marker::PhantomData, }) } @@ -855,6 +862,7 @@ impl PipelineState { &self.vertex, &self.fragment, global_params, + &self.blend_state, ); unsafe { device_context.DrawInstanced(vertex_count, instance_count, 0, 0); @@ -879,6 +887,7 @@ impl PipelineState { &self.vertex, &self.fragment, global_params, + &self.blend_state, ); unsafe { device_context.PSSetSamplers(0, Some(sampler)); @@ -891,123 +900,6 @@ impl PipelineState { } } -// impl PathRasterizationPipelineState { -// fn new(device: &ID3D11Device) -> Result { -// let (vertex, vertex_shader) = { -// let raw_vertex_shader = -// RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Vertex)?; -// ( -// create_vertex_shader(device, raw_vertex_shader.as_bytes())?, -// raw_vertex_shader, -// ) -// }; -// let fragment = { -// let raw_shader = -// RawShaderBytes::new(ShaderModule::PathRasterization, ShaderTarget::Fragment)?; -// create_fragment_shader(device, raw_shader.as_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, -// )?); - -// Ok(Self { -// vertex, -// fragment, -// buffer, -// buffer_size: 32, -// view, -// vertex_buffer, -// vertex_buffer_size: 32, -// }) -// } - -// fn update_buffer( -// &mut self, -// device: &ID3D11Device, -// device_context: &ID3D11DeviceContext, -// sprites: &[PathRasterizationSprite], -// vertices_data: &[DirectXPathVertex], -// ) -> Result<()> { -// if self.buffer_size < sprites.len() { -// let new_buffer_size = sprites.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, sprites)?; - -// 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, -// )?; - -// Ok(()) -// } - -// fn draw( -// &self, -// device_context: &ID3D11DeviceContext, -// vertex_count: u32, -// 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; -// const OFFSET: u32 = 0; -// device_context.IASetInputLayout(&self.input_layout); -// device_context.IASetVertexBuffers( -// 0, -// 1, -// Some(&self.vertex_buffer), -// Some(&STRIDE), -// Some(&OFFSET), -// ); -// device_context.Draw(vertex_count, 0); -// } -// Ok(()) -// } -// } - #[derive(Clone, Copy)] #[repr(C)] struct PathRasterizationSprite { @@ -1354,6 +1246,46 @@ fn create_blend_state(device: &ID3D11Device) -> Result { } } +#[inline] +fn create_blend_state_for_path_rasterization(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_INV_SRC_ALPHA; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + 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()) + } +} + +#[inline] +fn create_blend_state_for_path_sprite(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_INV_SRC_ALPHA; + 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()) + } +} + #[inline] fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result { unsafe { @@ -1425,6 +1357,7 @@ fn set_pipeline_state( vertex_shader: &ID3D11VertexShader, fragment_shader: &ID3D11PixelShader, global_params: &[Option], + blend_state: &ID3D11BlendState, ) { unsafe { device_context.VSSetShaderResources(1, Some(buffer_view)); @@ -1435,6 +1368,7 @@ fn set_pipeline_state( device_context.PSSetShader(fragment_shader, None); device_context.VSSetConstantBuffers(0, Some(global_params)); device_context.PSSetConstantBuffers(0, Some(global_params)); + device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF); } } From 599b82fc9d351f7ed99710176e77b85300e87990 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 14:41:52 +0800 Subject: [PATCH 173/202] remove unused --- .../src/platform/windows/directx_renderer.rs | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 73ecd7cfca..63db8ed779 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -54,10 +54,9 @@ struct DirectXResources { // Path intermediate textures (with MSAA) path_intermediate_texture: ID3D11Texture2D, - path_intermediate_view: [Option; 1], + path_intermediate_srv: [Option; 1], path_intermediate_msaa_texture: ID3D11Texture2D, path_intermediate_msaa_view: [Option; 1], - path_intermediate_srv: Option, // Cached window size and viewport width: u32, @@ -68,7 +67,6 @@ struct DirectXResources { struct DirectXRenderPipelines { shadow_pipeline: PipelineState, quad_pipeline: PipelineState, - // path_rasterization_pipeline: PathRasterizationPipelineState, path_rasterization_pipeline: PipelineState, path_sprite_pipeline: PipelineState, underline_pipeline: PipelineState, @@ -415,8 +413,6 @@ impl DirectXRenderer { 0, RENDER_TARGET_FORMAT, ); - // Flush to ensure the resolve operation is complete before using the texture - self.devices.device_context.Flush(); // Restore main render target self.devices .device_context @@ -462,7 +458,7 @@ impl DirectXRenderer { // Draw the sprites with the path texture self.pipelines.path_sprite_pipeline.draw_with_texture( &self.devices.device_context, - &[self.resources.path_intermediate_srv.clone()], + &self.resources.path_intermediate_srv, &self.resources.viewport, &self.globals.global_params_buffer, &self.globals.sampler, @@ -591,10 +587,9 @@ impl DirectXResources { render_target, render_target_view, path_intermediate_texture, - path_intermediate_view, + path_intermediate_srv, path_intermediate_msaa_texture, path_intermediate_msaa_view, - path_intermediate_srv, viewport, ) = create_resources(devices, &swap_chain, width, height)?; set_rasterizer_state(&devices.device, &devices.device_context)?; @@ -604,7 +599,6 @@ impl DirectXResources { render_target, render_target_view, path_intermediate_texture, - path_intermediate_view, path_intermediate_msaa_texture, path_intermediate_msaa_view, path_intermediate_srv, @@ -625,16 +619,14 @@ impl DirectXResources { render_target, render_target_view, path_intermediate_texture, - path_intermediate_view, + path_intermediate_srv, path_intermediate_msaa_texture, path_intermediate_msaa_view, - path_intermediate_srv, viewport, ) = create_resources(devices, &self.swap_chain, width, height)?; self.render_target = render_target; self.render_target_view = render_target_view; self.path_intermediate_texture = path_intermediate_texture; - self.path_intermediate_view = path_intermediate_view; self.path_intermediate_msaa_texture = path_intermediate_msaa_texture; self.path_intermediate_msaa_view = path_intermediate_msaa_view; self.path_intermediate_srv = path_intermediate_srv; @@ -1066,16 +1058,15 @@ fn create_resources( ManuallyDrop, [Option; 1], ID3D11Texture2D, - [Option; 1], + [Option; 1], ID3D11Texture2D, [Option; 1], - Option, [D3D11_VIEWPORT; 1], )> { let (render_target, render_target_view) = create_render_target_and_its_view(&swap_chain, &devices.device)?; - let (path_intermediate_texture, path_intermediate_view, path_intermediate_srv) = - create_path_intermediate_texture_and_view(&devices.device, width, height)?; + let (path_intermediate_texture, path_intermediate_srv) = + create_path_intermediate_texture(&devices.device, width, height)?; let (path_intermediate_msaa_texture, path_intermediate_msaa_view) = create_path_intermediate_msaa_texture_and_view(&devices.device, width, height)?; let viewport = set_viewport(&devices.device_context, width as f32, height as f32); @@ -1083,10 +1074,9 @@ fn create_resources( render_target, render_target_view, path_intermediate_texture, - path_intermediate_view, + path_intermediate_srv, path_intermediate_msaa_texture, path_intermediate_msaa_view, - path_intermediate_srv, viewport, )) } @@ -1109,15 +1099,11 @@ fn create_render_target_and_its_view( } #[inline] -fn create_path_intermediate_texture_and_view( +fn create_path_intermediate_texture( device: &ID3D11Device, width: u32, height: u32, -) -> Result<( - ID3D11Texture2D, - [Option; 1], - Option, -)> { +) -> Result<(ID3D11Texture2D, [Option; 1])> { let texture = unsafe { let mut output = None; let desc = D3D11_TEXTURE2D_DESC { @@ -1139,17 +1125,10 @@ fn create_path_intermediate_texture_and_view( output.unwrap() }; - let mut render_target_view = None; - unsafe { device.CreateRenderTargetView(&texture, None, Some(&mut render_target_view))? }; - let mut shader_resource_view = None; unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? }; - Ok(( - texture, - [Some(render_target_view.unwrap())], - shader_resource_view, - )) + Ok((texture, [Some(shader_resource_view.unwrap())])) } #[inline] From 741b38f9066e4c92861767fb739ffd6333d7b59a Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 14:45:28 +0800 Subject: [PATCH 174/202] remove unused `repr(c)` --- crates/gpui/src/platform/windows/directx_renderer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 63db8ed779..bdcad0d3ab 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -79,7 +79,6 @@ struct DirectXGlobalElements { sampler: [Option; 1], } -#[repr(C)] #[cfg(not(feature = "enable-renderdoc"))] struct DirectComposition { comp_device: IDCompositionDevice, From a1f03ee42c5a31e5c5d44cdcec4e4bae5eb0ae75 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 16:03:59 +0800 Subject: [PATCH 175/202] log unknown vendor id --- crates/gpui/src/platform/windows/directx_atlas.rs | 1 - crates/gpui/src/platform/windows/directx_renderer.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 8ab4092efd..669ca5b289 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -236,7 +236,6 @@ 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, }; textures[id.index as usize].as_ref().unwrap() } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index bdcad0d3ab..702ce5d091 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -549,13 +549,13 @@ impl DirectXRenderer { 0x10DE => "NVIDIA Corporation".to_string(), 0x1002 => "AMD Corporation".to_string(), 0x8086 => "Intel Corporation".to_string(), - _ => "Unknown Vendor".to_string(), + id => format!("Unknown Vendor (ID: {:#X})", id), }; let driver_version = match desc.VendorId { 0x10DE => nvidia::get_driver_version(), 0x1002 => amd::get_driver_version(), 0x8086 => intel::get_driver_version(&self.devices.adapter), - _ => Err(anyhow::anyhow!("Unknown vendor detected.")), + id => Err(anyhow::anyhow!("Unknown vendor detected (ID: {:#X}).", id)), } .context("Failed to get gpu driver info") .log_err() From 181f324473382ddf5f61dfa93e22de625939fd2c Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 16:50:10 +0800 Subject: [PATCH 176/202] fix device lost --- .../src/platform/windows/directx_renderer.rs | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 702ce5d091..6b4e065d78 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -191,18 +191,28 @@ impl DirectXRenderer { fn handle_device_lost(&mut self) -> Result<()> { unsafe { - ManuallyDrop::drop(&mut self.devices); + #[cfg(debug_assertions)] + report_live_objects(&self.devices.device) + .context("Failed to report live objects after device lost") + .log_err(); + ManuallyDrop::drop(&mut self.resources); + self.devices.device_context.OMSetRenderTargets(None, None); + self.devices.device_context.ClearState(); + self.devices.device_context.Flush(); + + #[cfg(debug_assertions)] + report_live_objects(&self.devices.device) + .context("Failed to report live objects after device lost") + .log_err(); + + ManuallyDrop::drop(&mut self.devices); #[cfg(not(feature = "enable-renderdoc"))] ManuallyDrop::drop(&mut self._direct_composition); } + let devices = ManuallyDrop::new(DirectXDevices::new().context("Recreating DirectX devices")?); - unsafe { - devices.device_context.OMSetRenderTargets(None, None); - devices.device_context.ClearState(); - devices.device_context.Flush(); - } #[cfg(not(feature = "enable-renderdoc"))] let resources = DirectXResources::new(&devices, self.resources.width, self.resources.height)?; @@ -908,6 +918,8 @@ struct PathSprite { impl Drop for DirectXRenderer { fn drop(&mut self) { + #[cfg(debug_assertions)] + report_live_objects(&self.devices.device).ok(); unsafe { ManuallyDrop::drop(&mut self.devices); ManuallyDrop::drop(&mut self.resources); @@ -1350,6 +1362,17 @@ fn set_pipeline_state( } } +#[cfg(debug_assertions)] +fn report_live_objects(device: &ID3D11Device) -> Result<()> { + use windows::core::Interface; + + let debug_device: ID3D11Debug = device.cast()?; + unsafe { + debug_device.ReportLiveDeviceObjects(D3D11_RLDO_DETAIL)?; + } + Ok(()) +} + const BUFFER_COUNT: usize = 3; mod shader_resources { From 98f31172abb556fe5eaf17d13af5eb1e92688f84 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 16:56:01 +0800 Subject: [PATCH 177/202] fix atlas sometime fails --- crates/gpui/src/platform/windows/directx_atlas.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 669ca5b289..988943c766 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -142,7 +142,7 @@ impl DirectXAtlasState { } } - let texture = self.push_texture(size, texture_kind); + let texture = self.push_texture(size, texture_kind)?; texture.allocate(size) } @@ -150,7 +150,7 @@ impl DirectXAtlasState { &mut self, min_size: Size, kind: AtlasTextureKind, - ) -> &mut DirectXAtlasTexture { + ) -> Option<&mut DirectXAtlasTexture> { const DEFAULT_ATLAS_SIZE: Size = Size { width: DevicePixels(1024), height: DevicePixels(1024), @@ -194,9 +194,11 @@ impl DirectXAtlasState { }; let mut texture: Option = None; unsafe { + // This only returns None if the device is lost, which we will recreate later. + // So it's ok to return None here. self.device .CreateTexture2D(&texture_desc, None, Some(&mut texture)) - .unwrap(); + .ok()?; } let texture = texture.unwrap(); @@ -209,7 +211,7 @@ impl DirectXAtlasState { let mut view = None; self.device .CreateShaderResourceView(&texture, None, Some(&mut view)) - .unwrap(); + .ok()?; [view] }; let atlas_texture = DirectXAtlasTexture { @@ -225,10 +227,10 @@ impl DirectXAtlasState { }; if let Some(ix) = index { texture_list.textures[ix] = Some(atlas_texture); - texture_list.textures.get_mut(ix).unwrap().as_mut().unwrap() + texture_list.textures.get_mut(ix).unwrap().as_mut() } else { texture_list.textures.push(Some(atlas_texture)); - texture_list.textures.last_mut().unwrap().as_mut().unwrap() + texture_list.textures.last_mut().unwrap().as_mut() } } From 73eaee8f6f6e0f2133ebedbc655ad76cec358d40 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 17:44:54 +0800 Subject: [PATCH 178/202] use none instead of stretch --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 6b4e065d78..c82723c338 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1048,7 +1048,7 @@ fn create_swap_chain( }, BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, BufferCount: BUFFER_COUNT as u32, - Scaling: DXGI_SCALING_STRETCH, + Scaling: DXGI_SCALING_NONE, SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, AlphaMode: DXGI_ALPHA_MODE_IGNORE, Flags: 0, From ead7a1e1f0c27b35c4bc25a2447dad881e0586c4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 19:54:34 +0800 Subject: [PATCH 179/202] remove blade --- crates/gpui/Cargo.toml | 5 ----- crates/gpui/build.rs | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index d0a6e977bf..e88911df2f 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -217,10 +217,6 @@ xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf x11-clipboard = { version = "0.9.3", optional = true } [target.'cfg(target_os = "windows")'.dependencies] -blade-util.workspace = true -bytemuck = "1" -blade-graphics.workspace = true -blade-macros.workspace = true flume = "0.11" rand.workspace = true windows.workspace = true @@ -241,7 +237,6 @@ util = { workspace = true, features = ["test-support"] } [target.'cfg(target_os = "windows")'.build-dependencies] embed-resource = "3.0" -naga.workspace = true [target.'cfg(target_os = "macos")'.build-dependencies] bindgen = "0.71" diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 0a67262bcc..adc9296b4d 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -29,6 +29,7 @@ fn main() { } #[allow(dead_code)] +#[cfg(not(target_os = "windows"))] fn check_wgsl_shaders() { use std::path::PathBuf; use std::process; From 441731de2ef623c1447278f5e69b41dff5ec6707 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 19:59:04 +0800 Subject: [PATCH 180/202] fix build.rs --- crates/gpui/build.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index adc9296b4d..f619d5b0fb 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -11,7 +11,7 @@ fn main() { #[cfg(any( not(any(target_os = "macos", target_os = "windows")), - feature = "macos-blade" + all(target_os = "macos", feature = "macos-blade") ))] check_wgsl_shaders(); @@ -28,8 +28,10 @@ fn main() { }; } -#[allow(dead_code)] -#[cfg(not(target_os = "windows"))] +#[cfg(any( + not(any(target_os = "macos", target_os = "windows")), + all(target_os = "macos", feature = "macos-blade") +))] fn check_wgsl_shaders() { use std::path::PathBuf; use std::process; From 89a863d0121affbcceba5375bf1914c6537444d2 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 20:01:39 +0800 Subject: [PATCH 181/202] update workspace-hack --- tooling/workspace-hack/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/tooling/workspace-hack/Cargo.toml b/tooling/workspace-hack/Cargo.toml index 1026454026..80b200d2e5 100644 --- a/tooling/workspace-hack/Cargo.toml +++ b/tooling/workspace-hack/Cargo.toml @@ -564,7 +564,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } -naga = { version = "25", features = ["spv-out", "wgsl-in"] } ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } scopeguard = { version = "1" } @@ -588,7 +587,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } -naga = { version = "25", features = ["spv-out", "wgsl-in"] } proc-macro2 = { version = "1", default-features = false, features = ["span-locations"] } ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } From 554b36fd3c4ca4b752e2b65f889762c21cc6ced4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 22:44:05 +0800 Subject: [PATCH 182/202] fix `where fxc.exe` --- crates/gpui/build.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index f619d5b0fb..912efabe37 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -322,7 +322,11 @@ mod windows { } // Try to find in PATH - if let Ok(output) = std::process::Command::new("where").arg("fxc.exe").output() { + // NOTE: This has to be `where.exe` on Windows, not `which` + if let Ok(output) = std::process::Command::new("where.exe") + .arg("fxc.exe") + .output() + { if output.status.success() { let path = String::from_utf8_lossy(&output.stdout); return path.trim().to_string(); From eb3bb95c91378621901fd1e6af21a00348e837e8 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Tue, 29 Jul 2025 22:45:58 +0800 Subject: [PATCH 183/202] fix error msg --- crates/gpui/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 912efabe37..bb1fdc4bb5 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -407,7 +407,7 @@ mod windows { return; } eprintln!( - "Pixel shader compilation failed for {}:\n{}", + "Shader compilation failed for {}:\n{}", entry_point, String::from_utf8_lossy(&result.stderr) ); From 1d60984cb6ef934e5076e4117aef04ffb87c27fb Mon Sep 17 00:00:00 2001 From: Kate Date: Tue, 29 Jul 2025 21:41:11 +0200 Subject: [PATCH 184/202] get the blending state a bit closer to vulkan still looks bad :( --- .../src/platform/windows/directx_atlas.rs | 4 ++-- .../src/platform/windows/directx_renderer.rs | 13 ++++++++--- crates/gpui/src/platform/windows/shaders.hlsl | 22 ++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 8ab4092efd..068d1b6461 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -7,7 +7,7 @@ use windows::Win32::Graphics::{ D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView, ID3D11Texture2D, }, - Dxgi::Common::{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}, + Dxgi::Common::*, }; use crate::{ @@ -167,7 +167,7 @@ impl DirectXAtlasState { let bytes_per_pixel; match kind { AtlasTextureKind::Monochrome => { - pixel_format = DXGI_FORMAT_A8_UNORM; + pixel_format = DXGI_FORMAT_R8_UNORM; bind_flag = D3D11_BIND_SHADER_RESOURCE; bytes_per_pixel = 1; } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c62055d1d1..a429d2049b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -149,6 +149,11 @@ impl DirectXRenderer { } fn pre_draw(&self) -> Result<()> { + #[cfg(not(feature = "enable-renderdoc"))] + let premultiplied_alpha = 1; + #[cfg(feature = "enable-renderdoc")] + let premultiplied_alpha = 0; + update_buffer( &self.devices.device_context, self.globals.global_params_buffer[0].as_ref().unwrap(), @@ -157,6 +162,7 @@ impl DirectXRenderer { self.resources.viewport[0].Width, self.resources.viewport[0].Height, ], + premultiplied_alpha, ..Default::default() }], )?; @@ -667,7 +673,8 @@ impl DirectXGlobalElements { #[repr(C)] struct GlobalParams { viewport_size: [f32; 2], - _pad: u64, + premultiplied_alpha: u32, + _pad: u32, } struct PipelineState { @@ -1093,7 +1100,7 @@ fn create_swap_chain( // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. Scaling: DXGI_SCALING_STRETCH, SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, - AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, Flags: 0, }; Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) @@ -1262,7 +1269,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result { desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8; unsafe { let mut state = None; diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index f0c773a673..2533aa1885 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1,6 +1,7 @@ cbuffer GlobalParams: register(b0) { float2 global_viewport_size; - uint2 _global_pad; + uint premultiplied_alpha; + uint _pad; }; Texture2D t_sprite: register(t0); @@ -256,7 +257,7 @@ float pick_corner_radius(float2 center_to_point, Corners corner_radii) { } } -float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds, +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; @@ -970,7 +971,7 @@ UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underli 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, + float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); float4 color = hsla_to_rgba(underline.color); @@ -1031,6 +1032,7 @@ struct MonochromeSpriteFragmentInput { float4 position: SV_Position; float2 tile_position: POSITION; nointerpolation float4 color: COLOR; + float4 clip_distance: SV_ClipDistance; }; StructuredBuffer mono_sprites: register(t1); @@ -1052,11 +1054,21 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI return output; } +float4 blend_color(float4 color, float alpha_factor) { + float alpha = color.a * alpha_factor; + float multiplier = premultiplied_alpha != 0 ? alpha : 1.0; + return float4(color.rgb * multiplier, alpha); +} + float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target { - float4 sample = t_sprite.Sample(s_sprite, input.tile_position); + float sample = t_sprite.Sample(s_sprite, input.tile_position).r; float4 color = input.color; - color.a *= sample.a; + color.a *= sample; return color; + // if (any(input.clip_distance < 0.0)) { + // return float4(0.0, 0.0, 0.0, 0.0); + // } + // return blend_color(input.color, sample); } /* From e370c3d6013a577c1ffe1dfee73257be7522da1b Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 09:29:09 +0800 Subject: [PATCH 185/202] misc --- crates/gpui/build.rs | 2 +- crates/gpui/src/platform/windows/directx_renderer.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index bb1fdc4bb5..87dd620700 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -322,7 +322,7 @@ mod windows { } // Try to find in PATH - // NOTE: This has to be `where.exe` on Windows, not `which` + // NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe` if let Ok(output) = std::process::Command::new("where.exe") .arg("fxc.exe") .output() diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index c82723c338..bb9771994a 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -564,8 +564,8 @@ impl DirectXRenderer { let driver_version = match desc.VendorId { 0x10DE => nvidia::get_driver_version(), 0x1002 => amd::get_driver_version(), - 0x8086 => intel::get_driver_version(&self.devices.adapter), - id => Err(anyhow::anyhow!("Unknown vendor detected (ID: {:#X}).", id)), + // For Intel and other vendors, we use the DXGI API to get the driver version. + _ => dxgi::get_driver_version(&self.devices.adapter), } .context("Failed to get gpu driver info") .log_err() @@ -1710,7 +1710,7 @@ mod amd { } } -mod intel { +mod dxgi { use windows::{ Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice}, core::Interface, From cc763729a0fdad84b5d04ecfaea08cf23181f0ab Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 14:53:36 +0800 Subject: [PATCH 186/202] add `GPUI_DISABLE_DIRECT_COMPOSITION` env --- .../src/platform/windows/directx_renderer.rs | 135 ++++++++++-------- 1 file changed, 72 insertions(+), 63 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index bb9771994a..d49dacea10 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -2,16 +2,18 @@ use std::{mem::ManuallyDrop, sync::Arc}; use ::util::ResultExt; use anyhow::{Context, Result}; -use windows::Win32::{ - Foundation::{HMODULE, HWND}, - Graphics::{ - Direct3D::*, - Direct3D11::*, - Dxgi::{Common::*, *}, +use windows::{ + Win32::{ + Foundation::{HMODULE, HWND}, + Graphics::{ + Direct3D::*, + Direct3D11::*, + DirectComposition::*, + Dxgi::{Common::*, *}, + }, }, + core::Interface, }; -#[cfg(not(feature = "enable-renderdoc"))] -use windows::{Win32::Graphics::DirectComposition::*, core::Interface}; use crate::{ platform::windows::directx_renderer::shader_resources::{ @@ -20,6 +22,7 @@ use crate::{ *, }; +const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION"; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; // This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11. const PATH_MULTISAMPLE_COUNT: u32 = 4; @@ -31,8 +34,7 @@ pub(crate) struct DirectXRenderer { resources: ManuallyDrop, globals: DirectXGlobalElements, pipelines: DirectXRenderPipelines, - #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: ManuallyDrop, + direct_composition: Option, } /// Direct3D objects @@ -40,10 +42,9 @@ pub(crate) struct DirectXRenderer { pub(crate) struct DirectXDevices { adapter: IDXGIAdapter1, dxgi_factory: IDXGIFactory6, - #[cfg(not(feature = "enable-renderdoc"))] - dxgi_device: IDXGIDevice, device: ID3D11Device, device_context: ID3D11DeviceContext, + dxgi_device: Option, } struct DirectXResources { @@ -79,7 +80,6 @@ struct DirectXGlobalElements { sampler: [Option; 1], } -#[cfg(not(feature = "enable-renderdoc"))] struct DirectComposition { comp_device: IDCompositionDevice, comp_target: IDCompositionTarget, @@ -87,7 +87,7 @@ struct DirectComposition { } impl DirectXDevices { - pub(crate) fn new() -> Result { + pub(crate) fn new(disable_direct_composition: bool) -> Result { let dxgi_factory = get_dxgi_factory()?; let adapter = get_adapter(&dxgi_factory)?; let (device, device_context) = { @@ -96,13 +96,15 @@ 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()?; + let dxgi_device = if disable_direct_composition { + None + } else { + Some(device.cast()?) + }; Ok(Self { adapter, dxgi_factory, - #[cfg(not(feature = "enable-renderdoc"))] dxgi_device, device, device_context, @@ -112,21 +114,28 @@ impl DirectXDevices { impl DirectXRenderer { pub(crate) fn new(hwnd: HWND) -> Result { - let devices = ManuallyDrop::new(DirectXDevices::new().context("Creating DirectX devices")?); + let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) + .is_ok_and(|value| value == "true" || value == "1"); + if disable_direct_composition { + log::info!("Direct Composition is disabled."); + } + + let devices = ManuallyDrop::new( + DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?, + ); let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context)); - #[cfg(not(feature = "enable-renderdoc"))] - let resources = DirectXResources::new(&devices, 1, 1)?; - #[cfg(feature = "enable-renderdoc")] - let resources = DirectXResources::new(&devices, 1, 1, hwnd)?; - + let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition)?; 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)?; + let direct_composition = if disable_direct_composition { + None + } else { + let composition = DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), hwnd)?; + composition.set_swap_chain(&resources.swap_chain)?; + Some(composition) + }; Ok(DirectXRenderer { hwnd, @@ -135,8 +144,7 @@ impl DirectXRenderer { resources, globals, pipelines, - #[cfg(not(feature = "enable-renderdoc"))] - _direct_composition: direct_composition, + direct_composition, }) } @@ -190,6 +198,8 @@ impl DirectXRenderer { } fn handle_device_lost(&mut self) -> Result<()> { + let disable_direct_composition = self.direct_composition.is_none(); + unsafe { #[cfg(debug_assertions)] report_live_objects(&self.devices.device) @@ -206,30 +216,32 @@ impl DirectXRenderer { .context("Failed to report live objects after device lost") .log_err(); + drop(self.direct_composition.take()); ManuallyDrop::drop(&mut self.devices); - #[cfg(not(feature = "enable-renderdoc"))] - ManuallyDrop::drop(&mut self._direct_composition); } - let devices = - ManuallyDrop::new(DirectXDevices::new().context("Recreating DirectX devices")?); - #[cfg(not(feature = "enable-renderdoc"))] - let resources = - DirectXResources::new(&devices, self.resources.width, self.resources.height)?; - #[cfg(feature = "enable-renderdoc")] + let devices = ManuallyDrop::new( + DirectXDevices::new(disable_direct_composition) + .context("Recreating DirectX devices")?, + ); let resources = DirectXResources::new( &devices, self.resources.width, self.resources.height, self.hwnd, + disable_direct_composition, )?; 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, self.hwnd)?; - #[cfg(not(feature = "enable-renderdoc"))] - direct_composition.set_swap_chain(&resources.swap_chain)?; + let direct_composition = if disable_direct_composition { + None + } else { + let composition = + DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), self.hwnd)?; + composition.set_swap_chain(&resources.swap_chain)?; + Some(composition) + }; self.atlas .handle_device_lost(&devices.device, &devices.device_context); @@ -237,10 +249,8 @@ impl DirectXRenderer { self.resources = resources; self.globals = globals; self.pipelines = pipelines; - #[cfg(not(feature = "enable-renderdoc"))] - { - self._direct_composition = direct_composition; - } + self.direct_composition = direct_composition; + unsafe { self.devices .device_context @@ -584,13 +594,19 @@ impl DirectXResources { devices: &DirectXDevices, width: u32, height: u32, - #[cfg(feature = "enable-renderdoc")] hwnd: HWND, + hwnd: HWND, + disable_direct_composition: bool, ) -> Result> { - #[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(&devices.dxgi_factory, &devices.device, hwnd, width, height)?; + let swap_chain = if disable_direct_composition { + create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)? + } else { + create_swap_chain_for_composition( + &devices.dxgi_factory, + &devices.device, + width, + height, + )? + }; let ( render_target, @@ -710,18 +726,17 @@ impl DirectXRenderPipelines { } } -#[cfg(not(feature = "enable-renderdoc"))] impl DirectComposition { - pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result> { + 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(ManuallyDrop::new(Self { + Ok(Self { comp_device, comp_target, comp_visual, - })) + }) } pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { @@ -923,8 +938,6 @@ impl Drop for DirectXRenderer { unsafe { ManuallyDrop::drop(&mut self.devices); ManuallyDrop::drop(&mut self.resources); - #[cfg(not(feature = "enable-renderdoc"))] - ManuallyDrop::drop(&mut self._direct_composition); } } } @@ -995,13 +1008,12 @@ fn get_device( Ok(()) } -#[cfg(not(feature = "enable-renderdoc"))] +#[inline] fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result { Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) } -#[cfg(not(feature = "enable-renderdoc"))] -fn create_swap_chain( +fn create_swap_chain_for_composition( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, width: u32, @@ -1027,7 +1039,6 @@ fn create_swap_chain( Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) } -#[cfg(feature = "enable-renderdoc")] fn create_swap_chain( dxgi_factory: &IDXGIFactory6, device: &ID3D11Device, @@ -1364,8 +1375,6 @@ fn set_pipeline_state( #[cfg(debug_assertions)] fn report_live_objects(device: &ID3D11Device) -> Result<()> { - use windows::core::Interface; - let debug_device: ID3D11Debug = device.cast()?; unsafe { debug_device.ReportLiveDeviceObjects(D3D11_RLDO_DETAIL)?; From d194bf4f52d52fb90e7c0d91661529c89eb62784 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 17:36:14 +0800 Subject: [PATCH 187/202] QOL improvement when device lost happens --- crates/gpui/src/platform.rs | 2 ++ .../src/platform/windows/directx_renderer.rs | 11 ++++++---- crates/gpui/src/platform/windows/events.rs | 20 ++++++++++++++++-- crates/gpui/src/platform/windows/window.rs | 21 ++++++++++++------- crates/gpui/src/window.rs | 2 +- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 76a5eb4c02..b495d70dfd 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -447,6 +447,8 @@ impl Tiling { #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] pub(crate) struct RequestFrameOptions { pub(crate) require_presentation: bool, + /// Force refresh of all rendering states when true + pub(crate) force_render: bool, } pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index d49dacea10..db93116e1d 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -22,7 +22,7 @@ use crate::{ *, }; -const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION"; +pub(crate) const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION"; const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM; // This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11. const PATH_MULTISAMPLE_COUNT: u32 = 4; @@ -113,9 +113,7 @@ impl DirectXDevices { } impl DirectXRenderer { - pub(crate) fn new(hwnd: HWND) -> Result { - let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) - .is_ok_and(|value| value == "true" || value == "1"); + pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result { if disable_direct_composition { log::info!("Direct Composition is disabled."); } @@ -198,6 +196,9 @@ impl DirectXRenderer { } fn handle_device_lost(&mut self) -> Result<()> { + // Here we wait a bit to ensure the the system has time to recover from the device lost state. + // If we don't wait, the final drawing result will be blank. + std::thread::sleep(std::time::Duration::from_millis(300)); let disable_direct_composition = self.direct_composition.is_none(); unsafe { @@ -323,6 +324,8 @@ impl DirectXRenderer { "DirectX device removed or reset when resizing. Reason: {:?}", reason ); + self.resources.width = width; + self.resources.height = height; self.handle_device_lost()?; return Ok(()); } diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 4b905302af..61f410a8c6 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -23,6 +23,7 @@ pub(crate) const WM_GPUI_CURSOR_STYLE_CHANGED: u32 = WM_USER + 1; pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2; pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3; pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4; +pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5; const SIZE_MOVE_LOOP_TIMER_ID: usize = 1; const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1; @@ -97,6 +98,7 @@ pub(crate) fn handle_msg( WM_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr), WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr), WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr), + WM_GPUI_FORCE_UPDATE_WINDOW => draw_window(handle, true, state_ptr), _ => None, }; if let Some(n) = handled { @@ -1202,6 +1204,19 @@ fn handle_device_change_msg( state_ptr: Rc, ) -> Option { if wparam.0 == DBT_DEVNODES_CHANGED as usize { + // The reason for sending this message is to actually trigger a redraw of the window. + unsafe { + PostMessageW( + Some(handle), + WM_GPUI_FORCE_UPDATE_WINDOW, + WPARAM(0), + LPARAM(0), + ) + .log_err(); + } + // If the GPU device is lost, this redraw will take care of recreating the device context. + // The WM_GPUI_FORCE_UPDATE_WINDOW message will take care of redrawing the window, after + // the device context has been recreated. draw_window(handle, true, state_ptr) } else { // Other device change messages are not handled. @@ -1212,7 +1227,7 @@ fn handle_device_change_msg( #[inline] fn draw_window( handle: HWND, - force_draw: bool, + force_render: bool, state_ptr: Rc, ) -> Option { let mut request_frame = state_ptr @@ -1222,7 +1237,8 @@ fn draw_window( .request_frame .take()?; request_frame(RequestFrameOptions { - require_presentation: force_draw, + require_presentation: false, + force_render, }); state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); unsafe { ValidateRect(Some(handle), None).ok().log_err() }; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 159cb6d95f..9e030ee41b 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -84,6 +84,7 @@ impl WindowsWindowState { display: WindowsDisplay, min_size: Option>, appearance: WindowAppearance, + disable_direct_composition: bool, ) -> Result { let scale_factor = { let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; @@ -100,7 +101,7 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; - let renderer = DirectXRenderer::new(hwnd)?; + let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)?; let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; @@ -208,6 +209,7 @@ impl WindowsWindowStatePtr { context.display, context.min_size, context.appearance, + context.disable_direct_composition, )?); Ok(Rc::new_cyclic(|this| Self { @@ -339,6 +341,7 @@ struct WindowCreateContext { main_receiver: flume::Receiver, main_thread_id_win32: u32, appearance: WindowAppearance, + disable_direct_composition: bool, } impl WindowsWindow { @@ -371,17 +374,20 @@ impl WindowsWindow { .map(|title| title.as_ref()) .unwrap_or(""), ); - let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp { - ( - WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP, - WINDOW_STYLE(0x0), - ) + let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) + .is_ok_and(|value| value == "true" || value == "1"); + + let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp { + (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0)) } else { ( - WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP, + WS_EX_APPWINDOW, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; + if !disable_direct_composition { + dwexstyle |= WS_EX_NOREDIRECTIONBITMAP; + } let hinstance = get_module_handle(); let display = if let Some(display_id) = params.display_id { @@ -406,6 +412,7 @@ impl WindowsWindow { main_receiver, main_thread_id_win32, appearance, + disable_direct_composition, }; let lpparam = Some(&context as *const _ as *const _); let creation_result = unsafe { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 01fbfff1c5..6ebb1cac40 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -1020,7 +1020,7 @@ impl Window { || (active.get() && last_input_timestamp.get().elapsed() < Duration::from_secs(1)); - if invalidator.is_dirty() { + if invalidator.is_dirty() || request_frame_options.force_render { measure("frame duration", || { handle .update(&mut cx, |_, window, cx| { From 98692cc928a514df4a4dc472a27c2917f16ccafa Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 17:42:47 +0800 Subject: [PATCH 188/202] fix linux --- crates/gpui/src/platform/linux/x11/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index d1cb7d00cc..0d98c1db19 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1793,6 +1793,7 @@ impl X11ClientState { drop(state); window.refresh(RequestFrameOptions { require_presentation: expose_event_received, + force_render: false, }); } xcb_connection From 8c8b91470ae43a75227bf3905b1d9c390e74ec7f Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 18:10:04 +0800 Subject: [PATCH 189/202] remove unused --- crates/gpui/src/platform/windows/directx_renderer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index db93116e1d..dfaf067070 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1216,7 +1216,6 @@ fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceCont SlopeScaledDepthBias: 0.0, DepthClipEnable: true.into(), ScissorEnable: false.into(), - // MultisampleEnable: false.into(), MultisampleEnable: true.into(), AntialiasedLineEnable: false.into(), }; From 0a0803e2a78e1eced2e6277a7d927a233e9232b9 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 18:22:30 +0800 Subject: [PATCH 190/202] remove `enable-renderdoc` --- crates/gpui/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index e88911df2f..2bf49fa7d8 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -72,7 +72,6 @@ screen-capture = [ "scap", ] windows-manifest = [] -enable-renderdoc = [] [lib] path = "src/gpui.rs" From 7c8074ce5cdc81d63aca58953d5afa02d3b3eebb Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 22:56:28 +0800 Subject: [PATCH 191/202] update feature level --- crates/gpui/build.rs | 4 ++-- crates/gpui/src/platform/windows/directx_renderer.rs | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 87dd620700..2b574ebdd8 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -360,7 +360,7 @@ mod windows { &output_file, &const_name, shader_path, - "vs_5_0", + "vs_4_1", ); generate_rust_binding(&const_name, &output_file, &rust_binding_path); @@ -373,7 +373,7 @@ mod windows { &output_file, &const_name, shader_path, - "ps_5_0", + "ps_4_1", ); generate_rust_binding(&const_name, &output_file, &rust_binding_path); } diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index dfaf067070..3ec2aa2f15 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -1000,8 +1000,11 @@ fn get_device( 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]), + Some(&[ + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + ]), D3D11_SDK_VERSION, device, None, @@ -1493,8 +1496,8 @@ mod shader_resources { } ); let target = match target { - ShaderTarget::Vertex => "vs_5_0\0", - ShaderTarget::Fragment => "ps_5_0\0", + ShaderTarget::Vertex => "vs_4_1\0", + ShaderTarget::Fragment => "ps_4_1\0", }; let mut compile_blob = None; From 345fd526fc620079eaba75a6b39fcfc23d904f88 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 23:17:15 +0800 Subject: [PATCH 192/202] log the feature level we are using --- .../src/platform/windows/directx_renderer.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 3ec2aa2f15..67e2cbc14b 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -93,7 +93,25 @@ impl DirectXDevices { let (device, device_context) = { let mut device: Option = None; let mut context: Option = None; - get_device(&adapter, Some(&mut device), Some(&mut context))?; + let mut feature_level = D3D_FEATURE_LEVEL::default(); + get_device( + &adapter, + Some(&mut device), + Some(&mut context), + Some(&mut feature_level), + )?; + match feature_level { + D3D_FEATURE_LEVEL_11_1 => { + log::info!("Created device with Direct3D 11.1 feature level.") + } + D3D_FEATURE_LEVEL_11_0 => { + log::info!("Created device with Direct3D 11.0 feature level.") + } + D3D_FEATURE_LEVEL_10_1 => { + log::info!("Created device with Direct3D 10.1 feature level.") + } + _ => unreachable!(), + } (device.unwrap(), context.unwrap()) }; let dxgi_device = if disable_direct_composition { @@ -976,7 +994,7 @@ fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result { } // Check to see whether the adapter supports Direct3D 11, but don't // create the actual device yet. - if get_device(&adapter, None, None).log_err().is_some() { + if get_device(&adapter, None, None, None).log_err().is_some() { return Ok(adapter); } } @@ -988,6 +1006,7 @@ fn get_device( adapter: &IDXGIAdapter1, device: Option<*mut Option>, context: Option<*mut Option>, + feature_level: Option<*mut D3D_FEATURE_LEVEL>, ) -> Result<()> { #[cfg(debug_assertions)] let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG; @@ -1007,7 +1026,7 @@ fn get_device( ]), D3D11_SDK_VERSION, device, - None, + feature_level, context, )?; } From 6a91ac26d759ff0e3f0ee49cbe697b46b94447d4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Wed, 30 Jul 2025 23:29:32 +0800 Subject: [PATCH 193/202] remove unneeded change --- crates/gpui/src/platform/windows/platform.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index a887c716d8..8433e29c6d 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -436,8 +436,7 @@ impl Platform for WindowsPlatform { handle: AnyWindowHandle, options: WindowParams, ) -> Result> { - let window = WindowsWindow::new(handle, options, self.generate_creation_info()) - .inspect_err(|err| show_error("Failed to open new window", err.to_string()))?; + let window = WindowsWindow::new(handle, options, self.generate_creation_info())?; let handle = window.get_raw_handle(); self.raw_window_handles.write().push(handle); From 34e433ad90a98974be523f5c8241b52dd32b27c4 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 31 Jul 2025 00:05:56 +0800 Subject: [PATCH 194/202] check debug layer before creating --- crates/gpui/src/platform/windows/directx_renderer.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 67e2cbc14b..8c65bc3921 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -974,7 +974,17 @@ impl Drop for DirectXResources { #[inline] fn get_dxgi_factory() -> Result { #[cfg(debug_assertions)] - let factory_flag = DXGI_CREATE_FACTORY_DEBUG; + let factory_flag = if unsafe { DXGIGetDebugInterface1::(0) } + .log_err() + .is_some() + { + DXGI_CREATE_FACTORY_DEBUG + } else { + log::error!( + "Failed to get DXGI debug interface. DirectX debugging features will be disabled." + ); + DXGI_CREATE_FACTORY_FLAGS::default() + }; #[cfg(not(debug_assertions))] let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default(); unsafe { Ok(CreateDXGIFactory2(factory_flag)?) } From b8f85be372ceeb8fca998e7f72ec5fefa376d4ad Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 31 Jul 2025 00:58:23 +0800 Subject: [PATCH 195/202] use `log::warn` instead --- crates/gpui/src/platform/windows/directx_renderer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 8c65bc3921..0215d3e67e 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -980,7 +980,7 @@ fn get_dxgi_factory() -> Result { { DXGI_CREATE_FACTORY_DEBUG } else { - log::error!( + log::warn!( "Failed to get DXGI debug interface. DirectX debugging features will be disabled." ); DXGI_CREATE_FACTORY_FLAGS::default() From b75f6e2210d41762a05d273645d9f71f67771345 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 31 Jul 2025 00:59:46 +0800 Subject: [PATCH 196/202] use `if-else` --- crates/gpui/src/platform/windows/directx_renderer.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 0215d3e67e..544b64cf92 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -346,9 +346,10 @@ impl DirectXRenderer { self.resources.height = height; self.handle_device_lost()?; return Ok(()); + } else { + log::error!("Failed to resize swap chain: {:?}", e); + return Err(e.into()); } - log::error!("Failed to resize swap chain: {:?}", e); - return Err(e.into()); } } From 80be0e29b987e27ce9b53d6a4ec2bcb86f35ff43 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 31 Jul 2025 01:15:39 +0800 Subject: [PATCH 197/202] add more context for errors --- crates/gpui/src/platform/windows/directx_renderer.rs | 9 +++++---- crates/gpui/src/platform/windows/window.rs | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index 544b64cf92..cdc0f289c1 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -88,8 +88,8 @@ struct DirectComposition { impl DirectXDevices { pub(crate) fn new(disable_direct_composition: bool) -> Result { - let dxgi_factory = get_dxgi_factory()?; - let adapter = get_adapter(&dxgi_factory)?; + let dxgi_factory = get_dxgi_factory().context("Creating DXGI factory")?; + let adapter = get_adapter(&dxgi_factory).context("Getting DXGI adapter")?; let (device, device_context) = { let mut device: Option = None; let mut context: Option = None; @@ -99,7 +99,8 @@ impl DirectXDevices { Some(&mut device), Some(&mut context), Some(&mut feature_level), - )?; + ) + .context("Creating Direct3D device")?; match feature_level { D3D_FEATURE_LEVEL_11_1 => { log::info!("Created device with Direct3D 11.1 feature level.") @@ -117,7 +118,7 @@ impl DirectXDevices { let dxgi_device = if disable_direct_composition { None } else { - Some(device.cast()?) + Some(device.cast().context("Creating DXGI device")?) }; Ok(Self { diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 9e030ee41b..1141e93565 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -101,7 +101,8 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let restore_from_minimized = None; - let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)?; + let renderer = DirectXRenderer::new(hwnd, disable_direct_composition) + .context("Creating DirectX renderer")?; let callbacks = Callbacks::default(); let input_handler = None; let pending_surrogate = None; From c995dd2016a3d9f8bd5dfe5d43972bc8de768ca6 Mon Sep 17 00:00:00 2001 From: Junkui Zhang <364772080@qq.com> Date: Thu, 31 Jul 2025 01:18:29 +0800 Subject: [PATCH 198/202] more context --- .../src/platform/windows/directx_renderer.rs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index cdc0f289c1..3698f10814 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -87,7 +87,7 @@ struct DirectComposition { } impl DirectXDevices { - pub(crate) fn new(disable_direct_composition: bool) -> Result { + pub(crate) fn new(disable_direct_composition: bool) -> Result> { let dxgi_factory = get_dxgi_factory().context("Creating DXGI factory")?; let adapter = get_adapter(&dxgi_factory).context("Getting DXGI adapter")?; let (device, device_context) = { @@ -121,13 +121,13 @@ impl DirectXDevices { Some(device.cast().context("Creating DXGI device")?) }; - Ok(Self { + Ok(ManuallyDrop::new(Self { adapter, dxgi_factory, dxgi_device, device, device_context, - }) + })) } } @@ -137,20 +137,25 @@ impl DirectXRenderer { log::info!("Direct Composition is disabled."); } - let devices = ManuallyDrop::new( - DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?, - ); + let devices = + DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?; let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context)); - let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition)?; - let globals = DirectXGlobalElements::new(&devices.device)?; - let pipelines = DirectXRenderPipelines::new(&devices.device)?; + let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition) + .context("Creating DirectX resources")?; + let globals = DirectXGlobalElements::new(&devices.device) + .context("Creating DirectX global elements")?; + let pipelines = DirectXRenderPipelines::new(&devices.device) + .context("Creating DirectX render pipelines")?; let direct_composition = if disable_direct_composition { None } else { - let composition = DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), hwnd)?; - composition.set_swap_chain(&resources.swap_chain)?; + let composition = DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), hwnd) + .context("Creating DirectComposition")?; + composition + .set_swap_chain(&resources.swap_chain) + .context("Setting swap chain for DirectComposition")?; Some(composition) }; @@ -240,10 +245,8 @@ impl DirectXRenderer { ManuallyDrop::drop(&mut self.devices); } - let devices = ManuallyDrop::new( - DirectXDevices::new(disable_direct_composition) - .context("Recreating DirectX devices")?, - ); + let devices = DirectXDevices::new(disable_direct_composition) + .context("Recreating DirectX devices")?; let resources = DirectXResources::new( &devices, self.resources.width, From 99c5b72b3d69cfcc5f6196669e3926dae811b2dc Mon Sep 17 00:00:00 2001 From: Kate Date: Thu, 31 Jul 2025 00:00:35 +0200 Subject: [PATCH 199/202] finally the font looks nice --- crates/gpui/examples/text.rs | 5 ++- .../gpui/src/platform/windows/direct_write.rs | 37 +++++++++---------- .../src/platform/windows/directx_atlas.rs | 1 + crates/gpui/src/platform/windows/shaders.hlsl | 19 +++++++--- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 1166bb2795..8de119c7a1 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -97,7 +97,7 @@ struct Specimen { impl Specimen { pub fn new(id: usize) -> Self { - let string = SharedString::new_static("The quick brown fox jumps over the lazy dog"); + let string = SharedString::new_static("many thihngs could be written here"); let id_string = format!("specimen-{}", id); let id = ElementId::Name(id_string.into()); Self { @@ -151,6 +151,7 @@ impl RenderOnce for Specimen { div() .id(self.id) .bg(theme.bg) + .font_family("Zed Plex Mono") .text_color(theme.fg) .text_size(px(font_size * scale)) .line_height(relative(line_height)) @@ -270,7 +271,7 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - .child(div().child(CharacterGrid::new().scale(base))) + // .child(div().child(CharacterGrid::new().scale(base))) .child( div() .child(Specimen::new(self.next_id()).scale(step_down_2)) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index f8826b0171..80ae3c8ea5 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -59,8 +59,8 @@ unsafe impl Send for Syncer {} unsafe impl Sync for Syncer {} struct DirectWriteState { - #[cfg(feature = "enable-renderdoc")] - renderdoc: Syncer>>>, + // #[cfg(feature = "enable-renderdoc")] + // renderdoc: Syncer>>>, components: DirectWriteComponent, system_ui_font_name: SharedString, system_font_collection: IDWriteFontCollection1, @@ -236,8 +236,8 @@ impl DirectWriteTextSystem { let system_ui_font_name = get_system_ui_font_name(); Ok(Self(RwLock::new(DirectWriteState { - #[cfg(feature = "enable-renderdoc")] - renderdoc: Syncer(Arc::new(RwLock::new(renderdoc::RenderDoc::new().unwrap()))), + // #[cfg(feature = "enable-renderdoc")] + // renderdoc: Syncer(Arc::new(RwLock::new(renderdoc::RenderDoc::new().unwrap()))), components, system_ui_font_name, system_font_collection, @@ -965,11 +965,10 @@ impl DirectWriteState { let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x; if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() { - let avg = (alpha_data[texture_idx] as u32 - + alpha_data[texture_idx + 1] as u32 - + alpha_data[texture_idx + 2] as u32) - / 3; - bitmap_data[bitmap_idx] = avg as u8; + let max_value = alpha_data[texture_idx] + .max(alpha_data[texture_idx + 1]) + .max(alpha_data[texture_idx + 2]); + bitmap_data[bitmap_idx] = max_value; } } } @@ -1157,11 +1156,11 @@ impl DirectWriteState { texture.unwrap() }; - #[cfg(feature = "enable-renderdoc")] - self.renderdoc - .0 - .write() - .start_frame_capture(std::ptr::null(), std::ptr::null()); + // #[cfg(feature = "enable-renderdoc")] + // self.renderdoc + // .0 + // .write() + // .start_frame_capture(std::ptr::null(), std::ptr::null()); let device_context = &gpu_state.device_context; unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) }; @@ -1240,11 +1239,11 @@ impl DirectWriteState { }; } - #[cfg(feature = "enable-renderdoc")] - self.renderdoc - .0 - .write() - .end_frame_capture(std::ptr::null(), std::ptr::null()); + // #[cfg(feature = "enable-renderdoc")] + // self.renderdoc + // .0 + // .write() + // .end_frame_capture(std::ptr::null(), std::ptr::null()); println!("render finished"); diff --git a/crates/gpui/src/platform/windows/directx_atlas.rs b/crates/gpui/src/platform/windows/directx_atlas.rs index 068d1b6461..7ad293cd39 100644 --- a/crates/gpui/src/platform/windows/directx_atlas.rs +++ b/crates/gpui/src/platform/windows/directx_atlas.rs @@ -264,6 +264,7 @@ impl DirectXAtlasTexture { bounds: Bounds, bytes: &[u8], ) { + println!("{:?}", bounds); unsafe { device_context.UpdateSubresource( &self.texture, diff --git a/crates/gpui/src/platform/windows/shaders.hlsl b/crates/gpui/src/platform/windows/shaders.hlsl index 2533aa1885..33f2e03925 100644 --- a/crates/gpui/src/platform/windows/shaders.hlsl +++ b/crates/gpui/src/platform/windows/shaders.hlsl @@ -1060,15 +1060,22 @@ float4 blend_color(float4 color, float alpha_factor) { return float4(color.rgb * multiplier, alpha); } +float3 linear_to_srgbee(float3 l) { + bool cutoff = l < float3(0.0031308, 0.0031308, 0.0031308); + float3 higher = float3(1.055, 1.055, 1.055) * pow(l, float3(1.0 / 2.4, 1.0 / 2.4, 1.0 / 2.4)) - float3(0.055, 0.055, 0.055); + float3 lower = l * float3(12.92, 12.92, 12.92); + return cutoff ? lower : higher; +} + float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target { float sample = t_sprite.Sample(s_sprite, input.tile_position).r; float4 color = input.color; - color.a *= sample; - return color; - // if (any(input.clip_distance < 0.0)) { - // return float4(0.0, 0.0, 0.0, 0.0); - // } - // return blend_color(input.color, sample); + // color.a *= sample; + // return float4(color.rgb, color.a); + if (any(input.clip_distance < 0.0)) { + return float4(0.0, 0.0, 0.0, 0.0); + } + return blend_color(input.color, sample); } /* From b3372e7eacc9d44cde943baa3e31326e93b81037 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 30 Jul 2025 15:51:26 -0700 Subject: [PATCH 200/202] Read direct_composition env var once, pass it everywhere --- crates/gpui/src/platform/windows/platform.rs | 9 ++++++++- crates/gpui/src/platform/windows/window.rs | 3 +-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 27acb91f68..bc09cc199d 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -44,6 +44,7 @@ pub(crate) struct WindowsPlatform { drop_target_helper: IDropTargetHelper, validation_number: usize, main_thread_id_win32: u32, + disable_direct_composition: bool, } pub(crate) struct WindowsPlatformState { @@ -93,9 +94,12 @@ impl WindowsPlatform { main_thread_id_win32, validation_number, )); + let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) + .is_ok_and(|value| value == "true" || value == "1"); let background_executor = BackgroundExecutor::new(dispatcher.clone()); let foreground_executor = ForegroundExecutor::new(dispatcher); - let directx_devices = DirectXDevices::new().context("Unable to init directx devices.")?; + let directx_devices = DirectXDevices::new(disable_direct_composition) + .context("Unable to init directx devices.")?; let bitmap_factory = ManuallyDrop::new(unsafe { CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER) .context("Error creating bitmap factory.")? @@ -121,6 +125,7 @@ impl WindowsPlatform { background_executor, foreground_executor, text_system, + disable_direct_composition, windows_version, bitmap_factory, drop_target_helper, @@ -185,6 +190,7 @@ impl WindowsPlatform { validation_number: self.validation_number, main_receiver: self.main_receiver.clone(), main_thread_id_win32: self.main_thread_id_win32, + disable_direct_composition: self.disable_direct_composition, } } @@ -716,6 +722,7 @@ pub(crate) struct WindowCreationInfo { pub(crate) validation_number: usize, pub(crate) main_receiver: flume::Receiver, pub(crate) main_thread_id_win32: u32, + pub(crate) disable_direct_composition: bool, } fn open_target(target: &str) { diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 1141e93565..68b667569b 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -360,6 +360,7 @@ impl WindowsWindow { validation_number, main_receiver, main_thread_id_win32, + disable_direct_composition, } = creation_info; let classname = register_wnd_class(icon); let hide_title_bar = params @@ -375,8 +376,6 @@ impl WindowsWindow { .map(|title| title.as_ref()) .unwrap_or(""), ); - let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION) - .is_ok_and(|value| value == "true" || value == "1"); let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp { (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0)) From 3519e8fd7c30e982c83975f251402db5da8d94cd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 30 Jul 2025 15:59:47 -0700 Subject: [PATCH 201/202] Restore text example --- crates/gpui/examples/text.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/gpui/examples/text.rs b/crates/gpui/examples/text.rs index 8de119c7a1..1166bb2795 100644 --- a/crates/gpui/examples/text.rs +++ b/crates/gpui/examples/text.rs @@ -97,7 +97,7 @@ struct Specimen { impl Specimen { pub fn new(id: usize) -> Self { - let string = SharedString::new_static("many thihngs could be written here"); + let string = SharedString::new_static("The quick brown fox jumps over the lazy dog"); let id_string = format!("specimen-{}", id); let id = ElementId::Name(id_string.into()); Self { @@ -151,7 +151,6 @@ impl RenderOnce for Specimen { div() .id(self.id) .bg(theme.bg) - .font_family("Zed Plex Mono") .text_color(theme.fg) .text_size(px(font_size * scale)) .line_height(relative(line_height)) @@ -271,7 +270,7 @@ impl Render for TextExample { .overflow_x_hidden() .bg(rgb(0xffffff)) .size_full() - // .child(div().child(CharacterGrid::new().scale(base))) + .child(div().child(CharacterGrid::new().scale(base))) .child( div() .child(Specimen::new(self.next_id()).scale(step_down_2)) From 4a3d56749e028c5aae65ca0826813b1e6de785fd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 30 Jul 2025 16:04:59 -0700 Subject: [PATCH 202/202] Remove debugging code, commented code --- Cargo.lock | 25 ----------------- crates/gpui/Cargo.toml | 2 -- .../gpui/src/platform/windows/direct_write.rs | 27 ------------------- .../src/platform/windows/directx_renderer.rs | 3 --- 4 files changed, 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0ca736fb6..25196fc349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5727,9 +5727,6 @@ name = "float-cmp" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] [[package]] name = "float-ord" @@ -7361,7 +7358,6 @@ dependencies = [ "rand 0.8.5", "raw-window-handle", "refineable", - "renderdoc", "reqwest_client", "resvg", "scap", @@ -13468,27 +13464,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "renderdoc" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac633a08f39bf3268714799bd8c4a1a19c19c203d817d3448bb8b91c97817cf0" -dependencies = [ - "bitflags 2.9.0", - "float-cmp", - "libloading", - "once_cell", - "renderdoc-sys", - "winapi", - "wio", -] - -[[package]] -name = "renderdoc-sys" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" - [[package]] name = "repl" version = "0.1.0" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index cc7b1d7fb8..2bf49fa7d8 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -130,8 +130,6 @@ lyon = "1.0" workspace-hack.workspace = true libc.workspace = true -renderdoc = "0.12.1" - [target.'cfg(target_os = "macos")'.dependencies] block = "0.1" cocoa.workspace = true diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index 80ae3c8ea5..35d85a1462 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -54,13 +54,7 @@ struct GPUState { pixel_shader: ID3D11PixelShader, } -struct Syncer(T); -unsafe impl Send for Syncer {} -unsafe impl Sync for Syncer {} - struct DirectWriteState { - // #[cfg(feature = "enable-renderdoc")] - // renderdoc: Syncer>>>, components: DirectWriteComponent, system_ui_font_name: SharedString, system_font_collection: IDWriteFontCollection1, @@ -236,8 +230,6 @@ impl DirectWriteTextSystem { let system_ui_font_name = get_system_ui_font_name(); Ok(Self(RwLock::new(DirectWriteState { - // #[cfg(feature = "enable-renderdoc")] - // renderdoc: Syncer(Arc::new(RwLock::new(renderdoc::RenderDoc::new().unwrap()))), components, system_ui_font_name, system_font_collection, @@ -1156,12 +1148,6 @@ impl DirectWriteState { texture.unwrap() }; - // #[cfg(feature = "enable-renderdoc")] - // self.renderdoc - // .0 - // .write() - // .start_frame_capture(std::ptr::null(), std::ptr::null()); - let device_context = &gpu_state.device_context; unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) }; unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) }; @@ -1239,14 +1225,6 @@ impl DirectWriteState { }; } - // #[cfg(feature = "enable-renderdoc")] - // self.renderdoc - // .0 - // .write() - // .end_frame_capture(std::ptr::null(), std::ptr::null()); - - println!("render finished"); - Ok(rasterized) } @@ -1893,11 +1871,6 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result { Ok(String::from_utf16_lossy(&name_vec[..name_length])) } -#[inline] -fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] { - [color.r, color.g, color.b, color.a] -} - fn get_system_ui_font_name() -> SharedString { unsafe { let mut info: LOGFONTW = std::mem::zeroed(); diff --git a/crates/gpui/src/platform/windows/directx_renderer.rs b/crates/gpui/src/platform/windows/directx_renderer.rs index cf5b538cea..9fb96e72a7 100644 --- a/crates/gpui/src/platform/windows/directx_renderer.rs +++ b/crates/gpui/src/platform/windows/directx_renderer.rs @@ -175,10 +175,7 @@ impl DirectXRenderer { } fn pre_draw(&self) -> Result<()> { - #[cfg(not(feature = "enable-renderdoc"))] let premultiplied_alpha = 1; - #[cfg(feature = "enable-renderdoc")] - let premultiplied_alpha = 0; update_buffer( &self.devices.device_context,