[Impeller] libImpeller: Allow wrapping external texture handles. (flutter/engine#55664)
Fixes https://github.com/flutter/flutter/issues/156013
This commit is contained in:
parent
d2e73aef5b
commit
7af6ae84e6
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user