/* * Copyright 2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include "piglit_glut_framework.h" #include "piglit-util-gl.h" struct piglit_glut_framework { struct piglit_gl_framework gl_fw; enum piglit_result result; int window; }; /** * This global variable exists because GLUT's API requires that data be passed * to the display function via a global. Ugh, GLUT is such an awful API. */ static struct piglit_glut_framework glut_fw; static void destroy(struct piglit_gl_framework *gl_fw) { piglit_gl_framework_teardown(gl_fw); glut_fw.result = 0; glut_fw.window = 0; } #ifdef _WIN32 /* Catch any exceptions and abort immediately. * * At least with certain implementations of GLUT (namely * freeglut-2.8.1), the glutDisplayFunc()'s callback called inside a * WindowProc callback function. * * And on certain cases (depending on the Windows version and 32 vs 64 * bits processes) uncaught exceptions inside WindowProc callbacks are * silently ignored! (See Remarks section of * http://msdn.microsoft.com/en-us/library/windows/desktop/ms633573.aspx * page.) The end result is that automatic tests end up blocking * waiting for user input when an uncaught exceptionhappens, as control * flow is interrupted before it reaches piglit_report_result(), and * the process never aborts. * * By installing our own exception handler we can ensure that uncaught * exceptions will never be silently ignored. * * NOTE: SetUnhandledExceptionFilter does not work here, as some Windows * versions never call the unhandled exception filter. We must use MSVC * __try/__except statements, or in MinGW, the __try1/__except1 macros. */ static LONG WINAPI exception_filter(PEXCEPTION_POINTERS pExceptionInfo) { PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; fflush(stdout); fprintf(stderr, "error: caught unhandled exception 0x%08lx\n", pExceptionRecord->ExceptionCode); fflush(stderr); _exit(pExceptionRecord->ExceptionCode); } # ifdef __MINGW32__ // http://www.programmingunlimited.net/siteexec/content.cgi?page=mingw-seh // http://www.microsoft.com/msj/0197/exception/exception.aspx EXCEPTION_DISPOSITION exception_handler(struct _EXCEPTION_RECORD *ExceptionRecord, void *EstablisherFrame, struct _CONTEXT *ContextRecord, void *DispatcherContext) { EXCEPTION_POINTERS ExceptionInfo = {ExceptionRecord, ContextRecord}; switch (exception_filter(&ExceptionInfo)) { case EXCEPTION_EXECUTE_HANDLER: return ExceptionExecuteHandler; case EXCEPTION_CONTINUE_SEARCH: return ExceptionContinueSearch; case EXCEPTION_CONTINUE_EXECUTION: return ExceptionContinueExecution; default: return ExceptionContinueSearch; } } # define TRY __try1(exception_handler); # define CATCH_ALL __except1; # else # define TRY __try { # define CATCH_ALL } __except(exception_filter(GetExceptionInformation())) {} # endif #else # define TRY # define CATCH_ALL #endif static void display(void) { const struct piglit_gl_test_config *test_config = glut_fw.gl_fw.test_config; TRY if (test_config->display) glut_fw.result = test_config->display(); CATCH_ALL if (piglit_automatic) { glutDestroyWindow(glut_fw.window); #ifdef FREEGLUT /* Tell GLUT to clean up and exit, so that we can * reasonably valgrind our testcases for memory * leaks by the GL. */ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS); glutLeaveMainLoop(); #else piglit_report_result(glut_fw.result); #endif } } static void default_reshape_func(int w, int h) { if (piglit_automatic && (w != piglit_width || h != piglit_height)) { printf("Got spurious window resize in automatic run " "(%d,%d to %d,%d)\n", piglit_width, piglit_height, w, h); piglit_report_result(PIGLIT_WARN); } piglit_width = w; piglit_height = h; glViewport(0, 0, w, h); } static void error_func(const char *fmt, va_list ap) { vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); piglit_report_result(PIGLIT_SKIP); } static void init_glut(void) { const struct piglit_gl_test_config *test_config = glut_fw.gl_fw.test_config; char *argv[] = {"piglit"}; int argc = 1; unsigned flags = GLUT_RGB; if (test_config->window_visual & PIGLIT_GL_VISUAL_RGBA) flags |= GLUT_ALPHA; if (test_config->window_visual & PIGLIT_GL_VISUAL_DEPTH) flags |= GLUT_DEPTH; if (test_config->window_visual & PIGLIT_GL_VISUAL_STENCIL) flags |= GLUT_STENCIL; if (test_config->window_visual & PIGLIT_GL_VISUAL_ACCUM) flags |= GLUT_ACCUM; if (test_config->window_visual & PIGLIT_GL_VISUAL_DOUBLE) flags |= GLUT_DOUBLE; else flags |= GLUT_SINGLE; /* * MacOSX GLUT. * * This will request a core profile. It will always return the highest * version supported. * * See: * /System/Library/Frameworks/GLUT.framework/Headers/glut.h * https://developer.apple.com/opengl/capabilities/ */ #if GLUT_MACOSX_IMPLEMENTATION >= 4 if (test_config->supports_gl_core_version >= 31) { flags |= GLUT_3_2_CORE_PROFILE; } #endif glutInit(&argc, argv); glutInitWindowPosition(0, 0); glutInitWindowSize(test_config->window_width, test_config->window_height); glutInitDisplayMode(flags); /* * FreeGLUT */ #ifdef PIGLIT_USE_GLUT_INIT_ERROR_FUNC glutInitErrorFunc(error_func); #else (void)error_func; #endif #ifdef GLUT_CORE_PROFILE if (test_config->supports_gl_core_version) { glutInitContextVersion(test_config->supports_gl_core_version / 10, test_config->supports_gl_core_version % 10); if (test_config->supports_gl_core_version >= 32) { glutInitContextProfile(GLUT_CORE_PROFILE); } } else { glutInitContextVersion(test_config->supports_gl_compat_version / 10, test_config->supports_gl_compat_version % 10); if (test_config->supports_gl_compat_version >= 32) { glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE); } } int context_flags = 0; /* There are no 3.1 core profiles -- the closest is 3.1 context without * ARB_compatibility or a 3.2 core context --, and setting * forward-compatible flag should ensure we don't get a 3.1 context w/ * ARB_compatibility. */ if (test_config->require_forward_compatible_context || test_config->supports_gl_core_version == 31) { context_flags |= GLUT_FORWARD_COMPATIBLE; } if (test_config->require_debug_context) { context_flags |= GLUT_DEBUG; } if (context_flags) { glutInitContextFlags(context_flags); } #endif glut_fw.window = glutCreateWindow("Piglit"); glutDisplayFunc(display); glutReshapeFunc(default_reshape_func); glutKeyboardFunc(piglit_escape_exit_key); #ifdef PIGLIT_USE_OPENGL piglit_dispatch_default_init(PIGLIT_DISPATCH_GL); #endif } static void run_test(struct piglit_gl_framework *gl_fw, int argc, char *argv[]) { const struct piglit_gl_test_config *test_config = glut_fw.gl_fw.test_config; if (test_config->init) test_config->init(argc, argv); glutMainLoop(); piglit_report_result(glut_fw.result); } static void swap_buffers(struct piglit_gl_framework *gl_fw) { glutSwapBuffers(); } static void post_redisplay(struct piglit_gl_framework *gl_fw) { glutPostRedisplay(); } static void set_keyboard_func(struct piglit_gl_framework *gl_fw, void (*func)(unsigned char key, int x, int y)) { glutKeyboardFunc(func); } static void set_reshape_func(struct piglit_gl_framework *gl_fw, void (*func)(int w, int h)) { glutReshapeFunc(func); } /** * Check that the context's actual version is no less than the * requested version. */ static bool check_gl_version(const struct piglit_gl_test_config *test_config) { int actual_version = piglit_get_gl_version(); if (test_config->supports_gl_core_version) { if (actual_version < test_config->supports_gl_core_version) { printf("Test requires GL version %d.%d, but actual version is " "%d.%d\n", test_config->supports_gl_core_version / 10, test_config->supports_gl_core_version % 10, actual_version / 10, actual_version % 10); return false; } return true; } if (actual_version < test_config->supports_gl_compat_version) { printf("Test requires GL version %d.%d, but actual version is " "%d.%d\n", test_config->supports_gl_compat_version / 10, test_config->supports_gl_compat_version % 10, actual_version / 10, actual_version % 10); return false; } if (piglit_is_core_profile && test_config->supports_gl_core_version == 0) { /* We have a core profile context but the test needs a * compat profile. We can't run the test. */ printf("Test requires compat version %d.%d or later but " "context is core profile %d.%d.\n", test_config->supports_gl_compat_version / 10, test_config->supports_gl_compat_version % 10, actual_version / 10, actual_version % 10); return false; } return true; } struct piglit_gl_framework* piglit_glut_framework_create(const struct piglit_gl_test_config *test_config) { bool ok = true; #if !defined(GLUT_CORE_PROFILE) && \ (!defined(GLUT_MACOSX_IMPLEMENTATION) || GLUT_MACOSX_IMPLEMENTATION < 4) if (!test_config->supports_gl_compat_version) { printf("GLUT can create only GL compatibility contexts, " "which the test does not support running under.\n"); piglit_report_result(PIGLIT_SKIP); } #endif if (test_config->window_samples > 1) { printf("GLUT doesn't support MSAA visuals.\n"); piglit_report_result(PIGLIT_SKIP); } ok = piglit_gl_framework_init(&glut_fw.gl_fw, test_config); if (!ok) return NULL; init_glut(); /* Check if we actually have a core profile */ { int actual_version = piglit_get_gl_version(); piglit_is_core_profile = actual_version >= 31 && !piglit_is_extension_supported("GL_ARB_compatibility"); } if (!check_gl_version(test_config)) piglit_report_result(PIGLIT_SKIP); glut_fw.gl_fw.swap_buffers = swap_buffers; glut_fw.gl_fw.run_test = run_test; glut_fw.gl_fw.post_redisplay = post_redisplay; glut_fw.gl_fw.set_keyboard_func = set_keyboard_func; glut_fw.gl_fw.set_reshape_func = set_reshape_func; glut_fw.gl_fw.destroy = destroy; return &glut_fw.gl_fw; }