blob: b165ca6c962749641a2a19327d2001bc851dff50 [file] [log] [blame]
// 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
#define RAPIDJSON_HAS_STDSTRING 1
#include <cstring>
#include <iostream>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "flutter/fml/build_config.h"
#include "flutter/fml/closure.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/thread.h"
#include "third_party/dart/runtime/bin/elf_loader.h"
#include "third_party/dart/runtime/include/dart_native_api.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GpuTypes.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#if !defined(FLUTTER_NO_EXPORT)
#if FML_OS_WIN
#define FLUTTER_EXPORT __declspec(dllexport)
#else // FML_OS_WIN
#define FLUTTER_EXPORT __attribute__((visibility("default")))
#endif // FML_OS_WIN
#endif // !FLUTTER_NO_EXPORT
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/graphics/persistent_cache.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/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_external_texture_resolver.h"
#include "flutter/shell/platform/embedder/embedder_platform_message_response.h"
#include "flutter/shell/platform/embedder/embedder_render_target.h"
#include "flutter/shell/platform/embedder/embedder_render_target_skia.h"
#include "flutter/shell/platform/embedder/embedder_semantics_update.h"
#include "flutter/shell/platform/embedder/embedder_struct_macros.h"
#include "flutter/shell/platform/embedder/embedder_task_runner.h"
#include "flutter/shell/platform/embedder/embedder_thread_host.h"
#include "flutter/shell/platform/embedder/pixel_formats.h"
#include "flutter/shell/platform/embedder/platform_view_embedder.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/writer.h"
// Note: the IMPELLER_SUPPORTS_RENDERING may be defined even when the
// embedder/BUILD.gn variable impeller_supports_rendering is disabled.
#ifdef SHELL_ENABLE_GL
#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
#ifdef IMPELLER_SUPPORTS_RENDERING
#include "flutter/shell/platform/embedder/embedder_render_target_impeller.h" // nogncheck
#include "flutter/shell/platform/embedder/embedder_surface_gl_impeller.h" // nogncheck
#include "impeller/core/texture.h" // nogncheck
#include "impeller/renderer/backend/gles/context_gles.h" // nogncheck
#include "impeller/renderer/backend/gles/texture_gles.h" // nogncheck
#include "impeller/renderer/context.h" // nogncheck
#include "impeller/renderer/render_target.h" // nogncheck
#endif // IMPELLER_SUPPORTS_RENDERING
#endif // SHELL_ENABLE_GL
#ifdef SHELL_ENABLE_METAL
#include "flutter/shell/platform/embedder/embedder_surface_metal.h"
#include "third_party/skia/include/ports/SkCFObject.h"
#ifdef IMPELLER_SUPPORTS_RENDERING
#include "flutter/shell/platform/embedder/embedder_render_target_impeller.h" // nogncheck
#include "flutter/shell/platform/embedder/embedder_surface_metal_impeller.h" // nogncheck
#include "impeller/core/texture.h" // nogncheck
#include "impeller/renderer/backend/metal/texture_wrapper_mtl.h" // nogncheck
#include "impeller/renderer/render_target.h" // nogncheck
#endif // IMPELLER_SUPPORTS_RENDERING
#endif // SHELL_ENABLE_METAL
#ifdef SHELL_ENABLE_VULKAN
#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
#endif // SHELL_ENABLE_VULKAN
const int32_t kFlutterSemanticsNodeIdBatchEnd = -1;
const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1;
static constexpr FlutterViewId kFlutterImplicitViewId = 0;
// A message channel to send platform-independent FlutterKeyData to the
// framework.
//
// This should be kept in sync with the following variables:
//
// - lib/ui/platform_dispatcher.dart, _kFlutterKeyDataChannel
// - shell/platform/darwin/ios/framework/Source/FlutterEngine.mm,
// FlutterKeyDataChannel
// - io/flutter/embedding/android/KeyData.java,
// CHANNEL
//
// Not to be confused with "flutter/keyevent", which is used to send raw
// key event data in a platform-dependent format.
//
// ## Format
//
// Send: KeyDataPacket.data().
//
// Expected reply: Whether the event is handled. Exactly 1 byte long, with value
// 1 for handled, and 0 for not. Malformed value is considered false.
const char* kFlutterKeyDataChannel = "flutter/keydata";
static FlutterEngineResult LogEmbedderError(FlutterEngineResult code,
const char* reason,
const char* code_name,
const char* function,
const char* file,
int line) {
#if FML_OS_WIN
constexpr char kSeparator = '\\';
#else
constexpr char kSeparator = '/';
#endif
const auto file_base =
(::strrchr(file, kSeparator) ? strrchr(file, kSeparator) + 1 : file);
char error[256] = {};
snprintf(error, (sizeof(error) / sizeof(char)),
"%s (%d): '%s' returned '%s'. %s", file_base, line, function,
code_name, reason);
std::cerr << error << std::endl;
return code;
}
#define LOG_EMBEDDER_ERROR(code, reason) \
LogEmbedderError(code, reason, #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_EXISTS(open_gl_config, make_current) ||
!SAFE_EXISTS(open_gl_config, clear_current) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, fbo_callback,
fbo_with_frame_info_callback) ||
!SAFE_EXISTS_ONE_OF(open_gl_config, present, present_with_info)) {
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 IsMetalRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kMetal) {
return false;
}
const FlutterMetalRendererConfig* metal_config = &config->metal;
bool device = SAFE_ACCESS(metal_config, device, nullptr);
bool command_queue =
SAFE_ACCESS(metal_config, present_command_queue, nullptr);
bool present = SAFE_ACCESS(metal_config, present_drawable_callback, nullptr);
bool get_texture =
SAFE_ACCESS(metal_config, get_next_drawable_callback, nullptr);
return device && command_queue && present && get_texture;
}
static bool IsVulkanRendererConfigValid(const FlutterRendererConfig* config) {
if (config->type != kVulkan) {
return false;
}
const FlutterVulkanRendererConfig* vulkan_config = &config->vulkan;
if (!SAFE_EXISTS(vulkan_config, instance) ||
!SAFE_EXISTS(vulkan_config, physical_device) ||
!SAFE_EXISTS(vulkan_config, device) ||
!SAFE_EXISTS(vulkan_config, queue) ||
!SAFE_EXISTS(vulkan_config, get_instance_proc_address_callback) ||
!SAFE_EXISTS(vulkan_config, get_next_image_callback) ||
!SAFE_EXISTS(vulkan_config, present_image_callback)) {
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);
case kMetal:
return IsMetalRendererConfigValid(config);
case kVulkan:
return IsVulkanRendererConfigValid(config);
default:
return false;
}
return false;
}
#if FML_OS_LINUX || FML_OS_WIN
static void* DefaultGLProcResolver(const char* name) {
static fml::RefPtr<fml::NativeLibrary> proc_library =
#if FML_OS_LINUX
fml::NativeLibrary::CreateForCurrentProcess();
#elif FML_OS_WIN // FML_OS_LINUX
fml::NativeLibrary::Create("opengl32.dll");
#endif // FML_OS_WIN
return static_cast<void*>(
const_cast<uint8_t*>(proc_library->ResolveSymbol(name)));
}
#endif // FML_OS_LINUX || FML_OS_WIN
#ifdef SHELL_ENABLE_GL
// Auxiliary function used to translate rectangles of type SkIRect to
// FlutterRect.
static FlutterRect SkIRectToFlutterRect(const SkIRect sk_rect) {
FlutterRect flutter_rect = {static_cast<double>(sk_rect.fLeft),
static_cast<double>(sk_rect.fTop),
static_cast<double>(sk_rect.fRight),
static_cast<double>(sk_rect.fBottom)};
return flutter_rect;
}
// Auxiliary function used to translate rectangles of type FlutterRect to
// SkIRect.
static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) {
SkIRect rect = {static_cast<int32_t>(flutter_rect.left),
static_cast<int32_t>(flutter_rect.top),
static_cast<int32_t>(flutter_rect.right),
static_cast<int32_t>(flutter_rect.bottom)};
return rect;
}
#endif
static inline flutter::Shell::CreateCallback<flutter::PlatformView>
InferOpenGLPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder,
bool enable_impeller) {
#ifdef SHELL_ENABLE_GL
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 =
[present = config->open_gl.present,
present_with_info = config->open_gl.present_with_info,
user_data](flutter::GLPresentInfo gl_present_info) -> bool {
if (present) {
return present(user_data);
} else {
// Format the frame and buffer damages accordingly. Note that, since the
// current compute damage algorithm only returns one rectangle for damage
// we are assuming the number of rectangles provided in frame and buffer
// damage are always 1. Once the function that computes damage implements
// support for multiple damage rectangles, GLPresentInfo should also
// contain the number of damage rectangles.
const size_t num_rects = 1;
std::array<FlutterRect, num_rects> frame_damage_rect = {
SkIRectToFlutterRect(*(gl_present_info.frame_damage))};
std::array<FlutterRect, num_rects> buffer_damage_rect = {
SkIRectToFlutterRect(*(gl_present_info.buffer_damage))};
FlutterDamage frame_damage{
.struct_size = sizeof(FlutterDamage),
.num_rects = frame_damage_rect.size(),
.damage = frame_damage_rect.data(),
};
FlutterDamage buffer_damage{
.struct_size = sizeof(FlutterDamage),
.num_rects = buffer_damage_rect.size(),
.damage = buffer_damage_rect.data(),
};
// Construct the present information concerning the frame being rendered.
FlutterPresentInfo present_info = {
.struct_size = sizeof(FlutterPresentInfo),
.fbo_id = gl_present_info.fbo_id,
.frame_damage = frame_damage,
.buffer_damage = buffer_damage,
};
return present_with_info(user_data, &present_info);
}
};
auto gl_fbo_callback =
[fbo_callback = config->open_gl.fbo_callback,
fbo_with_frame_info_callback =
config->open_gl.fbo_with_frame_info_callback,
user_data](flutter::GLFrameInfo gl_frame_info) -> intptr_t {
if (fbo_callback) {
return fbo_callback(user_data);
} else {
FlutterFrameInfo frame_info = {};
frame_info.struct_size = sizeof(FlutterFrameInfo);
frame_info.size = {gl_frame_info.width, gl_frame_info.height};
return fbo_with_frame_info_callback(user_data, &frame_info);
}
};
auto gl_populate_existing_damage =
[populate_existing_damage = config->open_gl.populate_existing_damage,
user_data](intptr_t id) -> flutter::GLFBOInfo {
// If no populate_existing_damage was provided, disable partial
// repaint.
if (!populate_existing_damage) {
return flutter::GLFBOInfo{
.fbo_id = static_cast<uint32_t>(id),
.existing_damage = std::nullopt,
};
}
// Given the FBO's ID, get its existing damage.
FlutterDamage existing_damage;
populate_existing_damage(user_data, id, &existing_damage);
std::optional<SkIRect> existing_damage_rect = std::nullopt;
// Verify that at least one damage rectangle was provided.
if (existing_damage.num_rects <= 0 || existing_damage.damage == nullptr) {
FML_LOG(INFO) << "No damage was provided. Forcing full repaint.";
} else {
existing_damage_rect = SkIRect::MakeEmpty();
for (size_t i = 0; i < existing_damage.num_rects; i++) {
existing_damage_rect->join(
FlutterRectToSkIRect(existing_damage.damage[i]));
}
}
// Pass the information about this FBO to the rendering backend.
return flutter::GLFBOInfo{
.fbo_id = static_cast<uint32_t>(id),
.existing_damage = existing_damage_rect,
};
};
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 //
);
};
// If there is an external view embedder, ask it to apply the surface
// transformation to its surfaces as well.
if (external_view_embedder) {
external_view_embedder->SetSurfaceTransformationCallback(
gl_surface_transformation_callback);
}
}
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 FML_OS_LINUX || FML_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
gl_populate_existing_damage, // gl_populate_existing_damage
};
return fml::MakeCopyable(
[gl_dispatch_table, fbo_reset_after_present, platform_dispatch_table,
enable_impeller,
external_view_embedder =
std::move(external_view_embedder)](flutter::Shell& shell) mutable {
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);
if (enable_impeller) {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::make_unique<flutter::EmbedderSurfaceGLImpeller>(
gl_dispatch_table, fbo_reset_after_present,
view_embedder), // embedder_surface
platform_dispatch_table, // embedder platform dispatch table
view_embedder // external view embedder
);
}
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::make_unique<flutter::EmbedderSurfaceGL>(
gl_dispatch_table, fbo_reset_after_present,
view_embedder), // embedder_surface
platform_dispatch_table, // embedder platform dispatch table
view_embedder // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferMetalPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder,
bool enable_impeller) {
if (config->type != kMetal) {
return nullptr;
}
#ifdef SHELL_ENABLE_METAL
std::function<bool(flutter::GPUMTLTextureInfo texture)> metal_present =
[ptr = config->metal.present_drawable_callback,
user_data](flutter::GPUMTLTextureInfo texture) {
FlutterMetalTexture embedder_texture;
embedder_texture.struct_size = sizeof(FlutterMetalTexture);
embedder_texture.texture = texture.texture;
embedder_texture.texture_id = texture.texture_id;
embedder_texture.user_data = texture.destruction_context;
embedder_texture.destruction_callback = texture.destruction_callback;
return ptr(user_data, &embedder_texture);
};
auto metal_get_texture =
[ptr = config->metal.get_next_drawable_callback,
user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo {
FlutterFrameInfo frame_info = {};
frame_info.struct_size = sizeof(FlutterFrameInfo);
frame_info.size = {static_cast<uint32_t>(frame_size.width()),
static_cast<uint32_t>(frame_size.height())};
flutter::GPUMTLTextureInfo texture_info;
FlutterMetalTexture metal_texture = ptr(user_data, &frame_info);
texture_info.texture_id = metal_texture.texture_id;
texture_info.texture = metal_texture.texture;
texture_info.destruction_callback = metal_texture.destruction_callback;
texture_info.destruction_context = metal_texture.user_data;
return texture_info;
};
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);
std::unique_ptr<flutter::EmbedderSurface> embedder_surface;
if (enable_impeller) {
flutter::EmbedderSurfaceMetalImpeller::MetalDispatchTable
metal_dispatch_table = {
.present = metal_present,
.get_texture = metal_get_texture,
};
embedder_surface = std::make_unique<flutter::EmbedderSurfaceMetalImpeller>(
const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device),
const_cast<flutter::GPUMTLCommandQueueHandle>(
config->metal.present_command_queue),
metal_dispatch_table, view_embedder);
} else {
flutter::EmbedderSurfaceMetal::MetalDispatchTable metal_dispatch_table = {
.present = metal_present,
.get_texture = metal_get_texture,
};
embedder_surface = std::make_unique<flutter::EmbedderSurfaceMetal>(
const_cast<flutter::GPUMTLDeviceHandle>(config->metal.device),
const_cast<flutter::GPUMTLCommandQueueHandle>(
config->metal.present_command_queue),
metal_dispatch_table, view_embedder);
}
// The static leak checker gets confused by the use of fml::MakeCopyable.
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
return fml::MakeCopyable(
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
external_view_embedder = view_embedder](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::move(embedder_surface), // embedder surface
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferVulkanPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
if (config->type != kVulkan) {
return nullptr;
}
#ifdef SHELL_ENABLE_VULKAN
std::function<void*(VkInstance, const char*)>
vulkan_get_instance_proc_address =
[ptr = config->vulkan.get_instance_proc_address_callback, user_data](
VkInstance instance, const char* proc_name) -> void* {
return ptr(user_data, instance, proc_name);
};
auto vulkan_get_next_image =
[ptr = config->vulkan.get_next_image_callback,
user_data](const SkISize& frame_size) -> FlutterVulkanImage {
FlutterFrameInfo frame_info = {
.struct_size = sizeof(FlutterFrameInfo),
.size = {static_cast<uint32_t>(frame_size.width()),
static_cast<uint32_t>(frame_size.height())},
};
return ptr(user_data, &frame_info);
};
auto vulkan_present_image_callback =
[ptr = config->vulkan.present_image_callback, user_data](
VkImage image, VkFormat format) -> bool {
FlutterVulkanImage image_desc = {
.struct_size = sizeof(FlutterVulkanImage),
.image = reinterpret_cast<uint64_t>(image),
.format = static_cast<uint32_t>(format),
};
return ptr(user_data, &image_desc);
};
auto vk_instance = static_cast<VkInstance>(config->vulkan.instance);
auto proc_addr =
vulkan_get_instance_proc_address(vk_instance, "vkGetInstanceProcAddr");
flutter::EmbedderSurfaceVulkan::VulkanDispatchTable vulkan_dispatch_table = {
.get_instance_proc_address =
reinterpret_cast<PFN_vkGetInstanceProcAddr>(proc_addr),
.get_next_image = vulkan_get_next_image,
.present_image = vulkan_present_image_callback,
};
std::shared_ptr<flutter::EmbedderExternalViewEmbedder> view_embedder =
std::move(external_view_embedder);
std::unique_ptr<flutter::EmbedderSurfaceVulkan> embedder_surface =
std::make_unique<flutter::EmbedderSurfaceVulkan>(
config->vulkan.version, vk_instance,
config->vulkan.enabled_instance_extension_count,
config->vulkan.enabled_instance_extensions,
config->vulkan.enabled_device_extension_count,
config->vulkan.enabled_device_extensions,
static_cast<VkPhysicalDevice>(config->vulkan.physical_device),
static_cast<VkDevice>(config->vulkan.device),
config->vulkan.queue_family_index,
static_cast<VkQueue>(config->vulkan.queue), vulkan_dispatch_table,
view_embedder);
return fml::MakeCopyable(
[embedder_surface = std::move(embedder_surface), platform_dispatch_table,
external_view_embedder =
std::move(view_embedder)](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
std::move(embedder_surface), // embedder surface
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
#else
return nullptr;
#endif
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferSoftwarePlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder) {
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 fml::MakeCopyable(
[software_dispatch_table, platform_dispatch_table,
external_view_embedder =
std::move(external_view_embedder)](flutter::Shell& shell) mutable {
return std::make_unique<flutter::PlatformViewEmbedder>(
shell, // delegate
shell.GetTaskRunners(), // task runners
software_dispatch_table, // software dispatch table
platform_dispatch_table, // platform dispatch table
std::move(external_view_embedder) // external view embedder
);
});
}
static flutter::Shell::CreateCallback<flutter::PlatformView>
InferPlatformViewCreationCallback(
const FlutterRendererConfig* config,
void* user_data,
const flutter::PlatformViewEmbedder::PlatformDispatchTable&
platform_dispatch_table,
std::unique_ptr<flutter::EmbedderExternalViewEmbedder>
external_view_embedder,
bool enable_impeller) {
if (config == nullptr) {
return nullptr;
}
switch (config->type) {
case kOpenGL:
return InferOpenGLPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder), enable_impeller);
case kSoftware:
return InferSoftwarePlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
case kMetal:
return InferMetalPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder), enable_impeller);
case kVulkan:
return InferVulkanPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder));
default:
return nullptr;
}
return nullptr;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLTexture* texture) {
#ifdef SHELL_ENABLE_GL
GrGLTextureInfo texture_info;
texture_info.fTarget = texture->target;
texture_info.fID = texture->name;
texture_info.fFormat = texture->format;
auto backend_texture = GrBackendTextures::MakeGL(config.size.width, //
config.size.height, //
skgpu::Mipmapped::kNo, //
texture_info //
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kBottomLeft_GrSurfaceOrigin, // surface origin
1, // sample count
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
texture->destruction_callback), // release proc
texture->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLFramebuffer* framebuffer) {
#ifdef SHELL_ENABLE_GL
GrGLFramebufferInfo framebuffer_info = {};
framebuffer_info.fFormat = framebuffer->target;
framebuffer_info.fFBOID = framebuffer->name;
auto backend_render_target =
GrBackendRenderTargets::MakeGL(config.size.width, // width
config.size.height, // height
1, // sample count
0, // stencil bits
framebuffer_info // framebuffer info
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendRenderTarget(
context, // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::RenderTargetReleaseProc>(
framebuffer->destruction_callback), // release proc
framebuffer->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied frame-buffer.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterSoftwareBackingStore* software) {
const auto image_info =
SkImageInfo::MakeN32Premul(config.size.width, config.size.height);
struct Captures {
VoidCallback destruction_callback;
void* user_data;
};
auto captures = std::make_unique<Captures>();
captures->destruction_callback = software->destruction_callback;
captures->user_data = software->user_data;
auto release_proc = [](void* pixels, void* context) {
auto captures = reinterpret_cast<Captures*>(context);
if (captures->destruction_callback) {
captures->destruction_callback(captures->user_data);
}
delete captures;
};
auto surface =
SkSurfaces::WrapPixels(image_info, // image info
const_cast<void*>(software->allocation), // pixels
software->row_bytes, // row bytes
release_proc, // release proc
captures.get() // get context
);
if (!surface) {
FML_LOG(ERROR)
<< "Could not wrap embedder supplied software render buffer.";
if (software->destruction_callback) {
software->destruction_callback(software->user_data);
}
return nullptr;
}
if (surface) {
captures.release(); // Skia has assumed ownership of the struct.
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterSoftwareBackingStore2* software) {
const auto color_info = getSkColorInfo(software->pixel_format);
if (!color_info) {
return nullptr;
}
const auto image_info = SkImageInfo::Make(
SkISize::Make(config.size.width, config.size.height), *color_info);
struct Captures {
VoidCallback destruction_callback;
void* user_data;
};
auto captures = std::make_unique<Captures>();
captures->destruction_callback = software->destruction_callback;
captures->user_data = software->user_data;
auto release_proc = [](void* pixels, void* context) {
auto captures = reinterpret_cast<Captures*>(context);
if (captures->destruction_callback) {
captures->destruction_callback(captures->user_data);
}
};
auto surface =
SkSurfaces::WrapPixels(image_info, // image info
const_cast<void*>(software->allocation), // pixels
software->row_bytes, // row bytes
release_proc, // release proc
captures.release() // release context
);
if (!surface) {
FML_LOG(ERROR)
<< "Could not wrap embedder supplied software render buffer.";
if (software->destruction_callback) {
software->destruction_callback(software->user_data);
}
return nullptr;
}
return surface;
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterMetalBackingStore* metal) {
#ifdef SHELL_ENABLE_METAL
GrMtlTextureInfo texture_info;
if (!metal->texture.texture) {
FML_LOG(ERROR) << "Embedder supplied null Metal texture.";
return nullptr;
}
sk_cfp<FlutterMetalTextureHandle> mtl_texture;
mtl_texture.retain(metal->texture.texture);
texture_info.fTexture = mtl_texture;
GrBackendTexture backend_texture(config.size.width, //
config.size.height, //
skgpu::Mipmapped::kNo, //
texture_info //
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kTopLeft_GrSurfaceOrigin, // surface origin
// TODO(dnfield): Update this when embedders support MSAA, see
// https://github.com/flutter/flutter/issues/100392
1, // sample count
kBGRA_8888_SkColorType, // color type
nullptr, // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
metal->texture.destruction_callback), // release proc
metal->texture.user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied Metal render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
MakeRenderTargetFromBackingStoreImpeller(
FlutterBackingStore backing_store,
const fml::closure& on_release,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
const FlutterBackingStoreConfig& config,
const FlutterOpenGLFramebuffer* framebuffer) {
#if defined(SHELL_ENABLE_GL) && defined(IMPELLER_SUPPORTS_RENDERING)
const auto& gl_context =
impeller::ContextGLES::Cast(*aiks_context->GetContext());
const auto size = impeller::ISize(config.size.width, config.size.height);
impeller::TextureDescriptor color0_tex;
color0_tex.type = impeller::TextureType::kTexture2D;
color0_tex.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
color0_tex.size = size;
color0_tex.usage = static_cast<impeller::TextureUsageMask>(
impeller::TextureUsage::kRenderTarget);
color0_tex.sample_count = impeller::SampleCount::kCount1;
color0_tex.storage_mode = impeller::StorageMode::kDevicePrivate;
impeller::ColorAttachment color0;
color0.texture = std::make_shared<impeller::TextureGLES>(
gl_context.GetReactor(), color0_tex,
impeller::TextureGLES::IsWrapped::kWrapped);
color0.clear_color = impeller::Color::DarkSlateGray();
color0.load_action = impeller::LoadAction::kClear;
color0.store_action = impeller::StoreAction::kStore;
impeller::TextureDescriptor stencil0_tex;
stencil0_tex.type = impeller::TextureType::kTexture2D;
stencil0_tex.format = impeller::PixelFormat::kR8G8B8A8UNormInt;
stencil0_tex.size = size;
stencil0_tex.usage = static_cast<impeller::TextureUsageMask>(
impeller::TextureUsage::kRenderTarget);
stencil0_tex.sample_count = impeller::SampleCount::kCount1;
impeller::StencilAttachment stencil0;
stencil0.clear_stencil = 0;
stencil0.texture = std::make_shared<impeller::TextureGLES>(
gl_context.GetReactor(), stencil0_tex,
impeller::TextureGLES::IsWrapped::kWrapped);
stencil0.load_action = impeller::LoadAction::kClear;
stencil0.store_action = impeller::StoreAction::kDontCare;
impeller::RenderTarget render_target_desc;
render_target_desc.SetColorAttachment(color0, 0u);
render_target_desc.SetStencilAttachment(stencil0);
return std::make_unique<flutter::EmbedderRenderTargetImpeller>(
backing_store, aiks_context,
std::make_unique<impeller::RenderTarget>(std::move(render_target_desc)),
on_release);
#else
return nullptr;
#endif
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
MakeRenderTargetFromBackingStoreImpeller(
FlutterBackingStore backing_store,
const fml::closure& on_release,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
const FlutterBackingStoreConfig& config,
const FlutterMetalBackingStore* metal) {
#if defined(SHELL_ENABLE_METAL) && defined(IMPELLER_SUPPORTS_RENDERING)
if (!metal->texture.texture) {
FML_LOG(ERROR) << "Embedder supplied null Metal texture.";
return nullptr;
}
const auto size = impeller::ISize(config.size.width, config.size.height);
impeller::TextureDescriptor resolve_tex_desc;
resolve_tex_desc.size = size;
resolve_tex_desc.sample_count = impeller::SampleCount::kCount1;
resolve_tex_desc.storage_mode = impeller::StorageMode::kDevicePrivate;
resolve_tex_desc.usage =
static_cast<uint64_t>(impeller::TextureUsage::kRenderTarget) |
static_cast<uint64_t>(impeller::TextureUsage::kShaderRead);
auto resolve_tex = impeller::WrapTextureMTL(
resolve_tex_desc, metal->texture.texture,
[callback = metal->texture.destruction_callback,
user_data = metal->texture.user_data]() { callback(user_data); });
if (!resolve_tex) {
FML_LOG(ERROR) << "Could not wrap embedder supplied Metal render texture.";
return nullptr;
}
resolve_tex->SetLabel("ImpellerBackingStoreResolve");
impeller::TextureDescriptor msaa_tex_desc;
msaa_tex_desc.storage_mode = impeller::StorageMode::kDeviceTransient;
msaa_tex_desc.type = impeller::TextureType::kTexture2DMultisample;
msaa_tex_desc.sample_count = impeller::SampleCount::kCount4;
msaa_tex_desc.format = resolve_tex->GetTextureDescriptor().format;
msaa_tex_desc.size = size;
msaa_tex_desc.usage =
static_cast<uint64_t>(impeller::TextureUsage::kRenderTarget);
auto msaa_tex =
aiks_context->GetContext()->GetResourceAllocator()->CreateTexture(
msaa_tex_desc);
if (!msaa_tex) {
FML_LOG(ERROR) << "Could not allocate MSAA color texture.";
return nullptr;
}
msaa_tex->SetLabel("ImpellerBackingStoreColorMSAA");
impeller::ColorAttachment color0;
color0.texture = msaa_tex;
color0.clear_color = impeller::Color::DarkSlateGray();
color0.load_action = impeller::LoadAction::kClear;
color0.store_action = impeller::StoreAction::kMultisampleResolve;
color0.resolve_texture = resolve_tex;
impeller::RenderTarget render_target_desc;
render_target_desc.SetColorAttachment(color0, 0u);
return std::make_unique<flutter::EmbedderRenderTargetImpeller>(
backing_store, aiks_context,
std::make_unique<impeller::RenderTarget>(std::move(render_target_desc)),
on_release);
#else
return nullptr;
#endif
}
static sk_sp<SkSurface> MakeSkSurfaceFromBackingStore(
GrDirectContext* context,
const FlutterBackingStoreConfig& config,
const FlutterVulkanBackingStore* vulkan) {
#ifdef SHELL_ENABLE_VULKAN
if (!vulkan->image) {
FML_LOG(ERROR) << "Embedder supplied null Vulkan image.";
return nullptr;
}
GrVkImageInfo image_info = {
.fImage = reinterpret_cast<VkImage>(vulkan->image->image),
.fImageTiling = VK_IMAGE_TILING_OPTIMAL,
.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.fFormat = static_cast<VkFormat>(vulkan->image->format),
.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
.fSampleCount = 1,
.fLevelCount = 1,
};
auto backend_texture = GrBackendTextures::MakeVk(
config.size.width, config.size.height, image_info);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendTexture(
context, // context
backend_texture, // back-end texture
kTopLeft_GrSurfaceOrigin, // surface origin
1, // sample count
flutter::GPUSurfaceVulkan::ColorTypeFromFormat(
static_cast<VkFormat>(vulkan->image->format)), // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
static_cast<SkSurfaces::TextureReleaseProc>(
vulkan->destruction_callback), // release proc
vulkan->user_data // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap embedder supplied Vulkan render texture.";
return nullptr;
}
return surface;
#else
return nullptr;
#endif
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
MakeRenderTargetFromSkSurface(FlutterBackingStore backing_store,
sk_sp<SkSurface> skia_surface,
fml::closure on_release) {
if (!skia_surface) {
return nullptr;
}
return std::make_unique<flutter::EmbedderRenderTargetSkia>(
backing_store, std::move(skia_surface), std::move(on_release));
}
static std::unique_ptr<flutter::EmbedderRenderTarget>
CreateEmbedderRenderTarget(
const FlutterCompositor* compositor,
const FlutterBackingStoreConfig& config,
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
bool enable_impeller) {
FlutterBackingStore backing_store = {};
backing_store.struct_size = sizeof(backing_store);
// Safe access checks on the compositor struct have been performed in
// InferExternalViewEmbedderFromArgs and are not necessary here.
auto c_create_callback = compositor->create_backing_store_callback;
auto c_collect_callback = compositor->collect_backing_store_callback;
{
TRACE_EVENT0("flutter", "FlutterCompositorCreateBackingStore");
if (!c_create_callback(&config, &backing_store, compositor->user_data)) {
FML_LOG(ERROR) << "Could not create the embedder backing store.";
return nullptr;
}
}
if (backing_store.struct_size != sizeof(backing_store)) {
FML_LOG(ERROR) << "Embedder modified the backing store struct size.";
return nullptr;
}
// In case we return early without creating an embedder render target, the
// embedder has still given us ownership of its baton which we must return
// back to it. If this method is successful, the closure is released when the
// render target is eventually released.
fml::ScopedCleanupClosure collect_callback(
[c_collect_callback, backing_store, user_data = compositor->user_data]() {
TRACE_EVENT0("flutter", "FlutterCompositorCollectBackingStore");
c_collect_callback(&backing_store, user_data);
});
// No safe access checks on the renderer are necessary since we allocated
// the struct.
std::unique_ptr<flutter::EmbedderRenderTarget> render_target;
switch (backing_store.type) {
case kFlutterBackingStoreTypeOpenGL: {
switch (backing_store.open_gl.type) {
case kFlutterOpenGLTargetTypeTexture: {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.open_gl.texture);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface),
collect_callback.Release());
break;
}
case kFlutterOpenGLTargetTypeFramebuffer: {
if (enable_impeller) {
render_target = MakeRenderTargetFromBackingStoreImpeller(
backing_store, collect_callback.Release(), aiks_context, config,
&backing_store.open_gl.framebuffer);
break;
} else {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.open_gl.framebuffer);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface),
collect_callback.Release());
break;
}
}
}
break;
}
case kFlutterBackingStoreTypeSoftware: {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.software);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface), collect_callback.Release());
break;
}
case kFlutterBackingStoreTypeSoftware2: {
auto skia_surface = MakeSkSurfaceFromBackingStore(
context, config, &backing_store.software2);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface), collect_callback.Release());
break;
}
case kFlutterBackingStoreTypeMetal: {
if (enable_impeller) {
render_target = MakeRenderTargetFromBackingStoreImpeller(
backing_store, collect_callback.Release(), aiks_context, config,
&backing_store.metal);
} else {
auto skia_surface = MakeSkSurfaceFromBackingStore(context, config,
&backing_store.metal);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface), collect_callback.Release());
}
break;
}
case kFlutterBackingStoreTypeVulkan: {
auto skia_surface =
MakeSkSurfaceFromBackingStore(context, config, &backing_store.vulkan);
render_target = MakeRenderTargetFromSkSurface(
backing_store, std::move(skia_surface), collect_callback.Release());
break;
}
};
if (!render_target) {
FML_LOG(ERROR) << "Could not create a surface from an embedder provided "
"render target.";
}
return render_target;
}
static std::pair<std::unique_ptr<flutter::EmbedderExternalViewEmbedder>,
bool /* halt engine launch if true */>
InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor,
bool enable_impeller) {
if (compositor == nullptr) {
return {nullptr, false};
}
auto c_create_callback =
SAFE_ACCESS(compositor, create_backing_store_callback, nullptr);
auto c_collect_callback =
SAFE_ACCESS(compositor, collect_backing_store_callback, nullptr);
auto c_present_callback =
SAFE_ACCESS(compositor, present_layers_callback, nullptr);
bool avoid_backing_store_cache =
SAFE_ACCESS(compositor, avoid_backing_store_cache, false);
// Make sure the required callbacks are present
if (!c_create_callback || !c_collect_callback || !c_present_callback) {
FML_LOG(ERROR) << "Required compositor callbacks absent.";
return {nullptr, true};
}
FlutterCompositor captured_compositor = *compositor;
flutter::EmbedderExternalViewEmbedder::CreateRenderTargetCallback
create_render_target_callback =
[captured_compositor, enable_impeller](
GrDirectContext* context,
const std::shared_ptr<impeller::AiksContext>& aiks_context,
const auto& config) {
return CreateEmbedderRenderTarget(&captured_compositor, config,
context, aiks_context,
enable_impeller);
};
flutter::EmbedderExternalViewEmbedder::PresentCallback present_callback =
[c_present_callback,
user_data = compositor->user_data](const auto& layers) {
TRACE_EVENT0("flutter", "FlutterCompositorPresentLayers");
return c_present_callback(
const_cast<const FlutterLayer**>(layers.data()), layers.size(),
user_data);
};
return {std::make_unique<flutter::EmbedderExternalViewEmbedder>(
avoid_backing_store_cache, create_render_target_callback,
present_callback),
false};
}
struct _FlutterPlatformMessageResponseHandle {
std::unique_ptr<flutter::PlatformMessage> message;
};
struct LoadedElfDeleter {
void operator()(Dart_LoadedElf* elf) {
if (elf) {
::Dart_UnloadELF(elf);
}
}
};
using UniqueLoadedElf = std::unique_ptr<Dart_LoadedElf, LoadedElfDeleter>;
struct _FlutterEngineAOTData {
UniqueLoadedElf loaded_elf = nullptr;
const uint8_t* vm_snapshot_data = nullptr;
const uint8_t* vm_snapshot_instrs = nullptr;
const uint8_t* vm_isolate_data = nullptr;
const uint8_t* vm_isolate_instrs = nullptr;
};
FlutterEngineResult FlutterEngineCreateAOTData(
const FlutterEngineAOTDataSource* source,
FlutterEngineAOTData* data_out) {
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"AOT data can only be created in AOT mode.");
} else if (!source) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null source specified.");
} else if (!data_out) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null data_out specified.");
}
switch (source->type) {
case kFlutterEngineAOTDataSourceTypeElfPath: {
if (!source->elf_path || !fml::IsFile(source->elf_path)) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid ELF path specified.");
}
auto aot_data = std::make_unique<_FlutterEngineAOTData>();
const char* error = nullptr;
#if OS_FUCHSIA
// TODO(gw280): https://github.com/flutter/flutter/issues/50285
// Dart doesn't implement Dart_LoadELF on Fuchsia
Dart_LoadedElf* loaded_elf = nullptr;
#else
Dart_LoadedElf* loaded_elf = Dart_LoadELF(
source->elf_path, // file path
0, // file offset
&error, // error (out)
&aot_data->vm_snapshot_data, // vm snapshot data (out)
&aot_data->vm_snapshot_instrs, // vm snapshot instr (out)
&aot_data->vm_isolate_data, // vm isolate data (out)
&aot_data->vm_isolate_instrs // vm isolate instr (out)
);
#endif
if (loaded_elf == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, error);
}
aot_data->loaded_elf.reset(loaded_elf);
*data_out = aot_data.release();
return kSuccess;
}
}
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineAOTDataSourceType type specified.");
}
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data) {
if (!data) {
// Deleting a null object should be a no-op.
return kSuccess;
}
// Created in a unique pointer in `FlutterEngineCreateAOTData`.
delete data;
return kSuccess;
}
// Constructs appropriate mapping callbacks if JIT snapshot locations have been
// explictly specified.
void PopulateJITSnapshotMappingCallbacks(const FlutterProjectArgs* args,
flutter::Settings& settings) {
auto make_mapping_callback = [](const char* path, bool executable) {
return [path, executable]() {
if (executable) {
return fml::FileMapping::CreateReadExecute(path);
} else {
return fml::FileMapping::CreateReadOnly(path);
}
};
};
// Users are allowed to specify only certain snapshots if they so desire.
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) {
settings.vm_snapshot_data = make_mapping_callback(
reinterpret_cast<const char*>(args->vm_snapshot_data), false);
}
if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) {
settings.vm_snapshot_instr = make_mapping_callback(
reinterpret_cast<const char*>(args->vm_snapshot_instructions), true);
}
if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) {
settings.isolate_snapshot_data = make_mapping_callback(
reinterpret_cast<const char*>(args->isolate_snapshot_data), false);
}
if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) {
settings.isolate_snapshot_instr = make_mapping_callback(
reinterpret_cast<const char*>(args->isolate_snapshot_instructions),
true);
}
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
settings.dart_library_sources_kernel = []() {
return std::make_unique<fml::NonOwnedMapping>(kPlatformStrongDill,
kPlatformStrongDillSize);
};
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE ==
// FLUTTER_RUNTIME_MODE_DEBUG)
}
void PopulateAOTSnapshotMappingCallbacks(
const FlutterProjectArgs* args,
flutter::Settings& settings) { // NOLINT(google-runtime-references)
// 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 (SAFE_ACCESS(args, aot_data, nullptr) != nullptr) {
settings.vm_snapshot_data =
make_mapping_callback(args->aot_data->vm_snapshot_data, 0);
settings.vm_snapshot_instr =
make_mapping_callback(args->aot_data->vm_snapshot_instrs, 0);
settings.isolate_snapshot_data =
make_mapping_callback(args->aot_data->vm_isolate_data, 0);
settings.isolate_snapshot_instr =
make_mapping_callback(args->aot_data->vm_isolate_instrs, 0);
}
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) != nullptr) {
settings.vm_snapshot_data = make_mapping_callback(
args->vm_snapshot_data, SAFE_ACCESS(args, vm_snapshot_data_size, 0));
}
if (SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) != nullptr) {
settings.vm_snapshot_instr = make_mapping_callback(
args->vm_snapshot_instructions,
SAFE_ACCESS(args, vm_snapshot_instructions_size, 0));
}
if (SAFE_ACCESS(args, isolate_snapshot_data, nullptr) != nullptr) {
settings.isolate_snapshot_data =
make_mapping_callback(args->isolate_snapshot_data,
SAFE_ACCESS(args, isolate_snapshot_data_size, 0));
}
if (SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr) != nullptr) {
settings.isolate_snapshot_instr = make_mapping_callback(
args->isolate_snapshot_instructions,
SAFE_ACCESS(args, isolate_snapshot_instructions_size, 0));
}
}
// Create a callback to notify the embedder of semantic updates
// using the legacy embedder callbacks 'update_semantics_node_callback' and
// 'update_semantics_custom_action_callback'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateEmbedderSemanticsUpdateCallbackV1(
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback,
FlutterUpdateSemanticsCustomActionCallback
update_semantics_custom_action_callback,
void* user_data) {
return [update_semantics_node_callback,
update_semantics_custom_action_callback,
user_data](const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
flutter::EmbedderSemanticsUpdate update{nodes, actions};
FlutterSemanticsUpdate* update_ptr = update.get();
// First, queue all node and custom action updates.
if (update_semantics_node_callback != nullptr) {
for (size_t i = 0; i < update_ptr->nodes_count; i++) {
update_semantics_node_callback(&update_ptr->nodes[i], user_data);
}
}
if (update_semantics_custom_action_callback != nullptr) {
for (size_t i = 0; i < update_ptr->custom_actions_count; i++) {
update_semantics_custom_action_callback(&update_ptr->custom_actions[i],
user_data);
}
}
// Second, mark node and action batches completed now that all
// updates are queued.
if (update_semantics_node_callback != nullptr) {
const FlutterSemanticsNode batch_end_sentinel = {
sizeof(FlutterSemanticsNode),
kFlutterSemanticsNodeIdBatchEnd,
};
update_semantics_node_callback(&batch_end_sentinel, user_data);
}
if (update_semantics_custom_action_callback != nullptr) {
const FlutterSemanticsCustomAction batch_end_sentinel = {
sizeof(FlutterSemanticsCustomAction),
kFlutterSemanticsCustomActionIdBatchEnd,
};
update_semantics_custom_action_callback(&batch_end_sentinel, user_data);
}
};
}
// Create a callback to notify the embedder of semantic updates
// using the deprecated embedder callback 'update_semantics_callback'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateEmbedderSemanticsUpdateCallbackV2(
FlutterUpdateSemanticsCallback update_semantics_callback,
void* user_data) {
return [update_semantics_callback, user_data](
const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
flutter::EmbedderSemanticsUpdate update{nodes, actions};
update_semantics_callback(update.get(), user_data);
};
}
// Create a callback to notify the embedder of semantic updates
// using the new embedder callback 'update_semantics_callback2'.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateEmbedderSemanticsUpdateCallbackV3(
FlutterUpdateSemanticsCallback2 update_semantics_callback,
void* user_data) {
return [update_semantics_callback, user_data](
const flutter::SemanticsNodeUpdates& nodes,
const flutter::CustomAccessibilityActionUpdates& actions) {
flutter::EmbedderSemanticsUpdate2 update{nodes, actions};
update_semantics_callback(update.get(), user_data);
};
}
// Creates a callback that receives semantic updates from the engine
// and notifies the embedder's callback(s). Returns null if the embedder
// did not register any callbacks.
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
CreateEmbedderSemanticsUpdateCallback(const FlutterProjectArgs* args,
void* user_data) {
// There are three variants for the embedder API's semantic update callbacks.
// Create a callback that maps to the embedder's desired semantic update API.
//
// Handle the case where the embedder registered the callback
// 'updated_semantics_callback2'
if (SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr) {
return CreateEmbedderSemanticsUpdateCallbackV3(
args->update_semantics_callback2, user_data);
}
// Handle the case where the embedder registered the deprecated callback
// 'update_semantics_callback'.
if (SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr) {
return CreateEmbedderSemanticsUpdateCallbackV2(
args->update_semantics_callback, user_data);
}
// Handle the case where the embedder registered the deprecated callbacks
// 'update_semantics_node_callback' and
// 'update_semantics_custom_action_callback'.
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr) {
update_semantics_node_callback = args->update_semantics_node_callback;
}
FlutterUpdateSemanticsCustomActionCallback
update_semantics_custom_action_callback = nullptr;
if (SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr) {
update_semantics_custom_action_callback =
args->update_semantics_custom_action_callback;
}
if (update_semantics_node_callback != nullptr ||
update_semantics_custom_action_callback != nullptr) {
return CreateEmbedderSemanticsUpdateCallbackV1(
update_semantics_node_callback, update_semantics_custom_action_callback,
user_data);
}
// Handle the case where the embedder registered no callbacks.
return nullptr;
}
FlutterEngineResult FlutterEngineRun(size_t version,
const FlutterRendererConfig* config,
const FlutterProjectArgs* args,
void* user_data,
FLUTTER_API_SYMBOL(FlutterEngine) *
engine_out) {
auto result =
FlutterEngineInitialize(version, config, args, user_data, engine_out);
if (result != kSuccess) {
return result;
}
return FlutterEngineRunInitialized(*engine_out);
}
FlutterEngineResult FlutterEngineInitialize(size_t version,
const FlutterRendererConfig* config,
const FlutterProjectArgs* args,
void* user_data,
FLUTTER_API_SYMBOL(FlutterEngine) *
engine_out) {
// Step 0: Figure out arguments for shell creation.
if (version != FLUTTER_ENGINE_VERSION) {
return LOG_EMBEDDER_ERROR(
kInvalidLibraryVersion,
"Flutter embedder version mismatch. There has been a breaking change. "
"Please consult the changelog and update the embedder.");
}
if (engine_out == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The engine out parameter was missing.");
}
if (args == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The Flutter project arguments were missing.");
}
if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"The assets path in the Flutter project arguments was missing.");
}
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)) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"The renderer configuration was invalid.");
}
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);
if (SAFE_ACCESS(args, aot_data, nullptr)) {
if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) ||
SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) ||
SAFE_ACCESS(args, isolate_snapshot_data, nullptr) ||
SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Multiple AOT sources specified. Embedders should provide either "
"*_snapshot_* buffers or aot_data, not both.");
}
}
if (flutter::DartVM::IsRunningPrecompiledCode()) {
PopulateAOTSnapshotMappingCallbacks(args, settings);
} else {
PopulateJITSnapshotMappingCallbacks(args, settings);
}
settings.icu_data_path = icu_data_path;
settings.assets_path = args->assets_path;
settings.leak_vm = !SAFE_ACCESS(args, shutdown_dart_vm_when_done, false);
settings.old_gen_heap_size = SAFE_ACCESS(args, dart_old_gen_heap_size, -1);
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)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Not running in AOT mode but could not resolve the kernel binary.");
}
settings.application_kernel_asset = kApplicationKernelSnapshotFileName;
}
settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, 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](const auto& isolate) { callback(user_data); };
}
if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) {
FlutterLogMessageCallback callback =
SAFE_ACCESS(args, log_message_callback, nullptr);
settings.log_message_callback = [callback, user_data](
const std::string& tag,
const std::string& message) {
callback(tag.c_str(), message.c_str(), user_data);
};
}
if (SAFE_ACCESS(args, log_tag, nullptr) != nullptr) {
settings.log_tag = SAFE_ACCESS(args, log_tag, nullptr);
}
bool has_update_semantics_2_callback =
SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr;
bool has_update_semantics_callback =
SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr;
bool has_legacy_update_semantics_callback =
SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr ||
SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) !=
nullptr;
int semantic_callback_count = (has_update_semantics_2_callback ? 1 : 0) +
(has_update_semantics_callback ? 1 : 0) +
(has_legacy_update_semantics_callback ? 1 : 0);
if (semantic_callback_count > 1) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Multiple semantics update callbacks provided. "
"Embedders should provide either `update_semantics_callback2`, "
"`update_semantics_callback`, or both "
"`update_semantics_node_callback` and "
"`update_semantics_custom_action_callback`.");
}
flutter::PlatformViewEmbedder::UpdateSemanticsCallback
update_semantics_callback =
CreateEmbedderSemanticsUpdateCallback(args, 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](std::unique_ptr<flutter::PlatformMessage> message) {
auto handle = new FlutterPlatformMessageResponseHandle();
const FlutterPlatformMessage incoming_message = {
sizeof(FlutterPlatformMessage), // struct_size
message->channel().c_str(), // channel
message->data().GetMapping(), // message
message->data().GetSize(), // 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::ComputePlatformResolvedLocaleCallback
compute_platform_resolved_locale_callback = nullptr;
if (SAFE_ACCESS(args, compute_platform_resolved_locale_callback, nullptr) !=
nullptr) {
compute_platform_resolved_locale_callback =
[ptr = args->compute_platform_resolved_locale_callback](
const std::vector<std::string>& supported_locales_data) {
const size_t number_of_strings_per_locale = 3;
size_t locale_count =
supported_locales_data.size() / number_of_strings_per_locale;
std::vector<FlutterLocale> supported_locales;
std::vector<const FlutterLocale*> supported_locales_ptr;
for (size_t i = 0; i < locale_count; ++i) {
supported_locales.push_back(
{.struct_size = sizeof(FlutterLocale),
.language_code =
supported_locales_data[i * number_of_strings_per_locale +
0]
.c_str(),
.country_code =
supported_locales_data[i * number_of_strings_per_locale +
1]
.c_str(),
.script_code =
supported_locales_data[i * number_of_strings_per_locale +
2]
.c_str(),
.variant_code = nullptr});
supported_locales_ptr.push_back(&supported_locales[i]);
}
const FlutterLocale* result =
ptr(supported_locales_ptr.data(), locale_count);
std::unique_ptr<std::vector<std::string>> out =
std::make_unique<std::vector<std::string>>();
if (result) {
std::string language_code(SAFE_ACCESS(result, language_code, ""));
if (language_code != "") {
out->push_back(language_code);
out->emplace_back(SAFE_ACCESS(result, country_code, ""));
out->emplace_back(SAFE_ACCESS(result, script_code, ""));
}
}
return out;
};
}
flutter::PlatformViewEmbedder::OnPreEngineRestartCallback
on_pre_engine_restart_callback = nullptr;
if (SAFE_ACCESS(args, on_pre_engine_restart_callback, nullptr) != nullptr) {
on_pre_engine_restart_callback = [ptr =
args->on_pre_engine_restart_callback,
user_data]() { return ptr(user_data); };
}
flutter::PlatformViewEmbedder::ChanneUpdateCallback channel_update_callback =
nullptr;
if (SAFE_ACCESS(args, channel_update_callback, nullptr) != nullptr) {
channel_update_callback = [ptr = args->channel_update_callback, user_data](
const std::string& name, bool listening) {
FlutterChannelUpdate update{sizeof(FlutterChannelUpdate), name.c_str(),
listening};
ptr(&update, user_data);
};
}
auto external_view_embedder_result = InferExternalViewEmbedderFromArgs(
SAFE_ACCESS(args, compositor, nullptr), settings.enable_impeller);
if (external_view_embedder_result.second) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Compositor arguments were invalid.");
}
flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table =
{
update_semantics_callback, //
platform_message_response_callback, //
vsync_callback, //
compute_platform_resolved_locale_callback, //
on_pre_engine_restart_callback, //
channel_update_callback, //
};
auto on_create_platform_view = InferPlatformViewCreationCallback(
config, user_data, platform_dispatch_table,
std::move(external_view_embedder_result.first), settings.enable_impeller);
if (!on_create_platform_view) {
return LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not infer platform view creation callback.");
}
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
[](flutter::Shell& shell) {
return std::make_unique<flutter::Rasterizer>(shell);
};
using ExternalTextureResolver = flutter::EmbedderExternalTextureResolver;
std::unique_ptr<ExternalTextureResolver> external_texture_resolver;
external_texture_resolver = std::make_unique<ExternalTextureResolver>();
#ifdef SHELL_ENABLE_GL
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, size_t width,
size_t height) -> std::unique_ptr<FlutterOpenGLTexture> {
std::unique_ptr<FlutterOpenGLTexture> texture =
std::make_unique<FlutterOpenGLTexture>();
if (!ptr(user_data, texture_identifier, width, height, texture.get())) {
return nullptr;
}
return texture;
};
external_texture_resolver =
std::make_unique<ExternalTextureResolver>(external_texture_callback);
}
}
#endif
#ifdef SHELL_ENABLE_METAL
flutter::EmbedderExternalTextureMetal::ExternalTextureCallback
external_texture_metal_callback;
if (config->type == kMetal) {
const FlutterMetalRendererConfig* metal_config = &config->metal;
if (SAFE_ACCESS(metal_config, external_texture_frame_callback, nullptr)) {
external_texture_metal_callback =
[ptr = metal_config->external_texture_frame_callback, user_data](
int64_t texture_identifier, size_t width,
size_t height) -> std::unique_ptr<FlutterMetalExternalTexture> {
std::unique_ptr<FlutterMetalExternalTexture> texture =
std::make_unique<FlutterMetalExternalTexture>();
texture->struct_size = sizeof(FlutterMetalExternalTexture);
if (!ptr(user_data, texture_identifier, width, height, texture.get())) {
return nullptr;
}
return texture;
};
external_texture_resolver = std::make_unique<ExternalTextureResolver>(
external_texture_metal_callback);
}
}
#endif
auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr);
auto thread_config_callback = [&custom_task_runners](
const fml::Thread::ThreadConfig& config) {
fml::Thread::SetCurrentThreadName(config);
if (!custom_task_runners || !custom_task_runners->thread_priority_setter) {
return;
}
FlutterThreadPriority priority = FlutterThreadPriority::kNormal;
switch (config.priority) {
case fml::Thread::ThreadPriority::kBackground:
priority = FlutterThreadPriority::kBackground;
break;
case fml::Thread::ThreadPriority::kNormal:
priority = FlutterThreadPriority::kNormal;
break;
case fml::Thread::ThreadPriority::kDisplay:
priority = FlutterThreadPriority::kDisplay;
break;
case fml::Thread::ThreadPriority::kRaster:
priority = FlutterThreadPriority::kRaster;
break;
}
custom_task_runners->thread_priority_setter(priority);
};
auto thread_host =
flutter::EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
custom_task_runners, thread_config_callback);
if (!thread_host || !thread_host->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not set up or infer thread configuration "
"to run the Flutter engine on.");
}
auto task_runners = thread_host->GetTaskRunners();
if (!task_runners.IsValid()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Task runner configuration was invalid.");
}
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.empty()) {
run_configuration.SetEntrypoint(std::move(dart_entrypoint));
}
}
if (SAFE_ACCESS(args, dart_entrypoint_argc, 0) > 0) {
if (SAFE_ACCESS(args, dart_entrypoint_argv, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not determine Dart entrypoint arguments "
"as dart_entrypoint_argc "
"was set, but dart_entrypoint_argv was null.");
}
std::vector<std::string> arguments(args->dart_entrypoint_argc);
for (int i = 0; i < args->dart_entrypoint_argc; ++i) {
arguments[i] = std::string{args->dart_entrypoint_argv[i]};
}
run_configuration.SetEntrypointArgs(std::move(arguments));
}
if (!run_configuration.IsValid()) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Could not infer the Flutter project to run from given arguments.");
}
// Create the engine but don't launch the shell or run the root isolate.
auto embedder_engine = std::make_unique<flutter::EmbedderEngine>(
std::move(thread_host), //
std::move(task_runners), //
std::move(settings), //
std::move(run_configuration), //
on_create_platform_view, //
on_create_rasterizer, //
std::move(external_texture_resolver) //
);
// Release the ownership of the embedder engine to the caller.
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(
embedder_engine.release());
return kSuccess;
}
FlutterEngineResult FlutterEngineRunInitialized(
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
if (!engine) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
// The engine must not already be running. Initialize may only be called
// once on an engine instance.
if (embedder_engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
// Step 1: Launch the shell.
if (!embedder_engine->LaunchShell()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not launch the engine using supplied "
"initialization arguments.");
}
// Step 2: Tell the platform view to initialize itself.
if (!embedder_engine->NotifyCreated()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not create platform view components.");
}
// Step 3: Launch the root isolate.
if (!embedder_engine->RunRootIsolate()) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Could not run the root isolate of the Flutter application using the "
"project arguments specified.");
}
return kSuccess;
}
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
embedder_engine->NotifyDestroyed();
embedder_engine->CollectShell();
return kSuccess;
}
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
auto result = FlutterEngineDeinitialize(engine);
if (result != kSuccess) {
return result;
}
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
delete embedder_engine;
return kSuccess;
}
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterWindowMetricsEvent* flutter_metrics) {
if (engine == nullptr || flutter_metrics == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
// TODO(dkwingsmt): Use a real view ID when multiview is supported.
int64_t view_id = kFlutterImplicitViewId;
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);
metrics.physical_view_inset_top =
SAFE_ACCESS(flutter_metrics, physical_view_inset_top, 0.0);
metrics.physical_view_inset_right =
SAFE_ACCESS(flutter_metrics, physical_view_inset_right, 0.0);
metrics.physical_view_inset_bottom =
SAFE_ACCESS(flutter_metrics, physical_view_inset_bottom, 0.0);
metrics.physical_view_inset_left =
SAFE_ACCESS(flutter_metrics, physical_view_inset_left, 0.0);
metrics.display_id = SAFE_ACCESS(flutter_metrics, display_id, 0);
if (metrics.device_pixel_ratio <= 0.0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Device pixel ratio was invalid. It must be greater than zero.");
}
if (metrics.physical_view_inset_top < 0 ||
metrics.physical_view_inset_right < 0 ||
metrics.physical_view_inset_bottom < 0 ||
metrics.physical_view_inset_left < 0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Physical view insets are invalid. They must be non-negative.");
}
if (metrics.physical_view_inset_top > metrics.physical_height ||
metrics.physical_view_inset_right > metrics.physical_width ||
metrics.physical_view_inset_bottom > metrics.physical_height ||
metrics.physical_view_inset_left > metrics.physical_width) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Physical view insets are invalid. They cannot "
"be greater than physical height or width.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetViewportMetrics(
view_id, metrics)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Viewport metrics were invalid.");
}
// 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;
case kPanZoomStart:
return flutter::PointerData::Change::kPanZoomStart;
case kPanZoomUpdate:
return flutter::PointerData::Change::kPanZoomUpdate;
case kPanZoomEnd:
return flutter::PointerData::Change::kPanZoomEnd;
}
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;
case kFlutterPointerDeviceKindStylus:
return flutter::PointerData::DeviceKind::kStylus;
case kFlutterPointerDeviceKindTrackpad:
return flutter::PointerData::DeviceKind::kTrackpad;
}
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;
case kFlutterPointerSignalKindScrollInertiaCancel:
return flutter::PointerData::SignalKind::kScrollInertiaCancel;
case kFlutterPointerSignalKindScale:
return flutter::PointerData::SignalKind::kScale;
}
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:
case flutter::PointerData::Change::kPanZoomStart:
case flutter::PointerData::Change::kPanZoomUpdate:
case flutter::PointerData::Change::kPanZoomEnd:
return 0;
}
return 0;
}
FlutterEngineResult FlutterEngineSendPointerEvent(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPointerEvent* pointers,
size_t events_count) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (pointers == nullptr || events_count == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid pointer events.");
}
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();
// this is currely in use only on android embedding.
pointer_data.embedder_id = 0;
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);
// Delta will be generated in pointer_data_packet_converter.cc.
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
pointer_data.device = SAFE_ACCESS(current, device, 0);
// Pointer identifier will be generated in
// pointer_data_packet_converter.cc.
pointer_data.pointer_identifier = 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);
}
}
pointer_data.pan_x = SAFE_ACCESS(current, pan_x, 0.0);
pointer_data.pan_y = SAFE_ACCESS(current, pan_y, 0.0);
// Delta will be generated in pointer_data_packet_converter.cc.
pointer_data.pan_delta_x = 0.0;
pointer_data.pan_delta_y = 0.0;
pointer_data.scale = SAFE_ACCESS(current, scale, 0.0);
pointer_data.rotation = SAFE_ACCESS(current, rotation, 0.0);
pointer_data.view_id =
SAFE_ACCESS(current, view_id, kFlutterImplicitViewId);
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(kInternalInconsistency,
"Could not dispatch pointer events to the "
"running Flutter application.");
}
static inline flutter::KeyEventType MapKeyEventType(
FlutterKeyEventType event_kind) {
switch (event_kind) {
case kFlutterKeyEventTypeUp:
return flutter::KeyEventType::kUp;
case kFlutterKeyEventTypeDown:
return flutter::KeyEventType::kDown;
case kFlutterKeyEventTypeRepeat:
return flutter::KeyEventType::kRepeat;
}
return flutter::KeyEventType::kUp;
}
static inline flutter::KeyEventDeviceType MapKeyEventDeviceType(
FlutterKeyEventDeviceType event_kind) {
switch (event_kind) {
case kFlutterKeyEventDeviceTypeKeyboard:
return flutter::KeyEventDeviceType::kKeyboard;
case kFlutterKeyEventDeviceTypeDirectionalPad:
return flutter::KeyEventDeviceType::kDirectionalPad;
case kFlutterKeyEventDeviceTypeGamepad:
return flutter::KeyEventDeviceType::kGamepad;
case kFlutterKeyEventDeviceTypeJoystick:
return flutter::KeyEventDeviceType::kJoystick;
case kFlutterKeyEventDeviceTypeHdmi:
return flutter::KeyEventDeviceType::kHdmi;
}
return flutter::KeyEventDeviceType::kKeyboard;
}
// Send a platform message to the framework.
//
// The `data_callback` will be invoked with `user_data`, and must not be empty.
static FlutterEngineResult InternalSendPlatformMessage(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const char* channel,
const uint8_t* data,
size_t size,
FlutterDataCallback data_callback,
void* user_data) {
FlutterEngineResult result;
FlutterPlatformMessageResponseHandle* response_handle;
result = FlutterPlatformMessageCreateResponseHandle(
engine, data_callback, user_data, &response_handle);
if (result != kSuccess) {
return result;
}
const FlutterPlatformMessage message = {
sizeof(FlutterPlatformMessage), // struct_size
channel, // channel
data, // message
size, // message_size
response_handle, // response_handle
};
result = FlutterEngineSendPlatformMessage(engine, &message);
// Whether `SendPlatformMessage` succeeds or not, the response handle must be
// released.
FlutterEngineResult release_result =
FlutterPlatformMessageReleaseResponseHandle(engine, response_handle);
if (result != kSuccess) {
return result;
}
return release_result;
}
FlutterEngineResult FlutterEngineSendKeyEvent(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterKeyEvent* event,
FlutterKeyEventCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (event == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid key event.");
}
const char* character = SAFE_ACCESS(event, character, nullptr);
flutter::KeyData key_data;
key_data.Clear();
key_data.timestamp = static_cast<uint64_t>(SAFE_ACCESS(event, timestamp, 0));
key_data.type = MapKeyEventType(
SAFE_ACCESS(event, type, FlutterKeyEventType::kFlutterKeyEventTypeUp));
key_data.physical = SAFE_ACCESS(event, physical, 0);
key_data.logical = SAFE_ACCESS(event, logical, 0);
key_data.synthesized = SAFE_ACCESS(event, synthesized, false);
key_data.device_type = MapKeyEventDeviceType(SAFE_ACCESS(
event, device_type,
FlutterKeyEventDeviceType::kFlutterKeyEventDeviceTypeKeyboard));
auto packet = std::make_unique<flutter::KeyDataPacket>(key_data, character);
struct MessageData {
FlutterKeyEventCallback callback;
void* user_data;
};
MessageData* message_data =
new MessageData{.callback = callback, .user_data = user_data};
return InternalSendPlatformMessage(
engine, kFlutterKeyDataChannel, packet->data().data(),
packet->data().size(),
[](const uint8_t* data, size_t size, void* user_data) {
auto message_data = std::unique_ptr<MessageData>(
reinterpret_cast<MessageData*>(user_data));
if (message_data->callback == nullptr) {
return;
}
bool handled = false;
if (size == 1) {
handled = *data != 0;
}
message_data->callback(handled, message_data->user_data);
},
message_data);
}
FlutterEngineResult FlutterEngineSendPlatformMessage(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessage* flutter_message) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (flutter_message == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid message argument.");
}
if (SAFE_ACCESS(flutter_message, channel, nullptr) == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments, "Message argument did not specify a valid channel.");
}
size_t message_size = SAFE_ACCESS(flutter_message, message_size, 0);
const uint8_t* message_data = SAFE_ACCESS(flutter_message, message, nullptr);
if (message_size != 0 && message_data == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Message size was non-zero but the message data was nullptr.");
}
const FlutterPlatformMessageResponseHandle* response_handle =
SAFE_ACCESS(flutter_message, response_handle, nullptr);
fml::RefPtr<flutter::PlatformMessageResponse> response;
if (response_handle && response_handle->message) {
response = response_handle->message->response();
}
std::unique_ptr<flutter::PlatformMessage> message;
if (message_size == 0) {
message = std::make_unique<flutter::PlatformMessage>(
flutter_message->channel, response);
} else {
message = std::make_unique<flutter::PlatformMessage>(
flutter_message->channel,
fml::MallocMapping::Copy(message_data, message_size), response);
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SendPlatformMessage(std::move(message))
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not send a message to the running "
"Flutter application.");
}
FlutterEngineResult FlutterPlatformMessageCreateResponseHandle(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterDataCallback data_callback,
void* user_data,
FlutterPlatformMessageResponseHandle** response_out) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (data_callback == nullptr || response_out == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments, "Data callback or the response handle was invalid.");
}
flutter::EmbedderPlatformMessageResponse::Callback response_callback =
[user_data, data_callback](const uint8_t* data, size_t size) {
data_callback(data, size, user_data);
};
auto platform_task_runner = reinterpret_cast<flutter::EmbedderEngine*>(engine)
->GetTaskRunners()
.GetPlatformTaskRunner();
auto handle = new FlutterPlatformMessageResponseHandle();
handle->message = std::make_unique<flutter::PlatformMessage>(
"", // The channel is empty and unused as the response handle is going
// to referenced directly in the |FlutterEngineSendPlatformMessage|
// with the container message discarded.
fml::MakeRefCounted<flutter::EmbedderPlatformMessageResponse>(
std::move(platform_task_runner), response_callback));
*response_out = handle;
return kSuccess;
}
FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterPlatformMessageResponseHandle* response) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (response == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid response handle.");
}
delete response;
return kSuccess;
}
// Note: This can execute on any thread.
FlutterEngineResult FlutterEngineSendPlatformMessageResponse(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
const FlutterPlatformMessageResponseHandle* handle,
const uint8_t* data,
size_t data_length) {
if (data_length != 0 && data == nullptr) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Data size was non zero but the pointer to the data was null.");
}
auto response = handle->message->response();
if (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(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Texture identifier was invalid.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->RegisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not register the specified texture.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUnregisterExternalTexture(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Texture identifier was invalid.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->UnregisterTexture(
texture_identifier)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not un-register the specified texture.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineMarkExternalTextureFrameAvailable(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
int64_t texture_identifier) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (texture_identifier == 0) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid texture identifier.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->MarkTextureFrameAvailable(texture_identifier)) {
return LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not mark the texture frame as being available.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateSemanticsEnabled(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
bool enabled) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->SetSemanticsEnabled(
enabled)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not update semantics state.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineUpdateAccessibilityFeatures(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterAccessibilityFeature flags) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SetAccessibilityFeatures(flags)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not update accessibility features.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineDispatchSemanticsAction(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
uint64_t node_id,
FlutterSemanticsAction action,
const uint8_t* data,
size_t data_length) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
auto engine_action = static_cast<flutter::SemanticsAction>(action);
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->DispatchSemanticsAction(
node_id, engine_action,
fml::MallocMapping::Copy(data, data_length))) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not dispatch semantics action.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineOnVsync(FLUTTER_API_SYMBOL(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, "Invalid engine handle.");
}
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,
"Could not notify the running engine instance of a Vsync event.");
}
return kSuccess;
}
FlutterEngineResult FlutterEngineReloadSystemFonts(
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
TRACE_EVENT0("flutter", "FlutterEngineReloadSystemFonts");
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)
->ReloadSystemFonts()) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not reload system fonts.");
}
return kSuccess;
}
void FlutterEngineTraceEventDurationBegin(const char* name) {
fml::tracing::TraceEvent0("flutter", name, /*flow_id_count=*/0,
/*flow_ids=*/nullptr);
}
void FlutterEngineTraceEventDurationEnd(const char* name) {
fml::tracing::TraceEventEnd(name);
}
void FlutterEngineTraceEventInstant(const char* name) {
fml::tracing::TraceEventInstant0("flutter", name, /*flow_id_count=*/0,
/*flow_ids=*/nullptr);
}
FlutterEngineResult FlutterEnginePostRenderThreadTask(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* baton) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Render thread callback was null.");
}
auto task = [callback, baton]() { callback(baton); };
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->PostRenderThreadTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not post the render thread task.");
}
uint64_t FlutterEngineGetCurrentTime() {
return fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
}
FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterTask* task) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->RunTask(task)
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not run the specified task.");
}
static bool DispatchJSONPlatformMessage(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const rapidjson::Document& document,
const std::string& channel_name) {
if (channel_name.empty()) {
return false;
}
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (!document.Accept(writer)) {
return false;
}
const char* message = buffer.GetString();
if (message == nullptr || buffer.GetSize() == 0) {
return false;
}
auto platform_message = std::make_unique<flutter::PlatformMessage>(
channel_name.c_str(), // channel
fml::MallocMapping::Copy(message,
buffer.GetSize()), // message
nullptr // response
);
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->SendPlatformMessage(std::move(platform_message));
}
FlutterEngineResult FlutterEngineUpdateLocales(FLUTTER_API_SYMBOL(FlutterEngine)
engine,
const FlutterLocale** locales,
size_t locales_count) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (locales_count == 0) {
return kSuccess;
}
if (locales == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "No locales were specified.");
}
rapidjson::Document document;
auto& allocator = document.GetAllocator();
document.SetObject();
document.AddMember("method", "setLocale", allocator);
rapidjson::Value args(rapidjson::kArrayType);
args.Reserve(locales_count * 4, allocator);
for (size_t i = 0; i < locales_count; ++i) {
const FlutterLocale* locale = locales[i];
const char* language_code_str = SAFE_ACCESS(locale, language_code, nullptr);
if (language_code_str == nullptr || ::strlen(language_code_str) == 0) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Language code is required but not present in FlutterLocale.");
}
const char* country_code_str = SAFE_ACCESS(locale, country_code, "");
const char* script_code_str = SAFE_ACCESS(locale, script_code, "");
const char* variant_code_str = SAFE_ACCESS(locale, variant_code, "");
rapidjson::Value language_code, country_code, script_code, variant_code;
language_code.SetString(language_code_str, allocator);
country_code.SetString(country_code_str ? country_code_str : "", allocator);
script_code.SetString(script_code_str ? script_code_str : "", allocator);
variant_code.SetString(variant_code_str ? variant_code_str : "", allocator);
// Required.
args.PushBack(language_code, allocator);
args.PushBack(country_code, allocator);
args.PushBack(script_code, allocator);
args.PushBack(variant_code, allocator);
}
document.AddMember("args", args, allocator);
return DispatchJSONPlatformMessage(engine, document, "flutter/localization")
? kSuccess
: LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not send message to update locale of "
"a running Flutter application.");
}
bool FlutterEngineRunsAOTCompiledDartCode(void) {
return flutter::DartVM::IsRunningPrecompiledCode();
}
FlutterEngineResult FlutterEnginePostDartObject(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterEngineDartPort port,
const FlutterEngineDartObject* object) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!reinterpret_cast<flutter::EmbedderEngine*>(engine)->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine not running.");
}
if (port == ILLEGAL_PORT) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Attempted to post to an illegal port.");
}
if (object == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid Dart object to post.");
}
Dart_CObject dart_object = {};
fml::ScopedCleanupClosure typed_data_finalizer;
switch (object->type) {
case kFlutterEngineDartObjectTypeNull:
dart_object.type = Dart_CObject_kNull;
break;
case kFlutterEngineDartObjectTypeBool:
dart_object.type = Dart_CObject_kBool;
dart_object.value.as_bool = object->bool_value;
break;
case kFlutterEngineDartObjectTypeInt32:
dart_object.type = Dart_CObject_kInt32;
dart_object.value.as_int32 = object->int32_value;
break;
case kFlutterEngineDartObjectTypeInt64:
dart_object.type = Dart_CObject_kInt64;
dart_object.value.as_int64 = object->int64_value;
break;
case kFlutterEngineDartObjectTypeDouble:
dart_object.type = Dart_CObject_kDouble;
dart_object.value.as_double = object->double_value;
break;
case kFlutterEngineDartObjectTypeString:
if (object->string_value == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"kFlutterEngineDartObjectTypeString must be "
"a null terminated string but was null.");
}
dart_object.type = Dart_CObject_kString;
dart_object.value.as_string = const_cast<char*>(object->string_value);
break;
case kFlutterEngineDartObjectTypeBuffer: {
auto* buffer = SAFE_ACCESS(object->buffer_value, buffer, nullptr);
if (buffer == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"kFlutterEngineDartObjectTypeBuffer must "
"specify a buffer but found nullptr.");
}
auto buffer_size = SAFE_ACCESS(object->buffer_value, buffer_size, 0);
auto callback =
SAFE_ACCESS(object->buffer_value, buffer_collect_callback, nullptr);
auto user_data = SAFE_ACCESS(object->buffer_value, user_data, nullptr);
// The user has provided a callback, let them manage the lifecycle of
// the underlying data. If not, copy it out from the provided buffer.
if (callback == nullptr) {
dart_object.type = Dart_CObject_kTypedData;
dart_object.value.as_typed_data.type = Dart_TypedData_kUint8;
dart_object.value.as_typed_data.length = buffer_size;
dart_object.value.as_typed_data.values = buffer;
} else {
struct ExternalTypedDataPeer {
void* user_data = nullptr;
VoidCallback trampoline = nullptr;
};
auto peer = new ExternalTypedDataPeer();
peer->user_data = user_data;
peer->trampoline = callback;
// This finalizer is set so that in case of failure of the
// Dart_PostCObject below, we collect the peer. The embedder is still
// responsible for collecting the buffer in case of non-kSuccess
// returns from this method. This finalizer must be released in case
// of kSuccess returns from this method.
typed_data_finalizer.SetClosure([peer]() {
// This is the tiny object we use as the peer to the Dart call so
// that we can attach the a trampoline to the embedder supplied
// callback. In case of error, we need to collect this object lest
// we introduce a tiny leak.
delete peer;
});
dart_object.type = Dart_CObject_kExternalTypedData;
dart_object.value.as_external_typed_data.type = Dart_TypedData_kUint8;
dart_object.value.as_external_typed_data.length = buffer_size;
dart_object.value.as_external_typed_data.data = buffer;
dart_object.value.as_external_typed_data.peer = peer;
dart_object.value.as_external_typed_data.callback =
+[](void* unused_isolate_callback_data, void* peer) {
auto typed_peer = reinterpret_cast<ExternalTypedDataPeer*>(peer);
typed_peer->trampoline(typed_peer->user_data);
delete typed_peer;
};
}
} break;
default:
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDartObjectType type specified.");
}
if (!Dart_PostCObject(port, &dart_object)) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Could not post the object to the Dart VM.");
}
// On a successful call, the VM takes ownership of and is responsible for
// invoking the finalizer.
typed_data_finalizer.Release();
return kSuccess;
}
FlutterEngineResult FlutterEngineNotifyLowMemoryWarning(
FLUTTER_API_SYMBOL(FlutterEngine) raw_engine) {
auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine);
if (engine == nullptr || !engine->IsValid()) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine was invalid.");
}
engine->GetShell().NotifyLowMemoryWarning();
rapidjson::Document document;
auto& allocator = document.GetAllocator();
document.SetObject();
document.AddMember("type", "memoryPressure", allocator);
return DispatchJSONPlatformMessage(raw_engine, document, "flutter/system")
? kSuccess
: LOG_EMBEDDER_ERROR(
kInternalInconsistency,
"Could not dispatch the low memory notification message.");
}
FlutterEngineResult FlutterEnginePostCallbackOnAllNativeThreads(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
FlutterNativeThreadCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Invalid native thread callback.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)
->PostTaskOnEngineManagedNativeThreads(
[callback, user_data](FlutterNativeThreadType type) {
callback(type, user_data);
})
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Internal error while attempting to post "
"tasks to all threads.");
}
namespace {
static bool ValidDisplayConfiguration(const FlutterEngineDisplay* displays,
size_t display_count) {
std::set<FlutterEngineDisplayId> display_ids;
for (size_t i = 0; i < display_count; i++) {
if (displays[i].single_display && display_count != 1) {
return false;
}
display_ids.insert(displays[i].display_id);
}
return display_ids.size() == display_count;
}
} // namespace
FlutterEngineResult FlutterEngineNotifyDisplayUpdate(
FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
const FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* embedder_displays,
size_t display_count) {
if (raw_engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (!ValidDisplayConfiguration(embedder_displays, display_count)) {
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDisplay configuration specified.");
}
auto engine = reinterpret_cast<flutter::EmbedderEngine*>(raw_engine);
switch (update_type) {
case kFlutterEngineDisplaysUpdateTypeStartup: {
std::vector<std::unique_ptr<flutter::Display>> displays;
const auto* display = embedder_displays;
for (size_t i = 0; i < display_count; i++) {
displays.push_back(std::make_unique<flutter::Display>(
SAFE_ACCESS(display, display_id, i), //
SAFE_ACCESS(display, refresh_rate, 0), //
SAFE_ACCESS(display, width, 0), //
SAFE_ACCESS(display, height, 0), //
SAFE_ACCESS(display, device_pixel_ratio, 1)));
display = reinterpret_cast<const FlutterEngineDisplay*>(
reinterpret_cast<const uint8_t*>(display) + display->struct_size);
}
engine->GetShell().OnDisplayUpdates(std::move(displays));
return kSuccess;
}
default:
return LOG_EMBEDDER_ERROR(
kInvalidArguments,
"Invalid FlutterEngineDisplaysUpdateType type specified.");
}
}
FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine)
engine) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
return reinterpret_cast<flutter::EmbedderEngine*>(engine)->ScheduleFrame()
? kSuccess
: LOG_EMBEDDER_ERROR(kInvalidArguments,
"Could not schedule frame.");
}
FlutterEngineResult FlutterEngineSetNextFrameCallback(
FLUTTER_API_SYMBOL(FlutterEngine) engine,
VoidCallback callback,
void* user_data) {
if (engine == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle.");
}
if (callback == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments,
"Next frame callback was null.");
}
flutter::EmbedderEngine* embedder_engine =
reinterpret_cast<flutter::EmbedderEngine*>(engine);
fml::WeakPtr<flutter::PlatformView> weak_platform_view =
embedder_engine->GetShell().GetPlatformView();
if (!weak_platform_view) {
return LOG_EMBEDDER_ERROR(kInternalInconsistency,
"Platform view unavailable.");
}
weak_platform_view->SetNextFrameCallback(
[callback, user_data]() { callback(user_data); });
return kSuccess;
}
FlutterEngineResult FlutterEngineGetProcAddresses(
FlutterEngineProcTable* table) {
if (!table) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Null table specified.");
}
#define SET_PROC(member, function) \
if (STRUCT_HAS_MEMBER(table, member)) { \
table->member = &function; \
}
SET_PROC(CreateAOTData, FlutterEngineCreateAOTData);
SET_PROC(CollectAOTData, FlutterEngineCollectAOTData);
SET_PROC(Run, FlutterEngineRun);
SET_PROC(Shutdown, FlutterEngineShutdown);
SET_PROC(Initialize, FlutterEngineInitialize);
SET_PROC(Deinitialize, FlutterEngineDeinitialize);
SET_PROC(RunInitialized, FlutterEngineRunInitialized);
SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent);
SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent);
SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent);
SET_PROC(SendPlatformMessage, FlutterEngineSendPlatformMessage);
SET_PROC(PlatformMessageCreateResponseHandle,
FlutterPlatformMessageCreateResponseHandle);
SET_PROC(PlatformMessageReleaseResponseHandle,
FlutterPlatformMessageReleaseResponseHandle);
SET_PROC(SendPlatformMessageResponse,
FlutterEngineSendPlatformMessageResponse);
SET_PROC(RegisterExternalTexture, FlutterEngineRegisterExternalTexture);
SET_PROC(UnregisterExternalTexture, FlutterEngineUnregisterExternalTexture);
SET_PROC(MarkExternalTextureFrameAvailable,
FlutterEngineMarkExternalTextureFrameAvailable);
SET_PROC(UpdateSemanticsEnabled, FlutterEngineUpdateSemanticsEnabled);
SET_PROC(UpdateAccessibilityFeatures,
FlutterEngineUpdateAccessibilityFeatures);
SET_PROC(DispatchSemanticsAction, FlutterEngineDispatchSemanticsAction);
SET_PROC(OnVsync, FlutterEngineOnVsync);
SET_PROC(ReloadSystemFonts, FlutterEngineReloadSystemFonts);
SET_PROC(TraceEventDurationBegin, FlutterEngineTraceEventDurationBegin);
SET_PROC(TraceEventDurationEnd, FlutterEngineTraceEventDurationEnd);
SET_PROC(TraceEventInstant, FlutterEngineTraceEventInstant);
SET_PROC(PostRenderThreadTask, FlutterEnginePostRenderThreadTask);
SET_PROC(GetCurrentTime, FlutterEngineGetCurrentTime);
SET_PROC(RunTask, FlutterEngineRunTask);
SET_PROC(UpdateLocales, FlutterEngineUpdateLocales);
SET_PROC(RunsAOTCompiledDartCode, FlutterEngineRunsAOTCompiledDartCode);
SET_PROC(PostDartObject, FlutterEnginePostDartObject);
SET_PROC(NotifyLowMemoryWarning, FlutterEngineNotifyLowMemoryWarning);
SET_PROC(PostCallbackOnAllNativeThreads,
FlutterEnginePostCallbackOnAllNativeThreads);
SET_PROC(NotifyDisplayUpdate, FlutterEngineNotifyDisplayUpdate);
SET_PROC(ScheduleFrame, FlutterEngineScheduleFrame);
SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback);
#undef SET_PROC
return kSuccess;
}