mutter/src/wayland/meta-wayland-egl-stream.c
Miguel A. Vico 435b3c4bdb wayland: Create EGLStream-backed buffers through wl_eglstream_controller
One of the current limitations of EGLStreams is that there's no way to
resize a surface consumer without re-creating the entire stream.

Therefore, while resizing, clients will send wl_surface::attach requests
so the compositor can re-create its endpoint of the stream, but no
buffer will be available actually. If we proceed with the rest of the
attach operation we'll be presenting an empty buffer.

In order to fix this, a separate wl_eglstream_controller protocol has
been introduced that clients can use to request a stream re-creation
without overloading wl_surface::attach for that purpose.

This change adds the required logic to create the corresponding
wl_eglstream_controller global interface that clients can bind to.

Whenever a client requests a stream to be created, we just need to
create and realize the new EGLStream buffer. The same buffer resource
will be given at a later time to wl_surface::attach, whenever new
content is made available by the application, so we can proceed to
acquire the stream buffer and update the surface state.

https://bugzilla.gnome.org/show_bug.cgi?id=782575
2018-06-15 13:45:29 -07:00

364 lines
11 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2016 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "wayland/meta-wayland-egl-stream.h"
#include "cogl/cogl-egl.h"
#include "backends/meta-backend-private.h"
#include "backends/meta-egl.h"
#include "backends/meta-egl-ext.h"
#include "meta/meta-backend.h"
#include "wayland/meta-wayland-buffer.h"
#include "wayland/meta-wayland-private.h"
#ifdef HAVE_WAYLAND_EGLSTREAM
#include "wayland-eglstream-controller-server-protocol.h"
#include <dlfcn.h>
static struct wl_interface *wl_eglstream_controller_interface_ptr = NULL;
static void
attach_eglstream_consumer (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *wl_surface,
struct wl_resource *wl_eglstream)
{
MetaWaylandBuffer *buffer = meta_wayland_buffer_from_resource (wl_eglstream);
if (!meta_wayland_buffer_is_realized (buffer))
meta_wayland_buffer_realize (buffer);
}
static const struct wl_eglstream_controller_interface
meta_eglstream_controller_interface = {
attach_eglstream_consumer
};
static void
bind_eglstream_controller (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
struct wl_resource *resource;
g_assert (wl_eglstream_controller_interface_ptr != NULL);
resource = wl_resource_create (client,
wl_eglstream_controller_interface_ptr,
version,
id);
if (resource == NULL)
{
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation (resource,
&meta_eglstream_controller_interface,
data,
NULL);
}
#endif /* HAVE_WAYLAND_EGLSTREAM */
gboolean
meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor)
{
#ifdef HAVE_WAYLAND_EGLSTREAM
/*
* wl_eglstream_controller_interface is provided by
* libnvidia-egl-wayland.so.1
*
* Since it might not be available on the
* system, dynamically load it at runtime and resolve the needed
* symbols. If available, it should be found under any of the search
* directories of dlopen()
*
* Failure to initialize wl_eglstream_controller is non-fatal
*/
void *lib = dlopen ("libnvidia-egl-wayland.so.1", RTLD_NOW | RTLD_LAZY);
if (!lib)
goto fail;
wl_eglstream_controller_interface_ptr =
dlsym (lib, "wl_eglstream_controller_interface");
if (!wl_eglstream_controller_interface_ptr)
goto fail;
if (wl_global_create (compositor->wayland_display,
wl_eglstream_controller_interface_ptr, 1,
NULL,
bind_eglstream_controller) == NULL)
goto fail;
return TRUE;
fail:
if (lib)
dlclose(lib);
g_debug ("WL: Unable to initialize wl_eglstream_controller.");
#endif
return FALSE;
}
struct _MetaWaylandEglStream
{
GObject parent;
EGLStreamKHR egl_stream;
MetaWaylandBuffer *buffer;
CoglTexture2D *texture;
gboolean is_y_inverted;
};
G_DEFINE_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream,
G_TYPE_OBJECT)
MetaWaylandEglStream *
meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer,
GError **error)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
EGLAttrib stream_attribs[] = {
EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib) buffer->resource,
EGL_NONE
};
EGLStreamKHR egl_stream;
MetaWaylandEglStream *stream;
egl_stream = meta_egl_create_stream_attrib (egl, egl_display, stream_attribs,
error);
if (egl_stream == EGL_NO_STREAM_KHR)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to create stream from wl_buffer resource");
return NULL;
}
stream = g_object_new (META_TYPE_WAYLAND_EGL_STREAM, NULL);
stream->egl_stream = egl_stream;
stream->buffer = buffer;
return stream;
}
static void
stream_texture_destroyed (gpointer data)
{
MetaWaylandEglStream *stream = data;
stream->texture = NULL;
g_object_unref (stream);
}
static gboolean
alloc_egl_stream_texture (CoglTexture2D *texture,
gpointer user_data,
GError **error)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
MetaWaylandEglStream *stream = user_data;
return meta_egl_stream_consumer_gl_texture_external (egl, egl_display,
stream->egl_stream,
error);
}
CoglTexture2D *
meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
GError **error)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
CoglTexture2D *texture;
int width, height;
int y_inverted;
if (!meta_egl_query_wayland_buffer (egl, egl_display,
stream->buffer->resource,
EGL_WIDTH, &width,
error))
return NULL;
if (!meta_egl_query_wayland_buffer (egl, egl_display,
stream->buffer->resource,
EGL_HEIGHT, &height,
error))
return NULL;
if (!meta_egl_query_wayland_buffer (egl, egl_display,
stream->buffer->resource,
EGL_WAYLAND_Y_INVERTED_WL, &y_inverted,
NULL))
y_inverted = EGL_TRUE;
texture =
cogl_texture_2d_new_from_egl_image_external (cogl_context,
width, height,
alloc_egl_stream_texture,
g_object_ref (stream),
stream_texture_destroyed,
error);
if (!texture)
{
g_object_unref (stream);
return NULL;
}
if (!cogl_texture_allocate (COGL_TEXTURE (texture), error))
{
cogl_object_unref (texture);
return NULL;
}
stream->texture = texture;
stream->is_y_inverted = !!y_inverted;
return texture;
}
gboolean
meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream,
GError **error)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
EGLint stream_state;
if (!meta_egl_query_stream (egl, egl_display, stream->egl_stream,
EGL_STREAM_STATE_KHR, &stream_state,
error))
return FALSE;
if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR)
{
if (!meta_egl_stream_consumer_acquire (egl, egl_display,
stream->egl_stream,
error))
return FALSE;
}
return TRUE;
}
gboolean
meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream)
{
return stream->is_y_inverted;
}
CoglSnippet *
meta_wayland_egl_stream_create_snippet (void)
{
CoglSnippet *snippet;
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
"uniform samplerExternalOES tex_external;",
NULL);
cogl_snippet_set_replace (snippet,
"cogl_texel = texture2D (tex_external,\n"
" cogl_tex_coord.xy);");
return snippet;
}
gboolean
meta_wayland_is_egl_stream_buffer (MetaWaylandBuffer *buffer)
{
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
int stream_fd;
if (!meta_egl_has_extensions (egl, egl_display, NULL,
"EGL_KHR_stream_consumer_gltexture",
"EGL_KHR_stream_cross_process_fd",
NULL))
return FALSE;
if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource,
EGL_WAYLAND_BUFFER_WL, &stream_fd,
NULL))
return FALSE;
return TRUE;
}
static void
meta_wayland_egl_stream_finalize (GObject *object)
{
MetaWaylandEglStream *stream = META_WAYLAND_EGL_STREAM (object);
MetaBackend *backend = meta_get_backend ();
MetaEgl *egl = meta_backend_get_egl (backend);
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
g_assert (!stream->texture);
meta_egl_destroy_stream (egl, egl_display, stream->egl_stream, NULL);
G_OBJECT_CLASS (meta_wayland_egl_stream_parent_class)->finalize (object);
}
static void
meta_wayland_egl_stream_init (MetaWaylandEglStream *stream)
{
}
static void
meta_wayland_egl_stream_class_init (MetaWaylandEglStreamClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_wayland_egl_stream_finalize;
}