xingwei.zhu
4 年前
当前提交
564e384b
共有 16 个文件被更改,包括 1546 次插入 和 8 次删除
-
4com.unity.uiwidgets/Runtime/engine2/UIWidgetsPanelWrapper.cs
-
2com.unity.uiwidgets/Runtime/material/material_state.cs
-
2com.unity.uiwidgets/Runtime/rendering/debug_overflow_indicator.cs
-
256engine/Build.bee.cs
-
26engine/src/external/ios/CustomAppController.m
-
106engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.cc
-
66engine/src/shell/platform/unity/darwin/ios/cocoa_task_runner.h
-
124engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.h
-
529engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm
-
79engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.h
-
106engine/src/shell/platform/unity/darwin/ios/uiwidgets_system.mm
-
54engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.h
-
200engine/src/shell/platform/unity/darwin/ios/unity_surface_manager.mm
|
|||
#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 |
|
|||
#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 { |
|||
|
|||
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 |
|
|||
#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 |
|
|||
#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 |
|
|||
#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 |
|
|||
#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 |
|
|||
#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 |
撰写
预览
正在加载...
取消
保存
Reference in new issue