Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions engine/src/flutter/shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,6 @@ typedef struct {
} FlutterTransformation;

typedef void (*VoidCallback)(void* /* user data */);
typedef bool (*BoolCallback)(void* /* user data */);

typedef enum {
/// Specifies an OpenGL texture target type. Textures are specified using
Expand Down Expand Up @@ -513,13 +512,6 @@ typedef struct {
uint32_t name;
/// The texture format (example GL_RGBA8).
uint32_t format;
/// The pixel data buffer.
const uint8_t* buffer;
/// The size of pixel buffer.
size_t buffer_size;
/// Callback invoked that the gpu surface texture start binding.
BoolCallback bind_callback;

/// User data to be returned on the invocation of the destruction callback.
void* user_data;
/// Callback invoked (on an engine managed thread) that asks the embedder to
Expand Down Expand Up @@ -613,6 +605,7 @@ typedef struct {
uint32_t format;
} FlutterOpenGLSurface;

typedef bool (*BoolCallback)(void* /* user data */);
typedef FlutterTransformation (*TransformationCallback)(void* /* user data */);
typedef uint32_t (*UIntCallback)(void* /* user data */);
typedef bool (*SoftwareSurfacePresentCallback)(void* /* user data */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,84 @@

namespace flutter {

std::optional<TextureLRU::Data> TextureLRU::FindTexture(
std::optional<GLuint> key) {
if (!key.has_value()) {
return std::nullopt;
}
auto key_value = key.value();
for (size_t i = 0u; i < kTextureMaxSize; i++) {
if (textures_[i].key == key_value) {
auto result = textures_[i].value;
UpdateTexture(Data{.key = key_value,
.value = result,
Comment thread
JSUYA marked this conversation as resolved.
Outdated
.width = textures_[i].width,
.height = textures_[i].height});
return std::make_optional(textures_[i]);
}
}
return std::nullopt;
}

void TextureLRU::UpdateTexture(Data data) {
if (textures_[0].key == data.key) {
textures_[0] = data;
return;
}
size_t i = 1u;
for (; i < kTextureMaxSize; i++) {
if (textures_[i].key == data.key) {
break;
}
}
for (auto j = i; j > 0; j--) {
textures_[j] = textures_[j - 1];
Comment thread
JSUYA marked this conversation as resolved.
}
textures_[0] = data;
}

GLuint TextureLRU::AddTexture(Data data) {
GLuint lru_key = textures_[kTextureMaxSize - 1].key;
bool updated_image = false;
for (size_t i = 0u; i < kTextureMaxSize; i++) {
if (textures_[i].key == lru_key) {
updated_image = true;
textures_[i] = data;
break;
}
}
if (!updated_image) {
textures_[0] = data;
}
UpdateTexture(data);
Comment thread
JSUYA marked this conversation as resolved.
return lru_key;
}

void TextureLRU::Clear() {
for (size_t i = 0u; i < kTextureMaxSize; i++) {
textures_[i] = Data{.key = 0u, .value = nullptr};
}
}

void TextureLRU::RemoveTexture(GLuint key) {
size_t i = 0u;
for (; i < kTextureMaxSize; i++) {
if (textures_[i].key == key) {
break;
}
}

if (i == kTextureMaxSize) {
return;
}

for (; i < kTextureMaxSize - 1; i++) {
textures_[i] = textures_[i + 1];
}

textures_[kTextureMaxSize - 1] = Data{.key = 0u, .value = nullptr};
}

EmbedderExternalTextureGL::EmbedderExternalTextureGL(
int64_t texture_identifier,
const ExternalTextureCallback& callback)
Expand Down Expand Up @@ -129,46 +207,27 @@ sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureSkia(
return DlImage::Make(std::move(image));
}

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpeller(
int64_t texture_id,
std::shared_ptr<impeller::TextureGLES>
EmbedderExternalTextureGL::CreateTextureGLES(
impeller::AiksContext* aiks_context,
const SkISize& size) {
std::unique_ptr<FlutterOpenGLTexture> texture =
external_texture_callback_(texture_id, size.width(), size.height());

if (!texture) {
return nullptr;
}

if (texture->bind_callback != nullptr) {
return ResolveTextureImpellerSurface(aiks_context, std::move(texture));
} else {
return ResolveTextureImpellerPixelbuffer(aiks_context, std::move(texture));
}
}

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpellerPixelbuffer(
impeller::AiksContext* aiks_context,
std::unique_ptr<FlutterOpenGLTexture> texture) {
FlutterOpenGLTexture* texture) {
impeller::TextureDescriptor desc;
desc.size = impeller::ISize(texture->width, texture->height);
desc.type = impeller::TextureType::kTexture2D;
desc.storage_mode = impeller::StorageMode::kDevicePrivate;
desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
if (texture->target == GL_TEXTURE_EXTERNAL_OES) {
desc.type = impeller::TextureType::kTextureExternalOES;
} else {
desc.type = impeller::TextureType::kTexture2D;
}
impeller::ContextGLES& context =
impeller::ContextGLES::Cast(*aiks_context->GetContext());
std::shared_ptr<impeller::TextureGLES> image =
std::make_shared<impeller::TextureGLES>(context.GetReactor(), desc);

image->MarkContentsInitialized();
if (!image->SetContents(texture->buffer, texture->buffer_size)) {
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
return nullptr;
}
impeller::HandleGLES handle = context.GetReactor()->CreateHandle(
impeller::HandleType::kTexture, texture->name);

if (!image) {
auto gles_texture =
impeller::TextureGLES::WrapTexture(context.GetReactor(), desc, handle);
if (!gles_texture) {
// In case Skia rejects the image, call the release proc so that
// embedders can perform collection of intermediates.
if (texture->destruction_callback) {
Expand All @@ -178,58 +237,74 @@ sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpellerPixelbuffer(
return nullptr;
}

if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
gles_texture->SetCoordinateSystem(
impeller::TextureCoordinateSystem::kUploadFromHost);

return impeller::DlImageImpeller::Make(image);
if (texture->destruction_callback &&
!context.GetReactor()->RegisterCleanupCallback(
handle,
[callback = texture->destruction_callback,
user_data = texture->user_data]() { callback(user_data); })) {
FML_LOG(ERROR) << "Could not register destruction callback";
return nullptr;
}
return gles_texture;
}

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpellerSurface(
sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpeller(
int64_t texture_id,
impeller::AiksContext* aiks_context,
std::unique_ptr<FlutterOpenGLTexture> texture) {
impeller::TextureDescriptor desc;
desc.size = impeller::ISize(texture->width, texture->height);
desc.storage_mode = impeller::StorageMode::kDevicePrivate;
desc.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
desc.type = impeller::TextureType::kTextureExternalOES;
impeller::ContextGLES& context =
impeller::ContextGLES::Cast(*aiks_context->GetContext());
std::shared_ptr<impeller::TextureGLES> image =
std::make_shared<impeller::TextureGLES>(context.GetReactor(), desc);
image->MarkContentsInitialized();
image->SetCoordinateSystem(
impeller::TextureCoordinateSystem::kUploadFromHost);
if (!image->Bind()) {
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
FML_LOG(ERROR) << "Could not bind texture";
return nullptr;
}
const SkISize& size) {
std::unique_ptr<FlutterOpenGLTexture> texture =
external_texture_callback_(texture_id, size.width(), size.height());

if (!image) {
// In case Skia rejects the image, call the release proc so that
// embedders can perform collection of intermediates.
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
FML_LOG(ERROR) << "Could not create external texture";
if (!texture) {
return nullptr;
}

if (!texture->bind_callback(texture->user_data)) {
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
return nullptr;
}
std::optional<TextureLRU::Data> texture_data =
texture_lru_.FindTexture(texture->name);

if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
bool size_change = false;

if (texture_data.has_value() &&
(texture_data.value().width != texture->width ||
texture_data.value().height != texture->height)) {
size_change = true;
}

return impeller::DlImageImpeller::Make(image);
if (texture_data.has_value() && !size_change) {
return impeller::DlImageImpeller::Make(texture_data.value().value);
} else if (texture_data.has_value() && size_change) {
std::shared_ptr<impeller::TextureGLES> old_gles_texture =
texture_data.value().value;
old_gles_texture->Leak();
std::shared_ptr<impeller::TextureGLES> new_gles_texture =
CreateTextureGLES(aiks_context, texture.get());
if (new_gles_texture) {
texture_lru_.UpdateTexture(TextureLRU::Data{.key = texture->name,
.value = new_gles_texture,
.width = texture->width,
.height = texture->height});

return impeller::DlImageImpeller::Make(new_gles_texture);
} else {
texture_lru_.RemoveTexture(texture->name);
return nullptr;
}
} else {
std::shared_ptr<impeller::TextureGLES> new_gles_texture =
CreateTextureGLES(aiks_context, texture.get());
if (new_gles_texture) {
texture_lru_.AddTexture(TextureLRU::Data{.key = texture->name,
.value = new_gles_texture,
.width = texture->width,
.height = texture->height});
return impeller::DlImageImpeller::Make(new_gles_texture);
} else {
return nullptr;
}
}
}

// |flutter::Texture|
Expand All @@ -244,6 +319,8 @@ void EmbedderExternalTextureGL::MarkNewFrameAvailable() {
}

// |flutter::Texture|
void EmbedderExternalTextureGL::OnTextureUnregistered() {}
void EmbedderExternalTextureGL::OnTextureUnregistered() {
texture_lru_.Clear();
}

} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,52 @@
#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_GL_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_GL_H_

#include <list>
#include <memory>
#include <unordered_map>
#include "flutter/common/graphics/texture.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
#include "third_party/skia/include/core/SkSize.h"

namespace flutter {
static constexpr size_t kTextureMaxSize = 6u;

class TextureLRU {
public:
struct Data {
GLuint key = 0u;
std::shared_ptr<impeller::TextureGLES> value;
Comment thread
JSUYA marked this conversation as resolved.
Outdated
size_t width = 0;
size_t height = 0;
};

TextureLRU() = default;

~TextureLRU() = default;

/// @brief Retrieve the Texture associated with the given [key], or nullptr.
std::optional<Data> FindTexture(std::optional<GLuint> key);

/// @brief Add a new texture to the cache with a key, returning the key of the
/// LRU entry that was removed.
///
/// The value may be `0`, in which case nothing was removed.
GLuint AddTexture(Data data);

/// @brief Remove all entires from the image cache.
void Clear();

/// @brief Remove a texture from the cache by key.
void RemoveTexture(GLuint key);

/// @brief Marks [key] as the most recently used.
void UpdateTexture(Data data);

private:
std::array<Data, kTextureMaxSize> textures_;
};

class EmbedderExternalTextureGL : public flutter::Texture {
public:
Expand All @@ -25,7 +65,7 @@ class EmbedderExternalTextureGL : public flutter::Texture {
private:
const ExternalTextureCallback& external_texture_callback_;
sk_sp<DlImage> last_image_;

TextureLRU texture_lru_ = TextureLRU();
sk_sp<DlImage> ResolveTexture(int64_t texture_id,
GrDirectContext* context,
impeller::AiksContext* aiks_context,
Expand All @@ -39,13 +79,9 @@ class EmbedderExternalTextureGL : public flutter::Texture {
impeller::AiksContext* aiks_context,
const SkISize& size);

sk_sp<DlImage> ResolveTextureImpellerPixelbuffer(
impeller::AiksContext* aiks_context,
std::unique_ptr<FlutterOpenGLTexture> texture);

sk_sp<DlImage> ResolveTextureImpellerSurface(
std::shared_ptr<impeller::TextureGLES> CreateTextureGLES(
impeller::AiksContext* aiks_context,
std::unique_ptr<FlutterOpenGLTexture> texture);
FlutterOpenGLTexture* texture);

// |flutter::Texture|
void Paint(PaintContext& context,
Expand Down