Reland: [Impeller] Use a device buffer for SkBitmap allocation, use Linear texture on Metal backend. (flutter/engine#41538)

Original PR: https://github.com/flutter/engine/pull/41374

This was reverted because it broke on simulators as they do not support linear textures. To fix this, I've ifdef'd out the DeviceBufferMTL implementation of AsTexture so that it falls back to the slow path copy. Also updated the capabilities check so that the glyph atlas updates the texture contents when it changes.
This commit is contained in:
Jonah Williams 2023-04-26 21:33:04 -07:00 committed by GitHub
parent 3335dcd1ee
commit 144af3487e
16 changed files with 286 additions and 84 deletions

View File

@ -45,6 +45,14 @@ static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
return supports_subgroups;
}
static constexpr bool SupportsLinearTexture() {
#ifdef FML_OS_IOS_SIMULATOR
return false;
#else
return true;
#endif // FML_OS_IOS_SIMULATOR
}
static std::unique_ptr<Capabilities> InferMetalCapabilities(
id<MTLDevice> device,
PixelFormat color_format) {
@ -55,6 +63,7 @@ static std::unique_ptr<Capabilities> InferMetalCapabilities(
.SetSupportsBufferToTextureBlits(true)
.SetSupportsTextureToTextureBlits(true)
.SetSupportsDecalTileMode(true)
.SetSupportsSharedDeviceBufferTextureMemory(SupportsLinearTexture())
.SetSupportsFramebufferFetch(DeviceSupportsFramebufferFetch(device))
.SetDefaultColorFormat(color_format)
.SetDefaultStencilFormat(PixelFormat::kS8UInt)

View File

@ -35,10 +35,12 @@ class DeviceBufferMTL final : public DeviceBuffer,
// |DeviceBuffer|
uint8_t* OnGetContents() const override;
#ifndef FML_OS_IOS_SIMULATOR
// |DeviceBuffer|
std::shared_ptr<Texture> AsTexture(Allocator& allocator,
const TextureDescriptor& descriptor,
uint16_t row_bytes) const override;
#endif // FML_OS_IOS_SIMULATOR
// |DeviceBuffer|
bool OnCopyHostBuffer(const uint8_t* source,

View File

@ -29,6 +29,7 @@ uint8_t* DeviceBufferMTL::OnGetContents() const {
return reinterpret_cast<uint8_t*>(buffer_.contents);
}
#ifndef FML_OS_IOS_SIMULATOR
std::shared_ptr<Texture> DeviceBufferMTL::AsTexture(
Allocator& allocator,
const TextureDescriptor& descriptor,
@ -52,6 +53,7 @@ std::shared_ptr<Texture> DeviceBufferMTL::AsTexture(
}
return std::make_shared<TextureMTL>(descriptor, texture);
}
#endif // FML_OS_IOS_SIMULATOR
[[nodiscard]] bool DeviceBufferMTL::OnCopyHostBuffer(const uint8_t* source,
Range source_range,

View File

@ -348,10 +348,16 @@ bool CapabilitiesVK::SupportsReadFromOnscreenTexture() const {
return false;
}
// |Capabilities|
bool CapabilitiesVK::SupportsDecalTileMode() const {
return true;
}
// |Capabilities|
bool CapabilitiesVK::SupportsSharedDeviceBufferTextureMemory() const {
return false;
}
// |Capabilities|
PixelFormat CapabilitiesVK::GetDefaultColorFormat() const {
return color_format_;

View File

@ -79,6 +79,9 @@ class CapabilitiesVK final : public Capabilities,
// |Capabilities|
bool SupportsDecalTileMode() const override;
// |Capabilities|
bool SupportsSharedDeviceBufferTextureMemory() const override;
// |Capabilities|
PixelFormat GetDefaultColorFormat() const override;

View File

@ -66,6 +66,11 @@ class StandardCapabilities final : public Capabilities {
return supports_decal_tile_mode_;
}
// |Capabilities|
bool SupportsSharedDeviceBufferTextureMemory() const override {
return supports_shared_device_buffer_texture_memory_;
}
// |Capabilities|
PixelFormat GetDefaultColorFormat() const override {
return default_color_format_;
@ -88,6 +93,7 @@ class StandardCapabilities final : public Capabilities {
bool supports_read_from_onscreen_texture,
bool supports_read_from_resolve,
bool supports_decal_tile_mode,
bool supports_shared_device_buffer_texture_memory,
PixelFormat default_color_format,
PixelFormat default_stencil_format)
: has_threading_restrictions_(has_threading_restrictions),
@ -102,6 +108,8 @@ class StandardCapabilities final : public Capabilities {
supports_read_from_onscreen_texture),
supports_read_from_resolve_(supports_read_from_resolve),
supports_decal_tile_mode_(supports_decal_tile_mode),
supports_shared_device_buffer_texture_memory_(
supports_shared_device_buffer_texture_memory),
default_color_format_(default_color_format),
default_stencil_format_(default_stencil_format) {}
@ -118,6 +126,7 @@ class StandardCapabilities final : public Capabilities {
bool supports_read_from_onscreen_texture_ = false;
bool supports_read_from_resolve_ = false;
bool supports_decal_tile_mode_ = false;
bool supports_shared_device_buffer_texture_memory_ = false;
PixelFormat default_color_format_ = PixelFormat::kUnknown;
PixelFormat default_stencil_format_ = PixelFormat::kUnknown;
@ -202,6 +211,12 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsDecalTileMode(bool value) {
return *this;
}
CapabilitiesBuilder&
CapabilitiesBuilder::SetSupportsSharedDeviceBufferTextureMemory(bool value) {
supports_shared_device_buffer_texture_memory_ = value;
return *this;
}
std::unique_ptr<Capabilities> CapabilitiesBuilder::Build() {
return std::unique_ptr<StandardCapabilities>(new StandardCapabilities( //
has_threading_restrictions_, //
@ -215,6 +230,7 @@ std::unique_ptr<Capabilities> CapabilitiesBuilder::Build() {
supports_read_from_onscreen_texture_, //
supports_read_from_resolve_, //
supports_decal_tile_mode_, //
supports_shared_device_buffer_texture_memory_, //
*default_color_format_, //
*default_stencil_format_ //
));

View File

@ -37,6 +37,8 @@ class Capabilities {
virtual bool SupportsDecalTileMode() const = 0;
virtual bool SupportsSharedDeviceBufferTextureMemory() const = 0;
virtual PixelFormat GetDefaultColorFormat() const = 0;
virtual PixelFormat GetDefaultStencilFormat() const = 0;
@ -79,6 +81,8 @@ class CapabilitiesBuilder {
CapabilitiesBuilder& SetSupportsDecalTileMode(bool value);
CapabilitiesBuilder& SetSupportsSharedDeviceBufferTextureMemory(bool value);
std::unique_ptr<Capabilities> Build();
private:
@ -93,6 +97,7 @@ class CapabilitiesBuilder {
bool supports_read_from_onscreen_texture_ = false;
bool supports_read_from_resolve_ = false;
bool supports_decal_tile_mode_ = false;
bool supports_shared_device_buffer_texture_memory_ = false;
std::optional<PixelFormat> default_color_format_ = std::nullopt;
std::optional<PixelFormat> default_stencil_format_ = std::nullopt;

View File

@ -29,6 +29,7 @@ CAPABILITY_TEST(SupportsComputeSubgroups, false);
CAPABILITY_TEST(SupportsReadFromOnscreenTexture, false);
CAPABILITY_TEST(SupportsReadFromResolve, false);
CAPABILITY_TEST(SupportsDecalTileMode, false);
CAPABILITY_TEST(SupportsSharedDeviceBufferTextureMemory, false);
} // namespace testing
} // namespace impeller

View File

@ -10,11 +10,13 @@
#include "flutter/fml/trace_event.h"
#include "impeller/base/allocation.h"
#include "impeller/core/allocator.h"
#include "impeller/core/device_buffer.h"
#include "impeller/typographer/backends/skia/typeface_skia.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/SkPixelRef.h"
#include "third_party/skia/include/core/SkRSXform.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/src/core/SkIPoint16.h" // nogncheck
@ -36,6 +38,16 @@ std::unique_ptr<TextRenderContext> TextRenderContext::Create(
// https://github.com/flutter/flutter/issues/114563
constexpr auto kPadding = 2;
std::optional<uint16_t> ComputeMinimumAlignment(
const std::shared_ptr<Allocator>& allocator,
const std::shared_ptr<const Capabilities>& capabilities,
PixelFormat format) {
if (!capabilities->SupportsSharedDeviceBufferTextureMemory()) {
return std::nullopt;
}
return allocator->MinimumBytesPerRow(format);
}
TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
: TextRenderContext(std::move(context)) {}
@ -138,13 +150,21 @@ namespace {
ISize OptimumAtlasSizeForFontGlyphPairs(
const FontGlyphPair::Set& pairs,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<GlyphAtlasContext>& atlas_context) {
static constexpr auto kMinAtlasSize = 8u;
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
std::optional<uint16_t> minimum_alignment) {
// This size needs to be above the minimum required aligment for linear
// textures. This is 256 for older intel macs and decreases on iOS devices.
static constexpr auto kMinAtlasSize = 256u;
static constexpr auto kMaxAtlasSize = 4096u;
// In case a device happens to have a larger minimum alignment, verify that
// 256 is sufficient here.
uint16_t minimum_size = minimum_alignment.value_or(0) > kMinAtlasSize
? minimum_alignment.value()
: kMinAtlasSize;
TRACE_EVENT0("impeller", __FUNCTION__);
ISize current_size(kMinAtlasSize, kMinAtlasSize);
ISize current_size(minimum_size, minimum_size);
size_t total_pairs = pairs.size() + 1;
do {
auto rect_packer = std::shared_ptr<GrRectanizer>(
@ -346,9 +366,12 @@ static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
return true;
}
static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
const ISize& atlas_size) {
static std::pair<std::shared_ptr<SkBitmap>, std::shared_ptr<DeviceBuffer>>
CreateAtlasBitmap(const GlyphAtlas& atlas,
std::shared_ptr<Allocator> allocator,
const ISize& atlas_size) {
TRACE_EVENT0("impeller", __FUNCTION__);
auto font_allocator = FontImpellerAllocator(std::move(allocator));
auto bitmap = std::make_shared<SkBitmap>();
SkImageInfo image_info;
@ -363,17 +386,18 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
break;
}
if (!bitmap->tryAllocPixels(image_info)) {
return nullptr;
bitmap->setInfo(image_info);
if (!bitmap->tryAllocPixels(&font_allocator)) {
return std::make_pair(nullptr, nullptr);
}
auto surface = SkSurface::MakeRasterDirect(bitmap->pixmap());
if (!surface) {
return nullptr;
return std::make_pair(nullptr, nullptr);
}
auto canvas = surface->getCanvas();
if (!canvas) {
return nullptr;
return std::make_pair(nullptr, nullptr);
}
bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
@ -384,13 +408,16 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
return true;
});
return bitmap;
auto device_buffer = font_allocator.GetDeviceBuffer();
if (!device_buffer.has_value()) {
return std::make_pair(nullptr, nullptr);
}
return std::make_pair(bitmap, device_buffer.value());
}
static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
const std::shared_ptr<Texture>& texture) {
TRACE_EVENT0("impeller", __FUNCTION__);
FML_DCHECK(bitmap != nullptr);
auto texture_descriptor = texture->GetTextureDescriptor();
@ -404,14 +431,12 @@ static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
}
static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
const std::shared_ptr<Allocator>& allocator,
std::shared_ptr<SkBitmap> bitmap,
Allocator& allocator,
const std::shared_ptr<DeviceBuffer>& device_buffer,
const std::shared_ptr<SkBitmap>& bitmap,
const ISize& atlas_size,
PixelFormat format) {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!allocator) {
return nullptr;
}
FML_DCHECK(bitmap != nullptr);
const auto& pixmap = bitmap->pixmap();
@ -421,32 +446,27 @@ static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
texture_descriptor.format = format;
texture_descriptor.size = atlas_size;
// If the alignment isn't a multiple of the pixel format, we cannot use
// a linear texture and instead must blit to a new texture.
if (pixmap.rowBytes() * pixmap.height() !=
texture_descriptor.GetByteSizeOfBaseMipLevel()) {
return nullptr;
}
auto texture = allocator->CreateTexture(texture_descriptor);
FML_DCHECK(allocator.MinimumBytesPerRow(format) <= pixmap.rowBytes());
auto texture = device_buffer->AsTexture(allocator, texture_descriptor,
texture_descriptor.GetBytesPerRow());
if (!texture || !texture->IsValid()) {
return nullptr;
}
texture->SetLabel("GlyphAtlas");
auto mapping = std::make_shared<fml::NonOwnedMapping>(
reinterpret_cast<const uint8_t*>(bitmap->getAddr(0, 0)), // data
texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
[bitmap](auto, auto) mutable { bitmap.reset(); } // proc
);
if (!texture->SetContents(mapping)) {
return nullptr;
}
return texture;
}
std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const std::shared_ptr<const Capabilities>& capabilities,
FrameIterator frame_iterator) const {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!IsValid()) {
@ -502,27 +522,42 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
// ---------------------------------------------------------------------------
// Step 5: Draw new font-glyph pairs into the existing bitmap.
// ---------------------------------------------------------------------------
auto bitmap = atlas_context->GetBitmap();
auto [bitmap, device_buffer] = atlas_context->GetBitmap();
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
return nullptr;
}
// ---------------------------------------------------------------------------
// Step 6: Update the existing texture with the updated bitmap.
// This is only necessary on backends that don't support creating
// a texture that shares memory with the underlying device buffer.
// ---------------------------------------------------------------------------
if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
if (!capabilities->SupportsSharedDeviceBufferTextureMemory() &&
!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
return nullptr;
}
return last_atlas;
}
// A new glyph atlas must be created.
PixelFormat format;
switch (type) {
case GlyphAtlas::Type::kSignedDistanceField:
case GlyphAtlas::Type::kAlphaBitmap:
format = PixelFormat::kA8UNormInt;
break;
case GlyphAtlas::Type::kColorBitmap:
format = PixelFormat::kR8G8B8A8UNormInt;
break;
}
// ---------------------------------------------------------------------------
// Step 4: Get the optimum size of the texture atlas.
// ---------------------------------------------------------------------------
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
auto min_alignment = ComputeMinimumAlignment(
GetContext()->GetResourceAllocator(), capabilities, format);
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
font_glyph_pairs, glyph_positions, atlas_context);
font_glyph_pairs, glyph_positions, atlas_context, min_alignment);
atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size);
if (atlas_size.IsEmpty()) {
@ -552,30 +587,24 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
// ---------------------------------------------------------------------------
// Step 7: Draw font-glyph pairs in the correct spot in the atlas.
// ---------------------------------------------------------------------------
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
auto [bitmap, device_buffer] = CreateAtlasBitmap(
*glyph_atlas, GetContext()->GetResourceAllocator(), atlas_size);
if (!bitmap) {
return nullptr;
}
atlas_context->UpdateBitmap(bitmap);
atlas_context->UpdateBitmap(bitmap, device_buffer);
// ---------------------------------------------------------------------------
// Step 8: Upload the atlas as a texture.
// ---------------------------------------------------------------------------
PixelFormat format;
switch (type) {
case GlyphAtlas::Type::kSignedDistanceField:
ConvertBitmapToSignedDistanceField(
reinterpret_cast<uint8_t*>(bitmap->getPixels()), atlas_size.width,
atlas_size.height);
case GlyphAtlas::Type::kAlphaBitmap:
format = PixelFormat::kA8UNormInt;
break;
case GlyphAtlas::Type::kColorBitmap:
format = PixelFormat::kR8G8B8A8UNormInt;
break;
if (type == GlyphAtlas::Type::kSignedDistanceField) {
ConvertBitmapToSignedDistanceField(
reinterpret_cast<uint8_t*>(bitmap->getPixels()), atlas_size.width,
atlas_size.height);
}
auto texture = UploadGlyphTextureAtlas(GetContext()->GetResourceAllocator(),
bitmap, atlas_size, format);
auto texture =
UploadGlyphTextureAtlas(*GetContext()->GetResourceAllocator().get(),
device_buffer, bitmap, atlas_size, format);
if (!texture) {
return nullptr;
}
@ -588,4 +617,43 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
return glyph_atlas;
}
FontImpellerAllocator::FontImpellerAllocator(
std::shared_ptr<impeller::Allocator> allocator)
: allocator_(std::move(allocator)) {}
std::optional<std::shared_ptr<impeller::DeviceBuffer>>
FontImpellerAllocator::GetDeviceBuffer() const {
return buffer_;
}
bool FontImpellerAllocator::allocPixelRef(SkBitmap* bitmap) {
const SkImageInfo& info = bitmap->info();
if (kUnknown_SkColorType == info.colorType() || info.width() < 0 ||
info.height() < 0 || !info.validRowBytes(bitmap->rowBytes())) {
return false;
}
DeviceBufferDescriptor descriptor;
descriptor.storage_mode = StorageMode::kHostVisible;
descriptor.size = ((bitmap->height() - 1) * bitmap->rowBytes()) +
(bitmap->width() * bitmap->bytesPerPixel());
auto device_buffer = allocator_->CreateBuffer(descriptor);
struct ImpellerPixelRef final : public SkPixelRef {
ImpellerPixelRef(int w, int h, void* s, size_t r)
: SkPixelRef(w, h, s, r) {}
~ImpellerPixelRef() override {}
};
auto pixel_ref = sk_sp<SkPixelRef>(
new ImpellerPixelRef(info.width(), info.height(),
device_buffer->OnGetContents(), bitmap->rowBytes()));
bitmap->setPixelRef(std::move(pixel_ref), 0, 0);
buffer_ = std::move(device_buffer);
return true;
}
} // namespace impeller

View File

@ -6,9 +6,37 @@
#include "flutter/fml/macros.h"
#include "impeller/typographer/text_render_context.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace impeller {
class DeviceBuffer;
class Allocator;
/// @brief An implementation of an SkBitmap allocator that deferrs allocation to
/// an Impeller allocator. This allows usage of Skia software rendering
/// to write to a host buffer or linear texture without an extra copy.
///
/// This class is an exact copy of the implementation in
/// image_decode_impeller.cc due to the lack of a reasonable library
/// that could be shared.
class FontImpellerAllocator : public SkBitmap::Allocator {
public:
explicit FontImpellerAllocator(
std::shared_ptr<impeller::Allocator> allocator);
~FontImpellerAllocator() = default;
// |Allocator|
bool allocPixelRef(SkBitmap* bitmap) override;
std::optional<std::shared_ptr<DeviceBuffer>> GetDeviceBuffer() const;
private:
std::shared_ptr<impeller::Allocator> allocator_;
std::optional<std::shared_ptr<DeviceBuffer>> buffer_;
};
class TextRenderContextSkia : public TextRenderContext {
public:
TextRenderContextSkia(std::shared_ptr<Context> context);
@ -19,6 +47,7 @@ class TextRenderContextSkia : public TextRenderContext {
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const std::shared_ptr<const Capabilities>& capabilities,
FrameIterator iterator) const override;
private:

View File

@ -22,8 +22,9 @@ const ISize& GlyphAtlasContext::GetAtlasSize() const {
return atlas_size_;
}
std::shared_ptr<SkBitmap> GlyphAtlasContext::GetBitmap() const {
return bitmap_;
std::pair<std::shared_ptr<SkBitmap>, std::shared_ptr<DeviceBuffer>>
GlyphAtlasContext::GetBitmap() const {
return std::make_pair(bitmap_, device_buffer_);
}
std::shared_ptr<skgpu::Rectanizer> GlyphAtlasContext::GetRectPacker() const {
@ -36,8 +37,11 @@ void GlyphAtlasContext::UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas,
atlas_size_ = size;
}
void GlyphAtlasContext::UpdateBitmap(std::shared_ptr<SkBitmap> bitmap) {
void GlyphAtlasContext::UpdateBitmap(
std::shared_ptr<SkBitmap> bitmap,
std::shared_ptr<DeviceBuffer> device_buffer) {
bitmap_ = std::move(bitmap);
device_buffer_ = std::move(device_buffer);
}
void GlyphAtlasContext::UpdateRectPacker(

View File

@ -10,6 +10,7 @@
#include <unordered_map>
#include "flutter/fml/macros.h"
#include "impeller/core/device_buffer.h"
#include "impeller/core/texture.h"
#include "impeller/geometry/rect.h"
#include "impeller/renderer/pipeline.h"
@ -153,7 +154,8 @@ class GlyphAtlasContext {
//----------------------------------------------------------------------------
/// @brief Retrieve the previous (if any) SkBitmap instance.
std::shared_ptr<SkBitmap> GetBitmap() const;
std::pair<std::shared_ptr<SkBitmap>, std::shared_ptr<DeviceBuffer>>
GetBitmap() const;
//----------------------------------------------------------------------------
/// @brief Retrieve the previous (if any) rect packer.
@ -163,7 +165,8 @@ class GlyphAtlasContext {
/// @brief Update the context with a newly constructed glyph atlas.
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas, ISize size);
void UpdateBitmap(std::shared_ptr<SkBitmap> bitmap);
void UpdateBitmap(std::shared_ptr<SkBitmap> bitmap,
std::shared_ptr<DeviceBuffer> device_buffer);
void UpdateRectPacker(std::shared_ptr<skgpu::Rectanizer> rect_packer);
@ -171,6 +174,7 @@ class GlyphAtlasContext {
std::shared_ptr<GlyphAtlas> atlas_;
ISize atlas_size_;
std::shared_ptr<SkBitmap> bitmap_;
std::shared_ptr<DeviceBuffer> device_buffer_;
std::shared_ptr<skgpu::Rectanizer> rect_packer_;
FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlasContext);

View File

@ -37,6 +37,7 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
}
}
auto capabilities = context->GetCapabilities();
auto text_context = TextRenderContext::Create(std::move(context));
if (!text_context || !text_context->IsValid()) {
return nullptr;
@ -50,8 +51,8 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
i++;
return &result;
};
auto atlas =
text_context->CreateGlyphAtlas(type, std::move(atlas_context), iterator);
auto atlas = text_context->CreateGlyphAtlas(type, std::move(atlas_context),
capabilities, iterator);
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Could not create valid atlas.";
return nullptr;

View File

@ -29,6 +29,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 std::shared_ptr<const Capabilities>& capabilities,
const TextFrame& frame) const {
size_t count = 0;
FrameIterator iterator = [&]() -> const TextFrame* {
@ -38,7 +39,8 @@ std::shared_ptr<GlyphAtlas> TextRenderContext::CreateGlyphAtlas(
}
return nullptr;
};
return CreateGlyphAtlas(type, std::move(atlas_context), iterator);
return CreateGlyphAtlas(type, std::move(atlas_context), capabilities,
iterator);
}
} // namespace impeller

View File

@ -45,11 +45,13 @@ class TextRenderContext {
virtual std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const std::shared_ptr<const Capabilities>& capabilities,
FrameIterator iterator) const = 0;
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const std::shared_ptr<const Capabilities>& capabilities,
const TextFrame& frame) const;
protected:

View File

@ -4,6 +4,7 @@
#include "flutter/testing/testing.h"
#include "impeller/playground/playground_test.h"
#include "impeller/renderer/capabilities.h"
#include "impeller/typographer/backends/skia/text_frame_skia.h"
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
#include "impeller/typographer/lazy_glyph_atlas.h"
@ -41,9 +42,9 @@ TEST_P(TypographerTest, CanCreateGlyphAtlas) {
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("hello", sk_font);
ASSERT_TRUE(blob);
auto atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
@ -116,9 +117,9 @@ TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
ASSERT_TRUE(blob);
auto atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@ -133,18 +134,18 @@ TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
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));
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), 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));
auto next_atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob));
ASSERT_EQ(atlas, next_atlas);
ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
}
@ -173,8 +174,9 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
}
return nullptr;
};
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
atlas_context, iterator);
auto atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), iterator);
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@ -182,18 +184,16 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
atlas->GetTexture()->GetSize().height);
}
// TODO(jonahwilliams): Re-enable
// https://github.com/flutter/flutter/issues/122839
TEST_P(TypographerTest, DISABLED_GlyphAtlasTextureIsRecycledIfUnchanged) {
TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
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 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@ -205,9 +205,9 @@ TEST_P(TypographerTest, DISABLED_GlyphAtlasTextureIsRecycledIfUnchanged) {
// Now create a new glyph atlas with a nearly identical blob.
auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
auto next_atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob2));
auto next_atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob2));
ASSERT_EQ(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();
@ -224,9 +224,9 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@ -239,9 +239,9 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
// but change the type.
auto blob2 = SkTextBlob::MakeFromString("spooky 1", sk_font);
auto next_atlas =
context->CreateGlyphAtlas(GlyphAtlas::Type::kColorBitmap, atlas_context,
TextFrameFromTextBlob(blob2));
auto next_atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kColorBitmap, atlas_context,
CapabilitiesBuilder().Build(), TextFrameFromTextBlob(blob2));
ASSERT_NE(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();
@ -251,6 +251,54 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
ASSERT_NE(old_packer, new_packer);
}
TEST_P(TypographerTest, GlyphAtlasUsesLinearTextureAlphaBitmap) {
if (!GetContext()
->GetCapabilities()
->SupportsSharedDeviceBufferTextureMemory()) {
GTEST_SKIP()
<< "Skipping test that requires "
"SupportsSharedDeviceBufferTextureMemory on non metal platform";
}
auto context = TextRenderContext::Create(GetContext());
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("s", sk_font);
ASSERT_TRUE(blob);
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kAlphaBitmap, atlas_context,
GetContext()->GetCapabilities(), TextFrameFromTextBlob(blob));
auto* first_texture = atlas->GetTexture().get();
ASSERT_TRUE(first_texture->IsValid());
}
TEST_P(TypographerTest, GlyphAtlasUsesLinearTextureColor) {
if (!GetContext()
->GetCapabilities()
->SupportsSharedDeviceBufferTextureMemory()) {
GTEST_SKIP()
<< "Skipping test that requires "
"SupportsSharedDeviceBufferTextureMemory on non metal platform";
}
auto context = TextRenderContext::Create(GetContext());
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString("s", sk_font);
ASSERT_TRUE(blob);
auto atlas = context->CreateGlyphAtlas(
GlyphAtlas::Type::kColorBitmap, atlas_context,
GetContext()->GetCapabilities(), TextFrameFromTextBlob(blob));
auto* first_texture = atlas->GetTexture().get();
ASSERT_TRUE(first_texture->IsValid());
}
TEST_P(TypographerTest, FontGlyphPairTypeChangesHashAndEquals) {
Font font = Font(nullptr, {});
FontGlyphPair pair_1 = {