blob: 8cb74dccae6751a2016631e346bc115e1b9c16eb [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/android/hardware_buffer_external_texture_gl.h"
#include <android/hardware_buffer_jni.h>
#include <android/sensor.h>
#include "impeller/toolkit/egl/image.h"
#include "impeller/toolkit/gles/texture.h"
#include "shell/platform/android/ndk_helpers.h"
#include "flutter/display_list/effects/dl_color_source.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"
namespace flutter {
HardwareBufferExternalTextureGL::HardwareBufferExternalTextureGL(
const std::shared_ptr<AndroidContextGLSkia>& context,
int64_t id,
const fml::jni::ScopedJavaGlobalRef<jobject>& image_texture_entry,
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade)
: Texture(id),
context_(context),
image_texture_entry_(image_texture_entry),
jni_facade_(jni_facade) {}
HardwareBufferExternalTextureGL::~HardwareBufferExternalTextureGL() {}
// Implementing flutter::Texture.
void HardwareBufferExternalTextureGL::Paint(PaintContext& context,
const SkRect& bounds,
bool freeze,
const DlImageSampling sampling) {
if (state_ == AttachmentState::kDetached) {
return;
}
if (state_ == AttachmentState::kUninitialized) {
GLuint texture_name;
glGenTextures(1, &texture_name);
texture_.reset(impeller::GLTexture{texture_name});
state_ = AttachmentState::kAttached;
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_.get().texture_name);
if (!freeze && new_frame_ready_) {
new_frame_ready_ = false;
Update();
}
GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES,
texture_.get().texture_name, GL_RGBA8_OES};
auto backendTexture =
GrBackendTextures::MakeGL(1, 1, skgpu::Mipmapped::kNo, textureInfo);
sk_sp<SkImage> image = SkImages::BorrowTextureFrom(
context.gr_context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
if (image) {
DlAutoCanvasRestore autoRestore(context.canvas, true);
context.canvas->Translate(bounds.x(), bounds.y());
context.canvas->Scale(bounds.width(), bounds.height());
auto dl_image = DlImage::Make(image);
context.canvas->DrawImage(dl_image, {0, 0}, sampling, context.paint);
} else {
FML_LOG(ERROR) << "Skia could not borrow texture";
}
}
// Implementing flutter::Texture.
void HardwareBufferExternalTextureGL::MarkNewFrameAvailable() {
new_frame_ready_ = true;
}
// Implementing flutter::Texture.
void HardwareBufferExternalTextureGL::OnTextureUnregistered() {}
// Implementing flutter::ContextListener.
void HardwareBufferExternalTextureGL::OnGrContextCreated() {
state_ = AttachmentState::kUninitialized;
}
AHardwareBuffer* HardwareBufferExternalTextureGL::GetLatestHardwareBuffer() {
JNIEnv* env = fml::jni::AttachCurrentThread();
FML_CHECK(env != nullptr);
// ImageTextureEntry.acquireLatestImage.
JavaLocalRef image_java = jni_facade_->ImageTextureEntryAcquireLatestImage(
JavaLocalRef(image_texture_entry_));
if (image_java.obj() == nullptr) {
return nullptr;
}
// Image.getHardwareBuffer.
JavaLocalRef hardware_buffer_java =
jni_facade_->ImageGetHardwareBuffer(image_java);
if (hardware_buffer_java.obj() == nullptr) {
jni_facade_->ImageClose(image_java);
return nullptr;
}
// Convert into NDK HardwareBuffer.
AHardwareBuffer* latest_hardware_buffer =
NDKHelpers::AHardwareBuffer_fromHardwareBuffer(
env, hardware_buffer_java.obj());
if (latest_hardware_buffer == nullptr) {
return nullptr;
}
// Keep hardware buffer alive.
NDKHelpers::AHardwareBuffer_acquire(latest_hardware_buffer);
// Now that we have referenced the native hardware buffer, close the Java
// Image and HardwareBuffer objects.
jni_facade_->HardwareBufferClose(hardware_buffer_java);
jni_facade_->ImageClose(image_java);
return latest_hardware_buffer;
}
void HardwareBufferExternalTextureGL::Update() {
EGLDisplay display = eglGetCurrentDisplay();
FML_CHECK(display != EGL_NO_DISPLAY);
image_.reset();
AHardwareBuffer* latest_hardware_buffer = GetLatestHardwareBuffer();
if (latest_hardware_buffer == nullptr) {
FML_LOG(WARNING) << "GetLatestHardwareBuffer returned null.";
return;
}
EGLClientBuffer client_buffer =
NDKHelpers::eglGetNativeClientBufferANDROID(latest_hardware_buffer);
if (client_buffer == nullptr) {
FML_LOG(WARNING) << "eglGetNativeClientBufferAndroid returned null.";
return;
}
FML_CHECK(client_buffer != nullptr);
image_.reset(impeller::EGLImageKHRWithDisplay{
eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
client_buffer, 0),
display});
FML_CHECK(image_.get().image != EGL_NO_IMAGE_KHR);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES,
(GLeglImageOES)image_.get().image);
// Drop our temporary reference to the hardware buffer as the call to
// eglCreateImageKHR now has the reference.
NDKHelpers::AHardwareBuffer_release(latest_hardware_buffer);
}
// Implementing flutter::ContextListener.
void HardwareBufferExternalTextureGL::OnGrContextDestroyed() {
if (state_ == AttachmentState::kAttached) {
image_.reset();
texture_.reset();
}
state_ = AttachmentState::kDetached;
}
} // namespace flutter