Hi PJ -
Thanks for the feedback, but unfortunately that can't work in our case.
While CA has a native 60hz refresh - we're targetting 30hz via CADisplayLink.
>>You want to ensure your present doesn't land in the current 60Hz interval but the next one so should target between
>>17ms and 33ms into the future (1.f/60.f * 1.2f would be a good choice).
That math assumes the building of our command buffer + driver & GPU consumption all happen within 2x 60hz ticks.
We've got two operations to do -- build the command buffer, and have the driver + GPU consume the command buffer.
The odd of that happening completely within the current 33ms frame (from CADisplayLink tick -> 2x 60hz intervals) is zero 🙂 Thats why we target 30hz not 60hz. And we're assuming the driver + GPU run in parallel with our CPU thread building the next command buffer.
(I'd attach a diagram from Metal System Trace, but it seems forum posts + images places them in moderation limbo, i'll attempt with ASCII)
What we're expecting to do:
a) Time 0 (CADisplayLink tick).
Build CommandBuffer0 + schedulePresent0.
Commit CB0. (Driver & GPU might start consumption)
b) Time 33ms (CAD tick + 1).
Build CB1 + schedulePresent1.
Driver & GPU are *still* consuming CB0 in parallel - when done, waits for c) to present
c) Time 66ms (CAD tick + 2).
CB0 is presented.
Build CB2 + schedulePresent2.
Driver & GPU consuming CB1 in parallel.
d: Time 99ms (CAD tick + 3)
CB1 is presented
...etc. The pattern continues.
So at a), we need to schedule the present to happen at c), which is (33ms * 1.6) -Aiming for 60hz * 4 vsync.
This all works as expected when we use the basic presentDrawable: -- all the expected parallelism between command buffers and the GPU is fine 🙂. Our workload is generally heavy and consistent enough we present @ 2 x 30hz (i.e. 4 x 60hz) from the display tick. The problem we're trying to solve is the occasionally fast frame that finishes on the GPU before 3 x 60hz tick, which leads to inconsistent output pacing.
I guess something I could try is change the commit & schedule present of the CB from when the building is done to the start of the *next* frame (i.e. change schedulePresent0 + commit CBO0 from a) -> start of b)).
Scheme 2
a) Time 0 (CADisplayLink tick).
Build CB0
b) Time 33ms (CAD tick + 1).
scedulePresent0 + commit CB0
Driver & GPU consuming CB0 in parallel - when done, waits for c) to present
build CB1
c) Time 66ms (CAD tick + 2).
CB0 is presented
schedulePresent1 + commit CB1
Driver & GPU consuming CB1 in parallel.
CB0 is presented.
Then the schedule present *would* follow your path. BUT we're now potentially leaving the GPU idle until the next CA tick. Not ideal.
Cheers! - j