1 // Copyright 2012 - 2014 Intel Corporation
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
8 // - Redistributions of source code must retain the above copyright notice, this
9 // list of conditions and the following disclaimer.
11 // - Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /// @brief Do some basic OpenGL rendering using Waffle.
29 /// This example does the following:
30 /// 1. Dynamically choose the platform and OpenGL API according to
31 /// command line arguments.
32 /// 2. Create a window and OpenGL context.
33 /// 3. Fill the window with red, then green, then blue, sleeping between
36 #define _POSIX_C_SOURCE 199309L // glibc feature macro for nanosleep.
37 #define WAFFLE_API_VERSION 0x0108
38 #define WAFFLE_API_EXPERIMENTAL
54 # import <Foundation/NSAutoreleasePool.h>
55 # import <AppKit/NSApplication.h>
58 removeXcodeArgs(int *argc
, char **argv
);
63 static const char *usage_message
=
65 " gl_basic --platform=android|cgl|gbm|glx|wayland|wgl|surfaceless_egl|x11_egl\n"
66 " --api=gl|gles1|gles2|gles3\n"
67 " [--version=MAJOR.MINOR]\n"
68 " [--profile=core|compat|none]\n"
69 " [--forward-compatible]\n"
72 " [--resize-window]\n"
73 " [--window-size=WIDTHxHEIGHT | --fullscreen]\n"
76 " gl_basic --platform=glx --api=gl\n"
77 " gl_basic --platform=x11_egl --api=gl --version=3.2 --profile=core\n"
78 " gl_basic --platform=wayland --api=gles3\n"
81 " Create a window. Fill it with red, green, then blue.\n"
84 " --forward-compatible\n"
85 " Create a forward-compatible context.\n"
88 " Create a debug context.\n"
91 " Create a robust context.\n"
94 " Resize the window between each draw call.\n"
97 " Create a fullscreen window.\n"
106 OPT_FORWARD_COMPATIBLE
,
113 static const struct option get_opts
[] = {
114 { .name
= "platform", .has_arg
= required_argument
, .val
= OPT_PLATFORM
},
115 { .name
= "api", .has_arg
= required_argument
, .val
= OPT_API
},
116 { .name
= "version", .has_arg
= required_argument
, .val
= OPT_VERSION
},
117 { .name
= "profile", .has_arg
= required_argument
, .val
= OPT_PROFILE
},
118 { .name
= "debug", .has_arg
= no_argument
, .val
= OPT_DEBUG
},
119 { .name
= "forward-compatible", .has_arg
= no_argument
, .val
= OPT_FORWARD_COMPATIBLE
},
120 { .name
= "robust", .has_arg
= no_argument
, .val
= OPT_ROBUST
},
121 { .name
= "resize-window", .has_arg
= no_argument
, .val
= OPT_RESIZE_WINDOW
},
122 { .name
= "window-size", .has_arg
= required_argument
, .val
= OPT_WINDOW_SIZE
},
123 { .name
= "fullscreen", .has_arg
= no_argument
, .val
= OPT_FULLSCREEN
},
127 #if defined(__GNUC__)
128 #define NORETURN __attribute__((noreturn))
129 #elif defined(_MSC_VER)
130 #define NORETURN __declspec(noreturn)
135 #if defined(__GNUC__)
136 #define PRINTFLIKE(f, a) __attribute__((__format__(__printf__, f, a)))
138 #define PRINTFLIKE(f, a)
141 static void NORETURN
PRINTFLIKE(1, 2) error_printf(const char *fmt
, ...)
148 fprintf(stderr
, "gl_basic: error: ");
149 vfprintf(stderr
, fmt
, ap
);
150 fprintf(stderr
, "\n");
156 static void NORETURN
PRINTFLIKE(1, 2) usage_error_printf(const char *fmt
, ...)
159 fprintf(stderr
, "gl_basic: usage error");
164 fprintf(stderr
, ": ");
165 vfprintf(stderr
, fmt
, ap
);
169 fprintf(stderr
, "\n");
170 fprintf(stderr
, "\n");
171 fprintf(stderr
, "%s", usage_message
);
179 const struct waffle_error_info
*info
= waffle_error_get_info();
180 const char *code
= waffle_error_to_string(info
->code
);
182 if (info
->message_length
> 0)
183 error_printf("%s: %s", code
, info
->message
);
185 error_printf("%s", code
);
189 error_get_gl_symbol(const char *name
)
191 error_printf("failed to get function pointer for %s", name
);
194 typedef float GLclampf
;
195 typedef unsigned int GLbitfield
;
196 typedef unsigned int GLint
;
198 typedef unsigned int GLenum
;
202 // Copied from <GL/gl*.h>.
203 GL_UNSIGNED_BYTE
= 0x00001401,
204 GL_RGBA
= 0x00001908,
205 GL_COLOR_BUFFER_BIT
= 0x00004000,
207 GL_CONTEXT_FLAGS
= 0x821e,
208 GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
= 0x00000001,
209 GL_CONTEXT_FLAG_DEBUG_BIT
= 0x00000002,
210 GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT
= 0x00000004 ,
213 static int window_width
= 320;
214 static int window_height
= 240;
220 #define APIENTRY __stdcall
224 static void (APIENTRY
*glClearColor
)(GLclampf red
, GLclampf green
, GLclampf blue
, GLclampf alpha
);
225 static void (APIENTRY
*glClear
)(GLbitfield mask
);
226 static GLenum (APIENTRY
*glGetError
)(void);
227 static void (APIENTRY
*glGetIntegerv
)(GLenum pname
, GLint
*params
);
228 static void (APIENTRY
*glReadPixels
)(GLint x
, GLint y
, GLsizei width
, GLsizei height
,
229 GLenum format
, GLenum type
, GLvoid
* data
);
230 static void (APIENTRY
*glViewport
)(GLint x
, GLint y
, GLsizei width
, GLsizei height
);
232 /// @brief Command line options.
234 /// @brief One of `WAFFLE_PLATFORM_*`.
237 /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
240 /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
245 bool context_forward_compatible
;
253 /// @brief One of `WAFFLE_DL_*`.
262 static const struct enum_map platform_map
[] = {
263 {WAFFLE_PLATFORM_ANDROID
, "android" },
264 {WAFFLE_PLATFORM_CGL
, "cgl", },
265 {WAFFLE_PLATFORM_GBM
, "gbm" },
266 {WAFFLE_PLATFORM_GLX
, "glx" },
267 {WAFFLE_PLATFORM_WAYLAND
, "wayland" },
268 {WAFFLE_PLATFORM_WGL
, "wgl" },
269 {WAFFLE_PLATFORM_SURFACELESS_EGL
, "surfaceless_egl" },
270 {WAFFLE_PLATFORM_X11_EGL
, "x11_egl" },
274 static const struct enum_map context_api_map
[] = {
275 {WAFFLE_CONTEXT_OPENGL
, "gl" },
276 {WAFFLE_CONTEXT_OPENGL_ES1
, "gles1" },
277 {WAFFLE_CONTEXT_OPENGL_ES2
, "gles2" },
278 {WAFFLE_CONTEXT_OPENGL_ES3
, "gles3" },
282 /// @brief Translate string to `enum waffle_enum`.
284 /// @param self is a list of map items. The last item must be zero-filled.
285 /// @param result is altered only if @a s if found.
286 /// @return true if @a s was found in @a map.
288 enum_map_translate_str(
289 const struct enum_map
*self
,
293 for (const struct enum_map
*i
= self
; i
->i
!= 0; ++i
) {
294 if (!strncmp(s
, i
->s
, strlen(i
->s
) + 1)) {
303 /// @return true on success.
305 parse_args(int argc
, char *argv
[], struct options
*opts
)
308 bool loop_get_opt
= true;
309 bool found_window_size
= false;
312 removeXcodeArgs(&argc
, argv
);
315 // Set some context attrs to invalid values.
316 opts
->context_profile
= -1;
317 opts
->context_version
= -1;
319 while (loop_get_opt
) {
320 int opt
= getopt_long(argc
, argv
, "", get_opts
, NULL
);
323 loop_get_opt
= false;
326 goto error_unrecognized_arg
;
328 ok
= enum_map_translate_str(platform_map
, optarg
,
331 usage_error_printf("'%s' is not a valid platform",
336 ok
= enum_map_translate_str(context_api_map
, optarg
,
339 usage_error_printf("'%s' is not a valid API for an OpenGL "
348 match_count
= sscanf(optarg
, "%d.%d", &major
, &minor
);
349 if (match_count
!= 2) {
350 usage_error_printf("'%s' is not a valid OpenGL version",
353 opts
->context_version
= 10 * major
+ minor
;
357 if (strcmp(optarg
, "none") == 0) {
358 opts
->context_profile
= WAFFLE_NONE
;
359 } else if (strcmp(optarg
, "core") == 0) {
360 opts
->context_profile
= WAFFLE_CONTEXT_CORE_PROFILE
;
361 } else if (strcmp(optarg
, "compat") == 0) {
362 opts
->context_profile
= WAFFLE_CONTEXT_COMPATIBILITY_PROFILE
;
364 usage_error_printf("'%s' is not a valid OpenGL profile",
368 case OPT_FORWARD_COMPATIBLE
:
369 opts
->context_forward_compatible
= true;
372 opts
->context_debug
= true;
375 opts
->context_robust
= true;
377 case OPT_RESIZE_WINDOW
:
378 opts
->resize_window
= true;
380 case OPT_WINDOW_SIZE
: {
382 match_count
= sscanf(optarg
, "%dx%d", &window_width
, &window_height
);
383 if (match_count
!= 2) {
384 usage_error_printf("'%s' is not a valid window geometry",
387 found_window_size
= true;
391 opts
->fullscreen
= true;
395 loop_get_opt
= false;
401 goto error_unrecognized_arg
;
404 if (!opts
->platform
) {
405 usage_error_printf("--platform is required");
408 if (!opts
->context_api
) {
409 usage_error_printf("--api is required");
412 if (opts
->fullscreen
&& found_window_size
) {
413 usage_error_printf("--fullscreen and --window-size are mutually "
414 "exclusive options");
418 switch (opts
->context_api
) {
419 case WAFFLE_CONTEXT_OPENGL
: opts
->dl
= WAFFLE_DL_OPENGL
; break;
420 case WAFFLE_CONTEXT_OPENGL_ES1
: opts
->dl
= WAFFLE_DL_OPENGL_ES1
; break;
421 case WAFFLE_CONTEXT_OPENGL_ES2
: opts
->dl
= WAFFLE_DL_OPENGL_ES2
; break;
422 case WAFFLE_CONTEXT_OPENGL_ES3
: opts
->dl
= WAFFLE_DL_OPENGL_ES3
; break;
430 error_unrecognized_arg
:
431 usage_error_printf("unrecognized option '%s'", optarg
);
434 // The rules that dictate how to properly query a GL symbol are complex. The
435 // rules depend on the OS, on the winsys API, and even on the particular driver
436 // being used. The rules differ between EGL 1.4 and EGL 1.5; differ between
437 // Linux, Windows, and Mac; and differ between Mesa and Mali.
439 // This function hides that complexity with a naive heuristic: try, then try
442 get_gl_symbol(const struct options
*opts
, const char *name
)
446 if (waffle_dl_can_open(opts
->dl
)) {
447 sym
= waffle_dl_sym(opts
->dl
, name
);
451 sym
= waffle_get_proc_address(name
);
458 draw(struct waffle_window
*window
, bool resize
)
461 unsigned char *colors
;
462 int width
= window_width
;
463 int height
= window_height
;
466 static const struct timespec sleep_time
= {
469 .tv_nsec
= 500000000,
473 for (int i
= 0; i
< 3; ++i
) {
475 case 0: glClearColor(1, 0, 0, 1); break;
476 case 1: glClearColor(0, 1, 0, 1); break;
477 case 2: glClearColor(0, 0, 1, 1); break;
478 case 3: abort(); break;
482 width
= (i
+ 2) * 40;
484 waffle_window_resize(window
, width
, height
);
485 glViewport(0, 0, width
, height
);
488 glClear(GL_COLOR_BUFFER_BIT
);
490 colors
= calloc(width
* height
* 4, sizeof(*colors
));
493 GL_RGBA
, GL_UNSIGNED_BYTE
,
495 for (int j
= 0; j
< width
* height
* 4; j
+= 4) {
496 if ((colors
[j
] != (i
== 0 ? 0xff : 0)) ||
497 (colors
[j
+1] != (i
== 1 ? 0xff : 0)) ||
498 (colors
[j
+2] != (i
== 2 ? 0xff : 0)) ||
499 (colors
[j
+3] != 0xff)) {
500 fprintf(stderr
, "glReadPixels returned unexpected result\n");
507 ok
= waffle_window_show(window
);
512 ok
= waffle_window_swap_buffers(window
);
517 nanosleep(&sleep_time
, NULL
);
528 static NSAutoreleasePool
*pool
;
533 // From the NSApplication Class Reference:
534 // [...] if you do need to use Cocoa classes within the main()
535 // function itself (other than to load nib files or to instantiate
536 // NSApplication), you should create an autorelease pool before using
537 // the classes and then release the pool when you’re done.
538 pool
= [[NSAutoreleasePool alloc
] init
];
540 // From the NSApplication Class Reference:
541 // The sharedApplication class method initializes the display
542 // environment and connects your program to the window server and the
545 // It also creates the singleton NSApp if it does not yet exist.
546 [NSApplication sharedApplication
];
556 removeArg(int index
, int *argc
, char **argv
)
559 for (; index
< *argc
; ++index
)
560 argv
[index
] = argv
[index
+ 1];
564 removeXcodeArgs(int *argc
, char **argv
)
566 // Xcode sometimes adds additional arguments.
567 for (int i
= 1; i
< *argc
; )
569 if (strcmp(argv
[i
], "-NSDocumentRevisionsDebugMode") == 0 ||
570 strcmp(argv
[i
], "-ApplePersistenceIgnoreState" ) == 0)
572 removeArg(i
, argc
, argv
);
573 removeArg(i
, argc
, argv
);
581 #ifdef __native_client__
582 #include "ppapi_simple/ps_main.h"
584 // We need to rename main() for native client
585 // because ppapi_simple already defines main().
587 int basic_test_main(int argc
, char **argv
);
588 PPAPI_SIMPLE_REGISTER_MAIN(basic_test_main
)
590 basic_test_main(int argc
, char **argv
)
593 main(int argc
, char **argv
)
599 struct options opts
= {0};
601 int32_t init_attrib_list
[3];
602 int32_t config_attrib_list
[64];
603 intptr_t window_attrib_list
[5];
605 struct waffle_display
*dpy
;
606 struct waffle_config
*config
;
607 struct waffle_context
*ctx
;
608 struct waffle_window
*window
;
610 GLint context_flags
= 0;
616 #ifdef __native_client__
617 // Fixed arguments for native client.
618 opts
.context_api
= WAFFLE_CONTEXT_OPENGL_ES2
;
619 opts
.platform
= WAFFLE_PLATFORM_NACL
;
620 opts
.dl
= WAFFLE_DL_OPENGL_ES2
;
621 opts
.context_profile
= WAFFLE_NONE
;
622 opts
.context_version
= -1;
624 ok
= parse_args(argc
, argv
, &opts
);
630 init_attrib_list
[i
++] = WAFFLE_PLATFORM
;
631 init_attrib_list
[i
++] = opts
.platform
;
632 init_attrib_list
[i
++] = WAFFLE_NONE
;
634 ok
= waffle_init(init_attrib_list
);
638 dpy
= waffle_display_connect(NULL
);
642 if (!waffle_display_supports_context_api(dpy
, opts
.context_api
)) {
643 error_printf("Display does not support %s",
644 waffle_enum_to_string(opts
.context_api
));
647 glClear
= get_gl_symbol(&opts
, "glClear");
649 error_get_gl_symbol("glClear");
651 glClearColor
= get_gl_symbol(&opts
, "glClearColor");
653 error_get_gl_symbol("glClearColor");
655 glGetError
= get_gl_symbol(&opts
, "glGetError");
657 error_get_gl_symbol("glGetError");
659 glGetIntegerv
= get_gl_symbol(&opts
, "glGetIntegerv");
661 error_get_gl_symbol("glGetIntegerv");
663 glReadPixels
= get_gl_symbol(&opts
, "glReadPixels");
665 error_get_gl_symbol("glReadPixels");
667 glViewport
= get_gl_symbol(&opts
, "glViewport");
669 error_get_gl_symbol("glViewport");
672 config_attrib_list
[i
++] = WAFFLE_CONTEXT_API
;
673 config_attrib_list
[i
++] = opts
.context_api
;
675 if (opts
.context_profile
!= -1) {
676 config_attrib_list
[i
++] = WAFFLE_CONTEXT_PROFILE
;
677 config_attrib_list
[i
++] = opts
.context_profile
;
680 if (opts
.context_version
!= -1) {
681 config_attrib_list
[i
++] = WAFFLE_CONTEXT_MAJOR_VERSION
;
682 config_attrib_list
[i
++] = opts
.context_version
/ 10;
683 config_attrib_list
[i
++] = WAFFLE_CONTEXT_MINOR_VERSION
;
684 config_attrib_list
[i
++] = opts
.context_version
% 10;
687 if (opts
.context_forward_compatible
) {
688 config_attrib_list
[i
++] = WAFFLE_CONTEXT_FORWARD_COMPATIBLE
;
689 config_attrib_list
[i
++] = true;
692 if (opts
.context_debug
) {
693 config_attrib_list
[i
++] = WAFFLE_CONTEXT_DEBUG
;
694 config_attrib_list
[i
++] = true;
697 if (opts
.context_robust
) {
698 config_attrib_list
[i
++] = WAFFLE_CONTEXT_ROBUST_ACCESS
,
699 config_attrib_list
[i
++] = true;
702 config_attrib_list
[i
++] = WAFFLE_RED_SIZE
;
703 config_attrib_list
[i
++] = 8;
704 config_attrib_list
[i
++] = WAFFLE_GREEN_SIZE
;
705 config_attrib_list
[i
++] = 8;
706 config_attrib_list
[i
++] = WAFFLE_BLUE_SIZE
;
707 config_attrib_list
[i
++] = 8;
708 config_attrib_list
[i
++] = WAFFLE_ALPHA_SIZE
;
709 config_attrib_list
[i
++] = 8;
710 config_attrib_list
[i
++] = WAFFLE_DOUBLE_BUFFERED
;
711 config_attrib_list
[i
++] = true;
712 config_attrib_list
[i
++] = 0;
714 config
= waffle_config_choose(dpy
, config_attrib_list
);
718 ctx
= waffle_context_create(config
, NULL
);
724 if (opts
.fullscreen
) {
725 window_attrib_list
[i
++] = WAFFLE_WINDOW_FULLSCREEN
;
726 window_attrib_list
[i
++] = true;
727 window_attrib_list
[i
++] = 0;
729 window_attrib_list
[i
++] = WAFFLE_WINDOW_WIDTH
;
730 window_attrib_list
[i
++] = window_width
;
731 window_attrib_list
[i
++] = WAFFLE_WINDOW_HEIGHT
;
732 window_attrib_list
[i
++] = window_height
;
733 window_attrib_list
[i
++] = 0;
736 window
= waffle_window_create2(config
, window_attrib_list
);
740 ok
= waffle_make_current(dpy
, window
, ctx
);
744 if (opts
.context_forward_compatible
|| opts
.context_debug
) {
745 glGetIntegerv(GL_CONTEXT_FLAGS
, &context_flags
);
749 error_printf("glGetIntegerv(GL_CONTEXT_FLAGS) failed");
751 if (opts
.context_forward_compatible
752 && !(context_flags
& GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
)) {
753 error_printf("context is not forward-compatible");
756 if (opts
.context_debug
757 && !(context_flags
& GL_CONTEXT_FLAG_DEBUG_BIT
)) {
758 error_printf("context is not a debug context");
761 // Don't verify if the context is robust because that pollutes this simple
762 // example program with hairy GL logic. The method of verifying if
763 // a context is robust varies on the combination of context profile,
764 // context version, and supported extensions.
766 ok
= draw(window
, opts
.resize_window
);
770 ok
= waffle_make_current(dpy
, NULL
, NULL
);
774 ok
= waffle_window_destroy(window
);
778 ok
= waffle_context_destroy(ctx
);
782 ok
= waffle_config_destroy(config
);
786 ok
= waffle_display_disconnect(dpy
);
790 ok
= waffle_teardown();
798 printf("gl_basic: run was successful\n");