未验证 提交 4e344e6c 编写于 作者: C Chinmay Garde 提交者: GitHub

Wire up custom event loop interop for the GLFW embedder. (#9089)

上级 54c62263
......@@ -918,6 +918,8 @@ FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutte
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/flutter_window_controller.h
FILE: ../../../flutter/shell/platform/glfw/client_wrapper/include/flutter/plugin_registrar_glfw.h
FILE: ../../../flutter/shell/platform/glfw/flutter_glfw.cc
FILE: ../../../flutter/shell/platform/glfw/glfw_event_loop.cc
FILE: ../../../flutter/shell/platform/glfw/glfw_event_loop.h
FILE: ../../../flutter/shell/platform/glfw/key_event_handler.cc
FILE: ../../../flutter/shell/platform/glfw/key_event_handler.h
FILE: ../../../flutter/shell/platform/glfw/keyboard_hook_handler.h
......
......@@ -33,6 +33,8 @@ source_set("flutter_glfw_headers") {
source_set("flutter_glfw") {
sources = [
"flutter_glfw.cc",
"glfw_event_loop.cc",
"glfw_event_loop.h",
"key_event_handler.cc",
"key_event_handler.h",
"keyboard_hook_handler.h",
......@@ -65,6 +67,11 @@ source_set("flutter_glfw") {
"$flutter_root/shell/platform/linux/config:gtk3",
"$flutter_root/shell/platform/linux/config:x11",
]
} else if (is_mac) {
libs = [
"CoreVideo.framework",
"IOKit.framework",
]
}
}
......
......@@ -15,6 +15,7 @@
#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h"
#include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/glfw/glfw_event_loop.h"
#include "flutter/shell/platform/glfw/key_event_handler.h"
#include "flutter/shell/platform/glfw/keyboard_hook_handler.h"
#include "flutter/shell/platform/glfw/platform_handler.h"
......@@ -79,8 +80,11 @@ struct FlutterDesktopWindowControllerState {
// Handler for the flutter/platform channel.
std::unique_ptr<flutter::PlatformHandler> platform_handler;
// Whether or not the pointer has been added (or if tracking is enabled, has
// been added since it was last removed).
// The event loop for the main thread that allows for delayed task execution.
std::unique_ptr<flutter::GLFWEventLoop> event_loop;
// Whether or not the pointer has been added (or if tracking is enabled,
// has been added since it was last removed).
bool pointer_currently_added = false;
// The screen coordinates per inch on the primary monitor. Defaults to a sane
......@@ -489,11 +493,13 @@ static void GLFWErrorCallback(int error_code, const char* description) {
// provided).
//
// Returns a caller-owned pointer to the engine.
static FlutterEngine RunFlutterEngine(GLFWwindow* window,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t arguments_count) {
static FlutterEngine RunFlutterEngine(
GLFWwindow* window,
const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t arguments_count,
const FlutterCustomTaskRunners* custom_task_runners) {
// FlutterProjectArgs is expecting a full argv, so when processing it for
// flags the first item is treated as the executable and ignored. Add a dummy
// value so that all provided arguments are used.
......@@ -528,6 +534,7 @@ static FlutterEngine RunFlutterEngine(GLFWwindow* window,
args.command_line_argc = static_cast<int>(argv.size());
args.command_line_argv = &argv[0];
args.platform_message_callback = GLFWOnFlutterPlatformMessage;
args.custom_task_runners = custom_task_runners;
FlutterEngine engine = nullptr;
auto result =
FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, window, &engine);
......@@ -578,9 +585,38 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow(
// GLFWMakeResourceContextCurrent immediately.
state->resource_window = CreateShareWindowForWindow(window);
// Create an event loop for the window. It is not running yet.
state->event_loop = std::make_unique<flutter::GLFWEventLoop>(
std::this_thread::get_id(), // main GLFW thread
[state = state.get()](const auto* task) {
if (FlutterEngineRunTask(state->engine, task) != kSuccess) {
std::cerr << "Could not post an engine task." << std::endl;
}
});
// Configure task runner interop.
FlutterTaskRunnerDescription platform_task_runner = {};
platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
platform_task_runner.user_data = state.get();
platform_task_runner.runs_task_on_current_thread_callback =
[](void* state) -> bool {
return reinterpret_cast<FlutterDesktopWindowControllerState*>(state)
->event_loop->RunsTasksOnCurrentThread();
};
platform_task_runner.post_task_callback =
[](FlutterTask task, uint64_t target_time_nanos, void* state) -> void {
reinterpret_cast<FlutterDesktopWindowControllerState*>(state)
->event_loop->PostTask(task, target_time_nanos);
};
FlutterCustomTaskRunners custom_task_runners = {};
custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
custom_task_runners.platform_task_runner = &platform_task_runner;
// Start the engine.
state->engine = RunFlutterEngine(window, assets_path, icu_data_path,
arguments, argument_count);
state->engine =
RunFlutterEngine(window, assets_path, icu_data_path, arguments,
argument_count, &custom_task_runners);
if (state->engine == nullptr) {
return nullptr;
}
......@@ -704,15 +740,17 @@ void FlutterDesktopRunWindowLoop(FlutterDesktopWindowControllerRef controller) {
// Necessary for GTK thread safety.
XInitThreads();
#endif
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
auto wait_duration = std::chrono::milliseconds::max();
#ifdef FLUTTER_USE_GTK
// If we are not using GTK, there is no point in waking up.
wait_duration = std::chrono::milliseconds(10);
if (gtk_events_pending()) {
gtk_main_iteration();
}
#endif
// TODO(awdavies): This will be deprecated soon.
__FlutterEngineFlushPendingTasksNow();
controller->event_loop->WaitForEvents(wait_duration);
}
FlutterDesktopDestroyWindow(controller);
}
......@@ -738,8 +776,9 @@ FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path,
const char* icu_data_path,
const char** arguments,
size_t argument_count) {
auto engine = RunFlutterEngine(nullptr, assets_path, icu_data_path, arguments,
argument_count);
auto engine =
RunFlutterEngine(nullptr, assets_path, icu_data_path, arguments,
argument_count, nullptr /* custom task runners */);
if (engine == nullptr) {
return nullptr;
}
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/glfw/glfw_event_loop.h"
#include <GLFW/glfw3.h>
#include <atomic>
#include <utility>
namespace flutter {
GLFWEventLoop::GLFWEventLoop(std::thread::id main_thread_id,
TaskExpiredCallback on_task_expired)
: main_thread_id_(main_thread_id),
on_task_expired_(std::move(on_task_expired)) {}
GLFWEventLoop::~GLFWEventLoop() = default;
bool GLFWEventLoop::RunsTasksOnCurrentThread() const {
return std::this_thread::get_id() == main_thread_id_;
}
void GLFWEventLoop::WaitForEvents(std::chrono::nanoseconds max_wait) {
const auto now = TaskTimePoint::clock::now();
std::vector<FlutterTask> 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();
}
}
// Fire expired tasks.
{
// Flushing tasks here without holing onto the task queue mutex.
for (const auto& task : expired_tasks) {
on_task_expired_(&task);
}
}
// Sleep till the next task needs to be processed. If a new task comes
// along, the wait in GLFW will be resolved early because PostTask posts an
// empty event.
{
// Make sure the seconds are not integral.
using Seconds = std::chrono::duration<double, std::ratio<1>>;
std::lock_guard<std::mutex> lock(task_queue_mutex_);
const auto next_wake = task_queue_.empty() ? TaskTimePoint::max()
: task_queue_.top().fire_time;
const auto duration_to_wait = std::chrono::duration_cast<Seconds>(
std::min(next_wake - now, max_wait));
if (duration_to_wait.count() > 0.0) {
::glfwWaitEventsTimeout(duration_to_wait.count());
} else {
// Avoid engine task priority inversion by making sure GLFW events are
// always processed even when there is no need to wait for pending engine
// tasks.
::glfwPollEvents();
}
}
}
GLFWEventLoop::TaskTimePoint GLFWEventLoop::TimePointFromFlutterTime(
uint64_t flutter_target_time_nanos) {
const auto now = TaskTimePoint::clock::now();
const auto flutter_duration =
flutter_target_time_nanos - FlutterEngineGetCurrentTime();
return now + std::chrono::nanoseconds(flutter_duration);
}
void GLFWEventLoop::PostTask(FlutterTask flutter_task,
uint64_t flutter_target_time_nanos) {
static std::atomic_uint64_t sGlobalTaskOrder(0);
Task task;
task.order = ++sGlobalTaskOrder;
task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos);
task.task = flutter_task;
{
std::lock_guard<std::mutex> lock(task_queue_mutex_);
task_queue_.push(task);
// Make sure the queue mutex is unlocked before waking up the loop. In case
// the wake causes this thread to be descheduled for the primary thread to
// process tasks, the acquisition of the lock on that thread while holding
// the lock here momentarily till the end of the scope is a pessimization.
}
::glfwPostEmptyEvent();
}
} // namespace flutter
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_
#define FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_
#include <chrono>
#include <deque>
#include <mutex>
#include <queue>
#include <thread>
#include "flutter/shell/platform/embedder/embedder.h"
namespace flutter {
// An event loop implementation that supports Flutter Engine tasks scheduling in
// the GLFW event loop.
class GLFWEventLoop {
public:
using TaskExpiredCallback = std::function<void(const FlutterTask*)>;
GLFWEventLoop(std::thread::id main_thread_id,
TaskExpiredCallback on_task_expired);
~GLFWEventLoop();
// Returns if the current thread is the thread used by the GLFW event loop.
bool RunsTasksOnCurrentThread() const;
// Wait for an any GLFW or pending Flutter Engine events and returns when
// either is encountered. Expired engine events are processed. The optional
// timeout should only be used when non-GLFW or engine events need to be
// processed in a polling manner.
void WaitForEvents(
std::chrono::nanoseconds max_wait = std::chrono::nanoseconds::max());
// Post a Flutter engine tasks to the event loop for delayed execution.
void PostTask(FlutterTask flutter_task, uint64_t flutter_target_time_nanos);
private:
using TaskTimePoint = std::chrono::steady_clock::time_point;
struct Task {
uint64_t order;
TaskTimePoint fire_time;
FlutterTask 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;
}
};
};
std::thread::id main_thread_id_;
TaskExpiredCallback on_task_expired_;
std::mutex task_queue_mutex_;
std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
std::condition_variable task_queue_cv_;
GLFWEventLoop(const GLFWEventLoop&) = delete;
GLFWEventLoop& operator=(const GLFWEventLoop&) = delete;
static TaskTimePoint TimePointFromFlutterTime(
uint64_t flutter_target_time_nanos);
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_GLFW_GLFW_EVENT_LOOP_H_
......@@ -253,6 +253,7 @@ def to_gn_args(args):
if sys.platform == 'darwin':
gn_args['mac_sdk_path'] = args.mac_sdk_path
gn_args['build_glfw_shell'] = args.build_glfw_shell
if gn_args['mac_sdk_path'] == '':
gn_args['mac_sdk_path'] = os.getenv('FLUTTER_MAC_SDK_PATH', '')
......@@ -323,6 +324,9 @@ def parse_args(args):
help='The IDE files to generate using GN. Use `gn gen help` and look for the --ide flag to' +
' see supported IDEs. If this flag is not specified, a platform specific default is selected.')
parser.add_argument('--build-glfw-shell', dest='build_glfw_shell', default=False, action='store_true',
help='Force building the GLFW shell on desktop platforms where it is not built by default.')
return parser.parse_args(args)
def main(argv):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册