Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] grow glyph atlas instead of resizing when rect packer is full. #52849

Merged
merged 12 commits into from
May 17, 2024
341 changes: 169 additions & 172 deletions impeller/typographer/backends/skia/typographer_context_skia.cc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ std::shared_ptr<GlyphAtlas> TypographerContextSTB::CreateGlyphAtlas(
context.GetResourceAllocator()->GetMaxTextureSizeSupported() //
);

atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size);
atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size, 0);
if (atlas_size.IsEmpty()) {
return nullptr;
}
Expand Down
8 changes: 7 additions & 1 deletion impeller/typographer/glyph_atlas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ const ISize& GlyphAtlasContext::GetAtlasSize() const {
return atlas_size_;
}

int64_t GlyphAtlasContext::GetHeightAdjustment() const {
return height_adjustment_;
}

std::shared_ptr<RectanglePacker> GlyphAtlasContext::GetRectPacker() const {
return rect_packer_;
}

void GlyphAtlasContext::UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas,
ISize size) {
ISize size,
int64_t height_adjustment) {
atlas_ = std::move(atlas);
atlas_size_ = size;
height_adjustment_ = height_adjustment;
}

void GlyphAtlasContext::UpdateRectPacker(
Expand Down
15 changes: 14 additions & 1 deletion impeller/typographer/glyph_atlas.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,29 @@ class GlyphAtlasContext {
/// @brief Retrieve the previous (if any) rect packer.
std::shared_ptr<RectanglePacker> GetRectPacker() const;

//----------------------------------------------------------------------------
/// @brief A y-coordinate shift that must be applied to glyphs appended
/// to
/// the atlas.
///
/// The rectangle packer is only initialized for unfilled regions
/// of the atlas. The area the rectangle packer covers is offset
/// from the origin by this height adjustment.
int64_t GetHeightAdjustment() const;

//----------------------------------------------------------------------------
/// @brief Update the context with a newly constructed glyph atlas.
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas, ISize size);
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas,
ISize size,
int64_t height_adjustment_);

void UpdateRectPacker(std::shared_ptr<RectanglePacker> rect_packer);

private:
std::shared_ptr<GlyphAtlas> atlas_;
ISize atlas_size_;
std::shared_ptr<RectanglePacker> rect_packer_;
int64_t height_adjustment_;

GlyphAtlasContext(const GlyphAtlasContext&) = delete;

Expand Down
13 changes: 0 additions & 13 deletions impeller/typographer/rectangle_packer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ class SkylineRectanglePacker final : public RectanglePacker {
return area_so_far_ / ((float)width() * height());
}

std::unique_ptr<RectanglePacker> Clone(uint32_t scale) final;

private:
struct SkylineSegment {
int x_;
Expand Down Expand Up @@ -173,17 +171,6 @@ void SkylineRectanglePacker::AddSkylineLevel(size_t skyline_index,
}
}

std::unique_ptr<RectanglePacker> SkylineRectanglePacker::Clone(uint32_t scale) {
FML_DCHECK(scale != 0);
auto packer =
std::make_unique<SkylineRectanglePacker>(width(), height() * scale);
for (SkylineSegment segment : skyline_) {
packer->skyline_.push_back(segment);
}
packer->area_so_far_ = area_so_far_;
return packer;
}

std::shared_ptr<RectanglePacker> RectanglePacker::Factory(int width,
int height) {
return std::make_shared<SkylineRectanglePacker>(width, height);
Expand Down
13 changes: 0 additions & 13 deletions impeller/typographer/rectangle_packer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,6 @@ class RectanglePacker {
///
virtual Scalar PercentFull() const = 0;

//----------------------------------------------------------------------------
/// @brief Create a new rectangle packer with a larger scaled height
/// scaled and initialize its contents to the current packer.
///
/// @param[in] scale The scaling factor to be applied to the new height.
///
/// @return A new rectangle packer.
///
/// This method is used for growing the glyph atlas while keeping
/// existing rects in place. The width of the rectangle packer
/// cannot be increased.
virtual std::unique_ptr<RectanglePacker> Clone(uint32_t scale) = 0;

//----------------------------------------------------------------------------
/// @brief Empty out all previously added rectangles.
///
Expand Down
103 changes: 51 additions & 52 deletions impeller/typographer/typographer_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -325,58 +325,6 @@ TEST_P(TypographerTest, RectanglePackerAddsNonoverlapingRectangles) {
ASSERT_EQ(packer->PercentFull(), 0);
}

TEST(TypographerTest, CanCloneRectanglePackerEmpty) {
auto skyline = RectanglePacker::Factory(256, 256);

EXPECT_EQ(skyline->PercentFull(), 0);

auto skyline_2 = skyline->Clone(/*scale=*/2);

EXPECT_EQ(skyline->PercentFull(), 0);
}

TEST(TypographerTest, CanCloneRectanglePackerAndPreservePositions) {
auto skyline = RectanglePacker::Factory(256, 256);
IPoint16 loc;
EXPECT_TRUE(skyline->AddRect(100, 100, &loc));

EXPECT_EQ(loc.x(), 0);
EXPECT_EQ(loc.y(), 0);
auto percent = skyline->PercentFull();

auto skyline_2 = skyline->Clone(/*scale=*/2);

EXPECT_LT(skyline_2->PercentFull(), percent);
}

TEST(TypographerTest, CanCloneRectanglePackerWhileFull) {
auto skyline = RectanglePacker::Factory(256, 256);
IPoint16 loc;
// Add a rectangle the size of the entire area.
EXPECT_TRUE(skyline->AddRect(256, 256, &loc));
// Packer is now full.
EXPECT_FALSE(skyline->AddRect(256, 256, &loc));

auto skyline_2 = skyline->Clone(/*scale=*/2);

// Can now fit one more
EXPECT_TRUE(skyline_2->AddRect(256, 256, &loc));
}

TEST(TypographerTest, CloneToSameSizePreservesContents) {
auto skyline = RectanglePacker::Factory(256, 256);
IPoint16 loc;
// Add a rectangle the size of the entire area.
EXPECT_TRUE(skyline->AddRect(256, 256, &loc));
// Packer is now full.
EXPECT_FALSE(skyline->AddRect(256, 256, &loc));

auto skyline_2 = skyline->Clone(/*scale=*/1);

// Packer is still full.
EXPECT_FALSE(skyline->AddRect(256, 256, &loc));
}

TEST(TypographerTest, RectanglePackerFillsRows) {
auto skyline = RectanglePacker::Factory(257, 256);

Expand All @@ -398,6 +346,57 @@ TEST(TypographerTest, RectanglePackerFillsRows) {
EXPECT_EQ(loc.y(), 16);
}

TEST_P(TypographerTest, GlyphAtlasTextureWillGrowTilMaxTextureSize) {
if (GetBackend() == PlaygroundBackend::kOpenGLES) {
GTEST_SKIP() << "Atlas growth isn't supported for OpenGLES currently.";
}

auto host_buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
auto context = TypographerContextSkia::Make();
auto atlas_context =
context->CreateGlyphAtlasContext(GlyphAtlas::Type::kAlphaBitmap);
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font = flutter::testing::CreateTestFontOfSize(12);
auto blob = SkTextBlob::MakeFromString("A", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(*GetContext(), context.get(), *host_buffer,
GlyphAtlas::Type::kAlphaBitmap, 1.0f, atlas_context,
*MakeTextFrameFromTextBlobSkia(blob));
// Continually append new glyphs until the glyph size grows to the maximum.
// Note that the sizes here are more or less experimentally determined, but
// the important expectation is that the atlas size will shrink again after
// growing to the maximum size.
constexpr ISize expected_sizes[13] = {
{4096, 4096}, //
{4096, 4096}, //
{4096, 8192}, //
{4096, 8192}, //
{4096, 8192}, //
{4096, 8192}, //
{4096, 16384}, //
{4096, 16384}, //
{4096, 16384}, //
{4096, 16384}, //
{4096, 16384}, //
{4096, 16384}, //
{4096, 4096} // Shrinks!
};

for (int i = 0; i < 13; i++) {
SkFont sk_font = flutter::testing::CreateTestFontOfSize(50 + i);
auto blob = SkTextBlob::MakeFromString("A", sk_font);

atlas =
CreateGlyphAtlas(*GetContext(), context.get(), *host_buffer,
GlyphAtlas::Type::kAlphaBitmap, 50 + i, atlas_context,
*MakeTextFrameFromTextBlobSkia(blob));
ASSERT_TRUE(!!atlas);
EXPECT_EQ(atlas->GetTexture()->GetTextureDescriptor().size,
expected_sizes[i]);
}
}

} // namespace testing
} // namespace impeller

Expand Down