[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;