Skip to content

Commit fd0bcb1

Browse files
committed
implement proper frame scheduling
1 parent 9d56e48 commit fd0bcb1

File tree

3 files changed

+106
-18
lines changed

3 files changed

+106
-18
lines changed

src/frame_scheduler.c

Lines changed: 72 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+
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,15 @@ frame_scheduler_new(bool uses_frame_requests, enum present_mode present_mode, fl
4351
}
4452

4553
scheduler->n_refs = REFCOUNT_INIT_1;
54+
mutex_init(&scheduler->mutex);
55+
4656
scheduler->uses_frame_requests = uses_frame_requests;
4757
scheduler->present_mode = present_mode;
4858
scheduler->vsync_cb = vsync_cb;
4959
scheduler->userdata = userdata;
60+
61+
scheduler->waiting_for_scanout = false;
62+
scheduler->has_scheduled_frame = false;
5063
return scheduler;
5164
}
5265

@@ -118,14 +131,44 @@ void frame_scheduler_request_fb(struct frame_scheduler *scheduler, uint64_t scan
118131
UNIMPLEMENTED();
119132
}
120133

121-
void frame_scheduler_present_frame(struct frame_scheduler *scheduler, void_callback_t present_cb, void *userdata, void_callback_t cancel_cb) {
134+
void frame_scheduler_present_frame(struct frame_scheduler *scheduler, void_callback_t present_cb, void *userdata, void_callback_t cancel_cb) {
122135
ASSERT_NOT_NULL(scheduler);
123136
ASSERT_NOT_NULL(present_cb);
124-
(void) scheduler;
125-
(void) cancel_cb;
137+
138+
frame_scheduler_lock(scheduler);
126139

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

131174
void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_timestamp, uint64_t timestamp_ns) {
@@ -135,6 +178,27 @@ void frame_scheduler_on_scanout(struct frame_scheduler *scheduler, bool has_time
135178
(void) has_timestamp;
136179
(void) timestamp_ns;
137180

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

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: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,14 +1077,23 @@ struct frame {
10771077
struct tracer *tracer;
10781078
struct kms_req *req;
10791079
struct drmdev *drmdev;
1080+
struct frame_scheduler *scheduler;
10801081
bool unset_should_apply_mode_on_commit;
10811082
};
10821083

10831084
UNUSED static void on_scanout(uint64_t vblank_ns, void *userdata) {
1084-
(void) vblank_ns;
1085-
(void) userdata;
1085+
struct frame *frame;
1086+
1087+
ASSERT_NOT_NULL(userdata);
1088+
frame = userdata;
10861089

1087-
/// TODO: What should we do here?
1090+
// This potentially presents a new frame.
1091+
frame_scheduler_on_scanout(frame->scheduler, true, vblank_ns);
1092+
1093+
frame_scheduler_unref(frame->scheduler);
1094+
tracer_unref(frame->tracer);
1095+
kms_req_unref(frame->req);
1096+
free(frame);
10881097
}
10891098

10901099
static void on_present_frame(void *userdata) {
@@ -1095,17 +1104,28 @@ static void on_present_frame(void *userdata) {
10951104

10961105
frame = userdata;
10971106

1098-
TRACER_BEGIN(frame->tracer, "kms_req_commit_nonblocking");
1099-
ok = kms_req_commit_nonblocking(frame->req, frame->drmdev, on_scanout, NULL, NULL);
1100-
TRACER_END(frame->tracer, "kms_req_commit_nonblocking");
1107+
{
1108+
// Keep our own reference on tracer, because the frame might be destroyed
1109+
// after kms_req_commit_nonblocking returns.
1110+
struct tracer *tracer = tracer_ref(frame->tracer);
1111+
1112+
// The pageflip events might be handled on a different thread, so on_scanout
1113+
// might already be executed and the frame instance already freed once
1114+
// kms_req_commit_nonblocking returns.
1115+
TRACER_BEGIN(tracer, "kms_req_commit_nonblocking");
1116+
ok = kms_req_commit_nonblocking(frame->req, frame->drmdev, on_scanout, frame, NULL);
1117+
TRACER_END(tracer, "kms_req_commit_nonblocking");
1118+
1119+
tracer_unref(tracer);
1120+
}
11011121

11021122
if (ok != 0) {
11031123
LOG_ERROR("Could not commit frame request.\n");
1124+
frame_scheduler_unref(frame->scheduler);
1125+
tracer_unref(frame->tracer);
1126+
kms_req_unref(frame->req);
1127+
free(frame);
11041128
}
1105-
1106-
tracer_unref(frame->tracer);
1107-
kms_req_unref(frame->req);
1108-
free(frame);
11091129
}
11101130

11111131
static void on_cancel_frame(void *userdata) {
@@ -1114,6 +1134,7 @@ static void on_cancel_frame(void *userdata) {
11141134

11151135
frame = userdata;
11161136

1137+
frame_scheduler_unref(frame->scheduler);
11171138
tracer_unref(frame->tracer);
11181139
kms_req_unref(frame->req);
11191140
drmdev_unref(frame->drmdev);
@@ -1234,6 +1255,7 @@ static int kms_window_push_composition_locked(struct window *window, struct fl_l
12341255
frame->req = req;
12351256
frame->tracer = tracer_ref(window->tracer);
12361257
frame->drmdev = drmdev_ref(window->kms.drmdev);
1258+
frame->scheduler = frame_scheduler_ref(window->frame_scheduler);
12371259
frame->unset_should_apply_mode_on_commit = window->kms.should_apply_mode;
12381260

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

0 commit comments

Comments
 (0)