fuchsia: create new flutter_runner render path (#19584)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index a1a2c18..09bc2ba 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1162,6 +1162,8 @@
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_fakes.h
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
+FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
+FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.h
 FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl_unittest.cc
diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn
index a5da492..d90c996 100644
--- a/shell/platform/fuchsia/flutter/BUILD.gn
+++ b/shell/platform/fuchsia/flutter/BUILD.gn
@@ -53,12 +53,12 @@
       "accessibility_bridge.h",
       "component.cc",
       "component.h",
-      "compositor_context.cc",
-      "compositor_context.h",
       "engine.cc",
       "engine.h",
       "flutter_runner_product_configuration.cc",
       "flutter_runner_product_configuration.h",
+      "fuchsia_external_view_embedder.cc",
+      "fuchsia_external_view_embedder.h",
       "fuchsia_intl.cc",
       "fuchsia_intl.h",
       "isolate_configurator.cc",
@@ -92,6 +92,12 @@
       "vulkan_surface_producer.cc",
       "vulkan_surface_producer.h",
     ]
+    if (flutter_enable_legacy_fuchsia_embedder) {
+      sources += [
+        "compositor_context.cc",
+        "compositor_context.h",
+      ]
+    }
 
     public_configs = runner_configs
 
diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc
index 0106931..f7de2e9 100644
--- a/shell/platform/fuchsia/flutter/component.cc
+++ b/shell/platform/fuchsia/flutter/component.cc
@@ -54,6 +54,7 @@
 constexpr char kAssetsKey[] = "assets";
 constexpr char kTmpPath[] = "/tmp";
 constexpr char kServiceRootPath[] = "/svc";
+constexpr char kRunnerConfigPath[] = "/config/data/flutter_runner_config";
 
 // static
 void Application::ParseProgramMetadata(
@@ -346,11 +347,13 @@
     }
   }
 
-  // Load and use product-specific configuration, if it exists.
+  // Load and use runner-specific configuration, if it exists.
   std::string json_string;
-  if (dart_utils::ReadFileToString(
-          "/config/data/frame_scheduling_performance_values", &json_string)) {
+  if (dart_utils::ReadFileToString(kRunnerConfigPath, &json_string)) {
     product_config_ = FlutterRunnerProductConfiguration(json_string);
+  } else {
+    FML_LOG(WARNING) << "Failed to load runner configuration from "
+                     << kRunnerConfigPath << "; using default config values.";
   }
 
 #if defined(DART_PRODUCT)
diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc
index fe4dff0..3d8d53c 100644
--- a/shell/platform/fuchsia/flutter/engine.cc
+++ b/shell/platform/fuchsia/flutter/engine.cc
@@ -8,7 +8,6 @@
 #include <zircon/status.h>
 
 #include "../runtime/dart/utils/files.h"
-#include "compositor_context.h"
 #include "flutter/common/task_runners.h"
 #include "flutter/fml/make_copyable.h"
 #include "flutter/fml/synchronization/waitable_event.h"
@@ -16,13 +15,20 @@
 #include "flutter/runtime/dart_vm_lifecycle.h"
 #include "flutter/shell/common/rasterizer.h"
 #include "flutter/shell/common/run_configuration.h"
+#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
+
 #include "flutter_runner_product_configuration.h"
+#include "fuchsia_external_view_embedder.h"
 #include "fuchsia_intl.h"
 #include "platform_view.h"
+#include "surface.h"
 #include "task_runner_adapter.h"
-#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
 #include "thread.h"
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "compositor_context.h"  // nogncheck
+#endif
+
 namespace flutter_runner {
 namespace {
 
@@ -65,6 +71,9 @@
                FlutterRunnerProductConfiguration product_config)
     : delegate_(delegate),
       thread_label_(std::move(thread_label)),
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+      use_legacy_renderer_(product_config.use_legacy_renderer()),
+#endif
       weak_factory_(this) {
   if (zx::event::create(0, &vsync_event_) != ZX_OK) {
     FML_DLOG(ERROR) << "Could not create the vsync event.";
@@ -124,9 +133,18 @@
             thread_label_, std::move(session),
             std::move(session_error_callback), [](auto) {}, vsync_handle);
         surface_producer_.emplace(session_connection_->get());
-        scene_update_context_.emplace(thread_label_, std::move(view_token),
-                                      std::move(view_ref_pair),
-                                      session_connection_.value());
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+        if (use_legacy_renderer_) {
+          legacy_external_view_embedder_.emplace(
+              thread_label_, std::move(view_token), std::move(view_ref_pair),
+              session_connection_.value());
+        } else
+#endif
+        {
+          external_view_embedder_.emplace(
+              thread_label_, std::move(view_token), std::move(view_ref_pair),
+              session_connection_.value(), surface_producer_.value());
+        }
       }));
 
   // Grab the parent environment services. The platform view may want to
@@ -152,11 +170,8 @@
   OnDestroyView on_destroy_view_callback =
       std::bind(&Engine::DestroyView, this, std::placeholders::_1);
 
-  OnGetViewEmbedder on_get_view_embedder_callback =
-      std::bind(&Engine::GetViewEmbedder, this);
-
-  OnGetGrContext on_get_gr_context_callback =
-      std::bind(&Engine::GetGrContext, this);
+  OnCreateSurface on_create_surface_callback =
+      std::bind(&Engine::CreateSurface, this);
 
   // SessionListener has a OnScenicError method; invoke this callback on the
   // platform thread when that happens. The Session itself should also be
@@ -187,11 +202,9 @@
            on_create_view_callback = std::move(on_create_view_callback),
            on_update_view_callback = std::move(on_update_view_callback),
            on_destroy_view_callback = std::move(on_destroy_view_callback),
-           on_get_view_embedder_callback =
-               std::move(on_get_view_embedder_callback),
-           on_get_gr_context_callback = std::move(on_get_gr_context_callback),
-           vsync_handle = vsync_event_.get(),
-           product_config = product_config](flutter::Shell& shell) mutable {
+           on_create_surface_callback = std::move(on_create_surface_callback),
+           vsync_offset = product_config.get_vsync_offset(),
+           vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
             return std::make_unique<flutter_runner::PlatformView>(
                 shell,                   // delegate
                 debug_label,             // debug label
@@ -206,27 +219,35 @@
                 std::move(on_create_view_callback),
                 std::move(on_update_view_callback),
                 std::move(on_destroy_view_callback),
-                std::move(on_get_view_embedder_callback),
-                std::move(on_get_gr_context_callback),
-                vsync_handle,  // vsync handle
-                product_config);
+                std::move(on_create_surface_callback),
+                std::move(vsync_offset),  // vsync offset
+                vsync_handle);
           });
 
   // Setup the callback that will instantiate the rasterizer.
-  flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
-      fml::MakeCopyable([this](flutter::Shell& shell) mutable {
-        FML_DCHECK(session_connection_);
-        FML_DCHECK(surface_producer_);
-        FML_DCHECK(scene_update_context_);
+  flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  on_create_rasterizer = [this](flutter::Shell& shell) {
+    if (use_legacy_renderer_) {
+      FML_DCHECK(session_connection_);
+      FML_DCHECK(surface_producer_);
+      FML_DCHECK(legacy_external_view_embedder_);
 
-        std::unique_ptr<flutter_runner::CompositorContext> compositor_context =
-            std::make_unique<flutter_runner::CompositorContext>(
-                session_connection_.value(), surface_producer_.value(),
-                scene_update_context_.value());
-
-        return std::make_unique<flutter::Rasterizer>(
-            shell, std::move(compositor_context));
-      });
+      auto compositor_context =
+          std::make_unique<flutter_runner::CompositorContext>(
+              session_connection_.value(), surface_producer_.value(),
+              legacy_external_view_embedder_.value());
+      return std::make_unique<flutter::Rasterizer>(
+          shell, std::move(compositor_context));
+    } else {
+      return std::make_unique<flutter::Rasterizer>(shell);
+    }
+  };
+#else
+  on_create_rasterizer = [](flutter::Shell& shell) {
+    return std::make_unique<flutter::Rasterizer>(shell);
+  };
+#endif
 
   settings.root_isolate_create_callback =
       std::bind(&Engine::OnMainIsolateStart, this);
@@ -479,59 +500,97 @@
 }
 
 void Engine::DebugWireframeSettingsChanged(bool enabled) {
-  if (!shell_ || !scene_update_context_) {
-    return;
-  }
+  FML_CHECK(shell_);
 
-  shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
-      [this, enabled]() { scene_update_context_->EnableWireframe(enabled); });
+  shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, enabled]() {
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+    if (use_legacy_renderer_) {
+      FML_CHECK(legacy_external_view_embedder_);
+      legacy_external_view_embedder_->EnableWireframe(enabled);
+    } else
+#endif
+    {
+      FML_CHECK(external_view_embedder_);
+      external_view_embedder_->EnableWireframe(enabled);
+    }
+  });
 }
 
 void Engine::CreateView(int64_t view_id, bool hit_testable, bool focusable) {
-  if (!shell_ || !scene_update_context_) {
-    return;
-  }
+  FML_CHECK(shell_);
 
   shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
       [this, view_id, hit_testable, focusable]() {
-        scene_update_context_->CreateView(view_id, hit_testable, focusable);
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+        if (use_legacy_renderer_) {
+          FML_CHECK(legacy_external_view_embedder_);
+          legacy_external_view_embedder_->CreateView(view_id, hit_testable,
+                                                     focusable);
+        } else
+#endif
+        {
+          FML_CHECK(external_view_embedder_);
+          external_view_embedder_->CreateView(view_id);
+          external_view_embedder_->SetViewProperties(view_id, hit_testable,
+                                                     focusable);
+        }
       });
 }
 
 void Engine::UpdateView(int64_t view_id, bool hit_testable, bool focusable) {
-  if (!shell_ || !scene_update_context_) {
-    return;
-  }
+  FML_CHECK(shell_);
 
   shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
       [this, view_id, hit_testable, focusable]() {
-        scene_update_context_->UpdateView(view_id, hit_testable, focusable);
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+        if (use_legacy_renderer_) {
+          FML_CHECK(legacy_external_view_embedder_);
+          legacy_external_view_embedder_->UpdateView(view_id, hit_testable,
+                                                     focusable);
+        } else
+#endif
+        {
+          FML_CHECK(external_view_embedder_);
+          external_view_embedder_->SetViewProperties(view_id, hit_testable,
+                                                     focusable);
+        }
       });
 }
 
 void Engine::DestroyView(int64_t view_id) {
-  if (!shell_ || !scene_update_context_) {
-    return;
-  }
+  FML_CHECK(shell_);
 
-  shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
-      [this, view_id]() { scene_update_context_->DestroyView(view_id); });
+  shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, view_id]() {
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+    if (use_legacy_renderer_) {
+      FML_CHECK(legacy_external_view_embedder_);
+      legacy_external_view_embedder_->DestroyView(view_id);
+    } else
+#endif
+    {
+      FML_CHECK(external_view_embedder_);
+      external_view_embedder_->DestroyView(view_id);
+    }
+  });
 }
 
-flutter::ExternalViewEmbedder* Engine::GetViewEmbedder() {
-  if (!scene_update_context_) {
-    return nullptr;
+std::unique_ptr<flutter::Surface> Engine::CreateSurface() {
+  flutter::ExternalViewEmbedder* external_view_embedder = nullptr;
+
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  if (use_legacy_renderer_) {
+    FML_CHECK(legacy_external_view_embedder_);
+    external_view_embedder = &legacy_external_view_embedder_.value();
+  } else
+#endif
+  {
+    FML_CHECK(external_view_embedder_);
+    external_view_embedder = &external_view_embedder_.value();
   }
+  FML_CHECK(external_view_embedder);
 
-  return &scene_update_context_.value();
-}
-
-GrDirectContext* Engine::GetGrContext() {
-  // GetGrContext should be called only after rasterizer is created.
-  FML_DCHECK(shell_);
-  FML_DCHECK(shell_->GetRasterizer());
-
-  return surface_producer_->gr_context();
+  return std::make_unique<Surface>(thread_label_, external_view_embedder,
+                                   surface_producer_->gr_context());
 }
 
 #if !defined(DART_PRODUCT)
diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h
index 410ff63..20e14e8 100644
--- a/shell/platform/fuchsia/flutter/engine.h
+++ b/shell/platform/fuchsia/flutter/engine.h
@@ -14,17 +14,21 @@
 #include <lib/ui/scenic/cpp/view_ref_pair.h>
 #include <lib/zx/event.h>
 
-#include "flutter/flow/embedded_views.h"
-#include "flutter/flow/scene_update_context.h"
+#include "flutter/flow/surface.h"
 #include "flutter/fml/macros.h"
 #include "flutter/shell/common/shell.h"
 
 #include "flutter_runner_product_configuration.h"
+#include "fuchsia_external_view_embedder.h"
 #include "isolate_configurator.h"
 #include "session_connection.h"
 #include "thread.h"
 #include "vulkan_surface_producer.h"
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "flutter/flow/scene_update_context.h"  // nogncheck
+#endif
+
 namespace flutter_runner {
 
 // Represents an instance of running Flutter engine along with the threads
@@ -65,7 +69,10 @@
 
   std::optional<SessionConnection> session_connection_;
   std::optional<VulkanSurfaceProducer> surface_producer_;
-  std::optional<flutter::SceneUpdateContext> scene_update_context_;
+  std::optional<FuchsiaExternalViewEmbedder> external_view_embedder_;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  std::optional<flutter::SceneUpdateContext> legacy_external_view_embedder_;
+#endif
 
   std::unique_ptr<IsolateConfigurator> isolate_configurator_;
   std::unique_ptr<flutter::Shell> shell_;
@@ -74,6 +81,10 @@
 
   zx::event vsync_event_;
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  bool use_legacy_renderer_ = true;
+#endif
+
   fml::WeakPtrFactory<Engine> weak_factory_;
 
   void OnMainIsolateStart();
@@ -87,9 +98,7 @@
   void UpdateView(int64_t view_id, bool hit_testable, bool focusable);
   void DestroyView(int64_t view_id);
 
-  flutter::ExternalViewEmbedder* GetViewEmbedder();
-
-  GrDirectContext* GetGrContext();
+  std::unique_ptr<flutter::Surface> CreateSurface();
 
   FML_DISALLOW_COPY_AND_ASSIGN(Engine);
 };
diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
index af7af50..3406faa 100644
--- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
+++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
@@ -20,6 +20,11 @@
   if (auto& val = document["vsync_offset_in_us"]; val.IsInt()) {
     vsync_offset_ = fml::TimeDelta::FromMicroseconds(val.GetInt());
   }
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  if (auto& val = document["use_legacy_renderer"]; val.IsBool()) {
+    use_legacy_renderer_ = val.GetBool();
+  }
+#endif
 }
 
 }  // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
index 29958f5..57313d7 100644
--- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
+++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
@@ -4,6 +4,7 @@
 
 #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
 #define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
+
 #include "flutter/fml/time/time_delta.h"
 
 namespace flutter_runner {
@@ -14,9 +15,15 @@
   FlutterRunnerProductConfiguration(std::string path);
 
   fml::TimeDelta get_vsync_offset() { return vsync_offset_; }
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  bool use_legacy_renderer() { return use_legacy_renderer_; }
+#endif
 
  private:
   fml::TimeDelta vsync_offset_ = fml::TimeDelta::Zero();
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+  bool use_legacy_renderer_ = true;
+#endif
 };
 
 }  // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
new file mode 100644
index 0000000..96fd028
--- /dev/null
+++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
@@ -0,0 +1,442 @@
+// 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 "fuchsia_external_view_embedder.h"
+
+#include <lib/ui/scenic/cpp/commands.h>
+#include <lib/ui/scenic/cpp/view_token_pair.h>
+#include <zircon/types.h>
+
+#include <algorithm>  // For std::clamp
+
+#include "flutter/fml/trace_event.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace flutter_runner {
+namespace {
+
+// Layer separation is as infinitesimal as possible without introducing
+// Z-fighting.
+constexpr float kScenicZElevationBetweenLayers = 0.0001f;
+constexpr float kScenicZElevationForPlatformView = 100.f;
+
+}  // namespace
+
+FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
+    std::string debug_label,
+    fuchsia::ui::views::ViewToken view_token,
+    scenic::ViewRefPair view_ref_pair,
+    SessionConnection& session,
+    VulkanSurfaceProducer& surface_producer)
+    : session_(session),
+      surface_producer_(surface_producer),
+      root_view_(session_.get(),
+                 std::move(view_token),
+                 std::move(view_ref_pair.control_ref),
+                 std::move(view_ref_pair.view_ref),
+                 debug_label),
+      metrics_node_(session_.get()),
+      root_node_(session_.get()) {
+  root_view_.AddChild(metrics_node_);
+  metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
+  metrics_node_.SetLabel("Flutter::MetricsWatcher");
+  metrics_node_.AddChild(root_node_);
+  root_node_.SetLabel("Flutter::LayerTree");
+
+  session_.Present();
+}
+
+FuchsiaExternalViewEmbedder::~FuchsiaExternalViewEmbedder() = default;
+
+SkCanvas* FuchsiaExternalViewEmbedder::GetRootCanvas() {
+  auto found = frame_layers_.find(kRootLayerId);
+  if (found == frame_layers_.end()) {
+    FML_DLOG(WARNING)
+        << "No root canvas could be found. This is extremely unlikely and "
+           "indicates that the external view embedder did not receive the "
+           "notification to begin the frame.";
+    return nullptr;
+  }
+
+  return found->second.canvas_spy->GetSpyingCanvas();
+}
+
+std::vector<SkCanvas*> FuchsiaExternalViewEmbedder::GetCurrentCanvases() {
+  std::vector<SkCanvas*> canvases;
+  for (const auto& layer : frame_layers_) {
+    // This method (for legacy reasons) expects non-root current canvases.
+    if (layer.first.has_value()) {
+      canvases.push_back(layer.second.canvas_spy->GetSpyingCanvas());
+    }
+  }
+  return canvases;
+}
+
+void FuchsiaExternalViewEmbedder::PrerollCompositeEmbeddedView(
+    int view_id,
+    std::unique_ptr<flutter::EmbeddedViewParams> params) {
+  zx_handle_t handle = static_cast<zx_handle_t>(view_id);
+  FML_DCHECK(frame_layers_.count(handle) == 0);
+
+  frame_layers_.emplace(std::make_pair(EmbedderLayerId{handle},
+                                       EmbedderLayer(frame_size_, *params)));
+  frame_composition_order_.push_back(handle);
+}
+
+SkCanvas* FuchsiaExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
+  zx_handle_t handle = static_cast<zx_handle_t>(view_id);
+  auto found = frame_layers_.find(handle);
+  FML_DCHECK(found != frame_layers_.end());
+
+  return found->second.canvas_spy->GetSpyingCanvas();
+}
+
+flutter::PostPrerollResult FuchsiaExternalViewEmbedder::PostPrerollAction(
+    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  return flutter::PostPrerollResult::kSuccess;
+}
+
+void FuchsiaExternalViewEmbedder::BeginFrame(
+    SkISize frame_size,
+    GrDirectContext* context,
+    double device_pixel_ratio,
+    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::BeginFrame");
+
+  // Reset for new frame.
+  Reset();
+  frame_size_ = frame_size;
+  frame_dpr_ = device_pixel_ratio;
+
+  // Create the root layer.
+  frame_layers_.emplace(
+      std::make_pair(kRootLayerId, EmbedderLayer(frame_size, std::nullopt)));
+  frame_composition_order_.push_back(kRootLayerId);
+}
+
+void FuchsiaExternalViewEmbedder::EndFrame(
+    bool should_resubmit_frame,
+    fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+  TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::EndFrame");
+}
+
+void FuchsiaExternalViewEmbedder::SubmitFrame(
+    GrDirectContext* context,
+    std::unique_ptr<flutter::SurfaceFrame> frame) {
+  TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame");
+
+  std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
+  std::unordered_map<EmbedderLayerId, size_t> frame_surface_indices;
+
+  // Create surfaces for the frame and associate them with layer IDs.
+  {
+    TRACE_EVENT0("flutter", "CreateSurfaces");
+
+    for (const auto& layer : frame_layers_) {
+      if (!layer.second.canvas_spy->DidDrawIntoCanvas()) {
+        continue;
+      }
+
+      auto surface =
+          surface_producer_.ProduceSurface(layer.second.surface_size);
+      FML_DCHECK(surface)
+          << "Embedder did not return a valid render target of size ("
+          << layer.second.surface_size.width() << ", "
+          << layer.second.surface_size.height() << ")";
+
+      frame_surface_indices.emplace(
+          std::make_pair(layer.first, frame_surfaces.size()));
+      frame_surfaces.emplace_back(std::move(surface));
+    }
+  }
+
+  // Submit layers and platform views to Scenic in composition order.
+  {
+    TRACE_EVENT0("flutter", "SubmitLayers");
+
+    std::unordered_map<uint64_t, size_t> scenic_rect_indices;
+    size_t scenic_layer_index = 0;
+    float embedded_views_height = 0.0f;
+
+    // First re-scale everything according to the DPR.
+    const float inv_dpr = 1.0f / frame_dpr_;
+    root_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
+
+    for (const auto& layer_id : frame_composition_order_) {
+      const auto& layer = frame_layers_.find(layer_id);
+      FML_DCHECK(layer != frame_layers_.end());
+
+      // Draw the PlatformView associated with each layer first.
+      if (layer_id.has_value()) {
+        FML_DCHECK(layer->second.embedded_view_params.has_value());
+        auto& view_params = layer->second.embedded_view_params.value();
+
+        // Compute offset and size for the platform view.
+        SkPoint view_offset =
+            SkPoint::Make(view_params.finalBoundingRect().left(),
+                          view_params.finalBoundingRect().top());
+        SkSize view_size =
+            SkSize::Make(view_params.finalBoundingRect().width(),
+                         view_params.finalBoundingRect().height());
+
+        // Compute opacity for the platform view.
+        float view_opacity = 1.0f;
+        for (auto i = view_params.mutatorsStack().Bottom();
+             i != view_params.mutatorsStack().Top(); ++i) {
+          const auto& mutator = *i;
+          switch (mutator->GetType()) {
+            case flutter::MutatorType::opacity: {
+              view_opacity *= std::clamp(mutator->GetAlphaFloat(), 0.0f, 1.0f);
+            } break;
+            default: {
+              break;
+            }
+          }
+        }
+
+        auto found = scenic_views_.find(layer_id.value());
+        FML_DCHECK(found != scenic_views_.end());
+        auto& view_holder = found->second;
+
+        // Set opacity.
+        if (view_opacity != view_holder.opacity) {
+          view_holder.opacity_node.SetOpacity(view_opacity);
+          view_holder.opacity = view_opacity;
+        }
+
+        // Set offset and elevation.
+        const float view_elevation =
+            kScenicZElevationBetweenLayers * scenic_layer_index +
+            embedded_views_height;
+        if (view_offset != view_holder.offset ||
+            view_elevation != view_holder.elevation) {
+          view_holder.entity_node.SetTranslation(view_offset.fX, view_offset.fY,
+                                                 -view_elevation);
+          view_holder.elevation = view_elevation;
+        }
+
+        // Set HitTestBehavior.
+        if (view_holder.pending_hit_testable != view_holder.hit_testable) {
+          view_holder.entity_node.SetHitTestBehavior(
+              view_holder.pending_hit_testable
+                  ? fuchsia::ui::gfx::HitTestBehavior::kDefault
+                  : fuchsia::ui::gfx::HitTestBehavior::kSuppress);
+          view_holder.hit_testable = view_holder.pending_hit_testable;
+        }
+
+        // Set size and focusable.
+        //
+        // Scenic rejects `SetViewProperties` calls with a zero size.
+        if (!view_size.isEmpty() &&
+            (view_size != view_holder.size ||
+             view_holder.pending_focusable != view_holder.focusable)) {
+          view_holder.view_holder.SetViewProperties({
+              .bounding_box =
+                  {
+                      .min = {.x = 0.f, .y = 0.f, .z = -1000.f},
+                      .max = {.x = view_size.width(),
+                              .y = view_size.height(),
+                              .z = 0.f},
+                  },
+              .inset_from_min = {.x = 0.f, .y = 0.f, .z = 0.f},
+              .inset_from_max = {.x = 0.f, .y = 0.f, .z = 0.f},
+              .focus_change = view_holder.pending_focusable,
+          });
+          view_holder.size = view_size;
+          view_holder.focusable = view_holder.pending_focusable;
+        }
+
+        // Attach the ScenicView to the main scene graph.
+        root_node_.AddChild(view_holder.opacity_node);
+
+        // Account for the ScenicView's height when positioning the next layer.
+        embedded_views_height += kScenicZElevationForPlatformView;
+      }
+
+      if (layer->second.canvas_spy->DidDrawIntoCanvas()) {
+        const auto& surface_index = frame_surface_indices.find(layer_id);
+        FML_DCHECK(surface_index != frame_surface_indices.end());
+        scenic::Image* surface_image =
+            frame_surfaces[surface_index->second]->GetImage();
+
+        // Create a new layer if needed for the surface.
+        FML_DCHECK(scenic_layer_index <= scenic_layers_.size());
+        if (scenic_layer_index == scenic_layers_.size()) {
+          ScenicLayer new_layer{
+              .shape_node = scenic::ShapeNode(session_.get()),
+              .material = scenic::Material(session_.get()),
+          };
+          new_layer.shape_node.SetMaterial(new_layer.material);
+          scenic_layers_.emplace_back(std::move(new_layer));
+        }
+
+        // Compute a hash and index for the rect.
+        const uint64_t rect_hash =
+            (static_cast<uint64_t>(layer->second.surface_size.width()) << 32) +
+            layer->second.surface_size.height();
+        size_t rect_index = 0;
+        auto found_index = scenic_rect_indices.find(rect_hash);
+        if (found_index == scenic_rect_indices.end()) {
+          scenic_rect_indices.emplace(std::make_pair(rect_hash, 0));
+        } else {
+          rect_index = found_index->second + 1;
+          scenic_rect_indices[rect_hash] = rect_index;
+        }
+
+        // Create a new rect if needed for the surface.
+        auto found_rects = scenic_rects_.find(rect_hash);
+        if (found_rects == scenic_rects_.end()) {
+          auto [emplaced_rects, success] = scenic_rects_.emplace(
+              std::make_pair(rect_hash, std::vector<scenic::Rectangle>()));
+          FML_DCHECK(success);
+
+          found_rects = std::move(emplaced_rects);
+        }
+        FML_DCHECK(rect_index <= found_rects->second.size());
+        if (rect_index == found_rects->second.size()) {
+          found_rects->second.emplace_back(scenic::Rectangle(
+              session_.get(), layer->second.surface_size.width(),
+              layer->second.surface_size.height()));
+        }
+
+        // Set layer shape and texture.
+        // Scenic currently lacks an API to enable rendering of alpha channel;
+        // Flutter Embedder also lacks an API to detect if a layer has alpha or
+        // not. Alpha channels are only rendered if there is a OpacityNode
+        // higher in the tree with opacity != 1. For now, always assume t he
+        // layer has alpha and clamp to a infinitesimally smaller value than 1.
+        //
+        // This does not cause visual problems in practice, but probably has
+        // performance implications.
+        auto& scenic_layer = scenic_layers_[scenic_layer_index];
+        auto& scenic_rect = found_rects->second[rect_index];
+        const float layer_elevation =
+            kScenicZElevationBetweenLayers * scenic_layer_index +
+            embedded_views_height;
+        scenic_layer.shape_node.SetLabel("Flutter::Layer");
+        scenic_layer.shape_node.SetShape(scenic_rect);
+        scenic_layer.shape_node.SetTranslation(
+            layer->second.surface_size.width() * 0.5f,
+            layer->second.surface_size.height() * 0.5f, -layer_elevation);
+        scenic_layer.material.SetColor(SK_AlphaOPAQUE, SK_AlphaOPAQUE,
+                                       SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1);
+        scenic_layer.material.SetTexture(*surface_image);
+
+        // Attach the ScenicLayer to the main scene graph.
+        root_node_.AddChild(scenic_layer.shape_node);
+
+        // Account for the ScenicLayer's height when positioning the next layer.
+        scenic_layer_index++;
+      }
+    }
+  }
+
+  // Present the session to Scenic, along with surface acquire/release fencess.
+  {
+    TRACE_EVENT0("flutter", "SessionPresent");
+
+    session_.Present();
+  }
+
+  // Render the recorded SkPictures into the surfaces.
+  {
+    TRACE_EVENT0("flutter", "RasterizeSurfaces");
+
+    for (const auto& surface_index : frame_surface_indices) {
+      TRACE_EVENT0("flutter", "RasterizeSurface");
+
+      const auto& layer = frame_layers_.find(surface_index.first);
+      FML_DCHECK(layer != frame_layers_.end());
+      sk_sp<SkPicture> picture =
+          layer->second.recorder->finishRecordingAsPicture();
+      FML_DCHECK(picture);
+
+      sk_sp<SkSurface> sk_surface =
+          frame_surfaces[surface_index.second]->GetSkiaSurface();
+      FML_DCHECK(sk_surface);
+      FML_DCHECK(SkISize::Make(sk_surface->width(), sk_surface->height()) ==
+                 frame_size_);
+
+      SkCanvas* canvas = sk_surface->getCanvas();
+      FML_DCHECK(canvas);
+
+      canvas->setMatrix(SkMatrix::I());
+      canvas->clear(SK_ColorTRANSPARENT);
+      canvas->drawPicture(picture);
+      canvas->flush();
+    }
+  }
+
+  // Flush deferred Skia work and inform Scenic that render targets are ready.
+  {
+    TRACE_EVENT0("flutter", "PresentSurfaces");
+
+    surface_producer_.OnSurfacesPresented(std::move(frame_surfaces));
+  }
+
+  // Submit the underlying render-backend-specific frame for processing.
+  frame->Submit();
+}
+
+void FuchsiaExternalViewEmbedder::EnableWireframe(bool enable) {
+  session_.get()->Enqueue(
+      scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
+  session_.Present();
+}
+
+void FuchsiaExternalViewEmbedder::CreateView(int64_t view_id) {
+  FML_DCHECK(scenic_views_.find(view_id) == scenic_views_.end());
+
+  ScenicView new_view = {
+      .opacity_node = scenic::OpacityNodeHACK(session_.get()),
+      .entity_node = scenic::EntityNode(session_.get()),
+      .view_holder = scenic::ViewHolder(
+          session_.get(),
+          scenic::ToViewHolderToken(zx::eventpair((zx_handle_t)view_id)),
+          "Flutter::PlatformView"),
+  };
+
+  new_view.opacity_node.SetLabel("flutter::PlatformView::OpacityMutator");
+  new_view.entity_node.SetLabel("flutter::PlatformView::TransformMutator");
+  new_view.opacity_node.AddChild(new_view.entity_node);
+  new_view.entity_node.Attach(new_view.view_holder);
+  new_view.entity_node.SetTranslation(0.f, 0.f,
+                                      -kScenicZElevationBetweenLayers);
+
+  scenic_views_.emplace(std::make_pair(view_id, std::move(new_view)));
+}
+
+void FuchsiaExternalViewEmbedder::DestroyView(int64_t view_id) {
+  size_t erased = scenic_views_.erase(view_id);
+  FML_DCHECK(erased == 1);
+}
+
+void FuchsiaExternalViewEmbedder::SetViewProperties(int64_t view_id,
+                                                    bool hit_testable,
+                                                    bool focusable) {
+  auto found = scenic_views_.find(view_id);
+  FML_DCHECK(found != scenic_views_.end());
+  auto& view_holder = found->second;
+
+  view_holder.pending_hit_testable = hit_testable;
+  view_holder.pending_focusable = focusable;
+}
+
+void FuchsiaExternalViewEmbedder::Reset() {
+  frame_layers_.clear();
+  frame_composition_order_.clear();
+  frame_size_ = SkISize::Make(0, 0);
+  frame_dpr_ = 1.f;
+
+  // Detach the root node to prepare for the next frame.
+  session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id()));
+
+  // Clear images on all layers so they aren't cached unnecesarily.
+  for (auto& layer : scenic_layers_) {
+    layer.material.SetTexture(0);
+  }
+}
+
+}  // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
new file mode 100644
index 0000000..f299a2f
--- /dev/null
+++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
@@ -0,0 +1,158 @@
+// 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_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
+#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
+
+#include <fuchsia/ui/views/cpp/fidl.h>
+#include <lib/ui/scenic/cpp/resources.h>
+#include <lib/ui/scenic/cpp/view_ref_pair.h>
+
+#include <cstdint>  // For uint32_t & uint64_t
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "flutter/flow/embedded_views.h"
+#include "flutter/fml/logging.h"
+#include "flutter/fml/macros.h"
+#include "flutter/shell/common/canvas_spy.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
+
+#include "session_connection.h"
+#include "vulkan_surface_producer.h"
+
+namespace flutter_runner {
+
+// This class orchestrates interaction with the Scenic compositor on Fuchsia. It
+// ensures that flutter content and platform view content are both rendered
+// correctly in a unified scene.
+class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
+ public:
+  FuchsiaExternalViewEmbedder(std::string debug_label,
+                              fuchsia::ui::views::ViewToken view_token,
+                              scenic::ViewRefPair view_ref_pair,
+                              SessionConnection& session,
+                              VulkanSurfaceProducer& surface_producer);
+  ~FuchsiaExternalViewEmbedder();
+
+  // |ExternalViewEmbedder|
+  SkCanvas* GetRootCanvas() override;
+
+  // |ExternalViewEmbedder|
+  std::vector<SkCanvas*> GetCurrentCanvases() override;
+
+  // |ExternalViewEmbedder|
+  void PrerollCompositeEmbeddedView(
+      int view_id,
+      std::unique_ptr<flutter::EmbeddedViewParams> params) override;
+
+  // |ExternalViewEmbedder|
+  SkCanvas* CompositeEmbeddedView(int view_id) override;
+
+  // |ExternalViewEmbedder|
+  flutter::PostPrerollResult PostPrerollAction(
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  void BeginFrame(
+      SkISize frame_size,
+      GrDirectContext* context,
+      double device_pixel_ratio,
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  void EndFrame(
+      bool should_resubmit_frame,
+      fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+  // |ExternalViewEmbedder|
+  void SubmitFrame(GrDirectContext* context,
+                   std::unique_ptr<flutter::SurfaceFrame> frame) override;
+
+  // |ExternalViewEmbedder|
+  void CancelFrame() override { Reset(); }
+
+  // |ExternalViewEmbedder|
+  bool SupportsDynamicThreadMerging() override { return false; }
+
+  // View manipulation.
+  // |SetViewProperties| doesn't manipulate the view directly -- it sets
+  // prending properties for the next |UpdateView| call.
+  void EnableWireframe(bool enable);
+  void CreateView(int64_t view_id);
+  void DestroyView(int64_t view_id);
+  void SetViewProperties(int64_t view_id, bool hit_testable, bool focusable);
+
+ private:
+  // Reset state for a new frame.
+  void Reset();
+
+  struct EmbedderLayer {
+    EmbedderLayer(const SkISize& frame_size,
+                  std::optional<flutter::EmbeddedViewParams> view_params)
+        : embedded_view_params(std::move(view_params)),
+          recorder(std::make_unique<SkPictureRecorder>()),
+          canvas_spy(std::make_unique<flutter::CanvasSpy>(
+              recorder->beginRecording(frame_size.width(),
+                                       frame_size.height()))),
+          surface_size(frame_size) {}
+
+    std::optional<flutter::EmbeddedViewParams> embedded_view_params;
+    std::unique_ptr<SkPictureRecorder> recorder;
+    std::unique_ptr<flutter::CanvasSpy> canvas_spy;
+    SkISize surface_size;
+  };
+
+  struct ScenicView {
+    scenic::OpacityNodeHACK opacity_node;
+    scenic::EntityNode entity_node;
+    scenic::ViewHolder view_holder;
+
+    SkPoint offset = SkPoint::Make(0.f, 0.f);
+    SkSize size = SkSize::MakeEmpty();
+    float elevation = 0.f;
+    float opacity = 1.f;
+    bool hit_testable = false;
+    bool focusable = false;
+
+    bool pending_hit_testable = false;
+    bool pending_focusable = false;
+  };
+
+  struct ScenicLayer {
+    scenic::ShapeNode shape_node;
+    scenic::Material material;
+  };
+
+  using EmbedderLayerId = std::optional<uint32_t>;
+  constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
+
+  SessionConnection& session_;
+  VulkanSurfaceProducer& surface_producer_;
+
+  scenic::View root_view_;
+  scenic::EntityNode metrics_node_;
+  scenic::EntityNode root_node_;
+
+  std::unordered_map<uint64_t, std::vector<scenic::Rectangle>> scenic_rects_;
+  std::unordered_map<int64_t, ScenicView> scenic_views_;
+  std::vector<ScenicLayer> scenic_layers_;
+
+  std::unordered_map<EmbedderLayerId, EmbedderLayer> frame_layers_;
+  std::vector<EmbedderLayerId> frame_composition_order_;
+  SkISize frame_size_ = SkISize::Make(0, 0);
+  float frame_dpr_ = 1.f;
+
+  FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder);
+};
+
+}  // namespace flutter_runner
+
+#endif  // FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc
index 5359c1e..0d289f5 100644
--- a/shell/platform/fuchsia/flutter/platform_view.cc
+++ b/shell/platform/fuchsia/flutter/platform_view.cc
@@ -10,10 +10,8 @@
 #include <sstream>
 
 #include "flutter/fml/logging.h"
-#include "flutter/lib/ui/compositing/scene_host.h"
 #include "flutter/lib/ui/window/pointer_data.h"
 #include "flutter/lib/ui/window/window.h"
-#include "flutter_runner_product_configuration.h"
 #include "logging.h"
 #include "rapidjson/document.h"
 #include "rapidjson/stringbuffer.h"
@@ -21,6 +19,10 @@
 #include "runtime/dart/utils/inlines.h"
 #include "vsync_waiter.h"
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "flutter/lib/ui/compositing/scene_host.h"
+#endif
+
 namespace flutter_runner {
 
 static constexpr char kFlutterPlatformChannel[] = "flutter/platform";
@@ -60,10 +62,9 @@
     OnCreateView on_create_view_callback,
     OnUpdateView on_update_view_callback,
     OnDestroyView on_destroy_view_callback,
-    OnGetViewEmbedder on_get_view_embedder_callback,
-    OnGetGrContext on_get_gr_context_callback,
-    zx_handle_t vsync_event_handle,
-    FlutterRunnerProductConfiguration product_config)
+    OnCreateSurface on_create_surface_callback,
+    fml::TimeDelta vsync_offset,
+    zx_handle_t vsync_event_handle)
     : flutter::PlatformView(delegate, std::move(task_runners)),
       debug_label_(std::move(debug_label)),
       view_ref_(std::move(view_ref)),
@@ -75,11 +76,10 @@
       on_create_view_callback_(std::move(on_create_view_callback)),
       on_update_view_callback_(std::move(on_update_view_callback)),
       on_destroy_view_callback_(std::move(on_destroy_view_callback)),
-      on_get_view_embedder_callback_(std::move(on_get_view_embedder_callback)),
-      on_get_gr_context_callback_(std::move(on_get_gr_context_callback)),
+      on_create_surface_callback_(std::move(on_create_surface_callback)),
       ime_client_(this),
-      vsync_event_handle_(vsync_event_handle),
-      product_config_(product_config) {
+      vsync_offset_(std::move(vsync_offset)),
+      vsync_event_handle_(vsync_event_handle) {
   // Register all error handlers.
   SetInterfaceErrorHandler(session_listener_binding_, "SessionListener");
   SetInterfaceErrorHandler(ime_, "Input Method Editor");
@@ -248,6 +248,7 @@
             }
             break;
           }
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
           case fuchsia::ui::gfx::Event::Tag::kViewConnected:
             OnChildViewConnected(event.gfx().view_connected().view_holder_id);
             break;
@@ -260,6 +261,7 @@
                 event.gfx().view_state_changed().view_holder_id,
                 event.gfx().view_state_changed().state.is_rendering);
             break;
+#endif
           case fuchsia::ui::gfx::Event::Tag::Invalid:
             FML_DCHECK(false) << "Flutter PlatformView::OnScenicEvent: Got "
                                  "an invalid GFX event.";
@@ -317,6 +319,7 @@
   }
 }
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
 void PlatformView::OnChildViewConnected(scenic::ResourceId view_holder_id) {
   task_runners_.GetUITaskRunner()->PostTask([view_holder_id]() {
     flutter::SceneHost::OnViewConnected(view_holder_id);
@@ -335,6 +338,7 @@
     flutter::SceneHost::OnViewStateChanged(view_holder_id, state);
   });
 }
+#endif
 
 static flutter::PointerData::Change GetChangeFromPointerEventPhase(
     fuchsia::ui::input::PointerEventPhase phase) {
@@ -513,21 +517,12 @@
 // |flutter::PlatformView|
 std::unique_ptr<flutter::VsyncWaiter> PlatformView::CreateVSyncWaiter() {
   return std::make_unique<flutter_runner::VsyncWaiter>(
-      debug_label_, vsync_event_handle_, task_runners_,
-      product_config_.get_vsync_offset());
+      debug_label_, vsync_event_handle_, task_runners_, vsync_offset_);
 }
 
 // |flutter::PlatformView|
 std::unique_ptr<flutter::Surface> PlatformView::CreateRenderingSurface() {
-  // This platform does not repeatly lose and gain a surface connection. So the
-  // surface is setup once during platform view setup and returned to the
-  // shell on the initial (and only) |NotifyCreated| call.
-  auto view_embedder = on_get_view_embedder_callback_
-                           ? on_get_view_embedder_callback_()
-                           : nullptr;
-  auto gr_context =
-      on_get_gr_context_callback_ ? on_get_gr_context_callback_() : nullptr;
-  return std::make_unique<Surface>(debug_label_, view_embedder, gr_context);
+  return on_create_surface_callback_ ? on_create_surface_callback_() : nullptr;
 }
 
 // |flutter::PlatformView|
diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h
index 35275b3..431272b 100644
--- a/shell/platform/fuchsia/flutter/platform_view.h
+++ b/shell/platform/fuchsia/flutter/platform_view.h
@@ -7,18 +7,21 @@
 
 #include <fuchsia/ui/input/cpp/fidl.h>
 #include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/fidl/cpp/binding.h>
 #include <lib/fit/function.h>
 
 #include <map>
 #include <set>
 
 #include "flutter/fml/macros.h"
+#include "flutter/fml/time/time_delta.h"
 #include "flutter/shell/common/platform_view.h"
-#include "flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h"
-#include "flutter_runner_product_configuration.h"
-#include "lib/fidl/cpp/binding.h"
-#include "lib/ui/scenic/cpp/id.h"
-#include "surface.h"
+
+#include "accessibility_bridge.h"
+
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include <lib/ui/scenic/cpp/id.h>  // nogncheck
+#endif
 
 namespace flutter_runner {
 
@@ -26,8 +29,7 @@
 using OnCreateView = fit::function<void(int64_t, bool, bool)>;
 using OnUpdateView = fit::function<void(int64_t, bool, bool)>;
 using OnDestroyView = fit::function<void(int64_t)>;
-using OnGetViewEmbedder = fit::function<flutter::ExternalViewEmbedder*()>;
-using OnGetGrContext = fit::function<GrDirectContext*()>;
+using OnCreateSurface = fit::function<std::unique_ptr<flutter::Surface>()>;
 
 // The per engine component residing on the platform thread is responsible for
 // all platform specific integrations.
@@ -55,10 +57,9 @@
                OnCreateView on_create_view_callback,
                OnUpdateView on_update_view_callback,
                OnDestroyView on_destroy_view_callback,
-               OnGetViewEmbedder on_get_view_embedder_callback,
-               OnGetGrContext on_get_gr_context_callback,
-               zx_handle_t vsync_event_handle,
-               FlutterRunnerProductConfiguration product_config);
+               OnCreateSurface on_create_surface_callback,
+               fml::TimeDelta vsync_offset,
+               zx_handle_t vsync_event_handle);
 
   ~PlatformView();
 
@@ -87,8 +88,7 @@
   OnCreateView on_create_view_callback_;
   OnUpdateView on_update_view_callback_;
   OnDestroyView on_destroy_view_callback_;
-  OnGetViewEmbedder on_get_view_embedder_callback_;
-  OnGetGrContext on_get_gr_context_callback_;
+  OnCreateSurface on_create_surface_callback_;
 
   int current_text_input_client_ = 0;
   fidl::Binding<fuchsia::ui::input::InputMethodEditorClient> ime_client_;
@@ -112,14 +112,14 @@
   // such. Notifying via logs multiple times results in log-spam. See:
   // https://github.com/flutter/flutter/issues/55966
   std::set<std::string /* channel */> unregistered_channels_;
+
+  fml::TimeDelta vsync_offset_;
   zx_handle_t vsync_event_handle_ = 0;
 
   float view_width_ = 0.0f;        // Width in logical pixels.
   float view_height_ = 0.0f;       // Height in logical pixels.
   float view_pixel_ratio_ = 0.0f;  // Logical / physical pixel ratio.
 
-  FlutterRunnerProductConfiguration product_config_;
-
   void RegisterPlatformMessageHandlers();
 
   // |fuchsia::ui::input::InputMethodEditorClient|
@@ -134,9 +134,11 @@
   void OnScenicError(std::string error) override;
   void OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events) override;
 
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
   void OnChildViewConnected(scenic::ResourceId view_holder_id);
   void OnChildViewDisconnected(scenic::ResourceId view_holder_id);
   void OnChildViewStateChanged(scenic::ResourceId view_holder_id, bool state);
+#endif
 
   bool OnHandlePointerEvent(const fuchsia::ui::input::PointerEvent& pointer);
 
diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc
index 6d236e8..1810c4c 100644
--- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc
+++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc
@@ -20,6 +20,7 @@
 #include "flutter/lib/ui/window/window.h"
 #include "gtest/gtest.h"
 
+#include "surface.h"
 #include "task_runner_adapter.h"
 
 namespace flutter_runner_test::flutter_runner_a11y_test {
@@ -116,10 +117,7 @@
 
   bool SemanticsEnabled() const { return semantics_enabled_; }
   int32_t SemanticsFeatures() const { return semantics_features_; }
-  flutter::ExternalViewEmbedder* get_view_embedder() {
-    return surface_->GetExternalViewEmbedder();
-  }
-  GrDirectContext* get_gr_context() { return surface_->GetContext(); }
+  flutter::Surface* surface() const { return surface_.get(); }
 
  private:
   std::unique_ptr<flutter::Surface> surface_;
@@ -162,18 +160,17 @@
       std::move(view_ref),                    // view_ref
       std::move(task_runners),                // task_runners
       services_provider.service_directory(),  // runner_services
-      nullptr,  // parent_environment_service_provider_handle
-      nullptr,  // session_listener_request
-      nullptr,  // focuser,
-      nullptr,  // on_session_listener_error_callback
-      nullptr,  // on_enable_wireframe_callback,
-      nullptr,  // on_create_view_callback,
-      nullptr,  // on_update_view_callback,
-      nullptr,  // on_destroy_view_callback,
-      nullptr,  // on_get_view_embedder_callback,
-      nullptr,  // on_get_gr_context_callback,
-      0u,       // vsync_event_handle
-      {}        // product_config
+      nullptr,                 // parent_environment_service_provider_handle
+      nullptr,                 // session_listener_request
+      nullptr,                 // focuser,
+      nullptr,                 // on_session_listener_error_callback
+      nullptr,                 // on_enable_wireframe_callback,
+      nullptr,                 // on_create_view_callback,
+      nullptr,                 // on_update_view_callback,
+      nullptr,                 // on_destroy_view_callback,
+      nullptr,                 // on_create_surface_callback,
+      fml::TimeDelta::Zero(),  // vsync_offset
+      ZX_HANDLE_INVALID        // vsync_event_handle
   );
 
   RunLoopUntilIdle();
@@ -227,10 +224,9 @@
       nullptr,                  // on_create_view_callback,
       nullptr,                  // on_update_view_callback,
       nullptr,                  // on_destroy_view_callback,
-      nullptr,                  // on_get_view_embedder_callback,
-      nullptr,                  // on_get_gr_context_callback,
-      0u,                       // vsync_event_handle
-      {}                        // product_config
+      nullptr,                  // on_create_surface_callback,
+      fml::TimeDelta::Zero(),   // vsync_offset
+      ZX_HANDLE_INVALID         // vsync_event_handle
   );
 
   // Cast platform_view to its base view so we can have access to the public
@@ -287,18 +283,17 @@
       std::move(view_ref),                    // view_refs
       std::move(task_runners),                // task_runners
       services_provider.service_directory(),  // runner_services
-      nullptr,             // parent_environment_service_provider_handle
-      nullptr,             // session_listener_request
-      nullptr,             // focuser,
-      nullptr,             // on_session_listener_error_callback
-      nullptr,             // on_enable_wireframe_callback,
-      CreateViewCallback,  // on_create_view_callback,
-      nullptr,             // on_update_view_callback,
-      nullptr,             // on_destroy_view_callback,
-      nullptr,             // on_get_view_embedder_callback,
-      nullptr,             // on_get_gr_context_callback,
-      0u,                  // vsync_event_handle
-      {}                   // product_config
+      nullptr,                 // parent_environment_service_provider_handle
+      nullptr,                 // session_listener_request
+      nullptr,                 // focuser,
+      nullptr,                 // on_session_listener_error_callback
+      nullptr,                 // on_enable_wireframe_callback,
+      CreateViewCallback,      // on_create_view_callback,
+      nullptr,                 // on_update_view_callback,
+      nullptr,                 // on_destroy_view_callback,
+      nullptr,                 // on_create_surface_callback,
+      fml::TimeDelta::Zero(),  // vsync_offset
+      ZX_HANDLE_INVALID        // vsync_event_handle
   );
 
   // Cast platform_view to its base view so we can have access to the public
@@ -357,18 +352,17 @@
       std::move(view_ref),                    // view_refs
       std::move(task_runners),                // task_runners
       services_provider.service_directory(),  // runner_services
-      nullptr,             // parent_environment_service_provider_handle
-      nullptr,             // session_listener_request
-      nullptr,             // focuser,
-      nullptr,             // on_session_listener_error_callback
-      nullptr,             // on_enable_wireframe_callback,
-      nullptr,             // on_create_view_callback,
-      UpdateViewCallback,  // on_update_view_callback,
-      nullptr,             // on_destroy_view_callback,
-      nullptr,             // on_get_view_embedder_callback,
-      nullptr,             // on_get_gr_context_callback,
-      0u,                  // vsync_event_handle
-      {}                   // product_config
+      nullptr,                 // parent_environment_service_provider_handle
+      nullptr,                 // session_listener_request
+      nullptr,                 // focuser,
+      nullptr,                 // on_session_listener_error_callback
+      nullptr,                 // on_enable_wireframe_callback,
+      nullptr,                 // on_create_view_callback,
+      UpdateViewCallback,      // on_update_view_callback,
+      nullptr,                 // on_destroy_view_callback,
+      nullptr,                 // on_create_surface_callback,
+      fml::TimeDelta::Zero(),  // vsync_offset
+      ZX_HANDLE_INVALID        // vsync_event_handle
   );
 
   // Cast platform_view to its base view so we can have access to the public
@@ -427,18 +421,17 @@
       std::move(view_ref),                    // view_refs
       std::move(task_runners),                // task_runners
       services_provider.service_directory(),  // runner_services
-      nullptr,              // parent_environment_service_provider_handle
-      nullptr,              // session_listener_request
-      nullptr,              // focuser,
-      nullptr,              // on_session_listener_error_callback
-      nullptr,              // on_enable_wireframe_callback,
-      nullptr,              // on_create_view_callback,
-      nullptr,              // on_update_view_callback,
-      DestroyViewCallback,  // on_destroy_view_callback,
-      nullptr,              // on_get_view_embedder_callback,
-      nullptr,              // on_get_gr_context_callback,
-      0u,                   // vsync_event_handle
-      {}                    // product_config
+      nullptr,                 // parent_environment_service_provider_handle
+      nullptr,                 // session_listener_request
+      nullptr,                 // focuser,
+      nullptr,                 // on_session_listener_error_callback
+      nullptr,                 // on_enable_wireframe_callback,
+      nullptr,                 // on_create_view_callback,
+      nullptr,                 // on_update_view_callback,
+      DestroyViewCallback,     // on_destroy_view_callback,
+      nullptr,                 // on_create_surface_callback,
+      fml::TimeDelta::Zero(),  // vsync_offset
+      ZX_HANDLE_INVALID        // vsync_event_handle
   );
 
   // Cast platform_view to its base view so we can have access to the public
@@ -499,10 +492,9 @@
       nullptr,                    // on_create_view_callback,
       nullptr,                    // on_update_view_callback,
       nullptr,                    // on_destroy_view_callback,
-      nullptr,                    // on_get_gr_context_callback,
-      nullptr,                    // on_get_view_embedder_callback,
-      0u,                         // vsync_event_handle
-      {}                          // product_config
+      nullptr,                    // on_create_surface_callback,
+      fml::TimeDelta::Zero(),     // vsync_offset
+      ZX_HANDLE_INVALID           // vsync_event_handle
   );
 
   // Cast platform_view to its base view so we can have access to the public
@@ -534,8 +526,8 @@
 }
 
 // Test to make sure that PlatformView correctly returns a Surface instance
-// that can surface the view_embedder provided from GetViewEmbedderCallback.
-TEST_F(PlatformViewTests, GetViewEmbedderTest) {
+// that can surface the provided gr_context and view_embedder.
+TEST_F(PlatformViewTests, CreateSurfaceTest) {
   sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
   MockPlatformViewDelegate delegate;
   zx::eventpair a, b;
@@ -552,63 +544,13 @@
                            nullptr                               // io
       );
 
-  // Test get view embedder callback function.
-  MockExternalViewEmbedder view_embedder;
-  auto GetViewEmbedderCallback = [&view_embedder]() { return &view_embedder; };
-
-  auto platform_view = flutter_runner::PlatformView(
-      delegate,                               // delegate
-      "test_platform_view",                   // label
-      std::move(view_ref),                    // view_refs
-      std::move(task_runners),                // task_runners
-      services_provider.service_directory(),  // runner_services
-      nullptr,                  // parent_environment_service_provider_handle
-      nullptr,                  // session_listener_request
-      nullptr,                  // focuser,
-      nullptr,                  // on_session_listener_error_callback
-      nullptr,                  // on_enable_wireframe_callback,
-      nullptr,                  // on_create_view_callback,
-      nullptr,                  // on_update_view_callback,
-      nullptr,                  // on_destroy_view_callback,
-      GetViewEmbedderCallback,  // on_get_view_embedder_callback,
-      nullptr,                  // on_get_gr_context_callback,
-      0u,                       // vsync_event_handle
-      {}                        // product_config
-  );
-
-  RunLoopUntilIdle();
-
-  platform_view.NotifyCreated();
-
-  RunLoopUntilIdle();
-
-  EXPECT_EQ(&view_embedder, delegate.get_view_embedder());
-}
-
-// Test to make sure that PlatformView correctly returns a Surface instance
-// that can surface the GrContext provided from GetGrContextCallback.
-TEST_F(PlatformViewTests, GetGrContextTest) {
-  sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
-  MockPlatformViewDelegate delegate;
-  zx::eventpair a, b;
-  zx::eventpair::create(/* flags */ 0u, &a, &b);
-  auto view_ref = fuchsia::ui::views::ViewRef({
-      .reference = std::move(a),
-  });
-  flutter::TaskRunners task_runners =
-      flutter::TaskRunners("test_runners",  // label
-                           nullptr,         // platform
-                           flutter_runner::CreateFMLTaskRunner(
-                               async_get_default_dispatcher()),  // raster
-                           nullptr,                              // ui
-                           nullptr                               // io
-      );
-
-  // Test get GrContext callback function.
+  // Test create surface callback function.
   sk_sp<GrDirectContext> gr_context =
       GrDirectContext::MakeMock(nullptr, GrContextOptions());
-  auto GetGrContextCallback = [gr_context = gr_context.get()]() {
-    return gr_context;
+  MockExternalViewEmbedder view_embedder;
+  auto CreateSurfaceCallback = [&view_embedder, gr_context]() {
+    return std::make_unique<flutter_runner::Surface>(
+        "PlatformViewTest", &view_embedder, gr_context.get());
   };
 
   auto platform_view = flutter_runner::PlatformView(
@@ -617,27 +559,24 @@
       std::move(view_ref),                    // view_refs
       std::move(task_runners),                // task_runners
       services_provider.service_directory(),  // runner_services
-      nullptr,               // parent_environment_service_provider_handle
-      nullptr,               // session_listener_request
-      nullptr,               // focuser
-      nullptr,               // on_session_listener_error_callback
-      nullptr,               // on_enable_wireframe_callback,
-      nullptr,               // on_create_view_callback,
-      nullptr,               // on_update_view_callback,
-      nullptr,               // on_destroy_view_callback,
-      nullptr,               // on_get_view_embedder_callback,
-      GetGrContextCallback,  // on_get_gr_context_callback,
-      0u,                    // vsync_event_handle
-      {}                     // product_config
+      nullptr,                 // parent_environment_service_provider_handle
+      nullptr,                 // session_listener_request
+      nullptr,                 // focuser,
+      nullptr,                 // on_session_listener_error_callback
+      nullptr,                 // on_enable_wireframe_callback,
+      nullptr,                 // on_create_view_callback,
+      nullptr,                 // on_update_view_callback,
+      nullptr,                 // on_destroy_view_callback,
+      CreateSurfaceCallback,   // on_create_surface_callback,
+      fml::TimeDelta::Zero(),  // vsync_offset
+      ZX_HANDLE_INVALID        // vsync_event_handle
   );
-
-  RunLoopUntilIdle();
-
   platform_view.NotifyCreated();
 
   RunLoopUntilIdle();
 
-  EXPECT_EQ(gr_context.get(), delegate.get_gr_context());
+  EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext());
+  EXPECT_EQ(&view_embedder, delegate.surface()->GetExternalViewEmbedder());
 }
 
 }  // namespace flutter_runner_test::flutter_runner_a11y_test