[impeller] OpenGL: Add support for threads and contexts. (flutter/engine#33575)

This commit is contained in:
Chinmay Garde
2022-05-23 20:38:01 -07:00
committed by GitHub
parent 38a66b5e06
commit a35efe4636
18 changed files with 358 additions and 57 deletions

View File

@@ -103,4 +103,12 @@ struct hash<impeller::UniqueID> {
}
};
template <>
struct less<impeller::UniqueID> {
constexpr bool operator()(const impeller::UniqueID& lhs,
const impeller::UniqueID& rhs) const {
return lhs.id < rhs.id;
}
};
} // namespace std

View File

@@ -16,6 +16,33 @@
namespace impeller {
class PlaygroundImplGLES::ReactorWorker final : public ReactorGLES::Worker {
public:
ReactorWorker() = default;
// |ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const override {
ReaderLock lock(mutex_);
auto found = reactions_allowed_.find(std::this_thread::get_id());
if (found == reactions_allowed_.end()) {
return false;
}
return found->second;
}
void SetReactionsAllowedOnCurrentThread(bool allowed) {
WriterLock lock(mutex_);
reactions_allowed_[std::this_thread::get_id()] = allowed;
}
private:
mutable RWMutex mutex_;
std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);
FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
};
void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
if (!handle) {
return;
@@ -24,7 +51,8 @@ void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) {
}
PlaygroundImplGLES::PlaygroundImplGLES()
: handle_(nullptr, &DestroyWindowHandle) {
: handle_(nullptr, &DestroyWindowHandle),
worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
::glfwDefaultWindowHints();
#if FML_OS_MACOSX
@@ -48,6 +76,7 @@ PlaygroundImplGLES::PlaygroundImplGLES()
auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr);
::glfwMakeContextCurrent(window);
worker_->SetReactionsAllowedOnCurrentThread(true);
handle_.reset(window);
}
@@ -79,8 +108,19 @@ std::shared_ptr<Context> PlaygroundImplGLES::GetContext() const {
return nullptr;
}
return ContextGLES::Create(std::move(gl),
ShaderLibraryMappingsForPlayground());
auto context =
ContextGLES::Create(std::move(gl), ShaderLibraryMappingsForPlayground());
if (!context) {
FML_LOG(ERROR) << "Could not create context.";
return nullptr;
}
auto worker_id = context->AddReactorWorker(worker_);
if (!worker_id.has_value()) {
FML_LOG(ERROR) << "Could not add reactor worker.";
return nullptr;
}
return context;
}
// |PlaygroundImpl|

View File

@@ -16,9 +16,12 @@ class PlaygroundImplGLES final : public PlaygroundImpl {
~PlaygroundImplGLES();
private:
class ReactorWorker;
static void DestroyWindowHandle(WindowHandle handle);
using UniqueHandle = std::unique_ptr<void, decltype(&DestroyWindowHandle)>;
UniqueHandle handle_;
std::shared_ptr<ReactorWorker> worker_;
// |PlaygroundImpl|
std::shared_ptr<Context> GetContext() const override;

View File

@@ -9,10 +9,10 @@
namespace impeller {
std::shared_ptr<Context> ContextGLES::Create(
std::shared_ptr<ContextGLES> ContextGLES::Create(
std::unique_ptr<ProcTableGLES> gl,
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries) {
return std::shared_ptr<Context>(
return std::shared_ptr<ContextGLES>(
new ContextGLES(std::move(gl), std::move(shader_libraries)));
}
@@ -70,10 +70,25 @@ ContextGLES::ContextGLES(
ContextGLES::~ContextGLES() = default;
const ReactorGLES::Ref ContextGLES::GetReactor() const {
const ReactorGLES::Ref& ContextGLES::GetReactor() const {
return reactor_;
}
std::optional<ReactorGLES::WorkerID> ContextGLES::AddReactorWorker(
std::shared_ptr<ReactorGLES::Worker> worker) {
if (!IsValid()) {
return std::nullopt;
}
return reactor_->AddWorker(std::move(worker));
}
bool ContextGLES::RemoveReactorWorker(ReactorGLES::WorkerID id) {
if (!IsValid()) {
return false;
}
return reactor_->RemoveWorker(id);
}
bool ContextGLES::IsValid() const {
return is_valid_;
}

View File

@@ -19,14 +19,19 @@ namespace impeller {
class ContextGLES final : public Context,
public BackendCast<ContextGLES, Context> {
public:
static std::shared_ptr<Context> Create(
static std::shared_ptr<ContextGLES> Create(
std::unique_ptr<ProcTableGLES> gl,
std::vector<std::shared_ptr<fml::Mapping>> shader_libraries);
// |Context|
~ContextGLES() override;
const ReactorGLES::Ref GetReactor() const;
const ReactorGLES::Ref& GetReactor() const;
std::optional<ReactorGLES::WorkerID> AddReactorWorker(
std::shared_ptr<ReactorGLES::Worker> worker);
bool RemoveReactorWorker(ReactorGLES::WorkerID id);
private:
ReactorGLES::Ref reactor_;

View File

@@ -27,9 +27,11 @@ struct AutoErrorCheck {
~AutoErrorCheck() {
if (error_fn) {
auto error = error_fn();
FML_CHECK(error == GL_NO_ERROR)
<< "GL Error " << GLErrorToString(error) << "(" << error << ")"
<< " encountered on call to " << name;
if (error != GL_NO_ERROR) {
FML_LOG(ERROR) << "GL Error " << GLErrorToString(error) << "(" << error
<< ")"
<< " encountered on call to " << name;
}
}
}
};

View File

@@ -27,7 +27,20 @@ bool ReactorGLES::IsValid() const {
return is_valid_;
}
ReactorGLES::WorkerID ReactorGLES::AddWorker(std::weak_ptr<Worker> worker) {
Lock lock(workers_mutex_);
auto id = WorkerID{};
workers_[id] = std::move(worker);
return id;
}
bool ReactorGLES::RemoveWorker(WorkerID worker) {
Lock lock(workers_mutex_);
return workers_.erase(worker) == 1;
}
bool ReactorGLES::HasPendingOperations() const {
Lock ops_lock(ops_mutex_);
return !pending_operations_.empty() || !gl_handles_to_collect_.empty();
}
@@ -37,6 +50,7 @@ const ProcTableGLES& ReactorGLES::GetProcTable() const {
}
std::optional<GLuint> ReactorGLES::GetGLHandle(const HandleGLES& handle) const {
ReaderLock handles_lock(handles_mutex_);
auto found = live_gl_handles_.find(handle);
if (found != live_gl_handles_.end()) {
return found->second;
@@ -48,8 +62,13 @@ bool ReactorGLES::AddOperation(Operation operation) {
if (!operation) {
return false;
}
pending_operations_.emplace_back(std::move(operation));
return React();
{
Lock ops_lock(ops_mutex_);
pending_operations_.emplace_back(std::move(operation));
}
// Attempt a reaction if able but it is not an error if this isn't possible.
[[maybe_unused]] auto result = React();
return true;
}
static std::optional<GLuint> CreateGLHandle(const ProcTableGLES& gl,
@@ -109,17 +128,20 @@ HandleGLES ReactorGLES::CreateHandle(HandleType type) {
if (new_handle.IsDead()) {
return HandleGLES::DeadHandle();
}
WriterLock handles_lock(handles_mutex_);
live_gl_handles_[new_handle] =
in_reaction_ ? CreateGLHandle(GetProcTable(), type) : std::nullopt;
return new_handle;
}
void ReactorGLES::CollectHandle(HandleGLES handle) {
WriterLock handles_lock(handles_mutex_);
auto live_handle = live_gl_handles_.find(handle);
if (live_handle == live_gl_handles_.end()) {
return;
}
if (live_handle->second.has_value()) {
Lock ops_lock(ops_mutex_);
gl_handles_to_collect_[live_handle->first] = live_handle->second.value();
}
live_gl_handles_.erase(live_handle);
@@ -127,6 +149,9 @@ void ReactorGLES::CollectHandle(HandleGLES handle) {
bool ReactorGLES::React() {
TRACE_EVENT0("impeller", "ReactorGLES::React");
if (!CanReactOnCurrentThread()) {
return false;
}
in_reaction_ = true;
fml::ScopedCleanupClosure reset_in_reaction([&]() { in_reaction_ = false; });
while (HasPendingOperations()) {
@@ -165,7 +190,13 @@ bool ReactorGLES::ReactOnce() {
//----------------------------------------------------------------------------
/// Collect all the handles for whom there is a GL handle sibling.
///
for (const auto& handle_to_collect : gl_handles_to_collect_) {
decltype(gl_handles_to_collect_) gl_handles_to_collect;
{
Lock ops_lock(ops_mutex_);
std::swap(gl_handles_to_collect_, gl_handles_to_collect);
FML_DCHECK(gl_handles_to_collect_.empty());
}
for (const auto& handle_to_collect : gl_handles_to_collect) {
if (!CollectGLHandle(gl, // proc table
handle_to_collect.first.type, // handle type
handle_to_collect.second // GL handle name
@@ -174,26 +205,52 @@ bool ReactorGLES::ReactOnce() {
return false;
}
}
gl_handles_to_collect_.clear();
//----------------------------------------------------------------------------
/// Make sure all pending handles have a GL handle sibling.
///
for (auto& live_handle : live_gl_handles_) {
if (live_handle.second.has_value()) {
// Already a realized GL handle.
continue;
{
WriterLock handles_lock(handles_mutex_);
for (auto& live_handle : live_gl_handles_) {
if (live_handle.second.has_value()) {
// Already a realized GL handle.
continue;
}
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
if (!gl_handle.has_value()) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
}
live_handle.second = gl_handle;
}
auto gl_handle = CreateGLHandle(gl, live_handle.first.type);
if (!gl_handle.has_value()) {
VALIDATION_LOG << "Could not create GL handle.";
return false;
}
live_handle.second = gl_handle;
}
if (can_set_debug_labels_) {
for (const auto& label : pending_debug_labels_) {
//----------------------------------------------------------------------------
/// Flush all pending operations in order.
///
decltype(pending_operations_) pending_operations;
{
Lock ops_lock(ops_mutex_);
std::swap(pending_operations_, pending_operations);
FML_DCHECK(pending_operations_.empty());
}
for (const auto& operation : pending_operations) {
TRACE_EVENT0("impeller", "ReactorGLES::Operation");
operation(*this);
}
//----------------------------------------------------------------------------
/// Make sure all pending debug labels have been flushed.
///
decltype(pending_debug_labels_) pending_debug_labels;
{
WriterLock handles_lock(handles_mutex_);
std::swap(pending_debug_labels_, pending_debug_labels);
FML_DCHECK(pending_debug_labels_.empty());
}
if (!pending_debug_labels.empty()) {
ReaderLock handles_lock(handles_mutex_);
for (const auto& label : pending_debug_labels) {
auto live_handle = live_gl_handles_.find(label.first);
if (live_handle == live_gl_handles_.end() ||
!live_handle->second.has_value()) {
@@ -205,16 +262,6 @@ bool ReactorGLES::ReactOnce() {
);
}
}
pending_debug_labels_.clear();
//----------------------------------------------------------------------------
/// Flush all pending operations in order.
///
auto operations = std::move(pending_operations_);
for (const auto& operation : operations) {
operation(*this);
}
pending_operations_.clear();
return true;
}
@@ -229,18 +276,27 @@ void ReactorGLES::SetDebugLabel(const HandleGLES& handle, std::string label) {
if (handle.IsDead()) {
return;
}
if (in_reaction_) {
if (auto found = live_gl_handles_.find(handle);
found != live_gl_handles_.end() && found->second.has_value()) {
GetProcTable().SetDebugLabel(
ToDebugResourceType(found->first.type), // type
found->second.value(), // name
label // label
);
return;
}
}
WriterLock handles_lock(handles_mutex_);
pending_debug_labels_[handle] = std::move(label);
}
bool ReactorGLES::CanReactOnCurrentThread() const {
std::vector<WorkerID> dead_workers;
Lock lock(workers_mutex_);
for (const auto& worker : workers_) {
auto worker_ptr = worker.second.lock();
if (!worker_ptr) {
dead_workers.push_back(worker.first);
continue;
}
if (worker_ptr->CanReactorReactOnCurrentThreadNow(*this)) {
return true;
}
}
for (const auto& worker_id : dead_workers) {
workers_.erase(worker_id);
}
return false;
}
} // namespace impeller

View File

@@ -10,6 +10,7 @@
#include "flutter/fml/closure.h"
#include "flutter/fml/macros.h"
#include "impeller/base/thread.h"
#include "impeller/renderer/backend/gles/handle_gles.h"
#include "impeller/renderer/backend/gles/proc_table_gles.h"
@@ -17,6 +18,16 @@ namespace impeller {
class ReactorGLES {
public:
using WorkerID = UniqueID;
class Worker {
public:
virtual ~Worker() = default;
virtual bool CanReactorReactOnCurrentThreadNow(
const ReactorGLES& reactor) const = 0;
};
using Ref = std::shared_ptr<ReactorGLES>;
ReactorGLES(std::unique_ptr<ProcTableGLES> gl);
@@ -25,7 +36,9 @@ class ReactorGLES {
bool IsValid() const;
bool HasPendingOperations() const;
WorkerID AddWorker(std::weak_ptr<Worker> worker);
bool RemoveWorker(WorkerID);
const ProcTableGLES& GetProcTable() const;
@@ -44,10 +57,22 @@ class ReactorGLES {
private:
std::unique_ptr<ProcTableGLES> proc_table_;
std::vector<Operation> pending_operations_;
GLESHandleMap<std::optional<GLuint>> live_gl_handles_;
GLESHandleMap<GLuint> gl_handles_to_collect_;
GLESHandleMap<std::string> pending_debug_labels_;
mutable Mutex ops_mutex_;
std::vector<Operation> pending_operations_ IPLR_GUARDED_BY(ops_mutex_);
GLESHandleMap<GLuint> gl_handles_to_collect_ IPLR_GUARDED_BY(ops_mutex_);
mutable RWMutex handles_mutex_;
GLESHandleMap<std::optional<GLuint>> live_gl_handles_
IPLR_GUARDED_BY(handles_mutex_);
GLESHandleMap<std::string> pending_debug_labels_
IPLR_GUARDED_BY(handles_mutex_);
mutable Mutex workers_mutex_;
mutable std::map<WorkerID, std::weak_ptr<Worker>> workers_
IPLR_GUARDED_BY(workers_mutex_);
// TODO(csg): Make this thread safe.
bool in_reaction_ = false;
bool can_set_debug_labels_ = false;
@@ -55,6 +80,10 @@ class ReactorGLES {
bool ReactOnce();
bool HasPendingOperations() const;
bool CanReactOnCurrentThread() const;
FML_DISALLOW_COPY_AND_ASSIGN(ReactorGLES);
};

View File

@@ -18,7 +18,10 @@ impeller_component("egl") {
"surface.h",
]
deps = [ "//flutter/fml" ]
deps = [
"../../base",
"//flutter/fml",
]
libs = []
if (is_android) {

View File

@@ -54,10 +54,12 @@ bool Context::MakeCurrent(const Surface& surface) const {
if (!result) {
IMPELLER_LOG_EGL_ERROR;
}
DispatchLifecyleEvent(LifecycleEvent::kDidMakeCurrent);
return result;
}
bool Context::ClearCurrent() const {
DispatchLifecyleEvent(LifecycleEvent::kWillClearCurrent);
const auto result = EGLMakeCurrentIfNecessary(display_, //
EGL_NO_SURFACE, //
EGL_NO_SURFACE, //
@@ -69,5 +71,33 @@ bool Context::ClearCurrent() const {
return result;
}
std::optional<UniqueID> Context::AddLifecycleListener(
LifecycleListener listener) {
if (!listener) {
return std::nullopt;
}
WriterLock lock(listeners_mutex_);
UniqueID id;
listeners_[id] = listener;
return id;
}
bool Context::RemoveLifecycleListener(UniqueID id) {
WriterLock lock(listeners_mutex_);
auto found = listeners_.find(id);
if (found == listeners_.end()) {
return false;
}
listeners_.erase(found);
return true;
}
void Context::DispatchLifecyleEvent(LifecycleEvent event) const {
ReaderLock lock(listeners_mutex_);
for (const auto& listener : listeners_) {
listener.second(event);
}
}
} // namespace egl
} // namespace impeller

View File

@@ -4,7 +4,11 @@
#pragma once
#include <functional>
#include "flutter/fml/macros.h"
#include "impeller/base/comparable.h"
#include "impeller/base/thread.h"
#include "impeller/toolkit/egl/egl.h"
namespace impeller {
@@ -26,9 +30,23 @@ class Context {
bool ClearCurrent() const;
enum class LifecycleEvent {
kDidMakeCurrent,
kWillClearCurrent,
};
using LifecycleListener = std::function<void(LifecycleEvent)>;
std::optional<UniqueID> AddLifecycleListener(LifecycleListener listener);
bool RemoveLifecycleListener(UniqueID id);
private:
EGLDisplay display_ = EGL_NO_DISPLAY;
EGLContext context_ = EGL_NO_CONTEXT;
mutable RWMutex listeners_mutex_;
std::map<UniqueID, LifecycleListener> listeners_
IPLR_GUARDED_BY(listeners_mutex_);
void DispatchLifecyleEvent(LifecycleEvent event) const;
FML_DISALLOW_COPY_AND_ASSIGN(Context);
};

View File

@@ -61,6 +61,7 @@ static sk_sp<DlImage> DecompressAndUploadTexture(
TRACE_EVENT0("flutter", __FUNCTION__);
if (!context || !descriptor) {
FML_DLOG(ERROR) << "Invalid context or descriptor.";
return nullptr;
}

View File

@@ -14,7 +14,39 @@
namespace flutter {
static std::shared_ptr<impeller::Context> CreateImpellerContext() {
class AndroidSurfaceGLImpeller::ReactorWorker final
: public impeller::ReactorGLES::Worker {
public:
ReactorWorker() = default;
// |impeller::ReactorGLES::Worker|
~ReactorWorker() override = default;
// |impeller::ReactorGLES::Worker|
bool CanReactorReactOnCurrentThreadNow(
const impeller::ReactorGLES& reactor) const override {
impeller::ReaderLock lock(mutex_);
auto found = reactions_allowed_.find(std::this_thread::get_id());
if (found == reactions_allowed_.end()) {
return false;
}
return found->second;
}
void SetReactionsAllowedOnCurrentThread(bool allowed) {
impeller::WriterLock lock(mutex_);
reactions_allowed_[std::this_thread::get_id()] = allowed;
}
private:
mutable impeller::RWMutex mutex_;
std::map<std::thread::id, bool> reactions_allowed_ IPLR_GUARDED_BY(mutex_);
FML_DISALLOW_COPY_AND_ASSIGN(ReactorWorker);
};
static std::shared_ptr<impeller::Context> CreateImpellerContext(
std::shared_ptr<impeller::ReactorGLES::Worker> worker) {
auto proc_table = std::make_unique<impeller::ProcTableGLES>(
impeller::egl::CreateProcAddressResolver());
@@ -35,13 +67,20 @@ static std::shared_ptr<impeller::Context> CreateImpellerContext() {
FML_LOG(ERROR) << "Could not create OpenGLES Impeller Context.";
return nullptr;
}
if (!context->AddReactorWorker(std::move(worker)).has_value()) {
FML_LOG(ERROR) << "Could not add reactor worker.";
return nullptr;
}
return context;
}
AndroidSurfaceGLImpeller::AndroidSurfaceGLImpeller(
const std::shared_ptr<AndroidContext>& android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade)
: AndroidSurface(android_context) {
: AndroidSurface(android_context),
reactor_worker_(std::shared_ptr<ReactorWorker>(new ReactorWorker())) {
auto display = std::make_unique<impeller::egl::Display>();
if (!display->IsValid()) {
FML_DLOG(ERROR) << "Could not create EGL display.";
@@ -94,7 +133,7 @@ AndroidSurfaceGLImpeller::AndroidSurfaceGLImpeller(
return;
}
auto impeller_context = CreateImpellerContext();
auto impeller_context = CreateImpellerContext(reactor_worker_);
if (!impeller_context) {
FML_DLOG(ERROR) << "Could not create Impeller context.";
@@ -106,6 +145,24 @@ AndroidSurfaceGLImpeller::AndroidSurfaceGLImpeller(
return;
}
// Setup context listeners.
impeller::egl::Context::LifecycleListener listener =
[worker =
reactor_worker_](impeller::egl ::Context::LifecycleEvent event) {
switch (event) {
case impeller::egl::Context::LifecycleEvent::kDidMakeCurrent:
worker->SetReactionsAllowedOnCurrentThread(true);
break;
case impeller::egl::Context::LifecycleEvent::kWillClearCurrent:
worker->SetReactionsAllowedOnCurrentThread(false);
break;
}
};
if (!onscreen_context->AddLifecycleListener(listener).has_value() ||
!offscreen_context->AddLifecycleListener(listener).has_value()) {
FML_DLOG(ERROR) << "Could not add lifecycle listeners";
}
display_ = std::move(display);
onscreen_config_ = std::move(onscreen_config);
offscreen_config_ = std::move(offscreen_config);
@@ -182,6 +239,12 @@ std::unique_ptr<Surface> AndroidSurfaceGLImpeller::CreateSnapshotSurface() {
FML_UNREACHABLE();
}
// |AndroidSurface|
std::shared_ptr<impeller::Context>
AndroidSurfaceGLImpeller::GetImpellerContext() {
return impeller_context_;
}
// |GPUSurfaceGLDelegate|
std::unique_ptr<GLContextResult>
AndroidSurfaceGLImpeller::GLContextMakeCurrent() {

View File

@@ -49,6 +49,9 @@ class AndroidSurfaceGLImpeller final : public GPUSurfaceGLDelegate,
// |AndroidSurface|
std::unique_ptr<Surface> CreateSnapshotSurface() override;
// |AndroidSurface|
std::shared_ptr<impeller::Context> GetImpellerContext() override;
// |GPUSurfaceGLDelegate|
std::unique_ptr<GLContextResult> GLContextMakeCurrent() override;
@@ -72,6 +75,9 @@ class AndroidSurfaceGLImpeller final : public GPUSurfaceGLDelegate,
sk_sp<const GrGLInterface> GetGLInterface() const override;
private:
class ReactorWorker;
std::shared_ptr<ReactorWorker> reactor_worker_;
std::unique_ptr<impeller::egl::Display> display_;
std::unique_ptr<impeller::egl::Config> onscreen_config_;
std::unique_ptr<impeller::egl::Config> offscreen_config_;

View File

@@ -332,6 +332,15 @@ void PlatformViewAndroid::ReleaseResourceContext() const {
}
}
// |PlatformView|
std::shared_ptr<impeller::Context> PlatformViewAndroid::GetImpellerContext()
const {
if (android_surface_) {
return android_surface_->GetImpellerContext();
}
return nullptr;
}
// |PlatformView|
std::unique_ptr<std::vector<std::string>>
PlatformViewAndroid::ComputePlatformResolvedLocales(

View File

@@ -158,6 +158,9 @@ class PlatformViewAndroid final : public PlatformView {
// |PlatformView|
void ReleaseResourceContext() const override;
// |PlatformView|
std::shared_ptr<impeller::Context> GetImpellerContext() const override;
// |PlatformView|
std::unique_ptr<std::vector<std::string>> ComputePlatformResolvedLocales(
const std::vector<std::string>& supported_locale_data) override;

View File

@@ -19,4 +19,8 @@ std::unique_ptr<Surface> AndroidSurface::CreateSnapshotSurface() {
return nullptr;
}
std::shared_ptr<impeller::Context> AndroidSurface::GetImpellerContext() {
return nullptr;
}
} // namespace flutter

View File

@@ -14,6 +14,10 @@
#include "flutter/shell/platform/android/surface/android_native_window.h"
#include "third_party/skia/include/core/SkSize.h"
namespace impeller {
class Context;
} // namespace impeller
namespace flutter {
class AndroidExternalViewEmbedder;
@@ -39,6 +43,8 @@ class AndroidSurface {
virtual std::unique_ptr<Surface> CreateSnapshotSurface();
virtual std::shared_ptr<impeller::Context> GetImpellerContext();
protected:
explicit AndroidSurface(
const std::shared_ptr<AndroidContext>& android_context);