Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Cache GPU resources using HardwareBuffer's id as key #50028

Merged
merged 18 commits into from
Jan 27, 2024
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 @@ -282,6 +282,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 @@ -6347,6 +6347,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 @@ -9203,6 +9205,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
113 changes: 59 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,61 @@ ImageExternalTextureGL::ImageExternalTextureGL(

void ImageExternalTextureGL::Attach(PaintContext& context) {
if (state_ == AttachmentState::kUninitialized) {
if (!android_image_.is_null()) {
johnmccutchan marked this conversation as resolved.
Show resolved Hide resolved
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);
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));
gl_entries_.erase(image_lru_.AddImage(dl_image_, key));
}

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 +124,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 +144,22 @@ 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,
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] = 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 +181,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,
HardwareBufferKey id,
impeller::UniqueEGLImageKHR&& egl_image) {
impeller::TextureDescriptor desc;
desc.type = impeller::TextureType::kTextureExternalOES;
desc.storage_mode = impeller::StorageMode::kDevicePrivate;
Expand All @@ -201,7 +203,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] = 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,
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 {
johnmccutchan marked this conversation as resolved.
Show resolved Hide resolved
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,
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,
HardwareBufferKey id,
impeller::UniqueEGLImageKHR&& egl_image) override;

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

Expand Down
24 changes: 19 additions & 5 deletions shell/platform/android/image_external_texture_vk.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

#include "flutter/shell/platform/android/image_external_texture_vk.h"
#include <cstdint>

#include "flutter/impeller/core/formats.h"
#include "flutter/impeller/core/texture_descriptor.h"
Expand Down Expand Up @@ -37,14 +38,26 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context,
if (image.is_null()) {
return;
}
JavaLocalRef old_android_image(android_image_);
android_image_.Reset(image);
JavaLocalRef hardware_buffer = HardwareBufferFor(android_image_);
JavaLocalRef old_android_image(latest_android_image_);
latest_android_image_.Reset(image);
JavaLocalRef hardware_buffer = HardwareBufferFor(latest_android_image_);
AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer);

AHardwareBuffer_Desc hb_desc = {};
flutter::NDKHelpers::AHardwareBuffer_describe(latest_hardware_buffer,
&hb_desc);
HardwareBufferKey key =
flutter::NDKHelpers::AHardwareBuffer_getId(latest_hardware_buffer);
auto existing_image = image_lru_.FindImage(key);
if (existing_image != nullptr) {
dl_image_ = existing_image;

CloseHardwareBuffer(hardware_buffer);
// IMPORTANT: We have just received a new frame to display so close the
// previous Java Image so that it is recycled and used for a future frame.
CloseImage(old_android_image);
return;
}

impeller::TextureDescriptor desc;
desc.storage_mode = impeller::StorageMode::kDevicePrivate;
Expand Down Expand Up @@ -88,9 +101,10 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context,
}

dl_image_ = impeller::DlImageImpeller::Make(texture);
image_lru_.AddImage(dl_image_, key);
CloseHardwareBuffer(hardware_buffer);
johnmccutchan marked this conversation as resolved.
Show resolved Hide resolved
// IMPORTANT: We only close the old image after texture stops referencing
// it.
// IMPORTANT: We have just received a new frame to display so close the
// previous Java Image so that it is recycled and used for a future frame.
CloseImage(old_android_image);
}

Expand Down
Loading