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 volatile 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 volatile 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