Skip to content

Commit b637456

Browse files
committed
feat: frame scheduling
Always do non-blocking atomic commits. To resolve issues with flutter trying to present frames too soon, while another frame was already sent to the kernel (i.e. comitted via KMS atomic commit), we either have to discard subsequent, too-early frames or queue them. In this case, we basically keep a 1-frame deep queue (or 2-frames, if you count the in-kernel waiting-for-scanout frame), with the 1 queued frame being displaced whenever a newer frame is available. (Similar to vulkans MAILBOX swapchain presentation mode)
1 parent dcf354e commit b637456

File tree

3 files changed

+111
-24
lines changed

3 files changed

+111
-24
lines changed

src/frame_scheduler.c

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@
1818

1919
struct frame_scheduler {
2020
refcount_t n_refs;
21+
pthread_mutex_t mutex;
2122

2223
bool uses_frame_requests;
2324
enum present_mode present_mode;
2425
fl_vsync_callback_t vsync_cb;
2526
void *userdata;
2627

27-
pthread_mutex_t mutex;
28+
bool waiting_for_scanout;
29+
30+
bool has_scheduled_frame;
31+
struct {
32+
void_callback_t present_cb;
33+
void_callback_t cancel_cb;
34+
void *userdata;
35+
} scheduled_frame;
2836
};
2937

3038
DEFINE_REF_OPS(frame_scheduler, n_refs)
@@ -43,10 +51,16 @@ frame_scheduler_new(bool uses_frame_requests, enum present_mode present_mode, fl
4351
}
4452

4553
scheduler->n_refs = REFCOUNT_INIT_1;
54+
ASSERTED int ok = pthread_mutex_init(&scheduler->mutex, get_default_mutex_attrs());
55+
ASSERT_ZERO(ok);
56+
4657
scheduler->uses_frame_requests = uses_frame_requests;
4758
scheduler->present_mode = present_mode;
4859
scheduler->vsync_cb = vsync_cb;
4960
scheduler->userdata = userdata;
61+
62+
scheduler->waiting_for_scanout = false;
63+
scheduler->has_scheduled_frame = false;
5064
return scheduler;
5165
}
5266

@@ -118,14 +132,44 @@ void frame_scheduler_request_fb(struct frame_scheduler *scheduler, uint64_t scan
118132
UNIMPLEMENTED();
119133
}
120134

121-
void frame_scheduler_present_frame(struct frame_scheduler *scheduler, void_callback_t present_cb, void *userdata, void_callback_t cancel_cb) {
135+
void frame_scheduler_present_frame(struct frame_scheduler *scheduler, void_callback_t present_cb, void *userdata, void_callback_t cancel_cb) {
122136
ASSERT_NOT_NULL(scheduler);
123137
ASSERT_NOT_NULL(present_cb);
124-
(void) scheduler;
125-
(void) cancel_cb;
138+
139+
frame_scheduler_lock(scheduler);
126140

127-
/// TODO: Implement
128-
present_cb(userdata);
141+
if (scheduler->waiting_for_scanout) {
142+
void_callback_t cancel_prev_sched_frame = NULL;
143+
void *prev_sched_frame_userdata = NULL;
144+
145+
// We're already waiting for a scanout, so we can't present a frame right now.
146+
// Wait till the previous frame is scanned out, and then present.
147+
148+
if (scheduler->has_scheduled_frame) {
149+
// If we already have a frame scheduled, cancel it.
150+
cancel_prev_sched_frame = scheduler->scheduled_frame.cancel_cb;
151+
prev_sched_frame_userdata = scheduler->scheduled_frame.userdata;
152+
153+
memset(&scheduler->scheduled_frame, 0, sizeof scheduler->scheduled_frame);
154+
}
155+
156+
scheduler->has_scheduled_frame = true;
157+
scheduler->scheduled_frame.present_cb = present_cb;
158+
scheduler->scheduled_frame.cancel_cb = cancel_cb;
159+
scheduler->scheduled_frame.userdata = userdata;
160+
161+
frame_scheduler_unlock(scheduler);
162+
163+
if (cancel_prev_sched_frame != NULL) {
164+
cancel_prev_sched_frame(prev_sched_frame_userdata);
165+
}
166+
} else {
167+
// We're not waiting for a scanout right now.
168+
scheduler->waiting_for_scanout = true;
169+
frame_scheduler_unlock(scheduler);
170+
171+
present_cb(userdata);
172+
}
129173
}
130174

131175
void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_timestamp, uint64_t timestamp_ns) {
@@ -135,6 +179,27 @@ void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_time
135179
(void) has_timestamp;
136180
(void) timestamp_ns;
137181

138-
/// TODO: Implement
139-
UNIMPLEMENTED();
182+
void_callback_t present_cb = NULL;
183+
void *userdata = NULL;
184+
185+
frame_scheduler_lock(scheduler);
186+
187+
if (scheduler->waiting_for_scanout) {
188+
scheduler->waiting_for_scanout = false;
189+
190+
if (scheduler->has_scheduled_frame) {
191+
present_cb = scheduler->scheduled_frame.present_cb;
192+
userdata = scheduler->scheduled_frame.userdata;
193+
194+
memset(&scheduler->scheduled_frame, 0, sizeof scheduler->scheduled_frame);
195+
196+
scheduler->has_scheduled_frame = false;
197+
}
198+
}
199+
200+
frame_scheduler_unlock(scheduler);
201+
202+
if (present_cb != NULL) {
203+
present_cb(userdata);
204+
}
140205
}

src/frame_scheduler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ void frame_scheduler_on_fb_released(struct frame_scheduler *scheduler, bool has_
8080
*/
8181
void frame_scheduler_present_frame(struct frame_scheduler *scheduler, void_callback_t present_cb, void *userdata, void_callback_t cancel_cb);
8282

83+
void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_timestamp, uint64_t timestamp_ns);
84+
8385
#endif // _FLUTTERPI_SRC_FRAME_SCHEDULER_H

src/window.c

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,42 +1074,61 @@ struct frame {
10741074
struct tracer *tracer;
10751075
struct drmdev *drmdev;
10761076
struct kms_req *req;
1077+
struct frame_scheduler *scheduler;
10771078
bool unset_should_apply_mode_on_commit;
10781079
};
10791080

1080-
UNUSED static void on_scanout(struct drmdev *drmdev, uint64_t vblank_ns, void *userdata) {
1081-
ASSERT_NOT_NULL(drmdev);
1082-
(void) drmdev;
1083-
(void) vblank_ns;
1084-
(void) userdata;
1081+
UNUSED static void on_scanout(uint64_t vblank_ns, void *userdata) {
1082+
ASSERT_NOT_NULL(userdata);
1083+
struct frame *frame = userdata;
10851084

1086-
/// TODO: What should we do here?
1085+
// This potentially presents a new frame.
1086+
frame_scheduler_on_scanout(frame->scheduler, true, vblank_ns);
1087+
1088+
frame_scheduler_unref(frame->scheduler);
1089+
tracer_unref(frame->tracer);
1090+
kms_req_unref(frame->req);
1091+
drmdev_unref(frame->drmdev);
1092+
free(frame);
10871093
}
10881094

10891095
static void on_present_frame(void *userdata) {
10901096
ASSERT_NOT_NULL(userdata);
10911097
struct frame *frame = userdata;
10921098

1093-
TRACER_BEGIN(frame->tracer, "kms_req_commit_nonblocking");
1094-
int ok = kms_req_commit_blocking(frame->req, frame->drmdev, NULL);
1095-
TRACER_END(frame->tracer, "kms_req_commit_nonblocking");
1099+
int ok;
1100+
1101+
{
1102+
// Keep our own reference on tracer, because the frame might be destroyed
1103+
// after kms_req_commit_nonblocking returns.
1104+
struct tracer *tracer = tracer_ref(frame->tracer);
1105+
1106+
// The pageflip events might be handled on a different thread, so on_scanout
1107+
// might already be executed and the frame instance already freed once
1108+
// kms_req_commit_nonblocking returns.
1109+
TRACER_BEGIN(tracer, "kms_req_commit_nonblocking");
1110+
ok = kms_req_commit_nonblocking(frame->req, frame->drmdev, on_scanout, frame, NULL);
1111+
TRACER_END(tracer, "kms_req_commit_nonblocking");
1112+
1113+
tracer_unref(tracer);
1114+
}
10961115

10971116
if (ok != 0) {
10981117
LOG_ERROR("Could not commit frame request.\n");
1118+
frame_scheduler_unref(frame->scheduler);
1119+
tracer_unref(frame->tracer);
1120+
kms_req_unref(frame->req);
1121+
drmdev_unref(frame->drmdev);
1122+
free(frame);
10991123
}
1100-
1101-
tracer_unref(frame->tracer);
1102-
kms_req_unref(frame->req);
1103-
drmdev_unref(frame->drmdev);
1104-
free(frame);
11051124
}
11061125

11071126
static void on_cancel_frame(void *userdata) {
1108-
struct frame *frame;
11091127
ASSERT_NOT_NULL(userdata);
11101128

1111-
frame = userdata;
1129+
struct frame *frame = userdata;
11121130

1131+
frame_scheduler_unref(frame->scheduler);
11131132
tracer_unref(frame->tracer);
11141133
kms_req_unref(frame->req);
11151134
drmdev_unref(frame->drmdev);
@@ -1226,6 +1245,7 @@ static int kms_window_push_composition_locked(struct window *window, struct fl_l
12261245
frame->req = req;
12271246
frame->tracer = tracer_ref(window->tracer);
12281247
frame->drmdev = drmdev_ref(window->kms.drmdev);
1248+
frame->scheduler = frame_scheduler_ref(window->frame_scheduler);
12291249
frame->unset_should_apply_mode_on_commit = window->kms.should_apply_mode;
12301250

12311251
frame_scheduler_present_frame(window->frame_scheduler, on_present_frame, frame, on_cancel_frame);

0 commit comments

Comments
 (0)