Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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 @@ -129,107 +129,136 @@ sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureSkia(
return DlImage::Make(std::move(image));
}

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpeller(
int64_t texture_id,
impeller::AiksContext* aiks_context,
const SkISize& size) {
std::unique_ptr<FlutterOpenGLTexture> texture =
external_texture_callback_(texture_id, size.width(), size.height());
bool EmbedderExternalTextureGL::IsExternalTextureChanged(
FlutterOpenGLTexture* texture) {
if (static_cast<int64_t>(texture->width) != desc_.size.width ||
static_cast<int64_t>(texture->height) != desc_.size.height) {
return true;
}

if (!texture) {
return nullptr;
if (!texture_image_) {
return true;
}

if (texture->bind_callback != nullptr) {
return ResolveTextureImpellerSurface(aiks_context, std::move(texture));
} else {
return ResolveTextureImpellerPixelbuffer(aiks_context, std::move(texture));
auto handle = texture_image_->GetGLHandle();
if (!handle.has_value()) {
return true;
}

if (handle.value() != texture->name) {
return true;
}

return false;
}
Comment on lines +132 to +153
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsExternalTextureChanged function assumes texture_image_ is non-null, but it's called inside ResolveTextureImpeller after checking if texture_image_ is nullptr on line 232. However, this check could fail if texture_image_ is not null but GetGLHandle() returns no value. In such cases, line 138 accesses texture_image_->GetGLHandle() which could be called on a null or invalid texture_image_.

Additionally, this function is called at line 235 where texture_image_ is guaranteed to be non-null due to the else clause, but there's no guard against texture_image_ being in an invalid state.

Copilot uses AI. Check for mistakes.

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpellerPixelbuffer(
std::shared_ptr<impeller::TextureGLES>
EmbedderExternalTextureGL::CreateImpellerTexture(
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);
impeller::HandleGLES handle = context.GetReactor()->CreateHandle(
impeller::HandleType::kTexture, texture->name);

image->MarkContentsInitialized();
if (!image->SetContents(texture->buffer, texture->buffer_size)) {
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
return nullptr;
}
std::shared_ptr<impeller::TextureGLES> texture_image =
impeller::TextureGLES::WrapTexture(context.GetReactor(), desc, handle);

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

texture_image->SetCoordinateSystem(
impeller::TextureCoordinateSystem::kUploadFromHost);

if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
if (!context.GetReactor()->RegisterCleanupCallback(
handle,
[callback = texture->destruction_callback,
user_data = texture->user_data]() { callback(user_data); })) {
FML_LOG(ERROR) << "Could not register destruction callback for texture: "
<< texture->name;
texture_image.reset();
return nullptr;
}
}

return impeller::DlImageImpeller::Make(image);
desc_ = desc;
return texture_image;
}

sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpellerSurface(
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()) {
bool EmbedderExternalTextureGL::ValidateTextureParameters(
FlutterOpenGLTexture* texture,
const SkISize& size) {
if (size.width() <= 0 || size.height() <= 0) {
FML_LOG(ERROR) << "Invalid texture size: " << size.width() << "x"
<< size.height();
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
FML_LOG(ERROR) << "Could not bind texture";
return nullptr;
return false;
}

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

return true;
}

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

if (!texture) {
FML_LOG(ERROR) << "External texture callback returned null for texture_id: "
<< texture_id;
return nullptr;
}

if (!texture->bind_callback(texture->user_data)) {
if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
}
if (!ValidateTextureParameters(texture.get(), size)) {
return nullptr;
}

if (texture->destruction_callback) {
texture->destruction_callback(texture->user_data);
if (!texture_image_) {
texture_image_ = CreateImpellerTexture(aiks_context, texture.get());
} else {
if (IsExternalTextureChanged(texture.get())) {
texture_image_.reset();
texture_image_ = CreateImpellerTexture(aiks_context, texture.get());
}
}

if (texture_image_ == nullptr) {
FML_LOG(ERROR) << "Failed to create Impeller texture for texture_id: "
<< texture_id;
return nullptr;
}

return impeller::DlImageImpeller::Make(image);
return impeller::DlImageImpeller::Make(texture_image_);
}
Comment on lines +229 to 262
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lifecycle management issue with the destruction callbacks. Every call to ResolveTextureImpeller invokes external_texture_callback_ which returns a new FlutterOpenGLTexture object (line 230-231). If the texture hasn't changed and texture_image_ is reused (lines 245-250), the destruction_callback from the newly returned texture is never invoked - it goes out of scope when the unique_ptr is destroyed at the end of the function. This means the embedder's cleanup code won't be called for that texture instance. The destruction callback should be invoked for each FlutterOpenGLTexture returned by the callback, regardless of whether texture_image_ is reused or recreated.

Copilot uses AI. Check for mistakes.

// |flutter::Texture|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#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 {
Expand All @@ -25,7 +26,8 @@ class EmbedderExternalTextureGL : public flutter::Texture {
private:
const ExternalTextureCallback& external_texture_callback_;
sk_sp<DlImage> last_image_;

std::shared_ptr<impeller::TextureGLES> texture_image_ = nullptr;
impeller::TextureDescriptor desc_;
sk_sp<DlImage> ResolveTexture(int64_t texture_id,
GrDirectContext* context,
impeller::AiksContext* aiks_context,
Expand All @@ -39,13 +41,14 @@ 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);
bool ValidateTextureParameters(FlutterOpenGLTexture* texture,
const SkISize& size);

bool IsExternalTextureChanged(FlutterOpenGLTexture* texture);

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

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