diff options
author | Eric Anholt <eric@anholt.net> | 2008-02-21 13:52:09 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2008-02-21 15:13:13 -0800 |
commit | 1a63befcc01d19ff203ff3fd434037ba618bed83 (patch) | |
tree | 5dc104260c3415d14a9d5fecade8e5055c4820ef | |
parent | 22fbbb5e8679ddf91532cb10eaa31be533383b48 (diff) |
Update glean tests to CVS HEAD from 2008-02-21.
34 files changed, 8097 insertions, 577 deletions
diff --git a/tests/all.tests b/tests/all.tests index 931ec3665..917631b6f 100644 --- a/tests/all.tests +++ b/tests/all.tests @@ -34,14 +34,17 @@ for line in out.split('\n'): # Collecting all tests glean = Group() glean['basic'] = GleanTest('basic') +glean['api2'] = GleanTest('api2') glean['makeCurrent'] = GleanTest('makeCurrent') glean['blendFunc'] = GleanTest('blendFunc') glean['depthStencil'] = GleanTest('depthStencil') glean['fpexceptions'] = GleanTest('fpexceptions') glean['fragProg1'] = GleanTest('fragProg1') # also add entire tests, because of interdependency bugs that only occur when several different FPs are used glean['getString'] = GleanTest('getString') +glean['glsl1'] = GleanTest('glsl1') glean['logicOp'] = GleanTest('logicOp') glean['maskedClear'] = GleanTest('maskedClear') +glean['occluquery'] = GleanTest('occluquery') glean['orthoPosRandTris'] = GleanTest('orthoPosRandTris') glean['orthoPosRandRects'] = GleanTest('orthoPosRandRects') glean['orthoPosTinyQuads'] = GleanTest('orthoPosTinyQuads') @@ -49,9 +52,11 @@ glean['orthoPosHLines'] = GleanTest('orthoPosHLines') glean['orthoPosVLines'] = GleanTest('orthoPosVLines') glean['orthoPosPoints'] = GleanTest('orthoPosPoints') glean['paths'] = GleanTest('paths') +glean['pbo'] = GleanTest('pbo') glean['polygonOffset'] = GleanTest('polygonOffset') glean['pixelFormats'] = GleanTest('pixelFormats') glean['pointAtten'] = GleanTest('pointAtten') +glean['pointSprite'] = GleanTest('pointSprite') glean['exactRGBA'] = GleanTest('exactRGBA') glean['readPixSanity'] = GleanTest('readPixSanity') glean['rgbTriStrip'] = GleanTest('rgbTriStrip') diff --git a/tests/glean/CMakeLists.txt b/tests/glean/CMakeLists.txt index 9156f1a8d..6bf3f671c 100644 --- a/tests/glean/CMakeLists.txt +++ b/tests/glean/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable (glean misc.cpp options.cpp rc.cpp + tapi2.cpp tbasic.cpp tbasicperf.cpp tbinding.cpp @@ -26,14 +27,18 @@ add_executable (glean tfpexceptions.cpp tfragprog1.cpp tgetstr.cpp + tglsl1.cpp tlogicop.cpp tmaskedclear.cpp tmultitest.cpp + toccluqry.cpp torthpos.cpp tpaths.cpp + tpbo.cpp tpgos.cpp tpixelformats.cpp tpointatten.cpp + tpointsprite.cpp treadpix.cpp treadpixperf.cpp trgbtris.cpp diff --git a/tests/glean/environ.cpp b/tests/glean/environ.cpp index d1447cd0a..39d8815c6 100644 --- a/tests/glean/environ.cpp +++ b/tests/glean/environ.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -75,7 +75,7 @@ Environment::Environment(Options& opt): } // If comparing previous runs, make a token attempt to verify // that the two databases exist. - } else if (opt.mode == Options::compare) { + } else { struct stat s; if (stat(opt.db1Name.c_str(), &s) || !S_ISDIR(s.st_mode)) throw DBCantOpen(opt.db1Name); @@ -99,8 +99,8 @@ Environment::Environment(Options& opt): } // If comparing previous runs, make a token attempt to verify // that the two databases exist. - } else if (opt.mode == Options::compare) { - struct _stat s; + } else { + struct _stat s; if (_stat(opt.db1Name.c_str(), &s) || !(s.st_mode & _S_IFDIR)) throw DBCantOpen(opt.db1Name); diff --git a/tests/glean/glutils.cpp b/tests/glean/glutils.cpp index 342bd4768..4b38d6203 100644 --- a/tests/glean/glutils.cpp +++ b/tests/glean/glutils.cpp @@ -99,6 +99,16 @@ haveExtensions(const char* required) { return haveAll; } // haveExtensions + +float +getVersion() +{ + const GLubyte *version = glGetString(GL_VERSION); + // we rely on atof() stopping parsing at whitespace + return atof((const char *) version); +} + + /////////////////////////////////////////////////////////////////////////////// // getProcAddress: Get address of an OpenGL or window-system-binding function. // This belongs here, rather than as a member of RenderingContext, because diff --git a/tests/glean/glutils.h b/tests/glean/glutils.h index 94db42c5f..bc96b59aa 100644 --- a/tests/glean/glutils.h +++ b/tests/glean/glutils.h @@ -59,6 +59,9 @@ inline bool haveExtension(const char* name) { // RenderingContext. void (*getProcAddress(const char* name))(); +// Return GL renderer version as a float (1.1, 2.0, etc) +float getVersion(); + // Check for OpenGL errors and log any that have occurred: void logGLErrors(Environment& env); diff --git a/tests/glean/main.cpp b/tests/glean/main.cpp index c40b5db5b..b7a1d2d53 100644 --- a/tests/glean/main.cpp +++ b/tests/glean/main.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -80,8 +80,6 @@ main(int argc, char* argv[]) { } else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--overwrite")) { o.overwrite = true; - } else if (!strcmp(argv[i], "--ignore-prereqs")) { - o.ignorePrereqs = true; } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare")) { o.mode = Options::compare; @@ -98,8 +96,6 @@ main(int argc, char* argv[]) { selectTests(o, allTestNames, argc, argv, i); } else if (!strcmp(argv[i], "--listtests")) { o.mode = Options::listtests; - } else if (!strcmp(argv[i], "--listdetails")) { - o.mode = Options::listdetails; # if defined(__X11__) } else if (!strcmp(argv[i], "-display") || !strcmp(argv[i], "--display")) { @@ -156,14 +152,6 @@ main(int argc, char* argv[]) { } break; } - case Options::listdetails: - { - for (Test* t = Test::testList; t; t = t->nextTest) - if (binary_search(o.selectedTests.begin(), - o.selectedTests.end(), t->name)) - t->details(e); - break; - } default: cerr << "Bad run mode in main()\n"; break; @@ -332,10 +320,8 @@ usage(char* command) { " (-o|--overwrite) # overwrite existing results database\n" " --visuals 'filter-string' # select subset of visuals (FBConfigs,\n" " # pixel formats) to test\n" -" --ignore-prereqs # do not force prerequisite tests\n" " (-t|--tests) {(+|-)test} # choose tests to include (+) or exclude (-)\n" " --listtests # list test names and exit\n" -" --listdetails # list details of the selected tests and exit\n" " --help # display usage information\n" #if defined(__X11__) " -display X11-display-name # select X11 display to use\n" diff --git a/tests/glean/options.cpp b/tests/glean/options.cpp index 9bda4e484..7e97e5fa7 100644 --- a/tests/glean/options.cpp +++ b/tests/glean/options.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -48,7 +48,6 @@ Options::Options() { visFilter = "1"; selectedTests.resize(0); overwrite = false; - ignorePrereqs = false; # if defined(__X11__) { char* display = getenv("DISPLAY"); diff --git a/tests/glean/options.h b/tests/glean/options.h index ad476d37c..8027069c8 100644 --- a/tests/glean/options.h +++ b/tests/glean/options.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -38,7 +38,7 @@ // We collect this information for two reasons. First, it allows the // (relatively) large number of parameters needed for creating an -// Environment to be passed cleanly to Environment's constructor. +// Environment to be passed cleanly to Environment's constructor. // Second, it allows the process of gathering parameters (by parsing a // command line, running a set of GUI dialogs, etc.) to be separated // from the creation of the Environment. @@ -57,7 +57,7 @@ namespace GLEAN { class Options { public: - typedef enum {notSet, run, compare, listtests, listdetails} RunMode; + typedef enum {notSet, run, compare, listtests} RunMode; RunMode mode; // Indicates whether we're generating // results, or comparing two previous runs. @@ -82,7 +82,6 @@ class Options { // Sorted list of tests to be executed. bool overwrite; // overwrite old results database if exists - bool ignorePrereqs; // ignore prerequisite tests #if defined(__X11__) string dpyName; // Name of the X11 display providing the // OpenGL implementation to be tested. diff --git a/tests/glean/tapi2.cpp b/tests/glean/tapi2.cpp new file mode 100644 index 000000000..d6860e2db --- /dev/null +++ b/tests/glean/tapi2.cpp @@ -0,0 +1,1019 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 1999 Allen Akin All Rights Reserved. +// +// 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 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + +// tapi2.h: Test OpenGL 2.x API functions/features +// Brian Paul 9 March 2007 + +#define GL_GLEXT_PROTOTYPES + +#include "tapi2.h" +#include <cassert> +#include <math.h> + + +namespace GLEAN { + + +static PFNGLATTACHSHADERPROC glAttachShader_func = NULL; +static PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation_func = NULL; +static PFNGLCOMPILESHADERPROC glCompileShader_func = NULL; +static PFNGLCREATEPROGRAMPROC glCreateProgram_func = NULL; +static PFNGLCREATESHADERPROC glCreateShader_func = NULL; +static PFNGLDELETEPROGRAMPROC glDeleteProgram_func = NULL; +static PFNGLDELETESHADERPROC glDeleteShader_func = NULL; +static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray_func = NULL; +static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray_func = NULL; +static PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders_func = NULL; +static PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation_func = NULL; +static PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog_func = NULL; +static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog_func = NULL; +static PFNGLGETSHADERIVPROC glGetShaderiv_func = NULL; +static PFNGLGETPROGRAMIVPROC glGetProgramiv_func = NULL; +static PFNGLGETSHADERSOURCEPROC glGetShaderSource_func = NULL; +static PFNGLGETUNIFORMFVPROC glGetUniformfv_func = NULL; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_func = NULL; +static PFNGLISPROGRAMPROC glIsProgram_func = NULL; +static PFNGLISSHADERPROC glIsShader_func = NULL; +static PFNGLLINKPROGRAMPROC glLinkProgram_func = NULL; +static PFNGLSHADERSOURCEPROC glShaderSource_func = NULL; +static PFNGLUNIFORM1FVPROC glUniform1fv_func = NULL; +static PFNGLUNIFORM2FVPROC glUniform2fv_func = NULL; +static PFNGLUNIFORM3FVPROC glUniform3fv_func = NULL; +static PFNGLUNIFORM4FVPROC glUniform4fv_func = NULL; +static PFNGLUNIFORM1FPROC glUniform1f_func = NULL; +static PFNGLUNIFORM2FPROC glUniform2f_func = NULL; +static PFNGLUNIFORM3FPROC glUniform3f_func = NULL; +static PFNGLUNIFORM4FPROC glUniform4f_func = NULL; +static PFNGLUNIFORM1IPROC glUniform1i_func = NULL; +static PFNGLUNIFORM2IPROC glUniform2i_func = NULL; +static PFNGLUNIFORM3IPROC glUniform3i_func = NULL; +static PFNGLUNIFORM4IPROC glUniform4i_func = NULL; +static PFNGLUNIFORM1IVPROC glUniform1iv_func = NULL; +static PFNGLUNIFORM2IVPROC glUniform2iv_func = NULL; +static PFNGLUNIFORM3IVPROC glUniform3iv_func = NULL; +static PFNGLUNIFORM4IVPROC glUniform4iv_func = NULL; +static PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv_func = NULL; +static PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv_func = NULL; +static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv_func = NULL; +static PFNGLUSEPROGRAMPROC glUseProgram_func = NULL; +static PFNGLVALIDATEPROGRAMPROC glValidateProgram_func = NULL; + +static PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f_func = NULL; +static PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f_func = NULL; +static PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f_func = NULL; +static PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f_func = NULL; +static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer_func = NULL; + +static PFNGLSTENCILOPSEPARATEPROC glStencilOpSeparate_func = NULL; +static PFNGLSTENCILFUNCSEPARATEPROC glStencilFuncSeparate_func = NULL; +static PFNGLSTENCILMASKSEPARATEPROC glStencilMaskSeparate_func = NULL; + +static PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate_func = NULL; +static PFNGLDRAWBUFFERSPROC glDrawBuffers_func = NULL; + + + +// Get ptrs to 2.0 API functions. +// \param errorFunc returns name of API function in case of error +// \return true for success, false for error +bool +API2Test::getFunctions_2_0(char **errorFunc) +{ +#define GET(PTR, TYPE, STR) \ + PTR = (TYPE) GLUtils::getProcAddress(STR); \ + if (!PTR) { \ + *errorFunc = STR; \ + return false; \ + } + + // shading language + GET(glAttachShader_func, PFNGLATTACHSHADERPROC, "glAttachShader"); + GET(glBindAttribLocation_func, PFNGLBINDATTRIBLOCATIONPROC, "glBindAttribLocation"); + GET(glCompileShader_func, PFNGLCOMPILESHADERPROC, "glCompileShader"); + GET(glCreateProgram_func, PFNGLCREATEPROGRAMPROC, "glCreateProgram"); + GET(glCreateShader_func, PFNGLCREATESHADERPROC, "glCreateShader"); + GET(glDeleteProgram_func, PFNGLDELETEPROGRAMPROC, "glDeleteProgram"); + GET(glDeleteShader_func, PFNGLDELETESHADERPROC, "glDeleteShader"); + GET(glDisableVertexAttribArray_func, PFNGLDISABLEVERTEXATTRIBARRAYPROC, "glDisableVertexAttribArray"); + GET(glEnableVertexAttribArray_func, PFNGLENABLEVERTEXATTRIBARRAYPROC, "glEnableVertexAttribArray"); + GET(glGetAttachedShaders_func, PFNGLGETATTACHEDSHADERSPROC, "glGetAttachedShaders"); + GET(glGetAttribLocation_func, PFNGLGETATTRIBLOCATIONPROC, "glGetAttribLocation"); + GET(glGetProgramInfoLog_func, PFNGLGETPROGRAMINFOLOGPROC, "glGetProgramInfoLog"); + GET(glGetShaderInfoLog_func, PFNGLGETSHADERINFOLOGPROC, "glGetShaderInfoLog"); + GET(glGetProgramiv_func, PFNGLGETPROGRAMIVPROC, "glGetProgramiv"); + GET(glGetShaderiv_func, PFNGLGETSHADERIVPROC, "glGetShaderiv"); + GET(glGetShaderSource_func, PFNGLGETSHADERSOURCEPROC, "glGetShaderSource"); + GET(glGetUniformLocation_func, PFNGLGETUNIFORMLOCATIONPROC, "glGetUniformLocation"); + GET(glGetUniformfv_func, PFNGLGETUNIFORMFVPROC, "glGetUniformfv"); + GET(glIsProgram_func, PFNGLISPROGRAMPROC, "glIsProgram"); + GET(glIsShader_func, PFNGLISSHADERPROC, "glIsShader"); + GET(glLinkProgram_func, PFNGLLINKPROGRAMPROC, "glLinkProgram"); + GET(glShaderSource_func, PFNGLSHADERSOURCEPROC, "glShaderSource"); + GET(glUniform1f_func, PFNGLUNIFORM1FPROC, "glUniform1f"); + GET(glUniform2f_func, PFNGLUNIFORM2FPROC, "glUniform2f"); + GET(glUniform3f_func, PFNGLUNIFORM3FPROC, "glUniform3f"); + GET(glUniform4f_func, PFNGLUNIFORM4FPROC, "glUniform4f"); + GET(glUniform1fv_func, PFNGLUNIFORM1FVPROC, "glUniform1fv"); + GET(glUniform2fv_func, PFNGLUNIFORM2FVPROC, "glUniform2fv"); + GET(glUniform3fv_func, PFNGLUNIFORM3FVPROC, "glUniform3fv"); + GET(glUniform4fv_func, PFNGLUNIFORM3FVPROC, "glUniform4fv"); + GET(glUniform1i_func, PFNGLUNIFORM1IPROC, "glUniform1i"); + GET(glUniform2i_func, PFNGLUNIFORM2IPROC, "glUniform2i"); + GET(glUniform3i_func, PFNGLUNIFORM3IPROC, "glUniform3i"); + GET(glUniform4i_func, PFNGLUNIFORM4IPROC, "glUniform4i"); + GET(glUniform1iv_func, PFNGLUNIFORM1IVPROC, "glUniform1iv"); + GET(glUniform2iv_func, PFNGLUNIFORM2IVPROC, "glUniform2iv"); + GET(glUniform3iv_func, PFNGLUNIFORM3IVPROC, "glUniform3iv"); + GET(glUniform4iv_func, PFNGLUNIFORM4IVPROC, "glUniform4iv"); + GET(glUniformMatrix2fv_func, PFNGLUNIFORMMATRIX2FVPROC, "glUniformMatrix2fv"); + GET(glUniformMatrix3fv_func, PFNGLUNIFORMMATRIX3FVPROC, "glUniformMatrix3fv"); + GET(glUniformMatrix4fv_func, PFNGLUNIFORMMATRIX4FVPROC, "glUniformMatrix4fv"); + GET(glUseProgram_func, PFNGLUSEPROGRAMPROC, "glUseProgram"); + GET(glValidateProgram_func, PFNGLVALIDATEPROGRAMPROC, "glValidateProgram"); + GET(glVertexAttrib1f_func, PFNGLVERTEXATTRIB1FPROC, "glVertexAttrib1f"); + GET(glVertexAttrib2f_func, PFNGLVERTEXATTRIB2FPROC, "glVertexAttrib2f"); + GET(glVertexAttrib3f_func, PFNGLVERTEXATTRIB3FPROC, "glVertexAttrib3f"); + GET(glVertexAttrib4f_func, PFNGLVERTEXATTRIB4FPROC, "glVertexAttrib4f"); + GET(glVertexAttribPointer_func, PFNGLVERTEXATTRIBPOINTERPROC, "glVertexAttribPointer"); + + // stencil + GET(glStencilOpSeparate_func, PFNGLSTENCILOPSEPARATEPROC, "glStencilOpSeparate"); + GET(glStencilFuncSeparate_func, PFNGLSTENCILFUNCSEPARATEPROC, "glStencilFuncSeparate"); + GET(glStencilMaskSeparate_func, PFNGLSTENCILMASKSEPARATEPROC, "glStencilMaskSeparate"); + + // misc + GET(glBlendEquationSeparate_func, PFNGLBLENDEQUATIONSEPARATEPROC, "glBlendEquationSeparate"); + GET(glDrawBuffers_func, PFNGLDRAWBUFFERSPROC, "glDrawBuffers"); + + return true; +#undef GET +} + + +bool +API2Test::setup(void) +{ + // check that we have OpenGL 2.0 + const char *version = (const char *) glGetString(GL_VERSION); + if (version[0] != '2' || version[1] != '.') { + //env->log << "OpenGL 2.0 not supported\n"; + return false; + } + + char *errorFunc; + if (!getFunctions_2_0(&errorFunc)) { + env->log << "Unable to get pointer to OpenGL 2.0 function '" + << errorFunc + << "'\n"; + return false; + } + + GLenum err = glGetError(); + assert(!err); // should be OK + + // setup vertex transform (we'll draw a quad in middle of window) + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-4.0, 4.0, -4.0, 4.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + // compute error tolerances (may need fine-tuning) + int bufferBits[5]; + glGetIntegerv(GL_RED_BITS, &bufferBits[0]); + glGetIntegerv(GL_GREEN_BITS, &bufferBits[1]); + glGetIntegerv(GL_BLUE_BITS, &bufferBits[2]); + glGetIntegerv(GL_ALPHA_BITS, &bufferBits[3]); + glGetIntegerv(GL_DEPTH_BITS, &bufferBits[4]); + + tolerance[0] = 2.0 / (1 << bufferBits[0]); + tolerance[1] = 2.0 / (1 << bufferBits[1]); + tolerance[2] = 2.0 / (1 << bufferBits[2]); + if (bufferBits[3]) + tolerance[3] = 2.0 / (1 << bufferBits[3]); + else + tolerance[3] = 1.0; + if (bufferBits[4]) + tolerance[4] = 16.0 / (1 << bufferBits[4]); + else + tolerance[4] = 1.0; + + return true; +} + + +void +API2Test::reportFailure(const char *msg, int line) const +{ + env->log << "FAILURE: " << msg << " (at tapi2.cpp:" << line << ")\n"; +} + + +void +API2Test::reportFailure(const char *msg, GLenum target, int line) const +{ + env->log << "FAILURE: " << msg; + if (target == GL_FRAGMENT_SHADER) + env->log << " (fragment)"; + else + env->log << " (vertex)"; + env->log << " (at tapi2.cpp:" << line << ")\n"; +} + + + +#define REPORT_FAILURE(MSG) reportFailure(MSG, __LINE__) +#define REPORT_FAILURE_T(MSG, TARGET) reportFailure(MSG, TARGET, __LINE__) + + +// Compare actual and expected colors +bool +API2Test::equalColors(const GLfloat act[4], const GLfloat exp[4]) const +{ + if ((fabsf(act[0] - exp[0]) > tolerance[0]) || + (fabsf(act[1] - exp[1]) > tolerance[1]) || + (fabsf(act[2] - exp[2]) > tolerance[2]) || + (fabsf(act[3] - exp[3]) > tolerance[3])) + return false; + else + return true; +} + + +// Render test quad w/ current shader program, return RGBA color of quad +void +API2Test::renderQuad(GLfloat *pixel) const +{ + const GLfloat r = 0.62; // XXX draw 16x16 pixel quad + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex2f(-r, -r); + glTexCoord2f(1, 0); glVertex2f( r, -r); + glTexCoord2f(1, 1); glVertex2f( r, r); + glTexCoord2f(0, 1); glVertex2f(-r, r); + glEnd(); + + // read a pixel from lower-left corder of rendered quad + glReadPixels(windowSize / 2 - 2, windowSize / 2 - 2, 1, 1, + GL_RGBA, GL_FLOAT, pixel); +} + + +// As above, but use vertex arrays +// \param attr which vertex attribute array to put colors into +// \param value 4-component valut to put into the attribute array +// \param pixel returns the rendered color obtained with glReadPixels +void +API2Test::renderQuadWithArrays(GLint attr, const GLfloat value[4], + GLfloat *pixel) const +{ + const GLfloat r = 0.62; // XXX draw 16x16 pixel quad + static const GLfloat vertcoords[4][3] = { + { -r, -r, 0 }, { r, -r, 0 }, { r, r, 0 }, { -r, r, 0 } + }; + GLfloat values[4][4]; + GLint i; + for (i = 0; i < 4; i++) { + values[i][0] = value[0]; + values[i][1] = value[1]; + values[i][2] = value[2]; + values[i][3] = value[3]; + }; + + glVertexPointer(3, GL_FLOAT, 0, vertcoords); + glEnable(GL_VERTEX_ARRAY); + glVertexAttribPointer_func(attr, 4, GL_FLOAT, GL_FALSE, 0, values); + glEnableVertexAttribArray_func(attr); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDrawArrays(GL_POLYGON, 0, 4); + + glDisable(GL_VERTEX_ARRAY); + glDisableVertexAttribArray_func(attr); + + // read a pixel from lower-left corder of rendered quad + glReadPixels(windowSize / 2 - 2, windowSize / 2 - 2, 1, 1, + GL_RGBA, GL_FLOAT, pixel); +} + + +GLuint +API2Test::loadAndCompileShader(GLenum target, const char *text) +{ + GLint stat, val; + GLuint shader = glCreateShader_func(target); + if (!shader) { + REPORT_FAILURE("glCreateShader failed (fragment)"); + return 0; + } + glShaderSource_func(shader, 1, + (const GLchar **) &text, NULL); + glCompileShader_func(shader); + glGetShaderiv_func(shader, GL_COMPILE_STATUS, &stat); + if (!stat) { + REPORT_FAILURE_T("glShaderSource or glCompileShader failed", target); + return 0; + } + if (!glIsShader_func(shader)) { + REPORT_FAILURE("glIsShader failed (fragment)"); + return false; + } + glGetShaderiv_func(shader, GL_SHADER_TYPE, &val); + if (val != (GLint) target) { + REPORT_FAILURE_T("glGetShaderiv(GL_SHADER_TYPE) failed", target); + return 0; + } + glGetShaderiv_func(shader, GL_COMPILE_STATUS, &val); + if (val != GL_TRUE) { + REPORT_FAILURE_T("glGetShaderiv(GL_COMPILE_STATUS) failed", target); + return 0; + } + glGetShaderiv_func(shader, GL_SHADER_SOURCE_LENGTH, &val); + // Note: some OpenGLs return a 1-char shorter length than strlen(text) + if (abs(val - (int) strlen(text)) > 1) { + REPORT_FAILURE_T("glGetShaderiv(GL_SHADER_SOURCE_LENGTH) failed", target); + return 0; + } + return shader; +} + + +GLuint +API2Test::createProgram(GLuint vertShader, GLuint fragShader) +{ + GLuint program = glCreateProgram_func(); + if (vertShader) + glAttachShader_func(program, vertShader); + if (fragShader) + glAttachShader_func(program, fragShader); + glLinkProgram_func(program); + return program; +} + + +bool +API2Test::testShaderObjectFuncs(void) +{ + static const char *vertShaderText = + "void main() { \n" + " gl_Position = ftransform(); \n" + "} \n"; + static const char *fragShaderText = + "void main() { \n" + " gl_FragColor = vec4(1.0, 0.5, 0.25, 0.0); \n" + "} \n"; + GLuint vertShader, fragShader, program; + GLint stat, val, err; + + vertShader = loadAndCompileShader(GL_VERTEX_SHADER, vertShaderText); + if (!vertShader) + return false; + fragShader = loadAndCompileShader(GL_FRAGMENT_SHADER, fragShaderText); + if (!fragShader) + return false; + + + program = createProgram(vertShader, fragShader); + if (!program) { + REPORT_FAILURE("glCreateProgram failed"); + return false; + } + glGetProgramiv_func(program, GL_LINK_STATUS, &stat); + if (!stat) { + REPORT_FAILURE("glLinkProgram failed"); + return false; + } + glUseProgram_func(program); + + glGetIntegerv(GL_CURRENT_PROGRAM, &val); + if (val != (GLint) program) { + REPORT_FAILURE("glGetInteger(GL_CURRENT_PROGRAM) failed"); + return false; + } + + err = glGetError(); + if (err) { + REPORT_FAILURE("OpenGL error detected in testShaderFuncs"); + return false; + } + + if (!glIsProgram_func(program)) { + REPORT_FAILURE("glIsProgram failed"); + return false; + } + + GLuint objects[2]; + GLsizei count; + glGetProgramiv_func(program, GL_ATTACHED_SHADERS, &val); + if (val != 2) { + REPORT_FAILURE("glGetProgramiv(GL_ATTACHED_SHADERS) failed"); + return false; + } + glGetAttachedShaders_func(program, 2, &count, objects); + if (count != 2) { + REPORT_FAILURE("glGetAttachedShaders failed (wrong count)"); + return false; + } + if (objects[0] != vertShader && objects[1] != vertShader) { + REPORT_FAILURE("glGetAttachedShaders failed (vertex shader missing)"); + return false; + } + if (objects[0] != fragShader && objects[1] != fragShader) { + REPORT_FAILURE("glGetAttachedShaders failed (fragment shader missing)"); + return false; + } + + glValidateProgram_func(program); + glGetProgramiv_func(program, GL_VALIDATE_STATUS, &stat); + if (!stat) { + REPORT_FAILURE("glValidateProgram failed"); + return false; + } + + // Delete vertex shader + glDeleteShader_func(vertShader); + if (!glIsShader_func(vertShader)) { + // the shader is still attached so the handle should be valid + REPORT_FAILURE("glIsShader(deleted shader) failed"); + return false; + } + glGetShaderiv_func(vertShader, GL_DELETE_STATUS, &stat); + if (stat != GL_TRUE) { + REPORT_FAILURE("Incorrect shader delete status"); + return false; + } + + // Delete fragment shader + glDeleteShader_func(fragShader); + + // Delete program object + glDeleteProgram_func(program); + if (!glIsProgram_func(program)) { + // the program is still in use so the handle should be valid + REPORT_FAILURE("glIsProgram(deleted program) failed"); + return false; + } + glGetProgramiv_func(program, GL_DELETE_STATUS, &stat); + if (stat != GL_TRUE) { + REPORT_FAILURE("Incorrect program delete status"); + return false; + } + + // now unbind the program + glUseProgram_func(0); + stat = glIsProgram_func(program); + if (stat) { + // the program and handle should have really been deleted now + REPORT_FAILURE("glIsProgram(deleted program) failed"); + return false; + } + + glGetProgramiv_func(program, GL_DELETE_STATUS, &stat); + err = glGetError(); + if (!err) { + // the program and handle should have been deleted now + // so glGetProgramiv() should have generated an error + REPORT_FAILURE("glGetProgramiv(deleted program) failed"); + return false; + } + + return true; +} + + +bool +API2Test::testUniformfFuncs(void) +{ + static const char *fragShaderText = + "uniform float uf1; \n" + "uniform vec2 uf2; \n" + "uniform vec3 uf3; \n" + "uniform vec4 uf4; \n" + "void main() { \n" + " gl_FragColor = vec4(uf1, uf2.y, uf3.z, uf4.w); \n" + "} \n"; + GLuint fragShader, program; + GLint uf1, uf2, uf3, uf4; + GLfloat value[4]; + + fragShader = loadAndCompileShader(GL_FRAGMENT_SHADER, fragShaderText); + if (!fragShader) { + return false; + } + program = createProgram(0, fragShader); + if (!program) { + REPORT_FAILURE("glCreateProgram (uniform test) failed"); + return false; + } + glUseProgram_func(program); + + uf1 = glGetUniformLocation_func(program, "uf1"); + if (uf1 < 0) { + REPORT_FAILURE("glGetUniform \"uf1\" failed"); + return false; + } + uf2 = glGetUniformLocation_func(program, "uf2"); + if (uf2 < 0) { + REPORT_FAILURE("glGetUniform \"uf2\" failed"); + return false; + } + uf3 = glGetUniformLocation_func(program, "uf3"); + if (uf3 < 0) { + REPORT_FAILURE("glGetUniform \"uf3\" failed"); + return false; + } + uf4 = glGetUniformLocation_func(program, "uf4"); + if (uf4 < 0) { + REPORT_FAILURE("glGetUniform \"uf4\" failed"); + return false; + } + + + GLfloat pixel[4], expected[4]; + + // Test glUniform[1234]f() + expected[0] = 0.1; + expected[1] = 0.2; + expected[2] = 0.3; + expected[3] = 0.4; + glUniform1f_func(uf1, expected[0]); + glUniform2f_func(uf2, 0.0, expected[1]); + glUniform3f_func(uf3, 0.0, 0.0, expected[2]); + glUniform4f_func(uf4, 0.0, 0.0, 0.0, expected[3]); + renderQuad(pixel); + if (!equalColors(pixel, expected)) { + REPORT_FAILURE("glUniform[1234]f failed"); + printf("%f %f %f %f\n", pixel[0], pixel[1], pixel[2], pixel[3]); + return false; + } + + // Test glUniform[1234]fv() + GLfloat u[4]; + expected[0] = 0.9; + expected[1] = 0.8; + expected[2] = 0.7; + expected[3] = 0.6; + u[0] = expected[0]; + glUniform1fv_func(uf1, 1, u); + u[0] = 0.0; u[1] = expected[1]; + glUniform2fv_func(uf2, 1, u); + u[0] = 0.0; u[1] = 0.0; u[2] = expected[2]; + glUniform3fv_func(uf3, 1, u); + u[0] = 0.0; u[1] = 0.0; u[2] = 0.0; u[3] = expected[3]; + glUniform4fv_func(uf4, 1, u); + renderQuad(pixel); + if (!equalColors(pixel, expected)) { + REPORT_FAILURE("glUniform[1234]f failed"); + return false; + } + + // Test glGetUniformfv + glUniform4fv_func(uf4, 1, expected); + glGetUniformfv_func(program, uf4, value); + if (value[0] != expected[0] || + value[1] != expected[1] || + value[2] != expected[2] || + value[3] != expected[3]) { + REPORT_FAILURE("glGetUniformfv failed"); + return false; + } + + return true; +} + + +bool +API2Test::testUniformiFuncs(void) +{ + static const char *fragShaderText = + "uniform int ui1; \n" + "uniform ivec2 ui2; \n" + "uniform ivec3 ui3; \n" + "uniform ivec4 ui4; \n" + "void main() { \n" + " gl_FragColor = vec4(ui1, ui2.y, ui3.z, ui4.w) * 0.1; \n" + "} \n"; + GLuint fragShader, program; + GLint ui1, ui2, ui3, ui4; + + fragShader = loadAndCompileShader(GL_FRAGMENT_SHADER, fragShaderText); + if (!fragShader) { + return false; + } + program = createProgram(0, fragShader); + if (!program) { + REPORT_FAILURE("glCreateProgram (uniform test) failed"); + return false; + } + glUseProgram_func(program); + + ui1 = glGetUniformLocation_func(program, "ui1"); + if (ui1 < 0) { + REPORT_FAILURE("glGetUniform \"ui1\" failed"); + return false; + } + ui2 = glGetUniformLocation_func(program, "ui2"); + if (ui2 < 0) { + REPORT_FAILURE("glGetUniform \"ui2\" failed"); + return false; + } + ui3 = glGetUniformLocation_func(program, "ui3"); + if (ui3 < 0) { + REPORT_FAILURE("glGetUniform \"ui3\" failed"); + return false; + } + ui4 = glGetUniformLocation_func(program, "ui4"); + if (ui4 < 0) { + REPORT_FAILURE("glGetUniform \"ui4\" failed"); + return false; + } + + GLfloat pixel[4], expected[4]; + GLint expectedInt[4]; + + // Test glUniform[1234]f() + expectedInt[0] = 1; + expectedInt[1] = 2; + expectedInt[2] = 3; + expectedInt[3] = 4; + expected[0] = 0.1; + expected[1] = 0.2; + expected[2] = 0.3; + expected[3] = 0.4; + glUniform1i_func(ui1, expectedInt[0]); + glUniform2i_func(ui2, 0, expectedInt[1]); + glUniform3i_func(ui3, 0, 0, expectedInt[2]); + glUniform4i_func(ui4, 0, 0, 0, expectedInt[3]); + renderQuad(pixel); + if (!equalColors(pixel, expected)) { + REPORT_FAILURE("glUniform[1234]i failed"); + printf("%f %f %f %f\n", pixel[0], pixel[1], pixel[2], pixel[3]); + return false; + } + + // Test glUniform[1234]fv() + GLint u[4]; + expectedInt[0] = 9; + expectedInt[1] = 8; + expectedInt[2] = 7; + expectedInt[3] = 6; + expected[0] = 0.9; + expected[1] = 0.8; + expected[2] = 0.7; + expected[3] = 0.6; + u[0] = expectedInt[0]; + glUniform1iv_func(ui1, 1, u); + u[0] = 0; u[1] = expectedInt[1]; + glUniform2iv_func(ui2, 1, u); + u[0] = 0; u[1] = 0; u[2] = expectedInt[2]; + glUniform3iv_func(ui3, 1, u); + u[0] = 0; u[1] = 0; u[2] = 0; u[3] = expectedInt[3]; + glUniform4iv_func(ui4, 1, u); + renderQuad(pixel); + if (!equalColors(pixel, expected)) { + REPORT_FAILURE("glUniform[1234]i failed"); + printf("%f %f %f %f\n", pixel[0], pixel[1], pixel[2], pixel[3]); + return false; + } + + return true; +} + + +bool +API2Test::testShaderAttribs(void) +{ + static const char *vertShaderText = + "attribute vec4 generic; \n" + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor = generic; \n" + "} \n"; + GLuint vertShader, program; + + vertShader = loadAndCompileShader(GL_VERTEX_SHADER, vertShaderText); + if (!vertShader) { + return false; + } + program = createProgram(vertShader, 0); + if (!program) { + REPORT_FAILURE("glCreateProgram (uniform test) failed"); + return false; + } + glUseProgram_func(program); + + static const GLfloat testColors[3][4] = { + { 1.0, 0.5, 0.25, 0.0 }, + { 0.0, 0.1, 0.2, 0.3 }, + { 0.5, 0.6, 0.7, 0.8 }, + }; + + // let compiler allocate the attribute location + const GLint attr = glGetAttribLocation_func(program, "generic"); + if (attr < 0) { + REPORT_FAILURE("glGetAttribLocation failed"); + return false; + } + for (int i = 0; i < 3; i++) { + GLfloat pixel[4]; + renderQuadWithArrays(attr, testColors[i], pixel); + if (!equalColors(pixel, testColors[i])) { + REPORT_FAILURE("Vertex array test failed"); + return false; + } + } + + // Test explicit attribute binding. + const GLint bindAttr = 6; // XXX a non-colliding alias + glBindAttribLocation_func(program, bindAttr, "generic"); + glLinkProgram_func(program); + GLint loc = glGetAttribLocation_func(program, "generic"); + if (loc != bindAttr) { + REPORT_FAILURE("glBindAttribLocation failed"); + return false; + } + for (int i = 0; i < 3; i++) { + GLfloat pixel[4]; + renderQuadWithArrays(bindAttr, testColors[i], pixel); + if (!equalColors(pixel, testColors[i])) { + REPORT_FAILURE("Vertex array test failed (2)"); + return false; + } + } + + return true; +} + +#define CLAMP( X, MIN, MAX ) ( (X)<(MIN) ? (MIN) : ((X)>(MAX) ? (MAX) : (X)) ) + +bool +API2Test::testStencilFuncSeparate(void) +{ + GLint val; + GLint stencilBits, stencilMax; + + glGetIntegerv(GL_STENCIL_BITS, &stencilBits); + stencilMax = (1 << stencilBits) - 1; + + glStencilFuncSeparate_func(GL_FRONT, GL_LEQUAL, 12, 0xf); + glStencilFuncSeparate_func(GL_BACK, GL_GEQUAL, 13, 0xe); + + glGetIntegerv(GL_STENCIL_BACK_FUNC, &val); + if (val != GL_GEQUAL) { + REPORT_FAILURE("GL_STENCIL_BACK_FUNC query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_FUNC, &val); + if (val != GL_LEQUAL) { + REPORT_FAILURE("GL_STENCIL_FUNC (front) query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_BACK_REF, &val); + if (val != CLAMP(13, 0, stencilMax)) { + REPORT_FAILURE("GL_STENCIL_BACK_REF query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_REF, &val); + if (val != CLAMP(12, 0, stencilMax)) { + REPORT_FAILURE("GL_STENCIL_REF (front) query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &val); + if (val != 0xe) { + REPORT_FAILURE("GL_STENCIL_BACK_VALUE_MASK query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_VALUE_MASK, &val); + if (val != 0xf) { + REPORT_FAILURE("GL_STENCIL_VALUE_MASK (front) query returned wrong value"); + return false; + } + + return true; +} + + +bool +API2Test::testStencilOpSeparate(void) +{ + GLint val; + + // face, fail, zfail, zpass + glStencilOpSeparate_func(GL_FRONT, GL_INVERT, GL_ZERO, GL_INCR); + glStencilOpSeparate_func(GL_BACK, GL_INCR, GL_KEEP, GL_REPLACE); + + glGetIntegerv(GL_STENCIL_BACK_FAIL, &val); + if (val != GL_INCR) { + REPORT_FAILURE("GL_STENCIL_BACK_FAIL query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_FAIL, &val); + if (val != GL_INVERT) { + REPORT_FAILURE("GL_STENCIL_FAIL (front) query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &val); + if (val != GL_KEEP) { + REPORT_FAILURE("GL_STENCIL_BACK_PASS_DEPTH_FAIL query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &val); + if (val != GL_ZERO) { + REPORT_FAILURE("GL_STENCIL_PASS_DEPTH_FAIL (front) query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &val); + if (val != GL_REPLACE) { + REPORT_FAILURE("GL_STENCIL_BACK_PASS_DEPTH_PASS query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &val); + if (val != GL_INCR) { + REPORT_FAILURE("GL_STENCIL_PASS_DEPTH_PASS (front) query returned wrong value"); + return false; + } + + return true; +} + + +bool +API2Test::testStencilMaskSeparate(void) +{ + GLint val; + + // face, fail, zfail, zpass + glStencilMaskSeparate_func(GL_BACK, 0xa); + glStencilMaskSeparate_func(GL_FRONT, 0xb); + + glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &val); + if (val != 0xa) { + REPORT_FAILURE("GL_STENCIL_BACK_WRITEMASK query returned wrong value"); + return false; + } + + glGetIntegerv(GL_STENCIL_WRITEMASK, &val); + if (val != 0xb) { + REPORT_FAILURE("GL_STENCIL_WRITEMASK (front) query returned wrong value"); + return false; + } + + return true; +} + + +bool +API2Test::testBlendEquationSeparate(void) +{ + GLint val; + + glBlendEquationSeparate_func(GL_MAX, GL_FUNC_SUBTRACT); + + glGetIntegerv(GL_BLEND_EQUATION, &val); + if (val != GL_MAX) { + REPORT_FAILURE("GL_BLEND_EQUATION (rgb) query returned wrong value"); + return false; + } + + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &val); + if (val != GL_FUNC_SUBTRACT) { + REPORT_FAILURE("GL_BLEND_EQUATION (rgb) query returned wrong value"); + return false; + } + + return true; +} + + +bool +API2Test::testDrawBuffers(void) +{ + const int MAX = 2; + GLint maxBuf = -1, i, n, val; + GLenum buffers[MAX], err; + GLint initDrawBuffer; + + glGetIntegerv(GL_DRAW_BUFFER, &initDrawBuffer); + + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxBuf); + if (maxBuf < 1) { + REPORT_FAILURE("GL_MAX_DRAW_BUFFERS query failed"); + return false; + } + + n = maxBuf < MAX ? maxBuf : MAX; + assert(n > 0); + for (i = 0; i < n; i++) { + buffers[i] = (i & 1) ? GL_FRONT_LEFT : GL_BACK_LEFT; + } + glDrawBuffers_func(n, buffers); + + for (i = 0; i < n; i++) { + glGetIntegerv(GL_DRAW_BUFFER0 + i, &val); + if (val != (GLint) buffers[i]) { + REPORT_FAILURE("glDrawBuffers failed"); + return false; + } + } + + // restore + glDrawBuffer(initDrawBuffer); + + err = glGetError(); + if (err) { + REPORT_FAILURE("glDrawBuffers generrated an OpenGL error"); + return false; + } + + return true; +} + + +// Run all the subtests, incrementing numPassed, numFailed +void +API2Test::runSubTests(MultiTestResult &r) +{ + static TestFunc funcs[] = { + &GLEAN::API2Test::testStencilFuncSeparate, + &GLEAN::API2Test::testStencilOpSeparate, + &GLEAN::API2Test::testStencilMaskSeparate, + &GLEAN::API2Test::testBlendEquationSeparate, + &GLEAN::API2Test::testDrawBuffers, + &GLEAN::API2Test::testShaderObjectFuncs, + &GLEAN::API2Test::testUniformfFuncs, + &GLEAN::API2Test::testUniformiFuncs, + &GLEAN::API2Test::testShaderAttribs, + NULL + }; + + for (int i = 0; funcs[i]; i++) + if ((this->*funcs[i])()) + r.numPassed++; + else + r.numFailed++; +} + + +void +API2Test::runOne(MultiTestResult &r, Window &w) +{ + (void) w; + + if (!setup()) { + r.pass = false; + return; + } + + runSubTests(r); + + r.pass = (r.numFailed == 0); +} + + +// The test object itself: +API2Test api2Test("api2", "window, rgb, z, db", + "", // no extension filter (we'll test for version 2.x during setup) + "API2 test: check that OpenGL 2.x API functions work.\n" + ); + + + +} // namespace GLEAN diff --git a/tests/glean/tapi2.h b/tests/glean/tapi2.h new file mode 100644 index 000000000..c4df77193 --- /dev/null +++ b/tests/glean/tapi2.h @@ -0,0 +1,87 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 1999 Allen Akin All Rights Reserved. +// +// 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 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + +// tapi2.h: Test OpenGL 2.x API functions/features +// Brian Paul 9 March 2007 + +#ifndef __tapi2_h__ +#define __tapi2_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +#define windowSize 100 + + +class API2Test: public MultiTest +{ +public: + API2Test(const char* testName, const char* filter, + const char *extensions, const char* description): + MultiTest(testName, filter, extensions, description) + { + } + + virtual void runOne(MultiTestResult &r, Window &w); + +private: + typedef bool (API2Test::*TestFunc)(void); + + GLfloat tolerance[5]; + bool getFunctions_2_0(char **errorFunc); + + GLuint loadAndCompileShader(GLenum target, const char *str); + GLuint createProgram(GLuint vertShader, GLuint fragShader); + + void renderQuad(GLfloat *pixel) const; + void renderQuadWithArrays(GLint attr, const GLfloat value[4], + GLfloat *pixel) const; + + bool testStencilFuncSeparate(void); + bool testStencilOpSeparate(void); + bool testStencilMaskSeparate(void); + bool testBlendEquationSeparate(void); + bool testDrawBuffers(void); + bool testShaderObjectFuncs(void); + bool testUniformfFuncs(void); + bool testUniformiFuncs(void); + bool testShaderAttribs(void); + + void runSubTests(MultiTestResult &r); + + bool setup(void); + bool equalColors(const GLfloat a[4], const GLfloat b[4]) const; + + void reportFailure(const char *msg, int line) const; + void reportFailure(const char *msg, GLenum target, int line) const; +}; + +} // namespace GLEAN + +#endif // __tglsl1_h__ diff --git a/tests/glean/tbase.h b/tests/glean/tbase.h index 476754278..afe539437 100644 --- a/tests/glean/tbase.h +++ b/tests/glean/tbase.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999-2000 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT /* @@ -67,7 +67,7 @@ constructing a test: contexts that support all the listed extensions. Extension names in the string may be separated with non alphanumerics; whitespace and commas are used by convention. - + A description string. This will be printed in the test log to describe the test. @@ -89,7 +89,7 @@ Your new test will need a few common declarations (such as constructors). To simplify writing them, this file provides a few helper macros. GLEAN_CLASS(TEST,RESULT) handles the declarations for a test class named TEST and a result class named RESULT, using the -default values for window width and height and the run-once flag. +default values for window width and height and the run-once flag. GLEAN_CLASS_WH() and GLEAN_CLASS_WHO() allow you to specify the width, height, and run-once flag if you choose. @@ -170,12 +170,12 @@ public: virtual void putresults(ostream& s) const = 0; virtual bool getresults(istream& s) = 0; - + virtual void put(ostream& s) const { s << config->canonicalDescription() << '\n'; putresults(s); } - + virtual bool get(istream& s) { SkipWhitespace(s); string configDesc; @@ -194,7 +194,7 @@ public: description = aDescription; fWidth = 258; fHeight = 258; - testOne = false; + testOne = false; } BaseTest(const char* aName, const char* aFilter, Test** thePrereqs, const char* aDescription): @@ -204,7 +204,7 @@ public: description = aDescription; fWidth = 258; fHeight = 258; - testOne = false; + testOne = false; } BaseTest(const char* aName, const char* aFilter, const char* anExtensionList, @@ -243,7 +243,7 @@ public: } return v; } - + virtual void logDescription() { if (env->options.verbosity) env->log << @@ -255,10 +255,8 @@ public: virtual void run(Environment& environment) { if (hasRun) return; // no multiple invocations // Invoke the prerequisite tests, if any: - if (!environment.options.ignorePrereqs) { - for (Test** t = prereqs; t != 0 && *t != 0; ++t) - (*t)->run(environment); - } + for (Test** t = prereqs; t != 0 && *t != 0; ++t) + (*t)->run(environment); env = &environment; // make environment available logDescription(); // log invocation WindowSystem& ws = env->winSys; @@ -291,7 +289,7 @@ public: r->config = *p; runOne(*r, w); logOne(*r); - + // Save the result results.push_back(r); r->put(os); @@ -311,10 +309,10 @@ public: env->log << "Could not create a rendering context\n"; } env->log << '\n'; - + hasRun = true; // Note that we've completed the run } - + virtual void compare(Environment& environment) { env = &environment; // Save the environment logDescription(); @@ -397,16 +395,6 @@ public: virtual void logConcise(ResultType& r) { env->log << r.config->conciseDescription() << '\n'; } - - virtual void printDetails() { - } - - virtual void details(Environment& environment) { - env = &environment; - env->log << "DETAILS for " << name << '\n'; - printDetails(); - env->log << "END DETAILS\n"; - } }; // class BaseTest } // namespace GLEAN diff --git a/tests/glean/tblend.cpp b/tests/glean/tblend.cpp index 87d0934d7..3fb8d6116 100644 --- a/tests/glean/tblend.cpp +++ b/tests/glean/tblend.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,20 +23,32 @@ // 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. -// +// // END_COPYRIGHT // tblend.cpp: Test blending functions. +#include <assert.h> #include "tblend.h" #include "rand.h" #include "image.h" #include <cmath> -namespace { +#define ELEMENTS(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0])) -struct factorNameMapping {GLenum factor; char* name;}; -factorNameMapping factorNames[] = { +static PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate_func = NULL; +static PFNGLBLENDCOLORPROC glBlendColor_func = NULL; +static PFNGLBLENDEQUATIONPROC glBlendEquation_func = NULL; +static PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate_func = NULL; + +//namespace { + +struct enumNameMapping { + GLenum token; + char* name; +}; + +enumNameMapping factorNames[] = { {GL_DST_ALPHA, "GL_DST_ALPHA"}, {GL_DST_COLOR, "GL_DST_COLOR"}, {GL_ONE, "GL_ONE"}, @@ -47,35 +59,75 @@ factorNameMapping factorNames[] = { {GL_SRC_ALPHA, "GL_SRC_ALPHA"}, {GL_SRC_ALPHA_SATURATE, "GL_SRC_ALPHA_SATURATE"}, {GL_SRC_COLOR, "GL_SRC_COLOR"}, - {GL_ZERO, "GL_ZERO"} + {GL_ZERO, "GL_ZERO"}, + {GL_CONSTANT_COLOR, "GL_CONSTANT_COLOR"}, + {GL_ONE_MINUS_CONSTANT_COLOR, "GL_ONE_MINUS_CONSTANT_COLOR"}, + {GL_CONSTANT_ALPHA, "GL_CONSTANT_ALPHA"}, + {GL_ONE_MINUS_CONSTANT_ALPHA, "GL_ONE_MINUS_CONSTANT_ALPHA"} }; +// aka blend "equation" +enumNameMapping blendopNames[] = { + {GL_FUNC_ADD, "GL_FUNC_ADD"}, + {GL_FUNC_SUBTRACT, "GL_FUNC_SUBTRACT"}, + {GL_FUNC_REVERSE_SUBTRACT, "GL_FUNC_REVERSE_SUBTRACT"}, + {GL_MIN, "GL_MIN"}, + {GL_MAX, "GL_MAX"} +}; + + char* factorToName(GLenum factor) { - for (unsigned int i = 0; - i < sizeof(factorNames) / sizeof(factorNames[0]); - ++i) - if (factorNames[i].factor == factor) + for (unsigned int i = 0; i < ELEMENTS(factorNames); ++i) + if (factorNames[i].token == factor) return factorNames[i].name; return 0; } // factorToName GLenum nameToFactor(string& name) { - for (unsigned int i = 0; - i < sizeof(factorNames) / sizeof(factorNames[0]); - ++i) + for (unsigned int i = 0; i < ELEMENTS(factorNames); ++i) if (factorNames[i].name == name) - return factorNames[i].factor; + return factorNames[i].token; return GL_ZERO; } // nameToFactor +char * +opToName(GLenum op) { + for (unsigned int i = 0; i < ELEMENTS(blendopNames); ++i) + if (blendopNames[i].token == op) + return blendopNames[i].name; + return 0; +} // opToName + +GLenum +nameToOp(string& name) { + for (unsigned int i = 0; i < ELEMENTS(blendopNames); ++i) + if (blendopNames[i].name == name) + return blendopNames[i].token; + return GL_ZERO; +} // nameToOp + + bool needsDstAlpha(const GLenum func) { return func == GL_DST_ALPHA || func == GL_ONE_MINUS_DST_ALPHA || func == GL_SRC_ALPHA_SATURATE; } +bool +needsBlendColor(const GLenum func) { + switch (func) { + case GL_CONSTANT_COLOR: + case GL_ONE_MINUS_CONSTANT_COLOR: + case GL_CONSTANT_ALPHA: + case GL_ONE_MINUS_CONSTANT_ALPHA: + return true; + default: + return false; + } +} + void makeRGBA(GLEAN::RandomBitsDouble& rRand, GLEAN::RandomBitsDouble& gRand, @@ -88,7 +140,7 @@ makeRGBA(GLEAN::RandomBitsDouble& rRand, rgba[3] = aRand.next(); } // makeRGBA -void +void drawQuad(const int x, const int y, const float* color) { glColor4fv(color); glBegin(GL_QUADS); @@ -109,101 +161,295 @@ clamp(float f) { return f; } // clamp -void -applyBlend(GLenum srcFactor, GLenum dstFactor, float* dst, float* src) { - // XXX Currently we don't test any of the const-color blend factors. - // It would be a good idea to do so as soon as we have access to an - // implementation that supports the OpenGL 1.2 imaging extensions. - - float sf[4]; - switch (srcFactor) { - case GL_ZERO: - sf[0] = sf[1] = sf[2] = sf[3] = 0.0; - break; - case GL_ONE: - sf[0] = sf[1] = sf[2] = sf[3] = 1.0; - break; - case GL_DST_COLOR: - sf[0] = dst[0]; sf[1] = dst[1]; sf[2] = dst[2]; sf[3] = dst[3]; - break; - case GL_ONE_MINUS_DST_COLOR: - sf[0] = 1.0 - dst[0]; sf[1] = 1.0 - dst[1]; - sf[2] = 1.0 - dst[2]; sf[3] = 1.0 - dst[3]; - break; - case GL_SRC_ALPHA: - sf[0] = sf[1] = sf[2] = sf[3] = src[3]; - break; - case GL_ONE_MINUS_SRC_ALPHA: - sf[0] = sf[1] = sf[2] = sf[3] = 1.0 - src[3]; - break; - case GL_DST_ALPHA: - sf[0] = sf[1] = sf[2] = sf[3] = dst[3]; - break; - case GL_ONE_MINUS_DST_ALPHA: - sf[0] = sf[1] = sf[2] = sf[3] = 1.0 - dst[3]; - break; - case GL_SRC_ALPHA_SATURATE: { - float f = 1.0 - dst[3]; - if (src[3] < f) - f = src[3]; - sf[0] = sf[1] = sf[2] = f; sf[3] = 1.0; +static void +applyBlend(GLenum srcFactorRGB, GLenum srcFactorA, + GLenum dstFactorRGB, GLenum dstFactorA, + GLenum opRGB, GLenum opA, + float* dst, const float* src, + const GLfloat constantColor[4]) +{ + float sf[4], df[4]; + + if (opRGB != GL_MIN && opRGB != GL_MAX) { + // Src RGB term + switch (srcFactorRGB) { + case GL_ZERO: + sf[0] = sf[1] = sf[2] = 0.0; + break; + case GL_ONE: + sf[0] = sf[1] = sf[2] = 1.0; + break; + case GL_DST_COLOR: + sf[0] = dst[0]; + sf[1] = dst[1]; + sf[2] = dst[2]; + break; + case GL_ONE_MINUS_DST_COLOR: + sf[0] = 1.0 - dst[0]; + sf[1] = 1.0 - dst[1]; + sf[2] = 1.0 - dst[2]; + break; + case GL_SRC_ALPHA: + sf[0] = sf[1] = sf[2] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + sf[0] = sf[1] = sf[2] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + sf[0] = sf[1] = sf[2] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + sf[0] = sf[1] = sf[2] = 1.0 - dst[3]; + break; + case GL_SRC_ALPHA_SATURATE: { + float f = 1.0 - dst[3]; + if (src[3] < f) + f = src[3]; + sf[0] = sf[1] = sf[2] = f; + } + break; + case GL_CONSTANT_COLOR: + sf[0] = constantColor[0]; + sf[1] = constantColor[1]; + sf[2] = constantColor[2]; + break; + case GL_ONE_MINUS_CONSTANT_COLOR: + sf[0] = 1.0 - constantColor[0]; + sf[1] = 1.0 - constantColor[1]; + sf[2] = 1.0 - constantColor[2]; + break; + case GL_CONSTANT_ALPHA: + sf[0] = + sf[1] = + sf[2] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_ALPHA: + sf[0] = + sf[1] = + sf[2] = 1.0 - constantColor[3]; + break; + default: + sf[0] = sf[1] = sf[2] = 0.0; + abort(); + break; + } + + // Dest RGB term + switch (dstFactorRGB) { + case GL_ZERO: + df[0] = df[1] = df[2] = 0.0; + break; + case GL_ONE: + df[0] = df[1] = df[2] = 1.0; + break; + case GL_SRC_COLOR: + df[0] = src[0]; + df[1] = src[1]; + df[2] = src[2]; + break; + case GL_ONE_MINUS_SRC_COLOR: + df[0] = 1.0 - src[0]; + df[1] = 1.0 - src[1]; + df[2] = 1.0 - src[2]; + break; + case GL_SRC_ALPHA: + df[0] = df[1] = df[2] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + df[0] = df[1] = df[2] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + df[0] = df[1] = df[2] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + df[0] = df[1] = df[2] = 1.0 - dst[3]; + break; + case GL_CONSTANT_COLOR: + df[0] = constantColor[0]; + df[1] = constantColor[1]; + df[2] = constantColor[2]; + break; + case GL_ONE_MINUS_CONSTANT_COLOR: + df[0] = 1.0 - constantColor[0]; + df[1] = 1.0 - constantColor[1]; + df[2] = 1.0 - constantColor[2]; + break; + case GL_CONSTANT_ALPHA: + df[0] = + df[1] = + df[2] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_ALPHA: + df[0] = + df[1] = + df[2] = 1.0 - constantColor[3]; + break; + default: + df[0] = df[1] = df[2] = 0.0; + abort(); + break; } - break; - default: - sf[0] = sf[1] = sf[2] = sf[3] = 0.0; - break; } - float df[4]; - switch (dstFactor) { - case GL_ZERO: - df[0] = df[1] = df[2] = df[3] = 0.0; + if (opA != GL_MIN && opA != GL_MAX) { + // Src Alpha term + switch (srcFactorA) { + case GL_ZERO: + sf[3] = 0.0; + break; + case GL_ONE: + sf[3] = 1.0; + break; + case GL_DST_COLOR: + sf[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_COLOR: + sf[3] = 1.0 - dst[3]; + break; + case GL_SRC_ALPHA: + sf[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + sf[3] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + sf[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + sf[3] = 1.0 - dst[3]; + break; + case GL_SRC_ALPHA_SATURATE: + sf[3] = 1.0; + break; + case GL_CONSTANT_COLOR: + sf[3] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_COLOR: + sf[3] = 1.0 - constantColor[3]; + break; + case GL_CONSTANT_ALPHA: + sf[3] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_ALPHA: + sf[3] = 1.0 - constantColor[3]; + break; + default: + sf[3] = 0.0; + abort(); + break; + } + + // Dst Alpha term + switch (dstFactorA) { + case GL_ZERO: + df[3] = 0.0; + break; + case GL_ONE: + df[3] = 1.0; + break; + case GL_SRC_COLOR: + df[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_COLOR: + df[3] = 1.0 - src[3]; + break; + case GL_SRC_ALPHA: + df[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + df[3] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + df[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + df[3] = 1.0 - dst[3]; + break; + case GL_CONSTANT_COLOR: + df[3] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_COLOR: + df[3] = 1.0 - constantColor[3]; + break; + case GL_CONSTANT_ALPHA: + df[3] = constantColor[3]; + break; + case GL_ONE_MINUS_CONSTANT_ALPHA: + df[3] = 1.0 - constantColor[3]; + break; + default: + df[3] = 0.0; + abort(); + break; + } + } + + switch (opRGB) { + case GL_FUNC_ADD: + dst[0] = clamp(src[0] * sf[0] + dst[0] * df[0]); + dst[1] = clamp(src[1] * sf[1] + dst[1] * df[1]); + dst[2] = clamp(src[2] * sf[2] + dst[2] * df[2]); + break; + case GL_FUNC_SUBTRACT: + dst[0] = clamp(src[0] * sf[0] - dst[0] * df[0]); + dst[1] = clamp(src[1] * sf[1] - dst[1] * df[1]); + dst[2] = clamp(src[2] * sf[2] - dst[2] * df[2]); break; - case GL_ONE: - df[0] = df[1] = df[2] = df[3] = 1.0; + case GL_FUNC_REVERSE_SUBTRACT: + dst[0] = clamp(dst[0] * df[0] - src[0] * sf[0]); + dst[1] = clamp(dst[1] * df[1] - src[1] * sf[1]); + dst[2] = clamp(dst[2] * df[2] - src[2] * sf[2]); break; - case GL_SRC_COLOR: - df[0] = src[0]; df[1] = src[1]; df[2] = src[2]; df[3] = src[3]; + case GL_MIN: + dst[0] = min(src[0], dst[0]); + dst[1] = min(src[1], dst[1]); + dst[2] = min(src[2], dst[2]); break; - case GL_ONE_MINUS_SRC_COLOR: - df[0] = 1.0 - src[0]; df[1] = 1.0 - src[1]; - df[2] = 1.0 - src[2]; df[3] = 1.0 - src[3]; + case GL_MAX: + dst[0] = max(src[0], dst[0]); + dst[1] = max(src[1], dst[1]); + dst[2] = max(src[2], dst[2]); break; - case GL_SRC_ALPHA: - df[0] = df[1] = df[2] = df[3] = src[3]; + default: + abort(); + } + + switch (opA) { + case GL_FUNC_ADD: + dst[3] = clamp(src[3] * sf[3] + dst[3] * df[3]); break; - case GL_ONE_MINUS_SRC_ALPHA: - df[0] = df[1] = df[2] = df[3] = 1.0 - src[3]; + case GL_FUNC_SUBTRACT: + dst[3] = clamp(src[3] * sf[3] - dst[3] * df[3]); break; - case GL_DST_ALPHA: - df[0] = df[1] = df[2] = df[3] = dst[3]; + case GL_FUNC_REVERSE_SUBTRACT: + dst[3] = clamp(dst[3] * df[3] - src[3] * sf[3]); break; - case GL_ONE_MINUS_DST_ALPHA: - df[0] = df[1] = df[2] = df[3] = 1.0 - dst[3]; + case GL_MIN: + dst[3] = min(src[3], dst[3]); break; - default: - df[0] = df[1] = df[2] = df[3] = 0.0; + case GL_MAX: + dst[3] = max(src[3], dst[3]); break; - } + default: + abort(); + } - dst[0] = clamp(src[0] * sf[0] + dst[0] * df[0]); - dst[1] = clamp(src[1] * sf[1] + dst[1] * df[1]); - dst[2] = clamp(src[2] * sf[2] + dst[2] * df[2]); - dst[3] = clamp(src[3] * sf[3] + dst[3] * df[3]); } // applyBlend -struct runFactorsResult { - float readbackErrorBits; - float blendRGBErrorBits; - float blendAlphaErrorBits; -}; -runFactorsResult -runFactors(GLenum srcFactor, GLenum dstFactor, - GLEAN::DrawingSurfaceConfig& config, GLEAN::Environment& env, - float rgbTolerance, float alphaTolerance) { - using namespace GLEAN; +namespace GLEAN { + +BlendFuncTest::runFactorsResult +BlendFuncTest::runFactors(GLenum srcFactorRGB, GLenum srcFactorA, + GLenum dstFactorRGB, GLenum dstFactorA, + GLenum opRGB, GLenum opA, + const GLfloat constantColor[4], + GLEAN::DrawingSurfaceConfig& config, + GLEAN::Environment& env) +{ + using namespace GLEAN; + runFactorsResult result; int y; @@ -262,7 +508,17 @@ runFactors(GLenum srcFactor, GLenum dstFactor, Image src(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); RandomBitsDouble srcARand(16, 42); - glBlendFunc(srcFactor, dstFactor); + if (haveSepFunc) + glBlendFuncSeparate_func(srcFactorRGB, dstFactorRGB, + srcFactorA, dstFactorA); + else + glBlendFunc(srcFactorRGB, dstFactorRGB); + + if (haveBlendEquationSep) + glBlendEquationSeparate_func(opRGB, opA); + else if (haveBlendEquation) + glBlendEquation_func(opRGB); + glEnable(GL_BLEND); dRow = expected.pixels(); @@ -278,7 +534,10 @@ runFactors(GLenum srcFactor, GLenum dstFactor, sPix[2] = rgba[2]; sPix[3] = rgba[3]; drawQuad(x + 1, y + 1, rgba); - applyBlend(srcFactor, dstFactor, pix, rgba); + applyBlend(srcFactorRGB, srcFactorA, + dstFactorRGB, dstFactorA, + opRGB, opA, + pix, rgba, constantColor); pix += 4; sPix += 4; } @@ -294,8 +553,7 @@ runFactors(GLenum srcFactor, GLenum dstFactor, // maximum error encountered. Image actual(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); actual.read(1, 1); - result.blendRGBErrorBits = 0.0; - result.blendAlphaErrorBits = 0.0; + result.blendErrorBits = 0.0; sRow = actual.pixels(); dRow = expected.pixels(); for (/*int */y = 0; y < drawingSize; ++y) { @@ -306,15 +564,13 @@ runFactors(GLenum srcFactor, GLenum dstFactor, float gError = fabs(aPix[1] - ePix[1]); float bError = fabs(aPix[2] - ePix[2]); float aError = fabs(aPix[3] - ePix[3]); - result.blendRGBErrorBits = - max(static_cast<double>(result.blendRGBErrorBits), + result.blendErrorBits = + max(static_cast<double>(result.blendErrorBits), max(ErrorBits(rError, config.r), max(ErrorBits(gError, config.g), - ErrorBits(bError, config.b)))); - result.blendAlphaErrorBits = - max(static_cast<double>(result.blendAlphaErrorBits), - ErrorBits(aError, config.a)); - if (result.blendRGBErrorBits > rgbTolerance || result.blendAlphaErrorBits > alphaTolerance) { + max(ErrorBits(bError, config.b), + ErrorBits(aError, config.a))))); + if (result.blendErrorBits > 1.0) { if (env.options.verbosity) { float* sPix = reinterpret_cast<float*>(src.pixels() + y * src.rowSizeInBytes() + x * 4 * sizeof(float)); @@ -345,9 +601,44 @@ env.log << '\n' return result; } // runOneSet -} // anonymous namespace -namespace GLEAN { +bool +BlendFuncTest::runCombo(BlendFuncResult& r, Window& w, + BlendFuncResult::PartialResult p, + GLEAN::Environment& env) +{ + runFactorsResult res(runFactors(p.srcRGB, p.srcA, p.dstRGB, p.dstA, + p.opRGB, p.opA, p.constColor, + *(r.config), env)); + w.swap(); + + p.rbErr = res.readbackErrorBits; + p.blErr = res.blendErrorBits; + r.results.push_back(p); + + if (p.rbErr > 1.0 || p.blErr > 1.0) { + env.log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\tsource factor RGB = " << factorToName(p.srcRGB) + << ", source factor A = " << factorToName(p.srcA) + << "\n\tdest factor RGB = " << factorToName(p.dstRGB) + << ", dest factor A = " << factorToName(p.dstA) + << "\n\tequation RGB = " << opToName(p.opRGB) + << ", equation A = " << opToName(p.opA) + << "\n\tconst color = { " + << p.constColor[0] << ", " + << p.constColor[1] << ", " + << p.constColor[2] << ", " + << p.constColor[3] << " }" + << "\n\tReadback had " << p.rbErr + << " bits in error; blending had " + << p.blErr << " bits in error.\n"; + return false; + } + return true; +} + + /////////////////////////////////////////////////////////////////////////////// // runOne: Run a single test case @@ -365,7 +656,11 @@ BlendFuncTest::runOne(BlendFuncResult& r, Window& w) { GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA_SATURATE + GL_SRC_ALPHA_SATURATE, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA }; static GLenum dstFactors[] = { GL_ZERO, @@ -375,64 +670,170 @@ BlendFuncTest::runOne(BlendFuncResult& r, Window& w) { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA + GL_ONE_MINUS_DST_ALPHA, + GL_CONSTANT_COLOR, + GL_ONE_MINUS_CONSTANT_COLOR, + GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA + }; + static GLenum operators[] = { + GL_FUNC_ADD, + GL_FUNC_SUBTRACT, + GL_FUNC_REVERSE_SUBTRACT, + GL_MIN, + GL_MAX }; - // Hack: Make driver tests on incorrect hardware feasible - // by adjusting the error tolerance to whatever the hardware can do - float rgbTolerance = 1.0; - float alphaTolerance = 1.0; - const char* s; + unsigned numSrcFactorsSep, numDstFactorsSep; + unsigned numOperatorsRGB, numOperatorsA; + BlendFuncResult::PartialResult p; + bool allPassed = true; - s = getenv("GLEAN_BLEND_RGB_TOLERANCE"); - if (s) { - rgbTolerance = atof(s); - env->log << "Note: RGB tolerance adjusted to " << rgbTolerance << "\n"; + // test for features, get function pointers + if (GLUtils::getVersion() >= 1.4) { + haveSepFunc = true; + glBlendFuncSeparate_func = (PFNGLBLENDFUNCSEPARATEPROC) + GLUtils::getProcAddress("glBlendFuncSeparate"); } - s = getenv("GLEAN_BLEND_ALPHA_TOLERANCE"); - if (s) { - alphaTolerance = atof(s); - env->log << "Note: Alpha tolerance adjusted to " << alphaTolerance << "\n"; + else if (GLUtils::haveExtension("GL_EXT_blend_func_separate")) { + haveSepFunc = true; + glBlendFuncSeparate_func = (PFNGLBLENDFUNCSEPARATEPROC) + GLUtils::getProcAddress("glBlendFuncSeparateEXT"); } - bool allPassed = true; - for (unsigned int sf = 0; sf < sizeof(srcFactors)/sizeof(srcFactors[0]); - ++sf) - - for (unsigned int df = 0; - df < sizeof(dstFactors)/sizeof(dstFactors[0]); ++df) { - - BlendFuncResult::PartialResult p; - p.src = srcFactors[sf]; - p.dst = dstFactors[df]; - - if ((needsDstAlpha(p.src) || needsDstAlpha(p.dst)) - && (r.config->a == 0)) - continue; - - runFactorsResult res(runFactors(p.src, p.dst, - *(r.config), *env, rgbTolerance, alphaTolerance)); - w.swap(); - - p.rbErr = res.readbackErrorBits; - p.blRGBErr = res.blendRGBErrorBits; - p.blAErr = res.blendAlphaErrorBits; - r.results.push_back(p); - - if (p.rbErr > 1.0 || p.blRGBErr > rgbTolerance || p.blAErr > alphaTolerance) { - env->log << name << ": FAIL " - << r.config->conciseDescription()<< '\n' - << "\tsource factor = " - << factorToName(p.src) - << ", dest factor = " - << factorToName(p.dst) - << "\n\tReadback had " << p.rbErr - << " bits in error; RGB blending had " - << p.blRGBErr << " bits in error, Alpha blending had " - << p.blAErr << " bits in error.\n"; - allPassed = false; + if (GLUtils::getVersion() >= 1.4) { + haveBlendColor = true; + glBlendColor_func = (PFNGLBLENDCOLORPROC) + GLUtils::getProcAddress("glBlendColor"); + } + else if (GLUtils::haveExtension("GL_EXT_blend_color")) { + haveBlendColor = true; + glBlendColor_func = (PFNGLBLENDCOLORPROC) + GLUtils::getProcAddress("glBlendColorEXT"); + } + + if (GLUtils::getVersion() >= 1.4) { + haveBlendEquation = true; + glBlendEquation_func = (PFNGLBLENDEQUATIONPROC) + GLUtils::getProcAddress("glBlendEquation"); + } + else if (GLUtils::haveExtension("GL_EXT_blend_subtract") && + GLUtils::haveExtension("GL_EXT_blend_min_max")) { + haveBlendEquation = true; + glBlendEquation_func = (PFNGLBLENDEQUATIONPROC) + GLUtils::getProcAddress("glBlendEquationEXT"); + } + + if (GLUtils::getVersion() >= 2.0) { + haveBlendEquationSep = true; + glBlendEquationSeparate_func = (PFNGLBLENDEQUATIONSEPARATEPROC) + GLUtils::getProcAddress("glBlendEquationSeparate"); + } + else if (GLUtils::haveExtension("GL_EXT_blend_equation_separate")) { + haveBlendEquationSep = true; + glBlendEquationSeparate_func = (PFNGLBLENDEQUATIONSEPARATEPROC) + GLUtils::getProcAddress("glBlendEquationSeparateEXT"); + } + + if (haveBlendColor) { + // Just one blend color setting for all tests + p.constColor[0] = 0.25; + p.constColor[1] = 0.0; + p.constColor[2] = 1.0; + p.constColor[3] = 0.75; + glBlendColor_func(p.constColor[0], p.constColor[1], + p.constColor[2], p.constColor[3]); + } + + if (haveSepFunc) { + numSrcFactorsSep = ELEMENTS(srcFactors); + numDstFactorsSep = ELEMENTS(dstFactors); + } + else { + numSrcFactorsSep = 1; + numDstFactorsSep = 1; + } + + if (haveBlendEquation) { + numOperatorsRGB = ELEMENTS(operators); + numOperatorsA = ELEMENTS(operators); + } + else { + numOperatorsRGB = 1; // just ADD + numOperatorsA = 1; // just ADD + } + +#if 0 + // use this to test a single combination: + p.srcRGB = p.srcA = GL_SRC_ALPHA; + p.dstRGB = p.dstA = GL_ONE_MINUS_SRC_ALPHA; + p.opRGB = GL_FUNC_ADD; + p.opA = GL_FUNC_ADD; + allPassed = runCombo(r, w, p, *env); +#else + for (unsigned int op = 0; op < numOperatorsRGB; ++op) { + p.opRGB = operators[op]; + + for (unsigned int opa = 0; opa < numOperatorsA; ++opa) { + p.opA = operators[opa]; + + unsigned int step; + if (p.opRGB == GL_FUNC_ADD && p.opA == GL_FUNC_ADD) { + // test _all_ blend term combinations + step = 1; + } + else if (p.opRGB == GL_MIN || p.opRGB == GL_MAX || + p.opA == GL_MIN || p.opA == GL_MAX) { + // blend terms are N/A so only do one iteration of loops + step = 1000; + } + else { + // subtract modes: do every 3rd blend term for speed + step = 3; + } + + for (unsigned int sf = 0; sf < ELEMENTS(srcFactors); sf += step) { + for (unsigned int sfa = 0; sfa < numSrcFactorsSep; sfa += step) { + for (unsigned int df = 0; df < ELEMENTS(dstFactors); df += step) { + for (unsigned int dfa = 0; dfa < numDstFactorsSep; dfa += step) { + + if (haveSepFunc) { + p.srcRGB = srcFactors[sf]; + p.srcA = srcFactors[sfa]; + p.dstRGB = dstFactors[df]; + p.dstA = dstFactors[dfa]; + } + else { + p.srcRGB = p.srcA = srcFactors[sf]; + p.dstRGB = p.dstA = dstFactors[df]; + } + + // skip test if it depends on non-existant alpha channel + if ((r.config->a == 0) + && (needsDstAlpha(p.srcRGB) || + needsDstAlpha(p.srcA) || + needsDstAlpha(p.dstRGB) || + needsDstAlpha(p.dstA))) + continue; + + // skip test if blend color used, but not supported. + if (!haveBlendColor + && (needsBlendColor(p.srcRGB) || + needsBlendColor(p.srcA) || + needsBlendColor(p.dstRGB) || + needsBlendColor(p.dstA))) + continue; + + if (!runCombo(r, w, p, *env)) { + allPassed = false; + } + } + } + } } } + } +#endif r.pass = allPassed; } // BlendFuncTest::runOne @@ -449,6 +850,37 @@ BlendFuncTest::logOne(BlendFuncResult& r) { } +bool +BlendFuncTest::equalMode(const BlendFuncResult::PartialResult &r1, + const BlendFuncResult::PartialResult &r2) const +{ + return (r1.srcRGB == r2.srcRGB && + r1.srcA == r2.srcA && + r1.dstRGB == r2.dstRGB && + r1.dstA == r2.dstA && + r1.opRGB == r2.opRGB && + r1.opA == r2.opA); +} + + +void +BlendFuncTest::printMode(const BlendFuncResult::PartialResult &r) const +{ + env->log << "\t\t" + << factorToName(r.srcRGB) + << ' ' + << factorToName(r.srcA) + << ' ' + << factorToName(r.dstRGB) + << ' ' + << factorToName(r.dstA) + << ' ' + << opToName(r.opRGB) + << ' ' + << opToName(r.opA) + << '\n'; +} + /////////////////////////////////////////////////////////////////////////////// // compareOne: Compare results for a single test case /////////////////////////////////////////////////////////////////////////////// @@ -463,15 +895,14 @@ BlendFuncTest::compareOne(BlendFuncResult& oldR, BlendFuncResult& newR) { for (np = newR.results.begin(); np != newR.results.end(); ++np) // Find the matching case, if any, in the old results: for (op = oldR.results.begin(); op != oldR.results.end(); ++op) - if (np->src == op->src && np->dst == op->dst) { + if (equalMode(*np, *op)) { readbackStats.sample(np->rbErr - op->rbErr); - blendStats.sample(np->blRGBErr - op->blRGBErr); - blendStats.sample(np->blAErr - op->blAErr); + blendStats.sample(np->blErr - op->blErr); } if (readbackStats.n() == static_cast<int>(newR.results.size()) - && newR.results.size() == oldR.results.size() - && readbackStats.mean() == 0.0 && blendStats.mean() == 0.0) { + && newR.results.size() == oldR.results.size() + && readbackStats.mean() == 0.0 && blendStats.mean() == 0.0) { if (env->options.verbosity) env->log << name << ": SAME " << newR.config->conciseDescription() << '\n'; @@ -501,15 +932,10 @@ BlendFuncTest::compareOne(BlendFuncResult& oldR, BlendFuncResult& newR) { np != newR.results.end(); ++np) { for (op = oldR.results.begin(); op != oldR.results.end(); ++op) - if (np->src == op->src - && np->dst == op->dst) + if (equalMode(*np, *op)) break; if (op == oldR.results.end()) - env->log << "\t\t" - << factorToName(np->src) - << ' ' - << factorToName(np->dst) - << '\n'; + printMode(*np); } } if (readbackStats.n() != static_cast<int>(oldR.results.size())){ @@ -522,15 +948,10 @@ BlendFuncTest::compareOne(BlendFuncResult& oldR, BlendFuncResult& newR) { op != oldR.results.end(); ++op) { for (np = newR.results.begin(); np != newR.results.end(); ++np) - if (op->src == np->src - && op->dst == np->dst) + if (equalMode(*op, *np)) break; if (np == newR.results.end()) - env->log << "\t\t" - << factorToName(op->src) - << ' ' - << factorToName(op->dst) - << '\n'; + printMode(*op); } } if (env->options.verbosity) { @@ -543,15 +964,10 @@ BlendFuncTest::compareOne(BlendFuncResult& oldR, BlendFuncResult& newR) { np != newR.results.end(); ++np){ for (op = oldR.results.begin(); op != oldR.results.end(); ++op) - if (np->src == op->src - && np->dst == op->dst) + if (equalMode(*op, *np)) break; if (op != oldR.results.end()) - env->log << "\t\t" - << factorToName(np->src) - << ' ' - << factorToName(np->dst) - << '\n'; + printMode(*op); } } } @@ -565,9 +981,13 @@ BlendFuncResult::putresults(ostream& s) const { s << results.size() << '\n'; for (vector<PartialResult>::const_iterator p = results.begin(); p != results.end(); ++p) - s << factorToName(p->src) << ' ' - << factorToName(p->dst) << ' ' - << p->rbErr << ' ' << p->blRGBErr << ' ' << p->blAErr << '\n'; + s << factorToName(p->srcRGB) << ' ' + << factorToName(p->srcA) << ' ' + << factorToName(p->dstRGB) << ' ' + << factorToName(p->dstA) << ' ' + << opToName(p->opRGB) << ' ' + << opToName(p->opA) << ' ' + << p->rbErr << ' ' << p->blErr << '\n'; } // BlendFuncResult::put bool @@ -576,11 +996,16 @@ BlendFuncResult::getresults(istream& s) { s >> n; for (int i = 0; i < n; ++i) { PartialResult p; - string src; - string dst; - s >> src >> dst >> p.rbErr >> p.blRGBErr >> p.blAErr; - p.src = nameToFactor(src); - p.dst = nameToFactor(dst); + string srcRGB, srcA; + string dstRGB, dstA; + string opRGB, opA; + s >> srcRGB >> srcA >> dstRGB >> dstA >> opRGB >> opA >> p.rbErr >> p.blErr; + p.srcRGB = nameToFactor(srcRGB); + p.srcA = nameToFactor(srcA); + p.srcRGB = nameToFactor(srcRGB); + p.dstA = nameToFactor(dstA); + p.opRGB = nameToFactor(opRGB); + p.opA = nameToFactor(opA); results.push_back(p); } diff --git a/tests/glean/tblend.h b/tests/glean/tblend.h index 5a83902c3..abf3c80a8 100644 --- a/tests/glean/tblend.h +++ b/tests/glean/tblend.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT // tblend.h: Test blending functions. @@ -35,7 +35,7 @@ namespace GLEAN { -#define drawingSize 64 // We will check each pair of blend factors +#define drawingSize 32 // We will check each pair of blend factors // for each pixel in a square image of this // dimension, so if you make it too large, // the tests may take quite a while to run. @@ -46,14 +46,15 @@ public: bool pass; // not written to log file struct PartialResult { - GLenum src; // Source blend factor. - GLenum dst; // Destination blend factor. + GLenum srcRGB, srcA; // Source blend factor. + GLenum dstRGB, dstA; // Destination blend factor. + GLenum opRGB, opA; // Operator (add, sub, min, max) + GLfloat constColor[4]; float rbErr; // Max readback error, in bits. - float blRGBErr; // Max RGB blend error, in bits. - float blAErr; // Max Alpha blend error, in bits. + float blErr; // Max blend error, in bits. }; vector<PartialResult> results; - + virtual void putresults(ostream& s) const; virtual bool getresults(istream& s); }; @@ -62,6 +63,34 @@ class BlendFuncTest: public BaseTest<BlendFuncResult> { public: GLEAN_CLASS_WH(BlendFuncTest, BlendFuncResult, windowSize, windowSize); + +private: + struct runFactorsResult { + float readbackErrorBits; + float blendErrorBits; + }; + + runFactorsResult runFactors(GLenum srcFactorRGB, GLenum srcFactorA, + GLenum dstFactorRGB, GLenum dstFactorA, + GLenum opRGB, GLenum opA, + const GLfloat constantColor[4], + GLEAN::DrawingSurfaceConfig& config, + GLEAN::Environment& env); + + bool runCombo(BlendFuncResult& r, Window& w, + BlendFuncResult::PartialResult p, + GLEAN::Environment& env); + + bool equalMode(const BlendFuncResult::PartialResult &r1, + const BlendFuncResult::PartialResult &r2) const; + + void printMode(const BlendFuncResult::PartialResult &r) const; + + bool haveSepFunc; + bool haveBlendEquation; + bool haveBlendEquationSep; + bool haveBlendColor; + }; // class BlendFuncTest } // namespace GLEAN diff --git a/tests/glean/tdepthstencil.cpp b/tests/glean/tdepthstencil.cpp index f691d2cf8..945d8bd1c 100644 --- a/tests/glean/tdepthstencil.cpp +++ b/tests/glean/tdepthstencil.cpp @@ -44,6 +44,15 @@ namespace GLEAN { static PFNGLWINDOWPOS2IARBPROC WindowPos2i = NULL; +DepthStencilResult::DepthStencilResult() +{ + pass = false; + readDepthStencilRate = 0; + readDepthUintRate = 0; + readDepthUshortRate = 0; +} + + bool DepthStencilTest::checkError(const char *where) { diff --git a/tests/glean/tdepthstencil.h b/tests/glean/tdepthstencil.h index 9c915ecc0..0709f733c 100644 --- a/tests/glean/tdepthstencil.h +++ b/tests/glean/tdepthstencil.h @@ -47,6 +47,8 @@ public: double readDepthUintRate; // pixels/second double readDepthUshortRate; // pixels/second + DepthStencilResult(); + virtual void putresults(ostream& s) const; virtual bool getresults(istream& s); }; diff --git a/tests/glean/test.h b/tests/glean/test.h index 1d6e78c47..55da31769 100644 --- a/tests/glean/test.h +++ b/tests/glean/test.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -43,7 +43,7 @@ // vary depending on the drawing surface configuration or the // particular type of drawing surface used). -// It is possible to derive test classes directly from this class. +// It is possible to derive test classes directly from this class. // Most people will find it more convenient to use the BaseTest // template class. See tbase.h for more information. @@ -90,7 +90,7 @@ class Test { Test** prereqs; // Pointer to array of prerequisite tests. // These will always be run before the // current test. - + bool hasRun; // True if test has been run. Environment* env; // Environment in which runs or comparisons @@ -99,8 +99,6 @@ class Test { virtual void run(Environment& env) = 0; // Run test, save results. virtual void compare(Environment& env) = 0; - - virtual void details(Environment& env) = 0; // Compare two previous runs. // Exceptions: diff --git a/tests/glean/tfragprog1.cpp b/tests/glean/tfragprog1.cpp index 632a5af29..b1a59bd93 100644 --- a/tests/glean/tfragprog1.cpp +++ b/tests/glean/tfragprog1.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT // tfragprog.cpp: Test GL_ARB_fragment_program extension. @@ -103,7 +103,6 @@ static const FragmentProgram Programs[] = { ABS(Param2[3]) }, DONT_CARE_Z, - false, }, { "ADD test", @@ -116,8 +115,7 @@ static const FragmentProgram Programs[] = { CLAMP01(FragColor[2] + Param1[2]), CLAMP01(FragColor[3] + Param1[3]) }, - DONT_CARE_Z, - false, + DONT_CARE_Z }, { "CMP test", @@ -128,8 +126,7 @@ static const FragmentProgram Programs[] = { "CMP result.color, p2, zero, p1; \n" "END \n", { Param0[0], Param1[1], Param1[2], Param0[3] }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "COS test", @@ -145,8 +142,23 @@ static const FragmentProgram Programs[] = { CLAMP01(0.8775), CLAMP01(0.5403) }, - DONT_CARE_Z, - false + DONT_CARE_Z + }, + { + "COS test 2", + "!!ARBfp1.0\n" + "PARAM values = { 6.78318, 7.28318, 6.28318, -5.78318 }; \n" + "COS result.color.x, values.x; \n" + "COS result.color.y, values.y; \n" + "COS result.color.z, values.z; \n" + "COS result.color.w, values.w; \n" + "END \n", + { CLAMP01(0.8775), + CLAMP01(0.5403), + CLAMP01(1.0), + CLAMP01(0.8775) + }, + DONT_CARE_Z }, { "DP3 test", @@ -158,8 +170,7 @@ static const FragmentProgram Programs[] = { Param1[1] * FragColor[1] + Param1[2] * FragColor[2])) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "DP4 test", @@ -172,8 +183,7 @@ static const FragmentProgram Programs[] = { Param1[2] * FragColor[2] + Param1[3] * FragColor[3])) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "DPH test", @@ -189,8 +199,7 @@ static const FragmentProgram Programs[] = { Param1[2] * FragColor[2] + FragColor[3]) * 0.1)) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "DST test", @@ -205,8 +214,7 @@ static const FragmentProgram Programs[] = { 0.16, // v1.z CLAMP01(2.5) // v2.w }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "EX2 test", @@ -224,8 +232,7 @@ static const FragmentProgram Programs[] = { 2.0 * 0.01, 16.0 * 0.01, 0.25 * 0.01 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "FLR test", @@ -241,8 +248,7 @@ static const FragmentProgram Programs[] = { CLAMP01(-0.1), 0.1 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "FRC test", @@ -251,8 +257,7 @@ static const FragmentProgram Programs[] = { "FRC result.color, values; \n" "END \n", { 0.9, 0.1, 0.8, 0.4 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "LG2 test", @@ -271,8 +276,7 @@ static const FragmentProgram Programs[] = { 0.49, 0.2 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "LIT test 1", @@ -285,8 +289,7 @@ static const FragmentProgram Programs[] = { 0.433, // roughly Pow(values.y, values.w) 1.0 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "LIT test 2 (degenerate case: 0 ^ 0 -> 1)", @@ -299,8 +302,7 @@ static const FragmentProgram Programs[] = { 1.0, // 0^0 1.0 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "LIT test 3 (case x < 0)", @@ -313,8 +315,7 @@ static const FragmentProgram Programs[] = { 0.0, 1.0 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "LRP test", @@ -328,8 +329,7 @@ static const FragmentProgram Programs[] = { 1.0 * FragColor[2] + (1.0 - 1.0) * Param1[2], 0.0 * FragColor[3] + (1.0 - 0.0) * Param1[3] }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "MAD test", @@ -343,8 +343,7 @@ static const FragmentProgram Programs[] = { CLAMP01(FragColor[2] * Param1[2] + Param2[2]), CLAMP01(FragColor[3] * Param1[3] + Param2[3]) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "MAX test", @@ -358,8 +357,7 @@ static const FragmentProgram Programs[] = { MAX(Param1[2], Param2[2]), MAX(Param1[3], Param2[3]), }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "MIN test", @@ -372,8 +370,7 @@ static const FragmentProgram Programs[] = { MIN(Param1[2], FragColor[2]), MIN(Param1[3], FragColor[3]), }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "MOV test", @@ -382,7 +379,6 @@ static const FragmentProgram Programs[] = { "END \n", FRAGCOLOR, DONT_CARE_Z, - false }, { "MUL test", @@ -395,8 +391,7 @@ static const FragmentProgram Programs[] = { CLAMP01(FragColor[2] * Param1[2]), CLAMP01(FragColor[3] * Param1[3]) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "masked MUL test", @@ -411,8 +406,7 @@ static const FragmentProgram Programs[] = { 0.0, 0.0 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "POW test (exponentiation)", @@ -427,8 +421,7 @@ static const FragmentProgram Programs[] = { 0.5 * 0.5 * 0.5, 0.5 * 0.5 * 0.5 * 0.5, CLAMP01(2.0) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "RCP test (reciprocal)", @@ -440,8 +433,7 @@ static const FragmentProgram Programs[] = { "RCP result.color.w, values.w; \n" "END \n", { 1.0 / 8.0, CLAMP01(1.0 / -10.0), 1, 1.0 / 12.0 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "RSQ test 1 (reciprocal square root)", @@ -453,8 +445,7 @@ static const FragmentProgram Programs[] = { "RSQ result.color.w, values.w; \n" "END \n", { 1.0, 0.5, 0.3333, 0.1 }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "RSQ test 2 (reciprocal square root of negative value)", @@ -470,8 +461,7 @@ static const FragmentProgram Programs[] = { 0.447, 1.0, }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "SCS test", @@ -485,8 +475,7 @@ static const FragmentProgram Programs[] = { DONT_CARE_COLOR, DONT_CARE_COLOR, }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "SGE test", @@ -500,8 +489,7 @@ static const FragmentProgram Programs[] = { Param2[2] >= Param0[2] ? 1.0 : 0.0, Param2[3] >= Param0[3] ? 1.0 : 0.0, }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "SIN test", @@ -517,8 +505,23 @@ static const FragmentProgram Programs[] = { CLAMP01(0.4794), CLAMP01(0.8414) }, - DONT_CARE_Z, - false + DONT_CARE_Z + }, + { + "SIN test 2", + "!!ARBfp1.0\n" + "PARAM values = { 3.14159, -3.14159, 6.78319, -5.78319 }; \n" + "SIN result.color.x, values.x; \n" + "SIN result.color.y, values.y; \n" + "SIN result.color.z, values.z; \n" + "SIN result.color.w, values.w; \n" + "END \n", + { CLAMP01(0.0), + CLAMP01(0.0), + CLAMP01(0.4794), + CLAMP01(0.4794) + }, + DONT_CARE_Z }, { "SLT test", @@ -531,8 +534,7 @@ static const FragmentProgram Programs[] = { FragColor[2] < Param1[2] ? 1.0 : 0.0, FragColor[3] < Param1[3] ? 1.0 : 0.0, }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "SUB test (with swizzle)", @@ -545,8 +547,7 @@ static const FragmentProgram Programs[] = { CLAMP01(Param1[3] - FragColor[3]), CLAMP01(Param1[2] - FragColor[2]) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "SWZ test", @@ -559,8 +560,7 @@ static const FragmentProgram Programs[] = { CLAMP01(Param1[2]), CLAMP01(0.0) }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "XPD test 1", @@ -574,8 +574,7 @@ static const FragmentProgram Programs[] = { CLAMP01(Param1[0] * Param2[1] - Param1[1] * Param2[0]), DONT_CARE_COLOR }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "Z-write test", @@ -589,8 +588,7 @@ static const FragmentProgram Programs[] = { Param1[2], Param1[3] }, - Param1[1], - false + Param1[1] }, // ============= Numeric stress tests ================================= @@ -610,8 +608,7 @@ static const FragmentProgram Programs[] = { DONT_CARE_COLOR, DONT_CARE_COLOR }, - DONT_CARE_Z, - false + DONT_CARE_Z }, { "Infinity / nan test", @@ -625,8 +622,7 @@ static const FragmentProgram Programs[] = { DONT_CARE_COLOR, DONT_CARE_COLOR }, - DONT_CARE_Z, - false + DONT_CARE_Z }, // ============= Fog tests ============================================ @@ -643,8 +639,7 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, { "Computed fog linear test", @@ -668,8 +663,7 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, #undef FOG_FACT @@ -686,8 +680,7 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, #undef FOG_FACT #define FOG_FACT 0.3535 // = ex2(-Density * Coord) @@ -714,8 +707,7 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, #undef FOG_FACT @@ -732,8 +724,7 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, #undef FOG_FACT #define FOG_FACT 0.2102 // = ex2(-(Density * Coord)^2) @@ -761,13 +752,12 @@ static const FragmentProgram Programs[] = { FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), FragColor[3] }, - DONT_CARE_Z, - true + DONT_CARE_Z }, #undef FOG_FACT // XXX add lots more tests here! - { NULL, NULL, {0,0,0,0}, 0, false } // end of list sentinal + { NULL, NULL, {0,0,0,0}, 0 } // end of list sentinal }; @@ -775,11 +765,6 @@ static const FragmentProgram Programs[] = { void FragmentProgramTest::setup(void) { - haveFogCoord = false; - - if (GLUtils::haveExtensions("EXT_fog_coord")) - haveFogCoord = true; - // setup Infinity, Nan values int nan; float *nanPtr; @@ -813,10 +798,8 @@ FragmentProgramTest::setup(void) glGetProgramivARB_func = (PFNGLGETPROGRAMIVARBPROC) GLUtils::getProcAddress("glGetProgramivARB"); assert(glGetProgramivARB_func); - if (haveFogCoord) { - glFogCoordf_func = (PFNGLFOGCOORDFPROC) GLUtils::getProcAddress("glFogCoordf"); - assert(glFogCoordf_func); - } + glFogCoordf_func = (PFNGLFOGCOORDFPROC) GLUtils::getProcAddress("glFogCoordf"); + assert(glFogCoordf_func); GLuint progID; glGenProgramsARB_func(1, &progID); @@ -844,17 +827,15 @@ FragmentProgramTest::setup(void) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDrawBuffer(GL_FRONT); - glReadBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); // other GL state - if (haveFogCoord) { - glFogf(GL_FOG_START, FogStart); - glFogf(GL_FOG_END, FogEnd); - glFogf(GL_FOG_DENSITY, FogDensity); - glFogfv(GL_FOG_COLOR, FogColor); - glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT); - glFogCoordf_func(FogCoord); - } + glFogf(GL_FOG_START, FogStart); + glFogf(GL_FOG_END, FogEnd); + glFogf(GL_FOG_DENSITY, FogDensity); + glFogfv(GL_FOG_COLOR, FogColor); + glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT); + glFogCoordf_func(FogCoord); // compute error tolerances (may need fine-tuning) int bufferBits[5]; @@ -977,7 +958,7 @@ FragmentProgramTest::testProgram(const FragmentProgram &p) printf("%s: Expect: %.3f %.3f %.3f %.3f found: %.3f %.3f %.3f %.3f\n", p.name, p.expectedColor[0], p.expectedColor[1], - p.expectedColor[2], p.expectedColor[3], + p.expectedColor[2], p.expectedColor[3], pixel[0], pixel[1], pixel[2], pixel[3]); if (!equalColors(pixel, p.expectedColor)) { @@ -1004,22 +985,10 @@ FragmentProgramTest::runOne(MultiTestResult &r, Window &w) (void) w; setup(); - const char* filter; - - filter = getenv("GLEAN_FRAGPROG"); - if (filter && !strlen(filter)) - filter = 0; - #if DEVEL_MODE glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); #endif for (int i = 0; Programs[i].name; i++) { - if (filter && strcmp(filter, Programs[i].name)) - continue; - - if (Programs[i].needFogCoord && !haveFogCoord) - continue; - #if DEVEL_MODE glViewport(0, i * 20, windowWidth, 20); #endif @@ -1038,13 +1007,6 @@ FragmentProgramTest::runOne(MultiTestResult &r, Window &w) r.pass = (r.numFailed == 0); } -void -FragmentProgramTest::printDetails() -{ - for (int i = 0; Programs[i].name; i++) - env->log << Programs[i].name << '\n'; -} - // The test object itself: FragmentProgramTest fragmentProgramTest("fragProg1", "window, rgb, z", diff --git a/tests/glean/tfragprog1.h b/tests/glean/tfragprog1.h index b36e7a6f9..37656eb0b 100644 --- a/tests/glean/tfragprog1.h +++ b/tests/glean/tfragprog1.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 1999 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT // tfragprog.h: Test GL_ARB_fragment_program extension. @@ -32,10 +32,6 @@ #ifndef __tfragprog_h__ #define __tfragprog_h__ -#include "tmultitest.h" - -namespace GLEAN { - // If DEVEL_MODE==1 we generate a tall window of color swatches, one per // fragment program, which can be eyeballed against a reference image. // Use this if glReadPixels functionality is not working yet. @@ -51,14 +47,18 @@ namespace GLEAN { #endif +#include "tmultitest.h" + +namespace GLEAN { + + class FragmentProgram { public: const char *name; const char *progString; - const GLfloat expectedColor[4]; - const GLfloat expectedZ; - const bool needFogCoord; + GLfloat expectedColor[4]; + GLfloat expectedZ; }; @@ -75,8 +75,6 @@ public: private: GLfloat tolerance[5]; - bool haveFogCoord; - void setup(void); bool equalColors(const GLfloat a[4], const GLfloat b[4]) const; bool equalDepth(GLfloat z0, GLfloat z1) const; @@ -86,7 +84,6 @@ private: const GLfloat actualColor[4] ) const; void reportZFailure(const char *programName, GLfloat expectedZ, GLfloat actualZ) const; - void printDetails(); }; } // namespace GLEAN diff --git a/tests/glean/tglsl1.cpp b/tests/glean/tglsl1.cpp new file mode 100644 index 000000000..397b3dc32 --- /dev/null +++ b/tests/glean/tglsl1.cpp @@ -0,0 +1,3396 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 1999 Allen Akin All Rights Reserved. +// +// 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 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + +// tglsl1.h: Test OpenGL shading language +// Brian Paul 6 March 2007 + +#define GL_GLEXT_PROTOTYPES + +#include "tglsl1.h" +#include <cassert> +#include <math.h> + + +namespace GLEAN { + + +static PFNGLATTACHSHADERPROC glAttachShader_func = NULL; +static PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation_func = NULL; +static PFNGLCOMPILESHADERPROC glCompileShader_func = NULL; +static PFNGLCREATEPROGRAMPROC glCreateProgram_func = NULL; +static PFNGLCREATESHADERPROC glCreateShader_func = NULL; +static PFNGLDELETEPROGRAMPROC glDeleteProgram_func = NULL; +static PFNGLDELETESHADERPROC glDeleteShader_func = NULL; +static PFNGLGETATTACHEDSHADERSPROC glGetAttachedShaders_func = NULL; +static PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation_func = NULL; +static PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog_func = NULL; +static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog_func = NULL; +static PFNGLGETSHADERIVPROC glGetShaderiv_func = NULL; +static PFNGLGETPROGRAMIVPROC glGetProgramiv_func = NULL; +static PFNGLGETSHADERSOURCEPROC glGetShaderSource_func = NULL; +static PFNGLGETUNIFORMFVPROC glGetUniformfv_func = NULL; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation_func = NULL; +static PFNGLISPROGRAMPROC glIsProgram_func = NULL; +static PFNGLISSHADERPROC glIsShader_func = NULL; +static PFNGLLINKPROGRAMPROC glLinkProgram_func = NULL; +static PFNGLSHADERSOURCEPROC glShaderSource_func = NULL; +static PFNGLUNIFORM1IPROC glUniform1i_func = NULL; +static PFNGLUNIFORM1FVPROC glUniform1fv_func = NULL; +static PFNGLUNIFORM2FVPROC glUniform2fv_func = NULL; +static PFNGLUNIFORM3FVPROC glUniform3fv_func = NULL; +static PFNGLUNIFORM4FVPROC glUniform4fv_func = NULL; +static PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv_func = NULL; +static PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv_func = NULL; +static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv_func = NULL; +static PFNGLUSEPROGRAMPROC glUseProgram_func = NULL; +static PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f_func = NULL; +static PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f_func = NULL; +static PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f_func = NULL; +static PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f_func = NULL; + +static PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv_func = NULL; +static PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv_func = NULL; + + +#define FLAG_NONE 0x0 +#define FLAG_LOOSE 0x1 // to indicate a looser tolerance test is needed +#define FLAG_ILLEGAL_SHADER 0x2 // the shader test should not compile +#define FLAG_ILLEGAL_LINK 0x4 // the shaders should not link +#define FLAG_VERSION_2_1 0x8 // OpenGL 2.1 test (or GLSL 1.20) +#define FLAG_WINDING_CW 0x10 // clockwise-winding polygon + +#define DONT_CARE_Z -1.0 + +#define NO_VERTEX_SHADER NULL +#define NO_FRAGMENT_SHADER NULL + +#define PRIMARY_R 0.25 +#define PRIMARY_G 0.75 +#define PRIMARY_B 0.5 +#define PRIMARY_A 0.25 +#define SECONDARY_R 0.0 +#define SECONDARY_G 0.25 +#define SECONDARY_B 0.25 +#define SECONDARY_A 1.0 + +#define AMBIENT { 0.2, 0.4, 0.6, 0.8 } +#define LIGHT_DIFFUSE { 0.1, 0.3, 0.5, 0.7 } +#define MAT_DIFFUSE { 0.1, 0.3, 0.5, 0.7 } +#define DIFFUSE_PRODUCT { 0.01, 0.09, 0.25, 0.7 } // note alpha! + +#define UNIFORM1 {1.0, 0.25, 0.75, 0.0 } // don't change! + +#define PSIZE 3.0 +#define PSIZE_MIN 2.0 +#define PSIZE_MAX 8.0 +#define PSIZE_THRESH 1.5 +#define PSIZE_ATTEN0 4.0 +#define PSIZE_ATTEN1 5.0 +#define PSIZE_ATTEN2 6.0 + +#define FOG_START 100.0 +#define FOG_END 200.0 +#define FOG_R 1.0 +#define FOG_G 0.5 +#define FOG_B 1.0 +#define FOG_A 0.0 + +static const GLfloat PrimaryColor[4] = { PRIMARY_R, PRIMARY_G, + PRIMARY_B, PRIMARY_A }; +static const GLfloat SecondaryColor[4] = { SECONDARY_R, SECONDARY_G, + SECONDARY_B, SECONDARY_A }; + +static const GLfloat Ambient[4] = AMBIENT; +static const GLfloat MatDiffuse[4] = MAT_DIFFUSE; +static const GLfloat LightDiffuse[4] = LIGHT_DIFFUSE; + +static const GLfloat Uniform1[4] = UNIFORM1; + +static const GLfloat PointAtten[3] = { PSIZE_ATTEN0, PSIZE_ATTEN1, PSIZE_ATTEN2 }; +static const GLfloat FogColor[4] = { FOG_R, FOG_G, FOG_B, FOG_A }; + +// Shader program test cases +static const ShaderProgram Programs[] = { + // Simple tests ======================================================= + { + "Directly set fragment color", // name + NO_VERTEX_SHADER, // vertex shader + // fragment shader: + "void main() { \n" + " gl_FragColor = vec4(1.0, 0.5, 0.25, 0.0); \n" + "} \n", + { 1.0, 0.5, 0.25, 0.0 }, // expectedColor + DONT_CARE_Z, // expectedZ + FLAG_NONE // flags + }, + + { + "Directly set vertex color", + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor = vec4(0.5, 1.0, 0.25, 0.0); \n" + "} \n", + NO_FRAGMENT_SHADER, + { 0.5, 1.0, 0.25, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Pass-through vertex color", + // vert shader: + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor = vec4(0.25, 1.0, 0.75, 0.0); \n" + "} \n", + // frag shader: + "void main() { \n" + " gl_FragColor = gl_Color; \n" + "} \n", + { 0.25, 1.0, 0.75, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Primary plus secondary color", + // vert shader: + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor = gl_Color + gl_SecondaryColor; \n" + "} \n", + // frag shader: + "void main() { \n" + " gl_FragColor = gl_Color; \n" + "} \n", + { PRIMARY_R + SECONDARY_R, + PRIMARY_G + SECONDARY_G, + PRIMARY_B + SECONDARY_B, + 1.0 /*PRIMARY_A + SECONDARY_A*/ }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Empty blocks ({}), nil (;) statements", + NO_VERTEX_SHADER, + "void main() { \n" + " {} \n" // empty block + " ; \n" // nil statement + " gl_FragColor = vec4(1.0, 0.5, 0.25, 0.0); \n" + "} \n", + { 1.0, 0.5, 0.25, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Global vars and initializers", + NO_VERTEX_SHADER, + "vec4 c = vec4(1.0, 0.5, 0.25, 0.0); \n" + "void main() { \n" + " gl_FragColor = c; \n" + "} \n", + { 1.0, 0.5, 0.25, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Global vars and initializers (2)", + NO_VERTEX_SHADER, + "vec4 c1 = vec4(0.4, 0.5, 0.25, 0.0); \n" + "vec4 c2 = vec4(0.3, 0.5, 0.5, 0.4); \n" + "vec4 c3 = c1 + c2; \n" + "void main() { \n" + " gl_FragColor = c3; \n" + "} \n", + { 0.7, 1.0, 0.75, 0.4 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Swizzle, writemask ================================================= + { + "Swizzle", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4(0.5, 0.25, 0.0, 1.0); \n" + " gl_FragColor = a.yxxz; \n" + "} \n", + { 0.25, 0.5, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzle (rgba)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4(0.5, 0.25, 0.0, 1.0); \n" + " gl_FragColor = a.grrb; \n" + "} \n", + { 0.25, 0.5, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzle (stpq)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4(0.5, 0.25, 0.0, 1.0); \n" + " gl_FragColor = a.tssp; \n" + "} \n", + { 0.25, 0.5, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Writemask", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(1.0); \n" + " gl_FragColor.x = 0.5; \n" + " gl_FragColor.z = 0.25; \n" + "} \n", + { 0.5, 1.0, 0.25, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzled writemask", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor.zwxy = vec4(1.0, 0.5, 0.25, 0.75); \n" + "} \n", + { 0.25, 0.75, 1.0, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzled writemask (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor.zy = vec2(1.0, 0.5); \n" + " gl_FragColor.wx = vec2(0.25, 0.75); \n" + "} \n", + { 0.75, 0.5, 1.0, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzled writemask (rgba)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor.bg = vec2(1.0, 0.5); \n" + " gl_FragColor.ar = vec2(0.25, 0.75); \n" + "} \n", + { 0.75, 0.5, 1.0, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Swizzled writemask (stpq)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor.pt = vec2(1.0, 0.5); \n" + " gl_FragColor.qs = vec2(0.25, 0.75); \n" + "} \n", + { 0.75, 0.5, 1.0, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Z-write ============================================================ + { + "gl_FragDepth writing", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.5); \n" + " gl_FragDepth = 0.25; \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + 0.25, // Z value + FLAG_NONE + }, + + // Basic arithmetic =================================================== + { + "Addition", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4(0.5, 0.25, 0.0, 0.0); \n" + " vec4 b = vec4(0.25, 0.0, 0.2, 0.0); \n" + " gl_FragColor = a + b; \n" + "} \n", + { 0.75, 0.25, 0.2, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vec4, scalar arithmetic", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4(0.5, 0.25, 0.2, 0.0); \n" + " vec4 b = vec4(0.25, 0.0, 0.0, 0.0); \n" + " gl_FragColor = a * 2.0 - b; \n" + "} \n", + { 0.75, 0.50, 0.4, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "chained assignment", + NO_VERTEX_SHADER, + "void main() { \n" + " float x, y, z; \n" + " x = y = z = 0.25; \n" + " gl_FragColor = vec4(x + y + z); \n" + "} \n", + { 0.75, 0.75, 0.75, 0.75 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "integer, float arithmetic", + NO_VERTEX_SHADER, + "void main() { \n" + " int k = 100; \n" + " gl_FragColor.x = k * 0.01; \n" + " gl_FragColor.y = k * 0.005; \n" + " gl_FragColor.z = k * 0.0025; \n" + " gl_FragColor.w = k * 0.0; \n" + "} \n", + { 1.0, 0.5, 0.25, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "unary negation", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(-1.0, -0.5, 0.5, -0.25); \n" + " gl_FragColor = -v; \n" + "} \n", + { 1.0, 0.5, 0.0, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "integer division", + NO_VERTEX_SHADER, + "void main() { \n" + " int i = 15, j = 6; \n" + " int k = i / j; \n" + " gl_FragColor = vec4(k * 0.1); \n" + "} \n", + { 0.2, 0.2, 0.2, 0.2 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "integer division with uniform var", + NO_VERTEX_SHADER, + "// as above, but prevent compile-time evaluation \n" + "uniform vec4 uniform1; \n" + "void main() { \n" + " int i = int(15 * uniform1.x); \n" + " int j = 6; \n" + " int k = i / j; \n" + " gl_FragColor = vec4(k * 0.1); \n" + "} \n", + { 0.2, 0.2, 0.2, 0.2 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "assignment operators", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(0.0, 0.25, 0.5, 0.75); \n" + " v *= 2.0; \n" + " v -= vec4(-0.5, 0.0, 0.25, 1.0); \n" + " gl_FragColor = v; \n" + "} \n", + { 0.5, 0.5, 0.75, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "post increment (x++)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " float x = uniform1.y; // should be 0.25 \n" + " float y = x++; // y should be 0.25 \n" + " gl_FragColor = vec4(y); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "pre increment (++x)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " float x = uniform1.y; // should be 0.25 \n" + " float y = ++x; // y should be 1.25 \n" + " gl_FragColor = vec4(y); \n" + "} \n", + { 1.0, 1.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "post decrement (x--)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " float x = uniform1.y; // should be 0.25 \n" + " float y = x--; // y should be 0.25 \n" + " gl_FragColor = vec4(y); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "pre decrement (--x)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " float x = uniform1.y; // should be 0.25 \n" + " float y = --x; // y should be -0.75 \n" + " gl_FragColor = vec4(-y); // negate \n" + "} \n", + { 0.75, 0.75, 0.75, 0.75 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // built-in functions ================================================ + { + "dot product", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 u = vec4(-1.0, 0.5, 0.5, -0.25); \n" + " vec4 v = vec4(0.5, 1.0, 0.5, 0.0); \n" + " gl_FragColor = vec4(dot(u, v)); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "length() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec3 u = vec3(0.25, 0.1, 0.2); \n" + " gl_FragColor = vec4(length(u)); \n" + "} \n", + { 0.335, 0.335, 0.335, 0.335 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "clamp() function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = uniform1 * vec4(3.0); \n" + " gl_FragColor = clamp(u, 0.0, 1.0); \n" + "} \n", + { 1.0, 0.75, 1.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "clamp() function, vec4", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = uniform1; \n" + " gl_FragColor = clamp(u, vec4(0.2), vec4(0.8)); \n" + "} \n", + { 0.8, 0.25, 0.75, 0.2 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "sin(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.0, 3.1415/2.0, 3.1415, -3.1415/2.0); \n" + " u = u * uniform1.x; // mul by one \n" + " u = sin(u); \n" + " gl_FragColor = u * 0.5 + 0.5; // scale to [0,1] range \n" + "} \n", + { 0.5, 1.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "cos(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.0, 3.1415/2.0, 3.1415, -3.1415/2.0); \n" + " u = u * uniform1.x; // mul by one \n" + " u = cos(u); \n" + " gl_FragColor = u * 0.5 + 0.5; // scale to [0,1] range \n" + "} \n", + { 1.0, 0.5, 0.0, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "asin(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.0, 1.0, -1.0, 0.0); \n" + " u = u * uniform1.x; // mul by one \n" + " u = asin(u); \n" + " gl_FragColor = u * 0.1 + 0.5; \n" + "} \n", + { 0.5, 0.5 + 0.157, 0.5 - 0.157, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "acos(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.0, 0.8, -0.8, 1.0); \n" + " u = u * uniform1.x; // mul by one \n" + " u = acos(u); \n" + " gl_FragColor = u * 0.1; \n" + "} \n", + { 0.157, 0.064, 0.249, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "atan(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.0, 0.8, -0.6, 0.5); \n" + " u = u * uniform1.x; // mul by one \n" + " u = atan(u); \n" + " gl_FragColor = u; \n" + " gl_FragColor.z = -u.z; \n" + "} \n", + { 0.0, 0.675, 0.540, 0.464 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "pow(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(0.5, 2.0, 0.3, 2.0); \n" + " u = u * uniform1.x; // mul by one \n" + " vec4 v = vec4(2.0, 0.5, 1.0, 0.0); \n" + " gl_FragColor = pow(u, v) * 0.5; \n" + "} \n", + { 0.25 * 0.5, 1.4142 * 0.5, 0.3 * 0.5, 1.0 * 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "exp(vec4) function", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " vec4 u = vec4(1.0, 0.5, -0.5, 2.0); \n" + " gl_FragColor = exp(u) * 0.1; \n" + "} \n", + { 0.2718, 0.1649, 0.0606, 0.7389 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "length() functions", + NO_VERTEX_SHADER, + "void main() { \n" + " vec2 v2 = vec2(1.0, 3.0); \n" + " vec3 v3 = vec3(0.5, -1.0, 2.0); \n" + " vec4 v4 = vec4(0.5, -1.0, 2.0, 1.0); \n" + " gl_FragColor.x = length(v2) * 0.1; \n" + " gl_FragColor.y = length(v3) * 0.1; \n" + " gl_FragColor.z = length(v4) * 0.1; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.3162, 0.2291, 0.25, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "normalize(vec3) function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec3 v3 = vec3(0.5, -1.0, 2.0); \n" + " v3 = normalize(v3); \n" + " gl_FragColor.x = v3.x; \n" + " gl_FragColor.y = v3.y; \n" + " gl_FragColor.z = v3.z; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.2182, /*-0.4364*/0.0, 0.8729, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "cross() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec3 u = vec3(0.5, 0.0, 0.0); \n" + " vec3 v = vec3(0.0, 0.5, 0.0); \n" + " vec3 w = cross(u, v); \n" + " gl_FragColor.xyz = w; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.0, 0.0, 0.25, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "abs() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(-0.3, -0.7, 0.2, 0.0); \n" + " gl_FragColor = abs(v); \n" + "} \n", + { 0.3, 0.7, 0.2, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "sign() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(-0.3, 0.0, 0.2, 0.0); \n" + " v = sign(v); \n" + " gl_FragColor.x = v.x + 1.5; \n" + " gl_FragColor.y = v.y + 0.5; \n" + " gl_FragColor.z = v.z - 0.5; \n" + " gl_FragColor.w = v.w + 0.5; \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "floor() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(1.3, -1.7, -0.2, 0.0); \n" + " v = floor(v); \n" + " gl_FragColor.x = v.x * 0.5; \n" + " gl_FragColor.y = v.y + 2.5; \n" + " gl_FragColor.z = v.z + 1.5; \n" + " gl_FragColor.w = v.w + 0.5; \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "ceil() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(1.3, -1.7, -0.2, 0.0); \n" + " v = ceil(v); \n" + " gl_FragColor.x = v.x - 1.5; \n" + " gl_FragColor.y = v.y + 1.5; \n" + " gl_FragColor.z = v.z + 0.5; \n" + " gl_FragColor.w = v.w + 0.5; \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "fract() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v = vec4(1.3, -1.7, -0.2, 1.0); \n" + " gl_FragColor = fract(v); \n" + "} \n", + { 0.3, 0.3, 0.8, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "mod() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 u = vec4(7.0, 5.2, 5.3, 0.5); \n" + " vec4 v = vec4(4.0, 5.0, -5.0, 1.0); \n" + " vec4 w = mod(u, v); \n" + " gl_FragColor.x = w.x * 0.1; \n" + " gl_FragColor.y = w.y; \n" + " gl_FragColor.z = w.z * -0.1; \n" + " gl_FragColor.w = w.w; \n" + "} \n", + { 0.3, 0.2, 0.47, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "min() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 u = vec4(-1.0, 0.5, 0.5, -0.25); \n" + " vec4 v = vec4(0.5, 1.0, 0.5, 0.0); \n" + " gl_FragColor = min(u, v); \n" + "} \n", + { 0.0, 0.5, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "max() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 u = vec4(-1.0, 0.5, 0.5, -0.25); \n" + " vec4 v = vec4(0.5, 1.0, 0.5, 0.0); \n" + " gl_FragColor = max(u, v); \n" + "} \n", + { 0.5, 1.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "step() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 edge = vec4(1.0, -2.0, 0.5, -1.0); \n" + " vec4 v = vec4(0.5, -1.0, 0.0, 0.0); \n" + " gl_FragColor = step(edge, v); \n" + "} \n", + { 0.0, 1.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "smoothstep() function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 edge0 = vec4(2.0); \n" + " vec4 edge1 = vec4(4.0); \n" + " vec4 v = vec4(1.0, 3.0, 4.0, 5.0); \n" + " gl_FragColor = smoothstep(edge0, edge1, v); \n" + "} \n", + { 0.0, 0.5, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "mix(vec4) function", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 v0 = vec4(0.0, 1.0, -4.8, 0.0); \n" + " vec4 v1 = vec4(1.0, 0.0, 15.2, 0.0); \n" + " gl_FragColor = mix(v0, v1, 0.25); \n" + "} \n", + { 0.25, 0.75, 0.2, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "mix(float) function", + NO_VERTEX_SHADER, + "void main() { \n" + " float v0 = 0.0; \n" + " float v1 = 1.0; \n" + " gl_FragColor.x = mix(v0, v1, 0.25); \n" + " v0 = 1.0; \n" + " v1 = 0.0; \n" + " gl_FragColor.y = mix(v0, v1, 0.25); \n" + " v0 = -4.8; \n" + " v1 = 15.2; \n" + " gl_FragColor.z = mix(v0, v1, 0.25); \n" + " v0 = 0.0; \n" + " v1 = 0.0; \n" + " gl_FragColor.w = mix(v0, v1, 0.25); \n" + "} \n", + { 0.25, 0.75, 0.2, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Flow Control ====================================================== + { + "simple if statement", + NO_VERTEX_SHADER, + "void main() { \n" + " // this should always be true \n" + " if (gl_FragCoord.x >= 0.0) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.5, 0.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "simple if statement (scalar test)", + NO_VERTEX_SHADER, + "void main() { \n" + " float x = 1.0; \n" + " if (x) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.5, 0.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "simple if/else statement", + NO_VERTEX_SHADER, + "void main() { \n" + " // this should always be false \n" + " if (gl_FragCoord.x < 0.0) { \n" + " gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); \n" + " } else { \n" + " gl_FragColor = vec4(0.5, 0.25, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.5, 0.25, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "while-loop", + NO_VERTEX_SHADER, + "void main() { \n" + " float sum = 0.0; \n" + " while (sum < 0.499999) { \n" + " sum += 0.1; \n" + " } \n" + " gl_FragColor = vec4(sum); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "do-loop", + NO_VERTEX_SHADER, + "void main() { \n" + " float sum = 0.0; \n" + " do { \n" + " sum += 0.1; \n" + " } while (sum < 0.499999); \n" + " gl_FragColor = vec4(sum); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "for-loop", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 sum = vec4(0.0); \n" + " int i; \n" + " for (i = 0; i < 5; ++i) { \n" + " sum += vec4(0.1); \n" + " } \n" + " gl_FragColor = sum; \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "do-loop with break", + NO_VERTEX_SHADER, + "void main() { \n" + " float sum = 0.0; \n" + " do { \n" + " sum += 0.1; \n" + " if (sum >= 0.499999) \n" + " break; \n" + " } while (1); \n" + " gl_FragColor = vec4(sum); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "do-loop with continue and break", + NO_VERTEX_SHADER, + "void main() { \n" + " float sum = 0.0; \n" + " do { \n" + " sum += 0.1; \n" + " if (sum < 0.499999) \n" + " continue; \n" + " break; \n" + " } while (1); \n" + " gl_FragColor = vec4(sum); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "discard statement (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(1.0); \n" + " if (gl_TexCoord[0].x < 0.5) \n" + " discard; \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, // glClear color + DONT_CARE_Z, + FLAG_NONE + }, + + { + "discard statement (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(1.0); \n" + " if (gl_TexCoord[0].x > 0.5) \n" + " discard; \n" + "} \n", + { 1.0, 1.0, 1.0, 1.0 }, // fragment color + DONT_CARE_Z, + FLAG_NONE + }, + + { + "conditional expression", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = gl_FragCoord.x < 0.0 ? vec4(0.0) : vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "sequence (comma) operator", + NO_VERTEX_SHADER, + "void main() { \n" + " float x, y, z; \n" + " x = 1.0, y = 0.5, z = x * y; \n" + " gl_FragColor = vec4(z); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "array with constant indexing", + NO_VERTEX_SHADER, + "void main() { \n" + " float ar[4]; \n" + " ar[0] = 0.5; \n" + " ar[1] = 1.0; \n" + " ar[2] = 0.25; \n" + " ar[3] = 0.2; \n" + " gl_FragColor.x = ar[0]; \n" + " gl_FragColor.y = ar[1]; \n" + " gl_FragColor.z = ar[2]; \n" + " gl_FragColor.w = ar[3]; \n" + "} \n", + { 0.5, 1.0, 0.25, 0.2 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Equality/inequality tests========================================== + { + "equality (float, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " float v = uniform1.x; \n" + " if (uniform1.x == v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (float, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " if (uniform1.x == 99.0) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (float, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " float v = uniform1.x; \n" + " if (uniform1.y != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (float, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " float v = uniform1.x; \n" + " if (uniform1.x != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (vec2, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec2 v = uniform1.xy; \n" + " if (uniform1.xy == v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (vec2, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec2 v = uniform1.xy; \n" + " if (v == vec2(99.0)) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "inequality (vec2, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec2 v = uniform1.yx; \n" + " if (uniform1.xy != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (vec2, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec2 v = uniform1.xy; \n" + " if (uniform1.xy != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (vec3, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec3 v = uniform1.xyz; \n" + " if (uniform1.xyz == v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (vec3, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " if (uniform1.xyz == vec3(99.0)) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (vec3, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec3 v = uniform1.zyx; \n" + " if (uniform1.xyz != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (vec3, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec3 v = uniform1.xyz; \n" + " if (uniform1.xyz != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "equality (vec4, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec4 v = uniform1; \n" + " if (uniform1 == v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "equality (vec4, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " if (uniform1 == vec4(99.0)) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (vec4, pass)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec4 v = uniform1.zyxw; \n" + " if (uniform1 != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "inequality (vec4, fail)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " vec4 v = uniform1.xyzw; \n" + " if (uniform1 != v) \n" + " gl_FragColor = vec4(0, 1, 0, 0); // green \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Logical operators ================================================= + { + "&& operator (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be true \n" + " if (gl_FragCoord.x >= 0.0 && gl_FragCoord.y >= 0.0) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.5, 0.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "&& operator (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be false \n" + " if (gl_FragCoord.x >= 0.0 && gl_FragCoord.y < 0.0) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "&& operator, short-circuit", + NO_VERTEX_SHADER, + "void main() { \n" + " float x = 0.75; \n" + " // this should always be false \n" + " if (x <= 0.5 && ++x) { \n" + " x += 0.1; \n" + " } \n" + " gl_FragColor = vec4(x); \n" + "} \n", + { 0.75, 0.75, 0.75, 0.75 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "|| operator (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be true \n" + " if (gl_FragCoord.x < 0.0 || gl_FragCoord.y >= 0.0) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.5, 0.0, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "|| operator (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be false \n" + " if (gl_FragCoord.x < 0.0 || gl_FragCoord.y < 0.0) { \n" + " gl_FragColor = vec4(0.5, 0.0, 0.5, 0.0); \n" + " } \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "|| operator, short-circuit", + NO_VERTEX_SHADER, + "void main() { \n" + " float x = 0.75; \n" + " // this should always be true \n" + " if (x >= 0.5 || ++x) { \n" + " x += 0.1; \n" + " } \n" + " gl_FragColor = vec4(x); \n" + "} \n", + { 0.85, 0.85, 0.85, 0.85 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "^^ operator (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be true \n" + " if (gl_FragCoord.x < 0.0 ^^ gl_FragCoord.y >= 0.0) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "^^ operator (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25); \n" + " // this should always be false \n" + " if (gl_FragCoord.x >= 0.0 ^^ gl_FragCoord.y >= 0.0) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "! (not) operator (1, pass)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " bool b = gl_FragCoord.x < 0.0; \n" + " if (!b) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "! (not) operator (1, fail)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " bool b = gl_FragCoord.x > 0.0; \n" + " if (!b) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "! (not) operator (2, pass)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " if (!(gl_FragCoord.x < 0.0)) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "! (not) operator (2, fail)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0); \n" + " if (!(gl_FragCoord.x > 0.0)) { \n" + " gl_FragColor = vec4(0.5); \n" + " } \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Uniform & Varying vars ============================================ + { + "uniform variable (fragment shader)", + NO_VERTEX_SHADER, + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FragColor = uniform1; \n" + "} \n", + UNIFORM1, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "uniform variable (vertex shader)", + "uniform vec4 uniform1; \n" + "void main() { \n" + " gl_FrontColor = uniform1; \n" + " gl_Position = ftransform(); \n" + "} \n", + NO_FRAGMENT_SHADER, + UNIFORM1, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "varying variable", + // vertex program: + "varying vec4 var1; \n" + "void main() { \n" + " var1 = vec4(1.0, 0.5, 0.25, 0.0); \n" + " gl_Position = ftransform(); \n" + "} \n", + // fragment program: + "varying vec4 var1; \n" + "void main() { \n" + " gl_FragColor = var1; \n" + "} \n", + { 1.0, 0.5, 0.25, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // GL state refs ===================================================== + { + "GL state variable reference (gl_FrontMaterial.ambient)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = gl_FrontMaterial.ambient; \n" + "} \n", + AMBIENT, + DONT_CARE_Z, + FLAG_NONE + }, + { + "GL state variable reference (gl_LightSource[0].diffuse)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = gl_LightSource[0].diffuse; \n" + "} \n", + LIGHT_DIFFUSE, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "GL state variable reference (diffuse product)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = gl_FrontLightProduct[0].diffuse; \n" + "} \n", + DIFFUSE_PRODUCT, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "GL state variable reference (point size)", + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor.x = gl_Point.size * 0.1; \n" + " gl_FrontColor.y = gl_Point.sizeMin * 0.1; \n" + " gl_FrontColor.z = gl_Point.sizeMax * 0.1; \n" + " gl_FrontColor.w = 0.0; \n" + "} \n", + NO_FRAGMENT_SHADER, + { PSIZE * 0.1, PSIZE_MIN * 0.1, PSIZE_MAX * 0.1, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "GL state variable reference (point attenuation)", + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FrontColor.x = gl_Point.distanceConstantAttenuation * 0.1; \n" + " gl_FrontColor.y = gl_Point.distanceLinearAttenuation * 0.1; \n" + " gl_FrontColor.z = gl_Point.distanceQuadraticAttenuation * 0.1; \n" + " gl_FrontColor.w = 0.0; \n" + "} \n", + NO_FRAGMENT_SHADER, + { PSIZE_ATTEN0 * 0.1, PSIZE_ATTEN1 * 0.1, + PSIZE_ATTEN2 * 0.1, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "linear fog", + // vertex prog: + "void main() { \n" + " gl_Position = ftransform(); \n" + " gl_FogFragCoord = 125.0; \n" + " gl_FrontColor = gl_Color; \n" + "} \n", + // fragment prog: + "void main() { \n" + " float bf = (gl_FogFragCoord - gl_Fog.start) * gl_Fog.scale; \n" + " gl_FragColor = mix(gl_Color, gl_Fog.color, bf); \n" + "} \n", +#define BF (125.0 - FOG_START) / (FOG_END - FOG_START) // Blend Factor + { PRIMARY_R + BF * (FOG_R - PRIMARY_R), + PRIMARY_G + BF * (FOG_G - PRIMARY_G), + PRIMARY_B + BF * (FOG_B - PRIMARY_B), + PRIMARY_A + BF * (FOG_A - PRIMARY_A) }, +#undef BF + DONT_CARE_Z, + FLAG_NONE + }, + + { + "built-in constants", + // vertex shader: + "void main() { \n" + " gl_Position = ftransform(); \n" + " // front color values should all be >= 1.0 \n" + " gl_FrontColor = vec4(gl_MaxLights, gl_MaxClipPlanes,\n" + " gl_MaxTextureUnits, \n" + " gl_MaxTextureCoords); \n" + "} \n", + NO_FRAGMENT_SHADER, + { 1.0, 1.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "gl_FrontFacing var (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.5 * float(gl_FrontFacing)); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "gl_FrontFacing var (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " gl_FragColor = vec4(0.25 + float(gl_FrontFacing)); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_WINDING_CW + }, + + // Texture functions ================================================== + { + "texture2D()", + NO_VERTEX_SHADER, + "uniform sampler2D tex2d; \n" + "void main() { \n" + " gl_FragColor = texture2D(tex2d, gl_TexCoord[0].xy);\n" + "} \n", + { 1.0, 0.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture2D(), computed coordinate", + NO_VERTEX_SHADER, + "uniform sampler2D tex2d; \n" + "void main() { \n" + " vec2 coord = gl_TexCoord[0].xy + vec2(0.5); \n" + " gl_FragColor = texture2D(tex2d, coord, 0.0); \n" + "} \n", + { 1.0, 1.0, 1.0, 1.0 }, // upper-right tex color + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture2D(), with bias", + NO_VERTEX_SHADER, + "uniform sampler2D tex2d; \n" + "void main() { \n" + " gl_FragColor = texture2D(tex2d, gl_TexCoord[0].xy, 1.0);\n" + "} \n", + { 0.5, 0.0, 0.0, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "2D Texture lookup with explicit lod (Vertex shader)", + "uniform sampler2D tex2d; \n" + "void main() { \n" + " gl_FrontColor = texture2DLod(tex2d, gl_MultiTexCoord0.xy, 2.0);\n" + " gl_Position = ftransform(); \n" + "} \n", + NO_FRAGMENT_SHADER, + { 0.25, 0.0, 0.0, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture2DProj()", + NO_VERTEX_SHADER, + "uniform sampler2D tex2d; \n" + "void main() { \n" + " vec4 coord = gl_TexCoord[0] * vec4(2.25); \n" + " // 'proj' will divide components by w (=2.25) \n" + " gl_FragColor = texture2DProj(tex2d, coord);\n" + "} \n", + { 1.0, 0.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture1D()", + NO_VERTEX_SHADER, + "uniform sampler1D tex1d; \n" + "void main() { \n" + " gl_FragColor = texture1D(tex1d, gl_TexCoord[0].x);\n" + "} \n", + { 1.0, 0.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture3D()", + NO_VERTEX_SHADER, + "uniform sampler3D tex3d; \n" + "void main() { \n" + " gl_FragColor = texture3D(tex3d, gl_TexCoord[0].xyz);\n" + "} \n", + { 1.0, 0.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "texture3D(), computed coord", + NO_VERTEX_SHADER, + "uniform sampler3D tex3d; \n" + "void main() { \n" + " vec3 coord = gl_TexCoord[0].xyz; \n" + " coord.y = 0.75; \n" + " coord.z = 0.75; \n" + " gl_FragColor = texture3D(tex3d, coord); \n" + "} \n", + { 0.0, 0.0, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "shadow2D(): 1", + NO_VERTEX_SHADER, + "uniform sampler2DShadow texZ; \n" + "void main() { \n" + " vec3 coord = vec3(0.1, 0.1, 0.5); \n" + " // shadow map value should be 0.25 \n" + " gl_FragColor = shadow2D(texZ, coord) + vec4(0.25); \n" + " // 0.5 <= 0.25 ? color = 1 : 0\n" + "} \n", + { 0.25, 0.25, 0.25, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "shadow2D(): 2", + NO_VERTEX_SHADER, + "uniform sampler2DShadow texZ; \n" + "void main() { \n" + " vec3 coord = vec3(0.1, 0.1, 0.2); \n" + " // shadow map value should be 0.25 \n" + " gl_FragColor = shadow2D(texZ, coord); \n" + " // 0.2 <= 0.25 ? color = 1 : 0\n" + "} \n", + { 1.0, 1.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "shadow2D(): 3", + NO_VERTEX_SHADER, + "uniform sampler2DShadow texZ; \n" + "void main() { \n" + " vec3 coord = vec3(0.9, 0.9, 0.95); \n" + " // shadow map value should be 0.75 \n" + " gl_FragColor = shadow2D(texZ, coord) + vec4(0.25); \n" + " // 0.95 <= 0.75 ? color = 1 : 0\n" + "} \n", + { 0.25, 0.25, 0.25, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "shadow2D(): 4", + NO_VERTEX_SHADER, + "uniform sampler2DShadow texZ; \n" + "void main() { \n" + " vec3 coord = vec3(0.9, 0.9, 0.65); \n" + " // shadow map value should be 0.75 \n" + " gl_FragColor = shadow2D(texZ, coord); \n" + " // 0.65 <= 0.75 ? color = 1 : 0\n" + "} \n", + { 1.0, 1.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Function calls ==================================================== + { + "simple function call", + NO_VERTEX_SHADER, + "vec4 avg(const in vec4 a, const in vec4 b) { \n" + " return (a + b) * 0.5; \n" + "} \n" + "\n" + "void main() { \n" + " vec4 a = vec4(1.0, 0.0, 0.5, 0.0); \n" + " vec4 b = vec4(0.0, 0.8, 0.5, 0.0); \n" + " gl_FragColor = avg(a, b); \n" + "} \n", + { 0.5, 0.4, 0.5, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "function call with inout params", + NO_VERTEX_SHADER, + "void swap(inout float x, inout float y) { \n" + " float t = x; \n" + " x = y; \n" + " y = t; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 0.5, b = 0.25; \n" + " swap(a, b); \n" + " gl_FragColor.x = a; \n" + " gl_FragColor.y = b; \n" + " gl_FragColor.z = 0.0; \n" + " gl_FragColor.w = 0.0; \n" + "} \n", + { 0.25, 0.5, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "function call with in, out params", + NO_VERTEX_SHADER, + "void half(in float x, out float y) { \n" + " y = 0.5 * x; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 0.5, b = 0.1; \n" + " half(a, b); \n" + " gl_FragColor = vec4(b); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "function with early return", + NO_VERTEX_SHADER, + "float minimum(in float x, in float y) { \n" + " if (x < y) \n" + " return x; \n" + " return y; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 0.5; \n" + " float z = minimum(a, 0.25); \n" + " gl_FragColor = vec4(z); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "nested function calls (1)", + NO_VERTEX_SHADER, + "float half(const in float x) { \n" + " return 0.5 * x; \n" + "} \n" + "\n" + "float square(const in float x) { \n" + " return x * x; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 0.5; \n" + " float b = square(half(1.0)); \n" + " gl_FragColor = vec4(b); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "nested function calls (2)", + NO_VERTEX_SHADER, + "float half(const in float x) { \n" + " return 0.5 * x; \n" + "} \n" + "\n" + "float square_half(const in float x) { \n" + " float y = half(x); \n" + " return y * y; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 1.0; \n" + " float b = square_half(a); \n" + " gl_FragColor = vec4(b); \n" + "} \n", + { 0.25, 0.25, 0.25, 0.25 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "nested function calls (3)", + NO_VERTEX_SHADER, + "float half(const in float x) { \n" + " return 0.5 * x; \n" + "} \n" + "\n" + "void main() { \n" + " float a = 0.5; \n" + " float b = half(half(a)); \n" + " gl_FragColor = vec4(b); \n" + "} \n", + { 0.125, 0.125, 0.125, 0.125 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Matrix tests ====================================================== + { + "matrix column check (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " mat4 m = gl_TextureMatrix[1]; \n" + " gl_FragColor = m[0]; \n" + "} \n", + { 1.0, 0.5, 0.6, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "matrix column check (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " mat4 m = gl_TextureMatrix[1]; \n" + " gl_FragColor = m[3]; \n" + "} \n", + { 0.1, 0.2, 0.3, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "matrix, vector multiply (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " mat4 m = mat4(0.5); // scale by 0.5 \n" + " vec4 color = gl_Color * m; \n" + " gl_FragColor = color; \n" + "} \n", + { 0.5 * PRIMARY_R, 0.5 * PRIMARY_G, + 0.5 * PRIMARY_B, 0.5 * PRIMARY_A }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "matrix, vector multiply (2)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 color = gl_TextureMatrix[1] * gl_Color; \n" + " gl_FragColor = color; \n" + "} \n", + { 0.2745, 0.9255, 0.7294, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "matrix, vector multiply (3)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 color = gl_Color * gl_TextureMatrix[1]; \n" + " gl_FragColor = color; \n" + "} \n", + { 0.925, 0.925, 0.6999, .5750 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "uniform matrix", + NO_VERTEX_SHADER, + "uniform mat4 uniformMat4; \n" + "void main() { \n" + " gl_FragColor = uniformMat4[3]; \n" + "} \n", + { 0.6, 0.7, 0.8, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "uniform matrix, transposed", + NO_VERTEX_SHADER, + "uniform mat4 uniformMat4t; \n" + "void main() { \n" + " gl_FragColor = uniformMat4t[2]; \n" + "} \n", + { 0.2, 0.0, 1.0, 0.8 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Vectors, booleans ================================================= + { + "vector relational (vec4 ==)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 1.0, 0.0, 0.2, 0.5); \n" + " vec4 b = vec4( 1.0, 3.0, 0.0, 0.5); \n" + " gl_FragColor = equal(a, b); \n" + "} \n", + { 1.0, 0.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (vec4 !=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 1.0, 0.0, 0.2, 0.5); \n" + " vec4 b = vec4( 1.0, 3.0, 0.0, 0.5); \n" + " gl_FragColor = notEqual(a, b); \n" + "} \n", + { 0.0, 1.0, 1.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (vec4 <=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 0.5, 1.0, 0.4, 0.0); \n" + " vec4 b = vec4( 1.0, 0.2, 0.4, 0.0); \n" + " gl_FragColor = lessThanEqual(a, b); \n" + "} \n", + { 1.0, 0.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (vec4 >=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 0.5, 1.0, 0.4, 0.0); \n" + " vec4 b = vec4( 1.0, 0.2, 0.4, 0.0); \n" + " gl_FragColor = greaterThanEqual(a, b); \n" + "} \n", + { 0.0, 1.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (vec4 <)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 0.5, 1.0, 0.4, 0.0); \n" + " vec4 b = vec4( 1.0, 0.2, 0.4, 0.0); \n" + " gl_FragColor = lessThan(a, b); \n" + "} \n", + { 1.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (vec4 >)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec4 a = vec4( 0.5, 1.0, 0.4, 0.0); \n" + " vec4 b = vec4( 1.0, 0.2, 0.4, 0.0); \n" + " gl_FragColor = greaterThan(a, b); \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (bvec2 <,<=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec2 a = vec2(-1.0, 2.0); \n" + " vec2 b = vec2( 1.0, 2.0); \n" + " vec2 c = vec2( 3.0, 2.0); \n" + " bvec2 b1 = lessThan(a, b); \n" + " bvec2 b2 = lessThanEqual(b, c); \n" + " gl_FragColor.x = float(b1.x); \n" + " gl_FragColor.y = float(b1.y); \n" + " gl_FragColor.z = float(b2.x); \n" + " gl_FragColor.w = float(b2.y); \n" + "} \n", + { 1.0, 0.0, 1.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (bvec2 >,>=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec2 a = vec2(-1.0, 3.0); \n" + " vec2 b = vec2( 1.0, 2.0); \n" + " vec2 c = vec2( 3.0, 2.0); \n" + " bvec2 b1 = greaterThan(a, b); \n" + " bvec2 b2 = greaterThanEqual(b, c); \n" + " gl_FragColor.x = float(b1.x); \n" + " gl_FragColor.y = float(b1.y); \n" + " gl_FragColor.z = float(b2.x); \n" + " gl_FragColor.w = float(b2.y); \n" + "} \n", + { 0.0, 1.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "vector relational (bvec2 ==,!=)", + NO_VERTEX_SHADER, + "void main() { \n" + " vec2 a = vec2(-1.0, 3.0); \n" + " vec2 b = vec2(-1.0, 2.0); \n" + " vec2 c = vec2( 3.0, 2.0); \n" + " bvec2 b1 = equal(a, b); \n" + " bvec2 b2 = notEqual(b, c); \n" + " gl_FragColor.x = b1.x; \n" + " gl_FragColor.y = b1.y; \n" + " gl_FragColor.z = b2.x; \n" + " gl_FragColor.w = b2.y; \n" + "} \n", + { 1.0, 0.0, 1.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "any() function", + NO_VERTEX_SHADER, + "void main() { \n" + " bvec4 b1 = bvec4(false, false, true, false); \n" + " bvec4 b2 = bvec4(false, false, false, false); \n" + " bool a1 = any(b1); \n" + " bool a2 = any(b2); \n" + " gl_FragColor.x = float(a1); \n" + " gl_FragColor.y = float(a2); \n" + " gl_FragColor.z = 0.0; \n" + " gl_FragColor.w = 0.0; \n" + "} \n", + { 1.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "all() function", + NO_VERTEX_SHADER, + "void main() { \n" + " bvec4 b1 = bvec4(false, true, true, false); \n" + " bvec4 b2 = bvec4(true, true, true, true ); \n" + " bool a1 = all(b1); \n" + " bool a2 = all(b2); \n" + " gl_FragColor.x = float(a1); \n" + " gl_FragColor.y = float(a2); \n" + " gl_FragColor.z = 0.0; \n" + " gl_FragColor.w = 0.0; \n" + "} \n", + { 0.0, 1.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Preprocessor tests ================================================ + { + "Preprocessor test (1)", + NO_VERTEX_SHADER, + "void main() { \n" + "#if 0 \n" + " gl_FragColor = vec4(0.5); \n" + "#else \n" + " gl_FragColor = vec4(0.3); \n" + "#endif \n" + "} \n", + { 0.3, 0.3, 0.3, 0.3 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Preprocessor test (2)", + NO_VERTEX_SHADER, + "void main() { \n" + "#if 1 \n" + " gl_FragColor = vec4(0.5); \n" + "#else \n" + " gl_FragColor = vec4(0.3); \n" + "#endif \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + { + "Preprocessor test (3)", + NO_VERTEX_SHADER, + "void main() { \n" + "#define SYMBOL 3 \n" + "#if SYMBOL == 3 \n" + " gl_FragColor = vec4(0.5); \n" + "#else \n" + " gl_FragColor = vec4(0.3); \n" + "#endif \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_NONE + }, + + // Illegal shaders ================================================== + { + "undefined variable", + NO_VERTEX_SHADER, + "void main() { \n" + " vec3 v = u; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "if (boolean/scalar) check", + NO_VERTEX_SHADER, + "void main() { \n" + " vec3 v; \n" + " if (v) { \n" + " } \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "break with no loop", + NO_VERTEX_SHADER, + "void main() { \n" + " break; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "continue with no loop", + NO_VERTEX_SHADER, + "void main() { \n" + " continue; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "illegal assignment", + NO_VERTEX_SHADER, + "void main() { \n" + " float x = main; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "syntax error check (1)", + NO_VERTEX_SHADER, + "void main() { \n" + " float x = ; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "syntax error check (2)", + NO_VERTEX_SHADER, + "main() { \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + { + "syntax error check (3)", + NO_VERTEX_SHADER, + "main() { \n" + " float x = 1.0 2.0; \n" + " gl_FragColor = vec4(0.5); \n" + "} \n", + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_SHADER + }, + + // GLSL 1.20 tests ====================================================== + { + "mat2x4 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat2x4 m = mat2x4(0.1, 0.2, 0.3, 0.4, \n" + " 0.5, 0.6, 0.7, 0.8); \n" + " gl_FragColor = m[1]; \n" + "} \n", + { 0.5, 0.6, 0.7, 0.8 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "mat4x2 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat4x2 m = mat4x2(0.1, 0.2, \n" + " 0.3, 0.4, \n" + " 0.5, 0.6, \n" + " 0.7, 0.8); \n" + " gl_FragColor.xy = m[1]; \n" + " gl_FragColor.zw = m[2]; \n" + "} \n", + { 0.3, 0.4, 0.5, 0.6 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "mat2x3 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat2x3 m = mat2x3(0.1, 0.2, 0.3, \n" + " 0.4, 0.5, 0.6); \n" + " gl_FragColor.xyz = m[1]; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.4, 0.5, 0.6, 1.0 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "mat3x2 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat3x2 m = mat3x2(0.1, 0.2, \n" + " 0.3, 0.4, \n" + " 0.5, 0.6); \n" + " gl_FragColor.xy = m[1]; \n" + " gl_FragColor.zw = m[2]; \n" + "} \n", + { 0.3, 0.4, 0.5, 0.6 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "mat4x3 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat4x3 m = mat4x3(0.1, 0.2, 0.3, \n" + " 0.4, 0.5, 0.6, \n" + " 0.7, 0.8, 0.9, \n" + " 1.0, 0.0, 1.0); \n" + " gl_FragColor.xyz = m[1]; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.4, 0.5, 0.6, 1.0 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "mat3x4 construct", + NO_VERTEX_SHADER, + "#version 120\n" + "void main() { \n" + " mat3x4 m = mat3x4(0.1, 0.2, 0.3, 0.4, \n" + " 0.5, 0.6, 0.7, 0.8, \n" + " 0.9, 1.0, 0.0, 1.0);\n" + " gl_FragColor = m[1]; \n" + "} \n", + { 0.5, 0.6, 0.7, 0.8 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + { + "vec4 * mat3x4 multiply", + NO_VERTEX_SHADER, + "#version 120 \n" + "void main() { \n" + " vec4 v = vec4(0.2, -0.2, 0.4, 0.1); \n" + " mat3x4 m = mat3x4(0.1, 0.2, 0.3, 0.4, \n" + " 0.5, 0.6, 0.7, 0.8, \n" + " 0.9, 1.0, 0.0, 1.0);\n" + " gl_FragColor.xyz = v * m; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.2 * 0.1 + -0.2 * 0.2 + 0.4 * 0.3 + 0.1 * 0.4, + 0.2 * 0.5 + -0.2 * 0.6 + 0.4 * 0.7 + 0.1 * 0.8, + 0.2 * 0.9 + -0.2 * 1.0 + 0.4 * 0.0 + 0.1 * 1.0, + 1.0 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + { + "mat4x2 * vec4", + NO_VERTEX_SHADER, + "#version 120 \n" + "void main() { \n" + " mat4x2 m = mat4x2(0.1, 0.2, \n" + " 0.3, 0.4, \n" + " 0.5, 0.6, \n" + " 0.7, 0.8); \n" + " vec4 v = vec4(0.9, 0.8, 0.7, 0.6); \n" + " gl_FragColor.xy = (m * v) * 0.5; \n" + " gl_FragColor.zw = vec2(0.0); \n" + "} \n", + { (0.1 * 0.9 + 0.3 * 0.8 + 0.5 * 0.7 + 0.7 * 0.6) * 0.5, + (0.2 * 0.9 + 0.4 * 0.8 + 0.6 * 0.7 + 0.8 * 0.6) * 0.5, + 0.0, + 0.0 + }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + { + "mat4x2 * mat2x4", + NO_VERTEX_SHADER, + "#version 120 \n" + "void main() { \n" + " mat4x2 m1 = mat4x2(0.1, 0.2, \n" + " 0.3, 0.4, \n" + " 0.5, 0.6, \n" + " 0.7, 0.8); \n" + " mat2x4 m2 = mat2x4(0.9, 0.8, 0.7, 0.6, \n" + " 0.5, 0.4, 0.3, 0.2); \n" + " mat2 m3 = m1 * m2; \n" + " vec4 v4; \n" + " v4.xy = m3[0]; \n" + " v4.zw = m3[1]; \n" + " gl_FragColor = v4 * 0.5; \n" + "} \n", + { (0.1 * 0.9 + 0.3 * 0.8 + 0.5 * 0.7 + 0.7 * 0.6) * 0.5, + (0.2 * 0.9 + 0.4 * 0.8 + 0.6 * 0.7 + 0.8 * 0.6) * 0.5, + (0.1 * 0.5 + 0.3 * 0.4 + 0.5 * 0.3 + 0.7 * 0.2) * 0.5, + (0.2 * 0.5 + 0.4 * 0.4 + 0.6 * 0.3 + 0.8 * 0.2) * 0.5 + }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + { + "vec2 * mat4x2 multiply", + NO_VERTEX_SHADER, + "#version 120 \n" + "void main() { \n" + " vec2 v = vec2(0.2, 0.5); \n" + " mat4x2 m = mat4x2(0.1, 0.2, \n" + " 0.3, 0.4, \n" + " 0.5, 0.6, \n" + " 0.7, 0.8); \n" + " gl_FragColor = v * m; \n" + "} \n", + { 0.2 * 0.1 + 0.5 * 0.2, + 0.2 * 0.3 + 0.5 * 0.4, + 0.2 * 0.5 + 0.5 * 0.6, + 0.2 * 0.7 + 0.5 * 0.8 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "vec3 * mat4x3 multiply", + NO_VERTEX_SHADER, + "#version 120 \n" + "void main() { \n" + " vec3 v = vec3(0.2, 0.5, 0.1); \n" + " mat4x3 m = mat4x3(0.1, 0.2, 0.3, \n" + " 0.4, 0.5, 0.6, \n" + " 0.7, 0.8, 0.9, \n" + " 1.0, 0.1, 0.2); \n" + " gl_FragColor = v * m; \n" + "} \n", + { 0.2 * 0.1 + 0.5 * 0.2 + 0.1 * 0.3, + 0.2 * 0.4 + 0.5 * 0.5 + 0.1 * 0.6, + 0.2 * 0.7 + 0.5 * 0.8 + 0.1 * 0.9, + 0.2 * 1.0 + 0.5 * 0.1 + 0.1 * 0.2 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + { + "uniform matrix 2x4", + NO_VERTEX_SHADER, + "#version 120 \n" + "uniform mat2x4 uniformMat2x4; \n" + "void main() { \n" + " gl_FragColor = uniformMat2x4[0]; \n" + "} \n", + { 0.0, 0.1, 0.2, 0.3 }, // first column of 2x4 matrix + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "uniform matrix 2x4, transposed", + NO_VERTEX_SHADER, + "#version 120 \n" + "uniform mat2x4 uniformMat2x4t; \n" + "void main() { \n" + " gl_FragColor = uniformMat2x4t[0]; \n" + "} \n", + { 0.0, 0.2, 0.4, 0.6 }, // first row of 4x2 matrix + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "uniform matrix 4x3", + NO_VERTEX_SHADER, + "#version 120 \n" + "uniform mat4x3 uniformMat4x3; \n" + "void main() { \n" + " gl_FragColor.xyz = uniformMat4x3[1]; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.3, 0.4, 0.5, 1.0 }, // second column of 4x3 matrix + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + { + "uniform matrix 4x3, transposed", + NO_VERTEX_SHADER, + "#version 120 \n" + "uniform mat4x3 uniformMat4x3t; \n" + "void main() { \n" + " gl_FragColor.xyz = uniformMat4x3t[1]; \n" + " gl_FragColor.w = 1.0; \n" + "} \n", + { 0.1, 0.5, 0.9, 1.0 }, + DONT_CARE_Z, + FLAG_VERSION_2_1 + }, + + // Illegal link test =================================================== + { + "gl_Position not written check", + "void main() { \n" + " gl_FrontColor = vec4(0.3); \n" + "} \n", + NO_FRAGMENT_SHADER, + { 0.5, 0.5, 0.5, 0.5 }, + DONT_CARE_Z, + FLAG_ILLEGAL_LINK + }, + + { + "varying var mismatch", + // vert shader: + "varying vec4 foo; \n" + "void main() { \n" + " foo = gl_Color; \n" + " gl_Position = ftransform(); \n" + "} \n", + // frag shader: + "varying vec4 bar; \n" + "void main() { \n" + " gl_FragColor = bar; \n" + "} \n", + { 0.0, 0.0, 0.0, 0.0 }, + DONT_CARE_Z, + FLAG_ILLEGAL_LINK + }, + + { NULL, NULL, NULL, {0,0,0,0}, 0, FLAG_NONE } // end of list sentinal +}; + + + +// Get ptrs to 2.0 API functions. +bool +GLSLTest::getFunctions(void) +{ + glAttachShader_func = (PFNGLATTACHSHADERPROC) GLUtils::getProcAddress("glAttachShader"); + if (!glAttachShader_func) + return false; + glBindAttribLocation_func = (PFNGLBINDATTRIBLOCATIONPROC) GLUtils::getProcAddress("glBindAttribLocation"); + if (!glBindAttribLocation_func) + return false; + glCompileShader_func = (PFNGLCOMPILESHADERPROC) GLUtils::getProcAddress("glCompileShader"); + if (!glCompileShader_func) + return false; + glCreateProgram_func = (PFNGLCREATEPROGRAMPROC) GLUtils::getProcAddress("glCreateProgram"); + if (!glCreateProgram_func) + return false; + glCreateShader_func = (PFNGLCREATESHADERPROC) GLUtils::getProcAddress("glCreateShader"); + if (!glCreateShader_func) + return false; + glDeleteProgram_func = (PFNGLDELETEPROGRAMPROC) GLUtils::getProcAddress("glDeleteProgram"); + if (!glDeleteProgram_func) + return false; + glDeleteShader_func = (PFNGLDELETESHADERPROC) GLUtils::getProcAddress("glDeleteShader"); + if (!glDeleteShader_func) + return false; + glGetAttachedShaders_func = (PFNGLGETATTACHEDSHADERSPROC) GLUtils::getProcAddress("glGetAttachedShaders"); + if (!glGetAttachedShaders_func) + return false; + glGetAttribLocation_func = (PFNGLGETATTRIBLOCATIONPROC) GLUtils::getProcAddress("glGetAttribLocation"); + if (!glGetAttribLocation_func) + return false; + glGetProgramInfoLog_func = (PFNGLGETPROGRAMINFOLOGPROC) GLUtils::getProcAddress("glGetProgramInfoLog"); + if (!glGetProgramInfoLog_func) + return false; + glGetShaderInfoLog_func = (PFNGLGETSHADERINFOLOGPROC) GLUtils::getProcAddress("glGetShaderInfoLog"); + if (!glGetShaderInfoLog_func) + return false; + glGetProgramiv_func = (PFNGLGETPROGRAMIVPROC) GLUtils::getProcAddress("glGetProgramiv"); + if (!glGetProgramiv_func) + return false; + glGetShaderiv_func = (PFNGLGETSHADERIVPROC) GLUtils::getProcAddress("glGetShaderiv"); + if (!glGetShaderiv_func) + return false; + glGetShaderSource_func = (PFNGLGETSHADERSOURCEPROC) GLUtils::getProcAddress("glGetShaderSource"); + if (!glGetShaderSource_func) + return false; + glGetUniformLocation_func = (PFNGLGETUNIFORMLOCATIONPROC) GLUtils::getProcAddress("glGetUniformLocation"); + if (!glGetUniformLocation_func) + return false; + glGetUniformfv_func = (PFNGLGETUNIFORMFVPROC) GLUtils::getProcAddress("glGetUniformfv"); + if (!glGetUniformfv_func) + return false; + glIsProgram_func = (PFNGLISPROGRAMPROC) GLUtils::getProcAddress("glIsProgram"); + if (!glIsProgram_func) + return false; + glIsShader_func = (PFNGLISSHADERPROC) GLUtils::getProcAddress("glIsShader"); + if (!glIsShader_func) + return false; + glLinkProgram_func = (PFNGLLINKPROGRAMPROC) GLUtils::getProcAddress("glLinkProgram"); + if (!glLinkProgram_func) + return false; + glShaderSource_func = (PFNGLSHADERSOURCEPROC) GLUtils::getProcAddress("glShaderSource"); + if (!glShaderSource_func) + return false; + glUniform1i_func = (PFNGLUNIFORM1IPROC) GLUtils::getProcAddress("glUniform1i"); + if (!glUniform1i_func) + return false; + glUniform1fv_func = (PFNGLUNIFORM1FVPROC) GLUtils::getProcAddress("glUniform1fv"); + if (!glUniform1fv_func) + return false; + glUniform2fv_func = (PFNGLUNIFORM2FVPROC) GLUtils::getProcAddress("glUniform2fv"); + if (!glUniform2fv_func) + return false; + glUniform3fv_func = (PFNGLUNIFORM3FVPROC) GLUtils::getProcAddress("glUniform3fv"); + if (!glUniform3fv_func) + return false; + glUniform4fv_func = (PFNGLUNIFORM3FVPROC) GLUtils::getProcAddress("glUniform4fv"); + if (!glUniform4fv_func) + return false; + glUniformMatrix2fv_func = (PFNGLUNIFORMMATRIX2FVPROC) GLUtils::getProcAddress("glUniformMatrix2fv"); + if (!glUniformMatrix2fv_func) + return false; + glUniformMatrix3fv_func = (PFNGLUNIFORMMATRIX3FVPROC) GLUtils::getProcAddress("glUniformMatrix3fv"); + if (!glUniformMatrix3fv_func) + return false; + glUniformMatrix4fv_func = (PFNGLUNIFORMMATRIX4FVPROC) GLUtils::getProcAddress("glUniformMatrix4fv"); + if (!glUniformMatrix4fv_func) + return false; + glUseProgram_func = (PFNGLUSEPROGRAMPROC) GLUtils::getProcAddress("glUseProgram"); + if (!glUseProgram_func) + return false; + glVertexAttrib1f_func = (PFNGLVERTEXATTRIB1FPROC) GLUtils::getProcAddress("glVertexAttrib1f"); + if (!glVertexAttrib1f_func) + return false; + glVertexAttrib2f_func = (PFNGLVERTEXATTRIB2FPROC) GLUtils::getProcAddress("glVertexAttrib2f"); + if (!glVertexAttrib2f_func) + return false; + glVertexAttrib3f_func = (PFNGLVERTEXATTRIB3FPROC) GLUtils::getProcAddress("glVertexAttrib3f"); + if (!glVertexAttrib3f_func) + return false; + glVertexAttrib4f_func = (PFNGLVERTEXATTRIB4FPROC) GLUtils::getProcAddress("glVertexAttrib4f"); + if (!glVertexAttrib4f_func) + return false; + + /* 2.1 */ + glUniformMatrix2x4fv_func = (PFNGLUNIFORMMATRIX2X4FVPROC) GLUtils::getProcAddress("glUniformMatrix2x4fv"); + if (!glUniformMatrix2x4fv_func) + return false; + glUniformMatrix4x3fv_func = (PFNGLUNIFORMMATRIX4X3FVPROC) GLUtils::getProcAddress("glUniformMatrix4x3fv"); + if (!glUniformMatrix4x3fv_func) + return false; + + return true; +} + + +void +GLSLTest::setupTextures(void) +{ + GLubyte teximage0[16][16][4]; + GLubyte teximage1[8][8][4]; + GLubyte teximage2[4][4][4]; + GLubyte teximage3D[16][16][16][4]; + GLfloat teximageZ[16][16]; + GLint i, j, k; + GLuint obj1D, obj2D, obj3D, objZ; + + glGenTextures(1, &obj1D); + glGenTextures(1, &obj2D); + glGenTextures(1, &obj3D); + glGenTextures(1, &objZ); + + glActiveTexture(GL_TEXTURE0); + + // + // 2D texture, w/ mipmap + // + glBindTexture(GL_TEXTURE_2D, obj2D); + // +-------+-------+ + // | blue | white | + // +-------+-------+ + // | red | green | + // +-------+-------+ + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + if (i < 8) { + // bottom half + if (j < 8) { + // red + teximage0[i][j][0] = 255; + teximage0[i][j][1] = 0; + teximage0[i][j][2] = 0; + teximage0[i][j][3] = 255; + } + else { + // green + teximage0[i][j][0] = 0; + teximage0[i][j][1] = 255; + teximage0[i][j][2] = 0; + teximage0[i][j][3] = 255; + } + } + else { + // top half + if (j < 8) { + // blue + teximage0[i][j][0] = 0; + teximage0[i][j][1] = 0; + teximage0[i][j][2] = 255; + teximage0[i][j][3] = 255; + } + else { + // white + teximage0[i][j][0] = 255; + teximage0[i][j][1] = 255; + teximage0[i][j][2] = 255; + teximage0[i][j][3] = 255; + } + } + } + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage0); + + // level 1: same colors, half intensity + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + teximage1[i][j][0] = teximage0[i*2][j*2][0] / 2; + teximage1[i][j][1] = teximage0[i*2][j*2][1] / 2; + teximage1[i][j][2] = teximage0[i*2][j*2][2] / 2; + teximage1[i][j][3] = teximage0[i*2][j*2][3] / 2; + } + } + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 8, 8, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage1); + + // level 2: 1/4 intensity + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + teximage2[i][j][0] = teximage0[i*4][j*4][0] / 4; + teximage2[i][j][1] = teximage0[i*4][j*4][1] / 4; + teximage2[i][j][2] = teximage0[i*4][j*4][2] / 4; + teximage2[i][j][3] = teximage0[i*4][j*4][3] / 4; + } + } + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage2); + + // level 3, 4: don't care + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, 2, 2, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage0); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage0); + + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // + // 1D texture: just bottom row of the 2D texture + // + glBindTexture(GL_TEXTURE_1D, obj1D); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage0); + + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // + // 3D texture: 2D texture, depth = 1 + // + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + for (k = 0; k < 16; k++) { + if (i < 8) { + teximage3D[i][j][k][0] = teximage0[j][k][0]; + teximage3D[i][j][k][1] = teximage0[j][k][1]; + teximage3D[i][j][k][2] = teximage0[j][k][2]; + teximage3D[i][j][k][3] = teximage0[j][k][3]; + } + else { + // back half: half intensity + teximage3D[i][j][k][0] = teximage0[j][k][0] / 2; + teximage3D[i][j][k][1] = teximage0[j][k][1] / 2; + teximage3D[i][j][k][2] = teximage0[j][k][2] / 2; + teximage3D[i][j][k][3] = teximage0[j][k][3] / 2; + } + } + } + } + glBindTexture(GL_TEXTURE_3D, obj3D); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, teximage3D); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // + // 2D GL_DEPTH_COMPONENT texture (for shadow sampler tests) + // + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + if (j < 8) + teximageZ[i][j] = 0.25; + else + teximageZ[i][j] = 0.75; + } + } + glActiveTexture(GL_TEXTURE1); // NOTE: Unit 1 + glBindTexture(GL_TEXTURE_2D, objZ); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 16, 16, 0, + GL_DEPTH_COMPONENT, GL_FLOAT, teximageZ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, + GL_COMPARE_R_TO_TEXTURE_ARB); + + glActiveTexture(GL_TEXTURE0); +} + + +void +GLSLTest::setupTextureMatrix1(void) +{ + // This matrix is used by some of the general matrix tests + static const GLfloat m[16] = { + 1.0, 0.5, 0.6, 0.0, // col 0 + 0.0, 1.0, 0.0, 0.7, // col 1 + 0.0, 0.0, 1.0, 0.8, // col 2 + 0.1, 0.2, 0.3, 1.0 // col 3 + }; + glMatrixMode(GL_TEXTURE); + glActiveTexture(GL_TEXTURE1); + glLoadMatrixf(m); + glActiveTexture(GL_TEXTURE0); + glMatrixMode(GL_MODELVIEW); +} + + +bool +GLSLTest::setup(void) +{ + // check that we have OpenGL 2.0 + const char *verString = (const char *) glGetString(GL_VERSION); + if (verString[0] != '2' || verString[1] != '.') { + //env->log << "OpenGL 2.x not supported\n"; + return false; + } + if (verString[2] >= '1') + version21 = GL_TRUE; // update when needed + else + version21 = GL_FALSE; // play it safe + + if (!getFunctions()) { + env->log << "Unable to get pointer to an OpenGL 2.0 API function\n"; + return false; + } + + setupTextures(); + setupTextureMatrix1(); + + // load program inputs + glColor4fv(PrimaryColor); + glSecondaryColor3fv(SecondaryColor); + + // other GL state + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Ambient); + glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MatDiffuse); + glPointSize(PSIZE); + glPointParameterf(GL_POINT_SIZE_MIN, PSIZE_MIN); + glPointParameterf(GL_POINT_SIZE_MAX, PSIZE_MAX); + glPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, PSIZE_THRESH); + glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, PointAtten); + glFogf(GL_FOG_START, FOG_START); + glFogf(GL_FOG_END, FOG_END); + glFogfv(GL_FOG_COLOR, FogColor); + + GLenum err = glGetError(); + assert(!err); // should be OK + + // setup vertex transform (we'll draw a quad in middle of window) + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-4.0, 4.0, -4.0, 4.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + // compute error tolerances (may need fine-tuning) + int bufferBits[5]; + glGetIntegerv(GL_RED_BITS, &bufferBits[0]); + glGetIntegerv(GL_GREEN_BITS, &bufferBits[1]); + glGetIntegerv(GL_BLUE_BITS, &bufferBits[2]); + glGetIntegerv(GL_ALPHA_BITS, &bufferBits[3]); + glGetIntegerv(GL_DEPTH_BITS, &bufferBits[4]); + + tolerance[0] = 2.0 / (1 << bufferBits[0]); + tolerance[1] = 2.0 / (1 << bufferBits[1]); + tolerance[2] = 2.0 / (1 << bufferBits[2]); + if (bufferBits[3]) + tolerance[3] = 2.0 / (1 << bufferBits[3]); + else + tolerance[3] = 1.0; + if (bufferBits[4]) + tolerance[4] = 16.0 / (1 << bufferBits[4]); + else + tolerance[4] = 1.0; + + // Some tests request a looser tolerance: + // XXX a factor of 4 may be too much... + for (int i = 0; i < 5; i++) + looseTolerance[i] = 4.0 * tolerance[i]; + + return true; +} + + +void +GLSLTest::reportFailure(const char *programName, + const GLfloat expectedColor[4], + const GLfloat actualColor[4] ) const +{ + env->log << "FAILURE:\n"; + env->log << " Shader test: " << programName << "\n"; + env->log << " Expected color: "; + env->log << expectedColor[0] << ", "; + env->log << expectedColor[1] << ", "; + env->log << expectedColor[2] << ", "; + env->log << expectedColor[3] << "\n"; + env->log << " Observed color: "; + env->log << actualColor[0] << ", "; + env->log << actualColor[1] << ", "; + env->log << actualColor[2] << ", "; + env->log << actualColor[3] << "\n"; +} + +void +GLSLTest::reportZFailure(const char *programName, + GLfloat expectedZ, GLfloat actualZ) const +{ + env->log << "FAILURE:\n"; + env->log << " Shader test: " << programName << "\n"; + env->log << " Expected Z: " << expectedZ << "\n"; + env->log << " Observed Z: " << actualZ << "\n"; +} + + +// Compare actual and expected colors +bool +GLSLTest::equalColors(const GLfloat act[4], const GLfloat exp[4], int flags) const +{ + const GLfloat *tol; + if (flags & FLAG_LOOSE) + tol = looseTolerance; + else + tol = tolerance; + if ((fabsf(act[0] - exp[0]) > tol[0]) || + (fabsf(act[1] - exp[1]) > tol[1]) || + (fabsf(act[2] - exp[2]) > tol[2]) || + (fabsf(act[3] - exp[3]) > tol[3])) + return false; + else + return true; +} + + +bool +GLSLTest::equalDepth(GLfloat z0, GLfloat z1) const +{ + if (fabsf(z0 - z1) > tolerance[4]) + return false; + else + return true; +} + + +GLuint +GLSLTest::loadAndCompileShader(GLenum target, const char *str) +{ + GLuint shader; + shader = glCreateShader_func(target); + glShaderSource_func(shader, 1, + (const GLchar **) &str, NULL); + glCompileShader_func(shader); + return shader; +} + + +// Check the compile status of the just compiled shader. +// If the outcome is unexpected, report an error. +bool +GLSLTest::checkCompileStatus(GLenum target, GLuint shader, + const ShaderProgram &p) +{ + GLint stat; + GLchar infoLog[1000]; + GLsizei len; + + glGetShaderiv_func(shader, GL_COMPILE_STATUS, &stat); + if (!stat) { + glGetShaderInfoLog_func(shader, 1000, &len, infoLog); + // env->log << infoLog << "\n"; + } + + if (!stat && (p.flags & FLAG_ILLEGAL_SHADER) == 0) { + // this _should_ have compiled + env->log << "FAILURE:\n"; + env->log << " Shader test: " << p.name << "\n"; + if (target == GL_FRAGMENT_SHADER) + env->log << "Fragment shader did not compile:\n"; + else + env->log << "Vertex shader did not compile:\n"; + env->log << infoLog; + return false; + } + else if (stat && (p.flags & FLAG_ILLEGAL_SHADER)) { + // this should _not_ have compiled! + env->log << "FAILURE:\n"; + env->log << " Shader test: " << p.name << "\n"; + env->log << " Shader should not have compiled, but it did.\n"; + return false; + } + return true; +} + + +bool +GLSLTest::testProgram(const ShaderProgram &p) +{ + static const GLfloat uniformMatrix[16] = { + 1.0, 0.1, 0.2, 0.3, // col 0 + 0.0, 1.0, 0.0, 0.4, // col 1 + 0.0, 1.0, 1.0, 0.5, // col 2 + 0.6, 0.7, 0.8, 1.0 // col 3 + }; + static const GLfloat uniformMatrix2x4[8] = { + 0.0, 0.1, 0.2, 0.3, // col 0 + 0.4, 0.5, 0.6, 0.7 // col 1 + }; + static const GLfloat uniformMatrix4x3[12] = { + 0.0, 0.1, 0.2, // col 0 + 0.3, 0.4, 0.5, // col 1 + 0.6, 0.7, 0.8, // col 2 + 0.9, 1.0, 0.0 // col 3 + }; + const GLfloat r = 0.62; // XXX draw 16x16 pixel quad + GLuint fragShader = 0, vertShader = 0, program = 0; + GLint u1, utex1d, utex2d, utex3d, utexZ, umat4, umat4t; + GLint umat2x4, umat2x4t, umat4x3, umat4x3t; + bool retVal = false; + + if (p.fragShaderString) { + fragShader = loadAndCompileShader(GL_FRAGMENT_SHADER, + p.fragShaderString); + if (!checkCompileStatus(GL_FRAGMENT_SHADER, fragShader, p)) { + retVal = false; + goto cleanup; + } + } + if (p.vertShaderString) { + vertShader = loadAndCompileShader(GL_VERTEX_SHADER, + p.vertShaderString); + if (!checkCompileStatus(GL_VERTEX_SHADER, vertShader, p)) { + retVal = false; + goto cleanup; + } + } + if (!fragShader && !vertShader) { + // must have had a compilation errror + retVal = false; + goto cleanup; + } + + if (p.flags & FLAG_ILLEGAL_SHADER) { + // don't render/test + retVal = true; + goto cleanup; + } + + program = glCreateProgram_func(); + if (fragShader) + glAttachShader_func(program, fragShader); + if (vertShader) + glAttachShader_func(program, vertShader); + glLinkProgram_func(program); + + // check link + { + GLint stat; + glGetProgramiv_func(program, GL_LINK_STATUS, &stat); + if (!stat) { + if (p.flags & FLAG_ILLEGAL_LINK) { + // this is the expected outcome + retVal = true; + goto cleanup; + } + else { + GLchar log[1000]; + GLsizei len; + glGetProgramInfoLog_func(program, 1000, &len, log); + env->log << "FAILURE:\n"; + env->log << " Shader test: " << p.name << "\n"; + env->log << " Link error: "; + env->log << log; + retVal = false; + goto cleanup; + } + } + else { + // link successful + if (p.flags & FLAG_ILLEGAL_LINK) { + // the shaders should _not_ have linked + env->log << "FAILURE:\n"; + env->log << " Shader test: " << p.name << "\n"; + env->log << " Program linked, but shouldn't have.\n"; + retVal = false; + goto cleanup; + } + } + } + + glUseProgram_func(program); + + // load uniform vars + u1 = glGetUniformLocation_func(program, "uniform1"); + if (u1 >= 0) + glUniform4fv_func(u1, 1, Uniform1); + + utex1d = glGetUniformLocation_func(program, "tex1d"); + if (utex1d >= 0) + glUniform1i_func(utex1d, 0); // bind to tex unit 0 + + utex2d = glGetUniformLocation_func(program, "tex2d"); + if (utex2d >= 0) + glUniform1i_func(utex2d, 0); // bind to tex unit 0 + + utex3d = glGetUniformLocation_func(program, "tex3d"); + if (utex3d >= 0) + glUniform1i_func(utex3d, 0); // bind to tex unit 0 + + utexZ = glGetUniformLocation_func(program, "texZ"); + if (utexZ >= 0) + glUniform1i_func(utexZ, 1); // bind to tex unit 1 + + umat4 = glGetUniformLocation_func(program, "uniformMat4"); + if (umat4 >= 0) + glUniformMatrix4fv_func(umat4, 1, GL_FALSE, uniformMatrix); + + umat4t = glGetUniformLocation_func(program, "uniformMat4t"); + if (umat4t >= 0) + glUniformMatrix4fv_func(umat4t, 1, GL_TRUE, uniformMatrix); + + umat2x4 = glGetUniformLocation_func(program, "uniformMat2x4"); + if (umat2x4 >= 0) + glUniformMatrix2x4fv_func(umat2x4, 1, GL_FALSE, uniformMatrix2x4); + + umat2x4t = glGetUniformLocation_func(program, "uniformMat2x4t"); + if (umat2x4t >= 0) + glUniformMatrix2x4fv_func(umat2x4t, 1, GL_TRUE, uniformMatrix2x4); + + umat4x3 = glGetUniformLocation_func(program, "uniformMat4x3"); + if (umat4x3 >= 0) + glUniformMatrix4x3fv_func(umat4x3, 1, GL_FALSE, uniformMatrix4x3); + + umat4x3t = glGetUniformLocation_func(program, "uniformMat4x3t"); + if (umat4x3t >= 0) + glUniformMatrix4x3fv_func(umat4x3t, 1, GL_TRUE, uniformMatrix4x3); + + + // to avoid potential issue with undefined result.depth.z + if (p.expectedZ == DONT_CARE_Z) + glDisable(GL_DEPTH_TEST); + else + glEnable(GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (p.flags & FLAG_WINDING_CW) { + /* Clockwise */ + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex2f(-r, -r); + glTexCoord2f(0, 1); glVertex2f(-r, r); + glTexCoord2f(1, 1); glVertex2f( r, r); + glTexCoord2f(1, 0); glVertex2f( r, -r); + glEnd(); + } + else { + /* Counter Clockwise */ + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex2f(-r, -r); + glTexCoord2f(1, 0); glVertex2f( r, -r); + glTexCoord2f(1, 1); glVertex2f( r, r); + glTexCoord2f(0, 1); glVertex2f(-r, r); + glEnd(); + } + + // read a pixel from lower-left corder of rendered quad + GLfloat pixel[4]; + glReadPixels(windowSize / 2 - 2, windowSize / 2 - 2, 1, 1, + GL_RGBA, GL_FLOAT, pixel); + if (0) // debug + printf("%s: Expect: %.3f %.3f %.3f %.3f found: %.3f %.3f %.3f %.3f\n", + p.name, + p.expectedColor[0], p.expectedColor[1], + p.expectedColor[2], p.expectedColor[3], + pixel[0], pixel[1], pixel[2], pixel[3]); + + if (!equalColors(pixel, p.expectedColor, p.flags)) { + reportFailure(p.name, p.expectedColor, pixel); + retVal = false; + goto cleanup; + } + + if (p.expectedZ != DONT_CARE_Z) { + GLfloat z; + // read z at center of quad + glReadPixels(windowSize / 2, windowSize / 2, 1, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, &z); + if (!equalDepth(z, p.expectedZ)) { + reportZFailure(p.name, p.expectedZ, z); + retVal = false; + goto cleanup; + } + } + + // passed! + retVal = true; + + if (0) // debug + printf("%s passed\n", p.name); + + cleanup: + if (fragShader) + glDeleteShader_func(fragShader); + if (vertShader) + glDeleteShader_func(vertShader); + glDeleteProgram_func(program); + + return retVal; +} + + +void +GLSLTest::runOne(MultiTestResult &r, Window &w) +{ + (void) w; + if (!setup()) { + r.pass = false; + return; + } + + // If you just want to run a single sub-test, assign the name to singleTest. + const char *singleTest = NULL; + if (singleTest) { + for (int i = 0; Programs[i].name; i++) { + if (strcmp(Programs[i].name, singleTest) == 0) { + r.numPassed = testProgram(Programs[i]); + r.numFailed = 1 - r.numPassed; + break; + } + } + } + else { + // loop over all tests + for (int i = 0; Programs[i].name; i++) { + if ((Programs[i].flags & FLAG_VERSION_2_1) && !version21) + continue; // skip non-applicable tests + if (testProgram(Programs[i])) { + r.numPassed++; + } + else { + r.numFailed++; + } + } + } + r.pass = (r.numFailed == 0); +} + + +// The test object itself: +GLSLTest glslTest("glsl1", "window, rgb, z", + "", // no extension filter (we'll test for version 2.x during setup) + "GLSL test 1: test basic Shading Language functionality.\n" + ); + + + +} // namespace GLEAN diff --git a/tests/glean/tglsl1.h b/tests/glean/tglsl1.h new file mode 100644 index 000000000..84a3d723f --- /dev/null +++ b/tests/glean/tglsl1.h @@ -0,0 +1,90 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 1999 Allen Akin All Rights Reserved. +// +// 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 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 ALLEN AKIN 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. +// +// END_COPYRIGHT + +// tglsl1.h: Test OpenGL shading language +// Brian Paul 6 March 2007 + +#ifndef __tglsl1_h__ +#define __tglsl1_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +#define windowSize 100 + + +class ShaderProgram +{ +public: + const char *name; + const char *vertShaderString; + const char *fragShaderString; + GLfloat expectedColor[4]; + GLfloat expectedZ; + int flags; +}; + + + +class GLSLTest: public MultiTest +{ +public: + GLSLTest(const char* testName, const char* filter, + const char *extensions, const char* description): + MultiTest(testName, filter, extensions, description) + { + } + + virtual void runOne(MultiTestResult &r, Window &w); + +private: + GLfloat tolerance[5]; + GLfloat looseTolerance[5]; + GLfloat version21; // OpenGL 2.1 or higher supported? + bool getFunctions(void); + void setupTextures(void); + void setupTextureMatrix1(void); + bool setup(void); + bool equalColors(const GLfloat a[4], const GLfloat b[4], int flags) const; + bool equalDepth(GLfloat z0, GLfloat z1) const; + GLuint loadAndCompileShader(GLenum target, const char *str); + bool checkCompileStatus(GLenum target, GLuint shader, + const ShaderProgram &p); + bool testProgram(const ShaderProgram &p); + void reportFailure(const char *programName, + const GLfloat expectedColor[4], + const GLfloat actualColor[4] ) const; + void reportZFailure(const char *programName, + GLfloat expectedZ, GLfloat actualZ) const; + +}; + +} // namespace GLEAN + +#endif // __tglsl1_h__ diff --git a/tests/glean/tmultitest.cpp b/tests/glean/tmultitest.cpp index 42cac0389..c98d7ec31 100644 --- a/tests/glean/tmultitest.cpp +++ b/tests/glean/tmultitest.cpp @@ -71,11 +71,19 @@ MultiTest::runOne(MultiTestResult &r, Window &) void MultiTest::logOne(MultiTestResult &r) { - logPassFail(r); - logConcise(r); - env->log << "\t" - << r.numPassed << " tests passed, " - << r.numFailed << " tests failed.\n"; + if (r.numPassed == 0 && r.numFailed == 0) { + // non-applicable test + env->log << name << ": NOTE "; + logConcise(r); + env->log << "\tTest skipped/non-applicable\n"; + } + else { + logPassFail(r); + logConcise(r); + env->log << "\t" + << r.numPassed << " tests passed, " + << r.numFailed << " tests failed.\n"; + } } diff --git a/tests/glean/toccluqry.cpp b/tests/glean/toccluqry.cpp new file mode 100644 index 000000000..7cc49b445 --- /dev/null +++ b/tests/glean/toccluqry.cpp @@ -0,0 +1,675 @@ +// BEGIN_COPYRIGHT -*- glean -*- + +/* + * Copyright © 2006 Intel Corporation + * Copyright © 1999 Allen Akin + * + * 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. + * + * Authors: + * Wei Wang <wei.z.wang@intel.com> + * + */ + +/* + * toccluqry.cpp: Conformance test on ARB_occlusion_query extension + */ + +#define GL_GLEXT_PROTOTYPES +#include "toccluqry.h" +#include <cassert> +#include <stdio.h> +#include <cmath> + + +#define START_QUERY(id)\ + glBeginQueryARB(GL_SAMPLES_PASSED_ARB, id); + + +#define TERM_QUERY()\ + glEndQueryARB(GL_SAMPLES_PASSED_ARB);\ + + +namespace GLEAN { + +/* Generate a box which will be occluded by the occluder */ +void OccluQryTest::gen_box(GLfloat left, GLfloat right, + GLfloat top, GLfloat btm) +{ + glBegin(GL_POLYGON); + glVertex3f(left, top, 0); + glVertex3f(right, top, 0); + glVertex3f(right, btm, 0); + glVertex3f(left, btm, 0); + glEnd(); +} + + +bool OccluQryTest::chk_ext() +{ + const char *ext = (const char *) glGetString(GL_EXTENSIONS); + + if (!strstr(ext, "GL_ARB_occlusion_query")) { + fprintf(stdout, "W: Extension GL_ARB_occlusion_query is missing.\n"); + return false; + } + + return true; +} + + +GLuint OccluQryTest::find_unused_id() +{ + unsigned int id; + int counter = 0; + +#define MAX_FIND_ID_ROUND 256 + + while (1) { + /* assuming that at least 2^32-1 <id> can be generated */ + id = random() % ((unsigned long)1 << 32 - 1); + if (id != 0 && glIsQueryARB(id) == GL_FALSE) + return id; + if (++ counter >= MAX_FIND_ID_ROUND) { + fprintf(stderr, + "W: Cannot find the unused id after [%d] tries.\n", + MAX_FIND_ID_ROUND); + return 0; + } + } +} + + +/* If multiple queries are issued on the same target and id prior to calling + * GetQueryObject[u]iVARB, the result returned will always be from the last + * query issued. The results from any queries before the last one will be lost + * if the results are not retrieved before starting a new query on the same + * target and id. + */ +bool OccluQryTest::conformOQ_GetObjivAval_multi1(GLuint id) +{ + GLint ready; + GLuint passed = 0; + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + glOrtho( -1.0, 1.0, -1.0, 1.0, 0.0, 25.0 ); + + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, -10.0); + + /* draw the occluder (red) */ + glColorMask(1, 1, 1, 1); + glDepthMask(GL_TRUE); + glColor3f(1, 0, 0); + gen_box(-0.5, 0.5, 0.5, -0.5); + + glPushMatrix(); + glTranslatef( 0.0, 0.0, -5.0); + glColorMask(0, 0, 0, 0); + glDepthMask(GL_FALSE); + + /* draw the 1st box (gren) which is occluded by the occluder partly */ + START_QUERY(id); + glColor3f(0, 1, 0); + gen_box(-0.51, 0.51, 0.51, -0.51); + TERM_QUERY(); + + /* draw the 2nd box (blue) which is occluded by the occluder throughly */ + START_QUERY(id); + glColor3f(0, 0, 1); + gen_box(-0.4, 0.4, 0.4, -0.4); + TERM_QUERY(); + + glPopMatrix(); + + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + do { + glGetQueryObjectivARB(id, GL_QUERY_RESULT_AVAILABLE_ARB, &ready); + } while (!ready); + glGetQueryObjectuivARB(id, GL_QUERY_RESULT_ARB, &passed); + + // 'passed' should be zero + return passed > 0 ? false : true; +} + + +/* If mutiple queries are issued on the same target and diff ids prior + * to calling GetQueryObject[u]iVARB, the results should be + * corresponding to those queries (ids) respectively. + */ +bool OccluQryTest::conformOQ_GetObjivAval_multi2() +{ + GLuint passed1 = 0, passed2 = 0, passed3 = 0; + GLuint id1, id2, id3; + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + glOrtho( -1.0, 1.0, -1.0, 1.0, 0.0, 25.0 ); + + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, -10.0); + + + /* draw the occluder (red) */ + glColorMask(1, 1, 1, 1); + glDepthMask(GL_TRUE); + glColor3f(1, 0, 0); + gen_box(-0.5, 0.5, 0.5, -0.5); + + glPushMatrix(); + glTranslatef( 0.0, 0.0, -5.0); + glColorMask(0, 0, 0, 0); + glDepthMask(GL_FALSE); + + id1 = find_unused_id(); + START_QUERY(id1); + /* draw green quad, much larger than occluder */ + glColor3f(0, 1, 0); + gen_box(-0.7, 0.7, 0.7, -0.7); + TERM_QUERY(); + + id2 = find_unused_id(); + START_QUERY(id2); + /* draw blue quad, slightly larger than occluder */ + glColor3f(0, 0, 1); + gen_box(-0.53, 0.53, 0.53, -0.53); + TERM_QUERY(); + + id3 = find_unused_id(); + START_QUERY(id3); + /* draw white quad, smaller than occluder (should not be visible) */ + glColor3f(1, 1, 1); + gen_box(-0.4, 0.4, 0.4, -0.4); + TERM_QUERY(); + + glPopMatrix(); + + glGetQueryObjectuivARB(id1, GL_QUERY_RESULT_ARB, &passed1); + glGetQueryObjectuivARB(id2, GL_QUERY_RESULT_ARB, &passed2); + glGetQueryObjectuivARB(id3, GL_QUERY_RESULT_ARB, &passed3); + + glDepthMask(GL_TRUE); + + + glDeleteQueriesARB(1, &id1); + glDeleteQueriesARB(1, &id2); + glDeleteQueriesARB(1, &id3); + + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + + if ( passed1 > passed2 && passed2 > passed3 && passed3 == 0) + return true; + else + return false; +} + + +/* + * void GetQueryivARB(enum target, enum pname, int *params); + * + * If <pname> is QUERY_COUNTER_BITS_ARB, the number of bits in the counter + * for <target> will be placed in <params>. The minimum number of query + * counter bits allowed is a function of the implementation's maximum + * viewport dimensions (MAX_VIEWPORT_DIMS). If the counter is non-zero, + * then the counter must be able to represent at least two overdraws for + * every pixel in the viewport using only one sample buffer. The formula to + * compute the allowable minimum value is below (where n is the minimum + * number of bits): + * n = (min (32, ceil (log2 (maxViewportWidth x maxViewportHeight x 2) ) ) ) or 0 + */ +bool OccluQryTest::conformOQ_GetQry_CnterBit() +{ + int bit_num, dims[2]; + GLenum err; + float min_impl, min_bit_num; + + /* get the minimum bit number supported by the implementation, + * and check the legality of result of GL_QUERY_COUNTER_BITS_ARB */ + glGetQueryivARB(GL_SAMPLES_PASSED_ARB, GL_QUERY_COUNTER_BITS_ARB, &bit_num); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, dims); + err = glGetError(); + if (err == GL_INVALID_OPERATION || err == GL_INVALID_ENUM) + return false; + + min_impl = ceil(logf((float)dims[0]*(float)dims[1]*1.0*2.0) / logf(2.0)); + min_bit_num = 32.0 > min_impl ? min_impl : 32.0; + + if ((float)bit_num < min_bit_num) + return false; + + return true; +} + + +/* If BeginQueryARB is called with an unused <id>, that name is marked as used + * and associated with a new query object. */ +bool OccluQryTest::conformOQ_Begin_unused_id() +{ + unsigned int id; + bool pass = true; + + id = find_unused_id(); + + if (id == 0) + return false; + + glBeginQuery(GL_SAMPLES_PASSED_ARB, id); + + if (glIsQueryARB(id) == GL_FALSE) { + fprintf(stderr, "F: Begin with a unused id failed.\n"); + pass = false; + } + + glEndQuery(GL_SAMPLES_PASSED_ARB); + + return pass; +} + +/* if EndQueryARB is called while no query with the same target is in progress, + * an INVALID_OPERATION error is generated. */ +bool OccluQryTest::conformOQ_EndAfter(GLuint id) +{ + START_QUERY(id); + TERM_QUERY(); + + glEndQueryARB(GL_SAMPLES_PASSED_ARB); + + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "EndQuery when there is no queries.\n"); + return false; + } + + return true; +} + + +/* Calling either GenQueriesARB while any query of any target is active causes + * an INVALID_OPERATION error to be generated. */ +bool OccluQryTest::conformOQ_GenIn(GLuint id) +{ + int pass = true; + + START_QUERY(id); + + glGenQueriesARB(1, &id); + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "GenQueries in the progress of another.\n"); + pass = false; + } + + TERM_QUERY(); + + return pass; +} + + +/* Calling either DeleteQueriesARB while any query of any target is active causes + * an INVALID_OPERATION error to be generated. */ +bool OccluQryTest::conformOQ_DeleteIn(GLuint id) +{ + int pass = true; + + START_QUERY(id); + + if (id > 0) { + glDeleteQueriesARB(1, &id); + + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "DeleteQueries in the progress of another.\n"); + pass = false; + } + } + + TERM_QUERY(); + + return pass; +} + + +/* If BeginQueryARB is called while another query is already in progress with + * the same target, an INVALID_OPERATION error should be generated. */ +bool OccluQryTest::conformOQ_BeginIn(GLuint id) +{ + int pass = true; + + START_QUERY(id); + + /* Issue another BeginQueryARB while another query is already in + progress */ + glBeginQueryARB(GL_SAMPLES_PASSED_ARB, id); + + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "BeginQuery in the progress of another.\n"); + pass = false; + } + + TERM_QUERY(); + return pass; +} + + +/* if the query object named by <id> is currently active, then an + * INVALID_OPERATION error is generated. */ +bool OccluQryTest::conformOQ_GetObjAvalIn(GLuint id) +{ + int pass = true; + GLint param; + + START_QUERY(id); + + glGetQueryObjectivARB(id, GL_QUERY_RESULT_AVAILABLE_ARB, ¶m); + if (glGetError() != GL_INVALID_OPERATION) + pass = false; + + glGetQueryObjectuivARB(id, GL_QUERY_RESULT_AVAILABLE_ARB, (GLuint *)¶m); + if (glGetError() != GL_INVALID_OPERATION) + pass = false; + + if (pass == false) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "GetQueryObjectuiv with GL_QUERY_RESULT_AVAILABLE_ARB " + "in the active progress.\n"); + } + TERM_QUERY(); + + return pass; +} + + +/* if the query object named by <id> is currently active, then an + * INVALID_OPERATION error is generated. */ +bool OccluQryTest::conformOQ_GetObjResultIn(GLuint id) +{ + int pass = true; + GLint param; + + START_QUERY(id); + + glGetQueryObjectivARB(id, GL_QUERY_RESULT_ARB, ¶m); + if (glGetError() != GL_INVALID_OPERATION) + pass = false; + + glGetQueryObjectuivARB(id, GL_QUERY_RESULT_ARB, (GLuint *)¶m); + if (glGetError() != GL_INVALID_OPERATION) + pass = false; + + if (pass == false) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "GetQueryObject[u]iv with GL_QUERY_RESULT_ARB " + "in the active progress.\n"); + } + TERM_QUERY(); + + return pass; +} + + +/* If <id> is not the name of a query object, then an INVALID_OPERATION error + * is generated. */ +bool OccluQryTest::conformOQ_GetObjivAval(GLuint id) +{ + GLuint id_tmp; + GLint param; + + START_QUERY(id); + TERM_QUERY(); + + id_tmp = find_unused_id(); + + if (id_tmp == 0) + return false; + + glGetQueryObjectivARB(id_tmp, GL_QUERY_RESULT_AVAILABLE_ARB, ¶m); + + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "GetQueryObjectuiv can still query the result" + "by an unused query id\n."); + return false; + } + + return true; +} + + +/* Basic tests on query id generation and deletion */ +bool OccluQryTest::conformOQ_Gen_Delete(unsigned int id_n) +{ + GLuint *ids1 = NULL, *ids2 = NULL; + unsigned int i, j; + bool pass = true; + + ids1 = (GLuint *)malloc(id_n * sizeof(GLuint)); + ids2 = (GLuint *)malloc(id_n * sizeof(GLuint)); + + if (!ids1 || !ids2) { + fprintf(stderr, "F: Cannot alloc memory to pointer ids[12].\n"); + return false; + } + + glGenQueriesARB(id_n, ids1); + glGenQueriesARB(id_n, ids2); + + /* compare whether <id> generated during the previous 2 rounds are + * duplicated */ + for (i = 0; i < id_n; i ++) { + for (j = 0; j < id_n; j ++) { + if (ids1[i] == ids2[j]) { + fprintf(stderr, "F: ids1[%d] == ids2[%d] == %u.\n", + i, j, ids1[i]); + pass = false; + } + } + } + + /* Note: the spec seems to indicate that glGenQueries reserves query + * IDs but doesn't create query objects for those IDs. A query object + * isn't created until they are used by glBeginQuery. So this part + * of the test is invalid. + */ +#if 0 + /* Checkout whether the Query ID just generated is valid */ + for (i = 0; i < id_n; i ++) { + if (glIsQueryARB(ids1[i]) == GL_FALSE) { + fprintf(stderr, "F: id [%d] just generated is not valid.\n", + ids1[i]); + pass = false; + } + } +#endif + + /* if <id> is a non-zero value that is not the name of a query object, + * IsQueryARB returns FALSE. */ + glDeleteQueriesARB(id_n, ids1); + for (i = 0; i < id_n; i ++) { + if (glIsQueryARB(ids1[i]) == GL_TRUE) { + fprintf(stderr, "F: id [%d] just deleted is still valid.\n", + ids1[i]); + pass = false; + } + } + + /* Delete only for sanity purpose */ + glDeleteQueriesARB(id_n, ids2); + + if (ids1) + free(ids1); + if (ids2) + free(ids2); + + + ids1 = (GLuint *)malloc(id_n * sizeof(GLuint)); + if (ids1 == NULL) + return false; + + for (i = 0; i < id_n; i ++) { + glGenQueriesARB(1, ids1 + i); + for (j = 0; j < i; j ++) { + if (ids1[i] == ids1[j]) { + fprintf(stderr, "E: duplicated id generated [ %u ]", + ids1[i]); + pass = false; + } + } + } + + glDeleteQueriesARB(id_n, ids1); + if (ids1) + free(ids1); + + return pass; +} + + +/* If <id> is zero, IsQueryARB should return FALSE.*/ +bool OccluQryTest::conformOQ_IsIdZero(void) +{ + if (glIsQueryARB(0) == GL_TRUE) { + fprintf(stderr, "F: zero is treated as a valid id by" + "IsQueryARB().\n"); + return false; + } + + return true; +} + + +/* If BeginQueryARB is called with an <id> of zero, an INVALID_OPERATION error + * should be generated. */ +bool OccluQryTest::conformOQ_BeginIdZero(void) +{ + glBeginQueryARB(GL_SAMPLES_PASSED_ARB, 0); + if (glGetError() != GL_INVALID_OPERATION) { + fprintf(stderr, "F: No GL_INVALID_OPERATION generated if " + "BeginQuery with zero ID.\n"); + return false; + } + + return true; +} + + +void OccluQryTest::runOne(MultiTestResult &r, Window &w) +{ + bool result; + (void) w; + GLuint queryId; + + if (!chk_ext()) + return; + glEnable(GL_DEPTH_TEST); + glGenQueriesARB(1, &queryId); + + if (queryId == 0) + return; + +#if defined(GL_ARB_occlusion_query) + result = conformOQ_GetQry_CnterBit(); + reportPassFail(r, result, "conformOQ_GetQry_CnterBit"); + + result = conformOQ_GetObjivAval_multi1(queryId); + reportPassFail(r, result, "conformOQ_GetObjivAval_multi1"); + + result = conformOQ_GetObjivAval_multi2(); + reportPassFail(r, result, "conformOQ_GetObjivAval_multi2"); + + result = conformOQ_Begin_unused_id(); + reportPassFail(r, result, "conformOQ_Begin_unused_id"); + + result = conformOQ_EndAfter(queryId); + reportPassFail(r, result, "conformOQ_EndAfter"); + + result = conformOQ_GenIn(queryId); + reportPassFail(r, result, "conformOQ_GenIn"); + + result = conformOQ_BeginIn(queryId); + reportPassFail(r, result, "conformOQ_BeginIn"); + + result = conformOQ_DeleteIn(queryId); + reportPassFail(r, result, "conformOQ_DeleteIn"); + + result = conformOQ_GetObjAvalIn(queryId); + reportPassFail(r, result, "conformOQ_GetObjAvalIn"); + + result = conformOQ_GetObjResultIn(queryId); + reportPassFail(r, result, "conformOQ_GetObjResultIn"); + + result = conformOQ_GetObjivAval(queryId); + reportPassFail(r, result, "conformOQ_GetObjivAval"); + + result = conformOQ_Gen_Delete(64); + reportPassFail(r, result, "conformOQ_Gen_Delete"); + + result = conformOQ_IsIdZero(); + reportPassFail(r, result, "conformOQ_IsIdZero"); + + result = conformOQ_BeginIdZero(); + reportPassFail(r, result, "conformOQ_BeginIdZero"); + + glDeleteQueriesARB(1, &queryId); + + r.pass = (r.numFailed == 0); +#endif +} + + +void OccluQryTest::reportPassFail(MultiTestResult &r, + bool pass, const char *msg) const +{ + if (pass) { + if (env->options.verbosity) + env->log << name << " subcase PASS: " + << msg << " test\n"; + r.numPassed++; + } else { + if (env->options.verbosity) + env->log << name << " subcase FAIL: " + << msg << " test\n"; + r.numFailed++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OccluQryTest occluQryTest("occluQry", "window, rgb, z", + "GL_ARB_occlusion_query", + "Test occlusion query comformance.\n"); +} // namespace GLEAN diff --git a/tests/glean/toccluqry.h b/tests/glean/toccluqry.h new file mode 100644 index 000000000..c4f74e3f4 --- /dev/null +++ b/tests/glean/toccluqry.h @@ -0,0 +1,72 @@ +// BEGIN_COPYRIGHT -*- glean -*- + +/* + * Copyright © 2006 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. + * + * Authors: + * Wei Wang <wei.z.wang@intel.com> + * + */ + + +// toccluqry.h: Test basic ARB_occlusion_query support. + +#ifndef __toccluqry_h__ +#define __toccluqry_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +class OccluQryTest: public MultiTest { + public: + OccluQryTest(const char* testName, const char* filter, + const char *prereqs, const char* description): + MultiTest(testName, filter, prereqs, description) { + } + + virtual void runOne(MultiTestResult& r, Window& w); + protected: + bool conformOQ_GetObjivAval_multi1(GLuint id); + bool conformOQ_GetObjivAval_multi2(); + bool conformOQ_GetQry_CnterBit(); + bool conformOQ_Begin_unused_id(); + bool conformOQ_EndAfter(GLuint id); + bool conformOQ_GenIn(GLuint id); + bool conformOQ_BeginIn(GLuint id); + bool conformOQ_DeleteIn(GLuint id); + bool conformOQ_GetObjAvalIn(GLuint id); + bool conformOQ_GetObjResultIn(GLuint id); + bool conformOQ_GetObjivAval(GLuint id); + bool conformOQ_Gen_Delete(unsigned int id_n); + bool conformOQ_IsIdZero(void); + bool conformOQ_BeginIdZero(void); + private: + void gen_box(GLfloat left, GLfloat right, GLfloat top, GLfloat btm); + GLuint find_unused_id(); + bool chk_ext(); + void reportPassFail(MultiTestResult &r, bool pass, const char *msg) const; +}; + +} // namespace GLEAN + +#endif diff --git a/tests/glean/tpbo.cpp b/tests/glean/tpbo.cpp new file mode 100644 index 000000000..ba24bde17 --- /dev/null +++ b/tests/glean/tpbo.cpp @@ -0,0 +1,1240 @@ +// BEGIN_COPYRIGHT -*- glean -*-
+//
+// Copyrigth (C) 2007 Intel Corporation
+// Copyright (C) 1999 Allen Akin All Rights Reserved.
+//
+// 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 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 ALLEN AKIN 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.
+//
+// END_COPYRIGHT
+//
+// Authors:
+// Shuang He <shuang.he@intel.com>
+//
+// tpbo.cpp: Test OpenGL Extension GL_ARB_pixel_buffer_object
+
+
+#define GL_GLEXT_PROTOTYPES
+#include "tpbo.h"
+#include <cassert>
+#include <math.h>
+#include "timer.h"
+namespace GLEAN
+{
+
+static int usePBO;
+#define BUFFER_OFFSET(i) ((char *)NULL + (i))
+
+bool PBOTest::setup(void)
+{
+ glMatrixMode(GL_PROJECTION);
+
+ glLoadIdentity();
+ gluOrtho2D(0, 100, 0, 100);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glDrawBuffer(GL_FRONT);
+ glReadBuffer(GL_FRONT);
+
+ // compute error tolerances (may need fine-tuning)
+ int
+ bufferBits[5];
+
+ glGetIntegerv(GL_RED_BITS, &bufferBits[0]);
+ glGetIntegerv(GL_GREEN_BITS, &bufferBits[1]);
+ glGetIntegerv(GL_BLUE_BITS, &bufferBits[2]);
+ glGetIntegerv(GL_ALPHA_BITS, &bufferBits[3]);
+ glGetIntegerv(GL_DEPTH_BITS, &bufferBits[4]);
+
+ tolerance[0] = 2.0 / (1 << bufferBits[0]);
+ tolerance[1] = 2.0 / (1 << bufferBits[1]);
+ tolerance[2] = 2.0 / (1 << bufferBits[2]);
+ if (bufferBits[3])
+ tolerance[3] = 2.0 / (1 << bufferBits[3]);
+ else
+ tolerance[3] = 1.0;
+ if (bufferBits[4])
+ tolerance[4] = 16.0 / (1 << bufferBits[4]);
+ else
+ tolerance[4] = 1.0;
+
+ // Check if GL_ARB_pixel_buffer_object is supported
+ if (!strstr((char *) glGetString(GL_EXTENSIONS), "GL_ARB_pixel_buffer_object")) {
+ printf("GL_ARB_pixel_buffer_object is not supported\n");
+ usePBO = 0;
+ return false;
+ }
+ else {
+ printf("GL_ARB_pixel_buffer_object is supported\n");
+ usePBO = 1;
+ }
+
+ return true;
+}
+
+
+void
+PBOTest::reportFailure(const char *msg, const int line) const
+{
+ env->log << "FAILURE: " << msg << " (at tpbo.cpp:" << line << ")\n";
+}
+
+void
+PBOTest::reportFailure(const char *msg, const GLenum target, const int line) const
+{
+ env->log << "FAILURE: " << msg;
+ if (target == GL_FRAGMENT_SHADER)
+ env->log << " (fragment)";
+ else
+ env->log << " (vertex)";
+ env->log << " (at tpbo.cpp:" << line << ")\n";
+}
+
+#define REPORT_FAILURE(MSG) reportFailure(MSG, __LINE__)
+#define REPORT_FAILURE_T(MSG, TARGET) reportFailure(MSG, TARGET, __LINE__)
+// Compare actual and expected colors
+bool PBOTest::equalColors(const GLfloat act[3], const GLfloat exp[3]) const
+{
+ if ((fabsf(act[0] - exp[0]) > tolerance[0])
+ || (fabsf(act[1] - exp[1]) > tolerance[1])
+ || (fabsf(act[2] - exp[2]) > tolerance[2])) {
+ return false;
+ }
+ else
+ return true;
+}
+
+bool PBOTest::equalColors1(const GLubyte act[3], const GLubyte exp[3]) const
+{
+ if ((act[0] != exp[0])
+ || (act[1] != exp[1])
+ || (act[2] != exp[2])) {
+ return false;
+ }
+ else
+ return true;
+}
+
+
+
+#define TEXSIZE 64
+
+bool PBOTest::testSanity(void)
+{
+ GLuint pbs[1];
+ GLuint pb_binding;
+
+ if (!usePBO)
+ return true;
+
+ // Check default binding
+ glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB, (GLint *) & pb_binding);
+ if (pb_binding != 0) {
+ REPORT_FAILURE("Failed to bind unpack pixel buffer object");
+ return false;
+ }
+
+ glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING_ARB, (GLint *) & pb_binding);
+ if (pb_binding != 0) {
+ REPORT_FAILURE("Failed to bind pack pixel buffer object");
+ return false;
+ }
+
+ glGenBuffersARB(1, pbs);
+
+ if (glIsBufferARB(pbs[0]) != GL_TRUE) {
+ REPORT_FAILURE("Failed to call glIsBuffersARB");
+ return false;
+ }
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pbs[0]);
+ glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB, (GLint *) & pb_binding);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+ if (pb_binding != pbs[0]) {
+ REPORT_FAILURE("Failed to bind unpack pixel buffer object");
+ return false;
+ }
+
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pbs[0]);
+ glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING_ARB, (GLint *) & pb_binding);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
+ if (pb_binding != pbs[0]) {
+ REPORT_FAILURE("Failed to bind unpack pixel buffer object");
+ return false;
+ }
+
+ glDeleteBuffersARB(1, pbs);
+
+ if (glIsBufferARB(pbs[0]) == GL_TRUE) {
+ REPORT_FAILURE("Failed to call glIsBuffersARB");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool PBOTest::testDrawPixels(void)
+{
+ int useUnpackBuffer;
+ int usePackBuffer;
+ GLuint pb_pack[1];
+ GLuint pb_unpack[1];
+ GLubyte buf[windowSize * windowSize * 4];
+ GLubyte t[TEXSIZE * TEXSIZE * 4];
+ int i, j;
+ GLubyte * pboPackMem = NULL;
+ GLubyte black[3] = { 0, 0, 0 };
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ for (useUnpackBuffer = 0; useUnpackBuffer < usePBO + 1; useUnpackBuffer++) {
+ for (usePackBuffer = 0; usePackBuffer < usePBO + 1; usePackBuffer++) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (useUnpackBuffer) {
+ glGenBuffersARB(1, pb_unpack);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb_unpack[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ TEXSIZE * TEXSIZE * 4 * sizeof(GLubyte), NULL,
+ GL_STREAM_DRAW);
+ }
+ GLubyte *pboMem = NULL;
+ if (useUnpackBuffer) {
+ pboMem = (GLubyte *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = t;
+ }
+
+ for (i = 0; i < TEXSIZE; i++)
+ for (j = 0; j < TEXSIZE; j++) {
+ pboMem[4 * (i * TEXSIZE + j)] = i % 256;
+ pboMem[4 * (i * TEXSIZE + j) + 1] = i % 256;
+ pboMem[4 * (i * TEXSIZE + j) + 2] = i % 256;
+ pboMem[4 * (i * TEXSIZE + j) + 3] = 0;
+ }
+
+ if (useUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+
+ if (useUnpackBuffer) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb_unpack[0]);
+ glDrawPixels(TEXSIZE, TEXSIZE, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ else
+ glDrawPixels(TEXSIZE, TEXSIZE, GL_BGRA, GL_UNSIGNED_BYTE, pboMem);
+
+ // Check the result
+ if (usePackBuffer) {
+ glGenBuffersARB(1, pb_pack);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pb_pack[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT,
+ windowSize * windowSize * 4 *
+ sizeof(GL_UNSIGNED_BYTE), NULL, GL_STREAM_DRAW);
+ glReadPixels(0, 0, windowSize, windowSize, GL_BGRA,
+ GL_UNSIGNED_BYTE, NULL);
+ pboPackMem = (GLubyte *) glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ }
+ else {
+ pboPackMem = buf;
+ glReadPixels(0, 0, windowSize, windowSize, GL_BGRA,
+ GL_UNSIGNED_BYTE, pboPackMem);
+ }
+
+ for (j = 0; j < windowSize; j++) {
+ for (i = 0; i < windowSize; i++) {
+ GLubyte exp[3];
+ exp[0] = j % 256;
+ exp[1] = j % 256;
+ exp[2] = j % 256;
+
+ if (i < TEXSIZE && j < TEXSIZE) {
+ if (equalColors1(&pboPackMem[(j * windowSize + i) * 4], exp)
+ != true) {
+ REPORT_FAILURE("glDrawPixels failed");
+ printf(" got (%d, %d) = [%d, %d, %d], ", i, j,
+ pboPackMem[(j * windowSize + i) * 4],
+ pboPackMem[(j * windowSize + i) * 4 + 1],
+ pboPackMem[(j * windowSize + i) * 4 + 2]);
+ printf("should be [%d, %d, %d]\n",
+ exp[0], exp[1], exp[2]);
+
+ return false;
+ }
+ }
+ else {
+ if (equalColors1(&pboPackMem[(j * windowSize + i) * 4],
+ black) != true) {
+ REPORT_FAILURE("glDrawPixels failed");
+ printf("(%d, %d) = [%d, %d, %d], ", i, j,
+ pboPackMem[(j * windowSize + i) * 4],
+ pboPackMem[(j * windowSize + i) * 4 + 1],
+ pboPackMem[(j * windowSize + i) * 4 + 2]);
+ printf("should be [0.0, 0.0, 0.0]\n");
+ return false;
+ }
+
+ }
+ }
+ }
+
+
+ if (usePackBuffer) {
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_pack);
+ }
+
+ if (useUnpackBuffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_unpack);
+ }
+
+ }
+ }
+
+ return true;
+}
+
+
+bool PBOTest::testPixelMap(void)
+{
+ int useUnpackBuffer;
+ int usePackBuffer;
+ GLuint pb_pack[1];
+ GLuint pb_unpack[1];
+ int i;
+ int size;
+ int max;
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ glGetIntegerv(GL_MAX_PIXEL_MAP_TABLE, &max);
+
+ for (usePackBuffer = 0; usePackBuffer < usePBO + 1; usePackBuffer++) {
+ for (useUnpackBuffer = 0; useUnpackBuffer < usePBO + 1;
+ useUnpackBuffer++) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (useUnpackBuffer) {
+ glGenBuffersARB(1, pb_unpack);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb_unpack[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, max * sizeof(GLushort),
+ NULL, GL_STREAM_DRAW);
+ }
+ GLushort *pboMem = NULL;
+ if (useUnpackBuffer) {
+ pboMem = (GLushort *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = (GLushort *) malloc(sizeof(GLushort) * max);
+ }
+ for (i = 0; i < max; i++)
+ pboMem[i] = max - i - 1;
+
+ if (useUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glPixelMapusv(GL_PIXEL_MAP_R_TO_R, max, NULL);
+ glPixelMapusv(GL_PIXEL_MAP_G_TO_G, max, NULL);
+ glPixelMapusv(GL_PIXEL_MAP_B_TO_B, max, NULL);
+ glPixelMapusv(GL_PIXEL_MAP_A_TO_A, max, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ else {
+ glPixelMapusv(GL_PIXEL_MAP_R_TO_R, max, pboMem);
+ glPixelMapusv(GL_PIXEL_MAP_G_TO_G, max, pboMem);
+ glPixelMapusv(GL_PIXEL_MAP_B_TO_B, max, pboMem);
+ glPixelMapusv(GL_PIXEL_MAP_A_TO_A, max, pboMem);
+ free(pboMem);
+ }
+
+
+ glGetIntegerv(GL_PIXEL_MAP_R_TO_R_SIZE, &size);
+ if (size != max) {
+ REPORT_FAILURE("glPixelMap failed");
+ return false;
+ }
+ glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
+
+ // Read back pixel map
+ if (usePackBuffer) {
+ glGenBuffersARB(1, pb_pack);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pb_pack[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, max * sizeof(GLushort),
+ NULL, GL_STREAM_DRAW);
+ glGetPixelMapusv(GL_PIXEL_MAP_R_TO_R, NULL);
+ pboMem = (GLushort *) glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ }
+ else {
+ pboMem = (GLushort *) malloc(sizeof(GLushort) * max);
+ glGetPixelMapusv(GL_PIXEL_MAP_R_TO_R, pboMem);
+ }
+
+ for (i = 0; i < max; i++) {
+ if (pboMem[i] != (255 - i)) {
+ REPORT_FAILURE("get PixelMap failed");
+ return false;
+ }
+ }
+
+
+ if (usePackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_EXT);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_pack);
+ }
+ else {
+ free(pboMem);
+ }
+
+ if (useUnpackBuffer) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_unpack);
+ }
+
+ }
+ }
+
+ return true;
+}
+
+bool PBOTest::testBitmap(void)
+{
+ GLuint pb_unpack[1];
+ GLuint pb_pack[1];
+ int useUnpackBuffer = usePBO;
+ int usePackBuffer = 0;
+ GLubyte bitmap[TEXSIZE * TEXSIZE / 8];
+ GLfloat buf[windowSize * windowSize * 3];
+ GLfloat white[3] = { 1.0, 1.0, 1.0 };
+ GLfloat black[3] = { 0.0, 0.0, 0.0 };
+ int i, j;
+ GLubyte *pboUnpackMem = NULL;
+ GLfloat *pboPackMem = NULL;
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ for (usePackBuffer = 0; usePackBuffer < usePBO + 1; usePackBuffer++) {
+ for (useUnpackBuffer = 0; useUnpackBuffer < usePBO + 1;
+ useUnpackBuffer++) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (useUnpackBuffer) {
+ glGenBuffersARB(1, pb_unpack);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pb_unpack[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, TEXSIZE * TEXSIZE, NULL,
+ GL_STREAM_DRAW);
+ pboUnpackMem = (GLubyte *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboUnpackMem = bitmap;
+ }
+
+ for (i = 0; i < TEXSIZE * TEXSIZE / 8; i++) {
+ pboUnpackMem[i] = 0xAA;
+ }
+
+
+ glColor4f(1.0, 1.0, 1.0, 0.0);
+ glRasterPos2f(0.0, 0.0);
+ if (useUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glBitmap(TEXSIZE, TEXSIZE, 0, 0, 0, 0, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ else
+ glBitmap(TEXSIZE, TEXSIZE, 0, 0, 0, 0, pboUnpackMem);
+
+ // Check the result
+ if (usePackBuffer) {
+ glGenBuffersARB(1, pb_pack);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pb_pack[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, TEXSIZE * TEXSIZE, NULL,
+ GL_STREAM_DRAW);
+ glReadPixels(0, 0, windowSize, windowSize, GL_RGB, GL_FLOAT,
+ NULL);
+ pboPackMem =
+ (GLfloat *) glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ }
+ else {
+ pboPackMem = buf;
+ glReadPixels(0, 0, windowSize, windowSize, GL_RGB, GL_FLOAT,
+ pboPackMem);
+ }
+
+ for (j = 0; j < windowSize; j++) {
+ for (i = 0; i < windowSize; i++) {
+ const GLfloat *exp;
+ if ((i & 1))
+ exp = black;
+ else
+ exp = white;
+ if (i < TEXSIZE && j < TEXSIZE) {
+ if (equalColors(&pboPackMem[(j * windowSize + i) * 3], exp)
+ != true) {
+ REPORT_FAILURE("glBitmap failed");
+ printf(" got (%d, %d) = [%f, %f, %f], ", i, j,
+ pboPackMem[(j * windowSize + i) * 3],
+ pboPackMem[(j * windowSize + i) * 3 + 1],
+ pboPackMem[(j * windowSize + i) * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ exp[0], exp[1], exp[2]);
+
+ return false;
+ }
+ }
+ else {
+ if (equalColors
+ (&pboPackMem[(j * windowSize + i) * 3],
+ black) != true) {
+ REPORT_FAILURE("glBitmap failed");
+ printf("(%d, %d) = [%f, %f, %f], ", i, j,
+ pboPackMem[(j * windowSize + i) * 3],
+ pboPackMem[(j * windowSize + i) * 3 + 1],
+ pboPackMem[(j * windowSize + i) * 3 + 2]);
+ printf("should be [0.0, 0.0, 0.0]\n");
+ return false;
+ }
+
+ }
+ }
+ }
+ if (usePackBuffer) {
+ glUnmapBuffer(GL_PIXEL_PACK_BUFFER_EXT);
+ glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_pack);
+ }
+
+ if (useUnpackBuffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pb_unpack);
+ }
+ }
+ }
+ return true;
+}
+
+
+bool PBOTest::testTexImage(void)
+{
+ int breakCOWPBO, breakCOWTexture;
+ int useTexUnpackBuffer, useTexPackBuffer;
+ GLuint unpack_pb[1];
+ GLuint pack_pb[1];
+ GLfloat t1[TEXSIZE * TEXSIZE * 3];
+ GLfloat t2[TEXSIZE * TEXSIZE * 3];
+ GLfloat *pboMem = NULL;
+ int i, j;
+ GLfloat green[3] = { 1.0, 1.0, 0.0 };
+ GLfloat black[3] = { 0.0, 0.0, 0.0 };
+ GLfloat buf[windowSize * windowSize * 3];
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (useTexPackBuffer = 0; useTexPackBuffer < usePBO + 1;
+ useTexPackBuffer++) {
+ for (useTexUnpackBuffer = 0; useTexUnpackBuffer < usePBO + 1;
+ useTexUnpackBuffer++) {
+ for (breakCOWPBO = 0; breakCOWPBO < useTexUnpackBuffer + 1;
+ breakCOWPBO++) {
+ for (breakCOWTexture = 0;
+ breakCOWTexture < useTexUnpackBuffer + 1;
+ breakCOWTexture++) {
+ if (useTexUnpackBuffer) {
+ glGenBuffersARB(1, unpack_pb);
+ if (glIsBufferARB(unpack_pb[0]) == false)
+ return false;
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, unpack_pb[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ TEXSIZE * TEXSIZE * 3 * sizeof(GLfloat), NULL,
+ GL_STREAM_DRAW);
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+
+ if (useTexUnpackBuffer) {
+ pboMem =
+ (GLfloat *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = t1;
+ }
+
+ for (i = 0; i < TEXSIZE * TEXSIZE; i++) {
+ pboMem[3 * i] = 1.0;
+ pboMem[3 * i + 1] = 1.0;
+ pboMem[3 * i + 2] = 0.0;
+ }
+
+ if (useTexUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0,
+ GL_RGB, GL_FLOAT, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ else
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0,
+ GL_RGB, GL_FLOAT, pboMem);
+
+ if (useTexUnpackBuffer) {
+ if (breakCOWPBO) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, unpack_pb[0]);
+ pboMem =
+ (GLfloat *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ for (i = 0; i < TEXSIZE * TEXSIZE * 3; i++)
+ pboMem[i] = 0.2;
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ }
+
+ if (useTexUnpackBuffer) {
+ if (breakCOWTexture) {
+ GLfloat temp[1 * 1 * 3];
+ for (i = 0; i < 1 * 1 * 3; i++)
+ temp[i] = 0.8;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB,
+ GL_FLOAT, temp);
+ }
+ }
+
+ // Check PBO's content
+ if (useTexUnpackBuffer) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, unpack_pb[0]);
+ pboMem = (GLfloat *) glMapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ if (breakCOWPBO) {
+ for (i = 0; i < TEXSIZE * TEXSIZE * 3; i++)
+ if (fabsf(pboMem[i] - 0.2) > tolerance[0]) {
+ REPORT_FAILURE
+ ("PBO modified by someone else, there must be something wrong");
+ return false;
+ }
+ }
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+
+
+ // Read texture back
+ if (useTexPackBuffer) {
+ glGenBuffersARB(1, pack_pb);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pack_pb[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT,
+ TEXSIZE * TEXSIZE * 3 * sizeof(GLfloat), NULL,
+ GL_STREAM_DRAW);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_FLOAT, NULL);
+ pboMem = (GLfloat *) glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ }
+ else {
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_FLOAT, t2);
+ pboMem = t2;
+ }
+
+ // Check texture image
+ for (i = 0; i < TEXSIZE * TEXSIZE; i++) {
+ if (i == 0 && breakCOWTexture && useTexUnpackBuffer) {
+ GLfloat exp[3] = { 0.8, 0.8, 0.8 };
+ if (equalColors(&pboMem[i * 3], exp) != true) {
+ REPORT_FAILURE("glGetTexImage failed");
+ printf(" got (%d) = [%f, %f, %f], ", i,
+ pboMem[i * 3],
+ pboMem[i * 3 + 1], pboMem[i * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ exp[0], exp[1], exp[2]);
+
+ return false;
+ }
+ }
+ else {
+ GLfloat exp[3] = { 1.0, 1.0, 0.0 };
+ if (equalColors(&pboMem[i * 3], exp) != true) {
+ REPORT_FAILURE("glGetTexImage failed");
+ printf(" got (%d) = [%f, %f, %f], ", i,
+ pboMem[i * 3],
+ pboMem[i * 3 + 1], pboMem[i * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ exp[0], exp[1], exp[2]);
+
+ return false;
+ }
+ }
+ }
+
+ if (useTexPackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_EXT);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pack_pb);
+ }
+ if (useTexUnpackBuffer) {
+ glDeleteBuffersARB(1, unpack_pb);
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ glBegin(GL_POLYGON);
+ glTexCoord2f(0, 0);
+ glVertex2f(0, 0);
+ glTexCoord2f(1, 0);
+ glVertex2f(TEXSIZE, 0);
+ glTexCoord2f(1, 1);
+ glVertex2f(TEXSIZE, TEXSIZE);
+ glTexCoord2f(0, 1);
+ glVertex2f(0, TEXSIZE);
+ glEnd();
+
+ glReadPixels(0, 0, windowSize, windowSize, GL_RGB, GL_FLOAT,
+ buf);
+ for (j = 0; j < windowSize; j++) {
+ for (i = 0; i < windowSize; i++) {
+ if (i == 0 && j == 0 && breakCOWTexture
+ && useTexUnpackBuffer) {
+ GLfloat exp[3] = { 0.8, 0.8, 0.8 };
+ if (equalColors(&buf[(j * windowSize + i) * 3], exp)
+ != true) {
+ REPORT_FAILURE("glTexImage failed");
+ printf(" got (%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ exp[0], exp[1], exp[2]);
+
+ return false;
+ }
+ }
+ else if (i < TEXSIZE && j < TEXSIZE) {
+ if (equalColors(&buf[(j * windowSize + i) * 3], green)
+ != true) {
+ REPORT_FAILURE("glTexImage failed");
+ printf(" got (%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ green[0], green[1], green[2]);
+
+ return false;
+ }
+ }
+ else {
+ if (equalColors(&buf[(j * windowSize + i) * 3], black)
+ != true) {
+ REPORT_FAILURE("glTexImage failed");
+ printf("(%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [0.0, 0.0, 0.0]\n");
+
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool PBOTest::testTexSubImage(void)
+{
+ GLuint pbs[1];
+ GLfloat t[TEXSIZE * TEXSIZE * 3];
+ int i, j;
+ int useUnpackBuffer = 0;
+ GLfloat green[3] = { 0.0, 1.0, 0.0 };
+ GLfloat black[3] = { 0.0, 0.0, 0.0 };
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ for (useUnpackBuffer = 0; useUnpackBuffer < usePBO + 1; useUnpackBuffer++) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (useUnpackBuffer) {
+ glGenBuffersARB(1, pbs);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pbs[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, TEXSIZE * TEXSIZE * 3 * sizeof(GLfloat),
+ NULL, GL_STREAM_DRAW);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEXSIZE, TEXSIZE, 0, GL_RGB,
+ GL_FLOAT, NULL);
+
+ GLfloat *pboMem = NULL;
+ if (useUnpackBuffer) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, pbs[0]);
+ pboMem = (GLfloat *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = t;
+ }
+
+ for (i = 0; i < TEXSIZE * TEXSIZE; i++) {
+ pboMem[3 * i] = 0.0;
+ pboMem[3 * i + 1] = 1.0;
+ pboMem[3 * i + 2] = 0.0;
+ }
+
+ if (useUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEXSIZE, TEXSIZE, GL_RGB,
+ GL_FLOAT, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ }
+ else
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEXSIZE, TEXSIZE, GL_RGB,
+ GL_FLOAT, pboMem);
+
+ glEnable(GL_TEXTURE_2D);
+ glBegin(GL_POLYGON);
+ glTexCoord2f(0, 0);
+ glVertex2f(0, 0);
+ glTexCoord2f(1, 0);
+ glVertex2f(10, 0);
+ glTexCoord2f(1, 1);
+ glVertex2f(10, 10);
+ glTexCoord2f(0, 1);
+ glVertex2f(0, 10);
+ glEnd();
+ glDisable(GL_TEXTURE_2D);
+
+ GLfloat buf[windowSize * windowSize * 3];
+
+ glReadPixels(0, 0, windowSize, windowSize, GL_RGB, GL_FLOAT, buf);
+ for (j = 0; j < windowSize; j++) {
+ for (i = 0; i < windowSize; i++) {
+ if (i < 10 && j < 10) {
+ if (equalColors(&buf[(j * windowSize + i) * 3], green) != true) {
+ REPORT_FAILURE("glTexSubImage failed");
+ printf(" got (%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [%f, %f, %f]\n",
+ green[0], green[1], green[2]);
+
+ return false;
+ }
+ }
+ else {
+ if (equalColors(&buf[(j * windowSize + i) * 3], black) != true) {
+ REPORT_FAILURE("glTexSubImage failed");
+ printf("(%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [0.0, 0.0, 0.0]\n");
+
+ return false;
+ }
+
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool PBOTest::testPolygonStip(void)
+{
+ int useUnpackBuffer = 0;
+ int usePackBuffer = 0;
+ GLuint unpack_pb[1];
+ GLuint pack_pb[1];
+ GLubyte t1[32 * 32 / 8];
+ GLubyte t2[32 * 32 / 8];
+ GLubyte *pboMem = NULL;
+ int i, j;
+ GLfloat white[3] = { 1.0, 1.0, 1.0 };
+ GLfloat black[3] = { 0.0, 0.0, 0.0 };
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ for (useUnpackBuffer = 0; useUnpackBuffer < usePBO + 1; useUnpackBuffer++) {
+ for (usePackBuffer = 0; usePackBuffer < usePBO + 1; usePackBuffer++) {
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (useUnpackBuffer) {
+ glGenBuffersARB(1, unpack_pb);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, unpack_pb[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, 32 * 32 / 8, NULL,
+ GL_STREAM_DRAW);
+ pboMem = (GLubyte *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = t1;
+ }
+
+
+ // Fill in the stipple pattern
+ for (i = 0; i < 32 * 32 / 8; i++) {
+ pboMem[i] = 0xAA;
+ }
+
+ if (useUnpackBuffer) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glPolygonStipple(NULL);
+ }
+ else {
+ glPolygonStipple(pboMem);
+ }
+
+ // Read back the stipple pattern
+ if (usePackBuffer) {
+ glGenBuffersARB(1, pack_pb);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, pack_pb[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, 32 * 32 / 8, NULL,
+ GL_STREAM_DRAW);
+ glGetPolygonStipple(NULL);
+ pboMem = (GLubyte *) glMapBufferARB(GL_PIXEL_PACK_BUFFER_EXT,
+ GL_READ_ONLY);
+ }
+ else {
+ glGetPolygonStipple(t2);
+ pboMem = t2;
+ }
+
+ for (i = 0; i < 32 * 32 / 8; i++) {
+ if (pboMem[i] != 0xAA) {
+ REPORT_FAILURE("glGetPolygonStipple failed");
+ return false;
+ }
+ }
+
+
+ if (useUnpackBuffer) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, unpack_pb);
+ }
+ if (usePackBuffer) {
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+ glDeleteBuffersARB(1, pack_pb);
+ }
+
+ glEnable(GL_POLYGON_STIPPLE);
+ glBegin(GL_POLYGON);
+ glVertex2f(0, 0);
+ glVertex2f(10, 0);
+ glVertex2f(10, 10);
+ glVertex2f(0, 10);
+ glEnd();
+
+ glDisable(GL_POLYGON_STIPPLE);
+
+ // Check the result
+ GLfloat buf[windowSize * windowSize * 3];
+
+ glReadPixels(0, 0, windowSize, windowSize, GL_RGB, GL_FLOAT, buf);
+
+ for (j = 0; j < windowSize; j++) {
+ for (i = 0; i < windowSize; i++) {
+ const GLfloat *exp;
+ if (i & 1)
+ exp = black;
+ else
+ exp = white;
+ if (i < 10 && j < 10) {
+ if (equalColors(&buf[(j * windowSize + i) * 3], exp) !=
+ true) {
+ REPORT_FAILURE("glGetPolygonStipple failed");
+ printf("(%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [1.0, 1.0, 1.0]\n");
+ return false;
+ }
+ }
+ else {
+ if (equalColors(&buf[(j * windowSize + i) * 3], black) !=
+ true) {
+ REPORT_FAILURE("glGetPolygonStipple failed");
+ printf("(%d, %d) = [%f, %f, %f], ", i, j,
+ buf[(j * windowSize + i) * 3],
+ buf[(j * windowSize + i) * 3 + 1],
+ buf[(j * windowSize + i) * 3 + 2]);
+ printf("should be [0.0, 0.0, 0.0]\n");
+ return false;
+ }
+
+ }
+ }
+ }
+
+ }
+ }
+
+ return true;
+}
+
+
+bool PBOTest::testErrorHandling(void)
+{
+ GLuint fbs[1];
+
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER_EXT, 0);
+
+ if (usePBO) {
+ //handle exceed memory size
+ glGenBuffersARB(1, fbs);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, fbs[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, 32 * 32 * 4, NULL,
+ GL_STREAM_DRAW);
+ glDrawPixels(32, 32 + 1, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ if (glGetError() != GL_INVALID_OPERATION)
+ return false;
+
+ glDeleteBuffersARB(1, fbs);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, 0);
+
+ glGenBuffersARB(1, fbs);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER, fbs[0]);
+ glBufferDataARB(GL_PIXEL_PACK_BUFFER_EXT, 32 * 32 * 4, NULL,
+ GL_STREAM_DRAW);
+ glReadPixels(0, 0, 32, 32 + 1, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ if (glGetError() != GL_INVALID_OPERATION)
+ return false;
+
+ glDeleteBuffersARB(1, fbs);
+ glBindBufferARB(GL_PIXEL_PACK_BUFFER, 0);
+ }
+ return true;
+}
+
+bool PBOTest::testFunctionality(MultiTestResult & r)
+{
+ static SubTestFunc
+ funcs[] = {
+ &GLEAN::PBOTest::testSanity,
+ &GLEAN::PBOTest::testBitmap,
+ &GLEAN::PBOTest::testDrawPixels,
+ &GLEAN::PBOTest::testPixelMap,
+ &GLEAN::PBOTest::testTexImage,
+ &GLEAN::PBOTest::testTexSubImage,
+ &GLEAN::PBOTest::testPolygonStip,
+ &GLEAN::PBOTest::testErrorHandling,
+ NULL
+ };
+
+ for (int i = 0; funcs[i]; i++)
+ if ((this->*funcs[i]) ())
+ r.numPassed++;
+ else
+ r.numFailed++;
+ return true;
+}
+
+enum {
+ BLACK,
+ RED,
+ GREEN,
+ BLUE,
+WHITE };
+
+GLfloat colors1[][4] = {
+ {0.0, 0.0, 0.0, 0.0},
+ {1.0, 0.0, 0.0, 1.0},
+ {0.0, 1.0, 0.0, 1.0},
+ {0.0, 0.0, 1.0, 1.0},
+ {1.0, 1.0, 1.0, 1.0}
+};
+
+#define TEXSIZE1 64
+bool PBOTest::testPerformance(MultiTestResult & r)
+{
+ GLuint pbs[1];
+ GLuint textures[1];
+ GLubyte data[TEXSIZE1 * TEXSIZE1 * 4];
+ int mode;
+ int i, j;
+ Timer t;
+ double t0, t1, perf[2];
+ GLubyte *pboMem = NULL;
+
+ (void) r;
+
+ for (mode = 0; mode < usePBO + 1; mode++) {
+ t0 = t.getClock();
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ if (mode) {
+ glGenBuffersARB(1, pbs);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, pbs[0]);
+ glBufferDataARB(GL_PIXEL_UNPACK_BUFFER,
+ TEXSIZE1 * TEXSIZE1 * 4 * sizeof(GLubyte), NULL,
+ GL_STREAM_DRAW);
+ }
+ glGenTextures(1, textures);
+ glBindTexture(GL_TEXTURE_2D, textures[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, TEXSIZE1,
+ TEXSIZE1, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ for (i = 0; i < 1024; i++) {
+ if (mode) {
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, pbs[0]);
+ pboMem =
+ (GLubyte *) glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT,
+ GL_WRITE_ONLY);
+ }
+ else {
+ pboMem = data;
+ }
+
+ for (j = 0; j < TEXSIZE1 * TEXSIZE1; j++) {
+ pboMem[4 * j] = 255;
+ pboMem[4 * j + 1] = 255;
+ pboMem[4 * j + 2] = 0;
+ pboMem[4 * j + 3] = 0;
+ }
+
+ if (mode) {
+ glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEXSIZE1,
+ TEXSIZE1, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+ else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEXSIZE1,
+ TEXSIZE1, GL_BGRA, GL_UNSIGNED_BYTE, data);
+ }
+
+
+ // Actually apply the texture
+ glEnable(GL_TEXTURE_2D);
+ glColor4fv(colors1[WHITE]);
+
+ glBegin(GL_POLYGON);
+ glTexCoord2f(0.0, 0.0);
+ glVertex2f(0, 0);
+ glTexCoord2f(1.0, 0.0);
+ glVertex2f(1, 0);
+ glTexCoord2f(1.0, 1.0);
+ glVertex2f(1, 1);
+ glTexCoord2f(0.0, 1.0);
+ glVertex2f(0, 1);
+ glEnd();
+ glFlush();
+ glDisable(GL_TEXTURE_2D);
+ }
+ t1 = t.getClock();
+ glDeleteTextures(1, textures);
+ if (mode)
+ glDeleteBuffersARB(1, pbs);
+
+ perf[mode] = (double) TEXSIZE1 * TEXSIZE1 * 3 * sizeof(GLfloat) / 1024 / (t1 - t0);
+
+ }
+
+ if (perf[1] < perf[0] && usePBO) {
+ env->log << name << ": NOTE "
+ << "perf[0] = " << perf[0] <<
+ " MB/s, which is in normal mode" << endl;
+ env->log << name << ": NOTE " << "perf[1] = " <<
+ perf[1] << " MB/s, which is using PBO" << endl;
+ }
+
+ return true;
+}
+
+
+
+// Run all the subtests, incrementing numPassed, numFailed
+void
+PBOTest::runSubTests(MultiTestResult & r)
+{
+ static TestFunc funcs[] = {
+ &GLEAN::PBOTest::testFunctionality,
+ &GLEAN::PBOTest::testPerformance,
+ NULL
+ };
+
+ for (int i = 0; funcs[i]; i++)
+ if ((this->*funcs[i]) (r))
+ r.numPassed++;
+ else
+ r.numFailed++;
+}
+
+
+void
+PBOTest::runOne(MultiTestResult & r, Window & w)
+{
+ (void) w;
+
+ if (!setup()) {
+ r.pass = false;
+ return;
+ }
+
+ runSubTests(r);
+
+ r.pass = (r.numFailed == 0);
+}
+
+
+// The test object itself:
+PBOTest pboTest("pbo", "window, rgb, z", "", // no extension filter
+ "pbo test: Test OpenGL Extension GL_ARB_pixel_buffer_object\n");
+
+
+
+} // namespace GLEAN
diff --git a/tests/glean/tpbo.h b/tests/glean/tpbo.h new file mode 100644 index 000000000..46321bf6a --- /dev/null +++ b/tests/glean/tpbo.h @@ -0,0 +1,86 @@ +// BEGIN_COPYRIGHT -*- glean -*-
+//
+// Copyrigth (C) 2007 Intel Corporation
+// Copyright (C) 1999 Allen Akin All Rights Reserved.
+//
+// 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 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 ALLEN AKIN 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.
+//
+// END_COPYRIGHT
+//
+// Authors:
+// Shuang He <shuang.he@intel.com>
+//
+// tfbo.h: Test OpenGL Extension GL_ARB_pixel_buffer_objec
+
+#ifndef __pfbo_h__
+#define __pfbo_h__
+
+#include "tmultitest.h"
+
+namespace GLEAN {
+
+#define windowSize 100
+
+
+class PBOTest: public MultiTest
+{
+public:
+ PBOTest(const char* testName, const char* filter,
+ const char *extensions, const char* description):
+ MultiTest(testName, filter, extensions, description)
+ {
+ }
+
+ virtual void runOne(MultiTestResult &r, Window &w);
+
+private:
+ typedef bool (PBOTest::*TestFunc)(MultiTestResult &r);
+ typedef bool (PBOTest::*SubTestFunc)(void);
+
+ GLfloat tolerance[5];
+
+ bool testFunctionality(MultiTestResult &r);
+ bool testPerformance(MultiTestResult &r);
+ bool testSanity(void);
+ bool testErrorHandling(void);
+ bool testDrawPixels(void);
+ bool testPixelMap(void);
+ bool testBitmap(void);
+ bool testTexImage(void);
+ bool testTexSubImage(void);
+ bool testPolygonStip(void);
+
+ void runSubTests(MultiTestResult &r);
+
+ bool setup(void);
+ bool checkResult(const GLfloat exp[4], const int depth, const int stencil) const;
+ bool equalColors(const GLfloat a[4], const GLfloat b[4]) const;
+ bool equalColors1(const GLubyte a[4], const GLubyte b[4]) const;
+
+ void reportFailure(const char *msg, int line) const;
+ void reportFailure(const char *msg, GLenum target, int line) const;
+};
+
+} // namespace GLEAN
+
+#endif // __tpbo_h__
diff --git a/tests/glean/tpointatten.cpp b/tests/glean/tpointatten.cpp index 879197806..086f8974f 100644 --- a/tests/glean/tpointatten.cpp +++ b/tests/glean/tpointatten.cpp @@ -63,7 +63,7 @@ PointAttenuationTest::setup(void) glGetFloatv(GL_SMOOTH_POINT_SIZE_RANGE, smoothLimits); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 10.0); + glOrtho(-10.0, 10.0, -10.0, 10.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } @@ -120,20 +120,22 @@ PointAttenuationTest::expectedSize(GLfloat initSize, size = CLAMP(size, smoothLimits[0], smoothLimits[1]); else size = CLAMP(size, aliasedLimits[0], aliasedLimits[1]); - return size; } -// measure size of rendered point +// measure size of rendered point at yPos (in model coords) GLfloat -PointAttenuationTest::measureSize() const +PointAttenuationTest::measureSize(GLfloat yPos) const { + assert(yPos >= -10.0); + assert(yPos <= 10.0); + float yNdc = (yPos + 10.0) / 20.0; // See glOrtho above int x = 0; - int y = windowSize / 2; - int w = windowSize; + int y = (int) (yNdc * windowHeight); + int w = windowWidth; int h = 1; - GLfloat image[windowSize * 3]; + GLfloat image[windowWidth * 3]; // Read row of pixels and add up colors, which should be white // or shades of gray if smoothing is enabled. glReadPixels(x, y, w, h, GL_RGB, GL_FLOAT, image); @@ -182,16 +184,22 @@ PointAttenuationTest::testPointRendering(GLboolean smooth) PointParameterfARB(GL_POINT_SIZE_MAX_ARB, max); for (float size = 1.0; size < MAX_SIZE; size += 4) { glPointSize(size); - for (float z = -8.0; z <= 8.0; z += 1.0) { - glClear(GL_COLOR_BUFFER_BIT); - glBegin(GL_POINTS); - glVertex3f(0, 0, z); - glEnd(); + + // draw column of points + glClear(GL_COLOR_BUFFER_BIT); + glBegin(GL_POINTS); + for (float z = -6.0; z <= 6.0; z += 1.0) { + glVertex3f(0, z, z); + } + glEnd(); + + // test the column of points + for (float z = -6.0; z <= 6.0; z += 1.0) { count++; float expected = expectedSize(size, atten, min, max, z, smooth); - float actual = measureSize(); + float actual = measureSize(z); if (fabs(expected - actual) > epsilon) { reportFailure(size, atten, min, max, z, smooth, @@ -243,8 +251,8 @@ PointAttenuationTest::PointAttenuationTest(const char *testName, const char *description) : BasicTest(testName, filter, extensions, description) { - fWidth = windowSize; - fHeight = windowSize; + fWidth = windowWidth; + fHeight = windowHeight; } diff --git a/tests/glean/tpointatten.h b/tests/glean/tpointatten.h index 4ee032d53..459220261 100644 --- a/tests/glean/tpointatten.h +++ b/tests/glean/tpointatten.h @@ -36,8 +36,8 @@ namespace GLEAN { -#define drawingSize 101 // yes, odd -#define windowSize (drawingSize + 2) +#define windowWidth 100 +#define windowHeight 503 // yes, odd class PointAttenuationTest: public BasicTest @@ -69,7 +69,7 @@ private: const GLfloat attenuation[3], GLfloat min, GLfloat max, GLfloat eyeZ, GLboolean smooth) const; - GLfloat measureSize() const; + GLfloat measureSize(GLfloat yPos) const; }; } // namespace GLEAN diff --git a/tests/glean/tpointsprite.cpp b/tests/glean/tpointsprite.cpp new file mode 100644 index 000000000..47cff89ae --- /dev/null +++ b/tests/glean/tpointsprite.cpp @@ -0,0 +1,428 @@ +// BEGIN_COPYRIGHT -*- glean -*- + +/* + * Copyright (C) 2007 Intel Corporation + * Copyright (C) 1999 Allen Akin All Rights Reserved. + * + * 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 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 ALLEN AKIN 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. + * + */ + +/* tpointsprite.cpp: Test the ARB_point_sprite extension + * Author: Nian Wu <nian.wu@intel.com> + * + * Test procedure: + * Create mipmap textures which size varies from 32x32 to 1x1, every texture + * has different two colors: the upper half is one color and the lower half + * is another color. + * Draw point and polygon which mode is GL_POINT, and check that the point + * is rendered correctly. + */ + +#define GL_GLEXT_PROTOTYPES +#include "tpointsprite.h" +#include <cassert> +#include <cmath> +#include <stdio.h> + +namespace GLEAN { + +//background color +static GLfloat bgColor[4] = {0.0, 0.0, 0.0, 0.0}; + +//mipmap texture's color, every texture partite to upper and lower part that +//has different colors +//for 1x1 texture, only lower part is used +static GLfloat texColor[6][2][4] = { + {{1.0, 0.0, 0.0, 1.0}, {0.0, 1.0, 0.0, 1.0}}, // 32x32 + {{0.0, 0.0, 1.0, 1.0}, {1.0, 1.0, 0.0, 1.0}}, // 16x16 + {{1.0, 0.0, 1.0, 1.0}, {0.0, 1.0, 1.0, 1.0}}, // 8x8 + {{1.0, 1.0, 1.0, 1.0}, {1.0, 0.0, 0.0, 1.0}}, // 4x4 + {{0.0, 1.0, 0.0, 1.0}, {0.0, 0.0, 1.0, 1.0}}, // 2x2 + {{1.0, 1.0, 0.0, 1.0}, {1.0, 1.0, 1.0, 1.0}}, // 1x1 +}; + +//generate mipmap +void +PointSpriteTest::GenMipmap() +{ + int level, i, j; + GLint texWidth; + GLfloat *texPtr; + GLfloat *upperColor, *lowColor; + + for (level = 0; level < 6; level++) + { + texWidth = 1 << (6 - level - 1); + texImages[level] = (GLfloat *)malloc(texWidth * texWidth * 4 * sizeof(GLfloat)); + texPtr = texImages[level]; + upperColor = texColor[level][0]; + lowColor = texColor[level][1]; + + for (i = 0; i < texWidth; i++) + { + for (j = 0; j < texWidth; j++) + { + if (i < texWidth / 2) //lower part + { + *texPtr++ = lowColor[0]; + *texPtr++ = lowColor[1]; + *texPtr++ = lowColor[2]; + *texPtr++ = lowColor[3]; + } else { //upper part + *texPtr++ = upperColor[0]; + *texPtr++ = upperColor[1]; + *texPtr++ = upperColor[2]; + *texPtr++ = upperColor[3]; + } + } + } + } +} + +//enable texture and setup mipmap +void +PointSpriteTest::SetupMipmap(GLuint *texID) +{ + glEnable(GL_TEXTURE_2D); + + glGenTextures(1, texID); + glBindTexture(GL_TEXTURE_2D, *texID); + + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, + GL_RGBA, GL_FLOAT, texImages[0]); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_FLOAT, texImages[1]); + glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 8, 8, 0, + GL_RGBA, GL_FLOAT, texImages[2]); + glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_FLOAT, texImages[3]); + glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA, 2, 2, 0, + GL_RGBA, GL_FLOAT, texImages[4]); + glTexImage2D(GL_TEXTURE_2D, 5, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_FLOAT, texImages[5]); + + glTexEnvf(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE); +} + +void +PointSpriteTest::CheckDefaultState(MultiTestResult &r) +{ + GLboolean enable; + GLint coordReplace; + GLint coordOrigin; + + // check point sprite status, default is GL_FALSE + enable = glIsEnabled(GL_POINT_SPRITE_ARB); + if (enable != GL_FALSE) + { + env->log << name << "subcase FAIL: " + << "PointSprite should be disabled defaultlly\n"; + r.numFailed++; + } else { + r.numPassed++; + } + + // check coordinate replacement, default is GL_FALSE + glGetTexEnviv(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, &coordReplace); + + if (coordReplace != GL_FALSE) + { + env->log << name << "subcase FAIL: " + << "default value of COORD_REPLACE should be GL_FALSE\n"; + r.numFailed++; + } else { + r.numPassed++; + } + + // check coordinate origin, default is UPPER_LEFT + glEnable(GL_POINT_SPRITE); + glGetIntegerv(GL_POINT_SPRITE_COORD_ORIGIN, &coordOrigin); + if (coordOrigin != GL_UPPER_LEFT) + { + env->log << name << "subcase FAIL: " + << "defult value of COORD_ORIGIN should be GL_UPPER_LEFT\n"; + r.numFailed++; + } else { + r.numPassed++; + } + + glDisable(GL_POINT_SPRITE); +} + +GLboolean +PointSpriteTest::OutOfPoint(int x, int y, int pSize, int x0, int y0) +{ + if ((x < x0) || + (y < y0) || + (x >= x0 + pSize) || + (y >= y0 + pSize)) + return GL_TRUE; + else + return GL_FALSE; +} + +GLfloat * +PointSpriteTest::GetTexColor(int pSize, int dir) +{ + int level; + + // Note: we use GL_NEAREST_MIPMAP_NEAREST for GL_TEXTURE_MIN_FILTER + if (pSize <= 1) level = 5; + else if (pSize < 3) level = 4; + else if (pSize < 6) level = 3; + else if (pSize < 12) level = 2; + else if (pSize < 24) level = 1; + else level = 0; + + return texColor[level][dir]; +} + +void +PointSpriteTest::CalculateTolerance() +{ + GLint rBits, gBits, bBits; + GLint rTexBits, gTexBits, bTexBits; + + // Get fb resolution + glGetIntegerv(GL_RED_BITS, &rBits); + glGetIntegerv(GL_GREEN_BITS, &gBits); + glGetIntegerv(GL_BLUE_BITS, &bBits); + + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_RED_SIZE, &rTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_GREEN_SIZE, &gTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_BLUE_SIZE, &bTexBits); + + // Find smaller of frame buffer and texture bits + rBits = (rBits < rTexBits) ? rBits : rTexBits; + gBits = (gBits < gTexBits) ? gBits : gTexBits; + bBits = (bBits < bTexBits) ? bBits : bTexBits; + + mTolerance[0] = 3.0 / (1 << rBits); + mTolerance[1] = 3.0 / (1 << gBits); + mTolerance[2] = 3.0 / (1 << bBits); +} + +//Test if two colors are colse enough to be considered the same. +GLboolean +PointSpriteTest::CompareColor(GLfloat *actual, GLfloat *expected) +{ + return (fabs(actual[0] - expected[0]) <= mTolerance[0] && + fabs(actual[1] - expected[1]) <= mTolerance[1] && + fabs(actual[2] - expected[2]) <= mTolerance[2] ); +} + + +static void +FindNonBlack(const GLfloat *buf, GLint w, GLint h, GLint *x0, GLint *y0) +{ + GLint i, j; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + int k = (i * w + j) * 3; + if (buf[k+0] != bgColor[0] || + buf[k+1] != bgColor[1] || + buf[k+2] != bgColor[2]) { + *x0 = j; + *y0 = i; + return; + } + } + } + abort(); +} + + +/** + * compare pixels located at (0,0) to (WINSIZE/2, WINSIZE/2). + * @param buf: pixels' RGB value + * @param pSize: point size + * @param coordOrigin: coordinate origin--UPPER_LEFT or LOWER_LEFT + */ +GLboolean +PointSpriteTest::ComparePixels(GLfloat *buf, int pSize, int coordOrigin) +{ + GLfloat *lowerColor, *upperColor, *expectedColor; + GLint i, j; + GLint x0, y0; + + lowerColor = GetTexColor(pSize, coordOrigin ? 0 : 1); + upperColor = GetTexColor(pSize, coordOrigin ? 1 : 0); + + // Find first (lower-left) pixel that's not black. + // The pixels hit by sprite rasterization may vary from one GL to + // another so try to compensate for that. + FindNonBlack(buf, WINSIZE/2, WINSIZE/2, &x0, &y0); + + for (i = 0; i < WINSIZE / 2; i++) + { + for (j = 0; j < WINSIZE / 2; j++) + { + if (OutOfPoint(i, j, pSize, x0, y0)) + { //pixel (i, j) is out of point + //its color should bebackground + if (!CompareColor(buf, bgColor)) + { + env->log << "Incorrect pixel at (" << i << ", " << j << "):\n" + <<"\tit should be backgound color: (" + << bgColor[0] << ", " << bgColor[1] << ", " << bgColor[2] + << "), actual read: (" << buf[0] << ", " << buf[1] << ", " << buf[2] << ")\n" ; + return GL_FALSE; + } + } else { //inside point + if (i - x0 < pSize/2) + expectedColor = lowerColor; + else + expectedColor = upperColor; + + if (!CompareColor(buf, expectedColor)) + { + env->log << "Incorrect pixel at (" << i << ", " << j << "):\n" + <<"\tit should be rendered with color: (" + << expectedColor[0] << ", " << expectedColor[1] << ", " << expectedColor[2] + << "), actual read: (" << buf[0] << ", " << buf[1] << ", " << buf[2] << ")\n" ; + return GL_FALSE; + } + } + buf += 3; + } + } + + return GL_TRUE; +} + +// Test default state. +// Test point and polygon which mode is GL_POINT, and texture's coordinate +// origin is UPPER_LEFT or LOWER_LEFT. +// Result will indicate number of passes and failures. +void +PointSpriteTest::runOne(MultiTestResult &r, Window &w) +{ + GLfloat maxPointSize, pointSize; + GLint expectedSize; + GLint primType, coordOrigin; + GLfloat *buf; + GLuint texID; + int i; + + (void) w; + + CheckDefaultState(r); + + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + glClearColor(bgColor[0], bgColor[1], bgColor[2], bgColor[3]); + + glViewport(0, 0, WINSIZE, WINSIZE); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, WINSIZE, 0, WINSIZE, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + GenMipmap(); + SetupMipmap(&texID); + CalculateTolerance(); + + buf = (GLfloat *)malloc(3 * WINSIZE * WINSIZE / 4 * sizeof(GLfloat)); + + // enable point_sprite_ARB + glEnable(GL_POINT_SPRITE_ARB); + + glGetFloatv(GL_POINT_SIZE_MAX_ARB, &maxPointSize); + if (maxPointSize > WINSIZE / 2) + maxPointSize = WINSIZE / 2; + + //primitive may be point or polygon which mode is GL_POINT + for (primType = 0; primType < 2; primType ++) + { + for (coordOrigin = 0; coordOrigin < 2; coordOrigin++) + { + + if (coordOrigin) + glPointParameterf(GL_POINT_SPRITE_COORD_ORIGIN, GL_UPPER_LEFT); + else + glPointParameterf(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); + + pointSize = 1.85; + for (; pointSize <= maxPointSize; pointSize += 2.0) + { + expectedSize = (int)(pointSize + 0.2); + + glPointSize(pointSize); + glClear(GL_COLOR_BUFFER_BIT); + + if (primType == 0) + { + glBegin(GL_POINTS); + glVertex2i(WINSIZE/4, WINSIZE/4); + glEnd(); + } else { + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + glBegin(GL_POLYGON); + glVertex2i(WINSIZE/4, WINSIZE/4); + glVertex2i(WINSIZE, WINSIZE / 4); + glVertex2i(WINSIZE, WINSIZE); + glVertex2i(WINSIZE / 4, WINSIZE); + glEnd(); + } + + glReadPixels(0, 0, WINSIZE/2, WINSIZE/2, GL_RGB, GL_FLOAT, buf); + + if (!ComparePixels(buf, expectedSize, coordOrigin)) + { + env->log << "\tPrimitive type: " << (primType ? "GL_POLYGON" : "GL_POINTS") << "\n"; + env->log << "\tCoord Origin at: " << (coordOrigin ? "GL_LOWER_LEFT" : "GL_UPPER_LEFT") << "\n"; + env->log << "\tPointSize: " << pointSize << "\n"; + r.numFailed++; + r.numPassed--; + break; + } + } + r.numPassed++; + } + } + + glDeleteTextures(1, &texID); + glDisable(GL_POINT_SPRITE_ARB); + free(buf); + for (i = 0; i < 6; i++) + free(texImages[i]); + + r.pass = (r.numFailed == 0); +} + +// The test object itself: +PointSpriteTest pointSpriteTest("pointSprite", "window, rgb", + "GL_ARB_point_sprite", + "Test basic point sprite functionality.\n"); + +} // namespace GLEAN diff --git a/tests/glean/tpointsprite.h b/tests/glean/tpointsprite.h new file mode 100644 index 000000000..ed04c8f56 --- /dev/null +++ b/tests/glean/tpointsprite.h @@ -0,0 +1,70 @@ +// BEGIN_COPYRIGHT -*- glean -*- + +/* + * Copyright (C) 2007 Intel Coporation All Rights Reserved. + * + * 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 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 ALLEN AKIN 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. + * + */ + +// tpointsprite.h: Test basic ARB_point_sprite support. +// Author: Nian Wu <nian.wu@intel.com> + + +#ifndef __tpointsprite_h__ +#define __tpointsprite_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +#define WINSIZE 80 + +class PointSpriteTest: public MultiTest +{ + public: + PointSpriteTest(const char* testName, const char* filter, + const char *extensions, const char* description): + MultiTest(testName, filter, extensions, description){ + } + + virtual void runOne(MultiTestResult &r, Window &w); + +private: + GLfloat *texImages[6]; + GLfloat mTolerance[3]; + + void GenMipmap(); + void SetupMipmap(GLuint *texID); + void CheckDefaultState(MultiTestResult &r); + void CalculateTolerance(); + GLboolean OutOfPoint(int x, int y, int pSize, int x0, int y0); + GLfloat *GetTexColor(int pSize, int dir); + GLboolean CompareColor(GLfloat *actual, GLfloat *expected); + GLboolean ComparePixels(GLfloat *buf, int pSize, int coordOrigin); +}; // class PointSpriteTest + +} // namespace GLEAN + +#endif // __tpointsprite_h__ + diff --git a/tests/glean/treadpix.cpp b/tests/glean/treadpix.cpp index bc1b3d078..3c146d425 100644 --- a/tests/glean/treadpix.cpp +++ b/tests/glean/treadpix.cpp @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 2001 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT // treadpix.cpp: implementation of ReadPixels tests @@ -269,7 +269,7 @@ ReadPixSanityTest::runOne(ReadPixSanityResult& r, GLEAN::Window& w) { checkDepth(r, w); if (r.config->s) checkStencil(r, w); - + r.pass = r.passRGBA & r.passDepth & r.passStencil & r.passIndex; } // ReadPixSanityTest::runOne @@ -500,7 +500,7 @@ namespace { template<class T> void check(GLEAN::ExactRGBAResult::Flavor& r, GLEAN::DrawingSurfaceConfig& config, - GLenum type, int roundingMode) { + GLenum type) { unsigned size = EXACT_RGBA_WIN_SIZE - 2; unsigned nPixels = size * size; unsigned nComponents = 4 * nPixels; @@ -563,18 +563,15 @@ check(GLEAN::ExactRGBAResult::Flavor& r, GLEAN::DrawingSurfaceConfig& config, hostBits = 32; break; } - T Mask[4]; - - Mask[0] = static_cast<T>(-1) << (hostBits - min(hostBits, config.r)); - Mask[1] = static_cast<T>(-1) << (hostBits - min(hostBits, config.g)); - Mask[2] = static_cast<T>(-1) << (hostBits - min(hostBits, config.b)); - Mask[3] = static_cast<T>(-1) << (hostBits - min(hostBits, config.a)); - + T rMask = static_cast<T>(-1) << (hostBits - min(hostBits, config.r)); + T gMask = static_cast<T>(-1) << (hostBits - min(hostBits, config.g)); + T bMask = static_cast<T>(-1) << (hostBits - min(hostBits, config.b)); + T aMask = static_cast<T>(-1) << (hostBits - min(hostBits, config.a)); // Patch up arithmetic for RGB drawing surfaces. All other nasty cases // are eliminated by the drawing surface filter, which requires // nonzero R, G, and B. - if (config.a == 0) - Mask[3] = 0; + if (aMask == static_cast<T>(-1)) + aMask = 0; // Compare masked actual and expected values, and record the // worst-case error location and magnitude. @@ -584,35 +581,18 @@ check(GLEAN::ExactRGBAResult::Flavor& r, GLEAN::DrawingSurfaceConfig& config, for (y = 0; y < size; ++y) for (x = 0; x < size; ++x) { T e[4]; + e[0] = p[0] & rMask; + e[1] = p[1] & gMask; + e[2] = p[2] & bMask; + e[3] = p[3] & aMask; T a[4]; - if (roundingMode == 1) { - e[0] = p[0]; - e[1] = p[1]; - e[2] = p[2]; - e[3] = p[3]; - a[0] = q[0]; - a[1] = q[1]; - a[2] = q[2]; - a[3] = q[3]; - if (config.a == 0) { - e[3] = a[3] = 0; - } - } else { - e[0] = p[0] & Mask[0]; - e[1] = p[1] & Mask[1]; - e[2] = p[2] & Mask[2]; - e[3] = p[3] & Mask[3]; - a[0] = q[0] & Mask[0]; - a[1] = q[1] & Mask[1]; - a[2] = q[2] & Mask[2]; - a[3] = q[3] & Mask[3]; - } + a[0] = q[0] & rMask; + a[1] = q[1] & gMask; + a[2] = q[2] & bMask; + a[3] = q[3] & aMask; for (unsigned i = 0; i < 4; ++i) { - GLuint err = max(e[i], a[i]) - min(e[i], a[i]); - if (roundingMode == 1) { - if (err < ~Mask[i] / 2) - err = 0; - } + GLuint err = + max(e[i], a[i]) - min(e[i], a[i]); if (err > r.err) { r.x = x; r.y = y; @@ -620,8 +600,6 @@ check(GLEAN::ExactRGBAResult::Flavor& r, GLEAN::DrawingSurfaceConfig& config, for (unsigned j = 0; j < 4; ++j) { r.expected[j] = e[j]; r.actual[j] = a[j]; - r.written[j] = p[j]; - r.read[j] = q[j]; } } } @@ -673,18 +651,16 @@ ExactRGBATest::runOne(ExactRGBAResult& r, GLEAN::Window& w) { // Don't bother running if the ReadPixels sanity test for this // display surface configuration failed: - if (!env->options.ignorePrereqs) { - vector<ReadPixSanityResult*>::const_iterator rpsRes; - for (rpsRes = readPixSanityTest.results.begin(); - rpsRes != readPixSanityTest.results.end(); - ++rpsRes) - if ((*rpsRes)->config == r.config) - break; - if (rpsRes == readPixSanityTest.results.end() || !(*rpsRes)->pass) { - r.skipped = true; - r.pass = false; - return; - } + vector<ReadPixSanityResult*>::const_iterator rpsRes; + for (rpsRes = readPixSanityTest.results.begin(); + rpsRes != readPixSanityTest.results.end(); + ++rpsRes) + if ((*rpsRes)->config == r.config) + break; + if (rpsRes == readPixSanityTest.results.end() || !(*rpsRes)->pass) { + r.skipped = true; + r.pass = false; + return; } // Much of this state should already be set, if the defaults are @@ -751,25 +727,11 @@ ExactRGBATest::runOne(ExactRGBAResult& r, GLEAN::Window& w) { glPixelTransferf(GL_ALPHA_BIAS, 0.0); glPixelTransferf(GL_DEPTH_BIAS, 0.0); - // Hack: Make hardware driver tests feasible - // The OpenGL spec apparently requires insane behaviour on the part - // of the implementation: On the one hand, implementations should round - // color values to the nearest representable color value, while on the - // other hand it has to truncate. Silly... - int roundingMode = 0; - const char* s; - - s = getenv("GLEAN_EXACTRGBA_ROUNDING"); - if (s) { - roundingMode = atoi(s); - env->log << "Note: Rounding mode changed to " << roundingMode << "\n"; - } - - check<GLubyte>(r.ub, *(r.config), GL_UNSIGNED_BYTE, roundingMode); + check<GLubyte>(r.ub, *(r.config), GL_UNSIGNED_BYTE); w.swap(); - check<GLushort>(r.us, *(r.config), GL_UNSIGNED_SHORT, roundingMode); + check<GLushort>(r.us, *(r.config), GL_UNSIGNED_SHORT); w.swap(); - check<GLuint>(r.ui, *(r.config), GL_UNSIGNED_INT, roundingMode); + check<GLuint>(r.ui, *(r.config), GL_UNSIGNED_INT); w.swap(); r.pass = r.ub.pass && r.us.pass && r.ui.pass; r.skipped = false; @@ -908,15 +870,7 @@ ExactRGBATest::logFlavor(const char* label, const ExactRGBAResult::Flavor& r) { << r.actual[0] << ", 0x" << r.actual[1] << ", 0x" << r.actual[2] << ", 0x" - << r.actual[3] << ")\n\t\twrote (0x" - << r.written[0] << ", 0x" - << r.written[1] << ", 0x" - << r.written[2] << ", 0x" - << r.written[3] << ")\n\t\tread (0x" - << r.read[0] << ", 0x" - << r.read[1] << ", 0x" - << r.read[2] << ", 0x" - << r.read[3] << ")\n" + << r.actual[3] << ")\n" << dec ; } diff --git a/tests/glean/treadpix.h b/tests/glean/treadpix.h index 07106883f..8dc8d9e01 100644 --- a/tests/glean/treadpix.h +++ b/tests/glean/treadpix.h @@ -1,7 +1,7 @@ // BEGIN_COPYRIGHT -*- glean -*- -// +// // Copyright (C) 2001 Allen Akin All Rights Reserved. -// +// // 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 @@ -10,11 +10,11 @@ // 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 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 @@ -23,7 +23,7 @@ // 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. -// +// // END_COPYRIGHT @@ -77,7 +77,7 @@ public: = expectedRGBA[3] = 0.0; actualRGBA[0] = actualRGBA[1] = actualRGBA[2] = actualRGBA[3] = 0.0; - + passDepth = true; xDepth = yDepth = 0; errDepth = 0.0; @@ -94,7 +94,7 @@ public: expectedIndex = 0; actualIndex = 0; } - + void putresults(ostream& s) const { s << pass << '\n' @@ -125,7 +125,7 @@ public: << actualIndex << '\n' ; } - + bool getresults(istream& s) { s >> pass @@ -184,8 +184,6 @@ public: GLuint err; GLuint expected[4]; GLuint actual[4]; - GLuint written[4]; - GLuint read[4]; bool operator== (const Flavor& f) const { return pass == f.pass @@ -200,14 +198,6 @@ public: && actual[1] == f.actual[1] && actual[2] == f.actual[2] && actual[3] == f.actual[3] - && written[0] == f.written[0] - && written[1] == f.written[1] - && written[2] == f.written[2] - && written[3] == f.written[3] - && read[0] == f.read[0] - && read[1] == f.read[1] - && read[2] == f.read[2] - && read[3] == f.read[3] ; } @@ -218,8 +208,6 @@ public: expected[0] = expected[1] = expected[2] = expected[3] = 0; actual[0] = actual[1] = actual[2] = actual[3] = 0; - written[0] = written[1] = written[2] = written[3] = 0; - read[0] = read[1] = read[2] = read[3] = 0; } void put(ostream& s) const { @@ -235,14 +223,6 @@ public: << actual[1] << ' ' << actual[2] << ' ' << actual[3] << '\n' - << written[0] << ' ' - << written[1] << ' ' - << written[2] << ' ' - << written[3] << '\n' - << read[0] << ' ' - << read[1] << ' ' - << read[2] << ' ' - << read[3] << '\n' ; } void get(istream& s) { @@ -258,14 +238,6 @@ public: >> actual[1] >> actual[2] >> actual[3] - >> written[0] - >> written[1] - >> written[2] - >> written[3] - >> read[0] - >> read[1] - >> read[2] - >> read[3] ; } }; @@ -281,7 +253,7 @@ public: skipped = false; pass = true; } - + void putresults(ostream& s) const { s << skipped << '\n' @@ -291,7 +263,7 @@ public: us.put(s); ui.put(s); } - + bool getresults(istream& s) { s >> skipped diff --git a/tests/glean/treadpixperf.cpp b/tests/glean/treadpixperf.cpp index 058e67a09..0cde676d3 100644 --- a/tests/glean/treadpixperf.cpp +++ b/tests/glean/treadpixperf.cpp @@ -467,9 +467,9 @@ ReadpixPerfTest::compareOne(ReadpixPerfResult &oldR, << descrip << "' changed by " << diff - << " percent (old: " + << " percent (new: " << newres.rate - << " new: " + << " old: " << oldres.rate << " MPixels/sec)\n"; } diff --git a/tests/glean/ttexrect.cpp b/tests/glean/ttexrect.cpp index 7adcb3a0c..ccc5ba3dc 100644 --- a/tests/glean/ttexrect.cpp +++ b/tests/glean/ttexrect.cpp @@ -142,10 +142,8 @@ TexRectTest::runOne(BasicResult& r, Window& w) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnable(GL_TEXTURE_RECTANGLE_ARB); - if (w.config->db) { - glDrawBuffer(GL_BACK); - glReadBuffer(GL_BACK); - } + glDrawBuffer(GL_BACK); + glReadBuffer(GL_BACK); r.pass = true; diff --git a/tests/glean/tvertprog1.h b/tests/glean/tvertprog1.h index df42073cc..b40f59f78 100644 --- a/tests/glean/tvertprog1.h +++ b/tests/glean/tvertprog1.h @@ -47,8 +47,8 @@ class VertexProgram public: const char *name; const char *progString; - const GLfloat expectedColor[4]; - const GLfloat expectedZ; + GLfloat expectedColor[4]; + GLfloat expectedZ; int flags; }; |