1 | /* GDK - The GIMP Drawing Kit |
2 | * |
3 | * gdkglcontext.c: GL context abstraction |
4 | * |
5 | * Copyright © 2014 Emmanuele Bassi |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public |
18 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /** |
22 | * SECTION:gdkglcontext |
23 | * @Title: GdkGLContext |
24 | * @Short_description: OpenGL context |
25 | * |
26 | * #GdkGLContext is an object representing the platform-specific |
27 | * OpenGL drawing context. |
28 | * |
29 | * #GdkGLContexts are created for a #GdkWindow using |
30 | * gdk_window_create_gl_context(), and the context will match |
31 | * the #GdkVisual of the window. |
32 | * |
33 | * A #GdkGLContext is not tied to any particular normal framebuffer. |
34 | * For instance, it cannot draw to the #GdkWindow back buffer. The GDK |
35 | * repaint system is in full control of the painting to that. Instead, |
36 | * you can create render buffers or textures and use gdk_cairo_draw_from_gl() |
37 | * in the draw function of your widget to draw them. Then GDK will handle |
38 | * the integration of your rendering with that of other widgets. |
39 | * |
40 | * Support for #GdkGLContext is platform-specific, context creation |
41 | * can fail, returning %NULL context. |
42 | * |
43 | * A #GdkGLContext has to be made "current" in order to start using |
44 | * it, otherwise any OpenGL call will be ignored. |
45 | * |
46 | * ## Creating a new OpenGL context ## |
47 | * |
48 | * In order to create a new #GdkGLContext instance you need a |
49 | * #GdkWindow, which you typically get during the realize call |
50 | * of a widget. |
51 | * |
52 | * A #GdkGLContext is not realized until either gdk_gl_context_make_current(), |
53 | * or until it is realized using gdk_gl_context_realize(). It is possible to |
54 | * specify details of the GL context like the OpenGL version to be used, or |
55 | * whether the GL context should have extra state validation enabled after |
56 | * calling gdk_window_create_gl_context() by calling gdk_gl_context_realize(). |
57 | * If the realization fails you have the option to change the settings of the |
58 | * #GdkGLContext and try again. |
59 | * |
60 | * ## Using a GdkGLContext ## |
61 | * |
62 | * You will need to make the #GdkGLContext the current context |
63 | * before issuing OpenGL calls; the system sends OpenGL commands to |
64 | * whichever context is current. It is possible to have multiple |
65 | * contexts, so you always need to ensure that the one which you |
66 | * want to draw with is the current one before issuing commands: |
67 | * |
68 | * |[<!-- language="C" --> |
69 | * gdk_gl_context_make_current (context); |
70 | * ]| |
71 | * |
72 | * You can now perform your drawing using OpenGL commands. |
73 | * |
74 | * You can check which #GdkGLContext is the current one by using |
75 | * gdk_gl_context_get_current(); you can also unset any #GdkGLContext |
76 | * that is currently set by calling gdk_gl_context_clear_current(). |
77 | */ |
78 | |
79 | #include "config.h" |
80 | |
81 | #include "gdkglcontextprivate.h" |
82 | #include "gdkdisplayprivate.h" |
83 | #include "gdkinternals.h" |
84 | |
85 | #include "gdkintl.h" |
86 | #include "gdk-private.h" |
87 | |
88 | #include <epoxy/gl.h> |
89 | |
90 | typedef struct { |
91 | GdkDisplay *display; |
92 | GdkWindow *window; |
93 | GdkGLContext *shared_context; |
94 | |
95 | int major; |
96 | int minor; |
97 | int gl_version; |
98 | |
99 | guint realized : 1; |
100 | guint use_texture_rectangle : 1; |
101 | guint has_gl_framebuffer_blit : 1; |
102 | guint has_frame_terminator : 1; |
103 | guint has_unpack_subimage : 1; |
104 | guint extensions_checked : 1; |
105 | guint debug_enabled : 1; |
106 | guint forward_compatible : 1; |
107 | guint is_legacy : 1; |
108 | guint use_es : 1; |
109 | |
110 | GdkGLContextPaintData *paint_data; |
111 | } GdkGLContextPrivate; |
112 | |
113 | enum { |
114 | PROP_0, |
115 | |
116 | PROP_DISPLAY, |
117 | PROP_WINDOW, |
118 | PROP_SHARED_CONTEXT, |
119 | |
120 | LAST_PROP |
121 | }; |
122 | |
123 | static GParamSpec *obj_pspecs[LAST_PROP] = { NULL, }; |
124 | |
125 | G_DEFINE_QUARK (gdk-gl-error-quark, gdk_gl_error) |
126 | |
127 | G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkGLContext, gdk_gl_context, G_TYPE_OBJECT) |
128 | |
129 | static GPrivate thread_current_context = G_PRIVATE_INIT (g_object_unref); |
130 | |
131 | static void |
132 | gdk_gl_context_dispose (GObject *gobject) |
133 | { |
134 | GdkGLContext *context = GDK_GL_CONTEXT (gobject); |
135 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
136 | GdkGLContext *current; |
137 | |
138 | current = g_private_get (&thread_current_context); |
139 | if (current == context) |
140 | g_private_replace (&thread_current_context, NULL); |
141 | |
142 | g_clear_object (&priv->display); |
143 | g_clear_object (&priv->window); |
144 | g_clear_object (&priv->shared_context); |
145 | |
146 | G_OBJECT_CLASS (gdk_gl_context_parent_class)->dispose (gobject); |
147 | } |
148 | |
149 | static void |
150 | gdk_gl_context_finalize (GObject *gobject) |
151 | { |
152 | GdkGLContext *context = GDK_GL_CONTEXT (gobject); |
153 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
154 | |
155 | g_clear_pointer (&priv->paint_data, g_free); |
156 | } |
157 | |
158 | static void |
159 | gdk_gl_context_set_property (GObject *gobject, |
160 | guint prop_id, |
161 | const GValue *value, |
162 | GParamSpec *pspec) |
163 | { |
164 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject); |
165 | |
166 | switch (prop_id) |
167 | { |
168 | case PROP_DISPLAY: |
169 | { |
170 | GdkDisplay *display = g_value_get_object (value); |
171 | |
172 | if (display) |
173 | g_object_ref (display); |
174 | |
175 | if (priv->display) |
176 | g_object_unref (priv->display); |
177 | |
178 | priv->display = display; |
179 | } |
180 | break; |
181 | |
182 | case PROP_WINDOW: |
183 | { |
184 | GdkWindow *window = g_value_get_object (value); |
185 | |
186 | if (window) |
187 | g_object_ref (window); |
188 | |
189 | if (priv->window) |
190 | g_object_unref (priv->window); |
191 | |
192 | priv->window = window; |
193 | } |
194 | break; |
195 | |
196 | case PROP_SHARED_CONTEXT: |
197 | { |
198 | GdkGLContext *context = g_value_get_object (value); |
199 | |
200 | if (context != NULL) |
201 | priv->shared_context = g_object_ref (context); |
202 | } |
203 | break; |
204 | |
205 | default: |
206 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
207 | } |
208 | } |
209 | |
210 | static void |
211 | gdk_gl_context_get_property (GObject *gobject, |
212 | guint prop_id, |
213 | GValue *value, |
214 | GParamSpec *pspec) |
215 | { |
216 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private ((GdkGLContext *) gobject); |
217 | |
218 | switch (prop_id) |
219 | { |
220 | case PROP_DISPLAY: |
221 | g_value_set_object (value, priv->display); |
222 | break; |
223 | |
224 | case PROP_WINDOW: |
225 | g_value_set_object (value, priv->window); |
226 | break; |
227 | |
228 | case PROP_SHARED_CONTEXT: |
229 | g_value_set_object (value, priv->shared_context); |
230 | break; |
231 | |
232 | default: |
233 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
234 | } |
235 | } |
236 | |
237 | void |
238 | gdk_gl_context_upload_texture (GdkGLContext *context, |
239 | cairo_surface_t *image_surface, |
240 | int width, |
241 | int height, |
242 | guint texture_target) |
243 | { |
244 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
245 | |
246 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
247 | |
248 | /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if |
249 | * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available |
250 | */ |
251 | if (!priv->use_es || |
252 | (priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))) |
253 | { |
254 | glPixelStorei (GL_UNPACK_ALIGNMENT, 4); |
255 | glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (image_surface) / 4); |
256 | |
257 | if (priv->use_es) |
258 | glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
259 | cairo_image_surface_get_data (image_surface)); |
260 | else |
261 | glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, |
262 | cairo_image_surface_get_data (image_surface)); |
263 | |
264 | glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
265 | } |
266 | else |
267 | { |
268 | GLvoid *data = cairo_image_surface_get_data (image_surface); |
269 | int stride = cairo_image_surface_get_stride (image_surface); |
270 | int i; |
271 | |
272 | if (priv->use_es) |
273 | { |
274 | glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); |
275 | |
276 | for (i = 0; i < height; i++) |
277 | glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char*) data + (i * stride)); |
278 | } |
279 | else |
280 | { |
281 | glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); |
282 | |
283 | for (i = 0; i < height; i++) |
284 | glTexSubImage2D (texture_target, 0, 0, i, width, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (unsigned char*) data + (i * stride)); |
285 | } |
286 | } |
287 | } |
288 | |
289 | static gboolean |
290 | gdk_gl_context_real_realize (GdkGLContext *self, |
291 | GError **error) |
292 | { |
293 | g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, |
294 | "The current backend does not support OpenGL" ); |
295 | |
296 | return FALSE; |
297 | } |
298 | |
299 | static void |
300 | gdk_gl_context_class_init (GdkGLContextClass *klass) |
301 | { |
302 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
303 | |
304 | klass->realize = gdk_gl_context_real_realize; |
305 | |
306 | /** |
307 | * GdkGLContext:display: |
308 | * |
309 | * The #GdkDisplay used to create the #GdkGLContext. |
310 | * |
311 | * Since: 3.16 |
312 | */ |
313 | obj_pspecs[PROP_DISPLAY] = |
314 | g_param_spec_object ("display" , |
315 | P_("Display" ), |
316 | P_("The GDK display used to create the GL context" ), |
317 | GDK_TYPE_DISPLAY, |
318 | G_PARAM_READWRITE | |
319 | G_PARAM_CONSTRUCT_ONLY | |
320 | G_PARAM_STATIC_STRINGS); |
321 | |
322 | /** |
323 | * GdkGLContext:window: |
324 | * |
325 | * The #GdkWindow the gl context is bound to. |
326 | * |
327 | * Since: 3.16 |
328 | */ |
329 | obj_pspecs[PROP_WINDOW] = |
330 | g_param_spec_object ("window" , |
331 | P_("Window" ), |
332 | P_("The GDK window bound to the GL context" ), |
333 | GDK_TYPE_WINDOW, |
334 | G_PARAM_READWRITE | |
335 | G_PARAM_CONSTRUCT_ONLY | |
336 | G_PARAM_STATIC_STRINGS); |
337 | |
338 | /** |
339 | * GdkGLContext:shared-context: |
340 | * |
341 | * The #GdkGLContext that this context is sharing data with, or #NULL |
342 | * |
343 | * Since: 3.16 |
344 | */ |
345 | obj_pspecs[PROP_SHARED_CONTEXT] = |
346 | g_param_spec_object ("shared-context" , |
347 | P_("Shared context" ), |
348 | P_("The GL context this context shares data with" ), |
349 | GDK_TYPE_GL_CONTEXT, |
350 | G_PARAM_READWRITE | |
351 | G_PARAM_CONSTRUCT_ONLY | |
352 | G_PARAM_STATIC_STRINGS); |
353 | |
354 | gobject_class->set_property = gdk_gl_context_set_property; |
355 | gobject_class->get_property = gdk_gl_context_get_property; |
356 | gobject_class->dispose = gdk_gl_context_dispose; |
357 | gobject_class->finalize = gdk_gl_context_finalize; |
358 | |
359 | g_object_class_install_properties (gobject_class, LAST_PROP, obj_pspecs); |
360 | } |
361 | |
362 | static void |
363 | gdk_gl_context_init (GdkGLContext *self) |
364 | { |
365 | } |
366 | |
367 | /*< private > |
368 | * gdk_gl_context_end_frame: |
369 | * @context: a #GdkGLContext |
370 | * @painted: The area that has been redrawn this frame |
371 | * @damage: The area that we know is actually different from the last frame |
372 | * |
373 | * Copies the back buffer to the front buffer. |
374 | * |
375 | * This function may call `glFlush()` implicitly before returning; it |
376 | * is not recommended to call `glFlush()` explicitly before calling |
377 | * this function. |
378 | * |
379 | * Since: 3.16 |
380 | */ |
381 | void |
382 | gdk_gl_context_end_frame (GdkGLContext *context, |
383 | cairo_region_t *painted, |
384 | cairo_region_t *damage) |
385 | { |
386 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
387 | |
388 | GDK_GL_CONTEXT_GET_CLASS (context)->end_frame (context, painted, damage); |
389 | } |
390 | |
391 | GdkGLContextPaintData * |
392 | gdk_gl_context_get_paint_data (GdkGLContext *context) |
393 | { |
394 | |
395 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
396 | |
397 | if (priv->paint_data == NULL) |
398 | { |
399 | priv->paint_data = g_new0 (GdkGLContextPaintData, 1); |
400 | priv->paint_data->is_legacy = priv->is_legacy; |
401 | priv->paint_data->use_es = priv->use_es; |
402 | } |
403 | |
404 | return priv->paint_data; |
405 | } |
406 | |
407 | gboolean |
408 | gdk_gl_context_use_texture_rectangle (GdkGLContext *context) |
409 | { |
410 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
411 | |
412 | return priv->use_texture_rectangle; |
413 | } |
414 | |
415 | gboolean |
416 | gdk_gl_context_has_framebuffer_blit (GdkGLContext *context) |
417 | { |
418 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
419 | |
420 | return priv->has_gl_framebuffer_blit; |
421 | } |
422 | |
423 | gboolean |
424 | gdk_gl_context_has_frame_terminator (GdkGLContext *context) |
425 | { |
426 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
427 | |
428 | return priv->has_frame_terminator; |
429 | } |
430 | |
431 | gboolean |
432 | gdk_gl_context_has_unpack_subimage (GdkGLContext *context) |
433 | { |
434 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
435 | |
436 | return priv->has_unpack_subimage; |
437 | } |
438 | |
439 | /** |
440 | * gdk_gl_context_set_debug_enabled: |
441 | * @context: a #GdkGLContext |
442 | * @enabled: whether to enable debugging in the context |
443 | * |
444 | * Sets whether the #GdkGLContext should perform extra validations and |
445 | * run time checking. This is useful during development, but has |
446 | * additional overhead. |
447 | * |
448 | * The #GdkGLContext must not be realized or made current prior to |
449 | * calling this function. |
450 | * |
451 | * Since: 3.16 |
452 | */ |
453 | void |
454 | gdk_gl_context_set_debug_enabled (GdkGLContext *context, |
455 | gboolean enabled) |
456 | { |
457 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
458 | |
459 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
460 | g_return_if_fail (!priv->realized); |
461 | |
462 | enabled = !!enabled; |
463 | |
464 | priv->debug_enabled = enabled; |
465 | } |
466 | |
467 | /** |
468 | * gdk_gl_context_get_debug_enabled: |
469 | * @context: a #GdkGLContext |
470 | * |
471 | * Retrieves the value set using gdk_gl_context_set_debug_enabled(). |
472 | * |
473 | * Returns: %TRUE if debugging is enabled |
474 | * |
475 | * Since: 3.16 |
476 | */ |
477 | gboolean |
478 | gdk_gl_context_get_debug_enabled (GdkGLContext *context) |
479 | { |
480 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
481 | |
482 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE); |
483 | |
484 | return priv->debug_enabled; |
485 | } |
486 | |
487 | /** |
488 | * gdk_gl_context_set_forward_compatible: |
489 | * @context: a #GdkGLContext |
490 | * @compatible: whether the context should be forward compatible |
491 | * |
492 | * Sets whether the #GdkGLContext should be forward compatible. |
493 | * |
494 | * Forward compatibile contexts must not support OpenGL functionality that |
495 | * has been marked as deprecated in the requested version; non-forward |
496 | * compatible contexts, on the other hand, must support both deprecated and |
497 | * non deprecated functionality. |
498 | * |
499 | * The #GdkGLContext must not be realized or made current prior to calling |
500 | * this function. |
501 | * |
502 | * Since: 3.16 |
503 | */ |
504 | void |
505 | gdk_gl_context_set_forward_compatible (GdkGLContext *context, |
506 | gboolean compatible) |
507 | { |
508 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
509 | |
510 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
511 | g_return_if_fail (!priv->realized); |
512 | |
513 | compatible = !!compatible; |
514 | |
515 | priv->forward_compatible = compatible; |
516 | } |
517 | |
518 | /** |
519 | * gdk_gl_context_get_forward_compatible: |
520 | * @context: a #GdkGLContext |
521 | * |
522 | * Retrieves the value set using gdk_gl_context_set_forward_compatible(). |
523 | * |
524 | * Returns: %TRUE if the context should be forward compatible |
525 | * |
526 | * Since: 3.16 |
527 | */ |
528 | gboolean |
529 | gdk_gl_context_get_forward_compatible (GdkGLContext *context) |
530 | { |
531 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
532 | |
533 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE); |
534 | |
535 | return priv->forward_compatible; |
536 | } |
537 | |
538 | /** |
539 | * gdk_gl_context_set_required_version: |
540 | * @context: a #GdkGLContext |
541 | * @major: the major version to request |
542 | * @minor: the minor version to request |
543 | * |
544 | * Sets the major and minor version of OpenGL to request. |
545 | * |
546 | * Setting @major and @minor to zero will use the default values. |
547 | * |
548 | * The #GdkGLContext must not be realized or made current prior to calling |
549 | * this function. |
550 | * |
551 | * Since: 3.16 |
552 | */ |
553 | void |
554 | gdk_gl_context_set_required_version (GdkGLContext *context, |
555 | int major, |
556 | int minor) |
557 | { |
558 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
559 | int version, min_ver; |
560 | |
561 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
562 | g_return_if_fail (!priv->realized); |
563 | |
564 | /* this will take care of the default */ |
565 | if (major == 0 && minor == 0) |
566 | { |
567 | priv->major = 0; |
568 | priv->minor = 0; |
569 | return; |
570 | } |
571 | |
572 | /* Enforce a minimum context version number of 3.2 */ |
573 | version = (major * 100) + minor; |
574 | |
575 | if (priv->use_es || (_gdk_gl_flags & GDK_GL_GLES) != 0) |
576 | min_ver = 200; |
577 | else |
578 | min_ver = 302; |
579 | |
580 | if (version < min_ver) |
581 | { |
582 | g_warning ("gdk_gl_context_set_required_version - GL context versions less than 3.2 are not supported." ); |
583 | version = min_ver; |
584 | } |
585 | priv->major = version / 100; |
586 | priv->minor = version % 100; |
587 | } |
588 | |
589 | /** |
590 | * gdk_gl_context_get_required_version: |
591 | * @context: a #GdkGLContext |
592 | * @major: (out) (nullable): return location for the major version to request |
593 | * @minor: (out) (nullable): return location for the minor version to request |
594 | * |
595 | * Retrieves the major and minor version requested by calling |
596 | * gdk_gl_context_set_required_version(). |
597 | * |
598 | * Since: 3.16 |
599 | */ |
600 | void |
601 | gdk_gl_context_get_required_version (GdkGLContext *context, |
602 | int *major, |
603 | int *minor) |
604 | { |
605 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
606 | int default_major, default_minor; |
607 | int maj, min; |
608 | |
609 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
610 | |
611 | if (priv->use_es || (_gdk_gl_flags & GDK_GL_GLES) != 0) |
612 | { |
613 | default_major = 2; |
614 | default_minor = 0; |
615 | } |
616 | else |
617 | { |
618 | default_major = 3; |
619 | default_minor = 2; |
620 | } |
621 | |
622 | if (priv->major > 0) |
623 | maj = priv->major; |
624 | else |
625 | maj = default_major; |
626 | |
627 | if (priv->minor > 0) |
628 | min = priv->minor; |
629 | else |
630 | min = default_minor; |
631 | |
632 | if (major != NULL) |
633 | *major = maj; |
634 | if (minor != NULL) |
635 | *minor = min; |
636 | } |
637 | |
638 | /** |
639 | * gdk_gl_context_is_legacy: |
640 | * @context: a #GdkGLContext |
641 | * |
642 | * Whether the #GdkGLContext is in legacy mode or not. |
643 | * |
644 | * The #GdkGLContext must be realized before calling this function. |
645 | * |
646 | * When realizing a GL context, GDK will try to use the OpenGL 3.2 core |
647 | * profile; this profile removes all the OpenGL API that was deprecated |
648 | * prior to the 3.2 version of the specification. If the realization is |
649 | * successful, this function will return %FALSE. |
650 | * |
651 | * If the underlying OpenGL implementation does not support core profiles, |
652 | * GDK will fall back to a pre-3.2 compatibility profile, and this function |
653 | * will return %TRUE. |
654 | * |
655 | * You can use the value returned by this function to decide which kind |
656 | * of OpenGL API to use, or whether to do extension discovery, or what |
657 | * kind of shader programs to load. |
658 | * |
659 | * Returns: %TRUE if the GL context is in legacy mode |
660 | * |
661 | * Since: 3.20 |
662 | */ |
663 | gboolean |
664 | gdk_gl_context_is_legacy (GdkGLContext *context) |
665 | { |
666 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
667 | |
668 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE); |
669 | g_return_val_if_fail (priv->realized, FALSE); |
670 | |
671 | return priv->is_legacy; |
672 | } |
673 | |
674 | void |
675 | gdk_gl_context_set_is_legacy (GdkGLContext *context, |
676 | gboolean is_legacy) |
677 | { |
678 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
679 | |
680 | priv->is_legacy = !!is_legacy; |
681 | } |
682 | |
683 | /** |
684 | * gdk_gl_context_set_use_es: |
685 | * @context: a #GdkGLContext: |
686 | * @use_es: whether the context should use OpenGL ES instead of OpenGL |
687 | * |
688 | * Requests that GDK create a OpenGL ES context instead of an OpenGL one, |
689 | * if the platform and windowing system allows it. |
690 | * |
691 | * The @context must not have been realized. |
692 | * |
693 | * You should check the return value of gdk_gl_context_get_use_es() after |
694 | * calling gdk_gl_context_realize() to decide whether to use the OpenGL or |
695 | * OpenGL ES API, extensions, or shaders. |
696 | * |
697 | * Since: 3.22 |
698 | */ |
699 | void |
700 | gdk_gl_context_set_use_es (GdkGLContext *context, |
701 | gboolean use_es) |
702 | { |
703 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
704 | |
705 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
706 | g_return_if_fail (!priv->realized); |
707 | |
708 | priv->use_es = !!use_es; |
709 | } |
710 | |
711 | /** |
712 | * gdk_gl_context_get_use_es: |
713 | * @context: a #GdkGLContext |
714 | * |
715 | * Checks whether the @context is using an OpenGL or OpenGL ES profile. |
716 | * |
717 | * Returns: %TRUE if the #GdkGLContext is using an OpenGL ES profile |
718 | * |
719 | * Since: 3.22 |
720 | */ |
721 | gboolean |
722 | gdk_gl_context_get_use_es (GdkGLContext *context) |
723 | { |
724 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
725 | |
726 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE); |
727 | |
728 | return priv->use_es; |
729 | } |
730 | |
731 | /** |
732 | * gdk_gl_context_realize: |
733 | * @context: a #GdkGLContext |
734 | * @error: return location for a #GError |
735 | * |
736 | * Realizes the given #GdkGLContext. |
737 | * |
738 | * It is safe to call this function on a realized #GdkGLContext. |
739 | * |
740 | * Returns: %TRUE if the context is realized |
741 | * |
742 | * Since: 3.16 |
743 | */ |
744 | gboolean |
745 | gdk_gl_context_realize (GdkGLContext *context, |
746 | GError **error) |
747 | { |
748 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
749 | |
750 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE); |
751 | |
752 | if (priv->realized) |
753 | return TRUE; |
754 | |
755 | priv->realized = GDK_GL_CONTEXT_GET_CLASS (context)->realize (context, error); |
756 | |
757 | return priv->realized; |
758 | } |
759 | |
760 | static void |
761 | gdk_gl_context_check_extensions (GdkGLContext *context) |
762 | { |
763 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
764 | gboolean has_npot, has_texture_rectangle; |
765 | |
766 | if (!priv->realized) |
767 | return; |
768 | |
769 | if (priv->extensions_checked) |
770 | return; |
771 | |
772 | priv->use_es = !epoxy_is_desktop_gl (); |
773 | priv->gl_version = epoxy_gl_version (); |
774 | |
775 | if (priv->use_es) |
776 | { |
777 | has_npot = priv->gl_version >= 20; |
778 | has_texture_rectangle = FALSE; |
779 | |
780 | /* This should check for GL_NV_framebuffer_blit - see extension at: |
781 | * |
782 | * https://www.khronos.org/registry/gles/extensions/NV/NV_framebuffer_blit.txt |
783 | */ |
784 | priv->has_gl_framebuffer_blit = FALSE; |
785 | |
786 | /* No OES version */ |
787 | priv->has_frame_terminator = FALSE; |
788 | |
789 | priv->has_unpack_subimage = epoxy_has_gl_extension ("GL_EXT_unpack_subimage" ); |
790 | } |
791 | else |
792 | { |
793 | has_npot = epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two" ); |
794 | has_texture_rectangle = epoxy_has_gl_extension ("GL_ARB_texture_rectangle" ); |
795 | |
796 | priv->has_gl_framebuffer_blit = epoxy_has_gl_extension ("GL_EXT_framebuffer_blit" ); |
797 | priv->has_frame_terminator = epoxy_has_gl_extension ("GL_GREMEDY_frame_terminator" ); |
798 | priv->has_unpack_subimage = TRUE; |
799 | } |
800 | |
801 | if (!priv->use_es && G_UNLIKELY (_gdk_gl_flags & GDK_GL_TEXTURE_RECTANGLE)) |
802 | priv->use_texture_rectangle = TRUE; |
803 | else if (has_npot) |
804 | priv->use_texture_rectangle = FALSE; |
805 | else if (has_texture_rectangle) |
806 | priv->use_texture_rectangle = TRUE; |
807 | else |
808 | g_warning ("GL implementation doesn't support any form of non-power-of-two textures" ); |
809 | |
810 | GDK_NOTE (OPENGL, |
811 | g_message ("%s version: %d.%d\n" |
812 | "* Extensions checked:\n" |
813 | " - GL_ARB_texture_non_power_of_two: %s\n" |
814 | " - GL_ARB_texture_rectangle: %s\n" |
815 | " - GL_EXT_framebuffer_blit: %s\n" |
816 | " - GL_GREMEDY_frame_terminator: %s\n" |
817 | "* Using texture rectangle: %s" , |
818 | priv->use_es ? "OpenGL ES" : "OpenGL" , |
819 | priv->gl_version / 10, priv->gl_version % 10, |
820 | has_npot ? "yes" : "no" , |
821 | has_texture_rectangle ? "yes" : "no" , |
822 | priv->has_gl_framebuffer_blit ? "yes" : "no" , |
823 | priv->has_frame_terminator ? "yes" : "no" , |
824 | priv->use_texture_rectangle ? "yes" : "no" )); |
825 | |
826 | priv->extensions_checked = TRUE; |
827 | } |
828 | |
829 | /** |
830 | * gdk_gl_context_make_current: |
831 | * @context: a #GdkGLContext |
832 | * |
833 | * Makes the @context the current one. |
834 | * |
835 | * Since: 3.16 |
836 | */ |
837 | void |
838 | gdk_gl_context_make_current (GdkGLContext *context) |
839 | { |
840 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
841 | GdkGLContext *current; |
842 | |
843 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
844 | |
845 | current = g_private_get (&thread_current_context); |
846 | if (current == context) |
847 | return; |
848 | |
849 | /* we need to realize the GdkGLContext if it wasn't explicitly realized */ |
850 | if (!priv->realized) |
851 | { |
852 | GError *error = NULL; |
853 | |
854 | gdk_gl_context_realize (context, &error); |
855 | if (error != NULL) |
856 | { |
857 | g_critical ("Could not realize the GL context: %s" , error->message); |
858 | g_error_free (error); |
859 | return; |
860 | } |
861 | } |
862 | |
863 | if (gdk_display_make_gl_context_current (priv->display, context)) |
864 | { |
865 | g_private_replace (&thread_current_context, g_object_ref (context)); |
866 | gdk_gl_context_check_extensions (context); |
867 | } |
868 | } |
869 | |
870 | /** |
871 | * gdk_gl_context_get_display: |
872 | * @context: a #GdkGLContext |
873 | * |
874 | * Retrieves the #GdkDisplay the @context is created for |
875 | * |
876 | * Returns: (transfer none): a #GdkDisplay or %NULL |
877 | * |
878 | * Since: 3.16 |
879 | */ |
880 | GdkDisplay * |
881 | gdk_gl_context_get_display (GdkGLContext *context) |
882 | { |
883 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
884 | |
885 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); |
886 | |
887 | return priv->display; |
888 | } |
889 | |
890 | /** |
891 | * gdk_gl_context_get_window: |
892 | * @context: a #GdkGLContext |
893 | * |
894 | * Retrieves the #GdkWindow used by the @context. |
895 | * |
896 | * Returns: (transfer none): a #GdkWindow or %NULL |
897 | * |
898 | * Since: 3.16 |
899 | */ |
900 | GdkWindow * |
901 | gdk_gl_context_get_window (GdkGLContext *context) |
902 | { |
903 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
904 | |
905 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); |
906 | |
907 | return priv->window; |
908 | } |
909 | |
910 | /** |
911 | * gdk_gl_context_get_shared_context: |
912 | * @context: a #GdkGLContext |
913 | * |
914 | * Retrieves the #GdkGLContext that this @context share data with. |
915 | * |
916 | * Returns: (transfer none): a #GdkGLContext or %NULL |
917 | * |
918 | * Since: 3.16 |
919 | */ |
920 | GdkGLContext * |
921 | gdk_gl_context_get_shared_context (GdkGLContext *context) |
922 | { |
923 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
924 | |
925 | g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); |
926 | |
927 | return priv->shared_context; |
928 | } |
929 | |
930 | /** |
931 | * gdk_gl_context_get_version: |
932 | * @context: a #GdkGLContext |
933 | * @major: (out): return location for the major version |
934 | * @minor: (out): return location for the minor version |
935 | * |
936 | * Retrieves the OpenGL version of the @context. |
937 | * |
938 | * The @context must be realized prior to calling this function. |
939 | * |
940 | * Since: 3.16 |
941 | */ |
942 | void |
943 | gdk_gl_context_get_version (GdkGLContext *context, |
944 | int *major, |
945 | int *minor) |
946 | { |
947 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); |
948 | |
949 | g_return_if_fail (GDK_IS_GL_CONTEXT (context)); |
950 | g_return_if_fail (priv->realized); |
951 | |
952 | if (major != NULL) |
953 | *major = priv->gl_version / 10; |
954 | if (minor != NULL) |
955 | *minor = priv->gl_version % 10; |
956 | } |
957 | |
958 | /** |
959 | * gdk_gl_context_clear_current: |
960 | * |
961 | * Clears the current #GdkGLContext. |
962 | * |
963 | * Any OpenGL call after this function returns will be ignored |
964 | * until gdk_gl_context_make_current() is called. |
965 | * |
966 | * Since: 3.16 |
967 | */ |
968 | void |
969 | gdk_gl_context_clear_current (void) |
970 | { |
971 | GdkGLContext *current; |
972 | |
973 | current = g_private_get (&thread_current_context); |
974 | if (current != NULL) |
975 | { |
976 | GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (current); |
977 | |
978 | if (gdk_display_make_gl_context_current (priv->display, NULL)) |
979 | g_private_replace (&thread_current_context, NULL); |
980 | } |
981 | } |
982 | |
983 | /** |
984 | * gdk_gl_context_get_current: |
985 | * |
986 | * Retrieves the current #GdkGLContext. |
987 | * |
988 | * Returns: (transfer none): the current #GdkGLContext, or %NULL |
989 | * |
990 | * Since: 3.16 |
991 | */ |
992 | GdkGLContext * |
993 | gdk_gl_context_get_current (void) |
994 | { |
995 | GdkGLContext *current; |
996 | |
997 | current = g_private_get (&thread_current_context); |
998 | |
999 | return current; |
1000 | } |
1001 | |
1002 | /** |
1003 | * gdk_gl_get_flags: |
1004 | * |
1005 | * Returns the currently active GL flags. |
1006 | * |
1007 | * Returns: the GL flags |
1008 | * |
1009 | * Since: 3.16 |
1010 | */ |
1011 | GdkGLFlags |
1012 | gdk_gl_get_flags (void) |
1013 | { |
1014 | return _gdk_gl_flags; |
1015 | } |
1016 | |
1017 | /** |
1018 | * gdk_gl_set_flags: |
1019 | * @flags: #GdkGLFlags to set |
1020 | * |
1021 | * Sets GL flags. |
1022 | * |
1023 | * Since: 3.16 |
1024 | */ |
1025 | void |
1026 | gdk_gl_set_flags (GdkGLFlags flags) |
1027 | { |
1028 | _gdk_gl_flags = flags; |
1029 | } |
1030 | |