diff --git a/protocol/meson.build b/protocol/meson.build index df11a728b0..7c08b855b2 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -14,6 +14,7 @@ protocols = [ # Upstream protocols wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', wl_protocol_dir / 'stable/presentation-time/presentation-time.xml', + wl_protocol_dir / 'staging/commit-queue/commit-queue-v1.xml', # Gamescope protocols 'gamescope-xwayland.xml', diff --git a/src/meson.build b/src/meson.build index d3dec0d2fe..f0d99d5319 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,7 +22,7 @@ stb_dep = dependency('stb') wlroots_dep = dependency( 'wlroots', - version: ['>= 0.17.0', '< 0.18.0'], + version: ['>= 0.18.0', '< 0.19.0'], fallback: ['wlroots', 'wlroots'], default_options: ['default_library=static', 'examples=false', 'xwayland=enabled', 'backends=libinput', 'renderers=[]', 'allocators=[]', 'session=enabled'], ) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 1c6ed8d63a..6821d2d095 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,8 @@ extern float g_flInternalDisplayBrightnessNits; extern float g_flHDRItmSdrNits; extern float g_flHDRItmTargetNits; +uint64_t g_lastWinSeq = 0; + extern std::atomic g_lastVblank; static std::shared_ptr s_scRGB709To2020Matrix; @@ -753,8 +756,10 @@ struct commit_t : public gamescope::IWaitable uint64_t commitID = 0; bool done = false; bool async = false; + bool fifo = false; std::optional feedback = std::nullopt; + uint64_t win_seq = 0; struct wlr_surface *surf = nullptr; std::vector presentation_feedbacks; @@ -791,7 +796,12 @@ struct commit_t : public gamescope::IWaitable // When we get the new IWaitable stuff in there. { std::unique_lock< std::mutex > lock( pDoneCommits->listCommitsDoneLock ); - pDoneCommits->listCommitsDone.push_back( CommitDoneEntry_t{ commitID, desired_present_time } ); + pDoneCommits->listCommitsDone.push_back( CommitDoneEntry_t{ + .winSeq = win_seq, + .commitID = commitID, + .desiredPresentTime = desired_present_time, + .fifo = fifo, + } ); } if ( m_bMangoNudge ) @@ -1364,14 +1374,16 @@ destroy_buffer( struct wl_listener *listener, void * ) } static std::shared_ptr -import_commit ( struct wlr_surface *surf, struct wlr_buffer *buf, bool async, std::shared_ptr swapchain_feedback, std::vector presentation_feedbacks, std::optional present_id, uint64_t desired_present_time ) +import_commit ( steamcompmgr_win_t *w, struct wlr_surface *surf, struct wlr_buffer *buf, bool async, std::shared_ptr swapchain_feedback, std::vector presentation_feedbacks, std::optional present_id, uint64_t desired_present_time, bool fifo ) { std::shared_ptr commit = std::make_shared(); std::unique_lock lock( wlr_buffer_map_lock ); + commit->win_seq = w->seq; commit->surf = surf; commit->buf = buf; commit->async = async; + commit->fifo = fifo; commit->presentation_feedbacks = std::move(presentation_feedbacks); if (swapchain_feedback) commit->feedback = *swapchain_feedback; @@ -4580,6 +4592,7 @@ add_win(xwayland_ctx_t *ctx, Window id, Window prev, unsigned long sequence) if (!new_win) return; + new_win->seq = ++g_lastWinSeq; new_win->type = steamcompmgr_win_type_t::XWAYLAND; new_win->_window_types.emplace(); @@ -6361,7 +6374,7 @@ bool handle_done_commit( steamcompmgr_win_t *w, xwayland_ctx_t *ctx, uint64_t co } // TODO: Merge these two functions. -void handle_done_commits_xwayland( xwayland_ctx_t *ctx ) +void handle_done_commits_xwayland( xwayland_ctx_t *ctx, bool vblank ) { std::lock_guard lock( ctx->doneCommits.listCommitsDoneLock ); @@ -6370,11 +6383,20 @@ void handle_done_commits_xwayland( xwayland_ctx_t *ctx ) // commits that were not ready to be presented based on their display timing. std::vector< CommitDoneEntry_t > commits_before_their_time; + // windows in FIFO mode we got a new frame to present for this vblank + std::unordered_set< uint64_t > fifo_win_seqs; + uint64_t now = get_time_in_nanos(); // very fast loop yes for ( auto& entry : ctx->doneCommits.listCommitsDone ) { + if (entry.fifo && (!vblank || fifo_win_seqs.count(entry.winSeq) > 0)) + { + commits_before_their_time.push_back( entry ); + continue; + } + if (!entry.earliestPresentTime) { entry.earliestPresentTime = next_refresh_time; @@ -6389,8 +6411,14 @@ void handle_done_commits_xwayland( xwayland_ctx_t *ctx ) for ( steamcompmgr_win_t *w = ctx->list; w; w = w->xwayland().next ) { + if (w->seq != entry.winSeq) + continue; if (handle_done_commit(w, ctx, entry.commitID, entry.earliestPresentTime, entry.earliestLatchTime)) + { + if (entry.fifo) + fifo_win_seqs.insert(entry.winSeq); break; + } } } @@ -6575,7 +6603,7 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re return; } - std::shared_ptr newCommit = import_commit( reslistentry.surf, buf, reslistentry.async, std::move(reslistentry.feedback), std::move(reslistentry.presentation_feedbacks), reslistentry.present_id, reslistentry.desired_present_time ); + std::shared_ptr newCommit = import_commit( w, reslistentry.surf, buf, reslistentry.async, std::move(reslistentry.feedback), std::move(reslistentry.presentation_feedbacks), reslistentry.present_id, reslistentry.desired_present_time, reslistentry.fifo ); int fence = -1; if ( newCommit ) @@ -7918,7 +7946,7 @@ steamcompmgr_main(int argc, char **argv) gamescope_xwayland_server_t *server = NULL; for (size_t i = 0; (server = wlserver_get_xwayland_server(i)); i++) { - handle_done_commits_xwayland(server->ctx.get()); + handle_done_commits_xwayland(server->ctx.get(), vblank); // When we have observed both a complete commit and a VBlank, we should request a new frame. if (vblank) diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index fb8a5d2814..e77665afcb 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -140,6 +140,7 @@ extern float focusedWindowOffsetY; extern bool g_bFSRActive; extern uint32_t inputCounter; +extern uint64_t g_lastWinSeq; void nudge_steamcompmgr( void ); void take_screenshot( int flags = TAKE_SCREENSHOT_BASEPLANE_ONLY ); diff --git a/src/steamcompmgr_shared.hpp b/src/steamcompmgr_shared.hpp index 2b0f2e4ef7..3bcdb16e82 100644 --- a/src/steamcompmgr_shared.hpp +++ b/src/steamcompmgr_shared.hpp @@ -90,6 +90,8 @@ struct steamcompmgr_xdg_win_t struct steamcompmgr_win_t { unsigned int opacity; + uint64_t seq; + std::shared_ptr title; bool utf8_title; pid_t pid; diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 49f96ed62f..d4a3dae1cd 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -33,6 +33,7 @@ extern "C" { #include #include #include +#include #undef static #undef class } @@ -43,6 +44,7 @@ extern "C" { #include "gamescope-swapchain-protocol.h" #include "gamescope-tearing-control-unstable-v1-protocol.h" #include "presentation-time-protocol.h" +#include "commit-queue-v1-protocol.h" #include "wlserver.hpp" #include "drm.hpp" @@ -104,10 +106,13 @@ void gamescope_xwayland_server_t::wayland_commit(struct wlr_surface *surf, struc auto wl_surf = get_wl_surface_info( surf ); + auto queue_mode = wlr_commit_queue_v1_get_surface_mode(surf); + ResListEntry_t newEntry = { .surf = surf, .buf = buf, .async = wlserver_surface_is_async(surf), + .fifo = queue_mode == WP_COMMIT_QUEUE_V1_QUEUE_MODE_FIFO, .feedback = wlserver_surface_swapchain_feedback(surf), .presentation_feedbacks = std::move(wl_surf->pending_presentation_feedbacks), .present_id = wl_surf->present_id, @@ -1177,7 +1182,8 @@ bool wlsession_init( void ) { if ( BIsNested() ) return true; - wlserver.wlr.session = wlr_session_create( wlserver.display ); + auto loop = wl_display_get_event_loop( wlserver.display ); + wlserver.wlr.session = wlr_session_create( loop ); if ( wlserver.wlr.session == nullptr ) { wl_log.errorf( "Failed to create session" ); @@ -1265,7 +1271,7 @@ gamescope_xwayland_server_t::gamescope_xwayland_server_t(wl_display *display) update_output_info(); - wlr_output_create_global(output); + wlr_output_create_global(output, wlserver.display); } gamescope_xwayland_server_t::~gamescope_xwayland_server_t() @@ -1344,15 +1350,10 @@ static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { wlserver_surface->xdg_surface = nullptr; } -void xdg_surface_new(struct wl_listener *listener, void *data) +void xdg_toplevel_new(struct wl_listener *listener, void *data) { - struct wlr_xdg_surface *xdg_surface = (struct wlr_xdg_surface *)data; - - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) - { - wl_log.infof("Not top level surface."); - return; - } + struct wlr_xdg_toplevel *xdg_toplevel = (struct wlr_xdg_toplevel *)data; + struct wlr_xdg_surface *xdg_surface = xdg_toplevel->base; wlserver_wl_surface_info *wlserver_surface = get_wl_surface_info(xdg_surface->surface); if (!wlserver_surface) @@ -1367,6 +1368,7 @@ void xdg_surface_new(struct wl_listener *listener, void *data) wlserver.xdg_wins.emplace_back(window); } + window->seq = ++g_lastWinSeq; window->type = steamcompmgr_win_type_t::XDG; window->_window_types.emplace(); @@ -1376,7 +1378,7 @@ void xdg_surface_new(struct wl_listener *listener, void *data) wlserver_xdg_surface_info* xdg_surface_info = &window->xdg().surface; xdg_surface_info->main_surface = xdg_surface->surface; xdg_surface_info->win = window.get(); - xdg_surface_info->xdg_toplevel = xdg_surface->toplevel; + xdg_surface_info->xdg_toplevel = xdg_toplevel; wlserver_surface->xdg_surface = xdg_surface_info; @@ -1461,14 +1463,16 @@ bool wlserver_init( void ) { create_presentation_time(); + wlr_commit_queue_manager_v1_create(wlserver.display, 1); + wlserver.xdg_shell = wlr_xdg_shell_create(wlserver.display, 3); if (!wlserver.xdg_shell) { wl_log.infof("Unable to create XDG shell interface"); return false; } - wlserver.new_xdg_surface.notify = xdg_surface_new; - wl_signal_add(&wlserver.xdg_shell->events.new_surface, &wlserver.new_xdg_surface); + wlserver.new_xdg_toplevel.notify = xdg_toplevel_new; + wl_signal_add(&wlserver.xdg_shell->events.new_toplevel, &wlserver.new_xdg_toplevel); int result = -1; int display_slot = 0; diff --git a/src/wlserver.hpp b/src/wlserver.hpp index 07f36d6140..edefa1df91 100644 --- a/src/wlserver.hpp +++ b/src/wlserver.hpp @@ -38,6 +38,7 @@ struct ResListEntry_t { struct wlr_surface *surf; struct wlr_buffer *buf; bool async; + bool fifo; std::shared_ptr feedback; std::vector presentation_feedbacks; std::optional present_id; @@ -131,7 +132,7 @@ struct wlserver_t { struct wl_listener new_input_method; struct wlr_xdg_shell *xdg_shell; - struct wl_listener new_xdg_surface; + struct wl_listener new_xdg_toplevel; std::vector> xdg_wins; std::atomic xdg_dirty; std::mutex xdg_commit_lock; diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index f4704f869f..52f42ef42d 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -35,10 +35,12 @@ struct focus_t struct CommitDoneEntry_t { + uint64_t winSeq; uint64_t commitID; uint64_t desiredPresentTime; uint64_t earliestPresentTime; uint64_t earliestLatchTime; + bool fifo; }; struct CommitDoneList_t