1 /*
2  * GStreamer
3  * Copyright (C) 2016 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:gstglquery
23  * @short_description: OpenGL query abstraction
24  * @title: GstGLQuery
25  * @see_also:
26  *
27  * A #GstGLQuery represents and holds an OpenGL query object.  Various types of
28  * queries can be run or counters retrieved.
29  *
30  * Since: 1.10
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <string.h>
38 
39 #include "gstglquery.h"
40 
41 #include "gstglcontext.h"
42 #include "gstglfuncs.h"
43 
44 #ifndef GL_TIME_ELAPSED
45 #define GL_TIME_ELAPSED 0x88BF
46 #endif
47 
48 #ifndef GL_TIMESTAMP
49 #define GL_TIMESTAMP 0x8E28
50 #endif
51 
52 #ifndef GL_QUERY_RESULT
53 #define GL_QUERY_RESULT 0x8866
54 #endif
55 
56 #define GST_CAT_DEFAULT gst_gl_query_debug
57 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
58 
59 static void
_init_debug(void)60 _init_debug (void)
61 {
62   static volatile gsize _init = 0;
63 
64   if (g_once_init_enter (&_init)) {
65     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glquery", 0, "glquery element");
66     g_once_init_leave (&_init, 1);
67   }
68 }
69 
70 static const gchar *
_query_type_to_string(guint query_type)71 _query_type_to_string (guint query_type)
72 {
73   switch (query_type) {
74     case GST_GL_QUERY_TIME_ELAPSED:
75     case GL_TIME_ELAPSED:
76       return "time elapsed";
77     case GL_TIMESTAMP:
78     case GST_GL_QUERY_TIMESTAMP:
79       return "timestamp";
80     default:
81       return "unknown";
82   }
83 }
84 
85 static guint
_gst_gl_query_type_to_gl(GstGLQueryType query_type)86 _gst_gl_query_type_to_gl (GstGLQueryType query_type)
87 {
88   if (query_type == GST_GL_QUERY_TIME_ELAPSED)
89     return GL_TIME_ELAPSED;
90   if (query_type == GST_GL_QUERY_TIMESTAMP)
91     return GL_TIMESTAMP;
92 
93   return 0;
94 }
95 
96 static gboolean
_query_type_supports_counter(guint gl_query_type)97 _query_type_supports_counter (guint gl_query_type)
98 {
99   return gl_query_type == GL_TIMESTAMP;
100 }
101 
102 static gboolean
_query_type_supports_begin_end(guint gl_query_type)103 _query_type_supports_begin_end (guint gl_query_type)
104 {
105   return gl_query_type == GL_TIME_ELAPSED;
106 }
107 
108 static gboolean
_context_supports_query_type(GstGLContext * context,guint gl_query_type)109 _context_supports_query_type (GstGLContext * context, guint gl_query_type)
110 {
111   return gl_query_type != 0 && context->gl_vtable->GenQueries != NULL;
112 }
113 
114 static gchar *
_log_time(gpointer user_data)115 _log_time (gpointer user_data)
116 {
117   GstGLQuery *query = user_data;
118   gint64 result;
119 
120   result = gst_gl_query_result (query);
121 
122   return gst_info_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (result));
123 }
124 
125 /**
126  * gst_gl_query_init:
127  * @query: a #GstGLQuery
128  * @context: a #GstGLContext
129  * @query_type: the #GstGLQueryType
130  *
131  * Since: 1.10
132  */
133 void
gst_gl_query_init(GstGLQuery * query,GstGLContext * context,GstGLQueryType query_type)134 gst_gl_query_init (GstGLQuery * query, GstGLContext * context,
135     GstGLQueryType query_type)
136 {
137   const GstGLFuncs *gl;
138   GLenum gl_query_type;
139 
140   g_return_if_fail (query != NULL);
141   g_return_if_fail (GST_IS_GL_CONTEXT (context));
142   gl = context->gl_vtable;
143   gl_query_type = _gst_gl_query_type_to_gl (query_type);
144   g_return_if_fail (gl_query_type != GL_NONE);
145 
146   memset (query, 0, sizeof (*query));
147 
148   _init_debug ();
149 
150   query->query_type = gl_query_type;
151   query->context = gst_object_ref (context);
152   query->supported = _context_supports_query_type (context, query->query_type);
153 
154   if (query->supported)
155     gl->GenQueries (1, &query->query_id);
156 
157   gst_gl_async_debug_init (&query->debug);
158   query->debug.callback = _log_time;
159   query->debug.user_data = query;
160 }
161 
162 /**
163  * gst_gl_query_unset:
164  * @query: a #GstGLQuery
165  *
166  * Free any dynamically allocated resources
167  *
168  * Since: 1.10
169  */
170 void
gst_gl_query_unset(GstGLQuery * query)171 gst_gl_query_unset (GstGLQuery * query)
172 {
173   const GstGLFuncs *gl;
174 
175   g_return_if_fail (query != NULL);
176   if (query->start_called)
177     g_critical ("Unsetting a running query. This may not be what you wanted."
178         "Be sure to pair calls to gst_gl_query_start() and gst_gl_query_end()");
179 
180   GST_TRACE ("%p unsetting query %u", query, query->query_id);
181 
182   gl = query->context->gl_vtable;
183 
184   /* unset the debug object as it may callback to print the last message */
185   gst_gl_async_debug_unset (&query->debug);
186 
187   if (query->query_id)
188     gl->DeleteQueries (1, &query->query_id);
189 
190   gst_object_unref (query->context);
191 }
192 
193 /**
194  * gst_gl_query_new: (skip)
195  * @context: a #GstGLContext
196  * @query_type: the #GstGLQueryType to create
197  *
198  * Free with gst_gl_query_free()
199  *
200  * Returns: a new #GstGLQuery
201  *
202  * Since: 1.10
203  */
204 GstGLQuery *
gst_gl_query_new(GstGLContext * context,GstGLQueryType query_type)205 gst_gl_query_new (GstGLContext * context, GstGLQueryType query_type)
206 {
207   GstGLQuery *query = g_new0 (GstGLQuery, 1);
208 
209   gst_gl_query_init (query, context, query_type);
210 
211   return query;
212 }
213 
214 /**
215  * gst_gl_query_free:
216  * @query: a #GstGLQuery
217  *
218  * Frees a #GstGLQuery
219  *
220  * Since: 1.10
221  */
222 void
gst_gl_query_free(GstGLQuery * query)223 gst_gl_query_free (GstGLQuery * query)
224 {
225   g_return_if_fail (query != NULL);
226 
227   gst_gl_query_unset (query);
228   g_free (query);
229 }
230 
231 /**
232  * gst_gl_query_start:
233  * @query: a #GstGLQuery
234  *
235  * Start counting the query
236  *
237  * Since: 1.10
238  */
239 void
gst_gl_query_start(GstGLQuery * query)240 gst_gl_query_start (GstGLQuery * query)
241 {
242   const GstGLFuncs *gl;
243 
244   g_return_if_fail (query != NULL);
245   g_return_if_fail (_query_type_supports_begin_end (query->query_type));
246   g_return_if_fail (query->start_called == FALSE);
247 
248   query->start_called = TRUE;
249 
250   if (!query->supported)
251     return;
252 
253   gst_gl_async_debug_output_log_msg (&query->debug);
254 
255   GST_TRACE ("%p start query type \'%s\' id %u", query,
256       _query_type_to_string (query->query_type), query->query_id);
257 
258   gl = query->context->gl_vtable;
259   gl->BeginQuery (query->query_type, query->query_id);
260 }
261 
262 /**
263  * gst_gl_query_end:
264  * @query: a #GstGLQuery
265  *
266  * End counting the query
267  *
268  * Since: 1.10
269  */
270 void
gst_gl_query_end(GstGLQuery * query)271 gst_gl_query_end (GstGLQuery * query)
272 {
273   const GstGLFuncs *gl;
274 
275   g_return_if_fail (query != NULL);
276   g_return_if_fail (_query_type_supports_begin_end (query->query_type));
277   g_return_if_fail (query->start_called);
278 
279   query->start_called = FALSE;
280 
281   if (!query->supported)
282     return;
283 
284   GST_TRACE ("%p end query type \'%s\' id %u", query,
285       _query_type_to_string (query->query_type), query->query_id);
286 
287   gl = query->context->gl_vtable;
288 
289   gl->EndQuery (query->query_type);
290 }
291 
292 /**
293  * gst_gl_query_counter:
294  * @query: a #GstGLQuery
295  *
296  * Record the result of a counter
297  *
298  * Since: 1.10
299  */
300 void
gst_gl_query_counter(GstGLQuery * query)301 gst_gl_query_counter (GstGLQuery * query)
302 {
303   const GstGLFuncs *gl;
304 
305   g_return_if_fail (query != NULL);
306   g_return_if_fail (_query_type_supports_counter (query->query_type));
307 
308   if (!query->supported)
309     return;
310 
311   GST_TRACE ("%p query counter type \'%s\' id %u", query,
312       _query_type_to_string (query->query_type), query->query_id);
313 
314   gst_gl_async_debug_output_log_msg (&query->debug);
315 
316   gl = query->context->gl_vtable;
317   gl->QueryCounter (query->query_id, query->query_type);
318 }
319 
320 /**
321  * gst_gl_query_result:
322  * @query: a #GstGLQuery
323  *
324  * Returns: the result of the query
325  *
326  * Since: 1.10
327  */
328 guint64
gst_gl_query_result(GstGLQuery * query)329 gst_gl_query_result (GstGLQuery * query)
330 {
331   const GstGLFuncs *gl;
332   guint64 ret;
333 
334   g_return_val_if_fail (query != NULL, 0);
335   g_return_val_if_fail (!query->start_called, 0);
336 
337   if (!query->supported)
338     return 0;
339 
340   gl = query->context->gl_vtable;
341   if (gl->GetQueryObjectui64v) {
342     gl->GetQueryObjectui64v (query->query_id, GL_QUERY_RESULT,
343         (GLuint64 *) & ret);
344   } else {
345     guint tmp;
346     gl->GetQueryObjectuiv (query->query_id, GL_QUERY_RESULT, &tmp);
347     ret = tmp;
348   }
349 
350   GST_TRACE ("%p get result %" G_GUINT64_FORMAT " type \'%s\' id %u", query,
351       ret, _query_type_to_string (query->query_type), query->query_id);
352 
353   return ret;
354 }
355