Qt Quick adapter implementing IMotionClock.
More...
#include <phosphor-animation/include/PhosphorAnimation/QtQuickClock.h>
Public Member Functions | |
| QtQuickClock (QQuickWindow *window) | |
Construct a clock bound to window. | |
| ~QtQuickClock () override | |
| std::chrono::nanoseconds | now () const override |
| Monotonically non-decreasing steady-clock reading (nanoseconds). | |
| qreal | refreshRate () const override |
| Nominal refresh rate in Hz, or zero if unknown. | |
| void | requestFrame () override |
| Schedule another paint tick. Idempotent within a single frame. | |
| const void * | epochIdentity () const override |
| Opaque epoch identity for rebindClock compatibility. | |
| QQuickWindow * | window () const |
| The QQuickWindow this clock is bound to. May be null. | |
Public Member Functions inherited from PhosphorAnimation::IMotionClock | |
| virtual | ~IMotionClock ()=default |
| IMotionClock (const IMotionClock &)=delete | |
| IMotionClock & | operator= (const IMotionClock &)=delete |
| IMotionClock (IMotionClock &&)=delete | |
| IMotionClock & | operator= (IMotionClock &&)=delete |
Additional Inherited Members | |
Static Public Member Functions inherited from PhosphorAnimation::IMotionClock | |
| static const void * | steadyClockEpoch () |
| Shared sentinel for std::chrono::steady_clock-backed clocks. | |
| static bool | epochCompatible (const IMotionClock *a, const IMotionClock *b) |
| True iff both clocks are non-null and share the same non-null epochIdentity. | |
Protected Member Functions inherited from PhosphorAnimation::IMotionClock | |
| IMotionClock ()=default | |
Qt Quick adapter implementing IMotionClock.
Ships only when the library is built with PHOSPHOR_ANIMATION_QUICK=ON — the flag gates both the header install and the Qt6::Quick link dependency so core consumers that don't need QML motion (the KWin compositor adapter, headless tools, daemon) don't pull in Qt Quick transitively.
Bound to one QQuickWindow per instance. Multi-window QML shells construct one QtQuickClock per top-level window and route their AnimatedValue<T> instances through the matching clock — same per-output phase-locking rationale as CompositorClock.
Two QtQuickClock instances bound to the same QQuickWindow will each connect to its beforeRendering signal and each write into its own independent m_nowCache. That works mechanically (no shared state between them, no UAF) but defeats the entire point of caching: the two clocks report identical timestamps with double the signal-dispatch cost, and a rebind from one to the other would waste an epoch-compatibility check on clocks that are already equivalent. Callers must therefore maintain one QtQuickClock per QQuickWindow (cache it on the window via dynamic property or a lookup map). The class does not enforce this because the identity lookup belongs on the caller's side — two unrelated shells may legitimately each want their own clock on the same embedded window — and a Q_ASSERT here would force that policy rather than document it.
The clock subscribes to QQuickWindow::beforeRendering — fires once per scene-graph render pass on the render thread. The connected lambda latches the current std::chrono::steady_clock::now() into the clock's cached timestamp, so now() returns a vsync-aligned reading for the frame currently being rendered.
requestFrame() forwards to QQuickWindow::update(), which is the Qt Quick equivalent of effects->addRepaint() — schedules another render pass from whichever thread the call lands on (Qt's update() is thread-safe by design).
refreshRate() reads from window->screen()->refreshRate() when available. Returns 0.0 (unknown) if the window is not yet shown or has no screen attached — the usual state during construction.
std::chrono::steady_clock is monotonic by contract, so the clamp CompositorClock applies for KWin's (rarely) regressing presentTime is unnecessary here.
The beforeRendering slot fires on the Qt Quick render thread; any consumer calling now() from a different thread (e.g., a QML scene animation driven from the GUI thread) would otherwise race on a plain 64-bit field. The cached timestamp is held as std::atomic<int64_t> with memory_order_relaxed load/store so the cross-thread read is well-defined even though the common case (both reader and writer on the render thread) pays only the cost of a plain aligned load/store on x86_64.
Qt::DirectConnection slots execute synchronously on the signal's emitting thread; Qt's implicit ~QObject disconnect does not join already-running slot bodies. Destroying the clock on the GUI thread while the render thread was mid-slot would otherwise write to a freed cache. The adapter mitigates this internally via a three-stage teardown: set detached, disconnect the signal, then spin-wait until every in-progress slot body has exited. The join makes destruction strictly synchronous with respect to the render-thread slot — consumers do not need to externally synchronise destruction and are not exposed to the narrow atomic-ordering UAF the flag-only pattern permitted.
The join imposes one constraint: the destructor MUST NOT run on the render thread that fires the slot. A self-join would deadlock. QtQuickClock's ownership model makes this trivially satisfied — clocks are owned by GUI-thread objects (compositor effects, QtQuick items driving custom animations) and destroyed on that thread. The spin is typically zero-iterations and at worst waits for one slot body's microsecond-scale duration.
Tearing down a clock while the render thread is still active is legal but wasteful — the destructor blocks the owning thread on the render thread's next scheduling window. Prefer destroying clocks after the scene graph has been torn down.
|
explicit |
Construct a clock bound to window.
The clock subscribes to the window's beforeRendering signal immediately — construction does not require the window to be shown. window may be null for testing; in that mode now() stays at zero and requestFrame() is a no-op.
|
override |
|
overridevirtual |
Opaque epoch identity for rebindClock compatibility.
nullptr (default) = incompatible with rebind onto any other clock. Clocks sharing the same non-null identity are rebase-safe.
Reimplemented from PhosphorAnimation::IMotionClock.
|
overridevirtual |
Monotonically non-decreasing steady-clock reading (nanoseconds).
Implements PhosphorAnimation::IMotionClock.
|
overridevirtual |
Nominal refresh rate in Hz, or zero if unknown.
Implements PhosphorAnimation::IMotionClock.
|
overridevirtual |
Schedule another paint tick. Idempotent within a single frame.
Implements PhosphorAnimation::IMotionClock.
| QQuickWindow * PhosphorAnimation::QtQuickClock::window | ( | ) | const |
The QQuickWindow this clock is bound to. May be null.