浏览代码
Merge branch 'zxw/skia_mac_dev' of github.com:Unity-Technologies/com.unity.uiwidgets into dev_1.17.5
Merge branch 'zxw/skia_mac_dev' of github.com:Unity-Technologies/com.unity.uiwidgets into dev_1.17.5
# Conflicts: # com.unity.uiwidgets/Runtime/cupertino.meta # com.unity.uiwidgets/Runtime/semantics.meta/siyaoH-1.17-PlatformMessage
xingweizhu
4 年前
当前提交
e1cd46a6
共有 45 个文件被更改,包括 1770 次插入 和 271 次删除
-
175com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanel.cs
-
253engine/Build.bee.cs
-
101engine/README.md
-
90engine/src/engine.cc
-
2engine/src/lib/ui/text/asset_manager_font_provider.cc
-
2engine/src/lib/ui/text/font_collection.cc
-
2engine/src/lib/ui/text/icu_util.cc
-
1engine/src/lib/ui/text/paragraph.cc
-
1engine/src/lib/ui/text/paragraph_builder.cc
-
4engine/src/lib/ui/window/pointer_data.h
-
10engine/src/runtime/mono_api.cc
-
2engine/src/shell/platform/unity/gfx_worker_task_runner.h
-
79engine/src/shell/platform/unity/uiwidgets_system.h
-
4engine/third_party/Unity/IUnityUIWidgets.h
-
2engine/src/shell/platform/unity/windows/uiwidgets_panel.h
-
106engine/src/shell/platform/unity/darwin/macos/cocoa_task_runner.cc
-
66engine/src/shell/platform/unity/darwin/macos/cocoa_task_runner.h
-
105engine/src/shell/platform/unity/darwin/macos/uiwidgets_panel.h
-
448engine/src/shell/platform/unity/darwin/macos/uiwidgets_panel.mm
-
76engine/src/shell/platform/unity/darwin/macos/uiwidgets_system.h
-
94engine/src/shell/platform/unity/darwin/macos/uiwidgets_system.mm
-
49engine/src/shell/platform/unity/darwin/macos/unity_surface_manager.h
-
192engine/src/shell/platform/unity/darwin/macos/unity_surface_manager.mm
-
76engine/src/shell/platform/unity/windows/uiwidgets_system.h
-
101engine/src/tests/render_engine.cc
-
0/engine/src/shell/platform/unity/windows/uiwidgets_panel.cc
-
0/engine/src/shell/platform/unity/windows/uiwidgets_panel.h
-
0/engine/src/shell/platform/unity/windows/unity_surface_manager.cc
-
0/engine/src/shell/platform/unity/windows/unity_surface_manager.h
-
0/engine/src/shell/platform/unity/windows/win32_task_runner.h
-
0/engine/src/shell/platform/unity/windows/unity_external_texture_gl.cc
-
0/engine/src/shell/platform/unity/windows/unity_external_texture_gl.h
-
0/engine/src/shell/platform/unity/windows/win32_task_runner.cc
-
0/engine/src/shell/platform/unity/windows/uiwidgets_system.cc
-
0/engine/src/tests/render_api.cc
-
0/engine/src/tests/render_api.h
-
0/engine/src/tests/render_api_d3d11.cc
-
0/engine/src/tests/render_api_opengles.cc
-
0/engine/src/tests/render_api_vulkan.cc
-
0/engine/src/tests/TestLoadICU.cpp
-
0/engine/src/tests/TestLoadICU.h
|
|||
#pragma once |
|||
|
|||
#include <flutter/fml/closure.h> |
|||
|
|||
#include <chrono> |
|||
#include <cstdarg> |
|||
#include <set> |
|||
#include <unordered_map> |
|||
|
|||
#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_; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|||
#ifdef __APPLE__ |
|||
#include "shell/platform/unity/darwin/macos/uiwidgets_system.h" |
|||
#elif _WIN64 |
|||
#include "shell/platform/unity/windows/uiwidgets_system.h" |
|||
#endif |
|
|||
#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
|
|
|||
#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 |
|
|||
#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 { |
|||
|
|||
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, 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 OnMouseMove(float x, float y); |
|||
|
|||
void OnMouseDown(float x, float y, int button); |
|||
|
|||
void OnMouseUp(float x, float y, int button); |
|||
|
|||
void OnMouseLeave(); |
|||
|
|||
private: |
|||
UIWidgetsPanel(Mono_Handle handle, 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 SetEventPhaseFromCursorButtonState(UIWidgetsPointerEvent* event_data); |
|||
|
|||
void SendPointerEventWithData(const UIWidgetsPointerEvent& event_data); |
|||
|
|||
Mono_Handle handle_; |
|||
EntrypointCallback entrypoint_callback_; |
|||
|
|||
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 |
|
|||
#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, EntrypointCallback entrypoint_callback) { |
|||
return fml::MakeRefCounted<UIWidgetsPanel>(handle, entrypoint_callback); |
|||
} |
|||
|
|||
UIWidgetsPanel::UIWidgetsPanel(Mono_Handle handle, |
|||
EntrypointCallback entrypoint_callback) |
|||
: handle_(handle), entrypoint_callback_(entrypoint_callback) {} |
|||
|
|||
UIWidgetsPanel::~UIWidgetsPanel() = default; |
|||
|
|||
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 |
|||
//args.icu_mapper = GetICUStaticMapping; |
|||
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 |
|||
return 0; |
|||
} |
|||
|
|||
void UIWidgetsPanel::UnregisterTexture(int texture_id) { |
|||
//TODO: add implementation |
|||
} |
|||
|
|||
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::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::OnMouseMove(float x, float y) { |
|||
if (process_events_) { |
|||
SendMouseMove(x, y); |
|||
} |
|||
} |
|||
|
|||
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, |
|||
UIWidgetsPanel::EntrypointCallback entrypoint_callback) { |
|||
const auto panel = UIWidgetsPanel::Create(handle, 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_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(); } |
|||
|
|||
} // namespace uiwidgets |
|
|||
#pragma once |
|||
|
|||
#include <flutter/fml/closure.h> |
|||
|
|||
#include <chrono> |
|||
#include <cstdarg> |
|||
#include <set> |
|||
#include <unordered_map> |
|||
|
|||
#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_; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#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_) { |
|||
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_) { |
|||
uiwidgets_panel->ProcessVSync(); |
|||
} |
|||
} |
|||
|
|||
void UIWidgetsSystem::WakeUp() {} |
|||
|
|||
void UIWidgetsSystem::GfxWorkerCallback(int eventId, void* data) { |
|||
const fml::closure task(std::move(gfx_worker_tasks_[eventId])); |
|||
gfx_worker_tasks_.erase(eventId); |
|||
|
|||
task(); |
|||
} |
|||
|
|||
void UIWidgetsSystem::PostTaskToGfxWorker(const fml::closure& task) { |
|||
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 |
|
|||
#pragma once |
|||
|
|||
#include <OpenGL/gl3.h> |
|||
#include <AppKit/AppKit.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; |
|||
//openGL handlers |
|||
NSOpenGLContext *gl_context_ = NULL; |
|||
NSOpenGLContext *gl_resource_context_ = NULL; |
|||
GLuint default_fbo_ = 0; |
|||
GLuint gl_tex_ = 0; |
|||
CVOpenGLTextureCacheRef gl_tex_cache_ref_ = nullptr; |
|||
CVOpenGLTextureRef 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 |
|
|||
#include "unity_surface_manager.h" |
|||
#include <flutter/fml/logging.h> |
|||
|
|||
#include "Unity/IUnityGraphics.h" |
|||
#include "Unity/IUnityGraphicsMetal.h" |
|||
|
|||
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_); |
|||
|
|||
NSOpenGLPixelFormatAttribute attrs[] = |
|||
{ |
|||
NSOpenGLPFAAccelerated, |
|||
0 |
|||
}; |
|||
|
|||
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; |
|||
gl_context_ = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; |
|||
gl_resource_context_ = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:gl_context_]; |
|||
|
|||
FML_DCHECK(gl_context_ != nullptr && gl_resource_context_ != nullptr); |
|||
} |
|||
|
|||
UnitySurfaceManager::~UnitySurfaceManager() { ReleaseNativeRenderContext(); } |
|||
|
|||
void* UnitySurfaceManager::CreateRenderTexture(size_t width, size_t height) |
|||
{ |
|||
//Constants |
|||
const MTLPixelFormat ConstMetalViewPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; |
|||
const int ConstCVPixelFormat = kCVPixelFormatType_32BGRA; |
|||
const GLuint ConstGLInternalFormat = GL_SRGB8_ALPHA8; |
|||
const GLuint ConstGLFormat = GL_BGRA; |
|||
const GLuint ConstGLType = GL_UNSIGNED_INT_8_8_8_8_REV; |
|||
|
|||
//render context must be available |
|||
FML_DCHECK(metal_device_ != nullptr && gl_context_ != nullptr && gl_resource_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); |
|||
//create pixel buffer |
|||
auto gl_pixelformat_ = gl_context_.pixelFormat.CGLPixelFormatObj; |
|||
|
|||
NSDictionary* cvBufferProperties = @{ |
|||
(__bridge NSString*)kCVPixelBufferOpenGLCompatibilityKey : @YES, |
|||
(__bridge NSString*)kCVPixelBufferMetalCompatibilityKey : @YES, |
|||
}; |
|||
|
|||
CVReturn cvret = CVPixelBufferCreate(kCFAllocatorDefault, |
|||
width, height, |
|||
ConstCVPixelFormat, |
|||
(__bridge CFDictionaryRef)cvBufferProperties, |
|||
&pixelbuffer_ref); |
|||
FML_DCHECK(cvret == kCVReturnSuccess); |
|||
|
|||
//create metal texture |
|||
cvret = CVMetalTextureCacheCreate( |
|||
kCFAllocatorDefault, |
|||
nil, |
|||
metal_device_, |
|||
nil, |
|||
&metal_tex_cache_ref_); |
|||
FML_DCHECK(cvret == kCVReturnSuccess); |
|||
|
|||
cvret = CVMetalTextureCacheCreateTextureFromImage( |
|||
kCFAllocatorDefault, |
|||
metal_tex_cache_ref_, |
|||
pixelbuffer_ref, nil, |
|||
ConstMetalViewPixelFormat, |
|||
width, height, |
|||
0, |
|||
&metal_tex_ref_); |
|||
FML_DCHECK(cvret == kCVReturnSuccess); |
|||
|
|||
metal_tex_ = CVMetalTextureGetTexture(metal_tex_ref_); |
|||
FML_DCHECK(metal_tex_ != nullptr); |
|||
|
|||
//create opengl texture |
|||
cvret = CVOpenGLTextureCacheCreate( |
|||
kCFAllocatorDefault, |
|||
nil, |
|||
gl_context_.CGLContextObj, |
|||
gl_pixelformat_, |
|||
nil, |
|||
&gl_tex_cache_ref_); |
|||
FML_DCHECK(cvret == kCVReturnSuccess); |
|||
|
|||
cvret = CVOpenGLTextureCacheCreateTextureFromImage( |
|||
kCFAllocatorDefault, |
|||
gl_tex_cache_ref_, |
|||
pixelbuffer_ref, |
|||
nil, |
|||
&gl_tex_ref_); |
|||
FML_DCHECK(cvret == kCVReturnSuccess); |
|||
|
|||
gl_tex_ = CVOpenGLTextureGetName(gl_tex_ref_); |
|||
|
|||
//initialize gl renderer |
|||
[gl_context_ makeCurrentContext]; |
|||
glGenFramebuffers(1, &default_fbo_); |
|||
glBindFramebuffer(GL_FRAMEBUFFER, default_fbo_); |
|||
|
|||
const GLenum texType = GL_TEXTURE_RECTANGLE; |
|||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texType, gl_tex_, 0); |
|||
|
|||
return (__bridge void*)metal_tex_; |
|||
} |
|||
|
|||
bool UnitySurfaceManager::ClearCurrentContext() |
|||
{ |
|||
[NSOpenGLContext clearCurrentContext]; |
|||
return true; |
|||
} |
|||
|
|||
bool UnitySurfaceManager::MakeCurrentContext() |
|||
{ |
|||
[gl_context_ makeCurrentContext]; |
|||
return true; |
|||
} |
|||
|
|||
bool UnitySurfaceManager::MakeCurrentResourceContext() |
|||
{ |
|||
[gl_resource_context_ makeCurrentContext]; |
|||
return true; |
|||
} |
|||
|
|||
uint32_t UnitySurfaceManager::GetFbo() |
|||
{ |
|||
return default_fbo_; |
|||
} |
|||
|
|||
void UnitySurfaceManager::ReleaseNativeRenderContext() |
|||
{ |
|||
FML_DCHECK(gl_resource_context_); |
|||
CGLReleaseContext(gl_resource_context_.CGLContextObj); |
|||
gl_resource_context_ = nullptr; |
|||
|
|||
FML_DCHECK(gl_context_); |
|||
CGLReleaseContext(gl_context_.CGLContextObj); |
|||
gl_context_ = nullptr; |
|||
|
|||
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 |
|
|||
#pragma once |
|||
|
|||
#include <flutter/fml/closure.h> |
|||
|
|||
#include <chrono> |
|||
#include <cstdarg> |
|||
#include <set> |
|||
#include <unordered_map> |
|||
|
|||
#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_; |
|||
}; |
|||
|
|||
} // namespace uiwidgets |
|
|||
#include <assert.h>
|
|||
|
|||
#include "Unity/IUnityGraphics.h"
|
|||
#include "Unity/IUnityUIWidgets.h"
|
|||
#include "include/core/SkSurface.h"
|
|||
#include "include/gpu/GrBackendSurface.h"
|
|||
|
|||
|
|||
static IUnityInterfaces* s_UnityInterfaces = NULL; |
|||
static IUnityGraphics* s_Graphics = NULL; |
|||
static UnityGfxRenderer s_DeviceType = kUnityGfxRendererNull; |
|||
static RenderAPI* s_CurrentAPI = NULL; |
|||
|
|||
static void UNITY_INTERFACE_API |
|||
OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType) { |
|||
if (eventType == kUnityGfxDeviceEventInitialize) { |
|||
assert(s_CurrentAPI == NULL); |
|||
s_DeviceType = s_Graphics->GetRenderer(); |
|||
s_CurrentAPI = CreateRenderAPI(s_DeviceType); |
|||
} |
|||
|
|||
if (s_CurrentAPI) { |
|||
s_CurrentAPI->ProcessDeviceEvent(eventType, s_UnityInterfaces); |
|||
} |
|||
|
|||
if (eventType == kUnityGfxDeviceEventShutdown) { |
|||
delete s_CurrentAPI; |
|||
s_CurrentAPI = NULL; |
|||
s_DeviceType = kUnityGfxRendererNull; |
|||
} |
|||
} |
|||
|
|||
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API |
|||
UnityPluginLoad(IUnityInterfaces* unityInterfaces) { |
|||
//do nothing
|
|||
} |
|||
|
|||
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload() { |
|||
//do nothing
|
|||
} |
|||
|
|||
// --------------------------------------------------------------------------
|
|||
// SetTextureFromUnity, an example function we export which is called by one of
|
|||
// the scripts.
|
|||
|
|||
|
|||
extern "C" UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API |
|||
CreateTexture(int w, int h) { |
|||
if (s_CurrentAPI == NULL) { |
|||
return NULL; |
|||
} |
|||
|
|||
return s_CurrentAPI->CreateTexture(w, h); |
|||
} |
|||
|
|||
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API |
|||
CreateTexture1(void *ptr, int w, int h) { |
|||
if (s_CurrentAPI == NULL) { |
|||
return; |
|||
} |
|||
|
|||
s_CurrentAPI->CreateTexture1(ptr, w, h); |
|||
} |
|||
|
|||
extern "C" UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API |
|||
SetImageTexture(void *ptr) { |
|||
if (s_CurrentAPI == NULL) { |
|||
return; |
|||
} |
|||
|
|||
return s_CurrentAPI->SetImageTexture(ptr); |
|||
} |
|||
|
|||
extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API Draw123() { |
|||
if (s_CurrentAPI == NULL) { |
|||
return; |
|||
} |
|||
|
|||
s_CurrentAPI->Draw(); |
|||
} |
|||
|
|||
static void UNITY_INTERFACE_API OnRenderEvent(int eventID) { |
|||
// Unknown / unsupported graphics device type? Do nothing
|
|||
if (s_CurrentAPI == NULL) { |
|||
return; |
|||
} |
|||
|
|||
if (eventID == 1) { |
|||
s_CurrentAPI->Draw(); |
|||
} else if (eventID == 2) { |
|||
s_CurrentAPI->PreDraw(); |
|||
} else if (eventID == 3) { |
|||
s_CurrentAPI->PostDraw(); |
|||
} |
|||
} |
|||
|
|||
extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API |
|||
GetRenderEventFunc() { |
|||
return OnRenderEvent; |
|||
} |
|||
|
撰写
预览
正在加载...
取消
保存
Reference in new issue