diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 284664d02f3d8a1cbeff54b490b8010615578d32..1b687a2b361acdee3d78a27fbff91443409dc2db 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 7fc9c24393907d3991edcf9ae82b25eee419e766..d60398690930e0ccd1531a95603b58fa72fec895 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 0000000000000000000000000000000000000000..b19bc05ac9a4c307f4278f06d0dfde4d2a8fe158 --- /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 cbe4f50ba72ff73b817914dbf889c55d23800c2e..655a88393890232de876572d388cae2879109ead 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 54478e9b02dc4a982e72d0fdd487cea158d7dae7..a857b350ece7ee5feaa2edf22987b65183597ec4 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 ae6c4cc73d460b2c00fcbe3a09392fc02d85e2ad..d8415a65017bd73ce02aca15329a21f6404afaec 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 {