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 0x0106
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|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)
136 error_printf(const char *fmt
, ...)
143 fprintf(stderr
, "gl_basic: error: ");
144 vfprintf(stderr
, fmt
, ap
);
145 fprintf(stderr
, "\n");
152 usage_error_printf(const char *fmt
, ...)
155 fprintf(stderr
, "gl_basic: usage error");
160 fprintf(stderr
, ": ");
161 vfprintf(stderr
, fmt
, ap
);
165 fprintf(stderr
, "\n");
166 fprintf(stderr
, "\n");
167 fprintf(stderr
, "%s", usage_message
);
175 const struct waffle_error_info
*info
= waffle_error_get_info();
176 const char *code
= waffle_error_to_string(info
->code
);
178 if (info
->message_length
> 0)
179 error_printf("%s: %s", code
, info
->message
);
181 error_printf("%s", code
);
185 error_get_gl_symbol(const char *name
)
187 error_printf("failed to get function pointer for %s", name
);
190 typedef float GLclampf
;
191 typedef unsigned int GLbitfield
;
192 typedef unsigned int GLint
;
194 typedef unsigned int GLenum
;
198 // Copied from <GL/gl*.h>.
199 GL_UNSIGNED_BYTE
= 0x00001401,
200 GL_RGBA
= 0x00001908,
201 GL_COLOR_BUFFER_BIT
= 0x00004000,
203 GL_CONTEXT_FLAGS
= 0x821e,
204 GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
= 0x00000001,
205 GL_CONTEXT_FLAG_DEBUG_BIT
= 0x00000002,
206 GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT
= 0x00000004 ,
209 static int window_width
= 320;
210 static int window_height
= 240;
216 #define APIENTRY __stdcall
220 static void (APIENTRY
*glClearColor
)(GLclampf red
, GLclampf green
, GLclampf blue
, GLclampf alpha
);
221 static void (APIENTRY
*glClear
)(GLbitfield mask
);
222 static GLenum (APIENTRY
*glGetError
)(void);
223 static void (APIENTRY
*glGetIntegerv
)(GLenum pname
, GLint
*params
);
224 static void (APIENTRY
*glReadPixels
)(GLint x
, GLint y
, GLsizei width
, GLsizei height
,
225 GLenum format
, GLenum type
, GLvoid
* data
);
226 static void (APIENTRY
*glViewport
)(GLint x
, GLint y
, GLsizei width
, GLsizei height
);
228 /// @brief Command line options.
230 /// @brief One of `WAFFLE_PLATFORM_*`.
233 /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
236 /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
241 bool context_forward_compatible
;
249 /// @brief One of `WAFFLE_DL_*`.
258 static const struct enum_map platform_map
[] = {
259 {WAFFLE_PLATFORM_ANDROID
, "android" },
260 {WAFFLE_PLATFORM_CGL
, "cgl", },
261 {WAFFLE_PLATFORM_GBM
, "gbm" },
262 {WAFFLE_PLATFORM_GLX
, "glx" },
263 {WAFFLE_PLATFORM_WAYLAND
, "wayland" },
264 {WAFFLE_PLATFORM_WGL
, "wgl" },
265 {WAFFLE_PLATFORM_X11_EGL
, "x11_egl" },
269 static const struct enum_map context_api_map
[] = {
270 {WAFFLE_CONTEXT_OPENGL
, "gl" },
271 {WAFFLE_CONTEXT_OPENGL_ES1
, "gles1" },
272 {WAFFLE_CONTEXT_OPENGL_ES2
, "gles2" },
273 {WAFFLE_CONTEXT_OPENGL_ES3
, "gles3" },
277 /// @brief Translate string to `enum waffle_enum`.
279 /// @param self is a list of map items. The last item must be zero-filled.
280 /// @param result is altered only if @a s if found.
281 /// @return true if @a s was found in @a map.
283 enum_map_translate_str(
284 const struct enum_map
*self
,
288 for (const struct enum_map
*i
= self
; i
->i
!= 0; ++i
) {
289 if (!strncmp(s
, i
->s
, strlen(i
->s
) + 1)) {
298 /// @return true on success.
300 parse_args(int argc
, char *argv
[], struct options
*opts
)
303 bool loop_get_opt
= true;
304 bool found_window_size
= false;
307 removeXcodeArgs(&argc
, argv
);
310 // Set some context attrs to invalid values.
311 opts
->context_profile
= -1;
312 opts
->context_version
= -1;
314 while (loop_get_opt
) {
315 int opt
= getopt_long(argc
, argv
, "", get_opts
, NULL
);
318 loop_get_opt
= false;
321 goto error_unrecognized_arg
;
323 ok
= enum_map_translate_str(platform_map
, optarg
,
326 usage_error_printf("'%s' is not a valid platform",
331 ok
= enum_map_translate_str(context_api_map
, optarg
,
334 usage_error_printf("'%s' is not a valid API for an OpenGL "
343 match_count
= sscanf(optarg
, "%d.%d", &major
, &minor
);
344 if (match_count
!= 2) {
345 usage_error_printf("'%s' is not a valid OpenGL version",
348 opts
->context_version
= 10 * major
+ minor
;
352 if (strcmp(optarg
, "none") == 0) {
353 opts
->context_profile
= WAFFLE_NONE
;
354 } else if (strcmp(optarg
, "core") == 0) {
355 opts
->context_profile
= WAFFLE_CONTEXT_CORE_PROFILE
;
356 } else if (strcmp(optarg
, "compat") == 0) {
357 opts
->context_profile
= WAFFLE_CONTEXT_COMPATIBILITY_PROFILE
;
359 usage_error_printf("'%s' is not a valid OpenGL profile",
363 case OPT_FORWARD_COMPATIBLE
:
364 opts
->context_forward_compatible
= true;
367 opts
->context_debug
= true;
370 opts
->context_robust
= true;
372 case OPT_RESIZE_WINDOW
:
373 opts
->resize_window
= true;
375 case OPT_WINDOW_SIZE
: {
377 match_count
= sscanf(optarg
, "%dx%d", &window_width
, &window_height
);
378 if (match_count
!= 2) {
379 usage_error_printf("'%s' is not a valid window geometry",
382 found_window_size
= true;
386 opts
->fullscreen
= true;
390 loop_get_opt
= false;
396 goto error_unrecognized_arg
;
399 if (!opts
->platform
) {
400 usage_error_printf("--platform is required");
403 if (!opts
->context_api
) {
404 usage_error_printf("--api is required");
407 if (opts
->fullscreen
&& found_window_size
) {
408 usage_error_printf("--fullscreen and --window-size are mutually "
409 "exclusive options");
413 switch (opts
->context_api
) {
414 case WAFFLE_CONTEXT_OPENGL
: opts
->dl
= WAFFLE_DL_OPENGL
; break;
415 case WAFFLE_CONTEXT_OPENGL_ES1
: opts
->dl
= WAFFLE_DL_OPENGL_ES1
; break;
416 case WAFFLE_CONTEXT_OPENGL_ES2
: opts
->dl
= WAFFLE_DL_OPENGL_ES2
; break;
417 case WAFFLE_CONTEXT_OPENGL_ES3
: opts
->dl
= WAFFLE_DL_OPENGL_ES3
; break;
425 error_unrecognized_arg
:
426 usage_error_printf("unrecognized option '%s'", optarg
);
429 // The rules that dictate how to properly query a GL symbol are complex. The
430 // rules depend on the OS, on the winsys API, and even on the particular driver
431 // being used. The rules differ between EGL 1.4 and EGL 1.5; differ between
432 // Linux, Windows, and Mac; and differ between Mesa and Mali.
434 // This function hides that complexity with a naive heuristic: try, then try
437 get_gl_symbol(const struct options
*opts
, const char *name
)
441 if (waffle_dl_can_open(opts
->dl
)) {
442 sym
= waffle_dl_sym(opts
->dl
, name
);
446 sym
= waffle_get_proc_address(name
);
453 draw(struct waffle_window
*window
, bool resize
)
456 unsigned char *colors
;
457 int width
= window_width
;
458 int height
= window_height
;
461 static const struct timespec sleep_time
= {
464 .tv_nsec
= 500000000,
468 for (int i
= 0; i
< 3; ++i
) {
470 case 0: glClearColor(1, 0, 0, 1); break;
471 case 1: glClearColor(0, 1, 0, 1); break;
472 case 2: glClearColor(0, 0, 1, 1); break;
473 case 3: abort(); break;
477 width
= (i
+ 2) * 40;
479 waffle_window_resize(window
, width
, height
);
480 glViewport(0, 0, width
, height
);
483 glClear(GL_COLOR_BUFFER_BIT
);
485 colors
= calloc(width
* height
* 4, sizeof(*colors
));
488 GL_RGBA
, GL_UNSIGNED_BYTE
,
490 for (int j
= 0; j
< width
* height
* 4; j
+= 4) {
491 if ((colors
[j
] != (i
== 0 ? 0xff : 0)) ||
492 (colors
[j
+1] != (i
== 1 ? 0xff : 0)) ||
493 (colors
[j
+2] != (i
== 2 ? 0xff : 0)) ||
494 (colors
[j
+3] != 0xff)) {
495 fprintf(stderr
, "glReadPixels returned unexpected result\n");
502 ok
= waffle_window_show(window
);
507 ok
= waffle_window_swap_buffers(window
);
512 nanosleep(&sleep_time
, NULL
);
523 static NSAutoreleasePool
*pool
;
528 // From the NSApplication Class Reference:
529 // [...] if you do need to use Cocoa classes within the main()
530 // function itself (other than to load nib files or to instantiate
531 // NSApplication), you should create an autorelease pool before using
532 // the classes and then release the pool when you’re done.
533 pool
= [[NSAutoreleasePool alloc
] init
];
535 // From the NSApplication Class Reference:
536 // The sharedApplication class method initializes the display
537 // environment and connects your program to the window server and the
540 // It also creates the singleton NSApp if it does not yet exist.
541 [NSApplication sharedApplication
];
551 removeArg(int index
, int *argc
, char **argv
)
554 for (; index
< *argc
; ++index
)
555 argv
[index
] = argv
[index
+ 1];
559 removeXcodeArgs(int *argc
, char **argv
)
561 // Xcode sometimes adds additional arguments.
562 for (int i
= 1; i
< *argc
; )
564 if (strcmp(argv
[i
], "-NSDocumentRevisionsDebugMode") == 0 ||
565 strcmp(argv
[i
], "-ApplePersistenceIgnoreState" ) == 0)
567 removeArg(i
, argc
, argv
);
568 removeArg(i
, argc
, argv
);
576 #ifdef __native_client__
577 #include "ppapi_simple/ps_main.h"
579 // We need to rename main() for native client
580 // because ppapi_simple already defines main().
582 int basic_test_main(int argc
, char **argv
);
583 PPAPI_SIMPLE_REGISTER_MAIN(basic_test_main
)
585 basic_test_main(int argc
, char **argv
)
588 main(int argc
, char **argv
)
594 struct options opts
= {0};
596 int32_t init_attrib_list
[3];
597 int32_t config_attrib_list
[64];
598 intptr_t window_attrib_list
[5];
600 struct waffle_display
*dpy
;
601 struct waffle_config
*config
;
602 struct waffle_context
*ctx
;
603 struct waffle_window
*window
;
605 GLint context_flags
= 0;
611 #ifdef __native_client__
612 // Fixed arguments for native client.
613 opts
.context_api
= WAFFLE_CONTEXT_OPENGL_ES2
;
614 opts
.platform
= WAFFLE_PLATFORM_NACL
;
615 opts
.dl
= WAFFLE_DL_OPENGL_ES2
;
616 opts
.context_profile
= WAFFLE_NONE
;
617 opts
.context_version
= -1;
619 ok
= parse_args(argc
, argv
, &opts
);
625 init_attrib_list
[i
++] = WAFFLE_PLATFORM
;
626 init_attrib_list
[i
++] = opts
.platform
;
627 init_attrib_list
[i
++] = WAFFLE_NONE
;
629 ok
= waffle_init(init_attrib_list
);
633 dpy
= waffle_display_connect(NULL
);
637 if (!waffle_display_supports_context_api(dpy
, opts
.context_api
)) {
638 error_printf("Display does not support %s",
639 waffle_enum_to_string(opts
.context_api
));
642 glClear
= get_gl_symbol(&opts
, "glClear");
644 error_get_gl_symbol("glClear");
646 glClearColor
= get_gl_symbol(&opts
, "glClearColor");
648 error_get_gl_symbol("glClearColor");
650 glGetError
= get_gl_symbol(&opts
, "glGetError");
652 error_get_gl_symbol("glGetError");
654 glGetIntegerv
= get_gl_symbol(&opts
, "glGetIntegerv");
656 error_get_gl_symbol("glGetIntegerv");
658 glReadPixels
= get_gl_symbol(&opts
, "glReadPixels");
660 error_get_gl_symbol("glReadPixels");
662 glViewport
= get_gl_symbol(&opts
, "glViewport");
664 error_get_gl_symbol("glViewport");
667 config_attrib_list
[i
++] = WAFFLE_CONTEXT_API
;
668 config_attrib_list
[i
++] = opts
.context_api
;
670 if (opts
.context_profile
!= -1) {
671 config_attrib_list
[i
++] = WAFFLE_CONTEXT_PROFILE
;
672 config_attrib_list
[i
++] = opts
.context_profile
;
675 if (opts
.context_version
!= -1) {
676 config_attrib_list
[i
++] = WAFFLE_CONTEXT_MAJOR_VERSION
;
677 config_attrib_list
[i
++] = opts
.context_version
/ 10;
678 config_attrib_list
[i
++] = WAFFLE_CONTEXT_MINOR_VERSION
;
679 config_attrib_list
[i
++] = opts
.context_version
% 10;
682 if (opts
.context_forward_compatible
) {
683 config_attrib_list
[i
++] = WAFFLE_CONTEXT_FORWARD_COMPATIBLE
;
684 config_attrib_list
[i
++] = true;
687 if (opts
.context_debug
) {
688 config_attrib_list
[i
++] = WAFFLE_CONTEXT_DEBUG
;
689 config_attrib_list
[i
++] = true;
692 if (opts
.context_robust
) {
693 config_attrib_list
[i
++] = WAFFLE_CONTEXT_ROBUST_ACCESS
,
694 config_attrib_list
[i
++] = true;
697 config_attrib_list
[i
++] = WAFFLE_RED_SIZE
;
698 config_attrib_list
[i
++] = 8;
699 config_attrib_list
[i
++] = WAFFLE_GREEN_SIZE
;
700 config_attrib_list
[i
++] = 8;
701 config_attrib_list
[i
++] = WAFFLE_BLUE_SIZE
;
702 config_attrib_list
[i
++] = 8;
703 config_attrib_list
[i
++] = WAFFLE_ALPHA_SIZE
;
704 config_attrib_list
[i
++] = 8;
705 config_attrib_list
[i
++] = WAFFLE_DOUBLE_BUFFERED
;
706 config_attrib_list
[i
++] = true;
707 config_attrib_list
[i
++] = 0;
709 config
= waffle_config_choose(dpy
, config_attrib_list
);
713 ctx
= waffle_context_create(config
, NULL
);
719 if (opts
.fullscreen
) {
720 window_attrib_list
[i
++] = WAFFLE_WINDOW_FULLSCREEN
;
721 window_attrib_list
[i
++] = true;
722 window_attrib_list
[i
++] = 0;
724 window_attrib_list
[i
++] = WAFFLE_WINDOW_WIDTH
;
725 window_attrib_list
[i
++] = window_width
;
726 window_attrib_list
[i
++] = WAFFLE_WINDOW_HEIGHT
;
727 window_attrib_list
[i
++] = window_height
;
728 window_attrib_list
[i
++] = 0;
731 window
= waffle_window_create2(config
, window_attrib_list
);
735 ok
= waffle_make_current(dpy
, window
, ctx
);
739 if (opts
.context_forward_compatible
|| opts
.context_debug
) {
740 glGetIntegerv(GL_CONTEXT_FLAGS
, &context_flags
);
744 error_printf("glGetIntegerv(GL_CONTEXT_FLAGS) failed");
746 if (opts
.context_forward_compatible
747 && !(context_flags
& GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT
)) {
748 error_printf("context is not forward-compatible");
751 if (opts
.context_debug
752 && !(context_flags
& GL_CONTEXT_FLAG_DEBUG_BIT
)) {
753 error_printf("context is not a debug context");
756 // Don't verify if the context is robust because that pollutes this simple
757 // example program with hairy GL logic. The method of verifying if
758 // a context is robust varies on the combination of context profile,
759 // context version, and supported extensions.
761 ok
= draw(window
, opts
.resize_window
);
765 ok
= waffle_make_current(dpy
, NULL
, NULL
);
769 ok
= waffle_window_destroy(window
);
773 ok
= waffle_context_destroy(ctx
);
777 ok
= waffle_config_destroy(config
);
781 ok
= waffle_display_disconnect(dpy
);
785 ok
= waffle_teardown();
793 printf("gl_basic: run was successful\n");