[Impeller] use render pass to clear glyph atlas texture to transparent black. (flutter/engine#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.
This commit is contained in:
Jonah Williams 2024-05-13 23:06:12 -07:00 committed by GitHub
parent f331d37840
commit 12b56a350b
3 changed files with 66 additions and 15 deletions

View File

@ -276,7 +276,7 @@ bool BlitCopyBufferToTextureCommandGLES::Encode(
0u, // border
data.external_format, // external format
data.type, // type
tex_data // data
nullptr // data
);
texture_gles.MarkSliceInitialized(slice);
}

View File

@ -104,5 +104,29 @@ TEST_P(BlitPassTest, ChecksInvalidSliceParameters) {
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

View File

@ -306,6 +306,43 @@ static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
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 @@ std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(
}
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;