Skip to content

Commit 54cc8fd

Browse files
committed
WIP: feat: implement frame timing
Make frame_scheduler able to time frames, e.g. tell flutter when to start rendering a frame when it receives a frame request. For now, if no frame is currently waiting to be scanned out, this just responds to the frame request with the current frame and a 60Hz frame interval, i.e. starts the frame immediately. If a frame is waiting to be scanned out, this waits until the scanout to reply to the frame timings request. (With the timestamp of the scanout being used as the frame start time in the reply to the frame timings request) If another frame is already queued after that, the scheduler will wait until that other queued frame is scanned out to reply to the frame timings request.
1 parent 23d39c2 commit 54cc8fd

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

src/frame_scheduler.c

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ struct frame_scheduler {
3333
void_callback_t cancel_cb;
3434
void *userdata;
3535
} scheduled_frame;
36+
37+
intptr_t pending_frame_timings_request;
3638
};
3739

3840
DEFINE_REF_OPS(frame_scheduler, n_refs)
@@ -59,6 +61,9 @@ frame_scheduler_new(bool uses_frame_requests, enum present_mode present_mode, fl
5961

6062
scheduler->waiting_for_scanout = false;
6163
scheduler->has_scheduled_frame = false;
64+
65+
scheduler->pending_frame_timings_request = 0;
66+
6267
return scheduler;
6368
}
6469

@@ -94,12 +99,28 @@ void frame_scheduler_on_fl_vsync_request(struct frame_scheduler *scheduler, intp
9499
// as well if we draw too many frames at once. (Especially considering one framebuffer is probably busy with scanout right now)
95100
//
96101

97-
/// TODO: Implement
98-
/// For now, just unconditionally reply
99102
if (scheduler->present_mode == kTripleBufferedVsync_PresentMode) {
100-
scheduler->vsync_cb(scheduler->userdata, vsync_baton, 0, 0);
103+
/// TODO: Query actual frame interval and vblank timestamp here
104+
uint64_t now = get_monotonic_time();
105+
uint64_t now_plus_frame_interval = now + 1000000000/60;
106+
scheduler->vsync_cb(scheduler->userdata, vsync_baton, now, now_plus_frame_interval);
101107
} else if (scheduler->present_mode == kDoubleBufferedVsync_PresentMode) {
102-
scheduler->vsync_cb(scheduler->userdata, vsync_baton, 0, 0);
108+
frame_scheduler_lock(scheduler);
109+
110+
if (scheduler->waiting_for_scanout) {
111+
// Reply to the frame timings request once the current frame has been scanned out.
112+
ASSERT_ZERO(scheduler->pending_frame_timings_request);
113+
scheduler->pending_frame_timings_request = vsync_baton;
114+
} else {
115+
/// TODO: Query actual frame interval and vblank timestamp here
116+
uint64_t now = get_monotonic_time();
117+
uint64_t now_plus_frame_interval = now + 1000000000/60;
118+
scheduler->vsync_cb(scheduler->userdata, vsync_baton, now, now_plus_frame_interval);
119+
}
120+
121+
frame_scheduler_unlock(scheduler);
122+
} else {
123+
UNREACHABLE();
103124
}
104125
}
105126

@@ -179,6 +200,7 @@ void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_time
179200

180201
void_callback_t present_cb = NULL;
181202
void *userdata = NULL;
203+
intptr_t pending_frame_timings_request = 0;
182204

183205
frame_scheduler_lock(scheduler);
184206

@@ -193,11 +215,28 @@ void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_time
193215

194216
scheduler->has_scheduled_frame = false;
195217
scheduler->waiting_for_scanout = true;
218+
} else if (scheduler->pending_frame_timings_request) {
219+
// only reply to the frame timings request if we don't have another frame already
220+
// pending. We only want to start a new frame once the previous frame has been scanned out
221+
/// TODO: Maybe do start it before the previous one has been scanned out?
222+
pending_frame_timings_request = scheduler->pending_frame_timings_request;
223+
scheduler->pending_frame_timings_request = 0;
196224
}
197225
}
198226

199227
frame_scheduler_unlock(scheduler);
200228

229+
if (pending_frame_timings_request) {
230+
if (!has_timestamp) {
231+
timestamp_ns = get_monotonic_time();
232+
}
233+
234+
/// TODO: Use actual frame interval here
235+
uint64_t now_plus_frame_interval = timestamp_ns + 1000000000 / 60;
236+
237+
scheduler->vsync_cb(scheduler->userdata, pending_frame_timings_request, timestamp_ns, now_plus_frame_interval);
238+
}
239+
201240
if (present_cb != NULL) {
202241
present_cb(userdata);
203242
}

0 commit comments

Comments
 (0)