xwayland-glamor-eglstream.c 29.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * Copyright © 2017 Red Hat Inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including
 * the next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Lyude Paul <lyude@redhat.com>
 *
 */

30
#include <xwayland-config.h>
31 32

#define MESA_EGL_NO_X11_HEADERS
33
#define EGL_NO_X11
34 35 36 37 38 39 40 41 42
#include <glamor_egl.h>
#include <glamor.h>
#include <glamor_transform.h>
#include <glamor_transfer.h>

#include <xf86drm.h>

#include <epoxy/egl.h>

43
#include "xwayland-glamor.h"
44
#include "xwayland-pixmap.h"
45
#include "xwayland-screen.h"
46
#include "xwayland-window.h"
47 48 49 50

#include "wayland-eglstream-client-protocol.h"
#include "wayland-eglstream-controller-client-protocol.h"

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
struct xwl_eglstream_pending_stream {
    PixmapPtr pixmap;
    WindowPtr window;

    struct xwl_pixmap *xwl_pixmap;
    struct wl_callback *cb;

    Bool is_valid;

    struct xorg_list link;
};

struct xwl_eglstream_private {
    EGLDeviceEXT egl_device;
    struct wl_eglstream_display *display;
    struct wl_eglstream_controller *controller;
    uint32_t display_caps;

    EGLConfig config;

    SetWindowPixmapProcPtr SetWindowPixmap;

    struct xorg_list pending_streams;

    Bool have_egl_damage;

    GLint blit_prog;
    GLuint blit_vao;
    GLuint blit_vbo;
    GLuint blit_is_rgba_pos;
};

struct xwl_pixmap {
    struct wl_buffer *buffer;
    struct xwl_screen *xwl_screen;

Alan Coopersmith's avatar
Alan Coopersmith committed
87
    /* The stream and associated resources have their own lifetime separate
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
     * from the pixmap's */
    int refcount;

    EGLStreamKHR stream;
    EGLSurface surface;
};

static DevPrivateKeyRec xwl_eglstream_private_key;
static DevPrivateKeyRec xwl_eglstream_window_private_key;

static inline struct xwl_eglstream_private *
xwl_eglstream_get(struct xwl_screen *xwl_screen)
{
    return dixLookupPrivate(&xwl_screen->screen->devPrivates,
                            &xwl_eglstream_private_key);
}

static inline struct xwl_eglstream_pending_stream *
xwl_eglstream_window_get_pending(WindowPtr window)
{
    return dixLookupPrivate(&window->devPrivates,
                            &xwl_eglstream_window_private_key);
}

static inline void
xwl_eglstream_window_set_pending(WindowPtr window,
                                 struct xwl_eglstream_pending_stream *stream)
{
    dixSetPrivate(&window->devPrivates,
                  &xwl_eglstream_window_private_key, stream);
}

static GLint
xwl_eglstream_compile_glsl_prog(GLenum type, const char *source)
{
    GLint ok;
    GLint prog;

    prog = glCreateShader(type);
    glShaderSource(prog, 1, (const GLchar **) &source, NULL);
    glCompileShader(prog);
    glGetShaderiv(prog, GL_COMPILE_STATUS, &ok);
    if (!ok) {
        GLchar *info;
        GLint size;

        glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size);
        info = malloc(size);
        if (info) {
            glGetShaderInfoLog(prog, size, NULL, info);
            ErrorF("Failed to compile %s: %s\n",
                   type == GL_FRAGMENT_SHADER ? "FS" : "VS", info);
            ErrorF("Program source:\n%s", source);
            free(info);
        }
        else
            ErrorF("Failed to get shader compilation info.\n");
        FatalError("GLSL compile failure\n");
    }

    return prog;
}

static GLuint
xwl_eglstream_build_glsl_prog(GLuint vs, GLuint fs)
{
    GLint ok;
    GLuint prog;

    prog = glCreateProgram();
    glAttachShader(prog, vs);
    glAttachShader(prog, fs);

    glLinkProgram(prog);
    glGetProgramiv(prog, GL_LINK_STATUS, &ok);
    if (!ok) {
        GLchar *info;
        GLint size;

        glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
        info = malloc(size);

        glGetProgramInfoLog(prog, size, NULL, info);
        ErrorF("Failed to link: %s\n", info);
        FatalError("GLSL link failure\n");
    }

    return prog;
}

static void
xwl_eglstream_cleanup(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);

    if (xwl_eglstream->display)
        wl_eglstream_display_destroy(xwl_eglstream->display);
    if (xwl_eglstream->controller)
        wl_eglstream_controller_destroy(xwl_eglstream->controller);
    if (xwl_eglstream->blit_prog) {
        glDeleteProgram(xwl_eglstream->blit_prog);
        glDeleteBuffers(1, &xwl_eglstream->blit_vbo);
    }

    free(xwl_eglstream);
}

196 197 198 199 200 201 202 203 204
static Bool
xwl_glamor_egl_supports_device_probing(void)
{
    return epoxy_has_egl_extension(NULL, "EGL_EXT_device_base");
}

static void **
xwl_glamor_egl_get_devices(int *num_devices)
{
205
    EGLDeviceEXT *devices, *tmp;
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    Bool ret;
    int drm_dev_count = 0;
    int i;

    if (!xwl_glamor_egl_supports_device_probing())
        return NULL;

    /* Get the number of devices */
    ret = eglQueryDevicesEXT(0, NULL, num_devices);
    if (!ret || *num_devices < 1)
        return NULL;

    devices = calloc(*num_devices, sizeof(EGLDeviceEXT));
    if (!devices)
        return NULL;

    ret = eglQueryDevicesEXT(*num_devices, devices, num_devices);
    if (!ret)
        goto error;

    /* We're only ever going to care about devices that support
     * EGL_EXT_device_drm, so filter out the ones that don't
     */
    for (i = 0; i < *num_devices; i++) {
        const char *extension_str =
            eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS);

        if (!epoxy_extension_in_string(extension_str, "EGL_EXT_device_drm"))
            continue;

        devices[drm_dev_count++] = devices[i];
    }
    if (!drm_dev_count)
        goto error;

    *num_devices = drm_dev_count;
242 243 244 245 246
    tmp = realloc(devices, sizeof(EGLDeviceEXT) * drm_dev_count);
    if (!tmp)
        goto error;

    devices = tmp;
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

    return devices;

error:
    free(devices);

    return NULL;
}

static Bool
xwl_glamor_egl_device_has_egl_extensions(void *device,
                                         const char **ext_list, size_t size)
{
    EGLDisplay egl_display;
    int i;
    Bool has_exts = TRUE;

    egl_display = glamor_egl_get_display(EGL_PLATFORM_DEVICE_EXT, device);
    if (!egl_display || !eglInitialize(egl_display, NULL, NULL))
        return FALSE;

    for (i = 0; i < size; i++) {
        if (!epoxy_has_egl_extension(egl_display, ext_list[i])) {
            has_exts = FALSE;
            break;
        }
    }

    eglTerminate(egl_display);
    return has_exts;
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
static void
xwl_eglstream_unref_pixmap_stream(struct xwl_pixmap *xwl_pixmap)
{
    struct xwl_screen *xwl_screen = xwl_pixmap->xwl_screen;

    if (--xwl_pixmap->refcount >= 1)
        return;

    /* If we're using this stream in the current egl context, unbind it so the
     * driver doesn't keep it around until the next eglMakeCurrent()
     * don't have to keep it around until something else changes the surface
     */
    xwl_glamor_egl_make_current(xwl_screen);
    if (eglGetCurrentSurface(EGL_READ) == xwl_pixmap->surface ||
        eglGetCurrentSurface(EGL_DRAW) == xwl_pixmap->surface) {
        eglMakeCurrent(xwl_screen->egl_display,
                       EGL_NO_SURFACE, EGL_NO_SURFACE,
                       xwl_screen->egl_context);
    }

    if (xwl_pixmap->surface)
        eglDestroySurface(xwl_screen->egl_display, xwl_pixmap->surface);

    eglDestroyStreamKHR(xwl_screen->egl_display, xwl_pixmap->stream);

    wl_buffer_destroy(xwl_pixmap->buffer);
    free(xwl_pixmap);
}

static Bool
xwl_glamor_eglstream_destroy_pixmap(PixmapPtr pixmap)
{
    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);

313 314
    if (xwl_pixmap && pixmap->refcnt == 1) {
        xwl_pixmap_del_buffer_release_cb(pixmap);
315
        xwl_eglstream_unref_pixmap_stream(xwl_pixmap);
316
    }
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    return glamor_destroy_pixmap(pixmap);
}

static struct wl_buffer *
xwl_glamor_eglstream_get_wl_buffer_for_pixmap(PixmapPtr pixmap,
                                              Bool *created)
{
    /* XXX created? */
    return xwl_pixmap_get(pixmap)->buffer;
}

static void
xwl_eglstream_set_window_pixmap(WindowPtr window, PixmapPtr pixmap)
{
    struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen);
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    struct xwl_eglstream_pending_stream *pending;

    pending = xwl_eglstream_window_get_pending(window);
    if (pending) {
        /* The pixmap for this window has changed before the compositor
         * finished attaching the consumer for the window's pixmap's original
         * eglstream. A producer can no longer be attached, so the stream's
         * useless
         */
        pending->is_valid = FALSE;

        /* The compositor may still be using the stream, so we can't destroy
         * it yet. We'll only have a guarantee that the stream is safe to
         * destroy once we receive the pending wl_display_sync() for this
         * stream
         */
        pending->xwl_pixmap->refcount++;
    }

    xwl_screen->screen->SetWindowPixmap = xwl_eglstream->SetWindowPixmap;
    (*xwl_screen->screen->SetWindowPixmap)(window, pixmap);
    xwl_eglstream->SetWindowPixmap = xwl_screen->screen->SetWindowPixmap;
    xwl_screen->screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;
}

/* Because we run asynchronously with our wayland compositor, it's possible
 * that an X client event could cause us to begin creating a stream for a
 * pixmap/window combo before the stream for the pixmap this window
 * previously used has been fully initialized. An example:
 *
 * - Start processing X client events.
 * - X window receives resize event, causing us to create a new pixmap and
 *   begin creating the corresponding eglstream. This pixmap is known as
 *   pixmap A.
Alan Coopersmith's avatar
Alan Coopersmith committed
368
 * - X window receives another resize event, and again changes its current
369 370 371 372 373 374 375 376 377
 *   pixmap causing us to create another corresponding eglstream for the same
 *   window. This pixmap is known as pixmap B.
 * - Start handling events from the wayland compositor.
 *
 * Since both pixmap A and B will have scheduled wl_display_sync events to
 * indicate when their respective streams are connected, we will receive each
 * callback in the original order the pixmaps were created. This means the
 * following would happen:
 *
Alan Coopersmith's avatar
Alan Coopersmith committed
378
 * - Receive pixmap A's stream callback, attach its stream to the surface of
379 380 381 382 383 384 385 386 387 388
 *   the window that just orphaned it.
 * - Receive pixmap B's stream callback, fall over and fail because the
 *   window's surface now incorrectly has pixmap A's stream attached to it.
 *
 * We work around this problem by keeping a queue of pending streams, and
 * only allowing one queue entry to exist for each window. In the scenario
 * listed above, this should happen:
 *
 * - Begin processing X events...
 * - A window is resized, causing us to add an eglstream (known as eglstream
Alan Coopersmith's avatar
Alan Coopersmith committed
389
 *   A) waiting for its consumer to finish attachment to be added to the
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
 *   queue.
 * - Resize on same window happens. We invalidate the previously pending
 *   stream and add another one to the pending queue (known as eglstream B).
 * - Begin processing Wayland events...
 * - Receive invalidated callback from compositor for eglstream A, destroy
 *   stream.
 * - Receive callback from compositor for eglstream B, create producer.
 * - Success!
 */
static void
xwl_eglstream_consumer_ready_callback(void *data,
                                      struct wl_callback *callback,
                                      uint32_t time)
{
    struct xwl_screen *xwl_screen = data;
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    struct xwl_pixmap *xwl_pixmap;
    struct xwl_eglstream_pending_stream *pending;
    Bool found = FALSE;

    wl_callback_destroy(callback);

    xorg_list_for_each_entry(pending, &xwl_eglstream->pending_streams, link) {
        if (pending->cb == callback) {
            found = TRUE;
            break;
        }
    }
    assert(found);

    if (!pending->is_valid) {
        xwl_eglstream_unref_pixmap_stream(pending->xwl_pixmap);
        goto out;
    }

    xwl_glamor_egl_make_current(xwl_screen);

    xwl_pixmap = pending->xwl_pixmap;
    xwl_pixmap->surface = eglCreateStreamProducerSurfaceKHR(
        xwl_screen->egl_display, xwl_eglstream->config,
        xwl_pixmap->stream, (int[]) {
            EGL_WIDTH,  pending->pixmap->drawable.width,
            EGL_HEIGHT, pending->pixmap->drawable.height,
            EGL_NONE
        });

    DebugF("eglstream: win %d completes eglstream for pixmap %p, congrats!\n",
           pending->window->drawable.id, pending->pixmap);

    xwl_eglstream_window_set_pending(pending->window, NULL);
out:
    xorg_list_del(&pending->link);
    free(pending);
}

static const struct wl_callback_listener consumer_ready_listener = {
    xwl_eglstream_consumer_ready_callback
};

static void
xwl_eglstream_queue_pending_stream(struct xwl_screen *xwl_screen,
                                   WindowPtr window, PixmapPtr pixmap)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    struct xwl_eglstream_pending_stream *pending_stream;

#ifdef DEBUG
    if (!xwl_eglstream_window_get_pending(window))
        DebugF("eglstream: win %d begins new eglstream for pixmap %p\n",
               window->drawable.id, pixmap);
    else
        DebugF("eglstream: win %d interrupts and replaces pending eglstream for pixmap %p\n",
               window->drawable.id, pixmap);
#endif

    pending_stream = malloc(sizeof(*pending_stream));
    pending_stream->window = window;
    pending_stream->pixmap = pixmap;
    pending_stream->xwl_pixmap = xwl_pixmap_get(pixmap);
    pending_stream->is_valid = TRUE;
    xorg_list_init(&pending_stream->link);
    xorg_list_add(&pending_stream->link, &xwl_eglstream->pending_streams);
    xwl_eglstream_window_set_pending(window, pending_stream);

    pending_stream->cb = wl_display_sync(xwl_screen->display);
    wl_callback_add_listener(pending_stream->cb, &consumer_ready_listener,
                             xwl_screen);
}

static void
xwl_eglstream_buffer_release_callback(void *data, struct wl_buffer *wl_buffer)
{
484 485 486 487
    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(data);

    xwl_pixmap_buffer_release_cb(data, wl_buffer);
    xwl_eglstream_unref_pixmap_stream(xwl_pixmap);
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
}

static const struct wl_buffer_listener xwl_eglstream_buffer_release_listener = {
    xwl_eglstream_buffer_release_callback
};

static void
xwl_eglstream_create_pending_stream(struct xwl_screen *xwl_screen,
                                    WindowPtr window, PixmapPtr pixmap)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    struct xwl_pixmap *xwl_pixmap;
    struct xwl_window *xwl_window = xwl_window_from_window(window);
    struct wl_array stream_attribs;
    int stream_fd = -1;

    xwl_pixmap = calloc(sizeof(*xwl_pixmap), 1);
    if (!xwl_pixmap)
        FatalError("Not enough memory to create pixmap\n");
    xwl_pixmap_set_private(pixmap, xwl_pixmap);

    xwl_glamor_egl_make_current(xwl_screen);

    xwl_pixmap->xwl_screen = xwl_screen;
    xwl_pixmap->refcount = 1;
    xwl_pixmap->stream = eglCreateStreamKHR(xwl_screen->egl_display, NULL);
    stream_fd = eglGetStreamFileDescriptorKHR(xwl_screen->egl_display,
                                              xwl_pixmap->stream);

    wl_array_init(&stream_attribs);
    xwl_pixmap->buffer =
        wl_eglstream_display_create_stream(xwl_eglstream->display,
                                           pixmap->drawable.width,
                                           pixmap->drawable.height,
                                           stream_fd,
                                           WL_EGLSTREAM_HANDLE_TYPE_FD,
                                           &stream_attribs);

    wl_buffer_add_listener(xwl_pixmap->buffer,
                           &xwl_eglstream_buffer_release_listener,
529
                           pixmap);
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

    wl_eglstream_controller_attach_eglstream_consumer(
        xwl_eglstream->controller, xwl_window->surface, xwl_pixmap->buffer);

    xwl_eglstream_queue_pending_stream(xwl_screen, window, pixmap);

    close(stream_fd);
}

static Bool
xwl_glamor_eglstream_allow_commits(struct xwl_window *xwl_window)
{
    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
    struct xwl_eglstream_pending_stream *pending =
        xwl_eglstream_window_get_pending(xwl_window->window);
    PixmapPtr pixmap =
        (*xwl_screen->screen->GetWindowPixmap)(xwl_window->window);
    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);

    if (xwl_pixmap) {
        if (pending) {
            /* Wait for the compositor to finish connecting the consumer for
             * this eglstream */
            if (pending->is_valid)
                return FALSE;

            /* The pixmap for this window was changed before the compositor
             * finished connecting the eglstream for the window's previous
             * pixmap. Begin creating a new eglstream. */
        } else {
            return TRUE;
        }
    }

    /* Glamor pixmap has no backing stream yet; begin making one and disallow
     * commits until then
     */
    xwl_eglstream_create_pending_stream(xwl_screen, xwl_window->window,
                                        pixmap);

    return FALSE;
}

static void
xwl_glamor_eglstream_post_damage(struct xwl_window *xwl_window,
                                 PixmapPtr pixmap, RegionPtr region)
{
    struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
    BoxPtr box = RegionExtents(region);
    EGLint egl_damage[] = {
        box->x1,           box->y1,
        box->x2 - box->x1, box->y2 - box->y1
    };
    GLint saved_vao;

    /* Unbind the framebuffer BEFORE binding the EGLSurface, otherwise we
     * won't actually draw to it
     */
    xwl_glamor_egl_make_current(xwl_screen);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    if (eglGetCurrentSurface(EGL_READ) != xwl_pixmap->surface ||
        eglGetCurrentSurface(EGL_DRAW) != xwl_pixmap->surface)
        eglMakeCurrent(xwl_screen->egl_display,
                       xwl_pixmap->surface, xwl_pixmap->surface,
                       xwl_screen->egl_context);

    /* Save current GL state */
    glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao);

    /* Setup our GL state */
    glUseProgram(xwl_eglstream->blit_prog);
    glViewport(0, 0, pixmap->drawable.width, pixmap->drawable.height);
    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(xwl_eglstream->blit_vao);
    glBindTexture(GL_TEXTURE_2D, glamor_get_pixmap_texture(pixmap));

    glUniform1i(xwl_eglstream->blit_is_rgba_pos,
                pixmap->drawable.depth >= 32);

    /* Blit rendered image into EGLStream surface */
    glDrawBuffer(GL_BACK);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    if (xwl_eglstream->have_egl_damage)
        eglSwapBuffersWithDamageKHR(xwl_screen->egl_display,
                                    xwl_pixmap->surface, egl_damage, 1);
    else
        eglSwapBuffers(xwl_screen->egl_display, xwl_pixmap->surface);

    /* Restore previous state */
    glBindVertexArray(saved_vao);
    glBindTexture(GL_TEXTURE_2D, 0);

    /* After this we will hand off the eglstream's wl_buffer to the
     * compositor, which will own it until it sends a release() event. */
    xwl_pixmap->refcount++;
}

static void
xwl_eglstream_display_handle_caps(void *data,
                                  struct wl_eglstream_display *disp,
                                  int32_t caps)
{
    xwl_eglstream_get(data)->display_caps = caps;
}

static void
xwl_eglstream_display_handle_swapinterval_override(void *data,
                                                   struct wl_eglstream_display *disp,
                                                   int32_t swapinterval,
                                                   struct wl_buffer *stream)
{
}

const struct wl_eglstream_display_listener eglstream_display_listener = {
    .caps = xwl_eglstream_display_handle_caps,
    .swapinterval_override = xwl_eglstream_display_handle_swapinterval_override,
};

653
static Bool
654 655
xwl_glamor_eglstream_init_wl_registry(struct xwl_screen *xwl_screen,
                                      struct wl_registry *wl_registry,
656 657
                                      uint32_t id, const char *name,
                                      uint32_t version)
658 659 660 661 662 663 664 665 666 667 668
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);

    if (strcmp(name, "wl_eglstream_display") == 0) {
        xwl_eglstream->display = wl_registry_bind(
            wl_registry, id, &wl_eglstream_display_interface, version);

        wl_eglstream_display_add_listener(xwl_eglstream->display,
                                          &eglstream_display_listener,
                                          xwl_screen);
669
        return TRUE;
670 671 672
    } else if (strcmp(name, "wl_eglstream_controller") == 0) {
        xwl_eglstream->controller = wl_registry_bind(
            wl_registry, id, &wl_eglstream_controller_interface, version);
673
        return TRUE;
674
    }
675 676 677

    /* no match */
    return FALSE;
678 679
}

680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
static Bool
xwl_glamor_eglstream_has_wl_interfaces(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);

    if (xwl_eglstream->display == NULL) {
        ErrorF("glamor: 'wl_eglstream_display' not supported\n");
        return FALSE;
    }

    if (xwl_eglstream->controller == NULL) {
        ErrorF("glamor: 'wl_eglstream_controller' not supported\n");
        return FALSE;
    }

    return TRUE;
}

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
static inline void
xwl_eglstream_init_shaders(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    GLint fs, vs, attrib;
    GLuint vbo;

    const char *blit_vs_src =
        "attribute vec2 texcoord;\n"
        "attribute vec2 position;\n"
        "varying vec2 t;\n"
        "void main() {\n"
        "    t = texcoord;\n"
        "    gl_Position = vec4(position, 0, 1);\n"
        "}";

    const char *blit_fs_src =
        "varying vec2 t;\n"
        "uniform sampler2D s;\n"
        "uniform bool is_rgba;\n"
        "void main() {\n"
        "    if (is_rgba)\n"
        "        gl_FragColor = texture2D(s, t);\n"
        "    else\n"
        "        gl_FragColor = vec4(texture2D(s, t).rgb, 1.0);\n"
        "}";

    static const float position[] = {
        /* position */
        -1, -1,
         1, -1,
         1,  1,
        -1,  1,
        /* texcoord */
         0,  1,
         1,  1,
         1,  0,
         0,  0,
    };

    vs = xwl_eglstream_compile_glsl_prog(GL_VERTEX_SHADER, blit_vs_src);
    fs = xwl_eglstream_compile_glsl_prog(GL_FRAGMENT_SHADER, blit_fs_src);

    xwl_eglstream->blit_prog = xwl_eglstream_build_glsl_prog(vs, fs);
    glDeleteShader(vs);
    glDeleteShader(fs);

    /* Create the blitter's vao */
    glGenVertexArrays(1, &xwl_eglstream->blit_vao);
    glBindVertexArray(xwl_eglstream->blit_vao);

    /* Set the data for both position and texcoord in the vbo */
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
    xwl_eglstream->blit_vbo = vbo;

    /* Define each shader attribute's data location in our vbo */
    attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "position");
    glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0, NULL);
    glEnableVertexAttribArray(attrib);

    attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "texcoord");
    glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0,
                          (void*)(sizeof(float) * 8));
    glEnableVertexAttribArray(attrib);

    /* Save the location of uniforms we'll set later */
    xwl_eglstream->blit_is_rgba_pos =
        glGetUniformLocation(xwl_eglstream->blit_prog, "is_rgba");
}

static Bool
xwl_glamor_eglstream_init_egl(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    EGLConfig config;
    const EGLint attrib_list[] = {
        EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR,
        EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
        EGL_CONTEXT_MAJOR_VERSION_KHR,
        GLAMOR_GL_CORE_VER_MAJOR,
        EGL_CONTEXT_MINOR_VERSION_KHR,
        GLAMOR_GL_CORE_VER_MINOR,
        EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
        EGL_NONE
    };
    const EGLint config_attribs[] = {
        EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR,
        EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_NONE,
    };
    int n;

    xwl_screen->egl_display = glamor_egl_get_display(
        EGL_PLATFORM_DEVICE_EXT, xwl_eglstream->egl_device);
    if (!xwl_screen->egl_display)
        goto error;

    if (!eglInitialize(xwl_screen->egl_display, NULL, NULL)) {
        xwl_screen->egl_display = NULL;
        goto error;
    }

809 810 811 812 813 814
    if (!epoxy_has_egl_extension(xwl_screen->egl_display,
                                 "EGL_IMG_context_priority")) {
        ErrorF("EGL_IMG_context_priority not available\n");
        goto error;
    }

815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
    eglChooseConfig(xwl_screen->egl_display, config_attribs, &config, 1, &n);
    if (!n) {
        ErrorF("No acceptable EGL configs found\n");
        goto error;
    }

    xwl_eglstream->config = config;
#if 0
    xwl_screen->formats =
        XWL_FORMAT_RGB565 | XWL_FORMAT_XRGB8888 | XWL_FORMAT_ARGB8888;
#endif

    eglBindAPI(EGL_OPENGL_API);
    xwl_screen->egl_context = eglCreateContext(
        xwl_screen->egl_display, config, EGL_NO_CONTEXT, attrib_list);
    if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
        ErrorF("Failed to create main EGL context: 0x%x\n", eglGetError());
        goto error;
    }

    if (!eglMakeCurrent(xwl_screen->egl_display,
                        EGL_NO_SURFACE, EGL_NO_SURFACE,
                        xwl_screen->egl_context)) {
        ErrorF("Failed to make EGL context current\n");
        goto error;
    }

    xwl_eglstream->have_egl_damage =
        epoxy_has_egl_extension(xwl_screen->egl_display,
                                "EGL_KHR_swap_buffers_with_damage");
    if (!xwl_eglstream->have_egl_damage)
        ErrorF("Driver lacks EGL_KHR_swap_buffers_with_damage, performance "
               "will be affected\n");

    xwl_eglstream_init_shaders(xwl_screen);

    return TRUE;
error:
    xwl_eglstream_cleanup(xwl_screen);
    return FALSE;
}

static Bool
xwl_glamor_eglstream_init_screen(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream =
        xwl_eglstream_get(xwl_screen);
    ScreenPtr screen = xwl_screen->screen;

    /* We can just let glamor handle CreatePixmap */
    screen->DestroyPixmap = xwl_glamor_eglstream_destroy_pixmap;

    xwl_eglstream->SetWindowPixmap = screen->SetWindowPixmap;
    screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap;

    if (!dixRegisterPrivateKey(&xwl_eglstream_window_private_key,
                               PRIVATE_WINDOW, 0))
        return FALSE;

    return TRUE;
}

static EGLDeviceEXT
xwl_eglstream_get_device(struct xwl_screen *xwl_screen)
{
    void **devices = NULL;
    const char *exts[] = {
        "EGL_KHR_stream",
        "EGL_KHR_stream_producer_eglsurface",
    };
    int num_devices, i;
    EGLDeviceEXT device = EGL_NO_DEVICE_EXT;

    /* No device specified by the user, so find one ourselves */
    devices = xwl_glamor_egl_get_devices(&num_devices);
    if (!devices)
        goto out;

    for (i = 0; i < num_devices; i++) {
        if (xwl_glamor_egl_device_has_egl_extensions(devices[i], exts,
                                                     ARRAY_SIZE(exts))) {
            device = devices[i];
            break;
        }
    }

    free(devices);
out:
    if (!device)
        ErrorF("glamor: No eglstream capable devices found\n");
    return device;
}

908
void
909 910 911 912 913
xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
{
    struct xwl_eglstream_private *xwl_eglstream;
    EGLDeviceEXT egl_device;

914
    xwl_screen->eglstream_backend.is_available = FALSE;
915 916
    egl_device = xwl_eglstream_get_device(xwl_screen);
    if (egl_device == EGL_NO_DEVICE_EXT)
917
        return;
918 919

    if (!dixRegisterPrivateKey(&xwl_eglstream_private_key, PRIVATE_SCREEN, 0))
920
        return;
921

922
    xwl_eglstream = calloc(1, sizeof(*xwl_eglstream));
923
    if (!xwl_eglstream) {
924 925
        ErrorF("Failed to allocate memory required to init EGLStream support\n");
        return;
926 927 928 929 930 931 932 933
    }

    dixSetPrivate(&xwl_screen->screen->devPrivates,
                  &xwl_eglstream_private_key, xwl_eglstream);

    xwl_eglstream->egl_device = egl_device;
    xorg_list_init(&xwl_eglstream->pending_streams);

934 935 936 937 938 939 940 941
    xwl_screen->eglstream_backend.init_egl = xwl_glamor_eglstream_init_egl;
    xwl_screen->eglstream_backend.init_wl_registry = xwl_glamor_eglstream_init_wl_registry;
    xwl_screen->eglstream_backend.has_wl_interfaces = xwl_glamor_eglstream_has_wl_interfaces;
    xwl_screen->eglstream_backend.init_screen = xwl_glamor_eglstream_init_screen;
    xwl_screen->eglstream_backend.get_wl_buffer_for_pixmap = xwl_glamor_eglstream_get_wl_buffer_for_pixmap;
    xwl_screen->eglstream_backend.post_damage = xwl_glamor_eglstream_post_damage;
    xwl_screen->eglstream_backend.allow_commits = xwl_glamor_eglstream_allow_commits;
    xwl_screen->eglstream_backend.is_available = TRUE;
942
    xwl_screen->eglstream_backend.backend_flags = XWL_EGL_BACKEND_NO_FLAG;
943
}