[Impeller] leave glyph atlas in transfer dst to improve vulkan throughput. (#52908)
On the vulkan backend everytime we blit a glyph we go shader read -> transfer dst -> shader read. This is pretty inefficient if we're appending many glyphs.
Poke a hole in the blitpass API so we can leave the glyph atlas in transfer_dst to reduce the number of layout transitions.
diff --git a/impeller/renderer/backend/gles/blit_pass_gles.cc b/impeller/renderer/backend/gles/blit_pass_gles.cc
index 363400d..d83ef32 100644
--- a/impeller/renderer/backend/gles/blit_pass_gles.cc
+++ b/impeller/renderer/backend/gles/blit_pass_gles.cc
@@ -132,7 +132,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) {
+ uint32_t slice,
+ bool convert_to_read) {
auto command = std::make_unique<BlitCopyBufferToTextureCommandGLES>();
command->label = label;
command->source = std::move(source);
diff --git a/impeller/renderer/backend/gles/blit_pass_gles.h b/impeller/renderer/backend/gles/blit_pass_gles.h
index 1b5385f..e34bb16 100644
--- a/impeller/renderer/backend/gles/blit_pass_gles.h
+++ b/impeller/renderer/backend/gles/blit_pass_gles.h
@@ -59,7 +59,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) override;
+ uint32_t slice,
+ bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.h b/impeller/renderer/backend/metal/blit_pass_mtl.h
index 3363eb9..1de0f45 100644
--- a/impeller/renderer/backend/metal/blit_pass_mtl.h
+++ b/impeller/renderer/backend/metal/blit_pass_mtl.h
@@ -58,7 +58,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) override;
+ uint32_t slice,
+ bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
diff --git a/impeller/renderer/backend/metal/blit_pass_mtl.mm b/impeller/renderer/backend/metal/blit_pass_mtl.mm
index 9caa449..329fe51 100644
--- a/impeller/renderer/backend/metal/blit_pass_mtl.mm
+++ b/impeller/renderer/backend/metal/blit_pass_mtl.mm
@@ -166,7 +166,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) {
+ uint32_t slice,
+ bool convert_to_read) {
auto source_mtl = DeviceBufferMTL::Cast(*source.buffer).GetMTLBuffer();
if (!source_mtl) {
return false;
diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.cc b/impeller/renderer/backend/vulkan/blit_pass_vk.cc
index 289561a..b8c5e98 100644
--- a/impeller/renderer/backend/vulkan/blit_pass_vk.cc
+++ b/impeller/renderer/backend/vulkan/blit_pass_vk.cc
@@ -221,13 +221,37 @@
return true;
}
+bool BlitPassVK::ConvertTextureToShaderRead(
+ const std::shared_ptr<Texture>& texture) {
+ auto& encoder = *command_buffer_->GetEncoder();
+ const auto& cmd_buffer = encoder.GetCommandBuffer();
+
+ BarrierVK barrier;
+ barrier.cmd_buffer = cmd_buffer;
+ barrier.src_access = vk::AccessFlagBits::eTransferWrite;
+ barrier.src_stage = vk::PipelineStageFlagBits::eTransfer;
+ barrier.dst_access = vk::AccessFlagBits::eShaderRead;
+ barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
+
+ barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
+
+ const auto& texture_vk = TextureVK::Cast(*texture);
+
+ if (!encoder.Track(texture)) {
+ return false;
+ }
+
+ return texture_vk.SetLayout(barrier);
+}
+
// |BlitPass|
bool BlitPassVK::OnCopyBufferToTextureCommand(
BufferView source,
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) {
+ uint32_t slice,
+ bool convert_to_read) {
auto& encoder = *command_buffer_->GetEncoder();
const auto& cmd_buffer = encoder.GetCommandBuffer();
@@ -262,6 +286,9 @@
image_copy.imageExtent.height = destination_region.GetHeight();
image_copy.imageExtent.depth = 1u;
+ // Note: this barrier should do nothing if we're already in the transfer dst
+ // optimal state. This is important for performance of repeated blit pass
+ // encoding.
if (!dst.SetLayout(dst_barrier)) {
VALIDATION_LOG << "Could not encode layout transition.";
return false;
@@ -274,7 +301,7 @@
);
// Transition to shader-read.
- {
+ if (convert_to_read) {
BarrierVK barrier;
barrier.cmd_buffer = cmd_buffer;
barrier.src_access = vk::AccessFlagBits::eTransferWrite;
diff --git a/impeller/renderer/backend/vulkan/blit_pass_vk.h b/impeller/renderer/backend/vulkan/blit_pass_vk.h
index b3967ed..7d9c4c7 100644
--- a/impeller/renderer/backend/vulkan/blit_pass_vk.h
+++ b/impeller/renderer/backend/vulkan/blit_pass_vk.h
@@ -38,6 +38,10 @@
const std::shared_ptr<Allocator>& transients_allocator) const override;
// |BlitPass|
+ bool ConvertTextureToShaderRead(
+ const std::shared_ptr<Texture>& texture) override;
+
+ // |BlitPass|
bool OnCopyTextureToTextureCommand(std::shared_ptr<Texture> source,
std::shared_ptr<Texture> destination,
IRect source_region,
@@ -56,7 +60,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) override;
+ uint32_t slice,
+ bool convert_to_read) override;
// |BlitPass|
bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) override;
diff --git a/impeller/renderer/blit_pass.cc b/impeller/renderer/blit_pass.cc
index e215b05..0126c65 100644
--- a/impeller/renderer/blit_pass.cc
+++ b/impeller/renderer/blit_pass.cc
@@ -124,7 +124,8 @@
std::shared_ptr<Texture> destination,
std::optional<IRect> destination_region,
std::string label,
- uint32_t slice) {
+ uint32_t slice,
+ bool convert_to_read) {
if (!destination) {
VALIDATION_LOG << "Attempted to add a texture blit with no destination.";
return false;
@@ -156,7 +157,12 @@
return OnCopyBufferToTextureCommand(std::move(source), std::move(destination),
destination_region_value,
- std::move(label), slice);
+ std::move(label), slice, convert_to_read);
+}
+
+bool BlitPass::ConvertTextureToShaderRead(
+ const std::shared_ptr<Texture>& texture) {
+ return true;
}
bool BlitPass::GenerateMipmap(std::shared_ptr<Texture> texture,
diff --git a/impeller/renderer/blit_pass.h b/impeller/renderer/blit_pass.h
index 5b2f56b..6f5f332 100644
--- a/impeller/renderer/blit_pass.h
+++ b/impeller/renderer/blit_pass.h
@@ -32,11 +32,18 @@
void SetLabel(std::string label);
//----------------------------------------------------------------------------
+ /// @brief If the texture is not already in a shader read internal
+ /// state, then convert it to that state.
+ ///
+ /// This API is only used by Vulkan.
+ virtual bool ConvertTextureToShaderRead(
+ const std::shared_ptr<Texture>& texture);
+
+ //----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of one texture to
/// another texture. The blit area is limited by the intersection
/// of the texture coverage with respect the source region and
/// destination origin.
- /// No work is encoded into the command buffer at this time.
///
/// @param[in] source The texture to read for copying.
/// @param[in] destination The texture to overwrite using the source
@@ -60,7 +67,6 @@
//----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of the buffer to
/// the texture.
- /// No work is encoded into the command buffer at this time.
///
/// @param[in] source The texture to read for copying.
/// @param[in] destination The buffer to overwrite using the source
@@ -84,7 +90,6 @@
//----------------------------------------------------------------------------
/// @brief Record a command to copy the contents of the buffer to
/// the texture.
- /// No work is encoded into the command buffer at this time.
///
/// @param[in] source The buffer view to read for copying.
/// @param[in] destination The texture to overwrite using the source
@@ -96,6 +101,8 @@
/// command.
/// @param[in] slice For cubemap textures, the slice to write
/// data to.
+ /// @param[in] convert_to_read Whether to convert the texture to a shader
+ /// read state. Defaults to true.
///
/// @return If the command was valid for subsequent commitment.
///
@@ -111,11 +118,11 @@
std::shared_ptr<Texture> destination,
std::optional<IRect> destination_region = std::nullopt,
std::string label = "",
- uint32_t slice = 0);
+ uint32_t slice = 0,
+ bool convert_to_read = true);
//----------------------------------------------------------------------------
/// @brief Record a command to generate all mip levels for a texture.
- /// No work is encoded into the command buffer at this time.
///
/// @param[in] texture The texture to generate mipmaps for.
/// @param[in] label The optional debug label to give the command.
@@ -159,7 +166,8 @@
std::shared_ptr<Texture> destination,
IRect destination_region,
std::string label,
- uint32_t slice) = 0;
+ uint32_t slice,
+ bool convert_to_read) = 0;
virtual bool OnGenerateMipmapCommand(std::shared_ptr<Texture> texture,
std::string label) = 0;
diff --git a/impeller/renderer/testing/mocks.h b/impeller/renderer/testing/mocks.h
index c333ac9..d4e2744 100644
--- a/impeller/renderer/testing/mocks.h
+++ b/impeller/renderer/testing/mocks.h
@@ -84,7 +84,8 @@
std::shared_ptr<Texture> destination,
IRect destination_rect,
std::string label,
- uint32_t slice),
+ uint32_t slice,
+ bool convert_to_read),
(override));
MOCK_METHOD(bool,
OnGenerateMipmapCommand,
diff --git a/impeller/typographer/backends/skia/typographer_context_skia.cc b/impeller/typographer/backends/skia/typographer_context_skia.cc
index 14485f4..52616ed 100644
--- a/impeller/typographer/backends/skia/typographer_context_skia.cc
+++ b/impeller/typographer/backends/skia/typographer_context_skia.cc
@@ -298,13 +298,21 @@
DrawGlyph(canvas, pair.scaled_font, pair.glyph, has_color);
- if (!blit_pass->AddCopy(allocator.TakeBufferView(), texture,
+ // convert_to_read is set to false so that the texture remains in a transfer
+ // dst layout until we finish writing to it below. This only has an impact
+ // on Vulkan where we are responsible for managing image layouts.
+ if (!blit_pass->AddCopy(allocator.TakeBufferView(), //
+ texture, //
IRect::MakeXYWH(pos->GetLeft(), pos->GetTop(),
- size.width, size.height))) {
+ size.width, size.height), //
+ /*label=*/"", //
+ /*slice=*/0, //
+ /*convert_to_read=*/false //
+ )) {
return false;
}
}
- return true;
+ return blit_pass->ConvertTextureToShaderRead(texture);
}
std::shared_ptr<GlyphAtlas> TypographerContextSkia::CreateGlyphAtlas(