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