prepare for gpu rasterization

Kate created

Change summary

crates/gpui/Cargo.toml                                  |   2 
crates/gpui/src/color.rs                                |   1 
crates/gpui/src/platform/windows/color_text_raster.hlsl |  22 
crates/gpui/src/platform/windows/direct_write.rs        | 401 ++++++++++
crates/gpui/src/platform/windows/directx_renderer.rs    |  22 
crates/gpui/src/platform/windows/platform.rs            |   4 
6 files changed, 415 insertions(+), 37 deletions(-)

Detailed changes

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

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,

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);
+}

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>(T);
+unsafe impl<T> Send for Syncer<T> {}
+unsafe impl<T> Sync for Syncer<T> {}
+
 struct DirectWriteState {
+    gpu_state: GPUState,
+    #[cfg(feature = "enable-renderdoc")]
+    renderdoc: Syncer<Arc<RwLock<renderdoc::RenderDoc<renderdoc::V141>>>>,
     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<Self> {
+    pub(crate) fn new(
+        gpu_context: &DirectXDevices,
+        bitmap_factory: &IWICImagingFactory,
+    ) -> Result<Self> {
         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<u8>;
         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::<Vec<u8>>();
             // }
-            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::<Vec<u8>>();
         } 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<f32>,
+        bitmap_size: Size<DevicePixels>,
+        texture_size: Size<u32>,
+    ) -> Result<Vec<u8>> {
+        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::<Vec<_>>();
+                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::<Vec<_>>();
+
+        let params_buffer = {
+            let desc = D3D11_BUFFER_DESC {
+                ByteWidth: (std::mem::size_of::<GlyphLayerTextureParams>() * 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::<GlyphLayerTextureParams>() 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(
+                    &params_buffer,
+                    None,
+                    Some(&mut view),
+                )
+            }?;
+            [view]
+        };
+
+        unsafe {
+            let mut dest = std::mem::zeroed();
+            self.gpu_state.device_context.Map(
+                &params_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(&params_buffer, 0);
+        };
+
+        let textures = glyph_layers
+            .iter()
+            .map(|layer| Some(layer.texture_view.clone()))
+            .collect::<Vec<_>>();
+
+        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(&params_buffer_view)) };
+        unsafe { device_context.PSSetShaderResources(1, Some(&params_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<Bounds<f32>> {
         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<i32>,
+    texture: ID3D11Texture2D,
+    texture_view: ID3D11ShaderResourceView,
+}
+
+impl GlyphLayerTexture {
+    pub fn new(
+        gpu_state: &GPUState,
+        run_color: Rgba,
+        bounds: Bounds<i32>,
+        alpha_data: &[u8],
+    ) -> Result<Self> {
+        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<ID3D11Texture2D> = None;
+            unsafe {
+                gpu_state
+                    .device
+                    .CreateTexture2D(&desc, None, Some(&mut texture))?
+            };
+            texture.unwrap()
+        };
+        let texture_view = {
+            let mut view: Option<ID3D11ShaderResourceView> = 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<i32>,
+    alpha_texture_index: u32,
+}
+
 struct TextRendererWrapper(pub IDWriteTextRenderer);
 
 impl TextRendererWrapper {

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<T> PipelineState<T> {
         buffer_size: usize,
     ) -> Result<Self> {
         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<T> PipelineState<T> {
             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<T> PipelineState<T> {
 impl PathsPipelineState {
     fn new(device: &ID3D11Device) -> Result<Self> {
         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<ID3DBlob> {
+    pub(crate) fn build_shader_blob(filename: &str, entry: &str, target: &str) -> Result<ID3DBlob> {
         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");

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 {