/* -*- 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; }