]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_context_info.cpp
d5c607cd94eb0271cdf04e6304d5371c46207b3a
[vogl] / src / voglcommon / vogl_context_info.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: vogl_context_info.cpp
27 #include "vogl_context_info.h"
28 #include "vogl_entrypoints.h"
29
30 #include "vogl_console.h"
31
32 #include "btrace.h"
33
34 int vogl_determine_attrib_list_array_size(const int *attrib_list)
35 {
36     VOGL_FUNC_TRACER
37
38     if (!attrib_list)
39         return 0;
40
41     int n = 0;
42
43     if (attrib_list)
44     {
45         for (;;)
46         {
47             int key = attrib_list[n];
48             n++;
49             if (!key)
50                 break;
51
52             int val = attrib_list[n];
53             VOGL_NOTE_UNUSED(val);
54             n++;
55         }
56     }
57
58     return n;
59 }
60
61 vogl_context_attribs::vogl_context_attribs()
62 {
63     VOGL_FUNC_TRACER
64 }
65
66 vogl_context_attribs::vogl_context_attribs(const int *pAttribs)
67 {
68     VOGL_FUNC_TRACER
69
70     init(pAttribs);
71 }
72
73 vogl_context_attribs::vogl_context_attribs(const int *pAttribs, uint num_attribs)
74 {
75     VOGL_FUNC_TRACER
76
77     init(pAttribs, num_attribs);
78 }
79
80 vogl_context_attribs::vogl_context_attribs(const attrib_vec &vec)
81 {
82     VOGL_FUNC_TRACER
83
84     init(vec.get_ptr(), vec.size());
85 }
86
87 void vogl_context_attribs::init(const int *pAttribs)
88 {
89     VOGL_FUNC_TRACER
90
91     init(pAttribs, vogl_determine_attrib_list_array_size(pAttribs));
92 }
93
94 void vogl_context_attribs::init(const int *pAttribs, uint num_attribs)
95 {
96     VOGL_FUNC_TRACER
97
98     m_attribs.clear();
99
100     if ((pAttribs) && (num_attribs))
101     {
102         m_attribs.append(pAttribs, num_attribs);
103     }
104 }
105
106 void vogl_context_attribs::init(const attrib_vec &vec)
107 {
108     VOGL_FUNC_TRACER
109
110     init(vec.get_ptr(), vec.size());
111 }
112
113 int vogl_context_attribs::find_value_ofs(int key_to_find) const
114 {
115     VOGL_FUNC_TRACER
116
117     uint ofs = 0;
118     while (ofs < m_attribs.size())
119     {
120         int key = m_attribs[ofs];
121         if (!key)
122             break;
123
124         if (++ofs >= m_attribs.size())
125             break;
126
127         if (key == key_to_find)
128             return ofs;
129
130         ofs++;
131     }
132
133     return -1;
134 }
135
136 bool vogl_context_attribs::get_value(int key_to_find, int &val) const
137 {
138     VOGL_FUNC_TRACER
139
140     int ofs = find_value_ofs(key_to_find);
141     if (ofs < 0)
142     {
143         val = 0;
144         return false;
145     }
146
147     val = m_attribs[ofs];
148     return true;
149 }
150
151 bool vogl_context_attribs::check() const
152 {
153     VOGL_FUNC_TRACER
154
155     if (!m_attribs.size())
156         return true;
157
158     if (m_attribs.back() != 0)
159         return false;
160
161     uint ofs = 0;
162     do
163     {
164         int key = m_attribs[ofs];
165         if (!key)
166             return true;
167         ofs++;
168
169         if (ofs >= m_attribs.size())
170             return false;
171
172         ofs++;
173     } while (ofs < m_attribs.size());
174
175     return false;
176 }
177
178 bool vogl_context_attribs::serialize(vogl::json_node &node) const
179 {
180     VOGL_FUNC_TRACER
181
182     json_node &attribs_array = node.add_array("attribs");
183     for (uint i = 0; i < m_attribs.size(); i++)
184         attribs_array.add_value(m_attribs[i]);
185
186     return true;
187 }
188
189 bool vogl_context_attribs::deserialize(const vogl::json_node &node)
190 {
191     VOGL_FUNC_TRACER
192
193     const json_node *pAttribs_array = node.find_child_array("attribs");
194     if (!pAttribs_array)
195         return false;
196
197     m_attribs.resize(pAttribs_array->size());
198     for (uint i = 0; i < pAttribs_array->size(); i++)
199         m_attribs[i] = pAttribs_array->value_as_int(i);
200
201     return true;
202 }
203
204 void vogl_context_attribs::add_key(int key, int value)
205 {
206     VOGL_FUNC_TRACER
207
208     if (m_attribs.size())
209     {
210         VOGL_ASSERT(m_attribs.back() == 0);
211         if (m_attribs.back() == 0)
212             m_attribs.resize(m_attribs.size() - 1);
213     }
214
215     m_attribs.push_back(key);
216     m_attribs.push_back(value);
217     m_attribs.push_back(0);
218 }
219
220 vogl_context_info::vogl_context_info()
221     : m_version(VOGL_GL_VERSION_UNKNOWN),
222       m_forward_compatible(false),
223       m_core_profile(false),
224       m_debug_context(false),
225       m_max_vertex_attribs(0),
226       m_max_texture_coords(0),
227       m_max_texture_units(0),
228       m_max_texture_image_units(0),
229       m_max_combined_texture_coords(0),
230       m_max_draw_buffers(0),
231       m_max_lights(0),
232       m_max_uniform_buffer_bindings(0),
233       m_max_arb_program_matrices(0),
234       m_max_arb_vertex_program_env_params(0),
235       m_max_arb_fragment_program_env_params(0),
236       m_max_transform_feedback_separate_attribs(0),
237       m_is_valid(false)
238 {
239     VOGL_FUNC_TRACER
240 }
241
242 vogl_context_info::vogl_context_info(const vogl_context_desc &desc)
243     : m_version(VOGL_GL_VERSION_UNKNOWN),
244       m_forward_compatible(false),
245       m_core_profile(false),
246       m_debug_context(false),
247       m_max_vertex_attribs(0),
248       m_max_texture_coords(0),
249       m_max_texture_units(0),
250       m_max_texture_image_units(0),
251       m_max_combined_texture_coords(0),
252       m_max_draw_buffers(0),
253       m_max_lights(0),
254       m_max_uniform_buffer_bindings(0),
255       m_max_arb_program_matrices(0),
256       m_max_arb_vertex_program_env_params(0),
257       m_max_arb_fragment_program_env_params(0),
258       m_max_transform_feedback_separate_attribs(0),
259       m_is_valid(false)
260 {
261     VOGL_FUNC_TRACER
262
263     init(desc);
264 }
265
266 void vogl_context_info::query_string(vogl::dynamic_string &str, GLenum name)
267 {
268     VOGL_FUNC_TRACER
269
270     const char *pStr = reinterpret_cast<const char *>(GL_ENTRYPOINT(glGetString)(name));
271     str.set(pStr ? pStr : "");
272
273     vogl_check_gl_error();
274 }
275
276 vogl_gl_version_t vogl_context_info::parse_version_string(const vogl::dynamic_string &str)
277 {
278     VOGL_FUNC_TRACER
279
280     if (str.size() < 3)
281         return VOGL_GL_VERSION_UNKNOWN;
282
283     // major_number.minor_number major_number.minor_number.release_number
284     // release_number can be very large (tens of thousands)
285     // optionally followed by vendor/driver info
286
287     int major = 0, minor = 0;
288     uint n = sscanf(str.get_ptr(), "%d.%d", &major, &minor);
289     if ((n < 2) || (major < 1) || (minor < 0) || (minor > 255))
290     {
291         console::error("%s: Failed parsing GL/GLSL version string \"%s\"!\n", VOGL_METHOD_NAME, str.get_ptr());
292
293         return VOGL_GL_VERSION_UNKNOWN;
294     }
295
296     return VOGL_CREATE_GL_VERSION(major, minor, 0);
297 }
298
299 // See http://www.opengl.org/registry/specs/ARB/glx_create_context.txt
300 bool vogl_context_info::init(const vogl_context_desc &desc)
301 {
302     VOGL_FUNC_TRACER
303
304     clear();
305
306     if (!GL_ENTRYPOINT(glGetString))
307         return false;
308
309     m_desc = desc;
310
311     vogl_check_gl_error();
312
313     query_string(m_version_str, GL_VERSION);
314     query_string(m_glsl_version_str, GL_SHADING_LANGUAGE_VERSION);
315     query_string(m_vendor_str, GL_VENDOR);
316     query_string(m_renderer_str, GL_RENDERER);
317
318     // Update btrace with our gl info.
319     btrace_get_glinfo().version_str = m_version_str;
320     btrace_get_glinfo().glsl_version_str = m_glsl_version_str;
321     btrace_get_glinfo().vendor_str = m_vendor_str;
322     btrace_get_glinfo().renderer_str = m_renderer_str;
323
324     m_version = parse_version_string(m_version_str);
325     m_glsl_version = parse_version_string(m_glsl_version_str);
326
327     if (vogl_check_gl_error())
328     {
329         console::warning("%s: GL error querying context vendor/renderer/version strings!\n", VOGL_METHOD_NAME);
330     }
331
332     m_forward_compatible = false;
333     m_core_profile = false;
334     m_debug_context = false;
335
336     // glGetInteger is GL >= 3.0 (even if glGetIntegerv can still be non-null on lower version contexts)
337     if ((GL_ENTRYPOINT(glGetIntegerv)) && (m_version >= VOGL_GL_VERSION_3_0))
338     {
339         GLint major_vers = -1, minor_vers = -1;
340         GL_ENTRYPOINT(glGetIntegerv)(GL_MAJOR_VERSION, &major_vers);
341         GL_ENTRYPOINT(glGetIntegerv)(GL_MINOR_VERSION, &minor_vers);
342
343         if (!vogl_check_gl_error())
344         {
345             if ((major_vers > 0) && (minor_vers >= 0))
346             {
347                 vogl_gl_version_t context_version = VOGL_CREATE_GL_VERSION(major_vers, minor_vers, 0);
348                 VOGL_ASSERT(m_version == context_version);
349                 m_version = context_version;
350             }
351         }
352
353         GLint context_flags = 0;
354         GL_ENTRYPOINT(glGetIntegerv)(GL_CONTEXT_FLAGS, &context_flags);
355         if (!vogl_check_gl_error())
356         {
357             if (context_flags & GL_CONTEXT_FLAG_DEBUG_BIT)
358                 m_debug_context = true;
359
360             // forward compat is GL >= 3.0
361             if (context_flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
362                 m_forward_compatible = true;
363         }
364
365         // GL_CONTEXT_PROFILE_MASK is GL 3.2 or greater - (the core profile bit is ignored for context versions requested <v3.2)
366         if (m_version >= VOGL_GL_VERSION_3_2)
367         {
368             GLint profile_mask = 0;
369             GL_ENTRYPOINT(glGetIntegerv)(GL_CONTEXT_PROFILE_MASK, &profile_mask);
370
371             if (!vogl_check_gl_error())
372             {
373                 if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT)
374                 {
375                     VOGL_ASSERT((profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) == 0);
376                     m_core_profile = true;
377                 }
378             }
379         }
380     }
381
382     determine_core_extensions();
383
384     bool success = query_extensions();
385
386     m_is_valid = success && (m_version >= VOGL_GL_VERSION_1_0);
387
388     init_context_limits();
389
390     vogl_check_gl_error();
391
392     return m_is_valid;
393 }
394
395 void vogl_context_info::init_context_limits()
396 {
397     VOGL_FUNC_TRACER
398
399     VOGL_CHECK_GL_ERROR;
400
401     m_max_vertex_attribs = vogl_get_gl_integer(GL_MAX_VERTEX_ATTRIBS);
402     m_max_texture_coords = vogl_get_gl_integer(GL_MAX_TEXTURE_COORDS);
403     m_max_texture_units = vogl_get_gl_integer(GL_MAX_TEXTURE_UNITS);
404     m_max_texture_image_units = vogl_get_gl_integer(GL_MAX_TEXTURE_IMAGE_UNITS);
405     m_max_combined_texture_coords = vogl_get_gl_integer(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
406     m_max_draw_buffers = (get_version() >= VOGL_GL_VERSION_2_0) ? vogl_get_gl_integer(GL_MAX_DRAW_BUFFERS) : 0;
407     m_max_lights = is_core_profile() ? 0 : vogl_get_gl_integer(GL_MAX_LIGHTS);
408     m_max_uniform_buffer_bindings = vogl_get_gl_integer(GL_MAX_UNIFORM_BUFFER_BINDINGS);
409     m_max_transform_feedback_separate_attribs = (get_version() >= VOGL_GL_VERSION_3_0) ? vogl_get_gl_integer(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS) : 0;
410
411     VOGL_CHECK_GL_ERROR;
412
413     if (!is_core_profile() && supports_extension("GL_ARB_vertex_program") && GL_ENTRYPOINT(glGetProgramivARB))
414     {
415         m_max_arb_program_matrices = vogl_get_gl_integer(GL_MAX_PROGRAM_MATRICES_ARB);
416
417         GL_ENTRYPOINT(glGetProgramivARB)(GL_VERTEX_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, reinterpret_cast<GLint *>(&m_max_arb_vertex_program_env_params));
418         VOGL_CHECK_GL_ERROR;
419
420         GL_ENTRYPOINT(glGetProgramivARB)(GL_FRAGMENT_PROGRAM_ARB, GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, reinterpret_cast<GLint *>(&m_max_arb_fragment_program_env_params));
421         VOGL_CHECK_GL_ERROR;
422     }
423 }
424
425 bool vogl_context_info::query_extensions()
426 {
427     VOGL_FUNC_TRACER
428
429     // glGetStringi is available in OpenGL3.0 and newer
430     if (GL_ENTRYPOINT(glGetStringi))
431     {
432         GLint num_extensions = 0;
433         GL_ENTRYPOINT(glGetIntegerv)(GL_NUM_EXTENSIONS, &num_extensions);
434
435         m_extensions.reserve(num_extensions + 200);
436
437         for (int i = 0; i < num_extensions; i++)
438         {
439             const GLubyte *pGLExt = reinterpret_cast<const GLubyte *>(GL_ENTRYPOINT(glGetStringi)(GL_EXTENSIONS, i));
440             if (pGLExt)
441             {
442                 m_extensions.enlarge(1)->set(reinterpret_cast<const char *>(pGLExt));
443                 m_extensions.back().tolower();
444             }
445         }
446     }
447
448     // glGetString(GL_EXTENSIONS) is only supported in OpenGL3.0 and older.
449     // It was deprecrated in 3.1, so it is available in a compatibility profile (GL_ARB_compatibility).
450     if (get_version() <= VOGL_GL_VERSION_3_0 || is_compatibility_profile())
451     {
452         const char *pExtensions = reinterpret_cast<const char *>(GL_ENTRYPOINT(glGetString)(GL_EXTENSIONS));
453         if (pExtensions)
454         {
455             dynamic_string extensions(pExtensions);
456             dynamic_string_array tokens;
457             extensions.tokenize(" \t", tokens, true);
458             m_extensions.append(tokens);
459         }
460     }
461
462     if (GL_ENTRYPOINT(glXQueryExtensionsString))
463     {
464         const char *pExtensions = reinterpret_cast<const char *>(GL_ENTRYPOINT(glXQueryExtensionsString)(GL_ENTRYPOINT(glXGetCurrentDisplay)(), 0));
465         if (pExtensions)
466         {
467             dynamic_string extensions(pExtensions);
468             dynamic_string_array tokens;
469             extensions.tokenize(" \t", tokens, true);
470             m_extensions.append(tokens);
471         }
472     }
473
474     m_extensions.unique();
475
476     return true;
477 }
478
479 void vogl_context_info::clear()
480 {
481     VOGL_FUNC_TRACER
482
483     m_desc.clear();
484
485     m_version = VOGL_GL_VERSION_UNKNOWN;
486     m_glsl_version = VOGL_GL_VERSION_UNKNOWN;
487
488     m_forward_compatible = false;
489     m_core_profile = false;
490     m_debug_context = false;
491
492     m_version_str.clear();
493     m_glsl_version_str.clear();
494     m_vendor_str.clear();
495     m_renderer_str.clear();
496     m_extensions.clear();
497     m_core_extensions.clear();
498
499     m_max_vertex_attribs = 0;
500     m_max_texture_coords = 0;
501     m_max_texture_units = 0;
502     m_max_texture_image_units = 0;
503     m_max_combined_texture_coords = 0;
504     m_max_draw_buffers = 0;
505     m_max_lights = 0;
506     m_max_uniform_buffer_bindings = 0;
507     m_max_arb_program_matrices = 0;
508     m_max_arb_vertex_program_env_params = 0;
509     m_max_arb_fragment_program_env_params = 0;
510     m_max_transform_feedback_separate_attribs = 0;
511
512     m_is_valid = false;
513 }
514
515 // I've seen some engines do this, they must have a reason.
516 // Some newer drivers don't report back extensions added to core GL. So make a separate list that contains those extensions.
517 // TODO: I'm not so sure about this whole idea.
518 // TODO: Make sure all this makes sense in core profiles.
519 void vogl_context_info::determine_core_extensions()
520 {
521     VOGL_FUNC_TRACER
522
523 #define ADD_EXT(x)                       \
524     do                                   \
525     {                                    \
526         m_core_extensions.push_back(#x); \
527     } while (0)
528     if (m_version >= VOGL_GL_VERSION_1_2)
529     {
530         ADD_EXT(GL_EXT_texture3D);
531         ADD_EXT(GL_EXT_bgra);
532         ADD_EXT(GL_EXT_packed_pixels);
533         ADD_EXT(GL_EXT_rescale_normal);
534         ADD_EXT(GL_EXT_separate_specular_color);
535         ADD_EXT(GL_SGIS_texture_edge_clamp);
536         ADD_EXT(GL_SGIS_texture_lod);
537         ADD_EXT(GL_EXT_draw_range_elements);
538     }
539
540     if (m_version >= VOGL_GL_VERSION_1_3)
541     {
542         ADD_EXT(GL_ARB_texture_compression);
543         ADD_EXT(GL_ARB_texture_cube_map);
544         ADD_EXT(GL_ARB_multisample);
545         ADD_EXT(GL_ARB_multitexture);
546         ADD_EXT(GL_ARB_transpose_matrix);
547         ADD_EXT(GL_ARB_texture_env_add);
548         ADD_EXT(GL_ARB_texture_env_combine);
549         ADD_EXT(GL_ARB_texture_env_dot3);
550         ADD_EXT(GL_ARB_texture_border_clamp);
551     }
552
553     if (m_version >= VOGL_GL_VERSION_1_4)
554     {
555         ADD_EXT(GL_EXT_blend_color);
556         ADD_EXT(GL_EXT_blend_minmax);
557         ADD_EXT(GL_EXT_blend_subtract);
558         ADD_EXT(GL_SGIS_generate_mipmap);
559         ADD_EXT(GL_NV_blend_square);
560         ADD_EXT(GL_ARB_depth_texture);
561         ADD_EXT(GL_ARB_shadow);
562         ADD_EXT(GL_EXT_fog_coord);
563         ADD_EXT(GL_EXT_multi_draw_arrays);
564         ADD_EXT(GL_ARB_point_parameters);
565         ADD_EXT(GL_EXT_secondary_color);
566         ADD_EXT(GL_EXT_blend_func_separate);
567         ADD_EXT(GL_EXT_stencil_wrap);
568         ADD_EXT(GL_ARB_texture_env_crossbar);
569         ADD_EXT(GL_EXT_texture_lod_bias);
570         ADD_EXT(GL_ARB_texture_mirrored_repeat);
571         ADD_EXT(GL_ARB_window_pos);
572     }
573
574     if (m_version >= VOGL_GL_VERSION_1_5)
575     {
576         ADD_EXT(GL_ARB_vertex_buffer_object);
577         ADD_EXT(GL_ARB_occlusion_query);
578         ADD_EXT(GL_EXT_shadow_funcs);
579     }
580
581     if (m_version >= VOGL_GL_VERSION_2_0)
582     {
583         ADD_EXT(GL_ARB_shader_objects);
584         ADD_EXT(GL_ARB_vertex_shader);
585         ADD_EXT(GL_ARB_fragment_shader);
586         ADD_EXT(GL_ARB_shading_language_100);
587         ADD_EXT(GL_ARB_draw_buffers);
588         ADD_EXT(GL_ARB_texture_non_power_of_two);
589         ADD_EXT(GL_ARB_point_sprite);
590         ADD_EXT(GL_EXT_blend_equation_separate);
591     }
592
593     if (m_version >= VOGL_GL_VERSION_2_1)
594     {
595         ADD_EXT(GL_EXT_texture_sRGB);
596         ADD_EXT(GL_ARB_pixel_buffer_object);
597     }
598
599     if (m_version >= VOGL_GL_VERSION_3_0)
600     {
601         ADD_EXT(GL_EXT_framebuffer_sRGB);
602         ADD_EXT(GL_EXT_gpu_shader4);
603         ADD_EXT(GL_NV_conditional_render);
604         ADD_EXT(GL_ARB_color_buffer_float);
605         ADD_EXT(GL_ARB_depth_buffer_float);
606         ADD_EXT(GL_ARB_texture_float);
607         ADD_EXT(GL_EXT_packed_float);
608         ADD_EXT(GL_EXT_texture_shared_exponent);
609         ADD_EXT(GL_EXT_framebuffer_object);
610         ADD_EXT(GL_NV_half_float);
611         ADD_EXT(GL_EXT_texture_integer);
612         ADD_EXT(GL_EXT_texture_array);
613         ADD_EXT(GL_EXT_packed_depth_stencil);
614         ADD_EXT(GL_EXT_draw_buffers2);
615         ADD_EXT(GL_EXT_texture_compression_rgtc);
616         ADD_EXT(GL_EXT_transform_feedback);
617         ADD_EXT(GL_APPLE_vertex_array_object);
618         ADD_EXT(GL_ARB_half_float_pixel);
619         ADD_EXT(GL_EXT_framebuffer_multisample);
620         ADD_EXT(GL_EXT_framebuffer_blit);
621     }
622
623     if (m_version >= VOGL_GL_VERSION_3_2)
624     {
625         ADD_EXT(GL_ARB_sync);
626     }
627 #undef ADD_EXT
628
629     m_core_extensions.unique();
630 }
631
632 bool vogl_context_info::serialize(vogl::json_node &node, const vogl_blob_manager &blob_manager) const
633 {
634     VOGL_FUNC_TRACER
635
636     if (!m_is_valid)
637     {
638         VOGL_ASSERT_ALWAYS;
639         return false;
640     }
641
642     if (!m_desc.serialize(node, blob_manager))
643         return false;
644
645     node.add_key_value("version_major", VOGL_GL_VERSION_GET_MAJOR(m_version));
646     node.add_key_value("version_minor", VOGL_GL_VERSION_GET_MINOR(m_version));
647     node.add_key_value("version_patch", VOGL_GL_VERSION_GET_PATCH(m_version));
648
649     node.add_key_value("glsl_version_major", VOGL_GL_VERSION_GET_MAJOR(m_glsl_version));
650     node.add_key_value("glsl_version_minor", VOGL_GL_VERSION_GET_MINOR(m_glsl_version));
651
652     node.add_key_value("forward_compatible", m_forward_compatible);
653     node.add_key_value("core_profile", m_core_profile);
654     node.add_key_value("debug_context", m_debug_context);
655
656     node.add_key_value("version_string", m_version_str);
657     node.add_key_value("glsl_version_string", m_glsl_version_str);
658     node.add_key_value("vendor_string", m_vendor_str);
659     node.add_key_value("renderer_string", m_renderer_str);
660
661     node.add_key_value("max_vertex_attribs", m_max_vertex_attribs);
662     node.add_key_value("max_texture_coords", m_max_texture_coords);
663     node.add_key_value("max_texture_units", m_max_texture_units);
664     node.add_key_value("max_texture_image_units", m_max_texture_image_units);
665     node.add_key_value("max_combined_texture_coords", m_max_combined_texture_coords);
666     node.add_key_value("max_draw_buffers", m_max_draw_buffers);
667     node.add_key_value("max_lights", m_max_lights);
668     node.add_key_value("max_uniform_buffer_bindings", m_max_uniform_buffer_bindings);
669     node.add_key_value("max_arb_program_matrices", m_max_arb_program_matrices);
670     node.add_key_value("max_arb_vertex_program_env_params", m_max_arb_vertex_program_env_params);
671     node.add_key_value("max_arb_fragment_program_env_params", m_max_arb_fragment_program_env_params);
672     node.add_key_value("max_transform_feedback_separate_attribs", m_max_transform_feedback_separate_attribs);
673
674     json_node &extensions_array = node.add_array("extensions");
675     for (uint i = 0; i < m_extensions.size(); i++)
676         extensions_array.add_value(m_extensions[i]);
677
678     return true;
679 }
680
681 bool vogl_context_info::deserialize(const vogl::json_node &node, const vogl_blob_manager &blob_manager)
682 {
683     VOGL_FUNC_TRACER
684
685     clear();
686
687     if (!node.is_object())
688         return false;
689
690     bool success = node.get_value_as_string("version_string", m_version_str);
691
692     if (!m_desc.deserialize(node, blob_manager))
693         return false;
694
695     int version_major = 0, version_minor = 0, version_patch = 0;
696     version_major = node.value_as_int("version_major");
697     version_minor = node.value_as_int("version_minor");
698     version_patch = node.value_as_int("version_patch");
699     m_version = VOGL_CREATE_GL_VERSION(version_major, version_minor, version_patch);
700
701     int glsl_version_major = 0, glsl_version_minor = 0;
702     glsl_version_major = node.value_as_int("glsl_version_major");
703     glsl_version_minor = node.value_as_int("glsl_version_minor");
704     m_glsl_version = VOGL_CREATE_GL_VERSION(glsl_version_major, glsl_version_minor, 0);
705
706     m_forward_compatible = node.value_as_bool("forward_compatible");
707     m_core_profile = node.value_as_bool("core_profile");
708     m_debug_context = node.value_as_bool("debug_context");
709     m_glsl_version_str = node.value_as_string("glsl_version_string");
710     m_vendor_str = node.value_as_string("vendor_string");
711     m_renderer_str = node.value_as_string("renderer_string");
712
713     m_max_vertex_attribs = node.value_as_uint32("max_vertex_attribs");
714     m_max_texture_coords = node.value_as_uint32("max_texture_coords");
715     m_max_texture_units = node.value_as_uint32("max_texture_units");
716     m_max_texture_image_units = node.value_as_uint32("max_texture_image_units");
717     m_max_combined_texture_coords = node.value_as_uint32("max_combined_texture_coords");
718     m_max_draw_buffers = node.value_as_uint32("max_draw_buffers");
719     m_max_lights = node.value_as_uint32("max_lights");
720     m_max_uniform_buffer_bindings = node.value_as_uint32("max_uniform_buffer_bindings");
721     m_max_arb_program_matrices = node.value_as_uint32("max_arb_program_matrices");
722     m_max_arb_vertex_program_env_params = node.value_as_uint32("max_arb_vertex_program_env_params");
723     m_max_arb_fragment_program_env_params = node.value_as_uint32("max_arb_fragment_program_env_params");
724     m_max_transform_feedback_separate_attribs = node.value_as_uint32("max_transform_feedback_separate_attribs");
725
726     const json_node *pExtensions_array = node.find_child_array("extensions");
727     if (pExtensions_array)
728     {
729         m_extensions.resize(pExtensions_array->size());
730
731         for (uint i = 0; i < pExtensions_array->size(); i++)
732             m_extensions[i] = pExtensions_array->get_value(i).as_string();
733
734         m_extensions.unique();
735     }
736
737     determine_core_extensions();
738
739     VOGL_ASSERT(success);
740
741     m_is_valid = success;
742
743     return success;
744 }
745
746 bool vogl_context_info::supports_extension(const char *pExt) const
747 {
748     VOGL_FUNC_TRACER
749
750     if (!m_is_valid)
751     {
752         VOGL_ASSERT_ALWAYS;
753         return false;
754     }
755
756     vogl::dynamic_string ext(pExt);
757
758     if (!ext.begins_with("GL_", true))
759     {
760         vogl_warning_printf("%s: Extension does not begin with \"GL_\" prefix, was this intended?\n", VOGL_METHOD_NAME);
761     }
762
763 #if 0
764         // Let's see if this is ever necessary. Also not sure how it interacts with core profiles, yet.
765         if (m_core_extensions.find_sorted(ext) >= 0)
766                 return true;
767 #endif
768
769     return m_extensions.find_sorted(ext) >= 0;
770 }