flutter_flutter/shell/platform/embedder/tests/embedder_config_builder.cc
Chinmay Garde bf81971f7a
Allow embedders to specify a render task runner description. (#13124)
Embedders may use this to specify a thread whose event loop is managed by them
instead of the engine. In addition, specifying the same task runner for both
the platform and render task runners allows embedders to effectively perform
GPU rendering operations on the platform thread.

To affect this change, the following non breaking changes to the API have been
made:

* The `FlutterCustomTaskRunners` struct now has a new field `render_task_runner`
  for the specification of a custom render task runner.
* The `FlutterTaskRunnerDescription` has a new field `identifier`. Embedders
  must supply a unique identifier for each task runner they specify. In
  addition, when describing multiple task runners that run their tasks on the
  same thread, their identifiers must match.
* The embedder may need to process tasks during `FlutterEngineRun` and
  `FlutterEngineShutdown`. However, the embedder doesn't have the Flutter engine
  handle before `FlutterEngineRun` and is supposed to relinquish handle right
  before `FlutterEngineShutdown`. Since the embedder needs the Flutter engine
  handle to service tasks on other threads while these calls are underway,
  there exist opportunities for deadlock. To work around this scenario, three
  new calls have been added that allow more deliberate management of the Flutter
  engine instance.
  * `FlutterEngineRun` can be replaced with `FlutterEngineInitialize` and
    `FlutterEngineRunInitialized`. The embedder can obtain a handle to the
    engine after the first call but the engine will not post any tasks to custom
    task runners specified by the embedder till the
    `FlutterEngineRunInitialized` call. Embedders can guard the Flutter engine
    handle behind a mutex for safe task runner interop.
  * `FlutterEngineShutdown` can be preceded by the `FlutterEngineDeinitialize`
    call. After this call the Flutter engine will no longer post tasks onto
    embedder managed task runners. It is still embedder responsibility to
    collect the Flutter engine handle via `FlutterEngineShutdown`.
* To maintain backwards compatibility with the old APIs, `FlutterEngineRun` is
  now just a convenience for `FlutterEngineInitialize` and
  `FlutterEngineRunInitilaized`. `FlutterEngineShutdown` now implicitly calls
  `FlutterEngineDeinitialize` as well. This allows existing users who don't care
  are custom task runner interop to keep using the old APIs.
* Adds complete test coverage for both old and new paths.

Fixes https://github.com/flutter/flutter/issues/42460
Prerequisite for https://github.com/flutter/flutter/issues/17579
2019-10-15 14:26:31 -07:00

269 lines
9.3 KiB
C++

// 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 "flutter/shell/platform/embedder/tests/embedder_config_builder.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace flutter {
namespace testing {
EmbedderConfigBuilder::EmbedderConfigBuilder(
EmbedderTestContext& context,
InitializationPreference preference)
: context_(context) {
project_args_.struct_size = sizeof(project_args_);
project_args_.shutdown_dart_vm_when_done = true;
project_args_.platform_message_callback =
[](const FlutterPlatformMessage* message, void* context) {
reinterpret_cast<EmbedderTestContext*>(context)
->PlatformMessageCallback(message);
};
custom_task_runners_.struct_size = sizeof(FlutterCustomTaskRunners);
opengl_renderer_config_.struct_size = sizeof(FlutterOpenGLRendererConfig);
opengl_renderer_config_.make_current = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLMakeCurrent();
};
opengl_renderer_config_.clear_current = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLClearCurrent();
};
opengl_renderer_config_.present = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)->GLPresent();
};
opengl_renderer_config_.fbo_callback = [](void* context) -> uint32_t {
return reinterpret_cast<EmbedderTestContext*>(context)->GLGetFramebuffer();
};
opengl_renderer_config_.make_resource_current = [](void* context) -> bool {
return reinterpret_cast<EmbedderTestContext*>(context)
->GLMakeResourceCurrent();
};
opengl_renderer_config_.gl_proc_resolver = [](void* context,
const char* name) -> void* {
return reinterpret_cast<EmbedderTestContext*>(context)->GLGetProcAddress(
name);
};
opengl_renderer_config_.fbo_reset_after_present = true;
opengl_renderer_config_.surface_transformation =
[](void* context) -> FlutterTransformation {
return reinterpret_cast<EmbedderTestContext*>(context)
->GetRootSurfaceTransformation();
};
software_renderer_config_.struct_size = sizeof(FlutterSoftwareRendererConfig);
software_renderer_config_.surface_present_callback =
[](void* context, const void* allocation, size_t row_bytes,
size_t height) {
auto image_info =
SkImageInfo::MakeN32Premul(SkISize::Make(row_bytes / 4, height));
SkBitmap bitmap;
if (!bitmap.installPixels(image_info, const_cast<void*>(allocation),
row_bytes)) {
FML_LOG(ERROR) << "Could not copy pixels for the software "
"composition from the engine.";
return false;
}
bitmap.setImmutable();
return reinterpret_cast<EmbedderTestContext*>(context)->SofwarePresent(
SkImage::MakeFromBitmap(bitmap));
};
// The first argument is treated as the executable name. Don't make tests have
// to do this manually.
AddCommandLineArgument("embedder_unittest");
if (preference == InitializationPreference::kInitialize) {
SetAssetsPath();
SetSnapshots();
SetIsolateCreateCallbackHook();
SetSemanticsCallbackHooks();
AddCommandLineArgument("--disable-observatory");
}
}
EmbedderConfigBuilder::~EmbedderConfigBuilder() = default;
FlutterProjectArgs& EmbedderConfigBuilder::GetProjectArgs() {
return project_args_;
}
void EmbedderConfigBuilder::SetSoftwareRendererConfig(SkISize surface_size) {
renderer_config_.type = FlutterRendererType::kSoftware;
renderer_config_.software = software_renderer_config_;
// TODO(chinmaygarde): The compositor still uses a GL surface for operation.
// Once this is no longer the case, don't setup the GL surface when using the
// software renderer config.
context_.SetupOpenGLSurface(surface_size);
}
void EmbedderConfigBuilder::SetOpenGLRendererConfig(SkISize surface_size) {
renderer_config_.type = FlutterRendererType::kOpenGL;
renderer_config_.open_gl = opengl_renderer_config_;
context_.SetupOpenGLSurface(surface_size);
}
void EmbedderConfigBuilder::SetAssetsPath() {
project_args_.assets_path = context_.GetAssetsPath().c_str();
}
void EmbedderConfigBuilder::SetSnapshots() {
if (auto mapping = context_.GetVMSnapshotData()) {
project_args_.vm_snapshot_data = mapping->GetMapping();
project_args_.vm_snapshot_data_size = mapping->GetSize();
}
if (auto mapping = context_.GetVMSnapshotInstructions()) {
project_args_.vm_snapshot_instructions = mapping->GetMapping();
project_args_.vm_snapshot_instructions_size = mapping->GetSize();
}
if (auto mapping = context_.GetIsolateSnapshotData()) {
project_args_.isolate_snapshot_data = mapping->GetMapping();
project_args_.isolate_snapshot_data_size = mapping->GetSize();
}
if (auto mapping = context_.GetIsolateSnapshotInstructions()) {
project_args_.isolate_snapshot_instructions = mapping->GetMapping();
project_args_.isolate_snapshot_instructions_size = mapping->GetSize();
}
}
void EmbedderConfigBuilder::SetIsolateCreateCallbackHook() {
project_args_.root_isolate_create_callback =
EmbedderTestContext::GetIsolateCreateCallbackHook();
}
void EmbedderConfigBuilder::SetSemanticsCallbackHooks() {
project_args_.update_semantics_node_callback =
EmbedderTestContext::GetUpdateSemanticsNodeCallbackHook();
project_args_.update_semantics_custom_action_callback =
EmbedderTestContext::GetUpdateSemanticsCustomActionCallbackHook();
}
void EmbedderConfigBuilder::SetDartEntrypoint(std::string entrypoint) {
if (entrypoint.size() == 0) {
return;
}
dart_entrypoint_ = std::move(entrypoint);
project_args_.custom_dart_entrypoint = dart_entrypoint_.c_str();
}
void EmbedderConfigBuilder::AddCommandLineArgument(std::string arg) {
if (arg.size() == 0) {
return;
}
command_line_arguments_.emplace_back(std::move(arg));
}
void EmbedderConfigBuilder::SetPlatformTaskRunner(
const FlutterTaskRunnerDescription* runner) {
if (runner == nullptr) {
return;
}
custom_task_runners_.platform_task_runner = runner;
project_args_.custom_task_runners = &custom_task_runners_;
}
void EmbedderConfigBuilder::SetRenderTaskRunner(
const FlutterTaskRunnerDescription* runner) {
if (runner == nullptr) {
return;
}
custom_task_runners_.render_task_runner = runner;
project_args_.custom_task_runners = &custom_task_runners_;
}
void EmbedderConfigBuilder::SetPlatformMessageCallback(
std::function<void(const FlutterPlatformMessage*)> callback) {
context_.SetPlatformMessageCallback(callback);
}
void EmbedderConfigBuilder::SetCompositor() {
context_.SetupCompositor();
auto& compositor = context_.GetCompositor();
compositor_.struct_size = sizeof(compositor_);
compositor_.user_data = &compositor;
compositor_.create_backing_store_callback =
[](const FlutterBackingStoreConfig* config, //
FlutterBackingStore* backing_store_out, //
void* user_data //
) {
return reinterpret_cast<EmbedderTestCompositor*>(user_data)
->CreateBackingStore(config, backing_store_out);
};
compositor_.collect_backing_store_callback =
[](const FlutterBackingStore* backing_store, //
void* user_data //
) {
return reinterpret_cast<EmbedderTestCompositor*>(user_data)
->CollectBackingStore(backing_store);
};
compositor_.present_layers_callback = [](const FlutterLayer** layers, //
size_t layers_count, //
void* user_data //
) {
return reinterpret_cast<EmbedderTestCompositor*>(user_data)->Present(
layers, //
layers_count //
);
};
project_args_.compositor = &compositor_;
}
FlutterCompositor& EmbedderConfigBuilder::GetCompositor() {
return compositor_;
}
UniqueEngine EmbedderConfigBuilder::LaunchEngine() const {
return SetupEngine(true);
}
UniqueEngine EmbedderConfigBuilder::InitializeEngine() const {
return SetupEngine(false);
}
UniqueEngine EmbedderConfigBuilder::SetupEngine(bool run) const {
FlutterEngine engine = nullptr;
FlutterProjectArgs project_args = project_args_;
std::vector<const char*> args;
args.reserve(command_line_arguments_.size());
for (const auto& arg : command_line_arguments_) {
args.push_back(arg.c_str());
}
if (args.size() > 0) {
project_args.command_line_argv = args.data();
project_args.command_line_argc = args.size();
} else {
// Clear it out in case this is not the first engine launch from the
// embedder config builder.
project_args.command_line_argv = nullptr;
project_args.command_line_argc = 0;
}
auto result =
run ? FlutterEngineRun(FLUTTER_ENGINE_VERSION, &renderer_config_,
&project_args, &context_, &engine)
: FlutterEngineInitialize(FLUTTER_ENGINE_VERSION, &renderer_config_,
&project_args, &context_, &engine);
if (result != kSuccess) {
return {};
}
return UniqueEngine{engine};
}
} // namespace testing
} // namespace flutter