diff options
author | Nicolai Hähnle <nh@annarchy.freedesktop.org> | 2007-03-24 17:20:31 -0700 |
---|---|---|
committer | Nicolai Hähnle <nh@annarchy.freedesktop.org> | 2007-03-24 17:20:31 -0700 |
commit | 22fbbb5e8679ddf91532cb10eaa31be533383b48 (patch) | |
tree | cb43f83fe997f166f297dad671ddf04e2f601be4 /tests/glean |
Initial commit
Diffstat (limited to 'tests/glean')
108 files changed, 33864 insertions, 0 deletions
diff --git a/tests/glean/CMakeLists.txt b/tests/glean/CMakeLists.txt new file mode 100644 index 00000000..9156f1a8 --- /dev/null +++ b/tests/glean/CMakeLists.txt @@ -0,0 +1,69 @@ + +add_definitions ( -D__X11__ -D__UNIX__ ) + +include_directories( ${OPENGL_INCLUDE_PATH} ) + +add_executable (glean + codedid.cpp + dsconfig.cpp + dsfilt.cpp + dsurf.cpp + environ.cpp + geomrend.cpp + geomutil.cpp + glutils.cpp + main.cpp + misc.cpp + options.cpp + rc.cpp + tbasic.cpp + tbasicperf.cpp + tbinding.cpp + tblend.cpp + tchgperf.cpp + tdepthstencil.cpp + test.cpp + tfpexceptions.cpp + tfragprog1.cpp + tgetstr.cpp + tlogicop.cpp + tmaskedclear.cpp + tmultitest.cpp + torthpos.cpp + tpaths.cpp + tpgos.cpp + tpixelformats.cpp + tpointatten.cpp + treadpix.cpp + treadpixperf.cpp + trgbtris.cpp + tscissor.cpp + tteapot.cpp + ttexcombine.cpp + ttexcube.cpp + ttexenv.cpp + ttexgen.cpp + ttexrect.cpp + ttexture_srgb.cpp + tvertattrib.cpp + tvertprog1.cpp + tvtxperf.cpp + winsys.cpp + gl.cpp + image_misc.cpp + pack.cpp + rdtiff.cpp + reg.cpp + unpack.cpp + wrtiff.cpp + basic.cpp + lex.cpp + timer.cpp +) + +target_link_libraries (glean + ${OPENGL_gl_LIBRARY} + ${OPENGL_glu_LIBRARY} + ${TIFF_LIBRARY} +) + diff --git a/tests/glean/basic.cpp b/tests/glean/basic.cpp new file mode 100644 index 00000000..af317bd9 --- /dev/null +++ b/tests/glean/basic.cpp @@ -0,0 +1,64 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// basic.cpp: basic statistics utilities for glean + +#include <cfloat> +#include <cmath> +#include "stats.h" + +namespace GLEAN { + +void +BasicStats::init() { + _min = DBL_MAX; + _max = -DBL_MAX; + _sum = _sum2 = 0.0; + _n = 0; +} + +double +BasicStats::mean() const {return _n? (_sum / _n): 0.0;} + +double +BasicStats::variance() const { + if (_n < 2) + return 0.0; + return (_sum2 - _sum * _sum / _n) / (_n - 1); + // Not really numerically robust, but good enough for us. +} +double +BasicStats::deviation() const { + const double v = variance(); + return (v < 0.0)? 0.0: sqrt(v); +} + +} // namespace GLEAN diff --git a/tests/glean/codedid.cpp b/tests/glean/codedid.cpp new file mode 100644 index 00000000..44fe56b8 --- /dev/null +++ b/tests/glean/codedid.cpp @@ -0,0 +1,146 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + + + +// codedid.h: tool to map integer IDs into colors, and vice-versa + +using namespace std; + +#include <algorithm> +#include <vector> +#include "codedid.h" +#include "image.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// RGBCodedID: Create an object that maps integer identification numbers +// to RGB triples, and vice-versa +/////////////////////////////////////////////////////////////////////////////// +RGBCodedID::RGBCodedID(int r, int g, int b) { + rBits = min(8, r); // 8 because we use GLubyte color values + gBits = min(8, g); + bBits = min(8, b); + nsRBits = 8 - rBits; + nsGBits = 8 - gBits; + nsBBits = 8 - bBits; + rMask = (1 << rBits) - 1; + gMask = (1 << gBits) - 1; + bMask = (1 << bBits) - 1; +} // RGBCodedID::RGBCodedID + +RGBCodedID::~RGBCodedID() { +} // RGBCodedID::~RGBCodedID + +/////////////////////////////////////////////////////////////////////////////// +// maxID: Return the maximum allowable integer ID number +/////////////////////////////////////////////////////////////////////////////// +int +RGBCodedID::maxID() const { + return (1 << (rBits + gBits + bBits)) - 1; +} // RGBCodedID::maxID + +/////////////////////////////////////////////////////////////////////////////// +// toRGB: Convert integer ID number to RGB triple +/////////////////////////////////////////////////////////////////////////////// +void +RGBCodedID::toRGB(int id, GLubyte& r, GLubyte& g, GLubyte& b) const { + b = (id & bMask) << nsBBits; + id >>= bBits; + g = (id & gMask) << nsGBits; + id >>= gBits; + r = (id & rMask) << nsRBits; +} // RGBCodedID::toRGB + +/////////////////////////////////////////////////////////////////////////////// +// toID: Convert RGB triple to integer ID number +/////////////////////////////////////////////////////////////////////////////// +int +RGBCodedID::toID(GLubyte r, GLubyte g, GLubyte b) const { + int id = 0; + id |= (r >> nsRBits) & rMask; + id <<= gBits; + id |= (g >> nsGBits) & gMask; + id <<= bBits; + id |= (b >> nsBBits) & bMask; + return id; +} // RGBCodedID::toID + +/////////////////////////////////////////////////////////////////////////////// +// histogram: Compute histogram of coded IDs in an UNSIGNED_BYTE RGB image +/////////////////////////////////////////////////////////////////////////////// +void +RGBCodedID::histogram(Image& img, vector<int>& hist) const { + if (img.format() != GL_RGB || img.type() != GL_UNSIGNED_BYTE) { + hist.resize(0); + return; + } + + int max = maxID(); + hist.resize(max + 1); + for (vector<int>::iterator p = hist.begin(); p != hist.end(); ++p) + *p = 0; + + GLubyte* row = reinterpret_cast<GLubyte*>(img.pixels()); + for (GLsizei r = 0; r < img.height(); ++r) { + GLubyte* pix = row; + for (GLsizei c = 0; c < img.width(); ++c) { + int id = toID(pix[0], pix[1], pix[2]); + if (id <= max) + ++hist[id]; + pix += 3; + } + row += img.rowSizeInBytes(); + } +} // RGBCodedID::histogram + +/////////////////////////////////////////////////////////////////////////////// +// allPresent: See if all of a range of IDs are present in a given RGB image +/////////////////////////////////////////////////////////////////////////////// +bool +RGBCodedID::allPresent(Image& img, int first, int last) const { + vector<int> hist; + histogram(img, hist); + if (hist.size() == 0) + return false; + if (first >= static_cast<int>(hist.size())) + return false; + if (last >= static_cast<int>(hist.size())) + return false; + if (first > last) + return false; + + for (vector<int>::const_iterator p = hist.begin() + first; + p != hist.begin() + last + 1; ++p) + if (*p == 0) + return false; + return true; +} // RGBCodedID::allPresent + +} // namespace GLEAN diff --git a/tests/glean/codedid.h b/tests/glean/codedid.h new file mode 100644 index 00000000..b7bbc066 --- /dev/null +++ b/tests/glean/codedid.h @@ -0,0 +1,92 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + + + +// codedid.h: tool to map integer IDs into colors, and vice-versa + +// A note on principles of operation: The OpenGL spec normally allows +// a reasonable amount of slop when converting user-specified colors +// to a hardware-dependent representation in the framebuffer. One +// exception to this lenience is when lighting is disabled and the +// color is specified as an unsigned byte, short, or int. In this +// case the conversion from user-supplied color to hardware-determined +// color must be exact, up to the number of bits in the framebuffer or +// in the value supplied by the user (whichever is smaller). This is +// intended to allow object identification numbers to be encoded as +// colors, so that applications can implement object selection by +// drawing objects and reading back the image to determine the object +// ID of the closest visible object. glean uses this property in a +// number of cases, for example, where it needs to draw a large number +// of primitives and verify that all of them were actually drawn. See +// the OpenGL spec, version 1.2.1, section 2.13.9 (page 55) for the +// description of this convertibility requirement. + +#ifndef __codedid_h__ +#define __codedid_h__ + +using namespace std; + +#include <vector> +#include "glwrap.h" + +namespace GLEAN { + +class Image; // forward reference + +class RGBCodedID { + int rBits, gBits, bBits; // number of signif. bits in channels + int nsRBits, nsGBits, nsBBits; // non-significant bits in each + int rMask, gMask, bMask; // masks for significant bits in each + public: + RGBCodedID(int r, int g, int b); + ~RGBCodedID(); + + // Return the maximum ID number that the caller can use: + int maxID() const; + + // Map an ID number to an RGB triple: + void toRGB(int id, GLubyte& r, GLubyte& g, GLubyte& b) const; + + // Map an RGB triple to the equivalent ID number: + int toID(GLubyte r, GLubyte g, GLubyte b) const; + + // Histogram an UNSIGNED_BYTE RGB image: + void histogram(Image& img, vector<int>& hist) const; + + // Are all of a range of IDs present in an RGB image? + bool allPresent(Image& img, int first, int last) const; + +}; // RGBCodedID + +// XXX Might want an IndexCodedID class for use with color index drawing +// surfaces, even though such a class would be trivial. + +} // namespace GLEAN + +#endif // __codedid_h__ diff --git a/tests/glean/dsconfig.cpp b/tests/glean/dsconfig.cpp new file mode 100644 index 00000000..88f9a81b --- /dev/null +++ b/tests/glean/dsconfig.cpp @@ -0,0 +1,881 @@ +// BEGIN_COPYRIGHT +// +// 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 + + +// dsconfig.cpp: Implementation of drawing surface configuration utilities +#include "dsconfig.h" +#include <iostream> +#include <strstream> +#include <cstring> +#include <map> +#include <climits> + +#ifdef __WIN__ +// disable the annoying warning : "forcing value to bool 'true' or 'false' (performance warning)" +#pragma warning (disable : 4800) +#endif + + +#include "lex.h" + + + +namespace { + +#ifdef __X11__ + +bool +haveGLXExtension(::Display* dpy, const char* extName) { + const char* extString = + glXQueryExtensionsString(dpy, DefaultScreen(dpy)); + // We don't cache the result, so that subsequent calls + // with different values of ``dpy'' will work correctly. + // Would be nice to improve this, though. + + const char* start = extString; + for (;;) { + const char* where = strstr(start, extName); + if (!where) + return false; + + // Make sure we're not fooled by extensions whose names + // have the desired extName as an initial substring: + const char* terminator = where + strlen(extName); + if ((where == start || where[-1] == ' ') + && (*terminator == ' ' || *terminator == 0)) + return true; + + start = terminator; + } + + return false; +} // haveGLXExtension + +#endif + +typedef enum { // These variable tags are used as array indices, + // so they should represent a small dense set of + // nonnegative integers. 0 is reserved. + VID = 1, + VFBCID, + VCANRGBA, + VR, + VG, + VB, + VA, + VCANCI, + VBUFSIZE, + VLEVEL, + VDB, + VSTEREO, + VAUX, + VZ, + VS, + VACCUMR, + VACCUMG, + VACCUMB, + VACCUMA, + VCANWINDOW, + VCANPIXMAP, + VCANPBUFFER, + VMAXPBUFFERWIDTH, + VMAXPBUFFERHEIGHT, + VMAXPBUFFERPIXELS, + VCANWINSYSRENDER, + VFAST, + VCONFORMANT, + VTRANSPARENT, + VTRANSR, + VTRANSG, + VTRANSB, + VTRANSA, + VTRANSI, + V_LAST +} CanonVar; + +struct {CanonVar var; char* name;} varNames[] = { + {VID, "id"}, + {VFBCID, "fbcID"}, + {VCANRGBA, "canRGBA"}, + {VR, "r"}, + {VG, "g"}, + {VB, "b"}, + {VA, "a"}, + {VCANCI, "canCI"}, + {VBUFSIZE, "bufSize"}, + {VLEVEL, "level"}, + {VDB, "db"}, + {VSTEREO, "stereo"}, + {VAUX, "aux"}, + {VZ, "z"}, + {VS, "s"}, + {VACCUMR, "accumR"}, + {VACCUMG, "accumG"}, + {VACCUMB, "accumB"}, + {VACCUMA, "accumA"}, + {VCANWINDOW, "window"}, + {VCANPIXMAP, "pixmap"}, + {VCANPBUFFER, "pBuffer"}, + {VMAXPBUFFERWIDTH, "maxPBufferWidth"}, + {VMAXPBUFFERHEIGHT, "maxPBufferHeight"}, + {VMAXPBUFFERPIXELS, "maxPBufferPixels"}, + {VCANWINSYSRENDER, "winsys"}, + {VFAST, "fast"}, + {VCONFORMANT, "conformant"}, + {VTRANSPARENT, "transparent"}, + {VTRANSR, "transR"}, + {VTRANSG, "transG"}, + {VTRANSB, "transB"}, + {VTRANSA, "transA"}, + {VTRANSI, "transI"} +}; + +char* mapVarToName[V_LAST]; +map<string, CanonVar> mapNameToVar; +bool mapsInitialized = false; + +void +initializeMaps() { + for (unsigned i = 0; i < sizeof(varNames)/sizeof(varNames[0]); ++i) { + mapVarToName[varNames[i].var] = varNames[i].name; + mapNameToVar[varNames[i].name] = varNames[i].var; + } + mapsInitialized = true; +} // initializeMaps + +template<class T> inline T abs(T a) {return (a < 0)? -a: a;} + +} // anonymous namespace + + +namespace GLEAN { + + +#if defined(__X11__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::XVisualInfo* pvi) { + if (!mapsInitialized) + initializeMaps(); + + int var; + + vi = pvi; + visID = vi->visualid; +# if defined(GLX_VERSION_1_3) + fbcID = 0; +# endif + + glXGetConfig(dpy, vi, GLX_RGBA, &var); + canRGBA = var; + canCI = !var; + // There is no dual-personality Visual support in early + // versions of GLX. + + glXGetConfig(dpy, vi, GLX_BUFFER_SIZE, &bufSize); + + glXGetConfig(dpy, vi, GLX_LEVEL, &level); + + glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &var); + db = var; + + glXGetConfig(dpy, vi, GLX_STEREO, &var); + stereo = var; + + glXGetConfig(dpy, vi, GLX_AUX_BUFFERS, &aux); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_RED_SIZE, &r); + glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &g); + glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &b); + glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &a); + } else + r = g = b = a = 0; + + glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &z); + + glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &s); + + if (canRGBA) { + glXGetConfig(dpy, vi, GLX_ACCUM_RED_SIZE, &accR); + glXGetConfig(dpy, vi, GLX_ACCUM_GREEN_SIZE, &accG); + glXGetConfig(dpy, vi, GLX_ACCUM_BLUE_SIZE, &accB); + glXGetConfig(dpy, vi, GLX_ACCUM_ALPHA_SIZE, &accA); + } else + accR = accG = accB = accA = 0; + + canWindow = canPixmap = true; + // Only guaranteed in early versions of GLX. + +# if defined(GLX_VERSION_1_3) + canPBuffer = 0; + maxPBufferWidth = 0; + maxPBufferHeight = 0; + maxPBufferPixels = 0; +# endif + + canWinSysRender = true; + // Only guaranteed in early versions of GLX. + + fast = true; + conformant = true; +# if defined(GLX_EXT_visual_rating) + if (haveGLXExtension(dpy, "GLX_EXT_visual_rating")) { + glXGetConfig(dpy, vi, GLX_VISUAL_CAVEAT_EXT, &var); + if (var == GLX_SLOW_VISUAL_EXT) + fast = false; + else if (var == GLX_NON_CONFORMANT_VISUAL_EXT) + conformant = false; + } +# endif + + transparent = false; + transR = transG = transB = transA = transI = 0; +# if defined(GLX_EXT_visual_info) + if (haveGLXExtension(dpy, "GLX_EXT_visual_info")) { + glXGetConfig(dpy, vi, GLX_TRANSPARENT_TYPE_EXT, &var); + if (var == GLX_TRANSPARENT_RGB_EXT) { + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_RED_VALUE_EXT, &transR); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_GREEN_VALUE_EXT, &transG); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_BLUE_VALUE_EXT, &transB); + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_ALPHA_VALUE_EXT, &transA); + } else + glXGetConfig(dpy, vi, + GLX_TRANSPARENT_INDEX_VALUE_EXT, &transI); + } +# endif +} // DrawingSurfaceConfig::DrawingSurfaceConfig + +#if defined(GLX_VERSION_1_3) +DrawingSurfaceConfig::DrawingSurfaceConfig(::Display* dpy, ::GLXFBConfig* pfbc) +{ + // silence warnings about unused parameters: + (void) dpy; + (void) pfbc; + + if (!mapsInitialized) + initializeMaps(); +// XXX Need to write drawing surface config code for GLX 1.3 + cerr << "GLX 1.3 version of DrawingSurfaceConfig constructor is not" + "implemented.\n"; +} // DrawingSurfaceConfig::DrawingSurfaceConfig +#endif + +#elif defined(__WIN__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(int id, ::PIXELFORMATDESCRIPTOR *ppfd) +{ + if (!mapsInitialized) + initializeMaps(); + + pfd = ppfd; + pfdID = id; + + canRGBA = pfd->iPixelType == PFD_TYPE_RGBA; + canCI = pfd->iPixelType == PFD_TYPE_COLORINDEX; + + bufSize = pfd->cColorBits + pfd->cAlphaBits; + + level = 0; + + db = pfd->dwFlags & PFD_DOUBLEBUFFER; + + stereo = pfd->dwFlags & PFD_STEREO; + + aux = pfd->cAuxBuffers; + + if (canRGBA) { + r = pfd->cRedBits; + g = pfd->cGreenBits; + b = pfd->cBlueBits; + a = pfd->cAlphaBits; + } + else + r = g = b = a = 0; + + z = pfd->cDepthBits; + s = pfd->cStencilBits; + + accR = pfd->cAccumRedBits; + accG = pfd->cAccumGreenBits; + accB = pfd->cAccumBlueBits; + accA = pfd->cAccumAlphaBits; + + canWindow = pfd->dwFlags & PFD_DRAW_TO_WINDOW; + + canWinSysRender = pfd->dwFlags & PFD_SUPPORT_GDI; + + if (pfd->dwFlags & PFD_GENERIC_FORMAT) + { + if (pfd->dwFlags & PFD_GENERIC_ACCELERATED) + { + // it's an MCD - at least it has some acceleration + fast = true; + } + else + { + // it's software + fast = false; + } + } + else + { + // it's an ICD + fast = true; + } + + // we'll assume that the OpenGL implementation thinks it is conformant + conformant = true; + + // chromakeying isn't supported + transparent = false; + transR = transG = transB = transA = transI = 0; +} +#elif defined(__BEWIN__) + +DrawingSurfaceConfig::DrawingSurfaceConfig() { + + if (!mapsInitialized) + initializeMaps(); + + /* these values are estimates for the moment */ + level = 0; + db = 1; + stereo =0; + r = g = b = a = 32; + + z = 30; + accR = 32; + accG = 32; + accB = 32; + accA = 32; + + + canWindow = 1; + canWinSysRender = 1; + + // This is a software-mode assumption + fast = false; + + // we'll assume that the OpenGL implementation thinks it is conformant + conformant = true; + + // chromakeying isn't supported + transparent = false; + transR = transG = transB = transA = transI = 0; +} + +#elif defined(__AGL__) + +DrawingSurfaceConfig::DrawingSurfaceConfig(int id, ::AGLPixelFormat pfd) +{ + long i; + + if (!mapsInitialized) + initializeMaps(); + + pf = pfd; + + if (aglDescribePixelFormat( pf, AGL_RGBA, &i)) + canRGBA = (i == GL_TRUE); + canCI = (i == GL_FALSE); + + if (aglDescribePixelFormat( pf, AGL_BUFFER_SIZE, &i)) + bufSize = i; + + level = 0; + + if (aglDescribePixelFormat( pf, AGL_DOUBLEBUFFER, &i)) + db = (i == GL_TRUE); + if (aglDescribePixelFormat( pf, AGL_STEREO, &i)) + stereo = (i == GL_TRUE); + if (aglDescribePixelFormat( pf, AGL_AUX_BUFFERS, &i)) + aux = i; + + if (canRGBA) { + aglDescribePixelFormat( pf, AGL_RED_SIZE, (long *)&r); + aglDescribePixelFormat( pf, AGL_GREEN_SIZE, (long *)&g); + aglDescribePixelFormat( pf, AGL_BLUE_SIZE, (long *)&b); + aglDescribePixelFormat( pf, AGL_ALPHA_SIZE, (long *)&a); + + //this is a workaround for some versions of AGL + if (r == 10) + { + r=g=b=8; + bufSize = r + g + b + a; + } + } + else + r = g = b = a = 0; + + aglDescribePixelFormat( pf, AGL_DEPTH_SIZE, (long *)& z); + aglDescribePixelFormat( pf, AGL_STENCIL_SIZE, (long *)& s); + + aglDescribePixelFormat( pf, AGL_ACCUM_RED_SIZE, (long *)& accR); + aglDescribePixelFormat( pf, AGL_ACCUM_GREEN_SIZE, (long *)& accG); + aglDescribePixelFormat( pf, AGL_ACCUM_BLUE_SIZE, (long *)& accB); + aglDescribePixelFormat( pf, AGL_ACCUM_ALPHA_SIZE, (long *)& accA); + + aglDescribePixelFormat( pf, AGL_WINDOW, &i); + canWindow = i; + aglDescribePixelFormat( pf, AGL_OFFSCREEN, &i); + canWinSysRender = i; + aglDescribePixelFormat( pf, AGL_ACCELERATED, & i); + fast = i; + + // we'll assume that the OpenGL implementation thinks it is conformant + conformant = true; + + // chromakeying isn't supported + transparent = false; + transR = transG = transB = transA = transI = 0; +} + +#endif + +DrawingSurfaceConfig::DrawingSurfaceConfig(string& str) { + if (!mapsInitialized) + initializeMaps(); + + try { + Lex lex(str.c_str()); + + for (lex.next(); lex.token != Lex::END; lex.next()) { + if (lex.token != Lex::ID) + throw Syntax("expected variable name", + lex.position()); + lex.next(); + if (lex.token != Lex::ICONST) + throw Syntax("expected integer value", + lex.position()); + + // Yes, this is an unpleasantly verbose way to + // handle this problem. However, it will be + // necessary when we have to deal with attributes + // that aren't all of a simple integral type. + + switch (mapNameToVar[lex.id]) { + case VID: +# if defined(__X11__) + visID = lex.iValue; +# endif + break; + case VFBCID: +# if defined(GLX_VERSION_1_3) + fbcID = lex.iValue; +# endif + break; + case VCANRGBA: + canRGBA = lex.iValue; + break; + case VR: + r = lex.iValue; + break; + case VG: + g = lex.iValue; + break; + case VB: + b = lex.iValue; + break; + case VA: + a = lex.iValue; + break; + case VCANCI: + canCI = lex.iValue; + break; + case VBUFSIZE: + bufSize = lex.iValue; + break; + case VLEVEL: + level = lex.iValue; + break; + case VDB: + db = lex.iValue; + break; + case VSTEREO: + stereo = lex.iValue; + break; + case VAUX: + aux = lex.iValue; + break; + case VZ: + z = lex.iValue; + break; + case VS: + s = lex.iValue; + break; + case VACCUMR: + accR = lex.iValue; + break; + case VACCUMG: + accG = lex.iValue; + break; + case VACCUMB: + accB = lex.iValue; + break; + case VACCUMA: + accA = lex.iValue; + break; + case VCANWINDOW: + canWindow = lex.iValue; + break; + case VCANPIXMAP: +# if defined(__X11__) + canPixmap = lex.iValue; +# endif + break; + case VCANPBUFFER: +# if defined(GLX_VERSION_1_3) + canPBuffer = lex.iValue; +# endif + break; + case VMAXPBUFFERWIDTH: +# if defined(GLX_VERSION_1_3) + maxPBufferWidth = lex.iValue; +# endif + break; + case VMAXPBUFFERHEIGHT: +# if defined(GLX_VERSION_1_3) + maxPBufferHeight = lex.iValue; +# endif + break; + case VMAXPBUFFERPIXELS: +# if defined(GLX_VERSION_1_3) + maxPBufferPixels = lex.iValue; +# endif + break; + case VCANWINSYSRENDER: + canWinSysRender = lex.iValue; + break; + case VFAST: + fast = lex.iValue; + break; + case VCONFORMANT: + conformant = lex.iValue; + break; + case VTRANSPARENT: + transparent = lex.iValue; + break; + case VTRANSR: + transR = lex.iValue; + break; + case VTRANSG: + transG = lex.iValue; + break; + case VTRANSB: + transB = lex.iValue; + break; + case VTRANSA: + transA = lex.iValue; + break; + case VTRANSI: + transI = lex.iValue; + break; + default: + throw Syntax("unrecognized variable", + lex.position()); + } + } + } + catch (Lex::Lexical e) { + throw Syntax(e.err, e.position); + } +} // DrawingSurfaceConfing::DrawingSurfaceConfig + + +/////////////////////////////////////////////////////////////////////////////// +// canonicalDescription - return a description string that can be used +// to reconstruct the essential attributes of a drawing surface +// configuration. Note that visual ID numbers are included for +// completeness, but they must be ignored when attempting to compare +// two surface configurations; there's no guarantee that they'll be +// valid (or even relevant, since they may have been created on another +// OS). +// +// This is ugly code, but it keeps the names used here and in +// the string-based constructor for DrawingSurfaceConfig in sync +// automatically. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::canonicalDescription() { + + // Would rather use ostringstream, but it's not available in + // egcs 1.1.2. + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + +# if defined(__X11__) + s << mapVarToName[VID] << ' ' << visID; +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VFBCID] << ' ' << fbcID; +# endif +# elif defined(__WIN__) + s << mapVarToName[VID] << ' ' << pfdID; +# endif + + s << ' ' << mapVarToName[VCANRGBA] << ' ' << canRGBA; + s << ' ' << mapVarToName[VR] << ' ' << r + << ' ' << mapVarToName[VG] << ' ' << g + << ' ' << mapVarToName[VB] << ' ' << b + << ' ' << mapVarToName[VA] << ' ' << a; + + s << ' ' << mapVarToName[VCANCI] << ' ' << canCI; + + s << ' ' << mapVarToName[VBUFSIZE] << ' ' << bufSize; + + s << ' ' << mapVarToName[VLEVEL] << ' ' << level; + + s << ' ' << mapVarToName[VDB] << ' ' << db; + + s << ' ' << mapVarToName[VSTEREO] << ' '<< stereo; + + s << ' ' << mapVarToName[VAUX] << ' ' << aux; + + s << ' ' << mapVarToName[VZ] << ' ' << z; + + s << ' ' << mapVarToName[VS] << ' ' << DrawingSurfaceConfig::s; + + s << ' ' << mapVarToName[VACCUMR] << ' ' << accR + << ' ' << mapVarToName[VACCUMG] << ' ' << accG + << ' ' << mapVarToName[VACCUMB] << ' ' << accB + << ' ' << mapVarToName[VACCUMA] << ' ' << accA; + + s << ' ' << mapVarToName[VCANWINDOW] << ' ' << canWindow; + +# if defined(__X11__) + s << ' ' << mapVarToName[VCANPIXMAP] << ' ' << canPixmap; + +# if defined(GLX_VERSION_1_3) + s << ' ' << mapVarToName[VCANPBUFFER] << ' ' << canPBuffer; + s << ' ' << mapVarToName[VMAXPBUFFERWIDTH] << ' ' << maxPBufferWidth; + s << ' ' << mapVarToName[VMAXPBUFFERHEIGHT] << ' ' << maxPBufferHeight; + s << ' ' << mapVarToName[VMAXPBUFFERPIXELS] << ' ' << maxPBufferPixels; +# endif + +# endif + + s << ' ' << mapVarToName[VCANWINSYSRENDER] << ' ' << canWinSysRender; + + s << ' ' << mapVarToName[VFAST] << ' ' << fast; + + s << ' ' << mapVarToName[VCONFORMANT] << ' ' << conformant; + + s << ' ' << mapVarToName[VTRANSPARENT] << ' ' << transparent; + s << ' ' << mapVarToName[VTRANSR] << ' ' << transR + << ' ' << mapVarToName[VTRANSG] << ' ' << transG + << ' ' << mapVarToName[VTRANSB] << ' ' << transB + << ' ' << mapVarToName[VTRANSA] << ' ' << transA + << ' ' << mapVarToName[VTRANSI] << ' ' << transI; + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::canonicalDescription + +/////////////////////////////////////////////////////////////////////////////// +// conciseDescription - return a description string that's appropriate for +// reading by humans, rather than parsing by machine. +/////////////////////////////////////////////////////////////////////////////// +string +DrawingSurfaceConfig::conciseDescription() { + char buf[1024]; + ostrstream s(buf, sizeof(buf)); + + if (canRGBA && canCI) + s << "dual "; + + if (canRGBA) { + if (a) { + if (r == g && g == b && b == a) + s << "rgba" << r; + else + s << "r" << r << "g" << g << "b" << b + << "a" << a; + } else { + if (r == g && g == b) + s << "rgb" << r; + else + s << "r" << r << "g" << g << "b" << b; + } + } + + if (canCI) { + if (canRGBA) s << "+"; + s << "ci" << bufSize; + } + + if (level < 0) + s << ", underlay"; + else if (level > 0) + s << ", overlay"; + + if (db) + s << ", db"; + + if (stereo) + s << ", stereo"; + + if (aux) + s << ", aux" << aux; + + if (z) + s << ", z" << z; + + if (DrawingSurfaceConfig::s) + s << ", s" << DrawingSurfaceConfig::s; + + if (accR) { + if (accA) { + if (accR == accG && accG == accB + && accB == accA) + s << ", accrgba" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB << "a" << accA; + } else { + if (accR == accG && accG == accB) + s << ", accrgb" << accR; + else + s << ", accr" << accR << "g" << accG + << "b" << accB; + } + } + + { + s << ", "; + bool sep = false; + if (canWindow) { + s << "win"; + sep = true; + } +# if defined(__X11__) + if (canPixmap) { + if (sep) + s << "+"; + s << "pmap"; + sep = true; + } +# endif +# if defined(GLX_VERSION_1_3) + if (canPBuffer) { + if (sep) + s << "+"; + s << "pbuf"; + } +# endif + } + + if (!fast) + s << ", slow"; + + if (!conformant) + s << ", nonconformant"; + + if (transparent) { + if (canRGBA) { + s << ", transrgba (" << transR << "," << transG + << "," << transB << "," << transA << ")"; + } + if (canCI) { + s << ", transci (" << transI << ")"; + } + } + +# if defined(__X11__) + s << ", id " << visID; +# if defined(GLX_VERSION_1_3) + if (fbcID) + s << ", fbcid " << fbcID; +# endif +# elif defined(__WIN__) + s << ", id " << pfdID; +# endif + + s << '\0'; + return s.str(); +} // DrawingSurfaceConfig::conciseDescription + +/////////////////////////////////////////////////////////////////////////////// +// match - select a config that ``matches'' the current config. +// To keep this problem manageable, we'll assume that both the config +// to be matched (call it the ``A'' config) and the vector of configs to +// choose from (call them the ``B'' configs) were selected by a test +// using a single filter. Thus we can ignore any differences in buffer +// availability (because we know those are irrelevant to the test), and +// concentrate on picking configs for which the available buffers are +// (in some sense) closest in size. +// +// This will not be an acceptable solution in all cases, but it should +// suffice for many. +/////////////////////////////////////////////////////////////////////////////// +int +DrawingSurfaceConfig::match(vector<DrawingSurfaceConfig*>& choices) { + typedef vector<DrawingSurfaceConfig*>::iterator cptr; + + int best = -1; + int bestError = INT_MAX; + + for (cptr p = choices.begin(); p < choices.end(); ++p) { + DrawingSurfaceConfig& c = **p; + int error = 0; + + if (bufSize && c.bufSize) + error += abs(bufSize - c.bufSize); + if (r && c.r) + error += abs(r - c.r); + if (g && c.g) + error += abs(g - c.g); + if (b && c.b) + error += abs(b - c.b); + if (a && c.a) + error += abs(a - c.a); + if (z && c.z) + error += abs(z - c.z); + if (s && c.s) + error += abs(s - c.s); + if (accR && c.accR) + error += abs(accR - c.accR); + if (accG && c.accG) + error += abs(accG - c.accG); + if (accB && c.accB) + error += abs(accB - c.accB); + if (accA && c.accA) + error += abs(accA - c.accA); + + if (error < bestError) { + bestError = error; + best = static_cast<int>(p - choices.begin()); + } + } + + return best; +} // DrawingSurfaceConfig::match + +} // namespace GLEAN diff --git a/tests/glean/dsconfig.h b/tests/glean/dsconfig.h new file mode 100644 index 00000000..a5f24f92 --- /dev/null +++ b/tests/glean/dsconfig.h @@ -0,0 +1,210 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// dsconfig.h: Drawing surface configuration utilities + +// This class abstracts the basic characteristics of drawing surfaces +// (size, depth, ancillary buffers, etc.) and operations on them. It +// serves as a wrapper for X11 Visual and FBConfig information on +// X11-based systems, and PixelFormatDescriptor information on +// Win32-based systems. + + +#ifndef __dsconfig_h__ +#define __dsconfig_h__ + +#include <string> +#include <vector> +#include "glwrap.h" + +using namespace std; + +namespace GLEAN { + +class DrawingSurfaceConfig { + public: + + // Constructors/Destructor: + +# if defined(__X11__) + DrawingSurfaceConfig(::Display* dpy, ::XVisualInfo* pvi); +# if defined(GLX_VERSION_1_3) + DrawingSurfaceConfig(::Display* dpy, ::GLXFBConfig* pfbc); +# endif +# elif defined(__WIN__) + DrawingSurfaceConfig(int id, ::PIXELFORMATDESCRIPTOR *ppfd); +# elif defined(__BEWIN__) + DrawingSurfaceConfig(); +# elif defined(__AGL__) + DrawingSurfaceConfig(int id, ::AGLPixelFormat pfd); +# endif + + DrawingSurfaceConfig(string& s); // s is a canonical description + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Syntax: public Error { // Syntax error in constructor string. + const char* err; + int position; + Syntax(const char* e, int p) { + err = e; + position = p; + } + }; + + // Attributes: + +# if defined(__X11__) + ::XVisualInfo* vi; // XVisualInfo pointer + ::XID visID; // Visual ID. +# if defined(GLX_VERSION_1_3) + ::GLXFBConfig* fbc; + ::XID fbcID; // Framebuffer Config ID. +# endif +# elif defined(__WIN__) + ::PIXELFORMATDESCRIPTOR *pfd; + int pfdID; +# elif defined(__AGL__) + AGLPixelFormat pf; + int pfID; +# endif + + bool canRGBA; // Can be used with RGBA contexts. + + bool canCI; // Can be used with color index + // contexts. + + int bufSize; // Total depth of color buffer. + + int level; // Framebuffer level. + // (<0 for underlay, 0 for main, + // >0 for overlay) + + bool db; // True if double buffered. + + bool stereo; // True if stereo-capable. + + int aux; // Number of aux color buffers. + + int r; // Depth of red channel. + + int g; // Depth of green channel. + + int b; // Depth of blue channel. + + int a; // Depth of alpha channel. + + int z; // Depth of ``z'' (depth) buffer. + + int s; // Depth of stencil buffer. + + int accR; // Depth of accum buf red channel. + + int accG; // Depth of accum buf green channel. + + int accB; // Depth of accum buf blue channel. + + int accA; // Depth of accum buf alpha channel. + + bool canWindow; // True if can be used for windows. + +# if defined(__X11__) + bool canPixmap; // True if can be used for pixmaps. +# if defined(GLX_VERSION_1_3) + bool canPBuffer; // True if can be used for pbuffers. + + int maxPBufferWidth; // Maximum width of PBuffer that + // may be created with this config. + + int maxPBufferHeight; // Maximum height of PBuffer that + // may be created with this config. + + int maxPBufferPixels; // Maximum size (in pixels) of + // PBuffer that may be created with + // this config. +# endif +# endif + + bool canWinSysRender; // True if the native window system + // can render to a drawable created + // with this config. + + bool fast; // True if config is probably + // hardware accelerated. (On GLX, + // it must not be marked ``slow.'') + + bool conformant; // True if config is advertised as + // conforming to the OpenGL spec. + + bool transparent; // True if config has some pixel value + // that is transparent (e.g., for + // overlays). + + int transR; // Transparent color red value. + + int transG; // Transparent color green value. + + int transB; // Transparent color blue value. + + int transA; // Transparent color alpha value. + + int transI; // Transparent color index value. + + // Utilities: + + string canonicalDescription(); + // Return a string containing all the attributes in a + // drawing surface configuration. This allows the config + // to be stored and recreated (essentially for use by + // configuration-matching algorithms in test result + // comparisons). + + string conciseDescription(); + // Return a description string that elides default + // attribute values and expresses some attributes in + // compressed form. Intended to be more easily readable + // for humans than the canonical description, at the + // cost of some ambiguity. + + int match(vector<DrawingSurfaceConfig*>& choices); + // Find the index of the config from ``choices'' that most + // closely matches the config specified by ``*this''. + // The matching scheme is heuristic, but intended to + // be good enough that test results for configs on one + // machine may be compared with test results for + // configs on another machine. + +}; // class DrawingSurfaceConfig + +} // namespace GLEAN + +#endif // __dsconfig_h__ diff --git a/tests/glean/dsfilt.cpp b/tests/glean/dsfilt.cpp new file mode 100644 index 00000000..9227bc6f --- /dev/null +++ b/tests/glean/dsfilt.cpp @@ -0,0 +1,690 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// dsfilt.cpp: Implementation of drawing surface configuration filtering + +#include <iostream> +#include <strstream> +#include <ctype.h> +#include <stdlib.h> +#include <algorithm> +#include "dsfilt.h" +#include "dsconfig.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructor: +/////////////////////////////////////////////////////////////////////////////// +DrawingSurfaceFilter::DrawingSurfaceFilter(const string& s): + lex(s.c_str(), true) { + + if (!varTableInitialized) + InitVarTable(); + + try { + GetSymbol(); + if (!ParseCriteria()) + throw Syntax("no criteria found", lex.position()); + } + catch (Lex::Lexical e) { + throw Syntax(e.err, e.position); + } + + // Make the final sort in order of increasing ID number: + EmitKey(MIN); + EmitKey(VAR_ID); +# if defined(GLX_VERSION_1_3) + EmitKey(MIN); + EmitKey(VAR_FBCID); +# endif +} // DrawingSurfaceFilter::DrawingSurfaceFilter + +/////////////////////////////////////////////////////////////////////////////// +// matches - determine if a drawing surface config matches the specified +// criteria +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::matches(DrawingSurfaceConfig& c) { + // Process the RPN expression in ``condition'', using the supplied + // drawing surface configuration to determine values of variables. + + vector<int> stack; + + for (vector<int>::const_iterator p = condition.begin(); + p < condition.end(); ++p) { + int right; + + switch (*p) { + case ADD: + right = stack.back(); stack.pop_back(); + stack.back() += right; + break; + case AND: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() && right; + break; + case DIV: + right = stack.back(); stack.pop_back(); + stack.back() = (right == 0)? 0: stack.back() / right; + break; + case EQ: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() == right; + break; + case GE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() >= right; + break; + case GT: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() > right; + break; + case LE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() <= right; + break; + case LT: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() < right; + break; + case MOD: + right = stack.back(); stack.pop_back(); + stack.back() = (right == 0)? 0: stack.back() % right; + break; + case MUL: + right = stack.back(); stack.pop_back(); + stack.back() *= right; + break; + case NE: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() != right; + break; + case NEGATE: + stack.back() = -stack.back(); + break; + case NOT: + stack.back() = !stack.back(); + break; + case OR: + right = stack.back(); stack.pop_back(); + stack.back() = stack.back() || right; + break; + case SUB: + right = stack.back(); stack.pop_back(); + stack.back() -= right; + break; + case CONSTANT: + stack.push_back(*++p); + break; + default: + // Must be a variable. + stack.push_back(FetchVariable(c, + static_cast<Token>(*p))); + break; + } + } + + return stack.back(); +} // DrawingSurfaceFilter::matches + +/////////////////////////////////////////////////////////////////////////////// +// filter - find and sort all matching drawing surface configurations +/////////////////////////////////////////////////////////////////////////////// +vector<DrawingSurfaceConfig*> +DrawingSurfaceFilter::filter(vector<DrawingSurfaceConfig*>& v) { + vector<DrawingSurfaceConfig*> result; + for (vector<DrawingSurfaceConfig*>::const_iterator p = v.begin(); + p < v.end(); ++p) { + if (matches(**p)) + result.push_back(*p); + } + + sort(result.begin(), result.end(), ConfigSort(sortKeys)); + return result; +} // DrawingSurfaceFilter::filter + +/////////////////////////////////////////////////////////////////////////////// +// ConfigSort operator() - sort comparison for final ordering of configurations +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ConfigSort::operator() + (const DrawingSurfaceConfig* c1, const DrawingSurfaceConfig* c2) { + for (vector<Token>::const_iterator p=keys.begin(); p<keys.end(); ++p) { + Token dir = *p++; + int d = FetchVariable(*c1, *p) - FetchVariable(*c2, *p); + if (dir == MAX) // sort largest first? + d = -d; + if (d < 0) + return true; // c1 goes before c2 + if (d > 0) + return false; // c1 goes after c2 + } + return false; // order doesn't matter +} + +/////////////////////////////////////////////////////////////////////////////// +// InitVarTable - initialize the table mapping variable names to token values +/////////////////////////////////////////////////////////////////////////////// + +map<string,DrawingSurfaceFilter::Token> DrawingSurfaceFilter::varTable; +bool DrawingSurfaceFilter::varTableInitialized; + +void +DrawingSurfaceFilter::InitVarTable() { + varTable["r"] = VAR_R; + varTable["g"] = VAR_G; + varTable["b"] = VAR_B; + varTable["a"] = VAR_A; + varTable["rgb"] = VAR_RGB; + varTable["rgba"] = VAR_RGBA; + varTable["ci"] = VAR_CI; + varTable["accumr"] = VAR_ACCUM_R; + varTable["accumg"] = VAR_ACCUM_G; + varTable["accumb"] = VAR_ACCUM_B; + varTable["accuma"] = VAR_ACCUM_A; + varTable["accumrgb"] = VAR_ACCUM_RGB; + varTable["accumrgba"] = VAR_ACCUM_RGBA; + varTable["aux"] = VAR_AUX; + varTable["db"] = VAR_DB; + varTable["sb"] = VAR_SB; + varTable["id"] = VAR_ID; + varTable["fbcid"] = VAR_FBCID; + varTable["level"] = VAR_LEVEL; + varTable["main"] = VAR_MAIN; + varTable["overlay"] = VAR_OVERLAY; + varTable["underlay"] = VAR_UNDERLAY; + varTable["mono"] = VAR_MONO; + varTable["stereo"] = VAR_STEREO; + varTable["ms"] = VAR_MS; + varTable["s"] = VAR_S; + varTable["z"] = VAR_Z; + varTable["fast"] = VAR_FAST; + varTable["conformant"] = VAR_CONFORMANT; + varTable["transparent"] = VAR_TRANSPARENT; + varTable["transr"] = VAR_TRANS_R; + varTable["transg"] = VAR_TRANS_G; + varTable["transb"] = VAR_TRANS_B; + varTable["transa"] = VAR_TRANS_A; + varTable["transci"] = VAR_TRANS_CI; + varTable["window"] = VAR_WINDOW; + varTable["pbuffer"] = VAR_PBUFFER; + varTable["pixmap"] = VAR_PIXMAP; + varTable["glonly"] = VAR_GL_ONLY; + varTable["max"] = MAX; + varTable["min"] = MIN; + + varTableInitialized = true; +} // DrawingSurfaceFilter::InitVarTable + +/////////////////////////////////////////////////////////////////////////////// +// FetchVariable - fetch the value of a variable from a +// DrawingSurfaceConfig +/////////////////////////////////////////////////////////////////////////////// + +int +DrawingSurfaceFilter::FetchVariable(const DrawingSurfaceConfig& c, Token v) { + switch (v) { + case VAR_R: + return c.r; + case VAR_G: + return c.g; + case VAR_B: + return c.b; + case VAR_A: + return c.a; + case VAR_RGB: + return min(c.r, min(c.g, c.b)); + case VAR_RGBA: + return min(c.r, min(c.g, min(c.b, c.a))); + + case VAR_CI: + return c.canCI? c.bufSize: 0; + + case VAR_ACCUM_R: + return c.accR; + case VAR_ACCUM_G: + return c.accG; + case VAR_ACCUM_B: + return c.accB; + case VAR_ACCUM_A: + return c.accA; + case VAR_ACCUM_RGB: + return min(c.accR, min(c.accG, c.accB)); + case VAR_ACCUM_RGBA: + return min(c.accR, min(c.accG, min(c.accB, c.accA))); + + case VAR_AUX: + return c.aux; + + case VAR_DB: + return c.db; + case VAR_SB: + return !c.db; + + case VAR_ID: +# if defined(__X11__) + return c.visID; +# elif defined(__WIN__) + return c.pfdID; +# endif + case VAR_FBCID: +# if defined(GLX_VERSION_1_3) + return c.fbcID; +# else + return 0; +# endif + + case VAR_LEVEL: + return c.level; + case VAR_MAIN: + return c.level == 0; + case VAR_OVERLAY: + return c.level > 0; + case VAR_UNDERLAY: + return c.level < 0; + + case VAR_MONO: + return !c.stereo; + break; + case VAR_STEREO: + return c.stereo; + + case VAR_MS: + // XXX Can't support this at the moment; have no way to + // compile or test. + return 0; + + case VAR_S: + return c.s; + + case VAR_Z: + return c.z; + + case VAR_FAST: + return c.fast; + case VAR_CONFORMANT: + return c.conformant; + + case VAR_TRANSPARENT: + return c.transparent; + case VAR_TRANS_R: + return c.transR; + case VAR_TRANS_G: + return c.transG; + case VAR_TRANS_B: + return c.transB; + case VAR_TRANS_A: + return c.transA; + case VAR_TRANS_CI: + return c.transI; + + case VAR_WINDOW: + return c.canWindow; + case VAR_PBUFFER: +# if defined(GLX_VERSION_1_3) + return c.canPBuffer; +# else + return 0; +# endif + case VAR_PIXMAP: +# if defined(__X11__) + return c.canPixmap; +# else + return 0; +# endif + + case VAR_GL_ONLY: + return !c.canWinSysRender; + + default: + throw InternalError(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// GetSymbol - Fetch next symbol from the input string +/////////////////////////////////////////////////////////////////////////////// +void +DrawingSurfaceFilter::GetSymbol() { + lex.next(); + switch(lex.token) { + case Lex::ID: + Symbol = varTable[lex.id]; + // Note: Because ERROR has value zero in the + // Token enumeration, if the user provides a + // variable that is not in varTable, then Symbol + // will be set to ERROR. + if (Symbol == ERROR) + throw Syntax("unrecognized variable", lex.position()); + break; + case Lex::ICONST: + Value = lex.iValue; + Symbol = CONSTANT; + break; + case Lex::OR_OR: + Symbol = OR; + break; + case Lex::AND_AND: + Symbol = AND; + break; + case Lex::LE: + Symbol = LE; + break; + case Lex::LT: + Symbol = LT; + break; + case Lex::GE: + Symbol = GE; + break; + case Lex::GT: + Symbol = GT; + break; + case Lex::EQ: + Symbol = EQ; + break; + case Lex::NE: + Symbol = NE; + break; + case Lex::BANG: + Symbol = NOT; + break; + case Lex::PLUS: + Symbol = ADD; + break; + case Lex::MINUS: + Symbol = SUB; + break; + case Lex::STAR: + Symbol = MUL; + break; + case Lex::SLASH: + Symbol = DIV; + break; + case Lex::PERCENT: + Symbol = MOD; + break; + case Lex::COMMA: + Symbol = SEPARATOR; + break; + case Lex::LPAREN: + Symbol = LPAREN; + break; + case Lex::RPAREN: + Symbol = RPAREN; + break; + case Lex::END: + Symbol = END; + break; + default: + throw Syntax("unrecognized symbol", lex.position()); + } + + return; +} // DrawingSurfaceFilter::GetSymbol + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithExpr +// Syntax: arithExpr -> arithTerm {('+'|'-') arithTerm} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithExpr() { + if (!ParseArithTerm()) + return false; + + for (;;) { + if (Symbol == ADD || Symbol == SUB) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithTerm()) + throw Syntax("missing operand of + or -", + lex.position()); + Emit(op); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithFactor +// Syntax: arithFactor -> ['+'|'-'|'!'] arithPrimary +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithFactor() { + if (Symbol == ADD || Symbol == SUB || Symbol == NOT) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithPrimary()) + throw Syntax("missing operand of unary +, -, or !", + lex.position()); + if (op == SUB) + Emit(NEGATE); + else if (op == NOT) + Emit(NOT); + return true; + } + + return ParseArithPrimary(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithPrimary +// Syntax: arithPrimary -> variable | constant | '(' expression ')' +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithPrimary() { + if (FIRST_VAR < Symbol && Symbol < LAST_VAR) { + Emit(Symbol); + GetSymbol(); + return true; + } + + if (Symbol == CONSTANT) { + Emit(CONSTANT); + Emit(Value); + GetSymbol(); + return true; + } + + if (Symbol == LPAREN) { + GetSymbol(); + if (!ParseExpression()) + throw Syntax("missing expression after (", + lex.position()); + if (Symbol == RPAREN) { + GetSymbol(); + return true; + } else + throw Syntax("missing )", lex.position()); + } + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseArithTerm +// Syntax: arithTerm -> arithFactor {('*'|'/'|'%') arithFactor} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseArithTerm() { + if (!ParseArithFactor()) + return false; + + for (;;) { + if (Symbol == MUL + || Symbol == DIV + || Symbol == MOD) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithFactor()) + throw Syntax("missing operand of *, /, or %", + lex.position()); + Emit(op); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseBoolFactor +// Syntax: boolFactor -> arithExpr [('<'|'>'|'<='|'>='|'=='|'!=') arithExpr] +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseBoolFactor() { + if (!ParseArithExpr()) + return false; + + if (Symbol == LT + || Symbol == GT + || Symbol == LE + || Symbol == GE + || Symbol == EQ + || Symbol == NE) { + Token op = Symbol; + GetSymbol(); + if (!ParseArithExpr()) + throw Syntax("missing operand of comparison", + lex.position()); + Emit(op); + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseBoolTerm +// Syntax: boolTerm -> boolFactor {'&&' boolFactor} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseBoolTerm() { + if (!ParseBoolFactor()) + return false; + + for (;;) { + if (Symbol == AND) { + GetSymbol(); + if (!ParseBoolFactor()) + throw Syntax("missing operand of &&", + lex.position()); + Emit(AND); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseCriteria +// Syntax: criteria -> criterion {',' criterion} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseCriteria() { + /* Process all the user-specified conditions and sort keys: */ + if (!ParseCriterion()) + return false; + + for (;;) { + if (Symbol == SEPARATOR) { + GetSymbol(); + if (!ParseCriterion()) + throw Syntax("missing criterion after comma", + lex.position()); + Emit(AND); + } else if (Symbol == END) + return true; + else + throw Syntax("expected comma or end of criteria", + lex.position()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseCriterion +// Syntax: criterion -> sortKey | expression +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseCriterion() { + if (ParseSortKey()) + return true; + return ParseExpression(); +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseExpression +// Syntax: expression -> boolTerm {'||' boolTerm} +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseExpression() { + if (!ParseBoolTerm()) + return false; + + for (;;) { + if (Symbol == OR) { + GetSymbol(); + if (!ParseBoolTerm()) + throw Syntax("missing operand of ||", + lex.position()); + Emit(OR); + } else + return true; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// ParseSortKey +// Syntax: sortKey -> ('max'|'min') variable +/////////////////////////////////////////////////////////////////////////////// +bool +DrawingSurfaceFilter::ParseSortKey() { + if (Symbol == MAX || Symbol == MIN) { + EmitKey(Symbol); + GetSymbol(); + if (FIRST_VAR < Symbol && Symbol < LAST_VAR) { + EmitKey(Symbol); + // + // When sorting, eliminate visuals with a zero value + // for the key. This is hard to justify on grounds + // of orthogonality, but it seems to yield the right + // behavior (especially for ``min''). + // + Emit(Symbol); + GetSymbol(); + return true; + } else + throw Syntax("missing variable name after sort key", + lex.position()); + } + + return false; +} // DrawingSurfaceFilter::ParseSortKey + + +} // namespace GLEAN diff --git a/tests/glean/dsfilt.h b/tests/glean/dsfilt.h new file mode 100644 index 00000000..89c6d583 --- /dev/null +++ b/tests/glean/dsfilt.h @@ -0,0 +1,268 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// dsfilt.h: Utilities for selecting (filtering) drawing surface configs + +// Given a string representing a Boolean expression involving +// attributes of drawing surface configurations, construct an internal +// representation of the expression which can be used to find matching +// configurations. The string may also include sorting criteria that +// will be used to select the order in which matching configurations +// are returned. + +// This class accepts a superset of the criteria supported by the +// visinfo package, originally released by SGI and used in the isfast +// library (among other things). Apologies for inconsistent naming +// conventions, capitalization, redundancy, etc.; they're due to an +// incomplete translation of the old C code. Here's the original +// copyright from visinfo, just in case the lawyers are interested: + +/* + * Copyright (c) 1994 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, + * provided that (i) the above copyright notices and this permission + * notice appear in all copies of the software and related documentation, + * and (ii) the name of Silicon Graphics may not be used in any + * advertising or publicity relating to the software without the specific, + * prior written permission of Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, + * INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + + +#ifndef __dsfilt_h__ +#define __dsfilt_h__ + +#include <string> +#include <vector> +#include <map> +#include "lex.h" + +using namespace std; + +namespace GLEAN { + +class DrawingSurfaceConfig; // forward reference + +class DrawingSurfaceFilter { + public: + + // Constructors: + + DrawingSurfaceFilter(const string& s); + // Creates a DrawingSurfaceFilter that implements the + // filtering and sorting criteria in the given string. + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Syntax: public Error { // Syntax error in string. + const char* err; + int position; + Syntax(const char* e, int p) { + err = e; + position = p; + } + }; + struct InternalError: public Error { // Shouldn't happen. + }; + + // Utilities: + + bool matches(DrawingSurfaceConfig& c); + // Returns true if the given DrawingSurfaceConfig matches + // the filter criteria. + + vector<DrawingSurfaceConfig*> filter(vector<DrawingSurfaceConfig*>& v); + // Returns a vector of DrawingSurfaceConfig pointers that + // match the filter criteria, sorted according to the sorting + // criteria. + + protected: + + typedef enum { + // These are items that may appear in the parsed + // representations of the filter or sort keys. + + // First, some special cases: + ERROR = 0, // erroneous token; must appear first + END, // end of expression + + // Next, arithmetic and Boolean operators: + + ADD, // C integer + + AND, // C integer && + DIV, // C integer / + EQ, // C integer == + GE, // C integer >= + GT, // C integer > + LE, // C integer <= + LT, // C integer < + MOD, // C integer % + MUL, // C integer * + NE, // C integer != + NEGATE, // C integer unary - + NOT, // C integer unary ! + OR, // C integer || + SUB, // C integer - + SEPARATOR, // comma, separating exprs and sort keys + LPAREN, // ( + RPAREN, // ) + + // Sort keys: + + MAX, // sort largest value first + MIN, // sort smallest value first + + // Finally, operands: + + CONSTANT, // integer constants + + FIRST_VAR, // marker; starts list of variables + + VAR_R, // red channel size + VAR_G, // green channel size + VAR_B, // blue channel size + VAR_A, // alpha channel size + VAR_RGB, // min(r, g, b) + VAR_RGBA, // min(r, g, b, a) + + VAR_CI, // color index channel size + + VAR_ACCUM_R, // accum buf red channel size + VAR_ACCUM_G, // accum buf green channel size + VAR_ACCUM_B, // accum buf blue channel size + VAR_ACCUM_A, // accum buf alpha channel size + VAR_ACCUM_RGB, // min(accum r, accum g, accum b) + VAR_ACCUM_RGBA, // min(accum r, accum g, accum b, accum a) + + VAR_AUX, // number of aux color buffers + + VAR_DB, // nonzero if double buffered + VAR_SB, // nonzero if single buffered + + VAR_ID, // X11 Visual or Win32 PixelFormat ID + VAR_FBCID, // GLXFBConfig ID + + VAR_LEVEL, // framebuffer level + VAR_MAIN, // nonzero for main buffers + VAR_OVERLAY, // nonzero for overlay buffers + VAR_UNDERLAY, // nonzero for underlay buffers + + VAR_MONO, // nonzero for monoscopic buffers + VAR_STEREO, // nonzero for stereoscopic buffers + + VAR_MS, // number of multisamples + + VAR_S, // stencil buffer depth + + VAR_Z, // depth (z) buffer depth + + VAR_FAST, // nonzero if accelerated (or not marked + // ``slow'' in GLX) + + VAR_CONFORMANT, // nonzero if conforms to OpenGL spec + + VAR_TRANSPARENT, // nonzero if some pixel value is + // transparent + VAR_TRANS_R, // transparent value red component + VAR_TRANS_G, // transparent value green component + VAR_TRANS_B, // transparent value blue component + VAR_TRANS_A, // transparent value alpha component + VAR_TRANS_CI, // transparent value color index + + VAR_WINDOW, // nonzero if can be used to create a window + VAR_PBUFFER, // nonzero if can be used to create a pbuffer + VAR_PIXMAP, // nonzero if can be used to create a pixmap + // XXXWIN need VAR_BITMAP, at least; + // possibly others + + VAR_GL_ONLY, // nonzero if only OpenGL can render into + // surfaces created with this config (i.e., + // the native window system *can't* render + // into them). + + LAST_VAR // marker; ends list of variables + } Token; + + vector<int> condition; + inline void Emit(Token op) {condition.push_back(static_cast<int>(op));} + inline void Emit(int v) {condition.push_back(v);} + vector<Token> sortKeys; + inline void EmitKey(Token key) {sortKeys.push_back(key);} + + // Expression-parsing state and utilities: + Lex lex; + Token Symbol; + int Value; + static map<string,Token> varTable; + static bool varTableInitialized; + + static int FetchVariable(const DrawingSurfaceConfig& c, Token v); + static void InitVarTable(); + bool ParseArithExpr(); + bool ParseArithFactor(); + bool ParseArithPrimary(); + bool ParseArithTerm(); + bool ParseBoolFactor(); + bool ParseBoolTerm(); + bool ParseCriteria(); + bool ParseCriterion(); + bool ParseExpression(); + bool ParseSortKey(); + void GetSymbol(); + + class ConfigSort { // comparison function-object used for sorting + protected: + vector<Token>& keys; + public: + ConfigSort(vector<Token>& k): keys(k) { } + bool operator() (const DrawingSurfaceConfig* c1, + const DrawingSurfaceConfig* c2); + }; + friend class ConfigSort; + +}; // class DrawingSurfaceFilter + +} // namespace GLEAN + +#endif // __dsfilt_h__ diff --git a/tests/glean/dsurf.cpp b/tests/glean/dsurf.cpp new file mode 100644 index 00000000..f158921e --- /dev/null +++ b/tests/glean/dsurf.cpp @@ -0,0 +1,263 @@ +// 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 + + + + +// dsurf.cpp: implementation of drawing surface utilities + +#include <iostream> +#include <algorithm> +#include "dsurf.h" +#include "dsconfig.h" +#include "winsys.h" + +namespace { + +#if defined(__X11__) + +Colormap +ChooseColormap(Display* dpy, XVisualInfo* vi) { + // We could be polite here and search for a standard colormap, + // but the normal mode of operation should be that glean is + // running alone, so there doesn't seem to be much point in sharing. + + return XCreateColormap(dpy, RootWindow(dpy, vi->screen), + vi->visual, AllocNone); +} // ChooseColormap + +#endif + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +DrawingSurface::DrawingSurface(WindowSystem& ws, DrawingSurfaceConfig& c) { + // Link back to enclosing window system, so as to simplify bookkeeping: + winSys = &ws; + ws.surfaces.push_back(this); + + // Save pointer to configuration information: + config = &c; +} // DrawingSurface::DrawingSurface + +Window::Window(WindowSystem& ws, DrawingSurfaceConfig& c, int w, int h, + int x, int y): + DrawingSurface(ws, c) { + +#if defined(__X11__) + +#if defined(GLX_VERSION_1_3) + if (ws.GLXVersMajor == 1 && ws.GLXVersMinor < 3) + goto legacyMethod; +// XXX Need GLX 1.3 window-creation code. For now, just fall through. +#endif + +legacyMethod: + // XXX There's basically no error-handling code here. + // See XErrorHandler(). + + // Create the window: + XSetWindowAttributes xswa; + xswa.background_pixmap = None; + xswa.border_pixel = 0; + xswa.colormap = ChooseColormap(winSys->dpy, config->vi); + xswa.event_mask = StructureNotifyMask; + xWindow = XCreateWindow(winSys->dpy, + RootWindow(winSys->dpy, config->vi->screen), + x, y, w, h, + 0, + config->vi->depth, + InputOutput, + config->vi->visual, + CWBackPixmap|CWBorderPixel|CWColormap|CWEventMask, + &xswa); + + // Set attributes for the benefit of the window manager: + XSizeHints sizeHints; + sizeHints.width = w; + sizeHints.height = h; + sizeHints.x = x; + sizeHints.y = y; + sizeHints.flags = USSize | USPosition; + XSetStandardProperties(winSys->dpy, xWindow, "glean", "glean", + None, 0, 0, &sizeHints); + + // Map the window and wait for it to appear: + XMapWindow(winSys->dpy, xWindow); + XEvent event; + for (;;) { + XNextEvent(winSys->dpy, &event); + if (event.type == MapNotify && event.xmap.window == xWindow) + break; + } + + +#elif defined(__WIN__) + // XXX There's basically no error-handling code here. + // create the window + RECT r; + int style = WS_POPUP | WS_CAPTION | WS_BORDER; + + r.left = x; + r.top = y; + r.right = r.left + w; + r.bottom = r.top + h; + AdjustWindowRect(&r,style,FALSE); + + hWindow = CreateWindow("glean","glean", + style | WS_VISIBLE, + r.left,r.top,r.right - r.left,r.bottom - r.top, + NULL, NULL, + GetModuleHandle(NULL), + NULL); + + if (!hWindow) + return; + + hDC = GetDC(hWindow); + + PIXELFORMATDESCRIPTOR pfd; + SetPixelFormat(hDC,config->pfdID,&pfd); + +#elif defined(__BEWIN__) + + tWindow = new GLTestWindow (BRect(x,y, x+w, y+h), "GL Test Window"); + tWindow->Show(); + +#elif defined(__AGL__) + Rect r ; + + //we need some extra room for the menu bar + r.left = x+16; + r.top = y+20; + if (h < 20) + r.bottom = r.top + 32; + else + r.bottom = r.top+w; + + if (w < 20) + r.right = r.left + 32; + else + r.right = r.left + w; + + macWindow = NewCWindow(nil, &r, (unsigned char*)"glean", true, documentProc, + (WindowPtr) -1, false, 0); + + SetPortWindowPort(macWindow); + +#endif +} // Window::Window + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// + +void +DrawingSurface::commonDestructorCode() { + remove(winSys->surfaces.begin(), winSys->surfaces.end(), this); +} // DrawingSurface::commonDestructorCode + +Window::~Window() { + +#if defined(__X11__) + XDestroyWindow(winSys->dpy, xWindow); +#elif defined(__WIN__) + ReleaseDC(hWindow,hDC); + DestroyWindow(hWindow); + +#elif defined(__BEWIN__) + + tWindow->Lock(); + tWindow->Quit(); + +#elif defined(__AGL__) +// ::CloseWindow(macWindow); +#endif + +} // Window::~Window + +/////////////////////////////////////////////////////////////////////////////// +// Utilities +/////////////////////////////////////////////////////////////////////////////// +void +Window::swap() { +# if defined(__X11__) + glXSwapBuffers(winSys->dpy, xWindow); +# elif defined(__WIN__) + SwapBuffers(hDC); +# elif defined(__BEWIN__) + tWindow->SwapBuffers(); +# elif defined(__AGL__) + aglSwapBuffers(aglGetCurrentContext()); +# endif +} // Window::swap + +#if defined(__WIN__) + +/////////////////////////////////////////////////////////////////////////////// +// Window procedure +/////////////////////////////////////////////////////////////////////////////// + +LRESULT CALLBACK +Window::WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) +{ + switch (message) + { + default : + return DefWindowProc(hwnd, message, wParam, lParam); + + } + + return FALSE; +} + +#endif + + +#if defined(__BEWIN__) + +GLTestWindow::GLTestWindow(BRect frame, const char *title) : + BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE) +{ + /* right now we just create all the buffers we can */ + tView = new BGLView(Bounds(), "glean_view", B_FOLLOW_ALL, B_WILL_DRAW, + BGL_RGB | BGL_DOUBLE | BGL_DEPTH | BGL_ALPHA | BGL_STENCIL | BGL_ACCUM ); + AddChild(tView); +} + +void +GLTestWindow::SwapBuffers() +{ + tView->SwapBuffers(); +} +#endif +} // namespace GLEAN diff --git a/tests/glean/dsurf.h b/tests/glean/dsurf.h new file mode 100644 index 00000000..9e81d965 --- /dev/null +++ b/tests/glean/dsurf.h @@ -0,0 +1,103 @@ +// 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 + + + + +// dsurf.h: utilities for manipulating drawing surfaces + +#ifndef __dsurf_h__ +#define __dsurf_h__ + +#include "glwrap.h" + +namespace GLEAN { + +class WindowSystem; // Forward and mutually-recursive references +class DrawingSurfaceConfig; + +class DrawingSurface { + public: + DrawingSurface(WindowSystem& ws, DrawingSurfaceConfig& c); + virtual ~DrawingSurface() { } + + WindowSystem* winSys; // Window system that owns this surface. + DrawingSurfaceConfig* config; // Configuration of this surface. + + protected: + void commonDestructorCode(); + +}; // class DrawingSurface + +/* we have to create a utility test window for BeOS */ +# if defined(__BEWIN__) +class GLTestWindow : public BWindow { +public: + GLTestWindow(BRect frame, const char *title); + void SwapBuffers(); +// void SwapBuffers( bool vSync ); + +private: + BGLView *tView; +}; +# endif + + +class Window: public DrawingSurface { + public: + Window(WindowSystem& ws, DrawingSurfaceConfig& c, int w, int h, + int x = 10, int y = 10); + ~Window(); + + // Utilities: + + void swap(); + + // XXX Add constructors for more specialized window creation -- + // for example, at a particular screen location, with a particular + // parent window, etc. -- as needed by new tests. + +# if defined(__X11__) + ::Window xWindow; +# elif defined(__WIN__) + ::HWND hWindow; + ::HDC hDC; + + ::HDC get_dc() const {return hDC;} + static LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam); + +# elif defined(__BEWIN__) + GLTestWindow *tWindow; +# elif defined(__AGL__) + ::WindowRef macWindow; +# endif +}; // class Window + +} // namespace GLEAN + +#endif // __dsurf_h__ diff --git a/tests/glean/environ.cpp b/tests/glean/environ.cpp new file mode 100644 index 00000000..d1447cd0 --- /dev/null +++ b/tests/glean/environ.cpp @@ -0,0 +1,161 @@ +// 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 + + + + +// environ.cpp: implementation of test environment class + +#include "environ.h" + +#if defined(__UNIX__) +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#elif defined(__MS__) + +#include <sys/stat.h> + +#endif + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructor +/////////////////////////////////////////////////////////////////////////////// +Environment::Environment(Options& opt): + options(opt), + log(cout), + winSys(opt) +{ +# if defined(__UNIX__) + + // If running tests, first create the results directory. + // Refuse to overwrite one that already exists. + if (opt.mode == Options::run) { + if (opt.overwrite) { + // remove existing db dir + // XXX using system() probably isn't ideal + char cmd[1000]; + snprintf(cmd, 999, "rm -rf %s", opt.db1Name.c_str()); + system(cmd); + } + if (mkdir(opt.db1Name.c_str(), 0755)) { + if (errno == EEXIST) + throw DBExists(); + else + throw DBCantOpen(opt.db1Name); + } + // If comparing previous runs, make a token attempt to verify + // that the two databases exist. + } else if (opt.mode == Options::compare) { + struct stat s; + if (stat(opt.db1Name.c_str(), &s) || !S_ISDIR(s.st_mode)) + throw DBCantOpen(opt.db1Name); + if (stat(opt.db2Name.c_str(), &s) || !S_ISDIR(s.st_mode)) + throw DBCantOpen(opt.db2Name); + } + +# elif defined(__MS__) + // If running tests, first create the results directory. + // Refuse to overwrite one that already exists. + if (opt.mode == Options::run) { + if (opt.overwrite) { + // XXX a Windows programmer needs to complete this + abort(); + } + if (!CreateDirectory(opt.db1Name.c_str(),0)) { + if (GetLastError() == ERROR_ALREADY_EXISTS) + throw DBExists(); + else + throw DBCantOpen(opt.db1Name); + } + // If comparing previous runs, make a token attempt to verify + // that the two databases exist. + } else if (opt.mode == Options::compare) { + struct _stat s; + + if (_stat(opt.db1Name.c_str(), &s) || !(s.st_mode & _S_IFDIR)) + throw DBCantOpen(opt.db1Name); + if (_stat(opt.db2Name.c_str(), &s) || !(s.st_mode & _S_IFDIR)) + throw DBCantOpen(opt.db2Name); + } + +# endif +} // Environment::Environment() + +/////////////////////////////////////////////////////////////////////////////// +// Results-file access utilities +/////////////////////////////////////////////////////////////////////////////// +string +Environment::resultFileName(string& dbName, string& testName) { +# if defined(__UNIX__) + string dirName(dbName + '/' + testName); + if (mkdir(dirName.c_str(), 0755)) { + if (errno != EEXIST) + throw DBCantOpen(dirName); + } + string fileName(dirName + "/results"); +# elif defined(__MS__) + string dirName(dbName + '/' + testName); + if (!CreateDirectory(dirName.c_str(),0)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + throw DBCantOpen(dirName); + } + string fileName(dirName + "/results"); +# endif + return fileName; +} // Environment::resultFileName + +string +Environment::imageFileName(string& dbName, string& testName, int n) { + char sn[4]; + sn[3] = 0; + sn[2] = static_cast<char>('0' + n % 10); + sn[1] = static_cast<char>('0' + (n / 10) % 10); + sn[0] = static_cast<char>('0' + (n / 100) % 10); +# if defined(__UNIX__) + string fileName(dbName + '/' + testName + "/i" + sn + ".tif"); +# elif defined(__MS__) + string fileName(dbName + '/' + testName + "/i" + sn + ".tif"); +# endif + return fileName; +} // Environment::imageFileName + +void +Environment::quiesce() { + winSys.quiesce(); +# if defined(__UNIX__) + sync(); +# elif defined(__MS__) +# endif +} // Environment::quiesce + +} // namespace GLEAN diff --git a/tests/glean/environ.h b/tests/glean/environ.h new file mode 100644 index 00000000..1bf08f0c --- /dev/null +++ b/tests/glean/environ.h @@ -0,0 +1,111 @@ +// 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 + + + + +// environ.h: global test environment + +// This class provides a facade for all the operating-system and +// window-system services that we need to run ``portable'' tests. +// Examples include logging services, opening streams to read or write +// database files, and gaining access to the window system. + + +#ifndef __environ_h__ +#define __environ_h__ + +#include <iostream> +#include "options.h" +#include "winsys.h" + +namespace GLEAN { + +class Image; // Forward and mutually-recursive references. + +class Environment { + public: + // Constructors: + Environment(Options& opt); + + // Exceptions: + struct Error { }; // Base class for all errors. + struct DBExists: public Error { // Output DB already exists. + }; + struct DBCantOpen: public Error { // Can't open a DB. + const string* db; + DBCantOpen(string& s) {db = &s;} + }; + + // Members: + Options options; // Global testing options. + + ostream& log; // Output stream used for logging results. + + WindowSystem winSys; // The window system providing the OpenGL + // implementation under test. + + string resultFileName(string& dbName, string& testName); + // Return name of results file for given + // test. Suitable for opening a stream. + // XXX Creates results directory as a side + // effect. Should separate this. + inline string resultFileName(string& testName) { + return resultFileName(options.db1Name, testName); + } + inline string result1FileName(string& testName) { + return resultFileName(options.db1Name, testName); + } + inline string result2FileName(string& testName) { + return resultFileName(options.db2Name, testName); + } + + string imageFileName(string& dbName, string& testName, int n); + // Return name of image file number ``n'' + // associated with the given test. Suitable + // for use with Image::readTIFF(), etc. + // XXX Doesn't create results directory, + // so resultFileName() must be called before + // using this. + inline string imageFileName(string& testName, int n) { + return imageFileName(options.db1Name, testName, n); + } + inline string image1FileName(string& testName, int n) { + return imageFileName(options.db1Name, testName, n); + } + inline string image2FileName(string& testName, int n) { + return imageFileName(options.db2Name, testName, n); + } + + void quiesce(); // Settle down before starting a benchmark. + +}; // class Environment + +} // namespace GLEAN + +#endif // __environ_h__ diff --git a/tests/glean/geomrend.cpp b/tests/glean/geomrend.cpp new file mode 100644 index 00000000..e8142a50 --- /dev/null +++ b/tests/glean/geomrend.cpp @@ -0,0 +1,504 @@ +// 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 +// 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 + + + +// geomrend.h: convenience object for rendering any geometry via +// a host of OpenGL paths: immediate mode (glVertex), vertex +// arrays with glDrawArrays, vertex arrays with glArrayElement, +// vertex arrays with glDrawElements, and any of the preceding +// methods stuffed in a display list. + +using namespace std; + +#include "geomrend.h" +#include "rand.h" +#include "glutils.h" +#include <algorithm> +#include <iostream> +#include <cmath> +#include <float.h> +#include <cassert> + +namespace GLEAN { + + +// geomrend.h: convenience object for rendering any geometry via +// a host of OpenGL paths: immediate mode (glVertex), vertex +// arrays with glDrawArrays, vertex arrays with glArrayElement, +// vertex arrays with glDrawElements, and any of the preceding +// methods stuffed in a display list. + +// Functions for the helper class ArrayData, which stores the info about each parameter's data. +ArrayData::ArrayData() +{ + size = 0; + type = GL_UNSIGNED_INT; + stride = 0; + pointer = 0; +} + +void ArrayData::setData(GLint sizeIn, GLenum typeIn, GLsizei strideIn, const GLvoid* pointerIn) +{ + size = sizeIn; + type = typeIn; + stride = strideIn; + pointer = pointerIn; + if (stride == 0) + { + stride = size; + switch(type) + { + case GL_BYTE: stride *= sizeof(GLbyte); break; + case GL_UNSIGNED_BYTE: stride *= sizeof(GLubyte); break; + case GL_SHORT: stride *= sizeof(GLshort); break; + case GL_UNSIGNED_SHORT: stride *= sizeof(GLushort); break; + case GL_INT: stride *= sizeof(GLint); break; + case GL_UNSIGNED_INT: stride *= sizeof(GLuint); break; + case GL_FLOAT: stride *= sizeof(GLfloat); break; + case GL_DOUBLE: stride *= sizeof(GLdouble); break; + default: assert(false); + } + } +} + +// Only a default constructor. +GeomRenderer::GeomRenderer() : vertexData(), colorData(), texCoordData(), normalData() +{ + drawMethod = GLVERTEX_MODE; + parameterBits = 0; + compileArrays = false; + + indicesCount = 0; + indicesType = GL_UNSIGNED_INT; + indices = 0; + + arrayLength = 0; +} + +// Used to set the method by which this GeomRenderer will pass the primitive data to the GL. +// Default is GLVERTEX_MODE. +void GeomRenderer::setDrawMethod(GeomRenderer::DrawMethod method) +{ + drawMethod = method; +} + +GeomRenderer::DrawMethod GeomRenderer::getDrawMethod() const +{ + return drawMethod; +} + +// Used to set the various parameters that are either enabled or disabled. Example usage: +// to tell the GeomRenderer to pass vertex, color, and texcoord data, but not normals, +// call setParameterBits(COLOR_BIT | TEXTURE_COORD_BIT). (Vertex data is implicitly enabled +// all the time.) The default is that only vertex data is enabled. +void GeomRenderer::setParameterBits(GLuint bits) +{ + parameterBits = bits; +} + +GLuint GeomRenderer::getParameterBits() const +{ + return parameterBits; +} + +// Used to specify whether EXT_compiled_vertex_array should be used if present. Default is false. +// If set to true, the arrays are kept unlocked and only locked just before rendering calls are issued. +// If you call setArraysCompiled(true) and the extension is not present, the function returns false +// and acts as though you had passed false in as the argument. +bool GeomRenderer::setArraysCompiled(bool compile) +{ + // Make sure we have the extension. + if (!GLUtils::haveExtension("GL_EXT_compiled_vertex_array") && compile == true) + { + compileArrays = false; + return false; + } + + compileArrays = compile; + return true; +} + +bool GeomRenderer::getArraysCompiled() const +{ + return compileArrays; +} + +// If you're using GLDRAWELEMENTS_MODE, GLARRAYELEMENT_MODE, or GLVERTEX_MODE, you need to give +// it the indices to pass into the GL. +void GeomRenderer::setVArrayIndices(GLuint count, GLenum type, const GLvoid* indicesIn) +{ + assert(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT); + + indicesCount = count; + indicesType = type; + indices = indicesIn; +} + +// This hands the actual primitive data to the GeomRenderer. It holds onto these as pointers, +// rather than copying them, so don't delete the data until you're done with the GeomRenderer. +// These are prototypically equivalent to their respective GL calls, except that there's an extra +// argument on the front of the vertex function for how many elements are in the array (this is +// atomic; if you pass in 5, it means there are 5 vertices, not 5 floats or bytes or whatever). +// The lengths of all other arrays are assumed to be >= the size passed in for the vertex array. +void GeomRenderer::setVertexPointer(GLuint length, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) +{ + arrayLength = length; + vertexData.setData(size, type, stride, pointer); +} + +void GeomRenderer::setColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) +{ + colorData.setData(size, type, stride, pointer); +} + +void GeomRenderer::setTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer) +{ + texCoordData.setData(size, type, stride, pointer); +} + +void GeomRenderer::setNormalPointer(GLenum type, GLsizei stride, const GLvoid* pointer) +{ + normalData.setData(3, type, stride, pointer); +} + +// Finally, the actual calls to do something with all this data. You can either choose to render +// it given the configuration, or generate a display list of rendering it with the given +// configuration (uses GL_COMPILE mode to build the list). Fails if insufficient data has +// been given (i.e. if you don't give it an array for an enabled parameter, if you don't +// give it an array of indices when it needs them). +// Note that rendering with GLVERTEX_MODE currently involves a lot of CPU overhead to +// unpack the data and pass it to the GL; while the results will be correct, it would be +// unwise to use this method for rendering that is to be benchmarked, because it will +// underestimate performance significantly on some machines. +bool GeomRenderer::renderPrimitives(GLenum mode) +{ + if (!isReadyToRender()) + { + return false; + } + + // Okay, different sections here depending on what we're doing. + if (drawMethod == GLVERTEX_MODE) + { + glBegin(mode); + for (unsigned int x=0; x<indicesCount; x++) + { + int directIndex = getIndex(x); + if (parameterBits & COLOR_BIT) sendColor(directIndex); + if (parameterBits & TEXTURE_COORD_BIT) sendTexCoord(directIndex); + if (parameterBits & NORMAL_BIT) sendNormal(directIndex); + sendVertex(directIndex); + } + glEnd(); + } + // Otherwise it has something to do with arrays; set up the arrays. + else + { + if (parameterBits & COLOR_BIT) + { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(colorData.size, colorData.type, colorData.stride, colorData.pointer); +// std::cout << "Enabled color arrays, size [" << colorData.size << "], type [" << colorData.type +// << "], stride [" << colorData.stride << "], pointer [" << colorData.pointer << "]" << std::endl; + } + if (parameterBits & TEXTURE_COORD_BIT) + { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(texCoordData.size, texCoordData.type, texCoordData.stride, texCoordData.pointer); +// std::cout << "Enabled texCoord arrays, size [" << texCoordData.size << "], type [" << texCoordData.type +// << "], stride [" << texCoordData.stride << "], pointer [" << texCoordData.pointer << "]" << std::endl; + } + if (parameterBits & NORMAL_BIT) + { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(normalData.type, normalData.stride, normalData.pointer); +// std::cout << "Enabled normal arrays, size [" << normalData.size << "], type [" << normalData.type +// << "], stride [" << normalData.stride << "], pointer [" << normalData.pointer << "]" << std::endl; + } + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(vertexData.size, vertexData.type, vertexData.stride, vertexData.pointer); +// std::cout << "Enabled vertex arrays, size [" << vertexData.size << "], type [" << vertexData.type +// << "], stride [" << vertexData.stride << "], pointer [" << vertexData.pointer << "]" << std::endl; + + // Should we lock? + if (compileArrays) + { + PFNGLLOCKARRAYSEXTPROC glLockArraysEXT = 0; + assert(GLUtils::haveExtension("GL_EXT_compiled_vertex_array")); + glLockArraysEXT = reinterpret_cast<PFNGLLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glLockArraysEXT")); + glLockArraysEXT(0, arrayLength); + } + + // Okay, arrays configured; what exactly are we doing? + if (drawMethod == GLARRAYELEMENT_MODE) + { + glBegin(mode); + for (unsigned int x=0; x<indicesCount; x++) + { + glArrayElement(getIndex(x)); + } + glEnd(); + } + else if (drawMethod == GLDRAWARRAYS_MODE) + { + glDrawArrays(mode, 0, arrayLength); + std::cout << "Called glDrawArrays, mode [" << mode << "], from 0 to " << arrayLength << std::endl; + } + else if (drawMethod == GLDRAWELEMENTS_MODE) + { + glDrawElements(mode, indicesCount, indicesType, indices); + } + + // Done. If we locked, unlock. + if (compileArrays) + { + PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = 0; + assert(GLUtils::haveExtension("GL_EXT_compiled_vertex_array")); + glUnlockArraysEXT = reinterpret_cast<PFNGLUNLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glUnlockArraysEXT")); + glUnlockArraysEXT(); + } + } + + return true; +} + +bool GeomRenderer::generateDisplayList(GLenum mode, GLint& listHandleOut) +{ + if (!isReadyToRender()) + { + return false; + } + + listHandleOut = glGenLists(1); + glNewList(listHandleOut, GL_COMPILE); + assert(renderPrimitives(mode)); + glEndList(); + + return true; +} + +bool GeomRenderer::isReadyToRender() +{ + // Make sure we have vertex data. + if (vertexData.pointer == 0) return false; + + // For the enabled parameters, make sure we have them, too. + if ((parameterBits & COLOR_BIT ) && (colorData.pointer == 0)) return false; + if ((parameterBits & TEXTURE_COORD_BIT) && (texCoordData.pointer == 0)) return false; + if ((parameterBits & NORMAL_BIT ) && (normalData.pointer == 0)) return false; + + // If we need indices, we'd better have them. + if ((drawMethod == GLVERTEX_MODE || + drawMethod == GLARRAYELEMENT_MODE || + drawMethod == GLDRAWELEMENTS_MODE) && indices == 0) + { + return false; + } + + // Otherwise we're good to go! + return true; +} + +// This unpacks the indices depending on their format and returns the specified one. +GLuint GeomRenderer::getIndex(int indicesIndex) +{ + assert(indicesIndex >= 0 && indicesIndex < static_cast<int>(indicesCount)); + + switch (indicesType) + { + case GL_UNSIGNED_BYTE: + return ((GLubyte*)indices)[indicesIndex]; + break; + + case GL_UNSIGNED_SHORT: + return ((GLushort*)indices)[indicesIndex]; + break; + + case GL_UNSIGNED_INT: + return ((GLuint*)indices)[indicesIndex]; + break; + + default: + assert(false); + break; + } + + // It never gets here, but let's quell the compiler warning... + return 0; +} + +// I thought about making a lookup table for this, but it would involve an STL map of STL vectors +// and some weird function casts, so I'm doing it the naive way instead. +void GeomRenderer::sendVertex(GLuint vertexIndex) +{ + assert(vertexData.size >= 2 && vertexData.size <= 4); + + switch(vertexData.type) + { + case GL_SHORT: + if (vertexData.size == 2) glVertex2sv((const GLshort*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 3) glVertex3sv((const GLshort*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 4) glVertex4sv((const GLshort*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + break; + + case GL_INT: + if (vertexData.size == 2) glVertex2iv((const GLint*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 3) glVertex3iv((const GLint*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 4) glVertex4iv((const GLint*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + break; + + case GL_FLOAT: + if (vertexData.size == 2) glVertex2fv((const GLfloat*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 3) glVertex3fv((const GLfloat*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 4) glVertex4fv((const GLfloat*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + break; + + case GL_DOUBLE: + if (vertexData.size == 2) glVertex2dv((const GLdouble*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 3) glVertex3dv((const GLdouble*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + if (vertexData.size == 4) glVertex4dv((const GLdouble*)((const char*)vertexData.pointer + vertexIndex*vertexData.stride)); + break; + } +} + +void GeomRenderer::sendColor(GLuint colorIndex) +{ + assert(colorData.size == 3 || colorData.size == 4); + + switch(colorData.type) + { + case GL_BYTE: + if (colorData.size == 3) glColor3bv((const GLbyte*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4bv((const GLbyte*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_UNSIGNED_BYTE: + if (colorData.size == 3) glColor3ubv((const GLubyte*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4ubv((const GLubyte*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_SHORT: + if (colorData.size == 3) glColor3sv((const GLshort*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4sv((const GLshort*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_UNSIGNED_SHORT: + if (colorData.size == 3) glColor3usv((const GLushort*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4usv((const GLushort*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_INT: + if (colorData.size == 3) glColor3iv((const GLint*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4iv((const GLint*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_UNSIGNED_INT: + if (colorData.size == 3) glColor3uiv((const GLuint*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4uiv((const GLuint*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_FLOAT: + if (colorData.size == 3) glColor3fv((const GLfloat*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4fv((const GLfloat*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + + case GL_DOUBLE: + if (colorData.size == 3) glColor3dv((const GLdouble*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + if (colorData.size == 4) glColor4dv((const GLdouble*)((const char*)colorData.pointer + colorIndex*colorData.stride)); + break; + } +} + +void GeomRenderer::sendTexCoord(GLuint texCoordIndex) +{ + assert(texCoordData.size >= 1 && texCoordData.size <= 4); + + switch(texCoordData.type) + { + case GL_SHORT: + if (texCoordData.size == 1) glTexCoord1sv((const GLshort*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 2) glTexCoord2sv((const GLshort*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 3) glTexCoord3sv((const GLshort*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 4) glTexCoord4sv((const GLshort*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + break; + + case GL_INT: + if (texCoordData.size == 1) glTexCoord1iv((const GLint*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 2) glTexCoord2iv((const GLint*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 3) glTexCoord3iv((const GLint*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 4) glTexCoord4iv((const GLint*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + break; + + case GL_FLOAT: + if (texCoordData.size == 1) glTexCoord1fv((const GLfloat*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 2) glTexCoord2fv((const GLfloat*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 3) glTexCoord3fv((const GLfloat*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 4) glTexCoord4fv((const GLfloat*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + break; + + case GL_DOUBLE: + if (texCoordData.size == 1) glTexCoord1dv((const GLdouble*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 2) glTexCoord2dv((const GLdouble*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 3) glTexCoord3dv((const GLdouble*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + if (texCoordData.size == 4) glTexCoord4dv((const GLdouble*)((const char*)texCoordData.pointer + texCoordIndex*texCoordData.stride)); + break; + } +} + +void GeomRenderer::sendNormal(GLuint normalIndex) +{ + assert(normalData.size == 3); + + switch(normalData.type) + { + case GL_BYTE: + glNormal3bv((const GLbyte*)((const char*)normalData.pointer + normalIndex*normalData.stride)); + break; + + case GL_SHORT: + glNormal3sv((const GLshort*)((const char*)normalData.pointer + normalIndex*normalData.stride)); + break; + + case GL_INT: + glNormal3iv((const GLint*)((const char*)normalData.pointer + normalIndex*normalData.stride)); + break; + + case GL_FLOAT: + glNormal3fv((const GLfloat*)((const char*)normalData.pointer + normalIndex*normalData.stride)); + break; + + case GL_DOUBLE: + glNormal3dv((const GLdouble*)((const char*)normalData.pointer + normalIndex*normalData.stride)); + break; + } +} + +} // namespace GLEAN diff --git a/tests/glean/geomrend.h b/tests/glean/geomrend.h new file mode 100644 index 00000000..31678b28 --- /dev/null +++ b/tests/glean/geomrend.h @@ -0,0 +1,146 @@ +// 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 +// 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 + + + + +// geomrend.h: convenience object for rendering any geometry via +// a host of OpenGL paths: immediate mode (glVertex), vertex +// arrays with glDrawArrays, vertex arrays with glArrayElement, +// vertex arrays with glDrawElements, and any of the preceding +// methods stuffed in a display list. + +#ifndef __geomrend_h__ +#define __geomrend_h__ + +#include "glwrap.h" +#include <cassert> + +namespace GLEAN { + +// A helper class to store parameter array data. +class ArrayData { +public: + GLint size; + GLenum type; + GLsizei stride; + const GLvoid* pointer; + + ArrayData(); + void setData(GLint sizeIn, GLenum typeIn, GLsizei strideIn, const GLvoid* pointerIn); +}; + +class GeomRenderer { + public: + // These indicate the methods of passing the primitive data to OpenGL. Note that whether + // the arrays are locked or not is an independent variable, not part of the method. See + // setArraysLocked and getArraysLocked. + enum DrawMethod {GLVERTEX_MODE, GLARRAYELEMENT_MODE, GLDRAWARRAYS_MODE, GLDRAWELEMENTS_MODE}; + + // Sorry, no indices, and especially no silly edge flags. There's no vertex bit because + // vertex data always implicitly enabled (you can't draw anything without vertex data). + enum ParameterBits {COLOR_BIT = 1, TEXTURE_COORD_BIT = 2, NORMAL_BIT = 4}; + + // Only a default constructor. + GeomRenderer(); + + // Used to set the method by which this GeomRenderer will pass the primitive data to the GL. + // Default is GLVERTEX_MODE. + void setDrawMethod(DrawMethod); + DrawMethod getDrawMethod() const; + + // Used to set the various parameters that are either enabled or disabled. Example usage: + // to tell the GeomRenderer to pass vertex, color, and texcoord data, but not normals, + // call setParameterBits(COLOR_BIT | TEXTURE_COORD_BIT). (Vertex data is implicitly enabled + // all the time.) The default is that only vertex data is enabled. + void setParameterBits(GLuint bits); + GLuint getParameterBits() const; + + // Used to specify whether EXT_compiled_vertex_array should be used if present. Default is false. + // If set to true, the arrays are kept unlocked and only locked just before rendering calls are issued. + // If you call setArraysCompiled(true) and the extension is not present, the function returns false + // and acts as though you had passed false in as the argument. + bool setArraysCompiled(bool); + bool getArraysCompiled() const; + + // If you're using GLDRAWELEMENTS_MODE, GLARRAYELEMENT_MODE, or GLVERTEX_MODE, you need to give + // it the indices to pass into the GL. + void setVArrayIndices(GLuint count, GLenum type, const GLvoid* indices); + + // This hands the actual primitive data to the GeomRenderer. It holds onto these as pointers, + // rather than copying them, so don't delete the data until you're done with the GeomRenderer. + // These are prototypically equivalent to their respective GL calls, except that there's an extra + // argument on the front of the vertex function for how many elements are in the array (this is + // atomic; if you pass in 5, it means there are 5 vertices, not 5 floats or bytes or whatever). + // The lengths of all other arrays are assumed to be >= the size passed in for the vertex array. + void setVertexPointer(GLuint arrayLength, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer); + void setColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer); + void setTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* pointer); + void setNormalPointer(GLenum type, GLsizei stride, const GLvoid* pointer); + + // Finally, the actual calls to do something with all this data. You can either choose to render + // it given the configuration, or generate a display list of rendering it with the given + // configuration (uses GL_COMPILE mode to build the list). Fails if insufficient data has + // been given (i.e. if you don't give it an array for an enabled parameter, if you don't + // give it an array of indices when it needs them). + bool renderPrimitives(GLenum mode); + bool generateDisplayList(GLenum mode, GLint& listHandleOut); + + private: + bool isReadyToRender(); + + // Helper functions for unpacking and translating the data from the indices, vertices, colors, + // texcoords, and normals arrays. + GLuint getIndex(int); + void sendVertex(GLuint index); + void sendColor(GLuint index); + void sendTexCoord(GLuint index); + void sendNormal(GLuint index); + + DrawMethod drawMethod; + GLuint parameterBits; + bool compileArrays; + + GLuint indicesCount; + GLenum indicesType; + const GLvoid* indices; + + GLuint arrayLength; + + ArrayData vertexData; + ArrayData colorData; + ArrayData texCoordData; + ArrayData normalData; +}; + +} // namespace GLEAN + +#endif // __geomrend_h__ + + + diff --git a/tests/glean/geomutil.cpp b/tests/glean/geomutil.cpp new file mode 100644 index 00000000..ab8ab2dc --- /dev/null +++ b/tests/glean/geomutil.cpp @@ -0,0 +1,360 @@ +// 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 +// 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 + + + +// geomutil.cpp: frequently-used geometric operations + +using namespace std; + +#include "geomutil.h" +#include "rand.h" +#include <cassert> +#include <algorithm> +#include <cmath> +#include <float.h> +#include <stdio.h> + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// RandomMesh2D: Generate 2D array with fixed boundaries but interior points +// that have been perturbed randomly. +/////////////////////////////////////////////////////////////////////////////// +RandomMesh2D::RandomMesh2D(float minX, float maxX, int xPoints, + float minY, float maxY, int yPoints, + RandomDouble& rand) { + m = new float[xPoints * yPoints * 2]; + rowLength = xPoints; + + // Loop var; we declare it here and not in the for loop because + // different compilers scope variables differently when + // declared in a for loop. + int iy; + + // Drop each point squarely into the center of its grid cell: + for (iy = 0; iy < yPoints; ++iy) + for (int ix = 0; ix < xPoints; ++ix) { + float* v = (*this)(iy, ix); + v[0] = minX + (ix * (maxX - minX)) / (xPoints - 1); + v[1] = minY + (iy * (maxY - minY)) / (yPoints - 1); + } + // Now perturb each interior point, but only within its cell: + double deltaX = 0.9 * (maxX - minX) / (xPoints - 1); + double deltaY = 0.9 * (maxY - minY) / (yPoints - 1); + for (iy = 1; iy < yPoints - 1; ++iy) + for (int ix = 1; ix < xPoints - 1; ++ix) { + float* v = (*this)(iy, ix); + v[0] += deltaX * (rand.next() - 0.5); + v[1] += deltaY * (rand.next() - 0.5); + } +} // RandomMesh2D::RandomMesh2D + +RandomMesh2D::~RandomMesh2D() { + delete[] m; +} // RandomMesh2D::~RandomMesh2D + + + +/////////////////////////////////////////////////////////////////////////////// +// SpiralStrip2D: Generate (x,y) vertices for a triangle strip of arbitrary +// length. The triangles are of approximately equal size, and arranged +// in a spiral so that a reasonably large number of triangles can be +// packed into a small screen area. +/////////////////////////////////////////////////////////////////////////////// +SpiralStrip2D::SpiralStrip2D(int nPoints, float minX, float maxX, + float minY, float maxY) { + + // Most of the complexity of this code results from attempting + // to keep the triangles approximately equal in area. + // + // Conceptually, we construct concentric rings whose inner and + // outer radii differ by a constant. We then split each ring + // (at theta == 0), and starting from the point of the split + // gradually increase both the inner and outer radii so that + // when we've wrapped all the way around the ring (to theta == + // 2*pi), the inner radius now matches the original outer + // radius. We then repeat the process with the next ring + // (working from innermost to outermost) until we've + // accumulated enough vertices to satisfy the caller's + // requirements. + // + // Finally, we scale and offset all the points so that the + // resulting spiral fits comfortably within the rectangle + // provided by the caller. + + // Set up the array of vertices: + v = new float[2 * nPoints]; + float* lastV = v + 2 * nPoints; + + // Initialize the ring parameters: + double innerRadius = 4.0; + double ringWidth = 1.0; + double segLength = 1.0; + + float* pV = v; + while (pV < lastV) { + // Each ring consists of segments. We'll make the arc + // length of each segment that lies on the inner + // radius approximately equal to segLength, but we'll + // adjust it to ensure there are an integral number of + // equal-sized segments in the ring. + int nSegments = static_cast<int> + (6.2831853 * innerRadius / segLength + 0.5); + + double dTheta = 6.2831853 / nSegments; + double dRadius = ringWidth / nSegments; + + double theta = 0.0; + for (int i = 0; i < nSegments; ++i) { + double c = cos(theta); + double s = sin(theta); + + *pV++ = innerRadius * c; + *pV++ = innerRadius * s; + if (pV >= lastV) + break; + + *pV++ = (innerRadius + ringWidth) * c; + *pV++ = (innerRadius + ringWidth) * s; + if (pV >= lastV) + break; + + theta += dTheta; + innerRadius += dRadius; + } + } + + // Find the bounding box for the spiral: + float lowX = FLT_MAX; + float highX = - FLT_MAX; + float lowY = FLT_MAX; + float highY = -FLT_MAX; + for (pV = v; pV < lastV; pV += 2) { + lowX = min(lowX, pV[0]); + highX = max(highX, pV[0]); + lowY = min(lowY, pV[1]); + highY = max(highY, pV[1]); + } + + // Find scale and offset to map the spiral into the bounds supplied + // by our caller, with a little bit of margin around the edges: + lowX -= ringWidth; + highX += ringWidth; + lowY -= ringWidth; + highY += ringWidth; + float scaleX = (maxX - minX) / (highX - lowX); + float offsetX = minX - scaleX * lowX; + float scaleY = (maxY - minY) / (highY - lowY); + float offsetY = minY - scaleY * lowY; + + // Finally scale and offset the constructed vertices so that + // they fit in the caller-supplied rectangle: + for (pV = v; pV < lastV; pV += 2) { + pV[0] = scaleX * pV[0] + offsetX; + pV[1] = scaleY * pV[1] + offsetY; + } +} // SpiralStrip2D::SpiralStrip2D + +SpiralStrip2D::~SpiralStrip2D() { + delete[] v; +} // SpiralStrip2D::~SpiralStrip2D + + + + +/////////////////////////////////////////////////////////////////////////////// +// SpiralTri2D: Generate (x,y) vertices for a set of independent triangles, +// arranged in spiral fashion exactly as in SpiralStrip2D. +// One may rely on the fact that SpiralTri2D generates exactly the +// same triangles as SpiralStrip2D, so that comparison of images +// using the two primitives is meaningful. +/////////////////////////////////////////////////////////////////////////////// +SpiralTri2D::SpiralTri2D(int nTris, float minX, float maxX, + float minY, float maxY) { + SpiralStrip2D ts(nTris + 2, minX, maxX, minY, maxY); + const int nVertices = 3 * nTris; + v = new float[2 * nVertices]; + + float* pTris = v; + float* pStrip = ts(0); + bool front = true; // winding order alternates in strip + for (int i = 0; i < nTris; ++i) { + if (front) { + pTris[0] = pStrip[0]; + pTris[1] = pStrip[1]; + + pTris[2] = pStrip[2]; + pTris[3] = pStrip[3]; + + pTris[4] = pStrip[4]; + pTris[5] = pStrip[5]; + } else { + pTris[0] = pStrip[0]; + pTris[1] = pStrip[1]; + + pTris[2] = pStrip[4]; + pTris[3] = pStrip[5]; + + pTris[4] = pStrip[2]; + pTris[5] = pStrip[3]; + } + + front = !front; + pTris += 6; + pStrip += 2; + } +} // SpiralTri2D::SpiralTri2D + +SpiralTri2D::~SpiralTri2D() { + delete[] v; +} // SpiralTri2D::~SpiralTri2D + + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sphere3D: Forms a stacks/slices sphere and can return the vertices and index list for drawing it. +////////////////////////////////////////////////////////////////////////////////////////////////////// +Sphere3D::Sphere3D(float radius, int slices, int stacks) +{ + // Loop vars. + int curStack, curSlice; + + // Can't have a sphere of less than 2 slices or stacks. + assert(slices >= 2 && stacks >= 2); + + // We have 2 verts for the top and bottom point, and then slices*(stacks-1) more for the + // middle rings (it's stacks-1 since the top and bottom points each count in the stack count). + numVertices = 2 + (slices*(stacks-1)); + vertices.reserve(numVertices*3); + normals.reserve(numVertices*3); + + // The top and bottom slices have <slices> tris in them, and the ones in the middle (since they're + // made of quads) have 2*<slices> each. + numIndices = 3*(2*slices + 2*(stacks-2)*slices); + indices.reserve(numIndices); + +#define VX(i) vertices[3*(i)+0] +#define VY(i) vertices[3*(i)+1] +#define VZ(i) vertices[3*(i)+2] +#define VINDEX(st,sl) (1 + (((st)-1)*slices) + (sl)) +#ifndef M_PI +#define M_PI 3.14159 +#endif + + // Generate the verts. The bottom and top verts are kind of special cases (they + // occupy the first and last vertex slots, respectively). + vertices.push_back(0); + vertices.push_back(0); + vertices.push_back(-radius); + normals.push_back(0); + normals.push_back(0); + normals.push_back(-1); + + // Now the inner rings; I can't decide whether it spreads the tri area out better to do this by + // increments in the spherical coordinate phi or in the cartesian z, but I think phi is a better bet. + for (curStack=1; curStack<stacks; curStack++) + { + float phi = M_PI - ((curStack / (float)stacks) * M_PI); + float zVal = radius * cos(phi); + float sliceRadius = sqrt(radius*radius - zVal*zVal); + for (curSlice = 0; curSlice < slices; curSlice++) + { + float theta = 2*M_PI*((float)curSlice / slices); + + float xVal = sliceRadius*cos(theta); + float yVal = sliceRadius*sin(theta); + + vertices.push_back(xVal); + vertices.push_back(yVal); + vertices.push_back(zVal); + normals.push_back(xVal/radius); + normals.push_back(yVal/radius); + normals.push_back(zVal/radius); + } + } + + vertices.push_back(0); + vertices.push_back(0); + vertices.push_back(radius); + normals.push_back(0); + normals.push_back(0); + normals.push_back(1); + + // Now to assemble them into triangles. Do the top and bottom slices first. + for (curSlice=0; curSlice<slices; curSlice++) + { + indices.push_back(0); + indices.push_back((curSlice+1)%slices + 1); + indices.push_back(curSlice+1); + + indices.push_back(numVertices - 1); + indices.push_back(numVertices - 2 - ((curSlice+1)%slices)); + indices.push_back(numVertices - 2 - curSlice); + } + + // Now for the inner rings. We're already done with 2*slices triangles, so start after that. + for (curStack=1; curStack<stacks-1; curStack++) + { + for (curSlice=0; curSlice<slices; curSlice++) + { + int nextStack = curStack+1; + int nextSlice = (curSlice+1)%slices; + indices.push_back(VINDEX(curStack, curSlice)); + indices.push_back(VINDEX(curStack, nextSlice)); + indices.push_back(VINDEX(nextStack, nextSlice)); + + indices.push_back(VINDEX(curStack, curSlice)); + indices.push_back(VINDEX(nextStack, nextSlice)); + indices.push_back(VINDEX(nextStack, curSlice)); + } + } + + assert(static_cast<int>(vertices.size()) == numVertices*3); + assert(static_cast<int>(indices.size()) == numIndices); + +#undef VX +#undef VY +#undef VZ +#undef VINDEX +} + +// This returns the vertices: 3 floats per vertex in a tightly packed array (no padding between vertices). +const float* Sphere3D::getVertices() const { return &(vertices[0]); } +int Sphere3D::getNumVertices() const { return numVertices; } + +// This returns the normals; same data format as the vertices. And of course the number of normals is +// the same as the number of vertices. +const float* Sphere3D::getNormals() const { return &(normals[0]); } + +// This returns a series of vertices that form triangles from the vertices (the indices specify loose +// triangles, not tristrips or fans or whatnot. So each triplet of indices is an individual triangle.) +const unsigned int* Sphere3D::getIndices() const { return &(indices[0]); } +int Sphere3D::getNumIndices() const { return numIndices; } + + +} // namespace GLEAN diff --git a/tests/glean/geomutil.h b/tests/glean/geomutil.h new file mode 100644 index 00000000..34e73566 --- /dev/null +++ b/tests/glean/geomutil.h @@ -0,0 +1,100 @@ +// 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 +// 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 + + + + +// geomutil.h: frequently-used geometric operations + +#ifndef __geomutil_h__ +#define __geomutil_h__ + +#include <vector> + +namespace GLEAN { + +class RandomDouble; // Forward reference. + +class RandomMesh2D { + float* m; + int rowLength; + public: + RandomMesh2D(float minX, float maxX, int xPoints, + float minY, float maxY, int yPoints, + RandomDouble& rand); + ~RandomMesh2D(); + inline float* operator() (int y, int x) + { return m + 2 * (y * rowLength + x); } +}; // RandomMesh2D + +class SpiralStrip2D { + float* v; + public: + SpiralStrip2D(int nPoints, float minX, float maxX, + float minY, float maxY); + ~SpiralStrip2D(); + inline float* operator() (int i) + { return v + 2 * i; } +}; // SpiralStrip2D + +class SpiralTri2D { + float* v; + public: + SpiralTri2D(int nTris, float minX, float maxX, + float minY, float maxY); + ~SpiralTri2D(); + inline float* operator() (int i) + { return v + 6 * i; } +}; // SpiralTri2D + +class Sphere3D { + std::vector<float> vertices; + std::vector<float> normals; + int numVertices; + std::vector<unsigned int> indices; + int numIndices; + public: + Sphere3D(float radius, int slices, int stacks); + + // This returns the vertices: 3 floats per vertex in a tightly packed array (no padding between vertices). + const float* getVertices() const; + int getNumVertices() const; + + // This returns the normals; same data format as the vertices. And of course the number of normals is + // the same as the number of vertices. + const float* getNormals() const; + + // This returns a series of vertices that form triangles from the vertices (the indices specify loose + // triangles, not tristrips or fans or whatnot. So each triplet of indices is an individual triangle.) + const unsigned int* getIndices() const; + int getNumIndices() const; +}; + +} // namespace GLEAN + +#endif // __geomutil_h__ diff --git a/tests/glean/gl.cpp b/tests/glean/gl.cpp new file mode 100644 index 00000000..89ca4ae9 --- /dev/null +++ b/tests/glean/gl.cpp @@ -0,0 +1,82 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// OpenGL utility routines for images + +#include "image.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// draw - draw image using glDrawPixels +/////////////////////////////////////////////////////////////////////////////// +void +Image::draw() { + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment()); + glDrawPixels(width(), height(), format(), type(), pixels()); +} // Image::draw + +/////////////////////////////////////////////////////////////////////////////// +// read - read image using glReadPixels +/////////////////////////////////////////////////////////////////////////////// +void +Image::read(GLint x, GLint y) { + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, alignment()); + glReadPixels(x, y, width(), height(), format(), type(), pixels()); +} // Image::read + +/////////////////////////////////////////////////////////////////////////////// +// makeMipmaps - generate and load mipmaps for texturing +/////////////////////////////////////////////////////////////////////////////// +void +Image::makeMipmaps(GLenum internalFormat) { + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment()); + gluBuild2DMipmaps(GL_TEXTURE_2D, internalFormat, width(), height(), + format(), type(), pixels()); +} // Image::makeMipmaps + +}; // namespace GLEAN diff --git a/tests/glean/glutils.cpp b/tests/glean/glutils.cpp new file mode 100644 index 00000000..342bd476 --- /dev/null +++ b/tests/glean/glutils.cpp @@ -0,0 +1,325 @@ +// 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 +// 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 + + + + +// glutils.cpp: frequently-used OpenGL operations + +#define GLX_GLXEXT_PROTOTYPES +#include "glwrap.h" +#include "environ.h" +#include "lex.h" +#include "glutils.h" +#if defined(__X11__) +# include <dlfcn.h> +#endif +#if defined(__AGL__) +# include <cstring> +#endif + +namespace GLEAN { + +namespace GLUtils { + +/////////////////////////////////////////////////////////////////////////////// +// useScreenCoords: Map object coords directly to screen coords. +/////////////////////////////////////////////////////////////////////////////// +void +useScreenCoords(int windowW, int windowH) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, windowW, 0, windowH, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glViewport(0, 0, windowW, windowH); + glTranslatef(0.375, 0.375, 0.0); +} // useScreenCoords + +/////////////////////////////////////////////////////////////////////////////// +// haveExtensions: See if the current rendering context supports a given +// set of extensions. +/////////////////////////////////////////////////////////////////////////////// +bool +haveExtensions(const char* required) { + const char* available = reinterpret_cast<const char*> + (glGetString(GL_EXTENSIONS)); + + if (!required) + return true; + if (!available) + return false; + + bool haveAll = true; + Lex lRequired(required); + for (lRequired.next(); lRequired.token != Lex::END; lRequired.next()) { + if (lRequired.token != Lex::ID) + continue; + bool haveOne = false; + Lex lAvailable(available); + for (lAvailable.next(); lAvailable.token != Lex::END; + lAvailable.next()) + if (lAvailable.token == Lex::ID + && lAvailable.id == lRequired.id) { + haveOne = true; + break; + } + haveAll &= haveOne; + if (!haveAll) + break; + } + + return haveAll; +} // haveExtensions + +/////////////////////////////////////////////////////////////////////////////// +// getProcAddress: Get address of an OpenGL or window-system-binding function. +// This belongs here, rather than as a member of RenderingContext, because +// on Windows it must only be applied to the *current* context. (The +// return value on Windows is context-dependent, and wglGetProcAddress +// doesn't take a rendering context as an argument.) +/////////////////////////////////////////////////////////////////////////////// +void +(*getProcAddress(const char* name))() { +#if defined(__X11__) +# if defined(GLX_ARB_get_proc_address) + return glXGetProcAddressARB(reinterpret_cast<const GLubyte*>(name)); +# else + // XXX This isn't guaranteed to work, but it may be the best option + // we have at the moment. + void* libHandle = dlopen("libGL.so", RTLD_LAZY); + if (libHandle) { + void* funcPointer = dlsym(libHandle, name); + dlclose(libHandle); + return funcPointer; + } else + return 0; +# endif +#elif defined(__WIN__) + // Gotta be a little more explicit about the cast to please MSVC. + typedef void (__cdecl* VOID_FUNC_VOID) (); + return reinterpret_cast<VOID_FUNC_VOID>(wglGetProcAddress(name)); +#elif defined(__BEWIN__) +# error "Need GetProcAddress (or equivalent) for BeOS" + return 0; +#elif defined(__AGL__) + // Very quick hack to keep things running for a few hours until + // a better solution is in place: + if (!strcmp(name, "glLockArraysEXT")) + return reinterpret_cast<void (*)()> (glLockArraysEXT); + else if (!strcmp(name, "glUnlockArraysEXT")) + return reinterpret_cast<void (*)()> (glUnlockArraysEXT); + else if (!strcmp(name, "glActiveTextureARB")) + return reinterpret_cast<void (*)()> (glActiveTextureARB); + else if (!strcmp(name, "glMultiTexCoord2fARB")) + return reinterpret_cast<void (*)()> (glMultiTexCoord2fARB); + else if (!strcmp(name, "glLockArraysEXT")) + return reinterpret_cast<void (*)()> (glLockArraysEXT); + else if (!strcmp(name, "glUnlockArraysEXT")) + return reinterpret_cast<void (*)()> (glUnlockArraysEXT); + else if (!strcmp(name, "glLockArraysEXT")) + return reinterpret_cast<void (*)()> (glLockArraysEXT); + else if (!strcmp(name, "glUnlockArraysEXT")) + return reinterpret_cast<void (*)()> (glUnlockArraysEXT); + else + return 0; +#endif +} // getProcAddress + +/////////////////////////////////////////////////////////////////////////////// +// logGLErrors: Check for OpenGL errors and log any that have occurred. +/////////////////////////////////////////////////////////////////////////////// +void +logGLErrors(Environment& env) { + GLenum err; + while ((err = glGetError())) + env.log << "\tOpenGL error: " << gluErrorString(err) << '\n'; +} // logGLErrors + + +/////////////////////////////////////////////////////////////////////////////// +// Syntactic sugar for light sources +/////////////////////////////////////////////////////////////////////////////// + +Light::Light(int l) { + lightNumber = static_cast<GLenum>(GL_LIGHT0 + l); +} // Light::Light + +void +Light::ambient(float r, float g, float b, float a){ + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glLightfv(lightNumber, GL_AMBIENT, v); +} // Light::ambient + +void +Light::diffuse(float r, float g, float b, float a){ + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glLightfv(lightNumber, GL_DIFFUSE, v); +} // Light::diffuse + +void +Light::specular(float r, float g, float b, float a){ + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glLightfv(lightNumber, GL_SPECULAR, v); +} // Light::specular + +void +Light::position(float x, float y, float z, float w){ + GLfloat v[4]; + v[0] = x; v[1] = y; v[2] = z; v[3] = w; + glLightfv(lightNumber, GL_POSITION, v); +} // Light::position + +void +Light::spotDirection(float x, float y, float z){ + GLfloat v[3]; + v[0] = x; v[1] = y; v[2] = z; + glLightfv(lightNumber, GL_SPOT_DIRECTION, v); +} // Light::spotDirection + +void +Light::spotExponent(float e){ + glLightf(lightNumber, GL_SPOT_EXPONENT, e); +} // Light::spotExponent + +void +Light::spotCutoff(float c){ + glLightf(lightNumber, GL_SPOT_CUTOFF, c); +} // Light::spotCutoff + +void +Light::constantAttenuation(float a){ + glLightf(lightNumber, GL_CONSTANT_ATTENUATION, a); +} // Light::constantAttenuation + +void +Light::linearAttenuation(float a){ + glLightf(lightNumber, GL_LINEAR_ATTENUATION, a); +} // Light::linearAttenuation + +void +Light::quadraticAttenuation(float a){ + glLightf(lightNumber, GL_QUADRATIC_ATTENUATION, a); +} // Light::quadraticAttenuation + +void +Light::enable() { + glEnable(lightNumber); +} // Light::enable + +void +Light::disable() { + glDisable(lightNumber); +} // Light::disable + +/////////////////////////////////////////////////////////////////////////////// +// Syntactic sugar for light model +/////////////////////////////////////////////////////////////////////////////// + +LightModel::LightModel() { +} // LightModel::LightModel + +void +LightModel::ambient(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, v); +} // LightModel::ambient + +void +LightModel::localViewer(bool v) { + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, static_cast<GLint>(v)); +} // LightModel::localViewer + +void +LightModel::twoSide(bool v) { + glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, static_cast<GLint>(v)); +} // LightModel::twoSide + +void +LightModel::colorControl(GLenum e) { + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, e); +} // LightModel::colorControl + +/////////////////////////////////////////////////////////////////////////////// +// Syntactic sugar for material properties +/////////////////////////////////////////////////////////////////////////////// + +Material::Material(GLenum f) { + face = f; +} // Material::Material + +void +Material::ambient(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glMaterialfv(face, GL_AMBIENT, v); +} // Material::ambient + +void +Material::diffuse(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glMaterialfv(face, GL_DIFFUSE, v); +} // Material::diffuse + +void +Material::ambientAndDiffuse(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glMaterialfv(face, GL_AMBIENT_AND_DIFFUSE, v); +} // Material::ambientAndDiffuse + +void +Material::specular(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glMaterialfv(face, GL_SPECULAR, v); +} // Material::specular + +void +Material::emission(float r, float g, float b, float a) { + GLfloat v[4]; + v[0] = r; v[1] = g; v[2] = b; v[3] = a; + glMaterialfv(face, GL_EMISSION, v); +} // Material::emission + +void +Material::shininess(float s) { + glMaterialf(face, GL_SHININESS, static_cast<GLfloat>(s)); +} // Material::shininess + + +} // namespace GLUtils + +} // namespace GLEAN diff --git a/tests/glean/glutils.h b/tests/glean/glutils.h new file mode 100644 index 00000000..94db42c5 --- /dev/null +++ b/tests/glean/glutils.h @@ -0,0 +1,111 @@ +// 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 +// 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 + + + + +// glutils.h: frequently-used OpenGL operations + +#ifndef __glutils_h__ +#define __glutils_h__ + +namespace GLEAN { + +class Environment; // Forward reference. + +namespace GLUtils { + +// Set up projection and modelview matrices so that first-quadrant +// object coordinates map directly to screen coordinates (using the +// normal Cartesian convention, with (0,0) at lower left). +void useScreenCoords(int windowW, int windowH); + +// Check to see if the current rendering context supports a given +// extension or set of extensions. (This is here, rather than in +// RenderingContext, because it can only be applied to the ``current'' +// context rather than to any arbitrary context.) +bool haveExtensions(const char* required); +inline bool haveExtension(const char* name) { + return haveExtensions(name); +} + +// Get a pointer to a function (usually, an extension function). +// Like haveExtension, we have to do this here rather than in +// RenderingContext. +void (*getProcAddress(const char* name))(); + +// Check for OpenGL errors and log any that have occurred: +void logGLErrors(Environment& env); + +// Syntactic sugar for dealing with light source parameters: +class Light { + GLenum lightNumber; + public: + Light(int l); + void ambient(float r, float g, float b, float a); + void diffuse(float r, float g, float b, float a); + void specular(float r, float g, float b, float a); + void position(float x, float y, float z, float w); + void spotDirection(float x, float y, float z); + void spotExponent(float e); + void spotCutoff(float c); + void constantAttenuation(float a); + void linearAttenuation(float a); + void quadraticAttenuation(float a); + void enable(); + void disable(); +}; // Light + +// Syntactic sugar for dealing with light model: +class LightModel { + public: + LightModel(); + void ambient(float r, float g, float b, float a); + void localViewer(bool v); + void twoSide(bool v); + void colorControl(GLenum e); +}; // LightModel + +// Syntactic sugar for dealing with material properties: +class Material { + GLenum face; + public: + Material(GLenum f = GL_FRONT_AND_BACK); + void ambient(float r, float g, float b, float a); + void diffuse(float r, float g, float b, float a); + void ambientAndDiffuse(float r, float g, float b, float a); + void specular(float r, float g, float b, float a); + void emission(float r, float g, float b, float a); + void shininess(float s); +}; // Material + +} // namespace GLUtils + +} // namespace GLEAN + +#endif // __glutils_h__ diff --git a/tests/glean/glwrap.h b/tests/glean/glwrap.h new file mode 100644 index 00000000..18136ca8 --- /dev/null +++ b/tests/glean/glwrap.h @@ -0,0 +1,784 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Microsoft's version of gl.h invokes macros that are defined in +// windows.h. To avoid a conditional #include <windows.h> in +// every file, we wrap gl.h with the proper conditions here, and +// have our source files #include "glwrap.h" instead. + +// As a bonus we ensure that all declarations for GLU are included, +// and on X11-based systems, we cover X11 and GLX as well. This +// should cover nearly everything needed by a typical glean test. + +// It's unfortunate that both Windows and Xlib are so casual about +// polluting the global namespace. The problem isn't easily resolved, +// even with the use of C++ namespace directives, because (a) macros +// in the include files refer to unqualified global variables, and (b) +// preprocessor macros themselves aren't affected by namespaces. + + +#ifndef __glwrap_h__ +#define __glwrap_h__ + +#if defined(__WIN__) +# include <windows.h> +# include <GL/gl.h> +# include <GL/glu.h> +# if !defined(GLAPIENTRY) +# define GLAPIENTRY __stdcall +# endif +# if !defined(GLCALLBACK) +# define GLCALLBACK __stdcall +# endif +# include <GL/glext.h> +#elif defined(__X11__) +# include <GL/glx.h> + // glx.h covers Xlib.h and gl.h, among others +# include <GL/glu.h> +# if !defined(GLAPIENTRY) +# define GLAPIENTRY +# endif +# if !defined(GLCALLBACK) +# define GLCALLBACK +# endif +# include <GL/glext.h> +#elif defined(__AGL__) +# include <Carbon/Carbon.h> +# include <OpenGL/glu.h> +# include <OpenGL/glext.h> +# include <AGL/agl.h> +# include <AGL/aglRenderers.h> +# if !defined(GLAPIENTRY) +# define GLAPIENTRY +# endif +# if !defined(GLCALLBACK) +# define GLCALLBACK +# endif +# if !defined(sinf) +# define sinf sin +# define cosf cos +# define sqrtf sqrt +# endif +#else +# error "Improper window system configuration; must be __WIN__ or __X11__." +#endif + +#ifndef GL_COMBINE_EXT + #ifdef GL_COMBINE_ARB + #define GL_COMBINE_EXT GL_COMBINE_ARB + #define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB + #define GL_COMBINE_ALPHA_EXT GL_COMBINE_ALPHA_ARB + #define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB + #define GL_ADD_SIGNED_EXT GL_ADD_SIGNED_ARB + #define GL_INTERPOLATE_EXT GL_INTERPOLATE_ARB + #define GL_CONSTANT_EXT GL_CONSTANT_ARB + #define GL_PRIMARY_COLOR_EXT GL_PRIMARY_COLOR_ARB + #define GL_PREVIOUS_EXT GL_PREVIOUS_ARB + #define GL_SUBTRACT_EXT GL_SUBTRACT_ARB + #define GL_SOURCE0_RGB_EXT GL_SOURCE0_RGB_ARB + #define GL_SOURCE1_RGB_EXT GL_SOURCE1_RGB_ARB + #define GL_SOURCE2_RGB_EXT GL_SOURCE2_RGB_ARB + #define GL_SOURCE0_ALPHA_EXT GL_SOURCE0_ALPHA_ARB + #define GL_SOURCE1_ALPHA_EXT GL_SOURCE1_ALPHA_ARB + #define GL_SOURCE2_ALPHA_EXT GL_SOURCE2_ALPHA_ARB + #define GL_OPERAND0_RGB_EXT GL_OPERAND0_RGB_ARB + #define GL_OPERAND1_RGB_EXT GL_OPERAND1_RGB_ARB + #define GL_OPERAND2_RGB_EXT GL_OPERAND2_RGB_ARB + #define GL_OPERAND0_ALPHA_EXT GL_OPERAND0_ALPHA_ARB + #define GL_OPERAND1_ALPHA_EXT GL_OPERAND1_ALPHA_ARB + #define GL_OPERAND2_ALPHA_EXT GL_OPERAND2_ALPHA_ARB + #endif +#endif +// Windows has a convention for typedef'ing pointers to OpenGL functions +// which encapsulates some of the oddities of Win32 calling conventions. +// Identical conventions are being established for Linux, but they are +// not yet in place, and are not necessarily supported in other UNIX +// variants. Therefore we need a similar mechanism that accomplishes +// the same goal. We'll use the standard typedef names, but put them +// in the GLEAN namespace; that should preserve as much source-code +// compatibility as possible elsewhere in glean. + +namespace GLEAN { +typedef void (GLAPIENTRY * PFNGLBLENDCOLOREXTPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (GLAPIENTRY * PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +typedef void (GLAPIENTRY * PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (GLAPIENTRY * PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (GLAPIENTRY * PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum types, GLvoid *values); +typedef void (GLAPIENTRY * PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY * PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY * PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (GLAPIENTRY * PFNGLRESETMINMAXEXTPROC) (GLenum target); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY * PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (GLAPIENTRY * PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY * PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (GLAPIENTRY * PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalFormat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (GLAPIENTRY * PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +typedef void (GLAPIENTRY * PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum target, GLfloat value); +typedef void (GLAPIENTRY * PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum target, const GLfloat *value); +typedef void (GLAPIENTRY * PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum target, GLint value); +typedef void (GLAPIENTRY * PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum target, const GLint *value); +typedef void (GLAPIENTRY * PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum target, GLfloat *value); +typedef void (GLAPIENTRY * PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum target, GLint *value); +typedef void (GLAPIENTRY * PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei extent, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei extent, GLenum format, GLenum type, const void *pixels); +typedef void (GLAPIENTRY * PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef void (GLAPIENTRY * PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (GLAPIENTRY * PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (GLAPIENTRY * PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +typedef GLboolean (GLAPIENTRY * PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef GLboolean (GLAPIENTRY * PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (GLAPIENTRY * PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (GLAPIENTRY * PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +typedef void (GLAPIENTRY * PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +typedef void (GLAPIENTRY * PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (GLAPIENTRY * PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (GLAPIENTRY * PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +typedef void (GLAPIENTRY * PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (GLAPIENTRY * PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (GLAPIENTRY * PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (GLAPIENTRY * PFNGLGETPOINTERVEXTPROC) (GLenum pname, GLvoid* *params); +typedef void (GLAPIENTRY * PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +typedef void (GLAPIENTRY * PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *param); +typedef void (GLAPIENTRY * PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *param); +typedef void (GLAPIENTRY * PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +typedef GLint (GLAPIENTRY * PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (GLAPIENTRY * PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buf); +typedef GLint (GLAPIENTRY * PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *markerp); +typedef void (GLAPIENTRY * PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (GLAPIENTRY * PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (GLAPIENTRY * PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (GLAPIENTRY * PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +typedef void (GLAPIENTRY * PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +typedef void (GLAPIENTRY * PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *plane); +typedef void (GLAPIENTRY * PFNGLFLUSHRASTERSGIXPROC) (void); +typedef void (GLAPIENTRY * PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (GLAPIENTRY * PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLHINTPGIPROC) (GLenum target, GLint mode); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum name, GLfloat *param); +typedef void (GLAPIENTRY * PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum name, GLint *param); +typedef void (GLAPIENTRY * PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum name, GLfloat param); +typedef void (GLAPIENTRY * PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum name, const GLfloat *param); +typedef void (GLAPIENTRY * PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum name, GLint param); +typedef void (GLAPIENTRY * PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum name, const GLint *param); +typedef void (GLAPIENTRY * PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +typedef void (GLAPIENTRY * PFNGLINDEXFUNCEXTPROC) (GLenum func, GLfloat ref); +typedef void (GLAPIENTRY * PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (GLAPIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void); +typedef void (GLAPIENTRY * PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble* params); +typedef void (GLAPIENTRY * PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat* params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat * params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint * params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat * params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint * params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat * params); +typedef void (GLAPIENTRY * PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint * params); +typedef void (GLAPIENTRY * PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat * params); +typedef void (GLAPIENTRY * PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint * params); +typedef void (GLAPIENTRY * PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat * params); +typedef void (GLAPIENTRY * PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint * params); +typedef void (GLAPIENTRY * PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (GLAPIENTRY * PFNGLFOGCOORDFVEXTPROC) (const GLfloat * coord); +typedef void (GLAPIENTRY * PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (GLAPIENTRY * PFNGLFOGCOORDDVEXTPROC) (const GLdouble * coord); +typedef void (GLAPIENTRY * PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const GLvoid * pointer); +typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (GLAPIENTRY * PFNGLADDSWAPHINTRECTWINPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (GLAPIENTRY * PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (GLAPIENTRY * PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (GLAPIENTRY * PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (GLAPIENTRY * PFNGLVERTEXARRAYRANGENVPROC) (GLsizei size, const GLvoid * pointer); +typedef void (GLAPIENTRY * PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat * params); +typedef void (GLAPIENTRY * PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (GLAPIENTRY * PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint * params); +typedef void (GLAPIENTRY * PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (GLAPIENTRY * PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (GLAPIENTRY * PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (GLAPIENTRY * PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (GLAPIENTRY * PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat * params); +typedef void (GLAPIENTRY * PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint * params); +typedef void (GLAPIENTRY * PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat * params); +typedef void (GLAPIENTRY * PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint * params); +typedef void (GLAPIENTRY * PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat * params); +typedef void (GLAPIENTRY * PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint * params); +typedef void (GLAPIENTRY * PFNGLRESIZEBUFFERSMESAPROC) (void); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2IVMESAPROC) (const GLint *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3IVMESAPROC) (const GLint *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4IVMESAPROC) (const GLint *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *p); +typedef void (GLAPIENTRY * PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *p); +typedef void (GLAPIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (GLAPIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLLOADTRANSPOSEMATRIXDARBPROC) ( const GLdouble m[16] ); +typedef void (GLAPIENTRY * PFNGLLOADTRANSPOSEMATRIXFARBPROC) ( const GLfloat m[16] ); +typedef void (GLAPIENTRY * PFNGLMULTTRANSPOSEMATRIXDARBPROC) ( const GLdouble m[16] ); +typedef void (GLAPIENTRY * PFNGLMULTTRANSPOSEMATRIXFARBPROC) ( const GLfloat m[16] ); +typedef void (GLAPIENTRY * PFNGLSAMPLEPASSARBPROC) (GLenum pass); +typedef void (GLAPIENTRY * PFNGLSAMPLECOVERAGEARBPROC) (GLclampf value, GLboolean invert); + +// OpenGL 1.2 enumerants, to allow glean to be compiled on OpenGL 1.1 systems. +// (This odd workaround is needed to handle problems with some copies of +// glext.h that are floating around the net.) + +#ifndef GL_PACK_SKIP_IMAGES +#define GL_PACK_SKIP_IMAGES 0x806B +#endif +#ifndef GL_PACK_IMAGE_HEIGHT +#define GL_PACK_IMAGE_HEIGHT 0x806C +#endif +#ifndef GL_UNPACK_SKIP_IMAGES +#define GL_UNPACK_SKIP_IMAGES 0x806D +#endif +#ifndef GL_UNPACK_IMAGE_HEIGHT +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#endif +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif +#ifndef GL_PROXY_TEXTURE_3D +#define GL_PROXY_TEXTURE_3D 0x8070 +#endif +#ifndef GL_TEXTURE_DEPTH +#define GL_TEXTURE_DEPTH 0x8071 +#endif +#ifndef GL_TEXTURE_WRAP_R +#define GL_TEXTURE_WRAP_R 0x8072 +#endif +#ifndef GL_MAX_3D_TEXTURE_SIZE +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#endif +#ifndef GL_TEXTURE_BINDING_3D +#define GL_TEXTURE_BINDING_3D 0x806A +#endif +#ifndef GL_RESCALE_NORMAL +#define GL_RESCALE_NORMAL 0x803A +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif +#ifndef GL_MAX_ELEMENTS_VERTICES +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#endif +#ifndef GL_MAX_ELEMENTS_INDICES +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#endif +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_UNSIGNED_BYTE_3_3_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#endif +#ifndef GL_UNSIGNED_BYTE_2_3_3_REV +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#endif +#ifndef GL_UNSIGNED_SHORT_5_6_5_REV +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#endif +#ifndef GL_UNSIGNED_SHORT_4_4_4_4_REV +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#endif +#ifndef GL_UNSIGNED_SHORT_5_5_5_1 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#endif +#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#endif +#ifndef GL_UNSIGNED_INT_8_8_8_8_REV +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#endif +#ifndef GL_UNSIGNED_INT_10_10_10_2 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#endif +#ifndef GL_UNSIGNED_INT_2_10_10_10_REV +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#endif +#ifndef GL_LIGHT_MODEL_COLOR_CONTROL +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#endif +#ifndef GL_SINGLE_COLOR +#define GL_SINGLE_COLOR 0x81F9 +#endif +#ifndef GL_SEPARATE_SPECULAR_COLOR +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#endif +#ifndef GL_TEXTURE_MIN_LOD +#define GL_TEXTURE_MIN_LOD 0x813A +#endif +#ifndef GL_TEXTURE_MAX_LOD +#define GL_TEXTURE_MAX_LOD 0x813B +#endif +#ifndef GL_TEXTURE_BASE_LEVEL +#define GL_TEXTURE_BASE_LEVEL 0x813C +#endif +#ifndef GL_TEXTURE_MAX_LEVEL +#define GL_TEXTURE_MAX_LEVEL 0x813D +#endif +#ifndef GL_SMOOTH_POINT_SIZE_RANGE +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#endif +#ifndef GL_SMOOTH_POINT_SIZE_GRANULARITY +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#endif +#ifndef GL_SMOOTH_LINE_WIDTH_RANGE +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#endif +#ifndef GL_SMOOTH_LINE_WIDTH_GRANULARITY +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#endif +#ifndef GL_ALIASED_POINT_SIZE_RANGE +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#endif +#ifndef GL_ALIASED_LINE_WIDTH_RANGE +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif +#ifndef GL_COLOR_TABLE +#define GL_COLOR_TABLE 0x80D0 +#endif +#ifndef GL_POST_CONVOLUTION_COLOR_TABLE +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#endif +#ifndef GL_POST_COLOR_MATRIX_COLOR_TABLE +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#endif +#ifndef GL_PROXY_COLOR_TABLE +#define GL_PROXY_COLOR_TABLE 0x80D3 +#endif +#ifndef GL_PROXY_POST_CONVOLUTION_COLOR_TABLE +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#endif +#ifndef GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#endif +#ifndef GL_COLOR_TABLE_SCALE +#define GL_COLOR_TABLE_SCALE 0x80D6 +#endif +#ifndef GL_COLOR_TABLE_BIAS +#define GL_COLOR_TABLE_BIAS 0x80D7 +#endif +#ifndef GL_COLOR_TABLE_FORMAT +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#endif +#ifndef GL_COLOR_TABLE_WIDTH +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#endif +#ifndef GL_COLOR_TABLE_RED_SIZE +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#endif +#ifndef GL_COLOR_TABLE_GREEN_SIZE +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#endif +#ifndef GL_COLOR_TABLE_BLUE_SIZE +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#endif +#ifndef GL_COLOR_TABLE_ALPHA_SIZE +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#endif +#ifndef GL_COLOR_TABLE_LUMINANCE_SIZE +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#endif +#ifndef GL_COLOR_TABLE_INTENSITY_SIZE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#endif +#ifndef GL_CONVOLUTION_1D +#define GL_CONVOLUTION_1D 0x8010 +#endif +#ifndef GL_CONVOLUTION_2D +#define GL_CONVOLUTION_2D 0x8011 +#endif +#ifndef GL_SEPARABLE_2D +#define GL_SEPARABLE_2D 0x8012 +#endif +#ifndef GL_CONVOLUTION_BORDER_MODE +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#endif +#ifndef GL_CONVOLUTION_FILTER_SCALE +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#endif +#ifndef GL_CONVOLUTION_FILTER_BIAS +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#endif +#ifndef GL_REDUCE +#define GL_REDUCE 0x8016 +#endif +#ifndef GL_CONVOLUTION_FORMAT +#define GL_CONVOLUTION_FORMAT 0x8017 +#endif +#ifndef GL_CONVOLUTION_WIDTH +#define GL_CONVOLUTION_WIDTH 0x8018 +#endif +#ifndef GL_CONVOLUTION_HEIGHT +#define GL_CONVOLUTION_HEIGHT 0x8019 +#endif +#ifndef GL_MAX_CONVOLUTION_WIDTH +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#endif +#ifndef GL_MAX_CONVOLUTION_HEIGHT +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#endif +#ifndef GL_POST_CONVOLUTION_RED_SCALE +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#endif +#ifndef GL_POST_CONVOLUTION_GREEN_SCALE +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#endif +#ifndef GL_POST_CONVOLUTION_BLUE_SCALE +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#endif +#ifndef GL_POST_CONVOLUTION_ALPHA_SCALE +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#endif +#ifndef GL_POST_CONVOLUTION_RED_BIAS +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#endif +#ifndef GL_POST_CONVOLUTION_GREEN_BIAS +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#endif +#ifndef GL_POST_CONVOLUTION_BLUE_BIAS +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#endif +#ifndef GL_POST_CONVOLUTION_ALPHA_BIAS +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#endif +#ifndef GL_CONSTANT_BORDER +#define GL_CONSTANT_BORDER 0x8151 +#endif +#ifndef GL_REPLICATE_BORDER +#define GL_REPLICATE_BORDER 0x8153 +#endif +#ifndef GL_CONVOLUTION_BORDER_COLOR +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +#endif +#ifndef GL_COLOR_MATRIX +#define GL_COLOR_MATRIX 0x80B1 +#endif +#ifndef GL_COLOR_MATRIX_STACK_DEPTH +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#endif +#ifndef GL_MAX_COLOR_MATRIX_STACK_DEPTH +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#endif +#ifndef GL_POST_COLOR_MATRIX_RED_SCALE +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#endif +#ifndef GL_POST_COLOR_MATRIX_GREEN_SCALE +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#endif +#ifndef GL_POST_COLOR_MATRIX_BLUE_SCALE +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#endif +#ifndef GL_POST_COLOR_MATRIX_ALPHA_SCALE +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#endif +#ifndef GL_POST_COLOR_MATRIX_RED_BIAS +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#endif +#ifndef GL_POST_COLOR_MATRIX_GREEN_BIAS +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#endif +#ifndef GL_POST_COLOR_MATRIX_BLUE_BIAS +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#endif +#ifndef GL_POST_COLOR_MATRIX_ALPHA_BIAS +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#endif +#ifndef GL_HISTOGRAM +#define GL_HISTOGRAM 0x8024 +#endif +#ifndef GL_PROXY_HISTOGRAM +#define GL_PROXY_HISTOGRAM 0x8025 +#endif +#ifndef GL_HISTOGRAM_WIDTH +#define GL_HISTOGRAM_WIDTH 0x8026 +#endif +#ifndef GL_HISTOGRAM_FORMAT +#define GL_HISTOGRAM_FORMAT 0x8027 +#endif +#ifndef GL_HISTOGRAM_RED_SIZE +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#endif +#ifndef GL_HISTOGRAM_GREEN_SIZE +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#endif +#ifndef GL_HISTOGRAM_BLUE_SIZE +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#endif +#ifndef GL_HISTOGRAM_ALPHA_SIZE +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#endif +#ifndef GL_HISTOGRAM_LUMINANCE_SIZE +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#endif +#ifndef GL_HISTOGRAM_SINK +#define GL_HISTOGRAM_SINK 0x802D +#endif +#ifndef GL_MINMAX +#define GL_MINMAX 0x802E +#endif +#ifndef GL_MINMAX_FORMAT +#define GL_MINMAX_FORMAT 0x802F +#endif +#ifndef GL_MINMAX_SINK +#define GL_MINMAX_SINK 0x8030 +#endif +#ifndef GL_TABLE_TOO_LARGE +#define GL_TABLE_TOO_LARGE 0x8031 +#endif +#ifndef GL_BLEND_EQUATION +#define GL_BLEND_EQUATION 0x8009 +#endif +#ifndef GL_MIN +#define GL_MIN 0x8007 +#endif +#ifndef GL_MAX +#define GL_MAX 0x8008 +#endif +#ifndef GL_FUNC_ADD +#define GL_FUNC_ADD 0x8006 +#endif +#ifndef GL_FUNC_SUBTRACT +#define GL_FUNC_SUBTRACT 0x800A +#endif +#ifndef GL_FUNC_REVERSE_SUBTRACT +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#endif +#ifndef GL_BLEND_COLOR +#define GL_BLEND_COLOR 0x8005 +#endif + +// Extension enumerants, in case they're not defined in glext.h. + +#ifndef GL_DOT3_RGB_EXT +#define GL_DOT3_RGB_EXT 0x8740 +#endif +#ifndef GL_DOT3_RGBA_EXT +#define GL_DOT3_RGBA_EXT 0x8741 +#endif +#ifndef GL_DOT3_RGB_ARB +#define GL_DOT3_RGB_ARB 0x86AE +#endif +#ifndef GL_DOT3_RGBA_ARB +#define GL_DOT3_RGBA_ARB 0x86AF +#endif + + +#ifndef GL_VERSION_1_2 +// OpenGL 1.2 function pointer types, to allow glean to +// be compiled on OpenGL 1.1 systems. +typedef void (GLAPIENTRY * PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (GLAPIENTRY * PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table); +typedef void (GLAPIENTRY * PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY * PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY * PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLvoid *table); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONPROC) (GLenum mode); +typedef void (GLAPIENTRY * PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); +typedef void (GLAPIENTRY * PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY * PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (GLAPIENTRY * PFNGLRESETMINMAXPROC) (GLenum target); +typedef void (GLAPIENTRY * PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum types, GLvoid *values); +typedef void (GLAPIENTRY * PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (GLAPIENTRY * PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (GLAPIENTRY * PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (GLAPIENTRY * PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *image); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (GLAPIENTRY * PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (GLAPIENTRY * PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column); +typedef void (GLAPIENTRY * PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span); +typedef void (GLAPIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (GLAPIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (GLAPIENTRY * PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#endif +} // namespace GLEAN + + +#endif // __glwrap_h__ diff --git a/tests/glean/image.h b/tests/glean/image.h new file mode 100644 index 00000000..83942f4f --- /dev/null +++ b/tests/glean/image.h @@ -0,0 +1,250 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// image.h: image data and attributes, image I/O + +// This class encapsulates OpenGL information related to images (size, +// format, etc.) and provides utilities for transferring images to and +// from files. + + +#ifndef __image_h__ +#define __image_h__ + +#include <string> +#include "glwrap.h" +#include "stats.h" + +namespace GLEAN { + +class Image { + + private: + + GLsizei _width; + GLsizei _height; + GLenum _format; + GLenum _type; + char* _pixels; + GLsizei _alignment; + GLsizei _rowSizeInBytes; + GLsizei _pixelSizeInBytes; + + enum { // validation bits, for lazy validation + vbRowSizeInBytes = 1, + vbPixelSizeInBytes = 2, + vbPacker = 4, + vbUnpacker = 8, + vbAll = ~0 + }; + int _invalid; + inline bool invalid(int bit) const { return _invalid & bit; } + inline bool valid(int bit) const { return !invalid(bit); } + inline void invalidate(int bits) { _invalid |= bits; } + inline void validate(int bits) { _invalid &= ~bits; } + + GLsizei validateRowSizeInBytes(); + GLsizei validatePixelSizeInBytes(); + + typedef void Unpacker(GLsizei n, double* rgba, char* nextPixel); + Unpacker* _unpacker; + Unpacker* validateUnpacker(); + typedef void Packer(GLsizei n, char* nextPixel, double* rgba); + Packer* _packer; + Packer* validatePacker(); + + // For now, we will require that: + // 1. All images are in native byte order (so that byte swapping + // at the OpenGL level is unnecessary). + // 2. The image width and height above describe the entire image + // (so that there is no need to specify row length + // independently). + // 3. We have no need to specify subimages at this level (so + // there is no need for SKIP_ROWS and SKIP_PIXELS attributes). + + // Should construction fix the format and type for all time? + // That would eliminate synchronization problems between data and + // descriptive information that might arise when an Image is reused, + // and might permit use of template functions instead of lots of + // switches. Probably not; it would make dynamic type assignment + // (such as reading a TIFF file) quite awkward. + + public: + + // Exceptions: + + struct Error { }; // Base class for all image errors. + struct BadFormat: public Error { // Bad image format. + GLenum format; + BadFormat(GLenum f) {format = f;} + }; + struct BadType: public Error { // Bad image type. + GLenum type; + BadType(GLenum t) {type = t;} + }; + struct CantOpen: public Error { // Can't open file. + const char* filename; + CantOpen(const char* p) {filename = p;} + }; + struct UnsupportedTIFF: public Error { // TIFF we can't handle. + }; + struct RefImageTooLarge: public Error { // Can't register ref image. + }; + + // Constructors/Destructor: + + Image(); + Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType); + Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType, + double r, double g, double b, double a); + Image(Image& i); + Image& operator= (Image& i); + ~Image(); + + // Reserve space for the pixel array: + + void reserve(); + + // Get/Set attributes. These attributes are useful for calls + // to glDrawPixels, glTexImage2D, etc. Note the alignment + // value; passing it to glPixelStore is essential before using + // one of the other OpenGL commands. + + inline GLsizei width() const // Image width, in pixels + { return _width; } + inline void width(GLsizei w) + { _width = w; invalidate(vbRowSizeInBytes); } + + inline GLsizei height() const // Image height, in pixels. + { return _height; } + inline void height(GLsizei h) + { _height = h; } + + inline GLenum format() const // Image format. Currently + { return _format; } // these formats are supported: + // GL_LUMINANCE, + // GL_LUMINANCE_ALPHA, + // GL_RGB, GL_RGBA. + // It may be easiest to treat + // stencil, depth, etc. images + // as luminance images. + inline void format(GLenum f) { + _format = f; + invalidate( + vbRowSizeInBytes + | vbPixelSizeInBytes + | vbPacker + | vbUnpacker); + } + + inline GLenum type() const // Pixel data type. Currently + { return _type; } // these types are supported: + // GL_BYTE, GL_UNSIGNED_BYTE, + // GL_SHORT, GL_UNSIGNED_SHORT, + // GL_INT, GL_UNSIGNED_INT, GL_FLOAT. + inline void type(GLenum t) { + _type = t; + invalidate( + vbRowSizeInBytes + | vbPixelSizeInBytes + | vbPacker + | vbUnpacker); + } + + inline char* pixels() // The pixels. + { return _pixels; } + inline const char* pixels() const + { return const_cast<const char*>(_pixels); } + void pixels(char* p); + + inline GLsizei alignment() const // Alignment. See glPixelStore. + { return _alignment; } + inline void alignment(GLsizei a) + { _alignment = a; invalidate(vbRowSizeInBytes); } + + inline GLsizei rowSizeInBytes() { // Size of scanline, in bytes + return valid(vbRowSizeInBytes)? + _rowSizeInBytes: validateRowSizeInBytes(); + } + + inline GLsizei pixelSizeInBytes() { // Size of pixel, in bytes + return valid(vbPixelSizeInBytes)? + _pixelSizeInBytes: validatePixelSizeInBytes(); + } + + // XXX Utilities to determine component size in bits/bytes? + // XXX Component range (min neg, max neg, min pos, max pos, eps?) + + // Pixel packing/unpacking utilities: + + void unpack(GLsizei n, double* rgba, char* nextPixel); + void pack(GLsizei n, char* nextPixel, double* rgba); + // XXX get(x, y, double* rgba); + // XXX put(x, y, double* rgba); + + // Image registration. The utility compares a reference image + // to the current image (which must be at least as large as the + // reference image in both dimensions), determines the offset at + // which the reference image minus the current image has minimum + // mean absolute error (summed over R, G, B, and A), and returns + // an object specifying the offset and corresponding statistics. + + struct Registration { + int wOffset; // offset in width (x) + int hOffset; // offset in height (y) + BasicStats stats[4]; // stats for absolute error in + // R, G, B, and A + }; + Registration reg(Image& ref); + + // Image arithmetic + // XXX type and format conversions, with appropriate scaling. + // XXX image difference + // XXX minmax, histogram, contrast stretch? + + // TIFF I/O utilities: + + void readTIFF(const char* filename); + inline void readTIFF(const std::string& s) { readTIFF(s.c_str()); } + void writeTIFF(const char* filename); + inline void writeTIFF(const std::string& s) { writeTIFF(s.c_str()); } + + // GL operation utilities: + + void draw(); // Invoke glDrawPixels. + void read(GLint x, GLint y); // Invoke glReadPixels. + void makeMipmaps(GLenum intFormat); // Load texture mipmaps. + +}; // class Image + +} // namespace GLEAN + +#endif // __image_h__ diff --git a/tests/glean/image_misc.cpp b/tests/glean/image_misc.cpp new file mode 100644 index 00000000..adfdc224 --- /dev/null +++ b/tests/glean/image_misc.cpp @@ -0,0 +1,210 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Implementation of image data, attribute, and I/O + +#include "image.h" +#include <string.h> + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/Destructor +/////////////////////////////////////////////////////////////////////////////// +// An empty image: +Image::Image() { + _width = _height = 0; + _format = GL_RGB; + _type = GL_UNSIGNED_BYTE; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; +} // Image::Image + +// An unitialized image of the given type and size: +Image::Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType) { + _width = aWidth; + _height = aHeight; + _format = aFormat; + _type = aType; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); +} // Image::Image(aWidth, aHeight, aFormat, aType) + +// An image of the given type and size, initialized to a solid color: +Image::Image(int aWidth, int aHeight, GLenum aFormat, GLenum aType, + double r, double g, double b, double a) { + _width = aWidth; + _height = aHeight; + _format = aFormat; + _type = aType; + _pixels = 0; + _alignment = 4; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); + int i; // VC++ 6 doesn't handle the definition of variables in a + // for-statement properly + + double* solidColor = new double[4 * width()]; + for (/*int */i = 0; i < 4 * width(); i += 4) { + solidColor[i + 0] = r; + solidColor[i + 1] = g; + solidColor[i + 2] = b; + solidColor[i + 3] = a; + } + + char* row = pixels(); + for (/*int */i = 0; i < height(); ++i) { + pack(width(), row, solidColor); + row += rowSizeInBytes(); + } +} // Image::Image(aWidth, aHeight, aFormat, aType) + +// Copy constructor: +Image::Image(Image& i) { + _width = i.width(); + _height = i.height(); + _format = i.format(); + _type = i.type(); + _alignment = i.alignment(); + _pixels = 0; + _packer = 0; + _unpacker = 0; + _invalid = vbAll; + reserve(); + memcpy(pixels(), i.pixels(), height() * rowSizeInBytes()); +} // Image::Image(Image&) + +/*Image::*/Image& +Image::operator= (Image& i) { + if (this == &i) + return *this; + width(i.width()); + height(i.height()); + format(i.format()); + type(i.type()); + alignment(i.alignment()); + _invalid = vbAll; + reserve(); + memcpy(pixels(), i.pixels(), height() * rowSizeInBytes()); + return *this; +} // Image::operator= + +Image::~Image() { + if (_pixels) + delete[] _pixels; +} + +/////////////////////////////////////////////////////////////////////////////// +// pixels - set pointer to pixel array +/////////////////////////////////////////////////////////////////////////////// +void +Image::pixels(char* p) { + // We always own our pixels, so delete the old ones (if any) before + // installing new ones: + if (_pixels) + delete[] _pixels; + _pixels = p; +} // Image::pixels + +/////////////////////////////////////////////////////////////////////////////// +// reserve - reserve memory for image (assuming current type, format, and size) +/////////////////////////////////////////////////////////////////////////////// +void +Image::reserve() { + pixels(0); // deallocate old pixel array + pixels(new char[height() * rowSizeInBytes()]); +} // Image::reserve + +/////////////////////////////////////////////////////////////////////////////// +// validateRowSizeInBytes - compute image row size, measured in bytes +/////////////////////////////////////////////////////////////////////////////// +GLsizei +Image::validateRowSizeInBytes() { + _rowSizeInBytes = + (width() * pixelSizeInBytes() + alignment() - 1) + & ~(alignment() - 1); + validate(vbRowSizeInBytes); + return _rowSizeInBytes; +} // Image::calcRowSizeInBytes + +/////////////////////////////////////////////////////////////////////////////// +// validatePixelSizeInBytes - compute pixel size, measured in bytes +/////////////////////////////////////////////////////////////////////////////// +GLsizei +Image::validatePixelSizeInBytes() { + switch (format()) { + case GL_LUMINANCE: + _pixelSizeInBytes = 1; + break; + case GL_LUMINANCE_ALPHA: + _pixelSizeInBytes = 2; + break; + case GL_RGB: + _pixelSizeInBytes = 3; + break; + case GL_RGBA: + _pixelSizeInBytes = 4; + break; + default: + throw BadFormat(format()); + } + + switch (type()) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + break; + case GL_SHORT: + case GL_UNSIGNED_SHORT: + _pixelSizeInBytes <<= 1; + break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + _pixelSizeInBytes <<= 2; + break; + default: + throw BadType(type()); + } + + validate(vbPixelSizeInBytes); + return _pixelSizeInBytes; +} + +}; // namespace GLEAN diff --git a/tests/glean/lex.cpp b/tests/glean/lex.cpp new file mode 100644 index 00000000..84891f14 --- /dev/null +++ b/tests/glean/lex.cpp @@ -0,0 +1,167 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// lex.cpp: Implementation of simple lexical analyzer + +#include <ctype.h> +#include <stdlib.h> +#include "lex.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructor: +/////////////////////////////////////////////////////////////////////////////// +Lex::Lex(const char* s, bool ignoreCase/* = false */) { + input = s; + p = input; + ignoringCase = ignoreCase; +} // Lex::Lex + +/////////////////////////////////////////////////////////////////////////////// +// next - Fetch next token from the input string +/////////////////////////////////////////////////////////////////////////////// +void +Lex::next() { + while (isspace(*p)) + ++p; + + if (isalpha(*p) || *p == '_') { + id = ""; + if (ignoringCase) + while (isalnum(*p) || *p == '_') + id += tolower(*p++); + else + while (isalnum(*p) || *p == '_') + id += *p++; + token = ID; + return; + } + + if (isdigit(*p)) { + iValue = strtol(p, const_cast<char**>(&p), 0); + token = ICONST; + return; + } + + char nextC = 0; + char c = *p++; + if (c) + nextC = *p; + switch (c) { + case '|': + if (nextC == '|') { + ++p; + token = OR_OR; + } + else + token = OR; + break; + case '&': + if (nextC == '&') { + ++p; + token = AND_AND; + } + else + token = AND; + break; + case '<': + if (nextC == '=') { + ++p; + token = LE; + } + else + token = LT; + break; + case '>': + if (nextC == '=') { + ++p; + token = GE; + } + else + token = GT; + break; + case '=': + if (nextC == '=') { + ++p; + token = EQ; + } + else + token = ASSIGN; + break; + case '!': + if (nextC == '=') { + ++p; + token = NE; + } + else + token = BANG; + break; + case '+': + token = PLUS; + break; + case '-': + token = MINUS; + break; + case '*': + token = STAR; + break; + case '/': + token = SLASH; + break; + case '%': + token = PERCENT; + break; + case ',': + token = COMMA; + break; + case '(': + token = LPAREN; + break; + case ')': + token = RPAREN; + break; + case '.': + token = DOT; + break; + case '\0': + token = END; + --p; // push back '\0' so that token will always be END + break; + default: + throw Lexical("unrecognized symbol", p - input); + } + + return; +} // Lex::next + +} // namespace GLEAN diff --git a/tests/glean/lex.h b/tests/glean/lex.h new file mode 100644 index 00000000..87e24dfc --- /dev/null +++ b/tests/glean/lex.h @@ -0,0 +1,128 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// lex.h: Simple lexical analysis utilities + +// Given a string containing C-style tokens, returns information about +// successive tokens. Doesn't support all C tokens; just the few that +// GLEAN needs. + + +#ifndef __lex_h__ +#define __lex_h__ + +#include <string> + +#ifdef __WIN__ +// ERROR is already defined in some windows header file +#undef ERROR +#endif + +namespace GLEAN { + +class Lex { + protected: + + // State information: + const char* input; + const char* p; + bool ignoringCase; + + public: + + // Constructors: + + Lex(const char* s, bool ignoreCase = false); + // Creates a lexer which will draw input from the given string. + + // Exceptions: + + struct Error { }; // Base class for errors. + struct Lexical: public Error { // Lexical error in string. + const char* err; + int position; + Lexical(const char* e, int p) { + err = e; + position = p; + } + }; + struct InternalError: public Error { // Shouldn't happen. + }; + + // Tokens: + + typedef enum { + ERROR = 0, // erroneous token; must be zero + END, // end of input + + AND, // & + AND_AND, // && + ASSIGN, // = + BANG, // ! + COMMA, // , + DOT, // . + EQ, // == + GE, // >= + GT, // > + LE, // <= + LPAREN, // ( + LT, // < + MINUS, // - + NE, // != + OR, // | + OR_OR, // || + PERCENT, // % + PLUS, // + + RPAREN, // ) + SLASH, // / + STAR, // * + + ICONST, // signed integer constant + + ID // identifier + } Token; + + // Utilities: + + void next(); // fetch next token + + Token token; // current token + std::string id; // most recent identifier + int iValue; // most recent signed integer value + + inline int position() { // current position in input string + return p - input; + } +}; // class Lex + +} // namespace GLEAN + +#endif // __lex_h__ diff --git a/tests/glean/main.cpp b/tests/glean/main.cpp new file mode 100644 index 00000000..c40b5db5 --- /dev/null +++ b/tests/glean/main.cpp @@ -0,0 +1,347 @@ +// 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 + + + + +// main.cpp: main program for Glean + +using namespace std; + +#include <cassert> +#include <iostream> +#include <string> +#include <vector> +#include <algorithm> +#include "options.h" +#include "environ.h" +#include "test.h" +#include "version.h" +#include "lex.h" +#include "dsfilt.h" + +using namespace GLEAN; + +char* mandatoryArg(int argc, char* argv[], int i); +void selectTests(Options& o, vector<string>& allTestNames, int argc, + char* argv[], int i); +void usage(char* command); +void listTests(const Test *tests, bool verbose); + +int +main(int argc, char* argv[]) { + + // Until someone gets around to writing a fancy GUI front-end, + // we'll set options the old-fashioned way. + Options o; + + vector<string> allTestNames; + for (Test* t = Test::testList; t; t = t->nextTest) + allTestNames.push_back(t->name); + sort(allTestNames.begin(), allTestNames.end()); + o.selectedTests = allTestNames; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) { + usage(argv[0]); + } else if (!strcmp(argv[i], "-v") + || !strcmp(argv[i], "--verbose")) { + ++o.verbosity; + } else if (!strcmp(argv[i], "-r") + || !strcmp(argv[i], "--run")) { + o.mode = Options::run; + ++i; + o.db1Name = mandatoryArg(argc, argv, i); + } 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; + ++i; + o.db1Name = mandatoryArg(argc, argv, i); + ++i; + o.db2Name = mandatoryArg(argc, argv, i); + } else if (!strcmp(argv[i], "--visuals")) { + ++i; + o.visFilter = mandatoryArg(argc, argv, i); + } else if (!strcmp(argv[i], "-t") + || !strcmp(argv[i], "--tests")) { + ++i; + 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")) { + ++i; + o.dpyName = mandatoryArg(argc, argv, i); +# endif + } else { + usage(argv[0]); + } + } + + if (o.mode == Options::notSet) + usage(argv[0]); + + if (o.mode == Options::listtests) { + listTests(Test::testList, o.verbosity); + exit(0); + } + + // Create the test environment, then invoke each test to generate + // results or compare two previous runs. + try { + Environment e(o); + switch (o.mode) { + case Options::run: + { + for (Test* t = Test::testList; t; t = t->nextTest) + if (binary_search(o.selectedTests.begin(), + o.selectedTests.end(), t->name)) + t->run(e); + break; + } + case Options::compare: + { + for (Test* t = Test::testList; t; t = t->nextTest) + if (binary_search(o.selectedTests.begin(), + o.selectedTests.end(), t->name)) + try { + t->compare(e); + } + catch (Test::CantOpenResultsFile e) { + // For comparisons, we want to + // continue running even if a + // test result file can't be + // opened. We report the + // problem here, but don't exit + // as we would in the catch{} + // below. + cerr << "Can't open results file for test " + << e.testName + << " in database " + << e.dbName + << '\n'; + } + 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; + } + } +#if defined(__X11__) + catch (WindowSystem::CantOpenDisplay) { + cerr << "can't open display " << o.dpyName << "\n"; + exit(1); + } +#endif + catch (WindowSystem::NoOpenGL) { + cerr << "display doesn't support OpenGL\n"; + exit(1); + } + catch (DrawingSurfaceFilter::Syntax e) { + cerr << "Syntax error in visual selection criteria:\n" + "'" << o.visFilter << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << '\n'; + exit(1); + } + catch (Environment::DBExists) { + cerr << "Won't overwrite existing database " << o.db1Name + << "\n"; + exit(1); + } + catch (Environment::DBCantOpen e) { + cerr << "Can't open database directory " << *e.db << "\n"; + exit(1); + } + catch (Test::CantOpenResultsFile e) { + cerr << "Can't open results file for test " << e.testName + << " in database " << e.dbName << '\n'; + exit(1); + } + catch (...) { + cerr << "caught an unexpected error in main()\n"; + exit(1); + } + + return 0; +} // main + + +char* +mandatoryArg(int argc, char* argv[], int i) { + if (i < argc && argv[i][0] != '-') + return argv[i]; + usage(argv[0]); + /*NOTREACHED*/ + return 0; +} // mandatoryArg + + +void +selectTests(Options& o, vector<string>& allTestNames, int argc, char* argv[], + int i) { + if (i >= argc) + usage(argv[0]); + + // At present, we deal with the following syntax: + // [+] testname {(+|-) testname} + // Assume we're running none of the tests, then include + // those preceded by "+" and exclude those preceded by + // "-". + // - testname {(+|-) testname} + // Assume we're running all of the tests, then exclude + // those preceded by "-" and include those preceded by + // "+". + // XXX It would be nice to support the syntax "@filename" to mean + // "the list of tests given in the named file." This could be + // preceded by "+" or "-" just like an ordinary test name, or maybe + // the +/- should be required in the file itself. + + struct SyntaxError { + int position; + SyntaxError(int pos): position(pos) { } + }; + + Lex lex(argv[i]); + try { + lex.next(); + if (lex.token == Lex::MINUS) + o.selectedTests = allTestNames; + else + o.selectedTests.resize(0); + + while (lex.token != Lex::END) { + bool inserting = true; + if (lex.token == Lex::MINUS) { + inserting = false; + lex.next(); + } else if (lex.token == Lex::PLUS) + lex.next(); + + if (lex.token != Lex::ID) + throw SyntaxError(lex.position()); + + if (!binary_search(allTestNames.begin(), + allTestNames.end(), lex.id)) + cerr << "Warning: " << lex.id << " ignored;" + " not a valid test name.\n"; + else { + vector<string>::iterator p = + lower_bound(o.selectedTests.begin(), + o.selectedTests.end(), lex.id); + if (inserting) { + if (p == o.selectedTests.end() + || *p != lex.id) + o.selectedTests.insert(p, + lex.id); + } else { + // removing + if (p != o.selectedTests.end() + && *p == lex.id) + o.selectedTests.erase(p); + } + } + lex.next(); + } + } + catch (Lex::Lexical e) { + cerr << "Lexical error in test inclusion/exclusion list:\n" + "'" << argv[i] << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ " << e.err << "\n\n"; + usage(argv[0]); + } + catch (SyntaxError e) { + cerr << "'" << argv[i] << "'\n"; + for (int i = 0; i < e.position; ++i) + cerr << ' '; + cerr << "^ Syntax error in test inclusion/exclusion list\n\n"; + usage(argv[0]); + } +} // selectTests + + +void +listTests(const Test *tests, bool verbose) { + for (const Test *t = tests; t; t = t->nextTest) { + cout << t->name << (verbose? ":" : "") << "\n"; + if (verbose) { + cout << t->description; + cout << '\n'; + } + } +} + + +void +usage(char* command) { + cerr << GLEAN::versionString << '\n'; + cerr << "Usage: " << command << " mode [options]\n" +"\n" +"mode:\n" +" (-r|--run) results-directory\n" +" or (-c|--compare) old-results-dir new-results-dir\n" +"\n" +"options:\n" +" (-v|--verbose) # each occurrence increases\n" +" # verbosity of output\n" +" (-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" +" (or --display)\n" +#elif defined(__WIN__) +#endif + ; + exit(1); +} // usage diff --git a/tests/glean/misc.cpp b/tests/glean/misc.cpp new file mode 100644 index 00000000..6d8aee4b --- /dev/null +++ b/tests/glean/misc.cpp @@ -0,0 +1,82 @@ +// 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 + + + + +// misc.cpp: implementation of miscellaneous functions + +#include <cctype> +#include <cmath> +#include "misc.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Utility routine to skip whitespace in streams +// This is helpful when interleaving invocations of getline() and +// operator>>. In particular, after operator>> at the end of a line, +// there may be whitespace (especially a newline) remaining; this may +// confuse a subsequent invocation of getline(). +/////////////////////////////////////////////////////////////////////////////// +void +SkipWhitespace(istream& s) { + char c; + while (s.get(c)) { + if (!isspace(c)) { + s.putback(c); + break; + } + } +} // SkipWhitespace + + +/////////////////////////////////////////////////////////////////////////////// +// Utility routine to compute logarithm, base two +/////////////////////////////////////////////////////////////////////////////// +double +log2(double x) { + return 1.4426950408889634 * log(x); +} + +/////////////////////////////////////////////////////////////////////////////// +// Utility routine to compute error, expressed in bits +// Typically used to convert a floating-point error (in the range [0,1]) +// to the number of bits in the representation of a color. +/////////////////////////////////////////////////////////////////////////////// +double +ErrorBits(double absError, int repBits) { + if (absError <= 0.0) + return 0.0; + double log2Error = log2(absError) + repBits; + return (log2Error <= 0.0)? 0.0: log2Error; +} // ErrorBits + + +} // namespace GLEAN diff --git a/tests/glean/misc.h b/tests/glean/misc.h new file mode 100644 index 00000000..0590a5fd --- /dev/null +++ b/tests/glean/misc.h @@ -0,0 +1,50 @@ +// 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 + + + + +// misc.h: Miscellaneous functions + + +#ifndef __misc_h__ +#define __misc_h__ + +using namespace std; + +#include <iostream> + +namespace GLEAN { + +void SkipWhitespace(istream& s); +double ErrorBits(double absError, int repBits); +double log2(double x); + +} // namespace GLEAN + +#endif // __misc_h__ diff --git a/tests/glean/options.cpp b/tests/glean/options.cpp new file mode 100644 index 00000000..9bda4e48 --- /dev/null +++ b/tests/glean/options.cpp @@ -0,0 +1,66 @@ +// 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 + + + + +// options.cpp: implementation of global options class + +#include "options.h" +#if defined(__X11__) +# include <stdlib.h> +#endif + +namespace GLEAN { + + + +Options::Options() { + mode = notSet; + verbosity = 0; + db1Name = "results"; + db2Name = "previous"; + visFilter = "1"; + selectedTests.resize(0); + overwrite = false; + ignorePrereqs = false; +# if defined(__X11__) + { + char* display = getenv("DISPLAY"); + if (display) + dpyName = display; + else + dpyName = ":0"; + } +# elif defined(__WIN__) +# endif +} // Options::Options() + + + +} // namespace GLEAN diff --git a/tests/glean/options.h b/tests/glean/options.h new file mode 100644 index 00000000..ad476d37 --- /dev/null +++ b/tests/glean/options.h @@ -0,0 +1,97 @@ +// 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 + + + + +// options.h: global test options + +// This class encapsulates global options that apply to the entire +// testing process -- things like the display name (for X11), +// constraints on the drawing surface configurations to be tested, +// locations of test results files, etc. + +// 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. +// 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. + + + +#ifndef __options_h__ +#define __options_h__ + +using namespace std; + +#include <string> +#include <vector> + +namespace GLEAN { + +class Options { + public: + typedef enum {notSet, run, compare, listtests, listdetails} RunMode; + RunMode mode; // Indicates whether we're generating + // results, or comparing two previous runs. + + int verbosity; // Verbosity level. 0 == concise; larger + // values imply more verbose output. + + string db1Name; // Name of output database, or one of + // the two databases being compared. + // Typically the pathname of a directory, + // provided on the command line. + + string db2Name; // Name of the second database being + // compared. + + string visFilter; // Filter constraining the set of visuals + // (FBConfigs, pixel formats) that will be + // available for test. See + // DrawingSurfaceFilter for description of + // contents. + + vector<string> selectedTests; + // 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. +#elif defined(__WIN__) +#endif + + Options(); +}; // class Options + +} // namespace GLEAN + +#endif // __options_h__ diff --git a/tests/glean/pack.cpp b/tests/glean/pack.cpp new file mode 100644 index 00000000..11617ef4 --- /dev/null +++ b/tests/glean/pack.cpp @@ -0,0 +1,262 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Data packing utilities. Note that these map component values per +// the usual OpenGL conversions. Also, see comments in unpack.cpp. + +#include "image.h" + +namespace { + +#define SCALE (static_cast<double>(num) / static_cast<double>(denom)) +#define BIAS (static_cast<double>(bias) / static_cast<double>(denom)) + +// The original implementation of packing functions (using function +// templates) wouldn't compile under VC6, but this slight variant +// using static member functions in a class template will compile. + +template<class component, int num, unsigned int denom, int bias> +class Pack +{ +public : + // pack_l + static void pack_l(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + else + out[0] = static_cast<component>(SCALE * rgba[0]); + out += 1; + } + } + + // pack_la + static void pack_la(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[3] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[3]); + } + out += 2; + } + } + + // pack_rga + static void pack_rgb(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[1] - BIAS); + out[2] = static_cast<component>(SCALE * rgba[2] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[1]); + out[2] = static_cast<component>(SCALE * rgba[2]); + } + out += 3; + } + } + + // pack_rgba + static void pack_rgba(GLsizei n, char* dst, double* rgba) + { + component* out = reinterpret_cast<component*>(dst); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + out[0] = static_cast<component>(SCALE * rgba[0] - BIAS); + out[1] = static_cast<component>(SCALE * rgba[1] - BIAS); + out[2] = static_cast<component>(SCALE * rgba[2] - BIAS); + out[3] = static_cast<component>(SCALE * rgba[3] - BIAS); + } else { + out[0] = static_cast<component>(SCALE * rgba[0]); + out[1] = static_cast<component>(SCALE * rgba[1]); + out[2] = static_cast<component>(SCALE * rgba[2]); + out[3] = static_cast<component>(SCALE * rgba[3]); + } + out += 4; + } + } + +}; // class Pack + +#undef SCALE +#undef BIAS + +}; // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Public interface +/////////////////////////////////////////////////////////////////////////////// +void +Image::pack(GLsizei n, char* nextPixel, double* rgba) { + (*(valid(vbPacker)? _packer: validatePacker())) (n, nextPixel, rgba); +} + +/////////////////////////////////////////////////////////////////////////////// +// validatePacker - select appropriate pixel-packing utility +/////////////////////////////////////////////////////////////////////////////// + +Image::Packer* +Image::validatePacker() { + switch (format()) { + case GL_LUMINANCE: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_l; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_l; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_l; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_l; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_l; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_l; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_l; + break; + default: + throw BadType(type()); + } + break; + case GL_LUMINANCE_ALPHA: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_la; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_la; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_la; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_la; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_la; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_la; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_la; + break; + default: + throw BadType(type()); + } + break; + case GL_RGB: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_rgb; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_rgb; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_rgb; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_rgb; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_rgb; + break; + default: + throw BadType(type()); + } + break; + case GL_RGBA: + switch (type()) { + case GL_BYTE: + _packer = Pack<GLbyte, 255, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_BYTE: + _packer = Pack<GLubyte, 255, 1, 0>::pack_rgba; + break; + case GL_SHORT: + _packer = Pack<GLshort, 65535, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_SHORT: + _packer = Pack<GLushort, 65535, 1, 0>::pack_rgba; + break; + case GL_INT: + _packer = Pack<GLint, 4294967295U, 2, 1>::pack_rgba; + break; + case GL_UNSIGNED_INT: + _packer = Pack<GLuint, 4294967295U, 1, 0>::pack_rgba; + break; + case GL_FLOAT: + _packer = Pack<GLfloat, 1, 1, 0>::pack_rgba; + break; + default: + throw BadType(type()); + } + break; + default: + throw BadFormat(format()); + } + + validate(vbPacker); + return _packer; +} + +}; // namespace GLEAN diff --git a/tests/glean/rand.h b/tests/glean/rand.h new file mode 100644 index 00000000..b47684d7 --- /dev/null +++ b/tests/glean/rand.h @@ -0,0 +1,125 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// rand.h: Simple random sequence generation utilities. + +// We provide these to eliminate dependencies on the operating +// system's random number generator. This makes it possible to +// compare results for a given graphics device running under different +// operating systems. + +// Based on Numerical Recipes, 2d ed., p. 284. + + +#ifndef __rand_h__ +#define __rand_h__ + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// RandomBase: Quick-and-dirty linear congruential generator that serves as +// a base for other random-sequence classes. +/////////////////////////////////////////////////////////////////////////////// +class RandomBase { + unsigned int i; + public: + inline RandomBase(unsigned int seed): i(seed) { } + inline RandomBase(): i(1) { } + inline unsigned int next() { + i = 1664525 * i + 1013904223; + return i; + } +}; // class RandomBase + +/////////////////////////////////////////////////////////////////////////////// +// RandomBits: Returns a given number of random bits (expressed as an unsigned +// int, so the maximum portable number of bits is 32). +/////////////////////////////////////////////////////////////////////////////// +class RandomBits: public RandomBase { + unsigned int shift; + public: + inline RandomBits(unsigned int bits, unsigned int seed): + RandomBase(seed), shift(32 - bits) { } + inline RandomBits(unsigned int bits): + RandomBase(), shift(32 - bits) { } + inline unsigned int next() { return RandomBase::next() >> shift; } +}; // class RandomBits + +/////////////////////////////////////////////////////////////////////////////// +// RandomSignedBits: Returns a given number of random bits (expressed as a +// signed int, so the maximum portable number of bits is 32 including sign). +/////////////////////////////////////////////////////////////////////////////// +class RandomSignedBits: public RandomBase { + unsigned int shift; + public: + inline RandomSignedBits(unsigned int bits, unsigned int seed): + RandomBase(seed), shift(32 - bits) { } + inline RandomSignedBits(unsigned int bits): + RandomBase(), shift(32 - bits) { } + inline int next() { + return static_cast<int>(RandomBase::next()) >> shift; + } +}; // class RandomSignedBits + +/////////////////////////////////////////////////////////////////////////////// +// RandomDouble: Returns a random floating-point value in the closed +// interval [0.0, 1.0]. +/////////////////////////////////////////////////////////////////////////////// +class RandomDouble: public RandomBase { + public: + inline RandomDouble(unsigned int seed): RandomBase(seed) { } + inline RandomDouble(): RandomBase() { } + inline double next() { + return static_cast<double>(RandomBase::next()) / 4294967295.0; + } +}; // class RandomDouble + +/////////////////////////////////////////////////////////////////////////////// +// RandomBitsDouble: Returns a random floating-point value in the closed +// interval [0.0, 1.0], but with possible values limited by a generator +// returning a specific number of bits. +/////////////////////////////////////////////////////////////////////////////// +class RandomBitsDouble: public RandomBits { + double scale; + public: + inline RandomBitsDouble(unsigned int bits, unsigned int seed): + RandomBits(bits, seed) { scale = (1 << bits) - 1.0; } + inline RandomBitsDouble(unsigned int bits): + RandomBits(bits) { scale = (1 << bits) - 1.0; } + inline double next() { + return static_cast<double>(RandomBits::next()) / scale; + } +}; // class RandomBitsDouble + +} // namespace GLEAN + +#endif // __rand_h__ diff --git a/tests/glean/rc.cpp b/tests/glean/rc.cpp new file mode 100644 index 00000000..8cc95b3d --- /dev/null +++ b/tests/glean/rc.cpp @@ -0,0 +1,159 @@ +// 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 + + + + +// rc.cpp: implementation of rendering context utilities + +#include <iostream> +#include <algorithm> +#include "rc.h" +#include "dsconfig.h" +#include "winsys.h" + +namespace { + +#if defined (__WIN__) + +// XXX +// wglCreateContext requires a handle to a device context. +// The ctor of RenderingContext doesn't know which window +// it is creating a surface for, only what the pixelformat +// of that window is. The hDC passed to wglCreateContext +// doesn't have to be the same as the one use in SwapBuffers +// or wglMakeCurrent, their pixelformats have to be the +// same though. A limitation is that the pixelformat of +// a window can only be set once. That is why a +// temporary window is created. + + +HGLRC create_context(GLEAN::DrawingSurfaceConfig &c) +{ + HWND hwnd = CreateWindow("STATIC","temp",WS_POPUP, + CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, + 0,0,GetModuleHandle(NULL),0); + + if (!hwnd) + return 0; + + HDC hDC = GetDC(hwnd); + if (!hDC) + return 0; + + PIXELFORMATDESCRIPTOR pfd; + + if (!SetPixelFormat(hDC,c.pfdID,&pfd)) + { + ReleaseDC(hwnd,hDC); + DestroyWindow(hwnd); + return 0; + } + + HGLRC rc = wglCreateContext(hDC); + if (!rc) + return 0; + + ReleaseDC(hwnd,hDC); + DestroyWindow(hwnd); + + return rc; +} + +#endif + +} // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +RenderingContext::RenderingContext(WindowSystem& ws, DrawingSurfaceConfig& c, + RenderingContext* share, bool direct) { + + // Link back to enclosing window system, so as to simplify bookkeeping: + winSys = &ws; + ws.contexts.push_back(this); + +# if defined(__X11__) + +# if defined(GLX_VERSION_1_3) + + if (ws.GLXVersMajor == 1 && ws.GLXVersMinor < 3) + goto legacyMethod; + // XXX Need GLX 1.3 rc constructor code + // For now, just fall through. + +# endif + +legacyMethod: + // Create the rendering context: + rc = glXCreateContext(winSys->dpy, c.vi, (share? share->rc: 0), + direct? True: False); + if (!rc) + throw Error(); + // XXX Ideally, we would deal with X11 and GLX errors here, too + // (Badmatch, BadValue, GLXBadContext, BadAlloc) + +# elif defined(__WIN__) + + rc = create_context(c); + if (!rc) + throw Error(); +# elif defined(__AGL__) + rc = aglCreateContext(c.pf, NULL); + if(rc == NULL) + throw Error(); +# endif + +} // RenderingContext::RenderingContext + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// + +RenderingContext::~RenderingContext() { + remove(winSys->contexts.begin(), winSys->contexts.end(), this); +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) + if (winSys->GLXVersMajor == 1 && winSys->GLXVersMinor < 3) + goto legacyMethod; + // XXX Need to write GLX 1.3 rc destructor + // For now, just fall through. +# endif +legacyMethod: + glXDestroyContext(winSys->dpy, rc); +# elif defined(__WIN__) + wglDeleteContext(rc); +# endif +} // RenderingContext::~RenderingContext + + +} // namespace GLEAN diff --git a/tests/glean/rc.h b/tests/glean/rc.h new file mode 100644 index 00000000..eaea5150 --- /dev/null +++ b/tests/glean/rc.h @@ -0,0 +1,68 @@ +// 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 + + + + +// rc.h: utilities for manipulating rendering contexts + +#ifndef __rc_h__ +#define __rc_h__ + +#include "glwrap.h" + +namespace GLEAN { + +class WindowSystem; // Forward and mutually-recursive references +class DrawingSurfaceConfig; + +class RenderingContext { + public: + RenderingContext(WindowSystem& ws, DrawingSurfaceConfig& c, + RenderingContext* share = 0, bool direct = true); + ~RenderingContext(); + + // Exceptions: + struct Error { }; // Base class for all errors. + + // Members: + WindowSystem* winSys; // Window system that owns this context. + +# if defined(__X11__) + GLXContext rc; +# elif defined(__WIN__) + ::HGLRC rc; +# elif defined(__AGL__) + ::AGLContext rc; +# endif + +}; // class RenderingContext + +} // namespace GLEAN + +#endif // __rc_h__ diff --git a/tests/glean/rdtiff.cpp b/tests/glean/rdtiff.cpp new file mode 100644 index 00000000..5e5068f2 --- /dev/null +++ b/tests/glean/rdtiff.cpp @@ -0,0 +1,156 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +#include "image.h" +#include "tiffio.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// readTIFF - read image from TIFF file, set attributes to match the file +/////////////////////////////////////////////////////////////////////////////// +void +Image::readTIFF(const char* filename) { + // XXX Things we explicitly don't handle: + // Varying number of bits per sample. Sam's library doesn't + // handle that. + // Bits per sample other than 8, 16, or 32. + // Tile-oriented TIFF files. We only do strip-oriented files. + // Planar configurations other than contiguous (R,G,B,R,G,B,...). + // Premultiplied alpha. If there's a fourth color channel, + // we just assume it's non-premultiplied alpha. + // Eventually would be good to add a ``validation'' function which + // checks a file before attempting to read the image it contains. + // Also: need error-reporting code. + + TIFF* tf = TIFFOpen(filename, "r"); + if (!tf) + throw CantOpen(filename); + + uint32 u32; + + TIFFGetFieldDefaulted(tf, TIFFTAG_IMAGELENGTH, &u32); + height(u32); + + TIFFGetFieldDefaulted(tf, TIFFTAG_IMAGEWIDTH, &u32); + width(u32); + + uint16 samplesPerPixel; + TIFFGetFieldDefaulted(tf, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel); + switch (samplesPerPixel) { + case 1: + format(GL_LUMINANCE); + break; + case 2: + format(GL_LUMINANCE_ALPHA); + break; + case 3: + format(GL_RGB); + break; + case 4: + format(GL_RGBA); + break; + default: + TIFFClose(tf); + throw UnsupportedTIFF(); + } + + uint16 bitsPerSample; + TIFFGetFieldDefaulted(tf, TIFFTAG_BITSPERSAMPLE, &bitsPerSample); + uint16 sampleFormat; + if (TIFFGetField(tf, TIFFTAG_SAMPLEFORMAT, &sampleFormat) != 1) + sampleFormat = SAMPLEFORMAT_UINT; + switch ((sampleFormat << 8) | bitsPerSample) { + case (SAMPLEFORMAT_UINT << 8) | 8: + type(GL_UNSIGNED_BYTE); + break; + case (SAMPLEFORMAT_UINT << 8) | 16: + type(GL_UNSIGNED_SHORT); + break; + case (SAMPLEFORMAT_UINT << 8) | 32: + type(GL_UNSIGNED_INT); + break; + case (SAMPLEFORMAT_INT << 8) | 8: + type(GL_BYTE); + break; + case (SAMPLEFORMAT_INT << 8) | 16: + type(GL_SHORT); + break; + case (SAMPLEFORMAT_INT << 8) | 32: + type(GL_INT); + break; + case (SAMPLEFORMAT_IEEEFP << 8) | 32: + type(GL_FLOAT); + break; + default: + TIFFClose(tf); + throw UnsupportedTIFF(); + } + + // At the moment it's not obvious whether we should pad + // scanlines to achieve a preferred alignment, so we'll just + // return an alignment that matches the data. + alignment(1); + switch (rowSizeInBytes() & 0x7) { + case 0: + alignment(8); + break; + case 4: + alignment(4); + break; + case 2: + case 6: + alignment(2); + break; + case 1: + case 3: + case 5: + case 7: + alignment(1); + break; + } + + reserve(); + + { + // Store rows in reverse order, so that the default TIFF + // orientation won't result in an upside-down image for + // OpenGL: + GLsizei rowStep = rowSizeInBytes(); + char* row = pixels() + (height() - 1) * rowStep; + for (GLsizei r = 0; r < height(); ++r, row -= rowStep) + TIFFReadScanline(tf, row, r); + } + + TIFFClose(tf); +} // Image::readTIFF + +}; // namespace GLEAN diff --git a/tests/glean/reg.cpp b/tests/glean/reg.cpp new file mode 100644 index 00000000..87c8c41a --- /dev/null +++ b/tests/glean/reg.cpp @@ -0,0 +1,176 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Image registration. + +#include <cfloat> +#include "image.h" + +#include <cmath> // for fabs + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// register: compare a reference image to the current (``test'') image. +// +// The reference image must be no larger than the current image, in +// both dimensions. Type doesn't matter, as both images will be +// converted to RGBA. +// +// The reference image will be slid into all possible positions over +// the current image, and the sum of the mean absolute errors for all +// four color channels computed at each position. +// +// Returns an Image::Registration struct that specifies the position at +// which the sum of mean absolute errors was minimal, plus the statistics +// at that position. +/////////////////////////////////////////////////////////////////////////////// +Image::Registration +Image::reg(Image& ref) { + int wt = width(); // Width of test image, in pixels. + int ht = height(); // Height of test image, in pixels. + int wr = ref.width(); // Width of reference image, in pixels. + int hr = ref.height(); // Height of test image, in pixels. + int dh = ht - hr; // Difference in heights, in pixels. + int dw = wt - wr; // Difference in widths, in pixels. + int i; + + if (dh < 0 || dw < 0) + throw RefImageTooLarge(); + + int wt4 = 4 * wt; // Width of test image, in RGBA samples. + int wr4 = 4 * wr; // Width of ref image, in RGBA samples. + int dw4 = 4 * dw; // Difference in widths, in samples. + + double** testPix; // Buffers containing all the rows of + // the test image that need to be + // accessed concurrently. + // XXX sure would be nice to use auto_ptr to allocate this stuff, + // but it isn't supported in the STL that came with egcs 1.1.2. + + // XXX testPix = new (double*) [dh + 1]; + // VC 6 seems to misinterpret this as a c-style cast + testPix = new double* [dh + 1]; + + + for (/*int */i = 0; i <= dh; ++i) + testPix[i] = new double [wt4]; + + double* refPix = new double [wr4]; + // Buffer containing the one row of + // the reference image that's accessed + // at any given time. + + BasicStats** stats; // Buffers containing a statistics- + // gathering structure for each of + // the possible reference image + // positions. + // XXX stats = new (BasicStats*) [dh + 1]; + // VC 6 seems to misinterpret this as a c-style cast + stats = new BasicStats * [dh + 1]; + + for (/*int*/ i = 0; i <= dh; ++i) + stats[i] = new BasicStats [dw4 + 4]; + + // Prime the pump by unpacking the first few rows of the test image: + char* testRow = pixels(); + for (/*int*/ i = 0; i < dh; ++i) { + unpack(wt, testPix[i], testRow); + testRow += rowSizeInBytes(); + } + + // Now accumulate statistics for one row of the reference image + // at a time, in all possible positions: + char* refRow = ref.pixels(); + for (/*int*/ i = 0; i < hr; ++i) { + // Get the next row of the reference image: + ref.unpack(wr, refPix, refRow); + refRow += ref.rowSizeInBytes(); + + // Get the next row of the test image: + unpack(wt, testPix[dh], testRow); + testRow += rowSizeInBytes(); + + // Accumulate absolute error for R,G,B,A in all positions: + for (int j = 0; j <= dh; ++j) + for (int k = 0; k <= dw4; k += 4) + for (int m = 0; m < wr4; m += 4) { + stats[j][k+0].sample( fabs( + refPix[m+0]-testPix[j][m+k+0])); + stats[j][k+1].sample( fabs( + refPix[m+1]-testPix[j][m+k+1])); + stats[j][k+2].sample( fabs( + refPix[m+2]-testPix[j][m+k+2])); + stats[j][k+3].sample( fabs( + refPix[m+3]-testPix[j][m+k+3])); + } + } + + // Now find the position for which the sum of the mean absolute errors + // is minimal: + double minErrorSum = DBL_MAX; + int minI = 0; + int minJ = 0; + for (/*int*/ i = 0; i <= dh; ++i) + for (int j = 0; j <= dw4; j += 4) { + double errorSum = stats[i][j+0].mean() + + stats[i][j+1].mean() + + stats[i][j+2].mean() + + stats[i][j+3].mean(); + if (errorSum < minErrorSum) { + minErrorSum = errorSum; + minI = i; + minJ = j; + } + } + + Registration r; + r.wOffset = minJ / 4; + r.hOffset = minI; + r.stats[0] = stats[minI][minJ+0]; + r.stats[1] = stats[minI][minJ+1]; + r.stats[2] = stats[minI][minJ+2]; + r.stats[3] = stats[minI][minJ+3]; + + // Clean up: + for (/*int*/ i = 0; i <= dh; ++i) + delete[] testPix[i]; + delete[] testPix; + delete[] refPix; + for (/*int*/ i = 0; i <= dh; ++i) + delete[] stats[i]; + delete[] stats; + + return r; +} // Image::register + +}; // namespace GLEAN diff --git a/tests/glean/stats.h b/tests/glean/stats.h new file mode 100644 index 00000000..d9eb019f --- /dev/null +++ b/tests/glean/stats.h @@ -0,0 +1,91 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// stats.h: simple statistics-gathering utilities for glean + +// These are rather simplistic. For more robust implementations, consider +// using Numerical Recipes. + +#ifndef __stats_h__ +#define __stats_h__ + +#include <vector> + +#ifdef __WIN__ +using namespace std; +#endif + +#if defined( __WIN__ ) + +#undef min +#undef max + +#endif + +namespace GLEAN { + +class BasicStats { + int _n; + double _min; + double _max; + double _sum; + double _sum2; + public: + void init(); + inline int n() const {return _n;} + inline double min() const {return _min;} + inline double max() const {return _max;} + inline double sum() const {return _sum;} + inline double sum2() const {return _sum2;} + double mean() const; + double variance() const; + double deviation() const; + inline void sample(double d) { + ++_n; + if (d < _min) + _min = d; + if (d > _max) + _max = d; + _sum += d; + _sum2 += d*d; + } + + BasicStats() {init();} + template<class T> BasicStats(std::vector<T>& v) { + init(); + for (typename std::vector<T>::const_iterator p = v.begin(); p < v.end(); ++p) + sample(*p); + } +}; // class BasicStats + +} // namespace GLEAN + +#endif // __stats_h__ diff --git a/tests/glean/tbase.h b/tests/glean/tbase.h new file mode 100644 index 00000000..47675427 --- /dev/null +++ b/tests/glean/tbase.h @@ -0,0 +1,414 @@ +// 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 +// 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 + +/* +tbase.h: Base template class for (most) tests + +In general, a glean test is an instance of a class derived from the +class Test. It produces a vector of results, which are instances of a +class derived from the class Result. Most glean tests are "portable" +in the sense that they don't contain OS- or window-system-specific +code; those things are abstracted by the Environment and WindowSystem +classes. + +This file contains a template class and result class that serve as +bases for portable tests, and several macros that simplify test class +declarations. + +The result class, BaseResult, includes utility functions that read and +write test results. To use it, derive a new class from it, add +whatever variables you need to store your test results, and override +the getresults() and putresults() member functions to read and write +those variables from and to a stream. + +The template class, BaseTest, is parameterized by the result class and +declares member functions and variables that are common to all +portable tests. These include the member functions run() and +compare() which are invoked for each test by main(). BaseTest also +provides several variables which you might want to use when +constructing a test: + + A drawing surface filter string. The test can be run on all + the drawing surface configurations that are selected by the + filter, and one result structure will be generated for each + such configuration. + + A flag indicating whether the test is to be run on *all* + drawing surface configurations, or just one. For tests that + take a long time to run, it is often sufficient to check just + one drawing surface configuration rather than all of them. + + An extension filter string. The test will only be run on + 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. + + Default width and height for any windows to be created by the + test. + + A pointer to an array of other tests that must be run before + running the current test. This makes the results of + "prerequisite" tests available. + +To use BaseTest, declare a new class derived from BaseTest, +parameterized by your result class. Then override the runOne(), +compareOne(), and logOne() member functions. runOne() runs a test and +generates a result. compareOne() compares one result from a test run +with the same result from another test run. logOne() generates a log +message summarizing the result of the test. + +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. +GLEAN_CLASS_WH() and GLEAN_CLASS_WHO() allow you to specify the width, +height, and run-once flag if you choose. + +Finally, declare an object using your new test class. This object +must be global, so that its constructor will automatically add it to +the list of all tests. + +You can find an example of this whole process in the files tbasic.h +and tbasic.cpp. + +*/ + + +#ifndef __tbase_h__ +#define __tbase_h__ + +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "misc.h" + +#include "test.h" + +class GLEAN::DrawingSurfaceConfig; // Forward reference. + +#define GLEAN_CLASS_WHO(TEST, RESULT, WIDTH, HEIGHT, ONE) \ + TEST(const char* aName, const char* aFilter, \ + const char* aDescription): \ + BaseTest<RESULT>(aName, aFilter, aDescription) { \ + fWidth = WIDTH; \ + fHeight = HEIGHT; \ + testOne = ONE; \ + } \ + TEST(const char* aName, const char* aFilter, Test** thePrereqs, \ + const char* aDescription): \ + BaseTest<RESULT>(aName, aFilter, thePrereqs, aDescription) { \ + fWidth = WIDTH; \ + fHeight = HEIGHT; \ + testOne = ONE; \ + } \ + TEST(const char* aName, const char* aFilter, \ + const char* anExtensionList, \ + const char* aDescription): \ + BaseTest<RESULT>(aName, aFilter, anExtensionList, aDescription) { \ + fWidth = WIDTH; \ + fHeight = HEIGHT; \ + } \ + virtual ~TEST() {} \ + \ + virtual void runOne(RESULT& r, Window& w); \ + virtual void compareOne(RESULT& oldR, RESULT& newR); \ + virtual void logOne(RESULT& r) + +#define GLEAN_CLASS_WH(TEST, RESULT, WIDTH, HEIGHT) \ + GLEAN_CLASS_WHO(TEST, RESULT, WIDTH, HEIGHT, false) + +#define GLEAN_CLASS(TEST, RESULT) \ + GLEAN_CLASS_WHO(TEST, RESULT, 258, 258, false) + +namespace GLEAN { + +class BaseResult : public Result { + // Class for a single test result. All basic tests have a + // drawing surface configuration, plus other information + // that's specific to the test. +public: + DrawingSurfaceConfig* config; + + 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; + if (!getline(s, configDesc)) return false; + config = new DrawingSurfaceConfig(configDesc); + return getresults(s); + } +}; + +template <class ResultType> class BaseTest: public Test { +public: + BaseTest(const char* aName, const char* aFilter, + const char* aDescription): Test(aName, aDescription) { + filter = aFilter; + extensions = 0; + description = aDescription; + fWidth = 258; + fHeight = 258; + testOne = false; + } + BaseTest(const char* aName, const char* aFilter, Test** thePrereqs, + const char* aDescription): + Test(aName, aDescription, thePrereqs) { + filter = aFilter; + extensions = 0; + description = aDescription; + fWidth = 258; + fHeight = 258; + testOne = false; + } + BaseTest(const char* aName, const char* aFilter, + const char* anExtensionList, + const char* aDescription): Test(aName, aDescription) { + filter = aFilter; + extensions = anExtensionList; + description = aDescription; + fWidth = 258; + fHeight = 258; + testOne = false; + } + + virtual ~BaseTest() {} + + const char* filter; // Drawing surface config filter. + const char* extensions; // Required extensions. + int fWidth; // Drawing surface width. + int fHeight; // Drawing surface height. + bool testOne; // Test only one config? + vector<ResultType*> results; // Test results. + + virtual void runOne(ResultType& r, Window& w) = 0; + virtual void compareOne(ResultType& oldR, ResultType& newR) = 0; + virtual void logOne(ResultType& r) = 0; + + virtual vector<ResultType*> getResults(istream& s) { + vector<ResultType*> v; + while (s.good()) { + ResultType* r = new ResultType(); + if (r->get(s)) + v.push_back(r); + else { + delete r; + break; + } + } + return v; + } + + virtual void logDescription() { + if (env->options.verbosity) + env->log << +"----------------------------------------------------------------------\n" + << description + << '\n'; + } + + 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); + } + env = &environment; // make environment available + logDescription(); // log invocation + WindowSystem& ws = env->winSys; + try { + OutputStream os(*this); // open results file + + // Select the drawing configurations for testing + DrawingSurfaceFilter f(filter); + vector<DrawingSurfaceConfig*> + configs(f.filter(ws.surfConfigs)); + + // Test each config + for (vector<DrawingSurfaceConfig*>::const_iterator + p = configs.begin(); + p < configs.end(); + ++p) { + Window w(ws, **p, fWidth, fHeight); + RenderingContext rc(ws, **p); + if (!ws.makeCurrent(rc, w)) + ; // XXX need to throw exception here + + // Check for all prerequisite extensions. Note + // that this must be done after the rendering + // context has been created and made current! + if (!GLUtils::haveExtensions(extensions)) + continue; + + // Create a result object and run the test: + ResultType* r = new ResultType(); + r->config = *p; + runOne(*r, w); + logOne(*r); + + // Save the result + results.push_back(r); + r->put(os); + if (testOne) break; + } + } + catch (DrawingSurfaceFilter::Syntax e) { + env->log << "Syntax error in test's drawing-surface" + << " selection criteria:\n'" + << filter + << "'\n"; + for (int i = 0; i < e.position; ++i) + env->log << ' '; + env->log << "^ " << e.err << '\n'; + } + catch (RenderingContext::Error) { + 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(); + // Read results from previous runs: + Input1Stream is1(*this); + vector<ResultType*> oldR(getResults(is1)); + Input2Stream is2(*this); + vector<ResultType*> newR(getResults(is2)); + + // Construct a vector of surface configurations from the + // old run. (Later we'll find the best match in this + // vector for each config in the new run.) + vector<DrawingSurfaceConfig*> oldConfigs; + for (typename vector<ResultType*>::const_iterator p = oldR.begin(); + p < oldR.end(); + ++p) + oldConfigs.push_back((*p)->config); + + // Compare results: + for (typename vector<ResultType*>::const_iterator newP = newR.begin(); + newP < newR.end(); + ++newP) { + + // Find the drawing surface config that most + // closely matches the config for this result: + int c = (*newP)->config->match(oldConfigs); + + // If there was a match, compare the results: + if (c < 0) + env->log << name + << ": NOTE no matching config for " + << (*newP) + ->config->conciseDescription() + << '\n'; + else + compareOne(*(oldR[c]), **newP); + } + + // Get rid of the results; we don't need them for future + // comparisons. + for (typename vector<ResultType*>::iterator np = newR.begin(); + np < newR.end(); + ++np) + delete *np; + for (typename vector<ResultType*>::iterator op = oldR.begin(); + op < oldR.end(); + ++op) + delete *op; + } + + // comparePassFail is a helper function for tests that have a + // boolean result as all or part of their ResultType + virtual void comparePassFail(ResultType& oldR, ResultType& newR) { + if (oldR.pass == newR.pass) { + if (env->options.verbosity) + env->log << name + << ": SAME " + << newR.config->conciseDescription() + << '\n' + << (oldR.pass + ? "\tBoth PASS\n" + : "\tBoth FAIL\n"); + } else { + env->log << name + << ": DIFF " + << newR.config->conciseDescription() + << '\n' + << '\t' + << env->options.db1Name + << (oldR.pass? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.pass? " PASS\n": " FAIL\n"); + } + } + + virtual void logPassFail(ResultType& r) { + env->log << name << (r.pass ? ": PASS ": ": FAIL "); + } + + 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 + +#endif // __tbasic_h__ diff --git a/tests/glean/tbasic.cpp b/tests/glean/tbasic.cpp new file mode 100644 index 00000000..b4fab023 --- /dev/null +++ b/tests/glean/tbasic.cpp @@ -0,0 +1,68 @@ +// 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 +// 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 + +// tbasic.cpp: implementation of example class for basic tests + +#include "tbasic.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::runOne(BasicResult& r, Window&) { + r.pass = true; +} // BasicTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::logOne(BasicResult& r) { + logPassFail(r); + logConcise(r); +} // BasicTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicTest::compareOne(BasicResult& oldR, BasicResult& newR) { + comparePassFail(oldR, newR); +} // BasicTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +BasicTest basicTest("basic", "window", + "This trivial test simply verifies the internal support for basic\n" + "tests. It is run on every OpenGL-capable drawing surface\n" + "configuration that supports creation of a window.\n"); + +} // namespace GLEAN diff --git a/tests/glean/tbasic.h b/tests/glean/tbasic.h new file mode 100644 index 00000000..2d3af683 --- /dev/null +++ b/tests/glean/tbasic.h @@ -0,0 +1,69 @@ +// 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 +// 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 + + + +// tbasic.h: Example class for basic tests + +// This class illustrates the use of the BaseResult class and BaseTest +// template class for constructing straightforward portable tests. +// See the file tbase.h for a discussion of this process. + +// BasicTest simply runs on all drawing surface configurations that +// permit the creation of a window, and always passes. + + +#ifndef __tbasic_h__ +#define __tbasic_h__ + +#include "tbase.h" + +namespace GLEAN { + +class BasicResult: public BaseResult { +public: + bool pass; + + void putresults(ostream& s) const { + s << pass << '\n'; + } + + bool getresults(istream& s) { + s >> pass; + return s.good(); + } +}; + +class BasicTest: public BaseTest<BasicResult> { +public: + GLEAN_CLASS(BasicTest, BasicResult); +}; + +} // namespace GLEAN + +#endif // __tbasic_h__ diff --git a/tests/glean/tbasicperf.cpp b/tests/glean/tbasicperf.cpp new file mode 100644 index 00000000..94e91796 --- /dev/null +++ b/tests/glean/tbasicperf.cpp @@ -0,0 +1,189 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// tbasicperf.cpp: implementation of example class for basic +// performance tests + +// To customize this for benchmarking a particular function, +// create a new performance test object of type GLEAN::Perf, +// overriding the preop(), op(), and postop() methods as needed. +// (For OpenGL timing tests preop() and postop() will both call +// glFinish(), but other pre- and post-ops may be used for +// timing things other than OpenGL.) Then invoke the object's +// calibrate() and time() methods as shown in runOne(). + +#include "tbasicperf.h" +#include "timer.h" +#include <algorithm> + +namespace { +class MyPerf : public GLEAN::Timer { +public: + int msec; + + void preop() { glFinish(); } + void op() { +#ifdef __WIN__ + Sleep(msec); /* milliseconds */ +#else + usleep(msec*1000); /* microseconds */ +#endif + } + void postop() { glFinish(); } + + MyPerf() { msec = 100; } +}; + + +// Complex results helper functions + +void +diffHeader(bool& same, const string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::Environment* env) { + if (same) { + same = false; + env->log << name << ": DIFF " + << config->conciseDescription() << '\n'; + } +} // diffHeader + +} + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicPerfTest::runOne(BasicPerfResult& r, Window &w) { + MyPerf perf; + + perf.calibrate(); + vector<float> m; + for (int i = 0; i < 5; i++) { + env->quiesce(); + double t = perf.time(); + w.swap(); // So user can see something + m.push_back(t); + } + sort(m.begin(), m.end()); + r.timeAvg = (m[1] + m[2] + m[3]) / 3.0; + r.timeLow = m[1]; + r.timeHigh = m[3]; + r.pass = true; +} // BasicPerfTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicPerfTest::logOne(BasicPerfResult& r) { + logPassFail(r); + logConcise(r); + logStats(r); +} // BasicPerfTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BasicPerfTest::compareOne(BasicPerfResult& oldR, BasicPerfResult& newR) { + bool same = true; + const char *title = "100mS sleep"; + + if (newR.timeLow < oldR.timeLow) { + double percent = (100.0 + * (oldR.timeLow - newR.timeLow) + / newR.timeLow + 0.5); + if (percent >= 5.0) { + diffHeader(same, name, oldR.config, env); + env->log << '\t' + << env->options.db1Name + << " may be " + << percent + << "% faster on " + << title + << '\n'; + } + } + if (newR.timeHigh > oldR.timeHigh) { + double percent = (100.0 + * (newR.timeHigh - oldR.timeHigh) + / oldR.timeHigh + 0.5); + if (percent >= 5.0) { + diffHeader(same, name, oldR.config, env); + env->log << '\t' + << env->options.db2Name + << " may be " + << percent + << "% faster on " + << title + << '\n'; + } + } + + if (same && env->options.verbosity) { + env->log << name + << ": SAME " + << newR.config->conciseDescription() + << "\n\t" + << env->options.db2Name + << " test time falls within the" + << " valid measurement range of\n\t" + << env->options.db1Name + << " test time.\n"; + } + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // BasicPerfTest::compareOne + +void +BasicPerfTest::logStats(BasicPerfResult& r) { + env->log << "\tAverage = " + << r.timeAvg + << "\tRange = [" + << r.timeLow + << ", " + << r.timeHigh + << "]\n"; +} + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +BasicPerfTest basicPerfTest("basicPerf", "window", + "This trivial test simply verifies the internal support for basic\n" + "performance tests. It is run on every OpenGL-capable drawing surface\n" + "configuration that supports creation of a window. If everything is\n" + "working correctly, each result should be close to 0.1 second.\n"); + +} // namespace GLEAN diff --git a/tests/glean/tbasicperf.h b/tests/glean/tbasicperf.h new file mode 100644 index 00000000..f53a6416 --- /dev/null +++ b/tests/glean/tbasicperf.h @@ -0,0 +1,85 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + + + +// tbasicperf.h: Example class for basic performance tests + +// This class provides a framework for performance tests. Like most +// glean tests, it's derived from the BaseResult class and BaseTest +// template class; see tbase.h for further information. However, it +// is specialized to include member variables and functions that show +// how to perform timing operations, save results, and compare +// results. + +// To produce a customized benchmark, one can derive a new class from +// this one and override the runOne() function to perform the timing +// measurements. For more information, see tbasicperf.cpp. + + +#ifndef __tbasicperf_h__ +#define __tbasicperf_h__ + +#include "tbase.h" + +class DrawingSurfaceConfig; // Forward reference. + +namespace GLEAN { + +class BasicPerfResult: public BaseResult { +public: + bool pass; + double timeAvg, timeLow, timeHigh; + + BasicPerfResult() { + timeAvg = timeLow = timeHigh = 0.0; + } + + void putresults(ostream& s) const { + s << pass + << ' ' << timeAvg + << ' ' << timeLow + << ' ' << timeHigh + << '\n'; + } + + bool getresults(istream& s) { + s >> pass >> timeAvg >> timeLow >> timeHigh; + return s.good(); + } +}; + +class BasicPerfTest: public BaseTest<BasicPerfResult> { +public: + GLEAN_CLASS(BasicPerfTest, BasicPerfResult); + void logStats(BasicPerfResult& r); +}; // class BasicPerfTest + +} // namespace GLEAN + +#endif // __tbasicperf_h__ diff --git a/tests/glean/tbinding.cpp b/tests/glean/tbinding.cpp new file mode 100644 index 00000000..6aedd025 --- /dev/null +++ b/tests/glean/tbinding.cpp @@ -0,0 +1,199 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// tbinding.cpp: Test functions in the window-system binding + +#include "tbinding.h" +#include "image.h" +#include "rand.h" +#include <cmath> + +#if 0 +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include <cmath> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "stats.h" +#include "tbinding.h" +#include "misc.h" +#endif + +namespace { + +bool +makeCurrentOK(GLEAN::DrawingSurfaceConfig& config) { + using namespace GLEAN; + float expected[4]; + glClear(GL_COLOR_BUFFER_BIT); + glGetFloatv(GL_COLOR_CLEAR_VALUE, expected); + Image probe(1, 1, GL_RGBA, GL_FLOAT); + probe.read(drawingSize/2, drawingSize/2); + const float* actual = reinterpret_cast<float*>(probe.pixels()); + double maxError = ErrorBits(fabs(expected[0] - actual[0]), config.r); + maxError = max(maxError, + ErrorBits(fabs(expected[1] - actual[1]), config.g)); + maxError = max(maxError, + ErrorBits(fabs(expected[2] - actual[2]), config.b)); + return maxError <= 1.0; +} // makeCurrentOK + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +MakeCurrentTest::runOne(MakeCurrentResult& r, Window& w) { + + DrawingSurfaceConfig& config = *(r.config); + WindowSystem& ws = env->winSys; + + // The rendering contexts to be used: + vector<RenderingContext*> rcs; + // Short descriptions of the rendering contexts: + + RandomBitsDouble rRand(config.r, 712105); + RandomBitsDouble gRand(config.g, 63230); + RandomBitsDouble bRand(config.b, 912167); + + // Create rendering contexts to be used with the test window. + // Note that the first context (at index 0) is always the + // null context. + + rcs.push_back(0); + r.descriptions.push_back("Null context"); + ws.makeCurrent(); + r.testSequence.push_back(static_cast<int>(rcs.size()) - 1); + + rcs.push_back(new RenderingContext(env->winSys, config, 0, true)); + r.descriptions.push_back("Direct-rendering context"); + ws.makeCurrent(*rcs.back(), w); + r.testSequence.push_back(static_cast<int>(rcs.size()) - 1); + glDisable(GL_DITHER); + glClearColor(rRand.next(), gRand.next(), bRand.next(), 1.0); + if (!makeCurrentOK(config)) + goto failed; + + rcs.push_back(new RenderingContext(env->winSys, config, 0, false)); + r.descriptions.push_back("Indirect-rendering context"); + ws.makeCurrent(*rcs.back(), w); + r.testSequence.push_back(static_cast<int>(rcs.size()) - 1); + glDisable(GL_DITHER); + glClearColor(rRand.next(), gRand.next(), bRand.next(), 1.0); + if (!makeCurrentOK(config)) + goto failed; + + // Now run through all the pairs of rendering contexts, making + // them current in sequence and checking that rendering looks + // correct. Don't worry about the redundant sequences; we want + // to check those, too! + + int i; + for (i = 0; i < static_cast<int>(rcs.size()); ++i) + for (int j = 0; j < static_cast<int>(rcs.size()); ++j) { + r.testSequence.push_back(i); + if (rcs[i] == 0) + ws.makeCurrent(); + else { + ws.makeCurrent(*rcs[i], w); + if (!makeCurrentOK(config)) + goto failed; + } + r.testSequence.push_back(j); + if (rcs[j] == 0) + ws.makeCurrent(); + else { + ws.makeCurrent(*rcs[j], w); + if (!makeCurrentOK(config)) + goto failed; + } + } + r.pass = true; + goto cleanup; + +failed: + r.pass = false; +cleanup: + for (i = 0; i < static_cast<int>(rcs.size()); ++i) + if (rcs[i]) + delete rcs[i]; +} // MakeCurrentTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +MakeCurrentTest::logOne(MakeCurrentResult& r) { + logPassFail(r); + logConcise(r); + if (!r.pass) { + env->log << "\tSequence of MakeCurrent operations was:\n"; + for (int k = 0; k < static_cast<int>(r.testSequence.size()); ++k) + env->log << "\t\t" + << r.descriptions[r.testSequence[k]] + << '\n'; + } +} // MakeCurrentTestTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +MakeCurrentTest::compareOne(MakeCurrentResult& oldR, MakeCurrentResult& newR) { + comparePassFail(oldR, newR); +} // MakeCurrentTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +MakeCurrentTest makeCurrentTest("makeCurrent", "window, rgb", + + "This test sanity-checks the ability to use multiple rendering\n" + "contexts. It creates several contexts with differing\n" + "characteristics (e.g., some are direct-rendering and some\n" + "are indirect-rendering, if the window system binding supports\n" + "that distinction). Then it runs through all pairs of contexts,\n" + "making each one \"current\" in turn and verifying that simple\n" + "rendering succeeds.\n" + + ); + +} // namespace GLEAN diff --git a/tests/glean/tbinding.h b/tests/glean/tbinding.h new file mode 100644 index 00000000..835bf7a3 --- /dev/null +++ b/tests/glean/tbinding.h @@ -0,0 +1,73 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + + + + +// tbinding.h: Test functions in the window-system binding + +#ifndef __tbinding_h__ +#define __tbinding_h__ + +#include "tbasic.h" + +class DrawingSurfaceConfig; // Forward reference. +class GLEAN::Window; + +namespace GLEAN { + +#define drawingSize 64 + +class MakeCurrentResult: public BaseResult { +public: + bool pass; + // Short descriptions of the rendering contexts: + vector<const char*> descriptions; + // Complete record of rendering contexts made "current" during + // the test: + vector<int> testSequence; + + void putresults(ostream& s) const { + s << pass << '\n'; + } + + bool getresults(istream& s) { + s >> pass; + return s.good(); + } +}; + +class MakeCurrentTest: public BaseTest<MakeCurrentResult> { +public: + GLEAN_CLASS_WH(MakeCurrentTest, MakeCurrentResult, + drawingSize, drawingSize); +}; // class MakeCurrentTest + +} // namespace GLEAN + +#endif // __tbinding_h__ diff --git a/tests/glean/tblend.cpp b/tests/glean/tblend.cpp new file mode 100644 index 00000000..87d0934d --- /dev/null +++ b/tests/glean/tblend.cpp @@ -0,0 +1,621 @@ +// 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 + +// tblend.cpp: Test blending functions. + +#include "tblend.h" +#include "rand.h" +#include "image.h" +#include <cmath> + +namespace { + +struct factorNameMapping {GLenum factor; char* name;}; +factorNameMapping factorNames[] = { + {GL_DST_ALPHA, "GL_DST_ALPHA"}, + {GL_DST_COLOR, "GL_DST_COLOR"}, + {GL_ONE, "GL_ONE"}, + {GL_ONE_MINUS_DST_ALPHA, "GL_ONE_MINUS_DST_ALPHA"}, + {GL_ONE_MINUS_DST_COLOR, "GL_ONE_MINUS_DST_COLOR"}, + {GL_ONE_MINUS_SRC_ALPHA, "GL_ONE_MINUS_SRC_ALPHA"}, + {GL_ONE_MINUS_SRC_COLOR, "GL_ONE_MINUS_SRC_COLOR"}, + {GL_SRC_ALPHA, "GL_SRC_ALPHA"}, + {GL_SRC_ALPHA_SATURATE, "GL_SRC_ALPHA_SATURATE"}, + {GL_SRC_COLOR, "GL_SRC_COLOR"}, + {GL_ZERO, "GL_ZERO"} +}; + +char* +factorToName(GLenum factor) { + for (unsigned int i = 0; + i < sizeof(factorNames) / sizeof(factorNames[0]); + ++i) + if (factorNames[i].factor == factor) + return factorNames[i].name; + return 0; +} // factorToName + +GLenum +nameToFactor(string& name) { + for (unsigned int i = 0; + i < sizeof(factorNames) / sizeof(factorNames[0]); + ++i) + if (factorNames[i].name == name) + return factorNames[i].factor; + return GL_ZERO; +} // nameToFactor + +bool +needsDstAlpha(const GLenum func) { + return func == GL_DST_ALPHA || func == GL_ONE_MINUS_DST_ALPHA + || func == GL_SRC_ALPHA_SATURATE; +} + +void +makeRGBA(GLEAN::RandomBitsDouble& rRand, + GLEAN::RandomBitsDouble& gRand, + GLEAN::RandomBitsDouble& bRand, + GLEAN::RandomBitsDouble& aRand, + float* rgba) { + rgba[0] = rRand.next(); + rgba[1] = gRand.next(); + rgba[2] = bRand.next(); + rgba[3] = aRand.next(); +} // makeRGBA + +void +drawQuad(const int x, const int y, const float* color) { + glColor4fv(color); + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x + 1, y); + glVertex2i(x + 1, y + 1); + glVertex2i(x, y + 1); + glEnd(); +} // drawQuad + +inline float +clamp(float f) { + if (f < 0.0) + return 0.0; + else if (f > 1.0) + return 1.0; + else + 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; + } + 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; + break; + case GL_ONE: + df[0] = df[1] = df[2] = df[3] = 1.0; + break; + case GL_SRC_COLOR: + df[0] = src[0]; df[1] = src[1]; df[2] = src[2]; df[3] = src[3]; + 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]; + break; + case GL_SRC_ALPHA: + df[0] = df[1] = df[2] = df[3] = src[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + df[0] = df[1] = df[2] = df[3] = 1.0 - src[3]; + break; + case GL_DST_ALPHA: + df[0] = df[1] = df[2] = df[3] = dst[3]; + break; + case GL_ONE_MINUS_DST_ALPHA: + df[0] = df[1] = df[2] = df[3] = 1.0 - dst[3]; + break; + default: + df[0] = df[1] = df[2] = df[3] = 0.0; + break; + } + + 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; + + runFactorsResult result; + int y; + + glDisable(GL_DITHER); + glClear(GL_COLOR_BUFFER_BIT); + + Image dst(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + RandomBitsDouble rRand(config.r, 6021023); + RandomBitsDouble gRand(config.g, 1137); + RandomBitsDouble bRand(config.b, 1138); + RandomBitsDouble dstARand(config.a? config.a: 1, 6); + + // Fill the framebuffer with random RGBA values, and place a copy + // in ``dst'': + glDisable(GL_BLEND); + char* dRow = dst.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* pix = reinterpret_cast<float*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + float rgba[4]; + makeRGBA(rRand, gRand, bRand, dstARand, rgba); + if (!config.a) + rgba[3] = 1.0; + drawQuad(x + 1, y + 1, rgba); + pix[0] = rgba[0]; + pix[1] = rgba[1]; + pix[2] = rgba[2]; + pix[3] = rgba[3]; + pix += 4; + } + dRow += dst.rowSizeInBytes(); + } + + // Read back the contents of the framebuffer, and measure any + // difference from what was actually written. We can't tell + // whether errors occurred when writing or when reading back, + // but at least we can report anything unusual. + Image fbDst(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + fbDst.read(1, 1); + Image::Registration reg1(fbDst.reg(dst)); + result.readbackErrorBits = + max(ErrorBits(reg1.stats[0].max(), config.r), + max(ErrorBits(reg1.stats[1].max(), config.g), + max(ErrorBits(reg1.stats[2].max(), config.b), + ErrorBits(reg1.stats[3].max(), config.a)))); + + // Now generate random source pixels and apply the blending + // operation to both the framebuffer and a copy in the image + // ``expected''. Note that a fresh source alpha must be + // generated here, because the range of source alpha values is + // not limited by the range of alpha values that can be + // represented in the framebuffer. Save the source pixels in + // the image ``src'' so we can diagnose any problems we find + // later. + Image expected(dst); + Image src(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + RandomBitsDouble srcARand(16, 42); + + glBlendFunc(srcFactor, dstFactor); + glEnable(GL_BLEND); + + dRow = expected.pixels(); + char* sRow = src.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* pix = reinterpret_cast<float*>(dRow); + float* sPix = reinterpret_cast<float*>(sRow); + for (int x = 0; x < drawingSize; ++x) { + float rgba[4]; + makeRGBA(rRand, gRand, bRand, srcARand, rgba); + sPix[0] = rgba[0]; + sPix[1] = rgba[1]; + sPix[2] = rgba[2]; + sPix[3] = rgba[3]; + drawQuad(x + 1, y + 1, rgba); + applyBlend(srcFactor, dstFactor, pix, rgba); + pix += 4; + sPix += 4; + } + dRow += expected.rowSizeInBytes(); + sRow += src.rowSizeInBytes(); + } + + // Read the generated image (``actual'') and compare it to the + // computed image (``expected'') to see if any pixels are + // outside the expected tolerance range (one LSB). If so, + // report the first such pixel, along with the source and + // destination values that generated it. Keep track of the + // maximum error encountered. + Image actual(drawingSize, drawingSize, GL_RGBA, GL_FLOAT); + actual.read(1, 1); + result.blendRGBErrorBits = 0.0; + result.blendAlphaErrorBits = 0.0; + sRow = actual.pixels(); + dRow = expected.pixels(); + for (/*int */y = 0; y < drawingSize; ++y) { + float* aPix = reinterpret_cast<float*>(sRow); + float* ePix = reinterpret_cast<float*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + float rError = fabs(aPix[0] - ePix[0]); + 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), + 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) { + if (env.options.verbosity) { +float* sPix = reinterpret_cast<float*>(src.pixels() + + y * src.rowSizeInBytes() + x * 4 * sizeof(float)); +float* dPix = reinterpret_cast<float*>(dst.pixels() + + y * dst.rowSizeInBytes() + x * 4 * sizeof(float)); +env.log << '\n' +<< "First failing pixel is at row " << y << " column " << x << "\n" +<< "Actual values are (" << aPix[0] << ", " << aPix[1] << ", " << aPix[2] + << ", " << aPix[3] << ")\n" +<< "Expected values are (" << ePix[0] << ", " << ePix[1] << ", " << ePix[2] + << ", " << ePix[3] << ")\n" +<< "Errors are (" << rError << ", " << gError << ", " << bError << ", " + << aError << ")\n" +<< "Source values are (" << sPix[0] << ", " << sPix[1] << ", " << sPix[2] + << ", " << sPix[3] << ")\n" +<< "Destination values are (" << dPix[0] << ", " << dPix[1] << ", " << dPix[2] + << ", " << dPix[3] << ")\n"; + } + return result; + } + aPix += 4; + ePix += 4; + } + sRow += actual.rowSizeInBytes(); + dRow += expected.rowSizeInBytes(); + } + + return result; +} // runOneSet + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::runOne(BlendFuncResult& r, Window& w) { + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + + static GLenum srcFactors[] = { + GL_ZERO, + GL_ONE, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_SRC_ALPHA_SATURATE + }; + static GLenum dstFactors[] = { + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA + }; + + // 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; + + s = getenv("GLEAN_BLEND_RGB_TOLERANCE"); + if (s) { + rgbTolerance = atof(s); + env->log << "Note: RGB tolerance adjusted to " << rgbTolerance << "\n"; + } + s = getenv("GLEAN_BLEND_ALPHA_TOLERANCE"); + if (s) { + alphaTolerance = atof(s); + env->log << "Note: Alpha tolerance adjusted to " << alphaTolerance << "\n"; + } + + 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; + } + } + + r.pass = allPassed; +} // BlendFuncTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::logOne(BlendFuncResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +BlendFuncTest::compareOne(BlendFuncResult& oldR, BlendFuncResult& newR) { + BasicStats readbackStats; + BasicStats blendStats; + + vector<BlendFuncResult::PartialResult>::const_iterator np; + vector<BlendFuncResult::PartialResult>::const_iterator op; + + 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) { + readbackStats.sample(np->rbErr - op->rbErr); + blendStats.sample(np->blRGBErr - op->blRGBErr); + blendStats.sample(np->blAErr - op->blAErr); + } + + if (readbackStats.n() == static_cast<int>(newR.results.size()) + && 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'; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + + if (readbackStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate readback.\n"; + else if (readbackStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate readback.\n"; + if (blendStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate blending.\n"; + else if (blendStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate blending.\n"; + if (readbackStats.n() != static_cast<int>(newR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db2Name + << " have no matching test in " + << env->options.db1Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np) { + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->src == op->src + && np->dst == op->dst) + break; + if (op == oldR.results.end()) + env->log << "\t\t" + << factorToName(np->src) + << ' ' + << factorToName(np->dst) + << '\n'; + } + } + if (readbackStats.n() != static_cast<int>(oldR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db1Name + << " have no matching test in " + << env->options.db2Name + << ":\n"; + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) { + for (np = newR.results.begin(); + np != newR.results.end(); ++np) + if (op->src == np->src + && op->dst == np->dst) + break; + if (np == newR.results.end()) + env->log << "\t\t" + << factorToName(op->src) + << ' ' + << factorToName(op->dst) + << '\n'; + } + } + if (env->options.verbosity) { + env->log << "\tThe following cases appear in both " + << env->options.db1Name + << " and " + << env->options.db2Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np){ + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->src == op->src + && np->dst == op->dst) + break; + if (op != oldR.results.end()) + env->log << "\t\t" + << factorToName(np->src) + << ' ' + << factorToName(np->dst) + << '\n'; + } + } + } +} // BlendFuncTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +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'; +} // BlendFuncResult::put + +bool +BlendFuncResult::getresults(istream& s) { + int n; + 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); + results.push_back(p); + } + + return s.good(); +} // BlendFuncResult::get + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +BlendFuncTest blendFuncTest("blendFunc", "window, rgb", + + "This test checks all combinations of source and destination\n" + "blend factors for the GL_FUNC_ADD blend equation. It operates\n" + "on all RGB or RGBA drawing surface configurations that support\n" + "the creation of windows.\n" + "\n" + "Note that a common cause of failures for this test is small errors\n" + "introduced when an implementation scales color values incorrectly;\n" + "for example, converting an 8-bit color value to float by\n" + "dividing by 256 rather than 255, or computing a blending result\n" + "by shifting a double-width intermediate value rather than scaling\n" + "it. Also, please note that the OpenGL spec requires that when\n" + "converting from floating-point colors to integer form, the result\n" + "must be rounded to the nearest integer, not truncated.\n" + "[1.2.1, 2.13.9]\n" + "\n" + "The test reports two error measurements. The first (readback) is\n" + "the error detected when reading back raw values that were written\n" + "to the framebuffer. The error in this case should be very close\n" + "to zero, since the values are carefully constructed so that they\n" + "can be represented accurately in the framebuffer. The second\n" + "(blending) is the error detected in the result of the blending\n" + "computation. For the test to pass, these errors must both be\n" + "no greater than one least-significant bit in the framebuffer\n" + "representation of a color.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tblend.h b/tests/glean/tblend.h new file mode 100644 index 00000000..5a83902c --- /dev/null +++ b/tests/glean/tblend.h @@ -0,0 +1,69 @@ +// 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 + +// tblend.h: Test blending functions. + +#ifndef __tblend_h__ +#define __tblend_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 64 // 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. +#define windowSize (drawingSize + 2) + +class BlendFuncResult: public BaseResult { +public: + bool pass; // not written to log file + + struct PartialResult { + GLenum src; // Source blend factor. + GLenum dst; // Destination blend factor. + float rbErr; // Max readback error, in bits. + float blRGBErr; // Max RGB blend error, in bits. + float blAErr; // Max Alpha blend error, in bits. + }; + vector<PartialResult> results; + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + +class BlendFuncTest: public BaseTest<BlendFuncResult> { +public: + GLEAN_CLASS_WH(BlendFuncTest, BlendFuncResult, + windowSize, windowSize); +}; // class BlendFuncTest + +} // namespace GLEAN + +#endif // __tblend_h__ diff --git a/tests/glean/tchgperf.cpp b/tests/glean/tchgperf.cpp new file mode 100644 index 00000000..b943f53d --- /dev/null +++ b/tests/glean/tchgperf.cpp @@ -0,0 +1,317 @@ +// 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 + +// tchgperf.cpp: Some basic tests of attribute-change performance. + +#include "tchgperf.h" +#include <algorithm> +#include "rand.h" +#include "image.h" +#include "timer.h" +#include "geomutil.h" + +#if 0 +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "timer.h" +#include "tchgperf.h" +#include "misc.h" +#endif + +namespace { + +GLEAN::Image redImage(64, 64, GL_RGB, GL_UNSIGNED_BYTE, 1.0, 0.0, 0.0, 0.0); +GLuint redTex; +GLEAN::Image greenImage(64, 64, GL_RGB, GL_UNSIGNED_BYTE, 0.0, 1.0, 0.0, 0.0); +GLuint greenTex; + +int nPoints; +float* vertices; +float* texCoords; + +void +noBindDraw() { + int rowSize = 2 * nPoints; + for (int y = 0; y < nPoints - 1; ++y) { + float* t0 = texCoords + y * rowSize; + float* v0 = vertices + y * rowSize; + for (int x = 0; x < nPoints - 1; ++x) { + float* t1 = t0 + rowSize; + float* t2 = t1 + 2; + float* t3 = t0 + 2; + float* v1 = v0 + rowSize; + float* v2 = v1 + 2; + float* v3 = v0 + 2; + + glBegin(GL_TRIANGLES); + glTexCoord2fv(t0); + glVertex2fv(v0); + glTexCoord2fv(t1); + glVertex2fv(v1); + glTexCoord2fv(t2); + glVertex2fv(v2); + glEnd(); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t2); + glVertex2fv(v2); + glTexCoord2fv(t3); + glVertex2fv(v3); + glTexCoord2fv(t0); + glVertex2fv(v0); + glEnd(); + + t0 += 2; + v0 += 2; + } + } +} // noBindDraw + +void +bindDraw() { + int rowSize = 2 * nPoints; + for (int y = 0; y < nPoints - 1; ++y) { + float* v0 = vertices + y * rowSize; + float* t0 = texCoords + y * rowSize; + for (int x = 0; x < nPoints - 1; ++x) { + float* t1 = t0 + rowSize; + float* t2 = t1 + 2; + float* t3 = t0 + 2; + float* v1 = v0 + rowSize; + float* v2 = v1 + 2; + float* v3 = v0 + 2; + + glBindTexture(GL_TEXTURE_2D, redTex); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t0); + glVertex2fv(v0); + glTexCoord2fv(t1); + glVertex2fv(v1); + glTexCoord2fv(t2); + glVertex2fv(v2); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, greenTex); + glBegin(GL_TRIANGLES); + glTexCoord2fv(t2); + glVertex2fv(v2); + glTexCoord2fv(t3); + glVertex2fv(v3); + glTexCoord2fv(t0); + glVertex2fv(v0); + glEnd(); + + t0 += 2; + v0 += 2; + } + } +} // BindDraw + +class BindDrawTimer: public GLEAN::Timer { + virtual void op() { bindDraw(); } + virtual void preop() { glFinish(); } + virtual void postop() { glFinish(); } +}; + +class NoBindDrawTimer: public GLEAN::Timer { + virtual void op() { noBindDraw(); } + virtual void preop() { glFinish(); } + virtual void postop() { glFinish(); } +}; + +void +logStats(GLEAN::TexBindPerfResult& r, GLEAN::Environment* env) { + env->log << "\tApproximate texture binding time = " << r.bindTime + << " microseconds.\n\tRange of valid measurements = [" + << r.lowerBound << ", " << r.upperBound << "]\n"; +} // logStats + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +TexBindPerf::runOne(TexBindPerfResult& r, Window& w) { + glGenTextures(1, &redTex); + glBindTexture(GL_TEXTURE_2D, redTex); + 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_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + redImage.makeMipmaps(GL_RGB); + + glGenTextures(1, &greenTex); + glBindTexture(GL_TEXTURE_2D, greenTex); + 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_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + greenImage.makeMipmaps(GL_RGB); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_TEXTURE_2D); + glColor4f(1.0, 1.0, 1.0, 1.0); + + nPoints = drawingSize / 2; // Yields 1-pixel triangles. + + RandomDouble vRand(142857); + RandomMesh2D v(1.0, drawingSize, nPoints, 1.0, drawingSize, nPoints, + vRand); + vertices = v(0, 0); + + RandomDouble tRand(314159); + RandomMesh2D t(0.0, 1.0, nPoints, 0.0, 1.0, nPoints, tRand); + texCoords = t(0, 0); + + int nTris = (nPoints - 1) * (nPoints - 1) / 2; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + BindDrawTimer bindDrawTimer; + NoBindDrawTimer noBindDrawTimer; + + bindDrawTimer.calibrate(); + noBindDrawTimer.calibrate(); + + vector<float> measurements; + for (int i = 0; i < 5; ++i) { + env->quiesce(); + double tBind = bindDrawTimer.time(); + w.swap(); // So the user can see something happening. + + env->quiesce(); + double tNoBind = noBindDrawTimer.time(); + w.swap(); + + double bindTime = 1E6 * (tBind - tNoBind) / nTris; + if (bindTime < 0.0) { + // This can happen if the system isn't quiescent; + // some process sneaks in and takes wall-clock time + // when ``noBindDraw'' is running. Just flush it + // and try again. (Note: You really shouldn't be + // running timing tests on a system where other + // processes are active!) + --i; + continue; + } + + measurements.push_back(bindTime); + } + + sort(measurements.begin(), measurements.end()); + r.bindTime = (measurements[1]+measurements[2]+measurements[3]) / 3.0; + r.lowerBound = measurements[1]; + r.upperBound = measurements[3]; + r.pass = true; +} // TexBindPerf::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::logOne(TexBindPerfResult& r) { + logPassFail(r); + logConcise(r); + logStats(r, env); +} // TexBindPerf::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TexBindPerf::compareOne(TexBindPerfResult& oldR, TexBindPerfResult& newR) { + if (newR.bindTime < oldR.lowerBound) { + int percent = static_cast<int>( + 100.0 * (oldR.bindTime - newR.bindTime) / newR.bindTime + + 0.5); + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n' + << '\t' << env->options.db2Name << " may be " + << percent << "% faster.\n"; + } else if (newR.bindTime > oldR.upperBound) { + int percent = static_cast<int>( + 100.0 * (newR.bindTime - oldR.bindTime) / oldR.bindTime + + 0.5); + env->log << name << ": DIFF " + << oldR.config->conciseDescription() << '\n' + << '\t' << env->options.db1Name << " may be " + << percent << "% faster.\n"; + } else { + if (env->options.verbosity) + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n\t" + << env->options.db2Name + << " test time falls within the " + << "valid measurement range of " + << env->options.db1Name + << " test time.\n"; + } + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR, env); + env->log << env->options.db2Name << ':'; + logStats(newR, env); + } +} // TexBindPerf::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexBindPerf texBindPerfTest("texBindPerf", "window, rgb, z", + + "This test makes a rough estimate of the cost of a glBindTexture()\n" + "operation, expressed in microseconds.\n" + "\n" + "Since the apparent cost of a texture bind is dependent on many\n" + "factors (including the fraction of the texture map that's actually\n" + "used for drawing, on machines that cache textures; texture map\n" + "size; texel format; etc.), a general-purpose test can only estimate\n" + "it. In this test we do so by drawing random triangles of very\n" + "small size, and reporting simple statistics concerning the cost.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tchgperf.h b/tests/glean/tchgperf.h new file mode 100644 index 00000000..11f3bd7e --- /dev/null +++ b/tests/glean/tchgperf.h @@ -0,0 +1,72 @@ +// 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 + + +// tchgperf.h: Some basic tests of attribute-change performance. + +#ifndef __tchgperf_h__ +#define __tchgperf_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 128 // must be power-of-2, 128 or greater + +class TexBindPerfResult: public BaseResult { +public: + bool pass; + double bindTime; + double lowerBound; + double upperBound; + + TexBindPerfResult() { bindTime = lowerBound = upperBound = 0.0; } + + void putresults(ostream& s) const { + s << bindTime + << ' ' << lowerBound + << ' ' << upperBound + << '\n'; + } + + bool getresults(istream& s) { + s >> bindTime >> lowerBound >> upperBound; + return s.good(); + } + +}; + +class TexBindPerf: public BaseTest<TexBindPerfResult> { +public: + GLEAN_CLASS_WH(TexBindPerf, TexBindPerfResult, + drawingSize, drawingSize); +}; // class TexBindPerf + +} // namespace GLEAN + +#endif // __tchgperf_h__ diff --git a/tests/glean/tdepthstencil.cpp b/tests/glean/tdepthstencil.cpp new file mode 100644 index 00000000..f691d2cf --- /dev/null +++ b/tests/glean/tdepthstencil.cpp @@ -0,0 +1,422 @@ +// 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 + +// tdepthstencil.h: Test GL_EXT_packed_depth_stencil extension. +// Brian Paul 1 October 2005 + + +#include "tdepthstencil.h" +#include "rand.h" +#include "timer.h" +#include "image.h" +#include <cassert> +#include <cmath> + +#ifdef GL_EXT_packed_depth_stencil + +namespace GLEAN { + +static PFNGLWINDOWPOS2IARBPROC WindowPos2i = NULL; + + +bool +DepthStencilTest::checkError(const char *where) +{ + GLenum err = glGetError(); + if (err) { + errorCode = err; + errorPos = where; + return true; + } + return false; +} + +void +DepthStencilTest::setup(void) +{ + glGetIntegerv(GL_DEPTH_BITS, &depthBits); + glGetIntegerv(GL_STENCIL_BITS, &stencilBits); + + WindowPos2i = (PFNGLWINDOWPOS2IARBPROC) + GLUtils::getProcAddress("glWindowPos2iARB"); + assert(WindowPos2i); +} + + +// If we're lacking a depth and/or stencil buffer we'll just run this test. +// Return true if pass, false if fail. +bool +DepthStencilTest::testInsufficientVisual(void) +{ + GLuint p[1]; + + glDrawPixels(1, 1, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, p); + if (glGetError() != GL_INVALID_OPERATION) { + sprintf(errorMsg, + "glDrawPixels failed to raise GL_INVALID_OPERATION" + " when there's no depth or stencil buffer."); + return false; + } + + glCopyPixels(0, 0, 5, 5, GL_DEPTH_STENCIL_EXT); + if (glGetError() != GL_INVALID_OPERATION) { + sprintf(errorMsg, + "glCopyPixels failed to raise GL_INVALID_OPERATION" + " when there's no depth or stencil buffer."); + return false; + } + + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, + 0, 0, 1, 1, 0); + if (glGetError() != GL_INVALID_OPERATION) { + sprintf(errorMsg, + "glCopyTexImage2D failed to raise GL_INVALID_OPERATION" + " when there's no depth or stencil buffer."); + return false; + } + + return true; +} + + +// Each of these OpenGL calls in this function should generate an error! +// Note to GL implementors: if you find any errors here, you better check +// your glTexImage functions too! +bool +DepthStencilTest::testErrorDetection(void) +{ + GLuint p[1]; + + glDrawPixels(1, 1, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT, p); + if (glGetError() != GL_INVALID_ENUM) { + sprintf(errorMsg, + "glDrawPixels(GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT)" + " failed to generate GL_INVALID_ENUM."); + return false; + } + + glDrawPixels(1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8_EXT, p); + if (glGetError() != GL_INVALID_OPERATION) { + sprintf(errorMsg, + "glDrawPixels(GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8_EXT)" + " failed to generate GL_INVALID_OPERATION."); + return false; + } + + glReadPixels(0, 0, 1, 1, GL_DEPTH_STENCIL_EXT, GL_FLOAT, p); + if (glGetError() != GL_INVALID_ENUM) { + sprintf(errorMsg, + "glReadPixels(GL_DEPTH_STENCIL_EXT, GL_FLOAT)" + " failed to generate GL_INVALID_ENUM."); + return false; + } + + glReadPixels(0, 0, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT_24_8_EXT, p); + if (glGetError() != GL_INVALID_OPERATION) { + sprintf(errorMsg, + "glReadPixels(GL_STENCIL_INDEX, GL_UNSIGNED_INT_24_8_EXT)" + " failed to generate GL_INVALID_OPERATION."); + return false; + } + + return true; +} + + +bool +DepthStencilTest::testDrawAndRead(void) +{ + // the reference image + static const GLuint image[4] = { + 0x00000000, + 0x000000ff, + 0xffffff00, + 0xffffffff + }; + GLuint readback[4]; + + WindowPos2i(0, 0); + glDrawPixels(2, 2, GL_DEPTH_STENCIL_EXT, + GL_UNSIGNED_INT_24_8_EXT, image); + if (checkError("glDrawPixels in testDrawAndRead")) + return false; + + glReadPixels(0, 0, 2, 2, GL_DEPTH_STENCIL_EXT, + GL_UNSIGNED_INT_24_8_EXT, readback); + if (checkError("glReadPixels in testDrawAndRead")) + return false; + + for (int i = 0; i < 4; i++) { + if (image[i] != readback[i]) { + sprintf(errorMsg, + "Image returned by glReadPixels didn't match" + " the expected result (0x%x != 0x%x)", + readback[i], image[i]); + return false; + } + } + + // test depth scale/bias and stencil mapping (in a trivial way) + glPixelTransferf(GL_DEPTH_SCALE, 0.0); // map all depths to 1.0 + glPixelTransferf(GL_DEPTH_BIAS, 1.0); + GLuint stencilMap[2] = { 2, 2 }; // map all stencil values to 2 + glPixelMapuiv(GL_PIXEL_MAP_S_TO_S, 2, stencilMap); + glPixelTransferi(GL_MAP_STENCIL, 1); + glReadPixels(0, 0, 2, 2, GL_DEPTH_STENCIL_EXT, + GL_UNSIGNED_INT_24_8_EXT, readback); + if (checkError("glReadPixels in testDrawAndRead")) + return false; + for (int i = 0; i < 4; i++) { + if (readback[i] != 0xffffff02) { + sprintf(errorMsg, + "Image returned by glReadPixels didn't match" + " the expected result (0x%x != 0xffffff02)", + readback[i]); + return false; + } + } + glPixelTransferf(GL_DEPTH_SCALE, 1.0); + glPixelTransferf(GL_DEPTH_BIAS, 0.0); + glPixelTransferi(GL_MAP_STENCIL, 0); + + return true; +} + + +bool +DepthStencilTest::testTextureOperations(void) +{ + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT, + 0, 0, 1, 1, 0); + if (checkError("glCopyTexImage2D in testTextureOperations.")) + return false; + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); + if (checkError("glCopyTexSubImage2D in testTextureOperations.")) + return false; + + return true; +} + + +double +DepthStencilTest::readPixelsRate(GLenum format, GLenum type) +{ + const int width = drawingSize, height = drawingSize; + GLuint *img = new GLuint [width * height]; + + WindowPos2i(0, 0); + glDrawPixels(width, height, GL_DEPTH_STENCIL_EXT, + GL_UNSIGNED_INT_24_8_EXT, img); + + const double minInterval = 2.0; // two seconds + Timer tTimer; + double start = tTimer.getClock(); + double elapsedTime = 0.0; + int iterations = 0; + do { + for (int i = 0; i < 50; i++) { + glReadPixels(0, 0, width, height, format, type, img); + iterations++; + } + + double finish = tTimer.getClock(); + elapsedTime = finish - start; + } while (elapsedTime < minInterval); + + delete [] img; + + double rate = width * height * iterations / elapsedTime; + return rate; // pixels/second +} + + +void +DepthStencilTest::testPerformance(DepthStencilResult &r) +{ + r.readDepthStencilRate = readPixelsRate(GL_DEPTH_STENCIL_EXT, + GL_UNSIGNED_INT_24_8_EXT); + r.readDepthUintRate = readPixelsRate(GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT); + r.readDepthUshortRate = readPixelsRate(GL_DEPTH_COMPONENT, + GL_UNSIGNED_SHORT); + + // XXX maybe also test glCopyTexImage, etc. +} + + +void +DepthStencilTest::runOne(DepthStencilResult &r, Window &w) +{ + (void) w; // silence warning + r.pass = true; + errorCode = 0; + errorPos = NULL; + errorMsg[0] = 0; + + setup(); + + if (depthBits == 0 || stencilBits == 0) { + r.pass = testInsufficientVisual(); + return; + } + + if (r.pass) + r.pass = testErrorDetection(); + if (r.pass) + r.pass = testDrawAndRead(); + if (r.pass) + r.pass = testTextureOperations(); + if (r.pass) + testPerformance(r); +} + + +void +DepthStencilTest::logOne(DepthStencilResult &r) +{ + if (r.pass) { + logPassFail(r); + logConcise(r); + + char str[1000]; + double mbps; + + env->log << "\tglReadPixels GL_DEPTH_STENCIL rate: "; + mbps = r.readDepthStencilRate * sizeof(GLuint) / (1024*1024); + sprintf(str, "%.2f", mbps); + env->log << str << " MBytes per second.\n"; + + env->log << "\tglReadPixels GL_DEPTH/GLuint rate: "; + mbps = r.readDepthUintRate * sizeof(GLuint) / (1024*1024); + sprintf(str, "%.2f", mbps); + env->log << str << " MBytes per second.\n"; + + env->log << "\tglReadPixels GL_DEPTH/GLushort rate: "; + mbps = r.readDepthUshortRate * sizeof(GLshort) / (1024*1024); + sprintf(str, "%.2f", mbps); + env->log << str << " MBytes per second.\n"; + } + else { + env->log << name << "FAIL\n"; + if (errorCode) { + env->log << "\tOpenGL Error " << gluErrorString(errorCode) + << " at " << errorPos << "\n"; + } + else if (errorMsg[0]) { + env->log << "\t" << errorMsg << "\n"; + } + } +} + + +void +DepthStencilTest::compareOne(DepthStencilResult &oldR, + DepthStencilResult &newR) +{ + comparePassFail(oldR, newR); + + if (newR.pass && oldR.pass == newR.pass) { + if (env->options.verbosity) { + env->log << "\tReadPixels rate:\n"; + env->log << "\t\tGL_DEPTH_STENCIL:\n"; + env->log << "\t\t\told: " << oldR.readDepthStencilRate; + env->log << "\t\t\tnew: " << newR.readDepthStencilRate; + env->log << "\t\tGL_DEPTH/GL_UNSIGNED_INT:\n"; + env->log << "\t\t\told: " << oldR.readDepthUintRate; + env->log << "\t\t\tnew: " << newR.readDepthUintRate; + env->log << "\t\tGL_DEPTH/GL_UNSIGNED_SHORT:\n"; + env->log << "\t\t\told: " << oldR.readDepthUshortRate; + env->log << "\t\t\tnew: " << newR.readDepthUshortRate; + } + } + else { + env->log << "\tNew: "; + env->log << (newR.pass ? "PASS" : "FAIL"); + env->log << "\tOld: "; + env->log << (oldR.pass ? "PASS" : "FAIL"); + } +} + + +void +DepthStencilResult::putresults(ostream &s) const +{ + if (pass) { + s << "PASS\n"; + + char str[1000]; + double mbps; + + mbps = readDepthStencilRate * sizeof(GLuint) / (1024*1024); + sprintf(str, "%.2f", mbps); + s << str << "\n"; + + mbps = readDepthUintRate * sizeof(GLuint) / (1024*1024); + sprintf(str, "%.2f", mbps); + s << str << "\n"; + + mbps = readDepthUshortRate * sizeof(GLushort) / (1024*1024); + sprintf(str, "%.2f", mbps); + s << str << "\n"; + } + else { + s << "FAIL\n"; + } +} + + +bool +DepthStencilResult::getresults(istream &s) +{ + char result[1000]; + s >> result; + + if (strcmp(result, "FAIL") == 0) { + pass = false; + } + else { + pass = true; + s >> readDepthStencilRate; + s >> readDepthUintRate; + s >> readDepthUshortRate; + } + return s.good(); +} + + +// The test object itself: +DepthStencilTest depthstencilTest("depthStencil", "window, rgb", + "GL_EXT_packed_depth_stencil GL_ARB_window_pos", + "Test the GL_EXT_packed_depth_stencil extension.\n"); + + + +} // namespace GLEAN + +#endif // GL_EXT_packed_depth_stencil diff --git a/tests/glean/tdepthstencil.h b/tests/glean/tdepthstencil.h new file mode 100644 index 00000000..9c915ecc --- /dev/null +++ b/tests/glean/tdepthstencil.h @@ -0,0 +1,80 @@ +// 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 + +// tdepthstencil.h: Test GL_EXT_packed_depth_stencil extension. +// Brian Paul 1 October 2005 + +#ifndef __tdepthstencil_h__ +#define __tdepthstencil_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 1000 +#define windowSize (drawingSize + 2) + +class DepthStencilResult: public BaseResult +{ +public: + bool pass; + double readDepthStencilRate; // pixels/second + double readDepthUintRate; // pixels/second + double readDepthUshortRate; // pixels/second + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + + +class DepthStencilTest: public BaseTest<DepthStencilResult> +{ +public: + GLEAN_CLASS_WH(DepthStencilTest, DepthStencilResult, + windowSize, windowSize); + +private: + int depthBits, stencilBits; + GLenum errorCode; + const char *errorPos; + char errorMsg[1000]; + + bool checkError(const char *where); + void setup(void); + bool testInsufficientVisual(void); + bool testErrorDetection(void); + bool testDrawAndRead(void); + bool testTextureOperations(void); + void testPerformance(DepthStencilResult &r); + double readPixelsRate(GLenum format, GLenum type); +}; + +} // namespace GLEAN + +#endif // __tdepthstencil_h__ + diff --git a/tests/glean/test.cpp b/tests/glean/test.cpp new file mode 100644 index 00000000..02bd16c5 --- /dev/null +++ b/tests/glean/test.cpp @@ -0,0 +1,134 @@ +// 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 + + + + +// test.cpp: implementation of base class for tests +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "test.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Class variables for automatic construction of list of all tests +/////////////////////////////////////////////////////////////////////////////// +Test* Test::testList; // Guaranteed initialized to zero at startup, + // before any constructors are invoked. + // (See discussion in section 10.4.9, + // page 252, of ``C++ Programming Language'' + // (third edition).) + +int Test::testCount; // Also initialized to zero. + +/////////////////////////////////////////////////////////////////////////////// +// Constructor/Destructor: +/////////////////////////////////////////////////////////////////////////////// +Test::Test(const char* testName, const char *descrip): + name(testName), description(descrip) { + prereqs = 0; + hasRun = false; + nextTest = testList; + testList = this; + ++testCount; +} // Test::Test() + +Test::Test(const char* testName, const char *descrip, Test** thePrereqs): + name(testName), description(descrip) { + prereqs = thePrereqs; + hasRun = false; + nextTest = testList; + testList = this; + ++testCount; +} // Test::Test() + +Test::~Test() { +} // Test::~Test + +/////////////////////////////////////////////////////////////////////////////// +// Stream opening utilities for results databases +/////////////////////////////////////////////////////////////////////////////// + +Test::OutputStream::OutputStream(Test& t) { + s = new ofstream(t.env->resultFileName(t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db1Name); +} // Test::OutputStream::OutputStream + +Test::OutputStream::~OutputStream() { + s->close(); + delete s; +} // Test::OutputStream::~OutputStream + +Test::OutputStream::operator ofstream& () { + return *s; +} // Test::OutputStream::operator ::ofstream& + +Test::Input1Stream::Input1Stream(Test& t) { + s = new ifstream(t.env->resultFileName( + t.env->options.db1Name, t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db1Name); +} // Test::Input1Stream::Input1Stream + +Test::Input1Stream::~Input1Stream() { + s->close(); + delete s; +} // Test::Input1Stream::~Input1Stream + +Test::Input1Stream::operator ifstream& () { + return *s; +} // Test::Input1Stream::operator ::ifstream& + +Test::Input2Stream::Input2Stream(Test& t) { + s = new ifstream(t.env->resultFileName( + t.env->options.db2Name, t.name).c_str()); + if (!*s) + throw Test::CantOpenResultsFile(t.name, t.env->options.db2Name); +} // Test::Input2Stream::Input2Stream + +Test::Input2Stream::~Input2Stream() { + s->close(); + delete s; +} // Test::Input2Stream::~Input2Stream + +Test::Input2Stream::operator ifstream& () { + return *s; +} // Test::Input2Stream::operator ::ifstream& + +} // namespace GLEAN diff --git a/tests/glean/test.h b/tests/glean/test.h new file mode 100644 index 00000000..1d6e78c4 --- /dev/null +++ b/tests/glean/test.h @@ -0,0 +1,152 @@ +// 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 + + + + +// test.h: Base class for all tests + +// This class encapsulates base functionality used by all tests. Some +// of this is fairly trivial (the test name, for example). One of the +// most important nontrivial functions is the use of the constructor +// to build a linked list of test objects; this eliminates the need to +// maintain a separate table of tests. This class also provides a +// flag for determining if a test has been run, which allows tests to +// invoke one another and make use of previous results without forcing +// tests to run multiple times. Finally, it provides a basic +// framework for recording a vector of results (which typically will +// 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. +// Most people will find it more convenient to use the BaseTest +// template class. See tbase.h for more information. + + + +#ifndef __test_h__ +#define __test_h__ + +using namespace std; + +#include <string> +#include <vector> +#include <fstream> + +namespace GLEAN { + +class Environment; // Mutually-recursive and forward references. +class DrawingSurfaceConfig; + +// Base class for a single test result. A test may have many results +// (for example, one per drawing surface configuration), so in general +// individual tests will have a vector of these objects. +class Result { +public: + virtual void put(ostream& s) const = 0; + virtual bool get(istream& s) = 0; + Result() { } + virtual ~Result() { } +}; + +class Test { + public: + Test(const char* testName, const char *descrip); + Test(const char* testName, const char *descrip, Test** prereqs); + virtual ~Test(); + + string name; // Test name. Should avoid characters + // that aren't universally available in + // filenames, since it might be used to + // construct such names. + + string description; // Verbose description of 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 + // will be performed. + + 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: + struct Error { }; // Base class for all exceptions. + struct CantOpenResultsFile: public Error { + const string& testName; + const string& dbName; + CantOpenResultsFile(const string& test, const string& db): + testName(test), dbName(db) { } + }; + + + // OutputStream and Input*Stream objects provide convenient access + // to the results database, and close the file streams automatically + // when their destructors are executed. + class OutputStream { // Open an output stream for storing results. + public: + ofstream* s; + OutputStream(Test& t); + ~OutputStream(); + operator ofstream& (); + }; + class Input1Stream { // Open db #1 input stream for reading results. + public: + ifstream* s; + Input1Stream(Test& t); + ~Input1Stream(); + operator ifstream& (); + }; + class Input2Stream { // Open db #2 input stream for reading results. + public: + ifstream* s; + Input2Stream(Test& t); + ~Input2Stream(); + operator ifstream& (); + }; + + + static Test* testList; // List of all test objects. Built by + // constructor Test::Test(...). + + Test* nextTest; // Link to next test object. + + static int testCount; // Count of elements in testList. +}; // class Test + +} // namespace GLEAN + +#endif // __test_h__ diff --git a/tests/glean/tfpexceptions.cpp b/tests/glean/tfpexceptions.cpp new file mode 100644 index 00000000..7a93eda1 --- /dev/null +++ b/tests/glean/tfpexceptions.cpp @@ -0,0 +1,602 @@ +// 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 + +// Authors: Brian Paul, Keith Whitwell + +#include "tfpexceptions.h" +#include <cassert> +#include <cmath> + +#define INCLUDE_FPU_CONTROL 0 +#if INCLUDE_FPU_CONTROL +#include <fpu_control.h> +#endif + + +namespace GLEAN { + + +// This might be useful at some point +void +FPExceptionsTest::enableExceptions(bool enable) +{ +#if INCLUDE_FPU_CONTROL + const fpu_control_t bits = + _FPU_MASK_IM | + _FPU_MASK_DM | + _FPU_MASK_ZM | + _FPU_MASK_OM | + _FPU_MASK_UM; + + if (enable) { + /* generate FP exceptions */ + fpu_control_t mask; + _FPU_GETCW(mask); + mask &= ~bits; + _FPU_SETCW(mask); + } + else { + fpu_control_t mask; + _FPU_GETCW(mask); + mask |= bits; + _FPU_SETCW(mask); + } +#else + (void) enable; +#endif +} + + + +// XXX any endian issues with this??? +// Works on x86 / little endian +union fi { + float f; + struct { + unsigned mantissa:23; + unsigned exponent:8; + unsigned sign:1; + } bits; + unsigned ui; +}; + + +static void +make_float(float *dest, unsigned sign, unsigned exponent, unsigned mantissa) +{ + union fi *destfi = (union fi *) dest; + destfi->bits.sign = sign; + destfi->bits.exponent = exponent; + destfi->bits.mantissa = mantissa; +} + +static void +make_denorm_float(float *dest, int sign, int mantissa) +{ + make_float(dest, sign, 0, mantissa); +} + +static void +make_pos_inf_float(float *dest) +{ + make_float(dest, 0, 255, 0); // or HUGE_VALF? +} + +static void +make_neg_inf_float(float *dest) +{ + make_float(dest, 1, 255, 0); // or -HUGE_VALF? +} + +static void +make_signaling_nan_float(float *dest) +{ + make_float(dest, 0, 255, 1); +} + +static void +make_quiet_nan_float(float *dest) +{ + make_float(dest, 0, 255, 1 << 22); +} + +static void +make_denorm_double(double * /*dest*/, int /*sign*/, int /*mantissa*/) +{ + // XXX to do +} + +static void +make_pos_inf_double(double *dest) +{ + *dest = HUGE_VAL; +} + +static void +make_neg_inf_double(double *dest) +{ + *dest = -HUGE_VAL; +} + +static void +make_signaling_nan_double(double * /*dest*/) +{ + // XXX to do +} + +static void +make_quiet_nan_double(double * /*dest*/) +{ + // XXX to do +} + + +static void +print_float(float f) +{ + union fi fi, fi2; + int iexp, imnt, isgn; + + fi.f = f; + printf("float %f (%e)\n\tuint 0x%x\n\tsign %d exponent %d mantissa 0x%x\n", + fi.f, fi.f, fi.ui, fi.bits.sign, fi.bits.exponent, fi.bits.mantissa); + + switch (fi.bits.exponent) { + case 0: + if (fi.bits.mantissa == 0) + printf("\t%szero\n", fi.bits.sign ? "-" : "+"); + else { + printf("\tdenormalized float\n"); + + iexp = -126 - 23; /* -149 */ + imnt = (int)fi.bits.mantissa; + isgn = fi.bits.sign ? -1 : 1; + fi2.f = isgn * imnt * ldexp(1.0, iexp); + + printf("\trecombining: %d * 0x%x * 2.0^%d == %f (%e)\n", + isgn, imnt, iexp, fi2.f, fi2.f); + printf("\trecombined: sign %d exponent %d mantissa 0x%x\n", + fi2.bits.sign, fi2.bits.exponent, fi2.bits.mantissa); + } + break; + + case 255: + if (fi.bits.mantissa & (1<<22)) + printf("\tQNaN (Quiet NaN/indeterminate value)\n"); + else if (fi.bits.mantissa) + printf("\tSNaN (Signalling NaN/invalid value)\n"); + else + printf("\t%sinf\n", fi.bits.sign ? "-" : "+"); + break; + + default: + iexp = fi.bits.exponent - (127 + 23); + imnt = (1<<23) + (int)fi.bits.mantissa; + isgn = fi.bits.sign ? -1 : 1; + fi2.f = isgn * imnt * ldexp(1.0, iexp); + + printf("\trecombining: %d * 0x%x * 2.0^%d == %f\n", + isgn, imnt, iexp, fi2.f); + + printf("\trecombined: sign %d exponent %d mantissa 0x%x\n", + fi2.bits.sign, fi2.bits.exponent, fi2.bits.mantissa); + break; + } + + /* Let's look and see what would happen if we interpret all these + * cases as normal floats: + */ + iexp = fi.bits.exponent - (127 + 23); + imnt = (1<<23) + (int)fi.bits.mantissa; + isgn = fi.bits.sign ? -1 : 1; + fi2.f = isgn * imnt * ldexp(1.0, iexp); + + printf("\tvalue if treated as normalized: %f (%e)\n", + fi2.f, fi2.f); +} + + +/* Examine some interesting floats + */ +#if 0 +int main() +{ + float f; + int i; + + for (i = -3; i < 10; i++) { + printf("%d:\n ", i); + print_float(ldexp(1.0, i)); + } + + for (f = -4 ; f < 4; f += 1) + print_float(f); + + for (f = -.01 ; f < .01; f += .002) + print_float(f); + + f = 1.0/0; + print_float(f); + + f += 1.0; + print_float(f); + + /* Explicitly make a denormal - I've no idea how to create these + * with regular calculations: + */ + make_float(&f, 0, 0, 0x1000); + print_float(f); + + /* It seems you can just specify them! + */ + f = 5.739719e-42; + print_float(f); + + /* A little, non-denormalized float + */ + make_float(&f, 0, 1, 0x1); + print_float(f); + + /* A negative little, non-denormalized float + */ + make_float(&f, 1, 1, 0x1); + print_float(f); + + /* A big float + */ + make_float(&f, 0, 254, ~0); + print_float(f); + + make_float(&f, 1, 254, ~0); + print_float(f); + + /* Littlest and biggest denormals: + */ + make_float(&f, 0, 0, 1); + print_float(f); + make_float(&f, 0, 0, ~0); + print_float(f); + + + make_float(&f, 1, 0, 1); + print_float(f); + make_float(&f, 1, 0, ~0); + print_float(f); + +} +#endif + + +bool +FPExceptionsTest::testVertices(Mode m) +{ + float v[3][4]; + // nice coords + for (int i = 0; i < 3; i++) { + v[i][0] = 0.0; + v[i][1] = 0.0; + v[i][2] = 0.0; + v[i][3] = 1.0; + } + + // set problematic values + switch (m) { + case MODE_INFINITY: + make_pos_inf_float(&v[1][0]); + make_neg_inf_float(&v[2][1]); + break; + case MODE_NAN: + make_signaling_nan_float(&v[1][0]); + make_quiet_nan_float(&v[2][1]); + break; + case MODE_DIVZERO: + v[0][3] = 0.0; + v[1][3] = 0.0; + v[2][3] = 0.0; + break; + case MODE_DENORM: + make_denorm_float(&v[0][0], 0, 1); + make_denorm_float(&v[1][1], 1, 1); + break; + default: + ; // nothing + } + + // vertex positions + glBegin(GL_POLYGON); + glVertex4fv(v[0]); + glVertex4fv(v[1]); + glVertex4fv(v[2]); + glEnd(); + + // colors + glBegin(GL_POLYGON); + glColor4fv(v[0]); glVertex2f(-1, -1); + glColor4fv(v[1]); glVertex2f( 1, -1); + glColor4fv(v[2]); glVertex2f( 0, 1); + glEnd(); + + // normals + glEnable(GL_LIGHTING); + glBegin(GL_POLYGON); + glNormal3fv(v[0]); glVertex2f(-1, -1); + glNormal3fv(v[1]); glVertex2f( 1, -1); + glNormal3fv(v[2]); glVertex2f( 0, 1); + glEnd(); + glDisable(GL_LIGHTING); + + // texcoords + glEnable(GL_TEXTURE_2D); + glBegin(GL_POLYGON); + glTexCoord4fv(v[0]); glVertex2f(-1, -1); + glTexCoord4fv(v[1]); glVertex2f( 1, -1); + glTexCoord4fv(v[2]); glVertex2f( 0, 1); + glEnd(); + glDisable(GL_TEXTURE_2D); + + return true; +} + + +bool +FPExceptionsTest::testTransformation(Mode m) +{ + float mat[16]; + + // identity + for (int i = 0; i < 15; i++) + mat[i] = 0.0; + mat[0] = mat[5] = mat[10] = mat[15] = 1.0; + + // set problematic values + switch (m) { + case MODE_INFINITY: + make_pos_inf_float(&mat[0]); // X scale + make_neg_inf_float(&mat[13]); // Y translate + break; + case MODE_NAN: + make_signaling_nan_float(&mat[0]); // X scale + make_quiet_nan_float(&mat[13]); // Y translate + break; + case MODE_DIVZERO: + // all zero matrix + mat[0] = mat[5] = mat[10] = mat[15] = 0.0; + break; + case MODE_DENORM: + make_denorm_float(&mat[0], 0, 1); + make_denorm_float(&mat[13], 1, 1); + break; + default: + ; // nothing + } + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(mat); + + // vertex positions + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 0, 1); + glEnd(); + + glPopMatrix(); + + return true; +} + + +bool +FPExceptionsTest::testClipping(Mode m) +{ + double plane[4]; + + // start w/ nice values + plane[0] = plane[1] = plane[2] = plane[3] = 0.0; + + // set problematic values + switch (m) { + case MODE_INFINITY: + make_pos_inf_double(&plane[0]); + make_neg_inf_double(&plane[3]); + break; + case MODE_NAN: + make_signaling_nan_double(&plane[0]); + make_quiet_nan_double(&plane[3]); + break; + case MODE_DIVZERO: + // nothing + break; + case MODE_DENORM: + make_denorm_double(&plane[0], 0, 1); + make_denorm_double(&plane[3], 1, 1); + break; + case MODE_OVERFLOW: + plane[0] = 1.0e300; + plane[3] = 1.0e-300; + break; + default: + ; // nothing + } + + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + + // vertex positions + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 0, 1); + glEnd(); + + glDisable(GL_CLIP_PLANE0); + + return true; +} + + +// pass large doubles to OpenGL and see what happens when converted to float. +bool +FPExceptionsTest::testOverflow(void) +{ + GLdouble v[3][4]; + for (int i = 0; i < 3; i++) { + v[i][0] = 0.0; + v[i][1] = 0.0; + v[i][2] = 0.0; + v[i][3] = 1.0; + } + v[0][0] = 1.0e300; + v[0][1] = -1.0e300; + v[1][0] = 1.0e-300; + v[1][1] = 1.0e-300; + + GLdouble mat[16]; + for (int i = 0; i < 15; i++) + mat[i] = 0.0; + mat[0] = mat[5] = mat[10] = mat[15] = 1.0e500; + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(mat); + + glBegin(GL_POLYGON); + glVertex4dv(v[0]); + glVertex4dv(v[1]); + glVertex4dv(v[2]); + glEnd(); + + glPopMatrix(); + + return true; +} + + + +void +FPExceptionsTest::setup(void) +{ + // Simple texture map + static const GLfloat texImage[2][2][3] = { + { {1, 1, 1}, {0, 0, 0} }, + { {0, 0, 0}, {1, 1, 1} } + }; + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_FLOAT, texImage); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // simple lighting + glEnable(GL_LIGHT0); + glEnable(GL_LIGHT1); + glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); +} + + +void +FPExceptionsTest::reportPassFail(MultiTestResult &r, + bool pass, const char *msg) const +{ + if (pass) { + if (env->options.verbosity) + env->log << name << " PASS: " << msg << " test\n"; + r.numPassed++; + } + else { + if (env->options.verbosity) + env->log << name << " FAILURE: " << msg << " test\n"; + r.numFailed++; + } +} + +void +FPExceptionsTest::runOne(MultiTestResult &r, Window &w) +{ + bool p; + + (void) w; + + p = testVertices(MODE_INFINITY); + reportPassFail(r, p, "Infinite value vertex"); + + p = testVertices(MODE_NAN); + reportPassFail(r, p, "NaN value vertex"); + + p = testVertices(MODE_DIVZERO); + reportPassFail(r, p, "Divide by zero vertex"); + + p = testVertices(MODE_DENORM); + reportPassFail(r, p, "Denorm vertex"); + + + p = testTransformation(MODE_INFINITY); + reportPassFail(r, p, "Infinite matrix transform"); + + p = testTransformation(MODE_NAN); + reportPassFail(r, p, "NaN matrix transform"); + + p = testTransformation(MODE_DIVZERO); + reportPassFail(r, p, "Zero matrix transform"); + + p = testTransformation(MODE_DENORM); + reportPassFail(r, p, "Denorm matrix transform"); + + + p = testClipping(MODE_INFINITY); + reportPassFail(r, p, "Infinite clip plane"); + + p = testClipping(MODE_NAN); + reportPassFail(r, p, "NaN clip plane"); + + p = testClipping(MODE_DIVZERO); + reportPassFail(r, p, "Zero clip plane"); + + p = testClipping(MODE_DENORM); + reportPassFail(r, p, "Denorm clip plane"); + + p = testClipping(MODE_OVERFLOW); + reportPassFail(r, p, "Overflow clip plane"); + + + p = testOverflow(); + reportPassFail(r, p, "Overflow"); + + r.pass = (r.numFailed == 0); +} + + +// The test object itself: +FPExceptionsTest FPExceptionsTest("fpexceptions", // test name + "window, rgb", // surface/pixel format + "", // no extensions required + "Test for floating point exceptions caused by +/-infinity, Nan, divide by zero, etc in a number of circumstances.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tfpexceptions.h b/tests/glean/tfpexceptions.h new file mode 100644 index 00000000..fee9a0c6 --- /dev/null +++ b/tests/glean/tfpexceptions.h @@ -0,0 +1,78 @@ +// 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 + +// tfpexceptions.h: Test for floating point exceptions caused by +// infinity, Nan, denormalized numbers, divide by zero, etc. +// Brian Paul 9 November 2005 + +#ifndef __tfpexceptions_h__ +#define __tfpexceptions_h__ + +#include "tmultitest.h" + +namespace GLEAN { + + +#define windowSize 100 + + +class FPExceptionsTest: public MultiTest +{ +public: + FPExceptionsTest(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: + enum Mode { + MODE_INFINITY, + MODE_NAN, + MODE_DIVZERO, + MODE_DENORM, + MODE_OVERFLOW + }; + + void enableExceptions(bool enable); + + bool testVertices(Mode m); + bool testTransformation(Mode m); + bool testClipping(Mode m); + bool testOverflow(void); + + void reportPassFail(MultiTestResult &r, bool pass, const char *msg) const; + void setup(void); +}; + + +} // namespace GLEAN + +#endif // __tfpexceptions_h__ diff --git a/tests/glean/tfragprog1.cpp b/tests/glean/tfragprog1.cpp new file mode 100644 index 00000000..632a5af2 --- /dev/null +++ b/tests/glean/tfragprog1.cpp @@ -0,0 +1,1056 @@ +// 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 + +// tfragprog.cpp: Test GL_ARB_fragment_program extension. +// Brian Paul 22 October 2005 +// +// This is pretty simple. Specific fragment programs are run, we read back +// the framebuffer color and compare the color to the expected result. +// Pretty much any fragment program can be tested in the manner. +// Ideally, an additional fragment program test should be developed which +// exhaustively tests instruction combinations with all the various swizzle +// and masking options, etc. +// But this test is good for regression testing to be sure that particular or +// unique programs work correctly. + + +#include "tfragprog1.h" +#include <cassert> +#include <cmath> +#include <math.h> + + +namespace GLEAN { + + +static PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB_func; +static PFNGLGENPROGRAMSARBPROC glGenProgramsARB_func; +static PFNGLPROGRAMSTRINGARBPROC glProgramStringARB_func; +static PFNGLBINDPROGRAMARBPROC glBindProgramARB_func; +static PFNGLISPROGRAMARBPROC glIsProgramARB_func; +static PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB_func; +static PFNGLGETPROGRAMIVARBPROC glGetProgramivARB_func; +static PFNGLFOGCOORDFPROC glFogCoordf_func; + + +// Clamp X to [0, 1] +#define CLAMP01( X ) ( (X)<(0.0) ? (0.0) : ((X)>(1.0) ? (1.0) : (X)) ) +// Absolute value +#define ABS(X) ( (X) < 0.0 ? -(X) : (X) ) +// Max +#define MAX( A, B ) ( (A) > (B) ? (A) : (B) ) +// Min +#define MIN( A, B ) ( (A) < (B) ? (A) : (B) ) +// Duplicate value four times +#define SMEAR(X) (X), (X), (X), (X) + +#define DONT_CARE_Z -1.0 +#define DONT_CARE_COLOR -1.0 + +#define FRAGCOLOR { 0.25, 0.75, 0.5, 0.25 } +#define PARAM0 { 0.0, 0.0, 0.0, 0.0 } +#define PARAM1 { 0.5, 0.25, 1.0, 0.5 } +#define PARAM2 { -1.0, 0.0, 0.25, -0.5 } +static const GLfloat FragColor[4] = FRAGCOLOR; +static const GLfloat Param0[4] = PARAM0; +static const GLfloat Param1[4] = PARAM1; +static const GLfloat Param2[4] = PARAM2; +static GLfloat InfNan[4]; +static GLfloat FogColor[4] = {1.0, 1.0, 0.0, 0.0}; +static GLfloat FogStart = 10.0; +static GLfloat FogEnd = 100.0; +static GLfloat FogDensity = 0.03; +static GLfloat FogCoord = 50.0; /* Between FogStart and FogEnd */ + + +// These are the specific fragment programs which we'll test +// Alphabetical order, please +static const FragmentProgram Programs[] = { + { + "ABS test", + "!!ARBfp1.0\n" + "PARAM p = program.local[2]; \n" + "ABS result.color, p; \n" + "END \n", + { ABS(Param2[0]), + ABS(Param2[1]), + ABS(Param2[2]), + ABS(Param2[3]) + }, + DONT_CARE_Z, + false, + }, + { + "ADD test", + "!!ARBfp1.0\n" + "PARAM p = program.local[1]; \n" + "ADD result.color, fragment.color, p; \n" + "END \n", + { CLAMP01(FragColor[0] + Param1[0]), + CLAMP01(FragColor[1] + Param1[1]), + CLAMP01(FragColor[2] + Param1[2]), + CLAMP01(FragColor[3] + Param1[3]) + }, + DONT_CARE_Z, + false, + }, + { + "CMP test", + "!!ARBfp1.0\n" + "PARAM zero = program.local[0]; \n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "CMP result.color, p2, zero, p1; \n" + "END \n", + { Param0[0], Param1[1], Param1[2], Param0[3] }, + DONT_CARE_Z, + false + }, + { + "COS test", + "!!ARBfp1.0\n" + "PARAM values = { 0.0, 3.14159, 0.5, 1.0 }; \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(1.0), + CLAMP01(-1.0), + CLAMP01(0.8775), + CLAMP01(0.5403) + }, + DONT_CARE_Z, + false + }, + { + "DP3 test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "DP3 result.color, p1, fragment.color; \n" + "END \n", + { SMEAR(CLAMP01(Param1[0] * FragColor[0] + + Param1[1] * FragColor[1] + + Param1[2] * FragColor[2])) + }, + DONT_CARE_Z, + false + }, + { + "DP4 test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "DP4 result.color, p1, fragment.color; \n" + "END \n", + { SMEAR(CLAMP01(Param1[0] * FragColor[0] + + Param1[1] * FragColor[1] + + Param1[2] * FragColor[2] + + Param1[3] * FragColor[3])) + }, + DONT_CARE_Z, + false + }, + { + "DPH test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "TEMP t; \n" + "DPH t, p1, fragment.color; \n" + "MUL result.color, t, scale; \n" + "END \n", + { SMEAR(CLAMP01((Param1[0] * FragColor[0] + + Param1[1] * FragColor[1] + + Param1[2] * FragColor[2] + + FragColor[3]) * 0.1)) + }, + DONT_CARE_Z, + false + }, + { + "DST test", + "!!ARBfp1.0\n" + "# let d = 0.4 \n" + "PARAM v1 = {9.9, 0.16, 0.16, 9.9}; \n" + "PARAM v2 = {9.9, 2.5, 9.9, 2.5}; \n" + "DST result.color, v1, v2; \n" + "END \n", + { 1.0, + 0.4, // v1.y * v2.y + 0.16, // v1.z + CLAMP01(2.5) // v2.w + }, + DONT_CARE_Z, + false + }, + { + "EX2 test", + "!!ARBfp1.0\n" + "PARAM scale = {0.01, 0.01, 0.01, 0.01}; \n" + "PARAM values = {0.0, 1.0, 4.0, -2.0 }; \n" + "TEMP t; \n" + "EX2 t.x, values.x; \n" + "EX2 t.y, values.y; \n" + "EX2 t.z, values.z; \n" + "EX2 t.w, values.w; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 1.0 * 0.01, + 2.0 * 0.01, + 16.0 * 0.01, + 0.25 * 0.01 }, + DONT_CARE_Z, + false + }, + { + "FLR test", + "!!ARBfp1.0\n" + "PARAM values = {4.8, 0.3, -0.2, 1.2}; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "TEMP t; \n" + "FLR t, values; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 0.4, + 0.0, + CLAMP01(-0.1), + 0.1 + }, + DONT_CARE_Z, + false + }, + { + "FRC test", + "!!ARBfp1.0\n" + "PARAM values = {-1.1, 0.1, -2.2, 2.4 }; \n" + "FRC result.color, values; \n" + "END \n", + { 0.9, 0.1, 0.8, 0.4 }, + DONT_CARE_Z, + false + }, + { + "LG2 test", + "!!ARBfp1.0\n" + "PARAM values = {64.0, 1, 30, 4}; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "TEMP t; \n" + "LG2 t.x, values.x; \n" + "LG2 t.y, values.y; \n" + "LG2 t.z, values.z; \n" + "LG2 t.w, values.w; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 0.6, + 0.0, + 0.49, + 0.2 + }, + DONT_CARE_Z, + false + }, + { + "LIT test 1", + "!!ARBfp1.0\n" + "PARAM values = {0.65, 0.9, 0.0, 8.0}; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + 0.65, // values.x + 0.433, // roughly Pow(values.y, values.w) + 1.0 + }, + DONT_CARE_Z, + false + }, + { + "LIT test 2 (degenerate case: 0 ^ 0 -> 1)", + "!!ARBfp1.0\n" + "PARAM values = {0.65, 0.0, 0.0, 0.0}; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + 0.65, // values.x + 1.0, // 0^0 + 1.0 + }, + DONT_CARE_Z, + false + }, + { + "LIT test 3 (case x < 0)", + "!!ARBfp1.0\n" + "PARAM values = {-0.5, 0.0, 0.0, 0.0}; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + CLAMP01(-0.5), // values.x + 0.0, + 1.0 + }, + DONT_CARE_Z, + false + }, + { + "LRP test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM t = {0.2, 0.5, 1.0, 0.0}; \n" + "LRP result.color, t, fragment.color, p1; \n" + "END \n", + { 0.2 * FragColor[0] + (1.0 - 0.2) * Param1[0], + 0.5 * FragColor[1] + (1.0 - 0.5) * Param1[1], + 1.0 * FragColor[2] + (1.0 - 1.0) * Param1[2], + 0.0 * FragColor[3] + (1.0 - 0.0) * Param1[3] + }, + DONT_CARE_Z, + false + }, + { + "MAD test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "MAD result.color, fragment.color, p1, p2; \n" + "END \n", + { CLAMP01(FragColor[0] * Param1[0] + Param2[0]), + CLAMP01(FragColor[1] * Param1[1] + Param2[1]), + CLAMP01(FragColor[2] * Param1[2] + Param2[2]), + CLAMP01(FragColor[3] * Param1[3] + Param2[3]) + }, + DONT_CARE_Z, + false + }, + { + "MAX test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "MAX result.color, p1, p2; \n" + "END \n", + { MAX(Param1[0], Param2[0]), + MAX(Param1[1], Param2[1]), + MAX(Param1[2], Param2[2]), + MAX(Param1[3], Param2[3]), + }, + DONT_CARE_Z, + false + }, + { + "MIN test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "MIN result.color, p1, fragment.color; \n" + "END \n", + { MIN(Param1[0], FragColor[0]), + MIN(Param1[1], FragColor[1]), + MIN(Param1[2], FragColor[2]), + MIN(Param1[3], FragColor[3]), + }, + DONT_CARE_Z, + false + }, + { + "MOV test", + "!!ARBfp1.0\n" + "MOV result.color, fragment.color; \n" + "END \n", + FRAGCOLOR, + DONT_CARE_Z, + false + }, + { + "MUL test", + "!!ARBfp1.0\n" + "PARAM p = program.local[1]; \n" + "MUL result.color, fragment.color, p; \n" + "END \n", + { CLAMP01(FragColor[0] * Param1[0]), + CLAMP01(FragColor[1] * Param1[1]), + CLAMP01(FragColor[2] * Param1[2]), + CLAMP01(FragColor[3] * Param1[3]) + }, + DONT_CARE_Z, + false + }, + { + "masked MUL test", + "!!ARBfp1.0\n" + "PARAM zero = program.local[0]; \n" + "PARAM p = program.local[1]; \n" + "MOV result.color, zero; \n" + "MUL result.color.xy, fragment.color, p; \n" + "END \n", + { CLAMP01(FragColor[0] * Param1[0]), + CLAMP01(FragColor[1] * Param1[1]), + 0.0, + 0.0 + }, + DONT_CARE_Z, + false + }, + { + "POW test (exponentiation)", + "!!ARBfp1.0\n" + "PARAM values = {0.5, 2, 3, 4}; \n" + "POW result.color.x, values.x, values.y; \n" + "POW result.color.y, values.x, values.z; \n" + "POW result.color.z, values.x, values.w; \n" + "POW result.color.w, values.w, values.x; \n" + "END \n", + { 0.5 * 0.5, + 0.5 * 0.5 * 0.5, + 0.5 * 0.5 * 0.5 * 0.5, + CLAMP01(2.0) }, + DONT_CARE_Z, + false + }, + { + "RCP test (reciprocal)", + "!!ARBfp1.0\n" + "PARAM values = {8, -10, 1, 12 }; \n" + "RCP result.color.x, values.x; \n" + "RCP result.color.y, values.y; \n" + "RCP result.color.z, values.z; \n" + "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 + }, + { + "RSQ test 1 (reciprocal square root)", + "!!ARBfp1.0\n" + "PARAM values = {1, 4, 9, 100 }; \n" + "RSQ result.color.x, values.x; \n" + "RSQ result.color.y, values.y; \n" + "RSQ result.color.z, values.z; \n" + "RSQ result.color.w, values.w; \n" + "END \n", + { 1.0, 0.5, 0.3333, 0.1 }, + DONT_CARE_Z, + false + }, + { + "RSQ test 2 (reciprocal square root of negative value)", + "!!ARBfp1.0\n" + "PARAM values = {0, -100, -5, -1}; \n" + "RSQ result.color.x, values.x; \n" + "RSQ result.color.y, values.y; \n" + "RSQ result.color.z, values.z; \n" + "RSQ result.color.w, values.w; \n" + "END \n", + { DONT_CARE_COLOR, + 0.1, + 0.447, + 1.0, + }, + DONT_CARE_Z, + false + }, + { + "SCS test", + "!!ARBfp1.0\n" + "PARAM values = { 0.5, 0.5, 0.0, 0.0 }; \n" + "SCS result.color.x, values.x; \n" + "SCS result.color.y, values.y; \n" + "END \n", + { CLAMP01(0.8775), + CLAMP01(0.4794), + DONT_CARE_COLOR, + DONT_CARE_COLOR, + }, + DONT_CARE_Z, + false + }, + { + "SGE test", + "!!ARBfp1.0\n" + "PARAM p0 = program.local[0]; \n" + "PARAM p2 = program.local[2]; \n" + "SGE result.color, p2, p0; \n" + "END \n", + { Param2[0] >= Param0[0] ? 1.0 : 0.0, + Param2[1] >= Param0[1] ? 1.0 : 0.0, + Param2[2] >= Param0[2] ? 1.0 : 0.0, + Param2[3] >= Param0[3] ? 1.0 : 0.0, + }, + DONT_CARE_Z, + false + }, + { + "SIN test", + "!!ARBfp1.0\n" + "PARAM values = { 1.57079, -1.57079, 0.5, 1.0 }; \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(1.0), + CLAMP01(-1.0), + CLAMP01(0.4794), + CLAMP01(0.8414) + }, + DONT_CARE_Z, + false + }, + { + "SLT test", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "SLT result.color, fragment.color, p1; \n" + "END \n", + { FragColor[0] < Param1[0] ? 1.0 : 0.0, + FragColor[1] < Param1[1] ? 1.0 : 0.0, + FragColor[2] < Param1[2] ? 1.0 : 0.0, + FragColor[3] < Param1[3] ? 1.0 : 0.0, + }, + DONT_CARE_Z, + false + }, + { + "SUB test (with swizzle)", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "SUB result.color, p1.yxwz, fragment.color.yxwz; \n" + "END \n", + { CLAMP01(Param1[1] - FragColor[1]), + CLAMP01(Param1[0] - FragColor[0]), + CLAMP01(Param1[3] - FragColor[3]), + CLAMP01(Param1[2] - FragColor[2]) + }, + DONT_CARE_Z, + false + }, + { + "SWZ test", + "!!ARBfp1.0\n" + "PARAM p = program.local[1]; \n" + "SWZ result.color, p, -1,-y,z,0; \n" + "END \n", + { CLAMP01(-1.0), + CLAMP01(-Param1[1]), + CLAMP01(Param1[2]), + CLAMP01(0.0) + }, + DONT_CARE_Z, + false + }, + { + "XPD test 1", + "!!ARBfp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "XPD result.color, p1, p2; \n" + "END \n", + { CLAMP01(Param1[1] * Param2[2] - Param1[2] * Param2[1]), + CLAMP01(Param1[2] * Param2[0] - Param1[0] * Param2[2]), + CLAMP01(Param1[0] * Param2[1] - Param1[1] * Param2[0]), + DONT_CARE_COLOR + }, + DONT_CARE_Z, + false + }, + { + "Z-write test", + "!!ARBfp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.color, p; \n" + "MOV result.depth.z, p.y; \n" + "END \n", + { Param1[0], + Param1[1], + Param1[2], + Param1[3] + }, + Param1[1], + false + }, + + // ============= Numeric stress tests ================================= + // Basically just check that we don't crash when we do divides by + // zero, etc. + { + "Divide by zero test", + "!!ARBfp1.0\n" + "PARAM zero = program.local[0]; \n" + "RCP result.color.x, zero.x; \n" + "RCP result.color.y, zero.y; \n" + "RCP result.color.z, zero.z; \n" + "RCP result.color.w, zero.w; \n" + "END \n", + { DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR + }, + DONT_CARE_Z, + false + }, + { + "Infinity / nan test", + "!!ARBfp1.0\n" + "PARAM zero = program.local[0]; \n" + "PARAM infNan = program.local[9]; \n" + "ADD result.color, infNan, zero; \n" + "END \n", + { DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR + }, + DONT_CARE_Z, + false + }, + + // ============= Fog tests ============================================ + // Linear fog +#define FOG_FACT ((FogEnd - FogCoord) / (FogEnd - FogStart)) + { + "ARB_fog_linear test", + "!!ARBfp1.0\n" + "OPTION ARB_fog_linear; \n" + "MOV result.color, fragment.color; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, + { + "Computed fog linear test", + "!!ARBfp1.0\n" + "# fogParams.x = density \n" + "# fogParams.y = start \n" + "# fogParams.z = end \n" + "# fogParams.w = 1/(end-start) \n" + "PARAM fogParams = state.fog.params; \n" + "ATTRIB fogCoord = fragment.fogcoord; \n" + "PARAM fogColor = state.fog.color; \n" + "TEMP numerator, f; \n" + "# f = (end - coord) / (end - start) \n" + "SUB numerator, fogParams.z, fogCoord.x; \n" + "MUL_SAT f, numerator, fogParams.w; \n" + "LRP result.color.rgb, f, fragment.color, fogColor; \n" + "MOV result.color.a, fragment.color.a; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, +#undef FOG_FACT + + // Exp fog +#define FOG_FACT 0.2231 // = exp(-Density * Coord) + { + "ARB_fog_exp test", + "!!ARBfp1.0\n" + "OPTION ARB_fog_exp; \n" + "MOV result.color, fragment.color; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, +#undef FOG_FACT +#define FOG_FACT 0.3535 // = ex2(-Density * Coord) + { + // NOTE: we could also do this with the POW instruction + "Computed fog exp test", + "!!ARBfp1.0\n" + "# fogParams.x = density \n" + "# fogParams.y = start \n" + "# fogParams.z = end \n" + "# fogParams.w = 1/(end-start) \n" + "PARAM fogParams = state.fog.params; \n" + "ATTRIB fogCoord = fragment.fogcoord; \n" + "PARAM fogColor = state.fog.color; \n" + "TEMP f, dc; \n" + "# f = exp(-density * coord) \n" + "MUL dc.x, fogParams.x, fogCoord.x; \n" + "EX2_SAT f, -dc.x; \n" + "LRP result.color.rgb, f, fragment.color, fogColor; \n" + "MOV result.color.a, fragment.color.a; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, +#undef FOG_FACT + + // Exp2 fog +#define FOG_FACT 0.1054 // = exp(-(Density * Coord)^2) + { + "ARB_fog_exp2 test", + "!!ARBfp1.0\n" + "OPTION ARB_fog_exp2; \n" + "MOV result.color, fragment.color; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, +#undef FOG_FACT +#define FOG_FACT 0.2102 // = ex2(-(Density * Coord)^2) + { + // NOTE: we could also do this with the POW instruction + "Computed fog exp2 test", + "!!ARBfp1.0\n" + "# fogParams.x = density \n" + "# fogParams.y = start \n" + "# fogParams.z = end \n" + "# fogParams.w = 1/(end-start) \n" + "PARAM fogParams = state.fog.params; \n" + "ATTRIB fogCoord = fragment.fogcoord; \n" + "PARAM fogColor = state.fog.color; \n" + "TEMP f, dc; \n" + "# f = exp(-(density * coord)^2) \n" + "MUL dc.x, fogParams.x, fogCoord.x; \n" + "MUL dc.x, dc.x, dc.x; \n" + "EX2_SAT f, -dc.x; \n" + "LRP result.color.rgb, f, fragment.color, fogColor; \n" + "MOV result.color.a, fragment.color.a; \n" + "END \n", + { FragColor[0] * FOG_FACT + FogColor[0] * (1.0 - FOG_FACT), + FragColor[1] * FOG_FACT + FogColor[1] * (1.0 - FOG_FACT), + FragColor[2] * FOG_FACT + FogColor[2] * (1.0 - FOG_FACT), + FragColor[3] + }, + DONT_CARE_Z, + true + }, +#undef FOG_FACT + + // XXX add lots more tests here! + { NULL, NULL, {0,0,0,0}, 0, false } // end of list sentinal +}; + + + +void +FragmentProgramTest::setup(void) +{ + haveFogCoord = false; + + if (GLUtils::haveExtensions("EXT_fog_coord")) + haveFogCoord = true; + + // setup Infinity, Nan values + int nan; + float *nanPtr; + + nan = (0xff << 23) | (1 << 0); + nanPtr = (float *) &nan; + InfNan[0] = HUGE_VAL; + InfNan[1] = -HUGE_VAL; + InfNan[2] = (float) (*nanPtr); + InfNan[3] = 1.0 / HUGE_VAL; + + // get function pointers + glProgramLocalParameter4fvARB_func = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLUtils::getProcAddress("glProgramLocalParameter4fvARB"); + assert(glProgramLocalParameter4fvARB_func); + + glGenProgramsARB_func = (PFNGLGENPROGRAMSARBPROC) GLUtils::getProcAddress("glGenProgramsARB"); + assert(glGenProgramsARB_func); + + glProgramStringARB_func = (PFNGLPROGRAMSTRINGARBPROC) GLUtils::getProcAddress("glProgramStringARB"); + assert(glProgramStringARB_func); + + glBindProgramARB_func = (PFNGLBINDPROGRAMARBPROC) GLUtils::getProcAddress("glBindProgramARB"); + assert(glBindProgramARB_func); + + glIsProgramARB_func = (PFNGLISPROGRAMARBPROC) GLUtils::getProcAddress("glIsProgramARB"); + assert(glIsProgramARB_func); + + glDeleteProgramsARB_func = (PFNGLDELETEPROGRAMSARBPROC) GLUtils::getProcAddress("glDeleteProgramsARB"); + assert(glDeleteProgramsARB_func); + + glGetProgramivARB_func = (PFNGLGETPROGRAMIVARBPROC) GLUtils::getProcAddress("glGetProgramivARB"); + assert(glGetProgramivARB_func); + + if (haveFogCoord) { + glFogCoordf_func = (PFNGLFOGCOORDFPROC) GLUtils::getProcAddress("glFogCoordf"); + assert(glFogCoordf_func); + } + + GLuint progID; + glGenProgramsARB_func(1, &progID); + glBindProgramARB_func(GL_FRAGMENT_PROGRAM_ARB, progID); + glEnable(GL_FRAGMENT_PROGRAM_ARB); + + // load program inputs + glColor4fv(FragColor); + glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB, 0, Param0); + glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB, 1, Param1); + glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB, 2, Param2); + glProgramLocalParameter4fvARB_func(GL_FRAGMENT_PROGRAM_ARB, 9, InfNan); + + GLenum err = glGetError(); + assert(!err); // should be OK + + // setup vertex transform (we'll draw a quad in middle of window) + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +#if DEVEL_MODE + glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0); +#else + glOrtho(-4.0, 4.0, -4.0, 4.0, 0.0, 1.0); +#endif + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glDrawBuffer(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); + } + + // 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; +} + + +void +FragmentProgramTest::reportFailure(const char *programName, + const GLfloat expectedColor[4], + const GLfloat actualColor[4] ) const +{ + env->log << "FAILURE:\n"; + env->log << " Program: " << 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 +FragmentProgramTest::reportZFailure(const char *programName, + GLfloat expectedZ, GLfloat actualZ) const +{ + env->log << "FAILURE:\n"; + env->log << " Program: " << programName << "\n"; + env->log << " Expected Z: " << expectedZ << "\n"; + env->log << " Observed Z: " << actualZ << "\n"; +} + + +// Compare actual and expected colors +bool +FragmentProgramTest::equalColors(const GLfloat act[4], const GLfloat exp[4]) const +{ + if (fabsf(act[0] - exp[0]) > tolerance[0] && exp[0] != DONT_CARE_COLOR) + return false; + if (fabsf(act[1] - exp[1]) > tolerance[1] && exp[1] != DONT_CARE_COLOR) + return false; + if (fabsf(act[2] - exp[2]) > tolerance[2] && exp[2] != DONT_CARE_COLOR) + return false; + if (fabsf(act[3] - exp[3]) > tolerance[3] && exp[3] != DONT_CARE_COLOR) + return false; + return true; +} + + +bool +FragmentProgramTest::equalDepth(GLfloat z0, GLfloat z1) const +{ + if (fabsf(z0 - z1) > tolerance[4]) + return false; + else + return true; +} + + +bool +FragmentProgramTest::testProgram(const FragmentProgram &p) +{ + glProgramStringARB_func(GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(p.progString), + (const GLubyte *) p.progString); + + GLenum err = glGetError(); + if (err) { + env->log << "OpenGL error " << (int) err << "\n"; + env->log << "Invalid Fragment Program:\n"; + env->log << p.progString; + env->log << glGetString(GL_PROGRAM_ERROR_STRING_ARB) << "\n"; + return false; + } + + // to avoid potential issue with undefined result.depth.z + if (p.expectedZ == DONT_CARE_Z) + glDisable(GL_DEPTH_TEST); + else + glEnable(GL_DEPTH_TEST); + +#if !DEVEL_MODE + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +#endif + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 1, 1); + glVertex2f(-1, 1); + glEnd(); + +#if !DEVEL_MODE + GLfloat pixel[4]; + glReadPixels(windowWidth / 2, windowHeight / 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)) { + reportFailure(p.name, p.expectedColor, pixel); + return false; + } + + if (p.expectedZ != DONT_CARE_Z) { + GLfloat z; + glReadPixels(windowWidth / 2, windowHeight / 2, 1, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, &z); + if (!equalDepth(z, p.expectedZ)) { + reportZFailure(p.name, p.expectedZ, z); + return false; + } + } +#endif + return true; +} + +void +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 + if (!testProgram(Programs[i])) { + r.numFailed++; + } + else { + r.numPassed++; + } + } + +#if DEVEL_MODE + glFinish(); + sleep(100); +#endif + 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", + "GL_ARB_fragment_program", + "Fragment Program test 1: test a specific set of fragment programs.\n"); + + + +} // namespace GLEAN diff --git a/tests/glean/tfragprog1.h b/tests/glean/tfragprog1.h new file mode 100644 index 00000000..b36e7a6f --- /dev/null +++ b/tests/glean/tfragprog1.h @@ -0,0 +1,94 @@ +// 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 + +// tfragprog.h: Test GL_ARB_fragment_program extension. +// Brian Paul 22 October 2005 + +#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. +#undef windowWidth +#undef windowHeight +#define DEVEL_MODE 0 +#if DEVEL_MODE +#define windowWidth 200 +#define windowHeight 850 +#else +#define windowWidth 100 +#define windowHeight 100 +#endif + + +class FragmentProgram +{ +public: + const char *name; + const char *progString; + const GLfloat expectedColor[4]; + const GLfloat expectedZ; + const bool needFogCoord; +}; + + +class FragmentProgramTest: public MultiTest +{ +public: + FragmentProgramTest(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]; + bool haveFogCoord; + + void setup(void); + bool equalColors(const GLfloat a[4], const GLfloat b[4]) const; + bool equalDepth(GLfloat z0, GLfloat z1) const; + bool testProgram(const FragmentProgram &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; + void printDetails(); +}; + +} // namespace GLEAN + +#endif // __tfragprog_h__ diff --git a/tests/glean/tgetstr.cpp b/tests/glean/tgetstr.cpp new file mode 100644 index 00000000..22c3347d --- /dev/null +++ b/tests/glean/tgetstr.cpp @@ -0,0 +1,167 @@ +// 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 + +// tgetstr.cpp: implementation of OpenGL glGetString() tests + +using namespace std; + +#include "tgetstr.h" +#include <algorithm> + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::runOne(GetStringResult& r, Window&) { + r.vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR)); + r.renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER)); + r.version = reinterpret_cast<const char*>(glGetString(GL_VERSION)); + r.extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)); + r.pass = true; +} // GetStringTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::logOne(GetStringResult& r) { + logPassFail(r); + logConcise(r); + if (env->options.verbosity) { + env->log << "\tvendor: " << r.vendor << '\n'; + env->log << "\trenderer: " << r.renderer << '\n'; + env->log << "\tversion: " << r.version << '\n'; + env->log << "\textensions: " << r.extensions << '\n'; + } +} // GetStringTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +GetStringTest::compareOne(GetStringResult& oldR, GetStringResult& newR) { + if (oldR.vendor == newR.vendor && oldR.renderer == newR.renderer + && oldR.version == newR.version && oldR.extensions == newR.extensions){ + if (env->options.verbosity) + env->log << name << ": SAME " << + newR.config->conciseDescription() << '\n'; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + if (oldR.vendor != newR.vendor) { + env->log << '\t' << env->options.db1Name + << " vendor: " << oldR.vendor; + env->log << '\t' << env->options.db2Name + << " vendor: " << newR.vendor; + } + if (oldR.renderer != newR.renderer) { + env->log << '\t' << env->options.db1Name + << " renderer: " << oldR.renderer; + env->log << '\t' << env->options.db2Name + << " renderer: " << newR.renderer; + } + if (oldR.version != newR.version) { + env->log << '\t' << env->options.db1Name + << " version: " << oldR.version; + env->log << '\t' << env->options.db2Name + << " version: " << newR.version; + } + if (oldR.extensions != newR.extensions) { + vector<string> oldExts; + Lex oldLex(oldR.extensions.c_str()); + for (;;) { + oldLex.next(); + if (oldLex.token == Lex::ID) + oldExts.push_back(oldLex.id); + else + break; + } + sort(oldExts.begin(), oldExts.end()); + + vector<string> newExts; + Lex newLex(newR.extensions.c_str()); + for (;;) { + newLex.next(); + if (newLex.token == Lex::ID) + newExts.push_back(newLex.id); + else + break; + } + sort(newExts.begin(), newExts.end()); + + vector<string> d(max(oldExts.size(), newExts.size())); + vector<string>::iterator dEnd; + + dEnd = set_difference(oldExts.begin(), oldExts.end(), + newExts.begin(), newExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in " << + env->options.db1Name << " but not in " + << env->options.db2Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + + dEnd = set_difference(newExts.begin(), newExts.end(), + oldExts.begin(), oldExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in " << + env->options.db2Name << " but not in " + << env->options.db1Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + + dEnd = set_intersection(newExts.begin(), newExts.end(), + oldExts.begin(), oldExts.end(), d.begin()); + if (dEnd != d.begin()) { + env->log << "\tExtensions in both " << + env->options.db2Name << " and in " + << env->options.db1Name << ":\n"; + for (vector<string>::iterator p = d.begin(); + p != dEnd; ++p) + env->log << "\t\t" << *p << '\n'; + } + } + } +} // GetStringTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +GetStringTest getStringTest("getString", "window", + "This test checks the contents of the strings returned by\n" + "glGetString(): the vendor name, renderer name, version, and\n" + "extensions. It is run on every OpenGL-capable drawing surface\n" + "configuration that supports creation of a window.\n"); + +} // namespace GLEAN diff --git a/tests/glean/tgetstr.h b/tests/glean/tgetstr.h new file mode 100644 index 00000000..d0465b5d --- /dev/null +++ b/tests/glean/tgetstr.h @@ -0,0 +1,75 @@ +// 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 + + +// tgetstr.h: Check OpenGL vendor, renderer, version, and extension strings + +// See tbasic.cpp for the basic test structure. + + +#ifndef __tgetstr_h__ +#define __tgetstr_h__ + +#include "tbase.h" + +class DrawingSurfaceConfig; // Forward reference. + +namespace GLEAN { + +class GetStringResult: public BaseResult { +public: + bool pass; + string vendor; + string renderer; + string version; + string extensions; + + void putresults(ostream& s) const { + s << vendor << '\n'; + s << renderer << '\n'; + s << version << '\n'; + s << extensions << '\n'; + } + + bool getresults(istream& s) { + getline(s, vendor); + getline(s, renderer); + getline(s, version); + getline(s, extensions); + return s.good(); + } +}; + +class GetStringTest: public BaseTest<GetStringResult> { +public: + GLEAN_CLASS(GetStringTest, GetStringResult); +}; // class GetStringTest + +} // namespace GLEAN + +#endif // __tgetstr_h__ diff --git a/tests/glean/timer.cpp b/tests/glean/timer.cpp new file mode 100644 index 00000000..54994603 --- /dev/null +++ b/tests/glean/timer.cpp @@ -0,0 +1,232 @@ +// 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 +// 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 + +// timer.cpp: Implementation of simple benchmark timer utilities. + +// This particular implementation is derived from the one in libpdb, +// part of the isfast library for OpenGL. + +// XXXWIN as of 5/8/99: The code for Windows timing is taken from +// Michael Gold's implementation of libpdb. I've probably introduced +// some bugs in the translation, unfortunately. [Allen] + +// Modified from original timer.cpp by Rickard E. (Rik) Faith +// <faith@valinux.com>, December 2000 + +#include "timer.h" +#include <vector> +#include <algorithm> +using namespace std; + +#if defined(__UNIX__) +# include <sys/time.h> // for gettimeofday, used by getClock +#elif defined(__MS__) +# include <windows.h> +# include <sys/types.h> +# include <sys/timeb.h> // for _ftime(), used by getClock +#endif + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// calibrate: Determine overhead of measurement, initialization routine, +// and finalization routine +/////////////////////////////////////////////////////////////////////////////// +void +Timer::calibrate() { + double runTime = chooseRunTime(); + + preop(); + + long reps = 0; + double current; + double start = waitForTick(); + do { + postop(); + ++reps; + } while ((current = getClock()) < start + runTime); + + overhead = (current - start) / (double) reps; + calibrated = true; +} // Timer::calibrate + +/////////////////////////////////////////////////////////////////////////////// +// chooseRunTime: Select an appropriate runtime for benchmarks. +// By running for at least 10000 ticks, and attempting to keep timing +// accurate to one tick, we hope to make our results repeatable. +// (ignoring all the other stuff that might be going on in the system, +// of course). Long runs reduce the effect of measurement error, but +// short runs reduce the chance that some other process on the system +// will steal time. +/////////////////////////////////////////////////////////////////////////////// +double +Timer::chooseRunTime() { + double start = getClock(); + double finish; + + // Wait for next tick: + while ((finish = getClock()) == start) + ; + + // Run for 10000 ticks, clamped to [0.1 sec, 5.0 sec]: + double runTime = 10000.0 * (finish - start); + if (runTime < 0.1) + runTime = 0.1; + else if (runTime > 5.0) + runTime = 5.0; + + return runTime; +} // Timer::chooseRunTime + +/////////////////////////////////////////////////////////////////////////////// +// getClock - get current wall-clock time (expressed in seconds) +/////////////////////////////////////////////////////////////////////////////// +double +Timer::getClock() { +#if defined(__MS__) + static int once = 1; + static double freq; + + if (once) { + LARGE_INTEGER fr; + freq = (double) (QueryPerformanceFrequency(&fr) ? + 1.0 / fr.QuadPart : 0); + once = 0; + } + + // Use high-resolution counter, if available + if (freq) { + LARGE_INTEGER pc; + QueryPerformanceCounter(&pc); + return freq * (double) pc.QuadPart; + } else { + struct _timeb t; + + _ftime(&t); + + return (double) t.time + (double) t.millitm * 1E-3; + } +#elif defined(__UNIX__) + struct timeval t; + + // XXX gettimeofday is different on SysV, if I remember correctly + gettimeofday(&t, 0); + + return (double) t.tv_sec + (double) t.tv_usec * 1E-6; +#endif +} // Timer::getClock + +/////////////////////////////////////////////////////////////////////////////// +// waitForTick: wait for beginning of next system clock tick; return the time. +/////////////////////////////////////////////////////////////////////////////// +double +Timer::waitForTick() { + double start; + double current; + + start = getClock(); + + // Wait for next tick: + while ((current = getClock()) == start) + ; + + // Start timing: + return current; +} // Timer::waitForTick + +/////////////////////////////////////////////////////////////////////////////// +// time: measure time (in seconds) to perform caller's operation +/////////////////////////////////////////////////////////////////////////////// +double +Timer::time() { + // Select a run time that's appropriate for our timer resolution: + double runTime = chooseRunTime(); + + // Measure successively larger batches of operations until we find + // one that's long enough to meet our runtime target: + long reps = 1; + double start; + double current; + for (;;) { + preop(); + + start = waitForTick(); + + for (long i = reps; i > 0; --i) op(); + + + postop(); + + current = getClock(); + if (current >= start + runTime + overhead) + break; + + // Try to reach runtime target in one fell swoop: + long newReps; + if (current > start + overhead) + newReps = static_cast<long> (reps * + (0.5 + runTime / (current - start - overhead))); + else + newReps = reps * 2; + if (newReps == reps) + reps += 1; + else + reps = newReps; + } + + // Subtract overhead to determine the final operation rate: + return (current - start - overhead) / reps; +} // Timer::time + +/////////////////////////////////////////////////////////////////////////////// +// measure: measure several results for performing caller's operation +/////////////////////////////////////////////////////////////////////////////// +void +Timer::measure(int count, double* low, double* avg, double* high) +{ + vector<double> m; + double sum = 0.0; + + if (!calibrated) calibrate(); + if (count < 3) count = 3; + premeasure(); + for (int i = 0; i < count; i++) { + preop(); + double t = time(); + postop(); + m.push_back(compute(t)); + } + postmeasure(); + sort(m.begin(), m.end()); + for (int j = 1; j < count - 1; j++) sum += m[j]; + *avg = sum / (count - 2); + *low = m[1]; + *high = m[count - 2]; +} // Timer::measure + +} // namespace GLEAN diff --git a/tests/glean/timer.h b/tests/glean/timer.h new file mode 100644 index 00000000..41a80c5a --- /dev/null +++ b/tests/glean/timer.h @@ -0,0 +1,74 @@ +// 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 +// 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 + +// timer.h: Simple benchmark timing utilities based on the previous timer.h +// Modified from timer.h by Rickard E. (Rik) Faith <faith@valinux.com> + +// Timer objects provide a framework for measuring the rate at which an +// operation can be performed. + +#ifndef __timer_h__ +#define __timer_h__ + +namespace GLEAN { + +class Timer { + double overhead; // Overhead (in seconds) of initial op, + // final op, and timer access. + + int calibrated; // Has calibrate been called? + + double chooseRunTime(); // Select a runtime that will reduce random + // timing error to an acceptable level. + +public: + virtual void premeasure() {}; // called in measure(), before time() + virtual void postmeasure() {}; // called in measure(), after time() + virtual void preop() {}; // before op, in each loop in time() + virtual void op() {}; // in each loop in time() + virtual void postop() {}; // after op, in each loop in time() + virtual double compute(double t) { + // modify measure()'s result -- e.g., by computing a rate + return t; + } + + void calibrate(); + double time(); + double getClock(); // Get wall-clock time, in seconds + double waitForTick(); // Wait for next clock tick; return time + void measure(int count, + double* low, double* avg, double* high); + + Timer() { overhead = 0.0; calibrated = false; } + virtual ~Timer() { /* just silence warning */ } + +}; // class Timer + +} // namespace GLEAN + +#endif // __timer_h__ diff --git a/tests/glean/tlogicop.cpp b/tests/glean/tlogicop.cpp new file mode 100644 index 00000000..06a80cfc --- /dev/null +++ b/tests/glean/tlogicop.cpp @@ -0,0 +1,573 @@ +// 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 + +// tlogicop.cpp: Test RGBA logic op functions. +// Based on Allen's blendFunc test. +// Brian Paul 10 May 2001 + +#include "tlogicop.h" +#include "rand.h" +#include "image.h" +#include <cmath> + +namespace { + +struct logicopNameMapping {GLenum op; char* name;}; +logicopNameMapping logicopNames[] = { + {GL_CLEAR, "GL_CLEAR"}, + {GL_SET, "GL_SET"}, + {GL_COPY, "GL_COPY"}, + {GL_COPY_INVERTED, "GL_COPY_INVERTED"}, + {GL_NOOP, "GL_NOOP"}, + {GL_INVERT, "GL_INVERT"}, + {GL_AND, "GL_AND"}, + {GL_NAND, "GL_NAND"}, + {GL_OR, "GL_OR"}, + {GL_NOR, "GL_NOR"}, + {GL_XOR, "GL_XOR"}, + {GL_EQUIV, "GL_EQUIV"}, + {GL_AND_REVERSE, "GL_AND_REVERSE"}, + {GL_AND_INVERTED, "GL_AND_INVERTED"}, + {GL_OR_REVERSE, "GL_OR_REVERSE"}, + {GL_OR_INVERTED, "GL_OR_INVERTED"} +}; + +char* +logicopToName(GLenum op) { + for (unsigned int i = 0; + i < sizeof(logicopNames) / sizeof(logicopNames[0]); ++i) { + if (logicopNames[i].op == op) + return logicopNames[i].name; + } + return 0; +} // logicopToName + +GLenum +nameToLogicop(string& name) { + for (unsigned int i = 0; + i < sizeof(logicopNames) / sizeof(logicopNames[0]); ++i) { + if (logicopNames[i].name == name) + return logicopNames[i].op; + } + return GL_ZERO; +} // nameToLogicop + +void +makeRGBA(GLEAN::RandomBits& rRand, + GLEAN::RandomBits& gRand, + GLEAN::RandomBits& bRand, + GLEAN::RandomBits& aRand, + GLubyte* rgba) { + rgba[0] = rRand.next() & 0xff; + rgba[1] = gRand.next() & 0xff; + rgba[2] = bRand.next() & 0xff; + rgba[3] = aRand.next() & 0xff; +} // makeRGBA + +void +drawQuad(const int x, const int y, const GLubyte* color) { + glColor4ubv(color); + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x + 1, y); + glVertex2i(x + 1, y + 1); + glVertex2i(x, y + 1); + glEnd(); +} // drawQuad + +void +applyLogicop(GLenum logicop, GLubyte dst[4], const GLubyte src[4]) { + + switch (logicop) { + case GL_CLEAR: + dst[0] = dst[1] = dst[2] = dst[3] = 0; + break; + case GL_SET: + dst[0] = dst[1] = dst[2] = dst[3] = ~0; + break; + case GL_COPY: + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + break; + case GL_COPY_INVERTED: + dst[0] = ~src[0]; + dst[1] = ~src[1]; + dst[2] = ~src[2]; + dst[3] = ~src[3]; + break; + case GL_NOOP: + break; + case GL_INVERT: + dst[0] = ~dst[0]; + dst[1] = ~dst[1]; + dst[2] = ~dst[2]; + dst[3] = ~dst[3]; + break; + case GL_AND: + dst[0] = src[0] & dst[0]; + dst[1] = src[1] & dst[1]; + dst[2] = src[2] & dst[2]; + dst[3] = src[3] & dst[3]; + break; + case GL_NAND: + dst[0] = ~(src[0] & dst[0]); + dst[1] = ~(src[1] & dst[1]); + dst[2] = ~(src[2] & dst[2]); + dst[3] = ~(src[3] & dst[3]); + break; + case GL_OR: + dst[0] = src[0] | dst[0]; + dst[1] = src[1] | dst[1]; + dst[2] = src[2] | dst[2]; + dst[3] = src[3] | dst[3]; + break; + case GL_NOR: + dst[0] = ~(src[0] | dst[0]); + dst[1] = ~(src[1] | dst[1]); + dst[2] = ~(src[2] | dst[2]); + dst[3] = ~(src[3] | dst[3]); + break; + case GL_XOR: + dst[0] = src[0] ^ dst[0]; + dst[1] = src[1] ^ dst[1]; + dst[2] = src[2] ^ dst[2]; + dst[3] = src[3] ^ dst[3]; + break; + case GL_EQUIV: + dst[0] = ~(src[0] ^ dst[0]); + dst[1] = ~(src[1] ^ dst[1]); + dst[2] = ~(src[2] ^ dst[2]); + dst[3] = ~(src[3] ^ dst[3]); + break; + case GL_AND_REVERSE: + dst[0] = src[0] & ~dst[0]; + dst[1] = src[1] & ~dst[1]; + dst[2] = src[2] & ~dst[2]; + dst[3] = src[3] & ~dst[3]; + break; + case GL_AND_INVERTED: + dst[0] = ~src[0] & dst[0]; + dst[1] = ~src[1] & dst[1]; + dst[2] = ~src[2] & dst[2]; + dst[3] = ~src[3] & dst[3]; + break; + case GL_OR_REVERSE: + dst[0] = src[0] | ~dst[0]; + dst[1] = src[1] | ~dst[1]; + dst[2] = src[2] | ~dst[2]; + dst[3] = src[3] | ~dst[3]; + break; + case GL_OR_INVERTED: + dst[0] = ~src[0] | dst[0]; + dst[1] = ~src[1] | dst[1]; + dst[2] = ~src[2] | dst[2]; + dst[3] = ~src[3] | dst[3]; + break; + default: + abort(); // implementation error + } +} // applyLogicop + +// return number of bits set differenty in a and b. +static int bitDifference(GLbyte a, GLubyte b) { + int count = 0; + for (int i = 0; i < 8; i++) { + GLubyte mask = 1 << i; + if ((a & mask) != (b & mask)) + count++; + } + return count; +} + +static GLubyte redMask, greenMask, blueMask, alphaMask; + +static void +computeError(const GLubyte aPix[4], const GLubyte ePix[4], + int &er, int &eg, int &eb, int &ea) { + if ((aPix[0] & redMask ) == (ePix[0] & redMask ) && + (aPix[1] & greenMask) == (ePix[1] & greenMask) && + (aPix[2] & blueMask ) == (ePix[2] & blueMask ) && + (aPix[3] & alphaMask) == (ePix[3] & alphaMask)) { + er = eg = eb = ea = 0; // no error at all + } + else { + // count up total bit difference + er = bitDifference(aPix[0] & redMask, ePix[0] & redMask); + eg = bitDifference(aPix[1] & greenMask, ePix[1] & greenMask); + eb = bitDifference(aPix[2] & blueMask, ePix[2] & blueMask); + ea = bitDifference(aPix[3] & alphaMask, ePix[3] & alphaMask); + } +} + +struct runResult {float readbackErrorBits; float logicopErrorBits;}; + +static runResult +runTest(GLenum logicop, + GLEAN::DrawingSurfaceConfig& config, GLEAN::Environment& env) { + using namespace GLEAN; + + runResult result; + int y; + + // Compute error bitmasks depending on color channel sizes + redMask = ((1 << config.r) - 1) << (8 - config.r); + greenMask = ((1 << config.g) - 1) << (8 - config.g); + blueMask = ((1 << config.b) - 1) << (8 - config.b); + alphaMask = ((1 << config.a) - 1) << (8 - config.a); + + glDisable(GL_DITHER); + glClear(GL_COLOR_BUFFER_BIT); + + Image dst(drawingSize, drawingSize, GL_RGBA, GL_UNSIGNED_BYTE); + RandomBits rRand(config.r, 6021023); + RandomBits gRand(config.g, 1137); + RandomBits bRand(config.b, 1138); + RandomBits aRand(config.a, 6); + + // Fill the framebuffer with random RGBA values, and place a copy + // in ``dst'': + glDisable(GL_COLOR_LOGIC_OP); + char* dRow = dst.pixels(); + for (y = 0; y < drawingSize; ++y) { + GLubyte* pix = reinterpret_cast<GLubyte*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + GLubyte rgba[4]; + makeRGBA(rRand, gRand, bRand, aRand, rgba); + drawQuad(x + 1, y + 1, rgba); + pix[0] = rgba[0]; + pix[1] = rgba[1]; + pix[2] = rgba[2]; + pix[3] = rgba[3]; + pix += 4; + } + dRow += dst.rowSizeInBytes(); + } + + // Read back the contents of the framebuffer, and measure any + // difference from what was actually written. We can't tell + // whether errors occurred when writing or when reading back, + // but at least we can report anything unusual. + Image fbDst(drawingSize, drawingSize, GL_RGBA, GL_UNSIGNED_BYTE); + fbDst.read(1, 1); + Image::Registration reg1(fbDst.reg(dst)); + result.readbackErrorBits = + max(ErrorBits(reg1.stats[0].max(), config.r), + max(ErrorBits(reg1.stats[1].max(), config.g), + max(ErrorBits(reg1.stats[2].max(), config.b), + ErrorBits(reg1.stats[3].max(), config.a)))); + + // Now generate random source pixels and apply the logicop + // operation to both the framebuffer and a copy in the image + // ``expected''. Save the source pixels in the image ``src'' + // so we can diagnose any problems we find later. + Image expected(fbDst); + Image src(drawingSize, drawingSize, GL_RGBA, GL_UNSIGNED_BYTE); + + glLogicOp(logicop); + glEnable(GL_COLOR_LOGIC_OP); + + dRow = expected.pixels(); + char* sRow = src.pixels(); + for (y = 0; y < drawingSize; ++y) { + GLubyte* pix = reinterpret_cast<GLubyte*>(dRow); + GLubyte* sPix = reinterpret_cast<GLubyte*>(sRow); + for (int x = 0; x < drawingSize; ++x) { + GLubyte rgba[4]; + makeRGBA(rRand, gRand, bRand, aRand, rgba); + sPix[0] = rgba[0]; + sPix[1] = rgba[1]; + sPix[2] = rgba[2]; + sPix[3] = rgba[3]; + drawQuad(x + 1, y + 1, rgba); + applyLogicop(logicop, pix, rgba); + pix += 4; + sPix += 4; + } + dRow += expected.rowSizeInBytes(); + sRow += src.rowSizeInBytes(); + } + + // Read the generated image (``actual'') and compare it to the + // computed image (``expected'') to see if any pixels are + // outside the expected tolerance range (one LSB). If so, + // report the first such pixel, along with the source and + // destination values that generated it. Keep track of the + // maximum error encountered. + Image actual(drawingSize, drawingSize, GL_RGBA, GL_UNSIGNED_BYTE); + actual.read(1, 1); + result.logicopErrorBits = 0.0; + sRow = actual.pixels(); + dRow = expected.pixels(); + for (y = 0; y < drawingSize; ++y) { + GLubyte* aPix = reinterpret_cast<GLubyte*>(sRow); + GLubyte* ePix = reinterpret_cast<GLubyte*>(dRow); + for (int x = 0; x < drawingSize; ++x) { + int rErr, gErr, bErr, aErr; + computeError(aPix, ePix, rErr, gErr, bErr, aErr); + result.logicopErrorBits = rErr + gErr + bErr + aErr; + + if (result.logicopErrorBits > 1.0) { + if (env.options.verbosity) { +GLubyte* sPix = reinterpret_cast<GLubyte*>(src.pixels() + + y * src.rowSizeInBytes() + x * 4 * sizeof(GLubyte)); +GLubyte* dPix = reinterpret_cast<GLubyte*>(dst.pixels() + + y * dst.rowSizeInBytes() + x * 4 * sizeof(GLubyte)); +env.log << '\n' +<< "First failing pixel is at row " << y << " column " << x << "\n" +<< "Actual values are (" << (int) aPix[0] << ", " << (int) aPix[1] << ", " + << (int) aPix[2] << ", " << (int) aPix[3] << ")\n" +<< "Expected values are (" << (int) ePix[0] << ", " << (int) ePix[1] << ", " + << (int) ePix[2] << ", " << (int) ePix[3] << ")\n" +<< "Errors (number of bad bits) are (" << rErr << ", " << gErr << ", " + << bErr << ", " << aErr << ")\n" +<< "Source values are (" << (int) sPix[0] << ", " << (int) sPix[1] << ", " + << (int) sPix[2] << ", " << (int) sPix[3] << ")\n" +<< "Destination values are (" << (int) dPix[0] << ", " << (int) dPix[1] << ", " + << (int) dPix[2] << ", " << (int) dPix[3] << ")\n"; + } + return result; + } + aPix += 4; + ePix += 4; + } + sRow += actual.rowSizeInBytes(); + dRow += expected.rowSizeInBytes(); + } + + return result; +} // runOneSet + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +LogicopFuncTest::runOne(LogicopFuncResult& r, Window& w) { + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + + static GLenum logicopModes[] = { + GL_CLEAR, + GL_SET, + GL_COPY, + GL_COPY_INVERTED, + GL_NOOP, + GL_INVERT, + GL_AND, + GL_NAND, + GL_OR, + GL_NOR, + GL_XOR, + GL_EQUIV, + GL_AND_REVERSE, + GL_AND_INVERTED, + GL_OR_REVERSE, + GL_OR_INVERTED + }; + + bool allPassed = true; + for (unsigned int op = 0; + op < sizeof(logicopModes)/sizeof(logicopModes[0]); ++op) { + + LogicopFuncResult::PartialResult p; + p.logicop = logicopModes[op]; + + runResult res = runTest(p.logicop, *(r.config), *env); + w.swap(); + + p.rbErr = res.readbackErrorBits; + p.opErr = res.logicopErrorBits; + r.results.push_back(p); + + if (p.rbErr > 1.0 || p.opErr > 1.0) { + env->log << name << ": FAIL " + << r.config->conciseDescription()<< '\n' + << "\tlogicop mode = " + << logicopToName(p.logicop) + << "\n\tReadback had " << p.rbErr + << " bits in error; logicop had " + << p.opErr << " bits in error.\n"; + allPassed = false; + } + } + + r.pass = allPassed; +} // LogicopFuncTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +LogicopFuncTest::logOne(LogicopFuncResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +LogicopFuncTest::compareOne(LogicopFuncResult& oldR, LogicopFuncResult& newR) { + BasicStats readbackStats; + BasicStats logicopStats; + + vector<LogicopFuncResult::PartialResult>::const_iterator np; + vector<LogicopFuncResult::PartialResult>::const_iterator op; + + 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->logicop == op->logicop) { + readbackStats.sample(np->rbErr - op->rbErr); + logicopStats.sample(np->opErr - op->opErr); + } + } + + if (readbackStats.n() == static_cast<int>(newR.results.size()) + && newR.results.size() == oldR.results.size() + && readbackStats.mean() == 0.0 && logicopStats.mean() == 0.0) { + if (env->options.verbosity) + env->log << name << ": SAME " + << newR.config->conciseDescription() << '\n'; + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n'; + + if (readbackStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate readback.\n"; + else if (readbackStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate readback.\n"; + if (logicopStats.mean() < 0.0) + env->log << '\t' << env->options.db2Name + << " appears to have more accurate logicoping.\n"; + else if (logicopStats.mean() > 0.0) + env->log << '\t' << env->options.db1Name + << " appears to have more accurate logicoping.\n"; + if (readbackStats.n() != static_cast<int>(newR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db2Name + << " have no matching test in " + << env->options.db1Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np) { + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->logicop == op->logicop) + break; + if (op == oldR.results.end()) + env->log << "\t\t" + << logicopToName(np->logicop) + << '\n'; + } + } + if (readbackStats.n() != static_cast<int>(oldR.results.size())){ + env->log << "\tThe following cases in " + << env->options.db1Name + << " have no matching test in " + << env->options.db2Name + << ":\n"; + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) { + for (np = newR.results.begin(); + np != newR.results.end(); ++np) + if (op->logicop == np->logicop) + break; + if (np == newR.results.end()) + env->log << "\t\t" + << logicopToName(op->logicop) + << '\n'; + } + } + if (env->options.verbosity) { + env->log << "\tThe following cases appear in both " + << env->options.db1Name + << " and " + << env->options.db2Name + << ":\n"; + for (np = newR.results.begin(); + np != newR.results.end(); ++np){ + for (op = oldR.results.begin(); + op != oldR.results.end(); ++op) + if (np->logicop == op->logicop) + break; + if (op != oldR.results.end()) + env->log << "\t\t" + << logicopToName(np->logicop) + << '\n'; + } + } + } +} // LogicopFuncTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// Result I/O functions: +/////////////////////////////////////////////////////////////////////////////// +void +LogicopFuncResult::putresults(ostream& s) const { + s << results.size() << '\n'; + for (vector<PartialResult>::const_iterator p = results.begin(); + p != results.end(); ++p) { + s << logicopToName(p->logicop) << ' ' + << p->rbErr << ' ' << p->opErr << '\n'; + } +} // LogicopFuncResult::put + +bool +LogicopFuncResult::getresults(istream& s) { + int n; + s >> n; + for (int i = 0; i < n; ++i) { + PartialResult p; + string src; + s >> src >> p.rbErr >> p.opErr; + p.logicop = nameToLogicop(src); + results.push_back(p); + } + + return s.good(); +} // LogicopFuncResult::get + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +LogicopFuncTest logicopFuncTest("logicOp", "window, rgb", + + "This test checks the logicop functions in RGBA mode.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tlogicop.h b/tests/glean/tlogicop.h new file mode 100644 index 00000000..0e670b0a --- /dev/null +++ b/tests/glean/tlogicop.h @@ -0,0 +1,66 @@ +// 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 + +// tlogicop.h: Test RGBA logic op functions. +// Based on Allen's blendFunc test. +// Brian Paul 10 May 2001 + +#ifndef __tlogicop_h__ +#define __tlogicop_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 64 +#define windowSize (drawingSize + 2) + +class LogicopFuncResult: public BaseResult { +public: + bool pass; // not written to log file + + struct PartialResult { + GLenum logicop; // The logic op + float rbErr; // Max readback error, in bits. + float opErr; // Max logicop error, in bits. + }; + vector<PartialResult> results; + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + +class LogicopFuncTest: public BaseTest<LogicopFuncResult> { +public: + GLEAN_CLASS_WH(LogicopFuncTest, LogicopFuncResult, + windowSize, windowSize); +}; // class LogicopFuncTest + +} // namespace GLEAN + +#endif // __tlogicop_h__ diff --git a/tests/glean/tmaskedclear.cpp b/tests/glean/tmaskedclear.cpp new file mode 100644 index 00000000..b8fd6f9e --- /dev/null +++ b/tests/glean/tmaskedclear.cpp @@ -0,0 +1,266 @@ +// 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 + +// tmaskedclear.cpp: Test color/index masking with glClear. + +#include "tmaskedclear.h" +#include "rand.h" +#include "image.h" + +namespace GLEAN { + +void +MaskedClearTest::failRGB(BasicResult &r, GLint chan, GLfloat expected, + GLfloat actual, GLint buffer) +{ + static const char *chanNames[] = { "Red", "Green", "Blue", "Alpha" }; + static const char *bufferNames[] = { "GL_FRONT", "GL_BACK" }; + GLboolean mask[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, mask); + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\t" << chanNames[chan] << " is " << actual + << ", expected " << expected + << " in " << bufferNames[buffer] << " buffer\n"; + env->log << "\tGL_COLOR_WRITEMASK = (" + << (mask[0] ? "GL_TRUE" : "GL_FALSE") << ", " + << (mask[1] ? "GL_TRUE" : "GL_FALSE") << ", " + << (mask[2] ? "GL_TRUE" : "GL_FALSE") << ", " + << (mask[3] ? "GL_TRUE" : "GL_FALSE") << ")\n"; +} + +void +MaskedClearTest::failCI(BasicResult& r, GLuint expected, GLuint actual, + GLint buffer) +{ + static const char *bufferNames[] = { "GL_FRONT", "GL_BACK" }; + GLint mask; + glGetIntegerv(GL_INDEX_WRITEMASK, &mask); + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\tcolor index is " << actual + << ", expected " << expected + << " in " << bufferNames[buffer] << " buffer\n"; + env->log << "\tGL_INDEX_WRITEMASK = " << mask << "\n"; +} + +void +MaskedClearTest::failZ(BasicResult& r, GLfloat expected, GLfloat actual) +{ + GLboolean mask; + glGetBooleanv(GL_DEPTH_WRITEMASK, &mask); + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\tdepth buffer value is " << actual + << ", expected " << expected << "\n"; + env->log << "\tGL_DEPTH_WRITEMASK = " + << (mask ? "GL_TRUE" : "GL_FALSE") << "\n"; +} + +void +MaskedClearTest::failStencil(BasicResult& r, GLuint expected, GLuint actual) +{ + GLint mask; + glGetIntegerv(GL_STENCIL_WRITEMASK, &mask); + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\tstencil buffer value is " << actual + << ", expected " << expected << "\n"; + env->log << "\tGL_STENCIL_WRITEMASK = " << mask << "\n"; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +MaskedClearTest::runOne(BasicResult& r, Window&) { + + bool passed = true; + + // GL init, just to be safe + glDisable(GL_SCISSOR_TEST); + + // only test front/back-left buffers, quad-buffer stereo in the future + const GLint numBuffers = r.config->db ? 2 : 1; + for (GLint buffer = 0; buffer < numBuffers && passed; buffer++) { + + if (buffer == 0) { + glReadBuffer(GL_FRONT); + glDrawBuffer(GL_FRONT); + } else { + glReadBuffer(GL_BACK); + glDrawBuffer(GL_BACK); + } + + if (r.config->canRGBA) { + const GLint numChannels = (r.config->a > 0) ? 4 : 3; + for (GLint chan = 0; + chan < numChannels && passed; chan++) { + // clear to black + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + // select one channel to "clear" to 1.0 + glColorMask(chan == 0, chan == 1, + chan == 2, chan == 3); + + // try to clear surface to white + glClearColor(1.0, 1.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + // read 1x1 image at (x,y)=(4,4) + GLfloat pixel[4]; + glReadPixels(4, 4, 1, 1, + GL_RGBA, GL_FLOAT, pixel); + + // test results + for (GLint comp = 0; + comp < numChannels && passed; comp++) { + if (comp == chan) { + // component should be 1.0 + if (pixel[comp] < 0.5) { + passed = false; + failRGB(r, comp, 1.0, + pixel[comp], buffer); + } + } else { + // component should be 0.0 + if (pixel[comp] > 0.5) { + passed = false; + failRGB(r, comp, 0.0, + pixel[comp], buffer); + } + } + } + } + } + else { + const GLint indexBits = r.config->bufSize; + // We just run <indexBits> tests rather than 2^indexBits + for (GLint bit = 0; bit < indexBits && passed; bit++) { + // clear to 0 + glIndexMask(~0); + glClearIndex(0); + glClear(GL_COLOR_BUFFER_BIT); + + // select one bit to "clear" to 1 + glIndexMask(1 << bit); + + // try to clear surface to ~0 + glClearIndex(~0); + glClear(GL_COLOR_BUFFER_BIT); + + // read 1x1 image at (x,y)=(4,4) + GLuint pixel; + glReadPixels(4, 4, 1, 1, + GL_COLOR_INDEX, GL_UNSIGNED_INT, &pixel); + + // test results + if (pixel != (1U << bit)) { + passed = false; + failCI(r, 1 << bit, pixel, buffer); + } + } + } + } + + if (passed && r.config->z > 0) { + // clear depth buffer to zero + glDepthMask(GL_TRUE); + glClearDepth(0.0); + glClear(GL_DEPTH_BUFFER_BIT); + + // disable Z writes, try to clear to one + glDepthMask(GL_FALSE); + glClearDepth(1.0); + glClear(GL_DEPTH_BUFFER_BIT); + + // read 1x1 image at (x,y)=(4,4); + GLfloat depth; + glReadPixels(4, 4, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); + + // test result + if (depth != 0.0) { + passed = false; + failZ(r, 0.0, depth); + } + } + + if (passed && r.config->s > 0) { + const GLint stencilBits = r.config->s; + // We just run <stencilBits> tests rather than 2^stencilBits + for (GLint bit = 0; bit < stencilBits && passed; bit++) { + // clear to 0 + glStencilMask(~0); + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + + // select one bit to "clear" to 1 + glStencilMask(1 << bit); + + // try to clear stencil buffer to ~0 + glClearStencil(~0); + glClear(GL_STENCIL_BUFFER_BIT); + + // read 1x1 image at (x,y)=(4,4) + GLuint stencil; + glReadPixels(4, 4, 1, 1, + GL_STENCIL_INDEX, GL_UNSIGNED_INT, &stencil); + + // test results + if (stencil != (1U << bit)) { + passed = false; + failStencil(r, 1 << bit, stencil); + } + } + } + r.pass = passed; +} // MaskedClearTest::runOne + + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +MaskedClearTest::logOne(BasicResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} // MaskedClearTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +MaskedClearTest maskedClearTest("maskedClear", "window", + "This test checks that glClear works correctly with glColorMask,\n" + "glIndexMask, glDepthMask and glStencilMask.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tmaskedclear.h b/tests/glean/tmaskedclear.h new file mode 100644 index 00000000..d03fd155 --- /dev/null +++ b/tests/glean/tmaskedclear.h @@ -0,0 +1,62 @@ +// 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 + +// tmaskedclear.h: Test clearing of colorbuffers with glColorMask or +// glIndexMask. +// Author: Brian Paul (brianp@valinux.com) September 2000 + + +#ifndef __tmaskedclear_h__ +#define __tmaskedclear_h__ + +#include "tbasic.h" + +namespace GLEAN { + +class MaskedClearTest: public BasicTest { + public: + MaskedClearTest(const char* testName, const char* filter, + const char* description): + BasicTest(testName, filter, description) { + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + void failRGB(BasicResult& r, GLint chan, GLfloat expected, + GLfloat actual, GLint buffer); + void failCI(BasicResult& r, GLuint expected, GLuint actual, + GLint buffer); + void failZ(BasicResult& r, GLfloat expected, GLfloat actual); + void failStencil(BasicResult& r, GLuint expected, GLuint actual); +}; // class MaskedClearTest + +} // namespace GLEAN + +#endif // __tmaskedclear_h__ diff --git a/tests/glean/tmultitest.cpp b/tests/glean/tmultitest.cpp new file mode 100644 index 00000000..42cac038 --- /dev/null +++ b/tests/glean/tmultitest.cpp @@ -0,0 +1,109 @@ +// 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 + +#include "tmultitest.h" + +namespace GLEAN { + + +MultiTestResult::MultiTestResult() +{ + numPassed = numFailed = 0; + pass = true; +} + + +void +MultiTestResult::putresults(ostream &s) const +{ + s << pass; + s << numPassed; + s << numFailed; +} + + +bool +MultiTestResult::getresults(istream &s) +{ + s >> pass; + s >> numPassed; + s >> numFailed; + return s.good(); +} + + +// VERY IMPORTANT: this function _must_ be defined here, even though +// it's never used. Otherwise, you'll get linker errors like this: +// tmultitest.h:83: undefined reference to `vtable for GLEAN::MultiTest' +void +MultiTest::runOne(MultiTestResult &r, Window &) +{ + r.numPassed = r.numFailed = 0; + r.pass = true; +} + + +void +MultiTest::logOne(MultiTestResult &r) +{ + logPassFail(r); + logConcise(r); + env->log << "\t" + << r.numPassed << " tests passed, " + << r.numFailed << " tests failed.\n"; +} + + +void +MultiTest::compareOne(MultiTestResult &oldR, + MultiTestResult &newR) +{ + if (oldR.numPassed != newR.numPassed || + oldR.numFailed != newR.numFailed) { + env->log << "Different results: passed: " + << oldR.numPassed + << " vs. " + << newR.numPassed + << " failed: " + << oldR.numFailed + << " vs. " + << newR.numFailed + << "\n"; + } +} + + +#if 0 +MultiTest multiTest("multi", "window", + "", + "Base class for multi pass/fail tests\n" + ); +#endif + + +} // namespace GLEAN diff --git a/tests/glean/tmultitest.h b/tests/glean/tmultitest.h new file mode 100644 index 00000000..4ecad043 --- /dev/null +++ b/tests/glean/tmultitest.h @@ -0,0 +1,77 @@ +// 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 + +// tmultitest.h - base class for Glean tests that produce a number of +// sub-pass/fail results. +// Brian Paul September 2006 + + +#ifndef __tmultitest_h__ +#define __tmultitest_h__ + +#include "tbase.h" + +namespace GLEAN { + +#ifndef windowSize +#define windowSize 100 +#endif + +#ifndef windowWidth +#define windowWidth windowSize +#endif + +#ifndef windowHeight +#define windowHeight windowSize +#endif + + +class MultiTestResult: public BaseResult +{ +public: + MultiTestResult(); + + bool pass; + int numPassed, numFailed; + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + + +class MultiTest: public BaseTest<MultiTestResult> +{ +public: + GLEAN_CLASS_WH(MultiTest, MultiTestResult, + windowWidth, windowHeight); +}; + + +} // namespace GLEAN + +#endif // __tmultitest_h__ diff --git a/tests/glean/torthpos.cpp b/tests/glean/torthpos.cpp new file mode 100644 index 00000000..a50cfeab --- /dev/null +++ b/tests/glean/torthpos.cpp @@ -0,0 +1,1159 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// torthopos.cpp: Test positioning of primitives in orthographic projection. +// Some applications use OpenGL extensively for 2D rendering: portable +// GUI toolkits, heads-up display generators, etc. These apps require +// primitives to be drawn with reliable position and size in orthographic +// projections. There are some potential pitfalls; for a good discussion, +// see the OpenGL Programming Guide (the Red Book). In the second edition, +// see the OpenGL Correctness Tips on page 601. + +#include "torthpos.h" +#include "image.h" +#include "rand.h" +#include "geomutil.h" + +#if 0 +#ifdef __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "torthpos.h" +#include "misc.h" +#endif + + +namespace { + +void +logStats1(const char* title, GLEAN::OPResult& r, + GLEAN::Environment* env) { + env->log << '\t' << title << ": "; + if (r.hasGaps || r.hasOverlaps || r.hasBadEdges) { + env->log << (r.hasGaps? " Gaps.": "") + << (r.hasOverlaps? " Overlaps.": "") + << (r.hasBadEdges? " Incorrect edges.": "") + << '\n'; + } else { + env->log << " No gaps, overlaps, or incorrect edges.\n"; + } +} // logStats1 + +void +diffHeader(bool& same, const string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::Environment* env) { + if (same) { + same = false; + env->log << name << ": DIFF " + << config->conciseDescription() << '\n'; + } +} // diffHeader + +void +failHeader(bool& pass, const string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::Environment* env) { + if (pass) { + pass = false; + env->log << name << ": FAIL " + << config->conciseDescription() << '\n'; + } +} // failHeader + +void +doComparison(const GLEAN::OPResult& oldR, + const GLEAN::OPResult& newR, + GLEAN::DrawingSurfaceConfig* config, + bool& same, + const string& name, + GLEAN::Environment* env, + const char* title) { + if (newR.hasGaps != oldR.hasGaps) { + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name + << ' ' << title << ' ' + << (oldR.hasGaps? " has": " does not have") + << " gaps\n"; + env->log << '\t' << env->options.db2Name + << ' ' << title << ' ' + << (newR.hasGaps? " has": " does not have") + << " gaps\n"; + } + if (newR.hasOverlaps != oldR.hasOverlaps) { + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name + << ' ' << title << ' ' + << (oldR.hasOverlaps? " has": " does not have") + << " overlaps\n"; + env->log << '\t' << env->options.db2Name + << ' ' << title << ' ' + << (newR.hasOverlaps? " has": " does not have") + << " overlaps\n"; + } + if (newR.hasBadEdges != oldR.hasBadEdges) { + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name + << ' ' << title << ' ' + << (oldR.hasBadEdges? " has": " does not have") + << " incorrect edges\n"; + env->log << '\t' << env->options.db2Name + << ' ' << title << ' ' + << (newR.hasBadEdges? " has": " does not have") + << " incorrect edges\n"; + } +} // doComparison + +GLubyte +logicalSum(GLubyte* start, int stride, int count) { + GLubyte* p = start; + GLubyte sum = 0; + for (int i = 0; i < count; ++i) { + sum |= p[0]; + sum |= p[1]; + sum |= p[2]; + p += stride; + } + return sum; +} + +void +verifyOrthPos(GLEAN::Window& w, bool& passed, string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::OPResult& res, + GLEAN::Environment* env, const char* title) { + + GLEAN::Image img(windowSize, windowSize, GL_RGB, GL_UNSIGNED_BYTE); + img.read(0, 0); + w.swap(); // give the user something to watch + + // All of the tests in this group are constructed so that the + // "correct" image covers a square of exactly drawingSize by + // drawingSize pixels, embedded in a window that's two pixels + // larger in both dimensions. The border consists of pixels + // with all components set to zero. Within the image, all + // pixels should be either red (only the red component is + // nonzero) or green (only the green component is nonzero). If + // any pixels with all zero components are found, that indicates + // the presence of gaps. If any pixels with both red and green + // nonzero components are found, that indicates the presence of + // overlaps. + + res.hasGaps = false; + res.hasOverlaps = false; + res.hasBadEdges = false; + + GLubyte* row0 = reinterpret_cast<GLubyte*>(img.pixels()); + GLubyte* row1 = row0 + img.rowSizeInBytes(); + GLubyte* rowLast = row0 + (windowSize - 1) * img.rowSizeInBytes(); + GLubyte* rowNextLast = rowLast - img.rowSizeInBytes(); + + // Check the bottom horizontal edge; it must be all zero. + if (logicalSum(row0, 3, windowSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": bottom border (at Y==0) was touched\n"; + res.hasBadEdges = true; + } + // Repeat the process for the top horizontal edge. + if (logicalSum(rowLast, 3, windowSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": top border (at Y==" << windowSize - 1 + << ") was touched\n"; + res.hasBadEdges = true; + } + // Check the second row; there must be at least one nonzero + // pixel in the "drawn" region (excluding the first and last + // column). + if (!logicalSum(row1 + 3/*skip first pixel's RGB*/, 3, drawingSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": first row (at Y==1) was not drawn\n"; + res.hasBadEdges = true; + } + // Repeat the process for the last row. + if (!logicalSum(rowNextLast + 3, 3, drawingSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": last row (at Y==" << windowSize - 2 + << ") was not drawn\n"; + res.hasBadEdges = true; + } + + // Check the left-hand vertical edge; it must be all zero. + if (logicalSum(row0, img.rowSizeInBytes(), windowSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": left border (at X==0) was touched\n"; + res.hasBadEdges = true; + } + // Repeat for the right-hand vertical edge. + if (logicalSum(row0 + 3 * (windowSize - 1), img.rowSizeInBytes(), + windowSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": right border (at X==" << windowSize - 1 + << ") was touched\n"; + res.hasBadEdges = true; + } + // Check the left-hand column; something must be nonzero. + if (!logicalSum(row1 + 3, img.rowSizeInBytes(), drawingSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": first column (at X==1) was not drawn\n"; + res.hasBadEdges = true; + } + // And repeat for the right-hand column: + if (!logicalSum(row1 + 3 * (drawingSize - 1), img.rowSizeInBytes(), + drawingSize)) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": last column (at X==" << windowSize - 2 + << ") was not drawn\n"; + res.hasBadEdges = true; + } + + // Scan the drawing area. Anytime we find a pixel with all zero + // components, that's a gap. Anytime we find a pixel with both + // red and green components nonzero, that's an overlap. + GLubyte* row = row1 + 3; // lower-left pixel in drawing area + for (int i = 0; i < drawingSize; ++i) { + GLubyte* p = row; + for (int j = 0; j < drawingSize; ++j) { + if (!p[0] && !p[1] && !p[2]) { + if (!res.hasGaps) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": found first gap at X==" + << j + 1 << ", Y==" << i + 1 + << '\n'; + res.hasGaps = true; + } + } + if (p[0] && p[1]) { + if (!res.hasOverlaps) { + failHeader(passed, name, config, env); + env->log << '\t' << title + << ": found first overlap at " + << "X==" << j + 1 << ", Y==" + << i + 1 << '\n'; + res.hasOverlaps = true; + } + } + p += 3; + } + row += img.rowSizeInBytes(); + } + +} // verifyOrthPos + +void +subdivideRects(int minX, int maxX, int minY, int maxY, + GLEAN::RandomDouble& rand, bool splitHoriz, bool drawInRed) { + // Basically we're just splitting the input rectangle + // recursively. At each step we alternate between splitting + // horizontally (dividing along Y) or vertically (along X). We + // also toggle colors (between red and green) at various times, + // in order to give us some adjacent edges of different colors + // that we can check for overlaps. Recursion bottoms out when + // the axis of interest drops below 30 pixels in length. + // + int min = splitHoriz? minY: minX; + int max = splitHoriz? maxY: maxX; + if (min + 30 > max) { + glColor4f(drawInRed? 1.0: 0.0, drawInRed? 0.0: 1.0, + 0.0, 0.5); + glBegin(GL_QUADS); + glVertex2i(minX, minY); + glVertex2i(maxX, minY); + glVertex2i(maxX, maxY); + glVertex2i(minX, maxY); + glEnd(); + return; + } + + int split = min + static_cast<int>((max - min) * rand.next()); + if (splitHoriz) { + subdivideRects(minX, maxX, minY, split, + rand, !splitHoriz, drawInRed); + subdivideRects(minX, maxX, split, maxY, + rand, !splitHoriz, !drawInRed); + } else { + subdivideRects(minX, split, minY, maxY, + rand, !splitHoriz, drawInRed); + subdivideRects(split, maxX, minY, maxY, + rand, !splitHoriz, !drawInRed); + } +} + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosPoints::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode points + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + { + glBegin(GL_POINTS); + for (int x = 1; x <= drawingSize; ++x) + for (int y = 1; y <= drawingSize; ++y) { + if ((x ^ y) & 1) + glColor4f(0.0, 1.0, 0.0, 0.5); + else + glColor4f(1.0, 0.0, 0.0, 0.5); + glVertex2i(x, y); + } + glEnd(); + } + verifyOrthPos(w, passed, name, r.config, r, env, "Immediate-mode points"); + r.pass = passed; +} // OrthoPosPoints::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosPoints::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosPoints::logOne + +void +OrthoPosPoints::logStats(OPResult& r) { + logStats1("Immediate-mode points", r, env); +} // OrthoPosPoints::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosPoints::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode points"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosPoints::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosPoints orthoPosPointsTest("orthoPosPoints", + "window, rgb > 1, z, fast", + + "This test checks the positioning of unit-sized points under\n" + "orthographic projection. (This is important for apps that\n" + "want to use OpenGL for precise 2D drawing.) It fills in an\n" + "entire rectangle one pixel at a time, drawing adjacent pixels\n" + "with different colors and with blending enabled. If there are\n" + "gaps (pixels that are the background color, and thus haven't\n" + "been filled), overlaps (pixels that show a blend of more than\n" + "one color), or improper edges (pixels around the edge of the\n" + "rectangle that haven't been filled, or pixels just outside the\n" + "edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the point\n" + "rasterization process may not be filling the correct pixels;\n" + "this can cause gaps, overlaps, or bad edges.\n" + + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +OrthoPosVLines::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode vertical lines + // Note that these are a little tricky, because of + // OpenGL's "diamond-exit rule" line semantics. In + // this case, we can safely treat them as half-open + // lines, where the terminal point isn't drawn. Thus + // we need to specify a terminal coordinate one pixel + // beyond the last pixel we wish to be drawn. + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + { + glBegin(GL_LINES); + for (int x = 1; x <= drawingSize; ++x) { + if (x & 1) + glColor4f(0.0, 1.0, 0.0, 0.5); + else + glColor4f(1.0, 0.0, 0.0, 0.5); + glVertex2i(x, 1); + glVertex2i(x, drawingSize + 1); + } + glEnd(); + } + verifyOrthPos(w, passed, name, r.config, r, env, + "Immediate-mode vertical lines"); + r.pass = passed; +} // OrthoPosVLines::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosVLines::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosVLines::logOne + +void +OrthoPosVLines::logStats(OPResult& r) { + logStats1("Immediate-mode vertical lines", r, env); +} // OrthoPosVLines::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosVLines::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode vertical lines"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosVLines::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosVLines orthoPosVLinesTest("orthoPosVLines", + "window, rgb > 1, z, fast", + + "This test checks the positioning of unit-width vertical lines\n" + "under orthographic projection. (This is important for apps\n" + "that want to use OpenGL for precise 2D drawing.) It fills in\n" + "an entire rectangle with a collection of vertical lines, drawing\n" + "adjacent lines with different colors and with blending enabled.\n" + "If there are gaps (pixels that are the background color, and\n" + "thus haven't been filled), overlaps (pixels that show a blend\n" + "of more than one color), or improper edges (pixels around the\n" + "edge of the rectangle that haven't been filled, or pixels just\n" + "outside the edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the\n" + "line rasterization process may not be filling the correct\n" + "pixels; this can cause gaps, overlaps, or bad edges. Fourth,\n" + "the OpenGL implementation may not handle the diamond-exit rule\n" + "(section 3.4.1 in version 1.2.1 of the OpenGL spec) correctly;\n" + "this should cause a bad border or bad top edge.\n" + "\n" + "It can be argued that this test is more strict that the OpenGL\n" + "specification requires. However, it is necessary to be this\n" + "strict in order for the results to be useful to app developers\n" + "using OpenGL for 2D drawing.\n" + + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +OrthoPosHLines::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode horizontal lines + // See the comments in the vertical line case above. + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + { + glBegin(GL_LINES); + for (int y = 1; y <= drawingSize; ++y) { + if (y & 1) + glColor4f(0.0, 1.0, 0.0, 0.5); + else + glColor4f(1.0, 0.0, 0.0, 0.5); + glVertex2i(1, y); + glVertex2i(drawingSize + 1, y); + } + glEnd(); + } + verifyOrthPos(w, passed, name, r.config, r, env, + "Immediate-mode horizontal lines"); + r.pass = passed; +} // OrthoPosHLines::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosHLines::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosHLines::logOne + +void +OrthoPosHLines::logStats(OPResult& r) { + logStats1("Immediate-mode horizontal lines", r, env); +} // OrthoPosHLines::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosHLines::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode horizontal lines"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosHLines::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosHLines orthoPosHLinesTest("orthoPosHLines", + "window, rgb > 1, z, fast", + + "This test checks the positioning of unit-width horizontal lines\n" + "under orthographic projection. (This is important for apps\n" + "that want to use OpenGL for precise 2D drawing.) It fills in\n" + "an entire rectangle with a stack of horizontal lines, drawing\n" + "adjacent lines with different colors and with blending enabled.\n" + "If there are gaps (pixels that are the background color, and\n" + "thus haven't been filled), overlaps (pixels that show a blend\n" + "of more than one color), or improper edges (pixels around the\n" + "edge of the rectangle that haven't been filled, or pixels just\n" + "outside the edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the\n" + "line rasterization process may not be filling the correct\n" + "pixels; this can cause gaps, overlaps, or bad edges. Fourth,\n" + "the OpenGL implementation may not handle the diamond-exit rule\n" + "(section 3.4.1 in version 1.2.1 of the OpenGL spec) correctly;\n" + "this should cause a bad border or bad right edge.\n" + "\n" + "It can be argued that this test is more strict that the OpenGL\n" + "specification requires. However, it is necessary to be this\n" + "strict in order for the results to be useful to app developers\n" + "using OpenGL for 2D drawing.\n" + + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +OrthoPosTinyQuads::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode 1x1-pixel quads + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + { + glBegin(GL_QUADS); + for (int x = 1; x <= drawingSize; ++x) + for (int y = 1; y <= drawingSize; ++y) { + if ((x ^ y) & 1) + glColor4f(0.0, 1.0, 0.0, 0.5); + else + glColor4f(1.0, 0.0, 0.0, 0.5); + glVertex2i(x, y); + glVertex2i(x + 1, y); + glVertex2i(x + 1, y + 1); + glVertex2i(x, y + 1); + } + glEnd(); + } + verifyOrthPos(w, passed, name, r.config, r, env, + "Immediate-mode 1x1 quads"); + r.pass = passed; +} // OrthoPosTinyQuads::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosTinyQuads::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosTinyQuads::logOne + +void +OrthoPosTinyQuads::logStats(OPResult& r) { + logStats1("Immediate-mode 1x1 quads", r, env); +} // OrthoPosTinyQuads::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosTinyQuads::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode 1x1 quads"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosTinyQuads::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosTinyQuads orthoPosTinyQuadsTest("orthoPosTinyQuads", + "window, rgb > 1, z, fast", + + "This test checks the positioning of 1x1-pixel quadrilaterals\n" + "under orthographic projection. (This is important for apps\n" + "that want to use OpenGL for precise 2D drawing.) It fills in\n" + "an entire rectangle with an array of quadrilaterals, drawing\n" + "adjacent quads with different colors and with blending enabled.\n" + "If there are gaps (pixels that are the background color, and\n" + "thus haven't been filled), overlaps (pixels that show a blend\n" + "of more than one color), or improper edges (pixels around the\n" + "edge of the rectangle that haven't been filled, or pixels just\n" + "outside the edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the\n" + "quad rasterization process may not be filling the correct\n" + "pixels; this can cause gaps, overlaps, or bad edges.\n" + + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +OrthoPosRandRects::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode random axis-aligned rectangles + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + RandomDouble rand(1618); + subdivideRects(1, drawingSize + 1, 1, drawingSize + 1, + rand, true, true); + verifyOrthPos(w, passed, name, r.config, r, env, + "Immediate-mode axis-aligned rectangles"); + r.pass = passed; +} // OrthoPosRandRects::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosRandRects::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosRandRects::logOne + +void +OrthoPosRandRects::logStats(OPResult& r) { + logStats1("Immediate-mode axis-aligned rectangles", r, env); +} // OrthoPosRandRects::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosRandRects::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode axis-aligned rectangles"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosRandRects::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosRandRects orthoPosRandRectsTest("orthoPosRandRects", + "window, rgb > 1, z, fast", + + "This test checks the positioning of axis-aligned rectangles\n" + "under orthographic projection. (This is important for apps\n" + "that want to use OpenGL for precise 2D drawing.) It fills in\n" + "an entire rectangle with an array of smaller rects, drawing\n" + "adjacent rects with different colors and with blending enabled.\n" + "If there are gaps (pixels that are the background color, and\n" + "thus haven't been filled), overlaps (pixels that show a blend\n" + "of more than one color), or improper edges (pixels around the\n" + "edge of the rectangle that haven't been filled, or pixels just\n" + "outside the edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the\n" + "rectangle rasterization process may not be filling the correct\n" + "pixels; this can cause gaps, overlaps, or bad edges.\n" + + ); + + + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +OrthoPosRandTris::runOne(OPResult& r, Window& w) { + bool passed = true; + + GLUtils::useScreenCoords(windowSize, windowSize); + + glFrontFace(GL_CCW); + + glDisable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0, 0, 0, 0); + + //////////////////////////////////////////////////////////// + // Immediate-mode random axis-aligned rectangles + //////////////////////////////////////////////////////////// + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT); + const int nPoints = 10; + RandomDouble vRand(141421356); + RandomMesh2D v(1, drawingSize + 1, nPoints, + 1, drawingSize + 1, nPoints, + vRand); + for (int i = nPoints - 1; i > 0; --i) { + glBegin(GL_TRIANGLE_STRIP); + for (int j = 0; j < nPoints; ++j) { + glColor4f(1.0, 0.0, 0.0, 0.5); + glVertex2fv(v(i, j)); + glColor4f(0.0, 1.0, 0.0, 0.5); + glVertex2fv(v(i - 1, j)); + } + glEnd(); + } + verifyOrthPos(w, passed, name, r.config, r, env, + "Immediate-mode triangles"); + r.pass = passed; +} // OrthoPosRandTris::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosRandTris::logOne(OPResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } else { + env->log << '\n'; + } + logStats(r); +} // OrthoPosRandTris::logOne + +void +OrthoPosRandTris::logStats(OPResult& r) { + logStats1("Immediate-mode triangles", r, env); +} // OrthoPosRandTris::logStats + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +OrthoPosRandTris::compareOne(OPResult& oldR, OPResult& newR) { + bool same = true; + + doComparison(oldR, newR, newR.config, same, name, + env, "immediate-mode triangles"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} // OrthoPosRandTris::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +OrthoPosRandTris orthoPosRandTrisTest("orthoPosRandTris", + "window, rgb > 1, z, fast", + + "This test checks the positioning of random triangles under\n" + "orthographic projection. (This is important for apps that\n" + "want to use OpenGL for precise 2D drawing.) It fills in an\n" + "entire rectangle with an array of randomly-generated triangles,\n" + "drawing adjacent triangles with different colors and with blending\n" + "enabled. If there are gaps (pixels that are the background color,\n" + "and thus haven't been filled), overlaps (pixels that show a blend\n" + "of more than one color), or improper edges (pixels around the\n" + "edge of the rectangle that haven't been filled, or pixels just\n" + "outside the edge that have), then the test fails.\n" + "\n" + "This test generally fails for one of several reasons. First,\n" + "the coordinate transformation process may have an incorrect bias;\n" + "this usually will cause a bad edge. Second, the coordinate\n" + "transformation process may round pixel coordinates incorrectly;\n" + "this will usually cause gaps and/or overlaps. Third, the\n" + "triangle rasterization process may not be filling the correct\n" + "pixels; this can cause gaps, overlaps, or bad edges.\n" + + ); + + +} // namespace GLEAN diff --git a/tests/glean/torthpos.h b/tests/glean/torthpos.h new file mode 100644 index 00000000..f12f3137 --- /dev/null +++ b/tests/glean/torthpos.h @@ -0,0 +1,103 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// torthpos.h: Test positioning of primitives in orthographic projection. +// Apps that require precise 2D rasterization depend on these semantics. + +#ifndef __torthpos_h__ +#define __torthpos_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 256 +#define windowSize (drawingSize + 2) + +// Auxiliary struct for holding a test result: +class OPResult: public BaseResult { +public: + bool pass; // not saved in results file + bool hasGaps; // true if gaps between prims were detected + bool hasOverlaps; // true if overlaps were detected + bool hasBadEdges; // true if edge-conditions were incorrect + + OPResult() { + hasGaps = hasOverlaps = hasBadEdges = false; + } + void putresults(ostream& s) const { + s << hasGaps + << ' ' << hasOverlaps + << ' ' << hasBadEdges + << '\n'; + } + bool getresults(istream& s) { + s >> hasGaps >> hasOverlaps >> hasBadEdges; + return s.good(); + } +}; + +class OrthoPosPoints: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosPoints, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosPoints + +class OrthoPosVLines: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosVLines, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosVLines + +class OrthoPosHLines: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosHLines, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosHLines + +class OrthoPosTinyQuads: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosTinyQuads, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosTinyQuads + +class OrthoPosRandRects: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosRandRects, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosRandRects + +class OrthoPosRandTris: public BaseTest<OPResult> { +public: + GLEAN_CLASS_WH(OrthoPosRandTris, OPResult, windowSize, windowSize); + void logStats(OPResult& r); +}; // class OrthoPosRandTris + +} // namespace GLEAN + +#endif // __torthpos_h__ diff --git a/tests/glean/tpaths.cpp b/tests/glean/tpaths.cpp new file mode 100644 index 00000000..00926ea1 --- /dev/null +++ b/tests/glean/tpaths.cpp @@ -0,0 +1,373 @@ +// 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 + + +// tpaths.h: Basic test of GL rendering paths. +// This test verifies that basic, trival OpenGL paths work as expected. +// For example, glAlphaFunc(GL_GEQUAL, 0.0) should always pass and +// glAlphaFunc(GL_LESS, 0.0) should always fail. We setup trivial +// pass and fail conditions for each of alpha test, blending, color mask, +// depth test, logic ops, scissor, stencil, stipple, and texture and +// make sure they work as expected. We also setup trival-pass for all +// these paths simultaneously and test that as well. +// +// To test for pass/fail we examine the color buffer for white or black, +// respectively. +// +// Author: Brian Paul (brianp@valinux.com) November 2000 + +#include "tpaths.h" + +namespace GLEAN { + +void +PathsTest::SetPathState(Path path, State state) const { + + switch (path) { + case ALPHA: + if (state == ALWAYS_PASS) { + glAlphaFunc(GL_GEQUAL, 0.0); + glEnable(GL_ALPHA_TEST); + } + else if (state == ALWAYS_FAIL) { + glAlphaFunc(GL_GREATER, 1.0); + glEnable(GL_ALPHA_TEST); + } + else { + glDisable(GL_ALPHA_TEST); + } + break; + case BLEND: + if (state == ALWAYS_PASS) { + glBlendFunc(GL_ONE, GL_ZERO); + glEnable(GL_BLEND); + } + else if (state == ALWAYS_FAIL) { + glBlendFunc(GL_ZERO, GL_ONE); + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + break; + case COLOR_MASK: + if (state == ALWAYS_PASS) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + else if (state == ALWAYS_FAIL) { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + } + else { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + break; + case DEPTH: + if (state == ALWAYS_PASS) { + glDepthFunc(GL_ALWAYS); + glEnable(GL_DEPTH_TEST); + } + else if (state == ALWAYS_FAIL) { + glDepthFunc(GL_NEVER); + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + break; + case LOGIC: + if (state == ALWAYS_PASS) { + glLogicOp(GL_OR); + glEnable(GL_COLOR_LOGIC_OP); + } + else if (state == ALWAYS_FAIL) { + glLogicOp(GL_AND); + glEnable(GL_COLOR_LOGIC_OP); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } + break; + case SCISSOR: + if (state == ALWAYS_PASS) { + glScissor(0, 0, 10, 10); + glEnable(GL_SCISSOR_TEST); + } + else if (state == ALWAYS_FAIL) { + glScissor(0, 0, 0, 0); + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } + break; + case STENCIL: + if (state == ALWAYS_PASS) { + // pass if reference <= stencil value (ref = 0) + glStencilFunc(GL_LEQUAL, 0, ~0); + glEnable(GL_STENCIL_TEST); + } + else if (state == ALWAYS_FAIL) { + // pass if reference > stencil value (ref = 0) + glStencilFunc(GL_GREATER, 0, ~0); + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } + break; + case STIPPLE: + if (state == ALWAYS_PASS) { + GLubyte stipple[4*32]; + for (int i = 0; i < 4*32; i++) + stipple[i] = 0xff; + glPolygonStipple(stipple); + glEnable(GL_POLYGON_STIPPLE); + } + else if (state == ALWAYS_FAIL) { + GLubyte stipple[4*32]; + for (int i = 0; i < 4*32; i++) + stipple[i] = 0x0; + glPolygonStipple(stipple); + glEnable(GL_POLYGON_STIPPLE); + } + else { + glDisable(GL_POLYGON_STIPPLE); + } + break; + case TEXTURE: + if (state == DISABLE) { + glDisable(GL_TEXTURE_2D); + } + else { + GLubyte val = (state == ALWAYS_PASS) ? 0xff : 0x00; + int i; + GLubyte texImage[4*4*4]; + for (i = 0; i < 4*4*4; i++) + texImage[i] = val; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_UNSIGNED_BYTE, texImage); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, + GL_MODULATE); + glEnable(GL_TEXTURE_2D); + } + break; + default: + abort(); // error + } +} + + +const char * +PathsTest::PathName(Path path) const { + + switch (path) { + case ALPHA: + return "Alpha Test"; + case BLEND: + return "Blending"; + case COLOR_MASK: + return "Color Mask"; + case DEPTH: + return "Depth Test"; + case LOGIC: + return "LogicOp"; + case SCISSOR: + return "Scissor Test"; + case STENCIL: + return "Stencil Test"; + case STIPPLE: + return "Polygon Stipple"; + case TEXTURE: + return "Modulated Texture"; + case ZZZ: + return "paths"; + default: + return "???"; + } +} + + +void +PathsTest::FailMessage(BasicResult &r, Path path, State state, GLfloat pixel[3]) + const { + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n'; + if (state == ALWAYS_PASS) { + env->log << "\t" << PathName(path) + << " should have had no effect (1, 1, 1)" + << " but actually modified the fragment: " + << "(" << pixel[0] << "," << pixel[1] << "," + << pixel[2] << ")\n"; + } + else { + env->log << "\t" << PathName(path) + << " should have culled the fragment (0, 0, 0)" + << " but actually didn't: (" << pixel[0] << "," + << pixel[1] << "," << pixel[2] << ")\n"; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +PathsTest::runOne(BasicResult& r, Window&) { + + Path p, paths[1000]; + int i, numPaths = 0; + + // draw 10x10 pixel quads + glViewport(0, 0, 10, 10); + + glDisable(GL_DITHER); + + // Build the list of paths to exercise. Skip depth test if we have no + // depth buffer. Skip stencil test if we have no stencil buffer. + for (p = ALPHA; p != ZZZ; p = (Path) (p + 1)) { + if (!((p == DEPTH && r.config->z == 0) || + (p == STENCIL && r.config->s == 0))) { + paths[numPaths++] = p; + } + } + + // test always-pass paths + for (i = 0; i < numPaths; i++) { + glClear(GL_COLOR_BUFFER_BIT); + + SetPathState(paths[i], ALWAYS_PASS); + + // draw polygon + glColor4f(1, 1, 1, 1); + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 1, 1); + glVertex2f(-1, 1); + glEnd(); + + SetPathState(paths[i], DISABLE); + + // test buffer + GLfloat pixel[3]; + glReadPixels(4, 4, 1, 1, GL_RGB, GL_FLOAT, pixel); + if (pixel[0] != 1.0 || pixel[1] != 1.0 || pixel[2] != 1.0) { + FailMessage(r, paths[i], ALWAYS_PASS, pixel); + r.pass = false; + return; + } + } + + // enable all always-pass paths + { + glClear(GL_COLOR_BUFFER_BIT); + + for (i = 0; i < numPaths; i++) { + SetPathState(paths[i], ALWAYS_PASS); + } + + // draw polygon + glColor4f(1, 1, 1, 1); + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 1, 1); + glVertex2f(-1, 1); + glEnd(); + + for (i = 0; i < numPaths; i++) { + SetPathState(paths[i], DISABLE); + } + + // test buffer + GLfloat pixel[3]; + glReadPixels(4, 4, 1, 1, GL_RGB, GL_FLOAT, pixel); + if (pixel[0] != 1.0 || pixel[1] != 1.0 || pixel[2] != 1.0) { + FailMessage(r, paths[i], ALWAYS_PASS, pixel); + r.pass = false; + return; + } + } + + // test never-pass paths + for (i = 0; i < numPaths; i++) { + glClear(GL_COLOR_BUFFER_BIT); + + SetPathState(paths[i], ALWAYS_FAIL); + + // draw polygon + glColor4f(1, 1, 1, 1); + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 1, 1); + glVertex2f(-1, 1); + glEnd(); + + SetPathState(paths[i], DISABLE); + + // test buffer + GLfloat pixel[3]; + glReadPixels(4, 4, 1, 1, GL_RGB, GL_FLOAT, pixel); + if (pixel[0] != 0.0 || pixel[1] != 0.0 || pixel[2] != 0.0) { + FailMessage(r, paths[i], ALWAYS_FAIL, pixel); + r.pass = false; + return; + } + } + + // success + r.pass = true; +} // PathsTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +PathsTest::logOne(BasicResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} // PathsTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +PathsTest pathsTest("paths", "window, rgb", + + "This test verifies that basic OpenGL operations such as the alpha\n" + "test, depth test, blending, stippling, and texturing work for\n" + "trivial cases.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tpaths.h b/tests/glean/tpaths.h new file mode 100644 index 00000000..3b1704b9 --- /dev/null +++ b/tests/glean/tpaths.h @@ -0,0 +1,79 @@ +// 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 + + +// tpaths.h: Basic test of GL rendering paths. +// Author: Brian Paul (brianp@valinux.com) November 2000 + + +#ifndef __tpaths_h__ +#define __tpaths_h__ + +#include "tbasic.h" + +namespace GLEAN { + +class PathsTest: public BasicTest { + public: + PathsTest(const char* testName, const char* filter, + const char* description): + BasicTest(testName, filter, description) { + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + enum Path { + ALPHA, + BLEND, + COLOR_MASK, + DEPTH, + LOGIC, + SCISSOR, + STENCIL, + STIPPLE, + TEXTURE, + ZZZ // end-of-list token + }; + enum State { + DISABLE, + ALWAYS_PASS, + ALWAYS_FAIL + }; + + const char *PathName(Path path) const; + void FailMessage(BasicResult &r, Path path, State state, + GLfloat pixel[3]) const; + void SetPathState(Path path, State state) const; + +}; // class PathsTest + +} // namespace GLEAN + +#endif // __tpaths_h__ diff --git a/tests/glean/tpgos.cpp b/tests/glean/tpgos.cpp new file mode 100644 index 00000000..71e982c4 --- /dev/null +++ b/tests/glean/tpgos.cpp @@ -0,0 +1,735 @@ +// 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 +// 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 + +// tpgos.cpp: implementation of polygon offset tests +// Derived in part from tests written by Angus Dorbie <dorbie@sgi.com> +// in September 2000 and Rickard E. (Rik) Faith <faith@valinux.com> in +// October 2000. + +#include "tpgos.h" +#include "image.h" +#include "misc.h" +#include <cmath> + + +namespace { + +typedef struct { + GLfloat angle; + GLfloat axis[3]; +} AngleAxis; + +void +red() { + glColor3f(1.0, 0.0, 0.0); +} + +void +black() { + glColor3f(0.0, 0.0, 0.0); +} + +void +drawQuadAtDistance(GLdouble dist) { + glBegin(GL_QUADS); + glVertex3d(-dist, -dist, -dist); + glVertex3d( dist, -dist, -dist); + glVertex3d( dist, dist, -dist); + glVertex3d(-dist, dist, -dist); + glEnd(); +} + +GLdouble +windowCoordDepth(GLdouble dist) { + // Assumes we're using the "far at infinity" projection matrix + // and simple viewport transformation. + return 0.5 * (dist - 2.0) / dist + 0.5; +} + +bool +redQuadWasDrawn() { + GLEAN::Image + img(PGOS_WIN_SIZE, PGOS_WIN_SIZE, GL_RGB, GL_UNSIGNED_BYTE); + img.read(0, 0); + + GLubyte* row = reinterpret_cast<GLubyte*>(img.pixels()); + for (GLsizei r = 0; r < img.height(); ++r) { + GLubyte* pix = row; + for (GLsizei c = 0; c < img.width(); ++c) { + if (pix[0] == 0 // bad red component? + || pix[1] != 0 // bad green component? + || pix[2] != 0) // bad blue component? + return false; // ...then quad wasn't drawn + pix += 3; + } + row += img.rowSizeInBytes(); + } + + return true; +} + +void +findIdealMRD(GLEAN::POResult& r, GLEAN::Window& w) { + + // MRD stands for Minimum Resolvable Difference, the smallest + // distance in depth that suffices to separate any two + // polygons (or a polygon and the near or far clipping + // planes). + // + // This function tries to determine the "ideal" MRD for the + // current rendering context. It's expressed in window + // coordinates, because the value in model or clipping + // coordinates depends on the scale factors in the modelview + // and projection matrices and on the distances to the near + // and far clipping planes. + // + // For simple unsigned-integer depth buffers that aren't too + // deep (so that precision isn't an issue during coordinate + // transformations), it should be about one least-significant + // bit. For deep or floating-point or compressed depth + // buffers the situation may be more complicated, so we don't + // pass or fail an implementation solely on the basis of its + // ideal MRD. + // + // There are two subtle parts of this function. The first is + // the projection matrix we use for rendering. This matrix + // places the far clip plane at infinity (so that we don't run + // into arbitrary limits during our search process). The + // second is the method used for drawing the polygon. We + // scale the x and y coords of the polygon vertices by the + // polygon's depth, so that it always occupies the full view + // frustum. This makes it easier to verify that the polygon + // was resolved completely -- we just read back the entire + // window and see if any background pixels appear. + // + // To insure that we get reasonable results on machines with + // unusual depth buffers (floating-point, or compressed), we + // determine the MRD twice, once close to the near clipping + // plane and once as far away from the eye as possible. On a + // simple integer depth buffer these two values should be + // essentially the same. For other depth-buffer formats, the + // ideal MRD is simply the largest of the two. + + GLdouble nearDist, farDist; + int i; + + // First, find a distance that is as far away as possible, yet + // a quad at that distance can be distinguished from the + // background. Start by pushing quads away from the eye until + // we find an interval where the closer quad can be resolved, + // but the farther quad cannot. Then binary-search to find + // the threshold. + + glDepthFunc(GL_LESS); + glClearDepth(1.0); + red(); + nearDist = 1.0; + farDist = 2.0; + for (;;) { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + drawQuadAtDistance(farDist); + w.swap(); + if (!redQuadWasDrawn()) + break; + nearDist = farDist; + farDist *= 2.0; + } + for (i = 0; i < 64; ++i) { + GLdouble halfDist = 0.5 * (nearDist + farDist); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + drawQuadAtDistance(halfDist); + w.swap(); + if (redQuadWasDrawn()) + nearDist = halfDist; + else + farDist = halfDist; + } + r.nextToFar = nearDist; + + // We can derive a resolvable difference from the value + // nextToFar, but it's not necessarily the one we want. + // Consider mapping the object coordinate range [0,1] onto the + // integer window coordinate range [0,2]. A natural way to do + // this is with a linear function, windowCoord = + // 2*objectCoord. With rounding, this maps [0,0.25) to 0, + // [0.25,0.75) to 1, and [0.75,1] to 2. Note that the + // intervals at either end are 0.25 wide, but the one in the + // middle is 0.5 wide. The difference we can derive from + // nextToFar is related to the width of the final interval. + // We want to back up just a bit so that we can get a + // (possibly much larger) difference that will work for the + // larger interval. To do this we need to find a difference + // that allows us to distinguish two quads when the more + // distant one is at distance nextToFar. + + nearDist = 1.0; + farDist = r.nextToFar; + for (i = 0; i < 64; ++i) { + GLdouble halfDist = 0.5 * (nearDist + farDist); + + black(); + glDepthFunc(GL_ALWAYS); + drawQuadAtDistance(r.nextToFar); + + red(); + glDepthFunc(GL_LESS); + drawQuadAtDistance(halfDist); + + w.swap(); + if (redQuadWasDrawn()) + nearDist = halfDist; + else + farDist = halfDist; + } + + r.idealMRDFar = windowCoordDepth(r.nextToFar) + - windowCoordDepth(nearDist); + + + // Now we apply a similar strategy at the near end of the + // depth range, but swapping the senses of various comparisons + // so that we approach the near clipping plane rather than the + // far. + + glClearDepth(0.0); + glDepthFunc(GL_GREATER); + red(); + nearDist = 1.0; + farDist = r.nextToFar; + for (i = 0; i < 64; ++i) { + GLdouble halfDist = 0.5 * (nearDist + farDist); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + drawQuadAtDistance(halfDist); + w.swap(); + if (redQuadWasDrawn()) + farDist = halfDist; + else + nearDist = halfDist; + } + r.nextToNear = farDist; + + nearDist = r.nextToNear; + farDist = r.nextToFar; + for (i = 0; i < 64; ++i) { + GLdouble halfDist = 0.5 * (nearDist + farDist); + + black(); + glDepthFunc(GL_ALWAYS); + drawQuadAtDistance(r.nextToNear); + + red(); + glDepthFunc(GL_GREATER); + drawQuadAtDistance(halfDist); + + w.swap(); + if (redQuadWasDrawn()) + farDist = halfDist; + else + nearDist = halfDist; + } + + r.idealMRDNear = windowCoordDepth(farDist) + - windowCoordDepth(r.nextToNear); +} // findIdealMRD + +double +readDepth(int x, int y) { + GLuint depth; + glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &depth); + + // This normalization of "depth" is correct even on 64-bit + // machines because GL types have machine-independent ranges. + return static_cast<double>(depth) / 4294967295.0; +} + +double +readDepth(double x, double y) { + return readDepth(static_cast<int>(x), static_cast<int>(y)); +} + +void +findActualMRD(GLEAN::POResult& r, GLEAN::Window& w) { + + // Here we use polygon offset to determine the + // implementation's actual MRD. + + double baseDepth; + + glDepthFunc(GL_ALWAYS); + + // Draw a quad far away from the eye and read the depth at its center: + glDisable(GL_POLYGON_OFFSET_FILL); + drawQuadAtDistance(r.nextToFar); + baseDepth = readDepth(PGOS_WIN_SIZE/2, PGOS_WIN_SIZE/2); + + // Now draw a quad that's one MRD closer to the eye: + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.0, -1.0); + drawQuadAtDistance(r.nextToFar); + + // The difference between the depths of the two quads is the + // value the implementation is actually using for one MRD: + r.actualMRDFar = baseDepth + - readDepth(PGOS_WIN_SIZE/2, PGOS_WIN_SIZE/2); + + // Repeat the process for a quad close to the eye: + glDisable(GL_POLYGON_OFFSET_FILL); + drawQuadAtDistance(r.nextToNear); + baseDepth = readDepth(PGOS_WIN_SIZE/2, PGOS_WIN_SIZE/2); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.0, 1.0); // 1 MRD further away + drawQuadAtDistance(r.nextToNear); + r.actualMRDNear = readDepth(PGOS_WIN_SIZE/2, PGOS_WIN_SIZE/2) + - baseDepth; + w.swap(); +} // findActualMRD + +void +logMRD(ostream& log, double mrd, GLEAN::DrawingSurfaceConfig* config) { + int bits = static_cast<int>(0.5 + (pow(2.0, config->z) - 1.0) * mrd); + log << mrd << " (nominally " << bits + << ((bits == 1)? " bit)": " bits)"); +} // logMRD + +void +draw2x2Quad() { + glBegin(GL_QUADS); + glVertex2f(-1.0, -1.0); + glVertex2f( 1.0, -1.0); + glVertex2f( 1.0, 1.0); + glVertex2f(-1.0, 1.0); + glEnd(); +} + +void +checkSlopeOffset(GLEAN::POResult& r, GLEAN::Window& w, AngleAxis* aa) { + // This function checks for correct slope-based offsets for + // a quad rotated to a given angle around a given axis. + // + // The basic strategy is to: + // Draw the quad. (Note: the quad's size and position + // are chosen so that it won't ever be clipped.) + // Sample three points in the quad's interior. + // Compute dz/dx and dz/dy based on those samples. + // Compute the range of allowable offsets; must be between + // max(abs(dz/dx), abs(dz/dy)) and + // sqrt((dz/dx)**2, (dz/dy)**2) + // Sample the depth of the quad at its center. + // Use PolygonOffset to produce an offset equal to one + // times the depth slope of the base quad. + // Draw another quad with the same orientation as the first. + // Sample the second quad at its center. + // Compute the difference in depths between the first quad + // and the second. + // Verify that the difference is within the allowable range. + // Repeat for a third quad at twice the offset from the first. + // (This verifies that the implementation is scaling + // the depth offset correctly.) + + if (!r.slopeOffsetsPassed) + return; + + const GLfloat quadDist = 2.5; // must be > 1+sqrt(2) to avoid + // clipping by the near plane + + glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + red(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.0, 0.0, -quadDist); + glRotatef(aa->angle, aa->axis[0], aa->axis[1], aa->axis[2]); + + GLdouble modelViewMat[16]; + glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMat); + GLdouble projectionMat[16]; + glGetDoublev(GL_PROJECTION_MATRIX, projectionMat); + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + glDisable(GL_POLYGON_OFFSET_FILL); + + draw2x2Quad(); + w.swap(); + + GLdouble centerW[3]; + gluProject(0.0, 0.0, 0.0, modelViewMat, projectionMat, viewport, + centerW + 0, centerW + 1, centerW + 2); + GLdouble baseDepth = readDepth(centerW[0], centerW[1]); + + GLdouble p0[3]; + gluProject(-0.9, -0.9, 0.0, modelViewMat, projectionMat, viewport, + p0 + 0, p0 + 1, p0 + 2); + p0[2] = readDepth(p0[0], p0[1]); + GLdouble p1[3]; + gluProject( 0.9, -0.9, 0.0, modelViewMat, projectionMat, viewport, + p1 + 0, p1 + 1, p1 + 2); + p1[2] = readDepth(p1[0], p1[1]); + GLdouble p2[3]; + gluProject( 0.9, 0.9, 0.0, modelViewMat, projectionMat, viewport, + p2 + 0, p2 + 1, p2 + 2); + p2[2] = readDepth(p2[0], p2[1]); + + double det = (p0[0] - p1[0]) * (p0[1] - p2[1]) + - (p0[0] - p2[0]) * (p0[1] - p1[1]); + if (fabs(det) < 0.001) + return; // too close to colinear to evaluate + + double dzdx = ((p0[2] - p1[2]) * (p0[1] - p2[1]) + - (p0[2] - p2[2]) * (p0[1] - p1[1])) / det; + double dzdy = ((p0[0] - p1[0]) * (p0[2] - p2[2]) + - (p0[0] - p2[0]) * (p0[2] - p1[2])) / det; + + double mMax = 1.1 * sqrt(dzdx * dzdx + dzdy * dzdy) + r.idealMRDNear; + // (adding idealMRDNear is a fudge for roundoff error when + // the slope is extremely close to zero) + double mMin = 0.9 * max(fabs(dzdx), fabs(dzdy)); + + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0, 0.0); + draw2x2Quad(); + GLdouble offsetDepth = readDepth(centerW[0], centerW[1]); + GLdouble offset = max(baseDepth - offsetDepth, 0.0); + if (offset < mMin || offset > mMax) { + r.slopeOffsetsPassed = false; + r.failingAngle = aa->angle; + r.failingAxis[0] = aa->axis[0]; + r.failingAxis[1] = aa->axis[1]; + r.failingAxis[2] = aa->axis[2]; + r.failingOffset = offset; + r.minGoodOffset = mMin; + r.maxGoodOffset = mMax; + return; + } + + glPolygonOffset(-2.0, 0.0); + draw2x2Quad(); + offsetDepth = readDepth(centerW[0], centerW[1]); + offset = max(baseDepth - offsetDepth, 0.0); + if (offset < 2.0 * mMin || offset > 2.0 * mMax) { + r.slopeOffsetsPassed = false; + r.failingAngle = aa->angle; + r.failingAxis[0] = aa->axis[0]; + r.failingAxis[1] = aa->axis[1]; + r.failingAxis[2] = aa->axis[2]; + r.failingOffset = offset; + r.minGoodOffset = 2.0 * mMin; + r.maxGoodOffset = 2.0 * mMax; + return; + } +} + +void +checkSlopeOffsets(GLEAN::POResult& r, GLEAN::Window& w) { + // This function checks that the implementation is offsetting + // primitives correctly according to their depth slopes. + // (Note that it uses some values computed by findIdealMRD, so + // that function must be run first.) + + // Rotation angles (degrees) and axes for which offset will be checked: + AngleAxis aa[] = { + { 0, {1, 0, 0}}, + {30, {1, 0, 0}}, + {45, {1, 0, 0}}, + {60, {1, 0, 0}}, + {80, {1, 0, 0}}, + { 0, {0, 1, 0}}, + {30, {0, 1, 0}}, + {45, {0, 1, 0}}, + {60, {0, 1, 0}}, + {80, {0, 1, 0}}, + { 0, {1, 1, 0}}, + {30, {1, 1, 0}}, + {45, {1, 1, 0}}, + {60, {1, 1, 0}}, + {80, {1, 1, 0}}, + { 0, {2, 1, 0}}, + {30, {2, 1, 0}}, + {45, {2, 1, 0}}, + {60, {2, 1, 0}}, + {80, {2, 1, 0}} + }; + + r.slopeOffsetsPassed = true; + for (unsigned int i = 0; + r.slopeOffsetsPassed && i < sizeof(aa)/sizeof(aa[0]); + ++i) + checkSlopeOffset(r, w, aa + i); +} // checkSlopeOffsets + + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +PgosTest::runOne(POResult& r, GLEAN::Window& w) { + + glViewport(0, 0, PGOS_WIN_SIZE, PGOS_WIN_SIZE); + glDepthRange(0.0, 1.0); + { + glMatrixMode(GL_PROJECTION); + + // The following projection matrix places the near clipping + // plane at distance 1.0, and the far clipping plane at + // infinity. This allows us to stress depth-buffer resolution + // as far away from the eye as possible, without introducing + // code that depends on the size or format of the depth + // buffer. + // + // (To derive this matrix, start with the matrix generated by + // glFrustum with near-plane distance equal to 1.0, and take + // the limit of the matrix elements as the far-plane distance + // goes to infinity.) + + static GLfloat near1_farInfinity[] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, -1.0, + 0.0, 0.0, -2.0, 0.0 + }; + glLoadMatrixf(near1_farInfinity); + } + + glDisable(GL_LIGHTING); + + glFrontFace(GL_CCW); + glDisable(GL_NORMALIZE); + glDisable(GL_COLOR_MATERIAL); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_TEXTURE_2D); + + glDisable(GL_FOG); + + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + glReadBuffer(GL_FRONT); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClearDepth(1.0); + + // Clear both front and back buffers and swap, to avoid confusing + // this test with results of the previous test: + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + w.swap(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + findIdealMRD(r, w); + findActualMRD(r, w); + double idealMRD = max(r.idealMRDNear, r.idealMRDFar); + double actualMRD = max(r.actualMRDNear, r.actualMRDFar); + r.bigEnoughMRD = (actualMRD >= 0.99 * idealMRD); + r.smallEnoughMRD = (actualMRD <= 2.0 * idealMRD); + + checkSlopeOffsets(r, w); + + r.pass = r.bigEnoughMRD && r.smallEnoughMRD && r.slopeOffsetsPassed; +} // PgosTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +PgosTest::compareOne(POResult& oldR, POResult& newR) { + comparePassFail(oldR, newR); + if (oldR.bigEnoughMRD != newR.bigEnoughMRD) { + env->log << "\tMin size MRD criterion: " + << env->options.db1Name + << (oldR.bigEnoughMRD? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.bigEnoughMRD? " PASS\n": " FAIL\n"); + } + if (oldR.smallEnoughMRD != newR.smallEnoughMRD) { + env->log << "\tMax size MRD criterion: " + << env->options.db1Name + << (oldR.smallEnoughMRD? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.smallEnoughMRD? " PASS\n": " FAIL\n"); + } + if (oldR.slopeOffsetsPassed != newR.slopeOffsetsPassed) { + env->log << "\tSlope-relative offsets criterion: " + << env->options.db1Name + << (oldR.slopeOffsetsPassed? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.slopeOffsetsPassed? " PASS\n": " FAIL\n"); + } + if (!oldR.slopeOffsetsPassed && !newR.slopeOffsetsPassed) { + if (oldR.failingAngle != newR.failingAngle) { + env->log << '\t' + << env->options.db1Name + << " failed at angle " + << oldR.failingAngle + << ", " + << env->options.db2Name + << " failed at angle " + << newR.failingAngle + << '\n'; + } + if (oldR.failingAxis[0] != newR.failingAxis[0] + || oldR.failingAxis[1] != newR.failingAxis[1] + || oldR.failingAxis[2] != newR.failingAxis[2]) { + env->log << '\t' + << env->options.db1Name + << " failed at axis (" + << oldR.failingAxis[0] + << ", " + << oldR.failingAxis[1] + << ", " + << oldR.failingAxis[2] + << "), " + << env->options.db2Name + << " failed at axis (" + << newR.failingAxis[0] + << ", " + << newR.failingAxis[1] + << ", " + << newR.failingAxis[2] + << ")\n"; + } + } +} // PgosTest::compareOne + +void +PgosTest::logOne(POResult& r) { + logPassFail(r); + logConcise(r); + + if (!r.bigEnoughMRD) + env->log << "\tActual MRD is too small " + "(may cause incorrect results)\n"; + if (!r.smallEnoughMRD) + env->log << "\tActual MRD is too large " + "(may waste depth-buffer range)\n"; + if (!r.slopeOffsetsPassed) { + env->log << "\tDepth-slope related offset was too " + << ((r.failingOffset < r.minGoodOffset)? + "small": "large") + << "; first failure at:\n"; + env->log << "\t\tAngle = " << r.failingAngle << " degrees, " + << "axis = (" << r.failingAxis[0] << ", " + << r.failingAxis[1] << ", " + << r.failingAxis[2] << ")\n"; + env->log << "\t\tFailing offset was " + << setprecision(16) << r.failingOffset << "\n"; + env->log << "\t\tAllowable range is (" + << r.minGoodOffset << ", " << r.maxGoodOffset << ")\n"; + } + + if (!r.pass) + env->log << '\n'; + + env->log << "\tIdeal MRD at near plane is "; + logMRD(env->log, r.idealMRDNear, r.config); + env->log << '\n'; + + env->log << "\tActual MRD at near plane is "; + logMRD(env->log, r.actualMRDNear, r.config); + env->log << '\n'; + + env->log << "\tIdeal MRD at infinity is "; + logMRD(env->log, r.idealMRDFar, r.config); + env->log << '\n'; + + env->log << "\tActual MRD at infinity is "; + logMRD(env->log, r.actualMRDFar, r.config); + env->log << '\n'; + +} // PgosTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +PgosTest +pgosTest("polygonOffset", "window, rgb, z", + + "This test verifies glPolygonOffset. It is run on every\n" + "OpenGL-capable drawing surface configuration that supports\n" + "creation of a window, has a depth buffer, and is RGB.\n" + "\n" + "The first subtest verifies that the OpenGL implementation is\n" + "using a plausible value for the \"minimum resolvable\n" + "difference\" (MRD). This is the offset in window coordinates\n" + "that is sufficient to provide separation in depth (Z) for any\n" + "two parallel surfaces. The subtest searches for the MRD by\n" + "drawing two surfaces at a distance from each other and\n" + "checking the resulting image to see if they were cleanly\n" + "separated. The distance is then modified (using a binary\n" + "search) until a minimum value is found. This is the so-called\n" + "\"ideal\" MRD. Then two surfaces are drawn using\n" + "glPolygonOffset to produce a separation that should equal one\n" + "MRD. The depth values at corresponding points on each surface\n" + "are subtracted to form the \"actual\" MRD. The subtest performs\n" + "these checks twice, once close to the viewpoint and once far\n" + "away from it, and passes if the largest of the ideal MRDs and\n" + "the largest of the actual MRDs are nearly the same.\n" + "\n" + "The second subtest verifies that the OpenGL implementation is\n" + "producing plausible values for slope-dependent offsets. The\n" + "OpenGL spec requires that the depth slope of a surface be\n" + "computed by an approximation that is at least as large as\n" + "max(abs(dz/dx),abs(dz/dy)) and no larger than\n" + "sqrt((dz/dx)**2+(dz/dy)**2). The subtest draws a quad rotated\n" + "by various angles along various axes, samples three points on\n" + "the quad's surface, and computes dz/dx and dz/dy. Then it\n" + "draws two additional quads offset by one and two times the\n" + "depth slope, respectively. The base quad and the two new\n" + "quads are sampled and their actual depths read from the depth\n" + "buffer. The subtest passes if the quads are offset by amounts\n" + "that are within one and two times the allowable range,\n" + "respectively.\n" + + ); + +} // namespace GLEAN diff --git a/tests/glean/tpgos.h b/tests/glean/tpgos.h new file mode 100644 index 00000000..8919756e --- /dev/null +++ b/tests/glean/tpgos.h @@ -0,0 +1,124 @@ +// 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 + + +// tpgos.h: Polygon offset tests. +// Derived in part from tests written by Angus Dorbie <dorbie@sgi.com> +// in September 2000 and Rickard E. (Rik) Faith <faith@valinux.com> in +// October 2000. + +#ifndef __tpgos_h__ +#define __tpgos_h__ + +#include "tbase.h" +#include <iomanip> + +namespace GLEAN { + +// Auxiliary struct for holding a glPolygonOffset result +class POResult: public BaseResult { +public: + bool pass; + double nextToNear; + double nextToFar; + double idealMRDNear; + double idealMRDFar; + double actualMRDNear; + double actualMRDFar; + bool bigEnoughMRD; + bool smallEnoughMRD; + bool slopeOffsetsPassed; + float failingAngle; + float failingAxis[3]; + double failingOffset; + double minGoodOffset; + double maxGoodOffset; + + POResult() { + pass = true; + nextToNear = 1.0; + nextToFar = 2.0; + idealMRDNear = 0.1; + idealMRDFar = 0.1; + actualMRDNear = 0.1; + actualMRDFar = 0.1; + bigEnoughMRD = true; + smallEnoughMRD = true; + slopeOffsetsPassed = true; + failingAngle = 0.0; + failingAxis[0] = failingAxis[1] = failingAxis[2] = 0.0; + failingOffset = 1.0; + minGoodOffset = 0.1; + maxGoodOffset = 0.1; + } + + void putresults(ostream& s) const { + s << setprecision(16) + << pass << '\n' + << nextToNear << ' ' << nextToFar << '\n' + << idealMRDNear << ' ' << idealMRDFar << '\n' + << actualMRDNear << ' ' << actualMRDFar << '\n' + << bigEnoughMRD << ' ' << smallEnoughMRD << '\n' + << slopeOffsetsPassed << '\n' + << failingAngle << '\n' + << failingAxis[0] << ' ' << failingAxis[1] << ' ' + << failingAxis[2] << '\n' + << failingOffset << ' ' << minGoodOffset << ' ' + << maxGoodOffset << '\n' + ; + } + + bool getresults(istream& s) { + s >> pass + >> nextToNear + >> nextToFar + >> idealMRDNear + >> idealMRDFar + >> actualMRDNear + >> actualMRDFar + >> bigEnoughMRD + >> smallEnoughMRD + >> slopeOffsetsPassed + >> failingAngle + >> failingAxis[0] >> failingAxis[1] >> failingAxis[2] + >> failingOffset >> minGoodOffset >> maxGoodOffset + ; + return s.good(); + } +}; + +#define PGOS_WIN_SIZE 128 + +class PgosTest: public BaseTest<POResult> { +public: + GLEAN_CLASS_WH(PgosTest, POResult, PGOS_WIN_SIZE, PGOS_WIN_SIZE); +}; // class PgosTest + +} // namespace GLEAN + +#endif // __tpgos_h__ diff --git a/tests/glean/tpixelformats.cpp b/tests/glean/tpixelformats.cpp new file mode 100644 index 00000000..867ba08f --- /dev/null +++ b/tests/glean/tpixelformats.cpp @@ -0,0 +1,1405 @@ +// 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 + + +#include "tpixelformats.h" +#include <cassert> +#include <cmath> + + +// Set to 1 to help debug test failures: +// Also, when a test failure is found, rearrange the order of the +// formats, types, internformats below so the failure case comes first. +#define DEBUG 0 + + +// just for extra debugging +// Maybe add fragment program path as a 3rd envMode (below) someday. +#define USE_FRAG_PROG 0 + + +namespace GLEAN { + + +struct NameTokenComps { + const char *Name; + GLenum Token; + GLuint Components; +}; + + +static const NameTokenComps Types[] = +{ + { "GL_UNSIGNED_BYTE", GL_UNSIGNED_BYTE, 0 }, + { "GL_BYTE", GL_BYTE, 0 }, + { "GL_UNSIGNED_INT", GL_UNSIGNED_INT, 0 }, + { "GL_SHORT", GL_SHORT, 0 }, + { "GL_UNSIGNED_SHORT", GL_UNSIGNED_SHORT, 0 }, + { "GL_INT", GL_INT, 0 }, + { "GL_FLOAT", GL_FLOAT, 0 }, +#ifdef GL_ARB_half_float_pixel + { "GL_HALF_FLOAT_ARB", GL_HALF_FLOAT_ARB, 0 }, +#endif + + { "GL_UNSIGNED_INT_8_8_8_8", GL_UNSIGNED_INT_8_8_8_8, 4 }, + { "GL_UNSIGNED_INT_8_8_8_8_REV", GL_UNSIGNED_INT_8_8_8_8_REV, 4 }, + { "GL_UNSIGNED_INT_10_10_10_2", GL_UNSIGNED_INT_10_10_10_2, 4 }, + { "GL_UNSIGNED_INT_2_10_10_10_REV", GL_UNSIGNED_INT_2_10_10_10_REV, 4 }, + { "GL_UNSIGNED_SHORT_5_5_5_1", GL_UNSIGNED_SHORT_5_5_5_1, 4 }, + { "GL_UNSIGNED_SHORT_1_5_5_5_REV", GL_UNSIGNED_SHORT_1_5_5_5_REV, 4 }, + { "GL_UNSIGNED_SHORT_4_4_4_4", GL_UNSIGNED_SHORT_4_4_4_4, 4 }, + { "GL_UNSIGNED_SHORT_4_4_4_4_REV", GL_UNSIGNED_SHORT_4_4_4_4_REV, 4 }, + { "GL_UNSIGNED_SHORT_5_6_5", GL_UNSIGNED_SHORT_5_6_5, 3 }, + { "GL_UNSIGNED_SHORT_5_6_5_REV", GL_UNSIGNED_SHORT_5_6_5_REV, 3 }, + { "GL_UNSIGNED_BYTE_3_3_2", GL_UNSIGNED_BYTE_3_3_2, 3 }, + { "GL_UNSIGNED_BYTE_2_3_3_REV", GL_UNSIGNED_BYTE_2_3_3_REV, 3 } +}; + +#define NUM_TYPES (sizeof(Types) / sizeof(Types[0])) + + +static const NameTokenComps Formats[] = +{ + { "GL_RGBA", GL_RGBA, 4 }, + { "GL_BGRA", GL_BGRA, 4 }, + { "GL_RGB", GL_RGB, 3 }, + { "GL_BGR", GL_BGR, 3 }, + { "GL_RED", GL_RED, 1 }, + { "GL_GREEN", GL_GREEN, 1 }, + { "GL_BLUE", GL_BLUE, 1 }, + { "GL_ALPHA", GL_ALPHA, 1 }, + { "GL_LUMINANCE", GL_LUMINANCE, 1 }, + { "GL_LUMINANCE_ALPHA", GL_LUMINANCE_ALPHA, 2 }, +#ifdef GL_EXT_abgr + { "GL_ABGR_EXT", GL_ABGR_EXT, 4 } +#endif +}; + +#define NUM_FORMATS (sizeof(Formats) / sizeof(Formats[0])) + + +static const NameTokenComps InternalFormats[] = +{ + { "glDrawPixels", 0, 4 }, // special case for glDrawPixels + + { "4", 4, 4 }, + { "GL_RGBA", GL_RGBA, 4 }, + { "GL_RGBA2", GL_RGBA2, 4 }, + { "GL_RGBA4", GL_RGBA4, 4 }, + { "GL_RGB5_A1", GL_RGB5_A1, 4 }, + { "GL_RGBA8", GL_RGBA8, 4 }, + { "GL_RGB10_A2", GL_RGB10_A2, 4 }, + { "GL_RGBA12", GL_RGBA12, 4 }, + { "GL_RGBA16", GL_RGBA16, 4 }, +#ifdef GL_EXT_texture_sRGB + { "GL_SRGB_ALPHA_EXT", GL_SRGB_ALPHA_EXT, 4 }, + { "GL_SRGB8_ALPHA8_EXT", GL_SRGB8_ALPHA8_EXT, 4 }, +#endif + + { "3", 3, 3 }, + { "GL_RGB", GL_RGB, 3 }, + { "GL_R3_G3_B2", GL_R3_G3_B2, 3 }, + { "GL_RGB4", GL_RGB4, 3 }, + { "GL_RGB5", GL_RGB5, 3 }, + { "GL_RGB8", GL_RGB8, 3 }, + { "GL_RGB10", GL_RGB10, 3 }, + { "GL_RGB12", GL_RGB12, 3 }, + { "GL_RGB16", GL_RGB16, 3 }, +#ifdef GL_EXT_texture_sRGB + { "GL_SRGB_EXT", GL_SRGB_EXT, 3 }, + { "GL_SRGB8_EXT", GL_SRGB8_EXT, 3 }, +#endif + + { "2", 2, 1 }, + { "GL_LUMINANCE_ALPHA", GL_LUMINANCE_ALPHA, 1 }, + { "GL_LUMINANCE4_ALPHA4", GL_LUMINANCE4_ALPHA4, 1 }, + { "GL_LUMINANCE6_ALPHA2", GL_LUMINANCE6_ALPHA2, 1 }, + { "GL_LUMINANCE8_ALPHA8", GL_LUMINANCE8_ALPHA8, 1 }, + { "GL_LUMINANCE12_ALPHA4", GL_LUMINANCE12_ALPHA4, 1 }, + { "GL_LUMINANCE12_ALPHA12", GL_LUMINANCE12_ALPHA12, 1 }, + { "GL_LUMINANCE16_ALPHA16", GL_LUMINANCE16_ALPHA16, 1 }, +#ifdef GL_EXT_texture_sRGB + { "GL_SLUMINANCE_ALPHA_EXT", GL_SLUMINANCE_ALPHA_EXT, 3 }, + { "GL_SLUMINANCE8_ALPHA8_EXT", GL_SLUMINANCE8_ALPHA8_EXT, 3 }, +#endif + + { "1", 1, 1 }, + { "GL_LUMINANCE", GL_LUMINANCE, 1 }, + { "GL_LUMINANCE4", GL_LUMINANCE4, 1 }, + { "GL_LUMINANCE8", GL_LUMINANCE8, 1 }, + { "GL_LUMINANCE12", GL_LUMINANCE12, 1 }, + { "GL_LUMINANCE16", GL_LUMINANCE16, 1 }, +#ifdef GL_EXT_texture_sRGB + { "GL_SLUMINANCE_EXT", GL_SLUMINANCE_EXT, 3 }, + { "GL_SLUMINANCE8_EXT", GL_SLUMINANCE8_EXT, 3 }, +#endif + + { "GL_ALPHA", GL_ALPHA, 1 }, + { "GL_ALPHA4", GL_ALPHA4, 1 }, + { "GL_ALPHA8", GL_ALPHA8, 1 }, + { "GL_ALPHA12", GL_ALPHA12, 1 }, + { "GL_ALPHA16", GL_ALPHA16, 1 }, + + { "GL_INTENSITY", GL_INTENSITY, 1 }, + { "GL_INTENSITY4", GL_INTENSITY4, 1 }, + { "GL_INTENSITY8", GL_INTENSITY8, 1 }, + { "GL_INTENSITY12", GL_INTENSITY12, 1 }, + { "GL_INTENSITY16", GL_INTENSITY16, 1 }, + + // XXX maybe add compressed formats too... +}; + +#define NUM_INT_FORMATS (sizeof(InternalFormats) / sizeof(InternalFormats[0])) + + +static const char *EnvModes[] = { + "GL_REPLACE", + "GL_COMBINE_ARB" +}; + + +// Return four bitmasks indicating which bits correspond to the +// 1st, 2nd, 3rd and 4th components in a packed datatype. +// Set all masks to zero for non-packed types. +static void +ComponentMasks(GLenum datatype, GLuint masks[4]) +{ + switch (datatype) { + case GL_UNSIGNED_BYTE: + case GL_BYTE: + case GL_UNSIGNED_SHORT: + case GL_SHORT: + case GL_UNSIGNED_INT: + case GL_INT: + case GL_FLOAT: +#ifdef GL_ARB_half_float_pixel + case GL_HALF_FLOAT_ARB: +#endif + masks[0] = + masks[1] = + masks[2] = + masks[3] = 0x0; + break; + case GL_UNSIGNED_INT_8_8_8_8: + masks[0] = 0xff000000; + masks[1] = 0x00ff0000; + masks[2] = 0x0000ff00; + masks[3] = 0x000000ff; + break; + case GL_UNSIGNED_INT_8_8_8_8_REV: + masks[0] = 0x000000ff; + masks[1] = 0x0000ff00; + masks[2] = 0x00ff0000; + masks[3] = 0xff000000; + break; + case GL_UNSIGNED_INT_10_10_10_2: + masks[0] = 0xffc00000; + masks[1] = 0x003ff000; + masks[2] = 0x00000ffc; + masks[3] = 0x00000003; + break; + case GL_UNSIGNED_INT_2_10_10_10_REV: + masks[0] = 0x000003ff; + masks[1] = 0x000ffc00; + masks[2] = 0x3ff00000; + masks[3] = 0xc0000000; + break; + case GL_UNSIGNED_SHORT_5_5_5_1: + masks[0] = 0xf800; + masks[1] = 0x07c0; + masks[2] = 0x003e; + masks[3] = 0x0001; + break; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + masks[0] = 0x001f; + masks[1] = 0x03e0; + masks[2] = 0x7c00; + masks[3] = 0x8000; + break; + case GL_UNSIGNED_SHORT_4_4_4_4: + masks[0] = 0xf000; + masks[1] = 0x0f00; + masks[2] = 0x00f0; + masks[3] = 0x000f; + break; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + masks[0] = 0x000f; + masks[1] = 0x00f0; + masks[2] = 0x0f00; + masks[3] = 0xf000; + break; + case GL_UNSIGNED_SHORT_5_6_5: + masks[0] = 0xf800; + masks[1] = 0x07e0; + masks[2] = 0x001f; + masks[3] = 0; + break; + case GL_UNSIGNED_SHORT_5_6_5_REV: + masks[0] = 0x001f; + masks[1] = 0x07e0; + masks[2] = 0xf800; + masks[3] = 0; + break; + case GL_UNSIGNED_BYTE_3_3_2: + masks[0] = 0xe0; + masks[1] = 0x1c; + masks[2] = 0x03; + masks[3] = 0; + break; + case GL_UNSIGNED_BYTE_2_3_3_REV: + masks[0] = 0x07; + masks[1] = 0x38; + masks[2] = 0xc0; + masks[3] = 0; + break; + default: + abort(); + } +} + + +// Return four values indicating the ordering of the Red, Green, Blue and +// Alpha components for the given image format. +// For example: GL_BGRA = {2, 1, 0, 3}. +static void +ComponentPositions(GLenum format, GLint pos[4]) +{ + switch (format) { + case GL_RGBA: + pos[0] = 0; + pos[1] = 1; + pos[2] = 2; + pos[3] = 3; + break; + case GL_BGRA: + pos[0] = 2; + pos[1] = 1; + pos[2] = 0; + pos[3] = 3; + break; + case GL_RGB: + pos[0] = 0; + pos[1] = 1; + pos[2] = 2; + pos[3] = -1; + break; + case GL_BGR: + pos[0] = 2; + pos[1] = 1; + pos[2] = 0; + pos[3] = -1; + break; + case GL_LUMINANCE: + pos[0] = 0; + pos[1] = -1; + pos[2] = -1; + pos[3] = -1; + break; + case GL_LUMINANCE_ALPHA: + pos[0] = 0; + pos[1] = -1; + pos[2] = -1; + pos[3] = 1; + break; + case GL_RED: + pos[0] = 0; + pos[1] = -1; + pos[2] = -1; + pos[3] = -1; + break; + case GL_GREEN: + pos[0] = -1; + pos[1] = 0; + pos[2] = -1; + pos[3] = -1; + break; + case GL_BLUE: + pos[0] = -1; + pos[1] = -1; + pos[2] = 0; + pos[3] = -1; + break; + case GL_ALPHA: + pos[0] = -1; + pos[1] = -1; + pos[2] = -1; + pos[3] = 0; + break; +#ifdef GL_EXT_abgr + case GL_ABGR_EXT: + pos[0] = 3; + pos[1] = 2; + pos[2] = 1; + pos[3] = 0; + break; +#endif + default: + abort(); + } +} + + +// Given a texture internal format, return the corresponding base format. +static GLenum +BaseTextureFormat(GLint intFormat) +{ + switch (intFormat) { + case 0: + return 0; // for glDrawPixels + case GL_ALPHA: + case GL_ALPHA4: + case GL_ALPHA8: + case GL_ALPHA12: + case GL_ALPHA16: + return GL_ALPHA; + case 1: + case GL_LUMINANCE: + case GL_LUMINANCE4: + case GL_LUMINANCE8: + case GL_LUMINANCE12: + case GL_LUMINANCE16: + return GL_LUMINANCE; + case 2: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE4_ALPHA4: + case GL_LUMINANCE6_ALPHA2: + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE12_ALPHA4: + case GL_LUMINANCE12_ALPHA12: + case GL_LUMINANCE16_ALPHA16: + return GL_LUMINANCE_ALPHA; + case GL_INTENSITY: + case GL_INTENSITY4: + case GL_INTENSITY8: + case GL_INTENSITY12: + case GL_INTENSITY16: + return GL_INTENSITY; + case 3: + case GL_RGB: + case GL_R3_G3_B2: + case GL_RGB4: + case GL_RGB5: + case GL_RGB8: + case GL_RGB10: + case GL_RGB12: + case GL_RGB16: + return GL_RGB; + case 4: + case GL_RGBA: + case GL_RGBA2: + case GL_RGBA4: + case GL_RGB5_A1: + case GL_RGBA8: + case GL_RGB10_A2: + case GL_RGBA12: + case GL_RGBA16: + return GL_RGBA; + +#ifdef GL_EXT_texture_sRGB + case GL_SRGB_EXT: + case GL_SRGB8_EXT: + case GL_COMPRESSED_SRGB_EXT: + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + return GL_RGB; + case GL_SRGB_ALPHA_EXT: + case GL_SRGB8_ALPHA8_EXT: + case GL_COMPRESSED_SRGB_ALPHA_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + return GL_RGBA; + case GL_SLUMINANCE_ALPHA_EXT: + case GL_SLUMINANCE8_ALPHA8_EXT: + case GL_COMPRESSED_SLUMINANCE_EXT: + case GL_COMPRESSED_SLUMINANCE_ALPHA_EXT: + return GL_LUMINANCE_ALPHA; + case GL_SLUMINANCE_EXT: + case GL_SLUMINANCE8_EXT: + return GL_LUMINANCE; +#endif + default: + abort(); + } +} + + + + +// Return number components in the given datatype. This is 3 or 4 for +// packed types and zero for non-packed types +// Ex: GL_UNSIGNED_SHORT_5_5_5_1 = 4 +// Ex: GL_INT = 0 +static int +NumberOfComponentsInPackedType(GLenum datatype) +{ + for (unsigned i = 0; i < NUM_TYPES; i++) { + if (Types[i].Token == datatype) + return Types[i].Components; + } + abort(); +} + + +static int +IsPackedType(GLenum datatype) +{ + return NumberOfComponentsInPackedType(datatype) > 0; +} + + +// Return number components in the given image format. +// Ex: GL_BGR = 3 +static int +NumberOfComponentsInFormat(GLenum format) +{ + for (unsigned i = 0; i < NUM_FORMATS; i++) { + if (Formats[i].Token == format) + return Formats[i].Components; + } + abort(); +} + + +// Return size, in bytes, of given datatype. +static int +SizeofType(GLenum datatype) +{ + switch (datatype) { + case GL_UNSIGNED_INT_10_10_10_2: + case GL_UNSIGNED_INT_2_10_10_10_REV: + case GL_UNSIGNED_INT_8_8_8_8: + case GL_UNSIGNED_INT_8_8_8_8_REV: + case GL_UNSIGNED_INT: + case GL_INT: + case GL_FLOAT: +#ifdef GL_ARB_half_float_pixel + case GL_HALF_FLOAT_ARB: +#endif + return 4; + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + case GL_UNSIGNED_SHORT: + case GL_SHORT: + return 2; + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + case GL_UNSIGNED_BYTE: + case GL_BYTE: + return 1; + default: + abort(); + } +} + + +// Check if the given image format and datatype are compatible. +// Also check for types/formats defined by GL extensions here. +bool +PixelFormatsTest::CompatibleFormatAndType(GLenum format, GLenum datatype) const +{ + // Special case: GL_BGR can't be used with packed types! + // This has to do with putting the most color bits in red and green, + // not blue. + if (format == GL_BGR && IsPackedType(datatype)) + return false; + +#ifdef GL_ARB_half_float_pixel + if (datatype == GL_HALF_FLOAT_ARB && !haveHalfFloat) + return false; +#endif + +#ifdef GL_EXT_abgr + if (format == GL_ABGR_EXT && !haveABGR) + return false; +#endif + + const int formatComps = NumberOfComponentsInFormat(format); + const int typeComps = NumberOfComponentsInPackedType(datatype); + return formatComps == typeComps || typeComps == 0; +} + + +bool +PixelFormatsTest::SupportedIntFormat(GLint intFormat) const +{ + switch (intFormat) { +#ifdef GL_EXT_texture_sRGB + case GL_SRGB_ALPHA_EXT: + case GL_SRGB8_ALPHA8_EXT: + case GL_SRGB_EXT: + case GL_SRGB8_EXT: + case GL_SLUMINANCE_ALPHA_EXT: + case GL_SLUMINANCE8_ALPHA8_EXT: + case GL_SLUMINANCE_EXT: + case GL_SLUMINANCE8_EXT: + return haveSRGB; +#endif + default: + return true; + } +} + + +// Determine if the ith pixel is in the upper-right quadrant of the +// rectangle of size 'width' x 'height'. +static bool +IsUpperRight(int i, int width, int height) +{ + const int y = i / width, x = i % width; + return (x >= width / 2 && y >= height / 2); +} + + + +// Create an image buffer and fill it so that a single image channel is +// the max value (1.0) while the other channels are zero. For example, +// if fillComponent==2 and we're filling a four-component image, the +// pixels will be (0, 0, max, 0). +// +// We always leave the upper-right quadrant black/zero. This is to help +// detect any image conversion issues related to stride, packing, etc. +static GLubyte * +MakeImage(int width, int height, GLenum format, GLenum type, + int fillComponent) +{ + assert(fillComponent < 4); + + if (IsPackedType(type)) { + const int bpp = SizeofType(type); + GLubyte *image = new GLubyte [width * height * bpp]; + GLuint masks[4]; + int pos[4]; + int i; + + ComponentMasks(type, masks); + ComponentPositions(format, pos); + + const GLuint value = masks[fillComponent]; + + switch (bpp) { + case 1: + for (i = 0; i < width * height; i++) { + if (IsUpperRight(i, width, height)) + image[i] = 0; + else + image[i] = (GLubyte) value; + } + break; + case 2: + { + GLushort *image16 = (GLushort *) image; + for (i = 0; i < width * height; i++) { + if (IsUpperRight(i, width, height)) + image16[i] = 0; + else + image16[i] = (GLushort) value; + } + } + break; + case 4: + { + GLuint *image32 = (GLuint *) image; + for (i = 0; i < width * height; i++) { + if (IsUpperRight(i, width, height)) + image32[i] = 0; + else + image32[i] = (GLuint) value; + } + } + break; + default: + abort(); + } + + return image; + } + else { + const int comps = NumberOfComponentsInFormat(format); + const int bpp = comps * SizeofType(type); + assert(bpp > 0); + GLubyte *image = new GLubyte [width * height * bpp]; + int i; + + switch (type) { + case GL_UNSIGNED_BYTE: + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + image[i] = 0xff; + else + image[i] = 0x0; + } + break; + case GL_BYTE: + { + GLbyte *b = (GLbyte *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + b[i] = 0x7f; + else + b[i] = 0x0; + } + } + break; + case GL_UNSIGNED_SHORT: + { + GLushort *us = (GLushort *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + us[i] = 0xffff; + else + us[i] = 0x0; + } + } + break; + case GL_SHORT: + { + GLshort *s = (GLshort *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + s[i] = 0x7fff; + else + s[i] = 0x0; + } + } + break; + case GL_UNSIGNED_INT: + { + GLuint *ui = (GLuint *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + ui[i] = 0xffffffff; + else + ui[i] = 0x0; + } + } + break; + case GL_INT: + { + GLint *in = (GLint *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + in[i] = 0x7fffffff; + else + in[i] = 0x0; + } + } + break; + case GL_FLOAT: + { + GLfloat *f = (GLfloat *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + f[i] = 1.0; + else + f[i] = 0.0; + } + } + break; +#ifdef GL_ARB_half_float_pixel + case GL_HALF_FLOAT_ARB: + { + GLhalfARB *f = (GLhalfARB *) image; + for (i = 0; i < width * height * comps; i++) { + if (i % comps == fillComponent && !IsUpperRight(i/comps, width, height)) + f[i] = 0x3c00; /* == 1.0 */ + else + f[i] = 0; + } + } + break; +#endif + default: + abort(); + } + return image; + } +} + + +bool +PixelFormatsTest::CheckError(const char *where) const +{ + GLint err = glGetError(); + if (err) { + char msg[1000]; + sprintf(msg, "GL Error: %s (0x%x) in %s\n", + gluErrorString(err), err, where); + env->log << msg; + return true; + } + return false; +} + + +// Draw the given image, either as a texture quad or glDrawPixels. +// Return true for success, false if GL error detected. +bool +PixelFormatsTest::DrawImage(int width, int height, + GLenum format, GLenum type, GLint intFormat, + const GLubyte *image) const +{ + if (intFormat) { + glEnable(GL_TEXTURE_2D); + glViewport(0, 0, width, height); + glTexImage2D(GL_TEXTURE_2D, 0, intFormat, width, height, 0, + format, type, image); + if (CheckError("glTexImage2D")) + return false; +#if USE_FRAG_PROG + glEnable(GL_FRAGMENT_PROGRAM_ARB); +#endif + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex2f(-1, -1); + glTexCoord2f(1, 0); glVertex2f(1, -1); + glTexCoord2f(1, 1); glVertex2f(1, 1); + glTexCoord2f(0, 1); glVertex2f(-1, 1); + glEnd(); + glDisable(GL_TEXTURE_2D); +#if USE_FRAG_PROG + glDisable(GL_FRAGMENT_PROGRAM_ARB); +#endif + } + else { + // glDrawPixels + glDrawPixels(width, height, format, type, image); + if (CheckError("glDrawPixels")) + return false; + } + return true; +} + + +static bool +ColorsEqual(const GLubyte img[4], const GLubyte expected[4]) +{ + const int tolerance = 1; + if ((abs(img[0] - expected[0]) > tolerance) || + (abs(img[1] - expected[1]) > tolerance) || + (abs(img[2] - expected[2]) > tolerance) || + (abs(img[3] - expected[3]) > tolerance)) { + return false; + } + else { + return true; + } +} + + +// Compute the expected RGBA color we're expecting to find with glReadPixels +// if the texture was defined with the given image format and texture +// internal format. 'testChan' indicates which of the srcFormat's image +// channels was set (to 1.0) when the image was filled. +void +PixelFormatsTest::ComputeExpected(GLenum srcFormat, int testChan, + GLint intFormat, GLubyte exp[4]) const +{ + const GLenum baseIntFormat = BaseTextureFormat(intFormat); + + switch (srcFormat) { + + case GL_RGBA: + case GL_BGRA: +#ifdef GL_EXT_abgr + case GL_ABGR_EXT: +#endif + assert(testChan < 4); + switch (baseIntFormat) { + case 0: // == glReadPixels + // fallthrough + case GL_RGBA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 0; + exp[testChan] = 255; + break; + case GL_RGB: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[testChan] = 255; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = testChan == 3 ? 255 : 0; + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = testChan == 3 ? 255 : 0; + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = + exp[3] = testChan == 0 ? 255 : 0; + break; + default: + abort(); + } + break; + + case GL_RGB: + case GL_BGR: + assert(testChan < 3); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[testChan] = 255; + exp[3] = 255; // texture's alpha + break; + case GL_RGB: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[testChan] = 255; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = 255; // texture's alpha + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = + exp[3] = testChan == 0 ? 255 : 0; + break; + default: + abort(); + } + break; + + case GL_RED: + assert(testChan == 0); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = 255; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_RGB: + exp[0] = 255; + exp[1] = 0; + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = 255; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = + exp[1] = + exp[2] = 255; + exp[3] = 255; // texture's alpha + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = 255; + exp[3] = 255; // texture's alpha + break; + default: + abort(); + } + break; + + case GL_GREEN: + case GL_BLUE: + assert(testChan == 0); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = 0; + exp[1] = 255; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_RGB: + exp[0] = 0; + exp[1] = 255; + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = 0; // texture's alpha + break; + default: + abort(); + } + break; + + case GL_ALPHA: + assert(testChan == 0); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_RGB: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + default: + abort(); + } + break; + + case GL_LUMINANCE: + assert(testChan == 0); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = 255; + exp[1] = 255; + exp[2] = 255; + exp[3] = 255; // texture's alpha + break; + case GL_RGB: + exp[0] = 255; + exp[1] = 255; + exp[2] = 255; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = 0; + exp[1] = 0; + exp[2] = 0; + exp[3] = 255; // texture's alpha + break; + case GL_LUMINANCE: + exp[0] = 255; + exp[1] = 255; + exp[2] = 255; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = 255; + exp[1] = 255; + exp[2] = 255; + exp[3] = 255; // texture's alpha + break; + case GL_INTENSITY: + exp[0] = 255; + exp[1] = 255; + exp[2] = 255; + exp[3] = 255; // texture's alpha + break; + default: + abort(); + } + break; + + case GL_LUMINANCE_ALPHA: + assert(testChan < 2); + switch (baseIntFormat) { + case 0: + case GL_RGBA: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = testChan == 1 ? 255 : 0; + break; + case GL_RGB: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_ALPHA: + exp[0] = + exp[1] = + exp[2] = 0; // fragment color + exp[3] = testChan == 1 ? 255 : 0; + break; + case GL_LUMINANCE: + exp[0] = + exp[1] = + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = defaultAlpha; // fragment alpha or texture alpha + break; + case GL_LUMINANCE_ALPHA: + exp[0] = testChan == 0 ? 255 : 0; + exp[1] = testChan == 0 ? 255 : 0; + exp[2] = testChan == 0 ? 255 : 0; + exp[3] = testChan == 1 ? 255 : 0; + break; + case GL_INTENSITY: + exp[0] = + exp[1] = + exp[2] = + exp[3] = testChan == 0 ? 255 : 0; + break; + default: + abort(); + } + break; + + default: + abort(); + } +} + + +// Read framebuffer and check that region [width x height] is the expected +// solid color, except the upper-right quadrant will always be black/zero. +// comp: which color channel in src image was set (0 = red, 1 = green, +// 2 = blue, 3 = alpha), other channels are zero. +// format is the color format we're testing. +bool +PixelFormatsTest::CheckRendering(int width, int height, int comp, + GLenum format, GLint intFormat) const +{ + const int checkAlpha = alphaBits > 0; + GLubyte *image = new GLubyte [width * height * 4]; + GLboolean ok = 1; + GLubyte expected[4]; + int i; + + assert(comp >= 0 && comp < 4); + + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image); + for (i = 0; i < width * height; i += 4) { + + ComputeExpected(format, comp, intFormat, expected); + if (IsUpperRight(i/4, width, height)) { + expected[0] = + expected[1] = + expected[2] = + expected[3] = 0; + } + + if (!checkAlpha) { + expected[3] = 0xff; + } + + // do the color check + if (!ColorsEqual(image+i, expected)) { + // report failure info + char msg[1000]; + env->log << name; + sprintf(msg, " failed at pixel (%d,%d), color channel %d:\n", + i/width, i%width, comp); + env->log << msg; + sprintf(msg, " Expected: 0x%02x 0x%02x 0x%02x 0x%02x\n", + expected[0], expected[1], expected[2], expected[3]); + env->log << msg; + sprintf(msg, " Found: 0x%02x 0x%02x 0x%02x 0x%02x\n", + image[i + 0], image[i + 1], image[i + 2], image[i + 3]); + env->log << msg; + ok = false; + break; + } + } + delete [] image; + return ok; +} + + + +// Exercise a particular combination of image format, type and internal +// texture format. +// Return true for success, false for failure. +bool +PixelFormatsTest::TestCombination(GLenum format, GLenum type, GLint intFormat) +{ + const int numComps = NumberOfComponentsInFormat(format); + const int width = 16; + const int height = 16; + int colorPos[4]; + ComponentPositions(format, colorPos); + + for (int comp = 0; comp < numComps; comp++) { + if (colorPos[comp] >= 0) { + // make original/incoming image + const int comp2 = colorPos[comp]; + GLubyte *image = MakeImage(width, height, format, type, comp2); + + // render with image (texture / glDrawPixels) + bool ok = DrawImage(width, height, format, type, intFormat, image); + + if (ok) { + // check rendering + ok = CheckRendering(width, height, comp, format, intFormat); + } + + delete [] image; + + if (!ok) { + return false; + } + } + } + + return true; +} + + +// Per visual setup. +void +PixelFormatsTest::setup(void) +{ + haveHalfFloat = GLUtils::haveExtensions("GL_ARB_half_float_pixel"); + haveABGR = GLUtils::haveExtensions("GL_EXT_abgr"); + haveSRGB = GLUtils::haveExtensions("GL_EXT_texture_sRGB"); +#if GL_ARB_texture_env_combine + haveCombine = GLUtils::haveExtensions("GL_ARB_texture_env_combine"); +#else + haveCombine = false; +#endif + + glGetIntegerv(GL_ALPHA_BITS, &alphaBits); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + glColor4f(0, 0, 0, 0); + +#if USE_FRAG_PROG + { + PFNGLPROGRAMSTRINGARBPROC glProgramStringARB_func; + PFNGLBINDPROGRAMARBPROC glBindProgramARB_func; + static const char *progText = + "!!ARBfp1.0\n" + "TEX result.color, fragment.texcoord[0], texture[0], 2D; \n" + "END \n" + ; + glProgramStringARB_func = (PFNGLPROGRAMSTRINGARBPROC) + GLUtils::getProcAddress("glProgramStringARB"); + assert(glProgramStringARB_func); + glBindProgramARB_func = (PFNGLBINDPROGRAMARBPROC) + GLUtils::getProcAddress("glBindProgramARB"); + assert(glBindProgramARB_func); + glBindProgramARB_func(GL_FRAGMENT_PROGRAM_ARB, 1); + glProgramStringARB_func(GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(progText), (const GLubyte *) progText); + if (glGetError()) { + fprintf(stderr, "Bad fragment program, error: %s\n", + (const char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB)); + exit(0); + } + } +#endif +} + + + +// Test all possible image formats, types and internal texture formats. +// Result will indicate number of passes and failures. +void +PixelFormatsTest::runOne(MultiTestResult &r, Window &w) +{ + int testNum = 0; + (void) w; // silence warning + + setup(); + + const unsigned numEnvModes = haveCombine ? 2 : 1; + + for (unsigned envMode = 0; envMode < numEnvModes; envMode++) { + if (envMode == 0) { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + // When the texture internal format is GL_LUMINANCE or GL_RGB, + // GL_REPLACE takes alpha from the fragment, which we set to zero + // with glColor4f(0,0,0,0). +#if USE_FRAG_PROG + defaultAlpha = 255; +#else + defaultAlpha = 0; +#endif + } + else { + assert(haveCombine); +#if GL_ARB_texture_env_combine + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA); +#endif + // For this GL_COMBINE mode, when sampling a texture that does + // not have an alpha channel, alpha is effectively 1.0. + defaultAlpha = 255; + } + + for (unsigned formatIndex = 0; formatIndex < NUM_FORMATS; formatIndex++) { + for (unsigned typeIndex = 0; typeIndex < NUM_TYPES; typeIndex++) { + + if (CompatibleFormatAndType(Formats[formatIndex].Token, + Types[typeIndex].Token)) { + + for (unsigned intFormat = 0; intFormat < NUM_INT_FORMATS; intFormat++) { + + if (!SupportedIntFormat(InternalFormats[intFormat].Token)) + continue; + +#if DEBUG + env->log << "testing " + << testNum + << ":\n"; + env->log << " Format: " << Formats[formatIndex].Name << "\n"; + env->log << " Type: " << Types[typeIndex].Name << "\n"; + env->log << " IntFormat: " << InternalFormats[intFormat].Name << "\n"; + +#endif + bool ok = TestCombination(Formats[formatIndex].Token, + Types[typeIndex].Token, + InternalFormats[intFormat].Token); + + if (!ok) { + // error was reported to log, add format info here: + env->log << " Format: " << Formats[formatIndex].Name << "\n"; + env->log << " Type: " << Types[typeIndex].Name << "\n"; + env->log << " Internal Format: " << InternalFormats[intFormat].Name << "\n"; + env->log << " EnvMode: " << EnvModes[envMode] << "\n"; + r.numFailed++; + } + else { + r.numPassed++; + } + testNum++; + } + } + } + } + } + + r.pass = (r.numFailed == 0); +} + + +// The test object itself: +PixelFormatsTest pixelFormatsTest("pixelFormats", "window, rgb", + "", + "Test that all the various pixel formats/types (like\n" + "GL_BGRA/GL_UNSIGNED_SHORT_4_4_4_4_REV) operate correctly.\n" + "Test both glTexImage and glDrawPixels.\n" + "For textures, also test all the various internal texture formats.\n" + "Thousands of combinations are possible!\n" + ); + + +} // namespace GLEAN diff --git a/tests/glean/tpixelformats.h b/tests/glean/tpixelformats.h new file mode 100644 index 00000000..faffc327 --- /dev/null +++ b/tests/glean/tpixelformats.h @@ -0,0 +1,85 @@ +// 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 + +// Brian Paul September 2006 + +#ifndef __tpixelformats_h__ +#define __tpixelformats_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +#define windowSize 100 + + +class PixelFormatsTest: public MultiTest +{ +public: + PixelFormatsTest(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: + int alphaBits; + int defaultAlpha; // depends on texture env mode + // extensions + bool haveHalfFloat; + bool haveABGR; + bool haveSRGB; + bool haveCombine; + + bool CheckError(const char *where) const; + + bool CompatibleFormatAndType(GLenum format, GLenum datatype) const; + + bool SupportedIntFormat(GLint intFormat) const; + + bool DrawImage(int width, int height, + GLenum format, GLenum type, GLint intFormat, + const GLubyte *image) const; + + void ComputeExpected(GLenum srcFormat, int testChan, + GLint intFormat, GLubyte exp[4]) const; + + bool CheckRendering(int width, int height, int color, + GLenum format, GLint intFormat) const; + + bool TestCombination(GLenum format, GLenum type, GLint intFormat); + + void setup(void); +}; + +} // namespace GLEAN + +#endif // __tpixelformats_h__ + diff --git a/tests/glean/tpointatten.cpp b/tests/glean/tpointatten.cpp new file mode 100644 index 00000000..87919780 --- /dev/null +++ b/tests/glean/tpointatten.cpp @@ -0,0 +1,259 @@ +// 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 + +// tpointatten.h: Test GL_ARB_point_parameters extension. +// Brian Paul 6 October 2005 + + +#include "tpointatten.h" +#include <cassert> +#include <cmath> + + +namespace GLEAN { + +// Max tested point size +#define MAX_SIZE 25.0 + + +/* Clamp X to [MIN,MAX] */ +#define CLAMP( X, MIN, MAX ) ( (X)<(MIN) ? (MIN) : ((X)>(MAX) ? (MAX) : (X)) ) + + +static PFNGLPOINTPARAMETERFVARBPROC PointParameterfvARB = NULL; +static PFNGLPOINTPARAMETERFARBPROC PointParameterfARB = NULL; + + +void +PointAttenuationTest::setup(void) +{ + PointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) + GLUtils::getProcAddress("glPointParameterfvARB"); + assert(PointParameterfvARB); + PointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) + GLUtils::getProcAddress("glPointParameterfARB"); + assert(PointParameterfARB); + + glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedLimits); + glGetFloatv(GL_SMOOTH_POINT_SIZE_RANGE, smoothLimits); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-1.0, 1.0, -1.0, 1.0, -10.0, 10.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + + +void +PointAttenuationTest::reportFailure(GLfloat initSize, + const GLfloat attenuation[3], + GLfloat min, GLfloat max, + GLfloat eyeZ, GLboolean smooth, + GLfloat expected, GLfloat actual) const +{ + env->log << "\tFAILURE:\n"; + env->log << "\tExpected size: " << expected << " Actual size: " << actual << "\n"; + env->log << "\tSize: " << initSize << "\n"; + env->log << "\tMin: " << min << " Max: " << max << "\n"; + env->log << "\tAttenuation: " << attenuation[0] << " " << attenuation[1] << " " << attenuation[2] << "\n"; + env->log << "\tEye Z: " << eyeZ << "\n"; + if (smooth) + env->log << "\tSmooth/antialiased\n"; + else + env->log << "\tAliased\n"; +} + + +void +PointAttenuationTest::reportSuccess(int count, GLboolean smooth) const +{ + env->log << "PASS: " << count; + if (smooth) + env->log << " aliased combinations tested.\n"; + else + env->log << " antialiased combinations tested.\n"; +} + + +// Compute the expected point size given various point state +GLfloat +PointAttenuationTest::expectedSize(GLfloat initSize, + const GLfloat attenuation[3], + GLfloat min, GLfloat max, + GLfloat eyeZ, GLboolean smooth) const +{ + const GLfloat dist = fabs(eyeZ); + const GLfloat atten = sqrt(1.0 / (attenuation[0] + + attenuation[1] * dist + + attenuation[2] * dist * dist)); + + float size = initSize * atten; + + size = CLAMP(size, min, max); + + if (smooth) + size = CLAMP(size, smoothLimits[0], smoothLimits[1]); + else + size = CLAMP(size, aliasedLimits[0], aliasedLimits[1]); + + return size; +} + + +// measure size of rendered point +GLfloat +PointAttenuationTest::measureSize() const +{ + int x = 0; + int y = windowSize / 2; + int w = windowSize; + int h = 1; + GLfloat image[windowSize * 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); + float sum = 0.0; + for (int i = 0; i < w; i++) { + sum += (image[i*3+0] + image[i*3+1] + image[i*3+2]) / 3.0; + } + return sum; +} + + +bool +PointAttenuationTest::testPointRendering(GLboolean smooth) +{ + // epsilon is the allowed size difference in pixels between the + // expected and actual rendering. We should use something tighter + // than 1.2 but 1.2 allows NVIDIA's driver to pass. + const GLfloat epsilon = 1.2; + GLfloat atten[3]; + int count = 0; + + // Enable front buffer if you want to see the rendering + //glDrawBuffer(GL_FRONT); + //glReadBuffer(GL_FRONT); + + if (smooth) { + glEnable(GL_POINT_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else { + glDisable(GL_POINT_SMOOTH); + glDisable(GL_BLEND); + } + + for (int a = 0; a < 3; a++) { + atten[0] = pow(10.0, -a); + for (int b = -2; b < 3; b++) { + atten[1] = (b == -1) ? 0.0 : pow(10.0, -b); + for (int c = -2; c < 3; c++) { + atten[2] = (c == -1) ? 0.0 : pow(10.0, -c); + PointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, atten); + for (float min = 1.0; min < MAX_SIZE; min += 5) { + PointParameterfARB(GL_POINT_SIZE_MIN_ARB, min); + for (float max = min; max < MAX_SIZE; max += 5) { + 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(); + count++; + float expected + = expectedSize(size, atten, min, max, + z, smooth); + float actual = measureSize(); + if (fabs(expected - actual) > epsilon) { + reportFailure(size, atten, min, max, + z, smooth, + expected, actual); + return false; + } + } + } + } + } + } + } + } + reportSuccess(count, smooth); + return true; +} + +void +PointAttenuationTest::runOne(BasicResult &r, Window &w) +{ + (void) w; // silence warning + r.pass = true; + errorCode = 0; + errorPos = NULL; + + setup(); + + if (r.pass) + r.pass = testPointRendering(GL_FALSE); + if (r.pass) + r.pass = testPointRendering(GL_TRUE); +} + + +void +PointAttenuationTest::logOne(BasicResult &r) +{ + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} + + +// constructor +PointAttenuationTest::PointAttenuationTest(const char *testName, + const char *filter, + const char *extensions, + const char *description) + : BasicTest(testName, filter, extensions, description) +{ + fWidth = windowSize; + fHeight = windowSize; +} + + + +// The test object itself: +PointAttenuationTest pointAttenuationTest("pointAtten", "window, rgb", + "GL_ARB_point_parameters", + "Test point size attenuation with the GL_ARB_point_parameters extension.\n"); + + + +} // namespace GLEAN diff --git a/tests/glean/tpointatten.h b/tests/glean/tpointatten.h new file mode 100644 index 00000000..4ee032d5 --- /dev/null +++ b/tests/glean/tpointatten.h @@ -0,0 +1,78 @@ +// 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 + +// tpointatten.h: Test GL_ARB_point_parameters extension. +// Brian Paul 6 October 2005 + +#ifndef __tpointatten_h__ +#define __tpointatten_h__ + +#include "tbasic.h" + +namespace GLEAN { + +#define drawingSize 101 // yes, odd +#define windowSize (drawingSize + 2) + + +class PointAttenuationTest: public BasicTest +{ +public: + PointAttenuationTest(const char *testName, + const char *filter, + const char *extensions, + const char *description); + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + +private: + GLenum errorCode; + const char *errorPos; + GLfloat aliasedLimits[2]; // min/max + GLfloat smoothLimits[2]; // min/max + + void setup(void); + bool testPointRendering(GLboolean smooth); + void reportFailure(GLfloat initSize, + const GLfloat attenuation[3], + GLfloat min, GLfloat max, + GLfloat eyeZ, GLboolean smooth, + GLfloat expected, GLfloat actual) const; + void reportSuccess(int count, GLboolean smooth) const; + GLfloat expectedSize(GLfloat initSize, + const GLfloat attenuation[3], + GLfloat min, GLfloat max, + GLfloat eyeZ, GLboolean smooth) const; + GLfloat measureSize() const; +}; + +} // namespace GLEAN + +#endif // __tpointatten_h__ + diff --git a/tests/glean/treadpix.cpp b/tests/glean/treadpix.cpp new file mode 100644 index 00000000..bc1b3d07 --- /dev/null +++ b/tests/glean/treadpix.cpp @@ -0,0 +1,970 @@ +// 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 +// 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 + +// treadpix.cpp: implementation of ReadPixels tests + +#include <cmath> +#include <iomanip> +#include "misc.h" +#include "rand.h" +#include "treadpix.h" + + +namespace GLEAN { + +void +ReadPixSanityTest::checkRGBA(ReadPixSanityResult& r, Window& w) { + DrawingSurfaceConfig& config = *r.config; + RandomBitsDouble rRand(config.r, 1066); + RandomBitsDouble gRand(config.g, 1492); + RandomBitsDouble bRand(config.b, 1776); + RandomBitsDouble aRand((config.a? config.a: 1), 1789); + int thresh = 1; + + r.passRGBA = true; + r.errRGBA = 0.0; + for (int i = 0; i < 100 && r.passRGBA; ++i) { + // Generate a random color and use it to clear the color buffer: + float expected[4]; + expected[0] = rRand.next(); + expected[1] = gRand.next(); + expected[2] = bRand.next(); + expected[3] = aRand.next(); + glClearColor(expected[0],expected[1],expected[2],expected[3]); + glClear(GL_COLOR_BUFFER_BIT); + + // If the color buffer doesn't have an alpha channel, then + // the spec requires the readback value to be 1.0: + if (!config.a) + expected[3] = 1.0; + + // Read the buffer: + GLfloat buf[READPIX_SANITY_WIN_SIZE][READPIX_SANITY_WIN_SIZE][4]; + glReadPixels(0, 0, READPIX_SANITY_WIN_SIZE, + READPIX_SANITY_WIN_SIZE, GL_RGBA, GL_FLOAT, buf); + + // Now compute the error for each pixel, and record the + // worst one we find: + for (int y = 0; y < READPIX_SANITY_WIN_SIZE; ++y) + for (int x = 0; x < READPIX_SANITY_WIN_SIZE; ++x) { + GLfloat dr = abs(buf[y][x][0] - expected[0]); + GLfloat dg = abs(buf[y][x][1] - expected[1]); + GLfloat db = abs(buf[y][x][2] - expected[2]); + GLfloat da = abs(buf[y][x][3] - expected[3]); + double err = + max(ErrorBits(dr, config.r), + max(ErrorBits(dg, config.g), + max(ErrorBits(db, config.b), + ErrorBits(da, + config.a? config.a: thresh+1)))); + // The "thresh+1" fudge above is + // needed to force the error to + // be greater than the threshold + // in the case where there is no + // alpha channel. Without it the + // error would be just equal to + // the threshold, and the test + // would spuriously pass. + if (err > r.errRGBA) { + r.xRGBA = x; + r.yRGBA = y; + r.errRGBA = err; + for (int j = 0; j < 4; ++j) { + r.expectedRGBA[j] = expected[j]; + r.actualRGBA[j] = buf[y][x][j]; + } + } + } + + if (r.errRGBA > thresh) + r.passRGBA = false; + w.swap(); + } +} // ReadPixSanityTest::checkRGBA + +void +ReadPixSanityTest::checkDepth(ReadPixSanityResult& r, Window& w) { + DrawingSurfaceConfig& config = *r.config; + RandomDouble dRand(35798); + int thresh = 1; + + r.passDepth = true; + r.errDepth = 0.0; + for (int i = 0; i < 100 && r.passDepth; ++i) { + // Generate a random depth and use it to clear the depth buffer: + GLdouble expected = dRand.next(); + glClearDepth(expected); + glClear(GL_DEPTH_BUFFER_BIT); + + // Because glReadPixels won't return data of type GLdouble, + // there's no straightforward portable way to deal with + // integer depth buffers that are deeper than 32 bits or + // floating-point depth buffers that have higher precision + // than a GLfloat. Since this is just a sanity check, we'll + // use integer readback and settle for 32 bits at best. + GLuint buf[READPIX_SANITY_WIN_SIZE][READPIX_SANITY_WIN_SIZE]; + glReadPixels(0, 0, READPIX_SANITY_WIN_SIZE, + READPIX_SANITY_WIN_SIZE, GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT, buf); + + // Now compute the error for each pixel, and record the + // worst one we find: + for (int y = 0; y < READPIX_SANITY_WIN_SIZE; ++y) + for (int x = 0; x < READPIX_SANITY_WIN_SIZE; ++x) { + GLfloat dd = abs(buf[y][x]/4294967295.0 + - expected); + double err = ErrorBits(dd, config.z); + if (err > r.errDepth) { + r.xDepth = x; + r.yDepth = y; + r.errDepth = err; + r.expectedDepth = expected; + r.actualDepth = buf[y][x]/4294967295.0; + } + } + + if (r.errDepth > thresh) + r.passDepth = false; + w.swap(); + } +} // ReadPixSanityTest::checkDepth + +void +ReadPixSanityTest::checkStencil(ReadPixSanityResult& r, Window& w) { + DrawingSurfaceConfig& config = *r.config; + RandomBits sRand(config.s, 10101); + + r.passStencil = true; + for (int i = 0; i < 100 && r.passStencil; ++i) { + GLuint expected = sRand.next(); + glClearStencil(expected); + glClear(GL_STENCIL_BUFFER_BIT); + + GLuint buf[READPIX_SANITY_WIN_SIZE][READPIX_SANITY_WIN_SIZE]; + glReadPixels(0, 0, READPIX_SANITY_WIN_SIZE, + READPIX_SANITY_WIN_SIZE, GL_STENCIL_INDEX, + GL_UNSIGNED_INT, buf); + + for (int y = 0; y < READPIX_SANITY_WIN_SIZE && r.passStencil; + ++y) + for (int x = 0; x < READPIX_SANITY_WIN_SIZE; ++x) + if (buf[y][x] != expected) { + r.passStencil = false; + r.xStencil = x; + r.yStencil = y; + r.expectedStencil = expected; + r.actualStencil = buf[y][x]; + break; + } + + w.swap(); + } +} // ReadPixSanityTest::checkStencil + +void +ReadPixSanityTest::checkIndex(ReadPixSanityResult& r, Window& w) { + DrawingSurfaceConfig& config = *r.config; + RandomBits iRand(config.bufSize, 2); + + r.passIndex = true; + for (int i = 0; i < 100 && r.passIndex; ++i) { + GLuint expected = iRand.next(); + glClearIndex(expected); + glClear(GL_COLOR_BUFFER_BIT); + + GLuint buf[READPIX_SANITY_WIN_SIZE][READPIX_SANITY_WIN_SIZE]; + glReadPixels(0, 0, READPIX_SANITY_WIN_SIZE, + READPIX_SANITY_WIN_SIZE, GL_COLOR_INDEX, + GL_UNSIGNED_INT, buf); + + for (int y = 0; y < READPIX_SANITY_WIN_SIZE && r.passIndex; ++y) + for (int x = 0; x < READPIX_SANITY_WIN_SIZE; ++x) + if (buf[y][x] != expected) { + r.passIndex = false; + r.xIndex = x; + r.yIndex = y; + r.expectedIndex = expected; + r.actualIndex = buf[y][x]; + break; + } + + w.swap(); + } +} // ReadPixSanityTest::checkIndex + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ReadPixSanityTest::runOne(ReadPixSanityResult& r, GLEAN::Window& w) { + + // Many (if not most) other tests need to read the contents of + // the framebuffer to determine if the correct image has been + // drawn. Obviously this is a waste of time if the basic + // functionality of glReadPixels isn't working. + // + // This test does a "sanity" check of glReadPixels. Using as + // little of the GL as practicable, it writes a random value + // in the framebuffer, reads it, and compares the value read + // with the value written. + + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + glPixelTransferi(GL_MAP_COLOR, GL_FALSE); + glPixelTransferi(GL_MAP_STENCIL, GL_FALSE); + glPixelTransferi(GL_INDEX_SHIFT, 0); + glPixelTransferi(GL_INDEX_OFFSET, 0); + glPixelTransferf(GL_RED_SCALE, 1.0); + glPixelTransferf(GL_GREEN_SCALE, 1.0); + glPixelTransferf(GL_BLUE_SCALE, 1.0); + glPixelTransferf(GL_ALPHA_SCALE, 1.0); + glPixelTransferf(GL_DEPTH_SCALE, 1.0); + glPixelTransferf(GL_RED_BIAS, 0.0); + glPixelTransferf(GL_GREEN_BIAS, 0.0); + glPixelTransferf(GL_BLUE_BIAS, 0.0); + glPixelTransferf(GL_ALPHA_BIAS, 0.0); + glPixelTransferf(GL_DEPTH_BIAS, 0.0); + + glDisable(GL_SCISSOR_TEST); + glDisable(GL_DITHER); + + glIndexMask(~0); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + glStencilMask(~0); + + if (r.config->canRGBA) + checkRGBA(r, w); + if (r.config->z) + checkDepth(r, w); + if (r.config->s) + checkStencil(r, w); + + r.pass = r.passRGBA & r.passDepth & r.passStencil & r.passIndex; +} // ReadPixSanityTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ReadPixSanityTest::compareOne(ReadPixSanityResult& oldR, ReadPixSanityResult& newR) { + comparePassFail(oldR, newR); + summarize("RGBA: ", oldR.passRGBA, newR.passRGBA); + summarize("Depth: ", oldR.passDepth, newR.passDepth); + summarize("Stencil: ", oldR.passStencil, newR.passStencil); + summarize("Index: ", oldR.passIndex, newR.passIndex); + if (env->options.verbosity) { + if (!oldR.passRGBA && !newR.passRGBA) { + if (oldR.xRGBA != newR.xRGBA + || oldR.yRGBA != newR.yRGBA) + env->log << "\tRGBA: " + << env->options.db1Name + << " failed at (" + << oldR.xRGBA + << ", " + << oldR.yRGBA + << "); " + << env->options.db2Name + << " failed at (" + << newR.xRGBA + << ", " + << newR.yRGBA + << ").\n" + ; + if (oldR.errRGBA != newR.errRGBA) + env->log << "\tRGBA: " + << env->options.db1Name + << " had " + << oldR.errRGBA + << " bits in error; " + << env->options.db2Name + << " had " + << newR.errRGBA + << " bits in error.\n" + ; + } + if (!oldR.passDepth && !newR.passDepth) { + if (oldR.xDepth != newR.xDepth + || oldR.yDepth != newR.yDepth) + env->log << "\tDepth: " + << env->options.db1Name + << " failed at (" + << oldR.xDepth + << ", " + << oldR.yDepth + << "); " + << env->options.db2Name + << " failed at (" + << newR.xDepth + << ", " + << newR.yDepth + << ").\n" + ; + if (oldR.errDepth != newR.errDepth) + env->log << "\tDepth: " + << env->options.db1Name + << " had " + << oldR.errDepth + << " bits in error; " + << env->options.db2Name + << " had " + << newR.errDepth + << " bits in error.\n" + ; + } + if (!oldR.passStencil && !newR.passStencil) { + if (oldR.xStencil != newR.xStencil + || oldR.yStencil != newR.yStencil) + env->log << "\tStencil: " + << env->options.db1Name + << " failed at (" + << oldR.xStencil + << ", " + << oldR.yStencil + << "); " + << env->options.db2Name + << " failed at (" + << newR.xStencil + << ", " + << newR.yStencil + << ").\n" + ; + } + if (!oldR.passIndex && !newR.passIndex) { + if (oldR.xIndex != newR.xIndex + || oldR.yIndex != newR.yIndex) + env->log << "\tIndex: " + << env->options.db1Name + << " failed at (" + << oldR.xIndex + << ", " + << oldR.yIndex + << "); " + << env->options.db2Name + << " failed at (" + << newR.xIndex + << ", " + << newR.yIndex + << ").\n" + ; + } + } +} // ReadPixSanityTest::compareOne + +void +ReadPixSanityTest::summarize(char* label, bool oldPass, bool newPass) { + if (oldPass == newPass) { + if (env->options.verbosity) + env->log << "\t" + << label + << "both " + << (oldPass? "passed": "failed") + << ".\n"; + } else { + env->log << "\t" + << label + << env->options.db1Name + << " " + << (oldPass? "passed": "failed") + << "; " + << env->options.db2Name + << " " + << (newPass? "passed": "failed") + << ".\n" + ; + } +} // ReadPixSanityTest::summarize + +void +ReadPixSanityTest::logOne(ReadPixSanityResult& r) { + logPassFail(r); + logConcise(r); + + if (!r.passRGBA) { + env->log << "\tRGB(A) worst-case error was " + << r.errRGBA << " bits at (" + << r.xRGBA << ", " << r.yRGBA << ")\n"; + env->log << "\t\texpected (" + << r.expectedRGBA[0] << ", " + << r.expectedRGBA[1] << ", " + << r.expectedRGBA[2] << ", " + << r.expectedRGBA[3] << ")\n\t\tgot (" + << r.actualRGBA[0] << ", " + << r.actualRGBA[1] << ", " + << r.actualRGBA[2] << ", " + << r.actualRGBA[3] << ")\n" + ; + } + if (!r.passDepth) { + env->log << "\tDepth worst-case error was " + << r.errDepth << " bits at (" + << r.xDepth << ", " << r.yDepth << ")\n"; + env->log << "\t\texpected " + << r.expectedDepth + << "; got " + << r.actualDepth + << ".\n" + ; + } + if (!r.passStencil) { + env->log << "\tStencil expected " + << r.expectedStencil + << "; got " + << r.actualStencil + << ".\n" + ; + } + if (!r.passIndex) { + env->log << "\tIndex expected " + << r.expectedIndex + << "; got " + << r.actualIndex + << ".\n" + ; + } + if (env->options.verbosity) { + if (r.config->canRGBA) + env->log << "\tRGBA largest readback error was " + << r.errRGBA + << " bits\n"; + if (r.config->z) + env->log << "\tDepth largest readback error was " + << r.errDepth + << " bits\n"; + } +} // ReadPixSanityTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +ReadPixSanityTest +readPixSanityTest("readPixSanity", "1", + + "This test performs a sanity check of glReadPixels, using as\n" + "few other portions of the GL as possible. If this test fails,\n" + "it may be pointless to run other tests, since so many of them\n" + "depend on reading the contents of the framebuffer to determine\n" + "if they pass.\n" + "\n" + "The test works by using glClear to fill the framebuffer with a\n" + "randomly-chosen value, reading the contents of the\n" + "framebuffer, and comparing the actual contents with the\n" + "expected contents. RGB, RGBA, color index, stencil, and depth\n" + "buffers (whichever are applicable to the current rendering\n" + "context) are checked. The test passes if the actual contents\n" + "are within 1 LSB of the expected contents.\n" + + ); +}; // namespace GLEAN + + + +//////////////////////////////////////////////////////////////////////////////// +// ExactRGBATest +// Verifies that unsigned RGBA values written to a framebuffer with +// sufficient depth are not altered by the OpenGL implementation. +//////////////////////////////////////////////////////////////////////////////// + +namespace { + +template<class T> +void +check(GLEAN::ExactRGBAResult::Flavor& r, GLEAN::DrawingSurfaceConfig& config, + GLenum type, int roundingMode) { + unsigned size = EXACT_RGBA_WIN_SIZE - 2; + unsigned nPixels = size * size; + unsigned nComponents = 4 * nPixels; + T* expected = new T[nComponents]; + T* actual = new T[nComponents]; + GLEAN::RandomBits rand(32, 1929); + unsigned x; + unsigned y; + T* p; + T* q; + + // Draw random colors into the window, recording the raw + // color data in the array "expected": + p = expected; + for (y = 0; y < size; ++y) + for (x = 0; x < size; ++x) { + p[0] = rand.next(); // r + p[1] = rand.next(); // g + p[2] = rand.next(); // b + p[3] = rand.next(); // a + switch (type) { + case GL_UNSIGNED_BYTE: + glColor4ubv(reinterpret_cast<GLubyte*> + (p)); + break; + case GL_UNSIGNED_SHORT: + glColor4usv(reinterpret_cast<GLushort*> + (p)); + break; + case GL_UNSIGNED_INT: + glColor4uiv(reinterpret_cast<GLuint*> + (p)); + break; + } + glBegin(GL_QUADS); + glVertex2i(x + 1, y + 1); + glVertex2i(x + 2, y + 1); + glVertex2i(x + 2, y + 2); + glVertex2i(x + 1, y + 2); + glEnd(); + p += 4; + } + + // Read the relevant contents of the window into the array + // "actual": + glReadPixels(1, 1, size, size, GL_RGBA, type, actual); + + // Find masks that select only the high-order bits that should + // be common to both the host representation and the framebuffer + // representation: + int hostBits; + switch (type) { + case GL_UNSIGNED_BYTE: + hostBits = 8; + break; + case GL_UNSIGNED_SHORT: + hostBits = 16; + break; + case GL_UNSIGNED_INT: + 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)); + + // 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; + + // Compare masked actual and expected values, and record the + // worst-case error location and magnitude. + r.err = 0; + p = expected; + q = actual; + for (y = 0; y < size; ++y) + for (x = 0; x < size; ++x) { + T e[4]; + 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]; + } + 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; + } + if (err > r.err) { + r.x = x; + r.y = y; + r.err = err; + 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]; + } + } + } + p += 4; + q += 4; + } + + // We only pass if the maximum error was zero. + r.pass = (r.err == 0); + + delete[] expected; + delete[] actual; +} + +}; + + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ExactRGBATest::runOne(ExactRGBAResult& r, GLEAN::Window& w) { + + // Many other tests depend on the ability of the OpenGL + // implementation to store fixed-point RGBA values in the + // framebuffer, and to read back exactly the value that + // was stored. The OpenGL spec guarantees that this will work + // under certain conditions, which are spelled out in section + // 2.13.9 in the 1.2.1 version of the spec: + // + // Suppose that lighting is disabled, the color associated + // with a vertex has not been clipped, and one of + // [gl]Colorub, [gl]Colorus, or [gl]Colorui was used to + // specify that color. When these conditions are + // satisfied, an RGBA component must convert to a value + // that matches the component as specified in the Color + // command: if m [the number of bits in the framebuffer + // color channel] is less than the number of bits b with + // which the component was specified, then the converted + // value must equal the most significant m bits of the + // specified value; otherwise, the most significant b bits + // of the converted value must equal the specified value. + // + // This test attempts to verify that behavior. + + + // 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; + } + } + + // Much of this state should already be set, if the defaults are + // implemented correctly. We repeat the setting here in order + // to insure reasonable results when there are bugs. + + GLUtils::useScreenCoords(EXACT_RGBA_WIN_SIZE, EXACT_RGBA_WIN_SIZE); + + glDisable(GL_LIGHTING); + + glFrontFace(GL_CCW); + + glDisable(GL_COLOR_MATERIAL); + + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); + glDisable(GL_TEXTURE_3D); + + glDisable(GL_CLIP_PLANE0); + glDisable(GL_CLIP_PLANE1); + glDisable(GL_CLIP_PLANE2); + glDisable(GL_CLIP_PLANE3); + glDisable(GL_CLIP_PLANE4); + glDisable(GL_CLIP_PLANE5); + + glDisable(GL_FOG); + + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glPixelStorei(GL_PACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_PACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + glPixelTransferi(GL_MAP_COLOR, GL_FALSE); + glPixelTransferi(GL_MAP_STENCIL, GL_FALSE); + glPixelTransferi(GL_INDEX_SHIFT, 0); + glPixelTransferi(GL_INDEX_OFFSET, 0); + glPixelTransferf(GL_RED_SCALE, 1.0); + glPixelTransferf(GL_GREEN_SCALE, 1.0); + glPixelTransferf(GL_BLUE_SCALE, 1.0); + glPixelTransferf(GL_ALPHA_SCALE, 1.0); + glPixelTransferf(GL_DEPTH_SCALE, 1.0); + glPixelTransferf(GL_RED_BIAS, 0.0); + glPixelTransferf(GL_GREEN_BIAS, 0.0); + glPixelTransferf(GL_BLUE_BIAS, 0.0); + 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); + w.swap(); + check<GLushort>(r.us, *(r.config), GL_UNSIGNED_SHORT, roundingMode); + w.swap(); + check<GLuint>(r.ui, *(r.config), GL_UNSIGNED_INT, roundingMode); + w.swap(); + r.pass = r.ub.pass && r.us.pass && r.ui.pass; + r.skipped = false; +} // ExactRGBATest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ExactRGBATest::compareOne(ExactRGBAResult& oldR, ExactRGBAResult& newR) { + if (oldR.skipped || newR.skipped) { + env->log << name + << ((oldR.skipped && newR.skipped)? ": SAME " + : ": DIFF ") + << newR.config->conciseDescription() + << '\n'; + if (oldR.skipped) + env->log << "\t" + << env->options.db1Name + << " skipped\n"; + if (newR.skipped) + env->log << "\t" + << env->options.db2Name + << " skipped\n"; + env->log << "\tNo comparison is possible.\n"; + return; + } + + if (oldR.ub == newR.ub && oldR.us == newR.us && oldR.ui == newR.ui) { + if (env->options.verbosity) + env->log << name + << ": SAME " + << newR.config->conciseDescription() + << '\n' + << (oldR.pass + ? "\tBoth PASS\n" + : "\tBoth FAIL\n"); + } else { + env->log << name + << ": DIFF " + << newR.config->conciseDescription() + << '\n' +#if 1 + << '\t' + << env->options.db1Name + << (oldR.pass? " PASS, ": " FAIL, ") + << env->options.db2Name + << (newR.pass? " PASS\n": " FAIL\n"); +#endif + ; + } + + summarize("Unsigned byte: ", oldR.ub, newR.ub); + summarize("Unsigned short: ", oldR.us, newR.us); + summarize("Unsigned int: ", oldR.ui, newR.ui); +} // ExactRGBATest::compareOne + +void +ExactRGBATest::summarize(const char* label, const ExactRGBAResult::Flavor& o, + const ExactRGBAResult::Flavor& n) { + if (o == n) { + if (env->options.verbosity) + env->log << "\t" + << label + << "both " + << (o.pass? "passed": "failed") + << ".\n"; + } else { + if (o.pass != n.pass) + env->log << "\t" + << label + << env->options.db1Name + << " " + << (o.pass? "passed": "failed") + << "; " + << env->options.db2Name + << " " + << (n.pass? "passed": "failed") + << ".\n" + ; + if (o.x != n.x || o.y != n.y) + env->log << "\t" + << env->options.db1Name + << " failed at (" + << o.x + << ", " + << o.y + << "); " + << env->options.db2Name + << " failed at (" + << n.x + << ", " + << n.y + << ")\n" + ; + if (o.err != n.err) + env->log << "\t" + << env->options.db1Name + << " had max error " + << o.err + << "; " + << env->options.db2Name + << " had max error " + << n.err + << "\n" + ; + if (o.expected[0] != n.expected[0] + || o.expected[1] != n.expected[1] + || o.expected[2] != n.expected[2] + || o.expected[3] != n.expected[3]) + env->log << "\tExpected values differ.\n"; + if (o.actual[0] != n.actual[0] + || o.actual[1] != n.actual[1] + || o.actual[2] != n.actual[2] + || o.actual[3] != n.actual[3]) + env->log << "\tActual values differ.\n"; + } +} // ExactRGBATest::summarize + +void +ExactRGBATest::logFlavor(const char* label, const ExactRGBAResult::Flavor& r) { + if (!r.pass) { + env->log << "\t" + << label + << " worst-case error was 0x" + << hex + << r.err << " at (" + << dec + << r.x << ", " << r.y << ")\n"; + env->log << "\t\texpected (0x" + << hex + << r.expected[0] << ", 0x" + << r.expected[1] << ", 0x" + << r.expected[2] << ", 0x" + << r.expected[3] << ")\n\t\tgot (0x" + << 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" + << dec + ; + } +} // ExactRGBATest::logFlavor + +void +ExactRGBATest::logOne(ExactRGBAResult& r) { + if (r.skipped) { + env->log << name << ": NOTE "; + logConcise(r); + env->log << "\tTest skipped; prerequisite test " + << readPixSanityTest.name + << " failed or was not run\n"; + return; + } + + logPassFail(r); + logConcise(r); + + logFlavor("Unsigned byte ", r.ub); + logFlavor("Unsigned short", r.us); + logFlavor("Unsigned int ", r.ui); +} // ExactRGBATest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +Test* exactRGBATestPrereqs[] = {&readPixSanityTest, 0}; +ExactRGBATest +exactRGBATest("exactRGBA", "rgb", exactRGBATestPrereqs, + + "The OpenGL specification requires that under certain conditions\n" + "(e.g. lighting disabled, no clipping, no dithering, etc.) colors\n" + "specified as unsigned integers are represented *exactly* in the\n" + "framebuffer (up to the number of bits common to both the\n" + "original color and the framebuffer color channel). Several glean\n" + "tests depend on this behavior, so this test is a prerequisite for\n" + "them.\n" + "\n" + "This test works by drawing many small quadrilaterals whose\n" + "colors are specified by glColorub, glColorus, and glColorui;\n" + "reading back the resulting image; and comparing the colors read\n" + "back to the colors written. The high-order bits shared by the\n" + "source representation of the colors and the framebuffer\n" + "representation of the colors must agree exactly for the test to\n" + "pass.\n" + + ); + + +} // namespace GLEAN diff --git a/tests/glean/treadpix.h b/tests/glean/treadpix.h new file mode 100644 index 00000000..07106883 --- /dev/null +++ b/tests/glean/treadpix.h @@ -0,0 +1,322 @@ +// 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 +// 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 + + +// treadpix.h: ReadPixels tests. + +#ifndef __treadpix_h__ +#define __treadpix_h__ + +#include <iomanip> +#include "tbase.h" + +namespace GLEAN { + +class ReadPixSanityResult: public BaseResult { +public: + bool pass; + + bool passRGBA; + int xRGBA; + int yRGBA; + float errRGBA; + GLfloat expectedRGBA[4]; + GLfloat actualRGBA[4]; + + bool passDepth; + int xDepth; + int yDepth; + float errDepth; + GLdouble expectedDepth; + GLdouble actualDepth; + + bool passStencil; + int xStencil; + int yStencil; + GLuint expectedStencil; + GLuint actualStencil; + + bool passIndex; + int xIndex; + int yIndex; + GLuint expectedIndex; + GLuint actualIndex; + + ReadPixSanityResult() { + pass = true; + + passRGBA = true; + xRGBA = yRGBA = 0; + errRGBA = 0.0; + expectedRGBA[0] = expectedRGBA[1] = expectedRGBA[2] + = expectedRGBA[3] = 0.0; + actualRGBA[0] = actualRGBA[1] = actualRGBA[2] + = actualRGBA[3] = 0.0; + + passDepth = true; + xDepth = yDepth = 0; + errDepth = 0.0; + expectedDepth = 0.0; + actualDepth = 0.0; + + passStencil = true; + xStencil = yStencil = 0; + expectedStencil = 0; + actualStencil = 0; + + passIndex = true; + xIndex = yIndex = 0; + expectedIndex = 0; + actualIndex = 0; + } + + void putresults(ostream& s) const { + s + << pass << '\n' + + << passRGBA << '\n' + << xRGBA << ' ' << yRGBA << '\n' + << errRGBA << '\n' + << expectedRGBA[0] << ' ' << expectedRGBA[1] << ' ' + << expectedRGBA[2] << ' ' << expectedRGBA[3] << '\n' + << actualRGBA[0] << ' ' << actualRGBA[1] << ' ' + << actualRGBA[2] << ' ' << actualRGBA[3] << '\n' + + << passDepth << '\n' + << xDepth << ' ' << yDepth << '\n' + << errDepth << '\n' + << setprecision(16) + << expectedDepth << '\n' + << actualDepth << '\n' + + << passStencil << '\n' + << xStencil << ' ' << yStencil << '\n' + << expectedStencil << '\n' + << actualStencil << '\n' + + << passIndex << '\n' + << xIndex << ' ' << yIndex << '\n' + << expectedIndex << '\n' + << actualIndex << '\n' + ; + } + + bool getresults(istream& s) { + s >> pass + + >> passRGBA + >> xRGBA >> yRGBA + >> errRGBA + >> expectedRGBA[0] >> expectedRGBA[1] >> expectedRGBA[2] + >> expectedRGBA[3] + >> actualRGBA[0] >> actualRGBA[1] >> actualRGBA[2] + >> actualRGBA[3] + + >> passDepth + >> xDepth >> yDepth + >> errDepth + >> expectedDepth + >> actualDepth + + >> passStencil + >> xStencil >> yStencil + >> expectedStencil + >> actualStencil + + >> passIndex + >> xIndex >> yIndex + >> expectedIndex + >> actualIndex + ; + return s.good(); + } +}; + +#define READPIX_SANITY_WIN_SIZE 32 + +class ReadPixSanityTest: public BaseTest<ReadPixSanityResult> { +public: + GLEAN_CLASS_WH(ReadPixSanityTest, ReadPixSanityResult, + READPIX_SANITY_WIN_SIZE, READPIX_SANITY_WIN_SIZE); + + void checkRGBA(ReadPixSanityResult& r, Window& w); + void checkDepth(ReadPixSanityResult& r, Window& w); + void checkStencil(ReadPixSanityResult& r, Window& w); + void checkIndex(ReadPixSanityResult& r, Window& w); + void summarize(char* label, bool oldPass, bool newPass); +}; // class ReadPixSanityTest +extern ReadPixSanityTest readPixSanityTest; + + + + +class ExactRGBAResult: public BaseResult { +public: + struct Flavor { + bool pass; + int x; + int y; + GLuint err; + GLuint expected[4]; + GLuint actual[4]; + GLuint written[4]; + GLuint read[4]; + + bool operator== (const Flavor& f) const { + return pass == f.pass + && x == f.x + && y == f.y + && err == f.err + && expected[0] == f.expected[0] + && expected[1] == f.expected[1] + && expected[2] == f.expected[2] + && expected[3] == f.expected[3] + && actual[0] == f.actual[0] + && 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] + ; + } + + Flavor() { + pass = true; + x = y = 0; + err = 0; + 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 { + s + << pass << '\n' + << x << ' ' << y << '\n' + << err << '\n' + << expected[0] << ' ' + << expected[1] << ' ' + << expected[2] << ' ' + << expected[3] << '\n' + << actual[0] << ' ' + << 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) { + s + >> pass + >> x >> y + >> err + >> expected[0] + >> expected[1] + >> expected[2] + >> expected[3] + >> actual[0] + >> actual[1] + >> actual[2] + >> actual[3] + >> written[0] + >> written[1] + >> written[2] + >> written[3] + >> read[0] + >> read[1] + >> read[2] + >> read[3] + ; + } + }; + + bool skipped; + bool pass; + + Flavor ub; + Flavor us; + Flavor ui; + + ExactRGBAResult(): ub(), us(), ui() { + skipped = false; + pass = true; + } + + void putresults(ostream& s) const { + s + << skipped << '\n' + << pass << '\n' + ; + ub.put(s); + us.put(s); + ui.put(s); + } + + bool getresults(istream& s) { + s + >> skipped + >> pass + ; + ub.get(s); + us.get(s); + ui.get(s); + return s.good(); + } +}; + +#define EXACT_RGBA_WIN_SIZE (512+2) + +class ExactRGBATest: public BaseTest<ExactRGBAResult> { +public: + GLEAN_CLASS_WH(ExactRGBATest, ExactRGBAResult, + EXACT_RGBA_WIN_SIZE, EXACT_RGBA_WIN_SIZE); + + void summarize(const char* label, const ExactRGBAResult::Flavor& o, + const ExactRGBAResult::Flavor& n); + void logFlavor(const char* label, const ExactRGBAResult::Flavor& r); +}; // class ExactRGBATest +extern ExactRGBATest exactRGBATest; + +} // namespace GLEAN + +#endif // __treadpix_h__ diff --git a/tests/glean/treadpixperf.cpp b/tests/glean/treadpixperf.cpp new file mode 100644 index 00000000..058e67a0 --- /dev/null +++ b/tests/glean/treadpixperf.cpp @@ -0,0 +1,548 @@ +// 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 + + +#include "treadpixperf.h" +#include "rand.h" +#include "timer.h" +#include "image.h" +#include <cassert> +#include <cmath> + +namespace GLEAN { + + +static PFNGLBINDBUFFERARBPROC BindBuffer = NULL; +static PFNGLBUFFERDATAARBPROC BufferData = NULL; +static PFNGLMAPBUFFERARBPROC MapBuffer = NULL; +static PFNGLUNMAPBUFFERARBPROC UnmapBuffer = NULL; +static PFNGLGETBUFFERSUBDATAARBPROC GetBufferSubData = NULL; + +const GLuint PBO1 = 42, PBO2 = 43; + +const double minInterval = 1.0; // seconds + + +struct ImageFormat +{ + const char *Name; + GLuint Bytes; // per pixel + GLenum Format; + GLenum Type; +}; + + +static ImageFormat Formats[] = +{ + { "GL_RGB, GL_UNSIGNED_BYTE", 3, GL_RGB, GL_UNSIGNED_BYTE }, + { "GL_BGR, GL_UNSIGNED_BYTE", 3, GL_BGR, GL_UNSIGNED_BYTE }, + { "GL_RGBA, GL_UNSIGNED_BYTE", 4, GL_RGBA, GL_UNSIGNED_BYTE }, + { "GL_BGRA, GL_UNSIGNED_BYTE", 4, GL_BGRA, GL_UNSIGNED_BYTE }, + { "GL_ABGR, GL_UNSIGNED_BYTE", 4, GL_ABGR_EXT, GL_UNSIGNED_BYTE }, + { "GL_RGBA, GL_UNSIGNED_INT_8_8_8_8", 4, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8 }, + { "GL_BGRA, GL_UNSIGNED_INT_8_8_8_8", 4, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8 }, + { "GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV", 4, GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV }, +#ifdef GL_EXT_packed_depth_stencil + { "GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8", 4, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT }, +#endif + { "GL_DEPTH_COMPONENT, GL_FLOAT", 4, GL_DEPTH_COMPONENT, GL_FLOAT }, + { "GL_DEPTH_COMPONENT, GL_UNSIGNED_INT", 4, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT }, + { "GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT", 2, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT }, + { NULL, 0, 0, 0 } // end of list marker +}; + + +static GLenum PBOmodes[4] = +{ + GL_NONE, +#ifdef GL_ARB_pixel_buffer_object + GL_STREAM_READ_ARB, + GL_STATIC_READ_ARB, + GL_DYNAMIC_READ_ARB +#endif +}; + +static const char *PBOmodeStrings[4] = +{ + "No PBO", + "GL_STREAM_READ PBO", + "GL_STATIC_READ PBO", + "GL_DYNAMIC_READ PBO" +}; + + +static bool +isDepthFormat(GLenum format) +{ + switch (format) { + case GL_DEPTH_COMPONENT: +#ifdef GL_EXT_packed_depth_stencil + case GL_DEPTH_STENCIL_EXT: +#endif + return true; + default: + return false; + } +} + + +static bool +isStencilFormat(GLenum format) +{ + switch (format) { + case GL_STENCIL_INDEX: +#ifdef GL_EXT_packed_depth_stencil + case GL_DEPTH_STENCIL_EXT: +#endif + return true; + default: + return false; + } +} + + +static bool +isDepthStencilFormat(GLenum format) +{ +#ifdef GL_EXT_packed_depth_stencil + if (format == GL_DEPTH_STENCIL_EXT) + return true; +#endif + return false; +} + + + +// print a SubResult test description in human-readable form +void +ReadpixPerfResult::SubResult::sprint(char *s) const +{ + sprintf(s, "glReadPixels(%d x %d, %s), %s, %s, GL_READ_BUFFER=%s", + width, height, Formats[formatNum].Name, + PBOmodeStrings[pboMode], + work ? "pixel sum" : "no pixel sum", + readBuf); +} + + +void +ReadpixPerfResult::SubResult::print(Environment *env) const +{ + char descrip[1000], str[1000]; + sprint(descrip); + sprintf(str, "\t%.3f Mpixels/second: %s\n", rate, descrip); + env->log << str; +} + + +static void +SimpleRender() +{ + glBegin(GL_POINTS); + glVertex2f(0, 0); + glEnd(); +} + + +// Exercise glReadPixels for a particular image size, format and type. +// Return read rate in megapixels / second +double +ReadpixPerfTest::runNonPBOtest(int formatNum, GLsizei width, GLsizei height, + GLuint *sumOut) +{ + const GLint bufferSize = width * height * Formats[formatNum].Bytes; + GLubyte *buffer = new GLubyte [bufferSize]; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + Timer t; + double start = t.getClock(); + double elapsedTime = 0.0; + int iter = 0; + + do { + iter++; + if (sumOut) { + SimpleRender(); + } + glReadPixels(0, 0, width, height, + Formats[formatNum].Format, + Formats[formatNum].Type, buffer); + if (sumOut) { + GLuint sum = 0; + for (int i = 0; i < bufferSize; i++) { + sum += buffer[i]; + } + *sumOut = sum; + } + double finish = t.getClock(); + elapsedTime = finish - start; + } while (elapsedTime < minInterval); + + delete buffer; + + double rate = width * height * iter / elapsedTime / 1000000.0; + return rate; +} + +// use glMapBufferARB or glGetBufferSubDataARB: +#define MAP_BUFFER 1 + +double +ReadpixPerfTest::runPBOtest(int formatNum, GLsizei width, GLsizei height, + GLenum bufferUsage, GLuint *sumOut) +{ +#ifdef GL_ARB_pixel_buffer_object + const GLint bufferSize = width * height * Formats[formatNum].Bytes / 2; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + + // setup PBOs + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO1); + BufferData(GL_PIXEL_PACK_BUFFER_ARB, bufferSize, NULL, bufferUsage); + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO2); + BufferData(GL_PIXEL_PACK_BUFFER_ARB, bufferSize, NULL, bufferUsage); + +#if !MAP_BUFFER + GLubyte *b = new GLubyte [bufferSize]; +#endif + + Timer t; + double start = t.getClock(); + double elapsedTime = 0.0; + int iter = 0; + + do { + iter++; + if (sumOut) { + SimpleRender(); + } + // read lower half + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO1); + glReadPixels(0, 0, width, height / 2, + Formats[formatNum].Format, + Formats[formatNum].Type, NULL); + // read upper half + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO2); + glReadPixels(0, height / 2, width, height / 2, + Formats[formatNum].Format, + Formats[formatNum].Type, NULL); + if (sumOut) { + GLuint sum = 0; + // sum lower half + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO1); +#if MAP_BUFFER + GLubyte *b = (GLubyte *) + MapBuffer(GL_PIXEL_PACK_BUFFER_ARB, + GL_READ_ONLY); +#else + GetBufferSubData(GL_PIXEL_PACK_BUFFER_ARB, + 0, bufferSize, b); +#endif + for (int i = 0; i < bufferSize; i++) { + sum += b[i]; + } +#if MAP_BUFFER + UnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); +#endif + + // sum upper half + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBO2); +#if MAP_BUFFER + b = (GLubyte *) MapBuffer(GL_PIXEL_PACK_BUFFER_ARB, + GL_READ_ONLY); +#else + GetBufferSubData(GL_PIXEL_PACK_BUFFER_ARB, + 0, bufferSize, b); +#endif + for (int i = 0; i < bufferSize; i++) { + sum += b[i]; + } +#if MAP_BUFFER + UnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB); +#endif + *sumOut = sum; + } + double finish = t.getClock(); + elapsedTime = finish - start; + } while (elapsedTime < minInterval); + + BindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0); + +#if !MAP_BUFFER + delete b; +#endif + + double rate = width * height * iter / elapsedTime / 1000000.0; + return rate; +#else + return 0.0; +#endif /* GL_ARB_pixel_buffer_object */ +} + + + +// Per visual setup. +void +ReadpixPerfTest::setup(void) +{ + env->log << name << ":\n"; + + glGetIntegerv(GL_DEPTH_BITS, &depthBits); + glGetIntegerv(GL_STENCIL_BITS, &stencilBits); + + if (GLUtils::haveExtensions("GL_ARB_pixel_buffer_object")) { + BindBuffer = (PFNGLBINDBUFFERARBPROC) + GLUtils::getProcAddress("glBindBufferARB"); + assert(BindBuffer); + BufferData = (PFNGLBUFFERDATAARBPROC) + GLUtils::getProcAddress("glBufferDataARB"); + assert(BufferData); + MapBuffer = (PFNGLMAPBUFFERARBPROC) + GLUtils::getProcAddress("glMapBufferARB"); + assert(MapBuffer); + UnmapBuffer = (PFNGLUNMAPBUFFERARBPROC) + GLUtils::getProcAddress("glUnmapBufferARB"); + assert(UnmapBuffer); + GetBufferSubData = (PFNGLGETBUFFERSUBDATAARBPROC) + GLUtils::getProcAddress("glGetBufferSubDataARB"); + assert(GetBufferSubData); + numPBOmodes = 4; + } + else { + numPBOmodes = 1; + } + + // Fill colorbuffer with random data + GLubyte *buffer = new GLubyte [windowSize * windowSize * 4]; + for (int i = 0; i < windowSize * windowSize * 4; i++) + buffer[i] = 5; + glDrawPixels(windowSize, windowSize, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + if (depthBits > 0) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDrawPixels(windowSize, windowSize, + GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, buffer); + } + if (stencilBits > 0) { + glDrawPixels(windowSize, windowSize, + GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer); + } + delete buffer; +} + + + +void +ReadpixPerfTest::runOne(ReadpixPerfResult &r, Window &w) +{ + ReadpixPerfResult::SubResult res; + (void) w; // silence warning + + setup(); + assert(numPBOmodes > 0); + + r.pass = true; + res.width = windowSize; + res.height = windowSize; + + { + GLint readBuf; + glGetIntegerv(GL_READ_BUFFER, &readBuf); + if (readBuf == GL_FRONT) + res.readBuf = "GL_FRONT"; + else + res.readBuf = "GL_BACK"; + } + + for (res.formatNum = 0; Formats[res.formatNum].Name; res.formatNum++) { + + if (isDepthFormat(Formats[res.formatNum].Format) && depthBits == 0) + continue; + if (isStencilFormat(Formats[res.formatNum].Format) && stencilBits == 0) + continue; + + if (isDepthStencilFormat(Formats[res.formatNum].Format) && + !GLUtils::haveExtensions("GL_EXT_packed_depth_stencil")) + continue; + + for (res.work = 0; res.work < 2; res.work++) { + GLuint firstSum = 0; + + for (res.pboMode = 0; res.pboMode < numPBOmodes; res.pboMode++) { + GLuint sum = 0; + + if (res.pboMode) { + GLenum usage = PBOmodes[res.pboMode]; + res.rate = runPBOtest(res.formatNum, res.width, res.height, usage, + res.work ? &sum : NULL); + } + else { + res.rate = runNonPBOtest(res.formatNum, res.width, res.height, + res.work ? &sum : NULL); + } + + res.print(env); + r.results.push_back(res); + + // sanity check + if (res.pboMode == 0) { + firstSum = sum; + } + else if (firstSum != sum) { + // this should never happen, probably an OpenGL bug + char s0[1000]; + res.sprint(s0); + env->log << name + << " Error: glReadPixels returned inconsistant data:\n" + << s0 + << " returned " + << firstSum + << " but expected sum is " + << sum << "\n"; + r.pass = false; + } + } + } + } +} + + +void +ReadpixPerfTest::logOne(ReadpixPerfResult &r) +{ + logPassFail(r); + logConcise(r); +} + + +void +ReadpixPerfTest::compareOne(ReadpixPerfResult &oldR, + ReadpixPerfResult &newR) +{ + const double threshold = 2.0; // percent + + comparePassFail(oldR, newR); + + if (newR.pass && oldR.pass) { + // if both tests failed, compare/report rates + ReadpixPerfResult::sub_iterator it_old = oldR.results.begin(); + ReadpixPerfResult::sub_iterator it_new = newR.results.begin(); + assert(oldR.results.size() == newR.results.size()); + for ( ; it_old != oldR.results.end(); ++it_old, ++it_new) { + const ReadpixPerfResult::SubResult &oldres = *it_old; + const ReadpixPerfResult::SubResult &newres = *it_new; + + double diff = (newres.rate - oldres.rate) / newres.rate; + diff *= 100.0; + if (fabs(diff) >= threshold) { + char descrip[1000]; + newres.sprint(descrip); + env->log << name << ": Warning: rate for '" + << descrip + << "' changed by " + << diff + << " percent (old: " + << newres.rate + << " new: " + << oldres.rate + << " MPixels/sec)\n"; + } + } + } + else { + // one test or the other failed + env->log << "\tNew: "; + env->log << (newR.pass ? "PASS" : "FAIL"); + env->log << "\tOld: "; + env->log << (oldR.pass ? "PASS" : "FAIL"); + } +} + + +// Write vector of sub results +void +ReadpixPerfResult::putresults(ostream &s) const +{ + s << pass << '\n'; + s << results.size() << '\n'; + for (ReadpixPerfResult::sub_iterator it = results.begin(); + it != results.end(); + ++it) { + const ReadpixPerfResult::SubResult &res = *it; + s << res.rate << '\n'; + s << res.width << '\n'; + s << res.height << '\n'; + s << res.formatNum << '\n'; + s << res.pboMode << '\n'; + s << res.work << '\n'; + } +} + + +// Read vector of sub results +bool +ReadpixPerfResult::getresults(istream &s) +{ + int count; + + s >> pass + >> count; + + results.reserve(count); + for (int i = 0; i < count; i++) { + ReadpixPerfResult::SubResult res; + s >> res.rate + >> res.width + >> res.height + >> res.formatNum + >> res.pboMode + >> res.work; + results.push_back(res); + } + return s.good(); +} + + +// The test object itself: +ReadpixPerfTest readpixperfTest("readpixPerf", "window, rgb", + "", + "Test the performance of glReadPixels for a variety of pixel\n" + "formats and datatypes.\n" + "When GL_ARB_pixel_buffer_object is supported, we also test reading\n" + "pixels into a PBO using the three types of buffer usage modes:\n" + "GL_STREAM_READ_ARB, GL_STATIC_READ_ARB and GL_DYNAMIC_READ_ARB.\n" + "Furthermore, test effect of summing the value of all image bytes\n" + "to simulate host-based image processing.\n" + ); + + + + +} // namespace GLEAN + diff --git a/tests/glean/treadpixperf.h b/tests/glean/treadpixperf.h new file mode 100644 index 00000000..8e710f91 --- /dev/null +++ b/tests/glean/treadpixperf.h @@ -0,0 +1,88 @@ +// 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 + +// Brian Paul September 2006 + +#ifndef __treadpixperf_h__ +#define __treadpixperf_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define windowSize 1000 + + +class ReadpixPerfResult: public BaseResult +{ +public: + struct SubResult + { + double rate; + GLsizei width, height; + int formatNum; + int pboMode; + int work; // really bool + void sprint(char *s) const; + void print(Environment *env) const; + const char *readBuf; // "GL_FRONT" or "GL_BACK" + }; + + bool pass; + + vector<SubResult> results; + + typedef vector<ReadpixPerfResult::SubResult>::const_iterator sub_iterator; + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + + +class ReadpixPerfTest: public BaseTest<ReadpixPerfResult> +{ +public: + GLEAN_CLASS_WH(ReadpixPerfTest, ReadpixPerfResult, + windowSize, windowSize); + +private: + int depthBits, stencilBits; + int numPBOmodes; + + double runPBOtest(int formatNum, GLsizei width, GLsizei height, + GLenum bufferUsage, GLuint *sumOut); + double runNonPBOtest(int formatNum, GLsizei width, GLsizei height, + GLuint *sumOut); + + void setup(void); +}; + +} // namespace GLEAN + +#endif // __treadpixperf_h__ + diff --git a/tests/glean/trgbtris.cpp b/tests/glean/trgbtris.cpp new file mode 100644 index 00000000..565a85fb --- /dev/null +++ b/tests/glean/trgbtris.cpp @@ -0,0 +1,196 @@ +// 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 + +// trgbtris.cpp: example image-based test to show use of TIFF images + +#include "trgbtris.h" +#include "stats.h" +#include "rand.h" +#include "geomutil.h" +#include "image.h" + +#if 0 +#if defined __UNIX__ +#include <unistd.h> +#endif + +#include <iostream> +#include <fstream> +#include <algorithm> +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "winsys.h" +#include "environ.h" +#include "rc.h" +#include "glutils.h" +#include "trgbtris.h" +#include "misc.h" +#endif + +namespace { + +void +logStats(GLEAN::BasicStats& stats, GLEAN::Environment* env) { + env->log << "\t\tmin = " << stats.min() << ", max = " << stats.max() + << "\n\t\tmean = " << stats.mean() << ", standard deviation = " + << stats.deviation() << '\n'; +} + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::runOne(RGBTriStripResult& r, Window& w) { + static int this_config = 0; + r.imageNumber = ++this_config; + + GLUtils::useScreenCoords(drawingSize + 2, drawingSize + 2); + + int nPoints = 20; // Exact value doesn't really matter. + RandomDouble vRand(142857); + RandomMesh2D v(1.0, drawingSize, nPoints, 1.0, drawingSize, nPoints, + vRand); + + RandomDouble cRand(271828); + + glClear(GL_COLOR_BUFFER_BIT); + glShadeModel(GL_SMOOTH); + + for (int row = 0; row < nPoints - 1; ++row) { + glBegin(GL_TRIANGLE_STRIP); + for (int col = 0; col < nPoints; ++col) { + float r = cRand.next(); + float g = cRand.next(); + float b = cRand.next(); + glColor3f(r, g, b); + glVertex2fv(v(row, col)); + r = cRand.next(); + g = cRand.next(); + b = cRand.next(); + glColor3f(r, g, b); + glVertex2fv(v(row + 1, col)); + } + glEnd(); + } + w.swap(); + + Image image(drawingSize + 2, drawingSize + 2, GL_RGB, GL_FLOAT); + image.read(0, 0); // Invoke glReadPixels to read the image. + image.writeTIFF(env->imageFileName(name, r.imageNumber)); + + r.pass = true; +} // RGBTriStripTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::logOne(RGBTriStripResult& r) { + env->log << name << ": NOTE " + << r.config->conciseDescription() << '\n' + << "\tImage number " << r.imageNumber << '\n'; + if (env->options.verbosity) + env->log << + "\tThis test does not check its result. Please view\n" + "\tthe image to verify that the result is correct, or\n" + "\tcompare it to a known-good result from a different\n" + "\trun of glean.\n"; +} // RGBTriStripTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +RGBTriStripTest::compareOne(RGBTriStripResult& oldR, RGBTriStripResult& newR) { + // Fetch the old and new images: + Image oldI; + oldI.readTIFF(env->image1FileName(name, oldR.imageNumber)); + Image newI; + newI.readTIFF(env->image2FileName(name, newR.imageNumber)); + + // Register the images, and gather statistics about the differences + // for each color channel: + Image::Registration reg(oldI.reg(newI)); + + // Compute worst-case tolerance (1 LSB in the shallowest drawing + // surface configuration) for each color channel: + double rTol = 1.0 / (1 << min(oldR.config->r, newR.config->r)); + double gTol = 1.0 / (1 << min(oldR.config->g, newR.config->g)); + double bTol = 1.0 / (1 << min(oldR.config->b, newR.config->b)); + + // We'll conclude that the images are the ``same'' if the maximum + // absolute error is no more than 1 LSB (in the shallowest config): + if (reg.stats[0].max() <= rTol && reg.stats[1].max() <= gTol + && reg.stats[2].max() <= bTol) { + if (env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() << '\n'; + if (reg.stats[0].max() == 0 && reg.stats[1].max() == 0 + && reg.stats[1].max() == 0) + env->log << "\tImages are exactly equal\n"; + else + env->log << "\tImages are approximately equal\n"; + } + } else { + env->log << name << ": DIFF " + << newR.config->conciseDescription() << '\n' + << "\tDifference exceeds 1 LSB in at least one " + "color channel\n"; + } + if (env->options.verbosity) { + env->log << "\tred:\n"; + logStats(reg.stats[0], env); + env->log << "\tgreen:\n"; + logStats(reg.stats[1], env); + env->log << "\tblue:\n"; + logStats(reg.stats[2], env); + } +} // RGBTriStripTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +RGBTriStripTest rgbTriStripTest("rgbTriStrip", "window, rgb", + + "The best approach when designing a test is to make it\n" + "self-checking; that is, the test itself should determine\n" + "whether the image or other data structure that it produces is\n" + "correct. However, some tests are difficult to design in this\n" + "way, and for some other tests (like stress tests or regression\n" + "tests concerning previously-reported bugs) it may be\n" + "unnecessary. For such tests, glean provides mechanisms to\n" + "save images and compare them to images generated from other\n" + "runs. This test simply exercises those mechanisms.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/trgbtris.h b/tests/glean/trgbtris.h new file mode 100644 index 00000000..b41a10bd --- /dev/null +++ b/tests/glean/trgbtris.h @@ -0,0 +1,63 @@ +// 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 + +// trgbtris.h: example image-based test to show use of TIFF images + +#ifndef __trgbtris_h__ +#define __trgbtris_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 64 + +class RGBTriStripResult: public BaseResult { +public: + bool pass; + int imageNumber; + + void putresults(ostream& s) const { + s << imageNumber << '\n'; + } + + bool getresults(istream& s) { + s >> imageNumber; + return s.good(); + } +}; + +class RGBTriStripTest: public BaseTest<RGBTriStripResult> { +public: + GLEAN_CLASS_WH(RGBTriStripTest, RGBTriStripResult, + drawingSize, drawingSize); +}; // class RGBTriStripTest + +} // namespace GLEAN + +#endif // __trgbtris_h__ diff --git a/tests/glean/tscissor.cpp b/tests/glean/tscissor.cpp new file mode 100644 index 00000000..f72bfb4a --- /dev/null +++ b/tests/glean/tscissor.cpp @@ -0,0 +1,188 @@ +// 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 + +// tscissor.cpp: Basic test of OpenGL scissor. +// +// This test verifies that the four corner pixels, and the four pixels +// diagonally inside the corners, of a scissored region are filled +// correctly. It then tests up to two pixels in both the horizontal and +// vertical directions of the scissor region to verify that they are +// unfilled. +// +// To test for pass/fail, we examine the color buffer for white or black, +// respectively. +// +// Author: Gareth Hughes <gareth@valinux.com> December 2000 + +#include "tscissor.h" + +namespace GLEAN { + +/* Verification helper macros: + */ +#define BAD_PIXEL( X, Y, R, G, B ) ( image[X][Y][0] != (R) || \ + image[X][Y][1] != (G) || \ + image[X][Y][2] != (B) ) + +#define TEST_PIXEL( X, Y, R, G, B, E ) \ +do { \ + if ( BAD_PIXEL( X, Y, R, G, B ) ) { \ + FailMessage( r, E, X, Y, \ + i, i, 10-2*i, 10-2*i ); \ + passed = false; \ + } \ +} while (0) + +#define TEST_CORNER( X, Y, SX, SY ) \ +do { \ + TEST_PIXEL( X, Y, 1.0, 1.0, 1.0, 1 ); \ + TEST_PIXEL( X SX 1, Y SY 1, 1.0, 1.0, 1.0, 2 ); \ + for ( j = 1 ; j <= i ; j++ ) { \ + TEST_PIXEL( X - SX j, Y, 0.0, 0.0, 0.0, j ); \ + TEST_PIXEL( X, Y - SY j, 0.0, 0.0, 0.0, j ); \ + } \ +} while (0) + + +void +ScissorTest::FailMessage( BasicResult &r, int error, int x, int y, + int sx, int sy, int sw, int sh ) const +{ + env->log << name << ": FAIL " + << r.config->conciseDescription() << "\n"; + env->log << "\tOff by " << error << " error at" + << " row " << x << " column " << y; + env->log << "\n\tglScissor( " + << sx << ", " << sy << ", " + << sw << ", " << sh << " )\n\n"; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ScissorTest::runOne( BasicResult& r, Window& w ) { + bool passed = true; + int i, j, k; + + // Draw 10x10 quads, as they fit nicely into a terminal window + // when dumped as RGB triplets... + glViewport( 0, 0, 10, 10 ); + + glDisable( GL_DITHER ); + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0.0, 1.0, 0.0, 1.0, -1.0, 1.0 ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glClearColor( 0.0, 0.0, 0.0, 0.0 ); + glColor3f( 1.0, 1.0, 1.0 ); + + if ( env->options.verbosity ) env->log << "\n"; + + for ( i = 0 ; i < 3 ; i++ ) { + float image[10][10][3]; + + glDisable( GL_SCISSOR_TEST ); + glClear( GL_COLOR_BUFFER_BIT ); + glEnable( GL_SCISSOR_TEST ); + + glScissor( i, i, 10-2*i, 10-2*i ); + + glBegin( GL_QUADS ); + glVertex3f( 0.0, 0.0, 0.0 ); + glVertex3f( 1.0, 0.0, 0.0 ); + glVertex3f( 1.0, 1.0, 0.0 ); + glVertex3f( 0.0, 1.0, 0.0 ); + glEnd(); + + w.swap(); + + glReadPixels( 0, 0, 10, 10, GL_RGB, GL_FLOAT, image ); + + // Dump the entire 10x10 image so the exact results can + // be inspected. Should make any failures pretty clear. + if ( env->options.verbosity ) { + env->log << "glScissor( " + << i << ", " << i << ", " + << 10-2*i << ", " << 10-2*i << " ):\n\n"; + for ( j = 0 ; j < 10 ; j++ ) { + for ( k = 0 ; k < 10 ; k++ ) { + env->log << " " << image[j][k][0] + << " " << image[j][k][1] + << " " << image[j][k][2]; + } + env->log << "\n"; + } + env->log << "\n"; + } + + // Test the four corners. Macro magic, I know... + TEST_CORNER( i, i, +, + ); + TEST_CORNER( 9 - i, i, -, + ); + TEST_CORNER( 9 - i, 9 - i, -, - ); + TEST_CORNER( i, 9 - i, +, - ); + } + + r.pass = passed; +} // ScissorTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ScissorTest::logOne( BasicResult& r ) { + if ( r.pass ) { + logPassFail( r ); + logConcise( r ); + } +} // ScissorTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ScissorTest::compareOne( BasicResult&, BasicResult& ) { + // FIXME: Implement this... +} // ScissorTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +ScissorTest scissorTest( "scissor", "window, rgb", + + "This test performs a basic test of the OpenGL scissor. It\n" + "checks for off-by-one errors around all four corners of the\n" + "scissored region, perhaps the most common cause of scissor\n" + "test failures.\n" + + ); + +} // namespace GLEAN diff --git a/tests/glean/tscissor.h b/tests/glean/tscissor.h new file mode 100644 index 00000000..75b13d9b --- /dev/null +++ b/tests/glean/tscissor.h @@ -0,0 +1,52 @@ +// 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 + + +// tscissor.h: Basic test of OpenGL scissor. +// +// Author: Gareth Hughes <gareth@valinux.com> December 2000 + + +#ifndef __tscissor_h__ +#define __tscissor_h__ + +#include "tbasic.h" + +namespace GLEAN { + +class ScissorTest: public BaseTest<BasicResult> { +public: + GLEAN_CLASS_WH( ScissorTest, BasicResult, 10, 10 ); + + void FailMessage( BasicResult &r, int err, int x, int y, + int sx, int sy, int sw, int sh ) const; +}; // class ScissorTest + +} // namespace GLEAN + +#endif // __tscissor_h__ diff --git a/tests/glean/tteapot.cpp b/tests/glean/tteapot.cpp new file mode 100644 index 00000000..44bff139 --- /dev/null +++ b/tests/glean/tteapot.cpp @@ -0,0 +1,3215 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 2000 Adam Haberlach 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 + +#include "tteapot.h" +#include "timer.h" + +namespace { + +float depthOfView = 30.0; +float zRatio = 10.0; + +float position[] = {0.0, 3.0, 3.0, 0.0}; +float position1[] = {-3.0, -3.0, 3.0, 0.0}; +float position2[] = {3.0, 0.0, 0.0, 0.0}; +float local_view[] = {0.0,0.0}; +float ambient[] = {0.1745, 0.03175, 0.03175}; +float diffuse[] = {0.61424, 0.10136, 0.10136}; +float specular[] = {0.727811, 0.626959, 0.626959}; +// rgb_color bg_black = {0,0,0,255}; + +enum lights { + lightNone = 0, + lightWhite, + lightYellow, + lightRed, + lightBlue, + lightGreen +}; + + +float white[3] = {1.0,1.0,1.0}; +float dimWhite[3] = {0.25,0.25,0.25}; +float black[3] = {0.0,0.0,0.0}; +float foggy[3] = {0.4,0.4,0.4}; +float blue[3] = {0.0,0.0,1.0}; +float dimBlue[3] = {0.0,0.0,0.5}; +float yellow[3] = {1.0,1.0,0.0}; +float dimYellow[3] = {0.5,0.5,0.0}; +float green[3] = {0.0,1.0,0.0}; +float dimGreen[3] = {0.0,0.5,0.0}; +float red[3] = {1.0,0.0,0.0}; + +float *bgColor = black; + +struct light { + float *ambient; + float *diffuse; + float *specular; +}; + +light lights[] = { + {NULL,NULL,NULL}, + {dimWhite,white,white}, + {dimWhite,yellow,yellow}, + {dimWhite,red,red}, + {dimWhite,blue,blue}, + {dimWhite,green,green} +}; + +struct material { + float ambient[3],diffuse[3],specular[3]; +}; + +float *colors[] = +{ + NULL,white,yellow,blue,red,green +}; + + +material materials[] = { + // Null + { + {0.1745, 0.03175, 0.03175}, + {0.61424, 0.10136, 0.10136}, + {0.727811, 0.626959, 0.626959} + }, + // White + { + {0.1745, 0.1745, 0.1745}, + {0.61424, 0.61424, 0.61424}, + {0.727811, 0.727811, 0.727811} + }, + // Yellow + { + {0.1745, 0.1745, 0.03175}, + {0.61424, 0.61424, 0.10136}, + {0.727811, 0.727811, 0.626959} + }, + // Blue + { + {0.03175, 0.03175, 0.1745}, + {0.10136, 0.10136, 0.61424}, + {0.626959, 0.626959, 0.727811} + }, + // Red + { + {0.1745, 0.03175, 0.03175}, + {0.61424, 0.10136, 0.10136}, + {0.727811, 0.626959, 0.626959} + }, + // Green + { + {0.03175, 0.1745, 0.03175}, + {0.10136, 0.61424, 0.10136}, + {0.626959, 0.727811, 0.626959} + }, +}; + +GLfloat vertexArrayData[] = { + 0.749768, 0.000000, 0.661700, 0.425044, 0.000000, 0.717239, + 0.902857, 0.000000, 0.429932, 0.436808, 0.000000, 0.698893, + 0.857866, -0.280456, 0.430593, 0.414863, -0.139359, 0.698893, + 0.712062, -0.232790, 0.662397, 0.403689, -0.135606, 0.717239, + 0.398032, 0.000000, 0.917368, 0.413454, 0.000000, 0.726412, + 0.377729, -0.123488, 0.917640, 0.392682, -0.131908, 0.726412, + -0.594549, 0.000000, 0.804060, 0.404834, 0.000000, 0.726412, + -0.564429, 0.184525, 0.804593, 0.384495, -0.129158, 0.726412, + -0.997566, 0.000000, -0.069671, 0.401980, 0.000000, 0.717239, + -0.948177, 0.309981, -0.069801, 0.381785, -0.128248, 0.717239, + -0.902860, 0.000000, -0.429934, 0.407688, 0.000000, 0.698893, + -0.857865, 0.280456, -0.430592, 0.387205, -0.130069, 0.698893, + 0.729170, -0.531333, 0.431273, 0.353500, -0.259429, 0.698893, + 0.604942, -0.440810, 0.663117, 0.343979, -0.252442, 0.717239, + 0.320655, -0.233655, 0.917921, 0.334600, -0.245558, 0.726412, + -0.479323, 0.349274, 0.805140, 0.327624, -0.240439, 0.726412, + -0.806215, 0.587474, -0.069936, 0.325314, -0.238744, 0.717239, + -0.729168, 0.531331, -0.431271, 0.329934, -0.242134, 0.698893, + 0.531333, -0.729170, 0.431273, 0.259429, -0.353500, 0.698893, + 0.440810, -0.604942, 0.663117, 0.252442, -0.343979, 0.717239, + 0.233655, -0.320655, 0.917921, 0.245558, -0.334600, 0.726412, + -0.349274, 0.479323, 0.805140, 0.240439, -0.327624, 0.726412, + -0.587474, 0.806215, -0.069936, 0.238744, -0.325314, 0.717239, + -0.531331, 0.729168, -0.431271, 0.242134, -0.329934, 0.698893, + 0.280456, -0.857866, 0.430593, 0.139359, -0.414863, 0.698893, + 0.232790, -0.712062, 0.662397, 0.135606, -0.403689, 0.717239, + 0.123488, -0.377729, 0.917640, 0.131908, -0.392682, 0.726412, + -0.184525, 0.564429, 0.804593, 0.129158, -0.384495, 0.726412, + -0.309981, 0.948177, -0.069801, 0.128248, -0.381785, 0.717239, + -0.280456, 0.857865, -0.430592, 0.130069, -0.387205, 0.698893, + 0.000000, -0.902857, 0.429932, 0.000000, -0.436808, 0.698893, + 0.000000, -0.749768, 0.661700, 0.000000, -0.425044, 0.717239, + 0.000000, -0.398032, 0.917368, 0.000000, -0.413454, 0.726412, + 0.000000, 0.594549, 0.804060, 0.000000, -0.404834, 0.726412, + 0.000000, 0.997566, -0.069671, 0.000000, -0.401980, 0.717239, + 0.000000, 0.902860, -0.429934, 0.000000, -0.407688, 0.698893, + 0.000000, 0.749768, 0.661700, 0.000000, 0.425044, 0.717239, + 0.000000, 0.902857, 0.429932, 0.000000, 0.436808, 0.698893, + 0.280456, 0.857866, 0.430593, 0.139359, 0.414863, 0.698893, + 0.232790, 0.712062, 0.662397, 0.135606, 0.403689, 0.717239, + 0.000000, 0.398032, 0.917368, 0.000000, 0.413454, 0.726412, + 0.123488, 0.377729, 0.917640, 0.131908, 0.392682, 0.726412, + 0.000000, -0.594549, 0.804060, 0.000000, 0.404834, 0.726412, + -0.184525, -0.564429, 0.804593, 0.129158, 0.384495, 0.726412, + 0.000000, -0.997566, -0.069671, 0.000000, 0.401980, 0.717239, + -0.309981, -0.948177, -0.069801, 0.128248, 0.381785, 0.717239, + 0.000000, -0.902860, -0.429934, 0.000000, 0.407688, 0.698893, + -0.280456, -0.857865, -0.430592, 0.130069, 0.387205, 0.698893, + 0.531333, 0.729170, 0.431273, 0.259429, 0.353500, 0.698893, + 0.440810, 0.604942, 0.663117, 0.252442, 0.343979, 0.717239, + 0.233655, 0.320655, 0.917921, 0.245558, 0.334600, 0.726412, + -0.349274, -0.479323, 0.805140, 0.240439, 0.327624, 0.726412, + -0.587474, -0.806215, -0.069936, 0.238744, 0.325314, 0.717239, + -0.531331, -0.729168, -0.431271, 0.242134, 0.329934, 0.698893, + 0.729170, 0.531333, 0.431273, 0.353500, 0.259429, 0.698893, + 0.604942, 0.440810, 0.663117, 0.343979, 0.252442, 0.717239, + 0.320655, 0.233655, 0.917921, 0.334600, 0.245558, 0.726412, + -0.479323, -0.349274, 0.805140, 0.327624, 0.240439, 0.726412, + -0.806215, -0.587474, -0.069936, 0.325314, 0.238744, 0.717239, + -0.729168, -0.531331, -0.431271, 0.329934, 0.242134, 0.698893, + 0.857866, 0.280456, 0.430593, 0.414863, 0.139359, 0.698893, + 0.712062, 0.232790, 0.662397, 0.403689, 0.135606, 0.717239, + 0.377729, 0.123488, 0.917640, 0.392682, 0.131908, 0.726412, + -0.564429, -0.184525, 0.804593, 0.384495, 0.129158, 0.726412, + -0.948177, -0.309981, -0.069801, 0.381785, 0.128248, 0.717239, + -0.857865, -0.280456, -0.430592, 0.387205, 0.130069, 0.698893, + -0.280456, -0.857866, 0.430593, -0.139359, -0.414863, 0.698893, + -0.232790, -0.712062, 0.662397, -0.135606, -0.403689, 0.717239, + -0.123488, -0.377729, 0.917640, -0.131908, -0.392682, 0.726412, + 0.184525, 0.564429, 0.804593, -0.129158, -0.384495, 0.726412, + 0.309981, 0.948177, -0.069801, -0.128248, -0.381785, 0.717239, + 0.280456, 0.857865, -0.430592, -0.130069, -0.387205, 0.698893, + -0.531333, -0.729170, 0.431273, -0.259429, -0.353500, 0.698893, + -0.440810, -0.604942, 0.663117, -0.252442, -0.343979, 0.717239, + -0.233655, -0.320655, 0.917921, -0.245558, -0.334600, 0.726412, + 0.349274, 0.479323, 0.805140, -0.240439, -0.327624, 0.726412, + 0.587474, 0.806215, -0.069936, -0.238744, -0.325314, 0.717239, + 0.531331, 0.729168, -0.431271, -0.242134, -0.329934, 0.698893, + -0.729170, -0.531333, 0.431273, -0.353500, -0.259429, 0.698893, + -0.604942, -0.440810, 0.663117, -0.343979, -0.252442, 0.717239, + -0.320655, -0.233655, 0.917921, -0.334600, -0.245558, 0.726412, + 0.479323, 0.349274, 0.805140, -0.327624, -0.240439, 0.726412, + 0.806215, 0.587474, -0.069936, -0.325314, -0.238744, 0.717239, + 0.729168, 0.531331, -0.431271, -0.329934, -0.242134, 0.698893, + -0.857866, -0.280456, 0.430593, -0.414863, -0.139359, 0.698893, + -0.712062, -0.232790, 0.662397, -0.403689, -0.135606, 0.717239, + -0.377729, -0.123488, 0.917640, -0.392682, -0.131908, 0.726412, + 0.564429, 0.184525, 0.804593, -0.384495, -0.129158, 0.726412, + 0.948177, 0.309981, -0.069801, -0.381785, -0.128248, 0.717239, + 0.857865, 0.280456, -0.430592, -0.387205, -0.130069, 0.698893, + -0.902857, 0.000000, 0.429932, -0.436808, 0.000000, 0.698893, + -0.749768, 0.000000, 0.661700, -0.425044, 0.000000, 0.717239, + -0.398032, 0.000000, 0.917368, -0.413454, 0.000000, 0.726412, + 0.594549, 0.000000, 0.804060, -0.404834, 0.000000, 0.726412, + 0.997566, 0.000000, -0.069671, -0.401980, 0.000000, 0.717239, + 0.902860, 0.000000, -0.429934, -0.407688, 0.000000, 0.698893, + -0.857866, 0.280456, 0.430593, -0.414863, 0.139359, 0.698893, + -0.712062, 0.232790, 0.662397, -0.403689, 0.135606, 0.717239, + -0.377729, 0.123488, 0.917640, -0.392682, 0.131908, 0.726412, + 0.564429, -0.184525, 0.804593, -0.384495, 0.129158, 0.726412, + 0.948177, -0.309981, -0.069801, -0.381785, 0.128248, 0.717239, + 0.857865, -0.280456, -0.430592, -0.387205, 0.130069, 0.698893, + -0.729170, 0.531333, 0.431273, -0.353500, 0.259429, 0.698893, + -0.604942, 0.440810, 0.663117, -0.343979, 0.252442, 0.717239, + -0.320655, 0.233655, 0.917921, -0.334600, 0.245558, 0.726412, + 0.479323, -0.349274, 0.805140, -0.327624, 0.240439, 0.726412, + 0.806215, -0.587474, -0.069936, -0.325314, 0.238744, 0.717239, + 0.729168, -0.531331, -0.431271, -0.329934, 0.242134, 0.698893, + -0.531333, 0.729170, 0.431273, -0.259429, 0.353500, 0.698893, + -0.440810, 0.604942, 0.663117, -0.252442, 0.343979, 0.717239, + -0.233655, 0.320655, 0.917921, -0.245558, 0.334600, 0.726412, + 0.349274, -0.479323, 0.805140, -0.240439, 0.327624, 0.726412, + 0.587474, -0.806215, -0.069936, -0.238744, 0.325314, 0.717239, + 0.531331, -0.729168, -0.431271, -0.242134, 0.329934, 0.698893, + -0.280456, 0.857866, 0.430593, -0.139359, 0.414863, 0.698893, + -0.232790, 0.712062, 0.662397, -0.135606, 0.403689, 0.717239, + -0.123488, 0.377729, 0.917640, -0.131908, 0.392682, 0.726412, + 0.184525, -0.564429, 0.804593, -0.129158, 0.384495, 0.726412, + 0.309981, -0.948177, -0.069801, -0.128248, 0.381785, 0.717239, + 0.280456, -0.857865, -0.430592, -0.130069, 0.387205, 0.698893, + 0.982662, 0.000000, 0.185408, 0.574257, 0.000000, 0.343157, + 0.999997, 0.000000, 0.000000, 0.582411, 0.000000, 0.262085, + 0.950495, -0.310739, 0.000000, 0.553151, -0.185812, 0.262085, + 0.933952, -0.305330, 0.185744, 0.545407, -0.183211, 0.343157, + 0.952068, 0.000000, 0.305886, 0.552126, 0.000000, 0.428422, + 0.904777, -0.295792, 0.306407, 0.524387, -0.176150, 0.428422, + 0.925461, 0.000000, 0.378844, 0.519511, 0.000000, 0.516832, + 0.879408, -0.287499, 0.379453, 0.493410, -0.165745, 0.516832, + 0.908570, 0.000000, 0.417733, 0.479907, 0.000000, 0.607338, + 0.863304, -0.282234, 0.418380, 0.455796, -0.153109, 0.607338, + 0.808194, -0.588917, 0.000000, 0.471334, -0.345906, 0.262085, + 0.794073, -0.578627, 0.186092, 0.464735, -0.341063, 0.343157, + 0.769179, -0.560487, 0.306945, 0.446824, -0.327918, 0.428422, + 0.747539, -0.544718, 0.380083, 0.420430, -0.308548, 0.516832, + 0.733807, -0.534712, 0.419049, 0.388379, -0.285026, 0.607338, + 0.588917, -0.808194, 0.000000, 0.345906, -0.471334, 0.262085, + 0.578627, -0.794073, 0.186092, 0.341063, -0.464735, 0.343157, + 0.560487, -0.769179, 0.306945, 0.327918, -0.446824, 0.428422, + 0.544718, -0.747539, 0.380083, 0.308548, -0.420430, 0.516832, + 0.534712, -0.733807, 0.419049, 0.285026, -0.388379, 0.607338, + 0.310739, -0.950495, 0.000000, 0.185812, -0.553151, 0.262085, + 0.305331, -0.933952, 0.185744, 0.183211, -0.545407, 0.343157, + 0.295792, -0.904777, 0.306407, 0.176150, -0.524387, 0.428422, + 0.287499, -0.879408, 0.379453, 0.165745, -0.493410, 0.516832, + 0.282234, -0.863304, 0.418380, 0.153109, -0.455796, 0.607338, + 0.000000, -0.999997, 0.000000, 0.000000, -0.582411, 0.262085, + 0.000000, -0.982662, 0.185408, 0.000000, -0.574257, 0.343157, + 0.000000, -0.952068, 0.305886, 0.000000, -0.552126, 0.428422, + 0.000000, -0.925461, 0.378844, 0.000000, -0.519511, 0.516832, + 0.000000, -0.908570, 0.417733, 0.000000, -0.479907, 0.607338, + 0.000000, 0.982662, 0.185408, 0.000000, 0.574257, 0.343157, + 0.000000, 0.999997, 0.000000, 0.000000, 0.582411, 0.262085, + 0.310739, 0.950495, 0.000000, 0.185812, 0.553151, 0.262085, + 0.305330, 0.933952, 0.185744, 0.183211, 0.545407, 0.343157, + 0.000000, 0.952068, 0.305886, 0.000000, 0.552126, 0.428422, + 0.295792, 0.904777, 0.306407, 0.176150, 0.524387, 0.428422, + 0.000000, 0.925461, 0.378844, 0.000000, 0.519511, 0.516832, + 0.287499, 0.879408, 0.379453, 0.165745, 0.493410, 0.516832, + 0.000000, 0.908570, 0.417733, 0.000000, 0.479907, 0.607338, + 0.282234, 0.863304, 0.418380, 0.153109, 0.455796, 0.607338, + 0.588917, 0.808194, 0.000000, 0.345906, 0.471334, 0.262085, + 0.578627, 0.794073, 0.186092, 0.341063, 0.464735, 0.343157, + 0.560487, 0.769179, 0.306945, 0.327918, 0.446824, 0.428422, + 0.544718, 0.747539, 0.380083, 0.308548, 0.420430, 0.516832, + 0.534712, 0.733807, 0.419049, 0.285026, 0.388379, 0.607338, + 0.808194, 0.588917, 0.000000, 0.471334, 0.345906, 0.262085, + 0.794073, 0.578627, 0.186092, 0.464735, 0.341063, 0.343157, + 0.769179, 0.560487, 0.306945, 0.446824, 0.327918, 0.428422, + 0.747539, 0.544718, 0.380083, 0.420430, 0.308548, 0.516832, + 0.733807, 0.534712, 0.419049, 0.388379, 0.285026, 0.607338, + 0.950495, 0.310739, 0.000000, 0.553151, 0.185812, 0.262085, + 0.933952, 0.305330, 0.185744, 0.545407, 0.183211, 0.343157, + 0.904777, 0.295792, 0.306407, 0.524387, 0.176150, 0.428422, + 0.879408, 0.287499, 0.379453, 0.493410, 0.165745, 0.516832, + 0.863304, 0.282234, 0.418380, 0.455796, 0.153109, 0.607338, + -0.310739, -0.950495, 0.000000, -0.185812, -0.553151, 0.262085, + -0.305330, -0.933952, 0.185744, -0.183211, -0.545407, 0.343157, + -0.295792, -0.904777, 0.306407, -0.176150, -0.524387, 0.428422, + -0.287499, -0.879408, 0.379453, -0.165745, -0.493410, 0.516832, + -0.282234, -0.863304, 0.418380, -0.153109, -0.455796, 0.607338, + -0.588917, -0.808194, 0.000000, -0.345906, -0.471334, 0.262085, + -0.578627, -0.794073, 0.186092, -0.341063, -0.464735, 0.343157, + -0.560487, -0.769179, 0.306945, -0.327918, -0.446824, 0.428422, + -0.544718, -0.747539, 0.380083, -0.308548, -0.420430, 0.516832, + -0.534712, -0.733807, 0.419049, -0.285026, -0.388379, 0.607338, + -0.808194, -0.588917, 0.000000, -0.471334, -0.345906, 0.262085, + -0.794073, -0.578627, 0.186092, -0.464735, -0.341063, 0.343157, + -0.769179, -0.560487, 0.306945, -0.446824, -0.327918, 0.428422, + -0.747539, -0.544718, 0.380083, -0.420430, -0.308548, 0.516832, + -0.733807, -0.534712, 0.419049, -0.388379, -0.285026, 0.607338, + -0.950495, -0.310739, 0.000000, -0.553151, -0.185812, 0.262085, + -0.933952, -0.305330, 0.185744, -0.545407, -0.183211, 0.343157, + -0.904777, -0.295792, 0.306407, -0.524387, -0.176150, 0.428422, + -0.879408, -0.287499, 0.379453, -0.493410, -0.165745, 0.516832, + -0.863304, -0.282234, 0.418380, -0.455796, -0.153109, 0.607338, + -0.999997, 0.000000, 0.000000, -0.582411, 0.000000, 0.262085, + -0.982662, 0.000000, 0.185408, -0.574257, 0.000000, 0.343157, + -0.952068, 0.000000, 0.305886, -0.552126, 0.000000, 0.428422, + -0.925461, 0.000000, 0.378844, -0.519511, 0.000000, 0.516832, + -0.908570, 0.000000, 0.417733, -0.479907, 0.000000, 0.607338, + -0.950495, 0.310739, 0.000000, -0.553151, 0.185812, 0.262085, + -0.933952, 0.305330, 0.185744, -0.545407, 0.183211, 0.343157, + -0.904777, 0.295792, 0.306407, -0.524387, 0.176150, 0.428422, + -0.879408, 0.287499, 0.379453, -0.493410, 0.165745, 0.516832, + -0.863304, 0.282234, 0.418380, -0.455796, 0.153109, 0.607338, + -0.808194, 0.588917, 0.000000, -0.471334, 0.345906, 0.262085, + -0.794073, 0.578627, 0.186092, -0.464735, 0.341063, 0.343157, + -0.769179, 0.560487, 0.306945, -0.446824, 0.327918, 0.428422, + -0.747539, 0.544718, 0.380083, -0.420430, 0.308548, 0.516832, + -0.733807, 0.534712, 0.419049, -0.388379, 0.285026, 0.607338, + -0.588917, 0.808194, 0.000000, -0.345906, 0.471334, 0.262085, + -0.578627, 0.794073, 0.186092, -0.341063, 0.464735, 0.343157, + -0.560487, 0.769179, 0.306945, -0.327918, 0.446824, 0.428422, + -0.544718, 0.747539, 0.380083, -0.308548, 0.420430, 0.516832, + -0.534712, 0.733807, 0.419049, -0.285026, 0.388379, 0.607338, + -0.310739, 0.950495, 0.000000, -0.185812, 0.553151, 0.262085, + -0.305331, 0.933952, 0.185744, -0.183211, 0.545407, 0.343157, + -0.295792, 0.904777, 0.306407, -0.176150, 0.524387, 0.428422, + -0.287499, 0.879408, 0.379453, -0.165745, 0.493410, 0.516832, + -0.282234, 0.863304, 0.418380, -0.153109, 0.455796, 0.607338, + 0.653126, 0.000000, -0.757248, 0.451951, 0.000000, 0.062201, + 0.999997, 0.000000, 0.000000, 0.436808, 0.000000, 0.043681, + 0.950495, -0.310739, 0.000000, 0.414863, -0.139359, 0.043681, + 0.620124, -0.202733, -0.757855, 0.429245, -0.144190, 0.062201, + 0.653126, 0.000000, -0.757247, 0.488060, 0.000000, 0.092254, + 0.620125, -0.202733, -0.757855, 0.463540, -0.155711, 0.092254, + 0.761538, 0.000000, -0.648117, 0.531159, 0.000000, 0.134886, + 0.723268, -0.236453, -0.648825, 0.504473, -0.169461, 0.134886, + 0.915054, 0.000000, -0.403329, 0.567268, 0.000000, 0.191147, + 0.869485, -0.284255, -0.403963, 0.538769, -0.180981, 0.191147, + 0.808194, -0.588917, 0.000000, 0.353500, -0.259429, 0.043681, + 0.526696, -0.383794, -0.758479, 0.365755, -0.268423, 0.062201, + 0.526697, -0.383795, -0.758480, 0.394977, -0.289869, 0.092254, + 0.614483, -0.447763, -0.649552, 0.429856, -0.315466, 0.134886, + 0.739078, -0.538553, -0.404618, 0.459079, -0.336912, 0.191147, + 0.588917, -0.808194, 0.000000, 0.259429, -0.353500, 0.043681, + 0.383794, -0.526696, -0.758479, 0.268423, -0.365755, 0.062201, + 0.383795, -0.526697, -0.758480, 0.289869, -0.394977, 0.092254, + 0.447763, -0.614483, -0.649552, 0.315466, -0.429856, 0.134886, + 0.538553, -0.739078, -0.404619, 0.336912, -0.459079, 0.191147, + 0.310739, -0.950495, 0.000000, 0.139359, -0.414863, 0.043681, + 0.202733, -0.620124, -0.757855, 0.144190, -0.429245, 0.062201, + 0.202733, -0.620125, -0.757855, 0.155711, -0.463540, 0.092254, + 0.236453, -0.723268, -0.648825, 0.169461, -0.504473, 0.134886, + 0.284255, -0.869485, -0.403963, 0.180981, -0.538769, 0.191147, + 0.000000, -0.999997, 0.000000, 0.000000, -0.436808, 0.043681, + 0.000000, -0.653126, -0.757248, 0.000000, -0.451951, 0.062201, + 0.000000, -0.653126, -0.757247, 0.000000, -0.488060, 0.092254, + 0.000000, -0.761538, -0.648117, 0.000000, -0.531159, 0.134886, + 0.000000, -0.915054, -0.403329, 0.000000, -0.567268, 0.191147, + 0.000000, 0.653126, -0.757248, 0.000000, 0.451951, 0.062201, + 0.000000, 0.999997, 0.000000, 0.000000, 0.436808, 0.043681, + 0.310739, 0.950495, 0.000000, 0.139359, 0.414863, 0.043681, + 0.202733, 0.620124, -0.757855, 0.144190, 0.429245, 0.062201, + 0.000000, 0.653126, -0.757247, 0.000000, 0.488060, 0.092254, + 0.202733, 0.620125, -0.757855, 0.155711, 0.463540, 0.092254, + 0.000000, 0.761538, -0.648117, 0.000000, 0.531159, 0.134886, + 0.236453, 0.723268, -0.648825, 0.169461, 0.504473, 0.134886, + 0.000000, 0.915054, -0.403329, 0.000000, 0.567268, 0.191147, + 0.284255, 0.869485, -0.403963, 0.180981, 0.538769, 0.191147, + 0.588917, 0.808194, 0.000000, 0.259429, 0.353500, 0.043681, + 0.383794, 0.526696, -0.758479, 0.268423, 0.365755, 0.062201, + 0.383795, 0.526697, -0.758480, 0.289869, 0.394977, 0.092254, + 0.447763, 0.614483, -0.649552, 0.315466, 0.429856, 0.134886, + 0.538553, 0.739078, -0.404618, 0.336912, 0.459079, 0.191147, + 0.808194, 0.588917, 0.000000, 0.353500, 0.259429, 0.043681, + 0.526696, 0.383794, -0.758479, 0.365755, 0.268423, 0.062201, + 0.526697, 0.383795, -0.758480, 0.394977, 0.289869, 0.092254, + 0.614483, 0.447763, -0.649552, 0.429856, 0.315466, 0.134886, + 0.739078, 0.538553, -0.404619, 0.459079, 0.336912, 0.191147, + 0.950495, 0.310739, 0.000000, 0.414863, 0.139359, 0.043681, + 0.620124, 0.202733, -0.757855, 0.429245, 0.144190, 0.062201, + 0.620125, 0.202733, -0.757855, 0.463540, 0.155711, 0.092254, + 0.723268, 0.236453, -0.648825, 0.504473, 0.169461, 0.134886, + 0.869485, 0.284255, -0.403963, 0.538769, 0.180981, 0.191147, + -0.310739, -0.950495, 0.000000, -0.139359, -0.414863, 0.043681, + -0.202733, -0.620124, -0.757855, -0.144190, -0.429245, 0.062201, + -0.202733, -0.620125, -0.757855, -0.155711, -0.463540, 0.092254, + -0.236453, -0.723268, -0.648825, -0.169461, -0.504473, 0.134886, + -0.284255, -0.869485, -0.403963, -0.180981, -0.538769, 0.191147, + -0.588917, -0.808194, 0.000000, -0.259429, -0.353500, 0.043681, + -0.383794, -0.526696, -0.758479, -0.268423, -0.365755, 0.062201, + -0.383795, -0.526697, -0.758480, -0.289869, -0.394977, 0.092254, + -0.447763, -0.614483, -0.649552, -0.315466, -0.429856, 0.134886, + -0.538553, -0.739078, -0.404618, -0.336912, -0.459079, 0.191147, + -0.808194, -0.588917, 0.000000, -0.353500, -0.259429, 0.043681, + -0.526696, -0.383794, -0.758479, -0.365755, -0.268423, 0.062201, + -0.526697, -0.383795, -0.758480, -0.394977, -0.289869, 0.092254, + -0.614483, -0.447763, -0.649552, -0.429856, -0.315466, 0.134886, + -0.739078, -0.538553, -0.404619, -0.459079, -0.336912, 0.191147, + -0.950495, -0.310739, 0.000000, -0.414863, -0.139359, 0.043681, + -0.620124, -0.202733, -0.757855, -0.429245, -0.144190, 0.062201, + -0.620125, -0.202733, -0.757855, -0.463540, -0.155711, 0.092254, + -0.723268, -0.236453, -0.648825, -0.504473, -0.169461, 0.134886, + -0.869485, -0.284255, -0.403963, -0.538769, -0.180981, 0.191147, + -0.999997, 0.000000, 0.000000, -0.436808, 0.000000, 0.043681, + -0.653126, 0.000000, -0.757248, -0.451951, 0.000000, 0.062201, + -0.653126, 0.000000, -0.757247, -0.488060, 0.000000, 0.092254, + -0.761538, 0.000000, -0.648117, -0.531159, 0.000000, 0.134886, + -0.915054, 0.000000, -0.403329, -0.567268, 0.000000, 0.191147, + -0.950495, 0.310739, 0.000000, -0.414863, 0.139359, 0.043681, + -0.620124, 0.202733, -0.757855, -0.429245, 0.144190, 0.062201, + -0.620125, 0.202733, -0.757855, -0.463540, 0.155711, 0.092254, + -0.723268, 0.236453, -0.648825, -0.504473, 0.169461, 0.134886, + -0.869485, 0.284255, -0.403963, -0.538769, 0.180981, 0.191147, + -0.808194, 0.588917, 0.000000, -0.353500, 0.259429, 0.043681, + -0.526696, 0.383794, -0.758479, -0.365755, 0.268423, 0.062201, + -0.526697, 0.383795, -0.758480, -0.394977, 0.289869, 0.092254, + -0.614483, 0.447763, -0.649552, -0.429856, 0.315466, 0.134886, + -0.739078, 0.538553, -0.404618, -0.459079, 0.336912, 0.191147, + -0.588917, 0.808194, 0.000000, -0.259429, 0.353500, 0.043681, + -0.383794, 0.526696, -0.758479, -0.268423, 0.365755, 0.062201, + -0.383795, 0.526697, -0.758480, -0.289869, 0.394977, 0.092254, + -0.447763, 0.614483, -0.649552, -0.315466, 0.429856, 0.134886, + -0.538553, 0.739078, -0.404619, -0.336912, 0.459079, 0.191147, + -0.310739, 0.950495, 0.000000, -0.139359, 0.414863, 0.043681, + -0.202733, 0.620124, -0.757855, -0.144190, 0.429245, 0.062201, + -0.202733, 0.620125, -0.757855, -0.155711, 0.463540, 0.092254, + -0.236453, 0.723268, -0.648825, -0.169461, 0.504473, 0.134886, + -0.284255, 0.869485, -0.403963, -0.180981, 0.538769, 0.191147, + 0.894427, 0.000000, -0.447213, 0.052184, 0.000000, 0.816657, + 0.600000, 0.000000, 0.800000, 0.058241, 0.000000, 0.786255, + 0.569610, -0.186218, 0.800539, 0.055315, -0.018581, 0.786255, + 0.849825, -0.277126, -0.448321, 0.049568, -0.016670, 0.816657, + 0.732528, 0.000000, -0.680733, 0.079674, 0.000000, 0.851252, + 0.695758, -0.226333, -0.681680, 0.075687, -0.025484, 0.851252, + 0.934487, 0.000000, -0.355995, 0.104368, 0.000000, 0.883750, + 0.888413, -0.288795, -0.356820, 0.099149, -0.033394, 0.883751, + 0.360398, 0.000000, 0.932794, 0.089924, 0.000000, 0.907862, + 0.342044, -0.111168, 0.933084, 0.085428, -0.028775, 0.907863, + 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.917297, + 0.483729, -0.352485, 0.801094, 0.047133, -0.034591, 0.786255, + 0.722111, -0.525753, -0.449592, 0.042248, -0.031017, 0.816657, + 0.590847, -0.429839, -0.682740, 0.064527, -0.047392, 0.851252, + 0.755194, -0.549270, -0.357749, 0.084536, -0.062095, 0.883751, + 0.290204, -0.211059, 0.933400, 0.072838, -0.053504, 0.907863, + 0.352485, -0.483729, 0.801094, 0.034591, -0.047133, 0.786255, + 0.525753, -0.722111, -0.449592, 0.031017, -0.042248, 0.816657, + 0.429838, -0.590847, -0.682740, 0.047392, -0.064527, 0.851252, + 0.549270, -0.755194, -0.357749, 0.062095, -0.084536, 0.883750, + 0.211060, -0.290203, 0.933400, 0.053504, -0.072838, 0.907862, + 0.186218, -0.569610, 0.800539, 0.018581, -0.055315, 0.786255, + 0.277127, -0.849825, -0.448321, 0.016670, -0.049568, 0.816657, + 0.226332, -0.695758, -0.681680, 0.025484, -0.075687, 0.851252, + 0.288795, -0.888413, -0.356820, 0.033394, -0.099149, 0.883750, + 0.111168, -0.342044, 0.933084, 0.028775, -0.085428, 0.907862, + 0.000000, -0.600000, 0.800000, 0.000000, -0.058241, 0.786255, + 0.000000, -0.894427, -0.447213, 0.000000, -0.052184, 0.816657, + 0.000000, -0.732528, -0.680733, 0.000000, -0.079674, 0.851252, + 0.000000, -0.934487, -0.355995, 0.000000, -0.104368, 0.883750, + 0.000000, -0.360398, 0.932794, 0.000000, -0.089924, 0.907862, + 0.000000, 0.894427, -0.447213, 0.000000, 0.052184, 0.816657, + 0.000000, 0.600000, 0.800000, 0.000000, 0.058241, 0.786255, + 0.186218, 0.569610, 0.800539, 0.018581, 0.055315, 0.786255, + 0.277126, 0.849825, -0.448321, 0.016670, 0.049568, 0.816657, + 0.000000, 0.732528, -0.680733, 0.000000, 0.079674, 0.851252, + 0.226333, 0.695758, -0.681680, 0.025484, 0.075687, 0.851252, + 0.000000, 0.934487, -0.355995, 0.000000, 0.104368, 0.883750, + 0.288795, 0.888413, -0.356820, 0.033394, 0.099149, 0.883751, + 0.000000, 0.360398, 0.932794, 0.000000, 0.089924, 0.907862, + 0.111168, 0.342044, 0.933084, 0.028775, 0.085428, 0.907863, + 0.352485, 0.483729, 0.801094, 0.034591, 0.047133, 0.786255, + 0.525753, 0.722111, -0.449592, 0.031017, 0.042248, 0.816657, + 0.429839, 0.590847, -0.682740, 0.047392, 0.064527, 0.851252, + 0.549270, 0.755194, -0.357749, 0.062095, 0.084536, 0.883751, + 0.211059, 0.290204, 0.933400, 0.053504, 0.072838, 0.907863, + 0.483729, 0.352485, 0.801094, 0.047133, 0.034591, 0.786255, + 0.722111, 0.525753, -0.449592, 0.042248, 0.031017, 0.816657, + 0.590847, 0.429838, -0.682740, 0.064527, 0.047392, 0.851252, + 0.755194, 0.549270, -0.357749, 0.084536, 0.062095, 0.883750, + 0.290203, 0.211060, 0.933400, 0.072838, 0.053504, 0.907862, + 0.569610, 0.186218, 0.800539, 0.055315, 0.018581, 0.786255, + 0.849825, 0.277126, -0.448321, 0.049568, 0.016670, 0.816657, + 0.695758, 0.226332, -0.681680, 0.075687, 0.025484, 0.851252, + 0.888413, 0.288795, -0.356820, 0.099149, 0.033394, 0.883750, + 0.342044, 0.111168, 0.933084, 0.085428, 0.028775, 0.907862, + -0.186218, -0.569610, 0.800539, -0.018581, -0.055315, 0.786255, + -0.277126, -0.849825, -0.448321, -0.016670, -0.049568, 0.816657, + -0.226333, -0.695758, -0.681680, -0.025484, -0.075687, 0.851252, + -0.288795, -0.888413, -0.356820, -0.033394, -0.099149, 0.883751, + -0.111168, -0.342044, 0.933084, -0.028775, -0.085428, 0.907863, + -0.352485, -0.483729, 0.801094, -0.034591, -0.047133, 0.786255, + -0.525753, -0.722111, -0.449592, -0.031017, -0.042248, 0.816657, + -0.429839, -0.590847, -0.682740, -0.047392, -0.064527, 0.851252, + -0.549270, -0.755194, -0.357749, -0.062095, -0.084536, 0.883751, + -0.211059, -0.290204, 0.933400, -0.053504, -0.072838, 0.907863, + -0.483729, -0.352485, 0.801094, -0.047133, -0.034591, 0.786255, + -0.722111, -0.525753, -0.449592, -0.042248, -0.031017, 0.816657, + -0.590847, -0.429838, -0.682740, -0.064527, -0.047392, 0.851252, + -0.755194, -0.549270, -0.357749, -0.084536, -0.062095, 0.883750, + -0.290203, -0.211060, 0.933400, -0.072838, -0.053504, 0.907862, + -0.569610, -0.186218, 0.800539, -0.055315, -0.018581, 0.786255, + -0.849825, -0.277126, -0.448321, -0.049568, -0.016670, 0.816657, + -0.695758, -0.226332, -0.681680, -0.075687, -0.025484, 0.851252, + -0.888413, -0.288795, -0.356820, -0.099149, -0.033394, 0.883750, + -0.342044, -0.111168, 0.933084, -0.085428, -0.028775, 0.907862, + -0.600000, 0.000000, 0.800000, -0.058241, 0.000000, 0.786255, + -0.894427, 0.000000, -0.447213, -0.052184, 0.000000, 0.816657, + -0.732528, 0.000000, -0.680733, -0.079674, 0.000000, 0.851252, + -0.934487, 0.000000, -0.355995, -0.104368, 0.000000, 0.883750, + -0.360398, 0.000000, 0.932794, -0.089924, 0.000000, 0.907862, + -0.569610, 0.186218, 0.800539, -0.055315, 0.018581, 0.786255, + -0.849825, 0.277126, -0.448321, -0.049568, 0.016670, 0.816657, + -0.695758, 0.226333, -0.681680, -0.075687, 0.025484, 0.851252, + -0.888413, 0.288795, -0.356820, -0.099149, 0.033394, 0.883751, + -0.342044, 0.111168, 0.933084, -0.085428, 0.028775, 0.907863, + -0.483729, 0.352485, 0.801094, -0.047133, 0.034591, 0.786255, + -0.722111, 0.525753, -0.449592, -0.042248, 0.031017, 0.816657, + -0.590847, 0.429839, -0.682740, -0.064527, 0.047392, 0.851252, + -0.755194, 0.549270, -0.357749, -0.084536, 0.062095, 0.883751, + -0.290204, 0.211059, 0.933400, -0.072838, 0.053504, 0.907863, + -0.352485, 0.483729, 0.801094, -0.034591, 0.047133, 0.786255, + -0.525753, 0.722111, -0.449592, -0.031017, 0.042248, 0.816657, + -0.429838, 0.590847, -0.682740, -0.047392, 0.064527, 0.851252, + -0.549270, 0.755194, -0.357749, -0.062095, 0.084536, 0.883750, + -0.211060, 0.290203, 0.933400, -0.053504, 0.072838, 0.907862, + -0.186218, 0.569610, 0.800539, -0.018581, 0.055315, 0.786255, + -0.277127, 0.849825, -0.448321, -0.016670, 0.049568, 0.816657, + -0.226332, 0.695758, -0.681680, -0.025484, 0.075687, 0.851252, + -0.288795, 0.888413, -0.356820, -0.033394, 0.099149, 0.883750, + -0.111168, 0.342044, 0.933084, -0.028775, 0.085428, 0.907862, + 0.325793, 0.000000, 0.945439, 0.350844, 0.000000, 0.720559, + 0.999999, 0.000000, 0.000000, 0.378567, 0.000000, 0.698893, + 0.950491, -0.310738, 0.000000, 0.359548, -0.120778, 0.698893, + 0.309144, -0.101066, 0.945625, 0.333218, -0.111934, 0.720559, + 0.165777, 0.000000, 0.986162, 0.282586, 0.000000, 0.735935, + 0.157282, -0.051419, 0.986211, 0.268389, -0.090156, 0.735935, + 0.152941, 0.000000, 0.988232, 0.196156, 0.000000, 0.749214, + 0.145104, -0.047438, 0.988278, 0.186301, -0.062582, 0.749214, + 0.238138, 0.000000, 0.971229, 0.113920, 0.000000, 0.764589, + 0.225949, -0.073868, 0.971335, 0.108196, -0.036345, 0.764589, + 0.808190, -0.588914, 0.000000, 0.306367, -0.224839, 0.698893, + 0.262406, -0.191211, 0.945819, 0.283931, -0.208374, 0.720559, + 0.133484, -0.097267, 0.986266, 0.228691, -0.167833, 0.735935, + 0.123146, -0.089735, 0.988323, 0.158745, -0.116501, 0.749214, + 0.191770, -0.139740, 0.971440, 0.092193, -0.067659, 0.764589, + 0.588914, -0.808190, 0.000000, 0.224839, -0.306367, 0.698893, + 0.191211, -0.262406, 0.945819, 0.208374, -0.283931, 0.720559, + 0.097267, -0.133484, 0.986266, 0.167833, -0.228691, 0.735935, + 0.089735, -0.123146, 0.988323, 0.116501, -0.158745, 0.749214, + 0.139740, -0.191770, 0.971440, 0.067659, -0.092193, 0.764589, + 0.310738, -0.950491, 0.000000, 0.120778, -0.359548, 0.698893, + 0.101066, -0.309144, 0.945625, 0.111933, -0.333218, 0.720559, + 0.051419, -0.157282, 0.986211, 0.090156, -0.268389, 0.735935, + 0.047438, -0.145104, 0.988278, 0.062582, -0.186301, 0.749214, + 0.073868, -0.225949, 0.971335, 0.036345, -0.108196, 0.764589, + 0.000000, -0.999999, 0.000000, 0.000000, -0.378567, 0.698893, + 0.000000, -0.325793, 0.945439, 0.000000, -0.350844, 0.720559, + 0.000000, -0.165777, 0.986162, 0.000000, -0.282586, 0.735935, + 0.000000, -0.152941, 0.988232, 0.000000, -0.196156, 0.749214, + 0.000000, -0.238138, 0.971229, 0.000000, -0.113920, 0.764589, + 0.000000, 0.325793, 0.945439, 0.000000, 0.350844, 0.720559, + 0.000000, 0.999999, 0.000000, 0.000000, 0.378567, 0.698893, + 0.310738, 0.950491, 0.000000, 0.120778, 0.359548, 0.698893, + 0.101066, 0.309144, 0.945625, 0.111934, 0.333218, 0.720559, + 0.000000, 0.165777, 0.986162, 0.000000, 0.282586, 0.735935, + 0.051419, 0.157282, 0.986211, 0.090156, 0.268389, 0.735935, + 0.000000, 0.152941, 0.988232, 0.000000, 0.196156, 0.749214, + 0.047438, 0.145104, 0.988278, 0.062582, 0.186301, 0.749214, + 0.000000, 0.238138, 0.971229, 0.000000, 0.113920, 0.764589, + 0.073868, 0.225949, 0.971335, 0.036345, 0.108196, 0.764589, + 0.588914, 0.808190, 0.000000, 0.224839, 0.306367, 0.698893, + 0.191211, 0.262406, 0.945819, 0.208374, 0.283931, 0.720559, + 0.097267, 0.133484, 0.986266, 0.167833, 0.228691, 0.735935, + 0.089735, 0.123146, 0.988323, 0.116501, 0.158745, 0.749214, + 0.139740, 0.191770, 0.971440, 0.067659, 0.092193, 0.764589, + 0.808190, 0.588914, 0.000000, 0.306367, 0.224839, 0.698893, + 0.262406, 0.191211, 0.945819, 0.283931, 0.208374, 0.720559, + 0.133484, 0.097267, 0.986266, 0.228691, 0.167833, 0.735935, + 0.123146, 0.089735, 0.988323, 0.158745, 0.116501, 0.749214, + 0.191770, 0.139740, 0.971440, 0.092193, 0.067659, 0.764589, + 0.950491, 0.310738, 0.000000, 0.359548, 0.120778, 0.698893, + 0.309144, 0.101066, 0.945625, 0.333218, 0.111933, 0.720559, + 0.157282, 0.051419, 0.986211, 0.268389, 0.090156, 0.735935, + 0.145104, 0.047438, 0.988278, 0.186301, 0.062582, 0.749214, + 0.225949, 0.073868, 0.971335, 0.108196, 0.036345, 0.764589, + -0.310738, -0.950491, 0.000000, -0.120778, -0.359548, 0.698893, + -0.101066, -0.309144, 0.945625, -0.111934, -0.333218, 0.720559, + -0.051419, -0.157282, 0.986211, -0.090156, -0.268389, 0.735935, + -0.047438, -0.145104, 0.988278, -0.062582, -0.186301, 0.749214, + -0.073868, -0.225949, 0.971335, -0.036345, -0.108196, 0.764589, + -0.588914, -0.808190, 0.000000, -0.224839, -0.306367, 0.698893, + -0.191211, -0.262406, 0.945819, -0.208374, -0.283931, 0.720559, + -0.097267, -0.133484, 0.986266, -0.167833, -0.228691, 0.735935, + -0.089735, -0.123146, 0.988323, -0.116501, -0.158745, 0.749214, + -0.139740, -0.191770, 0.971440, -0.067659, -0.092193, 0.764589, + -0.808190, -0.588914, 0.000000, -0.306367, -0.224839, 0.698893, + -0.262406, -0.191211, 0.945819, -0.283931, -0.208374, 0.720559, + -0.133484, -0.097267, 0.986266, -0.228691, -0.167833, 0.735935, + -0.123146, -0.089735, 0.988323, -0.158745, -0.116501, 0.749214, + -0.191770, -0.139740, 0.971440, -0.092193, -0.067659, 0.764589, + -0.950491, -0.310738, 0.000000, -0.359548, -0.120778, 0.698893, + -0.309144, -0.101066, 0.945625, -0.333218, -0.111933, 0.720559, + -0.157282, -0.051419, 0.986211, -0.268389, -0.090156, 0.735935, + -0.145104, -0.047438, 0.988278, -0.186301, -0.062582, 0.749214, + -0.225949, -0.073868, 0.971335, -0.108196, -0.036345, 0.764589, + -0.999999, 0.000000, 0.000000, -0.378567, 0.000000, 0.698893, + -0.325793, 0.000000, 0.945439, -0.350844, 0.000000, 0.720559, + -0.165777, 0.000000, 0.986162, -0.282586, 0.000000, 0.735935, + -0.152941, 0.000000, 0.988232, -0.196156, 0.000000, 0.749214, + -0.238138, 0.000000, 0.971229, -0.113920, 0.000000, 0.764589, + -0.950491, 0.310738, 0.000000, -0.359548, 0.120778, 0.698893, + -0.309144, 0.101066, 0.945625, -0.333218, 0.111934, 0.720559, + -0.157282, 0.051419, 0.986211, -0.268389, 0.090156, 0.735935, + -0.145104, 0.047438, 0.988278, -0.186301, 0.062582, 0.749214, + -0.225949, 0.073868, 0.971335, -0.108196, 0.036345, 0.764589, + -0.808190, 0.588914, 0.000000, -0.306367, 0.224839, 0.698893, + -0.262406, 0.191211, 0.945819, -0.283931, 0.208374, 0.720559, + -0.133484, 0.097267, 0.986266, -0.228691, 0.167833, 0.735935, + -0.123146, 0.089735, 0.988323, -0.158745, 0.116501, 0.749214, + -0.191770, 0.139740, 0.971440, -0.092193, 0.067659, 0.764589, + -0.588914, 0.808190, 0.000000, -0.224839, 0.306367, 0.698893, + -0.191211, 0.262406, 0.945819, -0.208374, 0.283931, 0.720559, + -0.097267, 0.133484, 0.986266, -0.167833, 0.228691, 0.735935, + -0.089735, 0.123146, 0.988323, -0.116501, 0.158745, 0.749214, + -0.139740, 0.191770, 0.971440, -0.067659, 0.092193, 0.764589, + -0.310738, 0.950491, 0.000000, -0.120778, 0.359548, 0.698893, + -0.101066, 0.309144, 0.945625, -0.111933, 0.333218, 0.720559, + -0.051419, 0.157282, 0.986211, -0.090156, 0.268389, 0.735935, + -0.047438, 0.145104, 0.988278, -0.062582, 0.186301, 0.749214, + -0.073868, 0.225949, 0.971335, -0.036345, 0.108196, 0.764589, + 0.000000, -0.664364, -0.747409, 0.000000, -0.431217, 0.030751, + 0.206227, -0.630811, -0.748027, 0.137576, -0.409553, 0.030751, + 0.000000, -0.232118, -0.972685, 0.000000, -0.402562, 0.018870, + 0.072000, -0.220235, -0.972782, 0.128434, -0.382338, 0.018870, + 0.000000, -0.087099, -0.996200, 0.000000, -0.333023, 0.009086, + 0.027015, -0.082633, -0.996211, 0.106247, -0.316292, 0.009086, + 0.000000, -0.028834, -0.999583, 0.000000, -0.204776, 0.002446, + 0.008943, -0.027355, -0.999585, 0.065332, -0.194488, 0.002446, + 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, + 0.390419, -0.535788, -0.748665, 0.256109, -0.348975, 0.030751, + 0.136205, -0.186920, -0.972884, 0.239090, -0.325786, 0.018870, + 0.051100, -0.070127, -0.996225, 0.197789, -0.269509, 0.009086, + 0.016916, -0.023215, -0.999586, 0.121621, -0.165721, 0.002446, + 0.535788, -0.390419, -0.748665, 0.348975, -0.256109, 0.030751, + 0.186920, -0.136205, -0.972884, 0.325786, -0.239090, 0.018870, + 0.070127, -0.051100, -0.996225, 0.269509, -0.197789, 0.009086, + 0.023215, -0.016916, -0.999586, 0.165721, -0.121621, 0.002446, + 0.630811, -0.206227, -0.748027, 0.409553, -0.137575, 0.030751, + 0.220235, -0.072000, -0.972782, 0.382338, -0.128434, 0.018870, + 0.082633, -0.027015, -0.996211, 0.316292, -0.106247, 0.009086, + 0.027355, -0.008943, -0.999585, 0.194488, -0.065332, 0.002446, + 0.664364, 0.000000, -0.747409, 0.431217, 0.000000, 0.030751, + 0.232118, 0.000000, -0.972685, 0.402562, 0.000000, 0.018870, + 0.087099, 0.000000, -0.996200, 0.333023, 0.000000, 0.009086, + 0.028834, 0.000000, -0.999583, 0.204776, 0.000000, 0.002446, + 0.630811, 0.206227, -0.748027, 0.409553, 0.137576, 0.030751, + 0.220235, 0.072000, -0.972782, 0.382338, 0.128434, 0.018870, + 0.082633, 0.027015, -0.996211, 0.316292, 0.106247, 0.009086, + 0.027355, 0.008943, -0.999585, 0.194488, 0.065332, 0.002446, + 0.535788, 0.390419, -0.748665, 0.348975, 0.256109, 0.030751, + 0.186920, 0.136205, -0.972884, 0.325786, 0.239090, 0.018870, + 0.070127, 0.051100, -0.996225, 0.269509, 0.197789, 0.009086, + 0.023215, 0.016916, -0.999586, 0.165721, 0.121621, 0.002446, + 0.390419, 0.535788, -0.748665, 0.256109, 0.348975, 0.030751, + 0.136205, 0.186920, -0.972884, 0.239090, 0.325786, 0.018870, + 0.051100, 0.070127, -0.996225, 0.197789, 0.269509, 0.009086, + 0.016916, 0.023215, -0.999586, 0.121621, 0.165721, 0.002446, + 0.206227, 0.630811, -0.748027, 0.137575, 0.409553, 0.030751, + 0.072000, 0.220235, -0.972782, 0.128434, 0.382338, 0.018870, + 0.027015, 0.082633, -0.996211, 0.106247, 0.316292, 0.009086, + 0.008943, 0.027355, -0.999585, 0.065332, 0.194488, 0.002446, + 0.000000, 0.664364, -0.747409, 0.000000, 0.431217, 0.030751, + 0.000000, 0.232118, -0.972685, 0.000000, 0.402562, 0.018870, + 0.000000, 0.087099, -0.996200, 0.000000, 0.333023, 0.009086, + 0.000000, 0.028834, -0.999583, 0.000000, 0.204776, 0.002446, + -0.664364, 0.000000, -0.747409, -0.431217, 0.000000, 0.030751, + -0.630811, -0.206227, -0.748027, -0.409553, -0.137576, 0.030751, + -0.232118, 0.000000, -0.972685, -0.402562, 0.000000, 0.018870, + -0.220235, -0.072000, -0.972782, -0.382338, -0.128434, 0.018870, + -0.087099, 0.000000, -0.996200, -0.333023, 0.000000, 0.009086, + -0.082633, -0.027015, -0.996211, -0.316292, -0.106247, 0.009086, + -0.028834, 0.000000, -0.999583, -0.204776, 0.000000, 0.002446, + -0.027355, -0.008943, -0.999585, -0.194488, -0.065332, 0.002446, + -0.535788, -0.390419, -0.748665, -0.348975, -0.256109, 0.030751, + -0.186920, -0.136205, -0.972884, -0.325786, -0.239090, 0.018870, + -0.070127, -0.051100, -0.996225, -0.269509, -0.197789, 0.009086, + -0.023215, -0.016916, -0.999586, -0.165721, -0.121621, 0.002446, + -0.390419, -0.535788, -0.748665, -0.256109, -0.348975, 0.030751, + -0.136205, -0.186920, -0.972884, -0.239090, -0.325786, 0.018870, + -0.051100, -0.070127, -0.996225, -0.197789, -0.269509, 0.009086, + -0.016916, -0.023215, -0.999586, -0.121621, -0.165721, 0.002446, + -0.206227, -0.630811, -0.748027, -0.137575, -0.409553, 0.030751, + -0.072000, -0.220235, -0.972782, -0.128434, -0.382338, 0.018870, + -0.027015, -0.082633, -0.996211, -0.106247, -0.316292, 0.009086, + -0.008943, -0.027355, -0.999585, -0.065332, -0.194488, 0.002446, + -0.206227, 0.630811, -0.748027, -0.137576, 0.409553, 0.030751, + -0.072000, 0.220235, -0.972782, -0.128434, 0.382338, 0.018870, + -0.027015, 0.082633, -0.996211, -0.106247, 0.316292, 0.009086, + -0.008943, 0.027355, -0.999585, -0.065332, 0.194488, 0.002446, + -0.390419, 0.535788, -0.748665, -0.256109, 0.348975, 0.030751, + -0.136205, 0.186920, -0.972884, -0.239090, 0.325786, 0.018870, + -0.051100, 0.070127, -0.996225, -0.197789, 0.269509, 0.009086, + -0.016916, 0.023215, -0.999586, -0.121621, 0.165721, 0.002446, + -0.535788, 0.390419, -0.748665, -0.348975, 0.256109, 0.030751, + -0.186920, 0.136205, -0.972884, -0.325786, 0.239090, 0.018870, + -0.070127, 0.051100, -0.996225, -0.269509, 0.197789, 0.009086, + -0.023215, 0.016916, -0.999586, -0.165721, 0.121621, 0.002446, + -0.630811, 0.206227, -0.748027, -0.409553, 0.137575, 0.030751, + -0.220235, 0.072000, -0.972782, -0.382338, 0.128434, 0.018870, + -0.082633, 0.027015, -0.996211, -0.316292, 0.106247, 0.009086, + -0.027355, 0.008943, -0.999585, -0.194488, 0.065332, 0.002446, + 0.678279, 0.000000, -0.734802, -0.772510, 0.000000, 0.556144, + 1.000000, 0.000000, 0.000000, -0.786255, 0.000000, 0.524170, + 0.882349, -0.470586, 0.000000, -0.795340, -0.041934, 0.524170, + 0.629799, -0.445736, -0.636138, -0.781208, -0.041934, 0.559470, + 0.257464, 0.000000, -0.966285, -0.732207, 0.000000, 0.575539, + 0.252433, -0.388174, -0.886339, -0.739645, -0.041934, 0.580881, + 0.080816, 0.000000, -0.996729, -0.666744, 0.000000, 0.585498, + 0.079910, -0.370650, -0.925328, -0.671905, -0.041934, 0.591876, + 0.015623, 0.000000, -0.999877, -0.577519, 0.000000, 0.589167, + 0.015400, -0.370124, -0.928855, -0.579239, -0.041934, 0.595927, + 0.000000, 0.000000, -0.999997, -0.465929, 0.000000, 0.589691, + 0.000000, -0.371391, -0.928477, -0.462900, -0.041934, 0.596505, + 0.384615, -0.923074, 0.000000, -0.817006, -0.062900, 0.524170, + 0.298688, -0.917366, -0.263094, -0.801949, -0.062900, 0.567399, + 0.140821, -0.889659, -0.434364, -0.757382, -0.062900, 0.593620, + 0.046970, -0.875201, -0.481465, -0.684211, -0.062900, 0.607085, + 0.009046, -0.873433, -0.486852, -0.583341, -0.062900, 0.612046, + 0.000000, -0.874153, -0.485641, -0.455678, -0.062900, 0.612755, + -0.384616, -0.923077, 0.000000, -0.842865, -0.062900, 0.524170, + -0.308779, -0.920335, 0.240078, -0.826705, -0.062900, 0.576864, + -0.153234, -0.894313, 0.420379, -0.778552, -0.062900, 0.608825, + -0.052052, -0.876688, 0.478236, -0.698899, -0.062900, 0.625238, + -0.010009, -0.873532, 0.486663, -0.588237, -0.062900, 0.631285, + 0.000000, -0.874157, 0.485643, -0.447059, -0.062900, 0.632149, + -0.882353, -0.470588, 0.000000, -0.864531, -0.041934, 0.524170, + -0.718844, -0.467462, 0.514531, -0.847446, -0.041934, 0.584793, + -0.333894, -0.411701, 0.847945, -0.796289, -0.041934, 0.621565, + -0.107561, -0.377010, 0.919942, -0.711205, -0.041934, 0.640448, + -0.020401, -0.370527, 0.928596, -0.592339, -0.041934, 0.647405, + 0.000000, -0.371390, 0.928475, -0.439837, -0.041934, 0.648398, + -1.000000, 0.000000, 0.000000, -0.873617, 0.000000, 0.524170, + -0.821368, 0.000000, 0.570394, -0.856144, 0.000000, 0.588119, + -0.375382, 0.000000, 0.926870, -0.803727, 0.000000, 0.626907, + -0.119145, 0.000000, 0.992876, -0.716366, 0.000000, 0.646826, + -0.022494, 0.000000, 0.999744, -0.594059, 0.000000, 0.654164, + 0.000000, 0.000000, 0.999998, -0.436808, 0.000000, 0.655212, + -0.882353, 0.470588, 0.000000, -0.864531, 0.041934, 0.524170, + -0.718844, 0.467462, 0.514531, -0.847446, 0.041934, 0.584793, + -0.333894, 0.411701, 0.847945, -0.796289, 0.041934, 0.621565, + -0.107561, 0.377010, 0.919942, -0.711205, 0.041934, 0.640448, + -0.020401, 0.370527, 0.928596, -0.592339, 0.041934, 0.647404, + 0.000000, 0.371390, 0.928475, -0.439837, 0.041934, 0.648398, + -0.384615, 0.923077, 0.000000, -0.842865, 0.062900, 0.524170, + -0.308779, 0.920335, 0.240078, -0.826705, 0.062900, 0.576864, + -0.153234, 0.894313, 0.420378, -0.778552, 0.062900, 0.608826, + -0.052052, 0.876688, 0.478235, -0.698899, 0.062900, 0.625238, + -0.010009, 0.873533, 0.486663, -0.588237, 0.062900, 0.631285, + 0.000000, 0.874157, 0.485643, -0.447059, 0.062900, 0.632149, + 0.384614, 0.923074, 0.000000, -0.817006, 0.062900, 0.524170, + 0.298687, 0.917366, -0.263094, -0.801949, 0.062900, 0.567399, + 0.140821, 0.889659, -0.434364, -0.757382, 0.062900, 0.593620, + 0.046970, 0.875201, -0.481465, -0.684211, 0.062900, 0.607085, + 0.009046, 0.873433, -0.486852, -0.583341, 0.062900, 0.612046, + 0.000000, 0.874153, -0.485641, -0.455678, 0.062900, 0.612755, + 0.882349, 0.470586, 0.000000, -0.795340, 0.041934, 0.524170, + 0.629799, 0.445736, -0.636138, -0.781208, 0.041934, 0.559470, + 0.252433, 0.388174, -0.886339, -0.739645, 0.041934, 0.580881, + 0.079910, 0.370650, -0.925328, -0.671905, 0.041934, 0.591876, + 0.015400, 0.370124, -0.928855, -0.579239, 0.041934, 0.595927, + 0.000000, 0.371391, -0.928477, -0.462900, 0.041934, 0.596505, + 0.611799, 0.000000, 0.791013, -0.659522, 0.000000, 0.308212, + 0.379236, -0.382045, 0.842747, -0.579382, -0.041934, 0.252999, + 0.558613, -0.364714, 0.744934, -0.660661, -0.041934, 0.300725, + 0.769924, 0.000000, 0.638135, -0.717064, 0.000000, 0.363774, + 0.691468, -0.406502, 0.597183, -0.721571, -0.041934, 0.357396, + 0.884111, 0.000000, 0.467278, -0.756435, 0.000000, 0.422481, + 0.781918, -0.451570, 0.429746, -0.763438, -0.041934, 0.417320, + 0.962253, 0.000000, 0.272152, -0.779033, 0.000000, 0.478043, + 0.846995, -0.471755, 0.245044, -0.787586, -0.041934, 0.474809, + 0.194296, -0.880806, 0.431768, -0.572161, -0.062900, 0.231334, + 0.280289, -0.873120, 0.398872, -0.663376, -0.062900, 0.282873, + 0.322750, -0.896343, 0.303976, -0.732317, -0.062900, 0.342187, + 0.344194, -0.916219, 0.205104, -0.780135, -0.062900, 0.405014, + 0.366848, -0.923633, 0.111013, -0.807980, -0.062900, 0.467096, + -0.194296, -0.880808, -0.431769, -0.563541, -0.062900, 0.205475, + -0.265223, -0.876079, -0.402660, -0.666617, -0.062900, 0.261565, + -0.307337, -0.897713, -0.315680, -0.745143, -0.062900, 0.324033, + -0.335932, -0.916638, -0.216621, -0.800063, -0.062900, 0.390326, + -0.365298, -0.923730, -0.115227, -0.832322, -0.062900, 0.457890, + -0.379235, -0.382044, -0.842744, -0.556319, -0.041934, 0.183809, + -0.493114, -0.377428, -0.783828, -0.669333, -0.041934, 0.243713, + -0.614546, -0.413988, -0.671527, -0.755889, -0.041934, 0.308824, + -0.736106, -0.454498, -0.501569, -0.816760, -0.041934, 0.378020, + -0.838352, -0.472505, -0.271853, -0.852717, -0.041934, 0.450177, + -0.410363, 0.000000, -0.911918, -0.553290, 0.000000, 0.174723, + -0.525858, 0.000000, -0.850568, -0.670472, 0.000000, 0.236226, + -0.666422, 0.000000, -0.745575, -0.760396, 0.000000, 0.302446, + -0.820903, 0.000000, -0.571063, -0.823762, 0.000000, 0.372860, + -0.950315, 0.000000, -0.311291, -0.861269, 0.000000, 0.446942, + -0.379235, 0.382044, -0.842744, -0.556319, 0.041934, 0.183809, + -0.493114, 0.377428, -0.783828, -0.669333, 0.041934, 0.243713, + -0.614546, 0.413988, -0.671527, -0.755889, 0.041934, 0.308824, + -0.736106, 0.454498, -0.501569, -0.816760, 0.041934, 0.378020, + -0.838352, 0.472506, -0.271853, -0.852717, 0.041934, 0.450177, + -0.194296, 0.880808, -0.431769, -0.563541, 0.062900, 0.205475, + -0.265223, 0.876079, -0.402660, -0.666617, 0.062900, 0.261565, + -0.307337, 0.897713, -0.315680, -0.745143, 0.062900, 0.324033, + -0.335932, 0.916638, -0.216621, -0.800063, 0.062900, 0.390326, + -0.365297, 0.923730, -0.115227, -0.832322, 0.062900, 0.457890, + 0.194296, 0.880806, 0.431768, -0.572161, 0.062900, 0.231334, + 0.280289, 0.873120, 0.398872, -0.663376, 0.062900, 0.282873, + 0.322750, 0.896343, 0.303976, -0.732317, 0.062900, 0.342187, + 0.344194, 0.916219, 0.205104, -0.780135, 0.062900, 0.405014, + 0.366849, 0.923633, 0.111013, -0.807980, 0.062900, 0.467096, + 0.379236, 0.382045, 0.842747, -0.579382, 0.041934, 0.252999, + 0.558613, 0.364714, 0.744935, -0.660661, 0.041934, 0.300725, + 0.691468, 0.406502, 0.597183, -0.721571, 0.041934, 0.357396, + 0.781918, 0.451570, 0.429746, -0.763437, 0.041934, 0.417320, + 0.846995, 0.471755, 0.245044, -0.787586, 0.041934, 0.474809, + -0.901385, 0.000000, 0.433018, 0.736400, 0.000000, 0.635818, + -0.599998, 0.000000, 0.799997, 0.786255, 0.000000, 0.698893, + -0.456679, -0.584548, 0.670626, 0.804426, -0.034945, 0.698893, + -0.695153, -0.572370, 0.434915, 0.748321, -0.040905, 0.633002, + -0.948683, 0.000000, 0.316228, 0.708911, 0.000000, 0.561211, + -0.806605, -0.470480, 0.357817, 0.718505, -0.055118, 0.553398, + -0.836177, 0.000000, 0.548460, 0.677227, 0.000000, 0.489749, + -0.703305, -0.462338, 0.539998, 0.685804, -0.072081, 0.475848, + -0.417663, 0.000000, 0.908600, 0.614793, 0.000000, 0.436109, + -0.336099, -0.511011, 0.791137, 0.621044, -0.086294, 0.416121, + 0.000000, 0.000000, 0.999999, 0.495049, 0.000000, 0.414968, + -0.020447, -0.554584, 0.831876, 0.495049, -0.092254, 0.389982, + -0.163754, -0.943222, 0.288977, 0.847758, -0.052417, 0.698893, + -0.216744, -0.928832, 0.300487, 0.776746, -0.061357, 0.626285, + -0.225716, -0.923283, 0.310808, 0.741384, -0.082676, 0.534765, + -0.144680, -0.927429, 0.344881, 0.706257, -0.108122, 0.442700, + -0.052027, -0.935870, 0.348483, 0.635950, -0.129441, 0.368457, + -0.023270, -0.948426, 0.316142, 0.495049, -0.138381, 0.330402, + 0.161183, -0.928415, -0.334758, 0.899476, -0.052417, 0.698893, + 0.293016, -0.956011, -0.013319, 0.810673, -0.061357, 0.618269, + 0.490797, -0.869914, 0.048673, 0.768691, -0.082676, 0.512526, + 0.538925, -0.840157, -0.060780, 0.730668, -0.108122, 0.403136, + 0.312657, -0.914026, -0.258463, 0.653741, -0.129441, 0.311567, + 0.037642, -0.948010, -0.316003, 0.495049, -0.138381, 0.259289, + 0.354182, -0.453353, -0.817936, 0.942807, -0.034945, 0.698893, + 0.714527, -0.579088, -0.392560, 0.839099, -0.040905, 0.611553, + 0.875791, -0.442525, -0.192752, 0.791570, -0.055118, 0.493894, + 0.854131, -0.405027, -0.326210, 0.751120, -0.072081, 0.369987, + 0.550028, -0.485514, -0.679517, 0.668647, -0.086294, 0.263902, + 0.123484, -0.550453, -0.825679, 0.495049, -0.092254, 0.199709, + 0.384614, 0.000000, -0.923073, 0.960978, 0.000000, 0.698893, + 0.840531, 0.000000, -0.541764, 0.851019, 0.000000, 0.608736, + 0.962006, 0.000000, -0.273019, 0.801165, 0.000000, 0.486080, + 0.916944, 0.000000, -0.399005, 0.759697, 0.000000, 0.356086, + 0.608573, 0.000000, -0.793498, 0.674898, 0.000000, 0.243914, + 0.158678, 0.000000, -0.987330, 0.495049, 0.000000, 0.174723, + 0.354182, 0.453353, -0.817936, 0.942807, 0.034945, 0.698893, + 0.714527, 0.579088, -0.392560, 0.839099, 0.040905, 0.611553, + 0.875791, 0.442525, -0.192752, 0.791570, 0.055118, 0.493894, + 0.854131, 0.405027, -0.326210, 0.751120, 0.072081, 0.369987, + 0.550028, 0.485514, -0.679517, 0.668647, 0.086294, 0.263902, + 0.123484, 0.550453, -0.825679, 0.495049, 0.092254, 0.199709, + 0.161183, 0.928415, -0.334758, 0.899476, 0.052417, 0.698893, + 0.293015, 0.956010, -0.013319, 0.810673, 0.061357, 0.618269, + 0.490797, 0.869914, 0.048673, 0.768691, 0.082676, 0.512526, + 0.538925, 0.840157, -0.060780, 0.730668, 0.108122, 0.403136, + 0.312657, 0.914026, -0.258463, 0.653741, 0.129441, 0.311567, + 0.037642, 0.948010, -0.316003, 0.495049, 0.138381, 0.259289, + -0.163754, 0.943222, 0.288977, 0.847758, 0.052417, 0.698893, + -0.216744, 0.928832, 0.300487, 0.776746, 0.061357, 0.626285, + -0.225716, 0.923282, 0.310808, 0.741384, 0.082676, 0.534765, + -0.144680, 0.927429, 0.344881, 0.706257, 0.108122, 0.442700, + -0.052027, 0.935870, 0.348483, 0.635950, 0.129441, 0.368457, + -0.023270, 0.948426, 0.316142, 0.495049, 0.138381, 0.330402, + -0.456678, 0.584549, 0.670626, 0.804426, 0.034945, 0.698893, + -0.695153, 0.572369, 0.434915, 0.748321, 0.040905, 0.633002, + -0.806605, 0.470480, 0.357817, 0.718505, 0.055118, 0.553398, + -0.703305, 0.462338, 0.539998, 0.685804, 0.072081, 0.475848, + -0.336099, 0.511011, 0.791137, 0.621044, 0.086294, 0.416121, + -0.020447, 0.554584, 0.831877, 0.495049, 0.092254, 0.389982, + 0.849056, 0.000000, -0.528304, 0.826325, 0.000000, 0.709377, + 0.599997, 0.000000, -0.800000, 0.815375, 0.000000, 0.698893, + 0.439826, 0.625530, -0.644411, 0.827490, -0.020967, 0.698893, + 0.516600, 0.831320, -0.205017, 0.841177, -0.022420, 0.709867, + 0.472217, 0.000000, 0.881480, 0.826092, 0.000000, 0.714618, + 0.224528, 0.423910, 0.877431, 0.843391, -0.025887, 0.715272, + -0.215408, 0.000000, 0.976522, 0.817472, 0.000000, 0.714618, + -0.168884, -0.217723, 0.961285, 0.836455, -0.030024, 0.715191, + -0.439383, 0.000000, 0.898295, 0.803261, 0.000000, 0.709377, + -0.334755, -0.458291, 0.823348, 0.822692, -0.033491, 0.709704, + 0.149135, 0.954466, -0.258366, 0.856377, -0.031450, 0.698893, + 0.123177, 0.962458, 0.241862, 0.876593, -0.033631, 0.711037, + 0.021956, 0.416878, 0.908693, 0.884642, -0.038831, 0.716832, + -0.082283, -0.381857, 0.920550, 0.881722, -0.045037, 0.716556, + -0.131880, -0.781597, 0.609678, 0.869028, -0.050236, 0.710484, + -0.147596, 0.944613, 0.293111, 0.890856, -0.031450, 0.698893, + -0.127286, 0.787953, 0.602428, 0.918864, -0.033631, 0.712434, + -0.079448, 0.324229, 0.942633, 0.933878, -0.038831, 0.718694, + 0.054175, -0.554736, 0.830261, 0.935750, -0.045037, 0.718185, + 0.150468, -0.980604, 0.125601, 0.924332, -0.050236, 0.711415, + -0.360813, 0.513155, 0.778771, 0.919743, -0.020967, 0.698893, + -0.285775, 0.419575, 0.861559, 0.954280, -0.022420, 0.713604, + -0.149331, 0.185220, 0.971282, 0.975129, -0.025887, 0.720254, + 0.459685, -0.738552, 0.493175, 0.981017, -0.030024, 0.719550, + 0.445183, -0.620266, -0.645815, 0.970668, -0.033491, 0.712195, + -0.410363, 0.000000, 0.911919, 0.931858, 0.000000, 0.698893, + -0.335142, 0.000000, 0.942166, 0.969132, 0.000000, 0.714094, + -0.180328, 0.000000, 0.983607, 0.992428, 0.000000, 0.720908, + 0.980198, 0.000000, -0.198018, 1.000000, 0.000000, 0.720122, + 0.487997, 0.000000, -0.872840, 0.990099, 0.000000, 0.712522, + -0.360812, -0.513156, 0.778771, 0.919743, 0.020967, 0.698893, + -0.285775, -0.419575, 0.861559, 0.954280, 0.022420, 0.713604, + -0.149330, -0.185221, 0.971282, 0.975129, 0.025887, 0.720254, + 0.459685, 0.738552, 0.493175, 0.981017, 0.030024, 0.719550, + 0.445183, 0.620266, -0.645815, 0.970668, 0.033491, 0.712195, + -0.147596, -0.944613, 0.293111, 0.890856, 0.031450, 0.698893, + -0.127286, -0.787953, 0.602428, 0.918864, 0.033631, 0.712434, + -0.079448, -0.324229, 0.942633, 0.933878, 0.038831, 0.718694, + 0.054174, 0.554736, 0.830261, 0.935750, 0.045037, 0.718185, + 0.150468, 0.980604, 0.125601, 0.924332, 0.050236, 0.711415, + 0.149135, -0.954466, -0.258366, 0.856377, 0.031450, 0.698893, + 0.123177, -0.962458, 0.241862, 0.876593, 0.033631, 0.711037, + 0.021956, -0.416878, 0.908693, 0.884642, 0.038831, 0.716832, + -0.082283, 0.381857, 0.920550, 0.881722, 0.045037, 0.716556, + -0.131879, 0.781597, 0.609677, 0.869028, 0.050236, 0.710484, + 0.439825, -0.625530, -0.644411, 0.827490, 0.020967, 0.698893, + 0.516600, -0.831320, -0.205017, 0.841177, 0.022420, 0.709867, + 0.224528, -0.423910, 0.877431, 0.843391, 0.025887, 0.715272, + -0.168884, 0.217723, 0.961285, 0.836455, 0.030024, 0.715191, + -0.334755, 0.458291, 0.823348, 0.822692, 0.033491, 0.709704, +}; + +int stripIndices[] = { + 12, + 1, + 2, + 0, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 2, + 12, + 3, + 13, + 5, + 14, + 7, + 15, + 9, + 16, + 11, + 17, + 12, + 12, + 18, + 13, + 19, + 14, + 20, + 15, + 21, + 16, + 22, + 17, + 23, + 12, + 18, + 24, + 19, + 25, + 20, + 26, + 21, + 27, + 22, + 28, + 23, + 29, + 12, + 24, + 30, + 25, + 31, + 26, + 32, + 27, + 33, + 28, + 34, + 29, + 35, + 12, + 37, + 38, + 36, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 12, + 38, + 48, + 39, + 49, + 41, + 50, + 43, + 51, + 45, + 52, + 47, + 53, + 12, + 48, + 54, + 49, + 55, + 50, + 56, + 51, + 57, + 52, + 58, + 53, + 59, + 12, + 54, + 60, + 55, + 61, + 56, + 62, + 57, + 63, + 58, + 64, + 59, + 65, + 12, + 60, + 1, + 61, + 0, + 62, + 4, + 63, + 6, + 64, + 8, + 65, + 10, + 12, + 30, + 66, + 31, + 67, + 32, + 68, + 33, + 69, + 34, + 70, + 35, + 71, + 12, + 66, + 72, + 67, + 73, + 68, + 74, + 69, + 75, + 70, + 76, + 71, + 77, + 12, + 72, + 78, + 73, + 79, + 74, + 80, + 75, + 81, + 76, + 82, + 77, + 83, + 12, + 78, + 84, + 79, + 85, + 80, + 86, + 81, + 87, + 82, + 88, + 83, + 89, + 12, + 84, + 90, + 85, + 91, + 86, + 92, + 87, + 93, + 88, + 94, + 89, + 95, + 12, + 90, + 96, + 91, + 97, + 92, + 98, + 93, + 99, + 94, + 100, + 95, + 101, + 12, + 96, + 102, + 97, + 103, + 98, + 104, + 99, + 105, + 100, + 106, + 101, + 107, + 12, + 102, + 108, + 103, + 109, + 104, + 110, + 105, + 111, + 106, + 112, + 107, + 113, + 12, + 108, + 114, + 109, + 115, + 110, + 116, + 111, + 117, + 112, + 118, + 113, + 119, + 12, + 114, + 37, + 115, + 36, + 116, + 40, + 117, + 42, + 118, + 44, + 119, + 46, + 12, + 121, + 122, + 120, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 1, + 2, + 12, + 122, + 130, + 123, + 131, + 125, + 132, + 127, + 133, + 129, + 134, + 2, + 12, + 12, + 130, + 135, + 131, + 136, + 132, + 137, + 133, + 138, + 134, + 139, + 12, + 18, + 12, + 135, + 140, + 136, + 141, + 137, + 142, + 138, + 143, + 139, + 144, + 18, + 24, + 12, + 140, + 145, + 141, + 146, + 142, + 147, + 143, + 148, + 144, + 149, + 24, + 30, + 12, + 151, + 152, + 150, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 37, + 38, + 12, + 152, + 160, + 153, + 161, + 155, + 162, + 157, + 163, + 159, + 164, + 38, + 48, + 12, + 160, + 165, + 161, + 166, + 162, + 167, + 163, + 168, + 164, + 169, + 48, + 54, + 12, + 165, + 170, + 166, + 171, + 167, + 172, + 168, + 173, + 169, + 174, + 54, + 60, + 12, + 170, + 121, + 171, + 120, + 172, + 124, + 173, + 126, + 174, + 128, + 60, + 1, + 12, + 145, + 175, + 146, + 176, + 147, + 177, + 148, + 178, + 149, + 179, + 30, + 66, + 12, + 175, + 180, + 176, + 181, + 177, + 182, + 178, + 183, + 179, + 184, + 66, + 72, + 12, + 180, + 185, + 181, + 186, + 182, + 187, + 183, + 188, + 184, + 189, + 72, + 78, + 12, + 185, + 190, + 186, + 191, + 187, + 192, + 188, + 193, + 189, + 194, + 78, + 84, + 12, + 190, + 195, + 191, + 196, + 192, + 197, + 193, + 198, + 194, + 199, + 84, + 90, + 12, + 195, + 200, + 196, + 201, + 197, + 202, + 198, + 203, + 199, + 204, + 90, + 96, + 12, + 200, + 205, + 201, + 206, + 202, + 207, + 203, + 208, + 204, + 209, + 96, + 102, + 12, + 205, + 210, + 206, + 211, + 207, + 212, + 208, + 213, + 209, + 214, + 102, + 108, + 12, + 210, + 215, + 211, + 216, + 212, + 217, + 213, + 218, + 214, + 219, + 108, + 114, + 12, + 215, + 151, + 216, + 150, + 217, + 154, + 218, + 156, + 219, + 158, + 114, + 37, + 12, + 221, + 222, + 220, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 121, + 122, + 12, + 222, + 230, + 223, + 231, + 225, + 232, + 227, + 233, + 229, + 234, + 122, + 130, + 12, + 230, + 235, + 231, + 236, + 232, + 237, + 233, + 238, + 234, + 239, + 130, + 135, + 12, + 235, + 240, + 236, + 241, + 237, + 242, + 238, + 243, + 239, + 244, + 135, + 140, + 12, + 240, + 245, + 241, + 246, + 242, + 247, + 243, + 248, + 244, + 249, + 140, + 145, + 12, + 251, + 252, + 250, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 151, + 152, + 12, + 252, + 260, + 253, + 261, + 255, + 262, + 257, + 263, + 259, + 264, + 152, + 160, + 12, + 260, + 265, + 261, + 266, + 262, + 267, + 263, + 268, + 264, + 269, + 160, + 165, + 12, + 265, + 270, + 266, + 271, + 267, + 272, + 268, + 273, + 269, + 274, + 165, + 170, + 12, + 270, + 221, + 271, + 220, + 272, + 224, + 273, + 226, + 274, + 228, + 170, + 121, + 12, + 245, + 275, + 246, + 276, + 247, + 277, + 248, + 278, + 249, + 279, + 145, + 175, + 12, + 275, + 280, + 276, + 281, + 277, + 282, + 278, + 283, + 279, + 284, + 175, + 180, + 12, + 280, + 285, + 281, + 286, + 282, + 287, + 283, + 288, + 284, + 289, + 180, + 185, + 12, + 285, + 290, + 286, + 291, + 287, + 292, + 288, + 293, + 289, + 294, + 185, + 190, + 12, + 290, + 295, + 291, + 296, + 292, + 297, + 293, + 298, + 294, + 299, + 190, + 195, + 12, + 295, + 300, + 296, + 301, + 297, + 302, + 298, + 303, + 299, + 304, + 195, + 200, + 12, + 300, + 305, + 301, + 306, + 302, + 307, + 303, + 308, + 304, + 309, + 200, + 205, + 12, + 305, + 310, + 306, + 311, + 307, + 312, + 308, + 313, + 309, + 314, + 205, + 210, + 12, + 310, + 315, + 311, + 316, + 312, + 317, + 313, + 318, + 314, + 319, + 210, + 215, + 12, + 315, + 251, + 316, + 250, + 317, + 254, + 318, + 256, + 319, + 258, + 215, + 151, + 12, + 321, + 322, + 320, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 330, + 12, + 322, + 331, + 323, + 332, + 325, + 333, + 327, + 334, + 329, + 335, + 330, + 330, + 12, + 331, + 336, + 332, + 337, + 333, + 338, + 334, + 339, + 335, + 340, + 330, + 330, + 12, + 336, + 341, + 337, + 342, + 338, + 343, + 339, + 344, + 340, + 345, + 330, + 330, + 12, + 341, + 346, + 342, + 347, + 343, + 348, + 344, + 349, + 345, + 350, + 330, + 330, + 12, + 352, + 353, + 351, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 330, + 330, + 12, + 353, + 361, + 354, + 362, + 356, + 363, + 358, + 364, + 360, + 365, + 330, + 330, + 12, + 361, + 366, + 362, + 367, + 363, + 368, + 364, + 369, + 365, + 370, + 330, + 330, + 12, + 366, + 371, + 367, + 372, + 368, + 373, + 369, + 374, + 370, + 375, + 330, + 330, + 12, + 371, + 321, + 372, + 320, + 373, + 324, + 374, + 326, + 375, + 328, + 330, + 330, + 12, + 346, + 376, + 347, + 377, + 348, + 378, + 349, + 379, + 350, + 380, + 330, + 330, + 12, + 376, + 381, + 377, + 382, + 378, + 383, + 379, + 384, + 380, + 385, + 330, + 330, + 12, + 381, + 386, + 382, + 387, + 383, + 388, + 384, + 389, + 385, + 390, + 330, + 330, + 12, + 386, + 391, + 387, + 392, + 388, + 393, + 389, + 394, + 390, + 395, + 330, + 330, + 12, + 391, + 396, + 392, + 397, + 393, + 398, + 394, + 399, + 395, + 400, + 330, + 330, + 12, + 396, + 401, + 397, + 402, + 398, + 403, + 399, + 404, + 400, + 405, + 330, + 330, + 12, + 401, + 406, + 402, + 407, + 403, + 408, + 404, + 409, + 405, + 410, + 330, + 330, + 12, + 406, + 411, + 407, + 412, + 408, + 413, + 409, + 414, + 410, + 415, + 330, + 330, + 12, + 411, + 416, + 412, + 417, + 413, + 418, + 414, + 419, + 415, + 420, + 330, + 330, + 12, + 416, + 352, + 417, + 351, + 418, + 355, + 419, + 357, + 420, + 359, + 330, + 330, + 12, + 422, + 423, + 421, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 321, + 322, + 12, + 423, + 431, + 424, + 432, + 426, + 433, + 428, + 434, + 430, + 435, + 322, + 331, + 12, + 431, + 436, + 432, + 437, + 433, + 438, + 434, + 439, + 435, + 440, + 331, + 336, + 12, + 436, + 441, + 437, + 442, + 438, + 443, + 439, + 444, + 440, + 445, + 336, + 341, + 12, + 441, + 446, + 442, + 447, + 443, + 448, + 444, + 449, + 445, + 450, + 341, + 346, + 12, + 452, + 453, + 451, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 352, + 353, + 12, + 453, + 461, + 454, + 462, + 456, + 463, + 458, + 464, + 460, + 465, + 353, + 361, + 12, + 461, + 466, + 462, + 467, + 463, + 468, + 464, + 469, + 465, + 470, + 361, + 366, + 12, + 466, + 471, + 467, + 472, + 468, + 473, + 469, + 474, + 470, + 475, + 366, + 371, + 12, + 471, + 422, + 472, + 421, + 473, + 425, + 474, + 427, + 475, + 429, + 371, + 321, + 12, + 446, + 476, + 447, + 477, + 448, + 478, + 449, + 479, + 450, + 480, + 346, + 376, + 12, + 476, + 481, + 477, + 482, + 478, + 483, + 479, + 484, + 480, + 485, + 376, + 381, + 12, + 481, + 486, + 482, + 487, + 483, + 488, + 484, + 489, + 485, + 490, + 381, + 386, + 12, + 486, + 491, + 487, + 492, + 488, + 493, + 489, + 494, + 490, + 495, + 386, + 391, + 12, + 491, + 496, + 492, + 497, + 493, + 498, + 494, + 499, + 495, + 500, + 391, + 396, + 12, + 496, + 501, + 497, + 502, + 498, + 503, + 499, + 504, + 500, + 505, + 396, + 401, + 12, + 501, + 506, + 502, + 507, + 503, + 508, + 504, + 509, + 505, + 510, + 401, + 406, + 12, + 506, + 511, + 507, + 512, + 508, + 513, + 509, + 514, + 510, + 515, + 406, + 411, + 12, + 511, + 516, + 512, + 517, + 513, + 518, + 514, + 519, + 515, + 520, + 411, + 416, + 12, + 516, + 452, + 517, + 451, + 518, + 455, + 519, + 457, + 520, + 459, + 416, + 352, + 12, + 245, + 240, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 529, + 12, + 240, + 235, + 522, + 530, + 524, + 531, + 526, + 532, + 528, + 533, + 529, + 529, + 12, + 235, + 230, + 530, + 534, + 531, + 535, + 532, + 536, + 533, + 537, + 529, + 529, + 12, + 230, + 222, + 534, + 538, + 535, + 539, + 536, + 540, + 537, + 541, + 529, + 529, + 12, + 222, + 221, + 538, + 542, + 539, + 543, + 540, + 544, + 541, + 545, + 529, + 529, + 12, + 221, + 270, + 542, + 546, + 543, + 547, + 544, + 548, + 545, + 549, + 529, + 529, + 12, + 270, + 265, + 546, + 550, + 547, + 551, + 548, + 552, + 549, + 553, + 529, + 529, + 12, + 265, + 260, + 550, + 554, + 551, + 555, + 552, + 556, + 553, + 557, + 529, + 529, + 12, + 260, + 252, + 554, + 558, + 555, + 559, + 556, + 560, + 557, + 561, + 529, + 529, + 12, + 252, + 251, + 558, + 562, + 559, + 563, + 560, + 564, + 561, + 565, + 529, + 529, + 12, + 295, + 290, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 529, + 529, + 12, + 290, + 285, + 567, + 574, + 569, + 575, + 571, + 576, + 573, + 577, + 529, + 529, + 12, + 285, + 280, + 574, + 578, + 575, + 579, + 576, + 580, + 577, + 581, + 529, + 529, + 12, + 280, + 275, + 578, + 582, + 579, + 583, + 580, + 584, + 581, + 585, + 529, + 529, + 12, + 275, + 245, + 582, + 521, + 583, + 523, + 584, + 525, + 585, + 527, + 529, + 529, + 12, + 251, + 315, + 562, + 586, + 563, + 587, + 564, + 588, + 565, + 589, + 529, + 529, + 12, + 315, + 310, + 586, + 590, + 587, + 591, + 588, + 592, + 589, + 593, + 529, + 529, + 12, + 310, + 305, + 590, + 594, + 591, + 595, + 592, + 596, + 593, + 597, + 529, + 529, + 12, + 305, + 300, + 594, + 598, + 595, + 599, + 596, + 600, + 597, + 601, + 529, + 529, + 12, + 300, + 295, + 598, + 566, + 599, + 568, + 600, + 570, + 601, + 572, + 529, + 529, + 12, + 603, + 604, + 602, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 12, + 604, + 614, + 605, + 615, + 607, + 616, + 609, + 617, + 611, + 618, + 613, + 619, + 12, + 614, + 620, + 615, + 621, + 616, + 622, + 617, + 623, + 618, + 624, + 619, + 625, + 12, + 620, + 626, + 621, + 627, + 622, + 628, + 623, + 629, + 624, + 630, + 625, + 631, + 12, + 626, + 632, + 627, + 633, + 628, + 634, + 629, + 635, + 630, + 636, + 631, + 637, + 12, + 632, + 638, + 633, + 639, + 634, + 640, + 635, + 641, + 636, + 642, + 637, + 643, + 12, + 638, + 644, + 639, + 645, + 640, + 646, + 641, + 647, + 642, + 648, + 643, + 649, + 12, + 644, + 650, + 645, + 651, + 646, + 652, + 647, + 653, + 648, + 654, + 649, + 655, + 12, + 650, + 656, + 651, + 657, + 652, + 658, + 653, + 659, + 654, + 660, + 655, + 661, + 12, + 656, + 603, + 657, + 602, + 658, + 606, + 659, + 608, + 660, + 610, + 661, + 612, + 12, + 195, + 663, + 662, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 603, + 604, + 12, + 663, + 671, + 664, + 672, + 666, + 673, + 668, + 674, + 670, + 675, + 604, + 614, + 12, + 671, + 676, + 672, + 677, + 673, + 678, + 674, + 679, + 675, + 680, + 614, + 620, + 12, + 676, + 681, + 677, + 682, + 678, + 683, + 679, + 684, + 680, + 685, + 620, + 626, + 12, + 681, + 686, + 682, + 687, + 683, + 688, + 684, + 689, + 685, + 690, + 626, + 632, + 12, + 686, + 691, + 687, + 692, + 688, + 693, + 689, + 694, + 690, + 695, + 632, + 638, + 12, + 691, + 696, + 692, + 697, + 693, + 698, + 694, + 699, + 695, + 700, + 638, + 644, + 12, + 696, + 701, + 697, + 702, + 698, + 703, + 699, + 704, + 700, + 705, + 644, + 650, + 12, + 701, + 706, + 702, + 707, + 703, + 708, + 704, + 709, + 705, + 710, + 650, + 656, + 12, + 706, + 195, + 707, + 662, + 708, + 665, + 709, + 667, + 710, + 669, + 656, + 603, + 12, + 712, + 713, + 711, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 12, + 713, + 723, + 714, + 724, + 716, + 725, + 718, + 726, + 720, + 727, + 722, + 728, + 12, + 723, + 729, + 724, + 730, + 725, + 731, + 726, + 732, + 727, + 733, + 728, + 734, + 12, + 729, + 735, + 730, + 736, + 731, + 737, + 732, + 738, + 733, + 739, + 734, + 740, + 12, + 735, + 741, + 736, + 742, + 737, + 743, + 738, + 744, + 739, + 745, + 740, + 746, + 12, + 741, + 747, + 742, + 748, + 743, + 749, + 744, + 750, + 745, + 751, + 746, + 752, + 12, + 747, + 753, + 748, + 754, + 749, + 755, + 750, + 756, + 751, + 757, + 752, + 758, + 12, + 753, + 759, + 754, + 760, + 755, + 761, + 756, + 762, + 757, + 763, + 758, + 764, + 12, + 759, + 765, + 760, + 766, + 761, + 767, + 762, + 768, + 763, + 769, + 764, + 770, + 12, + 765, + 712, + 766, + 711, + 767, + 715, + 768, + 717, + 769, + 719, + 770, + 721, + 12, + 772, + 773, + 771, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 712, + 713, + 12, + 773, + 781, + 774, + 782, + 776, + 783, + 778, + 784, + 780, + 785, + 713, + 723, + 12, + 781, + 786, + 782, + 787, + 783, + 788, + 784, + 789, + 785, + 790, + 723, + 729, + 12, + 786, + 791, + 787, + 792, + 788, + 793, + 789, + 794, + 790, + 795, + 729, + 735, + 12, + 791, + 796, + 792, + 797, + 793, + 798, + 794, + 799, + 795, + 800, + 735, + 741, + 12, + 796, + 801, + 797, + 802, + 798, + 803, + 799, + 804, + 800, + 805, + 741, + 747, + 12, + 801, + 806, + 802, + 807, + 803, + 808, + 804, + 809, + 805, + 810, + 747, + 753, + 12, + 806, + 811, + 807, + 812, + 808, + 813, + 809, + 814, + 810, + 815, + 753, + 759, + 12, + 811, + 816, + 812, + 817, + 813, + 818, + 814, + 819, + 815, + 820, + 759, + 765, + 12, + 816, + 772, + 817, + 771, + 818, + 775, + 819, + 777, + 820, + 779, + 765, + 712, + 0 +}; + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TeapotTest::runOne(TeapotResult& res, Window& w) { + + glCullFace(GL_BACK); + glDepthFunc(GL_LESS); + + +// glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, local_view); + + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_POSITION, position); + glLightfv(GL_LIGHT0, GL_SPECULAR, lights[lightWhite].specular); + glLightfv(GL_LIGHT0, GL_DIFFUSE,lights[lightWhite].diffuse); + glLightfv(GL_LIGHT0, GL_AMBIENT,lights[lightWhite].ambient); + + glEnable(GL_LIGHT1); + glLightfv(GL_LIGHT1, GL_POSITION, position2); + glLightfv(GL_LIGHT1, GL_SPECULAR, lights[lightBlue].specular); + glLightfv(GL_LIGHT1, GL_DIFFUSE,lights[lightBlue].diffuse); + glLightfv(GL_LIGHT1, GL_AMBIENT,lights[lightBlue].ambient); + + glFrontFace(GL_CW); + + glShadeModel(GL_SMOOTH); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + +// glEnable(GL_AUTO_NORMAL); +// glEnable(GL_NORMALIZE); + + glMaterialf(GL_FRONT, GL_SHININESS, 0.6*128.0); + + glClearColor(bgColor[0],bgColor[1],bgColor[2], 1.0); + glColor3f(1.0, 1.0, 1.0); + + glViewport(0, 0, (GLint)fWidth, (GLint)fHeight); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + const float scale=1.0; + + glOrtho(-scale, scale, -scale, scale, -scale*depthOfView, scale*depthOfView); +////////////////////////////////// End of Viewport Set-up ///////////////////// + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + int color = 4; + float c[3][4]; + c[0][0] = materials[color].ambient[0]; + c[0][1] = materials[color].ambient[1]; + c[0][2] = materials[color].ambient[2]; + c[1][0] = materials[color].diffuse[0]; + c[1][1] = materials[color].diffuse[1]; + c[1][2] = materials[color].diffuse[2]; + c[2][0] = materials[color].specular[0]; + c[2][1] = materials[color].specular[1]; + c[2][2] = materials[color].specular[2]; + + const int solidity = 0; + float alpha; + if (solidity == 0) + alpha = 1.0; + else if (solidity == 1) + alpha = 0.95; + else if (solidity == 2) + alpha = 0.6; + c[0][3] = c[1][3] = c[2][3] = alpha; + + if (solidity != 0) { + glBlendFunc(GL_SRC_ALPHA,GL_ONE); + glEnable(GL_BLEND); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + } else { + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); + } + + glMaterialfv(GL_FRONT, GL_AMBIENT, c[0]); + glMaterialfv(GL_FRONT, GL_DIFFUSE, c[1]); + glMaterialfv(GL_FRONT, GL_SPECULAR, c[2]); + +///////////////////////// End of materials set-up ////////////////////// + + glInterleavedArrays( GL_N3F_V3F, 0, vertexArrayData ); + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_NORMAL_ARRAY ); + + + // XXX The timing code here doesn't calibrate the timer + // overhead, doesn't scale the size of the test to insure + // consistent results on a wide range of hardware, and doesn't + // flush the pipeline before or after rendering, so the + // numbers that result are only a rough approximation of the + // actual performance. A better solution would be to use the + // timing methodology that's illustrated in tchgperf.cpp. + + Timer tTimer; + double start = tTimer.getClock(); + + const int startX = 0; + const int endX = 360; + + for (int rotX=startX; rotX < endX; rotX++) { + glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + glRotatef(rotX, 1.0,0.0,0.0); + glRotatef(rotX, 0.0,1.0,0.0); + + for (int* p = stripIndices; *p; ) { + glBegin(GL_QUAD_STRIP); + for (int nVertices = *p++; nVertices; --nVertices, ++p) + glArrayElement(*p); + glEnd(); + } + w.swap(); + glPopMatrix(); + } + + double finish = tTimer.getClock(); + + res.fTps = (endX - startX) / (finish - start); + res.pass = true; +} // TeapotTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TeapotTest::logOne(TeapotResult& r) { + logPassFail(r); + env->log << "Teapots/Sec: " << r.fTps << " "; + logConcise(r); +} // TeapotTest::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TeapotTest::compareOne(TeapotResult& oldR, TeapotResult& newR) { + comparePassFail(oldR, newR); + if (oldR.pass == newR.pass) { + if (env->options.verbosity) + env->log << "\tTeapots Comparison: " + << oldR.fTps + << " vs. " + << newR.fTps + << '\n'; + } else { + env->log << "\tTeapots Comparison: " + << oldR.fTps + << " vs. " + << newR.fTps + << '\n'; + } +} // TeapotTest::compareOne + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TeapotTest teapotTest("teapot", "window, rgb, z", + "This test simply displays a teapot, rotates it, and attempts to\n" + "determine the frame/sec the pipeline can generate\n"); + +} // namespace GLEAN diff --git a/tests/glean/tteapot.h b/tests/glean/tteapot.h new file mode 100644 index 00000000..0840a4f7 --- /dev/null +++ b/tests/glean/tteapot.h @@ -0,0 +1,63 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 2000 Adam Haberlach 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 + +#ifndef __tteapot_h_ +#define __tteapot_h_ + +#include "tbase.h" + + +// Simple teapot-drawing benchmark provided by Adam Haberlach. + +namespace GLEAN { + +class TeapotResult: public BaseResult { +public: + bool pass; + double fTps; // speed in "Teapots per Second" + + void putresults(ostream& s) const { + s << pass << '\n'; + s << fTps << '\n'; + } + + bool getresults(istream& s) { + s >> pass; + s >> fTps; + return s.good(); + } +}; + +class TeapotTest: public BaseTest<TeapotResult> { +public: + GLEAN_CLASS_WH(TeapotTest, TeapotResult, 300, 315); +}; + +} // namespace GLEAN + +#endif // __tteapot_h_ diff --git a/tests/glean/ttexcombine.cpp b/tests/glean/ttexcombine.cpp new file mode 100644 index 00000000..ed2ed33b --- /dev/null +++ b/tests/glean/ttexcombine.cpp @@ -0,0 +1,1584 @@ +// 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 + + +// ttexcombine.cpp: Test the GL_EXT_texture_env_combine extension +// Author: Brian Paul (brianp@valinux.com) September 2000 +// +// GL_EXT_texture_env_dot3 extension test +// Author: Gareth Hughes (gareth@valinux.com) January 2001 +// +// GL_ARB_texture_env_crossbar extension test +// Author: Brian Paul (brian@tungstengraphics.com) December 2002 +// +// The challenge with testing this extension is dealing with combinatorial +// explosion. There are 16 state variables in this extension: +// +// GL_COMBINE_RGB_EXT which has 5 possible values +// GL_COMBINE_ALPHA_EXT which has 5 possible values +// GL_SOURCE0_RGB_EXT which has 4 possible values +// GL_SOURCE1_RGB_EXT which has 4 possible values +// GL_SOURCE2_RGB_EXT which has 4 possible values +// GL_SOURCE0_ALPHA_EXT which has 4 possible values +// GL_SOURCE1_ALPHA_EXT which has 4 possible values +// GL_SOURCE2_ALPHA_EXT which has 4 possible values +// GL_OPERAND0_RGB_EXT which has 4 possible values +// GL_OPERAND1_RGB_EXT which has 4 possible values +// GL_OPERAND2_RGB_EXT which has 2 possible values +// GL_OPERAND0_ALPHA_EXT which has 2 possible values +// GL_OPERAND1_ALPHA_EXT which has 2 possible values +// GL_OPERAND2_ALPHA_EXT which has 1 possible value +// GL_RGB_SCALE_EXT which has 3 possible values +// GL_ALPHA_SCALE which has 3 possible values +// +// The product of those values is 117,964,800. And that's just for one +// texture unit! If we wanted to fully exercise N texture units we'd +// need to run 117,964,800 ^ N tests! Ideally we'd also like to test +// with a number of different fragment, texenv and texture colors. +// Clearly we can't test everything. +// +// So, we've partitioned the combination space into subsets defined +// by the ReplaceParams[], AddParams[], InterpolateParams[], etc arrays. +// For multitexture, we do an even more limited set of tests: testing +// all permutations of the 5 combine modes on all texture units. +// +// In the future we might look at programs that use the combine +// extension to see which mode combination are important to them and +// put them into this test. +// + +#include "ttexcombine.h" +#include <cassert> +#include <stdio.h> +#include <cmath> + +#define CLAMP(VAL, MIN, MAX) \ + ((VAL) < (MIN) ? (MIN) : ((VAL) > (MAX) ? (MAX) : (VAL))) + +#define COPY4(DST, SRC) \ +{ \ + (DST)[0] = (SRC)[0]; \ + (DST)[1] = (SRC)[1]; \ + (DST)[2] = (SRC)[2]; \ + (DST)[3] = (SRC)[3]; \ +} + + +namespace GLEAN { + +// +// These objects define the space of tex-env combinations that we exercise. +// Each array element is { state-var, { list of possible values, 0 } }. +// + +TexCombineTest::test_param TexCombineTest::ReplaceParams[] = { + { GL_COMBINE_RGB_EXT, { GL_REPLACE, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_REPLACE, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::AddParams[] = { + { GL_COMBINE_RGB_EXT, { GL_ADD, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_ADD, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::ModulateParams[] = { + { GL_COMBINE_RGB_EXT, { GL_MODULATE, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_MODULATE, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::AddSignedParams[] = { + { GL_COMBINE_RGB_EXT, { GL_ADD_SIGNED_EXT, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_ADD_SIGNED_EXT, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::InterpolateParams[] = { + { GL_COMBINE_RGB_EXT, { GL_INTERPOLATE_EXT, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_INTERPOLATE_EXT, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE2_RGB_EXT, { GL_TEXTURE, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE2_ALPHA_EXT, { GL_TEXTURE, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND2_RGB_EXT, { GL_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND2_ALPHA_EXT, { GL_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::Dot3RGBParams[] = { + { GL_COMBINE_RGB_EXT, { GL_DOT3_RGB_EXT, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_MODULATE, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + +TexCombineTest::test_param TexCombineTest::Dot3RGBAParams[] = { + { GL_COMBINE_RGB_EXT, { GL_DOT3_RGBA_EXT, 0 } }, + { GL_COMBINE_ALPHA_EXT, { GL_MODULATE, 0 } }, + { GL_SOURCE0_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_RGB_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_SOURCE0_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, 0 } }, + { GL_SOURCE1_ALPHA_EXT, { GL_TEXTURE, GL_CONSTANT_EXT, GL_PRIMARY_COLOR_EXT, GL_PREVIOUS_EXT, 0 } }, + { GL_OPERAND0_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_RGB_EXT, { GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND0_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_OPERAND1_ALPHA_EXT, { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 0 } }, + { GL_RGB_SCALE_EXT, { 1, 2, 4, 0 } }, + { GL_ALPHA_SCALE, { 1, 2, 4, 0 } }, + { 0, { 0, 0, 0, 0, 0 } } +}; + + +static void +problem(const char *s) { + cerr << "Problem in combine():" << s << "\n"; +} + + +// +// Set machine parameters to default values. +// +void +TexCombineTest::ResetMachine(glmachine &machine) { + for (int u = 0; u < MAX_TEX_UNITS; u++) { + machine.COMBINE_RGB[u] = GL_MODULATE; + machine.COMBINE_ALPHA[u] = GL_MODULATE; + machine.SOURCE0_RGB[u] = GL_TEXTURE; + machine.SOURCE1_RGB[u] = GL_PREVIOUS_EXT; + machine.SOURCE2_RGB[u] = GL_CONSTANT_EXT; + machine.SOURCE0_ALPHA[u] = GL_TEXTURE; + machine.SOURCE1_ALPHA[u] = GL_PREVIOUS_EXT; + machine.SOURCE2_ALPHA[u] = GL_CONSTANT_EXT; + machine.OPERAND0_RGB[u] = GL_SRC_COLOR; + machine.OPERAND1_RGB[u] = GL_SRC_COLOR; + machine.OPERAND2_RGB[u] = GL_SRC_ALPHA; + machine.OPERAND0_ALPHA[u] = GL_SRC_ALPHA; + machine.OPERAND1_ALPHA[u] = GL_SRC_ALPHA; + machine.OPERAND2_ALPHA[u] = GL_SRC_ALPHA; + machine.RGB_SCALE[u] = 1.0; + machine.ALPHA_SCALE[u] = 1.0; + machine.TexFormat[u] = GL_RGBA; + } +} + + +// +// This computes the expected texcombine result for one texture unit. +// +void +TexCombineTest::ComputeTexCombine(const glmachine &machine, int texUnit, + const GLfloat prevColor[4], + GLfloat result[4]) const { + GLfloat term0[4], term1[4], term2[4], dot; + const GLfloat *colorSrc0, *colorSrc1, *colorSrc2; + const GLfloat *alphaSrc0, *alphaSrc1 = NULL, *alphaSrc2 = NULL; + const GLfloat *fragColor = machine.FragColor; + const GLfloat *constColor = machine.EnvColor[texUnit]; + const GLfloat *texColor = machine.TexColor[texUnit]; + int srcUnit; + + switch (machine.SOURCE0_RGB[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + colorSrc0 = fragColor; + break; + case GL_TEXTURE: + colorSrc0 = texColor; + break; + case GL_CONSTANT_EXT: + colorSrc0 = constColor; + break; + case GL_PREVIOUS_EXT: + colorSrc0 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE0_RGB[texUnit] - GL_TEXTURE0_ARB; + colorSrc0 = machine.TexColor[srcUnit]; + break; + default: + problem("bad rgbSource0"); + return; + } + + switch (machine.SOURCE0_ALPHA[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + alphaSrc0 = fragColor; + break; + case GL_TEXTURE: + alphaSrc0 = texColor; + break; + case GL_CONSTANT_EXT: + alphaSrc0 = constColor; + break; + case GL_PREVIOUS_EXT: + alphaSrc0 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE0_ALPHA[texUnit] - GL_TEXTURE0_ARB; + alphaSrc0 = machine.TexColor[srcUnit]; + break; + default: + problem("bad alphaSource0"); + return; + } + + switch (machine.SOURCE1_RGB[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + colorSrc1 = fragColor; + break; + case GL_TEXTURE: + colorSrc1 = texColor; + break; + case GL_CONSTANT_EXT: + colorSrc1 = constColor; + break; + case GL_PREVIOUS_EXT: + colorSrc1 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE1_RGB[texUnit] - GL_TEXTURE0_ARB; + colorSrc1 = machine.TexColor[srcUnit]; + break; + default: + problem("bad rgbSource1"); + return; + } + + switch (machine.SOURCE1_ALPHA[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + alphaSrc1 = fragColor; + break; + case GL_TEXTURE: + alphaSrc1 = texColor; + break; + case GL_CONSTANT_EXT: + alphaSrc1 = constColor; + break; + case GL_PREVIOUS_EXT: + alphaSrc1 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE1_ALPHA[texUnit] - GL_TEXTURE0_ARB; + alphaSrc1 = machine.TexColor[srcUnit]; + break; + default: + problem("bad alphaSource1"); + return; + } + + switch (machine.SOURCE2_RGB[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + colorSrc2 = fragColor; + break; + case GL_TEXTURE: + colorSrc2 = texColor; + break; + case GL_CONSTANT_EXT: + colorSrc2 = constColor; + break; + case GL_PREVIOUS_EXT: + colorSrc2 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE2_RGB[texUnit] - GL_TEXTURE0_ARB; + colorSrc2 = machine.TexColor[srcUnit]; + break; + default: + problem("bad rgbSource2"); + return; + } + + switch (machine.SOURCE2_ALPHA[texUnit]) { + case GL_PRIMARY_COLOR_EXT: + alphaSrc2 = fragColor; + break; + case GL_TEXTURE: + alphaSrc2 = texColor; + break; + case GL_CONSTANT_EXT: + alphaSrc2 = constColor; + break; + case GL_PREVIOUS_EXT: + alphaSrc2 = prevColor; + break; + case GL_TEXTURE0_ARB: + case GL_TEXTURE1_ARB: + case GL_TEXTURE2_ARB: + case GL_TEXTURE3_ARB: + case GL_TEXTURE4_ARB: + case GL_TEXTURE5_ARB: + case GL_TEXTURE6_ARB: + case GL_TEXTURE7_ARB: + /* GL_ARB_texture_env_crossbar */ + srcUnit = machine.SOURCE2_ALPHA[texUnit] - GL_TEXTURE0_ARB; + alphaSrc2 = machine.TexColor[srcUnit]; + break; + default: + problem("bad alphaSource2"); + return; + } + + switch (machine.OPERAND0_RGB[texUnit]) { + case GL_SRC_COLOR: + term0[0] = colorSrc0[0]; + term0[1] = colorSrc0[1]; + term0[2] = colorSrc0[2]; + break; + case GL_ONE_MINUS_SRC_COLOR: + term0[0] = 1.0 - colorSrc0[0]; + term0[1] = 1.0 - colorSrc0[1]; + term0[2] = 1.0 - colorSrc0[2]; + break; + case GL_SRC_ALPHA: + term0[0] = colorSrc0[3]; + term0[1] = colorSrc0[3]; + term0[2] = colorSrc0[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + term0[0] = 1.0 - colorSrc0[3]; + term0[1] = 1.0 - colorSrc0[3]; + term0[2] = 1.0 - colorSrc0[3]; + break; + default: + problem("bad rgbOperand0"); + return; + } + + switch (machine.OPERAND0_ALPHA[texUnit]) { + case GL_SRC_ALPHA: + term0[3] = alphaSrc0[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + term0[3] = 1.0 - alphaSrc0[3]; + break; + default: + problem("bad alphaOperand0"); + return; + } + + switch (machine.OPERAND1_RGB[texUnit]) { + case GL_SRC_COLOR: + term1[0] = colorSrc1[0]; + term1[1] = colorSrc1[1]; + term1[2] = colorSrc1[2]; + break; + case GL_ONE_MINUS_SRC_COLOR: + term1[0] = 1.0 - colorSrc1[0]; + term1[1] = 1.0 - colorSrc1[1]; + term1[2] = 1.0 - colorSrc1[2]; + break; + case GL_SRC_ALPHA: + term1[0] = colorSrc1[3]; + term1[1] = colorSrc1[3]; + term1[2] = colorSrc1[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + term1[0] = 1.0 - colorSrc1[3]; + term1[1] = 1.0 - colorSrc1[3]; + term1[2] = 1.0 - colorSrc1[3]; + break; + default: + problem("bad rgbOperand1"); + return; + } + + switch (machine.OPERAND1_ALPHA[texUnit]) { + case GL_SRC_ALPHA: + term1[3] = alphaSrc1[3]; + break; + case GL_ONE_MINUS_SRC_ALPHA: + term1[3] = 1.0 - alphaSrc1[3]; + break; + default: + problem("bad alphaOperand1"); + return; + } + + switch (machine.OPERAND2_RGB[texUnit]) { + case GL_SRC_ALPHA: + term2[0] = colorSrc2[3]; + term2[1] = colorSrc2[3]; + term2[2] = colorSrc2[3]; + break; + default: + problem("bad rgbOperand2"); + return; + } + + switch (machine.OPERAND2_ALPHA[texUnit]) { + case GL_SRC_ALPHA: + term2[3] = alphaSrc2[3]; + break; + default: + problem("bad alphaOperand2"); + return; + } + + // Final combine + switch (machine.COMBINE_RGB[texUnit]) { + case GL_REPLACE: + result[0] = term0[0]; + result[1] = term0[1]; + result[2] = term0[2]; + break; + case GL_MODULATE: + result[0] = term0[0] * term1[0]; + result[1] = term0[1] * term1[1]; + result[2] = term0[2] * term1[2]; + break; + case GL_ADD: + result[0] = term0[0] + term1[0]; + result[1] = term0[1] + term1[1]; + result[2] = term0[2] + term1[2]; + break; + case GL_ADD_SIGNED_EXT: + result[0] = term0[0] + term1[0] - 0.5; + result[1] = term0[1] + term1[1] - 0.5; + result[2] = term0[2] + term1[2] - 0.5; + break; + case GL_INTERPOLATE_EXT: + result[0] = term0[0] * term2[0] + term1[0] * (1.0 - term2[0]); + result[1] = term0[1] * term2[1] + term1[1] * (1.0 - term2[1]); + result[2] = term0[2] * term2[2] + term1[2] * (1.0 - term2[2]); + break; + case GL_DOT3_RGB_EXT: + case GL_DOT3_RGBA_EXT: + dot = ((term0[0] - 0.5) * (term1[0] - 0.5) + + (term0[1] - 0.5) * (term1[1] - 0.5) + + (term0[2] - 0.5) * (term1[2] - 0.5)); + result[0] = dot; + result[1] = dot; + result[2] = dot; + if (machine.COMBINE_RGB[texUnit] == GL_DOT3_RGBA_EXT) + result[3] = dot; + break; + default: + problem("bad rgbCombine"); + return; + } + + switch (machine.COMBINE_ALPHA[texUnit]) { + case GL_REPLACE: + result[3] = term0[3]; + break; + case GL_MODULATE: + result[3] = term0[3] * term1[3]; + break; + case GL_ADD: + result[3] = term0[3] + term1[3]; + break; + case GL_ADD_SIGNED_EXT: + result[3] = term0[3] + term1[3] - 0.5; + break; + case GL_INTERPOLATE_EXT: + result[3] = term0[3] * term2[3] + term1[3] * (1.0 - term2[3]); + break; + default: + problem("bad alphaCombine"); + return; + } + + if (machine.COMBINE_RGB[texUnit] == GL_DOT3_RGBA_EXT) { + result[3] = result[0]; + } + + + // scaling + // GH: Remove this crud when the ARB extension is done. It + // most likely won't have this scale factor restriction. + switch (machine.COMBINE_RGB[texUnit]) { + case GL_DOT3_RGB_EXT: + case GL_DOT3_RGBA_EXT: + result[0] *= 4.0; + result[1] *= 4.0; + result[2] *= 4.0; + break; + default: + result[0] *= machine.RGB_SCALE[texUnit]; + result[1] *= machine.RGB_SCALE[texUnit]; + result[2] *= machine.RGB_SCALE[texUnit]; + break; + } + switch (machine.COMBINE_RGB[texUnit]) { + case GL_DOT3_RGBA_EXT: + result[3] *= 4.0; + break; + default: + result[3] *= machine.ALPHA_SCALE[texUnit]; + break; + } + + // final clamping + result[0] = CLAMP(result[0], 0.0, 1.0); + result[1] = CLAMP(result[1], 0.0, 1.0); + result[2] = CLAMP(result[2], 0.0, 1.0); + result[3] = CLAMP(result[3], 0.0, 1.0); +} + + +// +// Return string for an enum value. +// +const char * +EnumString(GLenum pname) +{ + static char s[100]; + switch (pname) { + case GL_COMBINE_RGB_EXT: + return "GL_COMBINE_RGB_EXT"; + case GL_COMBINE_ALPHA_EXT: + return "GL_COMBINE_ALPHA_EXT"; + case GL_REPLACE: + return "GL_REPLACE"; + case GL_MODULATE: + return "GL_MODULATE"; + case GL_ADD: + return "GL_ADD"; + case GL_ADD_SIGNED_EXT: + return "GL_ADD_SIGNED_EXT"; + case GL_INTERPOLATE_EXT: + return "GL_INTERPOLATE_EXT"; + case GL_DOT3_RGB_EXT: + return "GL_DOT3_RGB_EXT"; + case GL_DOT3_RGBA_EXT: + return "GL_DOT3_RGBA_EXT"; + case GL_TEXTURE: + return "GL_TEXTURE"; + case GL_CONSTANT_EXT: + return "GL_CONSTANT_EXT"; + case GL_PRIMARY_COLOR_EXT: + return "GL_PRIMARY_COLOR_EXT"; + case GL_PREVIOUS_EXT: + return "GL_PREVIOUS_EXT"; + case GL_SRC_COLOR: + return "GL_SRC_COLOR"; + case GL_ONE_MINUS_SRC_COLOR: + return "GL_ONE_MINUS_SRC_COLOR"; + case GL_SRC_ALPHA: + return "GL_SRC_ALPHA"; + case GL_ONE_MINUS_SRC_ALPHA: + return "GL_ONE_MINUS_SRC_ALPHA"; + case GL_TEXTURE0_ARB: + return "GL_TEXTURE0_ARB"; + case GL_TEXTURE1_ARB: + return "GL_TEXTURE1_ARB"; + case GL_TEXTURE2_ARB: + return "GL_TEXTURE2_ARB"; + case GL_TEXTURE3_ARB: + return "GL_TEXTURE3_ARB"; + case GL_TEXTURE4_ARB: + return "GL_TEXTURE4_ARB"; + case GL_TEXTURE5_ARB: + return "GL_TEXTURE5_ARB"; + case GL_TEXTURE6_ARB: + return "GL_TEXTURE6_ARB"; + case GL_TEXTURE7_ARB: + return "GL_TEXTURE7_ARB"; + default: + sprintf(s, "0x%04x", (unsigned int) pname); + return s; + } +} + + +// +// Print current values of all machine state vars. +// Used when reporting failures. +// +void +TexCombineTest::PrintMachineState(const glmachine &machine) const { + + env->log << "\tCurrent combine state:\n"; + env->log << "\tIncoming Fragment RGBA = " + << machine.FragColor[0] << ", " + << machine.FragColor[1] << ", " + << machine.FragColor[2] << ", " + << machine.FragColor[3] << "\n"; + for (int u = 0; u < machine.NumTexUnits; u++) { + env->log << "\tTexture Unit " << u << ":\n"; + env->log << "\t GL_COMBINE_RGB_EXT = " + << EnumString(machine.COMBINE_RGB[u]) << "\n"; + env->log << "\t GL_COMBINE_ALPHA_EXT = " + << EnumString(machine.COMBINE_ALPHA[u]) << "\n"; + env->log << "\t GL_SOURCE0_RGB_EXT = " + << EnumString(machine.SOURCE0_RGB[u]) << "\n"; + env->log << "\t GL_SOURCE1_RGB_EXT = " + << EnumString(machine.SOURCE1_RGB[u]) << "\n"; + env->log << "\t GL_SOURCE2_RGB_EXT = " + << EnumString(machine.SOURCE2_RGB[u]) << "\n"; + env->log << "\t GL_SOURCE0_ALPHA_EXT = " + << EnumString(machine.SOURCE0_ALPHA[u]) << "\n"; + env->log << "\t GL_SOURCE1_ALPHA_EXT = " + << EnumString(machine.SOURCE1_ALPHA[u]) << "\n"; + env->log << "\t GL_SOURCE2_ALPHA_EXT = " + << EnumString(machine.SOURCE2_ALPHA[u]) << "\n"; + env->log << "\t GL_OPERAND0_RGB_EXT = " + << EnumString(machine.OPERAND0_RGB[u]) << "\n"; + env->log << "\t GL_OPERAND1_RGB_EXT = " + << EnumString(machine.OPERAND1_RGB[u]) << "\n"; + env->log << "\t GL_OPERAND2_RGB_EXT = " + << EnumString(machine.OPERAND2_RGB[u]) << "\n"; + env->log << "\t GL_OPERAND0_ALPHA_EXT = " + << EnumString(machine.OPERAND0_ALPHA[u]) << "\n"; + env->log << "\t GL_OPERAND1_ALPHA_EXT = " + << EnumString(machine.OPERAND1_ALPHA[u]) << "\n"; + env->log << "\t GL_OPERAND2_ALPHA_EXT = " + << EnumString(machine.OPERAND2_ALPHA[u]) << "\n"; + env->log << "\t GL_RGB_SCALE_EXT = " + << machine.RGB_SCALE[u] << "\n"; + env->log << "\t GL_ALPHA_SCALE = " + << machine.ALPHA_SCALE[u] << "\n"; + env->log << "\t Tex Env RGBA = " + << machine.EnvColor[u][0] << ", " + << machine.EnvColor[u][1] << ", " + << machine.EnvColor[u][2] << ", " + << machine.EnvColor[u][3] << "\n"; + switch (machine.TexFormat[u]) { + case GL_ALPHA: + env->log << "\t Texture ALPHA = " + << machine.TexColor[u][3] << "\n"; + break; + case GL_LUMINANCE: + env->log << "\t Texture LUMINANCE = " + << machine.TexColor[u][0] << "\n"; + break; + case GL_LUMINANCE_ALPHA: + env->log << "\t Texture RGBA = " + << machine.TexColor[u][0] << ", " + << machine.TexColor[u][3] << "\n"; + break; + case GL_INTENSITY: + env->log << "\t Texture INTENSITY = " + << machine.TexColor[u][0] << "\n"; + break; + case GL_RGB: + env->log << "\t Texture RGB = " + << machine.TexColor[u][0] << ", " + << machine.TexColor[u][1] << ", " + << machine.TexColor[u][2] << "\n"; + break; + case GL_RGBA: + env->log << "\t Texture RGBA = " + << machine.TexColor[u][0] << ", " + << machine.TexColor[u][1] << ", " + << machine.TexColor[u][2] << ", " + << machine.TexColor[u][3] << "\n"; + break; + } + + } +} + + +// +// Check that the actual GL implementation's texture state matches what's +// in the given glean machine state. This is only used for debugging. +// +bool +TexCombineTest::VerifyMachineState(const glmachine &machine) const { + +#define VERIFY(var, expected) \ + glGetTexEnviv(GL_TEXTURE_ENV, var, &actual); \ + if ((GLint) (expected) != (actual)) { \ + cerr << "Expected " << var << " = " \ + << EnumString(expected) \ + << " but got " \ + << EnumString(actual) \ + << "\n"; \ + return false; \ + } +#define VERIFYF(var, expected) \ + glGetTexEnvfv(GL_TEXTURE_ENV, var, &actualf); \ + if ((expected) != (actualf)) { \ + cerr << "Expected " << var << " = " \ + << expected \ + << " but got " \ + << actualf \ + << "\n"; \ + return false; \ + } + + + for (int u = 0; u < machine.NumTexUnits; u++) { + GLint actual; + GLfloat actualf; + VERIFY(GL_COMBINE_RGB_EXT, machine.COMBINE_RGB[u]); + VERIFY(GL_COMBINE_ALPHA_EXT, machine.COMBINE_ALPHA[u]); + VERIFY(GL_SOURCE0_RGB_EXT, machine.SOURCE0_RGB[u]); + VERIFY(GL_SOURCE1_RGB_EXT, machine.SOURCE1_RGB[u]); + VERIFY(GL_SOURCE2_RGB_EXT, machine.SOURCE2_RGB[u]); + VERIFY(GL_OPERAND0_RGB_EXT, machine.OPERAND0_RGB[u]); + VERIFY(GL_OPERAND1_RGB_EXT, machine.OPERAND1_RGB[u]); + VERIFY(GL_OPERAND2_RGB_EXT, machine.OPERAND2_RGB[u]); + VERIFYF(GL_RGB_SCALE_EXT, machine.RGB_SCALE[u]); + VERIFYF(GL_ALPHA_SCALE, machine.ALPHA_SCALE[u]); + } + + return true; // state is AOK +} + + +// +// Print an error report. +// +void +TexCombineTest::ReportFailure(const glmachine &machine, + const GLfloat expected[4], + const GLfloat rendered[4], + BasicResult& r, + const char *where) { + + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n' + << "\texpected " + << expected[0] << ", " + << expected[1] << ", " + << expected[2] << ", " + << expected[3] << ", got " + << rendered[0] << ", " + << rendered[1] << ", " + << rendered[2] << ", " + << rendered[3] + << " in " << where << "\n"; + PrintMachineState(machine); +} + + +// +// Examine a set of test params and compute the number of possible +// state combinations. +// +int +TexCombineTest::CountTestCombinations(const test_param testParams[]) const { + + int numTests = 1; + for (int t = 0; testParams[t].target; t++) { + int values = 0; + for (int val = 0; testParams[t].validValues[val]; val++) { + values++; + } + numTests *= values; + } + return numTests; +} + + +// +// Setup the actual GL state and our internal simulated GL state. +// +void +TexCombineTest::TexEnv(glmachine &machine, int texUnit, + GLenum target, GLenum value) { + + if (machine.NumTexUnits > 1) + p_glActiveTextureARB(GL_TEXTURE0_ARB + texUnit); + + glTexEnvi(GL_TEXTURE_ENV, target, value); + int err = glGetError(); + if (err != GL_NO_ERROR) + printf("Problem: glTexEnvi() generated error 0x%x\n", err); + + switch (target) { + case GL_COMBINE_RGB_EXT: + machine.COMBINE_RGB[texUnit] = value; + break; + case GL_COMBINE_ALPHA_EXT: + machine.COMBINE_ALPHA[texUnit] = value; + break; + case GL_SOURCE0_RGB_EXT: + machine.SOURCE0_RGB[texUnit] = value; + break; + case GL_SOURCE1_RGB_EXT: + machine.SOURCE1_RGB[texUnit] = value; + break; + case GL_SOURCE2_RGB_EXT: + machine.SOURCE2_RGB[texUnit] = value; + break; + case GL_SOURCE0_ALPHA_EXT: + machine.SOURCE0_ALPHA[texUnit] = value; + break; + case GL_SOURCE1_ALPHA_EXT: + machine.SOURCE1_ALPHA[texUnit] = value; + break; + case GL_SOURCE2_ALPHA_EXT: + machine.SOURCE2_ALPHA[texUnit] = value; + break; + case GL_OPERAND0_RGB_EXT: + machine.OPERAND0_RGB[texUnit] = value; + break; + case GL_OPERAND1_RGB_EXT: + machine.OPERAND1_RGB[texUnit] = value; + break; + case GL_OPERAND2_RGB_EXT: + machine.OPERAND2_RGB[texUnit] = value; + break; + case GL_OPERAND0_ALPHA_EXT: + machine.OPERAND0_ALPHA[texUnit] = value; + break; + case GL_OPERAND1_ALPHA_EXT: + machine.OPERAND1_ALPHA[texUnit] = value; + break; + case GL_OPERAND2_ALPHA_EXT: + machine.OPERAND2_ALPHA[texUnit] = value; + break; + case GL_RGB_SCALE_EXT: + machine.RGB_SCALE[texUnit] = value; + break; + case GL_ALPHA_SCALE: + machine.ALPHA_SCALE[texUnit] = value; + break; + } +} + + +// +// Make the glTexEnv calls to setup one particular set of test parameters +// from <testParams>. +// <testNum> must be between 0 and CountTestCombinations(testParams)-1. +// +void +TexCombineTest::SetupTestEnv(struct glmachine &machine, int texUnit, + int testNum, const struct test_param testParams[]) { + + int divisor = 1; + for (int t = 0; testParams[t].target; t++) { + int numValues = 0; + for (int val = 0; testParams[t].validValues[val]; val++) { + numValues++; + } + int v = (testNum / divisor) % numValues; + GLenum target = testParams[t].target; + GLenum value = testParams[t].validValues[v]; + TexEnv(machine, texUnit, target, value); + divisor *= numValues; + } +} + + +// +// Set the fragment, texenv (constant), and texture colors for all the +// machine's texture units. +// +void +TexCombineTest::SetupColors(glmachine &machine) { + + static const GLfloat fragColor[4] = { 0.00, 0.25, 0.50, 0.75 }; + static const GLfloat envColors[][4] = { + { 0.25, 0.50, 0.75, 1.00 }, + { 0.50, 0.75, 1.00, 0.00 }, + { 0.75, 1.00, 0.00, 0.25 }, + { 1.00, 0.00, 0.25, 0.50 } + }; + static const GLfloat texColors[][8] = { + { 1.00, 0.00, 0.25, 0.50 }, + { 0.75, 1.00, 0.00, 0.25 }, + { 0.50, 0.75, 1.00, 0.00 }, + { 0.25, 0.50, 0.75, 1.00 }, + // extra colors that'll only be used for crossbar test + { 0.00, 0.00, 0.00, 0.00 }, + { 0.25, 0.50, 0.50, 0.00 }, + { 0.50, 0.25, 0.75, 0.25 }, + { 0.75, 1.00, 0.25, 0.00 } + }; + + COPY4(machine.FragColor, fragColor); + glColor4fv(fragColor); + + for (int u = 0; u < machine.NumTexUnits; u++) { + if (machine.NumTexUnits > 1) + p_glActiveTextureARB(GL_TEXTURE0_ARB + u); + glBindTexture(GL_TEXTURE_2D, mTextures[u]); + glEnable(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); + machine.EnvColor[u][0] = envColors[u % 4][0]; + machine.EnvColor[u][1] = envColors[u % 4][1]; + machine.EnvColor[u][2] = envColors[u % 4][2]; + machine.EnvColor[u][3] = envColors[u % 4][3]; + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, + envColors[u % 4]); + + const GLfloat *texCol = texColors[u % 8]; + + // Setup texture color, according to texture format + switch (machine.TexFormat[u]) { + case GL_RGBA: + machine.TexColor[u][0] = texCol[0]; + machine.TexColor[u][1] = texCol[1]; + machine.TexColor[u][2] = texCol[2]; + machine.TexColor[u][3] = texCol[3]; + break; + case GL_RGB: + machine.TexColor[u][0] = texCol[0]; + machine.TexColor[u][1] = texCol[1]; + machine.TexColor[u][2] = texCol[2]; + machine.TexColor[u][3] = 1.0; + break; + case GL_ALPHA: + machine.TexColor[u][0] = 0.0; + machine.TexColor[u][1] = 0.0; + machine.TexColor[u][2] = 0.0; + machine.TexColor[u][3] = texCol[3]; + break; + case GL_LUMINANCE: + machine.TexColor[u][0] = texCol[0]; + machine.TexColor[u][1] = texCol[0]; + machine.TexColor[u][2] = texCol[0]; + machine.TexColor[u][3] = 1.0; + break; + case GL_LUMINANCE_ALPHA: + machine.TexColor[u][0] = texCol[0]; + machine.TexColor[u][1] = texCol[0]; + machine.TexColor[u][2] = texCol[0]; + machine.TexColor[u][3] = texCol[3]; + break; + case GL_INTENSITY: + machine.TexColor[u][0] = texCol[0]; + machine.TexColor[u][1] = texCol[0]; + machine.TexColor[u][2] = texCol[0]; + machine.TexColor[u][3] = texCol[0]; + break; + default: + problem("bad texture format"); + return; + } + + // Make a 4x4 solid color texture + GLfloat image[16][4]; + int i; + for (i = 0; i < 16; i++) { + image[i][0] = texColors[u % 8][0]; + image[i][1] = texColors[u % 8][1]; + image[i][2] = texColors[u % 8][2]; + image[i][3] = texColors[u % 8][3]; + } + glTexImage2D(GL_TEXTURE_2D, 0, machine.TexFormat[u], + 4, 4, 0, GL_RGBA, GL_FLOAT, image); + +#if 0 // Debug + GLfloat check[16][4]; + GLint r, g, b, a; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_RED_SIZE, &r); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_GREEN_SIZE, &g); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_BLUE_SIZE, &b); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_ALPHA_SIZE, &a); + printf("Texture bits: %d %d %d %d\n", r, g, b, a); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, + check); + for (i = 0;i < 16; i++) { + printf("%2d: %4f %4f %4f %4f %4f %4f %4f %4f\n", i, + image[i][0], image[i][1], + image[i][2], image[i][3], + check[i][0], check[i][1], + check[i][2], check[i][3]); + } +#endif + } +} + + +// +// Test texenv-combine with a single texture unit. +// +bool +TexCombineTest::RunSingleTextureTest(glmachine &machine, + const test_param testParams[], BasicResult &r, Window& w) { + + assert(machine.NumTexUnits == 1); + SetupColors(machine); + + const int numTests = CountTestCombinations(testParams); + //printf("Testing %d combinations\n", numTests); + + for (int test = 0; test < numTests; test++) { + // 0. Setup state + ResetMachine(machine); + SetupTestEnv(machine, 0, test, testParams); + + // 1. Render with OpenGL + GLfloat renderedResult[4]; + glTexCoord2f(0, 0); // use texcoord (0,0) for all vertices + glBegin(GL_POLYGON); + glVertex2f(-1.0, -1.0); + glVertex2f( 1.0, -1.0); + glVertex2f( 1.0, 1.0); + glVertex2f(-1.0, 1.0); + glEnd(); + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, renderedResult); + w.swap(); + + // 2. Compute expected result + GLfloat expected[4]; + ComputeTexCombine(machine, 0, machine.FragColor, expected); + + // 3. Compare rendered result to expected result + const GLfloat dr = fabs(expected[0] - renderedResult[0]); + const GLfloat dg = fabs(expected[1] - renderedResult[1]); + const GLfloat db = fabs(expected[2] - renderedResult[2]); + const GLfloat da = fabs(expected[3] - renderedResult[3]); + if (dr > mTolerance[0] || dg > mTolerance[1] || + db > mTolerance[2] || da > mTolerance[3]) { + ReportFailure(machine, expected, renderedResult, r, + "Single Texture Test"); +#if 0 // Debug + VerifyMachineState(machine); + // For debugging, printing the state of the previous + // test is useful to see what's changed when we've + // failed a test but passed the previous one. + printf("single-texture test %d failed\n", test); + if (test > 0) { + printf("prev test:\n"); + SetupTestEnv(machine, 0, test - 1, testParams); + PrintMachineState(machine); + } +#endif + return false; + } +#if 0 // Debug + else { + printf("PASSED test %d!\n", test); + env->log << "\texpected " + << expected[0] << ", " + << expected[1] << ", " + << expected[2] << ", " + << expected[3] << ", got " + << renderedResult[0] << ", " + << renderedResult[1] << ", " + << renderedResult[2] << ", " + << renderedResult[3] << "\n"; + // PrintMachineState(machine); + } +#endif + } + return true; +} + + + +// +// For each texture unit, test each texenv-combine mode. +// That's 5 ^ NumTexUnits combinations. +// Or 7 ^ numTexUnits if DOT3 combine mode is supported +// +int +TexCombineTest::CountMultiTextureTestCombinations(const glmachine &machine) const { + + int numTests = 1; + int numUnits = machine.NumTexUnits > 4 ? 4 : machine.NumTexUnits; + for (int i = 0; i < numUnits; i++) + numTests *= (haveDot3 ? 7 : 5); + + return numTests; +} + + +// +// Test texenv-combine with multiple texture units. +// +bool +TexCombineTest::RunMultiTextureTest(glmachine &machine, BasicResult &r, + Window& w) { + + static const GLenum combineModes[7] = { + GL_REPLACE, + GL_ADD, + GL_ADD_SIGNED_EXT, + GL_MODULATE, + GL_INTERPOLATE_EXT, + GL_DOT3_RGB_EXT, + GL_DOT3_RGBA_EXT + }; + static const int numModes = haveDot3 ? 7 : 5; + + // four texture units is enough to test + if (machine.NumTexUnits > 4) + machine.NumTexUnits = 4; + + const int numTests = CountMultiTextureTestCombinations(machine); + //printf("Testing %d multitexture combinations\n", numTests); + + SetupColors(machine); + for (int testNum = 0; testNum < numTests; testNum++) { + // 0. Set up texture units + ResetMachine(machine); + int divisor = 1; + int u; + for (u = 0; u < machine.NumTexUnits; u++) { + const int m = (testNum / divisor) % numModes; + const GLenum mode = combineModes[m]; + + // Set GL_COMBINE_RGB_EXT and GL_COMBINE_ALPHA_EXT + TexEnv(machine, u, GL_COMBINE_RGB_EXT, mode); + TexEnv(machine, u, GL_COMBINE_ALPHA_EXT, + (mode == GL_DOT3_RGB_EXT || + mode == GL_DOT3_RGBA_EXT) ? GL_REPLACE : mode); + TexEnv(machine, u, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); + TexEnv(machine, u, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); + TexEnv(machine, u, GL_SOURCE2_RGB_EXT, GL_TEXTURE); + TexEnv(machine, u, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT); + TexEnv(machine, u, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT); + TexEnv(machine, u, GL_SOURCE2_ALPHA_EXT, GL_TEXTURE); + TexEnv(machine, u, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR); + TexEnv(machine, u, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR); + TexEnv(machine, u, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA); + TexEnv(machine, u, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA); + TexEnv(machine, u, GL_OPERAND1_ALPHA_EXT, GL_ONE_MINUS_SRC_ALPHA); + TexEnv(machine, u, GL_OPERAND2_ALPHA_EXT, GL_SRC_ALPHA); + TexEnv(machine, u, GL_RGB_SCALE_EXT, 1); + TexEnv(machine, u, GL_ALPHA_SCALE, 1); + + //printf("texenv%d = %s ", u, EnumString(mode)); + divisor *= numModes; + } + //printf("\n"); + + // 1. Render with OpenGL + GLfloat renderedResult[4]; + // use texcoord (0,0) for all vertices + for (int u = 0; u < machine.NumTexUnits; u++) + p_glMultiTexCoord2fARB(GL_TEXTURE0_ARB + u, 0, 0); + glBegin(GL_POLYGON); + glVertex2f(-1.0, -1.0); + glVertex2f( 1.0, -1.0); + glVertex2f( 1.0, 1.0); + glVertex2f(-1.0, 1.0); + glEnd(); + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, renderedResult); + w.swap(); + + // 2. Compute expected result + GLfloat prevColor[4]; + GLfloat expected[4]; + for (u = 0; u < machine.NumTexUnits; u++) { + if (u == 0) { + COPY4(prevColor, machine.FragColor); + } else { + COPY4(prevColor, expected); + } + ComputeTexCombine(machine, u, prevColor, expected); + } + + // 3. Compare rendered result to expected result + const GLfloat dr = fabs(expected[0] - renderedResult[0]); + const GLfloat dg = fabs(expected[1] - renderedResult[1]); + const GLfloat db = fabs(expected[2] - renderedResult[2]); + const GLfloat da = fabs(expected[3] - renderedResult[3]); + if (dr > mTolerance[0] || dg > mTolerance[1] || + db > mTolerance[2] || da > mTolerance[3]) { + ReportFailure(machine, expected, renderedResult, r, + "Multi-texture test"); +#if 0 // Debug + printf("multitex test %d failed\n", testNum); + if (testNum > 0) { + printf("prev test:\n"); + SetupTestEnv(machine, 0, testNum - 1, testParams); + PrintMachineState(machine); + } +#endif + + return false; + } + } + return true; +} + + +int +TexCombineTest::CountCrossbarCombinations() const +{ + GLint numUnits; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &numUnits); + return numUnits; +} + + +bool +TexCombineTest::RunCrossbarTest(glmachine &machine, BasicResult &r, Window& w) { + // We do a really short, simple test for GL_ARB_texture_env_crossbar + // since the preceeding tests are pretty comprehensive and the + // crossbar feature is just an incremental addition. + // Basically, if we have N texture units we run N tests. + // For test [i] we set texture unit [i] to fetch the texture color + // from unit [numUnits - i - 1]. For units != i we use the constant + // color (0,0,0,0). We use GL_ADD mode to compute the sum over all units. + // So effectively, the result of texture combine is simply the incoming + // fragment color plus unit [numUnits - test - 1]'s texture color. + + int unit; + + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, (GLint *) &Machine.NumTexUnits); + + // Set up constant texture state for all tests + ResetMachine(machine); + SetupColors(machine); + for (unit = 0; unit < machine.NumTexUnits; unit++) { + TexEnv(machine, unit, GL_COMBINE_RGB_EXT, GL_ADD); + TexEnv(machine, unit, GL_COMBINE_ALPHA_EXT, GL_ADD); + TexEnv(machine, unit, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); + TexEnv(machine, unit, GL_SOURCE0_ALPHA_EXT, GL_PREVIOUS_EXT); + // SOURCE1_RGB/ALPHA is set below, per test + TexEnv(machine, unit, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR); + TexEnv(machine, unit, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR); + TexEnv(machine, unit, GL_OPERAND2_RGB_EXT, GL_SRC_ALPHA); + TexEnv(machine, unit, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA); + TexEnv(machine, unit, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA); + TexEnv(machine, unit, GL_OPERAND2_ALPHA_EXT, GL_SRC_ALPHA); + TexEnv(machine, unit, GL_RGB_SCALE_EXT, 1); + TexEnv(machine, unit, GL_ALPHA_SCALE, 1); + + machine.EnvColor[unit][0] = 0.0F; + machine.EnvColor[unit][1] = 0.0F; + machine.EnvColor[unit][2] = 0.0F; + machine.EnvColor[unit][3] = 0.0F; + p_glActiveTextureARB(GL_TEXTURE0_ARB + unit); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, + machine.EnvColor[unit]); + } + + for (int test = 0; test < machine.NumTexUnits; test++) { + // 1. Set up texture state + for (unit = 0; unit < machine.NumTexUnits; unit++) { + if (unit == test) { + const int revUnit = machine.NumTexUnits - unit - 1; + TexEnv(machine, unit, GL_SOURCE1_RGB_EXT, + GL_TEXTURE0_ARB + revUnit); + TexEnv(machine, unit, GL_SOURCE1_ALPHA_EXT, + GL_TEXTURE0_ARB + revUnit); + } + else { + TexEnv(machine, unit, GL_SOURCE1_RGB_EXT, GL_CONSTANT_EXT); + TexEnv(machine, unit, GL_SOURCE1_ALPHA_EXT, GL_CONSTANT_EXT); + } + } + + // 2. Render with OpenGL + GLfloat renderedResult[4]; + // texcoord (0,) for all vertices is OK + for (unit = 0; unit < machine.NumTexUnits; unit++) + p_glMultiTexCoord2fARB(GL_TEXTURE0_ARB + unit, 0, 0); + glColor4fv(machine.FragColor); + glBegin(GL_POLYGON); + glVertex2f(-1.0, -1.0); + glVertex2f( 1.0, -1.0); + glVertex2f( 1.0, 1.0); + glVertex2f(-1.0, 1.0); + glEnd(); + glReadPixels(0, 0, 1, 1, GL_RGBA, GL_FLOAT, renderedResult); + w.swap(); + + // 3. Compute expected result + GLfloat prevColor[4]; + GLfloat expected[4]; + for (unit = 0; unit < machine.NumTexUnits; unit++) { + if (unit == 0) { + COPY4(prevColor, machine.FragColor); + } else { + COPY4(prevColor, expected); + } + ComputeTexCombine(machine, unit, prevColor, expected); + } + + // 4. Compare rendered result to expected result + const GLfloat dr = fabs(expected[0] - renderedResult[0]); + const GLfloat dg = fabs(expected[1] - renderedResult[1]); + const GLfloat db = fabs(expected[2] - renderedResult[2]); + const GLfloat da = fabs(expected[3] - renderedResult[3]); + if (dr > mTolerance[0] || dg > mTolerance[1] || + db > mTolerance[2] || da > mTolerance[3]) { + ReportFailure(machine, expected, renderedResult, r, + "Texture crossbar test"); +#if 0 // Debug + printf("crossbar test %d failed\n", testNum); + PrintMachineState(machine); +#endif + return false; + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +// XXX should we run a number of individual tests instead? +void +TexCombineTest::runOne(BasicResult& r, Window& w) { + // Grab pointers to the extension functions. It's safe to use + // these without testing them because we already know that we + // won't be invoked except on contexts that support the + // extension. + p_glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) + (GLUtils::getProcAddress("glActiveTextureARB")); + p_glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) + (GLUtils::getProcAddress("glMultiTexCoord2fARB")); + + // Test the availability of the DOT3 extenstion + haveDot3 = GLUtils::haveExtensions("GL_EXT_texture_env_dot3"); + if (0 == haveDot3) + haveDot3 = GLUtils::haveExtensions("GL_ARB_texture_env_dot3"); + + haveCrossbar = GLUtils::haveExtensions("GL_ARB_texture_env_crossbar"); + + // compute RGB error tolerance + { + GLint rBits, gBits, bBits, aBits; + GLint rTexBits, gTexBits, bTexBits, aTexBits; + GLfloat texImage[4][4][4]; + // Make dummy texture image + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, + GL_RGBA, GL_FLOAT, texImage); + glGetIntegerv(GL_RED_BITS, &rBits); + glGetIntegerv(GL_GREEN_BITS, &gBits); + glGetIntegerv(GL_BLUE_BITS, &bBits); + glGetIntegerv(GL_ALPHA_BITS, &aBits); + 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); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, + GL_TEXTURE_ALPHA_SIZE, &aTexBits); + // find smaller of frame buffer and texture bits + rBits = (rBits < rTexBits) ? rBits : rTexBits; + gBits = (gBits < gTexBits) ? gBits : gTexBits; + bBits = (bBits < bTexBits) ? bBits : bTexBits; + aBits = (aBits < aTexBits) ? aBits : aTexBits; + // tolerance is 3 bits of error + mTolerance[0] = 8.0 / (1 << rBits); + mTolerance[1] = 8.0 / (1 << gBits); + mTolerance[2] = 8.0 / (1 << bBits); + if (aBits == 0) + mTolerance[3] = 1.0; + else + mTolerance[3] = 8.0 / (1 << aBits); + /* + printf("Tolerance: %g %g %g %g\n", + mTolerance[0], mTolerance[1], + mTolerance[2], mTolerance[3]); + */ + } + + // Allocate our textures + glGenTextures(MAX_TEX_UNITS, mTextures); + + // We'll only render a 4-pixel polygon + glViewport(0, 0, 2, 2); + + ResetMachine(Machine); + Machine.NumTexUnits = 1; + + // Do single texture unit tests first. + bool passed = RunSingleTextureTest(Machine, ReplaceParams, r, w); + if (passed) + passed = RunSingleTextureTest(Machine, AddParams, r, w); + if (passed) + passed = RunSingleTextureTest(Machine, AddSignedParams, r, w); + if (passed) + passed = RunSingleTextureTest(Machine, ModulateParams, r, w); + if (passed) + passed = RunSingleTextureTest(Machine, InterpolateParams, r, w); + if (passed && haveDot3) + passed = RunSingleTextureTest(Machine, Dot3RGBParams, r, w); + if (passed && haveDot3) + passed = RunSingleTextureTest(Machine, Dot3RGBAParams, r, w); + + // Now do some multi-texture tests + if (passed) { + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, + (GLint *) &Machine.NumTexUnits); + if (Machine.NumTexUnits > 1) { + passed = RunMultiTextureTest(Machine, r, w); + } + } + + // Do crossbar tests + if (passed && haveCrossbar) { + passed = RunCrossbarTest(Machine, r, w); + } + + r.pass = passed; + + // Delete our textures + glDeleteTextures(MAX_TEX_UNITS, mTextures); + +} // TexCombineTest::runOne + +void +TexCombineTest::logOne(BasicResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + env->log << "\tTested " + << CountTestCombinations(ReplaceParams) + << " GL_REPLACE combinations\n"; + env->log << "\tTested " + << CountTestCombinations(AddParams) + << " GL_ADD combinations\n"; + env->log << "\tTested " + << CountTestCombinations(AddSignedParams) + << " GL_ADD_SIGNED_EXT combinations\n"; + env->log << "\tTested " + << CountTestCombinations(ModulateParams) + << " GL_MODULATE combinations\n"; + env->log << "\tTested " + << CountTestCombinations(InterpolateParams) + << " GL_INTERPOLATE_EXT combinations\n"; + if (haveDot3) { + env->log << "\tTested " + << CountTestCombinations(Dot3RGBParams) + << " GL_DOT3_RGB_EXT combinations\n"; + env->log << "\tTested " + << CountTestCombinations(Dot3RGBAParams) + << " GL_DOT3_RGBA_EXT combinations\n"; + } + env->log << "\tTested " + << CountMultiTextureTestCombinations(Machine) + << " multitexture combinations\n"; + if (haveCrossbar) { + env->log << "\tTested " + << CountCrossbarCombinations() + << " crossbar combinations\n"; + } + } +} // TexCombineTest::logOne + + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexCombineTest texCombTest("texCombine", "window, rgb", + + "GL_EXT_texture_env_combine verification test.\n" + "We only test a subset of all possible texture env combinations\n" + "because there's simply too many to exhaustively test them all.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/ttexcombine.h b/tests/glean/ttexcombine.h new file mode 100644 index 00000000..59b3b33b --- /dev/null +++ b/tests/glean/ttexcombine.h @@ -0,0 +1,135 @@ +// 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 + + +// ttexcombing.h: Test the GL_EXT_texture_env_combine extension +// Author: Brian Paul (brianp@valinux.com) September 2000 + + +#ifndef __ttexcombine_h__ +#define __ttexcombine_h__ + +#include "tbasic.h" + +namespace GLEAN { + +#define MAX_TEX_UNITS 8 + +class TexCombineTest: public BasicTest { + public: + TexCombineTest(const char* testName, const char* filter, + const char* description): +#if (__AGL__) + BasicTest(testName, filter, "GL_ARB_texture_env_combine", + description) { +#else + BasicTest(testName, filter, "GL_EXT_texture_env_combine", + description) { +#endif + fWidth = 2; + fHeight = 2; + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + // Our model of GL machine state + struct glmachine { + GLenum COMBINE_RGB[MAX_TEX_UNITS]; + GLenum COMBINE_ALPHA[MAX_TEX_UNITS]; + GLenum SOURCE0_RGB[MAX_TEX_UNITS]; + GLenum SOURCE1_RGB[MAX_TEX_UNITS]; + GLenum SOURCE2_RGB[MAX_TEX_UNITS]; + GLenum SOURCE0_ALPHA[MAX_TEX_UNITS]; + GLenum SOURCE1_ALPHA[MAX_TEX_UNITS]; + GLenum SOURCE2_ALPHA[MAX_TEX_UNITS]; + GLenum OPERAND0_RGB[MAX_TEX_UNITS]; + GLenum OPERAND1_RGB[MAX_TEX_UNITS]; + GLenum OPERAND2_RGB[MAX_TEX_UNITS]; + GLenum OPERAND0_ALPHA[MAX_TEX_UNITS]; + GLenum OPERAND1_ALPHA[MAX_TEX_UNITS]; + GLenum OPERAND2_ALPHA[MAX_TEX_UNITS]; + GLfloat RGB_SCALE[MAX_TEX_UNITS]; + GLfloat ALPHA_SCALE[MAX_TEX_UNITS]; + GLfloat FragColor[4]; // fragment color + GLfloat EnvColor[MAX_TEX_UNITS][4]; // texture env color + GLfloat TexColor[MAX_TEX_UNITS][4]; // texture image color + GLenum TexFormat[MAX_TEX_UNITS]; // texture base format + int NumTexUnits; + }; + + // describes possible state combinations + struct test_param { + GLenum target; + GLenum validValues[6]; + }; + + glmachine Machine; + static test_param ReplaceParams[]; + static test_param ModulateParams[]; + static test_param AddParams[]; + static test_param AddSignedParams[]; + static test_param InterpolateParams[]; + static test_param Dot3RGBParams[]; + static test_param Dot3RGBAParams[]; + static test_param MultitexParams[]; + static test_param CrossbarParams[]; + bool haveDot3; + bool haveCrossbar; + GLfloat mTolerance[4]; + GLuint mTextures[MAX_TEX_UNITS]; + + void ResetMachine(glmachine &machine); + void ComputeTexCombine(const glmachine &machine, int texUnit, + const GLfloat prevColor[4], GLfloat result[4]) const; + void PrintMachineState(const glmachine &machine) const; + bool VerifyMachineState(const glmachine &machine) const; + void ReportFailure(const glmachine &machine, const GLfloat expected[4], + const GLfloat rendered[4], BasicResult &r, const char *where); + void TexEnv(glmachine &machine, int texUnit, GLenum target, + GLenum value); + void SetupTestEnv(glmachine &machine, int texUnit, int testNum, + const test_param testParams[]); + void SetupColors(struct glmachine &machine); + int CountTestCombinations(const test_param testParams[]) const; + bool RunSingleTextureTest(glmachine &machine, + const test_param testParams[], BasicResult &r, Window &w); + int CountMultiTextureTestCombinations(const glmachine &machine) const; + bool RunMultiTextureTest(glmachine &machine, BasicResult &r, Window &w); + int CountCrossbarCombinations() const; + bool RunCrossbarTest(glmachine &machine, BasicResult &r, Window &w); + + PFNGLACTIVETEXTUREARBPROC p_glActiveTextureARB; + PFNGLMULTITEXCOORD2FARBPROC p_glMultiTexCoord2fARB; + +}; // class TexCombineTest + +} // namespace GLEAN + +#endif // __ttexcombine_h__ diff --git a/tests/glean/ttexcube.cpp b/tests/glean/ttexcube.cpp new file mode 100644 index 00000000..1d8ac29f --- /dev/null +++ b/tests/glean/ttexcube.cpp @@ -0,0 +1,426 @@ +// 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 + + +// ttexcube.cpp: Test the GL_ARB_texture_cube_map extension +// Author: Brian Paul (brianp@valinux.com) March 2001 +// +// Test procedure: +// We build a 6-sided texture cube map in which each side is a simple 2x2 +// checkboard pattern with known colors. Then we do three sets of tests. +// Each test draws a single quadrilateral. The tests are: +// +// 1. Directly specify texture coordinates. By changing the texture coords +// we can sample specific regions of the cube map. Check the rendered +// quad colors for correctness. +// 2. Use GL_NORMAL_MAP_ARB texgen mode with specific normal vectors to +// sample specific regions of the cube map. Check for correctness. +// 3. Test GL_REFLECTION_MAP_ARB texgen mode by specifying a quad with +// fixed vertices and normals but rotating the texture coordinate +// matrix to select each side of the cube map. Check that the rendered +// quad's four colors match the cube face. +// + +#include "ttexcube.h" +#include <stdio.h> +#include <cmath> + +namespace GLEAN { + + +#define VP_SIZE 20 + +static const char *faceName[6] = { + "POSITIVE_X", + "NEGATIVE_X", + "POSITIVE_Y", + "NEGATIVE_Y", + "POSITIVE_Z", + "NEGATIVE_Z" +}; + + +// +// Test if two colors are close enough to be considered the same +// +bool +TexCubeTest::TestColor(const GLfloat c1[3], const GLfloat c2[3]) { + if (fabs(c1[0] - c2[0]) <= mTolerance[0] && + fabs(c1[1] - c2[1]) <= mTolerance[1] && + fabs(c1[2] - c2[2]) <= mTolerance[2]) + return true; + else + return false; +} + + +// +// Define a 2x2 checkerboard texture image using the given four colors. +// +void +TexCubeTest::BuildTexImage(GLenum target, const GLfloat color[4][3]) { + const GLint w = 8, h = 8; + GLfloat texImage[8][8][4]; + for (int i = 0; i < h; i++) { + const int ibit = (i >= (h / 2)); + for (int j = 0; j < w; j++) { + const int jbit = (j >= (w / 2)); + const int c = ibit * 2 + jbit; + texImage[i][j][0] = color[c][0]; + texImage[i][j][1] = color[c][1]; + texImage[i][j][2] = color[c][2]; + texImage[i][j][3] = 1.0; + } + } + glTexImage2D(target, 0, GL_RGB, w, h, 0, GL_RGBA, GL_FLOAT, texImage); +} + + +// +// Draw a polygon either with texcoords or normal vectors and check that +// we hit the correct quadrant of each of the six cube faces. +// Return: true = pass, false = fail +// +bool +TexCubeTest::TestNormalMap(bool texCoordMode, const char *modeName) { + + // We use the coordinates both directly as texture coordinates + // and as normal vectors for testing NORMAL_MAP_ARB texgen mode). + static const GLfloat coords[6][4][3] = { + // +X + { + { 1.0, 0.5, 0.5 }, + { 1.0, 0.5, -0.5 }, + { 1.0, -0.5, 0.5 }, + { 1.0, -0.5, -0.5 }, + }, + // -X + { + { -1.0, 0.5, -0.5 }, + { -1.0, 0.5, 0.5 }, + { -1.0, -0.5, -0.5 }, + { -1.0, -0.5, 0.5 }, + }, + // +Y + { + { -0.5, 1.0, -0.5 }, + { 0.5, 1.0, -0.5 }, + { -0.5, 1.0, 0.5 }, + { 0.5, 1.0, 0.5 }, + }, + // -Y + { + { -0.5, -1.0, 0.5 }, + { 0.5, -1.0, 0.5 }, + { -0.5, -1.0, -0.5 }, + { 0.5, -1.0, -0.5 }, + }, + // +Z + { + { -0.5, 0.5, 1.0 }, + { 0.5, 0.5, 1.0 }, + { -0.5, -0.5, 1.0 }, + { 0.5, -0.5, 1.0 }, + }, + // -Z + { + { 0.5, 0.5, -1.0 }, + { -0.5, 0.5, -1.0 }, + { 0.5, -0.5, -1.0 }, + { -0.5, -0.5, -1.0 }, + } + }; + + // normal vectors to hit the four colors of each cube face when + + for (int face = 0; face < 6; face++) { + for (int quadrant = 0; quadrant < 4; quadrant++) { + + // draw the test quad + if (texCoordMode) + glTexCoord3fv(coords[face][quadrant]); + else + glNormal3fv(coords[face][quadrant]); + glColor3f(0, 1, 0); + glBegin(GL_POLYGON); + glVertex2f(-1, -1); + glVertex2f( 1, -1); + glVertex2f( 1, 1); + glVertex2f(-1, 1); + glEnd(); + + // check the color + GLfloat result[3]; + glReadPixels(1, 1, 1, 1, GL_RGB, GL_FLOAT, result); + + if (!TestColor(mColors[face][quadrant], result)) { + env->log << name + << ": FAIL: mode='" + << modeName + << "' face=" + << faceName[face] + << " quadrant=" + << quadrant + << " expected=(" + << mColors[face][quadrant][0] << ", " + << mColors[face][quadrant][1] << ", " + << mColors[face][quadrant][2] + << ") measured=(" + << result[0] << ", " + << result[1] << ", " + << result[2] + << ")\n"; + return false; + } + } + } + return true; +} + + +// +// Test GL_REFLECTION_MAP_ARB texgen mode. +// Return: true = pass, false = fail +// +bool +TexCubeTest::TestReflectionMap(const char *modeName) { + +// These are the glReadPixels coords we'll use for pixel testing +#define X0 ((int) (VP_SIZE * 0.25)) +#define X1 ((int) (VP_SIZE * 0.75)) +#define Y0 ((int) (VP_SIZE * 0.25)) +#define Y1 ((int) (VP_SIZE * 0.75)) + + // We'll rotate the texture coordinates to map each cube face + // onto a screen-aligned quad. + static const GLfloat rotation[6][4] = { + { -90, 0, 1, 0 }, // +X + { 90, 0, 1, 0 }, // -X + { 90, 1, 0, 0 }, // +Y + { -90, 1, 0, 0 }, // -Y + { 180, 1, 0, 0 }, // -Z + { 0, 1, 0, 0 } // +Z + }; + + // For each face we'll test the four quadrants to be sure test + // if the expected color is where it should be. + // These are the glReadPixels coordinates at which we should + // find the colors in the mColors[6][4] array. + static const GLint readPos[6][4][2] = { + // +X + { + { X1, Y1 }, { X0, Y1 }, { X1, Y0 }, { X0, Y0 } + }, + // -X + { + { X1, Y1 }, { X0, Y1 }, { X1, Y0 }, { X0, Y0 } + }, + // +Y + { + { X0, Y0 }, { X1, Y0 }, { X0, Y1 }, { X1, Y1 } + }, + // -Y + { + { X0, Y0 }, { X1, Y0 }, { X0, Y1 }, { X1, Y1 } + }, + // +Z + { + { X0, Y0 }, { X1, Y0 }, { X0, Y1 }, { X1, Y1 } + }, + // -Z + { + { X1, Y1 }, { X0, Y1 }, { X1, Y0 }, { X0, Y0 } + } + }; + + for (int face = 0; face < 6; face++) { + + // Draw the test quad. + // It'll be textured with one face of the cube map texture. + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glRotatef(rotation[face][0], rotation[face][1], + rotation[face][2], rotation[face][3]); + glNormal3f(0, 0, 1); + glColor3f(0, 1, 0); + glBegin(GL_POLYGON); + glVertex3f(-1, -1, 1); + glVertex3f( 1, -1, 1); + glVertex3f( 1, 1, 1); + glVertex3f(-1, 1, 1); + glEnd(); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + // Verify the colors + for (int quadrant = 0; quadrant < 4; quadrant++) { + + GLfloat result[3]; + glReadPixels(readPos[face][quadrant][0], + readPos[face][quadrant][1], + 1, 1, GL_RGB, GL_FLOAT, result); + + if (!TestColor(mColors[face][quadrant], result)) { + env->log << name + << ": FAIL: mode='" + << modeName + << "' face=" + << faceName[face] + << " quadrant=" + << quadrant + << " expected=(" + << mColors[face][quadrant][0] << ", " + << mColors[face][quadrant][1] << ", " + << mColors[face][quadrant][2] + << ") measured=(" + << result[0] << ", " + << result[1] << ", " + << result[2] + << ")\n"; + return false; + } + } + } + return true; +} + + +void +TexCubeTest::runOne(BasicResult& r, Window& w) { + + (void) w; + + // each of six faces needs four test colors + for (int i = 0; i < 6 * 4; i++) { + GLint r = i % 3; + GLint g = (i / 3) % 3; + GLint b = (i / 9) % 3; + mColors[i / 4][i % 4][0] = r * 0.5; + mColors[i / 4][i % 4][1] = g * 0.5; + mColors[i / 4][i % 4][2] = b * 0.5; + //printf("mColors[%d][%d] = %g %g %g\n", i/4, i%4, + // mColors[i/4][i%4][0], + // mColors[i/4][i%4][1], + // mColors[i/4][i%4][2]); + } + + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + BuildTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, mColors[0]); + BuildTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, mColors[1]); + BuildTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, mColors[2]); + BuildTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, mColors[3]); + BuildTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, mColors[4]); + BuildTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, mColors[5]); + + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_CUBE_MAP_ARB); + + // compute RGB error tolerance + { + GLint rBits, gBits, bBits; + GLint rTexBits, gTexBits, bTexBits; + glGetIntegerv(GL_RED_BITS, &rBits); + glGetIntegerv(GL_GREEN_BITS, &gBits); + glGetIntegerv(GL_BLUE_BITS, &bBits); + glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + 0, GL_TEXTURE_RED_SIZE, &rTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + 0, GL_TEXTURE_GREEN_SIZE, &gTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + 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] = 2.0 / (1 << rBits); + mTolerance[1] = 2.0 / (1 << gBits); + mTolerance[2] = 2.0 / (1 << bBits); + } + + glViewport(0, 0, VP_SIZE, VP_SIZE); + + bool passed = true; + + if (passed) { + // Test directly specifying texture coords + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + glDisable(GL_TEXTURE_GEN_R); + passed = TestNormalMap(true, + "Direct specification of texture coordinates"); + } + + if (passed) { + // Test GL_NORMAL_MAP_ARB mode + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + passed = TestNormalMap(false, "GL_NORMAL_MAP_ARB texgen"); + } + + if (passed) { + // Test GL_REFLECTION_MAP_ARB mode + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); + glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + glEnable(GL_TEXTURE_GEN_R); + glEnable(GL_NORMALIZE); + passed = TestReflectionMap("GL_REFLECTION_MAP_ARB texgen"); + } + + r.pass = passed; +} // TexCubeTest::runOne + + +void +TexCubeTest::logOne(BasicResult& r) { + logPassFail(r); + logConcise(r); +} // TexCubeTest::logOne + + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexCubeTest texCubeTest("texCube", "window, rgb", + + "GL_ARB_texture_cube_map verification test.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/ttexcube.h b/tests/glean/ttexcube.h new file mode 100644 index 00000000..8ca15e3e --- /dev/null +++ b/tests/glean/ttexcube.h @@ -0,0 +1,65 @@ +// 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 + + +// ttexcube.h: Test the GL_ARB_texture_cube_map extension +// Author: Brian Paul (brianp@valinux.com) March 2001 + + +#ifndef __ttexcube_h__ +#define __ttexcube_h__ + +#include "tbasic.h" + +namespace GLEAN { + +class TexCubeTest: public BasicTest { + public: + TexCubeTest(const char* testName, const char* filter, + const char* description): + BasicTest(testName, filter, "GL_ARB_texture_cube_map", + description) { + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + bool TestColor(const GLfloat c1[3], const GLfloat c2[3]); + void BuildTexImage(GLenum target, const GLfloat color[4][3]); + bool TestNormalMap(bool testTexCoords, const char *modeName); + bool TestReflectionMap(const char *modeName); + + GLfloat mColors[6][4][3]; + GLfloat mTolerance[3]; + +}; // class TexCubeTest + +} // namespace GLEAN + +#endif // __ttexcube_h__ diff --git a/tests/glean/ttexenv.cpp b/tests/glean/ttexenv.cpp new file mode 100644 index 00000000..223f53cf --- /dev/null +++ b/tests/glean/ttexenv.cpp @@ -0,0 +1,667 @@ +// 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 + + +// ttexenv.cpp: Test the basic texture env modes +// Author: Brian Paul (brianp@valinux.com) April 2001 +// +// Test procedure: +// Setup a texture with 81 columns of unique RGBA colors, 3 texels each. +// Draw a 81 uniquely-colored flat-shaded quads as wide horizontal bands, +// with the above texture. This makes a matrix of 81*81 colored squares +// for which we test that the current texture environment mode and texture +// format produced the correct color. +// Finally, we blend over a gray background in order to verify that the +// post-texture alpha value is correct. +// + +#include "ttexenv.h" +#include <cassert> +#include <stdio.h> +#include <cmath> + +namespace GLEAN { + + +// If this is true, we enable blending over a gray background in order +// to test the alpha results of the texture env. If this is false, +// we don't blend. It might be useful to disable blending in order to +// diagnose failures +#define BLEND_WITH_BACKGROUND 1 + +static GLfloat BgColor[4] = { 0.5, 0.5, 0.5, 0.5 }; + + +static const GLenum FormatEnums[] = { + GL_ALPHA, + GL_LUMINANCE, + GL_LUMINANCE_ALPHA, + GL_INTENSITY, + GL_RGB, + GL_RGBA +}; + +static const char *FormatNames[] = { + "GL_ALPHA", + "GL_LUMINANCE", + "GL_LUMINANCE_ALPHA", + "GL_INTENSITY", + "GL_RGB", + "GL_RGBA" +}; + +static const GLenum EnvModeEnums[] = { + GL_REPLACE, + GL_MODULATE, + GL_DECAL, + GL_BLEND, + GL_ADD +}; + +static const char *EnvModeNames[] = { + "GL_REPLACE", + "GL_MODULATE", + "GL_DECAL", + "GL_BLEND", + "GL_ADD" +}; + + +// +// Test if two colors are close enough to be considered the same +// +bool +TexEnvTest::TestColor(const GLfloat c1[3], const GLfloat c2[3]) { + if (fabs(c1[0] - c2[0]) <= mTolerance[0] && + fabs(c1[1] - c2[1]) <= mTolerance[1] && + fabs(c1[2] - c2[2]) <= mTolerance[2]) + return true; + else + return false; +} + +// +// Compute expected texenv result given the texture env mode, the texture +// base format, texture color, fragment color, and texture env color. +// This also blends the result with the background color if that option +// is enabled (see above). +// +void +TexEnvTest::ComputeExpectedColor(GLenum envMode, GLenum texFormat, + const GLfloat texColor[4], const GLfloat fragColor[4], + const GLfloat envColor[4], GLfloat result[4]) { + + switch (envMode) { + case GL_REPLACE: + switch (texFormat) { + case GL_ALPHA: + result[0] = fragColor[0]; + result[1] = fragColor[1]; + result[2] = fragColor[2]; + result[3] = texColor[3]; // alpha + break; + case GL_LUMINANCE: + result[0] = texColor[0]; // lum + result[1] = texColor[0]; + result[2] = texColor[0]; + result[3] = fragColor[3]; + break; + case GL_LUMINANCE_ALPHA: + result[0] = texColor[0]; // lum + result[1] = texColor[0]; + result[2] = texColor[0]; + result[3] = texColor[3]; // alpha + break; + case GL_INTENSITY: + result[0] = texColor[0]; // intensity + result[1] = texColor[0]; + result[2] = texColor[0]; + result[3] = texColor[0]; + break; + case GL_RGB: + result[0] = texColor[0]; // r + result[1] = texColor[1]; // g + result[2] = texColor[2]; // b + result[3] = fragColor[3]; + break; + case GL_RGBA: + result[0] = texColor[0]; // r + result[1] = texColor[1]; // g + result[2] = texColor[2]; // b + result[3] = texColor[3]; // a + break; + default: + abort(); // implementation error + } + break; + case GL_MODULATE: + switch (texFormat) { + case GL_ALPHA: + result[0] = fragColor[0]; + result[1] = fragColor[1]; + result[2] = fragColor[2]; + result[3] = fragColor[3] * texColor[3]; + break; + case GL_LUMINANCE: + result[0] = fragColor[0] * texColor[0]; + result[1] = fragColor[1] * texColor[0]; + result[2] = fragColor[2] * texColor[0]; + result[3] = fragColor[3]; + break; + case GL_LUMINANCE_ALPHA: + result[0] = fragColor[0] * texColor[0]; + result[1] = fragColor[1] * texColor[0]; + result[2] = fragColor[2] * texColor[0]; + result[3] = fragColor[3] * texColor[3]; + break; + case GL_INTENSITY: + result[0] = fragColor[0] * texColor[0]; + result[1] = fragColor[1] * texColor[0]; + result[2] = fragColor[2] * texColor[0]; + result[3] = fragColor[3] * texColor[0]; + break; + case GL_RGB: + result[0] = fragColor[0] * texColor[0]; + result[1] = fragColor[1] * texColor[1]; + result[2] = fragColor[2] * texColor[2]; + result[3] = fragColor[3]; + break; + case GL_RGBA: + result[0] = fragColor[0] * texColor[0]; + result[1] = fragColor[1] * texColor[1]; + result[2] = fragColor[2] * texColor[2]; + result[3] = fragColor[3] * texColor[3]; + break; + default: + abort(); // implementation error + } + break; + case GL_DECAL: + switch (texFormat) { + case GL_ALPHA: + result[0] = 0; // undefined + result[1] = 0; + result[2] = 0; + result[3] = 0; + break; + case GL_LUMINANCE: + result[0] = 0; // undefined + result[1] = 0; + result[2] = 0; + result[3] = 0; + break; + case GL_LUMINANCE_ALPHA: + result[0] = 0; // undefined + result[1] = 0; + result[2] = 0; + result[3] = 0; + break; + case GL_INTENSITY: + result[0] = 0; // undefined + result[1] = 0; + result[2] = 0; + result[3] = 0; + break; + case GL_RGB: + result[0] = texColor[0]; + result[1] = texColor[1]; + result[2] = texColor[2]; + result[3] = fragColor[3]; + break; + case GL_RGBA: { + const GLfloat a = texColor[3]; + const GLfloat oma = 1.0 - a; + result[0] = fragColor[0] * oma + texColor[0] * a; + result[1] = fragColor[1] * oma + texColor[1] * a; + result[2] = fragColor[2] * oma + texColor[2] * a; + result[3] = fragColor[3]; + } break; + default: + abort(); // implementation error + } + break; + case GL_BLEND: + switch (texFormat) { + case GL_ALPHA: + result[0] = fragColor[0]; + result[1] = fragColor[1]; + result[2] = fragColor[2]; + result[3] = fragColor[3] * texColor[3]; + break; + case GL_LUMINANCE: { + const GLfloat l = texColor[0]; + const GLfloat oml = 1.0 - l; + result[0] = fragColor[0] * oml + envColor[0] * l; + result[1] = fragColor[1] * oml + envColor[1] * l; + result[2] = fragColor[2] * oml + envColor[2] * l; + result[3] = fragColor[3]; + } break; + case GL_LUMINANCE_ALPHA: { + const GLfloat l = texColor[0]; + const GLfloat oml = 1.0 - l; + result[0] = fragColor[0] * oml + envColor[0] * l; + result[1] = fragColor[1] * oml + envColor[1] * l; + result[2] = fragColor[2] * oml + envColor[2] * l; + result[3] = fragColor[3] * texColor[3]; + } break; + case GL_INTENSITY: { + const GLfloat i = texColor[0]; + const GLfloat omi = 1.0 - i; + result[0] = fragColor[0] * omi + envColor[0] * i; + result[1] = fragColor[1] * omi + envColor[1] * i; + result[2] = fragColor[2] * omi + envColor[2] * i; + result[3] = fragColor[3] * omi + envColor[3] * i; + } break; + case GL_RGB: { + const GLfloat r = texColor[0]; + const GLfloat omr = 1.0 - r; + const GLfloat g = texColor[1]; + const GLfloat omg = 1.0 - g; + const GLfloat b = texColor[2]; + const GLfloat omb = 1.0 - b; + result[0] = fragColor[0] * omr + envColor[0] * r; + result[1] = fragColor[1] * omg + envColor[1] * g; + result[2] = fragColor[2] * omb + envColor[2] * b; + result[3] = fragColor[3]; + } break; + case GL_RGBA: { + const GLfloat r = texColor[0]; + const GLfloat omr = 1.0 - r; + const GLfloat g = texColor[1]; + const GLfloat omg = 1.0 - g; + const GLfloat b = texColor[2]; + const GLfloat omb = 1.0 - b; + result[0] = fragColor[0] * omr + envColor[0] * r; + result[1] = fragColor[1] * omg + envColor[1] * g; + result[2] = fragColor[2] * omb + envColor[2] * b; + result[3] = fragColor[3] * texColor[3]; + } break; + default: + abort(); // implementation error + } + break; + case GL_ADD: + switch (texFormat) { + case GL_ALPHA: + result[0] = fragColor[0]; + result[1] = fragColor[1]; + result[2] = fragColor[2]; + result[3] = fragColor[3] * texColor[3]; + break; + case GL_LUMINANCE: + result[0] = fragColor[0] + texColor[0]; + result[1] = fragColor[1] + texColor[0]; + result[2] = fragColor[2] + texColor[0]; + result[3] = fragColor[3]; + break; + case GL_LUMINANCE_ALPHA: + result[0] = fragColor[0] + texColor[0]; + result[1] = fragColor[1] + texColor[0]; + result[2] = fragColor[2] + texColor[0]; + result[3] = fragColor[3] * texColor[3]; + break; + case GL_INTENSITY: + result[0] = fragColor[0] + texColor[0]; + result[1] = fragColor[1] + texColor[0]; + result[2] = fragColor[2] + texColor[0]; + result[3] = fragColor[3] + texColor[0]; + break; + case GL_RGB: + result[0] = fragColor[0] + texColor[0]; + result[1] = fragColor[1] + texColor[1]; + result[2] = fragColor[2] + texColor[2]; + result[3] = fragColor[3]; + break; + case GL_RGBA: + result[0] = fragColor[0] + texColor[0]; + result[1] = fragColor[1] + texColor[1]; + result[2] = fragColor[2] + texColor[2]; + result[3] = fragColor[3] * texColor[3]; + break; + default: + abort(); // implementation error + } + // clamping + if (result[0] > 1.0) result[0] = 1.0; + if (result[1] > 1.0) result[1] = 1.0; + if (result[2] > 1.0) result[2] = 1.0; + if (result[3] > 1.0) result[3] = 1.0; + break; + default: + // implementation error + abort(); + } + +#if BLEND_WITH_BACKGROUND + // now blend result over a gray background + const GLfloat alpha = result[3]; + const GLfloat omAlpha = 1.0 - alpha; + result[0] = result[0] * alpha + BgColor[0] * omAlpha; + result[1] = result[1] * alpha + BgColor[1] * omAlpha; + result[2] = result[2] * alpha + BgColor[2] * omAlpha; + result[3] = result[3] * alpha + BgColor[3] * omAlpha; +#endif +} + + +// Make a texture in which the colors vary along the length +// according to the colors[] array. For example, we use +// 243 columns of the texture to store 81 colors, 3 texels each. +void +TexEnvTest::MakeTexImage(GLenum baseFormat, int numColors, + const GLfloat colors[][4]) { + + const int width = 256; + const int height = 4; + GLfloat img[width * height][4]; + + assert(numColors == 81); // for now + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + int c = j / 3; + if (c >= numColors) { + img[i * width + j][0] = 0.0; + img[i * width + j][1] = 0.0; + img[i * width + j][2] = 0.0; + img[i * width + j][3] = 0.0; + } + else { + img[i * width + j][0] = colors[c][0]; + img[i * width + j][1] = colors[c][1]; + img[i * width + j][2] = colors[c][2]; + img[i * width + j][3] = colors[c][3]; + } + } + } + glTexImage2D(GL_TEXTURE_2D, 0, baseFormat, width, height, 0, + GL_RGBA, GL_FLOAT, (void *) img); + + // Recompute color tolerance now because it depends on the + // texel resolution in the new texture. + { + // Get fb resolution + GLint rBits, gBits, bBits; + glGetIntegerv(GL_RED_BITS, &rBits); + glGetIntegerv(GL_GREEN_BITS, &gBits); + glGetIntegerv(GL_BLUE_BITS, &bBits); + // Get tex resolution + GLint rTexBits, gTexBits, bTexBits, aTexBits; + GLint iTexBits, lTexBits; + 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); + glGetTexLevelParameteriv(GL_TEXTURE_2D, + 0, GL_TEXTURE_ALPHA_SIZE, &aTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_2D, + 0, GL_TEXTURE_INTENSITY_SIZE, &iTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_2D, + 0, GL_TEXTURE_LUMINANCE_SIZE, &lTexBits); + // Special cases + if (baseFormat == GL_INTENSITY) { + rTexBits = gTexBits = bTexBits = iTexBits; + } + if (baseFormat == GL_ALPHA) { + rTexBits = gTexBits = bTexBits = aTexBits; + } + else if (baseFormat == GL_LUMINANCE || + baseFormat == GL_LUMINANCE_ALPHA) { + rTexBits = gTexBits = bTexBits = lTexBits; + } + // Find smaller of frame buffer and texture bits + rBits = (rBits < rTexBits) ? rBits : rTexBits; + gBits = (gBits < gTexBits) ? gBits : gTexBits; + bBits = (bBits < bTexBits) ? bBits : bTexBits; + // If these fail, something's seriously wrong. + assert(rBits > 0); + assert(gBits > 0); + assert(bBits > 0); + mTolerance[0] = 3.0 / (1 << rBits); + mTolerance[1] = 3.0 / (1 << gBits); + mTolerance[2] = 3.0 / (1 << bBits); + //printf("tol: %g %g %g\n", mTolerance[0], + // mTolerance[1], mTolerance[2]); + } + + +} + + +// Do numColors * numColors tests in one batch. +// Setup a texture in which the colors vary by column. +// Draw a quadstrip in which we draw horizontal bands of colors. +// Drawing the textured quadstrips will fill the window with +// numColors * numColors test squares. +// Verify that they're all correct. +// Return: true = pass, false = fail +bool +TexEnvTest::MatrixTest(GLenum envMode, GLenum texFormat, + const char *envName, const char *formatName, + int numColors, const GLfloat colors[][4], + const GLfloat envColor[4], Window &w) { + + if (envMode == GL_DECAL && (texFormat != GL_RGB && + texFormat != GL_RGBA)) { + // undefined mode + return true; + } + + glClear(GL_COLOR_BUFFER_BIT); + + // The texture colors are the columns + MakeTexImage(texFormat, numColors, colors); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, envMode); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, envColor); + + // The fragment colors are the rows + GLfloat W = numColors * 3; + GLfloat S = (float) (numColors*3) / (float) 256; + glBegin(GL_QUAD_STRIP); + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(S, 0); glVertex2f(W, 0); + for (int i = 0; i < numColors; i++) { + glColor4fv(colors[i]); + GLfloat y = i * 3 + 3; + GLfloat t = y / (numColors * 3); + glTexCoord2f(0, t); glVertex2f(0, y); + glTexCoord2f(S, t); glVertex2f(W, y); + } + glEnd(); + + GLfloat image[256][256][4]; + glReadPixels(0, 0, 256, 256, GL_RGBA, GL_FLOAT, image); + + w.swap(); // lets us watch the progress + + // Check results + for (int row = 0; row < numColors; row++) { + for (int col = 0; col < numColors; col++) { + + // compute expected + GLfloat expected[4]; + ComputeExpectedColor(envMode, texFormat, + colors[col], colors[row], + envColor, expected); + + // fetch actual pixel + int x = col * 3 + 1; + int y = row * 3 + 1; + const GLfloat *actual = image[y][x]; + + // compare + if (!TestColor(expected, actual)) { + // Report the error + env->log << name + << ": FAIL: GL_TEXTURE_ENV_MODE=" + << envName + << " Texture Format=" + << formatName + << " Fragment Color=(" + << colors[row][0] << ", " + << colors[row][1] << ", " + << colors[row][2] << ", " + << colors[row][3] << ") " + << " Texture Color=(" + << colors[col][0] << ", " + << colors[col][1] << ", " + << colors[col][2] << ", " + << colors[col][3] << ") " + << " Tex Env Color=(" + << envColor[0] << ", " + << envColor[1] << ", " + << envColor[2] << ", " + << envColor[3] << ") " +#if BLEND_WITH_BACKGROUND + << " Blend over=(" + << BgColor[0] << ", " + << BgColor[1] << ", " + << BgColor[2] << ", " + << BgColor[3] << ") " +#endif + << " Expected=(" + << expected[0] << ", " + << expected[1] << ", " + << expected[2] << ", " + << expected[3] << ") " + << " Measured=(" + << actual[0] << ", " + << actual[1] << ", " + << actual[2] << ", " + << actual[3] << ")\n"; + return false; + } + } + } + return true; +} + + +void +TexEnvTest::runOne(BasicResult& r, Window& w) { + + (void) w; + +#define COLORS (3*3*3*3) + + GLfloat colors[COLORS][4]; + + // colors[] is an array of all possible RGBA colors with component + // values of 0, 0.5, and 1.0 + for (int i = 0; i < COLORS; i++) { + GLint r = i % 3; + GLint g = (i / 3) % 3; + GLint b = (i / 9) % 3; + GLint a = (i / 27) % 3; + colors[i][0] = (float) r / 2.0; + colors[i][1] = (float) g / 2.0; + colors[i][2] = (float) b / 2.0; + colors[i][3] = (float) a / 2.0; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glEnable(GL_TEXTURE_2D); + +#if BLEND_WITH_BACKGROUND + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); +#endif + + glClearColor(BgColor[0], BgColor[1], BgColor[2], BgColor[3]); + glShadeModel(GL_FLAT); + + glViewport(0, 0, 256, 256); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 256, 0, 256, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0.375, 0.375, 0.0); + + int numModes; + if (GLUtils::haveExtensions("GL_EXT_texture_env_add") || + GLUtils::haveExtensions("GL_ARB_texture_env_add")) + numModes = 5; + else + numModes = 4; + + r.pass = true; + + for (int fmt = 0; fmt < 6; fmt++) { + const GLenum format = FormatEnums[fmt]; + const char *formatName = FormatNames[fmt]; + for (int mode = 0; mode < numModes; mode++) { + const GLenum envMode = EnvModeEnums[mode]; + const char *envName = EnvModeNames[mode]; + //printf("format %s mode %s\n", FormatNames[fmt], + // EnvModeNames[mode]); + if (envMode == GL_BLEND && format != GL_ALPHA) { + // also vary texenv color, every 5th is OK. + for (int eCol = 0; eCol < COLORS; eCol += 5) { + const GLfloat *envColor = colors[eCol]; + if (!MatrixTest(envMode, format, + envName, formatName, + COLORS, colors, envColor, w)) { + r.pass = false; + break; + } + } + } + else { + // texenv color not significant + if (!MatrixTest(envMode, format, + envName, formatName, + COLORS, colors, colors[0], w)) { + r.pass = false; + } + } + } + } +} // TexEnvTest::runOne + + +void +TexEnvTest::logOne(BasicResult& r) { + logPassFail(r); + logConcise(r); +} // TexEnvTest::logOne + + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexEnvTest texEnvTest("texEnv", "window, rgb", + + "Test basic texture env modes for all base texture formats.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/ttexenv.h b/tests/glean/ttexenv.h new file mode 100644 index 00000000..6cf1f1e9 --- /dev/null +++ b/tests/glean/ttexenv.h @@ -0,0 +1,68 @@ +// 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 + + +// ttexenv.h: Test the basic texture env modes +// Author: Brian Paul (brianp@valinux.com) April 2001 + + +#ifndef __ttexenv_h__ +#define __ttexenv_h__ + +#include "tbasic.h" + +namespace GLEAN { + +class TexEnvTest: public BasicTest { + public: + TexEnvTest(const char* testName, const char* filter, + const char* description): + BasicTest(testName, filter, description) { + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + bool TestColor(const GLfloat c1[3], const GLfloat c2[3]); + void ComputeExpectedColor(GLenum envMode, GLenum texFormat, + const GLfloat texColor[4], const GLfloat fragColor[4], + const GLfloat envColor[4], GLfloat result[4]); + void MakeTexImage(GLenum baseFormat, int numColors, + const GLfloat colors[][4]); + bool MatrixTest(GLenum envMode, GLenum texFormat, + const char *envName, const char *formatName, + int numColors, const GLfloat colors[][4], + const GLfloat envColor[4], Window &w); + GLfloat mTolerance[3]; + +}; // class TexEnvTest + +} // namespace GLEAN + +#endif // __ttexenv_h__ diff --git a/tests/glean/ttexgen.cpp b/tests/glean/ttexgen.cpp new file mode 100644 index 00000000..55dd7fae --- /dev/null +++ b/tests/glean/ttexgen.cpp @@ -0,0 +1,371 @@ +// 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 + + +// ttexgen.cpp: Basic test of GL texture coordinate generation. +// This test does a basic test of the glTexGen functions, including +// object_linear, eye_linear, and sphere_map. We use the Sphere3D with +// a GeomRenderer to draw a sphere, and map a check texture onto it. We +// use an ortho projection to keep it simple. The result should be a 1:1 +// mapping of the check texture for all three modes (sphere map maps 1:1 +// because mapping it onto a sphere inverts the spheremap math). +// +// Note that accuracy issues might cause this test to fail if the +// texcoords near the center are a little warped; I've specifically tried +// to keep the matrices as "pure" as possible (no rotations) to +// keep the numerical precision high. So far it seems to work fine. +// Introducing a rotation by 90 degrees about the x axis resulted, +// on one driver, in a warping at the center of the sphere which caused +// the test to fail. +// +// For the second test of the three, we offset the texture by 0.5, +// so that each test's rendering is visually distinct from the +// previous. +// +// To test for pass/fail we examine the color buffer for red and blue, +// (the check colors) in the appropriate places. +// +// Author: Brian Sharp (brian@maniacal.org) December 2000 + +#include "ttexgen.h" +#include <stdio.h> +#include "geomutil.h" + + +const GLuint viewSize=50; + + +namespace GLEAN { + +void +TexgenTest::FailMessage(BasicResult &r, const std::string& texgenMode, + GeomRenderer::DrawMethod method, bool arraysCompiled, + int retainedMode, + const std::string& colorMismatch) const { + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n'; + env->log << "\t" << "during mode " << texgenMode << ", "; + switch(method) + { + case GeomRenderer::GLVERTEX_MODE: env->log << "glVertex-style rendering, "; break; + case GeomRenderer::GLARRAYELEMENT_MODE: env->log << "glArrayElement-style rendering, "; break; + case GeomRenderer::GLDRAWELEMENTS_MODE: env->log << "glDrawElements-style rendering, "; break; + case GeomRenderer::GLDRAWARRAYS_MODE: env->log << "glDrawArrays-style rendering, "; break; + } + if (arraysCompiled) env->log << "arrays locked, "; + else env->log << "arrays not locked, "; + + if (retainedMode) env->log << "built into a display list, "; + else env->log << "called immediately (not display listed), "; + + env->log << colorMismatch << std::endl; +} + +bool +TexgenTest::compareColors(GLfloat* color0, GLfloat* color1, std::string& failureInfo) const { + + // Compare the colors; fail and report why if they don't match. + if (color0[0] != color1[0] || color0[1] != color1[1] || color0[2] != color1[2]) + { + // Assemble the error message into a C-string, then hand it back in the string. + char failureOut[1024]; + sprintf(failureOut, "expected [%f,%f,%f], read back [%f,%f,%f]", + color0[0], color0[1], color0[2], + color1[0], color1[1], color1[2]); + + failureInfo = std::string(failureOut); + return false; + } + + return true; +} + +bool +TexgenTest::verifyCheckers(GLfloat* pixels, GLfloat* upperLeftColor, GLfloat* upperRightColor, std::string& failureInfo) const { + + // My loop control variable, since gcc and MSVC do things differently. + GLint samp; + + // It's a viewSize x viewSize pixel block; since we drew a sphere that doesn't quite touch the + // edges, we need to be careful not to sample from what should be background. + // These pairs are hand-picked coordinates on the image that fall on the bottom-left quadrant + // of the sphere. + // XXX FIX ME: these sample coordinates assume that viewSize == 50. + GLuint samples[6][2] = {{13,13}, {4,22}, {22,4}, {20,20}, {20,10}, {10,20}}; + + // Run through those sample points in the bottom-left corner and make sure they're all the right color. + for (samp=0; samp<6; samp++) + { + GLuint sampleOffset = (samples[samp][0] + (viewSize*samples[samp][1]))*3; + if (!compareColors(upperRightColor, pixels + sampleOffset, failureInfo)) + { + return false; + } + } + + // Run through those sample points in the bottom-right corner and make sure they're all the right color. + // Note the "viewSize - samples[samp][0]" to flip it to the bottom-right quadrant. + for (samp=0; samp<6; samp++) + { + GLuint sampleOffset = ((viewSize - samples[samp][0]) + (viewSize*samples[samp][1]))*3; + if (!compareColors(upperLeftColor, pixels + sampleOffset, failureInfo)) + { + return false; + } + } + + // Run through those sample points in the upper-right corner and make sure they're all the right color. + for (samp=0; samp<6; samp++) + { + GLuint sampleOffset = ((viewSize - samples[samp][0]) + (viewSize*(viewSize - samples[samp][1])))*3; + if (!compareColors(upperRightColor, pixels + sampleOffset, failureInfo)) + { + return false; + } + } + + // Run through those sample points in the upper-left corner and make sure they're all the right color. + for (samp=0; samp<6; samp++) + { + GLuint sampleOffset = (samples[samp][0] + (viewSize*(viewSize - samples[samp][1])))*3; + if (!compareColors(upperLeftColor, pixels + sampleOffset, failureInfo)) + { + return false; + } + } + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TexgenTest::runOne(BasicResult& r, Window&) { + + // Temporary buffer to store pixels we've read back for verification. + GLfloat pixels[50*50*3]; + + // Colors for matching against when we readback pixels. + GLfloat matchBlue[3] = {0,0,1}; + GLfloat matchRed[3] = {1,0,0}; + + // A sphere to draw. + Sphere3D theSphere(9.9, 32, 16); + + // A GeomRenderer to draw it with. + GeomRenderer sphereRenderer; + sphereRenderer.setDrawMethod(GeomRenderer::GLVERTEX_MODE); + sphereRenderer.setParameterBits(GeomRenderer::NORMAL_BIT); + sphereRenderer.setVArrayIndices(theSphere.getNumIndices(),GL_UNSIGNED_INT,theSphere.getIndices()); + sphereRenderer.setVertexPointer(theSphere.getNumVertices(), 3, GL_FLOAT, 0, theSphere.getVertices()); + sphereRenderer.setNormalPointer(GL_FLOAT, 0, theSphere.getNormals()); + + // draw the sphere in a 50x50 pixel window for some precision. + glViewport(0, 0, 50, 50); + + // Basic GL setup. + glDisable(GL_DITHER); + glEnable(GL_CULL_FACE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glColor3f(1,1,1); + + // Setup the projection. + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-10,10,-10,10,-10,10); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Set up our texture. + glEnable(GL_TEXTURE_2D); + GLuint checkerTextureHandle; + glGenTextures(1, &checkerTextureHandle); + glBindTexture(GL_TEXTURE_2D, checkerTextureHandle); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glEnable(GL_TEXTURE_GEN_S); + glEnable(GL_TEXTURE_GEN_T); + + // Make a little checker texture. + unsigned char redBlueCheck[256*256*3]; + for (int x=0; x<256; x++) + { + for (int y=0; y<256; y++) + { + bool xPastHalf = x >= 128; + bool yPastHalf = y >= 128; + + redBlueCheck[(x+(256*y))*3 + 0] = ((xPastHalf && yPastHalf) || (!xPastHalf && !yPastHalf)) ? 255 : 0; + redBlueCheck[(x+(256*y))*3 + 1] = 0; + redBlueCheck[(x+(256*y))*3 + 2] = ((xPastHalf && !yPastHalf) || (!xPastHalf && yPastHalf)) ? 255 : 0; + } + } + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, 256, 256, GL_RGB, GL_UNSIGNED_BYTE, redBlueCheck); + + // Setup our arrays of configuration info; we loop over the rendering pass a number of times, + // using a different GL primitive path each time. + GeomRenderer::DrawMethod drawMethods[] = {GeomRenderer::GLVERTEX_MODE, GeomRenderer::GLARRAYELEMENT_MODE, + GeomRenderer::GLDRAWELEMENTS_MODE, GeomRenderer::GLARRAYELEMENT_MODE, + GeomRenderer::GLDRAWELEMENTS_MODE}; + + bool arraysCompiled[] = {false, false, false, true, true}; + + // Iterate once for all immediate mode styles, then once for retained mode styles. + for (int retainedMode=0; retainedMode<2; retainedMode++) + { + for (int testIteration=0; testIteration<5; testIteration++) + { + sphereRenderer.setDrawMethod(drawMethods[testIteration]); + if (!sphereRenderer.setArraysCompiled(arraysCompiled[testIteration])) + { + // We don't have the extension... not sure what we should do. + // May as well just keep going, it's no big deal (it should still + // yield correct results, of course, it's just redundant). + } + + // GL_SPHERE_MAP: with spheremap, the UL corner is blue + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + renderSphere(retainedMode, sphereRenderer); + glReadPixels(0,0,50,50, GL_RGB, GL_FLOAT, pixels); + + // Validate it. + std::string sphereMapResult; + if (!verifyCheckers(pixels, matchBlue, matchRed, sphereMapResult)) + { + FailMessage(r, std::string("GL_SPHERE_MAP"), drawMethods[testIteration], + arraysCompiled[testIteration], retainedMode, sphereMapResult); + r.pass = false; + glDeleteTextures(1, &checkerTextureHandle); + return; + } + + // GL_OBJECT_LINEAR: with object linear and the below planes, the UL corner is red. + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); + float sObjPlane[4] = {0,0.05,0,1.5}; // We flip the checker by setting W to 1.5 (phases by half a period) + float tObjPlane[4] = {0.05,0,0,1}; + glTexGenfv(GL_S, GL_OBJECT_PLANE, sObjPlane); + glTexGenfv(GL_T, GL_OBJECT_PLANE, tObjPlane); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + renderSphere(retainedMode, sphereRenderer); + glReadPixels(0,0,50,50, GL_RGB, GL_FLOAT, pixels); + + // Validate it. + std::string objectLinearResult; + if (!verifyCheckers(pixels, matchRed, matchBlue, objectLinearResult)) + { + FailMessage(r, std::string("GL_OBJECT_LINEAR"), drawMethods[testIteration], + arraysCompiled[testIteration], retainedMode, objectLinearResult); + r.pass = false; + glDeleteTextures(1, &checkerTextureHandle); + return; + } + + // GL_EYE_LINEAR: with eye linear and the below planes, the UL corner is blue. + glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); + float sEyePlane[4] = {0,0.05,0,1}; + float tEyePlane[4] = {0.05,0,0,1}; + glTexGenfv(GL_S, GL_EYE_PLANE, sEyePlane); + glTexGenfv(GL_T, GL_EYE_PLANE, tEyePlane); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + renderSphere(retainedMode, sphereRenderer); + glReadPixels(0,0,50,50, GL_RGB, GL_FLOAT, pixels); + + // Validate it. + std::string eyeLinearResult; + if (!verifyCheckers(pixels, matchBlue, matchRed, eyeLinearResult)) + { + FailMessage(r, std::string("GL_EYE_LINEAR"), drawMethods[testIteration], + arraysCompiled[testIteration], retainedMode, eyeLinearResult); + r.pass = false; + glDeleteTextures(1, &checkerTextureHandle); + return; + } + } + } + + // success + r.pass = true; +} // TexgenTest::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +TexgenTest::logOne(BasicResult& r) { + if (r.pass) { + logPassFail(r); + logConcise(r); + } +} // TexgenTest::logOne + +void +TexgenTest::renderSphere(int retainedMode, GeomRenderer& sphereRenderer) +{ + if (retainedMode) + { + GLint displayList; + assert(sphereRenderer.generateDisplayList(GL_TRIANGLES, displayList)); + glCallList(displayList); + glDeleteLists(displayList, 1); + } + else + { + assert(sphereRenderer.renderPrimitives(GL_TRIANGLES)); + } +} // TexgenTest::renderSphere + + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexgenTest texgenTest("texgen", "window, rgb", + + "This test verifies that the three basic OpenGL texture coordinate\n" + "modes: object_linear, eye_linear, and sphere_map, work for a simple\n" + "case.\n"); + + +} // namespace GLEAN + + + + + diff --git a/tests/glean/ttexgen.h b/tests/glean/ttexgen.h new file mode 100644 index 00000000..2a214b03 --- /dev/null +++ b/tests/glean/ttexgen.h @@ -0,0 +1,67 @@ +// 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 + + +// ttexgen.h: Basic test of GL texture coordinate generation. +// Author: Brian Sharp (brian@maniacal.org) December 2000 + + +#ifndef __ttexgen_h__ +#define __ttexgen_h__ + +#include "tbasic.h" +#include "geomrend.h" + +namespace GLEAN { + +class TexgenTest: public BasicTest { +public: + TexgenTest(const char* testName, const char* filter, + const char* description): + BasicTest(testName, filter, description) { + } + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + +private: + void FailMessage(BasicResult &r, const std::string& texgenMode, + GeomRenderer::DrawMethod, + bool arraysCompiled, int retainedMode, + const std::string& colorMismatch) const; + void renderSphere(int retainedMode, GeomRenderer& sphereRenderer); + bool compareColors(GLfloat* color0, GLfloat* color1, + std::string& failureInfo) const; + bool verifyCheckers(GLfloat* pixels, GLfloat* upperLeftColor, + GLfloat* upperRightColor, + std::string& failureInfo) const; + +}; // class TexgenTest + +} // namespace GLEAN + +#endif // __ttexgen_h__ diff --git a/tests/glean/ttexrect.cpp b/tests/glean/ttexrect.cpp new file mode 100644 index 00000000..7adcb3a0 --- /dev/null +++ b/tests/glean/ttexrect.cpp @@ -0,0 +1,217 @@ +// 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: + * Eric Anholt <eric@anholt.net> + * + */ + +/* ttexrect.cpp: Test the ARB_texture_rectangle extension + * Author: Eric Anholt <eric@anholt.net> + * + * Test procedure: + * Create a 255x127 texture of varying colors and bind it as a + * GL_ARB_texture_recangle target. Draw that rectangle to the window, and + * check that the texture was drawn correctly. The common failure to be + * caught with this test is not adjusting the non-normalized coordinates on + * hardware that expects normalized coordinates. + */ + +#include "ttexrect.h" +#include <cassert> +#include <stdio.h> +#include <cmath> + +namespace GLEAN { + +/** + * Test if two colors are close enough to be considered the same. + */ +bool +TexRectTest::TestColor(const GLfloat c1[3], const GLfloat c2[3]) +{ + if (fabs(c1[0] - c2[0]) <= mTolerance[0] && + fabs(c1[1] - c2[1]) <= mTolerance[1] && + fabs(c1[2] - c2[2]) <= mTolerance[2]) + return true; + else + return false; +} + +void +TexRectTest::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); + + // Get tex resolution + glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_ARB, + 0, GL_TEXTURE_RED_SIZE, &rTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_ARB, + 0, GL_TEXTURE_GREEN_SIZE, &gTexBits); + glGetTexLevelParameteriv(GL_TEXTURE_RECTANGLE_ARB, + 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; + + // If these fail, something's seriously wrong. + assert(rBits > 0); + assert(gBits > 0); + assert(bBits > 0); + mTolerance[0] = 3.0 / (1 << rBits); + mTolerance[1] = 3.0 / (1 << gBits); + mTolerance[2] = 3.0 / (1 << bBits); +} + +#define TEXTURE_WIDTH 255 +#define TEXTURE_HEIGHT 127 + +/** + * Creates a TEXTURE_WIDTH * TEXTURE_HEIGHT rectangular texture and draws it to + * the window. It then reads the output back to verify that the texture stayed + * intact. + */ +void +TexRectTest::runOne(BasicResult& r, Window& w) +{ + float image[TEXTURE_WIDTH * TEXTURE_HEIGHT * 3]; + float actual[TEXTURE_WIDTH * TEXTURE_HEIGHT * 3]; + (void) w; + + /* Set up a texture that is color ramps with red to black top to + * bottom and green to black left to right. + */ + for (int y = 0; y < TEXTURE_HEIGHT; y++) { + for (int x = 0; x < TEXTURE_WIDTH; x++) { + int i = (y * TEXTURE_WIDTH + x) * 3; + + image[i + 0] = (float)x / (TEXTURE_WIDTH - 1); + image[i + 1] = 1.0 - ((float) y / (TEXTURE_HEIGHT - 1)); + image[i + 2] = 0.0; + } + } + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + glShadeModel(GL_FLAT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 256, 0, 256, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, windowSize, windowSize); + + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, 255, 127, 0, + GL_RGB, GL_FLOAT, image); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + 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); + } + + r.pass = true; + + /* Draw our texture to the window such that each texel should map + * to the corresponding pixel of the window. + */ + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); + glVertex2f(0, 0); + + glTexCoord2f(TEXTURE_WIDTH, 0); + glVertex2f(TEXTURE_WIDTH, 0); + + glTexCoord2f(TEXTURE_WIDTH, TEXTURE_HEIGHT); + glVertex2f(TEXTURE_WIDTH, TEXTURE_HEIGHT); + + glTexCoord2f(0, TEXTURE_HEIGHT); + glVertex2f(0, TEXTURE_HEIGHT); + glEnd(); + + /* Read back the output */ + glReadPixels(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT, + GL_RGB, GL_FLOAT, actual); + + w.swap(); // lets us watch the progress + + CalculateTolerance(); + + /* Verify the output */ + for (int y = 0; y < TEXTURE_HEIGHT; y++) { + for (int x = 0; x < TEXTURE_WIDTH; x++) { + int i = (y * TEXTURE_WIDTH + x) * 3; + + if (!TestColor(&image[i], &actual[i])) { + // Report the error + env->log << name + << ": FAIL at (" << x << "," << y + << "):\n" + << " Expected=(" + << image[i + 0] << ", " + << image[i + 1] << ", " + << image[i + 2] << ")\n" + << " Measured=(" + << actual[i + 0] << ", " + << actual[i + 1] << ", " + << actual[i + 2] << ")\n"; + r.pass = false; + } + } + } +} // TexRectTest::runOne + + +void +TexRectTest::logOne(BasicResult& r) { + logPassFail(r); + logConcise(r); +} // TexRectTest::logOne + + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +TexRectTest texRectTest("texRect", "window, rgb", + "GL_ARB_texture_rectangle", + "Test basic texture rectangle functionality.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/ttexrect.h b/tests/glean/ttexrect.h new file mode 100644 index 00000000..5e8675bc --- /dev/null +++ b/tests/glean/ttexrect.h @@ -0,0 +1,61 @@ +// 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: + * Eric Anholt <eric@anholt.net> + * + */ + + +// ttexenv.h: Test basic ARB_texture_rectangle support. +// Author: Eric Anholt <eric@anholt.net> + +#ifndef __ttexrect_h__ +#define __ttexrect_h__ + +#include "tbasic.h" + +#define windowSize 256 +namespace GLEAN { + +class TexRectTest: public BasicTest { + public: + TexRectTest(const char* testName, const char* filter, + const char *prereqs, const char* description): + BasicTest(testName, filter, prereqs, description) { + } + + virtual void runOne(BasicResult& r, Window& w); + virtual void logOne(BasicResult& r); + + private: + bool TestColor(const GLfloat c1[3], const GLfloat c2[3]); + void CalculateTolerance(); + GLfloat mTolerance[3]; + +}; // class TexRectTest + +} // namespace GLEAN + +#endif // __ttexrect_h__ diff --git a/tests/glean/ttexture_srgb.cpp b/tests/glean/ttexture_srgb.cpp new file mode 100644 index 00000000..809a56a6 --- /dev/null +++ b/tests/glean/ttexture_srgb.cpp @@ -0,0 +1,373 @@ +// 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 + +// ttexture_srgb.h: Test GL_EXT_texture_sRGB extension. +// Brian Paul August 2006 + + +#include "ttexture_srgb.h" +#include "rand.h" +#include <cassert> +#include <cmath> + +#ifdef GL_EXT_texture_sRGB + +namespace GLEAN { + + +static const struct { + GLenum sFormat; + GLenum baseFormat; + GLint components; +} Formats[] = { + { GL_SRGB_EXT, GL_RGB, 3 }, + { GL_SRGB8_EXT, GL_RGB, 3 }, + { GL_SRGB_ALPHA_EXT, GL_RGBA, 4 }, + { GL_SRGB8_ALPHA8_EXT, GL_RGBA, 4 }, + { GL_SLUMINANCE_ALPHA_EXT, GL_LUMINANCE_ALPHA, 2 }, + { GL_SLUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, 2 }, + { GL_SLUMINANCE_EXT, GL_LUMINANCE, 1 }, + { GL_SLUMINANCE8_EXT, GL_LUMINANCE, 1 }, + { 0, 0, 0 } +}; + + + + +// Convert an 8-bit sRGB value from non-linear space to a +// linear RGB value in [0, 1]. +// Implemented with a 256-entry lookup table. +static float +nonlinear_to_linear(GLubyte cs8) +{ + static GLfloat table[256]; + static GLboolean tableReady = GL_FALSE; + if (!tableReady) { + // compute lookup table now + GLuint i; + for (i = 0; i < 256; i++) { + const GLfloat cs = i / 255.0; + if (cs <= 0.04045) { + table[i] = cs / 12.92; + } + else { + table[i] = pow((cs + 0.055) / 1.055, 2.4); + } + } + tableReady = GL_TRUE; + } + return table[cs8]; +} + + +// allocate and fill an array with random values +static GLubyte * +randomArray(int bytes, int seed) +{ + GLEAN::RandomBits r(8, seed); + GLubyte *img = new GLubyte [bytes]; + + for (int i = 0; i < bytes; i++) + img[i] = r.next(); + + return img; +} + + +// Test glTexImage and glGetTexImage functionality +bool +TextureSRGBTest::testImageTransfer(void) +{ + const GLubyte *image = randomArray(128 * 128 * 4, 0); + GLubyte image2[128 * 128 * 4]; + int i, j; + + for (i = 0; Formats[i].sFormat; i++) { + // upload tex image + glTexImage2D(GL_TEXTURE_2D, 0, Formats[i].sFormat, 128, 128, 0, + Formats[i].baseFormat, GL_UNSIGNED_BYTE, image); + + // retrieve tex image + glGetTexImage(GL_TEXTURE_2D, 0, + Formats[i].baseFormat, GL_UNSIGNED_BYTE, image2); + + // compare original and returned images + const int comps = Formats[i].components; + for (j = 0; j < 128 * 128 * comps; j++) { + if (image[j] != image2[j]) { + env->log << '\n' + << name + << " glGetTexImage failed for internalFormat " + << Formats[i].sFormat + << "\n"; + env->log << "Expected value at [" + << j + << "] should be " + << image[j] + << " found " + << image2[j] + << "\n"; + delete [] image; + return false; + } + image2[j] = 0; // reset for next GetTexImage + } + } + + delete [] image; + return true; +} + + +bool +TextureSRGBTest::testTextureFormat(GLenum intFormat, GLint components, + GLEAN::Environment &env) +{ + const GLubyte *image = randomArray(128 * 128 * 4, intFormat); + GLfloat readback[128 * 128 * 4]; + int i; + GLint redBits, alphaBits; + + glGetIntegerv(GL_RED_BITS, &redBits); + glGetIntegerv(GL_ALPHA_BITS, &alphaBits); + const float tolerance = 1.0 / ((1 << redBits) - 1); + + // setup matrices + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, windowSize, windowSize); + + // setup texture + glTexImage2D(GL_TEXTURE_2D, 0, intFormat, 128, 128, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnable(GL_TEXTURE_2D); + + glDrawBuffer(GL_FRONT); + glReadBuffer(GL_FRONT); + + // draw test polygon + glBegin(GL_POLYGON); + glTexCoord2f(0, 0); glVertex2f(-1, -1); + glTexCoord2f(1, 0); glVertex2f( 1, -1); + glTexCoord2f(1, 1); glVertex2f( 1, 1); + glTexCoord2f(0, 1); glVertex2f(-1, 1); + glEnd(); + + glReadPixels(0, 0, windowSize, windowSize, + GL_RGBA, GL_FLOAT, readback); + + // compare rendered results to expected values + for (i = 0; i < 128 * 128; i++) { + const GLfloat *actual = readback + i * 4; + GLfloat expected[4]; + + expected[0] = nonlinear_to_linear(image[i * 4 + 0]); + expected[1] = nonlinear_to_linear(image[i * 4 + 1]); + expected[2] = nonlinear_to_linear(image[i * 4 + 2]); + expected[3] = image[i * 4 + 3] / 255.0; + + if (components <= 2) { + if (fabs(actual[0] - expected[0]) > tolerance) { + env.log << '\n' + << name + << " failed for internalFormat " + << intFormat + << "\n"; + env.log << "Expected luminance " + << expected[0] + << " found " + << actual[0] + << "\n"; + delete [] image; + return GL_FALSE; + } + + } + else { + assert(components == 3 || components == 4); + if (fabs(actual[0] - expected[0]) > tolerance || + fabs(actual[1] - expected[1]) > tolerance || + fabs(actual[2] - expected[2]) > tolerance) { + env.log << '\n' + << name + << " failed for internalFormat " + << intFormat + << "\n"; + env.log << "Expected color " + << expected[0] + << ", " + << expected[1] + << ", " + << expected[2] + << " found " + << actual[0] + << ", " + << actual[1] + << ", " + << actual[2] + << "\n"; + delete [] image; + return GL_FALSE; + } + } + + if (alphaBits >= redBits + && components == 4 + && fabs(actual[3] - expected[3]) > tolerance) { + env.log << '\n' + << name + << " failed for internalFormat " + << intFormat + << "\n"; + env.log << "Expected alpha " + << expected[3] + << " found " + << actual[3] + << "\n"; + delete [] image; + return GL_FALSE; + } + } + + delete [] image; + return GL_TRUE; +} + + +// Test actual texture mapping using each of the sRGB formats +// Return GL_TRUE if all format tests pass, GL_FALSE if any fail. +bool +TextureSRGBTest::testTexturing(void) +{ + for (int i = 0; Formats[i].sFormat; i++) { + if (!testTextureFormat(Formats[i].sFormat, + Formats[i].components, *env)) + return GL_FALSE; + } + + return GL_TRUE; +} + + +void +TextureSRGBTest::runOne(TextureSRGBResult &r, Window &w) +{ + (void) w; // silence warning + r.pass = true; + errorCode = 0; + errorPos = NULL; + errorMsg[0] = 0; + + if (r.pass) + r.pass = testImageTransfer(); + if (r.pass) + r.pass = testTexturing(); +} + + +void +TextureSRGBTest::logOne(TextureSRGBResult &r) +{ + if (r.pass) { + logPassFail(r); + logConcise(r); + } + else { + env->log << name << " FAIL\n"; + if (errorCode) { + env->log << "\tOpenGL Error " << gluErrorString(errorCode) + << " at " << errorPos << "\n"; + } + else if (errorMsg[0]) { + env->log << "\t" << errorMsg << "\n"; + } + } +} + + +void +TextureSRGBTest::compareOne(TextureSRGBResult &oldR, + TextureSRGBResult &newR) +{ + comparePassFail(oldR, newR); + + if (newR.pass && oldR.pass == newR.pass) { + // XXX + } + else { + env->log << "\tNew: "; + env->log << (newR.pass ? "PASS" : "FAIL"); + env->log << "\tOld: "; + env->log << (oldR.pass ? "PASS" : "FAIL"); + } +} + + +void +TextureSRGBResult::putresults(ostream &s) const +{ + if (pass) { + s << "PASS\n"; + } + else { + s << "FAIL\n"; + } +} + + +bool +TextureSRGBResult::getresults(istream &s) +{ + char result[1000]; + s >> result; + + if (strcmp(result, "FAIL") == 0) { + pass = false; + } + else { + pass = true; + } + return s.good(); +} + + +// The test object itself: +TextureSRGBTest srgbTest("texture_srgb", "window, rgb", + "GL_EXT_texture_sRGB", + "Test the GL_EXT_texture_sRGB extension.\n"); + + + +} // namespace GLEAN + +#endif // GL_EXT_texture_sRGB diff --git a/tests/glean/ttexture_srgb.h b/tests/glean/ttexture_srgb.h new file mode 100644 index 00000000..1095565d --- /dev/null +++ b/tests/glean/ttexture_srgb.h @@ -0,0 +1,73 @@ +// 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 + +// ttexture_srgb.h: Test GL_EXT_texture_sRGB extension. +// Brian Paul August 2006 + +#ifndef __ttexture_srgb_h__ +#define __ttexture_srgb_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define windowSize 128 + +class TextureSRGBResult: public BaseResult +{ +public: + bool pass; + + virtual void putresults(ostream& s) const; + virtual bool getresults(istream& s); +}; + + +class TextureSRGBTest: public BaseTest<TextureSRGBResult> +{ +public: + GLEAN_CLASS_WH(TextureSRGBTest, TextureSRGBResult, + windowSize, windowSize); + +private: + GLenum errorCode; + const char *errorPos; + char errorMsg[1000]; + + bool testImageTransfer(void); + bool testTextureFormat(GLenum intFormat, GLint components, + GLEAN::Environment &env); + bool testTexturing(void); + + void testPerformance(TextureSRGBResult &r); +}; + +} // namespace GLEAN + +#endif // __ttexture_srgb_h__ + diff --git a/tests/glean/tvertattrib.cpp b/tests/glean/tvertattrib.cpp new file mode 100644 index 00000000..fd7e12cb --- /dev/null +++ b/tests/glean/tvertattrib.cpp @@ -0,0 +1,1620 @@ +// 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 + + +// tvertattrib.cpp: Test vertex attribute functions. +// +// Indexed vertex attributes may either alias with conventional attributes +// or name a separate set of generic attributes. The following extensions/ +// versions are tested (and whether aliasing is allowed): +// GL_NV_vertex_program (aliasing required) +// GL_ARB_vertex_program (aliasing optional) +// GL_ARB_vertex_shader (aliasing disallowed) +// OpenGL 2.0 (aliasing disallowed) +// +// If either GL_ARB_vertex_shader or OpenGL 2.0 is supported, that means +// aliasing is required for GL_ARB_vertex_program too. +// +// We test both immediate mode and display list mode. +// +// Author: Brian Paul (brian.paul a t tungstengraphics.com) October 2004 + + +#include <math.h> +#include "tvertattrib.h" +#include "glutils.h" +#include <cassert> + +namespace GLEAN { + +#define COPY1(DST, SRC) DST[0] = SRC[0]; DST[1] = 0.0F; DST[2] = 0.0F; DST[3] = 1.0F + +#define COPY2(DST, SRC) DST[0] = SRC[0]; DST[1] = SRC[1]; DST[2] = 0.0F; DST[3] = 1.0F + +#define COPY3(DST, SRC) DST[0] = SRC[0]; DST[1] = SRC[1]; DST[2] = SRC[2]; DST[3] = 1.0F + +#define COPY4(DST, SRC) DST[0] = SRC[0]; DST[1] = SRC[1]; DST[2] = SRC[2]; DST[3] = SRC[3] + +#define FLOAT_TO_BYTE(X) ( (((GLint) (255.0F * (X))) - 1) / 2 ) + +#define FLOAT_TO_UBYTE(X) ((GLubyte) (GLint) ((X) * 255.0F)) + +#define FLOAT_TO_SHORT(X) ( (((GLint) (65535.0F * (X))) - 1) / 2 ) + +#define FLOAT_TO_USHORT(X) ((GLushort) (GLint) ((X) * 65535.0F)) + +#define FLOAT_TO_INT(X) ( (GLint) (2147483647.0 * (X)) ) + +#define FLOAT_TO_UINT(X) ((GLuint) ((X) * 4294967295.0)) + + +#define NUM_NV_ATTRIB_FUNCS 26 +#define NUM_ARB_ATTRIB_FUNCS 36 +#define NUM_2_0_ATTRIB_FUNCS 36 + +static const char * +AttribFuncNames[NUM_NV_ATTRIB_FUNCS + NUM_ARB_ATTRIB_FUNCS + NUM_2_0_ATTRIB_FUNCS] = { + "glVertexAttrib1fNV", + "glVertexAttrib2fNV", + "glVertexAttrib3fNV", + "glVertexAttrib4fNV", + "glVertexAttrib1fvNV", + "glVertexAttrib2fvNV", + "glVertexAttrib3fvNV", + "glVertexAttrib4fvNV", + "glVertexAttrib1dNV", + "glVertexAttrib2dNV", + "glVertexAttrib3dNV", + "glVertexAttrib4dNV", + "glVertexAttrib1dvNV", + "glVertexAttrib2dvNV", + "glVertexAttrib3dvNV", + "glVertexAttrib4dvNV", + "glVertexAttrib1sNV", + "glVertexAttrib2sNV", + "glVertexAttrib3sNV", + "glVertexAttrib4sNV", + "glVertexAttrib1svNV", + "glVertexAttrib2svNV", + "glVertexAttrib3svNV", + "glVertexAttrib4svNV", + "glVertexAttrib4ubNV", + "glVertexAttrib4ubvNV", + + "glVertexAttrib1fARB", + "glVertexAttrib2fARB", + "glVertexAttrib3fARB", + "glVertexAttrib4fARB", + "glVertexAttrib1fvARB", + "glVertexAttrib2fvARB", + "glVertexAttrib3fvARB", + "glVertexAttrib4fvARB", + "glVertexAttrib1dARB", + "glVertexAttrib2dARB", + "glVertexAttrib3dARB", + "glVertexAttrib4dARB", + "glVertexAttrib1dvARB", + "glVertexAttrib2dvARB", + "glVertexAttrib3dvARB", + "glVertexAttrib4dvARB", + "glVertexAttrib1sARB", + "glVertexAttrib2sARB", + "glVertexAttrib3sARB", + "glVertexAttrib4sARB", + "glVertexAttrib1svARB", + "glVertexAttrib2svARB", + "glVertexAttrib3svARB", + "glVertexAttrib4svARB", + "glVertexAttrib4NsvARB", + "glVertexAttrib4NubARB", + "glVertexAttrib4NubvARB", + "glVertexAttrib4ubvARB", + "glVertexAttrib4NbvARB", + "glVertexAttrib4bvARB", + "glVertexAttrib4NivARB", + "glVertexAttrib4ivARB", + "glVertexAttrib4NuivARB", + "glVertexAttrib4uivARB", + "glVertexAttrib4NusvARB", + "glVertexAttrib4usvARB", + + "glVertexAttrib1f", + "glVertexAttrib2f", + "glVertexAttrib3f", + "glVertexAttrib4f", + "glVertexAttrib1fv", + "glVertexAttrib2fv", + "glVertexAttrib3fv", + "glVertexAttrib4fv", + "glVertexAttrib1d", + "glVertexAttrib2d", + "glVertexAttrib3d", + "glVertexAttrib4d", + "glVertexAttrib1dv", + "glVertexAttrib2dv", + "glVertexAttrib3dv", + "glVertexAttrib4dv", + "glVertexAttrib1s", + "glVertexAttrib2s", + "glVertexAttrib3s", + "glVertexAttrib4s", + "glVertexAttrib1sv", + "glVertexAttrib2sv", + "glVertexAttrib3sv", + "glVertexAttrib4sv", + "glVertexAttrib4Nsv" + "glVertexAttrib4Nub", + "glVertexAttrib4Nubv", + "glVertexAttrib4ubv", + "glVertexAttrib4Nbv", + "glVertexAttrib4bv", + "glVertexAttrib4Niv", + "glVertexAttrib4iv", + "glVertexAttrib4Nuiv", + "glVertexAttrib4uiv", + "glVertexAttrib4Nusv", + "glVertexAttrib4usv" +}; + + +// Set a vertex attribute with one of the many glVertexAttrib* functions. +// index = the vertex attribute +// v = the 4-element attribute value +// funcIndex = indicates which glVertexAttrib* function to use +// refOut = returns the value which should now be in the attribute register +// +// Yeah, calling getProcAddress every time isn't very efficient. Oh well. +// +static void +SetAttrib(GLuint index, const GLfloat v[4], GLuint funcIndex, GLfloat refOut[4]) +{ + switch (funcIndex) { + // ** GLfloat-valued functions +#if defined(GL_NV_vertex_program) + case 0: + { + PFNGLVERTEXATTRIB1FNVPROC f = (PFNGLVERTEXATTRIB1FNVPROC) + GLUtils::getProcAddress("glVertexAttrib1fNV"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 1: + { + PFNGLVERTEXATTRIB2FNVPROC f = (PFNGLVERTEXATTRIB2FNVPROC) + GLUtils::getProcAddress("glVertexAttrib2fNV"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 2: + { + PFNGLVERTEXATTRIB3FNVPROC f = (PFNGLVERTEXATTRIB3FNVPROC) + GLUtils::getProcAddress("glVertexAttrib3fNV"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 3: + { + PFNGLVERTEXATTRIB4FNVPROC f = (PFNGLVERTEXATTRIB4FNVPROC) + GLUtils::getProcAddress("glVertexAttrib4fNV"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 4: + { + PFNGLVERTEXATTRIB1FVNVPROC f = (PFNGLVERTEXATTRIB1FVNVPROC) + GLUtils::getProcAddress("glVertexAttrib1fvNV"); + f(index, v); + COPY1(refOut, v); + } + break; + case 5: + { + PFNGLVERTEXATTRIB2FVNVPROC f = (PFNGLVERTEXATTRIB2FVNVPROC) + GLUtils::getProcAddress("glVertexAttrib2fvNV"); + f(index, v); + COPY2(refOut, v); + } + break; + case 6: + { + PFNGLVERTEXATTRIB3FVNVPROC f = (PFNGLVERTEXATTRIB3FVNVPROC) + GLUtils::getProcAddress("glVertexAttrib3fvNV"); + f(index, v); + COPY3(refOut, v); + } + break; + case 7: + { + PFNGLVERTEXATTRIB4FVNVPROC f = (PFNGLVERTEXATTRIB4FVNVPROC) + GLUtils::getProcAddress("glVertexAttrib4fvNV"); + f(index, v); + COPY4(refOut, v); + } + break; + // ** GLdouble-valued functions + case 8: + { + PFNGLVERTEXATTRIB1DNVPROC f = (PFNGLVERTEXATTRIB1DNVPROC) + GLUtils::getProcAddress("glVertexAttrib1dNV"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 9: + { + PFNGLVERTEXATTRIB2DNVPROC f = (PFNGLVERTEXATTRIB2DNVPROC) + GLUtils::getProcAddress("glVertexAttrib2dNV"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 10: + { + PFNGLVERTEXATTRIB3DNVPROC f = (PFNGLVERTEXATTRIB3DNVPROC) + GLUtils::getProcAddress("glVertexAttrib3dNV"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 11: + { + PFNGLVERTEXATTRIB4DNVPROC f = (PFNGLVERTEXATTRIB4DNVPROC) + GLUtils::getProcAddress("glVertexAttrib4dNV"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 12: + { + PFNGLVERTEXATTRIB1DVNVPROC f = (PFNGLVERTEXATTRIB1DVNVPROC) + GLUtils::getProcAddress("glVertexAttrib1dvNV"); + GLdouble d[1]; + d[0] = v[0]; + f(index, d); + COPY1(refOut, v); + } + break; + case 13: + { + PFNGLVERTEXATTRIB2DVNVPROC f = (PFNGLVERTEXATTRIB2DVNVPROC) + GLUtils::getProcAddress("glVertexAttrib2dvNV"); + GLdouble d[2]; + d[0] = v[0]; + d[1] = v[1]; + f(index, d); + COPY2(refOut, v); + } + break; + case 14: + { + PFNGLVERTEXATTRIB3DVNVPROC f = (PFNGLVERTEXATTRIB3DVNVPROC) + GLUtils::getProcAddress("glVertexAttrib3dvNV"); + GLdouble d[3]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + f(index, d); + COPY3(refOut, v); + } + break; + case 15: + { + PFNGLVERTEXATTRIB4DVNVPROC f = (PFNGLVERTEXATTRIB4DVNVPROC) + GLUtils::getProcAddress("glVertexAttrib4dvNV"); + GLdouble d[4]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + d[3] = v[3]; + f(index, d); + COPY4(refOut, v); + } + break; + // ** GLshort-valued functions + case 16: + { + PFNGLVERTEXATTRIB1SNVPROC f = (PFNGLVERTEXATTRIB1SNVPROC) + GLUtils::getProcAddress("glVertexAttrib1sNV"); + f(index, (GLshort) v[0]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 17: + { + PFNGLVERTEXATTRIB2SNVPROC f = (PFNGLVERTEXATTRIB2SNVPROC) + GLUtils::getProcAddress("glVertexAttrib2sNV"); + f(index, (GLshort) v[0], (GLshort) v[1]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 18: + { + PFNGLVERTEXATTRIB3SNVPROC f = (PFNGLVERTEXATTRIB3SNVPROC) + GLUtils::getProcAddress("glVertexAttrib3sNV"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 19: + { + PFNGLVERTEXATTRIB4SNVPROC f = (PFNGLVERTEXATTRIB4SNVPROC) + GLUtils::getProcAddress("glVertexAttrib4sNV"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2], (GLshort) v[3]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + case 20: + { + PFNGLVERTEXATTRIB1SVNVPROC f = (PFNGLVERTEXATTRIB1SVNVPROC) + GLUtils::getProcAddress("glVertexAttrib1svNV"); + GLshort s[1]; + s[0] = (GLshort) v[0]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 21: + { + PFNGLVERTEXATTRIB2SVNVPROC f = (PFNGLVERTEXATTRIB2SVNVPROC) + GLUtils::getProcAddress("glVertexAttrib2svNV"); + GLshort s[2]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 22: + { + PFNGLVERTEXATTRIB3SVNVPROC f = (PFNGLVERTEXATTRIB3SVNVPROC) + GLUtils::getProcAddress("glVertexAttrib3svNV"); + GLshort s[3]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 23: + { + PFNGLVERTEXATTRIB4SVNVPROC f = (PFNGLVERTEXATTRIB4SVNVPROC) + GLUtils::getProcAddress("glVertexAttrib4svNV"); + GLshort s[4]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + s[3] = (GLshort) v[3]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + // ** GLubyte-valued functions + case 24: + { + PFNGLVERTEXATTRIB4UBNVPROC f = (PFNGLVERTEXATTRIB4UBNVPROC) + GLUtils::getProcAddress("glVertexAttrib4ubNV"); + f(index, FLOAT_TO_UBYTE(v[0]), FLOAT_TO_UBYTE(v[1]), FLOAT_TO_UBYTE(v[2]), FLOAT_TO_UBYTE(v[3])); + refOut[0] = v[0]; + refOut[1] = v[1]; + refOut[2] = v[2]; + refOut[3] = v[3]; + } + break; + case 25: + { + PFNGLVERTEXATTRIB4UBVNVPROC f = (PFNGLVERTEXATTRIB4UBVNVPROC) + GLUtils::getProcAddress("glVertexAttrib4ubvNV"); + GLubyte ub[4]; + for (int i = 0; i < 4; i++ ) + ub[i] = FLOAT_TO_UBYTE(v[i]); + f(index, ub); + refOut[0] = v[0]; + refOut[1] = v[1]; + refOut[2] = v[2]; + refOut[3] = v[3]; + } + break; + /* XXX Also test glVertexAttribs* functions? */ +#endif + +#if defined(GL_ARB_vertex_program) || defined (GL_ARB_vertex_shader) + // ** GLfloat-valued functions + case 26: + { + PFNGLVERTEXATTRIB1FARBPROC f = (PFNGLVERTEXATTRIB1FARBPROC) + GLUtils::getProcAddress("glVertexAttrib1fARB"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 27: + { + PFNGLVERTEXATTRIB2FARBPROC f = (PFNGLVERTEXATTRIB2FARBPROC) + GLUtils::getProcAddress("glVertexAttrib2fARB"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 28: + { + PFNGLVERTEXATTRIB3FARBPROC f = (PFNGLVERTEXATTRIB3FARBPROC) + GLUtils::getProcAddress("glVertexAttrib3fARB"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 29: + { + PFNGLVERTEXATTRIB4FARBPROC f = (PFNGLVERTEXATTRIB4FARBPROC) + GLUtils::getProcAddress("glVertexAttrib4fARB"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 30: + { + PFNGLVERTEXATTRIB1FVARBPROC f = (PFNGLVERTEXATTRIB1FVARBPROC) + GLUtils::getProcAddress("glVertexAttrib1fvARB"); + f(index, v); + COPY1(refOut, v); + } + break; + case 31: + { + PFNGLVERTEXATTRIB2FVARBPROC f = (PFNGLVERTEXATTRIB2FVARBPROC) + GLUtils::getProcAddress("glVertexAttrib2fvARB"); + f(index, v); + COPY2(refOut, v); + } + break; + case 32: + { + PFNGLVERTEXATTRIB3FVARBPROC f = (PFNGLVERTEXATTRIB3FVARBPROC) + GLUtils::getProcAddress("glVertexAttrib3fvARB"); + f(index, v); + COPY3(refOut, v); + } + break; + case 33: + { + PFNGLVERTEXATTRIB4FVARBPROC f = (PFNGLVERTEXATTRIB4FVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4fvARB"); + f(index, v); + COPY4(refOut, v); + } + break; + // ** GLdouble-valued functions + case 34: + { + PFNGLVERTEXATTRIB1DARBPROC f = (PFNGLVERTEXATTRIB1DARBPROC) + GLUtils::getProcAddress("glVertexAttrib1dARB"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 35: + { + PFNGLVERTEXATTRIB2DARBPROC f = (PFNGLVERTEXATTRIB2DARBPROC) + GLUtils::getProcAddress("glVertexAttrib2dARB"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 36: + { + PFNGLVERTEXATTRIB3DARBPROC f = (PFNGLVERTEXATTRIB3DARBPROC) + GLUtils::getProcAddress("glVertexAttrib3dARB"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 37: + { + PFNGLVERTEXATTRIB4DARBPROC f = (PFNGLVERTEXATTRIB4DARBPROC) + GLUtils::getProcAddress("glVertexAttrib4dARB"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 38: + { + PFNGLVERTEXATTRIB1DVARBPROC f = (PFNGLVERTEXATTRIB1DVARBPROC) + GLUtils::getProcAddress("glVertexAttrib1dvARB"); + GLdouble d[1]; + d[0] = v[0]; + f(index, d); + COPY1(refOut, v); + } + break; + case 39: + { + PFNGLVERTEXATTRIB2DVARBPROC f = (PFNGLVERTEXATTRIB2DVARBPROC) + GLUtils::getProcAddress("glVertexAttrib2dvARB"); + GLdouble d[2]; + d[0] = v[0]; + d[1] = v[1]; + f(index, d); + COPY2(refOut, v); + } + break; + case 40: + { + PFNGLVERTEXATTRIB3DVARBPROC f = (PFNGLVERTEXATTRIB3DVARBPROC) + GLUtils::getProcAddress("glVertexAttrib3dvARB"); + GLdouble d[3]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + f(index, d); + COPY3(refOut, v); + } + break; + case 41: + { + PFNGLVERTEXATTRIB4DVARBPROC f = (PFNGLVERTEXATTRIB4DVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4dvARB"); + GLdouble d[4]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + d[3] = v[3]; + f(index, d); + COPY4(refOut, v); + } + break; + // ** GLshort-valued functions + case 42: + { + PFNGLVERTEXATTRIB1SARBPROC f = (PFNGLVERTEXATTRIB1SARBPROC) + GLUtils::getProcAddress("glVertexAttrib1sARB"); + f(index, (GLshort) v[0]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 43: + { + PFNGLVERTEXATTRIB2SARBPROC f = (PFNGLVERTEXATTRIB2SARBPROC) + GLUtils::getProcAddress("glVertexAttrib2sARB"); + f(index, (GLshort) v[0], (GLshort) v[1]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 44: + { + PFNGLVERTEXATTRIB3SARBPROC f = (PFNGLVERTEXATTRIB3SARBPROC) + GLUtils::getProcAddress("glVertexAttrib3sARB"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 45: + { + PFNGLVERTEXATTRIB4SARBPROC f = (PFNGLVERTEXATTRIB4SARBPROC) + GLUtils::getProcAddress("glVertexAttrib4sARB"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2], (GLshort) v[3]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + case 46: + { + PFNGLVERTEXATTRIB1SVARBPROC f = (PFNGLVERTEXATTRIB1SVARBPROC) + GLUtils::getProcAddress("glVertexAttrib1svARB"); + GLshort s[1]; + s[0] = (GLshort) v[0]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 47: + { + PFNGLVERTEXATTRIB2SVARBPROC f = (PFNGLVERTEXATTRIB2SVARBPROC) + GLUtils::getProcAddress("glVertexAttrib2svARB"); + GLshort s[2]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 48: + { + PFNGLVERTEXATTRIB3SVARBPROC f = (PFNGLVERTEXATTRIB3SVARBPROC) + GLUtils::getProcAddress("glVertexAttrib3svARB"); + GLshort s[3]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 49: + { + PFNGLVERTEXATTRIB4SVARBPROC f = (PFNGLVERTEXATTRIB4SVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4svARB"); + GLshort s[4]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + s[3] = (GLshort) v[3]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + case 50: + { + PFNGLVERTEXATTRIB4NSVARBPROC f = (PFNGLVERTEXATTRIB4NSVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NsvARB"); + GLshort s[4]; + for (int i = 0; i < 4; i++) + s[i] = FLOAT_TO_SHORT(v[i]); + f(index, s); + COPY4(refOut, v); + } + break; + // ** GLubyte-valued functions + case 51: + { + PFNGLVERTEXATTRIB4NUBARBPROC f = (PFNGLVERTEXATTRIB4NUBARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NubARB"); + f(index, FLOAT_TO_UBYTE(v[0]), FLOAT_TO_UBYTE(v[1]), FLOAT_TO_UBYTE(v[2]), FLOAT_TO_UBYTE(v[3])); + COPY4(refOut, v); + } + break; + case 52: + { + PFNGLVERTEXATTRIB4NUBVARBPROC f = (PFNGLVERTEXATTRIB4NUBVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NubvARB"); + GLubyte ub[4]; + for (int i = 0; i < 4; i++ ) + ub[i] = FLOAT_TO_UBYTE(v[i]); + f(index, ub); + COPY4(refOut, v); + } + break; + case 53: + { + PFNGLVERTEXATTRIB4UBVARBPROC f = (PFNGLVERTEXATTRIB4UBVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4ubvARB"); + GLubyte ub[4]; + for (int i = 0; i < 4; i++ ) + ub[i] = (GLubyte) v[i]; + f(index, ub); + refOut[0] = (GLfloat) (GLubyte) v[0]; + refOut[1] = (GLfloat) (GLubyte) v[1]; + refOut[2] = (GLfloat) (GLubyte) v[2]; + refOut[3] = (GLfloat) (GLubyte) v[3]; + } + break; + // ** GLbyte-valued functions + case 54: + { + PFNGLVERTEXATTRIB4NBVARBPROC f = (PFNGLVERTEXATTRIB4NBVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NbvARB"); + GLbyte b[4]; + for (int i = 0; i < 4; i++ ) + b[i] = FLOAT_TO_BYTE(v[i]); + f(index, b); + COPY4(refOut, v); + } + break; + case 55: + { + PFNGLVERTEXATTRIB4BVARBPROC f = (PFNGLVERTEXATTRIB4BVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4bvARB"); + GLbyte b[4]; + for (int i = 0; i < 4; i++ ) + b[i] = (GLbyte) v[i]; + f(index, b); + refOut[0] = (GLfloat) (GLbyte) v[0]; + refOut[1] = (GLfloat) (GLbyte) v[1]; + refOut[2] = (GLfloat) (GLbyte) v[2]; + refOut[3] = (GLfloat) (GLbyte) v[3]; + } + break; + // ** GLint-valued functions + case 56: + { + PFNGLVERTEXATTRIB4NIVARBPROC f = (PFNGLVERTEXATTRIB4NIVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NivARB"); + GLint iv[4]; + for (int i = 0; i < 4; i++ ) + iv[i] = FLOAT_TO_INT(v[i]); + f(index, iv); + COPY4(refOut, v); + } + break; + case 57: + { + PFNGLVERTEXATTRIB4IVARBPROC f = (PFNGLVERTEXATTRIB4IVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4ivARB"); + GLint iv[4]; + for (int i = 0; i < 4; i++ ) + iv[i] = (GLint) v[i]; + f(index, iv); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; + // ** GLuint-valued functions + case 58: + { + PFNGLVERTEXATTRIB4NUIVARBPROC f = (PFNGLVERTEXATTRIB4NUIVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NuivARB"); + GLuint ui[4]; + for (int i = 0; i < 4; i++ ) + ui[i] = FLOAT_TO_UINT(v[i]); + f(index, ui); + COPY4(refOut, v); + } + break; + case 59: + { + PFNGLVERTEXATTRIB4UIVARBPROC f = (PFNGLVERTEXATTRIB4UIVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4uivARB"); + GLuint ui[4]; + for (int i = 0; i < 4; i++ ) + ui[i] = (GLint) v[i]; + f(index, ui); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; + // ** GLushort-valued functions + case 60: + { + PFNGLVERTEXATTRIB4NUSVARBPROC f = (PFNGLVERTEXATTRIB4NUSVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4NusvARB"); + GLushort us[4]; + for (int i = 0; i < 4; i++ ) + us[i] = FLOAT_TO_USHORT(v[i]); + f(index, us); + COPY4(refOut, v); + } + break; + case 61: + { + PFNGLVERTEXATTRIB4USVARBPROC f = (PFNGLVERTEXATTRIB4USVARBPROC) + GLUtils::getProcAddress("glVertexAttrib4usvARB"); + GLushort us[4]; + for (int i = 0; i < 4; i++ ) + us[i] = (GLint) v[i]; + f(index, us); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; +#endif + +#if defined(GL_VERSION_2_0) + case 62: + { + PFNGLVERTEXATTRIB1FPROC f = (PFNGLVERTEXATTRIB1FPROC) + GLUtils::getProcAddress("glVertexAttrib1f"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 63: + { + PFNGLVERTEXATTRIB2FPROC f = (PFNGLVERTEXATTRIB2FPROC) + GLUtils::getProcAddress("glVertexAttrib2f"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 64: + { + PFNGLVERTEXATTRIB3FPROC f = (PFNGLVERTEXATTRIB3FPROC) + GLUtils::getProcAddress("glVertexAttrib3f"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 65: + { + PFNGLVERTEXATTRIB4FPROC f = (PFNGLVERTEXATTRIB4FPROC) + GLUtils::getProcAddress("glVertexAttrib4f"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 66: + { + PFNGLVERTEXATTRIB1FVPROC f = (PFNGLVERTEXATTRIB1FVPROC) + GLUtils::getProcAddress("glVertexAttrib1fv"); + f(index, v); + COPY1(refOut, v); + } + break; + case 67: + { + PFNGLVERTEXATTRIB2FVPROC f = (PFNGLVERTEXATTRIB2FVPROC) + GLUtils::getProcAddress("glVertexAttrib2fv"); + f(index, v); + COPY2(refOut, v); + } + break; + case 68: + { + PFNGLVERTEXATTRIB3FVPROC f = (PFNGLVERTEXATTRIB3FVPROC) + GLUtils::getProcAddress("glVertexAttrib3fv"); + f(index, v); + COPY3(refOut, v); + } + break; + case 69: + { + PFNGLVERTEXATTRIB4FVPROC f = (PFNGLVERTEXATTRIB4FVPROC) + GLUtils::getProcAddress("glVertexAttrib4fv"); + f(index, v); + COPY4(refOut, v); + } + break; + // ** GLdouble-valued functions + case 70: + { + PFNGLVERTEXATTRIB1DPROC f = (PFNGLVERTEXATTRIB1DPROC) + GLUtils::getProcAddress("glVertexAttrib1d"); + f(index, v[0]); + COPY1(refOut, v); + } + break; + case 71: + { + PFNGLVERTEXATTRIB2DPROC f = (PFNGLVERTEXATTRIB2DPROC) + GLUtils::getProcAddress("glVertexAttrib2d"); + f(index, v[0], v[1]); + COPY2(refOut, v); + } + break; + case 72: + { + PFNGLVERTEXATTRIB3DPROC f = (PFNGLVERTEXATTRIB3DPROC) + GLUtils::getProcAddress("glVertexAttrib3d"); + f(index, v[0], v[1], v[2]); + COPY3(refOut, v); + } + break; + case 73: + { + PFNGLVERTEXATTRIB4DPROC f = (PFNGLVERTEXATTRIB4DPROC) + GLUtils::getProcAddress("glVertexAttrib4d"); + f(index, v[0], v[1], v[2], v[3]); + COPY4(refOut, v); + } + break; + case 74: + { + PFNGLVERTEXATTRIB1DVPROC f = (PFNGLVERTEXATTRIB1DVPROC) + GLUtils::getProcAddress("glVertexAttrib1dv"); + GLdouble d[1]; + d[0] = v[0]; + f(index, d); + COPY1(refOut, v); + } + break; + case 75: + { + PFNGLVERTEXATTRIB2DVPROC f = (PFNGLVERTEXATTRIB2DVPROC) + GLUtils::getProcAddress("glVertexAttrib2dv"); + GLdouble d[2]; + d[0] = v[0]; + d[1] = v[1]; + f(index, d); + COPY2(refOut, v); + } + break; + case 76: + { + PFNGLVERTEXATTRIB3DVPROC f = (PFNGLVERTEXATTRIB3DVPROC) + GLUtils::getProcAddress("glVertexAttrib3dv"); + GLdouble d[3]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + f(index, d); + COPY3(refOut, v); + } + break; + case 77: + { + PFNGLVERTEXATTRIB4DVPROC f = (PFNGLVERTEXATTRIB4DVPROC) + GLUtils::getProcAddress("glVertexAttrib4dv"); + GLdouble d[4]; + d[0] = v[0]; + d[1] = v[1]; + d[2] = v[2]; + d[3] = v[3]; + f(index, d); + COPY4(refOut, v); + } + break; + // ** GLshort-valued functions + case 78: + { + PFNGLVERTEXATTRIB1SPROC f = (PFNGLVERTEXATTRIB1SPROC) + GLUtils::getProcAddress("glVertexAttrib1s"); + f(index, (GLshort) v[0]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 79: + { + PFNGLVERTEXATTRIB2SPROC f = (PFNGLVERTEXATTRIB2SPROC) + GLUtils::getProcAddress("glVertexAttrib2s"); + f(index, (GLshort) v[0], (GLshort) v[1]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 80: + { + PFNGLVERTEXATTRIB3SPROC f = (PFNGLVERTEXATTRIB3SPROC) + GLUtils::getProcAddress("glVertexAttrib3s"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 81: + { + PFNGLVERTEXATTRIB4SPROC f = (PFNGLVERTEXATTRIB4SPROC) + GLUtils::getProcAddress("glVertexAttrib4s"); + f(index, (GLshort) v[0], (GLshort) v[1], (GLshort) v[2], (GLshort) v[3]); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + case 82: + { + PFNGLVERTEXATTRIB1SVPROC f = (PFNGLVERTEXATTRIB1SVPROC) + GLUtils::getProcAddress("glVertexAttrib1sv"); + GLshort s[1]; + s[0] = (GLshort) v[0]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = 0.0F; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 83: + { + PFNGLVERTEXATTRIB2SVPROC f = (PFNGLVERTEXATTRIB2SVPROC) + GLUtils::getProcAddress("glVertexAttrib2sv"); + GLshort s[2]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = 0.0F; + refOut[3] = 1.0F; + } + break; + case 84: + { + PFNGLVERTEXATTRIB3SVPROC f = (PFNGLVERTEXATTRIB3SVPROC) + GLUtils::getProcAddress("glVertexAttrib3sv"); + GLshort s[3]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = 1.0F; + } + break; + case 85: + { + PFNGLVERTEXATTRIB4SVPROC f = (PFNGLVERTEXATTRIB4SVPROC) + GLUtils::getProcAddress("glVertexAttrib4sv"); + GLshort s[4]; + s[0] = (GLshort) v[0]; + s[1] = (GLshort) v[1]; + s[2] = (GLshort) v[2]; + s[3] = (GLshort) v[3]; + f(index, s); + refOut[0] = (GLfloat) (GLshort) v[0]; + refOut[1] = (GLfloat) (GLshort) v[1]; + refOut[2] = (GLfloat) (GLshort) v[2]; + refOut[3] = (GLfloat) (GLshort) v[3]; + } + break; + case 86: + { + PFNGLVERTEXATTRIB4NSVPROC f = (PFNGLVERTEXATTRIB4NSVPROC) + GLUtils::getProcAddress("glVertexAttrib4Nsv"); + GLshort s[4]; + for (int i = 0; i < 4; i++) + s[i] = FLOAT_TO_SHORT(v[i]); + f(index, s); + COPY4(refOut, v); + } + break; + // ** GLubyte-valued functions + case 87: + { + PFNGLVERTEXATTRIB4NUBPROC f = (PFNGLVERTEXATTRIB4NUBPROC) + GLUtils::getProcAddress("glVertexAttrib4Nub"); + f(index, FLOAT_TO_UBYTE(v[0]), FLOAT_TO_UBYTE(v[1]), FLOAT_TO_UBYTE(v[2]), FLOAT_TO_UBYTE(v[3])); + COPY4(refOut, v); + } + break; + case 88: + { + PFNGLVERTEXATTRIB4NUBVPROC f = (PFNGLVERTEXATTRIB4NUBVPROC) + GLUtils::getProcAddress("glVertexAttrib4Nubv"); + GLubyte ub[4]; + for (int i = 0; i < 4; i++ ) + ub[i] = FLOAT_TO_UBYTE(v[i]); + f(index, ub); + COPY4(refOut, v); + } + break; + case 89: + { + PFNGLVERTEXATTRIB4UBVPROC f = (PFNGLVERTEXATTRIB4UBVPROC) + GLUtils::getProcAddress("glVertexAttrib4ubv"); + GLubyte ub[4]; + for (int i = 0; i < 4; i++ ) + ub[i] = (GLubyte) v[i]; + f(index, ub); + refOut[0] = (GLfloat) (GLubyte) v[0]; + refOut[1] = (GLfloat) (GLubyte) v[1]; + refOut[2] = (GLfloat) (GLubyte) v[2]; + refOut[3] = (GLfloat) (GLubyte) v[3]; + } + break; + // ** GLbyte-valued functions + case 90: + { + PFNGLVERTEXATTRIB4NBVPROC f = (PFNGLVERTEXATTRIB4NBVPROC) + GLUtils::getProcAddress("glVertexAttrib4Nbv"); + GLbyte b[4]; + for (int i = 0; i < 4; i++ ) + b[i] = FLOAT_TO_BYTE(v[i]); + f(index, b); + COPY4(refOut, v); + } + break; + case 91: + { + PFNGLVERTEXATTRIB4BVPROC f = (PFNGLVERTEXATTRIB4BVPROC) + GLUtils::getProcAddress("glVertexAttrib4bv"); + GLbyte b[4]; + for (int i = 0; i < 4; i++ ) + b[i] = (GLbyte) v[i]; + f(index, b); + refOut[0] = (GLfloat) (GLbyte) v[0]; + refOut[1] = (GLfloat) (GLbyte) v[1]; + refOut[2] = (GLfloat) (GLbyte) v[2]; + refOut[3] = (GLfloat) (GLbyte) v[3]; + } + break; + // ** GLint-valued functions + case 92: + { + PFNGLVERTEXATTRIB4NIVPROC f = (PFNGLVERTEXATTRIB4NIVPROC) + GLUtils::getProcAddress("glVertexAttrib4Niv"); + GLint iv[4]; + for (int i = 0; i < 4; i++ ) + iv[i] = FLOAT_TO_INT(v[i]); + f(index, iv); + COPY4(refOut, v); + } + break; + case 93: + { + PFNGLVERTEXATTRIB4IVPROC f = (PFNGLVERTEXATTRIB4IVPROC) + GLUtils::getProcAddress("glVertexAttrib4iv"); + GLint iv[4]; + for (int i = 0; i < 4; i++ ) + iv[i] = (GLint) v[i]; + f(index, iv); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; + // ** GLuint-valued functions + case 94: + { + PFNGLVERTEXATTRIB4NUIVPROC f = (PFNGLVERTEXATTRIB4NUIVPROC) + GLUtils::getProcAddress("glVertexAttrib4Nuiv"); + GLuint ui[4]; + for (int i = 0; i < 4; i++ ) + ui[i] = FLOAT_TO_UINT(v[i]); + f(index, ui); + COPY4(refOut, v); + } + break; + case 95: + { + PFNGLVERTEXATTRIB4UIVPROC f = (PFNGLVERTEXATTRIB4UIVPROC) + GLUtils::getProcAddress("glVertexAttrib4uiv"); + GLuint ui[4]; + for (int i = 0; i < 4; i++ ) + ui[i] = (GLint) v[i]; + f(index, ui); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; + // ** GLushort-valued functions + case 96: + { + PFNGLVERTEXATTRIB4NUSVPROC f = (PFNGLVERTEXATTRIB4NUSVPROC) + GLUtils::getProcAddress("glVertexAttrib4Nusv"); + GLushort us[4]; + for (int i = 0; i < 4; i++ ) + us[i] = FLOAT_TO_USHORT(v[i]); + f(index, us); + COPY4(refOut, v); + } + break; + case 97: + { + PFNGLVERTEXATTRIB4USVPROC f = (PFNGLVERTEXATTRIB4USVPROC) + GLUtils::getProcAddress("glVertexAttrib4usv"); + GLushort us[4]; + for (int i = 0; i < 4; i++ ) + us[i] = (GLint) v[i]; + f(index, us); + refOut[0] = (GLfloat) (GLint) v[0]; + refOut[1] = (GLfloat) (GLint) v[1]; + refOut[2] = (GLfloat) (GLint) v[2]; + refOut[3] = (GLfloat) (GLint) v[3]; + } + break; +#endif + + default: + // never get here! + abort(); + } + + assert(98 == NUM_NV_ATTRIB_FUNCS + NUM_ARB_ATTRIB_FUNCS + NUM_2_0_ATTRIB_FUNCS); +} + + +// Test if 'a and 'b' are within an epsilon of each other +static bool +NearlyEqual(const GLfloat a[4], const GLfloat b[4]) +{ + const GLfloat epsilon = 0.05; + if (fabsf(a[0] - b[0]) > epsilon || + fabsf(a[1] - b[1]) > epsilon || + fabsf(a[2] - b[2]) > epsilon || + fabsf(a[3] - b[3]) > epsilon) + return 0; + else + return 1; +} + + +void VertAttribTest::FailMessage(VertAttribResult &r, const char *msg, + const char *func, int dlistMode) const +{ + // record the failure + r.pass = false; + + // print the message + env->log << name << ": FAIL " + << r.config->conciseDescription() << '\n'; + env->log << "\t" << msg << " (Testing " << func << " in "; + + if (dlistMode) + env->log << "display list mode)\n"; + else + env->log << "immediate mode)\n"; +} + + + +// Test setting/getting a set of vertex attribute values +// Return true if pass, false if fail +bool +VertAttribTest::TestAttribs(VertAttribResult &r, + int attribFunc, + PFNGLGETVERTEXATTRIBFVARBPROC getAttribfv, + Aliasing aliasing, + int numAttribs) +{ + static const GLfloat refValues[7] = { 1.0F, 0.8F, 0.6F, 0.5F, 0.4F, 0.2F, 0.0F }; + GLfloat refValue[32][4]; + GLfloat refOut[32][4]; + bool result = true; + + assert(numAttribs <= 32); + + // Initialize the refValue array + int refIndex = 0; + for (int i = 1; i < numAttribs; i++) { + refValue[i][0] = refValues[refIndex++ % 7]; + refValue[i][1] = refValues[refIndex++ % 7]; + refValue[i][2] = refValues[refIndex++ % 7]; + refValue[i][3] = refValues[refIndex++ % 7]; + } + + for (int dlist = 0; dlist < 2; dlist++) { + + // set a couple conventional attribs for later aliasing tests + glNormal3f(-1.0F, -2.0F, -3.0F); + glTexCoord4f(-1.0F, -2.0F, -3.0F, -4.0F); + + if (dlist == 1) { + glNewList(42, GL_COMPILE); + } + + // Set all the vertex attributes + for (int i = 1; i < numAttribs; i++) { + SetAttrib(i, refValue[i], attribFunc, refOut[i]); + } + + if (dlist == 1) { + glEndList(); + glCallList(42); + } + + // Test all the vertex attributes + for (int i = 1; i < numAttribs; i++) { + const GLfloat *expected = refOut[i]; + GLfloat v[4]; + getAttribfv(i, GL_CURRENT_VERTEX_ATTRIB_ARB, v); + if (!NearlyEqual(v, expected)) { + char msg[1000]; + sprintf(msg, "Vertex Attribute %d is (%g, %g, %g, %g) but expected (%g, %g, %g, %g)", + i, v[0], v[1], v[2], v[3], + expected[0], expected[1], expected[2], expected[3]); + FailMessage(r, msg, AttribFuncNames[attribFunc], dlist); + result = false; + } + } + + if (aliasing == REQUIRED) { + // spot check a few aliased attribs + GLfloat v[4]; + glGetFloatv(GL_CURRENT_NORMAL, v); + v[3] = refOut[2][3]; + if (!NearlyEqual(v, refOut[2])) { + FailMessage(r, "Setting attribute 2 did not update GL_CURRENT_NORMAL", AttribFuncNames[attribFunc], dlist); + result = false; + } + + glGetFloatv(GL_CURRENT_TEXTURE_COORDS, v); + if (!NearlyEqual(v, refOut[8])) { + FailMessage(r, "Setting attribute 8 did not update GL_CURRENT_TEXTURE_COORDS", AttribFuncNames[attribFunc], dlist); + result = false; + } + } + else if (aliasing == DISALLOWED) { + // spot check a few non-aliased attribs + GLfloat v[4]; + glGetFloatv(GL_CURRENT_NORMAL, v); + if (v[0] != -1.0F || + v[1] != -2.0F || + v[2] != -3.0F) { + FailMessage(r, "GL_CURRENT_NORMAL was erroneously set by a glVertexAttrib call", AttribFuncNames[attribFunc], dlist); + result = false; + } + glGetFloatv(GL_CURRENT_TEXTURE_COORDS, v); + if (v[0] != -1.0F || + v[1] != -2.0F || + v[2] != -3.0F || + v[3] != -4.0F) { + FailMessage(r, "GL_CURRENT_TEXTURE_COORDS was erroneously set by a glVertexAttrib call", AttribFuncNames[attribFunc], dlist); + result = false; + } + } + + } // dlist loop + + return result; +} + + +// Test the GL_NV_vertex_program functions +// Return true if pass, false if fail +bool +VertAttribTest::TestNVfuncs(VertAttribResult &r) +{ + bool result = true; +#ifdef GL_NV_vertex_program + PFNGLGETVERTEXATTRIBFVNVPROC getAttribfv; + const GLint numAttribs = 16; + const Aliasing aliasing = REQUIRED; + + getAttribfv = (PFNGLGETVERTEXATTRIBFVNVPROC) GLUtils::getProcAddress("glGetVertexAttribfvNV"); + assert(getAttribfv); + + r.numNVtested = 0; + + for (int attribFunc = 0; attribFunc < NUM_NV_ATTRIB_FUNCS; attribFunc++) { + bool b; + b = TestAttribs(r, attribFunc, getAttribfv, aliasing, numAttribs); + if (!b) + result = false; + r.numNVtested++; + } +#else + (void) r; +#endif + return result; +} + + +// Test the GL_ARB_vertex_program/shader functions +// Return true if pass, false if fail +bool +VertAttribTest::TestARBfuncs(VertAttribResult &r, bool shader) +{ + bool result = true; +#if defined(GL_ARB_vertex_program) || defined(GL_ARB_vertex_shader) + PFNGLGETVERTEXATTRIBFVARBPROC getAttribfv; + GLint numAttribs; + + getAttribfv = (PFNGLGETVERTEXATTRIBFVARBPROC) GLUtils::getProcAddress("glGetVertexAttribfvARB"); + assert(getAttribfv); + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &numAttribs); + assert(numAttribs > 0); + + r.numARBtested = 0; + + if (shader) { + // test GL_ARB_vertex_shader (aliasing is disallowed) + const Aliasing aliasing = DISALLOWED; + for (int i = 0; i < NUM_ARB_ATTRIB_FUNCS; i++) { + int attribFunc = NUM_NV_ATTRIB_FUNCS + i; + bool b; + b = TestAttribs(r, attribFunc, getAttribfv, aliasing, numAttribs); + if (!b) + result = false; + r.numARBtested++; + } + } + else { + // test GL_ARB_vertex_program: + // Determine if attribute aliasing is allowed + Aliasing aliasing; + if (GLUtils::haveExtension("GL_ARB_vertex_shader")) { + aliasing = DISALLOWED; + } + else { + // check for OpenGL 2.x support + char *vers = (char *) glGetString(GL_VERSION); + if (vers[0] == '2' && vers[1] == '.') { + aliasing = DISALLOWED; + } + else { + assert(vers[0] == '1'); /* revisit when we have OpenGL 3.x */ + aliasing = OPTIONAL; + } + } + for (int i = 0; i < NUM_ARB_ATTRIB_FUNCS; i++) { + int attribFunc = NUM_NV_ATTRIB_FUNCS + i; + bool b; + b = TestAttribs(r, attribFunc, getAttribfv, aliasing, numAttribs); + if (!b) + result = false; + r.numARBtested++; + } + } +#else + (void) r; +#endif + return result; +} + + +// Test the OpenGL 2.x glVertexAttrib functions +// Return true if pass, false if fail +bool +VertAttribTest::Test20funcs(VertAttribResult &r) +{ + bool result = true; +#ifdef GL_VERSION_2_0 + PFNGLGETVERTEXATTRIBFVPROC getAttribfv; + GLint numAttribs; + const Aliasing aliasing = DISALLOWED; + + getAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) GLUtils::getProcAddress("glGetVertexAttribfv"); + assert(getAttribfv); + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numAttribs); + assert(numAttribs > 0); + + r.num20tested = 0; + + for (int i = 0; i < NUM_2_0_ATTRIB_FUNCS; i++) { + int attribFunc = NUM_NV_ATTRIB_FUNCS + NUM_ARB_ATTRIB_FUNCS+ i; + bool b; + b = TestAttribs(r, attribFunc, getAttribfv, aliasing, numAttribs); + if (!b) + result = false; + r.num20tested++; + } +#else + (void) r; +#endif + return result; +} + + +void +VertAttribTest::runOne(VertAttribResult& r, Window&) +{ + + assert(sizeof(AttribFuncNames) / sizeof(char *) == + NUM_NV_ATTRIB_FUNCS + NUM_ARB_ATTRIB_FUNCS + NUM_2_0_ATTRIB_FUNCS); + + r.pass = true; +#ifdef GL_NV_vertex_program + if (GLUtils::haveExtension("GL_NV_vertex_program")) { + bool p = TestNVfuncs(r); + if (!p) + r.pass = false; + } +#endif +#ifdef GL_ARB_vertex_program + if (GLUtils::haveExtension("GL_ARB_vertex_program")) { + bool p = TestARBfuncs(r, false); + if (!p) + r.pass = false; + } +#endif +#ifdef GL_ARB_vertex_shader + if (GLUtils::haveExtension("GL_ARB_vertex_shader")) { + bool p = TestARBfuncs(r, true); + if (!p) + r.pass = false; + } +#endif +#ifdef GL_VERSION_2_0 + const char *vers = (const char *) glGetString(GL_VERSION); + if (vers[0] == '2' && vers[1] == '.') { + bool p = Test20funcs(r); + if (!p) + r.pass = false; + } +#endif +} + + +void +VertAttribTest::logOne(VertAttribResult& r) +{ + logPassFail(r); + logConcise(r); + logStats(r); +} + + +void +VertAttribTest::logStats(VertAttribResult& r) +{ + env->log << "\t" << r.numNVtested << " GL_NV_vertex_program functions tested\n"; + env->log << "\t" << r.numARBtested << " GL_ARB_vertex_program/shader functions tested\n"; + env->log << "\t" << r.num20tested << " OpenGL 2.0 functions tested\n"; +} + + +void +VertAttribTest::compareOne(VertAttribResult& oldR, VertAttribResult& newR) +{ + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR); + env->log << env->options.db2Name << ':'; + logStats(newR); + } +} + + +// Instantiate this test object +VertAttribTest vertAttribTest("vertattrib", "window, rgb", + "Verify that the glVertexAttribNV, glVertexAttribARB, and glVertexAttrib\n" + "functions all work correctly.\n"); + + +} // namespace GLEAN diff --git a/tests/glean/tvertattrib.h b/tests/glean/tvertattrib.h new file mode 100644 index 00000000..3729dc00 --- /dev/null +++ b/tests/glean/tvertattrib.h @@ -0,0 +1,93 @@ +// 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 + + +#ifndef __tvertattrib_h__ +#define __tvertattrib_h__ + +#include "tbase.h" + +namespace GLEAN { + + +class VertAttribResult: public BaseResult +{ +public: + bool pass; + int numNVtested, numARBtested, num20tested; + + VertAttribResult() + { + numNVtested = numARBtested = num20tested = 0; + } + + virtual void putresults(ostream& s) const + { + s << pass + << ' ' << numNVtested + << ' ' << numARBtested + << ' ' << num20tested + << '\n'; + } + + virtual bool getresults(istream& s) + { + s >> pass >> numNVtested >> numARBtested >> num20tested; + return s.good(); + } +}; + + +class VertAttribTest: public BaseTest<VertAttribResult> +{ +public: + GLEAN_CLASS(VertAttribTest, VertAttribResult); + virtual void logStats(VertAttribResult& r); + +private: + enum Aliasing { + REQUIRED, + DISALLOWED, + OPTIONAL + }; + + void FailMessage(VertAttribResult &r, const char *msg, + const char *ext, int dlistMode) const; + bool TestAttribs(VertAttribResult &r, + int attribFunc, + PFNGLGETVERTEXATTRIBFVARBPROC getAttribfv, + Aliasing aliasing, + int numAttribs); + bool TestNVfuncs(VertAttribResult &r); + bool TestARBfuncs(VertAttribResult &r, bool shader); + bool Test20funcs(VertAttribResult &r); +}; + +} // namespace GLEAN + +#endif // __tvertattrib_h__ diff --git a/tests/glean/tvertprog1.cpp b/tests/glean/tvertprog1.cpp new file mode 100644 index 00000000..ac65fc6c --- /dev/null +++ b/tests/glean/tvertprog1.cpp @@ -0,0 +1,1042 @@ +// 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 + +// tvertprog1.cpp: Test GL_ARB_vertex_program extension. +// Brian Paul 22 October 2005 +// +// See tfragprog.cpp for comments (this test is very similar). + +#include "tvertprog1.h" +#include <cassert> +#include <cmath> +#include <math.h> + + +namespace GLEAN { + + +static PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB_func; +static PFNGLGENPROGRAMSARBPROC glGenProgramsARB_func; +static PFNGLPROGRAMSTRINGARBPROC glProgramStringARB_func; +static PFNGLBINDPROGRAMARBPROC glBindProgramARB_func; +static PFNGLISPROGRAMARBPROC glIsProgramARB_func; +static PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB_func; + +static GLuint progID; + + +// Clamp X to [0, 1] +#define CLAMP01( X ) ( (X)<(0.0) ? (0.0) : ((X)>(1.0) ? (1.0) : (X)) ) +// Absolute value +#define ABS(X) ( (X) < 0.0 ? -(X) : (X) ) +// Max +#define MAX( A, B ) ( (A) > (B) ? (A) : (B) ) +// Min +#define MIN( A, B ) ( (A) < (B) ? (A) : (B) ) +// Duplicate value four times +#define SMEAR(X) (X), (X), (X), (X) + +#define DONT_CARE_Z -1.0 +#define DONT_CARE_COLOR -1.0 + +#define VERTCOLOR { 0.25, 0.75, 0.5, 0.25 } +#define PARAM0 { 0.0, 0.0, 0.0, 0.0 } // all zero +#define PARAM1 { 0.5, 0.25, 0.9, 0.5 } // in [0,1] +#define PARAM2 { -1.0, 0.0, 0.25, -0.5 } // in [-1,1] +#define AMBIENT { 0.2, 0.4, 0.6, 0.8 } +#define DIFFUSE { 0.1, 0.3, 0.5, 0.7 } +static const GLfloat VertColor[4] = VERTCOLOR; +static const GLfloat Param0[4] = PARAM0; +static const GLfloat Param1[4] = PARAM1; +static const GLfloat Param2[4] = PARAM2; +static const GLfloat Ambient[4] = AMBIENT; +static const GLfloat Diffuse[4] = DIFFUSE; +static const GLfloat FogDensity = 0.5; +static const GLfloat FogStart = 0.2; +static const GLfloat FogEnd = 0.9; +static GLfloat InfNan[4]; + + +// These are the specific vertex programs which we'll test. +// Alphabetical order, please. +static const VertexProgram Programs[] = { + // ============= Basic instructions tests ============================= + { + "ABS test", + "!!ARBvp1.0\n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "ABS result.color, p2; \n" + "END \n", + { CLAMP01(ABS(Param2[0])), + CLAMP01(ABS(Param2[1])), + CLAMP01(ABS(Param2[2])), + CLAMP01(ABS(Param2[3])), + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "ADD test", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "ADD result.color, vertex.color, p; \n" + "END \n", + { CLAMP01(VertColor[0] + Param1[0]), + CLAMP01(VertColor[1] + Param1[1]), + CLAMP01(VertColor[2] + Param1[2]), + CLAMP01(VertColor[3] + Param1[3]) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "ARL test", + "!!ARBvp1.0\n" + "ADDRESS addr; \n" + "PARAM indexes = {-1, 0, 1, 2}; \n" + "PARAM myArray[4] = {{0.11, 0.12, 0.13, 0.14}, \n" + " {0.21, 0.22, 0.23, 0.24}, \n" + " {0.31, 0.32, 0.33, 0.34}, \n" + " {0.41, 0.42, 0.43, 0.44}}; \n" + "MOV result.position, vertex.position; \n" + "" + "# Load ARL with -1, get array[0].x \n" + "ARL addr.x, indexes.x; \n" + "MOV result.color.x, myArray[addr.x + 1]; \n" + "" + "# Load ARL with 0, get array[1].y \n" + "ARL addr.x, indexes.y; \n" + "MOV result.color.y, myArray[addr.x + 1]; \n" + "" + "# Load ARL with 1, get array[2].z \n" + "ARL addr.x, indexes.z; \n" + "MOV result.color.z, myArray[addr.x + 1]; \n" + "" + "# Load ARL with 2, get array[3].w\n" + "ARL addr.x, indexes.w; \n" + "MOV result.color.w, myArray[addr.x + 1]; \n" + "END \n", + { 0.11, + 0.22, + 0.33, + 0.44 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "DP3 test", + "!!ARBvp1.0\n" + "PARAM p2 = program.local[2]; \n" + "PARAM bias = { 0.5, 0.5, 0.5, 0.5 }; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "DP3 t, p2, vertex.color; \n" + "ADD result.color, t, bias; \n" + "END \n", + { SMEAR(Param2[0] * VertColor[0] + + Param2[1] * VertColor[1] + + Param2[2] * VertColor[2] + 0.5) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "DP4 test", + "!!ARBvp1.0\n" + "PARAM p2 = program.local[2]; \n" + "PARAM bias = { 0.5, 0.5, 0.5, 0.5 }; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "DP4 t, p2, vertex.color; \n" + "ADD result.color, t, bias; \n" + "END \n", + { SMEAR(Param2[0] * VertColor[0] + + Param2[1] * VertColor[1] + + Param2[2] * VertColor[2] + + Param2[3] * VertColor[3] + 0.5) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "DPH test", + "!!ARBvp1.0\n" + "PARAM p2 = program.local[2]; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "DPH result.color, p2, vertex.color; \n" + "END \n", + { SMEAR(CLAMP01(Param2[0] * VertColor[0] + + Param2[1] * VertColor[1] + + Param2[2] * VertColor[2] + + VertColor[3])) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "DST test", + "!!ARBvp1.0\n" + "# let d = 0.4 \n" + "PARAM v1 = {9.9, 0.16, 0.16, 9.9}; \n" + "PARAM v2 = {9.9, 2.5, 9.9, 2.5}; \n" + "MOV result.position, vertex.position; \n" + "DST result.color, v1, v2; \n" + "END \n", + { 1.0, + 0.4, // v1.y * v2.y + 0.16, // v1.z + CLAMP01(2.5) // v2.w + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "EX2 test", + "!!ARBvp1.0\n" + "PARAM scale = {0.01, 0.01, 0.01, 0.01}; \n" + "PARAM values = {0.0, 1.0, 4.0, -2.0 }; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "EX2 t.x, values.x; \n" + "EX2 t.y, values.y; \n" + "EX2 t.z, values.z; \n" + "EX2 t.w, values.w; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 1.0 * 0.01, + 2.0 * 0.01, + 16.0 * 0.01, + 0.25 * 0.01 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "EXP test", + "!!ARBvp1.0\n" + "PARAM scale = {0.01, 0.01, 0.01, 0.01}; \n" + "PARAM values = {4.5, 0, 0, 0}; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "EXP t, values.x; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 16.0 * 0.01, + 0.5 * 0.01, + 22.627 * 0.01, + 1.0 * 0.01 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "FLR test", + "!!ARBvp1.0\n" + "PARAM values = {4.8, 0.3, -0.2, 1.2}; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "MOV result.position, vertex.position; \n" + "TEMP t; \n" + "FLR t, values; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 0.4, + 0.0, + CLAMP01(-0.1), + 0.1 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "FRC test", + "!!ARBvp1.0\n" + "PARAM values = {1.344, -1.5, -10.1, 4.2}; \n" + "MOV result.position, vertex.position; \n" + "FRC result.color, values; \n" + "END \n", + { 0.344, + 0.5, + 0.9, + 0.2 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "LG2 test", + "!!ARBvp1.0\n" + "PARAM values = {64.0, 1, 30, 4}; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "MOV result.position, vertex.position; \n" + "TEMP t; \n" + "LG2 t.x, values.x; \n" + "LG2 t.y, values.y; \n" + "LG2 t.z, values.z; \n" + "LG2 t.w, values.w; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 0.6, + 0.0, + 0.49, + 0.2 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "LIT test 1", + "!!ARBvp1.0\n" + "PARAM values = {0.65, 0.9, 0.0, 8.0}; \n" + "MOV result.position, vertex.position; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + 0.65, // values.x + 0.430, // roughly Pow(values.y, values.w) + 1.0 + }, + DONT_CARE_Z, + FLAG_LOOSE + }, + { + "LIT test 2 (degenerate case: 0 ^ 0 -> 1)", + "!!ARBvp1.0\n" + "PARAM values = {0.65, 0.0, 0.0, 0.0}; \n" + "MOV result.position, vertex.position; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + 0.65, // values.x + 1.0, // 0^0 + 1.0 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "LIT test 3 (case x < 0)", + "!!ARBvp1.0\n" + "PARAM values = {-0.5, 0.0, 0.0, 0.0}; \n" + "MOV result.position, vertex.position; \n" + "LIT result.color, values; \n" + "END \n", + { 1.0, + CLAMP01(-0.5), // values.x + 0.0, + 1.0 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "LOG test", + "!!ARBvp1.0\n" + "PARAM values = {64.0, 50, 30, 4}; \n" + "PARAM scale = {0.1, 0.1, 0.1, 0.1}; \n" + "MOV result.position, vertex.position; \n" + "TEMP t; \n" + "LOG t.x, values.x; \n" + "LOG t.y, values.y; \n" + "LOG t.z, values.z; \n" + "LOG t.w, values.w; \n" + "MUL result.color, t, scale; \n" + "END \n", + { 0.6, // floor(log2(value.x)) + 0.15, // value.y / 2^(floor(log2(value.y))) + 0.49, // roughApproxLog2(value.z) + 0.1 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "MAD test", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "MAD result.color, vertex.color, p1, p2; \n" + "END \n", + { CLAMP01(VertColor[0] * Param1[0] + Param2[0]), + CLAMP01(VertColor[1] * Param1[1] + Param2[1]), + CLAMP01(VertColor[2] * Param1[2] + Param2[2]), + CLAMP01(VertColor[3] * Param1[3] + Param2[3]) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "MAX test", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "MAX result.color, p1, p2; \n" + "END \n", + { MAX(Param1[0], Param2[0]), + MAX(Param1[1], Param2[1]), + MAX(Param1[2], Param2[2]), + MAX(Param1[3], Param2[3]), + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "MIN test", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "MIN result.color, p1, vertex.color; \n" + "END \n", + { MIN(Param1[0], VertColor[0]), + MIN(Param1[1], VertColor[1]), + MIN(Param1[2], VertColor[2]), + MIN(Param1[3], VertColor[3]), + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "MOV test (with swizzle)", + "!!ARBvp1.0\n" + "MOV result.position, vertex.position; \n" + "MOV result.color, vertex.color.wzxy; \n" + "END \n", + { VertColor[3], + VertColor[2], + VertColor[0], + VertColor[1] + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "MUL test (with swizzle and masking)", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "MUL result.color.xy, p1.wzww, vertex.color.wzww; \n" + "MUL result.color.zw, p1.xxyx, vertex.color.xxyx; \n" + "END \n", + { CLAMP01(Param1[3] * VertColor[3]), + CLAMP01(Param1[2] * VertColor[2]), + CLAMP01(Param1[1] * VertColor[1]), + CLAMP01(Param1[0] * VertColor[0]), + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "POW test (exponentiation)", + "!!ARBvp1.0\n" + "PARAM values = {0.5, 2, 3, 4}; \n" + "MOV result.position, vertex.position; \n" + "POW result.color.x, values.x, values.y; \n" + "POW result.color.y, values.x, values.z; \n" + "POW result.color.z, values.x, values.w; \n" + "POW result.color.w, values.w, values.x; \n" + "END \n", + { 0.5 * 0.5, + 0.5 * 0.5 * 0.5, + 0.5 * 0.5 * 0.5 * 0.5, + CLAMP01(2.0) }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "RCP test (reciprocal)", + "!!ARBvp1.0\n" + "PARAM values = {8, -10, 1, 12 }; \n" + "MOV result.position, vertex.position; \n" + "RCP result.color.x, values.x; \n" + "RCP result.color.y, values.y; \n" + "RCP result.color.z, values.z; \n" + "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, + FLAG_NONE + }, + { + "RSQ test 1 (reciprocal square root)", + "!!ARBvp1.0\n" + "PARAM values = {1, 4, 9, 100 }; \n" + "MOV result.position, vertex.position; \n" + "RSQ result.color.x, values.x; \n" + "RSQ result.color.y, values.y; \n" + "RSQ result.color.z, values.z; \n" + "RSQ result.color.w, values.w; \n" + "END \n", + { 1.0, 0.5, 0.3333, 0.1 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "RSQ test 2 (reciprocal square root of negative value)", + "!!ARBvp1.0\n" + "PARAM values = {0, -100, -5, -1}; \n" + "MOV result.position, vertex.position; \n" + "RSQ result.color.x, values.x; \n" + "RSQ result.color.y, values.y; \n" + "RSQ result.color.z, values.z; \n" + "RSQ result.color.w, values.w; \n" + "END \n", + { DONT_CARE_COLOR, + 0.1, + 0.447, + 1.0, + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SGE test", + "!!ARBvp1.0\n" + "PARAM p0 = program.local[0]; \n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "SGE result.color, p2, p0; \n" + "END \n", + { Param2[0] >= Param0[0] ? 1.0 : 0.0, + Param2[1] >= Param0[1] ? 1.0 : 0.0, + Param2[2] >= Param0[2] ? 1.0 : 0.0, + Param2[3] >= Param0[3] ? 1.0 : 0.0, + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SLT test", + "!!ARBvp1.0\n" + "PARAM p0 = program.local[0]; \n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "SLT result.color, p2, p0; \n" + "END \n", + { Param2[0] < Param0[0] ? 1.0 : 0.0, + Param2[1] < Param0[1] ? 1.0 : 0.0, + Param2[2] < Param0[2] ? 1.0 : 0.0, + Param2[3] < Param0[3] ? 1.0 : 0.0, + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SUB test (with swizzle)", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SUB result.color, p1.yxwz, vertex.color.primary.yxwz; \n" + "END \n", + { CLAMP01(Param1[1] - VertColor[1]), + CLAMP01(Param1[0] - VertColor[0]), + CLAMP01(Param1[3] - VertColor[3]), + CLAMP01(Param1[2] - VertColor[2]) + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SWZ test 1", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SWZ result.color, p, w,x,x,y; \n" + "END \n", + { Param1[3], + Param1[0], + Param1[0], + Param1[1] + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SWZ test 2", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SWZ result.color, p, -w,-x,x,y; \n" + "END \n", + { CLAMP01(-Param1[3]), + CLAMP01(-Param1[0]), + Param1[0], + Param1[1] + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SWZ test 3", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SWZ result.color, p, 0,1,0,1; \n" + "END \n", + { 0.0, 1.0, 0.0, 1.0 }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SWZ test 4", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SWZ result.color, p, 1,x,z,0; \n" + "END \n", + { 1.0, + Param1[0], + Param1[2], + 0.0 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "SWZ test 5", + "!!ARBvp1.0\n" + "PARAM p = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "SWZ result.color, p, z,-y,-1,0; \n" + "END \n", + { CLAMP01(Param1[2]), + CLAMP01(-Param1[1]), + CLAMP01(-1), + 0.0 + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "XPD test 1", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "MOV result.position, vertex.position; \n" + "XPD result.color, p1, p2; \n" + "END \n", + { CLAMP01(Param1[1] * Param2[2] - Param1[2] * Param2[1]), + CLAMP01(Param1[2] * Param2[0] - Param1[0] * Param2[2]), + CLAMP01(Param1[0] * Param2[1] - Param1[1] * Param2[0]), + DONT_CARE_COLOR + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "XPD test 2 (same src/dst arg)", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "PARAM p2 = program.local[2]; \n" + "TEMP t; \n" + "MOV result.position, vertex.position; \n" + "MOV t, p1; \n" + "XPD t, t, p2; \n" + "MOV result.color, t; \n" + "END \n", + { CLAMP01(Param1[1] * Param2[2] - Param1[2] * Param2[1]), + CLAMP01(Param1[2] * Param2[0] - Param1[0] * Param2[2]), + CLAMP01(Param1[0] * Param2[1] - Param1[1] * Param2[0]), + DONT_CARE_COLOR + }, + DONT_CARE_Z, + FLAG_NONE + }, + + // ============= Test result.position writes ========================== + { + "Position write test (compute position from texcoord)", + "!!ARBvp1.0\n" + "ATTRIB texcoord = vertex.texcoord[0]; \n" + "PARAM scale = {0.5, 0.5, 0.0, 1.0}; \n" + "PARAM bias = {-0.25, -0.25, 0.0, 0.0}; \n" + "MAD result.position, texcoord, scale, bias; \n" + "MOV result.color, vertex.color; \n" + "END \n", + VERTCOLOR, + DONT_CARE_Z, + FLAG_NONE + }, + { + "Z-write test", + "!!ARBvp1.0\n" + "PARAM p1 = program.local[1]; \n" + "MOV result.position, vertex.position; \n" + "MOV result.position.z, p1.y; \n" + "MOV result.color, vertex.color; \n" + "END \n", + VERTCOLOR, + Param1[1] * 0.5 + 0.5, // map clip Z to win Z + FLAG_NONE + }, + + // ============= Global state reference tests ========================= + { + "State reference test 1 (material ambient)", + "!!ARBvp1.0\n" + "PARAM ambient = state.material.front.ambient; \n" + "MOV result.position, vertex.position; \n" + "MOV result.color, ambient; \n" + "END \n", + AMBIENT, + DONT_CARE_Z, + FLAG_NONE + }, + { + // Note: material.diffuse = VertColor + // light.diffuse = Diffuse + "State reference test 2 (light products)", + "!!ARBvp1.0\n" + "PARAM dprod = state.lightprod[0].diffuse; \n" + "MOV result.position, vertex.position; \n" + "MOV result.color, dprod; \n" + "END \n", + { CLAMP01(Diffuse[0] * VertColor[0]), + CLAMP01(Diffuse[1] * VertColor[1]), + CLAMP01(Diffuse[2] * VertColor[2]), + CLAMP01(VertColor[3]) // material's diffuse alpha + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "State reference test 3 (fog params)", + "!!ARBvp1.0\n" + "PARAM fog = state.fog.params; \n" + "PARAM scale = {1.0, 1.0, 1.0, 0.1}; \n" + "MOV result.position, vertex.position; \n" + "MUL result.color, fog, scale; \n" + "END \n", + { FogDensity, + FogStart, + FogEnd, + (1.0 / (FogEnd - FogStart)) * 0.1 + }, + DONT_CARE_Z, + FLAG_NONE + }, + + // ============= Numeric stress tests ================================= + // Basically just check that we don't crash when we do divides by + // zero, etc. + { + "Divide by zero test", + "!!ARBvp1.0\n" + "PARAM zero = program.local[0]; \n" + "MOV result.position, vertex.position; \n" + "RCP result.color.x, zero.x; \n" + "RCP result.color.y, zero.y; \n" + "RCP result.color.z, zero.z; \n" + "RCP result.color.w, zero.w; \n" + "END \n", + { DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR + }, + DONT_CARE_Z, + FLAG_NONE + }, + { + "Infinity / nan test", + "!!ARBvp1.0\n" + "PARAM zero = program.local[0]; \n" + "PARAM infNan = program.local[9]; \n" + "MOV result.position, vertex.position; \n" + "ADD result.color, infNan, zero; \n" + "END \n", + { DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR, + DONT_CARE_COLOR + }, + DONT_CARE_Z, + FLAG_NONE + }, + + // ============= Texcoord output tests ================================ + // XXX to do + + // XXX add lots more tests here! + { NULL, NULL, {0,0,0,0}, 0, FLAG_NONE } // end of list sentinal +}; + + + +void +VertexProgramTest::setup(void) +{ + // setup Infinity, Nan values + int nan; + float *nanPtr; + + nan = (0xff << 23) | (1 << 0); + nanPtr = (float *) &nan; + InfNan[0] = HUGE_VAL; + InfNan[1] = -HUGE_VAL; + InfNan[2] = (float) (*nanPtr); + InfNan[3] = 1.0 / HUGE_VAL; + /* + printf("InfNan = %f %f %f %f\n", + InfNan[0], InfNan[1], InfNan[2], InfNan[3]); + */ + + // get function pointers + glProgramLocalParameter4fvARB_func = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLUtils::getProcAddress("glProgramLocalParameter4fvARB"); + assert(glProgramLocalParameter4fvARB_func); + + glGenProgramsARB_func = (PFNGLGENPROGRAMSARBPROC) GLUtils::getProcAddress("glGenProgramsARB"); + assert(glGenProgramsARB_func); + + glProgramStringARB_func = (PFNGLPROGRAMSTRINGARBPROC) GLUtils::getProcAddress("glProgramStringARB"); + assert(glProgramStringARB_func); + + glBindProgramARB_func = (PFNGLBINDPROGRAMARBPROC) GLUtils::getProcAddress("glBindProgramARB"); + assert(glBindProgramARB_func); + + glIsProgramARB_func = (PFNGLISPROGRAMARBPROC) GLUtils::getProcAddress("glIsProgramARB"); + assert(glIsProgramARB_func); + + glDeleteProgramsARB_func = (PFNGLDELETEPROGRAMSARBPROC) GLUtils::getProcAddress("glDeleteProgramsARB"); + assert(glDeleteProgramsARB_func); + + glGenProgramsARB_func(1, &progID); + glBindProgramARB_func(GL_VERTEX_PROGRAM_ARB, progID); + glEnable(GL_VERTEX_PROGRAM_ARB); + + // load program inputs + glColor4fv(VertColor); + glProgramLocalParameter4fvARB_func(GL_VERTEX_PROGRAM_ARB, 0, Param0); + glProgramLocalParameter4fvARB_func(GL_VERTEX_PROGRAM_ARB, 1, Param1); + glProgramLocalParameter4fvARB_func(GL_VERTEX_PROGRAM_ARB, 2, Param2); + glProgramLocalParameter4fvARB_func(GL_VERTEX_PROGRAM_ARB, 9, InfNan); + + // other GL state + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Ambient); + glLightfv(GL_LIGHT0, GL_DIFFUSE, Diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, VertColor); + glFogf(GL_FOG_DENSITY, FogDensity); + glFogf(GL_FOG_START, FogStart); + glFogf(GL_FOG_END, FogEnd); + + 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]; +} + + +void +VertexProgramTest::reportFailure(const char *programName, + const GLfloat expectedColor[4], + const GLfloat actualColor[4] ) const +{ + env->log << "FAILURE:\n"; + env->log << " Program: " << 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 +VertexProgramTest::reportZFailure(const char *programName, + GLfloat expectedZ, GLfloat actualZ) const +{ + env->log << "FAILURE:\n"; + env->log << " Program: " << programName << "\n"; + env->log << " Expected Z: " << expectedZ << "\n"; + env->log << " Observed Z: " << actualZ << "\n"; +} + + + +// Compare actual and expected colors +bool +VertexProgramTest::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] && exp[0] != DONT_CARE_COLOR) || + (fabsf(act[1] - exp[1]) > tol[1] && exp[1] != DONT_CARE_COLOR) || + (fabsf(act[2] - exp[2]) > tol[2] && exp[2] != DONT_CARE_COLOR) || + (fabsf(act[3] - exp[3]) > tol[3] && exp[3] != DONT_CARE_COLOR)) + return false; + else + return true; +} + + +bool +VertexProgramTest::equalDepth(GLfloat z0, GLfloat z1) const +{ + if (fabsf(z0 - z1) > tolerance[4]) + return false; + else + return true; +} + + +bool +VertexProgramTest::testProgram(const VertexProgram &p) +{ + const GLfloat r = 0.25; + + glProgramStringARB_func(GL_VERTEX_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + strlen(p.progString), + (const GLubyte *) p.progString); + + GLenum err = glGetError(); + if (err) { + GLint errorPos; + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); + env->log << "OpenGL error " << (int) err << "\n"; + env->log << "Invalid Vertex Program:\n"; + env->log << p.progString; + env->log << "Error position: " << errorPos << "\n"; + env->log << "Error message: " << glGetString(GL_PROGRAM_ERROR_STRING_ARB) << "\n"; + return false; + } + + // 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); + 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(); + + GLfloat pixel[4]; + glReadPixels(windowSize / 2, windowSize / 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); + return false; + } + + if (p.expectedZ != DONT_CARE_Z) { + GLfloat z; + glReadPixels(windowSize / 2, windowSize / 2, 1, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, &z); + if (!equalDepth(z, p.expectedZ)) { + reportZFailure(p.name, p.expectedZ, z); + return false; + } + } + + if (0) // debug + printf("%s passed\n", p.name); + + return true; +} + +void +VertexProgramTest::runOne(MultiTestResult &r, Window &w) +{ + (void) w; + setup(); + + for (int i = 0; Programs[i].name; i++) { + if (!testProgram(Programs[i])) { + r.numFailed++; + } + else { + r.numPassed++; + } + } + r.pass = (r.numFailed == 0); +} + + +// The test object itself: +VertexProgramTest vertexProgramTest("vertProg1", "window, rgb, z", + "GL_ARB_vertex_program", + "Vertex Program test 1: test a specific set of vertex programs.\n" + ); + + + +} // namespace GLEAN diff --git a/tests/glean/tvertprog1.h b/tests/glean/tvertprog1.h new file mode 100644 index 00000000..df42073c --- /dev/null +++ b/tests/glean/tvertprog1.h @@ -0,0 +1,85 @@ +// 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 + +// tvertprog.h: Test GL_ARB_vertex_program extension. +// Brian Paul 22 October 2005 + +#ifndef __tvertprog_h__ +#define __tvertprog_h__ + +#include "tmultitest.h" + +namespace GLEAN { + +#define windowSize 100 + +// to indicate a looser tolerance test is needed +#define FLAG_NONE 0 +#define FLAG_LOOSE 1 + +class VertexProgram +{ +public: + const char *name; + const char *progString; + const GLfloat expectedColor[4]; + const GLfloat expectedZ; + int flags; +}; + + + +class VertexProgramTest: public MultiTest +{ +public: + VertexProgramTest(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]; + void setup(void); + bool equalColors(const GLfloat a[4], const GLfloat b[4], int flags) const; + bool equalDepth(GLfloat z0, GLfloat z1) const; + bool testProgram(const VertexProgram &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 // __tvertprog_h__ diff --git a/tests/glean/tvtxperf.cpp b/tests/glean/tvtxperf.cpp new file mode 100644 index 00000000..fa5bd877 --- /dev/null +++ b/tests/glean/tvtxperf.cpp @@ -0,0 +1,1424 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// tvtxperf.cpp: Test performance of various ways to specify vertex data + +#include "tvtxperf.h" +#include "geomutil.h" +#include "timer.h" +#include "rand.h" +#include "image.h" +#include "codedid.h" +#include "treadpix.h" + +namespace { +struct C4UB_N3F_V3F { + GLubyte c[4]; + GLfloat n[3]; + GLfloat v[3]; +}; + +struct C4UB_T2F_V3F { + GLubyte c[4]; + GLfloat t[2]; + GLfloat v[3]; +}; + +class TvtxBaseTimer: public GLEAN::Timer { +public: + int nVertices; + GLuint* indices; + int nTris; + GLEAN::Window* w; + GLEAN::Environment* env; + + TvtxBaseTimer(int v, GLuint* i, int t, GLEAN::Window* win, + GLEAN::Environment* e) { + nVertices = v; + indices = i; + nTris = t; + w = win; + env = e; + } + + virtual double compute(double t) { return nTris/t; } + virtual void premeasure() { + // Clear both front and back buffers and swap, to avoid + // confusing this test with results of the previous + // test: + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + w->swap(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + virtual void postmeasure() { w->swap(); } + virtual void preop() { env->quiesce(); glFinish(); } + virtual void postop() { glFinish(); } +}; + +class ColoredLit_imIndTri: public TvtxBaseTimer { +public: + C4UB_N3F_V3F* data; + ColoredLit_imIndTri(int v, C4UB_N3F_V3F* c, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, 0, t, w, env) { + data = c; + } + + virtual void op() { + C4UB_N3F_V3F* p = data; + glBegin(GL_TRIANGLES); + // Assume that the data is complete, thus allowing us + // to unroll 3X and do one tri per iteration rather than + // one vertex. + for (int i = nVertices / 3; i; --i) { + glColor4ubv(p[0].c); + glNormal3fv(p[0].n); + glVertex3fv(p[0].v); + glColor4ubv(p[1].c); + glNormal3fv(p[1].n); + glVertex3fv(p[1].v); + glColor4ubv(p[2].c); + glNormal3fv(p[2].n); + glVertex3fv(p[2].v); + p += 3; + } + glEnd(); + } +}; // coloredLit_imIndTri + +class ColoredTex_imIndTri: public TvtxBaseTimer { +public: + C4UB_T2F_V3F* data; + ColoredTex_imIndTri(int v, C4UB_T2F_V3F* c, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, 0, t, w, env) { + data = c; + } + + virtual void op() { + C4UB_T2F_V3F* p = data; + glBegin(GL_TRIANGLES); + // Assume that the data is complete, thus allowing us + // to unroll 3X and do one tri per iteration rather than + // one vertex. + for (int i = nVertices / 3; i; --i) { + glColor4ubv(p[0].c); + glTexCoord2fv(p[0].t); + glVertex3fv(p[0].v); + glColor4ubv(p[1].c); + glTexCoord2fv(p[0].t); + glVertex3fv(p[1].v); + glColor4ubv(p[2].c); + glTexCoord2fv(p[0].t); + glVertex3fv(p[2].v); + p += 3; + } + glEnd(); + } +}; // coloredTex_imIndTri + +class ColoredLit_imTriStrip: public TvtxBaseTimer { +public: + C4UB_N3F_V3F* data; + ColoredLit_imTriStrip(int v, C4UB_N3F_V3F* c, int t, + GLEAN::Window* w, GLEAN::Environment* env): + TvtxBaseTimer(v, 0, t, w, env) { + data = c; + } + + virtual void op() { + C4UB_N3F_V3F* p = data; + glBegin(GL_TRIANGLE_STRIP); + + int n = (nVertices + 3) >> 2; + // Duff's device. Yes, this is legal C (and C++). + // See Stroustrup, 3rd ed., p. 141 + switch (nVertices & 0x3) { + case 0: do { + glColor4ubv(p->c); + glNormal3fv(p->n); + glVertex3fv(p->v); + ++p; + case 3: + glColor4ubv(p->c); + glNormal3fv(p->n); + glVertex3fv(p->v); + ++p; + case 2: + glColor4ubv(p->c); + glNormal3fv(p->n); + glVertex3fv(p->v); + ++p; + case 1: + glColor4ubv(p->c); + glNormal3fv(p->n); + glVertex3fv(p->v); + ++p; + } while (--n > 0); + } + glEnd(); + } +}; // coloredLit_imTriStrip + +class ColoredTex_imTriStrip: public TvtxBaseTimer { +public: + C4UB_T2F_V3F* data; + + ColoredTex_imTriStrip(int v, C4UB_T2F_V3F* c, int t, + GLEAN::Window* w, GLEAN::Environment* env): + TvtxBaseTimer(v, 0, t, w, env) { + data = c; + } + + virtual void op() { + C4UB_T2F_V3F* p = data; + glBegin(GL_TRIANGLE_STRIP); + + int n = (nVertices + 3) >> 2; + // Duff's device. Yes, this is legal C (and C++). + // See Stroustrup, 3rd ed., p. 141 + switch (nVertices & 0x3) { + case 0: do { + glColor4ubv(p->c); + glTexCoord2fv(p->t); + glVertex3fv(p->v); + ++p; + case 3: + glColor4ubv(p->c); + glTexCoord2fv(p->t); + glVertex3fv(p->v); + ++p; + case 2: + glColor4ubv(p->c); + glTexCoord2fv(p->t); + glVertex3fv(p->v); + ++p; + case 1: + glColor4ubv(p->c); + glTexCoord2fv(p->t); + glVertex3fv(p->v); + ++p; + } while (--n > 0); + } + glEnd(); + } +}; // coloredTex_imTriStrip + +class daIndTriTimer: public TvtxBaseTimer { +public: + daIndTriTimer(int v, GLuint* i, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, i, t, w, env) { + } + virtual void op() {glDrawArrays(GL_TRIANGLES, 0, nVertices); } +}; // daIndTriTimer + +class daTriStripTimer: public TvtxBaseTimer { +public: + daTriStripTimer(int v, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, 0, t, w, env) { + } + virtual void op() { glDrawArrays(GL_TRIANGLE_STRIP, 0, nVertices); } +}; // daTriStripTimer + +class deIndTriTimer: public TvtxBaseTimer { +public: + deIndTriTimer(int v, GLuint* i, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, i, t, w, env) { + } + virtual void op() { + glDrawElements(GL_TRIANGLES, nVertices, GL_UNSIGNED_INT, + indices); + } +}; // deIndTriTimer + +class deTriStripTimer: public TvtxBaseTimer { +public: + deTriStripTimer(int v, GLuint* i, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(v, i, t, w, env) { + } + virtual void op() { + glDrawElements(GL_TRIANGLE_STRIP, nVertices, GL_UNSIGNED_INT, + indices); + } +}; // deTriStripTimer + + +class callDListTimer: public TvtxBaseTimer { +public: + int dList; + callDListTimer(int d, int t, GLEAN::Window* w, + GLEAN::Environment* env): + TvtxBaseTimer(0, 0, t, w, env) { + dList = d; + } + virtual void op() { glCallList(dList); } +}; // callDList + +void +logStats1(const char* title, GLEAN::VPSubResult& r, + GLEAN::Environment* env) { + env->log << '\t' << title << " rate = " + << r.tps << " tri/sec.\n" + << "\t\tRange of valid measurements = [" + << r.tpsLow << ", " << r.tpsHigh << "]\n" + << "\t\tImage sanity check " + << (r.imageOK? "passed\n": "failed\n") + << "\t\tImage consistency check " + << (r.imageMatch? "passed\n": "failed\n"); + +} // logStats1 + +void +diffHeader(bool& same, const string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::Environment* env) { + if (same) { + same = false; + env->log << name << ": DIFF " + << config->conciseDescription() << '\n'; + } +} // diffHeader + +void +failHeader(bool& pass, const string& name, + GLEAN::DrawingSurfaceConfig* config, GLEAN::Environment* env) { + if (pass) { + pass = false; + env->log << name << ": FAIL " + << config->conciseDescription() << '\n'; + } +} // failHeader + +void +doComparison(const GLEAN::VPSubResult& oldR, + const GLEAN::VPSubResult& newR, + GLEAN::DrawingSurfaceConfig* config, + bool& same, const string& name, GLEAN::Environment* env, + const char* title) { + if (newR.tps < oldR.tpsLow) { + int percent = static_cast<int>( + 100.0 * (oldR.tps - newR.tps) / newR.tps + 0.5); + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name + << " may be " << percent << "% faster on " + << title << " drawing.\n"; + } + if (newR.tps > oldR.tpsHigh) { + int percent = static_cast<int>( + 100.0 * (newR.tps - oldR.tps) / oldR.tps + 0.5); + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db2Name + << " may be " << percent << "% faster on " + << title << " drawing.\n"; + } + if (newR.imageOK != oldR.imageOK) { + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name << " image check " + << (oldR.imageOK? "passed\n": "failed\n"); + env->log << '\t' << env->options.db2Name << " image check " + << (newR.imageOK? "passed\n": "failed\n"); + } + if (newR.imageMatch != oldR.imageMatch) { + diffHeader(same, name, config, env); + env->log << '\t' << env->options.db1Name << " image compare " + << (oldR.imageMatch? "passed\n": "failed\n"); + env->log << '\t' << env->options.db2Name << " image compare " + << (newR.imageMatch? "passed\n": "failed\n"); + } +} // doComparison + +bool +imagesDiffer(GLEAN::Image& testImage, GLEAN::Image& goldenImage) { + GLEAN::Image::Registration imageReg(testImage.reg(goldenImage)); + return (imageReg.stats[0].max() + + imageReg.stats[1].max() + + imageReg.stats[2].max()) != 0.0; +} // imagesDiffer + +void +missingSome(GLEAN::Environment* env, const char* title) { + env->log << '\t' << title << " rendering is missing\n" + << "\t\tsome triangles.\n"; +} // missingSome + +void +theyDiffer(GLEAN::Environment* env, const char* title) { + env->log << '\t' << title << " image differs from\n" + << "\t\tthe reference image.\n"; +} // theyDiffer + +void +verifyVtxPerf(GLEAN::Image& testImage, GLEAN::RGBCodedID& colorGen, + int firstID, int lastID, GLEAN::Image& refImage, + bool& passed, string& name, GLEAN::DrawingSurfaceConfig* config, + GLEAN::VPSubResult& res, GLEAN::Environment* env, const char* title) { + + // Verify that the entire range of RGB coded identifiers is + // present in the image. (This is an indicator that all triangles + // were actually drawn.) + testImage.read(0, 0); + if (!colorGen.allPresent(testImage, firstID, lastID)) { + failHeader(passed, name, config, env); + missingSome(env, title); + res.imageOK = false; + } + + // Verify that the test image is the same as the reference image. + if (imagesDiffer(testImage, refImage)) { + failHeader(passed, name, config, env); + theyDiffer(env, title); + res.imageMatch = false; + } +} // verify + +} // anonymous namespace + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +ColoredLitPerf::runOne(VPResult& r, Window& w) { + // Don't bother running if the ExactRGBA test for this display + // surface configuration failed: + vector<ExactRGBAResult*>::const_iterator erRes; + for (erRes = exactRGBATest.results.begin(); + erRes != exactRGBATest.results.end(); + ++erRes) + if ((*erRes)->config == r.config) + break; + if (erRes == exactRGBATest.results.end() || !(*erRes)->ub.pass) { + r.skipped = true; + r.pass = false; + return; + } + + bool passed = true; + PFNGLLOCKARRAYSEXTPROC glLockArraysEXT = 0; + PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = 0; + if (GLUtils::haveExtension("GL_EXT_compiled_vertex_array")) { + glLockArraysEXT = reinterpret_cast<PFNGLLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glLockArraysEXT")); + glUnlockArraysEXT = reinterpret_cast<PFNGLUNLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glUnlockArraysEXT")); + } + + Image imTriImage(drawingSize, drawingSize, GL_RGB, GL_UNSIGNED_BYTE); + Image testImage(drawingSize, drawingSize, GL_RGB, GL_UNSIGNED_BYTE); + + // Make colors deterministic, so we can check them: + RGBCodedID colorGen(r.config->r, r.config->g, r.config->b); + int IDModulus = colorGen.maxID() + 1; + + // We need to minimize the number of pixels per triangle, so that + // we're measuring vertex-processing rate rather than fill rate. + // However, we'd also like to guarantee that every triangle covers + // at least one pixel, so that we can confirm drawing actually took + // place. As a compromise, we'll choose a number of triangles that + // yields approximately 3 pixels per triangle. + // We're drawing a filled spiral that approximates a circular area, + // so pi * (drawingSize/2)**2 / nTris = 3 implies... + const int nTris = static_cast<int> + (((3.14159 / 4.0) * drawingSize * drawingSize) / 3.0 + 0.5); + int nVertices = nTris * 3; + int lastID = min(IDModulus - 1, nTris - 1); + + C4UB_N3F_V3F *c4ub_n3f_v3f = new C4UB_N3F_V3F[nVertices]; + SpiralTri2D it(nTris, 0, drawingSize, 0, drawingSize); + int k = 0; + for (int j = 0; j < nTris; ++j) { + float* t = it(j); + GLubyte r, g, b; + colorGen.toRGB(j % IDModulus, r, g, b); + + c4ub_n3f_v3f[k+0].c[0] = r; + c4ub_n3f_v3f[k+0].c[1] = g; + c4ub_n3f_v3f[k+0].c[2] = b; + c4ub_n3f_v3f[k+0].c[3] = 0xFF; + c4ub_n3f_v3f[k+0].n[0] = 0.0; + c4ub_n3f_v3f[k+0].n[1] = 0.0; + c4ub_n3f_v3f[k+0].n[2] = 1.0; + c4ub_n3f_v3f[k+0].v[0] = t[0]; + c4ub_n3f_v3f[k+0].v[1] = t[1]; + c4ub_n3f_v3f[k+0].v[2] = 0.0; + + c4ub_n3f_v3f[k+1].c[0] = r; + c4ub_n3f_v3f[k+1].c[1] = g; + c4ub_n3f_v3f[k+1].c[2] = b; + c4ub_n3f_v3f[k+1].c[3] = 0xFF; + c4ub_n3f_v3f[k+1].n[0] = 0.0; + c4ub_n3f_v3f[k+1].n[1] = 0.0; + c4ub_n3f_v3f[k+1].n[2] = 1.0; + c4ub_n3f_v3f[k+1].v[0] = t[2]; + c4ub_n3f_v3f[k+1].v[1] = t[3]; + c4ub_n3f_v3f[k+1].v[2] = 0.0; + + c4ub_n3f_v3f[k+2].c[0] = r; + c4ub_n3f_v3f[k+2].c[1] = g; + c4ub_n3f_v3f[k+2].c[2] = b; + c4ub_n3f_v3f[k+2].c[3] = 0xFF; + c4ub_n3f_v3f[k+2].n[0] = 0.0; + c4ub_n3f_v3f[k+2].n[1] = 0.0; + c4ub_n3f_v3f[k+2].n[2] = 1.0; + c4ub_n3f_v3f[k+2].v[0] = t[4]; + c4ub_n3f_v3f[k+2].v[1] = t[5]; + c4ub_n3f_v3f[k+2].v[2] = 0.0; + + k += 3; + } + + GLuint *indices = new GLuint[nVertices]; + for (k = 0; k < nVertices; ++k) + indices[k] = k; + + GLUtils::useScreenCoords(drawingSize, drawingSize); + + // Diffuse white light at infinity, behind the eye: + GLUtils::Light light(0); + light.ambient(0, 0, 0, 0); + light.diffuse(1, 1, 1, 0); + light.specular(0, 0, 0, 0); + light.position(0, 0, 1, 0); + light.spotCutoff(180); + light.constantAttenuation(1); + light.linearAttenuation(0); + light.quadraticAttenuation(0); + light.enable(); + + GLUtils::LightModel lm; + lm.ambient(0, 0, 0, 0); + lm.localViewer(false); + lm.twoSide(false); + lm.colorControl(GL_SINGLE_COLOR); + + glFrontFace(GL_CCW); + glEnable(GL_NORMALIZE); + GLUtils::Material mat; + mat.ambient(0, 0, 0, 1); + mat.ambientAndDiffuse(1, 1, 1, 1); + mat.specular(0, 0, 0, 1); + mat.emission(0, 0, 0, 1); + mat.shininess(0); + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + glEnable(GL_COLOR_MATERIAL); + + glEnable(GL_LIGHTING); + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glReadBuffer(GL_FRONT); + + //////////////////////////////////////////////////////////// + // Immediate-mode independent triangles + //////////////////////////////////////////////////////////// + ColoredLit_imIndTri coloredLit_imIndTri(nVertices, c4ub_n3f_v3f, + nTris, &w, env); + coloredLit_imIndTri.measure(5, &r.imTri.tpsLow, &r.imTri.tps, + &r.imTri.tpsHigh); + imTriImage.read(0, 0); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.imTri, env, + "Immediate-mode independent triangle"); + + //////////////////////////////////////////////////////////// + // Display-listed independent triangles + //////////////////////////////////////////////////////////// + int dList = glGenLists(1); + glNewList(dList, GL_COMPILE); + coloredLit_imIndTri.op(); + glEndList(); + callDListTimer callDList(dList, nTris, &w, env); + callDList.measure(5, &r.dlTri.tpsLow, &r.dlTri.tps, &r.dlTri.tpsHigh); + glDeleteLists(dList, 1); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.dlTri, env, + "Display-listed independent triangle"); + + //////////////////////////////////////////////////////////// + // DrawArrays on independent triangles + //////////////////////////////////////////////////////////// + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].c); + glEnableClientState(GL_COLOR_ARRAY); + glNormalPointer(GL_FLOAT, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].n); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].v); + glEnableClientState(GL_VERTEX_ARRAY); + + daIndTriTimer daIndTri(nVertices, indices, nTris, &w, env); + daIndTri.measure(5, &r.daTri.tpsLow, &r.daTri.tps, &r.daTri.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.daTri, env, + "DrawArrays independent triangle"); + + //////////////////////////////////////////////////////////// + // Locked DrawArrays on independent triangles + // XXX This is probably unrealistically favorable to + // locked arrays. + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + daIndTri.measure(5, &r.ldaTri.tpsLow, &r.ldaTri.tps, + &r.ldaTri.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldaTri.tps = r.ldaTri.tpsLow = r.ldaTri.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldaTri, env, + "Locked DrawArrays independent triangle"); + + //////////////////////////////////////////////////////////// + // DrawElements on independent triangles + //////////////////////////////////////////////////////////// + deIndTriTimer deIndTri(nVertices, indices, nTris, &w, env); + deIndTri.measure(5, &r.deTri.tpsLow, &r.deTri.tps, &r.deTri.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.deTri, env, + "DrawElements independent triangle"); + + //////////////////////////////////////////////////////////// + // Locked DrawElements on independent triangles + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + deIndTri.measure(5, &r.ldeTri.tpsLow, &r.ldeTri.tps, + &r.ldeTri.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldeTri.tps = r.ldeTri.tpsLow = r.ldeTri.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldeTri, env, + "Locked DrawElements independent triangle"); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + delete[] c4ub_n3f_v3f; + delete[] indices; + + // Now we test triangle strips, rather than independent triangles. + + nVertices = nTris + 2; + lastID = min(IDModulus - 1, nTris - 1); + + c4ub_n3f_v3f = new C4UB_N3F_V3F[nVertices]; + SpiralStrip2D is(nVertices, 0, drawingSize, 0, drawingSize); + for (int j2 = 0; j2 < nVertices; ++j2) { + float* t = is(j2); + GLubyte r, g, b; + // Take care to get the correct color on the provoking vertex: + colorGen.toRGB((j2 - 2) % IDModulus, r, g, b); + + c4ub_n3f_v3f[j2].c[0] = r; + c4ub_n3f_v3f[j2].c[1] = g; + c4ub_n3f_v3f[j2].c[2] = b; + c4ub_n3f_v3f[j2].c[3] = 0xFF; + c4ub_n3f_v3f[j2].n[0] = 0.0; + c4ub_n3f_v3f[j2].n[1] = 0.0; + c4ub_n3f_v3f[j2].n[2] = 1.0; + c4ub_n3f_v3f[j2].v[0] = t[0]; + c4ub_n3f_v3f[j2].v[1] = t[1]; + c4ub_n3f_v3f[j2].v[2] = 0.0; + } + + indices = new GLuint[nVertices]; + for (int j3 = 0; j3 < nVertices; ++j3) + indices[j3] = j3; + + //////////////////////////////////////////////////////////// + // Immediate-mode triangle strips + //////////////////////////////////////////////////////////// + ColoredLit_imTriStrip coloredLit_imTriStrip(nVertices, c4ub_n3f_v3f, + nTris, &w, env); + coloredLit_imTriStrip.measure(5, &r.imTS.tpsLow, &r.imTS.tps, + &r.imTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.imTS, env, + "Immediate-mode triangle strip"); + + //////////////////////////////////////////////////////////// + // Display-listed triangle strips + //////////////////////////////////////////////////////////// + dList = glGenLists(1); + glNewList(dList, GL_COMPILE); + coloredLit_imTriStrip.op(); + glEndList(); + callDList.dList = dList; + callDList.measure(5, &r.dlTS.tpsLow, &r.dlTS.tps, &r.dlTS.tpsHigh); + glDeleteLists(dList, 1); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.dlTS, env, + "Display-listed triangle strip"); + + //////////////////////////////////////////////////////////// + // DrawArrays on triangle strips + //////////////////////////////////////////////////////////// + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].c); + glEnableClientState(GL_COLOR_ARRAY); + glNormalPointer(GL_FLOAT, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].n); + glEnableClientState(GL_NORMAL_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(c4ub_n3f_v3f[0]), + c4ub_n3f_v3f[0].v); + glEnableClientState(GL_VERTEX_ARRAY); + + daTriStripTimer daTriStrip(nVertices, nTris, &w, env); + daTriStrip.measure(5, &r.daTS.tpsLow, &r.daTS.tps, &r.daTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.daTS, env, + "DrawArrays triangle strip"); + + //////////////////////////////////////////////////////////// + // Locked DrawArrays on triangle strips + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + daTriStrip.measure(5, &r.ldaTS.tpsLow, &r.ldaTS.tps, &r.ldaTS.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldaTS.tps = r.ldaTS.tpsLow = r.ldaTS.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldaTS, env, + "Locked DrawArrays triangle strip"); + + //////////////////////////////////////////////////////////// + // DrawElements on triangle strips + //////////////////////////////////////////////////////////// + deTriStripTimer deTriStrip(nVertices, indices, nTris, &w, env); + deTriStrip.measure(5, &r.deTS.tpsLow, &r.deTS.tps, &r.deTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.deTS, env, + "DrawElements triangle strip"); + + //////////////////////////////////////////////////////////// + // Locked DrawElements on triangle strips + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + deTriStrip.measure(5, &r.ldeTS.tpsLow, &r.ldeTS.tps, &r.ldeTS.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldeTS.tps = r.ldeTS.tpsLow = r.ldeTS.tpsHigh = 0.0; + + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldeTS, env, + "Locked DrawElements triangle strip"); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + delete[] c4ub_n3f_v3f; + delete[] indices; + + r.pass = passed; + r.skipped = false; +} // ColoredLitPerf::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ColoredLitPerf::logOne(VPResult& r) { + if (r.skipped) { + env->log << name << ": NOTE "; + logConcise(r); + env->log << "\tTest skipped; prerequisite test " + << exactRGBATest.name + << " failed or was not run\n"; + return; + } + if (r.pass) { + logPassFail(r); + logConcise(r); + } else env->log << '\n'; // because verify logs failure + logStats(r, env); +} // ColoredLitPerf::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ColoredLitPerf::compareOne(VPResult& oldR, VPResult& newR) { + if (oldR.skipped || newR.skipped) { + env->log << name + << ((oldR.skipped && newR.skipped)? ": SAME " + : ": DIFF ") + << newR.config->conciseDescription() + << '\n'; + if (oldR.skipped) + env->log << "\t" + << env->options.db1Name + << " skipped\n"; + if (newR.skipped) + env->log << "\t" + << env->options.db2Name + << " skipped\n"; + env->log << "\tNo comparison is possible.\n"; + return; + } + + bool same = true; + doComparison(oldR.imTri, newR.imTri, newR.config, same, name, + env, "immediate-mode independent triangle"); + doComparison(oldR.dlTri, newR.dlTri, newR.config, same, name, + env, "display-listed independent triangle"); + doComparison(oldR.daTri, newR.daTri, newR.config, same, name, + env, "DrawArrays independent triangle"); + doComparison(oldR.ldaTri, newR.ldaTri, newR.config, same, name, + env, "Locked DrawArrays independent triangle"); + doComparison(oldR.deTri, newR.deTri, newR.config, same, name, + env, "DrawElements independent triangle"); + doComparison(oldR.ldeTri, newR.ldeTri, newR.config, same, name, + env, "Locked DrawElements independent triangle"); + doComparison(oldR.imTS, newR.imTS, newR.config, same, name, + env, "immediate-mode triangle strip"); + doComparison(oldR.dlTS, newR.dlTS, newR.config, same, name, + env, "display-listed triangle strip"); + doComparison(oldR.daTS, newR.daTS, newR.config, same, name, + env, "DrawArrays triangle strip"); + doComparison(oldR.ldaTS, newR.ldaTS, newR.config, same, name, + env, "Locked DrawArrays triangle strip"); + doComparison(oldR.deTS, newR.deTS, newR.config, same, name, + env, "DrawElements triangle strip"); + doComparison(oldR.ldeTS, newR.ldeTS, newR.config, same, name, + env, "Locked DrawElements triangle strip"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n\t" + << env->options.db2Name + << " test time falls within the " + << "valid measurement range of\n\t" + << env->options.db1Name + << " test time; both have the same" + << " image comparison results.\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR, env); + env->log << env->options.db2Name << ':'; + logStats(newR, env); + } +} // ColoredLitPerf::compareOne + +void +ColoredLitPerf::logStats(VPResult& r, GLEAN::Environment* env) { + logStats1("Immediate-mode independent triangle", r.imTri, env); + logStats1("Display-listed independent triangle", r.dlTri, env); + logStats1("DrawArrays independent triangle", r.daTri, env); + logStats1("Locked DrawArrays independent triangle", r.ldaTri, env); + logStats1("DrawElements independent triangle", r.deTri, env); + logStats1("Locked DrawElements independent triangle", r.ldeTri, env); + logStats1("Immediate-mode triangle strip", r.imTS, env); + logStats1("Display-listed triangle strip", r.dlTS, env); + logStats1("DrawArrays triangle strip", r.daTS, env); + logStats1("Locked DrawArrays triangle strip", r.ldaTS, env); + logStats1("DrawElements triangle strip", r.deTS, env); + logStats1("Locked DrawElements triangle strip", r.ldeTS, env); +} // ColoredLitPerf::logStats + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// + +Test* coloredLitPerfTestPrereqs[] = {&exactRGBATest, 0}; + +ColoredLitPerf coloredLitPerfTest("coloredLitPerf2", "window, rgb, z, fast", + coloredLitPerfTestPrereqs, + + "This test examines rendering performance for colored, lit,\n" + "flat-shaded triangles. It checks several different ways to\n" + "specify the vertex data in order to determine which is\n" + "fastest: fine-grained API calls, DrawArrays, DrawElements,\n" + "locked (compiled) DrawArrays, and locked DrawElements; for\n" + "independent triangles and for triangle strips. The test\n" + "result is performance measured in triangles per second for\n" + "each of the various vertex specification methods.\n" + + "\nAs a sanity-check on the correctness of each method, the test\n" + "colors each triangle with a unique color, and verifies that all\n" + "such colors are actually present in the final image. For\n" + "consistency, the test also verifies that the images are identical\n" + "for each of the specification methods.\n" + + ); + + +/////////////////////////////////////////////////////////////////////////////// +// runOne: Run a single test case +/////////////////////////////////////////////////////////////////////////////// + +void +ColoredTexPerf::runOne(VPResult& r, Window& w) { + // Don't bother running if the ExactRGBA test for this display + // surface configuration failed: + vector<ExactRGBAResult*>::const_iterator erRes; + for (erRes = exactRGBATest.results.begin(); + erRes != exactRGBATest.results.end(); + ++erRes) + if ((*erRes)->config == r.config) + break; + if (erRes == exactRGBATest.results.end() || !(*erRes)->ub.pass) { + r.skipped = true; + r.pass = false; + return; + } + + PFNGLLOCKARRAYSEXTPROC glLockArraysEXT = 0; + PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = 0; + if (GLUtils::haveExtension("GL_EXT_compiled_vertex_array")) { + glLockArraysEXT = reinterpret_cast<PFNGLLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glLockArraysEXT")); + glUnlockArraysEXT = reinterpret_cast<PFNGLUNLOCKARRAYSEXTPROC> + (GLUtils::getProcAddress("glUnlockArraysEXT")); + } + + Image imTriImage(drawingSize, drawingSize, GL_RGB, GL_UNSIGNED_BYTE); + Image testImage(drawingSize, drawingSize, GL_RGB, GL_UNSIGNED_BYTE); + bool passed = true; + + // Make colors deterministic, so we can check them: + RGBCodedID colorGen(r.config->r, r.config->g, r.config->b); + int IDModulus = colorGen.maxID() + 1; + + // We need to minimize the number of pixels per triangle, so that + // we're measuring vertex-processing rate rather than fill rate. + // However, we'd also like to guarantee that every triangle covers + // at least one pixel, so that we can confirm drawing actually took + // place. As a compromise, we'll choose a number of triangles that + // yields approximately 3 pixels per triangle. + // We're drawing a filled spiral that approximates a circular area, + // so pi * (drawingSize/2)**2 / nTris = 3 implies... + const int nTris = static_cast<int> + (((3.14159 / 4.0) * drawingSize * drawingSize) / 3.0 + 0.5); + int nVertices = nTris * 3; + int lastID = min(IDModulus - 1, nTris - 1); + + C4UB_T2F_V3F *c4ub_t2f_v3f = new C4UB_T2F_V3F[nVertices]; + SpiralTri2D it(nTris, 0, drawingSize, 0, drawingSize); + int k = 0; + for (int j = 0; j < nTris; ++j) { + float* t = it(j); + GLubyte r, g, b; + colorGen.toRGB(j % IDModulus, r, g, b); + + c4ub_t2f_v3f[k+0].c[0] = r; + c4ub_t2f_v3f[k+0].c[1] = g; + c4ub_t2f_v3f[k+0].c[2] = b; + c4ub_t2f_v3f[k+0].c[3] = 0xFF; + c4ub_t2f_v3f[k+0].t[0] = 0.5; + c4ub_t2f_v3f[k+0].t[1] = 0.5; + c4ub_t2f_v3f[k+0].v[0] = t[0]; + c4ub_t2f_v3f[k+0].v[1] = t[1]; + c4ub_t2f_v3f[k+0].v[2] = 0.0; + + c4ub_t2f_v3f[k+1].c[0] = r; + c4ub_t2f_v3f[k+1].c[1] = g; + c4ub_t2f_v3f[k+1].c[2] = b; + c4ub_t2f_v3f[k+1].c[3] = 0xFF; + c4ub_t2f_v3f[k+1].t[0] = 0.5; + c4ub_t2f_v3f[k+1].t[1] = 0.5; + c4ub_t2f_v3f[k+1].v[0] = t[2]; + c4ub_t2f_v3f[k+1].v[1] = t[3]; + c4ub_t2f_v3f[k+1].v[2] = 0.0; + + c4ub_t2f_v3f[k+2].c[0] = r; + c4ub_t2f_v3f[k+2].c[1] = g; + c4ub_t2f_v3f[k+2].c[2] = b; + c4ub_t2f_v3f[k+2].c[3] = 0xFF; + c4ub_t2f_v3f[k+2].t[0] = 0.5; + c4ub_t2f_v3f[k+2].t[1] = 0.5; + c4ub_t2f_v3f[k+2].v[0] = t[4]; + c4ub_t2f_v3f[k+2].v[1] = t[5]; + c4ub_t2f_v3f[k+2].v[2] = 0.0; + + k += 3; + } + + GLuint *indices = new GLuint[nVertices]; + for (k = 0; k < nVertices; ++k) + indices[k] = k; + + GLUtils::useScreenCoords(drawingSize, drawingSize); + + glFrontFace(GL_CCW); + glDisable(GL_NORMALIZE); + glDisable(GL_COLOR_MATERIAL); + + glDisable(GL_LIGHTING); + + // Set up an all-white RGB texture, including mipmap levels: + { + const int width = 8; + const int height = 8; + GLubyte whiteTex[width * height * 3]; + for (int i = 0; i < width * height * 3; ++i) + whiteTex[i] = 255; + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0); + glPixelTransferi(GL_MAP_COLOR, GL_FALSE); + glPixelTransferf(GL_RED_SCALE, 1.0); + glPixelTransferf(GL_GREEN_SCALE, 1.0); + glPixelTransferf(GL_BLUE_SCALE, 1.0); + glPixelTransferf(GL_ALPHA_SCALE, 1.0); + glPixelTransferf(GL_RED_BIAS, 0.0); + glPixelTransferf(GL_GREEN_BIAS, 0.0); + glPixelTransferf(GL_BLUE_BIAS, 0.0); + glPixelTransferf(GL_ALPHA_BIAS, 0.0); + gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_RGB, + GL_UNSIGNED_BYTE, whiteTex); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + glDisable(GL_TEXTURE_GEN_S); + glDisable(GL_TEXTURE_GEN_T); + + glEnable(GL_TEXTURE_2D); + } + + glDisable(GL_FOG); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_ALPHA_TEST); + glDisable(GL_STENCIL_TEST); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_COLOR_LOGIC_OP); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_POLYGON_OFFSET_FILL); + + glShadeModel(GL_FLAT); + + glReadBuffer(GL_FRONT); + + //////////////////////////////////////////////////////////// + // Immediate-mode independent triangles + //////////////////////////////////////////////////////////// + ColoredTex_imIndTri coloredTex_imIndTri(nVertices, c4ub_t2f_v3f, + nTris, &w, env); + coloredTex_imIndTri.measure(5, &r.imTri.tpsLow, &r.imTri.tps, + &r.imTri.tpsHigh); + imTriImage.read(0, 0); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.imTri, env, + "Immediate-mode independent triangle"); + + //////////////////////////////////////////////////////////// + // Display-listed independent triangles + //////////////////////////////////////////////////////////// + int dList = glGenLists(1); + glNewList(dList, GL_COMPILE); + coloredTex_imIndTri.op(); + glEndList(); + callDListTimer callDList(dList, nTris, &w, env); + callDList.measure(5, &r.dlTri.tpsLow, &r.dlTri.tps, &r.dlTri.tpsHigh); + glDeleteLists(dList, 1); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.dlTri, env, + "Display-listed independent triangle"); + + //////////////////////////////////////////////////////////// + // DrawArrays on independent triangles + //////////////////////////////////////////////////////////// + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].c); + glEnableClientState(GL_COLOR_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].t); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].v); + glEnableClientState(GL_VERTEX_ARRAY); + + daIndTriTimer daIndTri(nVertices, indices, nTris, &w, env); + daIndTri.measure(5, &r.daTri.tpsLow, &r.daTri.tps, &r.daTri.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.daTri, env, + "DrawArrays independent triangle"); + + //////////////////////////////////////////////////////////// + // Locked DrawArrays on independent triangles + // XXX This is probably unrealistically favorable to + // locked arrays. + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + daIndTri.measure(5, &r.ldaTri.tpsLow, &r.ldaTri.tps, + &r.ldaTri.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldaTri.tps = r.ldaTri.tpsLow = r.ldaTri.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldaTri, env, + "Locked DrawArrays independent triangle"); + + //////////////////////////////////////////////////////////// + // DrawElements on independent triangles + //////////////////////////////////////////////////////////// + deIndTriTimer deIndTri(nVertices, indices, nTris, &w, env); + deIndTri.measure(5, &r.deTri.tpsLow, &r.deTri.tps, &r.deTri.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.deTri, env, + "DrawElements independent triangle"); + + //////////////////////////////////////////////////////////// + // Locked DrawElements on independent triangles + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + deIndTri.measure(5, &r.ldeTri.tpsLow, &r.ldeTri.tps, + &r.ldeTri.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldeTri.tps = r.ldeTri.tpsLow = r.ldeTri.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldeTri, env, + "Locked DrawElements independent triangle"); + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + delete[] c4ub_t2f_v3f; + delete[] indices; + + // Now we test triangle strips, rather than independent triangles. + + nVertices = nTris + 2; + lastID = min(IDModulus - 1, nTris - 1); + + c4ub_t2f_v3f = new C4UB_T2F_V3F[nVertices]; + SpiralStrip2D is(nVertices, 0, drawingSize, 0, drawingSize); + for (int j2 = 0; j2 < nVertices; ++j2) { + float* t = is(j2); + GLubyte r, g, b; + // Take care to get the correct color on the provoking vertex: + colorGen.toRGB((j2 - 2) % IDModulus, r, g, b); + + c4ub_t2f_v3f[j2].c[0] = r; + c4ub_t2f_v3f[j2].c[1] = g; + c4ub_t2f_v3f[j2].c[2] = b; + c4ub_t2f_v3f[j2].c[3] = 0xFF; + c4ub_t2f_v3f[j2].t[0] = 0.5; + c4ub_t2f_v3f[j2].t[1] = 0.5; + c4ub_t2f_v3f[j2].v[0] = t[0]; + c4ub_t2f_v3f[j2].v[1] = t[1]; + c4ub_t2f_v3f[j2].v[2] = 0.0; + } + + indices = new GLuint[nVertices]; + for (int j3 = 0; j3 < nVertices; ++j3) + indices[j3] = j3; + + //////////////////////////////////////////////////////////// + // Immediate-mode triangle strips + //////////////////////////////////////////////////////////// + ColoredTex_imTriStrip coloredTex_imTriStrip(nVertices, c4ub_t2f_v3f, + nTris, &w, env); + coloredTex_imTriStrip.measure(5, &r.imTS.tpsLow, &r.imTS.tps, + &r.imTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.imTS, env, + "Immediate-mode triangle strip"); + + //////////////////////////////////////////////////////////// + // Display-listed triangle strips + //////////////////////////////////////////////////////////// + dList = glGenLists(1); + glNewList(dList, GL_COMPILE); + coloredTex_imTriStrip.op(); + glEndList(); + callDList.dList = dList; + callDList.measure(5, &r.dlTS.tpsLow, &r.dlTS.tps, &r.dlTS.tpsHigh); + glDeleteLists(dList, 1); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.dlTS, env, + "Display-listed triangle strip"); + + //////////////////////////////////////////////////////////// + // DrawArrays on triangle strips + //////////////////////////////////////////////////////////// + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].c); + glEnableClientState(GL_COLOR_ARRAY); + glTexCoordPointer(2, GL_FLOAT, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].t); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(c4ub_t2f_v3f[0]), + c4ub_t2f_v3f[0].v); + glEnableClientState(GL_VERTEX_ARRAY); + + daTriStripTimer daTriStrip(nVertices, nTris, &w, env); + daTriStrip.measure(5, &r.daTS.tpsLow, &r.daTS.tps, &r.daTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.daTS, env, + "DrawArrays triangle strip"); + + //////////////////////////////////////////////////////////// + // Locked DrawArrays on triangle strips + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + daTriStrip.measure(5, &r.ldaTS.tpsLow, &r.ldaTS.tps, &r.ldaTS.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldaTS.tps = r.ldaTS.tpsLow = r.ldaTS.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldaTS, env, + "Locked DrawArrays triangle strip"); + + //////////////////////////////////////////////////////////// + // DrawElements on triangle strips + //////////////////////////////////////////////////////////// + deTriStripTimer deTriStrip(nVertices, indices, nTris, &w, env); + deTriStrip.measure(5, &r.deTS.tpsLow, &r.deTS.tps, &r.deTS.tpsHigh); + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.deTS, env, + "DrawElements triangle strip"); + + //////////////////////////////////////////////////////////// + // Locked DrawElements on triangle strips + //////////////////////////////////////////////////////////// + if (glLockArraysEXT) + glLockArraysEXT(0, nVertices); + deTriStrip.measure(5, &r.ldeTS.tpsLow, &r.ldeTS.tps, &r.ldeTS.tpsHigh); + if (glUnlockArraysEXT) + glUnlockArraysEXT(); + if (!glLockArraysEXT) + r.ldeTS.tps = r.ldeTS.tpsLow = r.ldeTS.tpsHigh = 0.0; + verifyVtxPerf(testImage, colorGen, 0, lastID, imTriImage, + passed, name, r.config, r.ldeTS, env, + "Locked DrawElements triangle strip"); + + + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + delete[] c4ub_t2f_v3f; + delete[] indices; + + r.pass = passed; + r.skipped = false; +} // ColoredTexPerf::runOne + +/////////////////////////////////////////////////////////////////////////////// +// logOne: Log a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ColoredTexPerf::logOne(VPResult& r) { + if (r.skipped) { + env->log << name << ": NOTE "; + logConcise(r); + env->log << "\tTest skipped; prerequisite test " + << exactRGBATest.name + << " failed or was not run\n" + ; + return; + } + if (r.pass) { + logPassFail(r); + logConcise(r); + } else env->log << '\n'; // because verify logs failure + logStats(r, env); +} // ColoredTexPerf::logOne + +/////////////////////////////////////////////////////////////////////////////// +// compareOne: Compare results for a single test case +/////////////////////////////////////////////////////////////////////////////// +void +ColoredTexPerf::compareOne(VPResult& oldR, VPResult& newR) { + if (oldR.skipped || newR.skipped) { + env->log << name + << ((oldR.skipped && newR.skipped)? ": SAME " + : ": DIFF ") + << newR.config->conciseDescription() + << '\n'; + if (oldR.skipped) + env->log << "\t" + << env->options.db1Name + << " skipped\n"; + if (newR.skipped) + env->log << "\t" + << env->options.db2Name + << " skipped\n"; + env->log << "\tNo comparison is possible.\n"; + return; + } + + bool same = true; + doComparison(oldR.imTri, newR.imTri, newR.config, same, name, + env, "immediate-mode independent triangle"); + doComparison(oldR.dlTri, newR.dlTri, newR.config, same, name, + env, "display-listed independent triangle"); + doComparison(oldR.daTri, newR.daTri, newR.config, same, name, + env, "DrawArrays independent triangle"); + doComparison(oldR.ldaTri, newR.ldaTri, newR.config, same, name, + env, "Locked DrawArrays independent triangle"); + doComparison(oldR.deTri, newR.deTri, newR.config, same, name, + env, "DrawElements independent triangle"); + doComparison(oldR.ldeTri, newR.ldeTri, newR.config, same, name, + env, "Locked DrawElements independent triangle"); + doComparison(oldR.imTS, newR.imTS, newR.config, same, name, + env, "immediate-mode triangle strip"); + doComparison(oldR.dlTS, newR.dlTS, newR.config, same, name, + env, "display-listed triangle strip"); + doComparison(oldR.daTS, newR.daTS, newR.config, same, name, + env, "DrawArrays triangle strip"); + doComparison(oldR.ldaTS, newR.ldaTS, newR.config, same, name, + env, "Locked DrawArrays triangle strip"); + doComparison(oldR.deTS, newR.deTS, newR.config, same, name, + env, "DrawElements triangle strip"); + doComparison(oldR.ldeTS, newR.ldeTS, newR.config, same, name, + env, "Locked DrawElements triangle strip"); + + if (same && env->options.verbosity) { + env->log << name << ": SAME " + << newR.config->conciseDescription() + << "\n\t" + << env->options.db2Name + << " test time falls within the " + << "valid measurement range of\n\t" + << env->options.db1Name + << " test time; both have the same" + << " image comparison results.\n"; + } + + if (env->options.verbosity) { + env->log << env->options.db1Name << ':'; + logStats(oldR, env); + env->log << env->options.db2Name << ':'; + logStats(newR, env); + } +} // ColoredTexPerf::compareOne + +void +ColoredTexPerf::logStats(VPResult& r, GLEAN::Environment* env) { + logStats1("Immediate-mode independent triangle", r.imTri, env); + logStats1("Display-listed independent triangle", r.dlTri, env); + logStats1("DrawArrays independent triangle", r.daTri, env); + logStats1("Locked DrawArrays independent triangle", r.ldaTri, env); + logStats1("DrawElements independent triangle", r.deTri, env); + logStats1("Locked DrawElements independent triangle", r.ldeTri, env); + logStats1("Immediate-mode triangle strip", r.imTS, env); + logStats1("Display-listed triangle strip", r.dlTS, env); + logStats1("DrawArrays triangle strip", r.daTS, env); + logStats1("Locked DrawArrays triangle strip", r.ldaTS, env); + logStats1("DrawElements triangle strip", r.deTS, env); + logStats1("Locked DrawElements triangle strip", r.ldeTS, env); +} // ColoredTexPerf::logStats + +/////////////////////////////////////////////////////////////////////////////// +// The test object itself: +/////////////////////////////////////////////////////////////////////////////// +// +Test* coloredTexPerfTestPrereqs[] = {&exactRGBATest, 0}; + +ColoredTexPerf coloredTexPerfTest("coloredTexPerf2", "window, rgb, z, fast", + coloredTexPerfTestPrereqs, + + "This test examines rendering performance for colored, textured,\n" + "flat-shaded triangles. It checks several different ways to\n" + "specify the vertex data in order to determine which is\n" + "fastest: fine-grained API calls, DrawArrays, DrawElements,\n" + "locked (compiled) DrawArrays, and locked DrawElements; for\n" + "independent triangles and for triangle strips. The test\n" + "result is performance measured in triangles per second for\n" + "each of the various vertex specification methods.\n" + + "\nAs a sanity-check on the correctness of each method, the test\n" + "colors each triangle with a unique color, and verifies that all\n" + "such colors are actually present in the final image. For\n" + "consistency, the test also verifies that the images are identical\n" + "for each of the specification methods.\n" + + ); + +} // namespace GLEAN diff --git a/tests/glean/tvtxperf.h b/tests/glean/tvtxperf.h new file mode 100644 index 00000000..755902ea --- /dev/null +++ b/tests/glean/tvtxperf.h @@ -0,0 +1,147 @@ +// BEGIN_COPYRIGHT -*- glean -*- +// +// Copyright (C) 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 +// 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 + +// tvtxperf.h: Test performance of various ways to specify vertex data + +#ifndef __tvtxperf_h__ +#define __tvtxperf_h__ + +#include "tbase.h" + +namespace GLEAN { + +#define drawingSize 256 + +// Auxiliary struct for holding a vertex-performance result: +class VPSubResult { +public: + double tps; // Triangles Per Second + double tpsLow; // Low end of tps range + double tpsHigh; // High end of tps range + bool imageOK; // Image sanity-check status + bool imageMatch; // Image comparison status + + VPSubResult() { + tps = tpsLow = tpsHigh = 0.0; + imageOK = imageMatch = true; + } + + void put(ostream& s) const { + s << tps + << ' ' << tpsLow + << ' ' << tpsHigh + << ' ' << imageOK + << ' ' << imageMatch + << '\n'; + } + + void get(istream& s) { + s >> tps >> tpsLow >> tpsHigh >> imageOK >> imageMatch; + } +}; + +class VPResult: public BaseResult { +public: + bool skipped; // prerequisite tests failed + bool pass; + + VPSubResult imTri; // immediate-mode independent triangles + VPSubResult dlTri; // display-listed independent triangles + VPSubResult daTri; // DrawArrays independent triangles + VPSubResult ldaTri; // Locked DrawArrays independent tris + VPSubResult deTri; // DrawElements independent triangles + VPSubResult ldeTri; // Locked DrawElements ind. tris + + VPSubResult imTS; // immediate-mode triangle strip + VPSubResult dlTS; // display-listed triangle strip + VPSubResult daTS; // DrawArrays triangle strip + VPSubResult ldaTS; // Locked DrawArrays triangle strip + VPSubResult deTS; // DrawElements triangle strip + VPSubResult ldeTS; // Locked DrawElements triangle strip + + virtual void putresults(ostream& s) const { + s + << skipped << '\n' + << pass << '\n' + ; + + imTri.put(s); + dlTri.put(s); + daTri.put(s); + ldaTri.put(s); + deTri.put(s); + ldeTri.put(s); + + imTS.put(s); + dlTS.put(s); + daTS.put(s); + ldaTS.put(s); + deTS.put(s); + ldeTS.put(s); + } + + virtual bool getresults(istream& s) { + s + >> skipped + >> pass + ; + imTri.get(s); + dlTri.get(s); + daTri.get(s); + ldaTri.get(s); + deTri.get(s); + ldeTri.get(s); + + imTS.get(s); + dlTS.get(s); + daTS.get(s); + ldaTS.get(s); + deTS.get(s); + ldeTS.get(s); + + return s.good(); + } +}; + +class ColoredLitPerf: public BaseTest<VPResult> { +public: + GLEAN_CLASS_WHO(ColoredLitPerf, VPResult, + drawingSize, drawingSize, true); + void logStats(VPResult& r, GLEAN::Environment* env); +}; // class ColoredLitPerf + +class ColoredTexPerf: public BaseTest<VPResult> { +public: + GLEAN_CLASS_WHO(ColoredTexPerf, VPResult, + drawingSize, drawingSize, true); + void logStats(VPResult& r, GLEAN::Environment* env); +}; // class ColoredTexPerf + +} // namespace GLEAN + +#endif // __tvtxperf_h__ diff --git a/tests/glean/unpack.cpp b/tests/glean/unpack.cpp new file mode 100644 index 00000000..5c0c9a6f --- /dev/null +++ b/tests/glean/unpack.cpp @@ -0,0 +1,271 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Data unpacking utilities. Note that these map component values per +// the usual OpenGL conventions. + +// XXX The construction of SCALE and BIAS is clumsy, and the need to +// test bias is really unfortunate, but egcs 1.1.2 won't propagate +// floating-point constant expressions from equivalent const +// declarations. + +#include "image.h" + +namespace { + +#define SCALE (static_cast<double>(num) / static_cast<double>(denom)) +#define BIAS (static_cast<double>(bias) / static_cast<double>(denom)) + +// See comments in pack.cpp concerning this workaround for a VC6 problem. + +template<class component, int num, unsigned int denom, int bias> +class Unpack +{ +public : + // unpack_l + static void unpack_l(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + // XXX It seems to me that static_cast should be sufficient, + // but egcs 1.1.2 thinks otherwise. + + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) + rgba[0] = SCALE * in[0] + BIAS; + else + rgba[0] = SCALE * in[0]; + rgba[1] = rgba[2] = rgba[3] = 0.0; + in += 1; + } + } + + // unpack_la + static void unpack_la(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[3] = SCALE * in[1] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[3] = SCALE * in[1]; + } + rgba[1] = rgba[2] = 0.0; + in += 2; + } + } + + // unpack_rgb + static void unpack_rgb(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[1] = SCALE * in[1] + BIAS; + rgba[2] = SCALE * in[2] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[1] = SCALE * in[1]; + rgba[2] = SCALE * in[2]; + } + rgba[3] = 0.0; + in += 3; + } + } + + // unpack_rgba + static void unpack_rgba(GLsizei n, double* rgba, char* src) + { + component* in = reinterpret_cast<component*>(src); + double* end = rgba + 4 * n; + for (; rgba != end; rgba += 4) { + if (bias) { + rgba[0] = SCALE * in[0] + BIAS; + rgba[1] = SCALE * in[1] + BIAS; + rgba[2] = SCALE * in[2] + BIAS; + rgba[3] = SCALE * in[3] + BIAS; + } else { + rgba[0] = SCALE * in[0]; + rgba[1] = SCALE * in[1]; + rgba[2] = SCALE * in[2]; + rgba[3] = SCALE * in[3]; + } + in += 4; + } + } + +}; // class Unpack + +#undef SCALE +#undef BIAS + +}; // anonymous namespace + + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// Public interface +/////////////////////////////////////////////////////////////////////////////// +void +Image::unpack(GLsizei n, double* rgba, char* nextPixel) { + (*(valid(vbUnpacker)? _unpacker: validateUnpacker())) + (n, rgba, nextPixel); +} + +/////////////////////////////////////////////////////////////////////////////// +// validateUnpacker - select appropriate pixel-unpacking utility +/////////////////////////////////////////////////////////////////////////////// +Image::Unpacker* +Image::validateUnpacker() { + switch (format()) { + case GL_LUMINANCE: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_l; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_l; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_l; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_l; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_l; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_l; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_l; + break; + default: + throw BadType(type()); + } + break; + case GL_LUMINANCE_ALPHA: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_la; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_la; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_la; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_la; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_la; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 2, 4294967295U, 0>::unpack_la; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_la; + break; + default: + throw BadType(type()); + } + break; + case GL_RGB: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_rgb; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_rgb; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_rgb; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_rgb; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_rgb; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_rgb; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_rgb; + break; + default: + throw BadType(type()); + } + break; + case GL_RGBA: + switch (type()) { + case GL_BYTE: + _unpacker = Unpack<GLbyte, 2, 255, 1>::unpack_rgba; + break; + case GL_UNSIGNED_BYTE: + _unpacker = Unpack<GLubyte, 1, 255, 0>::unpack_rgba; + break; + case GL_SHORT: + _unpacker = Unpack<GLshort, 2, 65535, 1>::unpack_rgba; + break; + case GL_UNSIGNED_SHORT: + _unpacker = Unpack<GLushort, 1, 65535, 0>::unpack_rgba; + break; + case GL_INT: + _unpacker = Unpack<GLint, 2, 4294967295U, 1>::unpack_rgba; + break; + case GL_UNSIGNED_INT: + _unpacker = Unpack<GLuint, 1, 4294967295U, 0>::unpack_rgba; + break; + case GL_FLOAT: + _unpacker = Unpack<GLfloat, 1, 1, 0>::unpack_rgba; + break; + default: + throw BadType(type()); + } + break; + default: + throw BadFormat(format()); + } + + validate(vbUnpacker); + return _unpacker; +} + +}; // namespace GLEAN diff --git a/tests/glean/version.h b/tests/glean/version.h new file mode 100644 index 00000000..c9706890 --- /dev/null +++ b/tests/glean/version.h @@ -0,0 +1,47 @@ +// 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 + + + + +// GLEAN version information + + +#ifndef __version_h__ +#define __version_h__ + +#define GLEAN_MAJOR_VERSION 1 +#define GLEAN_MINOR_VERSION 1 + +namespace GLEAN { + +const char* versionString = "glean v1.1 20 January 2000"; + +} // namespace GLEAN + +#endif // __version_h__ diff --git a/tests/glean/winsys.cpp b/tests/glean/winsys.cpp new file mode 100644 index 00000000..e91b4eea --- /dev/null +++ b/tests/glean/winsys.cpp @@ -0,0 +1,273 @@ +// 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 + + + + +// winsys.cpp: implementation of window-system services class + +using namespace std; + +#include <iostream> +#include "options.h" +#include "winsys.h" +#include "dsconfig.h" +#include "dsfilt.h" +#include "dsurf.h" +#include "rc.h" + +namespace GLEAN { + + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// +#if defined(__X11__) +WindowSystem::WindowSystem(Options& o) { + // If running in "compare" mode we never actually use the window + // system, so we don't initialize it here. This allows us to run + // on systems without graphics hardware/software. + if (o.mode == Options::compare) { + dpy = 0; + GLXVersMajor = GLXVersMinor = 0; + vip = 0; + return; + } + + // Open the X11 display: + dpy = XOpenDisplay(o.dpyName.c_str()); + if (!dpy) + throw CantOpenDisplay(); + + // Verify that GLX is supported: + int error_base, event_base; + if (glXQueryExtension(dpy, &error_base, &event_base) == False) + throw NoOpenGL(); + + // Record version numbers for later use: + if (glXQueryVersion(dpy, &GLXVersMajor, &GLXVersMinor) == False) + throw Error(); // this should never happen :-) + + // Get the list of raw XVisualInfo structures: + XVisualInfo vit; + vit.screen = DefaultScreen(dpy); + int n; + vip = XGetVisualInfo(dpy, VisualScreenMask, &vit, &n); + + // Construct a vector of DrawingSurfaceConfigs corresponding to the + // XVisualInfo structures that indicate they support OpenGL: + vector<DrawingSurfaceConfig*> glxv; + for (int i = 0; i < n; ++i) { + int supportsOpenGL; + glXGetConfig(dpy, &vip[i], GLX_USE_GL, &supportsOpenGL); + if (supportsOpenGL) + glxv.push_back(new DrawingSurfaceConfig (dpy, &vip[i])); + } + + // Filter the basic list of DrawingSurfaceConfigs according to + // constraints provided by the user. (This makes it convenient + // to run tests on just a subset of all available configs.) + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glxv); +} // WindowSystem::WindowSystem + +#elif defined(__WIN__) +WindowSystem::WindowSystem(Options& o) { + // register an window class + WNDCLASS wc; + + wc.style = CS_OWNDC; + wc.lpfnWndProc = Window::WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(NULL); + wc.hIcon = LoadIcon(wc.hInstance, "glean"); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = "glean"; + + if (!RegisterClass(&wc)) + throw Error(); + + + HDC hDC = GetDC(GetDesktopWindow()); + + PIXELFORMATDESCRIPTOR pfd; + int n = DescribePixelFormat(hDC,0,sizeof(pfd),0); + + vector<DrawingSurfaceConfig*> glpf; + + for (int i = 1;i <= n;++i) { + DescribePixelFormat(hDC,i,sizeof(pfd),&pfd); + + glpf.push_back(new DrawingSurfaceConfig(i,&pfd)); + } + + ReleaseDC(GetDesktopWindow(),hDC); + + // Filter the basic list of DrawingSurfaceConfigs according to + // constraints provided by the user. (This makes it convenient + // to run tests on just a subset of all available configs.) + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glpf); +} + +#elif defined(__BEWIN__) +WindowSystem::WindowSystem(Options& o) { + //cout << "Implement Me! WindowSystem::WindowSystem(Options& o)\n"; + + theApp = new BApplication("application/x-AJH-glean"); + + /* for BeOS, we just stack the current config onto the vector so */ + /* there is at least one thing to iterate over */ + vector<DrawingSurfaceConfig*> glconfigs; + glconfigs.push_back(new DrawingSurfaceConfig()); + + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glconfigs); + +} + +#elif defined(__AGL__) +WindowSystem::WindowSystem(Options& o) { + GDHandle mainGD; + //HW/SW Depth Reserved + GLint testTypes[][3] = { + {AGL_ACCELERATED, 16, 0 }, + {AGL_ACCELERATED, 16, 0 }, + {AGL_ACCELERATED, 16, 0 }, + {0, 16, 0 }, + {0, 16, 0 }, + {0, 0, 0 } + }; + GLint testAttrib[][10]= { + { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_ACCELERATED, 16, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE}, + { AGL_RGBA, AGL_ACCELERATED, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE}, + { AGL_RGBA, AGL_DOUBLEBUFFER, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE}, + { AGL_RENDERER_ID, AGL_RENDERER_GENERIC_ID, AGL_RGBA, AGL_DOUBLEBUFFER, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE}, + { AGL_RENDERER_ID, AGL_RENDERER_GENERIC_ID, AGL_RGBA, AGL_DOUBLEBUFFER, AGL_DEPTH_SIZE, 16 , AGL_NONE, AGL_NONE, AGL_NONE, AGL_NONE} + }; + AGLPixelFormat pf; + GLint index = 0; + + mainGD = GetMainDevice(); + if (!mainGD) + throw CantOpenDisplay(); + + // Construct a vector of DrawingSurfaceConfigs corresponding to the + // returned pixel formats + vector<DrawingSurfaceConfig*> glpf; + + while (testTypes[index][1] != 0) + { + pf = aglChoosePixelFormat(&mainGD, 1, testAttrib[index]); + if ( (pf == NULL) && ( testTypes[index][0] == 0) ) + { + testAttrib[index][1] = 0x30300; + pf = aglChoosePixelFormat(&mainGD, 1, testAttrib[index]); + } + if (pf != NULL) glpf.push_back(new DrawingSurfaceConfig (index+1, pf)); + + index++; + } + + // Filter the basic list of DrawingSurfaceConfigs according to + // constraints provided by the user. (This makes it convenient + // to run tests on just a subset of all available configs.) + DrawingSurfaceFilter f(o.visFilter); // may throw an exception! + surfConfigs = f.filter(glpf); +} + +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Destructors +/////////////////////////////////////////////////////////////////////////////// +#if defined(__X11__) +WindowSystem::~WindowSystem() { + XFree(vip); +} // WindowSystem:: ~WindowSystem + +#elif defined(__WIN__) +WindowSystem::~WindowSystem() { +} +#elif defined(__BEWIN__) +WindowSystem::~WindowSystem() { + delete theApp; +} // WindowSystem:: ~WindowSystem +#elif defined(__AGL__) +WindowSystem::~WindowSystem() { +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// makeCurrent and friends - binding contexts and drawing surfaces +/////////////////////////////////////////////////////////////////////////////// + +bool +WindowSystem::makeCurrent() { +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) + // XXX Need to write GLX 1.3 MakeCurrent code +# endif + return glXMakeCurrent(dpy, None, 0); +# elif defined(__WIN__) + return wglMakeCurrent(0,0); +# elif defined(__AGL__) + return aglSetCurrentContext(NULL); +# endif +} // WindowSystem::makeCurrent + +bool +WindowSystem::makeCurrent(RenderingContext& r, Window& w) { +# if defined(__X11__) +# if defined(GLX_VERSION_1_3) + // XXX Need to write GLX 1.3 MakeCurrent code +# endif + return glXMakeCurrent(dpy, w.xWindow, r.rc); +# elif defined(__WIN__) + return wglMakeCurrent(w.get_dc(),r.rc); +# elif defined(__AGL__) + if (GL_FALSE == aglSetDrawable(r.rc, (AGLDrawable) GetWindowPort (w.macWindow))) + return GL_FALSE; + if (GL_FALSE == aglSetCurrentContext(r.rc)) + return GL_FALSE; + return true; +# endif +} // WindowSystem::makeCurrent + +void +WindowSystem::quiesce() { +# if defined(__X11__) + XSync(dpy, False); +# elif defined(__WIN__) +# endif +} // WindowSystem::quiesce + +} // namespace GLEAN diff --git a/tests/glean/winsys.h b/tests/glean/winsys.h new file mode 100644 index 00000000..595fb64f --- /dev/null +++ b/tests/glean/winsys.h @@ -0,0 +1,117 @@ +// 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 + + + + +// winsys.h: facade for common window-system operations + +// This class and related classes provide window system operations +// that are sufficient to support most basic rendering tests. These +// operations include initializing the window system, creating and +// destroying windows and rendering contexts, selecting pixel +// configurations, etc. + +// Tests using this set of classes for all window system services are +// ``portable'' in a useful sense. Not all tests are portable, +// however; in particular, tests of window-system-specific +// functionality must execute window system commands directly. Such +// tests may require access to class members that would ideally be +// private; for example, the X11 Display pointer. Thus most members +// of this class are public. + + + +#ifndef __winsys_h__ +#define __winsys_h__ + +using namespace std; + +#include <string> +#include <vector> +#include "glwrap.h" + +namespace GLEAN { + +class DrawingSurface; // Forward and mutually-recursive references. +class Window; +class DrawingSurfaceConfig; +class RenderingContext; +class Options; + +class WindowSystem { + public: + // Constructors/Destructor: + + WindowSystem(Options& o); + ~WindowSystem(); + + // Exceptions: + + struct Error { }; // Base class for window system errors. + struct CantOpenDisplay: public Error { // Can't initialize display. + }; + struct NoOpenGL: public Error { // Missing GLX, WGL, etc. + }; + + // Utilities: + + bool makeCurrent(); // Remove context/surface binding. + bool makeCurrent(RenderingContext& r, Window& w); + // Bind given context and surface. + void quiesce(); // Wait for system to go idle. + + // State information: + + vector<DrawingSurfaceConfig*> surfConfigs; + // All available drawing surface configurations. + vector<DrawingSurface*> surfaces; + // All currently-active surfaces. + vector<RenderingContext*> contexts; + // All currently-active rendering contexts. + +# if defined(__X11__) + Display* dpy; // Pointer to X11 Display structure. + + int GLXVersMajor; // GLX major version number. + int GLXVersMinor; // GLX minor version number. + + XVisualInfo* vip; // Array of raw XVisualInfo structures. + +# elif defined(__WIN__) + +# elif defined(__BEWIN__) + BApplication *theApp; + +# endif + +}; // class WindowSystem + +} // namespace GLEAN + +#endif // __winsys_h__ diff --git a/tests/glean/wrtiff.cpp b/tests/glean/wrtiff.cpp new file mode 100644 index 00000000..f4b95bac --- /dev/null +++ b/tests/glean/wrtiff.cpp @@ -0,0 +1,134 @@ +// BEGIN_COPYRIGHT +// +// 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 + + + + +// Implementation of image data, attribute, and I/O + +#include "image.h" +#include "tiffio.h" + +namespace GLEAN { + +/////////////////////////////////////////////////////////////////////////////// +// writeTIFF - write image to TIFF file +/////////////////////////////////////////////////////////////////////////////// +void +Image::writeTIFF(const char* filename) { + static uint16 unassocAlpha[] = {EXTRASAMPLE_UNASSALPHA}; + GLsizei rowStep = rowSizeInBytes(); + + TIFF* tf = TIFFOpen(filename, "w"); + if (!tf) + throw CantOpen(filename); + + TIFFSetField(tf, TIFFTAG_IMAGELENGTH, height()); + TIFFSetField(tf, TIFFTAG_IMAGEWIDTH, width()); + TIFFSetField(tf, TIFFTAG_XRESOLUTION, 100.0); + TIFFSetField(tf, TIFFTAG_YRESOLUTION, 100.0); + TIFFSetField(tf, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); + TIFFSetField(tf, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField(tf, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tf, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + // LZW would have been acceptable, were it not for patent + // issues. + TIFFSetField(tf, TIFFTAG_ROWSPERSTRIP, height()); + + switch (format()) { + case GL_LUMINANCE: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 1); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + break; + case GL_LUMINANCE_ALPHA: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 2); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + TIFFSetField(tf, TIFFTAG_EXTRASAMPLES, 1, unassocAlpha); + break; + case GL_RGB: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + break; + case GL_RGBA: + TIFFSetField(tf, TIFFTAG_SAMPLESPERPIXEL, 4); + TIFFSetField(tf, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField(tf, TIFFTAG_EXTRASAMPLES, 1, unassocAlpha); + break; + default: + TIFFClose(tf); + throw BadFormat(format()); + } + + switch (type()) { + case GL_BYTE: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_BYTE: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_SHORT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_SHORT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_INT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case GL_UNSIGNED_INT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case GL_FLOAT: + TIFFSetField(tf, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tf, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + break; + default: + TIFFClose(tf); + throw BadType(type()); + } + + { + // Write rows in reverse order, so that the usual OpenGL + // orientation won't result in an upside-down image for + // naive TIFF readers: + char* row = pixels() + (height() - 1) * rowStep; + for (GLsizei r = 0; r < height(); ++r, row -= rowStep) + TIFFWriteScanline(tf, row, r, 0); + } + + TIFFClose(tf); +}; // Image::writeTIFF + + +}; // namespace GLEAN |