[Impeller] use render pass to clear glyph atlas texture to transparent black. (#52791)

Alternative to https://github.com/flutter/engine/pull/52746

Work towards https://github.com/flutter/flutter/issues/138798

If possible, use a render pass to clear texture to transparent black. The goal is to reduce the CPU cost for larger atlases.
diff --git a/impeller/renderer/backend/gles/blit_command_gles.cc b/impeller/renderer/backend/gles/blit_command_gles.cc
index c7a5de9..b3b6d84 100644
--- a/impeller/renderer/backend/gles/blit_command_gles.cc
+++ b/impeller/renderer/backend/gles/blit_command_gles.cc
@@ -276,7 +276,7 @@
                   0u,                          // border
                   data.external_format,        // external format
                   data.type,                   // type
-                  tex_data                     // data
+                  nullptr                      // data
     );
     texture_gles.MarkSliceInitialized(slice);
   }
diff --git a/impeller/renderer/blit_pass_unittests.cc b/impeller/renderer/blit_pass_unittests.cc
index 732af99..e30d89f 100644
--- a/impeller/renderer/blit_pass_unittests.cc
+++ b/impeller/renderer/blit_pass_unittests.cc
@@ -104,5 +104,29 @@
                                  std::nullopt, "", /*slice=*/0));
 }
 
+TEST_P(BlitPassTest, CanBlitSmallRegionToUninitializedTexture) {
+  auto context = GetContext();
+  auto cmd_buffer = context->CreateCommandBuffer();
+  auto blit_pass = cmd_buffer->CreateBlitPass();
+
+  TextureDescriptor dst_format;
+  dst_format.storage_mode = StorageMode::kDevicePrivate;
+  dst_format.format = PixelFormat::kR8G8B8A8UNormInt;
+  dst_format.size = {1000, 1000};
+  auto dst = context->GetResourceAllocator()->CreateTexture(dst_format);
+
+  DeviceBufferDescriptor src_format;
+  src_format.size = 4;
+  src_format.storage_mode = StorageMode::kHostVisible;
+  auto src = context->GetResourceAllocator()->CreateBuffer(src_format);
+
+  ASSERT_TRUE(dst);
+
+  EXPECT_TRUE(blit_pass->AddCopy(DeviceBuffer::AsBufferView(src), dst,
+                                 IRect::MakeLTRB(0, 0, 1, 1), "", /*slice=*/0));
+  EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
+  EXPECT_TRUE(context->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok());
+}
+
 }  // namespace testing
 }  // namespace impeller
diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc
index 25f36c7..35c39a9 100644
--- a/impeller/typographer/backends/skia/typographer_context_skia.cc
+++ b/impeller/typographer/backends/skia/typographer_context_skia.cc
@@ -306,6 +306,43 @@
   return true;
 }
 
+// The texture needs to be cleared to transparent black so that linearly
+// samplex rotated/skewed glyphs do not grab uninitialized data.
+bool ClearTextureToTransparentBlack(Context& context,
+                                    HostBuffer& host_buffer,
+                                    std::shared_ptr<CommandBuffer>& cmd_buffer,
+                                    std::shared_ptr<BlitPass>& blit_pass,
+                                    std::shared_ptr<Texture>& texture) {
+  // The R8/A8 textures used for certain glyphs is not supported as color
+  // attachments in most graphics drivers. To be safe, just do a CPU clear
+  // for these.
+  if (texture->GetTextureDescriptor().format ==
+      context.GetCapabilities()->GetDefaultGlyphAtlasFormat()) {
+    size_t byte_size =
+        texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
+    BufferView buffer_view =
+        host_buffer.Emplace(nullptr, byte_size, DefaultUniformAlignment());
+
+    ::memset(buffer_view.buffer->OnGetContents() + buffer_view.range.offset, 0,
+             byte_size);
+    buffer_view.buffer->Flush();
+    return blit_pass->AddCopy(buffer_view, texture);
+  }
+  // In all other cases, we can use a render pass to clear to a transparent
+  // color.
+  ColorAttachment attachment;
+  attachment.clear_color = Color::BlackTransparent();
+  attachment.load_action = LoadAction::kClear;
+  attachment.store_action = StoreAction::kStore;
+  attachment.texture = texture;
+
+  RenderTarget render_target;
+  render_target.SetColorAttachment(attachment, 0u);
+
+  auto render_pass = cmd_buffer->CreateRenderPass(render_target);
+  return render_pass->EncodeCommands();
+}
+
 std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
     Context& context,
     GlyphAtlas::Type type,
@@ -455,28 +492,18 @@
     }
     descriptor.size = atlas_size;
     descriptor.storage_mode = StorageMode::kDevicePrivate;
+    descriptor.usage = TextureUsage::kShaderRead | TextureUsage::kRenderTarget;
     new_texture = context.GetResourceAllocator()->CreateTexture(descriptor);
   }
 
   if (!new_texture) {
     return nullptr;
   }
-  // The texture needs to be cleared to transparent black so that linearly
-  // samplex rotated/skewed glyphs do not grab uninitialized data. We could
-  // instead use a render pass to clear to transparent black, but there are
-  // more restrictions on what kinds of textures can be bound on GLES.
-  {
-    auto bytes =
-        new_texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
-    BufferView buffer_view =
-        host_buffer.Emplace(nullptr, bytes, DefaultUniformAlignment());
-
-    ::memset(buffer_view.buffer->OnGetContents() + buffer_view.range.offset, 0,
-             bytes);
-    blit_pass->AddCopy(buffer_view, new_texture);
-  }
 
   new_texture->SetLabel("GlyphAtlas");
+
+  ClearTextureToTransparentBlack(context, host_buffer, cmd_buffer, blit_pass,
+                                 new_texture);
   if (!UpdateAtlasBitmap(*glyph_atlas, blit_pass, host_buffer, new_texture,
                          font_glyph_pairs)) {
     return nullptr;