Chinmay Garde 7bb5b9aea5
Wire up Swiftshader based OpenGL ES unit-tests on hosts. (#9264)
Currently, all our host unit-tests that have rendering concerns use the software backend because of OpenGL ES availability and stability issues on the various platforms where we run host tests. Unfortunately, entire subsystems are disabled (and not tested) when rendering with the software backend. This patch pulls in SwiftShader and via pending patches in the buildroot, configures the host unit-tests to optionally use OpenGL ES in a stable manner without relying on the OpenGL drivers being present (and functional).

I have wired up the embedder test fixture in this patch to use the SwiftShader based OpenGL ES driver. I will update the shell and runtime unittests in a subsequent patch as well. The on and offscreen surfaces are configured as 1x1 pbuffer surface because we should be able to write pixel tests using OpenGL directly wihout having to deal with surfaces.
2019-06-12 12:32:09 -07:00

1012 lines
37 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.
#define FML_USED_ON_EMBEDDER
#include "flutter/fml/build_config.h"
#include "flutter/fml/native_library.h"
#if OS_WIN
#define FLUTTER_EXPORT __declspec(dllexport)
#else // OS_WIN
#define FLUTTER_EXPORT __attribute__((visibility("default")))
#endif // OS_WIN
extern "C" {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Used for debugging dart:* sources.
extern const uint8_t kPlatformStrongDill[];
extern const intptr_t kPlatformStrongDillSize;
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
}
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/command_line.h"
#include "flutter/fml/file.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/common/persistent_cache.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/switches.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/embedder_engine.h"
#include "flutter/shell/platform/embedder/embedder_safe_access.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/shell/platform/embedder/platform_view_embedder.h"
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
const char* name,
const char* function,
const char* file,
int line) {
FML_LOG(ERROR) << "Returning error '" << name << "' (" << code
<< ") from Flutter Embedder API call to '" << function
<< "'. Origin: " << file << ":" << line;
return code;
}
#define LOG_EMBEDDER_ERROR(code) \
LogEmbedderError(code, #code, __FUNCTION__, __FILE__, __LINE__)
static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kOpenGL) {
return false;
}
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
if (SAFE_ACCESS(open_gl_config, make_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, clear_current, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, present, nullptr) == nullptr ||
SAFE_ACCESS(open_gl_config, fbo_callback, nullptr) == nullptr) {
return false;
}
return true;
}
static bool IsSoftwareRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kSoftware) {
return false;
}
const FlutterSoftwareRendererConfig* software_config = &config->software;
if (SAFE_ACCESS(software_config, surface_present_callback, nullptr) ==
nullptr) {
return false;
}
return true;
}
static bool IsRendererValid(const FlutterRendererConfig* config) {
if (config == nullptr) {
return false;
}
switch (config->type) {
case kOpenGL:
return IsOpenGLRendererConfigValid(config);
case kSoftware:
return IsSoftwareRendererConfigValid(config);
default:
return false;
}
return false;
}
#if OS_LINUX || OS_WIN
static void* DefaultGLProcResolver(const char* name) {
static fml::RefPtr<fml::NativeLibrary> proc_library =
#if OS_LINUX
fml::NativeLibrary::CreateForCurrentProcess();
#elif OS_WIN // OS_LINUX
fml::NativeLibrary::Create("opengl32.dll");
#endif // OS_WIN
return static_cast<void*>(
const_cast<uint8_t*>(proc_library->ResolveSymbol(name)));
}
#endif // OS_LINUX || OS_WIN
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferOpenGLPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
flutter::PlatformViewEmbedder::PlatformDispatchTable
platform_dispatch_table) {
if (config->type != kOpenGL) {
return nullptr;
}
auto gl_make_current = [ptr = config->open_gl.make_current,
user_data]() -> bool { return ptr(user_data); };
auto gl_clear_current = [ptr = config->open_gl.clear_current,
user_data]() -> bool { return ptr(user_data); };
auto gl_present = [ptr = config->open_gl.present, user_data]() -> bool {
return ptr(user_data);
};
auto gl_fbo_callback = [ptr = config->open_gl.fbo_callback,
user_data]() -> intptr_t { return ptr(user_data); };
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
std::function<bool()> gl_make_resource_current_callback = nullptr;
if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) {
gl_make_resource_current_callback =
[ptr = config->open_gl.make_resource_current, user_data]() {
return ptr(user_data);
};
}
std::function<SkMatrix(void)> gl_surface_transformation_callback = nullptr;
if (SAFE_ACCESS(open_gl_config, surface_transformation, nullptr) != nullptr) {
gl_surface_transformation_callback =
[ptr = config->open_gl.surface_transformation, user_data]() {
FlutterTransformation transformation = ptr(user_data);
return SkMatrix::MakeAll(transformation.scaleX, //
transformation.skewX, //
transformation.transX, //
transformation.skewY, //
transformation.scaleY, //
transformation.transY, //
transformation.pers0, //
transformation.pers1, //
transformation.pers2 //
);
};
}
flutter::GPUSurfaceGLDelegate::GLProcResolver gl_proc_resolver = nullptr;
if (SAFE_ACCESS(open_gl_config, gl_proc_resolver, nullptr) != nullptr) {
gl_proc_resolver = [ptr = config->open_gl.gl_proc_resolver,
user_data](const char* gl_proc_name) {
return ptr(user_data, gl_proc_name);
};
} else {
#if OS_LINUX || OS_WIN
gl_proc_resolver = DefaultGLProcResolver;
#endif
}
bool fbo_reset_after_present =
SAFE_ACCESS(open_gl_config, fbo_reset_after_present, false);
flutter::EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table = {
gl_make_current, // gl_make_current_callback
gl_clear_current, // gl_clear_current_callback
gl_present, // gl_present_callback
gl_fbo_callback, // gl_fbo_callback
gl_make_resource_current_callback, // gl_make_resource_current_callback
gl_surface_transformation_callback, // gl_surface_transformation_callback
gl_proc_resolver, // gl_proc_resolver
};
return [gl_dispatch_table, fbo_reset_after_present,
platform_dispatch_table](flutter::Shell& shell) {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
gl_dispatch_table, // embedder GL dispatch table
fbo_reset_after_present, // fbo reset after present
platform_dispatch_table // embedder platform dispatch table
);
};
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferSoftwarePlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
flutter::PlatformViewEmbedder::PlatformDispatchTable
platform_dispatch_table) {
if (config->type != kSoftware) {
return nullptr;
}
auto software_present_backing_store =
[ptr = config->software.surface_present_callback, user_data](
const void* allocation, size_t row_bytes, size_t height) -> bool {
return ptr(user_data, allocation, row_bytes, height);
};
flutter::EmbedderSurfaceSoftware::SoftwareDispatchTable
software_dispatch_table = {
software_present_backing_store, // required
};
return [software_dispatch_table,
platform_dispatch_table](flutter::Shell& shell) {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
software_dispatch_table, // software dispatch table
platform_dispatch_table // platform dispatch table
);
};
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
flutter::PlatformViewEmbedder::PlatformDispatchTable
platform_dispatch_table) {
if (config == nullptr) {
return nullptr;
}
switch (config->type) {
case kOpenGL:
return InferOpenGLPlatformViewCreationCallback(config, user_data,
platform_dispatch_table);
case kSoftware:
return InferSoftwarePlatformViewCreationCallback(config, user_data,
platform_dispatch_table);
default:
return nullptr;
}
return nullptr;
}
struct _FlutterPlatformMessageResponseHandle {
fml::RefPtr<flutter::PlatformMessage> message;
};
void PopulateSnapshotMappingCallbacks(const FlutterProjectArgs* args,
flutter::Settings& settings) {
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
return [mapping, size]() {
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
};
};
if (flutter::DartVM::IsRunningPrecompiledCode()) {
if (SAFE_ACCESS(args, vm_snapshot_data_size, 0) != 0 &&
SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) {
settings.vm_snapshot_data = make_mapping_callback(
args->vm_snapshot_data, args->vm_snapshot_data_size);
}
if (SAFE_ACCESS(args, vm_snapshot_instructions_size, 0) != 0 &&
SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) {
settings.vm_snapshot_instr = make_mapping_callback(
args->vm_snapshot_instructions, args->vm_snapshot_instructions_size);
}
if (SAFE_ACCESS(args, isolate_snapshot_data_size, 0) != 0 &&
SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) {
settings.isolate_snapshot_data = make_mapping_callback(
args->isolate_snapshot_data, args->isolate_snapshot_data_size);
}
if (SAFE_ACCESS(args, isolate_snapshot_instructions_size, 0) != 0 &&
SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) {
settings.isolate_snapshot_instr =
make_mapping_callback(args->isolate_snapshot_instructions,
args->isolate_snapshot_instructions_size);
}
}
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
}
FlutterEngineResult FlutterEngineRun(size_t version,
const FlutterRendererConfig* config,
const FlutterProjectArgs* args,
void* user_data,
FlutterEngine* engine_out) {
// Step 0: Figure out arguments for shell creation.
if (version != FLUTTER_ENGINE_VERSION) {
return LOG_EMBEDDER_ERROR(kInvalidLibraryVersion);
}
if (engine_out == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (args == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (SAFE_ACCESS(args, main_path__unused__, nullptr) != nullptr) {
FML_LOG(WARNING)
<< "FlutterProjectArgs.main_path is deprecated and should be set null.";
}
if (SAFE_ACCESS(args, packages_path__unused__, nullptr) != nullptr) {
FML_LOG(WARNING) << "FlutterProjectArgs.packages_path is deprecated and "
"should be set null.";
}
if (!IsRendererValid(config)) {
FML_LOG(WARNING) << "Invalid renderer config.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
std::string icu_data_path;
if (SAFE_ACCESS(args, icu_data_path, nullptr) != nullptr) {
icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr);
}
if (SAFE_ACCESS(args, persistent_cache_path, nullptr) != nullptr) {
std::string persistent_cache_path =
SAFE_ACCESS(args, persistent_cache_path, nullptr);
flutter::PersistentCache::SetCacheDirectoryPath(persistent_cache_path);
}
if (SAFE_ACCESS(args, is_persistent_cache_read_only, false)) {
flutter::PersistentCache::gIsReadOnly = true;
}
fml::CommandLine command_line;
if (SAFE_ACCESS(args, command_line_argc, 0) != 0 &&
SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) {
command_line = fml::CommandLineFromArgcArgv(
SAFE_ACCESS(args, command_line_argc, 0),
SAFE_ACCESS(args, command_line_argv, nullptr));
}
flutter::Settings settings = flutter::SettingsFromCommandLine(command_line);
PopulateSnapshotMappingCallbacks(args, settings);
settings.icu_data_path = icu_data_path;
settings.assets_path = args->assets_path;
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
// Verify the assets path contains Dart 2 kernel assets.
const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin";
std::string application_kernel_path = fml::paths::JoinPaths(
{settings.assets_path, kApplicationKernelSnapshotFileName});
if (!fml::IsFile(application_kernel_path)) {
FML_LOG(ERROR) << "Not running in AOT mode but could not resolve the "
"kernel binary.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
settings.application_kernel_asset = kApplicationKernelSnapshotFileName;
}
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
if (SAFE_ACCESS(args, root_isolate_create_callback, nullptr) != nullptr) {
VoidCallback callback =
SAFE_ACCESS(args, root_isolate_create_callback, nullptr);
settings.root_isolate_create_callback = [callback, user_data]() {
callback(user_data);
};
}
flutter::PlatformViewEmbedder::UpdateSemanticsNodesCallback
update_semantics_nodes_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) {
update_semantics_nodes_callback =
[ptr = args->update_semantics_node_callback,
user_data](flutter::SemanticsNodeUpdates update) {
for (const auto& value : update) {
const auto& node = value.second;
SkMatrix transform = static_cast<SkMatrix>(node.transform);
FlutterTransformation flutter_transform{
transform.get(SkMatrix::kMScaleX),
transform.get(SkMatrix::kMSkewX),
transform.get(SkMatrix::kMTransX),
transform.get(SkMatrix::kMSkewY),
transform.get(SkMatrix::kMScaleY),
transform.get(SkMatrix::kMTransY),
transform.get(SkMatrix::kMPersp0),
transform.get(SkMatrix::kMPersp1),
transform.get(SkMatrix::kMPersp2)};
const FlutterSemanticsNode embedder_node{
sizeof(FlutterSemanticsNode),
node.id,
static_cast<FlutterSemanticsFlag>(node.flags),
static_cast<FlutterSemanticsAction>(node.actions),
node.textSelectionBase,
node.textSelectionExtent,
node.scrollChildren,
node.scrollIndex,
node.scrollPosition,
node.scrollExtentMax,
node.scrollExtentMin,
node.elevation,
node.thickness,
node.label.c_str(),
node.hint.c_str(),
node.value.c_str(),
node.increasedValue.c_str(),
node.decreasedValue.c_str(),
static_cast<FlutterTextDirection>(node.textDirection),
FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight,
node.rect.fBottom},
flutter_transform,
node.childrenInTraversalOrder.size(),
&node.childrenInTraversalOrder[0],
&node.childrenInHitTestOrder[0],
node.customAccessibilityActions.size(),
&node.customAccessibilityActions[0],
};
ptr(&embedder_node, user_data);
}
const FlutterSemanticsNode batch_end_sentinel = {
sizeof(FlutterSemanticsNode),
kFlutterSemanticsNodeIdBatchEnd,
};
ptr(&batch_end_sentinel, user_data);
};
}
flutter::PlatformViewEmbedder::UpdateSemanticsCustomActionsCallback
update_semantics_custom_actions_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr) {
update_semantics_custom_actions_callback =
[ptr = args->update_semantics_custom_action_callback,
user_data](flutter::CustomAccessibilityActionUpdates actions) {
for (const auto& value : actions) {
const auto& action = value.second;
const FlutterSemanticsCustomAction embedder_action = {
sizeof(FlutterSemanticsCustomAction),
action.id,
static_cast<FlutterSemanticsAction>(action.overrideId),
action.label.c_str(),
action.hint.c_str(),
};
ptr(&embedder_action, user_data);
}
const FlutterSemanticsCustomAction batch_end_sentinel = {
sizeof(FlutterSemanticsCustomAction),
kFlutterSemanticsCustomActionIdBatchEnd,
};
ptr(&batch_end_sentinel, user_data);
};
}
flutter::PlatformViewEmbedder::PlatformMessageResponseCallback
platform_message_response_callback = nullptr;
if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) {
platform_message_response_callback =
[ptr = args->platform_message_callback,
user_data](fml::RefPtr<flutter::PlatformMessage> message) {
auto handle = new FlutterPlatformMessageResponseHandle();
const FlutterPlatformMessage incoming_message = {
sizeof(FlutterPlatformMessage), // struct_size
message->channel().c_str(), // channel
message->data().data(), // message
message->data().size(), // message_size
handle, // response_handle
};
handle->message = std::move(message);
return ptr(&incoming_message, user_data);
};
}
flutter::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr;
if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) {
vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) {
return ptr(user_data, baton);
};
}
flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table =
{
update_semantics_nodes_callback, //
update_semantics_custom_actions_callback, //
platform_message_response_callback, //
vsync_callback, //
};
auto on_create_platform_view = InferPlatformViewCreationCallback(
config, user_data, platform_dispatch_table);
if (!on_create_platform_view) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
[](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell,
shell.GetTaskRunners());
};
// TODO(chinmaygarde): This is the wrong spot for this. It belongs in the
// platform view jump table.
flutter::EmbedderExternalTextureGL::ExternalTextureCallback
external_texture_callback;
if (config->type == kOpenGL) {
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback,
nullptr) != nullptr) {
external_texture_callback =
[ptr = open_gl_config->gl_external_texture_frame_callback, user_data](
int64_t texture_identifier, GrContext* context,
const SkISize& size) -> sk_sp<SkImage> {
FlutterOpenGLTexture texture = {};
if (!ptr(user_data, texture_identifier, size.width(), size.height(),
&texture)) {
return nullptr;
}
GrGLTextureInfo gr_texture_info = {texture.target, texture.name,
texture.format};
GrBackendTexture gr_backend_texture(size.width(), size.height(),
GrMipMapped::kNo, gr_texture_info);
SkImage::TextureReleaseProc release_proc = texture.destruction_callback;
auto image = SkImage::MakeFromTexture(
context, // context
gr_backend_texture, // texture handle
kTopLeft_GrSurfaceOrigin, // origin
kRGBA_8888_SkColorType, // color type
kPremul_SkAlphaType, // alpha type
nullptr, // colorspace
release_proc, // texture release proc
texture.user_data // texture release context
);
if (!image) {
// In case Skia rejects the image, call the release proc so that
// embedders can perform collection of intermediates.
if (release_proc) {
release_proc(texture.user_data);
}
FML_LOG(ERROR) << "Could not create external texture.";
return nullptr;
}
return image;
};
}
}
auto thread_host =
flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
SAFE_ACCESS(args, custom_task_runners, nullptr));
if (!thread_host || !thread_host->IsValid()) {
FML_LOG(ERROR) << "Could not setup or infer thread configuration to run "
"the Flutter engine on.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto task_runners = thread_host->GetTaskRunners();
if (!task_runners.IsValid()) {
FML_LOG(ERROR) << "Task runner configuration specified is invalid.";
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Step 1: Create the engine.
auto embedder_engine =
std::make_unique<flutter::EmbedderEngine>(std::move(thread_host), //
std::move(task_runners), //
settings, //
on_create_platform_view, //
on_create_rasterizer, //
external_texture_callback //
);
if (!embedder_engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Step 2: Setup the rendering surface.
if (!embedder_engine->NotifyCreated()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Step 3: Run the engine.
auto run_configuration =
flutter::RunConfiguration::InferFromSettings(settings);
if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) {
auto dart_entrypoint = std::string{args->custom_dart_entrypoint};
if (dart_entrypoint.size() != 0) {
run_configuration.SetEntrypoint(std::move(dart_entrypoint));
}
}
run_configuration.AddAssetResolver(
std::make_unique<flutter::DirectoryAssetBundle>(
fml::Duplicate(settings.assets_dir)));
run_configuration.AddAssetResolver(
std::make_unique<flutter::DirectoryAssetBundle>(fml::OpenDirectory(
settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
if (!run_configuration.IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (!embedder_engine->Run(std::move(run_configuration))) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Finally! Release the ownership of the embedder engine to the caller.
*engine_out = reinterpret_cast<FlutterEngine>(embedder_engine.release());
return kSuccess;
}
FlutterEngineResult FlutterEngineShutdown(FlutterEngine engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
embedder_engine->NotifyDestroyed();
delete embedder_engine;
return kSuccess;
}
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
FlutterEngine engine,
const FlutterWindowMetricsEvent* flutter_metrics) {
if (engine == nullptr || flutter_metrics == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
flutter::ViewportMetrics metrics;
metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0);
metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0);
metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0);
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics(
std::move(metrics))
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments);
}
// Returns the flutter::PointerData::Change for the given FlutterPointerPhase.
inline flutter::PointerData::Change ToPointerDataChange(
FlutterPointerPhase phase) {
switch (phase) {
case kCancel:
return flutter::PointerData::Change::kCancel;
case kUp:
return flutter::PointerData::Change::kUp;
case kDown:
return flutter::PointerData::Change::kDown;
case kMove:
return flutter::PointerData::Change::kMove;
case kAdd:
return flutter::PointerData::Change::kAdd;
case kRemove:
return flutter::PointerData::Change::kRemove;
case kHover:
return flutter::PointerData::Change::kHover;
}
return flutter::PointerData::Change::kCancel;
}
// Returns the flutter::PointerData::DeviceKind for the given
// FlutterPointerDeviceKind.
inline flutter::PointerData::DeviceKind ToPointerDataKind(
FlutterPointerDeviceKind device_kind) {
switch (device_kind) {
case kFlutterPointerDeviceKindMouse:
return flutter::PointerData::DeviceKind::kMouse;
case kFlutterPointerDeviceKindTouch:
return flutter::PointerData::DeviceKind::kTouch;
}
return flutter::PointerData::DeviceKind::kMouse;
}
// Returns the flutter::PointerData::SignalKind for the given
// FlutterPointerSignaKind.
inline flutter::PointerData::SignalKind ToPointerDataSignalKind(
FlutterPointerSignalKind kind) {
switch (kind) {
case kFlutterPointerSignalKindNone:
return flutter::PointerData::SignalKind::kNone;
case kFlutterPointerSignalKindScroll:
return flutter::PointerData::SignalKind::kScroll;
}
return flutter::PointerData::SignalKind::kNone;
}
// Returns the buttons to synthesize for a PointerData from a
// FlutterPointerEvent with no type or buttons set.
inline int64_t PointerDataButtonsForLegacyEvent(
flutter::PointerData::Change change) {
switch (change) {
case flutter::PointerData::Change::kDown:
case flutter::PointerData::Change::kMove:
// These kinds of change must have a non-zero `buttons`, otherwise gesture
// recognizers will ignore these events.
return flutter::kPointerButtonMousePrimary;
case flutter::PointerData::Change::kCancel:
case flutter::PointerData::Change::kAdd:
case flutter::PointerData::Change::kRemove:
case flutter::PointerData::Change::kHover:
case flutter::PointerData::Change::kUp:
return 0;
}
return 0;
}
FlutterEngineResult FlutterEngineSendPointerEvent(
FlutterEngine engine,
const FlutterPointerEvent* pointers,
size_t events_count) {
if (engine == nullptr || pointers == nullptr || events_count == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto packet = std::make_unique<flutter::PointerDataPacket>(events_count);
const FlutterPointerEvent* current = pointers;
for (size_t i = 0; i < events_count; ++i) {
flutter::PointerData pointer_data;
pointer_data.Clear();
pointer_data.time_stamp = SAFE_ACCESS(current, timestamp, 0);
pointer_data.change = ToPointerDataChange(
SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel));
pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0);
pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0);
pointer_data.device = SAFE_ACCESS(current, device, 0);
pointer_data.signal_kind = ToPointerDataSignalKind(
SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone));
pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0);
pointer_data.scroll_delta_y = SAFE_ACCESS(current, scroll_delta_y, 0.0);
FlutterPointerDeviceKind device_kind = SAFE_ACCESS(current, device_kind, 0);
// For backwards compatibility with embedders written before the device kind
// and buttons were exposed, if the device kind is not set treat it as a
// mouse, with a synthesized primary button state based on the phase.
if (device_kind == 0) {
pointer_data.kind = flutter::PointerData::DeviceKind::kMouse;
pointer_data.buttons =
PointerDataButtonsForLegacyEvent(pointer_data.change);
} else {
pointer_data.kind = ToPointerDataKind(device_kind);
if (pointer_data.kind == flutter::PointerData::DeviceKind::kTouch) {
// For touch events, set the button internally rather than requiring
// it at the API level, since it's a confusing construction to expose.
if (pointer_data.change == flutter::PointerData::Change::kDown ||
pointer_data.change == flutter::PointerData::Change::kMove) {
pointer_data.buttons = flutter::kPointerButtonTouchContact;
}
} else {
// Buttons use the same mask values, so pass them through directly.
pointer_data.buttons = SAFE_ACCESS(current, buttons, 0);
}
}
packet->SetPointerData(i, pointer_data);
current = reinterpret_cast<const FlutterPointerEvent*>(
reinterpret_cast<const uint8_t*>(current) + current->struct_size);
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->DispatchPointerDataPacket(std::move(packet))
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments);
}
FlutterEngineResult FlutterEngineSendPlatformMessage(
FlutterEngine engine,
const FlutterPlatformMessage* flutter_message) {
if (engine == nullptr || flutter_message == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr ||
SAFE_ACCESS(flutter_message, message, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto message = fml::MakeRefCounted<flutter::PlatformMessage>(
flutter_message->channel,
std::vector<uint8_t>(
flutter_message->message,
flutter_message->message + flutter_message->message_size),
nullptr);
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SendPlatformMessage(std::move(message))
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments);
}
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
FlutterEngine engine,
const FlutterPlatformMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
if (data_length != 0 && data == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto response = handle->message->response();
if (data_length == 0) {
response->CompleteEmpty();
} else {
response->Complete(std::make_unique<fml::DataMapping>(
std::vector<uint8_t>({data, data + data_length})));
}
delete handle;
return kSuccess;
}
FlutterEngineResult __FlutterEngineFlushPendingTasksNow() {
fml::MessageLoop::GetCurrent().RunExpiredTasksNow();
return kSuccess;
}
FlutterEngineResult FlutterEngineRegisterExternalTexture(
FlutterEngine engine,
int64_t texture_identifier) {
if (engine == nullptr || texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->RegisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUnregisterExternalTexture(
FlutterEngine engine,
int64_t texture_identifier) {
if (engine == nullptr || texture_identifier == 0) {
return kInvalidArguments;
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->UnregisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable(
FlutterEngine engine,
int64_t texture_identifier) {
if (engine == nullptr || texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->MarkTextureFrameAvailable(texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(FlutterEngine engine,
bool enabled) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetSemanticsEnabled(
enabled)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures(
FlutterEngine engine,
FlutterAccessibilityFeature flags) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SetAccessibilityFeatures(flags)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineDispatchSemanticsAction(
FlutterEngine engine,
uint64_t id,
FlutterSemanticsAction action,
const uint8_t* data,
size_t data_length) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto engine_action = static_cast<flutter::SemanticsAction>(action);
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->DispatchSemanticsAction(
id, engine_action,
std::vector<uint8_t>({data, data + data_length}))) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine,
intptr_t baton,
uint64_t frame_start_time_nanos,
uint64_t frame_target_time_nanos) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
TRACE_EVENT0("flutter", "FlutterEngineOnVsync");
auto start_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frame_start_time_nanos));
auto target_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frame_target_time_nanos));
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->OnVsyncEvent(
baton, start_time, target_time)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
return kSuccess;
}
void FlutterEngineTraceEventDurationBegin(const char* name) {
fml::tracing::TraceEvent0("flutter", name);
}
void FlutterEngineTraceEventDurationEnd(const char* name) {
fml::tracing::TraceEventEnd(name);
}
void FlutterEngineTraceEventInstant(const char* name) {
fml::tracing::TraceEventInstant0("flutter", name);
}
FlutterEngineResult FlutterEnginePostRenderThreadTask(FlutterEngine engine,
VoidCallback callback,
void* baton) {
if (engine == nullptr || callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
auto task = [callback, baton]() { callback(baton); };
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->PostRenderThreadTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency);
}
uint64_t FlutterEngineGetCurrentTime() {
return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
}
FlutterEngineResult FlutterEngineRunTask(FlutterEngine engine,
const FlutterTask* task) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments);
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->RunTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments);
}