blade: Fix initialization of atlas textures used for path rasterization (#8000)

Dzmitry Malyshau created

Generally the BladeAtlas logic has been deferring all the texture
initializations and updates till `begin_frame`. This doesn't work for
path rasterization, since a texture needs to be allocated after
`begin_frame` and used immediately.

Fixed validation error:
> UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout(ERROR / SPEC):
msgNum: 1303270965 - Validation Error: [
UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0:
handle = 0x60ce301b9010, name = main, type =
VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x51820000000007b,
name = atlas, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x4dae5635 |
vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] command buffer
VkCommandBuffer 0x60ce301b9010[main] expects VkImage
0x51820000000007b[atlas] (subresource: aspectMask 0x1 array layer 0, mip
level 0) to be in layout VK_IMAGE_LAYOUT_GENERAL--instead, current
layout is VK_IMAGE_LAYOUT_UNDEFINED.
    Objects: 2
        [0] 0x60ce301b9010, type: 6, name: main
        [1] 0x51820000000007b, type: 10, name: atlas

Release Notes:
- N/A

Change summary

crates/gpui/src/platform/blade/blade_atlas.rs    | 18 +++++++++++++++---
crates/gpui/src/platform/blade/blade_renderer.rs |  8 +++++---
2 files changed, 20 insertions(+), 6 deletions(-)

Detailed changes

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

@@ -69,9 +69,17 @@ impl BladeAtlas {
         }
     }
 
-    pub fn allocate(&self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
+    /// Allocate a rectangle and make it available for rendering immediately (without waiting for `before_frame`)
+    pub fn allocate_for_rendering(
+        &self,
+        size: Size<DevicePixels>,
+        texture_kind: AtlasTextureKind,
+        gpu_encoder: &mut gpu::CommandEncoder,
+    ) -> AtlasTile {
         let mut lock = self.0.lock();
-        lock.allocate(size, texture_kind)
+        let tile = lock.allocate(size, texture_kind);
+        lock.flush_initializations(gpu_encoder);
+        tile
     }
 
     pub fn before_frame(&self, gpu_encoder: &mut gpu::CommandEncoder) {
@@ -204,11 +212,15 @@ impl BladeAtlasState {
         self.uploads.push(PendingUpload { id, bounds, data });
     }
 
-    fn flush(&mut self, encoder: &mut gpu::CommandEncoder) {
+    fn flush_initializations(&mut self, encoder: &mut gpu::CommandEncoder) {
         for id in self.initializations.drain(..) {
             let texture = &self.storage[id];
             encoder.init_texture(texture.raw);
         }
+    }
+
+    fn flush(&mut self, encoder: &mut gpu::CommandEncoder) {
+        self.flush_initializations(encoder);
 
         let mut transfers = encoder.transfer();
         for upload in self.uploads.drain(..) {

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

@@ -450,9 +450,11 @@ impl BladeRenderer {
 
         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);
+            let tile = self.atlas.allocate_for_rendering(
+                clipped_bounds.size.map(Into::into),
+                AtlasTextureKind::Path,
+                &mut self.command_encoder,
+            );
             vertices_by_texture_id
                 .entry(tile.texture_id)
                 .or_insert(Vec::new())