From 491ce39b6dcfa7cef2c8db7bb03ac2ae340b3e0a Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 10 Aug 2023 11:54:56 -0700 Subject: [PATCH] [Impeller] Flutter GPU: Add context override. (flutter/engine#44566) Adds a way to inject a context override, which allows us to test the API in the Dart playground without spinning up an Engine/Shell. --- .../flutter/impeller/fixtures/dart_tests.dart | 8 +++- engine/src/flutter/impeller/renderer/BUILD.gn | 1 + .../renderer/renderer_dart_unittests.cc | 23 +++++++++- engine/src/flutter/lib/gpu/context.cc | 45 ++++++++++++------- engine/src/flutter/lib/gpu/context.h | 10 +++++ 5 files changed, 70 insertions(+), 17 deletions(-) diff --git a/engine/src/flutter/impeller/fixtures/dart_tests.dart b/engine/src/flutter/impeller/fixtures/dart_tests.dart index a025ba6606..ad6ad54c33 100644 --- a/engine/src/flutter/impeller/fixtures/dart_tests.dart +++ b/engine/src/flutter/impeller/fixtures/dart_tests.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'dart:ui' as ui; -//import 'dart:gpu'; +import '../../lib/gpu/lib/gpu.dart' as gpu; void main() {} @@ -11,3 +11,9 @@ void main() {} void sayHi() { print('Hi'); } + +@pragma('vm:entry-point') +void instantiateDefaultContext() { + // ignore: unused_local_variable + final gpu.GpuContext context = gpu.gpuContext; +} diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn index 04e6ef19f4..22a42063ac 100644 --- a/engine/src/flutter/impeller/renderer/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/BUILD.gn @@ -161,6 +161,7 @@ impeller_component("renderer_dart_unittests") { ":renderer_dart_fixtures", "../fixtures:shader_fixtures", "../playground:playground_test", + "//flutter/lib/gpu", "//flutter/runtime:runtime", "//flutter/testing:fixture_test", "//flutter/testing:testing", diff --git a/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc b/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc index defec38eab..91a84bae2b 100644 --- a/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc +++ b/engine/src/flutter/impeller/renderer/renderer_dart_unittests.cc @@ -10,6 +10,7 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/backtrace.h" #include "flutter/fml/command_line.h" +#include "flutter/lib/gpu/context.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_vm_lifecycle.h" @@ -46,7 +47,13 @@ class RendererDartTest : public PlaygroundTest, assert(isolate_->get()->GetPhase() == flutter::DartIsolate::Phase::Running); } - flutter::testing::AutoIsolateShutdown* GetIsolate() { return isolate_.get(); } + flutter::testing::AutoIsolateShutdown* GetIsolate() { + // Sneak the context into the Flutter GPU API. + assert(GetContext() != nullptr); + flutter::Context::SetOverrideContext(GetContext()); + + return isolate_.get(); + } private: std::unique_ptr CreateDartIsolate() { @@ -90,5 +97,19 @@ TEST_P(RendererDartTest, CanRunDartInPlaygroundFrame) { OpenPlaygroundHere(callback); } +TEST_P(RendererDartTest, CanInstantiateFlutterGPUContext) { + auto isolate = GetIsolate(); + bool result = isolate->RunInIsolateScope([]() -> bool { + if (tonic::CheckAndHandleError(::Dart_Invoke( + Dart_RootLibrary(), tonic::ToDart("instantiateDefaultContext"), 0, + nullptr))) { + return false; + } + return true; + }); + + ASSERT_TRUE(result); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/lib/gpu/context.cc b/engine/src/flutter/lib/gpu/context.cc index f56e938c6c..303c5d4e82 100644 --- a/engine/src/flutter/lib/gpu/context.cc +++ b/engine/src/flutter/lib/gpu/context.cc @@ -15,6 +15,16 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(gpu, Context); +std::shared_ptr Context::override_context_; + +void Context::SetOverrideContext(std::shared_ptr context) { + override_context_ = std::move(context); +} + +std::shared_ptr Context::GetOverrideContext() { + return override_context_; +} + Context::Context(std::shared_ptr context) : context_(std::move(context)) {} @@ -28,23 +38,28 @@ Context::~Context() = default; Dart_Handle InternalFlutterGpu_Context_InitializeDefault(Dart_Handle wrapper) { auto dart_state = flutter::UIDartState::Current(); - if (!dart_state->IsImpellerEnabled()) { - return tonic::ToDart( - "Flutter GPU requires the Impeller rendering backend to be enabled."); + + std::shared_ptr impeller_context = + flutter::Context::GetOverrideContext(); + + if (!impeller_context) { + if (!dart_state->IsImpellerEnabled()) { + return tonic::ToDart( + "Flutter GPU requires the Impeller rendering backend to be enabled."); + } + + // Grab the Impeller context from the IO manager. + std::promise> context_promise; + auto impeller_context_future = context_promise.get_future(); + dart_state->GetTaskRunners().GetIOTaskRunner()->PostTask( + fml::MakeCopyable([promise = std::move(context_promise), + io_manager = dart_state->GetIOManager()]() mutable { + promise.set_value(io_manager ? io_manager->GetImpellerContext() + : nullptr); + })); + impeller_context = impeller_context_future.get(); } - // Grab the Impeller context from the IO manager. - - std::promise> context_promise; - auto impeller_context_future = context_promise.get_future(); - dart_state->GetTaskRunners().GetIOTaskRunner()->PostTask( - fml::MakeCopyable([promise = std::move(context_promise), - io_manager = dart_state->GetIOManager()]() mutable { - promise.set_value(io_manager ? io_manager->GetImpellerContext() - : nullptr); - })); - - auto impeller_context = impeller_context_future.get(); if (!impeller_context) { return tonic::ToDart("Unable to retrieve the Impeller context."); } diff --git a/engine/src/flutter/lib/gpu/context.h b/engine/src/flutter/lib/gpu/context.h index 6667e1b29a..3133845624 100644 --- a/engine/src/flutter/lib/gpu/context.h +++ b/engine/src/flutter/lib/gpu/context.h @@ -16,9 +16,19 @@ class Context : public RefCountedDartWrappable { FML_FRIEND_MAKE_REF_COUNTED(Context); public: + static void SetOverrideContext(std::shared_ptr context); + + static std::shared_ptr GetOverrideContext(); + explicit Context(std::shared_ptr context); ~Context() override; + protected: + /// An Impeller context that takes precedent over the IO state context when + /// set. This is used to inject the context when running with the Impeller + /// playground, which doesn't instantiate an Engine instance. + static std::shared_ptr override_context_; + private: std::shared_ptr context_;