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