1 /*
2 * GStreamer
3 * Copyright (C) 2014 Matthew Waters <matthew@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstglsyncmeta
23 * @title: GstGLSyncMeta
24 * @short_description: synchronization primitives
25 * @see_also: #GstGLBaseMemory, #GstGLContext
26 *
27 * #GstGLSyncMeta provides the ability to synchronize the OpenGL command stream
28 * with the CPU or with other OpenGL contexts.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "gstglsyncmeta.h"
36
37 #include "gstglcontext.h"
38 #include "gstglfuncs.h"
39
40 GST_DEBUG_CATEGORY_STATIC (gst_gl_sync_meta_debug);
41 #define GST_CAT_DEFAULT gst_gl_sync_meta_debug
42
43 #ifndef GL_SYNC_GPU_COMMANDS_COMPLETE
44 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
45 #endif
46 #ifndef GL_SYNC_FLUSH_COMMANDS_BIT
47 #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
48 #endif
49 #ifndef GL_TIMEOUT_EXPIRED
50 #define GL_TIMEOUT_EXPIRED 0x911B
51 #endif
52 #ifndef GL_TIMEOUT_IGNORED
53 #define GL_TIMEOUT_IGNORED G_GUINT64_CONSTANT(0xFFFFFFFFFFFFFFFF)
54 #endif
55
56 static void
_default_set_sync_gl(GstGLSyncMeta * sync_meta,GstGLContext * context)57 _default_set_sync_gl (GstGLSyncMeta * sync_meta, GstGLContext * context)
58 {
59 const GstGLFuncs *gl = context->gl_vtable;
60
61 if (gl->FenceSync) {
62 if (sync_meta->data) {
63 GST_LOG ("deleting sync object %p", sync_meta->data);
64 gl->DeleteSync ((GLsync) sync_meta->data);
65 }
66 sync_meta->data =
67 (gpointer) gl->FenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
68 GST_LOG ("setting sync object %p", sync_meta->data);
69 }
70
71 if (gst_gl_context_is_shared (context))
72 gl->Flush ();
73 }
74
75 static void
_default_wait_gl(GstGLSyncMeta * sync_meta,GstGLContext * context)76 _default_wait_gl (GstGLSyncMeta * sync_meta, GstGLContext * context)
77 {
78 const GstGLFuncs *gl = context->gl_vtable;
79
80 if (sync_meta->data && gl->WaitSync) {
81 GST_LOG ("waiting on sync object %p", sync_meta->data);
82 gl->WaitSync ((GLsync) sync_meta->data, 0, GL_TIMEOUT_IGNORED);
83 }
84 }
85
86 static void
_default_wait_cpu_gl(GstGLSyncMeta * sync_meta,GstGLContext * context)87 _default_wait_cpu_gl (GstGLSyncMeta * sync_meta, GstGLContext * context)
88 {
89 const GstGLFuncs *gl = context->gl_vtable;
90 GLenum res;
91
92 if (sync_meta->data && gl->ClientWaitSync) {
93 do {
94 GST_LOG ("waiting on sync object %p", sync_meta->data);
95 res =
96 gl->ClientWaitSync ((GLsync) sync_meta->data,
97 GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000 /* 1s */ );
98 } while (res == GL_TIMEOUT_EXPIRED);
99 } else {
100 gl->Finish ();
101 }
102 }
103
104 static void
_default_copy(GstGLSyncMeta * src,GstBuffer * sbuffer,GstGLSyncMeta * dest,GstBuffer * dbuffer)105 _default_copy (GstGLSyncMeta * src, GstBuffer * sbuffer, GstGLSyncMeta * dest,
106 GstBuffer * dbuffer)
107 {
108 GST_LOG ("copy sync object %p from meta %p to %p", src->data, src, dest);
109
110 /* Setting a sync point here relies on GstBuffer copying
111 * metas after data */
112 gst_gl_sync_meta_set_sync_point (src, src->context);
113 }
114
115 static void
_default_free_gl(GstGLSyncMeta * sync_meta,GstGLContext * context)116 _default_free_gl (GstGLSyncMeta * sync_meta, GstGLContext * context)
117 {
118 const GstGLFuncs *gl = context->gl_vtable;
119
120 if (sync_meta->data) {
121 GST_LOG ("deleting sync object %p", sync_meta->data);
122 gl->DeleteSync ((GLsync) sync_meta->data);
123 sync_meta->data = NULL;
124 }
125 }
126
127 /**
128 * gst_buffer_add_gl_sync_meta_full:
129 * @context: a #GstGLContext
130 * @buffer: a #GstBuffer
131 * @data: sync data to hold
132 *
133 * Returns: (transfer none): the #GstGLSyncMeta added to #GstBuffer
134 *
135 * Since: 1.8
136 */
137 GstGLSyncMeta *
gst_buffer_add_gl_sync_meta_full(GstGLContext * context,GstBuffer * buffer,gpointer data)138 gst_buffer_add_gl_sync_meta_full (GstGLContext * context, GstBuffer * buffer,
139 gpointer data)
140 {
141 GstGLSyncMeta *meta;
142
143 g_return_val_if_fail (GST_IS_GL_CONTEXT (context), NULL);
144
145 meta =
146 (GstGLSyncMeta *) gst_buffer_add_meta ((buffer), GST_GL_SYNC_META_INFO,
147 NULL);
148
149 if (!meta)
150 return NULL;
151
152 meta->context = gst_object_ref (context);
153 meta->data = data;
154
155 return meta;
156 }
157
158 /**
159 * gst_buffer_add_gl_sync_meta:
160 * @context: a #GstGLContext
161 * @buffer: a #GstBuffer
162 *
163 * Returns: (transfer none): the #GstGLSyncMeta added to #GstBuffer
164 *
165 * Since: 1.6
166 */
167 GstGLSyncMeta *
gst_buffer_add_gl_sync_meta(GstGLContext * context,GstBuffer * buffer)168 gst_buffer_add_gl_sync_meta (GstGLContext * context, GstBuffer * buffer)
169 {
170 GstGLSyncMeta *ret = gst_buffer_add_gl_sync_meta_full (context, buffer, NULL);
171 if (!ret)
172 return NULL;
173
174 ret->set_sync_gl = _default_set_sync_gl;
175 ret->wait_gl = _default_wait_gl;
176 ret->wait_cpu_gl = _default_wait_cpu_gl;
177 ret->copy = _default_copy;
178 ret->free_gl = _default_free_gl;
179
180 return ret;
181 }
182
183 static void
_set_sync_point(GstGLContext * context,GstGLSyncMeta * sync_meta)184 _set_sync_point (GstGLContext * context, GstGLSyncMeta * sync_meta)
185 {
186 g_assert (sync_meta->set_sync_gl != NULL);
187
188 GST_LOG ("setting sync point %p", sync_meta);
189 sync_meta->set_sync_gl (sync_meta, context);
190 }
191
192 /**
193 * gst_gl_sync_meta_set_sync_point:
194 * @sync_meta: a #GstGLSyncMeta
195 * @context: a #GstGLContext
196 *
197 * Set a sync point to possibly wait on at a later time.
198 *
199 * Since: 1.6
200 */
201 void
gst_gl_sync_meta_set_sync_point(GstGLSyncMeta * sync_meta,GstGLContext * context)202 gst_gl_sync_meta_set_sync_point (GstGLSyncMeta * sync_meta,
203 GstGLContext * context)
204 {
205 if (sync_meta->set_sync)
206 sync_meta->set_sync (sync_meta, context);
207 else
208 gst_gl_context_thread_add (context,
209 (GstGLContextThreadFunc) _set_sync_point, sync_meta);
210 }
211
212 static void
_wait(GstGLContext * context,GstGLSyncMeta * sync_meta)213 _wait (GstGLContext * context, GstGLSyncMeta * sync_meta)
214 {
215 g_assert (sync_meta->wait_gl != NULL);
216
217 GST_LOG ("waiting %p", sync_meta);
218 sync_meta->wait_gl (sync_meta, context);
219 }
220
221 /**
222 * gst_gl_sync_meta_wait:
223 * @sync_meta: a #GstGLSyncMeta
224 * @context: a #GstGLContext
225 *
226 * Insert a wait into @context's command stream ensuring all previous OpenGL
227 * commands before @sync_meta have completed.
228 *
229 * Since: 1.6
230 */
231 void
gst_gl_sync_meta_wait(GstGLSyncMeta * sync_meta,GstGLContext * context)232 gst_gl_sync_meta_wait (GstGLSyncMeta * sync_meta, GstGLContext * context)
233 {
234 if (sync_meta->wait)
235 sync_meta->wait (sync_meta, context);
236 else
237 gst_gl_context_thread_add (context,
238 (GstGLContextThreadFunc) _wait, sync_meta);
239 }
240
241 static void
_wait_cpu(GstGLContext * context,GstGLSyncMeta * sync_meta)242 _wait_cpu (GstGLContext * context, GstGLSyncMeta * sync_meta)
243 {
244 g_assert (sync_meta->wait_cpu_gl != NULL);
245
246 GST_LOG ("waiting %p", sync_meta);
247 sync_meta->wait_cpu_gl (sync_meta, context);
248 }
249
250 /**
251 * gst_gl_sync_meta_wait_cpu:
252 * @sync_meta: a #GstGLSyncMeta
253 * @context: a #GstGLContext
254 *
255 * Perform a wait so that the sync point has passed from the CPU's perspective
256 * What that means, is that all GL operations changing CPU-visible data before
257 * the sync point are now visible.
258 *
259 * Since: 1.8
260 */
261 void
gst_gl_sync_meta_wait_cpu(GstGLSyncMeta * sync_meta,GstGLContext * context)262 gst_gl_sync_meta_wait_cpu (GstGLSyncMeta * sync_meta, GstGLContext * context)
263 {
264 if (sync_meta->wait_cpu)
265 sync_meta->wait_cpu (sync_meta, context);
266 else
267 gst_gl_context_thread_add (context,
268 (GstGLContextThreadFunc) _wait_cpu, sync_meta);
269 }
270
271 static gboolean
_gst_gl_sync_meta_transform(GstBuffer * dest,GstMeta * meta,GstBuffer * buffer,GQuark type,gpointer data)272 _gst_gl_sync_meta_transform (GstBuffer * dest, GstMeta * meta,
273 GstBuffer * buffer, GQuark type, gpointer data)
274 {
275 GstGLSyncMeta *dmeta, *smeta;
276
277 smeta = (GstGLSyncMeta *) meta;
278
279 if (GST_META_TRANSFORM_IS_COPY (type)) {
280 GstMetaTransformCopy *copy = data;
281
282 g_assert (smeta->copy != NULL);
283
284 if (!copy->region) {
285 /* only copy if the complete data is copied as well */
286 dmeta = gst_buffer_add_gl_sync_meta_full (smeta->context, dest, NULL);
287 if (!dmeta)
288 return FALSE;
289
290 dmeta->set_sync = smeta->set_sync;
291 dmeta->set_sync_gl = smeta->set_sync_gl;
292 dmeta->wait = smeta->wait;
293 dmeta->wait_gl = smeta->wait_gl;
294 dmeta->wait_cpu = smeta->wait_cpu;
295 dmeta->wait_cpu_gl = smeta->wait_cpu_gl;
296 dmeta->copy = smeta->copy;
297 dmeta->free = smeta->free;
298 dmeta->free_gl = smeta->free_gl;
299
300 GST_LOG ("copying sync meta %p into %p", smeta, dmeta);
301 smeta->copy (smeta, buffer, dmeta, dest);
302 }
303 } else {
304 /* return FALSE, if transform type is not supported */
305 return FALSE;
306 }
307
308 return TRUE;
309 }
310
311 static void
_free_gl_sync_meta(GstGLContext * context,GstGLSyncMeta * sync_meta)312 _free_gl_sync_meta (GstGLContext * context, GstGLSyncMeta * sync_meta)
313 {
314 g_assert (sync_meta->free_gl != NULL);
315
316 GST_LOG ("free sync meta %p", sync_meta);
317 sync_meta->free_gl (sync_meta, context);
318 }
319
320 static void
_gst_gl_sync_meta_free(GstGLSyncMeta * sync_meta,GstBuffer * buffer)321 _gst_gl_sync_meta_free (GstGLSyncMeta * sync_meta, GstBuffer * buffer)
322 {
323 if (sync_meta->free)
324 sync_meta->free (sync_meta, sync_meta->context);
325 else
326 gst_gl_context_thread_add (sync_meta->context,
327 (GstGLContextThreadFunc) _free_gl_sync_meta, sync_meta);
328
329 gst_object_unref (sync_meta->context);
330 }
331
332 static gboolean
_gst_gl_sync_meta_init(GstGLSyncMeta * sync_meta,gpointer params,GstBuffer * buffer)333 _gst_gl_sync_meta_init (GstGLSyncMeta * sync_meta, gpointer params,
334 GstBuffer * buffer)
335 {
336 static gsize _init;
337
338 if (g_once_init_enter (&_init)) {
339 GST_DEBUG_CATEGORY_INIT (gst_gl_sync_meta_debug, "glsyncmeta", 0,
340 "glsyncmeta");
341 g_once_init_leave (&_init, 1);
342 }
343
344 sync_meta->context = NULL;
345 sync_meta->data = NULL;
346 sync_meta->set_sync = NULL;
347 sync_meta->set_sync_gl = NULL;
348 sync_meta->wait = NULL;
349 sync_meta->wait_gl = NULL;
350 sync_meta->wait_cpu = NULL;
351 sync_meta->wait_cpu_gl = NULL;
352 sync_meta->copy = NULL;
353 sync_meta->free = NULL;
354 sync_meta->free_gl = NULL;
355
356 return TRUE;
357 }
358
359 GType
gst_gl_sync_meta_api_get_type(void)360 gst_gl_sync_meta_api_get_type (void)
361 {
362 static GType type = 0;
363 static const gchar *tags[] = { NULL };
364
365 if (g_once_init_enter (&type)) {
366 GType _type = gst_meta_api_type_register ("GstGLSyncMetaAPI", tags);
367 g_once_init_leave (&type, _type);
368 }
369
370 return type;
371 }
372
373 const GstMetaInfo *
gst_gl_sync_meta_get_info(void)374 gst_gl_sync_meta_get_info (void)
375 {
376 static const GstMetaInfo *meta_info = NULL;
377
378 if (g_once_init_enter (&meta_info)) {
379 const GstMetaInfo *meta =
380 gst_meta_register (GST_GL_SYNC_META_API_TYPE, "GstGLSyncMeta",
381 sizeof (GstGLSyncMeta), (GstMetaInitFunction) _gst_gl_sync_meta_init,
382 (GstMetaFreeFunction) _gst_gl_sync_meta_free,
383 _gst_gl_sync_meta_transform);
384 g_once_init_leave (&meta_info, meta);
385 }
386
387 return meta_info;
388 }
389