1 //========================================================================
2 // Context creation and information tool
3 // Copyright (c) Camilla Löwy <elmindreda@glfw.org>
4 //
5 // This software is provided 'as-is', without any express or implied
6 // warranty. In no event will the authors be held liable for any damages
7 // arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it
11 // freely, subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented; you must not
14 //    claim that you wrote the original software. If you use this software
15 //    in a product, an acknowledgment in the product documentation would
16 //    be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such, and must not
19 //    be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source
22 //    distribution.
23 //
24 //========================================================================
25 
26 #include <glad/gl.h>
27 #include <glad/vulkan.h>
28 #include <GLFW/glfw3.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "getopt.h"
35 
36 #ifdef _MSC_VER
37 #define strcasecmp(x, y) _stricmp(x, y)
38 #endif
39 
40 #define API_NAME_OPENGL     "gl"
41 #define API_NAME_OPENGL_ES  "es"
42 
43 #define API_NAME_NATIVE     "native"
44 #define API_NAME_EGL        "egl"
45 #define API_NAME_OSMESA     "osmesa"
46 
47 #define PROFILE_NAME_CORE   "core"
48 #define PROFILE_NAME_COMPAT "compat"
49 
50 #define STRATEGY_NAME_NONE  "none"
51 #define STRATEGY_NAME_LOSE  "lose"
52 
53 #define BEHAVIOR_NAME_NONE  "none"
54 #define BEHAVIOR_NAME_FLUSH "flush"
55 
usage(void)56 static void usage(void)
57 {
58     printf("Usage: glfwinfo [OPTION]...\n");
59     printf("Options:\n");
60     printf("  -a, --client-api=API      the client API to use ("
61                                         API_NAME_OPENGL " or "
62                                         API_NAME_OPENGL_ES ")\n");
63     printf("  -b, --behavior=BEHAVIOR   the release behavior to use ("
64                                         BEHAVIOR_NAME_NONE " or "
65                                         BEHAVIOR_NAME_FLUSH ")\n");
66     printf("  -c, --context-api=API     the context creation API to use ("
67                                         API_NAME_NATIVE " or "
68                                         API_NAME_EGL " or "
69                                         API_NAME_OSMESA ")\n");
70     printf("  -d, --debug               request a debug context\n");
71     printf("  -f, --forward             require a forward-compatible context\n");
72     printf("  -h, --help                show this help\n");
73     printf("  -l, --list-extensions     list all Vulkan and client API extensions\n");
74     printf("      --list-layers         list all Vulkan layers\n");
75     printf("  -m, --major=MAJOR         the major number of the required "
76                                         "client API version\n");
77     printf("  -n, --minor=MINOR         the minor number of the required "
78                                         "client API version\n");
79     printf("  -p, --profile=PROFILE     the OpenGL profile to use ("
80                                         PROFILE_NAME_CORE " or "
81                                         PROFILE_NAME_COMPAT ")\n");
82     printf("  -s, --robustness=STRATEGY the robustness strategy to use ("
83                                         STRATEGY_NAME_NONE " or "
84                                         STRATEGY_NAME_LOSE ")\n");
85     printf("  -v, --version             print version information\n");
86     printf("      --red-bits=N          the number of red bits to request\n");
87     printf("      --green-bits=N        the number of green bits to request\n");
88     printf("      --blue-bits=N         the number of blue bits to request\n");
89     printf("      --alpha-bits=N        the number of alpha bits to request\n");
90     printf("      --depth-bits=N        the number of depth bits to request\n");
91     printf("      --stencil-bits=N      the number of stencil bits to request\n");
92     printf("      --accum-red-bits=N    the number of red bits to request\n");
93     printf("      --accum-green-bits=N  the number of green bits to request\n");
94     printf("      --accum-blue-bits=N   the number of blue bits to request\n");
95     printf("      --accum-alpha-bits=N  the number of alpha bits to request\n");
96     printf("      --aux-buffers=N       the number of aux buffers to request\n");
97     printf("      --samples=N           the number of MSAA samples to request\n");
98     printf("      --stereo              request stereo rendering\n");
99     printf("      --srgb                request an sRGB capable framebuffer\n");
100     printf("      --singlebuffer        request single-buffering\n");
101     printf("      --no-error            request a context that does not emit errors\n");
102     printf("      --graphics-switching  request macOS graphics switching\n");
103 }
104 
error_callback(int error,const char * description)105 static void error_callback(int error, const char* description)
106 {
107     fprintf(stderr, "Error: %s\n", description);
108 }
109 
get_device_type_name(VkPhysicalDeviceType type)110 static const char* get_device_type_name(VkPhysicalDeviceType type)
111 {
112     if (type == VK_PHYSICAL_DEVICE_TYPE_OTHER)
113         return "other";
114     else if (type == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
115         return "integrated GPU";
116     else if (type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
117         return "discrete GPU";
118     else if (type == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
119         return "virtual GPU";
120     else if (type == VK_PHYSICAL_DEVICE_TYPE_CPU)
121         return "CPU";
122 
123     return "unknown";
124 }
125 
get_api_name(int api)126 static const char* get_api_name(int api)
127 {
128     if (api == GLFW_OPENGL_API)
129         return "OpenGL";
130     else if (api == GLFW_OPENGL_ES_API)
131         return "OpenGL ES";
132 
133     return "Unknown API";
134 }
135 
get_profile_name_gl(GLint mask)136 static const char* get_profile_name_gl(GLint mask)
137 {
138     if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT)
139         return PROFILE_NAME_COMPAT;
140     if (mask & GL_CONTEXT_CORE_PROFILE_BIT)
141         return PROFILE_NAME_CORE;
142 
143     return "unknown";
144 }
145 
get_profile_name_glfw(int profile)146 static const char* get_profile_name_glfw(int profile)
147 {
148     if (profile == GLFW_OPENGL_COMPAT_PROFILE)
149         return PROFILE_NAME_COMPAT;
150     if (profile == GLFW_OPENGL_CORE_PROFILE)
151         return PROFILE_NAME_CORE;
152 
153     return "unknown";
154 }
155 
get_strategy_name_gl(GLint strategy)156 static const char* get_strategy_name_gl(GLint strategy)
157 {
158     if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB)
159         return STRATEGY_NAME_LOSE;
160     if (strategy == GL_NO_RESET_NOTIFICATION_ARB)
161         return STRATEGY_NAME_NONE;
162 
163     return "unknown";
164 }
165 
get_strategy_name_glfw(int strategy)166 static const char* get_strategy_name_glfw(int strategy)
167 {
168     if (strategy == GLFW_LOSE_CONTEXT_ON_RESET)
169         return STRATEGY_NAME_LOSE;
170     if (strategy == GLFW_NO_RESET_NOTIFICATION)
171         return STRATEGY_NAME_NONE;
172 
173     return "unknown";
174 }
175 
list_context_extensions(int client,int major,int minor)176 static void list_context_extensions(int client, int major, int minor)
177 {
178     int i;
179     GLint count;
180     const GLubyte* extensions;
181 
182     printf("%s context extensions:\n", get_api_name(client));
183 
184     if (client == GLFW_OPENGL_API && major > 2)
185     {
186         glGetIntegerv(GL_NUM_EXTENSIONS, &count);
187 
188         for (i = 0;  i < count;  i++)
189             printf(" %s\n", (const char*) glGetStringi(GL_EXTENSIONS, i));
190     }
191     else
192     {
193         extensions = glGetString(GL_EXTENSIONS);
194         while (*extensions != '\0')
195         {
196             putchar(' ');
197 
198             while (*extensions != '\0' && *extensions != ' ')
199             {
200                 putchar(*extensions);
201                 extensions++;
202             }
203 
204             while (*extensions == ' ')
205                 extensions++;
206 
207             putchar('\n');
208         }
209     }
210 }
211 
list_vulkan_instance_extensions(void)212 static void list_vulkan_instance_extensions(void)
213 {
214     uint32_t i, ep_count = 0;
215     VkExtensionProperties* ep;
216 
217     printf("Vulkan instance extensions:\n");
218 
219     if (vkEnumerateInstanceExtensionProperties(NULL, &ep_count, NULL) != VK_SUCCESS)
220         return;
221 
222     ep = calloc(ep_count, sizeof(VkExtensionProperties));
223 
224     if (vkEnumerateInstanceExtensionProperties(NULL, &ep_count, ep) != VK_SUCCESS)
225     {
226         free(ep);
227         return;
228     }
229 
230     for (i = 0;  i < ep_count;  i++)
231         printf(" %s (v%u)\n", ep[i].extensionName, ep[i].specVersion);
232 
233     free(ep);
234 }
235 
list_vulkan_instance_layers(void)236 static void list_vulkan_instance_layers(void)
237 {
238     uint32_t i, lp_count = 0;
239     VkLayerProperties* lp;
240 
241     printf("Vulkan instance layers:\n");
242 
243     if (vkEnumerateInstanceLayerProperties(&lp_count, NULL) != VK_SUCCESS)
244         return;
245 
246     lp = calloc(lp_count, sizeof(VkLayerProperties));
247 
248     if (vkEnumerateInstanceLayerProperties(&lp_count, lp) != VK_SUCCESS)
249     {
250         free(lp);
251         return;
252     }
253 
254     for (i = 0;  i < lp_count;  i++)
255     {
256         printf(" %s (v%u) \"%s\"\n",
257                lp[i].layerName,
258                lp[i].specVersion >> 22,
259                lp[i].description);
260     }
261 
262     free(lp);
263 }
264 
list_vulkan_device_extensions(VkInstance instance,VkPhysicalDevice device)265 static void list_vulkan_device_extensions(VkInstance instance, VkPhysicalDevice device)
266 {
267     uint32_t i, ep_count;
268     VkExtensionProperties* ep;
269 
270     printf("Vulkan device extensions:\n");
271 
272     if (vkEnumerateDeviceExtensionProperties(device, NULL, &ep_count, NULL) != VK_SUCCESS)
273         return;
274 
275     ep = calloc(ep_count, sizeof(VkExtensionProperties));
276 
277     if (vkEnumerateDeviceExtensionProperties(device, NULL, &ep_count, ep) != VK_SUCCESS)
278     {
279         free(ep);
280         return;
281     }
282 
283     for (i = 0;  i < ep_count;  i++)
284         printf(" %s (v%u)\n", ep[i].extensionName, ep[i].specVersion);
285 
286     free(ep);
287 }
288 
list_vulkan_device_layers(VkInstance instance,VkPhysicalDevice device)289 static void list_vulkan_device_layers(VkInstance instance, VkPhysicalDevice device)
290 {
291     uint32_t i, lp_count;
292     VkLayerProperties* lp;
293 
294     printf("Vulkan device layers:\n");
295 
296     if (vkEnumerateDeviceLayerProperties(device, &lp_count, NULL) != VK_SUCCESS)
297         return;
298 
299     lp = calloc(lp_count, sizeof(VkLayerProperties));
300 
301     if (vkEnumerateDeviceLayerProperties(device, &lp_count, lp) != VK_SUCCESS)
302     {
303         free(lp);
304         return;
305     }
306 
307     for (i = 0;  i < lp_count;  i++)
308     {
309         printf(" %s (v%u) \"%s\"\n",
310                lp[i].layerName,
311                lp[i].specVersion >> 22,
312                lp[i].description);
313     }
314 
315     free(lp);
316 }
317 
valid_version(void)318 static int valid_version(void)
319 {
320     int major, minor, revision;
321     glfwGetVersion(&major, &minor, &revision);
322 
323     if (major != GLFW_VERSION_MAJOR)
324     {
325         printf("*** ERROR: GLFW major version mismatch! ***\n");
326         return GLFW_FALSE;
327     }
328 
329     if (minor != GLFW_VERSION_MINOR || revision != GLFW_VERSION_REVISION)
330         printf("*** WARNING: GLFW version mismatch! ***\n");
331 
332     return GLFW_TRUE;
333 }
334 
print_version(void)335 static void print_version(void)
336 {
337     int major, minor, revision;
338     glfwGetVersion(&major, &minor, &revision);
339 
340     printf("GLFW header version: %u.%u.%u\n",
341            GLFW_VERSION_MAJOR,
342            GLFW_VERSION_MINOR,
343            GLFW_VERSION_REVISION);
344     printf("GLFW library version: %u.%u.%u\n", major, minor, revision);
345     printf("GLFW library version string: \"%s\"\n", glfwGetVersionString());
346 }
347 
glad_vulkan_callback(const char * name,void * user)348 static GLADapiproc glad_vulkan_callback(const char* name, void* user)
349 {
350     return glfwGetInstanceProcAddress((VkInstance) user, name);
351 }
352 
main(int argc,char ** argv)353 int main(int argc, char** argv)
354 {
355     int ch, client, major, minor, revision, profile;
356     GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits;
357     int list_extensions = GLFW_FALSE, list_layers = GLFW_FALSE;
358     GLenum error;
359     GLFWwindow* window;
360 
361     enum { CLIENT, CONTEXT, BEHAVIOR, DEBUG, FORWARD, HELP, EXTENSIONS, LAYERS,
362            MAJOR, MINOR, PROFILE, ROBUSTNESS, VERSION,
363            REDBITS, GREENBITS, BLUEBITS, ALPHABITS, DEPTHBITS, STENCILBITS,
364            ACCUMREDBITS, ACCUMGREENBITS, ACCUMBLUEBITS, ACCUMALPHABITS,
365            AUXBUFFERS, SAMPLES, STEREO, SRGB, SINGLEBUFFER, NOERROR_SRSLY,
366            GRAPHICS_SWITCHING };
367     const struct option options[] =
368     {
369         { "behavior",           1, NULL, BEHAVIOR },
370         { "client-api",         1, NULL, CLIENT },
371         { "context-api",        1, NULL, CONTEXT },
372         { "debug",              0, NULL, DEBUG },
373         { "forward",            0, NULL, FORWARD },
374         { "help",               0, NULL, HELP },
375         { "list-extensions",    0, NULL, EXTENSIONS },
376         { "list-layers",        0, NULL, LAYERS },
377         { "major",              1, NULL, MAJOR },
378         { "minor",              1, NULL, MINOR },
379         { "profile",            1, NULL, PROFILE },
380         { "robustness",         1, NULL, ROBUSTNESS },
381         { "version",            0, NULL, VERSION },
382         { "red-bits",           1, NULL, REDBITS },
383         { "green-bits",         1, NULL, GREENBITS },
384         { "blue-bits",          1, NULL, BLUEBITS },
385         { "alpha-bits",         1, NULL, ALPHABITS },
386         { "depth-bits",         1, NULL, DEPTHBITS },
387         { "stencil-bits",       1, NULL, STENCILBITS },
388         { "accum-red-bits",     1, NULL, ACCUMREDBITS },
389         { "accum-green-bits",   1, NULL, ACCUMGREENBITS },
390         { "accum-blue-bits",    1, NULL, ACCUMBLUEBITS },
391         { "accum-alpha-bits",   1, NULL, ACCUMALPHABITS },
392         { "aux-buffers",        1, NULL, AUXBUFFERS },
393         { "samples",            1, NULL, SAMPLES },
394         { "stereo",             0, NULL, STEREO },
395         { "srgb",               0, NULL, SRGB },
396         { "singlebuffer",       0, NULL, SINGLEBUFFER },
397         { "no-error",           0, NULL, NOERROR_SRSLY },
398         { "graphics-switching", 0, NULL, GRAPHICS_SWITCHING },
399         { NULL, 0, NULL, 0 }
400     };
401 
402     // Initialize GLFW and create window
403 
404     if (!valid_version())
405         exit(EXIT_FAILURE);
406 
407     glfwSetErrorCallback(error_callback);
408 
409     glfwInitHint(GLFW_COCOA_MENUBAR, GLFW_FALSE);
410 
411     if (!glfwInit())
412         exit(EXIT_FAILURE);
413 
414     while ((ch = getopt_long(argc, argv, "a:b:c:dfhlm:n:p:s:v", options, NULL)) != -1)
415     {
416         switch (ch)
417         {
418             case 'a':
419             case CLIENT:
420                 if (strcasecmp(optarg, API_NAME_OPENGL) == 0)
421                     glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
422                 else if (strcasecmp(optarg, API_NAME_OPENGL_ES) == 0)
423                     glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
424                 else
425                 {
426                     usage();
427                     exit(EXIT_FAILURE);
428                 }
429                 break;
430             case 'b':
431             case BEHAVIOR:
432                 if (strcasecmp(optarg, BEHAVIOR_NAME_NONE) == 0)
433                 {
434                     glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR,
435                                    GLFW_RELEASE_BEHAVIOR_NONE);
436                 }
437                 else if (strcasecmp(optarg, BEHAVIOR_NAME_FLUSH) == 0)
438                 {
439                     glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR,
440                                    GLFW_RELEASE_BEHAVIOR_FLUSH);
441                 }
442                 else
443                 {
444                     usage();
445                     exit(EXIT_FAILURE);
446                 }
447                 break;
448             case 'c':
449             case CONTEXT:
450                 if (strcasecmp(optarg, API_NAME_NATIVE) == 0)
451                     glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
452                 else if (strcasecmp(optarg, API_NAME_EGL) == 0)
453                     glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
454                 else if (strcasecmp(optarg, API_NAME_OSMESA) == 0)
455                     glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API);
456                 else
457                 {
458                     usage();
459                     exit(EXIT_FAILURE);
460                 }
461                 break;
462             case 'd':
463             case DEBUG:
464                 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
465                 break;
466             case 'f':
467             case FORWARD:
468                 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
469                 break;
470             case 'h':
471             case HELP:
472                 usage();
473                 exit(EXIT_SUCCESS);
474             case 'l':
475             case EXTENSIONS:
476                 list_extensions = GLFW_TRUE;
477                 break;
478             case LAYERS:
479                 list_layers = GLFW_TRUE;
480                 break;
481             case 'm':
482             case MAJOR:
483                 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, atoi(optarg));
484                 break;
485             case 'n':
486             case MINOR:
487                 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, atoi(optarg));
488                 break;
489             case 'p':
490             case PROFILE:
491                 if (strcasecmp(optarg, PROFILE_NAME_CORE) == 0)
492                 {
493                     glfwWindowHint(GLFW_OPENGL_PROFILE,
494                                    GLFW_OPENGL_CORE_PROFILE);
495                 }
496                 else if (strcasecmp(optarg, PROFILE_NAME_COMPAT) == 0)
497                 {
498                     glfwWindowHint(GLFW_OPENGL_PROFILE,
499                                    GLFW_OPENGL_COMPAT_PROFILE);
500                 }
501                 else
502                 {
503                     usage();
504                     exit(EXIT_FAILURE);
505                 }
506                 break;
507             case 's':
508             case ROBUSTNESS:
509                 if (strcasecmp(optarg, STRATEGY_NAME_NONE) == 0)
510                 {
511                     glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS,
512                                    GLFW_NO_RESET_NOTIFICATION);
513                 }
514                 else if (strcasecmp(optarg, STRATEGY_NAME_LOSE) == 0)
515                 {
516                     glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS,
517                                    GLFW_LOSE_CONTEXT_ON_RESET);
518                 }
519                 else
520                 {
521                     usage();
522                     exit(EXIT_FAILURE);
523                 }
524                 break;
525             case 'v':
526             case VERSION:
527                 print_version();
528                 exit(EXIT_SUCCESS);
529             case REDBITS:
530                 if (strcmp(optarg, "-") == 0)
531                     glfwWindowHint(GLFW_RED_BITS, GLFW_DONT_CARE);
532                 else
533                     glfwWindowHint(GLFW_RED_BITS, atoi(optarg));
534                 break;
535             case GREENBITS:
536                 if (strcmp(optarg, "-") == 0)
537                     glfwWindowHint(GLFW_GREEN_BITS, GLFW_DONT_CARE);
538                 else
539                     glfwWindowHint(GLFW_GREEN_BITS, atoi(optarg));
540                 break;
541             case BLUEBITS:
542                 if (strcmp(optarg, "-") == 0)
543                     glfwWindowHint(GLFW_BLUE_BITS, GLFW_DONT_CARE);
544                 else
545                     glfwWindowHint(GLFW_BLUE_BITS, atoi(optarg));
546                 break;
547             case ALPHABITS:
548                 if (strcmp(optarg, "-") == 0)
549                     glfwWindowHint(GLFW_ALPHA_BITS, GLFW_DONT_CARE);
550                 else
551                     glfwWindowHint(GLFW_ALPHA_BITS, atoi(optarg));
552                 break;
553             case DEPTHBITS:
554                 if (strcmp(optarg, "-") == 0)
555                     glfwWindowHint(GLFW_DEPTH_BITS, GLFW_DONT_CARE);
556                 else
557                     glfwWindowHint(GLFW_DEPTH_BITS, atoi(optarg));
558                 break;
559             case STENCILBITS:
560                 if (strcmp(optarg, "-") == 0)
561                     glfwWindowHint(GLFW_STENCIL_BITS, GLFW_DONT_CARE);
562                 else
563                     glfwWindowHint(GLFW_STENCIL_BITS, atoi(optarg));
564                 break;
565             case ACCUMREDBITS:
566                 if (strcmp(optarg, "-") == 0)
567                     glfwWindowHint(GLFW_ACCUM_RED_BITS, GLFW_DONT_CARE);
568                 else
569                     glfwWindowHint(GLFW_ACCUM_RED_BITS, atoi(optarg));
570                 break;
571             case ACCUMGREENBITS:
572                 if (strcmp(optarg, "-") == 0)
573                     glfwWindowHint(GLFW_ACCUM_GREEN_BITS, GLFW_DONT_CARE);
574                 else
575                     glfwWindowHint(GLFW_ACCUM_GREEN_BITS, atoi(optarg));
576                 break;
577             case ACCUMBLUEBITS:
578                 if (strcmp(optarg, "-") == 0)
579                     glfwWindowHint(GLFW_ACCUM_BLUE_BITS, GLFW_DONT_CARE);
580                 else
581                     glfwWindowHint(GLFW_ACCUM_BLUE_BITS, atoi(optarg));
582                 break;
583             case ACCUMALPHABITS:
584                 if (strcmp(optarg, "-") == 0)
585                     glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, GLFW_DONT_CARE);
586                 else
587                     glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, atoi(optarg));
588                 break;
589             case AUXBUFFERS:
590                 if (strcmp(optarg, "-") == 0)
591                     glfwWindowHint(GLFW_AUX_BUFFERS, GLFW_DONT_CARE);
592                 else
593                     glfwWindowHint(GLFW_AUX_BUFFERS, atoi(optarg));
594                 break;
595             case SAMPLES:
596                 if (strcmp(optarg, "-") == 0)
597                     glfwWindowHint(GLFW_SAMPLES, GLFW_DONT_CARE);
598                 else
599                     glfwWindowHint(GLFW_SAMPLES, atoi(optarg));
600                 break;
601             case STEREO:
602                 glfwWindowHint(GLFW_STEREO, GLFW_TRUE);
603                 break;
604             case SRGB:
605                 glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
606                 break;
607             case SINGLEBUFFER:
608                 glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);
609                 break;
610             case NOERROR_SRSLY:
611                 glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE);
612                 break;
613             case GRAPHICS_SWITCHING:
614                 glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, GLFW_TRUE);
615                 break;
616             default:
617                 usage();
618                 exit(EXIT_FAILURE);
619         }
620     }
621 
622     print_version();
623 
624     glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
625 
626     window = glfwCreateWindow(200, 200, "Version", NULL, NULL);
627     if (!window)
628     {
629         glfwTerminate();
630         exit(EXIT_FAILURE);
631     }
632 
633     glfwMakeContextCurrent(window);
634     gladLoadGL(glfwGetProcAddress);
635 
636     error = glGetError();
637     if (error != GL_NO_ERROR)
638         printf("*** OpenGL error after make current: 0x%08x ***\n", error);
639 
640     // Report client API version
641 
642     client = glfwGetWindowAttrib(window, GLFW_CLIENT_API);
643     major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR);
644     minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR);
645     revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION);
646     profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE);
647 
648     printf("%s context version string: \"%s\"\n",
649            get_api_name(client),
650            glGetString(GL_VERSION));
651 
652     printf("%s context version parsed by GLFW: %u.%u.%u\n",
653            get_api_name(client),
654            major, minor, revision);
655 
656     // Report client API context properties
657 
658     if (client == GLFW_OPENGL_API)
659     {
660         if (major >= 3)
661         {
662             GLint flags;
663 
664             glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
665             printf("%s context flags (0x%08x):", get_api_name(client), flags);
666 
667             if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
668                 printf(" forward-compatible");
669             if (flags & 2/*GL_CONTEXT_FLAG_DEBUG_BIT*/)
670                 printf(" debug");
671             if (flags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB)
672                 printf(" robustness");
673             if (flags & 8/*GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR*/)
674                 printf(" no-error");
675             putchar('\n');
676 
677             printf("%s context flags parsed by GLFW:", get_api_name(client));
678 
679             if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT))
680                 printf(" forward-compatible");
681             if (glfwGetWindowAttrib(window, GLFW_OPENGL_DEBUG_CONTEXT))
682                 printf(" debug");
683             if (glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS) == GLFW_LOSE_CONTEXT_ON_RESET)
684                 printf(" robustness");
685             if (glfwGetWindowAttrib(window, GLFW_CONTEXT_NO_ERROR))
686                 printf(" no-error");
687             putchar('\n');
688         }
689 
690         if (major >= 4 || (major == 3 && minor >= 2))
691         {
692             GLint mask;
693             glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
694 
695             printf("%s profile mask (0x%08x): %s\n",
696                    get_api_name(client),
697                    mask,
698                    get_profile_name_gl(mask));
699 
700             printf("%s profile mask parsed by GLFW: %s\n",
701                    get_api_name(client),
702                    get_profile_name_glfw(profile));
703         }
704 
705         if (GLAD_GL_ARB_robustness)
706         {
707             const int robustness = glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS);
708             GLint strategy;
709             glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy);
710 
711             printf("%s robustness strategy (0x%08x): %s\n",
712                    get_api_name(client),
713                    strategy,
714                    get_strategy_name_gl(strategy));
715 
716             printf("%s robustness strategy parsed by GLFW: %s\n",
717                    get_api_name(client),
718                    get_strategy_name_glfw(robustness));
719         }
720     }
721 
722     printf("%s context renderer string: \"%s\"\n",
723            get_api_name(client),
724            glGetString(GL_RENDERER));
725     printf("%s context vendor string: \"%s\"\n",
726            get_api_name(client),
727            glGetString(GL_VENDOR));
728 
729     if (major >= 2)
730     {
731         printf("%s context shading language version: \"%s\"\n",
732                get_api_name(client),
733                glGetString(GL_SHADING_LANGUAGE_VERSION));
734     }
735 
736     printf("%s framebuffer:\n", get_api_name(client));
737 
738     if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE)
739     {
740         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
741                                               GL_BACK_LEFT,
742                                               GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE,
743                                               &redbits);
744         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
745                                               GL_BACK_LEFT,
746                                               GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
747                                               &greenbits);
748         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
749                                               GL_BACK_LEFT,
750                                               GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE,
751                                               &bluebits);
752         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
753                                               GL_BACK_LEFT,
754                                               GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE,
755                                               &alphabits);
756         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
757                                               GL_DEPTH,
758                                               GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE,
759                                               &depthbits);
760         glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER,
761                                               GL_STENCIL,
762                                               GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
763                                               &stencilbits);
764     }
765     else
766     {
767         glGetIntegerv(GL_RED_BITS, &redbits);
768         glGetIntegerv(GL_GREEN_BITS, &greenbits);
769         glGetIntegerv(GL_BLUE_BITS, &bluebits);
770         glGetIntegerv(GL_ALPHA_BITS, &alphabits);
771         glGetIntegerv(GL_DEPTH_BITS, &depthbits);
772         glGetIntegerv(GL_STENCIL_BITS, &stencilbits);
773     }
774 
775     printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n",
776            redbits, greenbits, bluebits, alphabits, depthbits, stencilbits);
777 
778     if (client == GLFW_OPENGL_ES_API ||
779         GLAD_GL_ARB_multisample ||
780         major > 1 || minor >= 3)
781     {
782         GLint samples, samplebuffers;
783         glGetIntegerv(GL_SAMPLES, &samples);
784         glGetIntegerv(GL_SAMPLE_BUFFERS, &samplebuffers);
785 
786         printf(" samples: %u sample buffers: %u\n", samples, samplebuffers);
787     }
788 
789     if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE)
790     {
791         GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits;
792         GLint auxbuffers;
793 
794         glGetIntegerv(GL_ACCUM_RED_BITS, &accumredbits);
795         glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumgreenbits);
796         glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbluebits);
797         glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumalphabits);
798         glGetIntegerv(GL_AUX_BUFFERS, &auxbuffers);
799 
800         printf(" accum red: %u accum green: %u accum blue: %u accum alpha: %u aux buffers: %u\n",
801                accumredbits, accumgreenbits, accumbluebits, accumalphabits, auxbuffers);
802     }
803 
804     if (list_extensions)
805         list_context_extensions(client, major, minor);
806 
807     printf("Vulkan loader: %s\n",
808            glfwVulkanSupported() ? "available" : "missing");
809 
810     if (glfwVulkanSupported())
811     {
812         uint32_t i, re_count, pd_count;
813         const char** re;
814         VkApplicationInfo ai = {0};
815         VkInstanceCreateInfo ici = {0};
816         VkInstance instance;
817         VkPhysicalDevice* pd;
818 
819         gladLoadVulkanUserPtr(NULL, glad_vulkan_callback, NULL);
820 
821         re = glfwGetRequiredInstanceExtensions(&re_count);
822 
823         printf("Vulkan required instance extensions:");
824         if (re)
825         {
826             for (i = 0;  i < re_count;  i++)
827                 printf(" %s", re[i]);
828             putchar('\n');
829         }
830         else
831             printf(" missing\n");
832 
833         if (list_extensions)
834             list_vulkan_instance_extensions();
835 
836         if (list_layers)
837             list_vulkan_instance_layers();
838 
839         ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
840         ai.pApplicationName = "glfwinfo";
841         ai.applicationVersion = GLFW_VERSION_MAJOR;
842         ai.pEngineName = "GLFW";
843         ai.engineVersion = GLFW_VERSION_MAJOR;
844         ai.apiVersion = VK_API_VERSION_1_0;
845 
846         ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
847         ici.pApplicationInfo = &ai;
848         ici.enabledExtensionCount = re_count;
849         ici.ppEnabledExtensionNames = re;
850 
851         if (vkCreateInstance(&ici, NULL, &instance) != VK_SUCCESS)
852         {
853             glfwTerminate();
854             exit(EXIT_FAILURE);
855         }
856 
857         gladLoadVulkanUserPtr(NULL, glad_vulkan_callback, instance);
858 
859         if (vkEnumeratePhysicalDevices(instance, &pd_count, NULL) != VK_SUCCESS)
860         {
861             vkDestroyInstance(instance, NULL);
862             glfwTerminate();
863             exit(EXIT_FAILURE);
864         }
865 
866         pd = calloc(pd_count, sizeof(VkPhysicalDevice));
867 
868         if (vkEnumeratePhysicalDevices(instance, &pd_count, pd) != VK_SUCCESS)
869         {
870             free(pd);
871             vkDestroyInstance(instance, NULL);
872             glfwTerminate();
873             exit(EXIT_FAILURE);
874         }
875 
876         for (i = 0;  i < pd_count;  i++)
877         {
878             VkPhysicalDeviceProperties pdp;
879 
880             vkGetPhysicalDeviceProperties(pd[i], &pdp);
881 
882             printf("Vulkan %s device: \"%s\"\n",
883                    get_device_type_name(pdp.deviceType),
884                    pdp.deviceName);
885 
886             if (list_extensions)
887                 list_vulkan_device_extensions(instance, pd[i]);
888 
889             if (list_layers)
890                 list_vulkan_device_layers(instance, pd[i]);
891         }
892 
893         free(pd);
894         vkDestroyInstance(instance, NULL);
895     }
896 
897     glfwTerminate();
898     exit(EXIT_SUCCESS);
899 }
900 
901