mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Implement OpenGL to Vulkan texture trampolining. (flutter/engine#53966)
This decouples the Impeller on-by-default effort from the release schedule and [plugin migrations](https://github.com/flutter/flutter/issues/151018). The plugin migration documented in [go/impeller-plugin-migration][plugin-migration] is still recommended and facilitates zero-copy texture transfers between OpenGL and Vulkan. To recap, the plugin migration is to move away from the OpenGL-only SurfaceTexture APIs in the plugin interface. This patch facilitates rendering OpenGL textures in a Vulkan renderer using texture trampolining using a single device-device transfer on all devices that support Impeller using the Vulkan renderer. The performance of this approach is more than acceptable but at the cost of an additional texture allocation and will serve as a fallback to the for any remaining unmigrated plugins (all first-party plugins will already be migrated when the Impeller is on by default and we are following up on the migration of the major third-party plugins as well). This is a straight improvement to the current state of things were unmigrated plugins will render an empty quad.
This commit is contained in:
parent
8f1ab25913
commit
f4e4700369
@ -214,6 +214,7 @@
|
||||
../../../flutter/impeller/tessellator/tessellator_unittests.cc
|
||||
../../../flutter/impeller/toolkit/android/README.md
|
||||
../../../flutter/impeller/toolkit/android/toolkit_android_unittests.cc
|
||||
../../../flutter/impeller/toolkit/glvk/README.md
|
||||
../../../flutter/impeller/tools/malioc_cores.py
|
||||
../../../flutter/impeller/tools/malioc_diff.py
|
||||
../../../flutter/impeller/tools/metal_library.py
|
||||
|
||||
@ -42612,6 +42612,10 @@ ORIGIN: ../../../flutter/impeller/toolkit/egl/surface.cc + ../../../flutter/LICE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/egl/surface.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/gles/gles.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/gles/texture.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/glvk/proc_table.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/glvk/proc_table.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/glvk/trampoline.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/toolkit/glvk/trampoline.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc + ../../../flutter/LICENSE
|
||||
@ -43502,6 +43506,8 @@ ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture
|
||||
ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_impeller.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_skia.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_skia.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/vsync_waiter_android.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/android/vsync_waiter_android.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/shell/platform/common/accessibility_bridge.cc + ../../../flutter/LICENSE
|
||||
@ -45491,6 +45497,10 @@ FILE: ../../../flutter/impeller/toolkit/egl/surface.h
|
||||
FILE: ../../../flutter/impeller/toolkit/gles/gles.h
|
||||
FILE: ../../../flutter/impeller/toolkit/gles/texture.cc
|
||||
FILE: ../../../flutter/impeller/toolkit/gles/texture.h
|
||||
FILE: ../../../flutter/impeller/toolkit/glvk/proc_table.cc
|
||||
FILE: ../../../flutter/impeller/toolkit/glvk/proc_table.h
|
||||
FILE: ../../../flutter/impeller/toolkit/glvk/trampoline.cc
|
||||
FILE: ../../../flutter/impeller/toolkit/glvk/trampoline.h
|
||||
FILE: ../../../flutter/impeller/tools/malioc.json
|
||||
FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc
|
||||
FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h
|
||||
@ -46395,6 +46405,8 @@ FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_g
|
||||
FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_impeller.h
|
||||
FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_skia.cc
|
||||
FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_gl_skia.h
|
||||
FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.cc
|
||||
FILE: ../../../flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h
|
||||
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.cc
|
||||
FILE: ../../../flutter/shell/platform/android/vsync_waiter_android.h
|
||||
FILE: ../../../flutter/shell/platform/common/accessibility_bridge.cc
|
||||
|
||||
@ -99,5 +99,9 @@ void Context::DispatchLifecyleEvent(LifecycleEvent event) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Context::IsCurrent() const {
|
||||
return ::eglGetCurrentContext() == context_;
|
||||
}
|
||||
|
||||
} // namespace egl
|
||||
} // namespace impeller
|
||||
|
||||
@ -99,6 +99,12 @@ class Context {
|
||||
///
|
||||
bool RemoveLifecycleListener(UniqueID id);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @return True if the context is current and attached to any surface,
|
||||
/// False otherwise.
|
||||
///
|
||||
bool IsCurrent() const;
|
||||
|
||||
private:
|
||||
friend class Display;
|
||||
|
||||
|
||||
@ -206,5 +206,9 @@ std::unique_ptr<Surface> Display::CreatePixelBufferSurface(const Config& config,
|
||||
return std::unique_ptr<Surface>(new Surface(display_, surface));
|
||||
}
|
||||
|
||||
const EGLDisplay& Display::GetHandle() const {
|
||||
return display_;
|
||||
}
|
||||
|
||||
} // namespace egl
|
||||
} // namespace impeller
|
||||
|
||||
@ -96,6 +96,8 @@ class Display {
|
||||
virtual std::unique_ptr<Surface>
|
||||
CreatePixelBufferSurface(const Config& config, size_t width, size_t height);
|
||||
|
||||
const EGLDisplay& GetHandle() const;
|
||||
|
||||
private:
|
||||
EGLDisplay display_ = EGL_NO_DISPLAY;
|
||||
|
||||
|
||||
23
engine/src/flutter/impeller/toolkit/glvk/BUILD.gn
Normal file
23
engine/src/flutter/impeller/toolkit/glvk/BUILD.gn
Normal file
@ -0,0 +1,23 @@
|
||||
# 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.
|
||||
|
||||
import("../../tools/impeller.gni")
|
||||
|
||||
impeller_component("glvk") {
|
||||
sources = [
|
||||
"proc_table.cc",
|
||||
"proc_table.h",
|
||||
"trampoline.cc",
|
||||
"trampoline.h",
|
||||
]
|
||||
|
||||
public_deps = [
|
||||
"../../renderer",
|
||||
"../../renderer/backend/gles",
|
||||
"../../renderer/backend/vulkan",
|
||||
"../../toolkit/egl",
|
||||
"../../toolkit/gles",
|
||||
"//flutter/fml",
|
||||
]
|
||||
}
|
||||
3
engine/src/flutter/impeller/toolkit/glvk/README.md
Normal file
3
engine/src/flutter/impeller/toolkit/glvk/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# GLVK Toolkit
|
||||
|
||||
A toolkit for inter-operating between OpenGL and Vulkan.
|
||||
45
engine/src/flutter/impeller/toolkit/glvk/proc_table.cc
Normal file
45
engine/src/flutter/impeller/toolkit/glvk/proc_table.cc
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 "impeller/toolkit/glvk/proc_table.h"
|
||||
|
||||
#include "impeller/base/validation.h"
|
||||
|
||||
namespace impeller::glvk {
|
||||
|
||||
ProcTable::ProcTable(const Resolver& resolver) {
|
||||
if (!resolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto error_fn = reinterpret_cast<PFNGLGETERRORPROC>(resolver("glGetError"));
|
||||
if (!error_fn) {
|
||||
VALIDATION_LOG << "Could not resolve " << "glGetError";
|
||||
return;
|
||||
}
|
||||
|
||||
#define GLVK_PROC(proc_ivar) \
|
||||
if (auto fn_ptr = resolver(proc_ivar.name)) { \
|
||||
proc_ivar.function = \
|
||||
reinterpret_cast<decltype(proc_ivar.function)>(fn_ptr); \
|
||||
proc_ivar.error_fn = error_fn; \
|
||||
} else { \
|
||||
VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \
|
||||
return; \
|
||||
}
|
||||
|
||||
FOR_EACH_GLVK_PROC(GLVK_PROC);
|
||||
|
||||
#undef GLVK_PROC
|
||||
|
||||
is_valid_ = true;
|
||||
}
|
||||
|
||||
ProcTable::~ProcTable() = default;
|
||||
|
||||
bool ProcTable::IsValid() const {
|
||||
return is_valid_;
|
||||
}
|
||||
|
||||
} // namespace impeller::glvk
|
||||
102
engine/src/flutter/impeller/toolkit/glvk/proc_table.h
Normal file
102
engine/src/flutter/impeller/toolkit/glvk/proc_table.h
Normal file
@ -0,0 +1,102 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_IMPELLER_TOOLKIT_GLVK_PROC_TABLE_H_
|
||||
#define FLUTTER_IMPELLER_TOOLKIT_GLVK_PROC_TABLE_H_
|
||||
|
||||
#include "impeller/renderer/backend/gles/proc_table_gles.h"
|
||||
#include "impeller/toolkit/gles/gles.h"
|
||||
|
||||
namespace impeller::glvk {
|
||||
|
||||
#define FOR_EACH_GLVK_PROC(PROC) \
|
||||
PROC(ActiveTexture) \
|
||||
PROC(AttachShader) \
|
||||
PROC(BindAttribLocation) \
|
||||
PROC(BindBuffer) \
|
||||
PROC(BindFramebuffer) \
|
||||
PROC(BindTexture) \
|
||||
PROC(BufferData) \
|
||||
PROC(CheckFramebufferStatus) \
|
||||
PROC(Clear) \
|
||||
PROC(ClearColor) \
|
||||
PROC(ColorMask) \
|
||||
PROC(CompileShader) \
|
||||
PROC(CreateProgram) \
|
||||
PROC(CreateShader) \
|
||||
PROC(DeleteBuffers) \
|
||||
PROC(DeleteFramebuffers) \
|
||||
PROC(DeleteProgram) \
|
||||
PROC(DeleteShader) \
|
||||
PROC(DeleteTextures) \
|
||||
PROC(Disable) \
|
||||
PROC(DrawArrays) \
|
||||
PROC(EGLImageTargetTexture2DOES) \
|
||||
PROC(Enable) \
|
||||
PROC(EnableVertexAttribArray) \
|
||||
PROC(Flush) \
|
||||
PROC(FramebufferTexture2D) \
|
||||
PROC(GenBuffers) \
|
||||
PROC(GenFramebuffers) \
|
||||
PROC(GenTextures) \
|
||||
PROC(GetProgramiv) \
|
||||
PROC(GetShaderiv) \
|
||||
PROC(GetUniformLocation) \
|
||||
PROC(LinkProgram) \
|
||||
PROC(ShaderSource) \
|
||||
PROC(TexParameteri) \
|
||||
PROC(Uniform1i) \
|
||||
PROC(UniformMatrix4fv) \
|
||||
PROC(UseProgram) \
|
||||
PROC(VertexAttribPointer) \
|
||||
PROC(Viewport)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief A proc. table consisting of methods that are useful when
|
||||
/// interoperating between OpenGL and Vulkan. This is different from
|
||||
/// the OpenGL proc. table since it may contain more interop
|
||||
/// extension related methods.
|
||||
///
|
||||
class ProcTable {
|
||||
public:
|
||||
using Resolver = std::function<void*(const char* function_name)>;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Create a proc table using a resolver to resolve OpenGL
|
||||
/// methods.
|
||||
///
|
||||
/// @param[in] resolver The resolver
|
||||
///
|
||||
explicit ProcTable(const Resolver& resolver);
|
||||
|
||||
~ProcTable();
|
||||
|
||||
ProcTable(const ProcTable&) = delete;
|
||||
|
||||
ProcTable& operator=(const ProcTable&) = delete;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Determines if a proc. table is suitable for interop purposes.
|
||||
/// The absence of optional extension methods that have fallbacks
|
||||
/// don't result in an invalid proc. table. But an invalid proc
|
||||
/// table must always be discarded as there can be no error
|
||||
/// recovery.
|
||||
///
|
||||
/// @return True if valid, False otherwise.
|
||||
///
|
||||
bool IsValid() const;
|
||||
|
||||
#define GLVK_PROC(name) GLProc<decltype(gl##name)> name = {"gl" #name, nullptr};
|
||||
|
||||
FOR_EACH_GLVK_PROC(GLVK_PROC);
|
||||
|
||||
#undef GLVK_PROC
|
||||
|
||||
private:
|
||||
bool is_valid_ = false;
|
||||
};
|
||||
|
||||
} // namespace impeller::glvk
|
||||
|
||||
#endif // FLUTTER_IMPELLER_TOOLKIT_GLVK_PROC_TABLE_H_
|
||||
332
engine/src/flutter/impeller/toolkit/glvk/trampoline.cc
Normal file
332
engine/src/flutter/impeller/toolkit/glvk/trampoline.cc
Normal file
@ -0,0 +1,332 @@
|
||||
// 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 "impeller/toolkit/glvk/trampoline.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "flutter/fml/closure.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "impeller/base/timing.h"
|
||||
#include "impeller/base/validation.h"
|
||||
#include "impeller/geometry/point.h"
|
||||
#include "impeller/renderer/backend/gles/formats_gles.h"
|
||||
#include "impeller/toolkit/android/proc_table.h"
|
||||
#include "impeller/toolkit/egl/image.h"
|
||||
|
||||
namespace impeller::glvk {
|
||||
|
||||
static GLuint kAttributeIndexPosition = 0u;
|
||||
static GLuint kAttributeIndexTexCoord = 1u;
|
||||
|
||||
static constexpr const char* kVertShader = R"IMPELLER_SHADER(#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
||||
attribute vec2 aPosition;
|
||||
attribute vec2 aTexCoord;
|
||||
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(aPosition, 0.0, 1.0);
|
||||
vTexCoord = aTexCoord;
|
||||
|
||||
}
|
||||
)IMPELLER_SHADER";
|
||||
|
||||
static constexpr const char* kFragShader = R"IMPELLER_SHADER(#version 100
|
||||
|
||||
#extension GL_OES_EGL_image_external : require
|
||||
|
||||
precision mediump float;
|
||||
|
||||
uniform samplerExternalOES uTexture;
|
||||
uniform mat4 uUVTransformation;
|
||||
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
vec2 texture_coords = (uUVTransformation * vec4(vTexCoord, 0, 1)).xy;
|
||||
gl_FragColor = texture2D(uTexture, texture_coords);
|
||||
}
|
||||
|
||||
)IMPELLER_SHADER";
|
||||
|
||||
Trampoline::Trampoline() {
|
||||
auto egl_display = std::make_unique<egl::Display>();
|
||||
if (!egl_display->IsValid()) {
|
||||
VALIDATION_LOG
|
||||
<< "Could not create EGL display for external texture interop.";
|
||||
return;
|
||||
}
|
||||
|
||||
egl::ConfigDescriptor egl_config_desc;
|
||||
egl_config_desc.api = egl::API::kOpenGLES2;
|
||||
egl_config_desc.samples = egl::Samples::kOne;
|
||||
egl_config_desc.color_format = egl::ColorFormat::kRGBA8888;
|
||||
egl_config_desc.stencil_bits = egl::StencilBits::kZero;
|
||||
egl_config_desc.depth_bits = egl::DepthBits::kZero;
|
||||
egl_config_desc.surface_type = egl::SurfaceType::kPBuffer;
|
||||
auto egl_config = egl_display->ChooseConfig(egl_config_desc);
|
||||
if (!egl_config) {
|
||||
VALIDATION_LOG
|
||||
<< "Could not choose EGL config for external texture interop.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto egl_surface = egl_display->CreatePixelBufferSurface(*egl_config, 1u, 1u);
|
||||
auto egl_context = egl_display->CreateContext(*egl_config, nullptr);
|
||||
|
||||
if (!egl_surface || !egl_context) {
|
||||
VALIDATION_LOG << "Could not create EGL surface and/or context for "
|
||||
"external texture interop.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Make the context current so the proc addresses can be resolved.
|
||||
if (!egl_context->MakeCurrent(*egl_surface)) {
|
||||
VALIDATION_LOG << "Could not make the context current.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto gl = std::make_unique<ProcTable>(egl::CreateProcAddressResolver());
|
||||
|
||||
if (!gl->IsValid()) {
|
||||
egl_context->ClearCurrent();
|
||||
VALIDATION_LOG << "Could not setup trampoline proc table.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate program object.
|
||||
auto vert_shader = gl->CreateShader(GL_VERTEX_SHADER);
|
||||
auto frag_shader = gl->CreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
GLint vert_shader_size = strlen(kVertShader);
|
||||
GLint frag_shader_size = strlen(kFragShader);
|
||||
|
||||
gl->ShaderSource(vert_shader, 1u, &kVertShader, &vert_shader_size);
|
||||
gl->ShaderSource(frag_shader, 1u, &kFragShader, &frag_shader_size);
|
||||
|
||||
gl->CompileShader(vert_shader);
|
||||
gl->CompileShader(frag_shader);
|
||||
|
||||
GLint vert_status = GL_FALSE;
|
||||
GLint frag_status = GL_FALSE;
|
||||
|
||||
gl->GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status);
|
||||
gl->GetShaderiv(frag_shader, GL_COMPILE_STATUS, &frag_status);
|
||||
|
||||
FML_CHECK(vert_status == GL_TRUE);
|
||||
FML_CHECK(frag_status == GL_TRUE);
|
||||
|
||||
program_ = gl->CreateProgram();
|
||||
gl->AttachShader(program_, vert_shader);
|
||||
gl->AttachShader(program_, frag_shader);
|
||||
|
||||
gl->BindAttribLocation(program_, kAttributeIndexPosition, "aPosition");
|
||||
gl->BindAttribLocation(program_, kAttributeIndexTexCoord, "aTexCoord");
|
||||
|
||||
gl->LinkProgram(program_);
|
||||
|
||||
GLint link_status = GL_FALSE;
|
||||
gl->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
|
||||
FML_CHECK(link_status == GL_TRUE);
|
||||
|
||||
texture_uniform_location_ = gl->GetUniformLocation(program_, "uTexture");
|
||||
uv_transformation_location_ =
|
||||
gl->GetUniformLocation(program_, "uUVTransformation");
|
||||
|
||||
gl->DeleteShader(vert_shader);
|
||||
gl->DeleteShader(frag_shader);
|
||||
|
||||
egl_context->ClearCurrent();
|
||||
|
||||
gl_ = std::move(gl);
|
||||
egl_display_ = std::move(egl_display);
|
||||
egl_context_ = std::move(egl_context);
|
||||
egl_surface_ = std::move(egl_surface);
|
||||
is_valid_ = true;
|
||||
}
|
||||
|
||||
Trampoline::~Trampoline() {
|
||||
if (!is_valid_) {
|
||||
return;
|
||||
}
|
||||
auto context = MakeCurrentContext();
|
||||
gl_->DeleteProgram(program_);
|
||||
}
|
||||
|
||||
bool Trampoline::IsValid() const {
|
||||
return is_valid_;
|
||||
}
|
||||
|
||||
static UniqueEGLImageKHR CreateEGLImageFromAHBTexture(
|
||||
const EGLDisplay& display,
|
||||
const AHBTextureSourceVK& to_texture) {
|
||||
if (!android::GetProcTable().eglGetNativeClientBufferANDROID.IsAvailable()) {
|
||||
VALIDATION_LOG << "Could not get native client buffer.";
|
||||
return {};
|
||||
}
|
||||
|
||||
EGLClientBuffer client_buffer =
|
||||
android::GetProcTable().eglGetNativeClientBufferANDROID(
|
||||
to_texture.GetBackingStore()->GetHandle());
|
||||
|
||||
if (!client_buffer) {
|
||||
VALIDATION_LOG
|
||||
<< "Could not get client buffer from Android hardware buffer.";
|
||||
return {};
|
||||
}
|
||||
|
||||
auto image = ::eglCreateImageKHR(display, //
|
||||
EGL_NO_CONTEXT, //
|
||||
EGL_NATIVE_BUFFER_ANDROID, //
|
||||
client_buffer, //
|
||||
nullptr //
|
||||
);
|
||||
if (image == NULL) {
|
||||
VALIDATION_LOG << "Could not create EGL Image.";
|
||||
return {};
|
||||
}
|
||||
|
||||
return UniqueEGLImageKHR(EGLImageKHRWithDisplay{image, display});
|
||||
}
|
||||
|
||||
bool Trampoline::BlitTextureOpenGLToVulkan(
|
||||
const GLTextureInfo& src_texture,
|
||||
const AHBTextureSourceVK& dst_texture) const {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
if (!is_valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FML_DCHECK(egl_context_->IsCurrent());
|
||||
|
||||
auto dst_egl_image =
|
||||
CreateEGLImageFromAHBTexture(egl_display_->GetHandle(), dst_texture);
|
||||
if (!dst_egl_image.is_valid()) {
|
||||
VALIDATION_LOG << "Could not create EGL image from AHB texture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& gl = *gl_;
|
||||
|
||||
GLuint dst_gl_texture = GL_NONE;
|
||||
gl.GenTextures(1u, &dst_gl_texture);
|
||||
gl.BindTexture(GL_TEXTURE_2D, dst_gl_texture);
|
||||
gl.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, dst_egl_image.get().image);
|
||||
|
||||
GLuint offscreen_fbo = GL_NONE;
|
||||
gl.GenFramebuffers(1u, &offscreen_fbo);
|
||||
gl.BindFramebuffer(GL_FRAMEBUFFER, offscreen_fbo);
|
||||
gl.FramebufferTexture2D(GL_FRAMEBUFFER, //
|
||||
GL_COLOR_ATTACHMENT0, //
|
||||
GL_TEXTURE_2D, //
|
||||
dst_gl_texture, //
|
||||
0 //
|
||||
);
|
||||
|
||||
FML_CHECK(gl.CheckFramebufferStatus(GL_FRAMEBUFFER) ==
|
||||
GL_FRAMEBUFFER_COMPLETE);
|
||||
|
||||
gl.Disable(GL_BLEND);
|
||||
gl.Disable(GL_SCISSOR_TEST);
|
||||
gl.Disable(GL_DITHER);
|
||||
gl.Disable(GL_CULL_FACE);
|
||||
gl.ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
gl.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
gl.Clear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
const auto& fb_size = dst_texture.GetTextureDescriptor().size;
|
||||
gl.Viewport(0, 0, fb_size.width, fb_size.height);
|
||||
|
||||
gl.UseProgram(program_);
|
||||
|
||||
struct VertexData {
|
||||
Point position;
|
||||
Point tex_coord;
|
||||
};
|
||||
|
||||
// The vertex coordinates assume OpenGL NDC because that's the API we are
|
||||
// using to draw the quad. But the texture will be sampled in Vulkan so the
|
||||
// texture coordinate system assumes Vulkan convention.
|
||||
//
|
||||
// See the following help link for an overview of the different coordinate
|
||||
// systems:
|
||||
// https://github.com/flutter/engine/blob/5810b3fc791f4bb82b9a454014310990eddc1181/impeller/docs/coordinate_system.md
|
||||
static constexpr const VertexData kVertData[] = {
|
||||
{{-1, -1}, {0, 1}}, // bottom left
|
||||
{{-1, +1}, {0, 0}}, // top left
|
||||
{{+1, +1}, {1, 0}}, // top right
|
||||
{{+1, -1}, {1, 1}}, // bottom right
|
||||
};
|
||||
|
||||
// This is tedious but we assume no vertex array objects (VAO) are available
|
||||
// because of ES 2 versioning constraints.
|
||||
GLuint vertex_buffer = GL_NONE;
|
||||
gl.GenBuffers(1u, &vertex_buffer);
|
||||
gl.BindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
|
||||
gl.BufferData(GL_ARRAY_BUFFER, sizeof(kVertData), kVertData, GL_STATIC_DRAW);
|
||||
gl.EnableVertexAttribArray(kAttributeIndexPosition);
|
||||
gl.EnableVertexAttribArray(kAttributeIndexTexCoord);
|
||||
gl.VertexAttribPointer(kAttributeIndexPosition, 2, GL_FLOAT, GL_FALSE,
|
||||
sizeof(VertexData),
|
||||
(void*)offsetof(VertexData, position));
|
||||
gl.VertexAttribPointer(kAttributeIndexTexCoord, 2, GL_FLOAT, GL_FALSE,
|
||||
sizeof(VertexData),
|
||||
(void*)offsetof(VertexData, tex_coord));
|
||||
|
||||
gl.ActiveTexture(GL_TEXTURE0);
|
||||
gl.BindTexture(src_texture.target, src_texture.texture);
|
||||
gl.TexParameteri(src_texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
gl.TexParameteri(src_texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
gl.TexParameteri(src_texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
gl.TexParameteri(src_texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
gl.Uniform1i(texture_uniform_location_, 0u);
|
||||
|
||||
auto gl_uv_transformation = src_texture.uv_transformation;
|
||||
|
||||
gl.UniformMatrix4fv(uv_transformation_location_, 1u, GL_FALSE,
|
||||
reinterpret_cast<GLfloat*>(&gl_uv_transformation));
|
||||
|
||||
gl.DrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
|
||||
gl.UseProgram(GL_NONE);
|
||||
|
||||
gl.Flush();
|
||||
|
||||
gl.DeleteFramebuffers(1u, &offscreen_fbo);
|
||||
gl.DeleteTextures(1u, &dst_gl_texture);
|
||||
gl.DeleteBuffers(1u, &vertex_buffer);
|
||||
|
||||
// Theoretically, this does nothing because the surface is a 1x1 pbuffer
|
||||
// surface. But frame capture tools use this to denote a frame boundary in
|
||||
// OpenGL. So add this as a debugging aid anyway.
|
||||
eglSwapBuffers(egl_display_->GetHandle(), egl_surface_->GetHandle());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoTrampolineContext Trampoline::MakeCurrentContext() const {
|
||||
FML_DCHECK(is_valid_);
|
||||
return AutoTrampolineContext{*this};
|
||||
}
|
||||
|
||||
AutoTrampolineContext::AutoTrampolineContext(const Trampoline& trampoline)
|
||||
: context_(trampoline.egl_context_.get()),
|
||||
surface_(trampoline.egl_surface_.get()) {
|
||||
if (!context_->IsCurrent() && !context_->MakeCurrent(*surface_)) {
|
||||
VALIDATION_LOG << "Could not make context current.";
|
||||
}
|
||||
};
|
||||
|
||||
AutoTrampolineContext::~AutoTrampolineContext() {
|
||||
if (!context_->ClearCurrent()) {
|
||||
VALIDATION_LOG << "Could not clear current context.";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impeller::glvk
|
||||
153
engine/src/flutter/impeller/toolkit/glvk/trampoline.h
Normal file
153
engine/src/flutter/impeller/toolkit/glvk/trampoline.h
Normal file
@ -0,0 +1,153 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_IMPELLER_TOOLKIT_GLVK_TRAMPOLINE_H_
|
||||
#define FLUTTER_IMPELLER_TOOLKIT_GLVK_TRAMPOLINE_H_
|
||||
|
||||
#include "impeller/base/thread_safety.h"
|
||||
#include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h"
|
||||
#include "impeller/toolkit/egl/context.h"
|
||||
#include "impeller/toolkit/egl/display.h"
|
||||
#include "impeller/toolkit/egl/surface.h"
|
||||
#include "impeller/toolkit/glvk/proc_table.h"
|
||||
|
||||
namespace impeller::glvk {
|
||||
|
||||
class AutoTrampolineContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief An object used to interoperate between OpenGL and Vulkan.
|
||||
///
|
||||
/// While these are not super expensive to create, they do manage an
|
||||
/// internal EGL context as well as some OpenGL state. For this
|
||||
/// reason, it is recommended that callers cache these for the
|
||||
/// duration of the lifecycle of main rendering context.
|
||||
///
|
||||
class Trampoline {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Constructs a new trampoline. It is recommended that these
|
||||
/// objects be cached and reused for all conversion operations.
|
||||
///
|
||||
/// EGL contexts on already bound to the callers thread may become
|
||||
/// unbound after a call to this method.
|
||||
///
|
||||
Trampoline();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Destroys the trampoline. There are no threading restrictions.
|
||||
/// EGL contexts on already bound to the callers thread may become
|
||||
/// unbound after a call to this method.
|
||||
///
|
||||
~Trampoline();
|
||||
|
||||
Trampoline(const Trampoline&) = delete;
|
||||
|
||||
Trampoline& operator=(const Trampoline&) = delete;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Determines if this is a valid trampoline. There is no error
|
||||
/// recovery mechanism if a trampoline cannot be constructed and
|
||||
/// an invalid trampoline must be immediately discarded.
|
||||
///
|
||||
/// @return True if valid, False otherwise.
|
||||
///
|
||||
bool IsValid() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Describes an OpenGL texture along with information on how to
|
||||
/// sample from it.
|
||||
///
|
||||
struct GLTextureInfo {
|
||||
//--------------------------------------------------------------------------
|
||||
/// The OpenGL texture handle.
|
||||
///
|
||||
GLuint texture = 0;
|
||||
//--------------------------------------------------------------------------
|
||||
/// The OpenGL texture target enum. For instance, GL_TEXTURE_2D or
|
||||
/// GL_TEXTURE_EXTERNAL_OES.
|
||||
///
|
||||
GLenum target = 0;
|
||||
//--------------------------------------------------------------------------
|
||||
/// A transformation applied to the texture coordinates in the form of (u,
|
||||
/// v, 0, 1) when sampling from the texture.
|
||||
///
|
||||
Matrix uv_transformation;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Perform a blit operation from the source OpenGL texture to a
|
||||
/// target Vulkan texture.
|
||||
///
|
||||
/// It is the callers responsibility to ensure that the EGL
|
||||
/// context associated with the trampoline is already current
|
||||
/// before making this call.
|
||||
///
|
||||
/// It is also the responsibility of the caller to ensure that the
|
||||
/// destination texture is the color-attachment-optimal layout.
|
||||
/// Failure to ensure this will lead to validation error.
|
||||
///
|
||||
/// @see `MakeCurrentContext`
|
||||
///
|
||||
/// @param[in] src_texture The source OpenGL texture.
|
||||
/// @param[in] dst_texture The destination Vulkan texture.
|
||||
///
|
||||
/// @return True if the blit was successful, False otherwise.
|
||||
///
|
||||
bool BlitTextureOpenGLToVulkan(const GLTextureInfo& src_texture,
|
||||
const AHBTextureSourceVK& dst_texture) const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Make the EGL context associated with this trampoline current
|
||||
/// on the calling thread.
|
||||
///
|
||||
/// @return The automatic trampoline context. The collection of this
|
||||
/// context clears the threads EGL binding.
|
||||
///
|
||||
[[nodiscard]] AutoTrampolineContext MakeCurrentContext() const;
|
||||
|
||||
private:
|
||||
friend class AutoTrampolineContext;
|
||||
|
||||
std::unique_ptr<egl::Display> egl_display_;
|
||||
std::unique_ptr<egl::Context> egl_context_;
|
||||
std::unique_ptr<egl::Surface> egl_surface_;
|
||||
std::unique_ptr<ProcTable> gl_;
|
||||
GLuint program_ = GL_NONE;
|
||||
GLint texture_uniform_location_ = 0;
|
||||
GLint uv_transformation_location_ = 0;
|
||||
bool is_valid_ = false;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief An RAII object that makes the trampolines EGL context current
|
||||
/// when constructed and clears the EGL binding on destruction.
|
||||
///
|
||||
class AutoTrampolineContext final {
|
||||
public:
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Constructs a new instance and makes the trampolines EGL
|
||||
/// context current on the calling thread.
|
||||
///
|
||||
/// @param[in] trampoline The trampoline.
|
||||
///
|
||||
explicit AutoTrampolineContext(const Trampoline& trampoline);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Destroys the object and clears the previous EGL binding.
|
||||
///
|
||||
~AutoTrampolineContext();
|
||||
|
||||
AutoTrampolineContext(const AutoTrampolineContext&) = delete;
|
||||
|
||||
AutoTrampolineContext& operator=(const AutoTrampolineContext&) = delete;
|
||||
|
||||
private:
|
||||
const egl::Context* context_ = nullptr;
|
||||
const egl::Surface* surface_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace impeller::glvk
|
||||
|
||||
#endif // FLUTTER_IMPELLER_TOOLKIT_GLVK_TRAMPOLINE_H_
|
||||
@ -133,6 +133,8 @@ source_set("flutter_shell_native_src") {
|
||||
"surface_texture_external_texture_gl_impeller.h",
|
||||
"surface_texture_external_texture_gl_skia.cc",
|
||||
"surface_texture_external_texture_gl_skia.h",
|
||||
"surface_texture_external_texture_vk_impeller.cc",
|
||||
"surface_texture_external_texture_vk_impeller.h",
|
||||
"vsync_waiter_android.cc",
|
||||
"vsync_waiter_android.h",
|
||||
]
|
||||
@ -152,6 +154,7 @@ source_set("flutter_shell_native_src") {
|
||||
"//flutter/impeller/toolkit/android",
|
||||
"//flutter/impeller/toolkit/egl",
|
||||
"//flutter/impeller/toolkit/gles",
|
||||
"//flutter/impeller/toolkit/glvk",
|
||||
"//flutter/lib/ui",
|
||||
"//flutter/runtime",
|
||||
"//flutter/runtime:libdart",
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "flutter/shell/platform/android/image_external_texture_gl_skia.h"
|
||||
#include "flutter/shell/platform/android/surface_texture_external_texture_gl_impeller.h"
|
||||
#include "flutter/shell/platform/android/surface_texture_external_texture_gl_skia.h"
|
||||
#include "flutter/shell/platform/android/surface_texture_external_texture_vk_impeller.h"
|
||||
#include "fml/logging.h"
|
||||
#if IMPELLER_ENABLE_VULKAN // b/258506856 for why this is behind an if
|
||||
#include "flutter/shell/platform/android/android_surface_vk_impeller.h"
|
||||
@ -279,23 +280,36 @@ void PlatformViewAndroid::RegisterExternalTexture(
|
||||
// Impeller GLES.
|
||||
RegisterTexture(std::make_shared<SurfaceTextureExternalTextureGLImpeller>(
|
||||
std::static_pointer_cast<impeller::ContextGLES>(
|
||||
android_context_->GetImpellerContext()),
|
||||
texture_id, surface_texture, jni_facade_));
|
||||
android_context_->GetImpellerContext()), //
|
||||
texture_id, //
|
||||
surface_texture, //
|
||||
jni_facade_ //
|
||||
));
|
||||
break;
|
||||
case AndroidRenderingAPI::kSkiaOpenGLES:
|
||||
// Legacy GL.
|
||||
RegisterTexture(std::make_shared<SurfaceTextureExternalTextureGLSkia>(
|
||||
texture_id, surface_texture, jni_facade_));
|
||||
texture_id, //
|
||||
surface_texture, //
|
||||
jni_facade_ //
|
||||
));
|
||||
break;
|
||||
case AndroidRenderingAPI::kSoftware:
|
||||
FML_LOG(INFO) << "Software rendering does not support external textures.";
|
||||
break;
|
||||
case AndroidRenderingAPI::kImpellerVulkan:
|
||||
FML_LOG(ERROR) << "Impeller requires migrating plugins that create and "
|
||||
"register surface textures to the new surface producer "
|
||||
"API. See "
|
||||
"https://docs.flutter.dev/release/breaking-changes/"
|
||||
"android-surface-plugins";
|
||||
FML_LOG(IMPORTANT)
|
||||
<< "Flutter recommends migrating plugins that create and "
|
||||
"register surface textures to the new surface producer "
|
||||
"API. See https://docs.flutter.dev/release/breaking-changes/"
|
||||
"android-surface-plugins";
|
||||
RegisterTexture(std::make_shared<SurfaceTextureExternalTextureVKImpeller>(
|
||||
std::static_pointer_cast<impeller::ContextVK>(
|
||||
android_context_->GetImpellerContext()), //
|
||||
texture_id, //
|
||||
surface_texture, //
|
||||
jni_facade_ //
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -54,32 +54,57 @@ void SurfaceTextureExternalTexture::Paint(PaintContext& context,
|
||||
}
|
||||
FML_CHECK(state_ == AttachmentState::kAttached);
|
||||
|
||||
if (dl_image_) {
|
||||
DlAutoCanvasRestore autoRestore(context.canvas, true);
|
||||
|
||||
// The incoming texture is vertically flipped, so we flip it
|
||||
// back. OpenGL's coordinate system has Positive Y equivalent to up, while
|
||||
// Skia's coordinate system has Negative Y equvalent to up.
|
||||
context.canvas->Translate(bounds.x(), bounds.y() + bounds.height());
|
||||
context.canvas->Scale(bounds.width(), -bounds.height());
|
||||
|
||||
if (!transform_.isIdentity()) {
|
||||
DlImageColorSource source(dl_image_, DlTileMode::kClamp,
|
||||
DlTileMode::kClamp, sampling, &transform_);
|
||||
|
||||
DlPaint paintWithShader;
|
||||
if (context.paint) {
|
||||
paintWithShader = *context.paint;
|
||||
}
|
||||
paintWithShader.setColorSource(&source);
|
||||
context.canvas->DrawRect(SkRect::MakeWH(1, 1), paintWithShader);
|
||||
} else {
|
||||
context.canvas->DrawImage(dl_image_, {0, 0}, sampling, context.paint);
|
||||
}
|
||||
} else {
|
||||
if (!dl_image_) {
|
||||
FML_LOG(WARNING)
|
||||
<< "No DlImage available for SurfaceTextureExternalTexture to paint.";
|
||||
return;
|
||||
}
|
||||
|
||||
DrawFrame(context, bounds, sampling);
|
||||
}
|
||||
|
||||
void SurfaceTextureExternalTexture::DrawFrame(
|
||||
PaintContext& context,
|
||||
const SkRect& bounds,
|
||||
const DlImageSampling sampling) const {
|
||||
auto transform = GetCurrentUVTransformation();
|
||||
|
||||
// Android's SurfaceTexture transform matrix works on texture coordinate
|
||||
// lookups in the range 0.0-1.0, while Skia's Shader transform matrix works on
|
||||
// the image itself, as if it were inscribed inside a clip rect.
|
||||
// An Android transform that scales lookup by 0.5 (displaying 50% of the
|
||||
// texture) is the same as a Skia transform by 2.0 (scaling 50% of the image
|
||||
// outside of the virtual "clip rect"), so we invert the incoming matrix.
|
||||
|
||||
SkMatrix inverted;
|
||||
if (!transform.invert(&inverted)) {
|
||||
FML_LOG(FATAL)
|
||||
<< "Invalid (not invertable) SurfaceTexture transformation matrix";
|
||||
}
|
||||
transform = inverted;
|
||||
|
||||
if (transform.isIdentity()) {
|
||||
context.canvas->DrawImage(dl_image_, {0, 0}, sampling, context.paint);
|
||||
return;
|
||||
}
|
||||
|
||||
DlAutoCanvasRestore autoRestore(context.canvas, true);
|
||||
|
||||
// The incoming texture is vertically flipped, so we flip it
|
||||
// back. OpenGL's coordinate system has Positive Y equivalent to up, while
|
||||
// Skia's coordinate system has Negative Y equvalent to up.
|
||||
context.canvas->Translate(bounds.x(), bounds.y() + bounds.height());
|
||||
context.canvas->Scale(bounds.width(), -bounds.height());
|
||||
|
||||
DlImageColorSource source(dl_image_, DlTileMode::kClamp, DlTileMode::kClamp,
|
||||
sampling, &transform);
|
||||
|
||||
DlPaint paintWithShader;
|
||||
if (context.paint) {
|
||||
paintWithShader = *context.paint;
|
||||
}
|
||||
paintWithShader.setColorSource(&source);
|
||||
context.canvas->DrawRect(SkRect::MakeWH(1, 1), paintWithShader);
|
||||
}
|
||||
|
||||
void SurfaceTextureExternalTexture::OnGrContextDestroyed() {
|
||||
@ -111,22 +136,13 @@ bool SurfaceTextureExternalTexture::ShouldUpdate() {
|
||||
void SurfaceTextureExternalTexture::Update() {
|
||||
jni_facade_->SurfaceTextureUpdateTexImage(
|
||||
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_));
|
||||
|
||||
jni_facade_->SurfaceTextureGetTransformMatrix(
|
||||
fml::jni::ScopedJavaLocalRef<jobject>(surface_texture_), transform_);
|
||||
}
|
||||
|
||||
// Android's SurfaceTexture transform matrix works on texture coordinate
|
||||
// lookups in the range 0.0-1.0, while Skia's Shader transform matrix works on
|
||||
// the image itself, as if it were inscribed inside a clip rect.
|
||||
// An Android transform that scales lookup by 0.5 (displaying 50% of the
|
||||
// texture) is the same as a Skia transform by 2.0 (scaling 50% of the image
|
||||
// outside of the virtual "clip rect"), so we invert the incoming matrix.
|
||||
SkMatrix inverted;
|
||||
if (!transform_.invert(&inverted)) {
|
||||
FML_LOG(FATAL)
|
||||
<< "Invalid (not invertable) SurfaceTexture transformation matrix";
|
||||
}
|
||||
transform_ = inverted;
|
||||
const SkMatrix& SurfaceTextureExternalTexture::GetCurrentUVTransformation()
|
||||
const {
|
||||
return transform_;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -54,14 +54,42 @@ class SurfaceTextureExternalTexture : public flutter::Texture {
|
||||
///
|
||||
virtual void ProcessFrame(PaintContext& context, const SkRect& bounds) = 0;
|
||||
|
||||
virtual void DrawFrame(PaintContext& context,
|
||||
const SkRect& bounds,
|
||||
const DlImageSampling sampling) const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Get the transformation that should be applied to the UV
|
||||
/// texture coordinates when sampling from this texture.
|
||||
///
|
||||
/// @return The current uv transformation.
|
||||
///
|
||||
const SkMatrix& GetCurrentUVTransformation() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Provides an opportunity for the subclasses to sever the
|
||||
/// connection between the OpenGL texture resource represented by
|
||||
/// this surface texture and the underlying package handle
|
||||
/// (SkImage, impeller::Texture, etc...).
|
||||
///
|
||||
/// @important It is the responsibility of the subclass to ensure that a
|
||||
/// context is current when this call is made. Subclass can do
|
||||
/// this by overriding this method, making the context current in
|
||||
/// the implementation and calling the base class method.
|
||||
///
|
||||
virtual void Detach();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Attaches the given OpenGL texture handle to the surface
|
||||
/// texture via a bind operation.
|
||||
///
|
||||
/// @important It is the responsibility of the subclass to ensure that a
|
||||
/// context is current when this call is made. Subclass can do
|
||||
/// this by overriding this method, making the context current in
|
||||
/// the implementation and calling the base class method.
|
||||
///
|
||||
/// @param[in] gl_tex_id The gl tex identifier
|
||||
///
|
||||
void Attach(int gl_tex_id);
|
||||
|
||||
bool ShouldUpdate();
|
||||
@ -80,10 +108,11 @@ class SurfaceTextureExternalTexture : public flutter::Texture {
|
||||
std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
|
||||
fml::jni::ScopedJavaGlobalRef<jobject> surface_texture_;
|
||||
AttachmentState state_ = AttachmentState::kUninitialized;
|
||||
SkMatrix transform_;
|
||||
sk_sp<flutter::DlImage> dl_image_;
|
||||
|
||||
private:
|
||||
SkMatrix transform_;
|
||||
|
||||
// |Texture|
|
||||
void Paint(PaintContext& context,
|
||||
const SkRect& bounds,
|
||||
|
||||
@ -0,0 +1,231 @@
|
||||
// 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/surface_texture_external_texture_vk_impeller.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
#include "flutter/fml/trace_event.h"
|
||||
#include "flutter/impeller/display_list/dl_image_impeller.h"
|
||||
#include "flutter/impeller/renderer/backend/vulkan/command_buffer_vk.h"
|
||||
#include "flutter/impeller/renderer/backend/vulkan/command_encoder_vk.h"
|
||||
#include "flutter/impeller/renderer/backend/vulkan/surface_context_vk.h"
|
||||
#include "flutter/impeller/renderer/backend/vulkan/texture_vk.h"
|
||||
#include "flutter/impeller/toolkit/android/hardware_buffer.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
using namespace impeller;
|
||||
|
||||
SurfaceTextureExternalTextureVKImpeller::
|
||||
SurfaceTextureExternalTextureVKImpeller(
|
||||
std::shared_ptr<impeller::ContextVK> context,
|
||||
int64_t id,
|
||||
const fml::jni::ScopedJavaGlobalRef<jobject>& surface_texture,
|
||||
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade)
|
||||
: SurfaceTextureExternalTexture(id, surface_texture, jni_facade),
|
||||
context_(std::move(context)),
|
||||
trampoline_(std::make_shared<glvk::Trampoline>()) {
|
||||
is_valid_ = trampoline_->IsValid();
|
||||
}
|
||||
|
||||
SurfaceTextureExternalTextureVKImpeller::
|
||||
~SurfaceTextureExternalTextureVKImpeller() = default;
|
||||
|
||||
enum class LayoutUpdateMode {
|
||||
kSync,
|
||||
kAsync,
|
||||
};
|
||||
|
||||
static bool SetTextureLayout(const ContextVK& context,
|
||||
const TextureSourceVK* texture,
|
||||
vk::ImageLayout layout,
|
||||
LayoutUpdateMode mode) {
|
||||
TRACE_EVENT0("flutter", __FUNCTION__);
|
||||
if (!texture) {
|
||||
return true;
|
||||
}
|
||||
auto command_buffer = context.CreateCommandBuffer();
|
||||
if (!command_buffer) {
|
||||
VALIDATION_LOG
|
||||
<< "Could not create command buffer for texture layout update.";
|
||||
return false;
|
||||
}
|
||||
command_buffer->SetLabel("GLVKTextureLayoutUpdateCB");
|
||||
const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder();
|
||||
const auto command_buffer_vk = encoder->GetCommandBuffer();
|
||||
|
||||
BarrierVK barrier;
|
||||
barrier.cmd_buffer = command_buffer_vk;
|
||||
barrier.new_layout = layout;
|
||||
barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput |
|
||||
impeller::vk::PipelineStageFlagBits::eFragmentShader;
|
||||
barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite |
|
||||
vk::AccessFlagBits::eShaderRead;
|
||||
barrier.dst_stage = impeller::vk::PipelineStageFlagBits::eFragmentShader;
|
||||
barrier.dst_access = vk::AccessFlagBits::eShaderRead;
|
||||
|
||||
if (!texture->SetLayout(barrier).ok()) {
|
||||
VALIDATION_LOG << "Could not encoder layout transition.";
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder->EndCommandBuffer();
|
||||
|
||||
vk::SubmitInfo submit_info;
|
||||
submit_info.setCommandBuffers(command_buffer_vk);
|
||||
|
||||
// There is no need to track the fence in the encoder since we are going to do
|
||||
// a sync wait for completion.
|
||||
vk::UniqueFence fence;
|
||||
|
||||
if (mode == LayoutUpdateMode::kSync) {
|
||||
auto fence_pair =
|
||||
context.GetDevice().createFenceUnique(vk::FenceCreateFlags{});
|
||||
if (fence_pair.result != impeller::vk::Result::eSuccess) {
|
||||
VALIDATION_LOG << "Could not create fence.";
|
||||
return false;
|
||||
}
|
||||
fence = std::move(fence_pair.value);
|
||||
}
|
||||
|
||||
if (context.GetGraphicsQueue()->Submit(submit_info, fence.get()) !=
|
||||
impeller::vk::Result::eSuccess) {
|
||||
VALIDATION_LOG << "Could not submit layout transition fence.";
|
||||
return false;
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if (fence &&
|
||||
context.GetDevice().waitForFences(
|
||||
fence.get(), //
|
||||
VK_TRUE, //
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count() //
|
||||
) != impeller::vk::Result::eSuccess) {
|
||||
VALIDATION_LOG << "Could not perform sync wait on fence.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void SurfaceTextureExternalTextureVKImpeller::ProcessFrame(
|
||||
PaintContext& context,
|
||||
const SkRect& bounds) {
|
||||
if (!is_valid_ || !context.aiks_context) {
|
||||
VALIDATION_LOG << "Invalid external texture.";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& surface_context =
|
||||
SurfaceContextVK::Cast(*context.aiks_context->GetContext());
|
||||
const auto& context_vk = ContextVK::Cast(*surface_context.GetParent());
|
||||
|
||||
auto dst_texture =
|
||||
GetCachedTextureSource(surface_context.GetParent(), //
|
||||
ISize::MakeWH(bounds.width(), bounds.height()) //
|
||||
);
|
||||
if (!dst_texture || !dst_texture->IsValid()) {
|
||||
VALIDATION_LOG << "Could not fetch trampoline texture target.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_context = trampoline_->MakeCurrentContext();
|
||||
|
||||
GLuint src_gl_texture = {};
|
||||
glGenTextures(1u, &src_gl_texture);
|
||||
Attach(src_gl_texture);
|
||||
Update();
|
||||
|
||||
SetTextureLayout(context_vk, dst_texture.get(),
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
LayoutUpdateMode::kSync);
|
||||
|
||||
SkM44 transformation(GetCurrentUVTransformation());
|
||||
impeller::Matrix uv_transformation;
|
||||
transformation.getColMajor(reinterpret_cast<SkScalar*>(&uv_transformation));
|
||||
|
||||
glvk::Trampoline::GLTextureInfo src_texture;
|
||||
src_texture.texture = src_gl_texture;
|
||||
src_texture.target = GL_TEXTURE_EXTERNAL_OES;
|
||||
src_texture.uv_transformation = uv_transformation;
|
||||
|
||||
if (!trampoline_->BlitTextureOpenGLToVulkan(src_texture, *dst_texture)) {
|
||||
VALIDATION_LOG << "Texture copy failed.";
|
||||
}
|
||||
|
||||
SetTextureLayout(context_vk, dst_texture.get(),
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
LayoutUpdateMode::kAsync);
|
||||
|
||||
glDeleteTextures(1u, &src_gl_texture);
|
||||
|
||||
dl_image_ = DlImageImpeller::Make(
|
||||
std::make_shared<TextureVK>(surface_context.GetParent(), dst_texture));
|
||||
}
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void SurfaceTextureExternalTextureVKImpeller::Detach() {
|
||||
// Detaching from the underlying surface texture requires a context to be
|
||||
// current. On the other hand, the texture source is a pure Vulkan construct
|
||||
// and has no EGL related requirements.
|
||||
auto context = trampoline_->MakeCurrentContext();
|
||||
SurfaceTextureExternalTexture::Detach();
|
||||
cached_texture_source_.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<impeller::AHBTextureSourceVK>
|
||||
SurfaceTextureExternalTextureVKImpeller::GetCachedTextureSource(
|
||||
const std::shared_ptr<Context>& context,
|
||||
const impeller::ISize& size) {
|
||||
if (cached_texture_source_ &&
|
||||
cached_texture_source_->GetTextureDescriptor().size == size) {
|
||||
return cached_texture_source_;
|
||||
}
|
||||
cached_texture_source_ = nullptr;
|
||||
|
||||
android::HardwareBufferDescriptor ahb_descriptor;
|
||||
ahb_descriptor.format = android::HardwareBufferFormat::kR8G8B8A8UNormInt;
|
||||
ahb_descriptor.size = size.Max(ISize{1u, 1u});
|
||||
ahb_descriptor.usage =
|
||||
android::HardwareBufferUsageFlags::kFrameBufferAttachment |
|
||||
android::HardwareBufferUsageFlags::kSampledImage;
|
||||
|
||||
if (!ahb_descriptor.IsAllocatable()) {
|
||||
VALIDATION_LOG << "Invalid hardware buffer texture descriptor.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ahb = std::make_unique<android::HardwareBuffer>(ahb_descriptor);
|
||||
if (!ahb->IsValid()) {
|
||||
VALIDATION_LOG << "Could not allocate hardware buffer.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto texture_source =
|
||||
std::make_shared<AHBTextureSourceVK>(context, std::move(ahb), false);
|
||||
|
||||
if (!texture_source->IsValid()) {
|
||||
VALIDATION_LOG << "Could not create trampoline texture source.";
|
||||
return nullptr;
|
||||
}
|
||||
cached_texture_source_ = std::move(texture_source);
|
||||
return cached_texture_source_;
|
||||
}
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void SurfaceTextureExternalTextureVKImpeller::DrawFrame(
|
||||
PaintContext& context,
|
||||
const SkRect& bounds,
|
||||
const DlImageSampling sampling) const {
|
||||
context.canvas->DrawImage(dl_image_, {0, 0}, sampling, context.paint);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_SURFACE_TEXTURE_EXTERNAL_TEXTURE_VK_IMPELLER_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_ANDROID_SURFACE_TEXTURE_EXTERNAL_TEXTURE_VK_IMPELLER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h"
|
||||
#include "flutter/impeller/renderer/backend/vulkan/context_vk.h"
|
||||
#include "flutter/impeller/toolkit/egl/context.h"
|
||||
#include "flutter/impeller/toolkit/egl/display.h"
|
||||
#include "flutter/impeller/toolkit/egl/surface.h"
|
||||
#include "flutter/impeller/toolkit/glvk/trampoline.h"
|
||||
#include "flutter/shell/platform/android/surface_texture_external_texture.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief An external texture implementation for Vulkan that uses OpenGL
|
||||
/// to Vulkan texture interop to read SurfaceTexture based image
|
||||
/// data and render it into a Vulkan texture.
|
||||
///
|
||||
/// Unlike other zero-copy implementations, the OpenGL to Vulkan
|
||||
/// texture interop has the additional overhead of one device-device
|
||||
/// copy and allocates one offscreen texture. For this reason, one
|
||||
/// of the "Image" based external texture implementations are a
|
||||
/// recommended replacement for this class. For example
|
||||
/// `ImageExternalTextureVKImpeller`.
|
||||
///
|
||||
/// This implementation is used only as a fallback during the
|
||||
/// migration away from the SurfaceTexture based plugin APIs. For
|
||||
/// more information about the plugin API migration, see
|
||||
/// https://docs.flutter.dev/release/breaking-changes/android-surface-plugins.
|
||||
///
|
||||
/// @see `ImageExternalTextureVKImpeller`
|
||||
///
|
||||
class SurfaceTextureExternalTextureVKImpeller final
|
||||
: public SurfaceTextureExternalTexture {
|
||||
public:
|
||||
SurfaceTextureExternalTextureVKImpeller(
|
||||
std::shared_ptr<impeller::ContextVK> context,
|
||||
int64_t id,
|
||||
const fml::jni::ScopedJavaGlobalRef<jobject>& surface_texture,
|
||||
const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade);
|
||||
|
||||
~SurfaceTextureExternalTextureVKImpeller() override;
|
||||
|
||||
SurfaceTextureExternalTextureVKImpeller(
|
||||
const SurfaceTextureExternalTextureVKImpeller&) = delete;
|
||||
|
||||
SurfaceTextureExternalTextureVKImpeller& operator=(
|
||||
const SurfaceTextureExternalTextureVKImpeller&) = delete;
|
||||
|
||||
private:
|
||||
std::shared_ptr<impeller::ContextVK> context_;
|
||||
std::shared_ptr<impeller::glvk::Trampoline> trampoline_;
|
||||
std::shared_ptr<impeller::AHBTextureSourceVK> cached_texture_source_;
|
||||
bool is_valid_ = false;
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void ProcessFrame(PaintContext& context, const SkRect& bounds) override;
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void Detach() override;
|
||||
|
||||
// |SurfaceTextureExternalTexture|
|
||||
void DrawFrame(PaintContext& context,
|
||||
const SkRect& bounds,
|
||||
const DlImageSampling sampling) const override;
|
||||
|
||||
std::shared_ptr<impeller::AHBTextureSourceVK> GetCachedTextureSource(
|
||||
const std::shared_ptr<impeller::Context>& context,
|
||||
const impeller::ISize& size);
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_ANDROID_SURFACE_TEXTURE_EXTERNAL_TEXTURE_VK_IMPELLER_H_
|
||||
@ -46,17 +46,6 @@ extension type const Options._(ArgResults _args) {
|
||||
);
|
||||
}
|
||||
|
||||
// Cannot use forceSurfaceProducerSurfaceTexture with Impeller+Vulkan.
|
||||
if (options.forceSurfaceProducerSurfaceTexture &&
|
||||
options.enableImpeller &&
|
||||
options.impellerBackend != 'opengles') {
|
||||
throw const FormatException(
|
||||
'Cannot use --force-surface-producer-surface-texture with '
|
||||
'--enable-impeller unless --impeller-backend="opengles" is used. See '
|
||||
'https://github.com/flutter/flutter/issues/143539 for details.',
|
||||
);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@ -152,11 +141,7 @@ extension type const Options._(ArgResults _args) {
|
||||
'rendering strategy. This is used to emulate the behavior of older '
|
||||
'devices that do not support ImageReader, or to explicitly test '
|
||||
'SurfaceTexture path for rendering plugins still using the older '
|
||||
'createSurfaceTexture() API.'
|
||||
'\n'
|
||||
'Cannot be used with --enable-impeller unless --impeller-backend='
|
||||
'"opengles" is used. See '
|
||||
'https://github.com/flutter/flutter/issues/143539 for details.',
|
||||
'createSurfaceTexture() API.',
|
||||
negatable: false
|
||||
)
|
||||
..addFlag(
|
||||
@ -322,13 +307,7 @@ extension type const Options._(ArgResults _args) {
|
||||
String? get outputContentsGolden => _args['output-contents-golden'] as String;
|
||||
|
||||
/// Whether to force the use of `SurfaceTexture` for `SurfaceProducer`.
|
||||
///
|
||||
/// Always returns `false` if `--enable-impeller` is `true` and
|
||||
/// `--impeller-backend` is not `opengles`.
|
||||
bool get forceSurfaceProducerSurfaceTexture {
|
||||
if (enableImpeller && impellerBackend != 'opengles') {
|
||||
return false;
|
||||
}
|
||||
return _args['force-surface-producer-surface-texture'] as bool;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user