[Impeller] implement external texture gl for embedder. (#56277)

Fixes https://github.com/flutter/flutter/issues/143809
diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn
index 3a99fa9..3562eae 100644
--- a/impeller/renderer/backend/gles/BUILD.gn
+++ b/impeller/renderer/backend/gles/BUILD.gn
@@ -22,6 +22,7 @@
     "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/impeller/renderer/backend/gles/reactor_gles.cc b/impeller/renderer/backend/gles/reactor_gles.cc
index 544c1f1..dca10df 100644
--- a/impeller/renderer/backend/gles/reactor_gles.cc
+++ b/impeller/renderer/backend/gles/reactor_gles.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "flutter/fml/trace_event.h"
+#include "fml/closure.h"
 #include "fml/logging.h"
 #include "impeller/base/validation.h"
 
@@ -85,6 +86,19 @@
   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<GLuint> CreateGLHandle(const ProcTableGLES& gl,
                                             HandleType type) {
   GLuint handle = GL_NONE;
diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h
index fa727c0..80fb687 100644
--- a/impeller/renderer/backend/gles/reactor_gles.h
+++ b/impeller/renderer/backend/gles/reactor_gles.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <vector>
 
+#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"
@@ -217,6 +218,23 @@
   [[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.
   ///
   ///             It is safe to call this simultaneously from multiple threads
@@ -231,6 +249,7 @@
     std::optional<GLuint> name;
     std::optional<std::string> pending_debug_label;
     bool pending_collection = false;
+    fml::ScopedCleanupClosure callback = {};
 
     LiveHandle() = default;
 
diff --git a/impeller/renderer/backend/gles/test/reactor_unittests.cc b/impeller/renderer/backend/gles/test/reactor_unittests.cc
new file mode 100644
index 0000000..97f15c4
--- /dev/null
+++ b/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 <memory>
+#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<ProcTableGLES>(resolver);
+  auto worker = std::make_shared<TestWorker>();
+  auto reactor = std::make_shared<ReactorGLES>(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/shell/platform/embedder/embedder_external_texture_gl.cc b/shell/platform/embedder/embedder_external_texture_gl.cc
index 7ad22cd..892b064 100644
--- a/shell/platform/embedder/embedder_external_texture_gl.cc
+++ b/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 @@
     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<DlImage> EmbedderExternalTextureGL::ResolveTextureSkia(
+    int64_t texture_id,
+    GrDirectContext* context,
+    const SkISize& size) {
   context->flushAndSubmit();
   context->resetContext(kAll_GrBackendState);
   std::unique_ptr<FlutterOpenGLTexture> texture =
@@ -110,6 +128,49 @@
   return DlImage::Make(std::move(image));
 }
 
+sk_sp<DlImage> EmbedderExternalTextureGL::ResolveTextureImpeller(
+    int64_t texture_id,
+    impeller::AiksContext* aiks_context,
+    const SkISize& size) {
+  std::unique_ptr<FlutterOpenGLTexture> 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<impeller::TextureGLES> image =
+      std::make_shared<impeller::TextureGLES>(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/shell/platform/embedder/embedder_external_texture_gl.h b/shell/platform/embedder/embedder_external_texture_gl.h
index a661a78..0d8853e 100644
--- a/shell/platform/embedder/embedder_external_texture_gl.h
+++ b/shell/platform/embedder/embedder_external_texture_gl.h
@@ -31,6 +31,14 @@
                                 impeller::AiksContext* aiks_context,
                                 const SkISize& size);
 
+  sk_sp<DlImage> ResolveTextureSkia(int64_t texture_id,
+                                    GrDirectContext* context,
+                                    const SkISize& size);
+
+  sk_sp<DlImage> ResolveTextureImpeller(int64_t texture_id,
+                                        impeller::AiksContext* aiks_context,
+                                        const SkISize& size);
+
   // |flutter::Texture|
   void Paint(PaintContext& context,
              const SkRect& bounds,