1 /*
2  * Copyright © 2017 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including
13  * the next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  *
25  * Authors:
26  *    Lyude Paul <lyude@redhat.com>
27  *
28  */
29 
30 #include "xwayland.h"
31 
32 #include "wayland-eglstream-client-protocol.h"
33 #include "wayland-eglstream-controller-client-protocol.h"
34 
35 #define MESA_EGL_NO_X11_HEADERS
36 #define EGL_NO_X11
37 #include <glamor_egl.h>
38 #include <glamor.h>
39 #include <glamor_transform.h>
40 #include <glamor_transfer.h>
41 
42 #include <xf86drm.h>
43 
44 #include <epoxy/egl.h>
45 
46 struct xwl_eglstream_pending_stream {
47     PixmapPtr pixmap;
48     WindowPtr window;
49 
50     struct xwl_pixmap *xwl_pixmap;
51     struct wl_callback *cb;
52 
53     Bool is_valid;
54 
55     struct xorg_list link;
56 };
57 
58 struct xwl_eglstream_private {
59     EGLDeviceEXT egl_device;
60     struct wl_eglstream_display *display;
61     struct wl_eglstream_controller *controller;
62     uint32_t display_caps;
63 
64     EGLConfig config;
65 
66     SetWindowPixmapProcPtr SetWindowPixmap;
67 
68     struct xorg_list pending_streams;
69 
70     Bool have_egl_damage;
71 
72     GLint blit_prog;
73     GLuint blit_vao;
74     GLuint blit_vbo;
75     GLuint blit_is_rgba_pos;
76 };
77 
78 struct xwl_pixmap {
79     struct wl_buffer *buffer;
80     struct xwl_screen *xwl_screen;
81 
82     /* The stream and associated resources have their own lifetime seperate
83      * from the pixmap's */
84     int refcount;
85 
86     EGLStreamKHR stream;
87     EGLSurface surface;
88 };
89 
90 static DevPrivateKeyRec xwl_eglstream_private_key;
91 static DevPrivateKeyRec xwl_eglstream_window_private_key;
92 
93 static inline struct xwl_eglstream_private *
xwl_eglstream_get(struct xwl_screen * xwl_screen)94 xwl_eglstream_get(struct xwl_screen *xwl_screen)
95 {
96     return dixLookupPrivate(&xwl_screen->screen->devPrivates,
97                             &xwl_eglstream_private_key);
98 }
99 
100 static inline struct xwl_eglstream_pending_stream *
xwl_eglstream_window_get_pending(WindowPtr window)101 xwl_eglstream_window_get_pending(WindowPtr window)
102 {
103     return dixLookupPrivate(&window->devPrivates,
104                             &xwl_eglstream_window_private_key);
105 }
106 
107 static inline void
xwl_eglstream_window_set_pending(WindowPtr window,struct xwl_eglstream_pending_stream * stream)108 xwl_eglstream_window_set_pending(WindowPtr window,
109                                  struct xwl_eglstream_pending_stream *stream)
110 {
111     dixSetPrivate(&window->devPrivates,
112                   &xwl_eglstream_window_private_key, stream);
113 }
114 
115 static GLint
xwl_eglstream_compile_glsl_prog(GLenum type,const char * source)116 xwl_eglstream_compile_glsl_prog(GLenum type, const char *source)
117 {
118     GLint ok;
119     GLint prog;
120 
121     prog = glCreateShader(type);
122     glShaderSource(prog, 1, (const GLchar **) &source, NULL);
123     glCompileShader(prog);
124     glGetShaderiv(prog, GL_COMPILE_STATUS, &ok);
125     if (!ok) {
126         GLchar *info;
127         GLint size;
128 
129         glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size);
130         info = malloc(size);
131         if (info) {
132             glGetShaderInfoLog(prog, size, NULL, info);
133             ErrorF("Failed to compile %s: %s\n",
134                    type == GL_FRAGMENT_SHADER ? "FS" : "VS", info);
135             ErrorF("Program source:\n%s", source);
136             free(info);
137         }
138         else
139             ErrorF("Failed to get shader compilation info.\n");
140         FatalError("GLSL compile failure\n");
141     }
142 
143     return prog;
144 }
145 
146 static GLuint
xwl_eglstream_build_glsl_prog(GLuint vs,GLuint fs)147 xwl_eglstream_build_glsl_prog(GLuint vs, GLuint fs)
148 {
149     GLint ok;
150     GLuint prog;
151 
152     prog = glCreateProgram();
153     glAttachShader(prog, vs);
154     glAttachShader(prog, fs);
155 
156     glLinkProgram(prog);
157     glGetProgramiv(prog, GL_LINK_STATUS, &ok);
158     if (!ok) {
159         GLchar *info;
160         GLint size;
161 
162         glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
163         info = malloc(size);
164 
165         glGetProgramInfoLog(prog, size, NULL, info);
166         ErrorF("Failed to link: %s\n", info);
167         FatalError("GLSL link failure\n");
168     }
169 
170     return prog;
171 }
172 
173 static void
xwl_eglstream_cleanup(struct xwl_screen * xwl_screen)174 xwl_eglstream_cleanup(struct xwl_screen *xwl_screen)
175 {
176     struct xwl_eglstream_private *xwl_eglstream =
177         xwl_eglstream_get(xwl_screen);
178 
179     if (xwl_eglstream->display)
180         wl_eglstream_display_destroy(xwl_eglstream->display);
181     if (xwl_eglstream->controller)
182         wl_eglstream_controller_destroy(xwl_eglstream->controller);
183     if (xwl_eglstream->blit_prog) {
184         glDeleteProgram(xwl_eglstream->blit_prog);
185         glDeleteBuffers(1, &xwl_eglstream->blit_vbo);
186     }
187 
188     free(xwl_eglstream);
189 }
190 
191 static Bool
xwl_glamor_egl_supports_device_probing(void)192 xwl_glamor_egl_supports_device_probing(void)
193 {
194     return epoxy_has_egl_extension(NULL, "EGL_EXT_device_base");
195 }
196 
197 static void **
xwl_glamor_egl_get_devices(int * num_devices)198 xwl_glamor_egl_get_devices(int *num_devices)
199 {
200     EGLDeviceEXT *devices, *tmp;
201     Bool ret;
202     int drm_dev_count = 0;
203     int i;
204 
205     if (!xwl_glamor_egl_supports_device_probing())
206         return NULL;
207 
208     /* Get the number of devices */
209     ret = eglQueryDevicesEXT(0, NULL, num_devices);
210     if (!ret || *num_devices < 1)
211         return NULL;
212 
213     devices = calloc(*num_devices, sizeof(EGLDeviceEXT));
214     if (!devices)
215         return NULL;
216 
217     ret = eglQueryDevicesEXT(*num_devices, devices, num_devices);
218     if (!ret)
219         goto error;
220 
221     /* We're only ever going to care about devices that support
222      * EGL_EXT_device_drm, so filter out the ones that don't
223      */
224     for (i = 0; i < *num_devices; i++) {
225         const char *extension_str =
226             eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS);
227 
228         if (!epoxy_extension_in_string(extension_str, "EGL_EXT_device_drm"))
229             continue;
230 
231         devices[drm_dev_count++] = devices[i];
232     }
233     if (!drm_dev_count)
234         goto error;
235 
236     *num_devices = drm_dev_count;
237     tmp = realloc(devices, sizeof(EGLDeviceEXT) * drm_dev_count);
238     if (!tmp)
239         goto error;
240 
241     devices = tmp;
242 
243     return devices;
244 
245 error:
246     free(devices);
247 
248     return NULL;
249 }
250 
251 static Bool
xwl_glamor_egl_device_has_egl_extensions(void * device,const char ** ext_list,size_t size)252 xwl_glamor_egl_device_has_egl_extensions(void *device,
253                                          const char **ext_list, size_t size)
254 {
255     EGLDisplay egl_display;
256     int i;
257     Bool has_exts = TRUE;
258 
259     egl_display = glamor_egl_get_display(EGL_PLATFORM_DEVICE_EXT, device);
260     if (!egl_display || !eglInitialize(egl_display, NULL, NULL))
261         return FALSE;
262 
263     for (i = 0; i < size; i++) {
264         if (!epoxy_has_egl_extension(egl_display, ext_list[i])) {
265             has_exts = FALSE;
266             break;
267         }
268     }
269 
270     eglTerminate(egl_display);
271     return has_exts;
272 }
273 
274 static void
xwl_eglstream_unref_pixmap_stream(struct xwl_pixmap * xwl_pixmap)275 xwl_eglstream_unref_pixmap_stream(struct xwl_pixmap *xwl_pixmap)
276 {
277     struct xwl_screen *xwl_screen = xwl_pixmap->xwl_screen;
278 
279     if (--xwl_pixmap->refcount >= 1)
280         return;
281 
282     /* If we're using this stream in the current egl context, unbind it so the
283      * driver doesn't keep it around until the next eglMakeCurrent()
284      * don't have to keep it around until something else changes the surface
285      */
286     xwl_glamor_egl_make_current(xwl_screen);
287     if (eglGetCurrentSurface(EGL_READ) == xwl_pixmap->surface ||
288         eglGetCurrentSurface(EGL_DRAW) == xwl_pixmap->surface) {
289         eglMakeCurrent(xwl_screen->egl_display,
290                        EGL_NO_SURFACE, EGL_NO_SURFACE,
291                        xwl_screen->egl_context);
292     }
293 
294     if (xwl_pixmap->surface)
295         eglDestroySurface(xwl_screen->egl_display, xwl_pixmap->surface);
296 
297     eglDestroyStreamKHR(xwl_screen->egl_display, xwl_pixmap->stream);
298 
299     wl_buffer_destroy(xwl_pixmap->buffer);
300     free(xwl_pixmap);
301 }
302 
303 static Bool
xwl_glamor_eglstream_destroy_pixmap(PixmapPtr pixmap)304 xwl_glamor_eglstream_destroy_pixmap(PixmapPtr pixmap)
305 {
306     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
307 
308     if (xwl_pixmap && pixmap->refcnt == 1)
309         xwl_eglstream_unref_pixmap_stream(xwl_pixmap);
310 
311     return glamor_destroy_pixmap(pixmap);
312 }
313 
314 static struct wl_buffer *
xwl_glamor_eglstream_get_wl_buffer_for_pixmap(PixmapPtr pixmap,Bool * created)315 xwl_glamor_eglstream_get_wl_buffer_for_pixmap(PixmapPtr pixmap,
316                                               Bool *created)
317 {
318     /* XXX created? */
319     return xwl_pixmap_get(pixmap)->buffer;
320 }
321 
322 static void
xwl_eglstream_set_window_pixmap(WindowPtr window,PixmapPtr pixmap)323 xwl_eglstream_set_window_pixmap(WindowPtr window, PixmapPtr pixmap)
324 {
325     struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
326     struct xwl_eglstream_private *xwl_eglstream =
327         xwl_eglstream_get(xwl_screen);
328     struct xwl_eglstream_pending_stream *pending;
329 
330     pending = xwl_eglstream_window_get_pending(window);
331     if (pending) {
332         /* The pixmap for this window has changed before the compositor
333          * finished attaching the consumer for the window's pixmap's original
334          * eglstream. A producer can no longer be attached, so the stream's
335          * useless
336          */
337         pending->is_valid = FALSE;
338 
339         /* The compositor may still be using the stream, so we can't destroy
340          * it yet. We'll only have a guarantee that the stream is safe to
341          * destroy once we receive the pending wl_display_sync() for this
342          * stream
343          */
344         pending->xwl_pixmap->refcount++;
345     }
346 
347     xwl_screen->screen->SetWindowPixmap = xwl_eglstream->SetWindowPixmap;
348     (*xwl_screen->screen->SetWindowPixmap)(window, pixmap);
349     xwl_eglstream->SetWindowPixmap = xwl_screen->screen->SetWindowPixmap;
350     xwl_screen->screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;
351 }
352 
353 /* Because we run asynchronously with our wayland compositor, it's possible
354  * that an X client event could cause us to begin creating a stream for a
355  * pixmap/window combo before the stream for the pixmap this window
356  * previously used has been fully initialized. An example:
357  *
358  * - Start processing X client events.
359  * - X window receives resize event, causing us to create a new pixmap and
360  *   begin creating the corresponding eglstream. This pixmap is known as
361  *   pixmap A.
362  * - X window receives another resize event, and again changes it's current
363  *   pixmap causing us to create another corresponding eglstream for the same
364  *   window. This pixmap is known as pixmap B.
365  * - Start handling events from the wayland compositor.
366  *
367  * Since both pixmap A and B will have scheduled wl_display_sync events to
368  * indicate when their respective streams are connected, we will receive each
369  * callback in the original order the pixmaps were created. This means the
370  * following would happen:
371  *
372  * - Receive pixmap A's stream callback, attach it's stream to the surface of
373  *   the window that just orphaned it.
374  * - Receive pixmap B's stream callback, fall over and fail because the
375  *   window's surface now incorrectly has pixmap A's stream attached to it.
376  *
377  * We work around this problem by keeping a queue of pending streams, and
378  * only allowing one queue entry to exist for each window. In the scenario
379  * listed above, this should happen:
380  *
381  * - Begin processing X events...
382  * - A window is resized, causing us to add an eglstream (known as eglstream
383  *   A) waiting for it's consumer to finish attachment to be added to the
384  *   queue.
385  * - Resize on same window happens. We invalidate the previously pending
386  *   stream and add another one to the pending queue (known as eglstream B).
387  * - Begin processing Wayland events...
388  * - Receive invalidated callback from compositor for eglstream A, destroy
389  *   stream.
390  * - Receive callback from compositor for eglstream B, create producer.
391  * - Success!
392  */
393 static void
xwl_eglstream_consumer_ready_callback(void * data,struct wl_callback * callback,uint32_t time)394 xwl_eglstream_consumer_ready_callback(void *data,
395                                       struct wl_callback *callback,
396                                       uint32_t time)
397 {
398     struct xwl_screen *xwl_screen = data;
399     struct xwl_eglstream_private *xwl_eglstream =
400         xwl_eglstream_get(xwl_screen);
401     struct xwl_pixmap *xwl_pixmap;
402     struct xwl_eglstream_pending_stream *pending;
403     Bool found = FALSE;
404 
405     wl_callback_destroy(callback);
406 
407     xorg_list_for_each_entry(pending, &xwl_eglstream->pending_streams, link) {
408         if (pending->cb == callback) {
409             found = TRUE;
410             break;
411         }
412     }
413     assert(found);
414 
415     if (!pending->is_valid) {
416         xwl_eglstream_unref_pixmap_stream(pending->xwl_pixmap);
417         goto out;
418     }
419 
420     xwl_glamor_egl_make_current(xwl_screen);
421 
422     xwl_pixmap = pending->xwl_pixmap;
423     xwl_pixmap->surface = eglCreateStreamProducerSurfaceKHR(
424         xwl_screen->egl_display, xwl_eglstream->config,
425         xwl_pixmap->stream, (int[]) {
426             EGL_WIDTH,  pending->pixmap->drawable.width,
427             EGL_HEIGHT, pending->pixmap->drawable.height,
428             EGL_NONE
429         });
430 
431     DebugF("eglstream: win %d completes eglstream for pixmap %p, congrats!\n",
432            pending->window->drawable.id, pending->pixmap);
433 
434 out:
435     xwl_eglstream_window_set_pending(pending->window, NULL);
436     xorg_list_del(&pending->link);
437     free(pending);
438 }
439 
440 static const struct wl_callback_listener consumer_ready_listener = {
441     xwl_eglstream_consumer_ready_callback
442 };
443 
444 static void
xwl_eglstream_queue_pending_stream(struct xwl_screen * xwl_screen,WindowPtr window,PixmapPtr pixmap)445 xwl_eglstream_queue_pending_stream(struct xwl_screen *xwl_screen,
446                                    WindowPtr window, PixmapPtr pixmap)
447 {
448     struct xwl_eglstream_private *xwl_eglstream =
449         xwl_eglstream_get(xwl_screen);
450     struct xwl_eglstream_pending_stream *pending_stream;
451 
452 #ifdef DEBUG
453     if (!xwl_eglstream_window_get_pending(window))
454         DebugF("eglstream: win %d begins new eglstream for pixmap %p\n",
455                window->drawable.id, pixmap);
456     else
457         DebugF("eglstream: win %d interrupts and replaces pending eglstream for pixmap %p\n",
458                window->drawable.id, pixmap);
459 #endif
460 
461     pending_stream = malloc(sizeof(*pending_stream));
462     pending_stream->window = window;
463     pending_stream->pixmap = pixmap;
464     pending_stream->xwl_pixmap = xwl_pixmap_get(pixmap);
465     pending_stream->is_valid = TRUE;
466     xorg_list_init(&pending_stream->link);
467     xorg_list_add(&pending_stream->link, &xwl_eglstream->pending_streams);
468     xwl_eglstream_window_set_pending(window, pending_stream);
469 
470     pending_stream->cb = wl_display_sync(xwl_screen->display);
471     wl_callback_add_listener(pending_stream->cb, &consumer_ready_listener,
472                              xwl_screen);
473 }
474 
475 static void
xwl_eglstream_buffer_release_callback(void * data,struct wl_buffer * wl_buffer)476 xwl_eglstream_buffer_release_callback(void *data, struct wl_buffer *wl_buffer)
477 {
478     xwl_eglstream_unref_pixmap_stream(data);
479 }
480 
481 static const struct wl_buffer_listener xwl_eglstream_buffer_release_listener = {
482     xwl_eglstream_buffer_release_callback
483 };
484 
485 static void
xwl_eglstream_create_pending_stream(struct xwl_screen * xwl_screen,WindowPtr window,PixmapPtr pixmap)486 xwl_eglstream_create_pending_stream(struct xwl_screen *xwl_screen,
487                                     WindowPtr window, PixmapPtr pixmap)
488 {
489     struct xwl_eglstream_private *xwl_eglstream =
490         xwl_eglstream_get(xwl_screen);
491     struct xwl_pixmap *xwl_pixmap;
492     struct xwl_window *xwl_window = xwl_window_from_window(window);
493     struct wl_array stream_attribs;
494     int stream_fd = -1;
495 
496     xwl_pixmap = calloc(sizeof(*xwl_pixmap), 1);
497     if (!xwl_pixmap)
498         FatalError("Not enough memory to create pixmap\n");
499     xwl_pixmap_set_private(pixmap, xwl_pixmap);
500 
501     xwl_glamor_egl_make_current(xwl_screen);
502 
503     xwl_pixmap->xwl_screen = xwl_screen;
504     xwl_pixmap->refcount = 1;
505     xwl_pixmap->stream = eglCreateStreamKHR(xwl_screen->egl_display, NULL);
506     stream_fd = eglGetStreamFileDescriptorKHR(xwl_screen->egl_display,
507                                               xwl_pixmap->stream);
508 
509     wl_array_init(&stream_attribs);
510     xwl_pixmap->buffer =
511         wl_eglstream_display_create_stream(xwl_eglstream->display,
512                                            pixmap->drawable.width,
513                                            pixmap->drawable.height,
514                                            stream_fd,
515                                            WL_EGLSTREAM_HANDLE_TYPE_FD,
516                                            &stream_attribs);
517 
518     wl_buffer_add_listener(xwl_pixmap->buffer,
519                            &xwl_eglstream_buffer_release_listener,
520                            xwl_pixmap);
521 
522     wl_eglstream_controller_attach_eglstream_consumer(
523         xwl_eglstream->controller, xwl_window->surface, xwl_pixmap->buffer);
524 
525     xwl_eglstream_queue_pending_stream(xwl_screen, window, pixmap);
526 
527     close(stream_fd);
528 }
529 
530 static Bool
xwl_glamor_eglstream_allow_commits(struct xwl_window * xwl_window)531 xwl_glamor_eglstream_allow_commits(struct xwl_window *xwl_window)
532 {
533     struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
534     struct xwl_eglstream_pending_stream *pending =
535         xwl_eglstream_window_get_pending(xwl_window->window);
536     PixmapPtr pixmap =
537         (*xwl_screen->screen->GetWindowPixmap)(xwl_window->window);
538     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
539 
540     if (xwl_pixmap) {
541         if (pending) {
542             /* Wait for the compositor to finish connecting the consumer for
543              * this eglstream */
544             if (pending->is_valid)
545                 return FALSE;
546 
547             /* The pixmap for this window was changed before the compositor
548              * finished connecting the eglstream for the window's previous
549              * pixmap. Begin creating a new eglstream. */
550         } else {
551             return TRUE;
552         }
553     }
554 
555     /* Glamor pixmap has no backing stream yet; begin making one and disallow
556      * commits until then
557      */
558     xwl_eglstream_create_pending_stream(xwl_screen, xwl_window->window,
559                                         pixmap);
560 
561     return FALSE;
562 }
563 
564 static void
xwl_glamor_eglstream_post_damage(struct xwl_window * xwl_window,PixmapPtr pixmap,RegionPtr region)565 xwl_glamor_eglstream_post_damage(struct xwl_window *xwl_window,
566                                  PixmapPtr pixmap, RegionPtr region)
567 {
568     struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
569     struct xwl_eglstream_private *xwl_eglstream =
570         xwl_eglstream_get(xwl_screen);
571     struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
572     BoxPtr box = RegionExtents(region);
573     EGLint egl_damage[] = {
574         box->x1,           box->y1,
575         box->x2 - box->x1, box->y2 - box->y1
576     };
577     GLint saved_vao;
578 
579     /* Unbind the framebuffer BEFORE binding the EGLSurface, otherwise we
580      * won't actually draw to it
581      */
582     xwl_glamor_egl_make_current(xwl_screen);
583     glBindFramebuffer(GL_FRAMEBUFFER, 0);
584 
585     if (eglGetCurrentSurface(EGL_READ) != xwl_pixmap->surface ||
586         eglGetCurrentSurface(EGL_DRAW) != xwl_pixmap->surface)
587         eglMakeCurrent(xwl_screen->egl_display,
588                        xwl_pixmap->surface, xwl_pixmap->surface,
589                        xwl_screen->egl_context);
590 
591     /* Save current GL state */
592     glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao);
593 
594     /* Setup our GL state */
595     glUseProgram(xwl_eglstream->blit_prog);
596     glViewport(0, 0, pixmap->drawable.width, pixmap->drawable.height);
597     glActiveTexture(GL_TEXTURE0);
598     glBindVertexArray(xwl_eglstream->blit_vao);
599     glBindTexture(GL_TEXTURE_2D, glamor_get_pixmap_texture(pixmap));
600 
601     glUniform1i(xwl_eglstream->blit_is_rgba_pos,
602                 pixmap->drawable.depth >= 32);
603 
604     /* Blit rendered image into EGLStream surface */
605     glDrawBuffer(GL_BACK);
606     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
607 
608     if (xwl_eglstream->have_egl_damage)
609         eglSwapBuffersWithDamageKHR(xwl_screen->egl_display,
610                                     xwl_pixmap->surface, egl_damage, 1);
611     else
612         eglSwapBuffers(xwl_screen->egl_display, xwl_pixmap->surface);
613 
614     /* Restore previous state */
615     glBindVertexArray(saved_vao);
616     glBindTexture(GL_TEXTURE_2D, 0);
617 
618     /* After this we will hand off the eglstream's wl_buffer to the
619      * compositor, which will own it until it sends a release() event. */
620     xwl_pixmap->refcount++;
621 }
622 
623 static void
xwl_eglstream_display_handle_caps(void * data,struct wl_eglstream_display * disp,int32_t caps)624 xwl_eglstream_display_handle_caps(void *data,
625                                   struct wl_eglstream_display *disp,
626                                   int32_t caps)
627 {
628     xwl_eglstream_get(data)->display_caps = caps;
629 }
630 
631 static void
xwl_eglstream_display_handle_swapinterval_override(void * data,struct wl_eglstream_display * disp,int32_t swapinterval,struct wl_buffer * stream)632 xwl_eglstream_display_handle_swapinterval_override(void *data,
633                                                    struct wl_eglstream_display *disp,
634                                                    int32_t swapinterval,
635                                                    struct wl_buffer *stream)
636 {
637 }
638 
639 const struct wl_eglstream_display_listener eglstream_display_listener = {
640     .caps = xwl_eglstream_display_handle_caps,
641     .swapinterval_override = xwl_eglstream_display_handle_swapinterval_override,
642 };
643 
644 static Bool
xwl_glamor_eglstream_init_wl_registry(struct xwl_screen * xwl_screen,struct wl_registry * wl_registry,uint32_t id,const char * name,uint32_t version)645 xwl_glamor_eglstream_init_wl_registry(struct xwl_screen *xwl_screen,
646                                       struct wl_registry *wl_registry,
647                                       uint32_t id, const char *name,
648                                       uint32_t version)
649 {
650     struct xwl_eglstream_private *xwl_eglstream =
651         xwl_eglstream_get(xwl_screen);
652 
653     if (strcmp(name, "wl_eglstream_display") == 0) {
654         xwl_eglstream->display = wl_registry_bind(
655             wl_registry, id, &wl_eglstream_display_interface, version);
656 
657         wl_eglstream_display_add_listener(xwl_eglstream->display,
658                                           &eglstream_display_listener,
659                                           xwl_screen);
660         return TRUE;
661     } else if (strcmp(name, "wl_eglstream_controller") == 0) {
662         xwl_eglstream->controller = wl_registry_bind(
663             wl_registry, id, &wl_eglstream_controller_interface, version);
664         return TRUE;
665     }
666 
667     /* no match */
668     return FALSE;
669 }
670 
671 static Bool
xwl_glamor_eglstream_has_wl_interfaces(struct xwl_screen * xwl_screen)672 xwl_glamor_eglstream_has_wl_interfaces(struct xwl_screen *xwl_screen)
673 {
674     struct xwl_eglstream_private *xwl_eglstream =
675         xwl_eglstream_get(xwl_screen);
676 
677     if (xwl_eglstream->display == NULL) {
678         ErrorF("glamor: 'wl_eglstream_display' not supported\n");
679         return FALSE;
680     }
681 
682     if (xwl_eglstream->controller == NULL) {
683         ErrorF("glamor: 'wl_eglstream_controller' not supported\n");
684         return FALSE;
685     }
686 
687     return TRUE;
688 }
689 
690 static inline void
xwl_eglstream_init_shaders(struct xwl_screen * xwl_screen)691 xwl_eglstream_init_shaders(struct xwl_screen *xwl_screen)
692 {
693     struct xwl_eglstream_private *xwl_eglstream =
694         xwl_eglstream_get(xwl_screen);
695     GLint fs, vs, attrib;
696     GLuint vbo;
697 
698     const char *blit_vs_src =
699         "attribute vec2 texcoord;\n"
700         "attribute vec2 position;\n"
701         "varying vec2 t;\n"
702         "void main() {\n"
703         "    t = texcoord;\n"
704         "    gl_Position = vec4(position, 0, 1);\n"
705         "}";
706 
707     const char *blit_fs_src =
708         "varying vec2 t;\n"
709         "uniform sampler2D s;\n"
710         "uniform bool is_rgba;\n"
711         "void main() {\n"
712         "    if (is_rgba)\n"
713         "        gl_FragColor = texture2D(s, t);\n"
714         "    else\n"
715         "        gl_FragColor = vec4(texture2D(s, t).rgb, 1.0);\n"
716         "}";
717 
718     static const float position[] = {
719         /* position */
720         -1, -1,
721          1, -1,
722          1,  1,
723         -1,  1,
724         /* texcoord */
725          0,  1,
726          1,  1,
727          1,  0,
728          0,  0,
729     };
730 
731     vs = xwl_eglstream_compile_glsl_prog(GL_VERTEX_SHADER, blit_vs_src);
732     fs = xwl_eglstream_compile_glsl_prog(GL_FRAGMENT_SHADER, blit_fs_src);
733 
734     xwl_eglstream->blit_prog = xwl_eglstream_build_glsl_prog(vs, fs);
735     glDeleteShader(vs);
736     glDeleteShader(fs);
737 
738     /* Create the blitter's vao */
739     glGenVertexArrays(1, &xwl_eglstream->blit_vao);
740     glBindVertexArray(xwl_eglstream->blit_vao);
741 
742     /* Set the data for both position and texcoord in the vbo */
743     glGenBuffers(1, &vbo);
744     glBindBuffer(GL_ARRAY_BUFFER, vbo);
745     glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
746     xwl_eglstream->blit_vbo = vbo;
747 
748     /* Define each shader attribute's data location in our vbo */
749     attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "position");
750     glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0, NULL);
751     glEnableVertexAttribArray(attrib);
752 
753     attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "texcoord");
754     glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0,
755                           (void*)(sizeof(float) * 8));
756     glEnableVertexAttribArray(attrib);
757 
758     /* Save the location of uniforms we'll set later */
759     xwl_eglstream->blit_is_rgba_pos =
760         glGetUniformLocation(xwl_eglstream->blit_prog, "is_rgba");
761 }
762 
763 static Bool
xwl_glamor_eglstream_init_egl(struct xwl_screen * xwl_screen)764 xwl_glamor_eglstream_init_egl(struct xwl_screen *xwl_screen)
765 {
766     struct xwl_eglstream_private *xwl_eglstream =
767         xwl_eglstream_get(xwl_screen);
768     EGLConfig config;
769     const EGLint attrib_list[] = {
770         EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
771         EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
772         EGL_CONTEXT_MAJOR_VERSION_KHR,
773         GLAMOR_GL_CORE_VER_MAJOR,
774         EGL_CONTEXT_MINOR_VERSION_KHR,
775         GLAMOR_GL_CORE_VER_MINOR,
776         EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
777         EGL_NONE
778     };
779     const EGLint config_attribs[] = {
780         EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR,
781         EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
782         EGL_RED_SIZE, 8,
783         EGL_GREEN_SIZE, 8,
784         EGL_BLUE_SIZE, 8,
785         EGL_ALPHA_SIZE, 8,
786         EGL_NONE,
787     };
788     int n;
789 
790     xwl_screen->egl_display = glamor_egl_get_display(
791         EGL_PLATFORM_DEVICE_EXT, xwl_eglstream->egl_device);
792     if (!xwl_screen->egl_display)
793         goto error;
794 
795     if (!eglInitialize(xwl_screen->egl_display, NULL, NULL)) {
796         xwl_screen->egl_display = NULL;
797         goto error;
798     }
799 
800     if (!epoxy_has_egl_extension(xwl_screen->egl_display,
801                                  "EGL_IMG_context_priority")) {
802         ErrorF("EGL_IMG_context_priority not available\n");
803         goto error;
804     }
805 
806     eglChooseConfig(xwl_screen->egl_display, config_attribs, &config, 1, &n);
807     if (!n) {
808         ErrorF("No acceptable EGL configs found\n");
809         goto error;
810     }
811 
812     xwl_eglstream->config = config;
813 #if 0
814     xwl_screen->formats =
815         XWL_FORMAT_RGB565 | XWL_FORMAT_XRGB8888 | XWL_FORMAT_ARGB8888;
816 #endif
817 
818     eglBindAPI(EGL_OPENGL_API);
819     xwl_screen->egl_context = eglCreateContext(
820         xwl_screen->egl_display, config, EGL_NO_CONTEXT, attrib_list);
821     if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
822         ErrorF("Failed to create main EGL context: 0x%x\n", eglGetError());
823         goto error;
824     }
825 
826     if (!eglMakeCurrent(xwl_screen->egl_display,
827                         EGL_NO_SURFACE, EGL_NO_SURFACE,
828                         xwl_screen->egl_context)) {
829         ErrorF("Failed to make EGL context current\n");
830         goto error;
831     }
832 
833     xwl_eglstream->have_egl_damage =
834         epoxy_has_egl_extension(xwl_screen->egl_display,
835                                 "EGL_KHR_swap_buffers_with_damage");
836     if (!xwl_eglstream->have_egl_damage)
837         ErrorF("Driver lacks EGL_KHR_swap_buffers_with_damage, performance "
838                "will be affected\n");
839 
840     xwl_eglstream_init_shaders(xwl_screen);
841 
842     return TRUE;
843 error:
844     xwl_eglstream_cleanup(xwl_screen);
845     return FALSE;
846 }
847 
848 static Bool
xwl_glamor_eglstream_init_screen(struct xwl_screen * xwl_screen)849 xwl_glamor_eglstream_init_screen(struct xwl_screen *xwl_screen)
850 {
851     struct xwl_eglstream_private *xwl_eglstream =
852         xwl_eglstream_get(xwl_screen);
853     ScreenPtr screen = xwl_screen->screen;
854 
855     /* We can just let glamor handle CreatePixmap */
856     screen->DestroyPixmap = xwl_glamor_eglstream_destroy_pixmap;
857 
858     xwl_eglstream->SetWindowPixmap = screen->SetWindowPixmap;
859     screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;
860 
861     if (!dixRegisterPrivateKey(&xwl_eglstream_window_private_key,
862                                PRIVATE_WINDOW, 0))
863         return FALSE;
864 
865     return TRUE;
866 }
867 
868 static EGLDeviceEXT
xwl_eglstream_get_device(struct xwl_screen * xwl_screen)869 xwl_eglstream_get_device(struct xwl_screen *xwl_screen)
870 {
871     void **devices = NULL;
872     const char *exts[] = {
873         "EGL_KHR_stream",
874         "EGL_KHR_stream_producer_eglsurface",
875     };
876     int num_devices, i;
877     EGLDeviceEXT device = EGL_NO_DEVICE_EXT;
878 
879     /* No device specified by the user, so find one ourselves */
880     devices = xwl_glamor_egl_get_devices(&num_devices);
881     if (!devices)
882         goto out;
883 
884     for (i = 0; i < num_devices; i++) {
885         if (xwl_glamor_egl_device_has_egl_extensions(devices[i], exts,
886                                                      ARRAY_SIZE(exts))) {
887             device = devices[i];
888             break;
889         }
890     }
891 
892     free(devices);
893 out:
894     if (!device)
895         ErrorF("glamor: No eglstream capable devices found\n");
896     return device;
897 }
898 
899 void
xwl_glamor_init_eglstream(struct xwl_screen * xwl_screen)900 xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
901 {
902     struct xwl_eglstream_private *xwl_eglstream;
903     EGLDeviceEXT egl_device;
904 
905     xwl_screen->eglstream_backend.is_available = FALSE;
906     egl_device = xwl_eglstream_get_device(xwl_screen);
907     if (egl_device == EGL_NO_DEVICE_EXT)
908         return;
909 
910     if (!dixRegisterPrivateKey(&xwl_eglstream_private_key, PRIVATE_SCREEN, 0))
911         return;
912 
913     xwl_eglstream = calloc(sizeof(*xwl_eglstream), 1);
914     if (!xwl_eglstream) {
915         ErrorF("Failed to allocate memory required to init EGLStream support\n");
916         return;
917     }
918 
919     dixSetPrivate(&xwl_screen->screen->devPrivates,
920                   &xwl_eglstream_private_key, xwl_eglstream);
921 
922     xwl_eglstream->egl_device = egl_device;
923     xorg_list_init(&xwl_eglstream->pending_streams);
924 
925     xwl_screen->eglstream_backend.init_egl = xwl_glamor_eglstream_init_egl;
926     xwl_screen->eglstream_backend.init_wl_registry = xwl_glamor_eglstream_init_wl_registry;
927     xwl_screen->eglstream_backend.has_wl_interfaces = xwl_glamor_eglstream_has_wl_interfaces;
928     xwl_screen->eglstream_backend.init_screen = xwl_glamor_eglstream_init_screen;
929     xwl_screen->eglstream_backend.get_wl_buffer_for_pixmap = xwl_glamor_eglstream_get_wl_buffer_for_pixmap;
930     xwl_screen->eglstream_backend.post_damage = xwl_glamor_eglstream_post_damage;
931     xwl_screen->eglstream_backend.allow_commits = xwl_glamor_eglstream_allow_commits;
932     xwl_screen->eglstream_backend.is_available = TRUE;
933 }
934