Keep at least 1 second of frame timings.
This is necessary for 2 reasons - a real one and a fun one.
First, with the difference in monitor refresh rates, we can have 48Hz
latops as well as 240Hz high refresh rate monitors. That's a factor of
4, and tracking frame rates in both situations reliably is kind of hard
- either we track over too many frames and the fps take a lot of time to
adjust, or we track too little time and the fps fluctuate wildly.
Second, when benchmarking with GDK_DEBUG=no-vsync with a somewhat fast
renderer (*cough*Vulkan*cough*) frame rates can go into insane dimensions
and only very few frames are actually getting presentation times
reported. So to report accurate frame rates in those cases, we need a
*very* large history that can be 1000s of times larger than the usual
history. And that's just a waste for normal usage.
It started out as busywork, but it does many separate things. If I could
start over, I'd take them apart into multiple commits:
1. Remove G_ENABLE_DEBUG around GDK_DEBUG_*() calls
This is not needed at all, the calls themselves take care of it.
2. Remove G_ENABLE_DEBUG around profiling code
This now enables profiling support in release builds.
3. Stop poking _gdk_debug_flags and use GDK_DEBUG_CHECK()
This was old code that was never updated.
4. Make !G_ENABLE_DEBUG turn off GDK_DEBUG_CHECK()
The code used to
#define GDK_DEBUG_CHECK(...) false
#define GDK_DEBUG(...)
which would compile away all the code inside those macros. This
means a lot of variable definitions and debug utility functions
would suddenly no longer be used and cause compiler errors.
In commit c6901a8b, the frame clock reported time was changed from
simply reporting the time we ran the frame clock cycle to reporting a
smoothed value that increased by the frame interval each time it was
called.
However, this change caused some problems, such as:
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1415https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1416https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1482
I think a lot of this is caused by the fact that we just overwrote the
old frame time with the smoothed, monotonous timestamp, breaking
some things that relied on knowing the actual time something happened.
This is a new approach to doing the smoothing that is more explicit.
The "frame_time" we store is the actual time we ran the update cycle,
and then we separately compute and store the derived smoothed time and
its period, allowing us to easily return a smoothed time at any time
by rounding the time difference to an integer number of frames.
The initial frame_time can be somewhat arbitrary, as it depends on the
first cycle which is not driven by the frame clock. But follow-up
cycles are typically tied to the the compositor sending the drawn
signal. It may happen that the initial frame is exactly in the middle
between two frames where jitter causes us to randomly round in
different directions when rounding to nearest frame. To fix this we
additionally do a quadratic convergence towards the "real" time,
during presentation driven clock cycles (i.e. when the frame times are
small).
To make a frame clock tick as long as any of the associated surfaces
expect to receive ticks, make the surfaces inhibit freezing the clock,
instead of directly tell the frame clock to freeze itself.
This makes it so that as long as any surface using a certain frame clock
is not frozen (e.g. just received a frame event from the display
server), the frame clock will not be frozen.
With this, the frame clock is initiated as frozen, and won't be thawed
until any surface inhibits freeze. It will be frozen again, when every
surface has that previously inhibited freeze uninhibited freeze.
We were adding incomplete frame timings to the
profile, which lead to occasional nonsense
numbers. Instead, only add timings to the profile
once we marked them as complete. This also
gives us an opportunity to add the presentation
time as a marker.
Typically, there won't be any references on old frame timings except for
the most recent timing. So instead of discarding these and re-entering
gslice twice, just steal the old frame timing and reuse it.
https://bugzilla.gnome.org/show_bug.cgi?id=765592
These were showing up higher in Sysprof profiles.
The simple fix is to avoid the emit_by_name() and let the interface emit
the signals directly. No function preconditions are provided since these
are internal API.
Add an API to start or stop continually updating the frame clock.
This is a slight convenience for applcations and avoids the problem
of getting one more frame run after an animation stops, but the
primary motivation for this is because it looks like we might have
to use timeBeginPeriod()/timeEndPeriod() on Windows to get reasonably
accurate timing, and for that we'll need to know if there is an
animation running.
https://bugzilla.gnome.org/show_bug.cgi?id=693934
* remove gdk_frame_clock_get_frame_time_val(); a convenience
function that would rarely be used.
* remove gdk_frame_clock_get_requested() and
::frame-requested signal; while we might want to eventually
be able to track the requested phases for a clock, we don't
have a current use case.
* Make gdk_frame_clock_freeze/thaw() private: they are only
used within GTK+ and have complex semantics.
* Remove gdk_frame_clock_get_last_complete(). Another convenience
function that I don't have a current use case for.
* Rename:
gdk_frame_clock_get_start() => gdk_frame_clock_get_history_start()
gdk_frame_clocK_get_current_frame_timings() => gdk_frame_clock_get_timings()
Since we're not exporting the ability to create your own frame
clock for now, remove the setters for GdkFrameTimings fields.
Also remove all setters and getters for fields that are more
about implementation than about quantities that are meaningful
to the applcation and just access the fields directly within
GDK.
Now that GdkFrameClock is a class, not interface, there's no real advantage
to splitting the frame history into an aggregate object, so directly
merge it into GdkFrameClock.
It's unlikely that anyone will want to have, say, a GtkWidget that
also acts as a GdkFrameClock, so an abstract base class is as
flexible as making GdkFrameClock an interface, but has advantages:
- If we decide to never make implementing your own frame clock
possible, we can remove the virtualization.
- We can put functionality like history into the base class.
- Avoids the oddity of a interface without a public interface
VTable, which may cause problems for language bindings.