1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /*
4  * Copyright (C) 2016 Red Hat Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19  * 02111-1307, USA.
20  *
21  * Written by:
22  *     Jonas Ådahl <jadahl@gmail.com>
23  */
24 
25 #include "config.h"
26 
27 #include "wayland/meta-wayland-egl-stream.h"
28 
29 #include <dlfcn.h>
30 
31 #include "backends/meta-backend-private.h"
32 #include "backends/meta-egl-ext.h"
33 #include "backends/meta-egl.h"
34 #include "cogl/cogl-egl.h"
35 #include "meta/meta-backend.h"
36 #include "wayland/meta-wayland-buffer.h"
37 #include "wayland/meta-wayland-private.h"
38 
39 #include "wayland-eglstream-controller-server-protocol.h"
40 
41 static struct wl_interface *wl_eglstream_controller_interface_ptr = NULL;
42 
43 static void
attach_eglstream_consumer(struct wl_client * client,struct wl_resource * resource,struct wl_resource * wl_surface,struct wl_resource * wl_eglstream)44 attach_eglstream_consumer (struct wl_client   *client,
45                            struct wl_resource *resource,
46                            struct wl_resource *wl_surface,
47                            struct wl_resource *wl_eglstream)
48 {
49   MetaWaylandBuffer *buffer = meta_wayland_buffer_from_resource (wl_eglstream);
50 
51   if (!meta_wayland_buffer_is_realized (buffer))
52     meta_wayland_buffer_realize (buffer);
53 }
54 
55 static const struct wl_eglstream_controller_interface
56 meta_eglstream_controller_interface = {
57   attach_eglstream_consumer
58 };
59 
60 static void
bind_eglstream_controller(struct wl_client * client,void * data,uint32_t version,uint32_t id)61 bind_eglstream_controller (struct wl_client *client,
62                            void             *data,
63                            uint32_t          version,
64                            uint32_t          id)
65 {
66   struct wl_resource *resource;
67 
68   g_assert (wl_eglstream_controller_interface_ptr != NULL);
69 
70   resource = wl_resource_create (client,
71                                  wl_eglstream_controller_interface_ptr,
72                                  version,
73                                  id);
74 
75   if (resource == NULL)
76     {
77       wl_client_post_no_memory(client);
78       return;
79     }
80 
81   wl_resource_set_implementation (resource,
82                                   &meta_eglstream_controller_interface,
83                                   data,
84                                   NULL);
85 }
86 
87 gboolean
meta_wayland_eglstream_controller_init(MetaWaylandCompositor * compositor)88 meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor)
89 {
90   /*
91    * wl_eglstream_controller_interface is provided by
92    * libnvidia-egl-wayland.so.1
93    *
94    * Since it might not be available on the
95    * system, dynamically load it at runtime and resolve the needed
96    * symbols. If available, it should be found under any of the search
97    * directories of dlopen()
98    *
99    * Failure to initialize wl_eglstream_controller is non-fatal
100    */
101 
102   void *lib = dlopen ("libnvidia-egl-wayland.so.1", RTLD_NOW | RTLD_LAZY);
103   if (!lib)
104     goto fail;
105 
106   wl_eglstream_controller_interface_ptr =
107     dlsym (lib, "wl_eglstream_controller_interface");
108 
109   if (!wl_eglstream_controller_interface_ptr)
110     goto fail;
111 
112   if (wl_global_create (compositor->wayland_display,
113                         wl_eglstream_controller_interface_ptr, 1,
114                         NULL,
115                         bind_eglstream_controller) == NULL)
116     goto fail;
117 
118   g_debug ("WL: loaded libnvidia-egl-wayland.so.1:wl_eglstream_controller.");
119 
120   return TRUE;
121 
122 fail:
123   if (lib)
124     dlclose(lib);
125 
126   g_debug ("WL: Unable to initialize wl_eglstream_controller.");
127 
128   return FALSE;
129 }
130 
131 struct _MetaWaylandEglStream
132 {
133   GObject parent;
134 
135   EGLStreamKHR egl_stream;
136   MetaWaylandBuffer *buffer;
137   CoglTexture2D *texture;
138   gboolean is_y_inverted;
139   CoglSnippet *snippet;
140 };
141 
G_DEFINE_TYPE(MetaWaylandEglStream,meta_wayland_egl_stream,G_TYPE_OBJECT)142 G_DEFINE_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream,
143                G_TYPE_OBJECT)
144 
145 MetaWaylandEglStream *
146 meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer,
147                              GError           **error)
148 {
149   MetaBackend *backend = meta_get_backend ();
150   MetaEgl *egl = meta_backend_get_egl (backend);
151   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
152   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
153   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
154   EGLAttrib stream_attribs[] = {
155     EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib) buffer->resource,
156     EGL_NONE
157   };
158   EGLStreamKHR egl_stream;
159   MetaWaylandEglStream *stream;
160 
161   egl_stream = meta_egl_create_stream_attrib (egl, egl_display, stream_attribs,
162                                               error);
163   if (egl_stream == EGL_NO_STREAM_KHR)
164     {
165       g_set_error (error, G_IO_ERROR,
166                    G_IO_ERROR_FAILED,
167                    "Failed to create stream from wl_buffer resource");
168       return NULL;
169     }
170 
171   stream = g_object_new (META_TYPE_WAYLAND_EGL_STREAM, NULL);
172   stream->egl_stream = egl_stream;
173   stream->buffer = buffer;
174 
175   return stream;
176 }
177 
178 static void
stream_texture_destroyed(gpointer data)179 stream_texture_destroyed (gpointer data)
180 {
181   MetaWaylandEglStream *stream = data;
182 
183   stream->texture = NULL;
184 
185   g_object_unref (stream);
186 }
187 
188 static gboolean
alloc_egl_stream_texture(CoglTexture2D * texture,gpointer user_data,GError ** error)189 alloc_egl_stream_texture (CoglTexture2D *texture,
190                           gpointer       user_data,
191                           GError       **error)
192 {
193   MetaBackend *backend = meta_get_backend ();
194   MetaEgl *egl = meta_backend_get_egl (backend);
195   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
196   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
197   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
198   MetaWaylandEglStream *stream = user_data;
199 
200   return meta_egl_stream_consumer_gl_texture_external (egl, egl_display,
201                                                        stream->egl_stream,
202                                                        error);
203 }
204 
205 CoglTexture2D *
meta_wayland_egl_stream_create_texture(MetaWaylandEglStream * stream,GError ** error)206 meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream,
207                                         GError              **error)
208 {
209   MetaBackend *backend = meta_get_backend ();
210   MetaEgl *egl = meta_backend_get_egl (backend);
211   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
212   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
213   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
214   CoglTexture2D *texture;
215   int width, height;
216   int y_inverted;
217 
218   if (!meta_egl_query_wayland_buffer (egl, egl_display,
219                                       stream->buffer->resource,
220                                       EGL_WIDTH, &width,
221                                       error))
222     return NULL;
223 
224   if (!meta_egl_query_wayland_buffer (egl, egl_display,
225                                       stream->buffer->resource,
226                                       EGL_HEIGHT, &height,
227                                       error))
228     return NULL;
229 
230   if (!meta_egl_query_wayland_buffer (egl, egl_display,
231                                       stream->buffer->resource,
232                                       EGL_WAYLAND_Y_INVERTED_WL, &y_inverted,
233                                       NULL))
234     y_inverted = EGL_TRUE;
235 
236   texture =
237     cogl_texture_2d_new_from_egl_image_external (cogl_context,
238                                                  width, height,
239                                                  alloc_egl_stream_texture,
240                                                  g_object_ref (stream),
241                                                  stream_texture_destroyed,
242                                                  error);
243   if (!texture)
244     {
245       g_object_unref (stream);
246       return NULL;
247     }
248 
249   if (!cogl_texture_allocate (COGL_TEXTURE (texture), error))
250     {
251       cogl_object_unref (texture);
252       return NULL;
253     }
254 
255   stream->texture = texture;
256   stream->is_y_inverted = !!y_inverted;
257 
258   return texture;
259 }
260 
261 gboolean
meta_wayland_egl_stream_attach(MetaWaylandEglStream * stream,GError ** error)262 meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream,
263                                 GError              **error)
264 {
265   MetaBackend *backend = meta_get_backend ();
266   MetaEgl *egl = meta_backend_get_egl (backend);
267   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
268   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
269   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
270   EGLint stream_state;
271 
272   if (!meta_egl_query_stream (egl, egl_display, stream->egl_stream,
273                               EGL_STREAM_STATE_KHR, &stream_state,
274                               error))
275     return FALSE;
276 
277   if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR)
278     {
279       if (!meta_egl_stream_consumer_acquire (egl, egl_display,
280                                              stream->egl_stream,
281                                              error))
282         return FALSE;
283     }
284 
285   return TRUE;
286 }
287 
288 gboolean
meta_wayland_egl_stream_is_y_inverted(MetaWaylandEglStream * stream)289 meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream)
290 {
291   return stream->is_y_inverted;
292 }
293 
294 CoglSnippet *
meta_wayland_egl_stream_create_snippet(MetaWaylandEglStream * stream)295 meta_wayland_egl_stream_create_snippet (MetaWaylandEglStream *stream)
296 {
297   if (!stream->snippet)
298     {
299       CoglSnippet *snippet;
300 
301       snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
302                                   "uniform samplerExternalOES tex_external;",
303                                   NULL);
304       cogl_snippet_set_replace (snippet,
305                                 "cogl_texel = texture2D (tex_external,\n"
306                                 "                        cogl_tex_coord.xy);");
307       stream->snippet = snippet;
308     }
309 
310   return cogl_object_ref (stream->snippet);
311 }
312 
313 gboolean
meta_wayland_is_egl_stream_buffer(MetaWaylandBuffer * buffer)314 meta_wayland_is_egl_stream_buffer (MetaWaylandBuffer *buffer)
315 {
316   MetaBackend *backend = meta_get_backend ();
317   MetaEgl *egl = meta_backend_get_egl (backend);
318   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
319   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
320   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
321   int stream_fd;
322 
323   if (!meta_egl_has_extensions (egl, egl_display, NULL,
324                                 "EGL_KHR_stream_consumer_gltexture",
325                                 "EGL_KHR_stream_cross_process_fd",
326                                 NULL))
327     return FALSE;
328 
329   if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource,
330                                       EGL_WAYLAND_BUFFER_WL, &stream_fd,
331                                       NULL))
332     return FALSE;
333 
334   return TRUE;
335 }
336 
337 static void
meta_wayland_egl_stream_finalize(GObject * object)338 meta_wayland_egl_stream_finalize (GObject *object)
339 {
340   MetaWaylandEglStream *stream = META_WAYLAND_EGL_STREAM (object);
341   MetaBackend *backend = meta_get_backend ();
342   MetaEgl *egl = meta_backend_get_egl (backend);
343   ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
344   CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
345   EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
346 
347   g_assert (!stream->texture);
348 
349   meta_egl_destroy_stream (egl, egl_display, stream->egl_stream, NULL);
350 
351   cogl_clear_object (&stream->snippet);
352 
353   G_OBJECT_CLASS (meta_wayland_egl_stream_parent_class)->finalize (object);
354 }
355 
356 static void
meta_wayland_egl_stream_init(MetaWaylandEglStream * stream)357 meta_wayland_egl_stream_init (MetaWaylandEglStream *stream)
358 {
359 }
360 
361 static void
meta_wayland_egl_stream_class_init(MetaWaylandEglStreamClass * klass)362 meta_wayland_egl_stream_class_init (MetaWaylandEglStreamClass *klass)
363 {
364   GObjectClass *object_class = G_OBJECT_CLASS (klass);
365 
366   object_class->finalize = meta_wayland_egl_stream_finalize;
367 }
368