1 /*
2 * Copyright © 2018 Roman Gilg
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of the
9 * copyright holders not be used in advertising or publicity
10 * pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no
12 * representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
21 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * SOFTWARE.
24 */
25
26 #include "xwayland.h"
27 #include "glamor.h"
28
29 #include <present.h>
30
31 /*
32 * When not flipping let Present copy with 60fps.
33 * When flipping wait on frame_callback, otherwise
34 * the surface is not visible, in this case update
35 * with long interval.
36 */
37 #define TIMER_LEN_COPY 17 // ~60fps
38 #define TIMER_LEN_FLIP 1000 // 1fps
39
40 static DevPrivateKeyRec xwl_present_window_private_key;
41
42 static struct xwl_present_window *
xwl_present_window_priv(WindowPtr window)43 xwl_present_window_priv(WindowPtr window)
44 {
45 return dixGetPrivate(&window->devPrivates,
46 &xwl_present_window_private_key);
47 }
48
49 static struct xwl_present_window *
xwl_present_window_get_priv(WindowPtr window)50 xwl_present_window_get_priv(WindowPtr window)
51 {
52 struct xwl_present_window *xwl_present_window = xwl_present_window_priv(window);
53
54 if (xwl_present_window == NULL) {
55 xwl_present_window = calloc (1, sizeof (struct xwl_present_window));
56 if (!xwl_present_window)
57 return NULL;
58
59 xwl_present_window->window = window;
60 xwl_present_window->msc = 1;
61 xwl_present_window->ust = GetTimeInMicros();
62
63 xorg_list_init(&xwl_present_window->frame_callback_list);
64 xorg_list_init(&xwl_present_window->event_list);
65 xorg_list_init(&xwl_present_window->release_queue);
66
67 dixSetPrivate(&window->devPrivates,
68 &xwl_present_window_private_key,
69 xwl_present_window);
70 }
71
72 return xwl_present_window;
73 }
74
75 static void
xwl_present_free_timer(struct xwl_present_window * xwl_present_window)76 xwl_present_free_timer(struct xwl_present_window *xwl_present_window)
77 {
78 TimerFree(xwl_present_window->frame_timer);
79 xwl_present_window->frame_timer = NULL;
80 }
81
82 static CARD32
83 xwl_present_timer_callback(OsTimerPtr timer,
84 CARD32 time,
85 void *arg);
86
87 static inline Bool
xwl_present_has_events(struct xwl_present_window * xwl_present_window)88 xwl_present_has_events(struct xwl_present_window *xwl_present_window)
89 {
90 return !!xwl_present_window->sync_flip ||
91 !xorg_list_is_empty(&xwl_present_window->event_list);
92 }
93
94 static void
xwl_present_reset_timer(struct xwl_present_window * xwl_present_window)95 xwl_present_reset_timer(struct xwl_present_window *xwl_present_window)
96 {
97 if (xwl_present_has_events(xwl_present_window)) {
98 CARD32 timeout;
99
100 if (!xorg_list_is_empty(&xwl_present_window->frame_callback_list))
101 timeout = TIMER_LEN_FLIP;
102 else
103 timeout = TIMER_LEN_COPY;
104
105 xwl_present_window->frame_timer = TimerSet(xwl_present_window->frame_timer,
106 0, timeout,
107 &xwl_present_timer_callback,
108 xwl_present_window);
109 } else {
110 xwl_present_free_timer(xwl_present_window);
111 }
112 }
113
114 static void
xwl_present_free_event(struct xwl_present_event * event)115 xwl_present_free_event(struct xwl_present_event *event)
116 {
117 if (!event)
118 return;
119
120 if (event->pixmap) {
121 if (!event->buffer_released) {
122 struct wl_buffer *buffer =
123 xwl_glamor_pixmap_get_wl_buffer(event->pixmap, NULL);
124
125 wl_buffer_set_user_data(buffer, NULL);
126 }
127
128 dixDestroyPixmap(event->pixmap, event->pixmap->drawable.id);
129 }
130
131 xorg_list_del(&event->list);
132 free(event);
133 }
134
135 void
xwl_present_cleanup(WindowPtr window)136 xwl_present_cleanup(WindowPtr window)
137 {
138 struct xwl_present_window *xwl_present_window = xwl_present_window_priv(window);
139 struct xwl_present_event *event, *tmp;
140
141 if (!xwl_present_window)
142 return;
143
144 xorg_list_del(&xwl_present_window->frame_callback_list);
145
146 if (xwl_present_window->sync_callback) {
147 wl_callback_destroy(xwl_present_window->sync_callback);
148 xwl_present_window->sync_callback = NULL;
149 }
150
151 /* Clear remaining events */
152 xorg_list_for_each_entry_safe(event, tmp, &xwl_present_window->event_list, list)
153 xwl_present_free_event(event);
154
155 xwl_present_free_event(xwl_present_window->sync_flip);
156
157 xorg_list_for_each_entry_safe(event, tmp, &xwl_present_window->release_queue, list)
158 xwl_present_free_event(event);
159
160 /* Clear timer */
161 xwl_present_free_timer(xwl_present_window);
162
163 /* Remove from privates so we don't try to access it later */
164 dixSetPrivate(&window->devPrivates,
165 &xwl_present_window_private_key,
166 NULL);
167
168 free(xwl_present_window);
169 }
170
171 static void
xwl_present_buffer_release(void * data,struct wl_buffer * buffer)172 xwl_present_buffer_release(void *data, struct wl_buffer *buffer)
173 {
174 struct xwl_present_event *event = data;
175 if (!event)
176 return;
177
178 wl_buffer_set_user_data(buffer, NULL);
179 event->buffer_released = TRUE;
180
181 if (event->abort) {
182 if (!event->pending)
183 xwl_present_free_event(event);
184 return;
185 }
186
187 if (!event->pending) {
188 present_wnmd_event_notify(event->xwl_present_window->window,
189 event->event_id,
190 event->xwl_present_window->ust,
191 event->xwl_present_window->msc);
192 xwl_present_free_event(event);
193 }
194 }
195
196 static const struct wl_buffer_listener xwl_present_release_listener = {
197 xwl_present_buffer_release
198 };
199
200 static void
xwl_present_msc_bump(struct xwl_present_window * xwl_present_window)201 xwl_present_msc_bump(struct xwl_present_window *xwl_present_window)
202 {
203 uint64_t msc = ++xwl_present_window->msc;
204 struct xwl_present_event *event, *tmp;
205
206 xwl_present_window->ust = GetTimeInMicros();
207
208 event = xwl_present_window->sync_flip;
209 xwl_present_window->sync_flip = NULL;
210 if (event) {
211 event->pending = FALSE;
212
213 present_wnmd_event_notify(xwl_present_window->window, event->event_id,
214 xwl_present_window->ust, msc);
215
216 if (event->buffer_released) {
217 /* If the buffer was already released, clean up now */
218 present_wnmd_event_notify(xwl_present_window->window, event->event_id,
219 xwl_present_window->ust, msc);
220 xwl_present_free_event(event);
221 } else {
222 xorg_list_add(&event->list, &xwl_present_window->release_queue);
223 }
224 }
225
226 xorg_list_for_each_entry_safe(event, tmp,
227 &xwl_present_window->event_list,
228 list) {
229 if (event->target_msc <= msc) {
230 present_wnmd_event_notify(xwl_present_window->window,
231 event->event_id,
232 xwl_present_window->ust,
233 msc);
234 xwl_present_free_event(event);
235 }
236 }
237 }
238
239 CARD32
xwl_present_timer_callback(OsTimerPtr timer,CARD32 time,void * arg)240 xwl_present_timer_callback(OsTimerPtr timer,
241 CARD32 time,
242 void *arg)
243 {
244 struct xwl_present_window *xwl_present_window = arg;
245
246 /* If we were expecting a frame callback for this window, it didn't arrive
247 * in a second. Stop listening to it to avoid double-bumping the MSC
248 */
249 xorg_list_del(&xwl_present_window->frame_callback_list);
250
251 xwl_present_msc_bump(xwl_present_window);
252 xwl_present_reset_timer(xwl_present_window);
253
254 return 0;
255 }
256
257 void
xwl_present_frame_callback(struct xwl_present_window * xwl_present_window)258 xwl_present_frame_callback(struct xwl_present_window *xwl_present_window)
259 {
260 xorg_list_del(&xwl_present_window->frame_callback_list);
261
262 xwl_present_msc_bump(xwl_present_window);
263
264 /* we do not need the timer anymore for this frame,
265 * reset it for potentially the next one
266 */
267 xwl_present_reset_timer(xwl_present_window);
268 }
269
270 static void
xwl_present_sync_callback(void * data,struct wl_callback * callback,uint32_t time)271 xwl_present_sync_callback(void *data,
272 struct wl_callback *callback,
273 uint32_t time)
274 {
275 struct xwl_present_event *event = data;
276 struct xwl_present_window *xwl_present_window = event->xwl_present_window;
277
278 wl_callback_destroy(xwl_present_window->sync_callback);
279 xwl_present_window->sync_callback = NULL;
280
281 event->pending = FALSE;
282
283 if (event->abort) {
284 /* Event might have been aborted */
285 if (event->buffer_released)
286 /* Buffer was already released, cleanup now */
287 xwl_present_free_event(event);
288 return;
289 }
290
291 present_wnmd_event_notify(xwl_present_window->window,
292 event->event_id,
293 xwl_present_window->ust,
294 xwl_present_window->msc);
295
296 if (event->buffer_released) {
297 /* If the buffer was already released, send the event now again */
298 present_wnmd_event_notify(xwl_present_window->window,
299 event->event_id,
300 xwl_present_window->ust,
301 xwl_present_window->msc);
302 xwl_present_free_event(event);
303 }
304 }
305
306 static const struct wl_callback_listener xwl_present_sync_listener = {
307 xwl_present_sync_callback
308 };
309
310 static RRCrtcPtr
xwl_present_get_crtc(WindowPtr present_window)311 xwl_present_get_crtc(WindowPtr present_window)
312 {
313 struct xwl_present_window *xwl_present_window = xwl_present_window_get_priv(present_window);
314 rrScrPrivPtr rr_private;
315
316 if (xwl_present_window == NULL)
317 return NULL;
318
319 rr_private = rrGetScrPriv(present_window->drawable.pScreen);
320
321 if (rr_private->numCrtcs == 0)
322 return NULL;
323
324 return rr_private->crtcs[0];
325 }
326
327 static int
xwl_present_get_ust_msc(WindowPtr present_window,uint64_t * ust,uint64_t * msc)328 xwl_present_get_ust_msc(WindowPtr present_window, uint64_t *ust, uint64_t *msc)
329 {
330 struct xwl_present_window *xwl_present_window = xwl_present_window_get_priv(present_window);
331 if (!xwl_present_window)
332 return BadAlloc;
333
334 *ust = xwl_present_window->ust;
335 *msc = xwl_present_window->msc;
336
337 return Success;
338 }
339
340 /*
341 * Queue an event to report back to the Present extension when the specified
342 * MSC has past
343 */
344 static int
xwl_present_queue_vblank(WindowPtr present_window,RRCrtcPtr crtc,uint64_t event_id,uint64_t msc)345 xwl_present_queue_vblank(WindowPtr present_window,
346 RRCrtcPtr crtc,
347 uint64_t event_id,
348 uint64_t msc)
349 {
350 struct xwl_present_window *xwl_present_window = xwl_present_window_get_priv(present_window);
351 struct xwl_window *xwl_window = xwl_window_from_window(present_window);
352 struct xwl_present_event *event;
353
354 event = malloc(sizeof *event);
355 if (!event)
356 return BadAlloc;
357
358 event->event_id = event_id;
359 event->pixmap = NULL;
360 event->xwl_present_window = xwl_present_window;
361 event->target_msc = msc;
362
363 xorg_list_append(&event->list, &xwl_present_window->event_list);
364
365 /* If there's a pending frame callback, use that */
366 if (xwl_window && xwl_window->frame_callback &&
367 xorg_list_is_empty(&xwl_present_window->frame_callback_list)) {
368 xorg_list_add(&xwl_present_window->frame_callback_list,
369 &xwl_window->frame_callback_list);
370 }
371
372 if ((xwl_window && xwl_window->frame_callback) ||
373 !xwl_present_window->frame_timer)
374 xwl_present_reset_timer(xwl_present_window);
375
376 return Success;
377 }
378
379 /*
380 * Remove a pending vblank event so that it is not reported
381 * to the extension
382 */
383 static void
xwl_present_abort_vblank(WindowPtr present_window,RRCrtcPtr crtc,uint64_t event_id,uint64_t msc)384 xwl_present_abort_vblank(WindowPtr present_window,
385 RRCrtcPtr crtc,
386 uint64_t event_id,
387 uint64_t msc)
388 {
389 struct xwl_present_window *xwl_present_window = xwl_present_window_priv(present_window);
390 struct xwl_present_event *event, *tmp;
391
392 if (!xwl_present_window)
393 return;
394
395 xorg_list_for_each_entry_safe(event, tmp, &xwl_present_window->event_list, list) {
396 if (event->event_id == event_id) {
397 xwl_present_free_event(event);
398 return;
399 }
400 }
401
402 xorg_list_for_each_entry(event, &xwl_present_window->release_queue, list) {
403 if (event->event_id == event_id) {
404 event->abort = TRUE;
405 return;
406 }
407 }
408 }
409
410 static void
xwl_present_flush(WindowPtr window)411 xwl_present_flush(WindowPtr window)
412 {
413 glamor_block_handler(window->drawable.pScreen);
414 }
415
416 static Bool
xwl_present_check_flip2(RRCrtcPtr crtc,WindowPtr present_window,PixmapPtr pixmap,Bool sync_flip,PresentFlipReason * reason)417 xwl_present_check_flip2(RRCrtcPtr crtc,
418 WindowPtr present_window,
419 PixmapPtr pixmap,
420 Bool sync_flip,
421 PresentFlipReason *reason)
422 {
423 struct xwl_window *xwl_window = xwl_window_from_window(present_window);
424 ScreenPtr screen = pixmap->drawable.pScreen;
425
426 if (!xwl_window)
427 return FALSE;
428
429 /* Can't flip if the window pixmap doesn't match the xwl_window parent
430 * window's, e.g. because a client redirected this window or one of its
431 * parents.
432 */
433 if (screen->GetWindowPixmap(xwl_window->window) != screen->GetWindowPixmap(present_window))
434 return FALSE;
435
436 /*
437 * We currently only allow flips of windows, that have the same
438 * dimensions as their xwl_window parent window. For the case of
439 * different sizes subsurfaces are presumably the way forward.
440 */
441 if (!RegionEqual(&xwl_window->window->winSize, &present_window->winSize))
442 return FALSE;
443
444 return TRUE;
445 }
446
447 static Bool
xwl_present_flip(WindowPtr present_window,RRCrtcPtr crtc,uint64_t event_id,uint64_t target_msc,PixmapPtr pixmap,Bool sync_flip,RegionPtr damage)448 xwl_present_flip(WindowPtr present_window,
449 RRCrtcPtr crtc,
450 uint64_t event_id,
451 uint64_t target_msc,
452 PixmapPtr pixmap,
453 Bool sync_flip,
454 RegionPtr damage)
455 {
456 struct xwl_window *xwl_window = xwl_window_from_window(present_window);
457 struct xwl_present_window *xwl_present_window = xwl_present_window_priv(present_window);
458 BoxPtr damage_box;
459 Bool buffer_created;
460 struct wl_buffer *buffer;
461 struct xwl_present_event *event;
462
463 if (!xwl_window)
464 return FALSE;
465
466 damage_box = RegionExtents(damage);
467
468 event = malloc(sizeof *event);
469 if (!event)
470 return FALSE;
471
472 pixmap->refcnt++;
473 buffer = xwl_glamor_pixmap_get_wl_buffer(pixmap, &buffer_created);
474
475 event->event_id = event_id;
476 event->xwl_present_window = xwl_present_window;
477 event->pixmap = pixmap;
478 event->target_msc = target_msc;
479 event->pending = TRUE;
480 event->abort = FALSE;
481 event->buffer_released = FALSE;
482
483 if (sync_flip) {
484 xorg_list_init(&event->list);
485 xwl_present_window->sync_flip = event;
486 } else {
487 xorg_list_add(&event->list, &xwl_present_window->release_queue);
488 }
489
490 if (buffer_created)
491 wl_buffer_add_listener(buffer, &xwl_present_release_listener, NULL);
492 wl_buffer_set_user_data(buffer, event);
493
494 /* We can flip directly to the main surface (full screen window without clips) */
495 wl_surface_attach(xwl_window->surface, buffer, 0, 0);
496
497 if (!xwl_window->frame_callback)
498 xwl_window_create_frame_callback(xwl_window);
499
500 if (xorg_list_is_empty(&xwl_present_window->frame_callback_list)) {
501 xorg_list_add(&xwl_present_window->frame_callback_list,
502 &xwl_window->frame_callback_list);
503 }
504
505 /* Realign timer */
506 xwl_present_reset_timer(xwl_present_window);
507
508 wl_surface_damage(xwl_window->surface,
509 damage_box->x1 - present_window->drawable.x,
510 damage_box->y1 - present_window->drawable.y,
511 damage_box->x2 - damage_box->x1,
512 damage_box->y2 - damage_box->y1);
513
514 wl_surface_commit(xwl_window->surface);
515
516 if (!sync_flip) {
517 xwl_present_window->sync_callback =
518 wl_display_sync(xwl_window->xwl_screen->display);
519 wl_callback_add_listener(xwl_present_window->sync_callback,
520 &xwl_present_sync_listener,
521 event);
522 }
523
524 wl_display_flush(xwl_window->xwl_screen->display);
525 xwl_window->present_flipped = TRUE;
526 return TRUE;
527 }
528
529 static void
xwl_present_flips_stop(WindowPtr window)530 xwl_present_flips_stop(WindowPtr window)
531 {
532 struct xwl_present_window *xwl_present_window = xwl_present_window_priv(window);
533
534 /* Change back to the fast refresh rate */
535 xwl_present_reset_timer(xwl_present_window);
536 }
537
538 void
xwl_present_unrealize_window(struct xwl_present_window * xwl_present_window)539 xwl_present_unrealize_window(struct xwl_present_window *xwl_present_window)
540 {
541 /* The pending frame callback may never be called, so drop it and shorten
542 * the frame timer interval.
543 */
544 xorg_list_del(&xwl_present_window->frame_callback_list);
545 xwl_present_reset_timer(xwl_present_window);
546 }
547
548 static present_wnmd_info_rec xwl_present_info = {
549 .version = PRESENT_SCREEN_INFO_VERSION,
550 .get_crtc = xwl_present_get_crtc,
551
552 .get_ust_msc = xwl_present_get_ust_msc,
553 .queue_vblank = xwl_present_queue_vblank,
554 .abort_vblank = xwl_present_abort_vblank,
555
556 .flush = xwl_present_flush,
557
558 .capabilities = PresentCapabilityAsync,
559 .check_flip2 = xwl_present_check_flip2,
560 .flip = xwl_present_flip,
561 .flips_stop = xwl_present_flips_stop
562 };
563
564 Bool
xwl_present_init(ScreenPtr screen)565 xwl_present_init(ScreenPtr screen)
566 {
567 struct xwl_screen *xwl_screen = xwl_screen_get(screen);
568
569 /*
570 * doesn't work with the EGLStream backend.
571 */
572 if (xwl_screen->egl_backend == &xwl_screen->eglstream_backend)
573 return FALSE;
574
575 if (!dixRegisterPrivateKey(&xwl_present_window_private_key, PRIVATE_WINDOW, 0))
576 return FALSE;
577
578 return present_wnmd_screen_init(screen, &xwl_present_info);
579 }
580