1 // SPDX-FileCopyrightText: Copyright 2014 Intel Corporation
2 // SPDX-License-Identifier: BSD-2-Clause
5 /// @brief Print OpenGL info using Waffle.
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
25 # import <Foundation/NSAutoreleasePool.h>
26 # import <AppKit/NSApplication.h>
29 removeXcodeArgs(int *argc
, char **argv
);
34 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
36 static const char *usage_message
=
38 " wflinfo <Required Parameters> [Options]\n"
41 " Create an OpenGL or OpenGL ES context and print information about it.\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"
49 " One of: gl, gles1, gles2 or gles3\n"
52 " -V, --version <version>\n"
53 " For example --api gl --version 3.2 would request OpenGL 3.2.\n"
55 " --profile <profile>\n"
56 " One of: core, compat or none\n"
59 " Print more information.\n"
61 " --forward-compatible\n"
62 " Create a forward-compatible context.\n"
65 " Create a debug context.\n"
67 " -f, --format <format>\n"
68 " One of: original (default) or json.\n"
71 " Print wflinfo usage information.\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"
90 OPT_FORWARD_COMPATIBLE
,
95 static const struct option get_opts
[] = {
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
},
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)
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)))
131 #define PRINTFLIKE(f, a)
137 fprintf(stderr
, "out of memory\n");
141 static void NORETURN
PRINTFLIKE(2, 3)
142 error_printf(const char *module
, const char *fmt
, ...)
147 fprintf(stderr
, "%s error: ", module
);
148 vfprintf(stderr
, fmt
, ap
);
149 fprintf(stderr
, "\n");
156 write_usage_and_exit(FILE *f
, int exit_code
)
158 fprintf(f
, "%s", usage_message
);
162 static void NORETURN
PRINTFLIKE(1, 2)
163 usage_error_printf(const char *fmt
, ...)
165 fprintf(stderr
, "Wflinfo usage error: ");
170 vfprintf(stderr
, fmt
, ap
);
172 fprintf(stderr
, " ");
175 fprintf(stderr
, "(see wflinfo --help)\n");
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
);
188 error_printf("Waffle", "0x%x %s", info
->code
, code
);
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
;
201 typedef unsigned int GLenum
;
203 typedef unsigned char GLubyte
;
206 // Copied from <GL/gl*.h>.
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,
215 GL_RENDERER
= 0x1F01,
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
235 #define APIENTRY __stdcall
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.
246 /// @brief One of `WAFFLE_PLATFORM_*`.
249 /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
252 /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
265 bool context_forward_compatible
;
270 /// @brief One of `WAFFLE_DL_*`.
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" },
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" },
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.
306 enum_map_translate_str(
307 const struct enum_map
*self
,
311 for (const struct enum_map
*i
= self
; i
->i
!= 0; ++i
) {
312 if (!strncmp(s
, i
->s
, strlen(i
->s
) + 1)) {
322 enum_map_to_str(const struct enum_map
*self
,
325 for (const struct enum_map
*i
= self
; i
->i
!= 0; ++i
) {
334 /// @return true on success.
336 parse_args(int argc
, char *argv
[], struct options
*opts
)
339 bool loop_get_opt
= true;
342 removeXcodeArgs(&argc
, argv
);
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
354 while (loop_get_opt
) {
355 int opt
= getopt_long(argc
, argv
, "a:f:hp:vV:", get_opts
, NULL
);
358 loop_get_opt
= false;
361 goto error_unrecognized_arg
;
363 ok
= enum_map_translate_str(platform_map
, optarg
,
366 usage_error_printf("'%s' is not a valid platform",
371 ok
= enum_map_translate_str(context_api_map
, optarg
,
374 usage_error_printf("'%s' is not a valid API for an OpenGL "
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",
388 opts
->context_major
= major
;
389 opts
->context_minor
= minor
;
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
;
400 usage_error_printf("'%s' is not a valid OpenGL profile",
405 if (strcmp(optarg
, "original") == 0) {
406 opts
->format
= FORMAT_ORIGINAL
;
407 } else if (strcmp(optarg
, "json") == 0) {
408 opts
->format
= FORMAT_JSON
;
410 usage_error_printf("'%s' is not a valid format", optarg
);
414 opts
->verbose
= true;
416 case OPT_FORWARD_COMPATIBLE
:
417 opts
->context_forward_compatible
= true;
419 case OPT_DEBUG_CONTEXT
:
420 opts
->context_debug
= true;
422 case OPT_ROBUST_CONTEXT
:
423 opts
->context_robust
= true;
425 case OPT_RESET_CONTEXT
:
426 opts
->context_reset
= true;
429 write_usage_and_exit(stdout
, EXIT_SUCCESS
);
433 loop_get_opt
= false;
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");
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;
463 error_unrecognized_arg
:
465 usage_error_printf("unrecognized option '%s'", optarg
);
467 usage_error_printf("unrecognized option '-%c'", optopt
);
469 usage_error_printf("unrecognized option");
473 parse_version(const char *version
)
475 int count
, major
, minor
;
480 while (*version
!= '\0' && !isdigit(*version
))
483 count
= sscanf(version
, "%d.%d", &major
, &minor
);
490 return (major
* 10) + minor
;
496 const char *vendor
= (const char *) glGetString(GL_VENDOR
);
497 if (glGetError() != GL_NO_ERROR
|| vendor
== NULL
) {
498 vendor
= "WFLINFO_GL_ERROR";
507 const char *renderer
= (const char *) glGetString(GL_RENDERER
);
508 if (glGetError() != GL_NO_ERROR
|| renderer
== NULL
) {
509 renderer
= "WFLINFO_GL_ERROR";
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";
527 print_extensions(bool use_stringi
)
532 printf("OpenGL extensions: ");
534 glGetIntegerv(GL_NUM_EXTENSIONS
, &count
);
535 if (glGetError() != GL_NO_ERROR
) {
536 printf("WFLINFO_GL_ERROR");
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
? " " : "");
546 const char *extensions
= (const char *) glGetString(GL_EXTENSIONS
);
547 if (glGetError() != GL_NO_ERROR
)
548 printf("WFLINFO_GL_ERROR");
550 printf("%s", extensions
);
555 static const struct {
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" },
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");
577 if (gl_context_flags
== 0) {
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
);
597 json_print_extensions(bool use_stringi
)
599 // Print extensions in JSON format
600 printf(" \"extensions\": [\n");
605 glGetIntegerv(GL_NUM_EXTENSIONS
, &count
);
606 if (glGetError() != GL_NO_ERROR
) {
607 printf(" \"WFLINFO_GL_ERROR\"");
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
? "," : "");
617 const char *extensions
= (const char *) glGetString(GL_EXTENSIONS
);
619 if (glGetError() != GL_NO_ERROR
|| !extensions
) {
620 printf(" \"WFLINFO_GL_ERROR\"");
622 // Copy the string because strtok() is destructive.
623 char *splitter
= strdup(extensions
);
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
633 printf(" \"%s\"", splitter
);
634 splitter
= strtok(NULL
, " ");
647 /// @brief Print JSON formatted OpenGL (ES) information
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
);
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";
685 printf(" \"waffle\": {\n");
686 printf(" \"platform\": \"%s\",\n", platform
);
687 printf(" \"api\": \"%s\"\n", api
);
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
);
703 /// @brief Print out information about the context that was created.
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
);
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");
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
);
764 static NSAutoreleasePool
*pool
;
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
781 // It also creates the singleton NSApp if it does not yet exist.
782 [NSApplication sharedApplication
];
792 removeArg(int index
, int *argc
, char **argv
)
795 for (; index
< *argc
; ++index
)
796 argv
[index
] = argv
[index
+ 1];
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
);
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.
828 /// @brief The version minor number.
831 /// @brief Create a forward-compatible context.
834 /// @brief Create a debug context.
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
,
848 int32_t config_attrib_list
[64];
849 struct waffle_context
*ctx
= NULL
;
850 struct waffle_config
*config
= NULL
;
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;
874 config_attrib_list
[i
++] = WAFFLE_CONTEXT_DEBUG
;
875 config_attrib_list
[i
++] = true;
879 config_attrib_list
[i
++] = WAFFLE_CONTEXT_ROBUST_ACCESS
;
880 config_attrib_list
[i
++] = true;
884 config_attrib_list
[i
++] = WAFFLE_CONTEXT_LOSE_CONTEXT_ON_RESET
;
885 config_attrib_list
[i
++] = true;
888 static int32_t dont_care_attribs
[] = {
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
);
912 ctx
= waffle_context_create(config
, NULL
);
918 *out_config
= config
;
926 waffle_context_destroy(ctx
);
929 waffle_config_destroy(config
);
935 /// @brief Return 10 * version of the current OpenGL context.
939 GLint major_version
= 0;
940 GLint minor_version
= 0;
942 glGetIntegerv(GL_MAJOR_VERSION
, &major_version
);
944 error_printf("Wflinfo", "glGetIntegerv(GL_MAJOR_VERSION) failed");
947 glGetIntegerv(GL_MINOR_VERSION
, &minor_version
);
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().
956 gl_has_extension_GetString(const char *name
)
961 const uint8_t *exts_orig
= glGetString(GL_EXTENSIONS
);
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
, " ");
971 if (strneq(ext
, name
, BUF_LEN
)) {
974 ext
= strtok(NULL
, " ");
981 /// @brief Check if current context has an extension using glGetStringi().
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
);
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
)) {
1005 /// @brief Check if current context has an extension.
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
);
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
);
1040 error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
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
;
1047 error_printf("Wflinfo", "glGetIntegerv(GL_CONTEXT_PROFILE_MASK) "
1048 "return a mask with no profile bit: 0x%x",
1051 } else if (version
== 31) {
1052 if (gl_has_extension("GL_ARB_compatibility")) {
1053 return WAFFLE_CONTEXT_CORE_PROFILE
;
1055 return WAFFLE_CONTEXT_COMPATIBILITY_PROFILE
;
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.
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
;
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
1090 const enum waffle_enum desired_profile
= attrs
.profile
;
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
) {
1102 // The user cares about the profile. We must bind the context to inspect
1105 // Skip window creation. No window is needed when binding an OpenGL >= 3.0
1107 ok
= waffle_make_current(dpy
, NULL
, ctx
);
1112 const enum waffle_enum actual_profile
= gl_get_profile();
1113 waffle_make_current(dpy
, NULL
, NULL
);
1114 if (actual_profile
== desired_profile
) {
1122 *out_config
= config
;
1126 /// Exit on failure.
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
)
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);
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);
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
&&
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);
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");
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);
1193 error_printf("Wflinfo", "Failed to create an OpenGL 3.1 or later "
1194 "context with requested profile");
1196 wflinfo_try_create_context(dpy
, attrs
, out_ctx
, out_config
,
1197 /*exit_on_fail*/ true);
1202 main(int argc
, char **argv
)
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
;
1220 ok
= parse_args(argc
, argv
, &opts
);
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
);
1233 dpy
= waffle_display_connect(NULL
);
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");
1244 error_get_gl_symbol("glGetError");
1246 glGetIntegerv
= waffle_dl_sym(opts
.dl
, "glGetIntegerv");
1248 error_get_gl_symbol("glGetIntegerv");
1250 glGetString
= waffle_dl_sym(opts
.dl
, "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
);
1272 ok
= waffle_make_current(dpy
, window
, ctx
);
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
);
1304 ok
= print_json(&opts
);
1311 ok
= waffle_make_current(dpy
, NULL
, NULL
);
1315 ok
= waffle_window_destroy(window
);
1319 ok
= waffle_context_destroy(ctx
);
1323 ok
= waffle_config_destroy(config
);
1327 ok
= waffle_display_disconnect(dpy
);
1331 ok
= waffle_teardown();
1339 return EXIT_SUCCESS
;