WIP path rasterization

Dzmitry Malyshau created

Change summary

crates/gpui/src/platform/linux/blade_atlas.rs    | 33 +++++++
crates/gpui/src/platform/linux/blade_renderer.rs | 76 +++++++++++++++++
crates/gpui/src/platform/linux/shaders.wgsl      |  2 
crates/gpui/src/platform/linux/window.rs         | 14 --
4 files changed, 113 insertions(+), 12 deletions(-)

Detailed changes

crates/gpui/src/platform/linux/blade_atlas.rs 🔗

@@ -10,6 +10,8 @@ use etagere::BucketedAtlasAllocator;
 use parking_lot::Mutex;
 use std::{borrow::Cow, sync::Arc};
 
+pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float;
+
 pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>);
 
 struct BladeAtlasState {
@@ -32,6 +34,7 @@ impl BladeAtlasState {
         }
         for texture in self.path_textures.drain(..) {
             self.gpu.destroy_texture(texture.raw);
+            self.gpu.destroy_texture_view(texture.raw_view.unwrap());
         }
         self.gpu.destroy_command_encoder(&mut self.gpu_encoder);
         self.upload_belt.destroy(&self.gpu);
@@ -78,6 +81,11 @@ impl BladeAtlas {
         lock.gpu_encoder.start();
     }
 
+    pub fn allocate(&self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
+        let mut lock = self.0.lock();
+        lock.allocate(size, texture_kind)
+    }
+
     pub fn finish_frame(&self) -> gpu::SyncPoint {
         let mut lock = self.0.lock();
         let gpu = lock.gpu.clone();
@@ -85,6 +93,16 @@ impl BladeAtlas {
         lock.upload_belt.flush(&sync_point);
         sync_point
     }
+
+    pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView {
+        let lock = self.0.lock();
+        let textures = match id.kind {
+            crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures,
+            crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures,
+            crate::AtlasTextureKind::Path => &lock.path_textures,
+        };
+        textures[id.index as usize].raw_view.unwrap()
+    }
 }
 
 impl PlatformAtlas for BladeAtlas {
@@ -146,7 +164,7 @@ impl BladeAtlasState {
                 usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
             }
             AtlasTextureKind::Path => {
-                format = gpu::TextureFormat::R16Float;
+                format = PATH_TEXTURE_FORMAT;
                 usage = gpu::TextureUsage::COPY
                     | gpu::TextureUsage::RESOURCE
                     | gpu::TextureUsage::TARGET;
@@ -166,6 +184,17 @@ impl BladeAtlasState {
             dimension: gpu::TextureDimension::D2,
             usage,
         });
+        let raw_view = if usage.contains(gpu::TextureUsage::TARGET) {
+            Some(self.gpu.create_texture_view(gpu::TextureViewDesc {
+                name: "",
+                texture: raw,
+                format,
+                dimension: gpu::ViewDimension::D2,
+                subresources: &Default::default(),
+            }))
+        } else {
+            None
+        };
 
         let textures = match kind {
             AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
@@ -180,6 +209,7 @@ impl BladeAtlasState {
             allocator: etagere::BucketedAtlasAllocator::new(size.into()),
             format,
             raw,
+            raw_view,
         };
         textures.push(atlas_texture);
         textures.last_mut().unwrap()
@@ -218,6 +248,7 @@ struct BladeAtlasTexture {
     id: AtlasTextureId,
     allocator: BucketedAtlasAllocator,
     raw: gpu::Texture,
+    raw_view: Option<gpu::TextureView>,
     format: gpu::TextureFormat,
 }
 

crates/gpui/src/platform/linux/blade_renderer.rs 🔗

@@ -2,8 +2,12 @@
 #![allow(irrefutable_let_patterns)]
 
 use super::{BladeBelt, BladeBeltDescriptor};
-use crate::{PrimitiveBatch, Quad, Scene, Shadow};
+use crate::{
+    AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch,
+    Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT,
+};
 use bytemuck::{Pod, Zeroable};
+use collections::HashMap;
 
 use blade_graphics as gpu;
 use std::sync::Arc;
@@ -33,6 +37,7 @@ struct ShaderShadowsData {
 struct BladePipelines {
     quads: gpu::RenderPipeline,
     shadows: gpu::RenderPipeline,
+    path_rasterization: gpu::RenderPipeline,
 }
 
 impl BladePipelines {
@@ -77,6 +82,22 @@ impl BladePipelines {
                     write_mask: gpu::ColorWrites::default(),
                 }],
             }),
+            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
+                name: "path_rasterization",
+                data_layouts: &[&shadows_layout],
+                vertex: shader.at("vs_path_rasterization"),
+                primitive: gpu::PrimitiveState {
+                    topology: gpu::PrimitiveTopology::TriangleStrip,
+                    ..Default::default()
+                },
+                depth_stencil: None,
+                fragment: shader.at("fs_path_rasterization"),
+                color_targets: &[gpu::ColorTargetState {
+                    format: PATH_TEXTURE_FORMAT,
+                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
+                    write_mask: gpu::ColorWrites::default(),
+                }],
+            }),
         }
     }
 }
@@ -88,6 +109,8 @@ pub struct BladeRenderer {
     pipelines: BladePipelines,
     instance_belt: BladeBelt,
     viewport_size: gpu::Extent,
+    path_tiles: HashMap<PathId, AtlasTile>,
+    atlas: Arc<BladeAtlas>,
 }
 
 impl BladeRenderer {
@@ -106,6 +129,8 @@ impl BladeRenderer {
             memory: gpu::Memory::Shared,
             min_chunk_size: 0x1000,
         });
+        let atlas = Arc::new(BladeAtlas::new(&gpu));
+
         Self {
             gpu,
             command_encoder,
@@ -113,6 +138,8 @@ impl BladeRenderer {
             pipelines,
             instance_belt,
             viewport_size: size,
+            path_tiles: HashMap::default(),
+            atlas,
         }
     }
 
@@ -126,6 +153,7 @@ impl BladeRenderer {
 
     pub fn destroy(&mut self) {
         self.wait_for_gpu();
+        self.atlas.destroy();
         self.instance_belt.destroy(&self.gpu);
         self.gpu.destroy_command_encoder(&mut self.command_encoder);
     }
@@ -140,11 +168,56 @@ impl BladeRenderer {
         self.viewport_size = size;
     }
 
+    pub fn atlas(&self) -> &Arc<BladeAtlas> {
+        &self.atlas
+    }
+
+    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
+        self.path_tiles.clear();
+        let mut vertices_by_texture_id = 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),
+                    st_position: vertex.st_position,
+                    content_mask: ContentMask {
+                        bounds: tile.bounds.map(Into::into),
+                    },
+                }));
+            self.path_tiles.insert(path.id, tile);
+        }
+
+        for (texture_id, vertices) in vertices_by_texture_id {
+            let instances = self.instance_belt.alloc_data(&vertices, &self.gpu);
+            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
+                colors: &[gpu::RenderTarget {
+                    view: self.atlas.get_texture_view(texture_id),
+                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
+                    finish_op: gpu::FinishOp::Store,
+                }],
+                depth_stencil: None,
+            });
+
+            let mut encoder = pass.with(&self.pipelines.path_rasterization);
+            encoder.draw(0, vertices.len() as u32, 0, 1);
+        }
+    }
+
     pub fn draw(&mut self, scene: &Scene) {
         let frame = self.gpu.acquire_frame();
         self.command_encoder.start();
         self.command_encoder.init_texture(frame.texture());
 
+        self.rasterize_paths(scene.paths());
+
         let globals = GlobalParams {
             viewport_size: [
                 self.viewport_size.width as f32,
@@ -187,6 +260,7 @@ impl BladeRenderer {
                         );
                         encoder.draw(0, 4, 0, shadows.len() as u32);
                     }
+                    PrimitiveBatch::Paths(paths) => {}
                     _ => continue,
                 }
             }

crates/gpui/src/platform/linux/window.rs 🔗

@@ -1,6 +1,6 @@
 use super::BladeRenderer;
 use crate::{
-    BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
+    Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
     PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
 };
 use blade_graphics as gpu;
@@ -57,7 +57,6 @@ pub(crate) struct LinuxWindowState {
     x_window: x::Window,
     callbacks: Mutex<Callbacks>,
     inner: Mutex<LinuxWindowInner>,
-    sprite_atlas: Arc<BladeAtlas>,
 }
 
 #[derive(Clone)]
@@ -186,7 +185,6 @@ impl LinuxWindowState {
             height: bounds.size.height as u32,
             depth: 1,
         };
-        let sprite_atlas = Arc::new(BladeAtlas::new(&gpu));
 
         Self {
             xcb_connection: Arc::clone(xcb_connection),
@@ -200,16 +198,11 @@ impl LinuxWindowState {
                 scale_factor: 1.0,
                 renderer: BladeRenderer::new(gpu, gpu_extent),
             }),
-            sprite_atlas,
         }
     }
 
     pub fn destroy(&self) {
-        self.sprite_atlas.destroy();
-        {
-            let mut inner = self.inner.lock();
-            inner.renderer.destroy();
-        }
+        self.inner.lock().renderer.destroy();
         self.xcb_connection.send_request(&x::UnmapWindow {
             window: self.x_window,
         });
@@ -379,6 +372,7 @@ impl PlatformWindow for LinuxWindow {
     }
 
     fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
-        self.0.sprite_atlas.clone()
+        let mut inner = self.0.inner.lock();
+        inner.renderer.atlas().clone()
     }
 }