[Impeller] Supply a text backend to the AiksContext at runtime. (flutter/engine#44884)

Resolves https://github.com/flutter/flutter/issues/130947.

- Allow for setting the text renderer (`TextRenderContext`) when the
`AiksContext` is built. Previously, the text renderer was selected at
build time through linking a `TextRenderContext::Create` symbol.
- Support using Impeller without a text backend. Throw a clear error
when trying to render text if no text backend is present.
- Don't track the Impeller context in `TextRenderContext`. Just let the
renderer supply its context when generating atlases.
- Allow for overriding the `TextRenderContext` for individual Aiks
playground tests/goldens.
This commit is contained in:
Brandon DeRosier
2023-08-21 21:55:28 -07:00
committed by GitHub
parent af5d8338a9
commit cdab07b84a
31 changed files with 231 additions and 152 deletions

View File

@@ -5,16 +5,19 @@
#include "impeller/aiks/aiks_context.h"
#include "impeller/aiks/picture.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
AiksContext::AiksContext(std::shared_ptr<Context> context)
AiksContext::AiksContext(std::shared_ptr<Context> context,
std::shared_ptr<TextRenderContext> text_render_context)
: context_(std::move(context)) {
if (!context_ || !context_->IsValid()) {
return;
}
content_context_ = std::make_unique<ContentContext>(context_);
content_context_ = std::make_unique<ContentContext>(
context_, std::move(text_render_context));
if (!content_context_->IsValid()) {
return;
}

View File

@@ -10,6 +10,7 @@
#include "impeller/entity/contents/content_context.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_target.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
@@ -18,7 +19,17 @@ class RenderPass;
class AiksContext {
public:
AiksContext(std::shared_ptr<Context> context);
/// Construct a new AiksContext.
///
/// @param context The Impeller context that Aiks should use for
/// allocating resources and executing device
/// commands. Required.
/// @param text_render_context The text backend to use for rendering text. If
/// `nullptr` is supplied, then attempting to draw
/// text with Aiks will result in validation
/// errors.
AiksContext(std::shared_ptr<Context> context,
std::shared_ptr<TextRenderContext> text_render_context);
~AiksContext();

View File

@@ -4,16 +4,25 @@
#include "impeller/aiks/aiks_playground.h"
#include "impeller/aiks/aiks_context.h"
#include <memory>
#include "impeller/aiks/aiks_context.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "impeller/typographer/text_render_context.h"
#include "third_party/imgui/imgui.h"
namespace impeller {
AiksPlayground::AiksPlayground() = default;
AiksPlayground::AiksPlayground()
: text_render_context_(TextRenderContextSkia::Make()) {}
AiksPlayground::~AiksPlayground() = default;
void AiksPlayground::SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context) {
text_render_context_ = std::move(text_render_context);
}
bool AiksPlayground::OpenPlaygroundHere(const Picture& picture) {
return OpenPlaygroundHere(
[&picture](AiksContext& renderer, RenderTarget& render_target) -> bool {
@@ -26,7 +35,7 @@ bool AiksPlayground::OpenPlaygroundHere(AiksPlaygroundCallback callback) {
return true;
}
AiksContext renderer(GetContext());
AiksContext renderer(GetContext(), text_render_context_);
if (!renderer.IsValid()) {
return false;

View File

@@ -8,6 +8,7 @@
#include "impeller/aiks/aiks_context.h"
#include "impeller/aiks/picture.h"
#include "impeller/playground/playground_test.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
@@ -20,11 +21,16 @@ class AiksPlayground : public PlaygroundTest {
~AiksPlayground();
void SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context);
bool OpenPlaygroundHere(const Picture& picture);
bool OpenPlaygroundHere(AiksPlaygroundCallback callback);
private:
std::shared_ptr<TextRenderContext> text_render_context_;
FML_DISALLOW_COPY_AND_ASSIGN(AiksPlayground);
};

View File

@@ -1002,7 +1002,7 @@ TEST_P(AiksTest, CanPictureConvertToImage) {
recorder_canvas.DrawRect({200.0, 200.0, 600, 600}, paint);
Canvas canvas;
AiksContext renderer(GetContext());
AiksContext renderer(GetContext(), nullptr);
paint.color = Color::BlackTransparent();
canvas.DrawPaint(paint);
Picture picture = recorder_canvas.EndRecordingAsPicture();
@@ -2139,7 +2139,7 @@ TEST_P(AiksTest, DrawPaintAbsorbsClears) {
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
ASSERT_EQ(spy->render_passes_.size(), 1llu);
@@ -2159,7 +2159,7 @@ TEST_P(AiksTest, DrawRectAbsorbsClears) {
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
ASSERT_EQ(spy->render_passes_.size(), 1llu);
@@ -2179,7 +2179,7 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRRect) {
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
ASSERT_EQ(spy->render_passes_.size(), 1llu);
@@ -2199,7 +2199,7 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegativeRotation) {
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
ASSERT_EQ(spy->render_passes_.size(), 1llu);
@@ -2219,7 +2219,7 @@ TEST_P(AiksTest, DrawRectAbsorbsClearsNegative) {
Picture picture = canvas.EndRecordingAsPicture();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {301, 301});
ASSERT_EQ(spy->render_passes_.size(), 1llu);
@@ -2243,7 +2243,7 @@ TEST_P(AiksTest, ClipRectElidesNoOpClips) {
std::shared_ptr<ContextSpy> spy = ContextSpy::Make();
std::shared_ptr<Context> real_context = GetContext();
std::shared_ptr<ContextMock> mock_context = spy->MakeContext(real_context);
AiksContext renderer(mock_context);
AiksContext renderer(mock_context, nullptr);
std::shared_ptr<Image> image = picture.ToImage(renderer, {300, 300});
ASSERT_EQ(spy->render_passes_.size(), 1llu);

View File

@@ -7,6 +7,7 @@
#include "flutter/testing/testing.h"
#include "impeller/aiks/aiks_context.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "third_party/imgui/imgui.h"
#include "third_party/skia/include/core/SkData.h"
@@ -29,7 +30,7 @@ bool DlPlayground::OpenPlaygroundHere(DisplayListPlaygroundCallback callback) {
return true;
}
AiksContext context(GetContext());
AiksContext context(GetContext(), TextRenderContextSkia::Make());
if (!context.IsValid()) {
return false;
}

View File

@@ -16,6 +16,7 @@
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"
#include "impeller/tessellator/tessellator.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
@@ -159,9 +160,12 @@ static std::unique_ptr<PipelineT> CreateDefaultPipeline(
return std::make_unique<PipelineT>(context, desc);
}
ContentContext::ContentContext(std::shared_ptr<Context> context)
ContentContext::ContentContext(
std::shared_ptr<Context> context,
std::shared_ptr<TextRenderContext> text_render_context)
: context_(std::move(context)),
lazy_glyph_atlas_(std::make_shared<LazyGlyphAtlas>()),
lazy_glyph_atlas_(
std::make_shared<LazyGlyphAtlas>(std::move(text_render_context))),
tessellator_(std::make_shared<Tessellator>()),
scene_context_(std::make_shared<scene::SceneContext>(context_)),
render_target_cache_(std::make_shared<RenderTargetCache>(

View File

@@ -19,6 +19,7 @@
#include "impeller/renderer/pipeline.h"
#include "impeller/renderer/render_target.h"
#include "impeller/scene/scene_context.h"
#include "impeller/typographer/text_render_context.h"
#ifdef IMPELLER_DEBUG
#include "impeller/entity/checkerboard.frag.h"
@@ -339,7 +340,9 @@ class RenderTargetCache;
class ContentContext {
public:
explicit ContentContext(std::shared_ptr<Context> context);
explicit ContentContext(
std::shared_ptr<Context> context,
std::shared_ptr<TextRenderContext> text_render_context);
~ContentContext();

View File

@@ -12,10 +12,8 @@
#include "impeller/core/sampler_descriptor.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/sampler_library.h"
#include "impeller/tessellator/tessellator.h"
#include "impeller/typographer/glyph_atlas.h"
#include "impeller/typographer/lazy_glyph_atlas.h"
@@ -30,12 +28,12 @@ void TextContents::SetTextFrame(const TextFrame& frame) {
}
std::shared_ptr<GlyphAtlas> TextContents::ResolveAtlas(
Context& context,
GlyphAtlas::Type type,
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
std::shared_ptr<Context> context) const {
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas) const {
FML_DCHECK(lazy_atlas);
if (lazy_atlas) {
return lazy_atlas->CreateOrGetGlyphAtlas(type, std::move(context));
return lazy_atlas->CreateOrGetGlyphAtlas(context, type);
}
return nullptr;
@@ -90,7 +88,7 @@ bool TextContents::Render(const ContentContext& renderer,
auto type = frame_.GetAtlasType();
auto atlas =
ResolveAtlas(type, renderer.GetLazyGlyphAtlas(), renderer.GetContext());
ResolveAtlas(*renderer.GetContext(), type, renderer.GetLazyGlyphAtlas());
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";

View File

@@ -63,9 +63,9 @@ class TextContents final : public Contents {
Vector2 offset_;
std::shared_ptr<GlyphAtlas> ResolveAtlas(
Context& context,
GlyphAtlas::Type type,
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
std::shared_ptr<Context> context) const;
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas) const;
FML_DISALLOW_COPY_AND_ASSIGN(TextContents);
};

View File

@@ -5,21 +5,27 @@
#include "impeller/entity/entity_playground.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "third_party/imgui/imgui.h"
namespace impeller {
EntityPlayground::EntityPlayground() = default;
EntityPlayground::EntityPlayground()
: text_render_context_(TextRenderContextSkia::Make()) {}
EntityPlayground::~EntityPlayground() = default;
void EntityPlayground::SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context) {
text_render_context_ = std::move(text_render_context);
}
bool EntityPlayground::OpenPlaygroundHere(EntityPass& entity_pass) {
if (!switches_.enable_playground) {
return true;
}
ContentContext content_context(GetContext());
ContentContext content_context(GetContext(), text_render_context_);
if (!content_context.IsValid()) {
return false;
}
@@ -35,7 +41,7 @@ bool EntityPlayground::OpenPlaygroundHere(Entity entity) {
return true;
}
ContentContext content_context(GetContext());
ContentContext content_context(GetContext(), text_render_context_);
if (!content_context.IsValid()) {
return false;
}
@@ -50,7 +56,7 @@ bool EntityPlayground::OpenPlaygroundHere(EntityPlaygroundCallback callback) {
return true;
}
ContentContext content_context(GetContext());
ContentContext content_context(GetContext(), text_render_context_);
if (!content_context.IsValid()) {
return false;
}

View File

@@ -4,12 +4,13 @@
#pragma once
#include "impeller/playground/playground_test.h"
#include "flutter/fml/macros.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/entity_pass.h"
#include "impeller/playground/playground_test.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
@@ -22,6 +23,9 @@ class EntityPlayground : public PlaygroundTest {
~EntityPlayground();
void SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context);
bool OpenPlaygroundHere(Entity entity);
bool OpenPlaygroundHere(EntityPass& entity_pass);
@@ -29,6 +33,8 @@ class EntityPlayground : public PlaygroundTest {
bool OpenPlaygroundHere(EntityPlaygroundCallback callback);
private:
std::shared_ptr<TextRenderContext> text_render_context_;
FML_DISALLOW_COPY_AND_ASSIGN(EntityPlayground);
};

View File

@@ -2175,7 +2175,8 @@ TEST_P(EntityTest, InheritOpacityTest) {
font.setSize(30);
auto blob = SkTextBlob::MakeFromString("A", font);
auto frame = TextFrameFromTextBlob(blob);
auto lazy_glyph_atlas = std::make_shared<LazyGlyphAtlas>();
auto lazy_glyph_atlas =
std::make_shared<LazyGlyphAtlas>(TextRenderContextSkia::Make());
lazy_glyph_atlas->AddTextFrame(frame, 1.0f);
auto text_contents = std::make_shared<TextContents>();

View File

@@ -11,6 +11,7 @@
#include "flutter/impeller/playground/playground.h"
#include "flutter/impeller/renderer/render_target.h"
#include "flutter/testing/testing.h"
#include "impeller/typographer/text_render_context.h"
#if FML_OS_MACOSX
#include "flutter/fml/platform/darwin/scoped_nsautorelease_pool.h"
@@ -26,12 +27,17 @@ class GoldenPlaygroundTest
GoldenPlaygroundTest();
~GoldenPlaygroundTest() override;
void SetUp();
void TearDown();
PlaygroundBackend GetBackend() const;
void SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context);
bool OpenPlaygroundHere(const Picture& picture);
bool OpenPlaygroundHere(const AiksPlaygroundCallback& callback);
@@ -58,6 +64,8 @@ class GoldenPlaygroundTest
fml::ScopedNSAutoreleasePool autorelease_pool_;
#endif
std::shared_ptr<TextRenderContext> text_render_context_;
struct GoldenPlaygroundTestImpl;
// This is only a shared_ptr so it can work with a forward declared type.
std::shared_ptr<GoldenPlaygroundTestImpl> pimpl_;

View File

@@ -4,12 +4,15 @@
#include <dlfcn.h>
#include <filesystem>
#include <memory>
#include "flutter/impeller/golden_tests/golden_playground_test.h"
#include "flutter/impeller/aiks/picture.h"
#include "flutter/impeller/golden_tests/golden_digest.h"
#include "flutter/impeller/golden_tests/metal_screenshoter.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
@@ -85,7 +88,15 @@ struct GoldenPlaygroundTest::GoldenPlaygroundTestImpl {
};
GoldenPlaygroundTest::GoldenPlaygroundTest()
: pimpl_(new GoldenPlaygroundTest::GoldenPlaygroundTestImpl()) {}
: text_render_context_(TextRenderContextSkia::Make()),
pimpl_(new GoldenPlaygroundTest::GoldenPlaygroundTestImpl()) {}
GoldenPlaygroundTest::~GoldenPlaygroundTest() = default;
void GoldenPlaygroundTest::SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context) {
text_render_context_ = std::move(text_render_context);
};
void GoldenPlaygroundTest::TearDown() {
ASSERT_FALSE(dlopen("/usr/local/lib/libMoltenVK.dylib", RTLD_NOLOAD));
@@ -124,8 +135,10 @@ PlaygroundBackend GoldenPlaygroundTest::GetBackend() const {
}
bool GoldenPlaygroundTest::OpenPlaygroundHere(const Picture& picture) {
auto screenshot =
pimpl_->screenshoter->MakeScreenshot(picture, pimpl_->window_size);
AiksContext renderer(GetContext(), text_render_context_);
auto screenshot = pimpl_->screenshoter->MakeScreenshot(renderer, picture,
pimpl_->window_size);
return SaveScreenshot(std::move(screenshot));
}
@@ -161,7 +174,7 @@ std::shared_ptr<RuntimeStage> GoldenPlaygroundTest::OpenAssetAsRuntimeStage(
}
std::shared_ptr<Context> GoldenPlaygroundTest::GetContext() const {
return pimpl_->screenshoter->GetContext().GetContext();
return pimpl_->screenshoter->GetPlayground().GetContext();
}
Point GoldenPlaygroundTest::GetContentScale() const {

View File

@@ -6,7 +6,14 @@
namespace impeller {
GoldenPlaygroundTest::GoldenPlaygroundTest() {}
GoldenPlaygroundTest::GoldenPlaygroundTest() = default;
GoldenPlaygroundTest::~GoldenPlaygroundTest() = default;
void GoldenPlaygroundTest::SetTextRenderContext(
std::shared_ptr<TextRenderContext> text_render_context) {
text_render_context_ = std::move(text_render_context);
};
void GoldenPlaygroundTest::TearDown() {}

View File

@@ -7,6 +7,7 @@
#include <sstream>
#include "flutter/fml/platform/darwin/scoped_nsautorelease_pool.h"
#include "impeller/aiks/aiks_context.h"
#include "impeller/aiks/canvas.h"
#include "impeller/entity/contents/conical_gradient_contents.h"
#include "impeller/geometry/path_builder.h"
@@ -56,7 +57,7 @@ class GoldenTests : public ::testing::Test {
void SetUp() override {
testing::GoldenDigest::Instance()->AddDimension(
"gpu_string",
Screenshoter().GetContext().GetContext()->DescribeGpuModel());
Screenshoter().GetPlayground().GetContext()->DescribeGpuModel());
}
private:
@@ -79,7 +80,10 @@ TEST_F(GoldenTests, ConicalGradient) {
paint.style = Paint::Style::kFill;
canvas.DrawRect(Rect(10, 10, 250, 250), paint);
Picture picture = canvas.EndRecordingAsPicture();
auto screenshot = Screenshoter().MakeScreenshot(picture);
auto aiks_context =
AiksContext(Screenshoter().GetPlayground().GetContext(), nullptr);
auto screenshot = Screenshoter().MakeScreenshot(aiks_context, picture);
ASSERT_TRUE(SaveScreenshot(std::move(screenshot)));
}
} // namespace testing

View File

@@ -12,24 +12,20 @@
namespace impeller {
namespace testing {
/// Converts `Picture`'s to `MetalScreenshot`'s with the playground backend.
/// Converts `Picture`s to `MetalScreenshot`s with the playground backend.
class MetalScreenshoter {
public:
MetalScreenshoter();
std::unique_ptr<MetalScreenshot> MakeScreenshot(const Picture& picture,
std::unique_ptr<MetalScreenshot> MakeScreenshot(AiksContext& aiks_context,
const Picture& picture,
const ISize& size = {300,
300});
AiksContext& GetContext() { return *aiks_context_; }
const AiksContext& GetContext() const { return *aiks_context_; }
const PlaygroundImpl& GetPlayground() const { return *playground_; }
private:
std::unique_ptr<PlaygroundImpl> playground_;
std::unique_ptr<AiksContext> aiks_context_;
};
} // namespace testing

View File

@@ -17,15 +17,15 @@ MetalScreenshoter::MetalScreenshoter() {
FML_CHECK(::glfwInit() == GLFW_TRUE);
playground_ =
PlaygroundImpl::Create(PlaygroundBackend::kMetal, PlaygroundSwitches{});
aiks_context_.reset(new AiksContext(playground_->GetContext()));
}
std::unique_ptr<MetalScreenshot> MetalScreenshoter::MakeScreenshot(
AiksContext& aiks_context,
const Picture& picture,
const ISize& size) {
Vector2 content_scale = playground_->GetContentScale();
std::shared_ptr<Image> image = picture.ToImage(
*aiks_context_,
aiks_context,
ISize(size.width * content_scale.x, size.height * content_scale.y));
std::shared_ptr<Texture> texture = image->GetTexture();
id<MTLTexture> metal_texture =

View File

@@ -125,7 +125,7 @@ TEST_P(ComputeSubgroupTest, PathPlayground) {
}
ImGui::End();
ContentContext renderer(context);
ContentContext renderer(context, nullptr);
if (!renderer.IsValid()) {
return false;
}
@@ -329,7 +329,7 @@ TEST_P(ComputeSubgroupTest, LargePath) {
->count;
});
ContentContext renderer(context);
ContentContext renderer(context, nullptr);
if (!renderer.IsValid()) {
return false;
}
@@ -413,7 +413,7 @@ TEST_P(ComputeSubgroupTest, QuadAndCubicInOnePath) {
ASSERT_EQ(status, ComputeTessellator::Status::kOk);
auto callback = [&](RenderPass& pass) -> bool {
ContentContext renderer(context);
ContentContext renderer(context, nullptr);
if (!renderer.IsValid()) {
return false;
}

View File

@@ -12,11 +12,10 @@
#include "impeller/core/allocator.h"
#include "impeller/typographer/backends/skia/typeface_skia.h"
#include "impeller/typographer/rectangle_packer.h"
#include "impeller/typographer/text_render_context.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkFontMetrics.h"
#include "third_party/skia/include/core/SkRSXform.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace impeller {
@@ -24,19 +23,16 @@ namespace impeller {
using FontGlyphPairRefVector =
std::vector<std::reference_wrapper<const FontGlyphPair>>;
std::unique_ptr<TextRenderContext> TextRenderContext::Create(
std::shared_ptr<Context> context) {
// There is only one backend today.
return std::make_unique<TextRenderContextSkia>(std::move(context));
}
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
// the underlying causes of the overlap.
// https://github.com/flutter/flutter/issues/114563
constexpr auto kPadding = 2;
TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
: TextRenderContext(std::move(context)) {}
std::shared_ptr<TextRenderContext> TextRenderContextSkia::Make() {
return std::make_shared<TextRenderContextSkia>();
}
TextRenderContextSkia::TextRenderContextSkia() = default;
TextRenderContextSkia::~TextRenderContextSkia() = default;
@@ -310,6 +306,7 @@ static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
}
std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const {
@@ -429,8 +426,8 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
format = PixelFormat::kR8G8B8A8UNormInt;
break;
}
auto texture = UploadGlyphTextureAtlas(GetContext()->GetResourceAllocator(),
bitmap, atlas_size, format);
auto texture = UploadGlyphTextureAtlas(context.GetResourceAllocator(), bitmap,
atlas_size, format);
if (!texture) {
return nullptr;
}

View File

@@ -11,12 +11,15 @@ namespace impeller {
class TextRenderContextSkia : public TextRenderContext {
public:
TextRenderContextSkia(std::shared_ptr<Context> context);
static std::shared_ptr<TextRenderContext> Make();
TextRenderContextSkia();
~TextRenderContextSkia() override;
// |TextRenderContext|
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const override;

View File

@@ -11,8 +11,10 @@
namespace impeller {
LazyGlyphAtlas::LazyGlyphAtlas()
: alpha_context_(std::make_shared<GlyphAtlasContext>()),
LazyGlyphAtlas::LazyGlyphAtlas(
std::shared_ptr<TextRenderContext> text_render_context)
: text_render_context_(std::move(text_render_context)),
alpha_context_(std::make_shared<GlyphAtlasContext>()),
color_context_(std::make_shared<GlyphAtlasContext>()) {}
LazyGlyphAtlas::~LazyGlyphAtlas() = default;
@@ -33,8 +35,8 @@ void LazyGlyphAtlas::ResetTextFrames() {
}
std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<Context> context) const {
Context& context,
GlyphAtlas::Type type) const {
{
auto atlas_it = atlas_map_.find(type);
if (atlas_it != atlas_map_.end()) {
@@ -42,14 +44,22 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
}
}
auto text_context = TextRenderContext::Create(std::move(context));
if (!text_context || !text_context->IsValid()) {
if (!text_render_context_) {
VALIDATION_LOG << "Unable to render text because a TextRenderContext has "
"not been set.";
return nullptr;
}
if (!text_render_context_->IsValid()) {
VALIDATION_LOG
<< "Unable to render text because the TextRenderContext is invalid.";
return nullptr;
}
auto& set = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_set_ : color_set_;
auto atlas_context =
type == GlyphAtlas::Type::kAlphaBitmap ? alpha_context_ : color_context_;
auto atlas = text_context->CreateGlyphAtlas(type, atlas_context, set);
auto atlas =
text_render_context_->CreateGlyphAtlas(context, type, atlas_context, set);
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Could not create valid atlas.";
return nullptr;

View File

@@ -10,12 +10,14 @@
#include "impeller/renderer/context.h"
#include "impeller/typographer/glyph_atlas.h"
#include "impeller/typographer/text_frame.h"
#include "impeller/typographer/text_render_context.h"
namespace impeller {
class LazyGlyphAtlas {
public:
LazyGlyphAtlas();
explicit LazyGlyphAtlas(
std::shared_ptr<TextRenderContext> text_render_context);
~LazyGlyphAtlas();
@@ -24,10 +26,12 @@ class LazyGlyphAtlas {
void ResetTextFrames();
std::shared_ptr<GlyphAtlas> CreateOrGetGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<Context> context) const;
Context& context,
GlyphAtlas::Type type) const;
private:
std::shared_ptr<TextRenderContext> text_render_context_;
FontGlyphPair::Set alpha_set_;
FontGlyphPair::Set color_set_;
std::shared_ptr<GlyphAtlasContext> alpha_context_;

View File

@@ -8,11 +8,7 @@
namespace impeller {
TextRenderContext::TextRenderContext(std::shared_ptr<Context> context)
: context_(std::move(context)) {
if (!context_ || !context_->IsValid()) {
return;
}
TextRenderContext::TextRenderContext() {
is_valid_ = true;
}
@@ -22,8 +18,4 @@ bool TextRenderContext::IsValid() const {
return is_valid_;
}
const std::shared_ptr<Context>& TextRenderContext::GetContext() const {
return context_;
}
} // namespace impeller

View File

@@ -10,7 +10,6 @@
#include "flutter/fml/macros.h"
#include "impeller/renderer/context.h"
#include "impeller/typographer/glyph_atlas.h"
#include "impeller/typographer/text_frame.h"
namespace impeller {
@@ -23,24 +22,15 @@ namespace impeller {
///
class TextRenderContext {
public:
static std::unique_ptr<TextRenderContext> Create(
std::shared_ptr<Context> context);
virtual ~TextRenderContext();
virtual bool IsValid() const;
//----------------------------------------------------------------------------
/// @brief Get the underlying graphics context.
///
/// @return The context.
///
const std::shared_ptr<Context>& GetContext() const;
// TODO(dnfield): Callers should not need to know which type of atlas to
// create. https://github.com/flutter/flutter/issues/111640
virtual std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
Context& context,
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const = 0;
@@ -50,12 +40,9 @@ class TextRenderContext {
/// @brief Create a new context to render text that talks to an
/// underlying graphics context.
///
/// @param[in] context The graphics context
///
TextRenderContext(std::shared_ptr<Context> context);
TextRenderContext();
private:
std::shared_ptr<Context> context_;
bool is_valid_ = false;
FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContext);

View File

@@ -8,7 +8,6 @@
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "impeller/typographer/lazy_glyph_atlas.h"
#include "impeller/typographer/rectangle_packer.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkRect.h"
@@ -24,14 +23,16 @@ using TypographerTest = PlaygroundTest;
INSTANTIATE_PLAYGROUND_SUITE(TypographerTest);
static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
const TextRenderContext* context,
Context& context,
const TextRenderContext* text_render_context,
GlyphAtlas::Type type,
Scalar scale,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
const TextFrame& frame) {
FontGlyphPair::Set set;
frame.CollectUniqueFontGlyphPairs(set, scale);
return context->CreateGlyphAtlas(type, atlas_context, set);
return text_render_context->CreateGlyphAtlas(context, type, atlas_context,
set);
}
TEST_P(TypographerTest, CanConvertTextBlob) {
@@ -48,20 +49,20 @@ TEST_P(TypographerTest, CanConvertTextBlob) {
}
TEST_P(TypographerTest, CanCreateRenderContext) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
ASSERT_TRUE(context && context->IsValid());
}
TEST_P(TypographerTest, CanCreateGlyphAtlas) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
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 =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto atlas = CreateGlyphAtlas(*GetContext(), context.get(),
GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
@@ -111,7 +112,7 @@ TEST_P(TypographerTest, LazyAtlasTracksColor) {
ASSERT_FALSE(frame.GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
LazyGlyphAtlas lazy_atlas;
LazyGlyphAtlas lazy_atlas(TextRenderContextSkia::Make());
lazy_atlas.AddTextFrame(frame, 1.0f);
@@ -123,24 +124,24 @@ TEST_P(TypographerTest, LazyAtlasTracksColor) {
// Creates different atlases for color and alpha bitmap.
auto color_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
GlyphAtlas::Type::kColorBitmap, GetContext());
*GetContext(), GlyphAtlas::Type::kColorBitmap);
auto bitmap_atlas = lazy_atlas.CreateOrGetGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, GetContext());
*GetContext(), GlyphAtlas::Type::kAlphaBitmap);
ASSERT_FALSE(color_atlas == bitmap_atlas);
}
TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
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 =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto atlas = CreateGlyphAtlas(*GetContext(), context.get(),
GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@@ -149,30 +150,30 @@ TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
}
TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
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 =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto atlas = CreateGlyphAtlas(*GetContext(), context.get(),
GlyphAtlas::Type::kAlphaBitmap, 1.0f,
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 =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto next_atlas = CreateGlyphAtlas(
*GetContext(), context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
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 context = TextRenderContextSkia::Make();
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
@@ -191,8 +192,9 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
for (size_t index = 0; index < size_count; index += 1) {
TextFrameFromTextBlob(blob).CollectUniqueFontGlyphPairs(set, 0.6 * index);
};
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
std::move(atlas_context), set);
auto atlas =
context->CreateGlyphAtlas(*GetContext(), GlyphAtlas::Type::kAlphaBitmap,
std::move(atlas_context), set);
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@@ -213,15 +215,15 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
}
TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto atlas = CreateGlyphAtlas(*GetContext(), context.get(),
GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@@ -233,9 +235,9 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
// Now create a new glyph atlas with a nearly identical blob.
auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
auto next_atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
auto next_atlas = CreateGlyphAtlas(
*GetContext(), context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
ASSERT_EQ(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();
@@ -246,15 +248,15 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
}
TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
auto context = TextRenderContext::Create(GetContext());
auto context = TextRenderContextSkia::Make();
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto atlas = CreateGlyphAtlas(*GetContext(), context.get(),
GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@@ -267,9 +269,9 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
// but change the type.
auto blob2 = SkTextBlob::MakeFromString("spooky 1", sk_font);
auto next_atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kColorBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
auto next_atlas = CreateGlyphAtlas(
*GetContext(), context.get(), GlyphAtlas::Type::kColorBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
ASSERT_NE(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();

View File

@@ -25,9 +25,10 @@
#include "flutter/fml/time/time_point.h"
#if IMPELLER_SUPPORTS_RENDERING
// GN is having trouble understanding how this works in the Fuchsia builds.
#include "flutter/impeller/aiks/aiks_context.h" // nogncheck
#include "flutter/impeller/renderer/context.h" // nogncheck
#endif // IMPELLER_SUPPORTS_RENDERING
#include "impeller/aiks/aiks_context.h" // nogncheck
#include "impeller/renderer/context.h" // nogncheck
#include "impeller/typographer/backends/skia/text_render_context_skia.h" // nogncheck
#endif // IMPELLER_SUPPORTS_RENDERING
#include "flutter/lib/ui/snapshot_delegate.h"
#include "flutter/shell/common/pipeline.h"
#include "flutter/shell/common/snapshot_controller.h"
@@ -544,7 +545,8 @@ class Rasterizer final : public SnapshotDelegate,
return surface_->GetAiksContext();
}
if (auto context = impeller_context_.lock()) {
return std::make_shared<impeller::AiksContext>(context);
return std::make_shared<impeller::AiksContext>(
context, impeller::TextRenderContextSkia::Make());
}
#endif
return nullptr;

View File

@@ -5,9 +5,10 @@
#include "flutter/shell/gpu/gpu_surface_gl_impeller.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/impeller/display_list/dl_dispatcher.h"
#include "flutter/impeller/renderer/backend/gles/surface_gles.h"
#include "flutter/impeller/renderer/renderer.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
#include "impeller/renderer/renderer.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
namespace flutter {
@@ -28,7 +29,8 @@ GPUSurfaceGLImpeller::GPUSurfaceGLImpeller(
return;
}
auto aiks_context = std::make_shared<impeller::AiksContext>(context);
auto aiks_context = std::make_shared<impeller::AiksContext>(
context, impeller::TextRenderContextSkia::Make());
if (!aiks_context->IsValid()) {
return;

View File

@@ -11,8 +11,9 @@
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/trace_event.h"
#include "flutter/impeller/display_list/dl_dispatcher.h"
#include "flutter/impeller/renderer/backend/metal/surface_mtl.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/renderer/backend/metal/surface_mtl.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
static_assert(!__has_feature(objc_arc), "ARC must be disabled.");
@@ -35,7 +36,8 @@ GPUSurfaceMetalImpeller::GPUSurfaceMetalImpeller(GPUSurfaceMetalDelegate* delega
render_target_type_(delegate->GetRenderTargetType()),
impeller_renderer_(CreateImpellerRenderer(context)),
aiks_context_(
std::make_shared<impeller::AiksContext>(impeller_renderer_ ? context : nullptr)),
std::make_shared<impeller::AiksContext>(impeller_renderer_ ? context : nullptr,
impeller::TextRenderContextSkia::Make())),
render_to_surface_(render_to_surface) {
// If this preference is explicitly set, we allow for disabling partial repaint.
NSNumber* disablePartialRepaint =

View File

@@ -5,10 +5,11 @@
#include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/impeller/display_list/dl_dispatcher.h"
#include "flutter/impeller/renderer/renderer.h"
#include "impeller/display_list/dl_dispatcher.h"
#include "impeller/renderer/backend/vulkan/surface_context_vk.h"
#include "impeller/renderer/renderer.h"
#include "impeller/renderer/surface.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
namespace flutter {
@@ -23,7 +24,8 @@ GPUSurfaceVulkanImpeller::GPUSurfaceVulkanImpeller(
return;
}
auto aiks_context = std::make_shared<impeller::AiksContext>(context);
auto aiks_context = std::make_shared<impeller::AiksContext>(
context, impeller::TextRenderContextSkia::Make());
if (!aiks_context->IsValid()) {
return;
}