tree: drop clang-format off for attributes
[mesa-waffle.git] / src / utils / wflinfo.c
blob87d4a5e53e78c9c2b1943edde8d2c86a729b2624
1 // SPDX-FileCopyrightText: Copyright 2014 Intel Corporation
2 // SPDX-License-Identifier: BSD-2-Clause
4 /// @file
5 /// @brief Print OpenGL info using Waffle.
6 ///
7 /// This program does the following:
8 /// 1. Dynamically choose the platform and OpenGL API according to
9 /// command line arguments.
10 /// 2. Create an OpenGL context.
11 /// 3. Print information about the context.
13 #define WAFFLE_API_VERSION 0x0108
15 #include <assert.h>
16 #include <ctype.h>
17 #include <getopt.h>
18 #include <stdarg.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #ifdef __APPLE__
25 # import <Foundation/NSAutoreleasePool.h>
26 # import <AppKit/NSApplication.h>
28 static void
29 removeXcodeArgs(int *argc, char **argv);
30 #endif
32 #include "waffle.h"
34 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
36 static const char *usage_message =
37 "Usage:\n"
38 " wflinfo <Required Parameters> [Options]\n"
39 "\n"
40 "Description:\n"
41 " Create an OpenGL or OpenGL ES context and print information about it.\n"
42 "\n"
43 "Required Parameters:\n"
44 " -p, --platform <platform>\n"
45 " One of: android, cgl, gbm, glx, surfaceless_egl (or short\n"
46 " alias 'sl'), wayland, wgl, or x11_egl.\n"
47 "\n"
48 " -a, --api <api>\n"
49 " One of: gl, gles1, gles2 or gles3\n"
50 "\n"
51 "Options:\n"
52 " -V, --version <version>\n"
53 " For example --api gl --version 3.2 would request OpenGL 3.2.\n"
54 "\n"
55 " --profile <profile>\n"
56 " One of: core, compat or none\n"
57 "\n"
58 " -v, --verbose\n"
59 " Print more information.\n"
60 "\n"
61 " --forward-compatible\n"
62 " Create a forward-compatible context.\n"
63 "\n"
64 " --debug-context\n"
65 " Create a debug context.\n"
66 "\n"
67 " -f, --format <format>\n"
68 " One of: original (default) or json.\n"
69 "\n"
70 " -h, --help\n"
71 " Print wflinfo usage information.\n"
72 "\n"
73 "Examples:\n"
74 " wflinfo --platform glx --api gl\n"
75 " wflinfo --platform x11_egl --api gl --version 3.2 --profile core\n"
76 " wflinfo --platform wayland --api gles3\n"
77 " wflinfo --platform gbm --api gl --version 3.2 --verbose\n"
78 " wflinfo -p gbm -a gl -V 3.2 -v\n"
81 enum {
82 OPT_PLATFORM = 'p',
83 OPT_API = 'a',
84 OPT_VERSION = 'V',
85 OPT_PROFILE,
86 OPT_VERBOSE = 'v',
87 OPT_DEBUG_CONTEXT,
88 OPT_ROBUST_CONTEXT,
89 OPT_RESET_CONTEXT,
90 OPT_FORWARD_COMPATIBLE,
91 OPT_FORMAT = 'f',
92 OPT_HELP = 'h',
95 static const struct option get_opts[] = {
96 // clang-format off
97 { .name = "platform", .has_arg = required_argument, .val = OPT_PLATFORM },
98 { .name = "api", .has_arg = required_argument, .val = OPT_API },
99 { .name = "version", .has_arg = required_argument, .val = OPT_VERSION },
100 { .name = "profile", .has_arg = required_argument, .val = OPT_PROFILE },
101 { .name = "verbose", .has_arg = no_argument, .val = OPT_VERBOSE },
102 { .name = "debug-context", .has_arg = no_argument, .val = OPT_DEBUG_CONTEXT },
103 { .name = "robust-context", .has_arg = no_argument, .val = OPT_ROBUST_CONTEXT },
104 { .name = "reset-notification", .has_arg = no_argument, .val = OPT_RESET_CONTEXT },
105 { .name = "forward-compatible", .has_arg = no_argument, .val = OPT_FORWARD_COMPATIBLE },
106 { .name = "format", .has_arg = required_argument, .val = OPT_FORMAT },
107 { .name = "help", .has_arg = no_argument, .val = OPT_HELP },
108 { 0 },
109 // clang-format on
112 static bool
113 strneq(const char *a, const char *b, size_t n)
115 return strncmp(a, b, n) == 0;
118 #if defined(__GNUC__)
119 #define NORETURN __attribute__((noreturn))
120 #elif defined(_MSC_VER)
121 #define NORETURN __declspec(noreturn)
122 #else
123 #define NORETURN
124 #endif
126 #if defined(__clang__)
127 #define PRINTFLIKE(f, a) __attribute__((format(printf, f, a)))
128 #elif defined(__GNUC__)
129 #define PRINTFLIKE(f, a) __attribute__((format(gnu_printf, f, a)))
130 #else
131 #define PRINTFLIKE(f, a)
132 #endif
134 static void NORETURN
135 error_oom(void)
137 fprintf(stderr, "out of memory\n");
138 exit(EXIT_FAILURE);
141 static void NORETURN PRINTFLIKE(2, 3)
142 error_printf(const char *module, const char *fmt, ...)
144 va_list ap;
146 va_start(ap, fmt);
147 fprintf(stderr, "%s error: ", module);
148 vfprintf(stderr, fmt, ap);
149 fprintf(stderr, "\n");
150 va_end(ap);
152 exit(EXIT_FAILURE);
155 static void NORETURN
156 write_usage_and_exit(FILE *f, int exit_code)
158 fprintf(f, "%s", usage_message);
159 exit(exit_code);
162 static void NORETURN PRINTFLIKE(1, 2)
163 usage_error_printf(const char *fmt, ...)
165 fprintf(stderr, "Wflinfo usage error: ");
167 if (fmt) {
168 va_list ap;
169 va_start(ap, fmt);
170 vfprintf(stderr, fmt, ap);
171 va_end(ap);
172 fprintf(stderr, " ");
175 fprintf(stderr, "(see wflinfo --help)\n");
176 exit(EXIT_FAILURE);
179 static void NORETURN
180 error_waffle(void)
182 const struct waffle_error_info *info = waffle_error_get_info();
183 const char *code = waffle_error_to_string(info->code);
185 if (info->message_length > 0)
186 error_printf("Waffle", "0x%x %s: %s", info->code, code, info->message);
187 else
188 error_printf("Waffle", "0x%x %s", info->code, code);
191 static void NORETURN
192 error_get_gl_symbol(const char *name)
194 error_printf("Wflinfo", "failed to get function pointer for %s", name);
197 typedef float GLclampf;
198 typedef unsigned int GLbitfield;
199 typedef unsigned int GLint;
200 typedef int GLsizei;
201 typedef unsigned int GLenum;
202 typedef void GLvoid;
203 typedef unsigned char GLubyte;
205 enum {
206 // Copied from <GL/gl*.h>.
207 GL_NO_ERROR = 0,
209 GL_CONTEXT_FLAGS = 0x821e,
210 GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = 0x00000001,
211 GL_CONTEXT_FLAG_DEBUG_BIT = 0x00000002,
212 GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB = 0x00000004,
214 GL_VENDOR = 0x1F00,
215 GL_RENDERER = 0x1F01,
216 GL_VERSION = 0x1F02,
217 GL_EXTENSIONS = 0x1F03,
218 GL_NUM_EXTENSIONS = 0x821D,
219 GL_SHADING_LANGUAGE_VERSION = 0x8B8C,
222 #define WINDOW_WIDTH 320
223 #define WINDOW_HEIGHT 240
225 #define GL_MAJOR_VERSION 0x821B
226 #define GL_MINOR_VERSION 0x821C
227 #define GL_CONTEXT_PROFILE_MASK 0x9126
228 #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
229 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
231 #ifndef _WIN32
232 #define APIENTRY
233 #else
234 #ifndef APIENTRY
235 #define APIENTRY __stdcall
236 #endif
237 #endif
239 static GLenum (APIENTRY *glGetError)(void);
240 static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params);
241 static const GLubyte * (APIENTRY *glGetString)(GLenum name);
242 static const GLubyte * (APIENTRY *glGetStringi)(GLenum name, GLint i);
244 /// @brief Command line options.
245 struct options {
246 /// @brief One of `WAFFLE_PLATFORM_*`.
247 int platform;
249 /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
250 int context_api;
252 /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
253 int context_profile;
255 int context_major;
256 int context_minor;
258 bool verbose;
260 enum format {
261 FORMAT_ORIGINAL,
262 FORMAT_JSON,
263 } format;
265 bool context_forward_compatible;
266 bool context_debug;
267 bool context_robust;
268 bool context_reset;
270 /// @brief One of `WAFFLE_DL_*`.
271 int dl;
274 struct enum_map {
275 int i;
276 const char *s;
279 static const struct enum_map platform_map[] = {
280 {WAFFLE_PLATFORM_ANDROID, "android" },
281 {WAFFLE_PLATFORM_CGL, "cgl", },
282 {WAFFLE_PLATFORM_GBM, "gbm" },
283 {WAFFLE_PLATFORM_GLX, "glx" },
284 {WAFFLE_PLATFORM_WAYLAND, "wayland" },
285 {WAFFLE_PLATFORM_WGL, "wgl" },
286 {WAFFLE_PLATFORM_X11_EGL, "x11_egl" },
287 {WAFFLE_PLATFORM_SURFACELESS_EGL, "surfaceless_egl" },
288 {WAFFLE_PLATFORM_SURFACELESS_EGL, "sl" },
289 {0, 0 },
292 static const struct enum_map context_api_map[] = {
293 {WAFFLE_CONTEXT_OPENGL, "gl" },
294 {WAFFLE_CONTEXT_OPENGL_ES1, "gles1" },
295 {WAFFLE_CONTEXT_OPENGL_ES2, "gles2" },
296 {WAFFLE_CONTEXT_OPENGL_ES3, "gles3" },
297 {0, 0 },
300 /// @brief Translate string to `enum waffle_enum`.
302 /// @param self is a list of map items. The last item must be zero-filled.
303 /// @param result is altered only if @a s if found.
304 /// @return true if @a s was found in @a map.
305 static bool
306 enum_map_translate_str(
307 const struct enum_map *self,
308 const char *s,
309 int *result)
311 for (const struct enum_map *i = self; i->i != 0; ++i) {
312 if (!strncmp(s, i->s, strlen(i->s) + 1)) {
313 *result = i->i;
314 return true;
318 return false;
321 static const char *
322 enum_map_to_str(const struct enum_map *self,
323 int val)
325 for (const struct enum_map *i = self; i->i != 0; ++i) {
326 if (i->i == val) {
327 return i->s;
331 return NULL;
334 /// @return true on success.
335 static bool
336 parse_args(int argc, char *argv[], struct options *opts)
338 bool ok;
339 bool loop_get_opt = true;
341 #ifdef __APPLE__
342 removeXcodeArgs(&argc, argv);
343 #endif
345 // Set options to default values.
346 opts->context_profile = WAFFLE_NONE;
347 opts->context_major = WAFFLE_DONT_CARE;
348 opts->context_minor = WAFFLE_DONT_CARE;
349 opts->format = FORMAT_ORIGINAL;
351 // prevent getopt_long from printing an error message
352 opterr = 0;
354 while (loop_get_opt) {
355 int opt = getopt_long(argc, argv, "a:f:hp:vV:", get_opts, NULL);
356 switch (opt) {
357 case -1:
358 loop_get_opt = false;
359 break;
360 case '?':
361 goto error_unrecognized_arg;
362 case OPT_PLATFORM:
363 ok = enum_map_translate_str(platform_map, optarg,
364 &opts->platform);
365 if (!ok) {
366 usage_error_printf("'%s' is not a valid platform",
367 optarg);
369 break;
370 case OPT_API:
371 ok = enum_map_translate_str(context_api_map, optarg,
372 &opts->context_api);
373 if (!ok) {
374 usage_error_printf("'%s' is not a valid API for an OpenGL "
375 "context", optarg);
377 break;
378 case OPT_VERSION: {
379 int major;
380 int minor;
381 int match_count;
383 match_count = sscanf(optarg, "%d.%d", &major, &minor);
384 if (match_count != 2 || major < 0 || minor < 0) {
385 usage_error_printf("'%s' is not a valid OpenGL version",
386 optarg);
388 opts->context_major = major;
389 opts->context_minor = minor;
390 break;
392 case OPT_PROFILE:
393 if (strcmp(optarg, "none") == 0) {
394 opts->context_profile = WAFFLE_NONE;
395 } else if (strcmp(optarg, "core") == 0) {
396 opts->context_profile = WAFFLE_CONTEXT_CORE_PROFILE;
397 } else if (strcmp(optarg, "compat") == 0) {
398 opts->context_profile = WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
399 } else {
400 usage_error_printf("'%s' is not a valid OpenGL profile",
401 optarg);
403 break;
404 case OPT_FORMAT:
405 if (strcmp(optarg, "original") == 0) {
406 opts->format = FORMAT_ORIGINAL;
407 } else if (strcmp(optarg, "json") == 0) {
408 opts->format = FORMAT_JSON;
409 } else {
410 usage_error_printf("'%s' is not a valid format", optarg);
412 break;
413 case OPT_VERBOSE:
414 opts->verbose = true;
415 break;
416 case OPT_FORWARD_COMPATIBLE:
417 opts->context_forward_compatible = true;
418 break;
419 case OPT_DEBUG_CONTEXT:
420 opts->context_debug = true;
421 break;
422 case OPT_ROBUST_CONTEXT:
423 opts->context_robust = true;
424 break;
425 case OPT_RESET_CONTEXT:
426 opts->context_reset = true;
427 break;
428 case OPT_HELP:
429 write_usage_and_exit(stdout, EXIT_SUCCESS);
430 break;
431 default:
432 abort();
433 loop_get_opt = false;
434 break;
438 if (optind < argc) {
439 goto error_unrecognized_arg;
442 if (!opts->platform) {
443 usage_error_printf("--platform is required");
446 if (!opts->context_api) {
447 usage_error_printf("--api is required");
450 // Set dl.
451 switch (opts->context_api) {
452 case WAFFLE_CONTEXT_OPENGL: opts->dl = WAFFLE_DL_OPENGL; break;
453 case WAFFLE_CONTEXT_OPENGL_ES1: opts->dl = WAFFLE_DL_OPENGL_ES1; break;
454 case WAFFLE_CONTEXT_OPENGL_ES2: opts->dl = WAFFLE_DL_OPENGL_ES2; break;
455 case WAFFLE_CONTEXT_OPENGL_ES3: opts->dl = WAFFLE_DL_OPENGL_ES3; break;
456 default:
457 abort();
458 break;
461 return true;
463 error_unrecognized_arg:
464 if (optarg)
465 usage_error_printf("unrecognized option '%s'", optarg);
466 else if (optopt)
467 usage_error_printf("unrecognized option '-%c'", optopt);
468 else
469 usage_error_printf("unrecognized option");
472 static int
473 parse_version(const char *version)
475 int count, major, minor;
477 if (version == NULL)
478 return 0;
480 while (*version != '\0' && !isdigit(*version))
481 version++;
483 count = sscanf(version, "%d.%d", &major, &minor);
484 if (count != 2)
485 return 0;
487 if (minor > 9)
488 return 0;
490 return (major * 10) + minor;
493 static const char *
494 get_vendor(void)
496 const char *vendor = (const char *) glGetString(GL_VENDOR);
497 if (glGetError() != GL_NO_ERROR || vendor == NULL) {
498 vendor = "WFLINFO_GL_ERROR";
501 return vendor;
504 static const char *
505 get_renderer(void)
507 const char *renderer = (const char *) glGetString(GL_RENDERER);
508 if (glGetError() != GL_NO_ERROR || renderer == NULL) {
509 renderer = "WFLINFO_GL_ERROR";
512 return renderer;
515 static const char *
516 get_version(void)
518 const char *version_str = (const char *) glGetString(GL_VERSION);
519 if (glGetError() != GL_NO_ERROR || version_str == NULL) {
520 version_str = "WFLINFO_GL_ERROR";
523 return version_str;
526 static void
527 print_extensions(bool use_stringi)
529 GLint count = 0, i;
530 const char *ext;
532 printf("OpenGL extensions: ");
533 if (use_stringi) {
534 glGetIntegerv(GL_NUM_EXTENSIONS, &count);
535 if (glGetError() != GL_NO_ERROR) {
536 printf("WFLINFO_GL_ERROR");
537 } else {
538 for (i = 0; i < count; i++) {
539 ext = (const char *) glGetStringi(GL_EXTENSIONS, i);
540 if (glGetError() != GL_NO_ERROR)
541 ext = "WFLINFO_GL_ERROR";
542 printf("%s%s", ext, (i + 1) < count ? " " : "");
545 } else {
546 const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
547 if (glGetError() != GL_NO_ERROR)
548 printf("WFLINFO_GL_ERROR");
549 else
550 printf("%s", extensions);
552 printf("\n");
555 static const struct {
556 GLint flag;
557 const char *str;
558 } context_flags[] = {
559 { GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, "FORWARD_COMPATIBLE" },
560 { GL_CONTEXT_FLAG_DEBUG_BIT, "DEBUG" },
561 { GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, "ROBUST_ACCESS" },
564 static void
565 print_context_flags(void)
567 GLint gl_context_flags = 0;
569 printf("OpenGL context flags:");
571 glGetIntegerv(GL_CONTEXT_FLAGS, &gl_context_flags);
572 if (glGetError() != GL_NO_ERROR) {
573 printf(" WFLINFO_GL_ERROR\n");
574 return;
577 if (gl_context_flags == 0) {
578 printf(" 0x0\n");
579 return;
582 for (unsigned i = 0; i < ARRAY_SIZE(context_flags); i++) {
583 if ((context_flags[i].flag & gl_context_flags) != 0) {
584 printf(" %s", context_flags[i].str);
585 gl_context_flags = gl_context_flags & ~context_flags[i].flag;
588 for (int i = 0; gl_context_flags != 0; gl_context_flags >>= 1, i++) {
589 if ((gl_context_flags & 1) != 0) {
590 printf(" 0x%x", 1 << i);
593 printf("\n");
596 static void
597 json_print_extensions(bool use_stringi)
599 // Print extensions in JSON format
600 printf(" \"extensions\": [\n");
601 if (use_stringi) {
602 GLint count = 0;
603 const char *ext;
605 glGetIntegerv(GL_NUM_EXTENSIONS, &count);
606 if (glGetError() != GL_NO_ERROR) {
607 printf(" \"WFLINFO_GL_ERROR\"");
608 } else {
609 for (GLint i = 0; i < count; i++) {
610 ext = (const char *) glGetStringi(GL_EXTENSIONS, i);
611 if (glGetError() != GL_NO_ERROR)
612 ext = "WFLINFO_GL_ERROR";
613 printf(" \"%s\"%s\n", ext, (i + 1) < count ? "," : "");
616 } else {
617 const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
619 if (glGetError() != GL_NO_ERROR || !extensions) {
620 printf(" \"WFLINFO_GL_ERROR\"");
621 } else {
622 // Copy the string because strtok() is destructive.
623 char *splitter = strdup(extensions);
624 if (!splitter)
625 error_oom();
627 splitter = strtok(splitter, " ");
629 // The JSON doesn't strictly need to be newline seperated, but it
630 // makes it much easier to read. Split the lines and add commas
631 // correctly.
632 while (splitter) {
633 printf(" \"%s\"", splitter);
634 splitter = strtok(NULL, " ");
635 if (splitter) {
636 printf(",\n");
641 printf("\n");
644 printf(" ]\n");
647 /// @brief Print JSON formatted OpenGL (ES) information
648 static bool
649 print_json(const struct options *opts)
651 while (glGetError() != GL_NO_ERROR) {
652 /* Clear all errors */
655 const char *vendor = get_vendor();
656 const char *renderer = get_renderer();
657 const char *version_str = get_version();
659 const char *platform = enum_map_to_str(platform_map, opts->platform);
660 assert(platform != NULL);
662 const char *api = enum_map_to_str(context_api_map, opts->context_api);
663 assert(api != NULL);
665 // OpenGL and OpenGL ES >= 3.0 support glGetStringi(GL_EXTENSION, i).
666 const int version = parse_version(version_str);
667 const bool use_getstringi = version >= 30;
669 if (!glGetStringi && use_getstringi) {
670 error_get_gl_symbol("glGetStringi");
673 // See the equivalent section in print_wflinfo() for more info
674 const char *language_str = "None";
675 if ((opts->context_api == WAFFLE_CONTEXT_OPENGL && version >= 20)
676 || opts->context_api == WAFFLE_CONTEXT_OPENGL_ES2
677 || opts->context_api == WAFFLE_CONTEXT_OPENGL_ES3) {
678 language_str = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
679 if (glGetError() != GL_NO_ERROR || language_str == NULL) {
680 language_str = "WFLINFO_GL_ERROR";
684 printf("{\n");
685 printf(" \"waffle\": {\n");
686 printf(" \"platform\": \"%s\",\n", platform);
687 printf(" \"api\": \"%s\"\n", api);
688 printf(" },\n");
689 printf(" \"OpenGL\": {\n");
690 printf(" \"vendor string\": \"%s\",\n", vendor);
691 printf(" \"renderer string\": \"%s\",\n", renderer);
692 printf(" \"version string\": \"%s\",\n", version_str);
693 printf(" \"shading language version string\": \"%s\",\n", language_str);
695 json_print_extensions(use_getstringi);
697 printf(" }\n");
698 printf("}\n");
700 return true;
703 /// @brief Print out information about the context that was created.
704 static bool
705 print_wflinfo(const struct options *opts)
707 while (glGetError() != GL_NO_ERROR) {
708 /* Clear all errors */
711 const char *vendor = get_vendor();
712 const char *renderer = get_renderer();
713 const char *version_str = get_version();
715 const char *platform = enum_map_to_str(platform_map, opts->platform);
716 assert(platform != NULL);
717 printf("Waffle platform: %s\n", platform);
719 const char *api = enum_map_to_str(context_api_map, opts->context_api);
720 assert(api != NULL);
721 printf("Waffle api: %s\n", api);
723 printf("OpenGL vendor string: %s\n", vendor);
724 printf("OpenGL renderer string: %s\n", renderer);
725 printf("OpenGL version string: %s\n", version_str);
727 int version = parse_version(version_str);
729 if (opts->context_api == WAFFLE_CONTEXT_OPENGL && version >= 31) {
730 print_context_flags();
733 // OpenGL and OpenGL ES >= 3.0 support glGetStringi(GL_EXTENSION, i).
734 const bool use_getstringi = version >= 30;
736 if (!glGetStringi && use_getstringi)
737 error_get_gl_symbol("glGetStringi");
739 if (opts->verbose) {
740 // There are two exceptional cases where wflinfo may not get a
741 // version (or a valid version): one is in gles1 and the other
742 // is GL < 2.0. In these cases do not return WFLINFO_GL_ERROR,
743 // return None. This is preferable to returning WFLINFO_GL_ERROR
744 // because it creates a consistant interface for parsers
745 const char *language_str = "None";
746 if ((opts->context_api == WAFFLE_CONTEXT_OPENGL && version >= 20) ||
747 opts->context_api == WAFFLE_CONTEXT_OPENGL_ES2 ||
748 opts->context_api == WAFFLE_CONTEXT_OPENGL_ES3) {
749 language_str = (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION);
750 if (glGetError() != GL_NO_ERROR || language_str == NULL) {
751 language_str = "WFLINFO_GL_ERROR";
755 printf("OpenGL shading language version string: %s\n", language_str);
756 print_extensions(use_getstringi);
759 return true;
762 #ifdef __APPLE__
764 static NSAutoreleasePool *pool;
766 static void
767 cocoa_init(void)
769 // From the NSApplication Class Reference:
770 // [...] if you do need to use Cocoa classes within the main()
771 // function itself (other than to load nib files or to instantiate
772 // NSApplication), you should create an autorelease pool before using
773 // the classes and then release the pool when you’re done.
774 pool = [[NSAutoreleasePool alloc] init];
776 // From the NSApplication Class Reference:
777 // The sharedApplication class method initializes the display
778 // environment and connects your program to the window server and the
779 // display server.
781 // It also creates the singleton NSApp if it does not yet exist.
782 [NSApplication sharedApplication];
785 static void
786 cocoa_finish(void)
788 [pool drain];
791 static void
792 removeArg(int index, int *argc, char **argv)
794 --*argc;
795 for (; index < *argc; ++index)
796 argv[index] = argv[index + 1];
799 static void
800 removeXcodeArgs(int *argc, char **argv)
802 // Xcode sometimes adds additional arguments.
803 for (int i = 1; i < *argc; )
805 if (strcmp(argv[i], "-NSDocumentRevisionsDebugMode") == 0 ||
806 strcmp(argv[i], "-ApplePersistenceIgnoreState" ) == 0)
808 removeArg(i, argc, argv);
809 removeArg(i, argc, argv);
810 } else
811 ++i;
815 #endif // __APPLE__
817 /// @brief Attributes for waffle_choose_config().
818 struct wflinfo_config_attrs {
819 /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
820 enum waffle_enum api;
822 /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
823 enum waffle_enum profile;
825 /// @brief The version major number.
826 int32_t major;
828 /// @brief The version minor number.
829 int32_t minor;
831 /// @brief Create a forward-compatible context.
832 bool forward_compat;
834 /// @brief Create a debug context.
835 bool debug;
836 bool robust;
837 bool reset;
840 static bool
841 wflinfo_try_create_context(struct waffle_display *dpy,
842 struct wflinfo_config_attrs attrs,
843 struct waffle_context **out_ctx,
844 struct waffle_config **out_config,
845 bool exit_on_fail)
847 int i;
848 int32_t config_attrib_list[64];
849 struct waffle_context *ctx = NULL;
850 struct waffle_config *config = NULL;
852 i = 0;
853 config_attrib_list[i++] = WAFFLE_CONTEXT_API;
854 config_attrib_list[i++] = attrs.api;
856 if (attrs.profile != WAFFLE_DONT_CARE) {
857 config_attrib_list[i++] = WAFFLE_CONTEXT_PROFILE;
858 config_attrib_list[i++] = attrs.profile;
861 if (attrs.major != WAFFLE_DONT_CARE && attrs.minor != WAFFLE_DONT_CARE) {
862 config_attrib_list[i++] = WAFFLE_CONTEXT_MAJOR_VERSION;
863 config_attrib_list[i++] = attrs.major;
864 config_attrib_list[i++] = WAFFLE_CONTEXT_MINOR_VERSION;
865 config_attrib_list[i++] = attrs.minor;
868 if (attrs.forward_compat) {
869 config_attrib_list[i++] = WAFFLE_CONTEXT_FORWARD_COMPATIBLE;
870 config_attrib_list[i++] = true;
873 if (attrs.debug) {
874 config_attrib_list[i++] = WAFFLE_CONTEXT_DEBUG;
875 config_attrib_list[i++] = true;
878 if (attrs.robust) {
879 config_attrib_list[i++] = WAFFLE_CONTEXT_ROBUST_ACCESS;
880 config_attrib_list[i++] = true;
883 if (attrs.reset) {
884 config_attrib_list[i++] = WAFFLE_CONTEXT_LOSE_CONTEXT_ON_RESET;
885 config_attrib_list[i++] = true;
888 static int32_t dont_care_attribs[] = {
889 WAFFLE_RED_SIZE,
890 WAFFLE_GREEN_SIZE,
891 WAFFLE_BLUE_SIZE,
892 WAFFLE_ALPHA_SIZE,
893 WAFFLE_DEPTH_SIZE,
894 WAFFLE_STENCIL_SIZE,
895 WAFFLE_DOUBLE_BUFFERED,
897 int dont_care_attribs_count =
898 sizeof(dont_care_attribs) / sizeof(dont_care_attribs[0]);
900 for (int j = 0; j < dont_care_attribs_count; j++) {
901 config_attrib_list[i++] = dont_care_attribs[j];
902 config_attrib_list[i++] = WAFFLE_DONT_CARE;
905 config_attrib_list[i++] = 0;
907 config = waffle_config_choose(dpy, config_attrib_list);
908 if (!config) {
909 goto fail;
912 ctx = waffle_context_create(config, NULL);
913 if (!ctx) {
914 goto fail;
917 *out_ctx = ctx;
918 *out_config = config;
919 return true;
921 fail:
922 if (exit_on_fail) {
923 error_waffle();
925 if (ctx) {
926 waffle_context_destroy(ctx);
928 if (config) {
929 waffle_config_destroy(config);
932 return false;
935 /// @brief Return 10 * version of the current OpenGL context.
936 static int
937 gl_get_version(void)
939 GLint major_version = 0;
940 GLint minor_version = 0;
942 glGetIntegerv(GL_MAJOR_VERSION, &major_version);
943 if (glGetError()) {
944 error_printf("Wflinfo", "glGetIntegerv(GL_MAJOR_VERSION) failed");
947 glGetIntegerv(GL_MINOR_VERSION, &minor_version);
948 if (glGetError()) {
949 error_printf("Wflinfo", "glGetIntegerv(GL_MINOR_VERSION) failed");
951 return 10 * major_version + minor_version;
954 /// @brief Check if current context has an extension using glGetString().
955 static bool
956 gl_has_extension_GetString(const char *name)
958 #define BUF_LEN 4096
959 char exts[BUF_LEN];
961 const uint8_t *exts_orig = glGetString(GL_EXTENSIONS);
962 if (glGetError()) {
963 error_printf("Wflinfo", "glGetInteger(GL_EXTENSIONS) failed");
966 memcpy(exts, exts_orig, BUF_LEN);
967 exts[BUF_LEN - 1] = 0;
969 char *ext = strtok(exts, " ");
970 do {
971 if (strneq(ext, name, BUF_LEN)) {
972 return true;
974 ext = strtok(NULL, " ");
975 } while (ext);
977 return false;
978 #undef BUF_LEN
981 /// @brief Check if current context has an extension using glGetStringi().
982 static bool
983 gl_has_extension_GetStringi(const char *name)
985 const size_t max_ext_len = 128;
986 uint32_t num_exts = 0;
988 glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts);
989 if (glGetError()) {
990 error_printf("Wflinfo", "glGetIntegerv(GL_NUM_EXTENSIONS) failed");
993 for (uint32_t i = 0; i < num_exts; i++) {
994 const uint8_t *ext = glGetStringi(GL_EXTENSIONS, i);
995 if (!ext || glGetError()) {
996 error_printf("Wflinfo", "glGetStringi(GL_EXTENSIONS) failed");
997 } else if (strneq((const char*) ext, name, max_ext_len)) {
998 return true;
1002 return false;
1005 /// @brief Check if current context has an extension.
1006 static bool
1007 gl_has_extension(const char *name)
1009 // Use the new glGetStringi when possible.
1011 // Due to the funky semantics of obtaining the function pointer, we can
1012 // get here, even when it is NULL.
1013 if (glGetStringi && gl_get_version() >= 30) {
1014 return gl_has_extension_GetStringi(name);
1015 } else {
1016 return gl_has_extension_GetString(name);
1020 /// @brief Get the profile of a desktop OpenGL context.
1022 /// Return one of WAFFLE_CONTEXT_CORE_PROFILE,
1023 /// WAFFLE_CONTEXT_COMPATIBILITY_PROFILE, or WAFFLE_NONE.
1025 /// Even though an OpenGL 3.1 context strictly has no profile, according to
1026 /// this function a 3.1 context belongs to the core profile if and only if it
1027 /// lacks the GL_ARB_compatibility extension.
1029 /// According to this function, a context has no profile if and only if its
1030 /// version is 3.0 or lower.
1031 static enum waffle_enum
1032 gl_get_profile(void)
1034 int version = gl_get_version();
1036 if (version >= 32) {
1037 uint32_t profile_mask = 0;
1038 glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &profile_mask);
1039 if (glGetError()) {
1040 error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
1041 "failed");
1042 } else if (profile_mask & GL_CONTEXT_CORE_PROFILE_BIT) {
1043 return WAFFLE_CONTEXT_CORE_PROFILE;
1044 } else if (profile_mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) {
1045 return WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
1046 } else {
1047 error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
1048 "return a mask with no profile bit: 0x%x",
1049 profile_mask);
1051 } else if (version == 31) {
1052 if (gl_has_extension("GL_ARB_compatibility")) {
1053 return WAFFLE_CONTEXT_CORE_PROFILE;
1054 } else {
1055 return WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
1057 } else {
1058 return WAFFLE_NONE;
1062 /// @brief Create an OpenGL >= 3.1 context.
1064 /// If the requested profile is WAFFLE_NONE or WAFFLE_DONT_CARE and context
1065 /// creation succeeds, then return true.
1067 /// If a specific profile of OpenGL 3.1 is requested, then this function tries
1068 /// to honor the intent of that request even though, strictly speaking, an
1069 /// OpenGL 3.1 context has no profile. (See gl_get_profile() for a description
1070 /// of how wflinfo determines the profile of a context). If context creation
1071 /// succeeds but its profile is incorrect, then return false.
1073 /// On failure, @a out_ctx and @out_config remain unmodified.
1075 static bool
1076 wflinfo_try_create_context_gl31(struct waffle_display *dpy,
1077 struct wflinfo_config_attrs attrs,
1078 struct waffle_context **out_ctx,
1079 struct waffle_config **out_config,
1080 bool exit_if_ctx_creation_fails)
1082 struct waffle_config *config = NULL;
1083 struct waffle_context *ctx = NULL;
1084 bool ok;
1086 // It's illegal to request a waffle_config with WAFFLE_CONTEXT_PROFILE
1087 // != WAFFLE_NONE. Therefore, request an OpenGL 3.1 config without
1088 // a profile and later verify that the desired and actual profile
1089 // agree.
1090 const enum waffle_enum desired_profile = attrs.profile;
1091 attrs.major = 3;
1092 attrs.minor = 1;
1093 attrs.profile = WAFFLE_NONE;
1094 wflinfo_try_create_context(dpy, attrs, &ctx, &config,
1095 exit_if_ctx_creation_fails);
1097 if (desired_profile == WAFFLE_NONE ||
1098 desired_profile == WAFFLE_DONT_CARE) {
1099 goto success;
1102 // The user cares about the profile. We must bind the context to inspect
1103 // its profile.
1105 // Skip window creation. No window is needed when binding an OpenGL >= 3.0
1106 // context.
1107 ok = waffle_make_current(dpy, NULL, ctx);
1108 if (!ok) {
1109 error_waffle();
1112 const enum waffle_enum actual_profile = gl_get_profile();
1113 waffle_make_current(dpy, NULL, NULL);
1114 if (actual_profile == desired_profile) {
1115 goto success;
1118 return false;
1120 success:
1121 *out_ctx = ctx;
1122 *out_config = config;
1123 return true;
1126 /// Exit on failure.
1127 static void
1128 wflinfo_create_context(struct waffle_display *dpy,
1129 struct wflinfo_config_attrs attrs,
1130 struct waffle_context **out_ctx,
1131 struct waffle_config **out_config)
1133 bool ok = false;
1135 if (attrs.api == WAFFLE_CONTEXT_OPENGL &&
1136 attrs.profile != WAFFLE_NONE &&
1137 attrs.major == WAFFLE_DONT_CARE) {
1139 // If the user requested OpenGL and a CORE or COMPAT profile,
1140 // but they didn't specify a version, then we'll try a set
1141 // of known versions from highest to lowest.
1143 static int known_gl_profile_versions[] =
1144 { 32, 33, 40, 41, 42, 43, 44 };
1146 for (int i = ARRAY_SIZE(known_gl_profile_versions) - 1; i >= 0; i--) {
1147 attrs.major = known_gl_profile_versions[i] / 10;
1148 attrs.minor = known_gl_profile_versions[i] % 10;
1149 ok = wflinfo_try_create_context(dpy, attrs,
1150 out_ctx, out_config, false);
1151 if (ok) {
1152 return;
1156 // Handle OpenGL 3.1 separately because profiles are weird in 3.1.
1157 ok = wflinfo_try_create_context_gl31(
1158 dpy, attrs, out_ctx, out_config,
1159 /*exit_if_ctx_creation_fails*/ false);
1160 if (ok) {
1161 return;
1164 error_printf("Wflinfo", "Failed to create context; Try choosing a "
1165 "specific context version with --version");
1166 } else if (attrs.api == WAFFLE_CONTEXT_OPENGL &&
1167 attrs.major == 3 &&
1168 attrs.minor == 1) {
1169 // The user requested a specific profile of an OpenGL 3.1 context.
1170 // Strictly speaking, an OpenGL 3.1 context has no profile, but let's
1171 // do what the user wants.
1172 ok = wflinfo_try_create_context_gl31(
1173 dpy, attrs, out_ctx, out_config,
1174 /*exit_if_ctx_creation_fails*/ true);
1175 if (ok) {
1176 return;
1179 printf("Wflinfo warn: Successfully requested an OpenGL 3.1 context, but returned\n"
1180 "Wflinfo warn: context had the wrong profile. Fallback to requesting an\n"
1181 "Wflinfo warn: OpenGL 3.2 context, which is guaranteed to have the correct\n"
1182 "Wflinfo warn: profile if context creation succeeds.\n");
1183 attrs.major = 3;
1184 attrs.minor = 2;
1185 assert(attrs.profile == WAFFLE_CONTEXT_CORE_PROFILE ||
1186 attrs.profile == WAFFLE_CONTEXT_COMPATIBILITY_PROFILE);
1187 ok = wflinfo_try_create_context(dpy, attrs, out_ctx, out_config,
1188 /*exit_on_fail*/ false);
1189 if (ok) {
1190 return;
1193 error_printf("Wflinfo", "Failed to create an OpenGL 3.1 or later "
1194 "context with requested profile");
1195 } else {
1196 wflinfo_try_create_context(dpy, attrs, out_ctx, out_config,
1197 /*exit_on_fail*/ true);
1202 main(int argc, char **argv)
1204 bool ok;
1205 int i;
1207 struct options opts = {0};
1209 int32_t init_attrib_list[3];
1211 struct waffle_display *dpy;
1212 struct waffle_config *config;
1213 struct waffle_context *ctx;
1214 struct waffle_window *window;
1216 #ifdef __APPLE__
1217 cocoa_init();
1218 #endif
1220 ok = parse_args(argc, argv, &opts);
1221 if (!ok)
1222 exit(EXIT_FAILURE);
1224 i = 0;
1225 init_attrib_list[i++] = WAFFLE_PLATFORM;
1226 init_attrib_list[i++] = opts.platform;
1227 init_attrib_list[i++] = WAFFLE_NONE;
1229 ok = waffle_init(init_attrib_list);
1230 if (!ok)
1231 error_waffle();
1233 dpy = waffle_display_connect(NULL);
1234 if (!dpy)
1235 error_waffle();
1237 if (!waffle_display_supports_context_api(dpy, opts.context_api)) {
1238 error_printf("Wflinfo", "Display does not support %s",
1239 waffle_enum_to_string(opts.context_api));
1242 glGetError = waffle_dl_sym(opts.dl, "glGetError");
1243 if (!glGetError)
1244 error_get_gl_symbol("glGetError");
1246 glGetIntegerv = waffle_dl_sym(opts.dl, "glGetIntegerv");
1247 if (!glGetIntegerv)
1248 error_get_gl_symbol("glGetIntegerv");
1250 glGetString = waffle_dl_sym(opts.dl, "glGetString");
1251 if (!glGetString) {
1252 error_get_gl_symbol("glGetString");
1255 const struct wflinfo_config_attrs config_attrs = {
1256 .api = opts.context_api,
1257 .profile = opts.context_profile,
1258 .major = opts.context_major,
1259 .minor = opts.context_minor,
1260 .forward_compat = opts.context_forward_compatible,
1261 .debug = opts.context_debug,
1262 .robust = opts.context_robust,
1263 .reset = opts.context_reset,
1266 wflinfo_create_context(dpy, config_attrs, &ctx, &config);
1268 window = waffle_window_create(config, WINDOW_WIDTH, WINDOW_HEIGHT);
1269 if (!window)
1270 error_waffle();
1272 ok = waffle_make_current(dpy, window, ctx);
1273 if (!ok)
1274 error_waffle();
1276 // Retrieving GL functions is tricky. When glGetStringi is supported, here
1277 // are some boggling variations as of 2014-11-19:
1278 // - Mali drivers on EGL 1.4 expose glGetStringi statically from
1279 // libGLESv2 but not dynamically from eglGetProcAddress. The EGL 1.4 spec
1280 // permits this behavior.
1281 // - EGL 1.5 requires that eglGetStringi be exposed dynamically through
1282 // eglGetProcAddress. Exposing statically with dlsym is optional.
1283 // - Windows requires that glGetStringi be exposed dynamically from
1284 // wglGetProcAddress. Exposing statically from GetProcAddress (Window's
1285 // dlsym equivalent) is optional.
1286 // - Mesa drivers expose glGetStringi statically from libGL and libGLESv2
1287 // and dynamically from eglGetProcAddress and glxGetProcAddress.
1288 // - Mac exposes glGetStringi only statically.
1290 // Try waffle_dl_sym before waffle_get_proc_address because
1291 // (1) egl/glXProcAddress can return invalid non-null pointers for
1292 // unsupported functions and (2) dlsym returns non-null if and only if the
1293 // library exposes the symbol.
1294 glGetStringi = waffle_dl_sym(opts.dl, "glGetStringi");
1295 if (!glGetStringi) {
1296 glGetStringi = waffle_get_proc_address("glGetStringi");
1299 switch (opts.format) {
1300 case FORMAT_ORIGINAL:
1301 ok = print_wflinfo(&opts);
1302 break;
1303 case FORMAT_JSON:
1304 ok = print_json(&opts);
1305 break;
1308 if (!ok)
1309 error_waffle();
1311 ok = waffle_make_current(dpy, NULL, NULL);
1312 if (!ok)
1313 error_waffle();
1315 ok = waffle_window_destroy(window);
1316 if (!ok)
1317 error_waffle();
1319 ok = waffle_context_destroy(ctx);
1320 if (!ok)
1321 error_waffle();
1323 ok = waffle_config_destroy(config);
1324 if (!ok)
1325 error_waffle();
1327 ok = waffle_display_disconnect(dpy);
1328 if (!ok)
1329 error_waffle();
1331 ok = waffle_teardown();
1332 if (!ok)
1333 error_waffle();
1335 #ifdef __APPLE__
1336 cocoa_finish();
1337 #endif
1339 return EXIT_SUCCESS;