浏览代码

compile engine for ios

/siyaoH-1.17-PlatformMessage
xingwei.zhu 4 年前
当前提交
564e384b
共有 16 个文件被更改,包括 1546 次插入8 次删除
  1. 4
      com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanelWrapper.cs
  2. 2
      com.unity.uiwidgets/Runtime/material/material_state.cs
  3. 2
      com.unity.uiwidgets/Runtime/rendering/debug_overflow_indicator.cs
  4. 256
      engine/Build.bee.cs
  5. 26
      engine/src/external/ios/CustomAppController.m
  6. 106
      engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.cc
  7. 66
      engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.h
  8. 124
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.h
  9. 529
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm
  10. 79
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.h
  11. 106
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.mm
  12. 54
      engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.h
  13. 200
      engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.mm

4
com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanelWrapper.cs


#endregion
#region Platform: MacOs Specific Functionalities
#region Platform: MacOs/iOS Specific Functionalities
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS
public partial class UIWidgetsPanelWrapper {
Texture _renderTexture;

2
com.unity.uiwidgets/Runtime/material/material_state.cs


using System.Collections.Generic;
using Unity.UIWidgets.material;
using UnityEditor.VersionControl;
namespace Unity.UIWidgets.material {
public enum MaterialState {

2
com.unity.uiwidgets/Runtime/rendering/debug_overflow_indicator.cs


using Unity.UIWidgets.painting;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEditor.PackageManager;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Gradient = Unity.UIWidgets.ui.Gradient;

256
engine/Build.bee.cs


}
}
//ios build helpers
class UserIOSSdkLocator : IOSSdkLocator
{
public UserIOSSdkLocator() : base(Architecture.Arm64) {}
public IOSSdk UserIOSSdk(NPath path)
{
return DefaultSdkFromXcodeApp(path);
}
}
class IOSAppToolchain : IOSToolchain
{
//there is a bug in XCodeProjectFile.cs and the class IosPlatform, where the acceptale Platform Name is not matched
//we workaround this bug by overriding LegacyPlatformIdentifier to the correct one
public override string LegacyPlatformIdentifier => $"iphone_{Architecture.Name}";
//copied from com.unity.platforms.ios/Editor/Unity.Platform.Editor/bee~/IOSAppToolchain.cs
private static NPath _XcodePath = null;
private static NPath XcodePath
{
get
{
if (_XcodePath == null)
{
string error = "";
try
{
if (HostPlatform.IsOSX)
{
var start = new ProcessStartInfo("xcode-select", "-p")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
};
string output = "";
using (var process = Process.Start(start))
{
process.OutputDataReceived += (sender, e) => { output += e.Data; };
process.ErrorDataReceived += (sender, e) => { error += e.Data; };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(); //? doesn't work correctly if time interval is set
}
_XcodePath = error == "" ? output : "";
if (_XcodePath != "" && _XcodePath.DirectoryExists())
{
_XcodePath = XcodePath.Parent.Parent;
}
else
{
throw new InvalidOperationException("Failed to find Xcode, xcode-select did not return a valid path");
}
}
}
catch (InvalidOperationException e)
{
Console.WriteLine(
$"xcode-select did not return a valid path. Error message, if any, was: {error}. " +
$"Often this can be fixed by making sure you have Xcode command line tools" +
$" installed correctly, and then running `sudo xcode-select -r`");
throw e;
}
}
return _XcodePath;
}
}
//private static string XcodePath = "/Applications/Xcode.app/";
public IOSAppToolchain() : base((new UserIOSSdkLocator()).UserIOSSdk(XcodePath))
{
}
}
/**
* How to add new target platform (taking iOS as the example)
* (1) add a new static void DeployIOS() {} in which we need call SetupLibUIWidgets() with UIWidgetsBuildTargetPlatform.ios as its

}
}
//bee.exe ios
static void DeployIOS()
{
var libUIWidgets = SetupLibUIWidgets(UIWidgetsBuildTargetPlatform.ios, out var dependencies);
var nativePrograms = new List<NativeProgram>();
nativePrograms.Add(libUIWidgets);
var xcodeProject = new XCodeProjectFile(nativePrograms, new NPath("libUIWidgetsIOS.xcodeproj/project.pbxproj"));
Backend.Current.AddAliasDependency("ios", new NPath("libUIWidgetsIOS.xcodeproj/project.pbxproj"));
foreach(var dep in dependencies) {
Backend.Current.AddAliasDependency("ios", dep);
}
}
static void Main()
{
flutterRoot = Environment.GetEnvironmentVariable("FLUTTER_ROOT");

else if (BuildUtils.IsHostMac())
{
DeployMac();
DeployIOS();
//this setting is disabled by default, don't change it unless you know what you are doing
//it must be set the same as the settings we choose to build skia and flutter engine
//refer to the readme file for the details
private static bool ios_bitcode_enabled = false;
static NativeProgram SetupLibUIWidgets(UIWidgetsBuildTargetPlatform platform, out List<NPath> dependencies)
{

"src/shell/platform/unity/darwin/macos/unity_surface_manager.h",
};
var iosSources = new NPath[] {
"src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm",
"src/shell/platform/unity/darwin/ios/uiwidgets_panel.h",
"src/shell/platform/unity/darwin/ios/uiwidgets_system.mm",
"src/shell/platform/unity/darwin/ios/uiwidgets_system.h",
"src/shell/platform/unity/darwin/ios/cocoa_task_runner.cc",
"src/shell/platform/unity/darwin/ios/cocoa_task_runner.h",
"src/shell/platform/unity/darwin/ios/unity_surface_manager.mm",
"src/shell/platform/unity/darwin/ios/unity_surface_manager.h",
};
np.Sources.Add(c => IsIosOrTvos(c), iosSources);
np.Libraries.Add(c => IsWindows(c), new BagOfObjectFilesLibrary(
new NPath[]{

np.CompilerSettings().Add(c => IsMac(c), c => c.WithCustomFlags(new []{"-Wno-c++11-narrowing"}));
np.CompilerSettings().Add(c => IsMac(c) || IsIosOrTvos(c), c => c.WithCustomFlags(new []{"-Wno-c++11-narrowing"}));
if (ios_bitcode_enabled) {
np.CompilerSettingsForIosOrTvos().Add(c => c.WithEmbedBitcode(true));
}
np.Defines.Add(c => IsMac(c), "UIWIDGETS_FORCE_ALIGNAS_8=\\\"1\\\"");
np.Defines.Add(c => IsMac(c) || IsIosOrTvos(c), "UIWIDGETS_FORCE_ALIGNAS_8=\\\"1\\\"");
np.Defines.Add(c => c.CodeGen == CodeGen.Debug,
new[] { "_ITERATOR_DEBUG_LEVEL=2", "_HAS_ITERATOR_DEBUGGING=1", "_SECURE_SCL=1" });

np.ValidConfigurations = validConfigurations;
}
else if (platform == UIWidgetsBuildTargetPlatform.ios)
{
var toolchain = new IOSAppToolchain();
var validConfigurations = new List<NativeProgramConfiguration>();
foreach (var codegen in codegens)
{
var config = new NativeProgramConfiguration(codegen, toolchain, lump: true);
validConfigurations.Add(config);
var buildProgram = np.SetupSpecificConfiguration(config, toolchain.StaticLibraryFormat);
var builtNP = buildProgram.DeployTo("build");
var deployNP = buildProgram.DeployTo("../Samples/UIWidgetsSamples_2019_4/Assets/Plugins/iOS/");
dependencies.Add(builtNP.Path);
dependencies.Add(deployNP.Path);
}
Backend.Current.AddAliasDependency("ios", CopyTool.Instance().Setup("../Samples/UIWidgetsSamples_2019_4/Assets/Plugins/iOS/CustomAppController.m", "src/external/ios/CustomAppController.m"));
np.ValidConfigurations = validConfigurations;
}
return np;
}

static void SetupDependency(NativeProgram np)
{
SetupRadidJson(np);
np.Defines.Add(c => IsIosOrTvos(c), new []
{
//lib flutter
"__STDC_CONSTANT_MACROS",
"__STDC_FORMAT_MACROS",
"_FORTIFY_SOURCE=2",
"_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
"_DEBUG",
"FLUTTER_RUNTIME_MODE_DEBUG=1",
"FLUTTER_RUNTIME_MODE_PROFILE=2",
"FLUTTER_RUNTIME_MODE_RELEASE=3",
"FLUTTER_RUNTIME_MODE_JIT_RELEASE=4",
"FLUTTER_RUNTIME_MODE=1",
"FLUTTER_JIT_RUNTIME=1",
//lib skia
"SK_ENABLE_SPIRV_VALIDATION",
"SK_ASSUME_GL_ES=1","SK_ENABLE_API_AVAILABLE",
"SK_GAMMA_APPLY_TO_A8",
"SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1",
"GR_TEST_UTILS=1",
"SKIA_IMPLEMENTATION=1",
"SK_GL",
"SK_ENABLE_DUMP_GPU",
"SK_SUPPORT_PDF",
"SK_CODEC_DECODES_JPEG",
"SK_ENCODE_JPEG",
"SK_ENABLE_ANDROID_UTILS",
"SK_USE_LIBGIFCODEC",
"SK_HAS_HEIF_LIBRARY",
"SK_CODEC_DECODES_PNG",
"SK_ENCODE_PNG",
"SK_CODEC_DECODES_RAW",
"SK_ENABLE_SKSL_INTERPRETER",
"SK_CODEC_DECODES_WEBP",
"SK_ENCODE_WEBP",
"SK_XML",
});
np.Defines.Add(c => IsIosOrTvos(c), new[] {
"__STDC_CONSTANT_MACROS",
"__STDC_FORMAT_MACROS",
"_FORTIFY_SOURCE=2",
"_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
"_DEBUG",
"SK_GL",
"SK_METAL",
"SK_ENABLE_DUMP_GPU",
"SK_CODEC_DECODES_JPEG",
"SK_ENCODE_JPEG",
"SK_CODEC_DECODES_PNG",
"SK_ENCODE_PNG",
"SK_CODEC_DECODES_WEBP",
"SK_ENCODE_WEBP",
"SK_HAS_WUFFS_LIBRARY",
"FLUTTER_RUNTIME_MODE_DEBUG=1",
"FLUTTER_RUNTIME_MODE_PROFILE=2",
"FLUTTER_RUNTIME_MODE_RELEASE=3",
"FLUTTER_RUNTIME_MODE_JIT_RELEASE=4",
"FLUTTER_RUNTIME_MODE=1",
"FLUTTER_JIT_RUNTIME=1",
"U_USING_ICU_NAMESPACE=0",
"U_ENABLE_DYLOAD=0",
"USE_CHROMIUM_ICU=1",
"U_STATIC_IMPLEMENTATION",
"ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE",
"UCHAR_TYPE=uint16_t",
"SK_DISABLE_REDUCE_OPLIST_SPLITTING",
"SK_ENABLE_DUMP_GPU",
"SK_DISABLE_AAA",
"SK_DISABLE_READBUFFER",
"SK_DISABLE_EFFECT_DESERIALIZATION",
"SK_DISABLE_LEGACY_SHADERCONTEXT",
"SK_DISABLE_LOWP_RASTER_PIPELINE",
"SK_FORCE_RASTER_PIPELINE_BLITTER",
"SK_GL",
"SK_ASSUME_GL_ES=1",
"SK_ENABLE_API_AVAILABLE"
});
np.CompilerSettings().Add(c => IsIosOrTvos(c), c => c.WithCustomFlags(new[] {
"-MD",
"-MF",
"-I.",
"-Ithird_party",
"-Isrc",
"-I"+ flutterRoot,
"-I"+ flutterRoot+"/third_party/rapidjson/include",
"-I"+ skiaRoot,
"-I"+ flutterRoot+"/flutter/third_party/txt/src",
"-I" + flutterRoot + "/third_party/harfbuzz/src",
"-I" + skiaRoot + "/third_party/externals/icu/source/common",
// "-Igen",
"-I"+ flutterRoot+"/third_party/icu/source/common",
"-I"+ flutterRoot+"/third_party/icu/source/i18n",
"-fvisibility-inlines-hidden",
}));
np.Libraries.Add(IsIosOrTvos, c => {
return new PrecompiledLibrary[]
{
new StaticLibrary(flutterRoot+"/out/ios_debug_unopt/obj/flutter/third_party/txt/libtxt_lib.a"),
new SystemFramework("CoreFoundation"),
new SystemFramework("ImageIO"),
new SystemFramework("MobileCoreServices"),
new SystemFramework("CoreGraphics"),
new SystemFramework("CoreText"),
new SystemFramework("UIKit"),
};
});
np.Defines.Add(c => IsMac(c), new []
{

26
engine/src/external/ios/CustomAppController.m


#if TARGET_OS_IOS
#import "UnityAppController.h"
#import <TargetConditionals.h>
extern void UnityPluginLoad(struct IUnityInterfaces *interfaces);
extern void UnityPluginUnload(void);
#pragma mark - App controller subclasssing
@interface CustomAppController : UnityAppController
{
}
- (void)shouldAttachRenderDelegate;
@end
@implementation CustomAppController
- (void)shouldAttachRenderDelegate;
{
UnityRegisterRenderingPluginV5(&UnityPluginLoad, &UnityPluginUnload);
}
@end
IMPL_APP_CONTROLLER_SUBCLASS(CustomAppController);
#endif

106
engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.cc


#include "cocoa_task_runner.h"
#include <flutter/fml/time/time_point.h>
#include <atomic>
#include <utility>
namespace uiwidgets {
CocoaTaskRunner::CocoaTaskRunner(const TaskExpiredCallback& on_task_expired)
: on_task_expired_(std::move(on_task_expired)) {}
CocoaTaskRunner::~CocoaTaskRunner() = default;
std::chrono::nanoseconds CocoaTaskRunner::ProcessTasks() {
const TaskTimePoint now = TaskTimePoint::clock::now();
std::vector<UIWidgetsTask> expired_tasks;
// Process expired tasks.
{
std::lock_guard<std::mutex> lock(task_queue_mutex_);
while (!task_queue_.empty()) {
const auto& top = task_queue_.top();
// If this task (and all tasks after this) has not yet expired, there is
// nothing more to do. Quit iterating.
if (top.fire_time > now) {
break;
}
// Make a record of the expired task. Do NOT service the task here
// because we are still holding onto the task queue mutex. We don't want
// other threads to block on posting tasks onto this thread till we are
// done processing expired tasks.
expired_tasks.push_back(task_queue_.top().task);
// Remove the tasks from the delayed tasks queue.
task_queue_.pop();
}
}
for (const auto& observer : task_observers_) {
observer.second();
}
// Fire expired tasks.
{
// Flushing tasks here without holing onto the task queue mutex.
for (const auto& task : expired_tasks) {
on_task_expired_(&task);
for (const auto& observer : task_observers_) {
observer.second();
}
}
}
if (!expired_tasks.empty()) {
return ProcessTasks();
}
// Calculate duration to sleep for on next iteration.
{
std::lock_guard<std::mutex> lock(task_queue_mutex_);
const auto next_wake = task_queue_.empty() ? TaskTimePoint::max()
: task_queue_.top().fire_time;
return std::min(next_wake - now, std::chrono::nanoseconds::max());
}
}
CocoaTaskRunner::TaskTimePoint CocoaTaskRunner::TimePointFromUIWidgetsTime(
uint64_t uiwidgets_target_time_nanos) {
const auto fml_now = fml::TimePoint::Now().ToEpochDelta().ToNanoseconds();
if (uiwidgets_target_time_nanos <= fml_now) {
return {};
}
const auto uiwidgets_duration = uiwidgets_target_time_nanos - fml_now;
const auto now = TaskTimePoint::clock::now();
return now + std::chrono::nanoseconds(uiwidgets_duration);
}
void CocoaTaskRunner::PostTask(UIWidgetsTask uiwidgets_task,
uint64_t uiwidgets_target_time_nanos) {
static std::atomic_uint64_t sGlobalTaskOrder(0);
Task task;
task.order = ++sGlobalTaskOrder;
task.fire_time = TimePointFromUIWidgetsTime(uiwidgets_target_time_nanos);
task.task = uiwidgets_task;
{
std::lock_guard<std::mutex> lock(task_queue_mutex_);
task_queue_.push(task);
}
}
void CocoaTaskRunner::AddTaskObserver(intptr_t key,
const fml::closure& callback) {
task_observers_[key] = callback;
}
void CocoaTaskRunner::RemoveTaskObserver(intptr_t key) {
task_observers_.erase(key);
}
} // namespace uiwidgets

66
engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.h


#pragma once
#include <flutter/fml/closure.h>
#include <chrono>
#include <deque>
#include <map>
#include <mutex>
#include <queue>
#include "shell/platform/embedder/embedder.h"
#include <flutter/fml/memory/ref_counted.h>
#include "runtime/mono_api.h"
namespace uiwidgets {
class CocoaTaskRunner {
public:
using TaskExpiredCallback = std::function<void(const UIWidgetsTask*)>;
CocoaTaskRunner(const TaskExpiredCallback& on_task_expired);
~CocoaTaskRunner();
std::chrono::nanoseconds ProcessTasks();
// Post a Flutter engine tasks to the event loop for delayed execution.
void PostTask(UIWidgetsTask uiwidgets_task,
uint64_t uiwidgets_target_time_nanos);
void AddTaskObserver(intptr_t key, const fml::closure& callback);
void RemoveTaskObserver(intptr_t key);
FML_DISALLOW_COPY_AND_ASSIGN(CocoaTaskRunner);
private:
using TaskTimePoint = std::chrono::steady_clock::time_point;
struct Task {
uint64_t order;
TaskTimePoint fire_time;
UIWidgetsTask task;
struct Comparer {
bool operator()(const Task& a, const Task& b) {
if (a.fire_time == b.fire_time) {
return a.order > b.order;
}
return a.fire_time > b.fire_time;
}
};
};
TaskExpiredCallback on_task_expired_;
std::mutex task_queue_mutex_;
std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
using TaskObservers = std::map<intptr_t, fml::closure>;
TaskObservers task_observers_;
static TaskTimePoint TimePointFromUIWidgetsTime(
uint64_t uiwidgets_target_time_nanos);
};
} // namespace uiwidgets

124
engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.h


#pragma once
#include <mutex>
#include <queue>
#include <map>
#include <flutter/fml/memory/ref_counted.h>
#include "shell/platform/unity/gfx_worker_task_runner.h"
#include "runtime/mono_api.h"
#include "cocoa_task_runner.h"
#include "unity_surface_manager.h"
namespace uiwidgets {
enum UIWidgetsWindowType {
InvalidPanel = 0,
GameObjectPanel = 1,
EditorWindowPanel = 2
};
struct MouseState {
bool state_is_down = false;
bool state_is_added = false;
uint64_t buttons = 0;
};
class UIWidgetsPanel : public fml::RefCountedThreadSafe<UIWidgetsPanel> {
FML_FRIEND_MAKE_REF_COUNTED(UIWidgetsPanel);
public:
typedef void (*EntrypointCallback)(Mono_Handle handle);
static fml::RefPtr<UIWidgetsPanel> Create(
Mono_Handle handle, UIWidgetsWindowType window_type, EntrypointCallback entrypoint_callback);
~UIWidgetsPanel();
void* OnEnable(size_t width, size_t height, float device_pixel_ratio, const char* streaming_assets_path, const char* settings);
void MonoEntrypoint();
void OnDisable();
void* OnRenderTexture(size_t width, size_t height,
float dpi);
bool ReleaseNativeRenderTexture();
int RegisterTexture(void* native_texture_ptr);
void UnregisterTexture(int texture_id);
std::chrono::nanoseconds ProcessMessages();
void ProcessVSync();
void VSyncCallback(intptr_t baton);
void SetEventLocationFromCursorPosition(UIWidgetsPointerEvent* event_data);
void OnKeyDown(int keyCode, bool isKeyDown);
void OnMouseMove(float x, float y);
void OnScroll(float x, float y, float px, float py);
void OnMouseDown(float x, float y, int button);
void OnMouseUp(float x, float y, int button);
void OnMouseLeave();
bool NeedUpdateByPlayerLoop();
bool NeedUpdateByEditorLoop();
private:
UIWidgetsPanel(Mono_Handle handle, UIWidgetsWindowType window_type, EntrypointCallback entrypoint_callback);
void CreateInternalUIWidgetsEngine(size_t width, size_t height, float device_pixel_ratio, const char* streaming_assets_path, const char* settings);
MouseState GetMouseState() { return mouse_state_; }
void ResetMouseState() { mouse_state_ = MouseState(); }
void SetMouseStateDown(bool is_down) { mouse_state_.state_is_down = is_down; }
void SetMouseStateAdded(bool is_added) {
mouse_state_.state_is_added = is_added;
}
void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; }
void SendMouseMove(float x, float y);
void SendMouseDown(float x, float y);
void SendMouseUp(float x, float y);
void SendMouseLeave();
void SendScroll(float delta_x, float delta_y, float px, float py);
void SetEventPhaseFromCursorButtonState(UIWidgetsPointerEvent* event_data);
void SendPointerEventWithData(const UIWidgetsPointerEvent& event_data);
Mono_Handle handle_;
EntrypointCallback entrypoint_callback_;
UIWidgetsWindowType window_type_;
std::unique_ptr<UnitySurfaceManager> surface_manager_;
std::unique_ptr<GfxWorkerTaskRunner> gfx_worker_task_runner_;
std::unique_ptr<CocoaTaskRunner> task_runner_;
UIWidgetsEngine engine_ = nullptr;
std::vector<intptr_t> vsync_batons_;
MouseState mouse_state_;
bool process_events_ = false;
};
} // namespace uiwidgets

529
engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm


#include <flutter/fml/synchronization/waitable_event.h>
#include <iostream>
#include "lib/ui/window/viewport_metrics.h"
#include "runtime/mono_api.h"
#include "shell/platform/embedder/embedder_engine.h"
#include "shell/platform/embedder/embedder.h"
#include "shell/common/switches.h"
#include "uiwidgets_system.h"
#include "uiwidgets_panel.h"
namespace uiwidgets {
fml::RefPtr<UIWidgetsPanel> UIWidgetsPanel::Create(
Mono_Handle handle, UIWidgetsWindowType window_type, EntrypointCallback entrypoint_callback) {
return fml::MakeRefCounted<UIWidgetsPanel>(handle, window_type, entrypoint_callback);
}
UIWidgetsPanel::UIWidgetsPanel(Mono_Handle handle,
UIWidgetsWindowType window_type,
EntrypointCallback entrypoint_callback)
: handle_(handle), window_type_(window_type), entrypoint_callback_(entrypoint_callback) {}
UIWidgetsPanel::~UIWidgetsPanel() = default;
bool UIWidgetsPanel::NeedUpdateByPlayerLoop() {
return window_type_ == GameObjectPanel;
}
bool UIWidgetsPanel::NeedUpdateByEditorLoop() {
return window_type_ == EditorWindowPanel;
}
void* UIWidgetsPanel::OnEnable(size_t width, size_t height, float device_pixel_ratio,
const char* streaming_assets_path, const char* settings)
{
surface_manager_ = std::make_unique<UnitySurfaceManager>(
UIWidgetsSystem::GetInstancePtr()->GetUnityInterfaces());
void* metal_tex = surface_manager_->CreateRenderTexture(width, height);
CreateInternalUIWidgetsEngine(width, height, device_pixel_ratio, streaming_assets_path, settings);
return metal_tex;
}
void UIWidgetsPanel::CreateInternalUIWidgetsEngine(size_t width, size_t height, float device_pixel_ratio,
const char* streaming_assets_path, const char* settings)
{
fml::AutoResetWaitableEvent latch;
std::thread::id gfx_worker_thread_id;
UIWidgetsSystem::GetInstancePtr()->PostTaskToGfxWorker(
[&latch, &gfx_worker_thread_id]() -> void {
gfx_worker_thread_id = std::this_thread::get_id();
latch.Signal();
});
latch.Wait();
gfx_worker_task_runner_ = std::make_unique<GfxWorkerTaskRunner>(
gfx_worker_thread_id, [this](const auto* task) {
if (UIWidgetsEngineRunTask(engine_, task) != kSuccess) {
std::cerr << "Could not post an gfx worker task." << std::endl;
}
});
//render task runner configs
UIWidgetsTaskRunnerDescription render_task_runner = {};
render_task_runner.struct_size = sizeof(UIWidgetsTaskRunnerDescription);
render_task_runner.identifier = 1;
render_task_runner.user_data = gfx_worker_task_runner_.get();
render_task_runner.runs_task_on_current_thread_callback =
[](void* user_data) -> bool {
return static_cast<GfxWorkerTaskRunner*>(user_data)
->RunsTasksOnCurrentThread();
};
render_task_runner.post_task_callback = [](UIWidgetsTask task,
uint64_t target_time_nanos,
void* user_data) -> void {
static_cast<GfxWorkerTaskRunner*>(user_data)->PostTask(task,
target_time_nanos);
};
//renderer config
UIWidgetsRendererConfig config = {};
config.type = kOpenGL;
config.open_gl.struct_size = sizeof(config.open_gl);
config.open_gl.clear_current = [](void* user_data) -> bool {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
return panel->surface_manager_->ClearCurrentContext();
};
config.open_gl.make_current = [](void* user_data) -> bool {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
return panel->surface_manager_->MakeCurrentContext();
};
config.open_gl.make_resource_current = [](void* user_data) -> bool {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
return panel->surface_manager_->MakeCurrentResourceContext();
};
config.open_gl.fbo_callback = [](void* user_data) -> uint32_t {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
return panel->surface_manager_->GetFbo();
};
config.open_gl.present = [](void* user_data) -> bool { return true; };
config.open_gl.fbo_reset_after_present = true;
//main thread task runner
task_runner_ = std::make_unique<CocoaTaskRunner>(
[this](const auto* task) {
if (UIWidgetsEngineRunTask(engine_, task) != kSuccess) {
std::cerr << "Could not post an engine task." << std::endl;
}
});
UIWidgetsTaskRunnerDescription main_task_runner = {};
main_task_runner.struct_size = sizeof(UIWidgetsTaskRunnerDescription);
main_task_runner.identifier = 2;
main_task_runner.user_data = task_runner_.get();
main_task_runner.runs_task_on_current_thread_callback = [](void* user_data) -> bool {
return [[NSThread currentThread] isMainThread];
};
main_task_runner.post_task_callback = [](UIWidgetsTask task, uint64_t target_time_nanos,
void* user_data) -> void {
static_cast<CocoaTaskRunner*>(user_data)->PostTask(task, target_time_nanos);
};
//setup custom task runners
UIWidgetsCustomTaskRunners custom_task_runners = {};
custom_task_runners.struct_size = sizeof(UIWidgetsCustomTaskRunners);
custom_task_runners.platform_task_runner = &main_task_runner;
custom_task_runners.ui_task_runner = &main_task_runner;
custom_task_runners.render_task_runner = &render_task_runner;
UIWidgetsProjectArgs args = {};
args.struct_size = sizeof(UIWidgetsProjectArgs);
args.assets_path = streaming_assets_path;
args.font_asset = settings;
//TODO: not support icu yet
std::string icu_path = std::string(streaming_assets_path) + "/icudtl.dat";
args.icu_data_path = icu_path.c_str();
args.command_line_argc = 0;
args.command_line_argv = nullptr;
args.platform_message_callback =
[](const UIWidgetsPlatformMessage* engine_message,
void* user_data) -> void {};
args.custom_task_runners = &custom_task_runners;
args.task_observer_add = [](intptr_t key, void* callback,
void* user_data) -> void {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
panel->task_runner_->AddTaskObserver(key, *static_cast<fml::closure*>(callback));
};
args.task_observer_remove = [](intptr_t key, void* user_data) -> void {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
panel->task_runner_->RemoveTaskObserver(key);
};
args.custom_mono_entrypoint = [](void* user_data) -> void {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
panel->MonoEntrypoint();
};
args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
auto* panel = static_cast<UIWidgetsPanel*>(user_data);
panel->VSyncCallback(baton);
};
args.initial_window_metrics.width = width;
args.initial_window_metrics.height = height;
args.initial_window_metrics.pixel_ratio = device_pixel_ratio;
UIWidgetsEngine engine = nullptr;
auto result = UIWidgetsEngineInitialize(&config, &args, this, &engine);
if (result != kSuccess || engine == nullptr) {
std::cerr << "Failed to start UIWidgets engine: error " << result
<< std::endl;
return;
}
engine_ = engine;
UIWidgetsEngineRunInitialized(engine);
UIWidgetsSystem::GetInstancePtr()->RegisterPanel(this);
process_events_ = true;
}
void UIWidgetsPanel::MonoEntrypoint() { entrypoint_callback_(handle_); }
void UIWidgetsPanel::OnDisable() {
// drain pending messages
ProcessMessages();
// drain pending vsync batons
ProcessVSync();
process_events_ = false;
UIWidgetsSystem::GetInstancePtr()->UnregisterPanel(this);
if (engine_) {
UIWidgetsEngineShutdown(engine_);
engine_ = nullptr;
}
gfx_worker_task_runner_ = nullptr;
task_runner_ = nullptr;
//release all resources
if (surface_manager_)
{
surface_manager_->ReleaseNativeRenderTexture();
surface_manager_ = nullptr;
}
}
void* UIWidgetsPanel::OnRenderTexture(size_t width,
size_t height, float device_pixel_ratio) {
ViewportMetrics metrics;
metrics.physical_width = static_cast<float>(width);
metrics.physical_height = static_cast<float>(height);
metrics.device_pixel_ratio = device_pixel_ratio;
reinterpret_cast<EmbedderEngine*>(engine_)->SetViewportMetrics(metrics);
return surface_manager_->CreateRenderTexture(width, height);
}
bool UIWidgetsPanel::ReleaseNativeRenderTexture() { return surface_manager_->ReleaseNativeRenderTexture(); }
int UIWidgetsPanel::RegisterTexture(void* native_texture_ptr) {
//TODO: add implementation
std::cerr << "registering external texture is not implemented for MacOS" << std::endl;
return 0;
}
void UIWidgetsPanel::UnregisterTexture(int texture_id) {
//TODO: add implementation
std::cerr << "unregistering external texture is not implemented for MacOS" << std::endl;
}
std::chrono::nanoseconds UIWidgetsPanel::ProcessMessages() {
return std::chrono::nanoseconds(task_runner_->ProcessTasks().count());
}
void UIWidgetsPanel::ProcessVSync() {
std::vector<intptr_t> batons;
vsync_batons_.swap(batons);
for (intptr_t baton : batons) {
reinterpret_cast<EmbedderEngine*>(engine_)->OnVsyncEvent(
baton, fml::TimePoint::Now(),
fml::TimePoint::Now() +
fml::TimeDelta::FromNanoseconds(1000000000 / 60));
}
}
void UIWidgetsPanel::VSyncCallback(intptr_t baton) {
vsync_batons_.push_back(baton);
}
void UIWidgetsPanel::SetEventPhaseFromCursorButtonState(
UIWidgetsPointerEvent* event_data) {
MouseState state = GetMouseState();
event_data->phase = state.buttons == 0
? state.state_is_down ? UIWidgetsPointerPhase::kUp
: UIWidgetsPointerPhase::kHover
: state.state_is_down ? UIWidgetsPointerPhase::kMove
: UIWidgetsPointerPhase::kDown;
}
void UIWidgetsPanel::SendMouseMove(float x, float y) {
UIWidgetsPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendMouseDown(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
SetMouseStateDown(true);
}
void UIWidgetsPanel::SendMouseUp(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
if (event.phase == UIWidgetsPointerPhase::kUp) {
SetMouseStateDown(false);
}
}
void UIWidgetsPanel::SendMouseLeave() {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kRemove;
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendScroll(float delta_x, float delta_y, float px, float py) {
UIWidgetsPointerEvent event = {};
// TODO: this is a native method, use unity position instead.
event.x = px;
event.y = py;
SetEventPhaseFromCursorButtonState(&event);
event.signal_kind = UIWidgetsPointerSignalKind::kUIWidgetsPointerSignalKindScroll;
// TODO: See if this can be queried from the OS; this value is chosen
// arbitrarily to get something that feels reasonable.
const int kScrollOffsetMultiplier = 20;
event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
event.scroll_delta_y = delta_y * kScrollOffsetMultiplier;
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendPointerEventWithData(
const UIWidgetsPointerEvent& event_data) {
MouseState mouse_state = GetMouseState();
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!mouse_state.state_is_added &&
event_data.phase != UIWidgetsPointerPhase::kAdd) {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
event.buttons = 0;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (mouse_state.state_is_added &&
event_data.phase == UIWidgetsPointerPhase::kAdd) {
return;
}
UIWidgetsPointerEvent event = event_data;
event.device_kind = kUIWidgetsPointerDeviceKindMouse;
event.buttons = mouse_state.buttons;
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
UIWidgetsEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == UIWidgetsPointerPhase::kAdd) {
SetMouseStateAdded(true);
} else if (event_data.phase == UIWidgetsPointerPhase::kRemove) {
SetMouseStateAdded(false);
ResetMouseState();
}
}
void UIWidgetsPanel::OnKeyDown(int keyCode, bool isKeyDown) {
if (process_events_) {
UIWidgetsPointerEvent event = {};
event.phase = isKeyDown ? UIWidgetsPointerPhase::kMouseDown : UIWidgetsPointerPhase::kMouseUp;
event.device_kind =
UIWidgetsPointerDeviceKind::kUIWidgetsPointerDeviceKindKeyboard;
event.buttons = keyCode;
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
UIWidgetsEngineSendPointerEvent(engine_, &event, 1);
}
}
void UIWidgetsPanel::OnMouseMove(float x, float y) {
if (process_events_) {
SendMouseMove(x, y);
}
}
void UIWidgetsPanel::OnScroll(float x, float y, float px, float py) {
if (process_events_) {
SendScroll(x, y, px, py);
}
}
static uint64_t ConvertToUIWidgetsButton(int button) {
switch (button) {
case -1:
return kUIWidgetsPointerButtonMousePrimary;
case -2:
return kUIWidgetsPointerButtonMouseSecondary;
case -3:
return kUIWidgetsPointerButtonMouseMiddle;
}
std::cerr << "Mouse button not recognized: " << button << std::endl;
return 0;
}
void UIWidgetsPanel::OnMouseDown(float x, float y, int button) {
if (process_events_) {
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons | uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseDown(x, y);
}
}
}
void UIWidgetsPanel::OnMouseUp(float x, float y, int button) {
if (process_events_) {
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons & ~uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseUp(x, y);
}
}
}
void UIWidgetsPanel::OnMouseLeave() {
if (process_events_) {
SendMouseLeave();
}
}
UIWIDGETS_API(UIWidgetsPanel*)
UIWidgetsPanel_constructor(
Mono_Handle handle, int windowType,
UIWidgetsPanel::EntrypointCallback entrypoint_callback) {
UIWidgetsWindowType window_type = static_cast<UIWidgetsWindowType>(windowType);
const auto panel = UIWidgetsPanel::Create(handle, window_type, entrypoint_callback);
panel->AddRef();
return panel.get();
}
UIWIDGETS_API(void) UIWidgetsPanel_dispose(UIWidgetsPanel* panel) {
panel->Release();
}
UIWIDGETS_API(void*)
UIWidgetsPanel_onEnable(UIWidgetsPanel* panel,
size_t width, size_t height, float device_pixel_ratio,
const char* streaming_assets_path,
const char* settings) {
return panel->OnEnable(width, height, device_pixel_ratio,
streaming_assets_path, settings);
}
UIWIDGETS_API(void) UIWidgetsPanel_onDisable(UIWidgetsPanel* panel) {
panel->OnDisable();
}
UIWIDGETS_API(bool) UIWidgetsPanel_releaseNativeTexture(UIWidgetsPanel* panel) {
return panel->ReleaseNativeRenderTexture();
}
UIWIDGETS_API(void*)
UIWidgetsPanel_onRenderTexture(UIWidgetsPanel* panel,
int width, int height, float dpi) {
return panel->OnRenderTexture(width, height, dpi);
}
UIWIDGETS_API(int)
UIWidgetsPanel_registerTexture(UIWidgetsPanel* panel,
void* native_texture_ptr) {
return panel->RegisterTexture(native_texture_ptr);
}
UIWIDGETS_API(void)
UIWidgetsPanel_unregisterTexture(UIWidgetsPanel* panel, int texture_id) {
panel->UnregisterTexture(texture_id);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onKey(UIWidgetsPanel* panel, int keyCode, bool isKeyDown) {
panel->OnKeyDown(keyCode, isKeyDown);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseDown(UIWidgetsPanel* panel, float x, float y,
int button) {
panel->OnMouseDown(x, y, button);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseUp(UIWidgetsPanel* panel, float x, float y, int button) {
panel->OnMouseUp(x, y, button);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y) {
panel->OnMouseMove(x, y);
}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseLeave(UIWidgetsPanel* panel) { panel->OnMouseLeave(); }
UIWIDGETS_API(void)
UIWidgetsPanel_onEditorUpdate(UIWidgetsPanel* panel) {
if (!panel->NeedUpdateByEditorLoop()) {
std::cerr << "only EditorWindowPanel can be updated using onEditorUpdate" << std::endl;
return;
}
//_Update
panel->ProcessMessages();
//_ProcessVSync
panel->ProcessVSync();
//_Wait
panel->ProcessMessages();
}
UIWIDGETS_API(void)
UIWidgetsPanel_onScroll(UIWidgetsPanel* panel, float x, float y, float px, float py) {
panel->OnScroll(x, y, px, py);
}
} // namespace uiwidgets

79
engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.h


#pragma once
#include <flutter/fml/closure.h>
#include <chrono>
#include <cstdarg>
#include <set>
#include <unordered_map>
#include <mutex>
#include "Unity/IUnityInterface.h"
#include "Unity/IUnityUIWidgets.h"
#include "flutter/fml/macros.h"
#include "runtime/mono_api.h"
namespace uiwidgets {
using TimePoint = std::chrono::steady_clock::time_point;
class UIWidgetsPanel;
class UIWidgetsSystem {
public:
UIWidgetsSystem();
~UIWidgetsSystem();
void RegisterPanel(UIWidgetsPanel* panel);
void UnregisterPanel(UIWidgetsPanel* panel);
void PostTaskToGfxWorker(const fml::closure& task);
void printf_console(const char* log, ...) {
va_list vl;
va_start(vl, log);
unity_uiwidgets_->printf_consolev(log, vl);
va_end(vl);
}
void BindUnityInterfaces(IUnityInterfaces* unity_interfaces);
void UnBindUnityInterfaces();
IUnityInterfaces* GetUnityInterfaces() { return unity_interfaces_; }
static UIWidgetsSystem* GetInstancePtr();
FML_DISALLOW_COPY_AND_ASSIGN(UIWidgetsSystem);
private:
UIWIDGETS_CALLBACK(void) _Update() { GetInstancePtr()->Update(); }
UIWIDGETS_CALLBACK(void) _Wait(long max_duration) {
GetInstancePtr()->Wait(std::chrono::nanoseconds(max_duration));
}
UIWIDGETS_CALLBACK(void) _VSync() { GetInstancePtr()->VSync(); }
UIWIDGETS_CALLBACK(void) _WakeUp() { GetInstancePtr()->WakeUp(); }
UIWIDGETS_CALLBACK(void) _GfxWorkerCallback(int eventId, void* data) {
GetInstancePtr()->GfxWorkerCallback(eventId, data);
}
void Update();
void Wait(std::chrono::nanoseconds max_duration);
void VSync();
void WakeUp();
void GfxWorkerCallback(int eventId, void* data);
IUnityInterfaces* unity_interfaces_ = nullptr;
UnityUIWidgets::IUnityUIWidgets* unity_uiwidgets_ = nullptr;
std::unordered_map<int, fml::closure> gfx_worker_tasks_;
int last_task_id_ = 0;
TimePoint next_uiwidgets_event_time_ = TimePoint::clock::now();
std::set<UIWidgetsPanel*> uiwidgets_panels_;
std::mutex task_mutex_;
};
} // namespace uiwidgets

106
engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.mm


#include "uiwidgets_system.h"
#include <algorithm>
#include <chrono>
#include "uiwidgets_panel.h"
namespace uiwidgets {
UIWidgetsSystem g_uiwidgets_system;
UIWidgetsSystem::UIWidgetsSystem() = default;
UIWidgetsSystem::~UIWidgetsSystem() = default;
void UIWidgetsSystem::RegisterPanel(UIWidgetsPanel* panel) {
uiwidgets_panels_.insert(panel);
}
void UIWidgetsSystem::UnregisterPanel(UIWidgetsPanel* panel) {
uiwidgets_panels_.erase(panel);
}
void UIWidgetsSystem::Wait(std::chrono::nanoseconds max_duration) {
Update();
std::chrono::nanoseconds wait_duration =
std::max(std::chrono::nanoseconds(0),
next_uiwidgets_event_time_ - TimePoint::clock::now());
wait_duration = std::min(max_duration, wait_duration);
//TODO: find a proper api similar to MsgWaitForMultipleObjects on Windows
// which will notify os to wait for the given period of time
}
void UIWidgetsSystem::Update() {
TimePoint next_event_time = TimePoint::max();
for (auto* uiwidgets_panel : uiwidgets_panels_) {
if (!uiwidgets_panel->NeedUpdateByPlayerLoop()) {
continue;
}
std::chrono::nanoseconds wait_duration = uiwidgets_panel->ProcessMessages();
if (wait_duration != std::chrono::nanoseconds::max()) {
next_event_time =
std::min(next_event_time, TimePoint::clock::now() + wait_duration);
}
}
next_uiwidgets_event_time_ = next_event_time;
}
void UIWidgetsSystem::VSync() {
for (auto* uiwidgets_panel : uiwidgets_panels_) {
if (!uiwidgets_panel->NeedUpdateByPlayerLoop()) {
continue;
}
uiwidgets_panel->ProcessVSync();
}
}
void UIWidgetsSystem::WakeUp() {}
void UIWidgetsSystem::GfxWorkerCallback(int eventId, void* data) {
const fml::closure task(std::move(gfx_worker_tasks_[eventId]));
{
std::scoped_lock lock(task_mutex_);
gfx_worker_tasks_.erase(eventId);
}
task();
}
void UIWidgetsSystem::PostTaskToGfxWorker(const fml::closure& task) {
{
std::scoped_lock lock(task_mutex_);
last_task_id_++;
gfx_worker_tasks_[last_task_id_] = task;
}
unity_uiwidgets_->IssuePluginEventAndData(&_GfxWorkerCallback, last_task_id_,
nullptr);
}
void UIWidgetsSystem::BindUnityInterfaces(IUnityInterfaces* unity_interfaces) {
unity_interfaces_ = unity_interfaces;
unity_uiwidgets_ = unity_interfaces_->Get<UnityUIWidgets::IUnityUIWidgets>();
unity_uiwidgets_->SetUpdateCallback(_Update);
unity_uiwidgets_->SetVSyncCallback(_VSync);
unity_uiwidgets_->SetWaitCallback(_Wait);
unity_uiwidgets_->SetWakeUpCallback(_WakeUp);
}
void UIWidgetsSystem::UnBindUnityInterfaces() {
unity_uiwidgets_->SetUpdateCallback(nullptr);
unity_uiwidgets_->SetVSyncCallback(nullptr);
unity_uiwidgets_->SetWaitCallback(nullptr);
unity_uiwidgets_->SetWakeUpCallback(nullptr);
unity_uiwidgets_ = nullptr;
unity_interfaces_ = nullptr;
}
UIWidgetsSystem* UIWidgetsSystem::GetInstancePtr() {
return &g_uiwidgets_system;
}
} // namespace uiwidgets

54
engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.h


#pragma once
#include <OpenGLES/EAGL.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include <OpenGLES/ES3/gl.h>
#include <UIKit/UIKit.h>
#include <Metal/Metal.h>
#include <CoreVideo/CoreVideo.h>
#include "Unity/IUnityInterface.h"
#include "flutter/fml/macros.h"
namespace uiwidgets {
class UnitySurfaceManager {
public:
UnitySurfaceManager(IUnityInterfaces* unity_interfaces);
~UnitySurfaceManager();
void* CreateRenderTexture(size_t width, size_t height);
void ReleaseNativeRenderContext();
bool ReleaseNativeRenderTexture();
bool ClearCurrentContext();
bool MakeCurrentContext();
bool MakeCurrentResourceContext();
uint32_t GetFbo();
private:
//pixel buffer handles
CVPixelBufferRef pixelbuffer_ref = nullptr;
//gl handlers
EAGLContext *gl_context_ = NULL;
EAGLContext *gl_resource_context_ = NULL;
GLuint default_fbo_ = 0;
GLuint gl_tex_ = 0;
CVOpenGLESTextureCacheRef gl_tex_cache_ref_ = nullptr;
CVOpenGLESTextureRef gl_tex_ref_ = nullptr;
//metal handlers
id<MTLDevice> metal_device_;
id<MTLTexture> metal_tex_;
CVMetalTextureRef metal_tex_ref_ = nullptr;
CVMetalTextureCacheRef metal_tex_cache_ref_ = nullptr;
};
} // namespace uiwidgets

200
engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.mm


#include "unity_surface_manager.h"
#include <flutter/fml/logging.h>
#include "Unity/IUnityGraphics.h"
#include "Unity/IUnityGraphicsMetal.h"
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
namespace uiwidgets {
UnitySurfaceManager::UnitySurfaceManager(IUnityInterfaces* unity_interfaces)
{
FML_DCHECK(metal_device_ == nullptr);
//get main gfx device (metal)
auto* graphics = unity_interfaces->Get<IUnityGraphics>();
FML_DCHECK(graphics->GetRenderer() == kUnityGfxRendererMetal);
auto* metalGraphics = unity_interfaces->Get<IUnityGraphicsMetalV1>();
metal_device_ = metalGraphics->MetalDevice();
//create opengl context
FML_DCHECK(!gl_context_);
FML_DCHECK(!gl_resource_context_);
gl_context_ = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
gl_resource_context_ = [[EAGLContext alloc] initWithAPI:[gl_context_ API] sharegroup: [gl_context_ sharegroup]];
FML_DCHECK(gl_context_ != nullptr && gl_resource_context_ != nullptr);
}
UnitySurfaceManager::~UnitySurfaceManager() { ReleaseNativeRenderContext(); }
void* UnitySurfaceManager::CreateRenderTexture(size_t width, size_t height)
{
//Constants
const int ConstCVPixelFormat = kCVPixelFormatType_32BGRA;
const MTLPixelFormat ConstMetalViewPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
const GLuint ConstGLInternalFormat = GL_RGBA;
const GLuint ConstGLFormat = GL_BGRA_EXT;
const GLuint ConstGLType = GL_UNSIGNED_INT_8_8_8_8_REV;
//render context must be available
FML_DCHECK(metal_device_ != nullptr && gl_context_ != nullptr);
//render textures must be released already
FML_DCHECK(pixelbuffer_ref == nullptr && default_fbo_ == 0 && gl_tex_ == 0 && gl_tex_cache_ref_ == nullptr && gl_tex_ref_ == nullptr && metal_tex_ == nullptr && metal_tex_ref_ == nullptr && metal_tex_cache_ref_ == nullptr);
CVReturn cvret;
//create pixel buffer
NSDictionary* cvBufferProperties = @{
(__bridge NSString*)kCVPixelBufferOpenGLCompatibilityKey : @YES,
(__bridge NSString*)kCVPixelBufferMetalCompatibilityKey : @YES,
};
cvret = CVPixelBufferCreate(kCFAllocatorDefault,
width, height,
ConstCVPixelFormat,
(__bridge CFDictionaryRef)cvBufferProperties,
&pixelbuffer_ref);
FML_DCHECK(cvret == kCVReturnSuccess);
//create metal context
// 1. Create a Metal Core Video texture cache from the pixel buffer.
cvret = CVMetalTextureCacheCreate(
kCFAllocatorDefault,
nil,
metal_device_,
nil,
&metal_tex_cache_ref_);
FML_DCHECK(cvret == kCVReturnSuccess);
// 2. Create a CoreVideo pixel buffer backed Metal texture image from the texture cache.
cvret = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
metal_tex_cache_ref_,
pixelbuffer_ref, nil,
ConstMetalViewPixelFormat,
width, height,
0,
&metal_tex_ref_);
FML_DCHECK(cvret == kCVReturnSuccess);
// 3. Get a Metal texture using the CoreVideo Metal texture reference.
metal_tex_ = CVMetalTextureGetTexture(metal_tex_ref_);
FML_DCHECK(metal_tex_ != nullptr);
//create GL Texture
// 1. Create an OpenGL ES CoreVideo texture cache from the pixel buffer.
cvret = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault,
nil,
gl_context_,
nil,
&gl_tex_cache_ref_);
FML_DCHECK(cvret == kCVReturnSuccess);
// 2. Create a CVPixelBuffer-backed OpenGL ES texture image from the texture cache.
cvret = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
gl_tex_cache_ref_,
pixelbuffer_ref,
nil,
GL_TEXTURE_2D,
ConstGLInternalFormat,
width, height,
ConstGLFormat,
ConstGLType,
0,
&gl_tex_ref_);
FML_DCHECK(cvret == kCVReturnSuccess);
// 3. Get an OpenGL ES texture name from the CVPixelBuffer-backed OpenGL ES texture image.
gl_tex_ = CVOpenGLESTextureGetName(gl_tex_ref_);
//initialize gl renderer
[EAGLContext setCurrentContext:gl_context_];
glGenFramebuffers(1, &default_fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, default_fbo_);
const GLenum texType = GL_TEXTURE_2D;
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texType, gl_tex_, 0);
return (__bridge void*)metal_tex_;
}
bool UnitySurfaceManager::ClearCurrentContext()
{
[EAGLContext setCurrentContext:nil];
return true;
}
bool UnitySurfaceManager::MakeCurrentContext()
{
[EAGLContext setCurrentContext:gl_context_];
return true;
}
bool UnitySurfaceManager::MakeCurrentResourceContext()
{
[EAGLContext setCurrentContext:gl_resource_context_];
return true;
}
uint32_t UnitySurfaceManager::GetFbo()
{
return default_fbo_;
}
void UnitySurfaceManager::ReleaseNativeRenderContext()
{
FML_DCHECK(gl_resource_context_);
gl_resource_context_ = nullptr;
FML_DCHECK(gl_context_);
gl_context_ = nullptr;
[EAGLContext setCurrentContext:nil];
FML_DCHECK(metal_device_ != nullptr);
metal_device_ = nullptr;
}
bool UnitySurfaceManager::ReleaseNativeRenderTexture()
{
//release gl resources
FML_DCHECK(default_fbo_ != 0);
glDeleteFramebuffers(1, &default_fbo_);
default_fbo_ = 0;
FML_DCHECK(gl_tex_ != 0);
glDeleteTextures(1, &gl_tex_);
gl_tex_ = 0;
CFRelease(gl_tex_cache_ref_);
gl_tex_cache_ref_ = nullptr;
CFRelease(gl_tex_ref_);
gl_tex_ref_ = nullptr;
//release metal resources
//since ARC is enabled by default, no need to release the texture
metal_tex_ = nullptr;
CFRelease(metal_tex_ref_);
metal_tex_ref_ = nullptr;
CFRelease(metal_tex_cache_ref_);
metal_tex_cache_ref_ = nullptr;
//release cv pixelbuffer
CVPixelBufferRelease(pixelbuffer_ref);
pixelbuffer_ref = nullptr;
return true;
}
} // namespace uiwidgets
正在加载...
取消
保存