[Impeller] libImpeller: Allow wrapping external texture handles. (flutter/engine#55664)

Fixes https://github.com/flutter/flutter/issues/156013
This commit is contained in:
Chinmay Garde 2024-10-08 13:33:26 -07:00 committed by GitHub
parent d2e73aef5b
commit 7af6ae84e6
9 changed files with 176 additions and 18 deletions

View File

@ -130,7 +130,7 @@ static bool CollectGLHandle(const ProcTableGLES& gl,
return false;
}
HandleGLES ReactorGLES::CreateHandle(HandleType type) {
HandleGLES ReactorGLES::CreateHandle(HandleType type, GLuint external_handle) {
if (type == HandleType::kUnknown) {
return HandleGLES::DeadHandle();
}
@ -139,9 +139,13 @@ HandleGLES ReactorGLES::CreateHandle(HandleType type) {
return HandleGLES::DeadHandle();
}
WriterLock handles_lock(handles_mutex_);
auto gl_handle = CanReactOnCurrentThread()
? CreateGLHandle(GetProcTable(), type)
: std::nullopt;
std::optional<GLuint> gl_handle;
if (external_handle != GL_NONE) {
gl_handle = external_handle;
} else if (CanReactOnCurrentThread()) {
gl_handle = CreateGLHandle(GetProcTable(), type);
}
handles_[new_handle] = LiveHandle{gl_handle};
return new_handle;
}

View File

@ -163,11 +163,12 @@ class ReactorGLES {
/// This can be called on any thread. Even one that doesn't have
/// an OpenGL context.
///
/// @param[in] type The type of handle to create.
/// @param[in] type The type of handle to create.
/// @param[in] external_handle An already created GL handle if one exists.
///
/// @return The reactor handle.
///
HandleGLES CreateHandle(HandleType type);
HandleGLES CreateHandle(HandleType type, GLuint external_handle = GL_NONE);
//----------------------------------------------------------------------------
/// @brief Collect a reactor handle.

View File

@ -140,28 +140,41 @@ HandleType ToHandleType(TextureGLES::Type type) {
}
TextureGLES::TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc)
: TextureGLES(std::move(reactor), desc, false, std::nullopt) {}
: TextureGLES(std::move(reactor), desc, false, std::nullopt, std::nullopt) {
}
TextureGLES::TextureGLES(ReactorGLES::Ref reactor,
TextureDescriptor desc,
enum IsWrapped wrapped)
: TextureGLES(std::move(reactor), desc, true, std::nullopt) {}
: TextureGLES(std::move(reactor), desc, true, std::nullopt, std::nullopt) {}
TextureGLES::TextureGLES(ReactorGLES::Ref reactor,
TextureDescriptor desc,
HandleGLES external_handle)
: TextureGLES(std::move(reactor),
desc,
true,
std::nullopt,
external_handle) {}
std::shared_ptr<TextureGLES> TextureGLES::WrapFBO(ReactorGLES::Ref reactor,
TextureDescriptor desc,
GLuint fbo) {
return std::shared_ptr<TextureGLES>(
new TextureGLES(std::move(reactor), desc, true, fbo));
new TextureGLES(std::move(reactor), desc, true, fbo, std::nullopt));
}
TextureGLES::TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool is_wrapped,
std::optional<GLuint> fbo)
std::optional<GLuint> fbo,
std::optional<HandleGLES> external_handle)
: Texture(desc),
reactor_(std::move(reactor)),
type_(GetTextureTypeFromDescriptor(GetTextureDescriptor())),
handle_(reactor_->CreateHandle(ToHandleType(type_))),
handle_(external_handle.has_value()
? external_handle.value()
: reactor_->CreateHandle(ToHandleType(type_))),
is_wrapped_(is_wrapped),
wrapped_fbo_(fbo) {
// Ensure the texture descriptor itself is valid.
@ -567,4 +580,12 @@ Scalar TextureGLES::GetYCoordScale() const {
FML_UNREACHABLE();
}
bool TextureGLES::IsWrapped() const {
return is_wrapped_;
}
std::optional<GLuint> TextureGLES::GetFBO() const {
return wrapped_fbo_;
}
} // namespace impeller

View File

@ -34,6 +34,10 @@ class TextureGLES final : public Texture,
TextureDescriptor desc,
IsWrapped wrapped);
TextureGLES(ReactorGLES::Ref reactor,
TextureDescriptor desc,
HandleGLES external_handle);
static std::shared_ptr<TextureGLES> WrapFBO(ReactorGLES::Ref reactor,
TextureDescriptor desc,
GLuint fbo);
@ -41,6 +45,9 @@ class TextureGLES final : public Texture,
// |Texture|
~TextureGLES() override;
// |Texture|
bool IsValid() const override;
std::optional<GLuint> GetGLHandle() const;
[[nodiscard]] bool Bind() const;
@ -58,9 +65,9 @@ class TextureGLES final : public Texture,
Type GetType() const;
bool IsWrapped() const { return is_wrapped_; }
bool IsWrapped() const;
std::optional<GLuint> GetFBO() const { return wrapped_fbo_; }
std::optional<GLuint> GetFBO() const;
// For non cubemap textures, 0 indicates uninitialized and 1 indicates
// initialized. For cubemap textures, each face is initialized separately with
@ -81,7 +88,8 @@ class TextureGLES final : public Texture,
TextureGLES(std::shared_ptr<ReactorGLES> reactor,
TextureDescriptor desc,
bool is_wrapped,
std::optional<GLuint> fbo);
std::optional<GLuint> fbo,
std::optional<HandleGLES> external_handle);
// |Texture|
void SetLabel(std::string_view label) override;
@ -95,9 +103,6 @@ class TextureGLES final : public Texture,
bool OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
size_t slice) override;
// |Texture|
bool IsValid() const override;
// |Texture|
ISize GetSize() const override;

View File

@ -8,7 +8,11 @@
#include "flutter/fml/mapping.h"
#include "impeller/base/validation.h"
#include "impeller/core/texture.h"
#include "impeller/geometry/scalar.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/texture_gles.h"
#include "impeller/renderer/context.h"
#include "impeller/toolkit/interop/color_filter.h"
#include "impeller/toolkit/interop/color_source.h"
#include "impeller/toolkit/interop/context.h"
@ -501,6 +505,48 @@ ImpellerTexture ImpellerTextureCreateWithContentsNew(
return texture.Leak();
}
IMPELLER_EXTERN_C
ImpellerTexture ImpellerTextureCreateWithOpenGLTextureHandleNew(
ImpellerContext context,
const ImpellerTextureDescriptor* descriptor,
uint64_t external_gl_handle) {
auto impeller_context = GetPeer(context)->GetContext();
if (impeller_context->GetBackendType() !=
impeller::Context::BackendType::kOpenGLES) {
VALIDATION_LOG << "Context is not OpenGL.";
return nullptr;
}
const auto& impeller_context_gl = ContextGLES::Cast(*impeller_context);
const auto& reactor = impeller_context_gl.GetReactor();
auto wrapped_external_gl_handle =
reactor->CreateHandle(HandleType::kTexture, external_gl_handle);
if (wrapped_external_gl_handle.IsDead()) {
VALIDATION_LOG << "Could not wrap external handle.";
return nullptr;
}
TextureDescriptor desc;
desc.storage_mode = StorageMode::kDevicePrivate;
desc.type = TextureType::kTexture2D;
desc.format = ToImpellerType(descriptor->pixel_format);
desc.size = ToImpellerType(descriptor->size);
desc.mip_count = std::min(descriptor->mip_count, 1u);
desc.usage = TextureUsage::kShaderRead;
desc.compression_type = CompressionType::kLossless;
auto texture = std::make_shared<TextureGLES>(reactor, //
desc, //
wrapped_external_gl_handle //
);
if (!texture || !texture->IsValid()) {
VALIDATION_LOG << "Could not wrap external texture.";
return nullptr;
}
texture->SetCoordinateSystem(TextureCoordinateSystem::kUploadFromHost);
return Create<Texture>(std::move(texture)).Leak();
}
IMPELLER_EXTERN_C
void ImpellerTextureRetain(ImpellerTexture texture) {
ObjectBase::SafeRetain(texture);

View File

@ -57,7 +57,7 @@ IMPELLER_EXTERN_C_BEGIN
#define IMPELLER_VERSION_VARIANT 1
#define IMPELLER_VERSION_MAJOR 1
#define IMPELLER_VERSION_MINOR 1
#define IMPELLER_VERSION_MINOR 2
#define IMPELLER_VERSION_PATCH 0
#define IMPELLER_VERSION \
@ -480,6 +480,13 @@ ImpellerTextureCreateWithContentsNew(
const ImpellerMapping* IMPELLER_NONNULL contents,
void* IMPELLER_NULLABLE contents_on_release_user_data);
IMPELLER_EXPORT IMPELLER_NODISCARD ImpellerTexture IMPELLER_NULLABLE
ImpellerTextureCreateWithOpenGLTextureHandleNew(
ImpellerContext IMPELLER_NONNULL context,
const ImpellerTextureDescriptor* IMPELLER_NONNULL descriptor,
uint64_t handle // transfer-in ownership
);
IMPELLER_EXPORT
void ImpellerTextureRetain(ImpellerTexture IMPELLER_NULLABLE texture);

View File

@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "flutter/testing/testing.h"
#include "impeller/base/allocation.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/toolkit/interop/context.h"
#include "impeller/toolkit/interop/dl.h"
#include "impeller/toolkit/interop/dl_builder.h"
@ -111,6 +113,73 @@ TEST_P(InteropPlaygroundTest, CanDrawImage) {
}));
}
TEST_P(InteropPlaygroundTest, CanCreateOpenGLImage) {
auto context = GetInteropContext();
auto impeller_context = context->GetContext();
if (impeller_context->GetBackendType() !=
impeller::Context::BackendType::kOpenGLES) {
GTEST_SKIP() << "This test works with OpenGL handles is only suitable for "
"that backend.";
return;
}
const auto& gl_context = ContextGLES::Cast(*impeller_context);
const auto& gl = gl_context.GetReactor()->GetProcTable();
constexpr ISize external_texture_size = {200, 300};
Allocation texture_data;
ASSERT_TRUE(
texture_data.Truncate(Bytes{external_texture_size.Area() * 4u}, false));
const auto kClearColor = Color::Fuchsia().ToR8G8B8A8();
for (size_t i = 0; i < external_texture_size.Area() * 4u; i += 4u) {
memcpy(texture_data.GetBuffer() + i, kClearColor.data(), 4);
}
GLuint external_texture = GL_NONE;
gl.GenTextures(1u, &external_texture);
ASSERT_NE(external_texture, 0u);
gl.BindTexture(GL_TEXTURE_2D, external_texture);
gl.TexImage2D(GL_TEXTURE_2D, //
0, //
GL_RGBA, //
external_texture_size.width, //
external_texture_size.height, //
0, //
GL_RGBA, //
GL_UNSIGNED_BYTE, //
texture_data.GetBuffer() //
);
ImpellerTextureDescriptor desc = {};
desc.pixel_format = ImpellerPixelFormat::kImpellerPixelFormatRGBA8888;
desc.size = {external_texture_size.width, external_texture_size.height};
desc.mip_count = 1u;
auto texture = Adopt<Texture>(ImpellerTextureCreateWithOpenGLTextureHandleNew(
context.GetC(), //
&desc, //
external_texture //
));
ASSERT_TRUE(texture);
auto builder =
Adopt<DisplayListBuilder>(ImpellerDisplayListBuilderNew(nullptr));
ImpellerPoint point = {100, 100};
ImpellerDisplayListBuilderDrawTexture(builder.GetC(), texture.GetC(), &point,
kImpellerTextureSamplingLinear,
nullptr);
auto dl = Adopt<DisplayList>(
ImpellerDisplayListBuilderCreateDisplayListNew(builder.GetC()));
ASSERT_TRUE(
OpenPlaygroundHere([&](const auto& context, const auto& surface) -> bool {
ImpellerSurfaceDrawDisplayList(surface.GetC(), dl.GetC());
return true;
}));
}
TEST_P(InteropPlaygroundTest, CanCreateParagraphs) {
// Create a typography context.
auto type_context = Adopt<TypographyContext>(ImpellerTypographyContextNew());

View File

@ -18,6 +18,9 @@ Texture::Texture(const Context& context, const TextureDescriptor& descriptor) {
texture_ = std::move(texture);
}
Texture::Texture(std::shared_ptr<impeller::Texture> texture)
: texture_(std::move(texture)) {}
Texture::~Texture() = default;
bool Texture::IsValid() const {

View File

@ -18,6 +18,8 @@ class Texture final
public:
explicit Texture(const Context& context, const TextureDescriptor& descriptor);
explicit Texture(std::shared_ptr<impeller::Texture> texture);
~Texture() override;
Texture(const Texture&) = delete;