Skip to content

Commit 041c69d

Browse files
committed
Merge pull request godotengine#101454 from Riteo/fifo_ftw
Wayland: Handle `fifo_v1` and clean up suspension logic
2 parents 795b11e + 48882f3 commit 041c69d

File tree

3 files changed

+93
-65
lines changed

3 files changed

+93
-65
lines changed

platform/linuxbsd/wayland/display_server_wayland.cpp

Lines changed: 68 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -822,8 +822,9 @@ void DisplayServerWayland::show_window(WindowID p_window_id) {
822822
}
823823
#endif
824824

825-
// NOTE: The public window-handling methods might depend on this flag being
826-
// set. Ensure to not make any of these calls before this assignment.
825+
// NOTE: Some public window-handling methods might depend on this flag being
826+
// set. Make sure the method you're calling does not depend on it before this
827+
// assignment.
827828
wd.visible = true;
828829

829830
// Actually try to apply the window's mode now that it's visible.
@@ -1349,7 +1350,7 @@ void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn
13491350
if (rendering_context) {
13501351
rendering_context->window_set_vsync_mode(p_window_id, p_vsync_mode);
13511352

1352-
wd.emulate_vsync = (rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);
1353+
wd.emulate_vsync = (!wayland_thread.is_fifo_available() && rendering_context->window_get_vsync_mode(p_window_id) == DisplayServer::VSYNC_ENABLED);
13531354

13541355
if (wd.emulate_vsync) {
13551356
print_verbose("VSYNC: manually throttling frames using MAILBOX.");
@@ -1362,6 +1363,8 @@ void DisplayServerWayland::window_set_vsync_mode(DisplayServer::VSyncMode p_vsyn
13621363
if (egl_manager) {
13631364
egl_manager->set_use_vsync(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
13641365

1366+
// NOTE: Mesa's EGL implementation does not seem to make use of fifo_v1 so
1367+
// we'll have to always emulate V-Sync.
13651368
wd.emulate_vsync = egl_manager->is_using_vsync();
13661369

13671370
if (wd.emulate_vsync) {
@@ -1542,38 +1545,14 @@ bool DisplayServerWayland::color_picker(const Callable &p_callback) {
15421545
}
15431546

15441547
void DisplayServerWayland::try_suspend() {
1545-
bool must_emulate = false;
1546-
1547-
for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {
1548-
if (pair.value.emulate_vsync) {
1549-
must_emulate = true;
1550-
break;
1551-
}
1552-
}
1553-
15541548
// Due to various reasons, we manually handle display synchronization by
15551549
// waiting for a frame event (request to draw) or, if available, the actual
15561550
// window's suspend status. When a window is suspended, we can avoid drawing
15571551
// altogether, either because the compositor told us that we don't need to or
15581552
// because the pace of the frame events became unreliable.
1559-
if (must_emulate) {
1560-
bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);
1561-
if (!frame) {
1562-
suspend_state = SuspendState::TIMEOUT;
1563-
}
1564-
}
1565-
1566-
// If we suspended by capability, we'll know with this check. We must do this
1567-
// after `wait_frame_suspend_ms` as it progressively dispatches the event queue
1568-
// during the "timeout".
1569-
if (wayland_thread.is_suspended()) {
1570-
suspend_state = SuspendState::CAPABILITY;
1571-
}
1572-
1573-
if (suspend_state == SuspendState::TIMEOUT) {
1574-
DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");
1575-
} else if (suspend_state == SuspendState::CAPABILITY) {
1576-
DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");
1553+
bool frame = wayland_thread.wait_frame_suspend_ms(WAYLAND_MAX_FRAME_TIME_US / 1000);
1554+
if (!frame) {
1555+
suspend_state = SuspendState::TIMEOUT;
15771556
}
15781557
}
15791558

@@ -1724,51 +1703,75 @@ void DisplayServerWayland::process_events() {
17241703

17251704
wayland_thread.keyboard_echo_keys();
17261705

1727-
if (suspend_state == SuspendState::NONE) {
1728-
// Due to the way legacy suspension works, we have to treat low processor
1729-
// usage mode very differently than the regular one.
1730-
if (OS::get_singleton()->is_in_low_processor_usage_mode()) {
1731-
// NOTE: We must avoid committing a surface if we expect a new frame, as we
1732-
// might otherwise commit some inconsistent data (e.g. buffer scale). Note
1733-
// that if a new frame is expected it's going to be committed by the renderer
1734-
// soon anyways.
1735-
if (!RenderingServer::get_singleton()->has_changed()) {
1736-
// We _can't_ commit in a different thread (such as in the frame callback
1737-
// itself) because we would risk to step on the renderer's feet, which would
1738-
// cause subtle but severe issues, such as crashes on setups with explicit
1739-
// sync. This isn't normally a problem, as the renderer commits at every
1740-
// frame (which is what we need for atomic surface updates anyways), but in
1741-
// low processor usage mode that expectation is broken. When it's on, our
1742-
// frame rate stops being constant. This also reflects in the frame
1743-
// information we use for legacy suspension. In order to avoid issues, let's
1744-
// manually commit all surfaces, so that we can get fresh frame data.
1745-
wayland_thread.commit_surfaces();
1746-
try_suspend();
1706+
switch (suspend_state) {
1707+
case SuspendState::NONE: {
1708+
bool emulate_vsync = false;
1709+
for (KeyValue<DisplayServer::WindowID, WindowData> &pair : windows) {
1710+
if (pair.value.emulate_vsync) {
1711+
emulate_vsync = true;
1712+
break;
1713+
}
17471714
}
1748-
} else {
1749-
try_suspend();
1750-
}
1751-
} else {
1752-
if (suspend_state == SuspendState::CAPABILITY) {
1753-
// If we suspended by capability we can assume that it will be reset when
1754-
// the compositor wants us to repaint.
1755-
if (!wayland_thread.is_suspended()) {
1756-
suspend_state = SuspendState::NONE;
1757-
DEBUG_LOG_WAYLAND("Unsuspending from capability.");
1715+
1716+
if (emulate_vsync) {
1717+
// Due to the way legacy suspension works, we have to treat low processor
1718+
// usage mode very differently than the regular one.
1719+
if (OS::get_singleton()->is_in_low_processor_usage_mode()) {
1720+
// NOTE: We must avoid committing a surface if we expect a new frame, as we
1721+
// might otherwise commit some inconsistent data (e.g. buffer scale). Note
1722+
// that if a new frame is expected it's going to be committed by the renderer
1723+
// soon anyways.
1724+
if (!RenderingServer::get_singleton()->has_changed()) {
1725+
// We _can't_ commit in a different thread (such as in the frame callback
1726+
// itself) because we would risk to step on the renderer's feet, which would
1727+
// cause subtle but severe issues, such as crashes on setups with explicit
1728+
// sync. This isn't normally a problem, as the renderer commits at every
1729+
// frame (which is what we need for atomic surface updates anyways), but in
1730+
// low processor usage mode that expectation is broken. When it's on, our
1731+
// frame rate stops being constant. This also reflects in the frame
1732+
// information we use for legacy suspension. In order to avoid issues, let's
1733+
// manually commit all surfaces, so that we can get fresh frame data.
1734+
wayland_thread.commit_surfaces();
1735+
try_suspend();
1736+
}
1737+
} else {
1738+
try_suspend();
1739+
}
1740+
}
1741+
1742+
if (wayland_thread.is_suspended()) {
1743+
suspend_state = SuspendState::CAPABILITY;
17581744
}
1759-
} else if (suspend_state == SuspendState::TIMEOUT) {
1745+
1746+
if (suspend_state == SuspendState::TIMEOUT) {
1747+
DEBUG_LOG_WAYLAND("Suspending. Reason: timeout.");
1748+
} else if (suspend_state == SuspendState::CAPABILITY) {
1749+
DEBUG_LOG_WAYLAND("Suspending. Reason: capability.");
1750+
}
1751+
} break;
1752+
1753+
case SuspendState::TIMEOUT: {
17601754
// Certain compositors might not report the "suspended" wm_capability flag.
17611755
// Because of this we'll wake up at the next frame event, indicating the
17621756
// desire for the compositor to let us repaint.
17631757
if (wayland_thread.get_reset_frame()) {
17641758
suspend_state = SuspendState::NONE;
17651759
DEBUG_LOG_WAYLAND("Unsuspending from timeout.");
17661760
}
1767-
}
17681761

1769-
// Since we're not rendering, nothing is committing the windows'
1770-
// surfaces. We have to do it ourselves.
1771-
wayland_thread.commit_surfaces();
1762+
// Since we're not rendering, nothing is committing the windows'
1763+
// surfaces. We have to do it ourselves.
1764+
wayland_thread.commit_surfaces();
1765+
} break;
1766+
1767+
case SuspendState::CAPABILITY: {
1768+
// If we suspended by capability we can assume that it will be reset when
1769+
// the compositor wants us to repaint.
1770+
if (!wayland_thread.is_suspended()) {
1771+
suspend_state = SuspendState::NONE;
1772+
DEBUG_LOG_WAYLAND("Unsuspending from capability.");
1773+
}
1774+
} break;
17721775
}
17731776

17741777
#ifdef DBUS_ENABLED

platform/linuxbsd/wayland/wayland_thread.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
#define DEBUG_LOG_WAYLAND_THREAD(...)
6161
#endif
6262

63+
// Since we're never going to use this interface directly, it's not worth
64+
// generating the whole deal.
65+
#define FIFO_INTERFACE_NAME "wp_fifo_manager_v1"
66+
6367
// Read the content pointed by fd into a Vector<uint8_t>.
6468
Vector<uint8_t> WaylandThread::_read_fd(int fd) {
6569
// This is pretty much an arbitrary size.
@@ -640,6 +644,10 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
640644

641645
return;
642646
}
647+
648+
if (strcmp(interface, FIFO_INTERFACE_NAME) == 0) {
649+
registry->wp_fifo_manager_name = name;
650+
}
643651
}
644652

645653
void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) {
@@ -1028,6 +1036,10 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
10281036
it = it->next();
10291037
}
10301038
}
1039+
1040+
if (name == registry->wp_fifo_manager_name) {
1041+
registry->wp_fifo_manager_name = 0;
1042+
}
10311043
}
10321044

10331045
void WaylandThread::_wl_surface_on_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) {
@@ -4275,6 +4287,10 @@ Error WaylandThread::init() {
42754287
}
42764288
#endif // DBUS_ENABLED
42774289

4290+
if (!registry.wp_fifo_manager_name) {
4291+
WARN_PRINT("FIFO protocol not found! Frame pacing will be degraded.");
4292+
}
4293+
42784294
// Wait for seat capabilities.
42794295
wl_display_roundtrip(wl_display);
42804296

@@ -4777,6 +4793,10 @@ bool WaylandThread::window_is_suspended(DisplayServer::WindowID p_window_id) con
47774793
return windows[p_window_id].suspended;
47784794
}
47794795

4796+
bool WaylandThread::is_fifo_available() const {
4797+
return registry.wp_fifo_manager_name != 0;
4798+
}
4799+
47804800
bool WaylandThread::is_suspended() const {
47814801
for (const KeyValue<DisplayServer::WindowID, WindowState> &E : windows) {
47824802
if (!E.value.suspended) {

platform/linuxbsd/wayland/wayland_thread.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ class WaylandThread {
216216

217217
struct zwp_text_input_manager_v3 *wp_text_input_manager = nullptr;
218218
uint32_t wp_text_input_manager_name = 0;
219+
220+
// We're really not meant to use this one directly but we still need to know
221+
// whether it's available.
222+
uint32_t wp_fifo_manager_name = 0;
219223
};
220224

221225
// General Wayland-specific states. Shouldn't be accessed directly.
@@ -1068,6 +1072,7 @@ class WaylandThread {
10681072
void set_frame();
10691073
bool get_reset_frame();
10701074
bool wait_frame_suspend_ms(int p_timeout);
1075+
bool is_fifo_available() const;
10711076

10721077
uint64_t window_get_last_frame_time(DisplayServer::WindowID p_window_id) const;
10731078
bool window_is_suspended(DisplayServer::WindowID p_window_id) const;

0 commit comments

Comments
 (0)