From deaddd1ed9b35d38a8a03ff0237c7d33ee62037c Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 1 Nov 2024 16:04:06 -0700 Subject: [PATCH] [Impeller] implement external texture gl for embedder. (flutter/engine#56277) Fixes https://github.com/flutter/flutter/issues/143809 --- .../impeller/renderer/backend/gles/BUILD.gn | 1 + .../renderer/backend/gles/reactor_gles.cc | 14 +++++ .../renderer/backend/gles/reactor_gles.h | 19 ++++++ .../backend/gles/test/reactor_unittests.cc | 47 ++++++++++++++ .../embedder/embedder_external_texture_gl.cc | 63 ++++++++++++++++++- .../embedder/embedder_external_texture_gl.h | 8 +++ 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/test/reactor_unittests.cc diff --git a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn index 3a99fa9f99..3562eae59b 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn @@ -22,6 +22,7 @@ impeller_component("gles_unittests") { "test/mock_gles_unittests.cc", "test/pipeline_library_gles_unittests.cc", "test/proc_table_gles_unittests.cc", + "test/reactor_unittests.cc", "test/specialization_constants_unittests.cc", ] deps = [ diff --git a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc index 544c1f1a4f..dca10dff3d 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.cc @@ -7,6 +7,7 @@ #include #include "flutter/fml/trace_event.h" +#include "fml/closure.h" #include "fml/logging.h" #include "impeller/base/validation.h" @@ -85,6 +86,19 @@ bool ReactorGLES::AddOperation(Operation operation) { return true; } +bool ReactorGLES::RegisterCleanupCallback(const HandleGLES& handle, + const fml::closure& callback) { + if (handle.IsDead()) { + return false; + } + WriterLock handles_lock(handles_mutex_); + if (auto found = handles_.find(handle); found != handles_.end()) { + found->second.callback = fml::ScopedCleanupClosure(callback); + return true; + } + return false; +} + static std::optional CreateGLHandle(const ProcTableGLES& gl, HandleType type) { GLuint handle = GL_NONE; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.h index fa727c0869..80fb687736 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/reactor_gles.h @@ -9,6 +9,7 @@ #include #include +#include "fml/closure.h" #include "impeller/base/thread.h" #include "impeller/renderer/backend/gles/handle_gles.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" @@ -216,6 +217,23 @@ class ReactorGLES { /// [[nodiscard]] bool AddOperation(Operation operation); + //---------------------------------------------------------------------------- + /// @brief Register a cleanup callback that will be invokved with the + /// provided user data when the handle is destroyed. + /// + /// This operation is not guaranteed to run immediately. It will + /// complete in a finite amount of time on any thread as long as + /// there is a reactor worker and the reactor itself is not being + /// torn down. + /// + /// @param[in] handle The handle to attach the cleanup to. + /// @param[in] callback The cleanup callback to execute. + /// + /// @return If the operation was successfully queued for completion. + /// + bool RegisterCleanupCallback(const HandleGLES& handle, + const fml::closure& callback); + //---------------------------------------------------------------------------- /// @brief Perform a reaction on the current thread if able. /// @@ -231,6 +249,7 @@ class ReactorGLES { std::optional name; std::optional pending_debug_label; bool pending_collection = false; + fml::ScopedCleanupClosure callback = {}; LiveHandle() = default; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/test/reactor_unittests.cc b/engine/src/flutter/impeller/renderer/backend/gles/test/reactor_unittests.cc new file mode 100644 index 0000000000..97f15c4a5d --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/test/reactor_unittests.cc @@ -0,0 +1,47 @@ +// 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 +#include "flutter/testing/testing.h" // IWYU pragma: keep +#include "gtest/gtest.h" +#include "impeller/renderer/backend/gles/handle_gles.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/backend/gles/test/mock_gles.h" + +namespace impeller { +namespace testing { + +class TestWorker : public ReactorGLES::Worker { + public: + bool CanReactorReactOnCurrentThreadNow( + const ReactorGLES& reactor) const override { + return true; + } +}; + +TEST(ReactorGLES, CanAttachCleanupCallbacksToHandles) { + auto mock_gles = MockGLES::Init(); + ProcTableGLES::Resolver resolver = kMockResolverGLES; + auto proc_table = std::make_unique(resolver); + auto worker = std::make_shared(); + auto reactor = std::make_shared(std::move(proc_table)); + reactor->AddWorker(worker); + + int value = 0; + auto handle = reactor->CreateHandle(HandleType::kTexture, 1123); + auto added = + reactor->RegisterCleanupCallback(handle, [&value]() { value++; }); + + EXPECT_TRUE(added); + EXPECT_TRUE(reactor->React()); + + reactor->CollectHandle(handle); + EXPECT_TRUE(reactor->AddOperation([](const ReactorGLES& reactor) {})); + EXPECT_TRUE(reactor->React()); + EXPECT_EQ(value, 1); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc index 7ad22cdadb..892b064af0 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.cc @@ -5,7 +5,14 @@ #include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" #include "flutter/fml/logging.h" -#include "include/core/SkCanvas.h" +#include "impeller/core/texture_descriptor.h" +#include "impeller/display_list/aiks_context.h" +#include "impeller/display_list/dl_image_impeller.h" +#include "impeller/geometry/size.h" +#include "impeller/renderer/backend/gles/context_gles.h" +#include "impeller/renderer/backend/gles/handle_gles.h" +#include "impeller/renderer/backend/gles/texture_gles.h" + #include "include/core/SkPaint.h" #include "third_party/skia/include/core/SkAlphaType.h" #include "third_party/skia/include/core/SkColorSpace.h" @@ -62,6 +69,17 @@ sk_sp EmbedderExternalTextureGL::ResolveTexture( GrDirectContext* context, impeller::AiksContext* aiks_context, const SkISize& size) { + if (!!aiks_context) { + return ResolveTextureImpeller(texture_id, aiks_context, size); + } else { + return ResolveTextureSkia(texture_id, context, size); + } +} + +sk_sp EmbedderExternalTextureGL::ResolveTextureSkia( + int64_t texture_id, + GrDirectContext* context, + const SkISize& size) { context->flushAndSubmit(); context->resetContext(kAll_GrBackendState); std::unique_ptr texture = @@ -110,6 +128,49 @@ sk_sp EmbedderExternalTextureGL::ResolveTexture( return DlImage::Make(std::move(image)); } +sk_sp EmbedderExternalTextureGL::ResolveTextureImpeller( + int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size) { + std::unique_ptr texture = + external_texture_callback_(texture_id, size.width(), size.height()); + + if (!texture) { + return nullptr; + } + + impeller::TextureDescriptor desc; + desc.size = impeller::ISize(texture->width, texture->height); + + impeller::ContextGLES& context = + impeller::ContextGLES::Cast(*aiks_context->GetContext()); + impeller::HandleGLES handle = context.GetReactor()->CreateHandle( + impeller::HandleType::kTexture, texture->target); + std::shared_ptr image = + std::make_shared(context.GetReactor(), desc, + handle); + + if (!image) { + // In case Skia rejects the image, call the release proc so that + // embedders can perform collection of intermediates. + if (texture->destruction_callback) { + texture->destruction_callback(texture->user_data); + } + FML_LOG(ERROR) << "Could not create external texture"; + return nullptr; + } + if (texture->destruction_callback && + !context.GetReactor()->RegisterCleanupCallback( + handle, + [callback = texture->destruction_callback, + user_data = texture->user_data]() { callback(user_data); })) { + FML_LOG(ERROR) << "Could not register destruction callback"; + return nullptr; + } + + return impeller::DlImageImpeller::Make(image); +} + // |flutter::Texture| void EmbedderExternalTextureGL::OnGrContextCreated() {} diff --git a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h index a661a78341..0d8853e11d 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_external_texture_gl.h @@ -31,6 +31,14 @@ class EmbedderExternalTextureGL : public flutter::Texture { impeller::AiksContext* aiks_context, const SkISize& size); + sk_sp ResolveTextureSkia(int64_t texture_id, + GrDirectContext* context, + const SkISize& size); + + sk_sp ResolveTextureImpeller(int64_t texture_id, + impeller::AiksContext* aiks_context, + const SkISize& size); + // |flutter::Texture| void Paint(PaintContext& context, const SkRect& bounds,