diff --git a/src/Makefile.am b/src/Makefile.am index 0c033c664..30ef6404d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -293,6 +293,8 @@ libmutter_la_SOURCES += \ wayland/meta-wayland-data-device.c \ wayland/meta-wayland-data-device.h \ wayland/meta-wayland-data-device-private.h \ + wayland/meta-wayland-egl-stream.c \ + wayland/meta-wayland-egl-stream.h \ wayland/meta-wayland-input-device.c \ wayland/meta-wayland-input-device.h \ wayland/meta-wayland-pointer-gestures.c \ diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c index b1a183eaa..23f3edad5 100644 --- a/src/backends/native/meta-renderer-native.c +++ b/src/backends/native/meta-renderer-native.c @@ -846,6 +846,12 @@ static gboolean meta_renderer_native_init_egl_context (CoglContext *cogl_context, GError **error) { +#ifdef HAVE_EGL_DEVICE + CoglRenderer *cogl_renderer = cogl_context->display->renderer; + CoglRendererEGL *egl_renderer = cogl_renderer->winsys; + MetaRendererNative *renderer_native = egl_renderer->platform; +#endif + COGL_FLAGS_SET (cogl_context->features, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE); /* TODO: remove this deprecated feature */ @@ -859,6 +865,12 @@ meta_renderer_native_init_egl_context (CoglContext *cogl_context, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); +#ifdef HAVE_EGL_DEVICE + if (renderer_native->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE) + COGL_FLAGS_SET (cogl_context->features, + COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, TRUE); +#endif + return TRUE; } diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c index eeb2495bb..1f4d384fe 100644 --- a/src/wayland/meta-wayland-buffer.c +++ b/src/wayland/meta-wayland-buffer.c @@ -86,6 +86,7 @@ typedef enum _MetaWaylandBufferType META_WAYLAND_BUFFER_TYPE_UNKNOWN, META_WAYLAND_BUFFER_TYPE_SHM, META_WAYLAND_BUFFER_TYPE_EGL_IMAGE, + META_WAYLAND_BUFFER_TYPE_EGL_STREAM, } MetaWaylandBufferType; static MetaWaylandBufferType @@ -107,6 +108,9 @@ determine_buffer_type (MetaWaylandBuffer *buffer) NULL)) return META_WAYLAND_BUFFER_TYPE_EGL_IMAGE; + if (meta_wayland_is_egl_stream_buffer (buffer)) + return META_WAYLAND_BUFFER_TYPE_EGL_STREAM; + return META_WAYLAND_BUFFER_TYPE_UNKNOWN; } @@ -276,6 +280,39 @@ egl_image_buffer_attach (MetaWaylandBuffer *buffer, return TRUE; } +static gboolean +egl_stream_buffer_attach (MetaWaylandBuffer *buffer, + GError **error) +{ + MetaWaylandEglStream *stream; + + stream = buffer->egl_stream.stream; + if (!stream) + stream = meta_wayland_egl_stream_new (buffer, error); + + if (!stream) + return FALSE; + + buffer->egl_stream.stream = stream; + + if (!buffer->texture) + { + CoglTexture2D *texture; + + texture = meta_wayland_egl_stream_create_texture (stream, error); + if (!texture) + return FALSE; + + buffer->texture = COGL_TEXTURE (texture); + buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream); + } + + if (!meta_wayland_egl_stream_attach (stream, error)) + return FALSE; + + return TRUE; +} + gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, GError **error) @@ -292,6 +329,9 @@ meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, return shm_buffer_attach (buffer, error); case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: return egl_image_buffer_attach (buffer, error); + case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: + return egl_stream_buffer_attach (buffer, error); + break; case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -308,6 +348,15 @@ meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer) return buffer->texture; } +CoglSnippet * +meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer) +{ + if (!buffer->egl_stream.stream) + return NULL; + + return meta_wayland_egl_stream_create_snippet (); +} + gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer) { @@ -376,6 +425,7 @@ meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, case META_WAYLAND_BUFFER_TYPE_SHM: res = process_shm_buffer_damage (buffer, region, &error); case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: + case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: res = TRUE; break; case META_WAYLAND_BUFFER_TYPE_UNKNOWN: @@ -398,6 +448,7 @@ meta_wayland_buffer_finalize (GObject *object) MetaWaylandBuffer *buffer = META_WAYLAND_BUFFER (object); g_clear_pointer (&buffer->texture, cogl_object_unref); + g_clear_object (&buffer->egl_stream.stream); G_OBJECT_CLASS (meta_wayland_buffer_parent_class)->finalize (object); } diff --git a/src/wayland/meta-wayland-buffer.h b/src/wayland/meta-wayland-buffer.h index 1ec2efbe9..9189e2873 100644 --- a/src/wayland/meta-wayland-buffer.h +++ b/src/wayland/meta-wayland-buffer.h @@ -30,6 +30,7 @@ #include #include "meta-wayland-types.h" +#include "meta-wayland-egl-stream.h" struct _MetaWaylandBuffer { @@ -40,6 +41,10 @@ struct _MetaWaylandBuffer CoglTexture *texture; gboolean is_y_inverted; + + struct { + MetaWaylandEglStream *stream; + } egl_stream; }; #define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ()) @@ -50,6 +55,7 @@ MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resou gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, GError **error); CoglTexture * meta_wayland_buffer_get_texture (MetaWaylandBuffer *buffer); +CoglSnippet * meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer); gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer); void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, cairo_region_t *region); diff --git a/src/wayland/meta-wayland-egl-stream.c b/src/wayland/meta-wayland-egl-stream.c new file mode 100644 index 000000000..d3fab3e53 --- /dev/null +++ b/src/wayland/meta-wayland-egl-stream.c @@ -0,0 +1,279 @@ +/* -*- 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 + */ + +#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 "meta/meta-backend.h" +#include "wayland/meta-wayland-buffer.h" + +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); + g_autoptr (MetaWaylandEglStream) stream = NULL; + int stream_fd; + EGLStreamKHR egl_stream; + + stream = g_object_new (META_TYPE_WAYLAND_EGL_STREAM, NULL); + + /* + * HACK: Use a (as far as I can tell) undocumented hack by passing + * EGL_WAYLAND_BUFFER_WL to eglQueryWaylandBufferWL. If it happens to be a + * dummy EGLStream buffer, we'll get a EGLStream file descriptor. + * + * FIXME: At some point, replace this with the EGL_WL_wayland_eglstream + * extension. + */ + if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, + EGL_WAYLAND_BUFFER_WL, &stream_fd, + error)) + return NULL; + + if (stream_fd == EGL_NO_FILE_DESCRIPTOR_KHR) + { + g_set_error (error, G_IO_ERROR, + G_IO_ERROR_FAILED, + "Stream already used with other wl_buffer"); + return NULL; + } + + egl_stream = meta_egl_create_stream_from_file_descriptor (egl, egl_display, stream_fd, + error); + close (stream_fd); + if (egl_stream == EGL_NO_STREAM_KHR) + return NULL; + + stream->egl_stream = egl_stream; + stream->buffer = buffer; + + return g_steal_pointer (&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; +} diff --git a/src/wayland/meta-wayland-egl-stream.h b/src/wayland/meta-wayland-egl-stream.h new file mode 100644 index 000000000..db9a94013 --- /dev/null +++ b/src/wayland/meta-wayland-egl-stream.h @@ -0,0 +1,52 @@ +/* -*- 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 + */ + +#ifndef META_WAYLAND_EGL_STREAM_H +#define META_WAYLAND_EGL_STREAM_H + +#include +#include + +#include "cogl/cogl/cogl.h" +#include "wayland/meta-wayland-types.h" + +#define META_TYPE_WAYLAND_EGL_STREAM (meta_wayland_egl_stream_get_type ()) +G_DECLARE_FINAL_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream, + META, WAYLAND_EGL_STREAM, GObject); + +gboolean meta_wayland_is_egl_stream_buffer (MetaWaylandBuffer *buffer); + +MetaWaylandEglStream * meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer, + GError **error); + +gboolean meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream, + GError **error); + +CoglTexture2D * meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream, + GError **error); +CoglSnippet * meta_wayland_egl_stream_create_snippet (void); + +gboolean meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream); + +#endif /* META_WAYLAND_EGL_STREAM_H */ diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 046806c36..b780c0fdc 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -755,14 +755,18 @@ apply_pending_state (MetaWaylandSurface *surface, { MetaShapedTexture *stex; CoglTexture *texture; + CoglSnippet *snippet; gboolean is_y_inverted; stex = meta_surface_actor_get_texture (surface->surface_actor); texture = meta_wayland_buffer_get_texture (pending->buffer); + snippet = meta_wayland_buffer_create_snippet (pending->buffer); is_y_inverted = meta_wayland_buffer_is_y_inverted (pending->buffer); meta_shaped_texture_set_texture (stex, texture); + meta_shaped_texture_set_snippet (stex, snippet); meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); + g_clear_pointer (&snippet, cogl_object_unref); } }