Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@
../../../flutter/shell/platform/android/external_view_embedder/surface_pool_unittests.cc
../../../flutter/shell/platform/android/flutter_shell_native_unittests.cc
../../../flutter/shell/platform/android/gradle.properties
../../../flutter/shell/platform/android/image_lru_unittests.cc
../../../flutter/shell/platform/android/jni/jni_mock_unittest.cc
../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc
../../../flutter/shell/platform/android/platform_view_android_unittests.cc
Expand Down
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -6356,6 +6356,8 @@ ORIGIN: ../../../flutter/shell/platform/android/image_external_texture.h + ../..
ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/image_lru.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/image_lru.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/BuildConfig.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/android/io/flutter/Log.java + ../../../flutter/LICENSE
Expand Down Expand Up @@ -9217,6 +9219,8 @@ FILE: ../../../flutter/shell/platform/android/image_external_texture_gl.cc
FILE: ../../../flutter/shell/platform/android/image_external_texture_gl.h
FILE: ../../../flutter/shell/platform/android/image_external_texture_vk.cc
FILE: ../../../flutter/shell/platform/android/image_external_texture_vk.h
FILE: ../../../flutter/shell/platform/android/image_lru.cc
FILE: ../../../flutter/shell/platform/android/image_lru.h
FILE: ../../../flutter/shell/platform/android/io/flutter/BuildConfig.java
FILE: ../../../flutter/shell/platform/android/io/flutter/FlutterInjector.java
FILE: ../../../flutter/shell/platform/android/io/flutter/Log.java
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ executable("flutter_shell_native_unittests") {
"android_shell_holder_unittests.cc",
"apk_asset_provider_unittests.cc",
"flutter_shell_native_unittests.cc",
"image_lru_unittests.cc",
"platform_view_android_unittests.cc",
]
public_configs = [ "//flutter:config" ]
Expand Down Expand Up @@ -111,6 +112,8 @@ source_set("flutter_shell_native_src") {
"image_external_texture_gl.h",
"image_external_texture_vk.cc",
"image_external_texture_vk.h",
"image_lru.cc",
"image_lru.h",
"library_loader.cc",
"ndk_helpers.cc",
"ndk_helpers.h",
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/android/image_external_texture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ void ImageExternalTexture::Paint(PaintContext& context,
if (state_ == AttachmentState::kDetached) {
return;
}
latest_bounds_ = bounds;
Attach(context);
const bool should_process_frame = !freeze;
if (should_process_frame) {
Expand Down Expand Up @@ -61,6 +62,7 @@ void ImageExternalTexture::OnGrContextCreated() {
void ImageExternalTexture::OnGrContextDestroyed() {
if (state_ == AttachmentState::kAttached) {
dl_image_.reset();
image_lru_.Clear();
Detach();
}
state_ = AttachmentState::kDetached;
Expand Down
5 changes: 4 additions & 1 deletion shell/platform/android/image_external_texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "flutter/common/graphics/texture.h"
#include "flutter/fml/logging.h"
#include "flutter/shell/platform/android/image_lru.h"
#include "flutter/shell/platform/android/jni/platform_view_android_jni.h"
#include "flutter/shell/platform/android/platform_view_android_jni_impl.h"

Expand Down Expand Up @@ -58,11 +59,13 @@ class ImageExternalTexture : public flutter::Texture {

fml::jni::ScopedJavaGlobalRef<jobject> image_texture_entry_;
std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
SkRect latest_bounds_ = SkRect::MakeEmpty();
fml::jni::ScopedJavaGlobalRef<jobject> latest_android_image_;

enum class AttachmentState { kUninitialized, kAttached, kDetached };
AttachmentState state_ = AttachmentState::kUninitialized;

sk_sp<flutter::DlImage> dl_image_;
ImageLRU image_lru_ = ImageLRU();

FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTexture);
};
Expand Down
116 changes: 62 additions & 54 deletions shell/platform/android/image_external_texture_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,13 @@
#include <android/sensor.h>

#include "flutter/common/graphics/texture.h"
#include "flutter/display_list/effects/dl_color_source.h"
#include "flutter/flow/layers/layer.h"
#include "flutter/impeller/core/formats.h"
#include "flutter/impeller/display_list/dl_image_impeller.h"
#include "flutter/impeller/renderer/backend/gles/texture_gles.h"
#include "flutter/impeller/toolkit/egl/image.h"
#include "flutter/impeller/toolkit/gles/texture.h"
#include "flutter/shell/platform/android/ndk_helpers.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
Expand All @@ -36,40 +29,63 @@ ImageExternalTextureGL::ImageExternalTextureGL(

void ImageExternalTextureGL::Attach(PaintContext& context) {
if (state_ == AttachmentState::kUninitialized) {
if (!android_image_.is_null()) {
JavaLocalRef hardware_buffer = HardwareBufferFor(android_image_);
AHardwareBuffer* hardware_buffer_ahw =
AHardwareBufferFor(hardware_buffer);
egl_image_ = CreateEGLImage(hardware_buffer_ahw);
if (!latest_android_image_.is_null() && !latest_bounds_.isEmpty()) {
// After detach the cache of textures will have been cleared. If
// there is an android image we must populate it now so that the
// first frame isn't blank.
JavaLocalRef hardware_buffer = HardwareBufferFor(latest_android_image_);
UpdateImage(hardware_buffer, context);
CloseHardwareBuffer(hardware_buffer);
}
state_ = AttachmentState::kAttached;
}
}

void ImageExternalTextureGL::Detach() {
egl_image_.reset();
void ImageExternalTextureGL::UpdateImage(JavaLocalRef& hardware_buffer,
PaintContext& context) {
AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer);
std::optional<HardwareBufferKey> key =
flutter::NDKHelpers::AHardwareBuffer_getId(latest_hardware_buffer);
auto existing_image = image_lru_.FindImage(key);
if (existing_image != nullptr) {
dl_image_ = existing_image;
return;
}

auto egl_image = CreateEGLImage(latest_hardware_buffer);
if (!egl_image.is_valid()) {
return;
}

dl_image_ = CreateDlImage(context, latest_bounds_, key, std::move(egl_image));
if (key.has_value()) {
gl_entries_.erase(image_lru_.AddImage(dl_image_, key.value()));
}
}

bool ImageExternalTextureGL::MaybeSwapImages() {
void ImageExternalTextureGL::ProcessFrame(PaintContext& context,
const SkRect& bounds) {
JavaLocalRef image = AcquireLatestImage();
if (image.is_null()) {
return false;
return;
}
JavaLocalRef hardware_buffer = HardwareBufferFor(image);
UpdateImage(hardware_buffer, context);
CloseHardwareBuffer(hardware_buffer);

// NOTE: In the following code it is important that old_android_image is
// not closed until after the update of egl_image_ otherwise the image might
// be closed before the old EGLImage referencing it has been deleted. After
// an image is closed the underlying HardwareBuffer may be recycled and used
// for a future frame.
JavaLocalRef old_android_image(android_image_);
android_image_.Reset(image);
JavaLocalRef hardware_buffer = HardwareBufferFor(image);
egl_image_ = CreateEGLImage(AHardwareBufferFor(hardware_buffer));
CloseHardwareBuffer(hardware_buffer);
// IMPORTANT: We only close the old image after egl_image_ stops referencing
// it.
JavaLocalRef old_android_image(latest_android_image_);
latest_android_image_.Reset(image);
CloseImage(old_android_image);
return true;
}

void ImageExternalTextureGL::Detach() {
image_lru_.Clear();
gl_entries_.clear();
}

impeller::UniqueEGLImageKHR ImageExternalTextureGL::CreateEGLImage(
Expand Down Expand Up @@ -110,26 +126,11 @@ void ImageExternalTextureGLSkia::Attach(PaintContext& context) {
// After this call state_ will be AttachmentState::kAttached and egl_image_
// will have been created if we still have an Image associated with us.
ImageExternalTextureGL::Attach(context);
GLuint texture_name;
glGenTextures(1, &texture_name);
texture_.reset(impeller::GLTexture{texture_name});
}
}

void ImageExternalTextureGLSkia::Detach() {
ImageExternalTextureGL::Detach();
texture_.reset();
}

void ImageExternalTextureGLSkia::ProcessFrame(PaintContext& context,
const SkRect& bounds) {
const bool swapped = MaybeSwapImages();
if (!swapped && !egl_image_.is_valid()) {
// Nothing to do.
return;
}
BindImageToTexture(egl_image_, texture_.get().texture_name);
dl_image_ = CreateDlImage(context, bounds);
}

void ImageExternalTextureGLSkia::BindImageToTexture(
Expand All @@ -145,11 +146,23 @@ void ImageExternalTextureGLSkia::BindImageToTexture(

sk_sp<flutter::DlImage> ImageExternalTextureGLSkia::CreateDlImage(
PaintContext& context,
const SkRect& bounds) {
GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES,
texture_.get().texture_name, GL_RGBA8_OES};
const SkRect& bounds,
std::optional<HardwareBufferKey> id,
impeller::UniqueEGLImageKHR&& egl_image) {
GLuint texture_name;
glGenTextures(1, &texture_name);
auto gl_texture = impeller::GLTexture{texture_name};
impeller::UniqueGLTexture unique_texture;
unique_texture.reset(gl_texture);

BindImageToTexture(egl_image, unique_texture.get().texture_name);
GrGLTextureInfo textureInfo = {
GL_TEXTURE_EXTERNAL_OES, unique_texture.get().texture_name, GL_RGBA8_OES};
auto backendTexture =
GrBackendTextures::MakeGL(1, 1, skgpu::Mipmapped::kNo, textureInfo);

gl_entries_[id.value_or(0)] = GlEntry{.egl_image = std::move(egl_image),
.texture = std::move(unique_texture)};
return DlImage::Make(SkImages::BorrowTextureFrom(
context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr));
Expand All @@ -171,19 +184,11 @@ void ImageExternalTextureGLImpeller::Attach(PaintContext& context) {
}
}

void ImageExternalTextureGLImpeller::ProcessFrame(PaintContext& context,
const SkRect& bounds) {
const bool swapped = MaybeSwapImages();
if (!swapped && !egl_image_.is_valid()) {
// Nothing to do.
return;
}
dl_image_ = CreateDlImage(context, bounds);
}

sk_sp<flutter::DlImage> ImageExternalTextureGLImpeller::CreateDlImage(
PaintContext& context,
const SkRect& bounds) {
const SkRect& bounds,
std::optional<HardwareBufferKey> id,
impeller::UniqueEGLImageKHR&& egl_image) {
impeller::TextureDescriptor desc;
desc.type = impeller::TextureType::kTextureExternalOES;
desc.storage_mode = impeller::StorageMode::kDevicePrivate;
Expand All @@ -201,7 +206,10 @@ sk_sp<flutter::DlImage> ImageExternalTextureGLImpeller::CreateDlImage(
}
// Associate the hardware buffer image with the texture.
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
(GLeglImageOES)egl_image_.get().image);
(GLeglImageOES)egl_image.get().image);
gl_entries_[id.value_or(0)] = GlEntry{
.egl_image = std::move(egl_image),
};
return impeller::DlImageImpeller::Make(texture);
}

Expand Down
41 changes: 29 additions & 12 deletions shell/platform/android/image_external_texture_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_GL_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_IMAGE_EXTERNAL_TEXTURE_GL_H_

#include <unordered_map>

#include "flutter/fml/platform/android/scoped_java_ref.h"
#include "flutter/shell/platform/android/image_external_texture.h"

Expand All @@ -30,14 +32,26 @@ class ImageExternalTextureGL : public ImageExternalTexture {
protected:
void Attach(PaintContext& context) override;
void Detach() override;
void ProcessFrame(PaintContext& context, const SkRect& bounds) override;
void UpdateImage(JavaLocalRef& hardware_buffer, PaintContext& context);

virtual sk_sp<flutter::DlImage> CreateDlImage(
PaintContext& context,
const SkRect& bounds,
std::optional<HardwareBufferKey> id,
impeller::UniqueEGLImageKHR&& egl_image) = 0;

// Returns true if a new image was acquired and android_image_ and egl_image_
// were updated.
bool MaybeSwapImages();
impeller::UniqueEGLImageKHR CreateEGLImage(AHardwareBuffer* buffer);

fml::jni::ScopedJavaGlobalRef<jobject> android_image_;
impeller::UniqueEGLImageKHR egl_image_;
struct GlEntry {
impeller::UniqueEGLImageKHR egl_image;
impeller::UniqueGLTexture texture;
};

// Each GL entry is keyed off of the currently active
// hardware buffers and evicted when the hardware buffer
// is removed from the LRU cache.
std::unordered_map<HardwareBufferKey, GlEntry> gl_entries_;

FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTextureGL);
};
Expand All @@ -53,13 +67,14 @@ class ImageExternalTextureGLSkia : public ImageExternalTextureGL {
private:
void Attach(PaintContext& context) override;
void Detach() override;
void ProcessFrame(PaintContext& context, const SkRect& bounds) override;

void BindImageToTexture(const impeller::UniqueEGLImageKHR& image, GLuint tex);
sk_sp<flutter::DlImage> CreateDlImage(PaintContext& context,
const SkRect& bounds);

impeller::UniqueGLTexture texture_;
sk_sp<flutter::DlImage> CreateDlImage(
PaintContext& context,
const SkRect& bounds,
std::optional<HardwareBufferKey> id,
impeller::UniqueEGLImageKHR&& egl_image) override;

FML_DISALLOW_COPY_AND_ASSIGN(ImageExternalTextureGLSkia);
};
Expand All @@ -75,11 +90,13 @@ class ImageExternalTextureGLImpeller : public ImageExternalTextureGL {

private:
void Attach(PaintContext& context) override;
void ProcessFrame(PaintContext& context, const SkRect& bounds) override;
void Detach() override;

sk_sp<flutter::DlImage> CreateDlImage(PaintContext& context,
const SkRect& bounds);
sk_sp<flutter::DlImage> CreateDlImage(
PaintContext& context,
const SkRect& bounds,
std::optional<HardwareBufferKey> id,
impeller::UniqueEGLImageKHR&& egl_image) override;

const std::shared_ptr<impeller::ContextGLES> impeller_context_;

Expand Down
Loading