[Impeller] cache glyph atlas if contents are unchanged (flutter/engine#36719)
This commit is contained in:
@@ -143,7 +143,8 @@ static std::unique_ptr<PipelineT> CreateDefaultPipeline(
|
||||
|
||||
ContentContext::ContentContext(std::shared_ptr<Context> context)
|
||||
: context_(std::move(context)),
|
||||
tessellator_(std::make_shared<Tessellator>()) {
|
||||
tessellator_(std::make_shared<Tessellator>()),
|
||||
glyph_atlas_context_(std::make_shared<GlyphAtlasContext>()) {
|
||||
if (!context_ || !context_->IsValid()) {
|
||||
return;
|
||||
}
|
||||
@@ -291,6 +292,11 @@ std::shared_ptr<Tessellator> ContentContext::GetTessellator() const {
|
||||
return tessellator_;
|
||||
}
|
||||
|
||||
std::shared_ptr<GlyphAtlasContext> ContentContext::GetGlyphAtlasContext()
|
||||
const {
|
||||
return glyph_atlas_context_;
|
||||
}
|
||||
|
||||
std::shared_ptr<Context> ContentContext::GetContext() const {
|
||||
return context_;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
#include "impeller/entity/position_color.vert.h"
|
||||
#include "impeller/entity/position_uv.vert.h"
|
||||
|
||||
#include "impeller/typographer/glyph_atlas.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
using LinearGradientFillPipeline =
|
||||
@@ -376,6 +378,8 @@ class ContentContext {
|
||||
|
||||
std::shared_ptr<Context> GetContext() const;
|
||||
|
||||
std::shared_ptr<GlyphAtlasContext> GetGlyphAtlasContext() const;
|
||||
|
||||
using SubpassCallback =
|
||||
std::function<bool(const ContentContext&, RenderPass&)>;
|
||||
|
||||
@@ -465,6 +469,7 @@ class ContentContext {
|
||||
|
||||
bool is_valid_ = false;
|
||||
std::shared_ptr<Tessellator> tessellator_;
|
||||
std::shared_ptr<GlyphAtlasContext> glyph_atlas_context_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ContentContext);
|
||||
};
|
||||
|
||||
@@ -34,10 +34,11 @@ void TextContents::SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas) {
|
||||
|
||||
std::shared_ptr<GlyphAtlas> TextContents::ResolveAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const {
|
||||
FML_DCHECK(lazy_atlas_);
|
||||
if (lazy_atlas_) {
|
||||
return lazy_atlas_->CreateOrGetGlyphAtlas(type, context);
|
||||
return lazy_atlas_->CreateOrGetGlyphAtlas(type, atlas_context, context);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -172,8 +173,9 @@ static bool CommonRender(const ContentContext& renderer,
|
||||
bool TextContents::RenderSdf(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto atlas = ResolveAtlas(GlyphAtlas::Type::kSignedDistanceField,
|
||||
renderer.GetContext());
|
||||
auto atlas =
|
||||
ResolveAtlas(GlyphAtlas::Type::kSignedDistanceField,
|
||||
renderer.GetGlyphAtlasContext(), renderer.GetContext());
|
||||
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
|
||||
@@ -208,7 +210,7 @@ bool TextContents::Render(const ContentContext& renderer,
|
||||
auto atlas =
|
||||
ResolveAtlas(lazy_atlas_->HasColor() ? GlyphAtlas::Type::kColorBitmap
|
||||
: GlyphAtlas::Type::kAlphaBitmap,
|
||||
renderer.GetContext());
|
||||
renderer.GetGlyphAtlasContext(), renderer.GetContext());
|
||||
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
|
||||
|
||||
@@ -52,6 +52,7 @@ class TextContents final : public Contents {
|
||||
|
||||
std::shared_ptr<GlyphAtlas> ResolveAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(TextContents);
|
||||
|
||||
@@ -340,13 +340,13 @@ static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
|
||||
|
||||
std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator frame_iterator) const {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
if (!IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
|
||||
auto last_atlas = atlas_context->GetGlyphAtlas();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 1: Collect unique font-glyph pairs in the frame.
|
||||
@@ -354,11 +354,23 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
|
||||
auto font_glyph_pairs = CollectUniqueFontGlyphPairs(type, frame_iterator);
|
||||
if (font_glyph_pairs.empty()) {
|
||||
return glyph_atlas;
|
||||
return last_atlas;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 2: Get the optimum size of the texture atlas.
|
||||
// Step 2: Determine if the atlas type and font glyph pairs are compatible
|
||||
// with the current atlas and reuse if possible.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (last_atlas->GetType() == type &&
|
||||
last_atlas->HasSamePairs(font_glyph_pairs)) {
|
||||
return last_atlas;
|
||||
}
|
||||
|
||||
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
|
||||
atlas_context->UpdateGlyphAtlas(glyph_atlas);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 3: Get the optimum size of the texture atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
std::vector<Rect> glyph_positions;
|
||||
const auto atlas_size =
|
||||
@@ -368,7 +380,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 3: Find location of font-glyph pairs in the atlas. We have this from
|
||||
// Step 4: Find location of font-glyph pairs in the atlas. We have this from
|
||||
// the last step. So no need to do create another rect packer. But just do a
|
||||
// sanity check of counts. This could also be just an assertion as only a
|
||||
// construction issue would cause such a failure.
|
||||
@@ -378,7 +390,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Record the positions in the glyph atlas.
|
||||
// Step 5: Record the positions in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
|
||||
glyph_atlas->AddTypefaceGlyphPosition(font_glyph_pairs[i],
|
||||
@@ -386,7 +398,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 5: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// Step 6: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
|
||||
if (!bitmap) {
|
||||
@@ -394,7 +406,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 6: Upload the atlas as a texture.
|
||||
// Step 7: Upload the atlas as a texture.
|
||||
// ---------------------------------------------------------------------------
|
||||
PixelFormat format;
|
||||
switch (type) {
|
||||
@@ -416,7 +428,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 7: Record the texture in the glyph atlas.
|
||||
// Step 8: Record the texture in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
glyph_atlas->SetTexture(std::move(texture));
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ class TextRenderContextSkia : public TextRenderContext {
|
||||
// |TextRenderContext|
|
||||
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator iterator) const override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -6,6 +6,19 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
GlyphAtlasContext::GlyphAtlasContext()
|
||||
: atlas_(std::make_shared<GlyphAtlas>(GlyphAtlas::Type::kAlphaBitmap)) {}
|
||||
|
||||
GlyphAtlasContext::~GlyphAtlasContext() {}
|
||||
|
||||
std::shared_ptr<GlyphAtlas> GlyphAtlasContext::GetGlyphAtlas() const {
|
||||
return atlas_;
|
||||
}
|
||||
|
||||
void GlyphAtlasContext::UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas) {
|
||||
atlas_ = atlas;
|
||||
}
|
||||
|
||||
GlyphAtlas::GlyphAtlas(Type type) : type_(type) {}
|
||||
|
||||
GlyphAtlas::~GlyphAtlas() = default;
|
||||
@@ -61,4 +74,13 @@ size_t GlyphAtlas::IterateGlyphs(
|
||||
return count;
|
||||
}
|
||||
|
||||
bool GlyphAtlas::HasSamePairs(const FontGlyphPair::Vector& new_glyphs) {
|
||||
for (const auto& pair : new_glyphs) {
|
||||
if (positions_.find(pair) == positions_.end()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -116,6 +116,16 @@ class GlyphAtlas {
|
||||
///
|
||||
std::optional<Rect> FindFontGlyphPosition(const FontGlyphPair& pair) const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief whether this atlas contains all of the same font-glyph pairs
|
||||
/// as the vector.
|
||||
///
|
||||
/// @param[in] new_glyphs The full set of new glyphs
|
||||
///
|
||||
/// @return Whether this atlas contains all passed pairs.
|
||||
///
|
||||
bool HasSamePairs(const FontGlyphPair::Vector& new_glyphs);
|
||||
|
||||
private:
|
||||
const Type type_;
|
||||
std::shared_ptr<Texture> texture_;
|
||||
@@ -129,4 +139,27 @@ class GlyphAtlas {
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlas);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief A container for caching a glyph atlas across frames.
|
||||
///
|
||||
class GlyphAtlasContext {
|
||||
public:
|
||||
GlyphAtlasContext();
|
||||
|
||||
~GlyphAtlasContext();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Retrieve the current glyph atlas.
|
||||
std::shared_ptr<GlyphAtlas> GetGlyphAtlas() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Update the context with a newly constructed glyph atlas.
|
||||
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas);
|
||||
|
||||
private:
|
||||
std::shared_ptr<GlyphAtlas> atlas_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlasContext);
|
||||
};
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -26,6 +26,7 @@ bool LazyGlyphAtlas::HasColor() const {
|
||||
|
||||
std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const {
|
||||
{
|
||||
auto atlas_it = atlas_map_.find(type);
|
||||
@@ -47,7 +48,7 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
|
||||
i++;
|
||||
return &result;
|
||||
};
|
||||
auto atlas = text_context->CreateGlyphAtlas(type, iterator);
|
||||
auto atlas = text_context->CreateGlyphAtlas(type, atlas_context, iterator);
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Could not create valid atlas.";
|
||||
return nullptr;
|
||||
|
||||
@@ -23,6 +23,7 @@ class LazyGlyphAtlas {
|
||||
|
||||
std::shared_ptr<GlyphAtlas> CreateOrGetGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const;
|
||||
|
||||
bool HasColor() const;
|
||||
|
||||
@@ -34,6 +34,7 @@ const std::shared_ptr<Context>& TextRenderContext::GetContext() const {
|
||||
|
||||
std::shared_ptr<GlyphAtlas> TextRenderContext::CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
const TextFrame& frame) const {
|
||||
size_t count = 0;
|
||||
FrameIterator iterator = [&]() -> const TextFrame* {
|
||||
@@ -43,7 +44,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContext::CreateGlyphAtlas(
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
return CreateGlyphAtlas(type, iterator);
|
||||
return CreateGlyphAtlas(type, atlas_context, iterator);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@@ -44,10 +44,13 @@ class TextRenderContext {
|
||||
|
||||
virtual std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator iterator) const = 0;
|
||||
|
||||
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(GlyphAtlas::Type type,
|
||||
const TextFrame& frame) const;
|
||||
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
const TextFrame& frame) const;
|
||||
|
||||
protected:
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -36,12 +36,14 @@ TEST_P(TypographerTest, CanCreateRenderContext) {
|
||||
|
||||
TEST_P(TypographerTest, CanCreateGlyphAtlas) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
SkFont sk_font;
|
||||
auto blob = SkTextBlob::MakeFromString("hello", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
|
||||
TextFrameFromTextBlob(blob));
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
OpenPlaygroundHere([](RenderTarget&) { return true; });
|
||||
}
|
||||
@@ -95,12 +97,14 @@ TEST_P(TypographerTest, LazyAtlasTracksColor) {
|
||||
|
||||
TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
SkFont sk_font;
|
||||
auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
|
||||
TextFrameFromTextBlob(blob));
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
|
||||
@@ -110,8 +114,32 @@ TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
|
||||
atlas->GetTexture()->GetSize().height);
|
||||
}
|
||||
|
||||
TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
SkFont sk_font;
|
||||
auto blob = SkTextBlob::MakeFromString("spooky skellingtons", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
|
||||
|
||||
// now attempt to re-create an atlas with the same text blob.
|
||||
|
||||
auto next_atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
ASSERT_EQ(atlas, next_atlas);
|
||||
ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
|
||||
}
|
||||
|
||||
TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
SkFont sk_font;
|
||||
|
||||
@@ -133,9 +161,8 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, iterator);
|
||||
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
|
||||
atlas_context, iterator);
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user