1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

ComponentPeer::VBlankListener: Add timestamp parameter to the vblank callback

This commit is contained in:
attila 2024-10-03 10:58:51 +02:00
parent 4bc2952419
commit d9a3efd3cb
11 changed files with 88 additions and 33 deletions

View file

@ -1075,9 +1075,9 @@ public:
return areAnyWindowsInLiveResize();
}
void onVBlank()
void onVBlank (double timestampSec)
{
vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
callVBlankListeners (timestampSec);
setNeedsDisplayRectangles();
}
@ -1758,13 +1758,18 @@ private:
explicit AsyncRepainter (NSViewComponentPeer& o) : owner (o) {}
~AsyncRepainter() override { cancelPendingUpdate(); }
void markUpdated (const CGDirectDisplayID x)
void markUpdated (const CGDirectDisplayID x, double timestampSec)
{
{
const std::scoped_lock lock { mutex };
if (std::find (backgroundDisplays.cbegin(), backgroundDisplays.cend(), x) == backgroundDisplays.cend())
backgroundDisplays.push_back (x);
if (const auto it = std::find_if (backgroundVBlankEvents.cbegin(),
backgroundVBlankEvents.cend(),
[&x] (const auto& event) { return event.display == x; });
it == backgroundVBlankEvents.cend())
{
backgroundVBlankEvents.push_back ({ x, timestampSec });
}
}
triggerAsyncUpdate();
@ -1775,20 +1780,28 @@ private:
{
{
const std::scoped_lock lock { mutex };
mainThreadDisplays = backgroundDisplays;
backgroundDisplays.clear();
mainThreadVBlankEvents = backgroundVBlankEvents;
backgroundVBlankEvents.clear();
}
for (const auto& display : mainThreadDisplays)
for (const auto& event : mainThreadVBlankEvents)
{
if (auto* peerView = owner.view)
if (auto* peerWindow = [peerView window])
if (display == ScopedDisplayLink::getDisplayIdForScreen ([peerWindow screen]))
owner.onVBlank();
if (event.display == ScopedDisplayLink::getDisplayIdForScreen ([peerWindow screen]))
owner.onVBlank (event.timeSec);
}
}
struct VBlankEvent
{
CGDirectDisplayID display{};
double timeSec{};
};
NSViewComponentPeer& owner;
std::mutex mutex;
std::vector<CGDirectDisplayID> backgroundDisplays, mainThreadDisplays;
std::vector<VBlankEvent> backgroundVBlankEvents, mainThreadVBlankEvents;
};
AsyncRepainter asyncRepainter { *this };
@ -1801,7 +1814,10 @@ private:
{
sharedDisplayLinks->registerFactory ([this] (CGDirectDisplayID display)
{
return [this, display] { asyncRepainter.markUpdated (display); };
return [this, display] (double timestampSec)
{
asyncRepainter.markUpdated (display, timestampSec);
};
})
};

View file

@ -119,7 +119,7 @@ public:
return (CGDirectDisplayID) [[screen.deviceDescription objectForKey: @"NSScreenNumber"] unsignedIntegerValue];
}
ScopedDisplayLink (NSScreen* screenIn, std::function<void()> onCallbackIn)
ScopedDisplayLink (NSScreen* screenIn, std::function<void (double)> onCallbackIn)
: displayId (getDisplayIdForScreen (screenIn)),
link ([display = displayId]
{
@ -133,12 +133,13 @@ public:
{
const auto callback = [] (CVDisplayLinkRef,
const CVTimeStamp*,
const CVTimeStamp*,
const CVTimeStamp* outputTime,
CVOptionFlags,
CVOptionFlags*,
void* context) -> int
{
static_cast<const ScopedDisplayLink*> (context)->onCallback();
const auto outputTimeSec = (double) outputTime->videoTime / (double) outputTime->videoTimeScale;
static_cast<const ScopedDisplayLink*> (context)->onCallback (outputTimeSec);
return kCVReturnSuccess;
};
@ -179,7 +180,7 @@ private:
CGDirectDisplayID displayId;
std::unique_ptr<std::remove_pointer_t<CVDisplayLinkRef>, DisplayLinkDestructor> link;
std::function<void()> onCallback;
std::function<void (double)> onCallback;
// Instances can't be copied or moved, because 'this' is passed as context to
// CVDisplayLinkSetOutputCallback
@ -202,7 +203,7 @@ public:
refreshScreens();
}
using RefreshCallback = std::function<void()>;
using RefreshCallback = std::function<void (double)>;
using Factory = std::function<RefreshCallback (CGDirectDisplayID)>;
/*
@ -293,10 +294,10 @@ private:
// This is the callback that will actually fire in response to this screen's display
// link callback.
result.emplace_back (screen, [cbs = std::move (callbacks)]
result.emplace_back (screen, [cbs = std::move (callbacks)] (double timestampSec)
{
for (const auto& callback : cbs)
callback();
callback (timestampSec);
});
}

View file

@ -420,7 +420,7 @@ public:
void setIcon (const Image& newIcon) override;
StringArray getAvailableRenderingEngines() override { return StringArray ("CoreGraphics Renderer"); }
void displayLinkCallback();
void displayLinkCallback (double timestampSec);
void drawRect (CGRect);
void drawRectWithContext (CGContextRef, CGRect);
@ -768,7 +768,7 @@ MultiTouchMapper<UITouch*> UIViewComponentPeer::currentTouches;
- (void) displayLinkCallback: (CADisplayLink*) dl
{
if (owner != nullptr)
owner->displayLinkCallback();
owner->displayLinkCallback (dl.targetTimestamp);
}
#if JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS
@ -2160,9 +2160,9 @@ void UIViewComponentPeer::dismissPendingTextInput()
}
//==============================================================================
void UIViewComponentPeer::displayLinkCallback()
void UIViewComponentPeer::displayLinkCallback (double timestampSec)
{
vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
callVBlankListeners (timestampSec);
if (deferredRepaints.isEmpty())
return;

View file

@ -122,7 +122,7 @@ private:
if (output->WaitForVBlank() == S_OK)
{
if (const auto now = Time::getMillisecondCounterHiRes();
now - std::exchange (lastVBlankEvent, now) < 1.0)
now - lastVBlankEvent.exchange (now) < 1.0)
{
Thread::sleep (1);
}
@ -145,8 +145,10 @@ private:
void handleAsyncUpdate() override
{
const auto timestampSec = lastVBlankEvent / 1000.0;
for (auto& listener : listeners)
listener.get().onVBlank();
listener.get().onVBlank (timestampSec);
{
const std::scoped_lock lock { mutex };
@ -170,7 +172,7 @@ private:
exit,
};
double lastVBlankEvent = 0.0;
std::atomic<double> lastVBlankEvent{};
ThreadState threadState = ThreadState::paint;
std::condition_variable condvar;
std::mutex mutex;

View file

@ -1848,7 +1848,8 @@ public:
//==============================================================================
static void handleDoFrameCallback (JNIEnv*, AndroidComponentPeer& t, [[maybe_unused]] int64 frameTimeNanos)
{
t.vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
const auto timestampSec = (double) frameTimeNanos / (double) 1'000'000'000;
t.callVBlankListeners (timestampSec);
}
static void handlePaintCallback (JNIEnv* env, AndroidComponentPeer& t, jobject canvas, jobject paint)

View file

@ -573,7 +573,8 @@ private:
void onVBlank()
{
vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
const auto timestampSec = Time::getMillisecondCounterHiRes() / 1000.0;
callVBlankListeners (timestampSec);
if (repainter != nullptr)
repainter->dispatchDeferredRepaints();

View file

@ -1920,9 +1920,9 @@ public:
}
//==============================================================================
void onVBlank() override
void onVBlank (double timestampSec) override
{
vBlankListeners.call ([] (auto& l) { l.onVBlank(); });
callVBlankListeners (timestampSec);
dispatchDeferredRepaints();
if (renderContext != nullptr)

View file

@ -617,6 +617,11 @@ void ComponentPeer::forceDisplayUpdate()
Desktop::getInstance().displays->refresh();
}
void ComponentPeer::callVBlankListeners (double timestampSec)
{
vBlankListeners.call ([timestampSec] (auto& l) { l.onVBlank (timestampSec); });
}
void ComponentPeer::globalFocusChanged ([[maybe_unused]] Component* comp)
{
refreshTextInputTarget();

View file

@ -516,8 +516,12 @@ public:
/** Destructor. */
virtual ~VBlankListener() = default;
/** Called on every vertical blank of the display to which the peer is associated. */
virtual void onVBlank() = 0;
/** Called on every vertical blank of the display to which the peer is associated.
The timestampSec parameter is a monotonically increasing value expressed in seconds
that corresponds to the time at which the next frame will be displayed.
*/
virtual void onVBlank (double timestampSec) = 0;
};
/** Adds a VBlankListener. */
@ -577,6 +581,7 @@ public:
protected:
//==============================================================================
static void forceDisplayUpdate();
void callVBlankListeners (double timestampSec);
Component& component;
const int styleFlags;

View file

@ -899,7 +899,7 @@ public:
{
connection.emplace (sharedDisplayLinks->registerFactory ([this] (CGDirectDisplayID display)
{
return [this, display]
return [this, display] (double)
{
if (display == lastDisplay)
triggerRepaint();