1/*
2 * Copyright (C) 1999-2014  Brian Paul   All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22#include <assert.h>
23#include <ctype.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include "glinfo_common.h"
28
29#ifdef _WIN32
30#define snprintf _snprintf
31#endif
32
33#ifndef GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
34#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001
35#endif
36#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
37#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
38#endif
39#ifndef GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB
40#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004
41#endif
42#ifndef GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR
43#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008
44#endif
45
46/**
47 * Return the GL enum name for a numeric value.
48 * We really only care about the compressed texture formats for now.
49 */
50static const char *
51enum_name(GLenum val)
52{
53   static const struct {
54      const char *name;
55      GLenum val;
56   } enums [] = {
57      { "GL_COMPRESSED_ALPHA", 0x84E9 },
58      { "GL_COMPRESSED_LUMINANCE", 0x84EA },
59      { "GL_COMPRESSED_LUMINANCE_ALPHA", 0x84EB },
60      { "GL_COMPRESSED_INTENSITY", 0x84EC },
61      { "GL_COMPRESSED_RGB", 0x84ED },
62      { "GL_COMPRESSED_RGBA", 0x84EE },
63      { "GL_COMPRESSED_TEXTURE_FORMATS", 0x86A3 },
64      { "GL_COMPRESSED_RGB", 0x84ED },
65      { "GL_COMPRESSED_RGBA", 0x84EE },
66      { "GL_COMPRESSED_TEXTURE_FORMATS", 0x86A3 },
67      { "GL_COMPRESSED_ALPHA", 0x84E9 },
68      { "GL_COMPRESSED_LUMINANCE", 0x84EA },
69      { "GL_COMPRESSED_LUMINANCE_ALPHA", 0x84EB },
70      { "GL_COMPRESSED_INTENSITY", 0x84EC },
71      { "GL_COMPRESSED_SRGB", 0x8C48 },
72      { "GL_COMPRESSED_SRGB_ALPHA", 0x8C49 },
73      { "GL_COMPRESSED_SLUMINANCE", 0x8C4A },
74      { "GL_COMPRESSED_SLUMINANCE_ALPHA", 0x8C4B },
75      { "GL_COMPRESSED_RED", 0x8225 },
76      { "GL_COMPRESSED_RG", 0x8226 },
77      { "GL_COMPRESSED_RED_RGTC1", 0x8DBB },
78      { "GL_COMPRESSED_SIGNED_RED_RGTC1", 0x8DBC },
79      { "GL_COMPRESSED_RG_RGTC2", 0x8DBD },
80      { "GL_COMPRESSED_SIGNED_RG_RGTC2", 0x8DBE },
81      { "GL_COMPRESSED_RGB8_ETC2", 0x9274 },
82      { "GL_COMPRESSED_SRGB8_ETC2", 0x9275 },
83      { "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", 0x9276 },
84      { "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", 0x9277 },
85      { "GL_COMPRESSED_RGBA8_ETC2_EAC", 0x9278 },
86      { "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", 0x9279 },
87      { "GL_COMPRESSED_R11_EAC", 0x9270 },
88      { "GL_COMPRESSED_SIGNED_R11_EAC", 0x9271 },
89      { "GL_COMPRESSED_RG11_EAC", 0x9272 },
90      { "GL_COMPRESSED_SIGNED_RG11_EAC", 0x9273 },
91      { "GL_COMPRESSED_ALPHA_ARB", 0x84E9 },
92      { "GL_COMPRESSED_LUMINANCE_ARB", 0x84EA },
93      { "GL_COMPRESSED_LUMINANCE_ALPHA_ARB", 0x84EB },
94      { "GL_COMPRESSED_INTENSITY_ARB", 0x84EC },
95      { "GL_COMPRESSED_RGB_ARB", 0x84ED },
96      { "GL_COMPRESSED_RGBA_ARB", 0x84EE },
97      { "GL_COMPRESSED_TEXTURE_FORMATS_ARB", 0x86A3 },
98      { "GL_COMPRESSED_RGBA_BPTC_UNORM_ARB", 0x8E8C },
99      { "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB", 0x8E8D },
100      { "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB", 0x8E8E },
101      { "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB", 0x8E8F },
102      { "GL_COMPRESSED_RGBA_ASTC_4x4_KHR", 0x93B0 },
103      { "GL_COMPRESSED_RGBA_ASTC_5x4_KHR", 0x93B1 },
104      { "GL_COMPRESSED_RGBA_ASTC_5x5_KHR", 0x93B2 },
105      { "GL_COMPRESSED_RGBA_ASTC_6x5_KHR", 0x93B3 },
106      { "GL_COMPRESSED_RGBA_ASTC_6x6_KHR", 0x93B4 },
107      { "GL_COMPRESSED_RGBA_ASTC_8x5_KHR", 0x93B5 },
108      { "GL_COMPRESSED_RGBA_ASTC_8x6_KHR", 0x93B6 },
109      { "GL_COMPRESSED_RGBA_ASTC_8x8_KHR", 0x93B7 },
110      { "GL_COMPRESSED_RGBA_ASTC_10x5_KHR", 0x93B8 },
111      { "GL_COMPRESSED_RGBA_ASTC_10x6_KHR", 0x93B9 },
112      { "GL_COMPRESSED_RGBA_ASTC_10x8_KHR", 0x93BA },
113      { "GL_COMPRESSED_RGBA_ASTC_10x10_KHR", 0x93BB },
114      { "GL_COMPRESSED_RGBA_ASTC_12x10_KHR", 0x93BC },
115      { "GL_COMPRESSED_RGBA_ASTC_12x12_KHR", 0x93BD },
116      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR", 0x93D0 },
117      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR", 0x93D1 },
118      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR", 0x93D2 },
119      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR", 0x93D3 },
120      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR", 0x93D4 },
121      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR", 0x93D5 },
122      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR", 0x93D6 },
123      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR", 0x93D7 },
124      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR", 0x93D8 },
125      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR", 0x93D9 },
126      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR", 0x93DA },
127      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR", 0x93DB },
128      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR", 0x93DC },
129      { "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR", 0x93DD },
130      { "GL_COMPRESSED_RGB_FXT1_3DFX", 0x86B0 },
131      { "GL_COMPRESSED_RGBA_FXT1_3DFX", 0x86B1 },
132      { "GL_COMPRESSED_LUMINANCE_LATC1_EXT", 0x8C70 },
133      { "GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT", 0x8C71 },
134      { "GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT", 0x8C72 },
135      { "GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT", 0x8C73 },
136      { "GL_COMPRESSED_RED_RGTC1_EXT", 0x8DBB },
137      { "GL_COMPRESSED_SIGNED_RED_RGTC1_EXT", 0x8DBC },
138      { "GL_COMPRESSED_RED_GREEN_RGTC2_EXT", 0x8DBD },
139      { "GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT", 0x8DBE },
140      { "GL_COMPRESSED_RGB_S3TC_DXT1_EXT", 0x83F0 },
141      { "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT", 0x83F1 },
142      { "GL_COMPRESSED_RGBA_S3TC_DXT3_EXT", 0x83F2 },
143      { "GL_COMPRESSED_RGBA_S3TC_DXT5_EXT", 0x83F3 },
144      { "GL_COMPRESSED_SRGB_EXT", 0x8C48 },
145      { "GL_COMPRESSED_SRGB_ALPHA_EXT", 0x8C49 },
146      { "GL_COMPRESSED_SLUMINANCE_EXT", 0x8C4A },
147      { "GL_COMPRESSED_SLUMINANCE_ALPHA_EXT", 0x8C4B },
148      { "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT", 0x8C4C },
149      { "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT", 0x8C4D },
150      { "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT", 0x8C4E },
151      { "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT", 0x8C4F },
152      { "GL_PALETTE4_RGB8_OES", 0x8B90 },
153      { "GL_PALETTE4_RGBA8_OES", 0x8B91 },
154      { "GL_PALETTE4_R5_G6_B5_OES", 0x8B92 },
155      { "GL_PALETTE4_RGBA4_OES", 0x8B93 },
156      { "GL_PALETTE4_RGB5_A1_OES", 0x8B94 },
157      { "GL_PALETTE8_RGB8_OES", 0x8B95 },
158      { "GL_PALETTE8_RGBA8_OES", 0x8B96 },
159      { "GL_PALETTE8_R5_G6_B5_OES", 0x8B97 },
160      { "GL_PALETTE8_RGBA4_OES", 0x8B98 },
161      { "GL_PALETTE8_RGB5_A1_OES", 0x8B99 }
162   };
163   const int n = sizeof(enums) / sizeof(enums[0]);
164   static char buffer[100];
165   int i;
166   for (i = 0; i < n; i++) {
167      if (enums[i].val == val) {
168         return enums[i].name;
169      }
170   }
171   /* enum val not found, just print hexadecimal value into static buffer */
172   snprintf(buffer, sizeof(buffer), "0x%x", val);
173   return buffer;
174}
175
176
177/*
178 * qsort callback for string comparison.
179 */
180static int
181compare_string_ptr(const void *p1, const void *p2)
182{
183   return strcmp(* (char * const *) p1, * (char * const *) p2);
184}
185
186/*
187 * Print a list of extensions, with word-wrapping.
188 */
189void
190print_extension_list(const char *ext, GLboolean singleLine)
191{
192   char **extensions;
193   int num_extensions;
194   const char *indentString = "    ";
195   const int indent = 4;
196   const int max = 79;
197   int width, i, j, k;
198
199   if (!ext || !ext[0])
200      return;
201
202   /* count the number of extensions, ignoring successive spaces */
203   num_extensions = 0;
204   j = 1;
205   do {
206      if ((ext[j] == ' ' || ext[j] == 0) && ext[j - 1] != ' ') {
207         ++num_extensions;
208      }
209   } while(ext[j++]);
210
211   /* copy individual extensions to an array */
212   extensions = malloc(num_extensions * sizeof *extensions);
213   if (!extensions) {
214      fprintf(stderr, "Error: malloc() failed\n");
215      exit(1);
216   }
217   i = j = k = 0;
218   while (1) {
219      if (ext[j] == ' ' || ext[j] == 0) {
220         /* found end of an extension name */
221         const int len = j - i;
222
223         if (len) {
224            assert(k < num_extensions);
225
226            extensions[k] = malloc(len + 1);
227            if (!extensions[k]) {
228               fprintf(stderr, "Error: malloc() failed\n");
229               exit(1);
230            }
231
232            memcpy(extensions[k], ext + i, len);
233            extensions[k][len] = 0;
234
235            ++k;
236         };
237
238         i += len + 1;
239
240         if (ext[j] == 0) {
241            break;
242         }
243      }
244      j++;
245   }
246   assert(k == num_extensions);
247
248   /* sort extensions alphabetically */
249   qsort(extensions, num_extensions, sizeof extensions[0], compare_string_ptr);
250
251   /* print the extensions */
252   width = indent;
253   printf("%s", indentString);
254   for (k = 0; k < num_extensions; ++k) {
255      const int len = strlen(extensions[k]);
256      if ((!singleLine) && (width + len > max)) {
257         /* start a new line */
258         printf("\n");
259         width = indent;
260         printf("%s", indentString);
261      }
262      /* print the extension name */
263      printf("%s", extensions[k]);
264
265      /* either we're all done, or we'll continue with next extension */
266      width += len + 1;
267
268      if (singleLine) {
269         printf("\n");
270         width = indent;
271         printf("%s", indentString);
272      }
273      else if (k < (num_extensions -1)) {
274         printf(", ");
275         width += 2;
276      }
277   }
278   printf("\n");
279
280   for (k = 0; k < num_extensions; ++k) {
281      free(extensions[k]);
282   }
283   free(extensions);
284}
285
286
287
288
289/**
290 * Get list of OpenGL extensions using core profile's glGetStringi().
291 */
292char *
293build_core_profile_extension_list(const struct ext_functions *extfuncs)
294{
295   GLint i, n, totalLen;
296   char *buffer;
297
298   glGetIntegerv(GL_NUM_EXTENSIONS, &n);
299
300   /* compute totalLen */
301   totalLen = 0;
302   for (i = 0; i < n; i++) {
303      const char *ext = (const char *) extfuncs->GetStringi(GL_EXTENSIONS, i);
304      if (ext)
305          totalLen += strlen(ext) + 1; /* plus a space */
306   }
307
308   if (!totalLen)
309     return NULL;
310
311   buffer = malloc(totalLen + 1);
312   if (buffer) {
313      int pos = 0;
314      for (i = 0; i < n; i++) {
315         const char *ext = (const char *) extfuncs->GetStringi(GL_EXTENSIONS, i);
316         strcpy(buffer + pos, ext);
317         pos += strlen(ext);
318         buffer[pos++] = ' ';
319      }
320      buffer[pos] = '\0';
321   }
322   return buffer;
323}
324
325
326/** Is extension 'ext' supported? */
327GLboolean
328extension_supported(const char *ext, const char *extensionsList)
329{
330   while (1) {
331      const char *p = strstr(extensionsList, ext);
332      if (p) {
333         /* check that next char is a space or end of string */
334         int extLen = strlen(ext);
335         if (p[extLen] == 0 || p[extLen] == ' ') {
336            return 1;
337         }
338         else {
339            /* We found a superset string, keep looking */
340            extensionsList += extLen;
341         }
342      }
343      else {
344         break;
345      }
346   }
347   return 0;
348}
349
350
351/**
352 * Is verNum >= verString?
353 * \param verString  such as "2.1", "3.0", etc.
354 * \param verNum  such as 20, 21, 30, 31, 32, etc.
355 */
356static GLboolean
357version_supported(const char *verString, int verNum)
358{
359   int v;
360
361   if (!verString ||
362       !isdigit(verString[0]) ||
363       verString[1] != '.' ||
364       !isdigit(verString[2])) {
365      return GL_FALSE;
366   }
367
368   v = (verString[0] - '0') * 10 + (verString[2] - '0');
369
370   return verNum >= v;
371}
372
373
374struct token_name
375{
376   GLenum token;
377   const char *name;
378};
379
380
381static void
382print_shader_limit_list(const struct token_name *lim)
383{
384   GLint max[1];
385   unsigned i;
386
387   for (i = 0; lim[i].token; i++) {
388      glGetIntegerv(lim[i].token, max);
389      if (glGetError() == GL_NO_ERROR) {
390	 printf("        %s = %d\n", lim[i].name, max[0]);
391      }
392   }
393}
394
395
396/**
397 * Print interesting limits for vertex/fragment shaders.
398 */
399static void
400print_shader_limits(GLenum target)
401{
402   static const struct token_name vertex_limits[] = {
403      { GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, "GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB" },
404      { GL_MAX_VARYING_FLOATS_ARB, "GL_MAX_VARYING_FLOATS_ARB" },
405      { GL_MAX_VERTEX_ATTRIBS_ARB, "GL_MAX_VERTEX_ATTRIBS_ARB" },
406      { GL_MAX_TEXTURE_IMAGE_UNITS_ARB, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" },
407      { GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB" },
408      { GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB" },
409      { GL_MAX_TEXTURE_COORDS_ARB, "GL_MAX_TEXTURE_COORDS_ARB" },
410      { GL_MAX_VERTEX_OUTPUT_COMPONENTS  , "GL_MAX_VERTEX_OUTPUT_COMPONENTS  " },
411      { (GLenum) 0, NULL }
412   };
413   static const struct token_name fragment_limits[] = {
414      { GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB" },
415      { GL_MAX_TEXTURE_COORDS_ARB, "GL_MAX_TEXTURE_COORDS_ARB" },
416      { GL_MAX_TEXTURE_IMAGE_UNITS_ARB, "GL_MAX_TEXTURE_IMAGE_UNITS_ARB" },
417      { GL_MAX_FRAGMENT_INPUT_COMPONENTS , "GL_MAX_FRAGMENT_INPUT_COMPONENTS " },
418      { (GLenum) 0, NULL }
419   };
420   static const struct token_name geometry_limits[] = {
421      { GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS" },
422      { GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS" },
423      { GL_MAX_GEOMETRY_OUTPUT_VERTICES  , "GL_MAX_GEOMETRY_OUTPUT_VERTICES  " },
424      { GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS" },
425      { GL_MAX_GEOMETRY_INPUT_COMPONENTS , "GL_MAX_GEOMETRY_INPUT_COMPONENTS " },
426      { GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS" },
427      { (GLenum) 0, NULL }
428   };
429
430   switch (target) {
431   case GL_VERTEX_SHADER:
432      printf("    GL_VERTEX_SHADER_ARB:\n");
433      print_shader_limit_list(vertex_limits);
434      break;
435
436   case GL_FRAGMENT_SHADER:
437      printf("    GL_FRAGMENT_SHADER_ARB:\n");
438      print_shader_limit_list(fragment_limits);
439      break;
440
441   case GL_GEOMETRY_SHADER:
442      printf("    GL_GEOMETRY_SHADER:\n");
443      print_shader_limit_list(geometry_limits);
444      break;
445   }
446}
447
448
449/**
450 * Print interesting limits for vertex/fragment programs.
451 */
452static void
453print_program_limits(GLenum target,
454                     const struct ext_functions *extfuncs)
455{
456#if defined(GL_ARB_vertex_program) || defined(GL_ARB_fragment_program)
457   struct token_name {
458      GLenum token;
459      const char *name;
460   };
461   static const struct token_name common_limits[] = {
462      { GL_MAX_PROGRAM_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_INSTRUCTIONS_ARB" },
463      { GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB" },
464      { GL_MAX_PROGRAM_TEMPORARIES_ARB, "GL_MAX_PROGRAM_TEMPORARIES_ARB" },
465      { GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, "GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB" },
466      { GL_MAX_PROGRAM_PARAMETERS_ARB, "GL_MAX_PROGRAM_PARAMETERS_ARB" },
467      { GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB, "GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB" },
468      { GL_MAX_PROGRAM_ATTRIBS_ARB, "GL_MAX_PROGRAM_ATTRIBS_ARB" },
469      { GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB, "GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB" },
470      { GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, "GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB" },
471      { GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB, "GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB" },
472      { GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB, "GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB" },
473      { GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, "GL_MAX_PROGRAM_ENV_PARAMETERS_ARB" },
474      { (GLenum) 0, NULL }
475   };
476   static const struct token_name fragment_limits[] = {
477      { GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB" },
478      { GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB" },
479      { GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB, "GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB" },
480      { GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB" },
481      { GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB, "GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB" },
482      { GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB, "GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB" },
483      { (GLenum) 0, NULL }
484   };
485
486   GLint max[1];
487   int i;
488
489   if (target == GL_VERTEX_PROGRAM_ARB) {
490      printf("    GL_VERTEX_PROGRAM_ARB:\n");
491   }
492   else if (target == GL_FRAGMENT_PROGRAM_ARB) {
493      printf("    GL_FRAGMENT_PROGRAM_ARB:\n");
494   }
495   else {
496      return; /* something's wrong */
497   }
498
499   for (i = 0; common_limits[i].token; i++) {
500      extfuncs->GetProgramivARB(target, common_limits[i].token, max);
501      if (glGetError() == GL_NO_ERROR) {
502         printf("        %s = %d\n", common_limits[i].name, max[0]);
503      }
504   }
505   if (target == GL_FRAGMENT_PROGRAM_ARB) {
506      for (i = 0; fragment_limits[i].token; i++) {
507         extfuncs->GetProgramivARB(target, fragment_limits[i].token, max);
508         if (glGetError() == GL_NO_ERROR) {
509            printf("        %s = %d\n", fragment_limits[i].name, max[0]);
510         }
511      }
512   }
513#endif /* GL_ARB_vertex_program / GL_ARB_fragment_program */
514}
515
516
517/**
518 * Print interesting OpenGL implementation limits.
519 * \param version  20, 21, 30, 31, 32, etc.
520 */
521void
522print_limits(const char *extensions, const char *oglstring, int version,
523             const struct ext_functions *extfuncs)
524{
525   struct token_name {
526      GLuint count;
527      GLenum token;
528      const char *name;
529      const char *extension; /* NULL or GL extension name or version string */
530   };
531   static const struct token_name limits[] = {
532      { 1, GL_MAX_ATTRIB_STACK_DEPTH, "GL_MAX_ATTRIB_STACK_DEPTH", NULL },
533      { 1, GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, "GL_MAX_CLIENT_ATTRIB_STACK_DEPTH", NULL },
534      { 1, GL_MAX_CLIP_PLANES, "GL_MAX_CLIP_PLANES", NULL },
535      { 1, GL_MAX_COLOR_MATRIX_STACK_DEPTH, "GL_MAX_COLOR_MATRIX_STACK_DEPTH", "GL_ARB_imaging" },
536      { 1, GL_MAX_ELEMENTS_VERTICES, "GL_MAX_ELEMENTS_VERTICES", NULL },
537      { 1, GL_MAX_ELEMENTS_INDICES, "GL_MAX_ELEMENTS_INDICES", NULL },
538      { 1, GL_MAX_EVAL_ORDER, "GL_MAX_EVAL_ORDER", NULL },
539      { 1, GL_MAX_LIGHTS, "GL_MAX_LIGHTS", NULL },
540      { 1, GL_MAX_LIST_NESTING, "GL_MAX_LIST_NESTING", NULL },
541      { 1, GL_MAX_MODELVIEW_STACK_DEPTH, "GL_MAX_MODELVIEW_STACK_DEPTH", NULL },
542      { 1, GL_MAX_NAME_STACK_DEPTH, "GL_MAX_NAME_STACK_DEPTH", NULL },
543      { 1, GL_MAX_PIXEL_MAP_TABLE, "GL_MAX_PIXEL_MAP_TABLE", NULL },
544      { 1, GL_MAX_PROJECTION_STACK_DEPTH, "GL_MAX_PROJECTION_STACK_DEPTH", NULL },
545      { 1, GL_MAX_TEXTURE_STACK_DEPTH, "GL_MAX_TEXTURE_STACK_DEPTH", NULL },
546      { 1, GL_MAX_TEXTURE_SIZE, "GL_MAX_TEXTURE_SIZE", NULL },
547      { 1, GL_MAX_3D_TEXTURE_SIZE, "GL_MAX_3D_TEXTURE_SIZE", NULL },
548#if defined(GL_EXT_texture_array)
549      { 1, GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, "GL_MAX_ARRAY_TEXTURE_LAYERS", "GL_EXT_texture_array" },
550#endif
551      { 2, GL_MAX_VIEWPORT_DIMS, "GL_MAX_VIEWPORT_DIMS", NULL },
552      { 2, GL_ALIASED_LINE_WIDTH_RANGE, "GL_ALIASED_LINE_WIDTH_RANGE", NULL },
553      { 2, GL_SMOOTH_LINE_WIDTH_RANGE, "GL_SMOOTH_LINE_WIDTH_RANGE", NULL },
554      { 2, GL_ALIASED_POINT_SIZE_RANGE, "GL_ALIASED_POINT_SIZE_RANGE", NULL },
555      { 2, GL_SMOOTH_POINT_SIZE_RANGE, "GL_SMOOTH_POINT_SIZE_RANGE", NULL },
556#if defined(GL_ARB_texture_cube_map)
557      { 1, GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, "GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB", "GL_ARB_texture_cube_map" },
558#endif
559#if defined(GL_NV_texture_rectangle)
560      { 1, GL_MAX_RECTANGLE_TEXTURE_SIZE_NV, "GL_MAX_RECTANGLE_TEXTURE_SIZE_NV", "GL_NV_texture_rectangle" },
561#endif
562#if defined(GL_ARB_multitexture)
563      { 1, GL_MAX_TEXTURE_UNITS_ARB, "GL_MAX_TEXTURE_UNITS_ARB", "GL_ARB_multitexture" },
564#endif
565#if defined(GL_EXT_texture_lod_bias)
566      { 1, GL_MAX_TEXTURE_LOD_BIAS_EXT, "GL_MAX_TEXTURE_LOD_BIAS_EXT", "GL_EXT_texture_lod_bias" },
567#endif
568#if defined(GL_EXT_texture_filter_anisotropic)
569      { 1, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT", "GL_EXT_texture_filter_anisotropic" },
570#endif
571#if defined(GL_ARB_draw_buffers)
572      { 1, GL_MAX_DRAW_BUFFERS_ARB, "GL_MAX_DRAW_BUFFERS_ARB", "GL_ARB_draw_buffers" },
573#endif
574#if defined(GL_ARB_blend_func_extended)
575      { 1, GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, "GL_MAX_DUAL_SOURCE_DRAW_BUFFERS", "GL_ARB_blend_func_extended" },
576#endif
577#if defined (GL_ARB_framebuffer_object)
578      { 1, GL_MAX_RENDERBUFFER_SIZE, "GL_MAX_RENDERBUFFER_SIZE", "GL_ARB_framebuffer_object" },
579      { 1, GL_MAX_COLOR_ATTACHMENTS, "GL_MAX_COLOR_ATTACHMENTS", "GL_ARB_framebuffer_object" },
580      { 1, GL_MAX_SAMPLES, "GL_MAX_SAMPLES", "GL_ARB_framebuffer_object" },
581#endif
582#if defined (GL_EXT_transform_feedback)
583     { 1, GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, "GL_MAX_TRANSFORM_FEEDBACK_BUFFERS", "GL_EXT_transform_feedback" },
584     { 1, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT, "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", "GL_EXT_transform_feedback" },
585     { 1, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", "GL_EXT_transform_feedback", },
586     { 1, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", "GL_EXT_transform_feedback" },
587#endif
588#if defined (GL_ARB_texture_buffer_object)
589      { 1, GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT", "GL_ARB_texture_buffer_object" },
590      { 1, GL_MAX_TEXTURE_BUFFER_SIZE, "GL_MAX_TEXTURE_BUFFER_SIZE", "GL_ARB_texture_buffer_object" },
591#endif
592#if defined (GL_ARB_texture_multisample)
593      { 1, GL_MAX_COLOR_TEXTURE_SAMPLES, "GL_MAX_COLOR_TEXTURE_SAMPLES", "GL_ARB_texture_multisample" },
594      { 1, GL_MAX_DEPTH_TEXTURE_SAMPLES, "GL_MAX_DEPTH_TEXTURE_SAMPLES", "GL_ARB_texture_multisample" },
595      { 1, GL_MAX_INTEGER_SAMPLES, "GL_MAX_INTEGER_SAMPLES", "GL_ARB_texture_multisample" },
596#endif
597#if defined (GL_ARB_uniform_buffer_object)
598      { 1, GL_MAX_VERTEX_UNIFORM_BLOCKS, "GL_MAX_VERTEX_UNIFORM_BLOCKS", "GL_ARB_uniform_buffer_object" },
599      { 1, GL_MAX_FRAGMENT_UNIFORM_BLOCKS, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS", "GL_ARB_uniform_buffer_object" },
600      { 1, GL_MAX_GEOMETRY_UNIFORM_BLOCKS, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS" , "GL_ARB_uniform_buffer_object" },
601      { 1, GL_MAX_COMBINED_UNIFORM_BLOCKS, "GL_MAX_COMBINED_UNIFORM_BLOCKS", "GL_ARB_uniform_buffer_object" },
602      { 1, GL_MAX_UNIFORM_BUFFER_BINDINGS, "GL_MAX_UNIFORM_BUFFER_BINDINGS", "GL_ARB_uniform_buffer_object" },
603      { 1, GL_MAX_UNIFORM_BLOCK_SIZE, "GL_MAX_UNIFORM_BLOCK_SIZE", "GL_ARB_uniform_buffer_object" },
604      { 1, GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", "GL_ARB_uniform_buffer_object" },
605      { 1, GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", "GL_ARB_uniform_buffer_object" },
606      { 1, GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS", "GL_ARB_uniform_buffer_object" },
607      { 1, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT", "GL_ARB_uniform_buffer_object" },
608#endif
609#if defined (GL_ARB_vertex_attrib_binding)
610      { 1, GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET", "GL_ARB_vertex_attrib_binding" },
611      { 1, GL_MAX_VERTEX_ATTRIB_BINDINGS, "GL_MAX_VERTEX_ATTRIB_BINDINGS", "GL_ARB_vertex_attrib_binding" },
612#endif
613#if defined(GL_ARB_tessellation_shader)
614      { 1, GL_MAX_TESS_GEN_LEVEL, "GL_MAX_TESS_GEN_LEVEL", "GL_ARB_tessellation_shader" },
615      { 1, GL_MAX_TESS_PATCH_COMPONENTS, "GL_MAX_TESS_PATCH_COMPONENTS", "GL_ARB_tessellation_shader" },
616      { 1, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, "GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS", "GL_ARB_tessellation_shader" },
617      { 1, GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, "GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS", "GL_ARB_tessellation_shader" },
618      { 1, GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, "GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS", "GL_ARB_tessellation_shader" },
619      { 1, GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, "GL_MAX_TESS_CONTROL_INPUT_COMPONENTS", "GL_ARB_tessellation_shader" },
620      { 1, GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, "GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS", "GL_ARB_tessellation_shader" },
621      { 1, GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, "GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS", "GL_ARB_tessellation_shader" },
622      { 1, GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS", "GL_ARB_tessellation_shader" },
623      { 1, GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, "GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS", "GL_ARB_tessellation_shader" },
624      { 1, GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, "GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS", "GL_ARB_tessellation_shader" },
625      { 1, GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, "GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS", "GL_ARB_tessellation_shader" },
626      { 1, GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS", "GL_ARB_tessellation_shader" },
627      { 1, GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, "GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS", "GL_ARB_tessellation_shader" },
628      { 1, GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS", "GL_ARB_tessellation_shader" },
629#endif
630
631#if defined(GL_VERSION_3_0)
632      { 1, GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", "3.0" },
633      { 1, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", "3.0" },
634      { 1, GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", "3.0" },
635#endif
636#if defined(GL_VERSION_3_1)
637      { 1, GL_MAX_TEXTURE_BUFFER_SIZE, "GL_MAX_TEXTURE_BUFFER_SIZE", "3.1" },
638      { 1, GL_MAX_RECTANGLE_TEXTURE_SIZE, "GL_MAX_RECTANGLE_TEXTURE_SIZE", "3.1" },
639#endif
640#if defined(GL_VERSION_3_2)
641      { 1, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS", "3.2" },
642      { 1, GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS", "3.2" },
643      { 1, GL_MAX_GEOMETRY_OUTPUT_VERTICES, "GL_MAX_GEOMETRY_OUTPUT_VERTICES", "3.2" },
644      { 1, GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS", "3.2" },
645      { 1, GL_MAX_VERTEX_OUTPUT_COMPONENTS, "GL_MAX_VERTEX_OUTPUT_COMPONENTS", "3.2" },
646      { 1, GL_MAX_GEOMETRY_INPUT_COMPONENTS, "GL_MAX_GEOMETRY_INPUT_COMPONENTS", "3.2" },
647      { 1, GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS", "3.2" },
648      { 1, GL_MAX_FRAGMENT_INPUT_COMPONENTS, "GL_MAX_FRAGMENT_INPUT_COMPONENTS", "3.2" },
649      { 1, GL_MAX_SERVER_WAIT_TIMEOUT, "GL_MAX_SERVER_WAIT_TIMEOUT", "3.2" },
650      { 1, GL_MAX_SAMPLE_MASK_WORDS, "GL_MAX_SAMPLE_MASK_WORDS", "3.2" },
651      { 1, GL_MAX_COLOR_TEXTURE_SAMPLES, "GL_MAX_COLOR_TEXTURE_SAMPLES", "3.2" },
652      { 1, GL_MAX_DEPTH_TEXTURE_SAMPLES, "GL_MAX_DEPTH_TEXTURE_SAMPLES", "3.2" },
653      { 1, GL_MAX_INTEGER_SAMPLES, "GL_MAX_INTEGER_SAMPLES", "3.2" },
654#endif
655#if defined(GL_VERSION_3_3)
656      { 1, GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, "GL_MAX_DUAL_SOURCE_DRAW_BUFFERS", "3.3" },
657#endif
658#if defined(GL_VERSION_4_0)
659      { 1, GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, "GL_MAX_TRANSFORM_FEEDBACK_BUFFERS", "4.0" },
660#endif
661#if defined(GL_VERSION_4_1)
662      { 1, GL_MAX_VERTEX_UNIFORM_VECTORS, "GL_MAX_VERTEX_UNIFORM_VECTORS", "4.1" },
663      { 1, GL_MAX_VARYING_VECTORS, "GL_MAX_VARYING_VECTORS", "4.1" },
664      { 1, GL_MAX_FRAGMENT_UNIFORM_VECTORS, "GL_MAX_FRAGMENT_UNIFORM_VECTORS", "4.1" },
665#endif
666#if defined(GL_VERSION_4_2)
667      { 1, GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS, "GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS", "4.2" },
668      { 1, GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS", "4.2" },
669      { 1, GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS", "4.2" },
670      { 1, GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS, "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS", "4.2" },
671      { 1, GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, "GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS", "4.2" },
672      { 1, GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS, "GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS", "4.2" },
673      { 1, GL_MAX_VERTEX_ATOMIC_COUNTERS, "GL_MAX_VERTEX_ATOMIC_COUNTERS", "4.2" },
674      { 1, GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS", "4.2" },
675      { 1, GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS", "4.2" },
676      { 1, GL_MAX_GEOMETRY_ATOMIC_COUNTERS, "GL_MAX_GEOMETRY_ATOMIC_COUNTERS", "4.2" },
677      { 1, GL_MAX_FRAGMENT_ATOMIC_COUNTERS, "GL_MAX_FRAGMENT_ATOMIC_COUNTERS", "4.2" },
678      { 1, GL_MAX_COMBINED_ATOMIC_COUNTERS, "GL_MAX_COMBINED_ATOMIC_COUNTERS", "4.2" },
679      { 1, GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE, "GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE", "4.2" },
680      { 1, GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, "GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS", "4.2" },
681      { 1, GL_MAX_IMAGE_UNITS, "GL_MAX_IMAGE_UNITS", "4.2" },
682      { 1, GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS, "GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS", "4.2" },
683      { 1, GL_MAX_IMAGE_SAMPLES, "GL_MAX_IMAGE_SAMPLES", "4.2" },
684      { 1, GL_MAX_VERTEX_IMAGE_UNIFORMS , "GL_MAX_VERTEX_IMAGE_UNIFORMS", "4.2" },
685      { 1, GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS", "4.2" },
686      { 1, GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS", "4.2" },
687      { 1, GL_MAX_GEOMETRY_IMAGE_UNIFORMS, "GL_MAX_GEOMETRY_IMAGE_UNIFORMS", "4.2" },
688      { 1, GL_MAX_FRAGMENT_IMAGE_UNIFORMS, "GL_MAX_FRAGMENT_IMAGE_UNIFORMS", "4.2" },
689      { 1, GL_MAX_COMBINED_IMAGE_UNIFORMS, "GL_MAX_COMBINED_IMAGE_UNIFORMS", "4.2" },
690#endif
691#if defined(GL_VERSION_4_3)
692      { 1, GL_MAX_ELEMENT_INDEX, "GL_MAX_ELEMENT_INDEX", "4.3" },
693      { 1, GL_MAX_COMPUTE_UNIFORM_BLOCKS, "GL_MAX_COMPUTE_UNIFORM_BLOCKS", "4.3" },
694      { 1, GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, "GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS", "4.3" },
695      { 1, GL_MAX_COMPUTE_IMAGE_UNIFORMS, "GL_MAX_COMPUTE_IMAGE_UNIFORMS", "4.3" },
696      { 1, GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, "GL_MAX_COMPUTE_SHARED_MEMORY_SIZE", "4.3" },
697      { 1, GL_MAX_COMPUTE_UNIFORM_COMPONENTS, "GL_MAX_COMPUTE_UNIFORM_COMPONENTS", "4.3" },
698      { 1, GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, "GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS", "4.3" },
699      { 1, GL_MAX_COMPUTE_ATOMIC_COUNTERS, "GL_MAX_COMPUTE_ATOMIC_COUNTERS", "4.3" },
700      { 1, GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS, "GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS", "4.3" },
701      { 1, GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, "GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS", "4.3" },
702      { 1, GL_MAX_COMPUTE_WORK_GROUP_COUNT, "GL_MAX_COMPUTE_WORK_GROUP_COUNT", "4.3" },
703      { 1, GL_MAX_COMPUTE_WORK_GROUP_SIZE, "GL_MAX_COMPUTE_WORK_GROUP_SIZE", "4.3" },
704      { 1, GL_MAX_DEBUG_MESSAGE_LENGTH, "GL_MAX_DEBUG_MESSAGE_LENGTH", "4.3" },
705      { 1, GL_MAX_DEBUG_LOGGED_MESSAGES, "GL_MAX_DEBUG_LOGGED_MESSAGES", "4.3" },
706      { 1, GL_MAX_DEBUG_GROUP_STACK_DEPTH, "GL_MAX_DEBUG_GROUP_STACK_DEPTH", "4.3" },
707      { 1, GL_MAX_LABEL_LENGTH, "GL_MAX_LABEL_LENGTH", "4.3" },
708      { 1, GL_MAX_UNIFORM_LOCATIONS, "GL_MAX_UNIFORM_LOCATIONS", "4.3" },
709      { 1, GL_MAX_FRAMEBUFFER_WIDTH, "GL_MAX_FRAMEBUFFER_WIDTH", "4.3" },
710      { 1, GL_MAX_FRAMEBUFFER_HEIGHT, "GL_MAX_FRAMEBUFFER_HEIGHT", "4.3" },
711      { 1, GL_MAX_FRAMEBUFFER_LAYERS, "GL_MAX_FRAMEBUFFER_LAYERS", "4.3" },
712      { 1, GL_MAX_FRAMEBUFFER_SAMPLES, "GL_MAX_FRAMEBUFFER_SAMPLES", "4.3" },
713      { 1, GL_MAX_WIDTH, "GL_MAX_WIDTH", "4.3" },
714      { 1, GL_MAX_HEIGHT, "GL_MAX_HEIGHT", "4.3" },
715      { 1, GL_MAX_DEPTH, "GL_MAX_DEPTH", "4.3" },
716      { 1, GL_MAX_LAYERS, "GL_MAX_LAYERS", "4.3" },
717      { 1, GL_MAX_COMBINED_DIMENSIONS, "GL_MAX_COMBINED_DIMENSIONS", "4.3" },
718      { 1, GL_MAX_NAME_LENGTH, "GL_MAX_NAME_LENGTH", "4.3" },
719      { 1, GL_MAX_NUM_ACTIVE_VARIABLES, "GL_MAX_NUM_ACTIVE_VARIABLES", "4.3" },
720      { 1, GL_MAX_NUM_COMPATIBLE_SUBROUTINES, "GL_MAX_NUM_COMPATIBLE_SUBROUTINES", "4.3" },
721      { 1, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", "4.3" },
722      { 1, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS", "4.3" },
723      { 1, GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, "GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS", "4.3" },
724      { 1, GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS", "4.3" },
725      { 1, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", "4.3" },
726      { 1, GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS", "4.3" },
727      { 1, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, "GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS", "4.3" },
728      { 1, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, "GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS", "4.3" },
729      { 1, GL_MAX_SHADER_STORAGE_BLOCK_SIZE, "GL_MAX_SHADER_STORAGE_BLOCK_SIZE", "4.3" },
730      { 1, GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, "GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES", "4.3" },
731      { 1, GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET", "4.3" },
732      { 1, GL_MAX_VERTEX_ATTRIB_BINDINGS, "GL_MAX_VERTEX_ATTRIB_BINDINGS", "4.3" },
733#endif
734#if defined(GL_VERSION_4_4)
735      { 1, GL_MAX_VERTEX_ATTRIB_STRIDE, "GL_MAX_VERTEX_ATTRIB_STRIDE", "4.4" },
736#endif
737#if defined(GL_VERSION_4_5)
738      { 1, GL_MAX_CULL_DISTANCES, "GL_MAX_CULL_DISTANCES", "4.5" },
739      { 1, GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES", "4.5" },
740#endif
741#if defined(GL_VERSION_4_6)
742      { 1, GL_MAX_TEXTURE_MAX_ANISOTROPY, "GL_MAX_TEXTURE_MAX_ANISOTROPY", "4.6" },
743#endif
744#if defined(GL_ARB_transform_feedback3)
745      { 1, GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, "GL_MAX_TRANSFORM_FEEDBACK_BUFFERS", "GL_ARB_transform_feedback3" },
746      { 1, GL_MAX_VERTEX_STREAMS, "GL_MAX_VERTEX_STREAMS", "GL_ARB_transform_feedback3" },
747#endif
748      { 0, (GLenum) 0, NULL, NULL }
749   };
750   GLint i, max[2];
751   const char *prev_ext = "none";
752
753   printf("%s limits:\n", oglstring);
754   for (i = 0; limits[i].count; i++) {
755      if (!limits[i].extension ||
756          version_supported(limits[i].extension, version) ||
757          extension_supported(limits[i].extension, extensions)) {
758         glGetIntegerv(limits[i].token, max);
759         if (glGetError() == GL_NO_ERROR) {
760            if (limits[i].extension && strcmp(limits[i].extension, prev_ext) != 0) {
761               printf("  %s:\n", limits[i].extension);
762               prev_ext = limits[i].extension;
763            }
764            if (limits[i].count == 1) {
765               printf("    %s = %d\n", limits[i].name, max[0]);
766            }
767            else {
768               assert(limits[i].count == 2);
769               printf("    %s = %d, %d\n", limits[i].name, max[0], max[1]);
770            }
771         }
772      }
773   }
774
775   /* these don't fit into the above mechanism, unfortunately */
776   if (extension_supported("GL_ARB_imaging", extensions)) {
777      extfuncs->GetConvolutionParameteriv(GL_CONVOLUTION_2D,
778                                          GL_MAX_CONVOLUTION_WIDTH, max);
779      extfuncs->GetConvolutionParameteriv(GL_CONVOLUTION_2D,
780                                          GL_MAX_CONVOLUTION_HEIGHT, max+1);
781      printf("  GL_ARB_imaging:\n");
782      printf("    GL_MAX_CONVOLUTION_WIDTH/HEIGHT = %d, %d\n", max[0], max[1]);
783   }
784
785   if (extension_supported("GL_ARB_texture_compression", extensions)) {
786      GLint i, n;
787      GLint *formats;
788      printf("  GL_ARB_texture_compression:\n");
789      glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &n);
790      printf("    GL_NUM_COMPRESSED_TEXTURE_FORMATS = %d\n", n);
791      formats = (GLint *) malloc(n * sizeof(GLint));
792      glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
793      for (i = 0; i < n; i++) {
794         printf("      %s\n", enum_name(formats[i]));
795      }
796      free(formats);
797   }
798
799#if defined(GL_VERSION_4_3)
800   if (version >= 43) {
801      GLint i, n = 0;
802      printf("  4.3:\n");
803      glGetIntegerv(GL_NUM_SHADING_LANGUAGE_VERSIONS, &n);
804      printf("    GL_NUM_SHADING_LANGUAGE_VERSIONS = %d\n", n);
805      for (i = 0; i < n; i++) {
806         printf("      %s\n", (const char *)
807                extfuncs->GetStringi(GL_SHADING_LANGUAGE_VERSION, i));
808      }
809   }
810#endif
811
812#if defined(GL_ARB_vertex_program)
813   if (extension_supported("GL_ARB_vertex_program", extensions)) {
814      print_program_limits(GL_VERTEX_PROGRAM_ARB, extfuncs);
815   }
816#endif
817#if defined(GL_ARB_fragment_program)
818   if (extension_supported("GL_ARB_fragment_program", extensions)) {
819      print_program_limits(GL_FRAGMENT_PROGRAM_ARB, extfuncs);
820   }
821#endif
822   if (extension_supported("GL_ARB_vertex_shader", extensions)) {
823      print_shader_limits(GL_VERTEX_SHADER_ARB);
824   }
825   if (extension_supported("GL_ARB_fragment_shader", extensions)) {
826      print_shader_limits(GL_FRAGMENT_SHADER_ARB);
827   }
828   if (version >= 32) {
829      print_shader_limits(GL_GEOMETRY_SHADER);
830   }
831}
832
833
834
835/**
836 * Return string representation for bits in a bitmask.
837 */
838const char *
839bitmask_to_string(const struct bit_info bits[], int numBits, int mask)
840{
841   static char buffer[256], *p;
842   int i;
843
844   strcpy(buffer, "(none)");
845   p = buffer;
846   for (i = 0; i < numBits; i++) {
847      if (mask & bits[i].bit) {
848         if (p > buffer)
849            *p++ = ',';
850         strcpy(p, bits[i].name);
851         p += strlen(bits[i].name);
852      }
853   }
854
855   return buffer;
856}
857
858/**
859 * Return string representation for the bitmask returned by
860 * GL_CONTEXT_PROFILE_MASK (OpenGL 3.2 or later).
861 */
862const char *
863profile_mask_string(int mask)
864{
865   const static struct bit_info bits[] = {
866#ifdef GL_CONTEXT_CORE_PROFILE_BIT
867      { GL_CONTEXT_CORE_PROFILE_BIT, "core profile"},
868#endif
869#ifdef GL_CONTEXT_COMPATIBILITY_PROFILE_BIT
870      { GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, "compatibility profile" }
871#endif
872   };
873
874   return bitmask_to_string(bits, ELEMENTS(bits), mask);
875}
876
877
878/**
879 * Return string representation for the bitmask returned by
880 * GL_CONTEXT_FLAGS (OpenGL 3.0 or later).
881 */
882const char *
883context_flags_string(int mask)
884{
885   const static struct bit_info bits[] = {
886      { GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, "forward-compatible" },
887      { GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, "robust-access" },
888      { GL_CONTEXT_FLAG_DEBUG_BIT, "debug" },
889      { GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR, "no-error" },
890   };
891
892   return bitmask_to_string(bits, ELEMENTS(bits), mask);
893}
894
895
896static void
897usage(void)
898{
899#ifdef _WIN32
900   printf("Usage: wglinfo [-v] [-t] [-h] [-b] [-l] [-s]\n");
901#else
902   printf("Usage: glxinfo [-v] [-t] [-h] [-b] [-l] [-s] [-i] [-display <dname>]\n");
903   printf("\t-display <dname>: Print GLX visuals on specified server.\n");
904   printf("\t-i: Force an indirect rendering context.\n");
905#endif
906   printf("\t-B: brief output, print only the basics.\n");
907   printf("\t-v: Print visuals info in verbose form.\n");
908   printf("\t-t: Print verbose visual information table.\n");
909   printf("\t-h: This information.\n");
910   printf("\t-b: Find the 'best' visual and print its number.\n");
911   printf("\t-l: Print interesting OpenGL limits.\n");
912   printf("\t-s: Print a single extension per line.\n");
913}
914
915void
916parse_args(int argc, char *argv[], struct options *options)
917{
918   int i;
919
920   options->mode = Normal;
921   options->findBest = GL_FALSE;
922   options->limits = GL_FALSE;
923   options->singleLine = GL_FALSE;
924   options->displayName = NULL;
925   options->allowDirect = GL_TRUE;
926
927   for (i = 1; i < argc; i++) {
928#ifndef _WIN32
929      if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
930         options->displayName = argv[i + 1];
931         i++;
932      }
933      else if (strcmp(argv[i], "-i") == 0) {
934         options->allowDirect = GL_FALSE;
935      }
936      else
937#endif
938      if (strcmp(argv[i], "-t") == 0) {
939         options->mode = Wide;
940      }
941      else if (strcmp(argv[i], "-v") == 0) {
942         options->mode = Verbose;
943      }
944      else if (strcmp(argv[i], "-B") == 0) {
945         options->mode = Brief;
946      }
947      else if (strcmp(argv[i], "-b") == 0) {
948         options->findBest = GL_TRUE;
949      }
950      else if (strcmp(argv[i], "-l") == 0) {
951         options->limits = GL_TRUE;
952      }
953      else if (strcmp(argv[i], "-h") == 0) {
954         usage();
955         exit(0);
956      }
957      else if(strcmp(argv[i], "-s") == 0) {
958         options->singleLine = GL_TRUE;
959      }
960      else {
961         printf("Unknown option `%s'\n", argv[i]);
962         usage();
963         exit(0);
964      }
965   }
966}
967
968static void
969query_ATI_meminfo(void)
970{
971#ifdef GL_ATI_meminfo
972    int i[4];
973
974    printf("Memory info (GL_ATI_meminfo):\n");
975
976    glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, i);
977    printf("    VBO free memory - total: %u MB, largest block: %u MB\n",
978           i[0] / 1024, i[1] / 1024);
979    printf("    VBO free aux. memory - total: %u MB, largest block: %u MB\n",
980           i[2] / 1024, i[3] / 1024);
981
982    glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, i);
983    printf("    Texture free memory - total: %u MB, largest block: %u MB\n",
984           i[0] / 1024, i[1] / 1024);
985    printf("    Texture free aux. memory - total: %u MB, largest block: %u MB\n",
986           i[2] / 1024, i[3] / 1024);
987
988    glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, i);
989    printf("    Renderbuffer free memory - total: %u MB, largest block: %u MB\n",
990           i[0] / 1024, i[1] / 1024);
991    printf("    Renderbuffer free aux. memory - total: %u MB, largest block: %u MB\n",
992           i[2] / 1024, i[3] / 1024);
993#endif
994}
995
996static void
997query_NVX_gpu_memory_info(void)
998{
999#ifdef GL_NVX_gpu_memory_info
1000    int i;
1001
1002    printf("Memory info (GL_NVX_gpu_memory_info):\n");
1003
1004    glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &i);
1005    printf("    Dedicated video memory: %u MB\n", i / 1024);
1006
1007    glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &i);
1008    printf("    Total available memory: %u MB\n", i / 1024);
1009
1010    glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &i);
1011    printf("    Currently available dedicated video memory: %u MB\n", i / 1024);
1012#endif
1013}
1014
1015void
1016print_gpu_memory_info(const char *glExtensions)
1017{
1018   if (strstr(glExtensions, "GL_ATI_meminfo"))
1019      query_ATI_meminfo();
1020   if (strstr(glExtensions, "GL_NVX_gpu_memory_info"))
1021      query_NVX_gpu_memory_info();
1022}
1023