It was assuming an immediate transition from compositing (triple
buffering) to direct scanout (double buffering), whereas there is
a one frame delay in that transition as the buffer queue shrinks.
We don't lose any frames in the transition.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
1. When direct scanout is attempted
There's no compositing during direct scanout so the "render" time is zero.
Thus there is no need to implement triple buffering for direct scanouts.
Stick to double buffering and enjoy the lower latency.
2. If disabled by environment variable MUTTER_DEBUG_TRIPLE_BUFFERING
With possible values {never, auto, always} where auto is the default.
3. When VRR is in use
VRR calls `clutter_frame_clock_schedule_update_now` which would keep
the buffer queue full, which in turn prevented direct scanout mode.
Because OnscreenNative currently only supports direct scanout with
double buffering.
We now break that feedback loop by preventing triple buffering from
being scheduled when the frame clock mode becomes variable. Long term
this could also be solved by supporting triple buffering in direct
scanout mode. But whether or not that would be desirable given the
latency penalty remains to be seen.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
We need this hint whether direct scanout succeeds or fails because it's
the mechanism by which we will tell the clock to enforce double buffering,
thus making direct scanout possible on future frames. Triple buffering
will be disabled until such time that direct scanout is not being attempted.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
Chronologically they already overlap in time as presentation may
complete in the middle of the dispatch function, otherwise they are
contiguous in time. And most switch statements treated the two states
the same already so they're easy to merge into a single `DISPATCHED`
state.
Having fewer states now will make life easier when we add more states
later.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
Error diffusion was introduced in 0555a5bbc1 for Nvidia where last
presentation time is always unknown (zero). Dispatch times would drift
apart always being a fraction of a frame late, and accumulated to cause
periodic frame skips. So error diffusion corrected that precisely and
avoided the skips.
That works great with double buffering but less great with triple
buffering. It's certainly still needed with triple buffering but
correcting for a lateness of many milliseconds isn't a good idea. That's
because a dispatch being that late is not due to main loop jitter but due
to Nvidia's swap buffers blocking when the queue is full. So scheduling
the next frame even earlier using last_dispatch_lateness_us would just
perpetuate the problem of swap buffers blocking for too long.
So now we lower the threshold of when error diffusion gets disabled. It's
still high enough to fix the original smoothness problem it was for, but
now low enough to detect Nvidia's occasionally blocking swaps and backs
off in that case.
Since the average duration of a blocking swap is half a frame interval
and we want to distinguish between that and sub-millisecond jitter, the
logical threshold is halfway again: refresh_interval_us/4.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
It's analogous to discard_pending_page_flips but represents swaps that
might become flips after the next frame notification callbacks, thanks
to triple buffering. Since the views are being rebuilt and their onscreens
are about to be destroyed, turning those swaps into more flips/posts would
just lead to unexpected behaviour (like trying to flip on a half-destroyed
inactive CRTC).
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
Otherwise we could get:
meta_kms_prepare_shutdown ->
flush_callbacks ->
... ->
try_post_latest_swap ->
post and queue more callbacks
So later in shutdown those callbacks would trigger an assertion failure
in meta_kms_impl_device_atomic_finalize:
g_hash_table_size (impl_device_atomic->page_flip_datas) == 0
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
So that they don't get overwritten prematurely during triple buffering
causing tearing.
https://launchpad.net/bugs/1999216
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
And when the number of pending posts decreases we know it's safe to submit
a new one. Since KMS generally only supports one outstanding post right now,
"decreases" means equal to zero.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This will allow us to keep track of up to two buffers that have been
swapped but not yet scanning out, for triple buffering.
This commit replaces mutter!1968
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
All paths out of `meta_onscreen_native_swap_buffers_with_damage` from
here onward would set the same `CLUTTER_FRAME_RESULT_PENDING_PRESENTED`
(or terminate with `g_assert_not_reached`).
Even failed posts set this result because they will do a
`meta_onscreen_native_notify_frame_complete` in
`page_flip_feedback_discarded`.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
Because it soon won't be the maximum. But we do want to verify that the
frame info queue is not empty, to avoid NULL dereferencing and catch logic
errors.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This is a case that triple buffering will encounter. We don't want it
to queue the same onscreen multiple times because that would represent
multiple flips occurring simultaneously.
It's a linear search but the list length is typically only 1 or 2 so
no need for anything fancier yet.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
cogl_framebuffer_finish can result in a CPU-side stall because it waits for
the primary GPU to flush and execute all commands that were queued before
that. By using a GPU-side EGLSync we can let the primary GPU inform us when
it is done with the queued commands instead. We then create another EGLSync
on the secondary GPU using the same fd so the primary GPU effectively
signals the secondary GPU when it is done rendering, causing the latter
to wait for the former before copying part of the frames it needs for
monitors attached to it directly.
This solves the corruption that cogl_framebuffer_finish also solved, but
without needing a CPU-side stall.
Signed-off-by: Mingi Sung <sungmg@saltyming.net>
Track toplevels being saved, and save state some time after. This
will make session state somewhat remembered on shell crashes, as
long as there was time to snapshot the data in disk.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
Allow saving the session gvdb file in the background, with as little
overhead in the main thread as possible. We still need to serialize
all created/deserialized MetaSessionState to a GVDB hashtable there,
in order to avoid these being poked from the async task thread.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
The xdg_session_manager_v1 global interface is the generator
of xdg_session_v1 objects for clients. These will notify of an
unique ID that can be used for future instantiations.
Once a xdg_session_v1 object is obtained, toplevels can be added
to be managed by it, and clients may get a hint about whether the
toplevel was restored to a saved state.
Changes by Carlos Garnacho: Integrate with MetaSessionManager core
object. Flesh out event emission of xdg_session_v1 and
xdg_toplevel_session_v1 objects, handle sessions being
replaced/deleted.
Changes by Sebastian Wick:
* make lifetimes of xdg_sessions entirely determined by the wayland and
handle its destruction via the signal
* fix session destruction vs deletion
* do not drop refcount of replaced session state temporarily to make
sure the replacing session keeps the state
* disconnect signals of destroyed and replaced sessions
* disconnect window-unmanaging signal handler for
MetaWaylandXdgToplevelSession
* call wl_resource_destroy in xdg_toplevel_session_remove to make it a
destructor
* handle session being destroyed before topevel-sessions
* handle the toplevel going away before the topevel-sessions
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
This object is a windowing-specific implementation of MetaSessionState,
allowing to save window state for toplevel surfaces of a Wayland client
using the xdg_session_management_v1 protocol.
This object is detached from windowing logic itself, and will be
integrated in later commits.
Changes from Carlos Garnacho: Integrate state serialization with
MetaSessionState and MetaSessionManager.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
Make this core object own the MetaSessionManager, for the window management
code to access.
At this level, we will be able to integrate with systemd notification
system, and use systemd fdstore to keep the mapped memory warm for
us for the case of soft reboot. This is at the moment not implemented
here.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
This core object will be the manager of "client sessions", allowing
the windowing-specific paths to generate MetaSessionState objects to
track their clients.
This object is unused at the moment, and will be integrated in later
commits.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
This is an abstract base class to implement a "client session",
carrying the accounting of the windows, and allowing to serialize/read
their state into a Gvdb table.
Since different windowing backends may require slightly different
data to be saved for each window, this is meant to have windowing-specific
implementations.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
A nick property is a bit similar to the nick of a GObject property, in
that it's a shorter version of the name. It's intended to be used to
store state on the file system, where the state depends on the desktop
environment being used. E.g. gnome-shell sets the name "GNOME Shell",
which is, if no nick is explicitly set, transformed into the nick
"gnome-shell", which will be used for file paths.
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>