forked from flutter/flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wire up custom event loop interop for the GLFW embedder. (flutter#9089)
- Loading branch information
1 parent
54c6226
commit 4e344e6
Showing
6 changed files
with
253 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// 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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters