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