Skip to content

Commit 369bab4

Browse files
authored
Implement setPresentationTime for Metal (#9470)
1 parent c98038b commit 369bab4

4 files changed

Lines changed: 46 additions & 12 deletions

File tree

NEW_RELEASE_NOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
77

88
## Release notes for next branch cut
9+
10+
- Support `setPresentationTime` with the Metal backend.

filament/backend/src/metal/MetalDriver.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@
312312
}
313313

314314
void MetalDriver::setPresentationTime(int64_t monotonic_clock_ns) {
315+
assert_invariant(mContext->currentDrawSwapChain);
316+
mContext->currentDrawSwapChain->setPresentationTime(monotonic_clock_ns);
315317
}
316318

317319
void MetalDriver::endFrame(uint32_t frameId) {

filament/backend/src/metal/MetalHandles.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ class MetalSwapChain : public HwSwapChain {
141141
// FrameScheduledCallback.
142142
void present();
143143

144+
void setPresentationTime(int64_t timeNs) { presentationTimeNs = timeNs; }
145+
144146
NSUInteger getSurfaceWidth() const;
145147
NSUInteger getSurfaceHeight() const;
146148
NSUInteger getSampleCount() const;
@@ -159,7 +161,7 @@ class MetalSwapChain : public HwSwapChain {
159161
bool isCaMetalLayer() const { return type == SwapChainType::CAMETALLAYER; }
160162
bool isHeadless() const { return type == SwapChainType::HEADLESS; }
161163

162-
void scheduleFrameScheduledCallback();
164+
void scheduleFrameScheduledCallback(int64_t presentationTimeNs);
163165
void scheduleFrameCompletedCallback();
164166

165167
MetalAttachment acquireBaseDrawable();
@@ -192,6 +194,11 @@ class MetalSwapChain : public HwSwapChain {
192194

193195
int64_t abandonedUntilFrame = -1;
194196

197+
// If zero, the next presentation should happen as soon as possible.
198+
// Otherwise, this is the timestamp when the present should happen.
199+
// Resets to 0 after the present.
200+
int64_t presentationTimeNs = 0;
201+
195202
// These fields store a callback to notify the client that a frame is ready for presentation. If
196203
// !frameScheduled.callback, then the Metal backend automatically calls presentDrawable when the
197204
// frame is committed. Otherwise, the Metal backend will not automatically present the frame.

filament/backend/src/metal/MetalHandles.mm

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,19 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
325325
if (frameCompleted.callback) {
326326
scheduleFrameCompletedCallback();
327327
}
328+
const auto timeNs = presentationTimeNs;
329+
presentationTimeNs = 0;
328330
if (drawable) {
329331
if (frameScheduled.callback) {
330-
scheduleFrameScheduledCallback();
332+
scheduleFrameScheduledCallback(timeNs);
331333
} else {
332-
[getPendingCommandBuffer(&context) presentDrawable:drawable];
334+
if (presentationTimeNs) {
335+
const CFTimeInterval timeSeconds =
336+
(CFTimeInterval) presentationTimeNs / 1000000000.0;
337+
[getPendingCommandBuffer(&context) presentDrawable:drawable atTime:timeSeconds];
338+
} else {
339+
[getPendingCommandBuffer(&context) presentDrawable:drawable];
340+
}
333341
}
334342
}
335343
}
@@ -341,15 +349,22 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
341349
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
342350

343351
static PresentDrawableData* create(id<CAMetalDrawable> drawable,
344-
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags) {
352+
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags,
353+
int64_t presentationTimeNs) {
345354
assert_invariant(drawableMutex);
346355
assert_invariant(driver);
347-
return new PresentDrawableData(drawable, drawableMutex, driver, flags);
356+
return new PresentDrawableData(drawable, drawableMutex, driver, flags, presentationTimeNs);
348357
}
349358

350359
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
351360
if (shouldPresent) {
352-
[that->mDrawable present];
361+
if (that->mPresentationTimeNs) {
362+
const CFTimeInterval timeSeconds =
363+
(CFTimeInterval) that->mPresentationTimeNs / 1000000000.0;
364+
[that->mDrawable presentAtTime:timeSeconds];
365+
} else {
366+
[that->mDrawable present];
367+
}
353368
}
354369

355370
if (that->mFlags & SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER) {
@@ -367,8 +382,12 @@ static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPr
367382

368383
private:
369384
PresentDrawableData(id<CAMetalDrawable> drawable, std::shared_ptr<std::mutex> drawableMutex,
370-
MetalDriver* driver, uint64_t flags)
371-
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver), mFlags(flags) {}
385+
MetalDriver* driver, uint64_t flags, int64_t presentationTimeNs)
386+
: mDrawable(drawable),
387+
mDrawableMutex(drawableMutex),
388+
mDriver(driver),
389+
mFlags(flags),
390+
mPresentationTimeNs(presentationTimeNs) {}
372391

373392
static void cleanupAndDestroy(PresentDrawableData *that) {
374393
if (that->mDrawable) {
@@ -384,14 +403,15 @@ static void cleanupAndDestroy(PresentDrawableData *that) {
384403
std::shared_ptr<std::mutex> mDrawableMutex;
385404
MetalDriver* mDriver = nullptr;
386405
uint64_t mFlags = 0;
406+
int64_t mPresentationTimeNs = 0;
387407
};
388408

389409
void presentDrawable(bool presentFrame, void* user) {
390410
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
391411
PresentDrawableData::maybePresentAndDestroyAsync(presentDrawableData, presentFrame);
392412
}
393413

394-
void MetalSwapChain::scheduleFrameScheduledCallback() {
414+
void MetalSwapChain::scheduleFrameScheduledCallback(int64_t presentationTimeNs) {
395415
if (!frameScheduled.callback) {
396416
return;
397417
}
@@ -400,8 +420,11 @@ void presentDrawable(bool presentFrame, void* user) {
400420

401421
struct Callback {
402422
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
403-
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags)
404-
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver, flags)) {}
423+
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags,
424+
int64_t presentationTimeNs)
425+
: f(callback),
426+
data(PresentDrawableData::create(drawable, drawableMutex, driver, flags,
427+
presentationTimeNs)) {}
405428
std::shared_ptr<FrameScheduledCallback> f;
406429
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
407430
std::unique_ptr<PresentDrawableData> data;
@@ -419,7 +442,7 @@ static void func(void* user) {
419442
uint64_t const flags = frameScheduled.flags;
420443
ReleasablePointer* callback = [[ReleasablePointer alloc]
421444
initWithPointer:new Callback(frameScheduled.callback, drawable, layerDrawableMutex,
422-
context.driver, flags)];
445+
context.driver, flags, presentationTimeNs)];
423446

424447
backend::CallbackHandler* handler = frameScheduled.handler;
425448
MetalDriver* driver = context.driver;

0 commit comments

Comments
 (0)