diff --git a/engine/src/flutter/shell/platform/embedder/embedder.h b/engine/src/flutter/shell/platform/embedder/embedder.h index 6f5b50ec17fbd..9249f082d30af 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.h +++ b/engine/src/flutter/shell/platform/embedder/embedder.h @@ -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 @@ -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 @@ -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 */, diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index e8d2ad8bd12a3..1a9383a6922ff 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -27,6 +27,84 @@ namespace flutter { +std::optional TextureLRU::FindTexture( + std::optional 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, + .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]; + } + 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); + 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) @@ -129,46 +207,27 @@ sk_sp EmbedderExternalTextureGL::ResolveTextureSkia( return DlImage::Make(std::move(image)); } -sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( - int64_t texture_id, +std::shared_ptr +EmbedderExternalTextureGL::CreateTextureGLES( impeller::AiksContext* aiks_context, - const SkISize& size) { - std::unique_ptr 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 EmbedderExternalTextureGL::ResolveTextureImpellerPixelbuffer( - impeller::AiksContext* aiks_context, - std::unique_ptr 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 image = - std::make_shared(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) { @@ -178,58 +237,74 @@ sk_sp 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 EmbedderExternalTextureGL::ResolveTextureImpellerSurface( +sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( + int64_t texture_id, impeller::AiksContext* aiks_context, - std::unique_ptr 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 image = - std::make_shared(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 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 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 old_gles_texture = + texture_data.value().value; + old_gles_texture->Leak(); + std::shared_ptr 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 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| @@ -244,6 +319,8 @@ void EmbedderExternalTextureGL::MarkNewFrameAvailable() { } // |flutter::Texture| -void EmbedderExternalTextureGL::OnTextureUnregistered() {} +void EmbedderExternalTextureGL::OnTextureUnregistered() { + texture_lru_.Clear(); +} } // namespace flutter diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h index 6ecb6843208d1..1826cdd720e0c 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h @@ -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 +#include +#include #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 value; + 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 FindTexture(std::optional 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 textures_; +}; class EmbedderExternalTextureGL : public flutter::Texture { public: @@ -25,7 +65,7 @@ class EmbedderExternalTextureGL : public flutter::Texture { private: const ExternalTextureCallback& external_texture_callback_; sk_sp last_image_; - + TextureLRU texture_lru_ = TextureLRU(); sk_sp ResolveTexture(int64_t texture_id, GrDirectContext* context, impeller::AiksContext* aiks_context, @@ -39,13 +79,9 @@ class EmbedderExternalTextureGL : public flutter::Texture { impeller::AiksContext* aiks_context, const SkISize& size); - sk_sp ResolveTextureImpellerPixelbuffer( - impeller::AiksContext* aiks_context, - std::unique_ptr texture); - - sk_sp ResolveTextureImpellerSurface( + std::shared_ptr CreateTextureGLES( impeller::AiksContext* aiks_context, - std::unique_ptr texture); + FlutterOpenGLTexture* texture); // |flutter::Texture| void Paint(PaintContext& context,