aboutsummaryrefslogtreecommitdiff
path: root/tests/glean
diff options
context:
space:
mode:
authorNicolai Hähnle <nh@annarchy.freedesktop.org>2007-03-24 17:20:31 -0700
committerNicolai Hähnle <nh@annarchy.freedesktop.org>2007-03-24 17:20:31 -0700
commit22fbbb5e8679ddf91532cb10eaa31be533383b48 (patch)
treecb43f83fe997f166f297dad671ddf04e2f601be4 /tests/glean
Initial commit
Diffstat (limited to 'tests/glean')
-rw-r--r--tests/glean/CMakeLists.txt69
-rw-r--r--tests/glean/basic.cpp64
-rw-r--r--tests/glean/codedid.cpp146
-rw-r--r--tests/glean/codedid.h92
-rw-r--r--tests/glean/dsconfig.cpp881
-rw-r--r--tests/glean/dsconfig.h210
-rw-r--r--tests/glean/dsfilt.cpp690
-rw-r--r--tests/glean/dsfilt.h268
-rw-r--r--tests/glean/dsurf.cpp263
-rw-r--r--tests/glean/dsurf.h103
-rw-r--r--tests/glean/environ.cpp161
-rw-r--r--tests/glean/environ.h111
-rw-r--r--tests/glean/geomrend.cpp504
-rw-r--r--tests/glean/geomrend.h146
-rw-r--r--tests/glean/geomutil.cpp360
-rw-r--r--tests/glean/geomutil.h100
-rw-r--r--tests/glean/gl.cpp82
-rw-r--r--tests/glean/glutils.cpp325
-rw-r--r--tests/glean/glutils.h111
-rw-r--r--tests/glean/glwrap.h784
-rw-r--r--tests/glean/image.h250
-rw-r--r--tests/glean/image_misc.cpp210
-rw-r--r--tests/glean/lex.cpp167
-rw-r--r--tests/glean/lex.h128
-rw-r--r--tests/glean/main.cpp347
-rw-r--r--tests/glean/misc.cpp82
-rw-r--r--tests/glean/misc.h50
-rw-r--r--tests/glean/options.cpp66
-rw-r--r--tests/glean/options.h97
-rw-r--r--tests/glean/pack.cpp262
-rw-r--r--tests/glean/rand.h125
-rw-r--r--tests/glean/rc.cpp159
-rw-r--r--tests/glean/rc.h68
-rw-r--r--tests/glean/rdtiff.cpp156
-rw-r--r--tests/glean/reg.cpp176
-rw-r--r--tests/glean/stats.h91
-rw-r--r--tests/glean/tbase.h414
-rw-r--r--tests/glean/tbasic.cpp68
-rw-r--r--tests/glean/tbasic.h69
-rw-r--r--tests/glean/tbasicperf.cpp189
-rw-r--r--tests/glean/tbasicperf.h85
-rw-r--r--tests/glean/tbinding.cpp199
-rw-r--r--tests/glean/tbinding.h73
-rw-r--r--tests/glean/tblend.cpp621
-rw-r--r--tests/glean/tblend.h69
-rw-r--r--tests/glean/tchgperf.cpp317
-rw-r--r--tests/glean/tchgperf.h72
-rw-r--r--tests/glean/tdepthstencil.cpp422
-rw-r--r--tests/glean/tdepthstencil.h80
-rw-r--r--tests/glean/test.cpp134
-rw-r--r--tests/glean/test.h152
-rw-r--r--tests/glean/tfpexceptions.cpp602
-rw-r--r--tests/glean/tfpexceptions.h78
-rw-r--r--tests/glean/tfragprog1.cpp1056
-rw-r--r--tests/glean/tfragprog1.h94
-rw-r--r--tests/glean/tgetstr.cpp167
-rw-r--r--tests/glean/tgetstr.h75
-rw-r--r--tests/glean/timer.cpp232
-rw-r--r--tests/glean/timer.h74
-rw-r--r--tests/glean/tlogicop.cpp573
-rw-r--r--tests/glean/tlogicop.h66
-rw-r--r--tests/glean/tmaskedclear.cpp266
-rw-r--r--tests/glean/tmaskedclear.h62
-rw-r--r--tests/glean/tmultitest.cpp109
-rw-r--r--tests/glean/tmultitest.h77
-rw-r--r--tests/glean/torthpos.cpp1159
-rw-r--r--tests/glean/torthpos.h103
-rw-r--r--tests/glean/tpaths.cpp373
-rw-r--r--tests/glean/tpaths.h79
-rw-r--r--tests/glean/tpgos.cpp735
-rw-r--r--tests/glean/tpgos.h124
-rw-r--r--tests/glean/tpixelformats.cpp1405
-rw-r--r--tests/glean/tpixelformats.h85
-rw-r--r--tests/glean/tpointatten.cpp259
-rw-r--r--tests/glean/tpointatten.h78
-rw-r--r--tests/glean/treadpix.cpp970
-rw-r--r--tests/glean/treadpix.h322
-rw-r--r--tests/glean/treadpixperf.cpp548
-rw-r--r--tests/glean/treadpixperf.h88
-rw-r--r--tests/glean/trgbtris.cpp196
-rw-r--r--tests/glean/trgbtris.h63
-rw-r--r--tests/glean/tscissor.cpp188
-rw-r--r--tests/glean/tscissor.h52
-rw-r--r--tests/glean/tteapot.cpp3215
-rw-r--r--tests/glean/tteapot.h63
-rw-r--r--tests/glean/ttexcombine.cpp1584
-rw-r--r--tests/glean/ttexcombine.h135
-rw-r--r--tests/glean/ttexcube.cpp426
-rw-r--r--tests/glean/ttexcube.h65
-rw-r--r--tests/glean/ttexenv.cpp667
-rw-r--r--tests/glean/ttexenv.h68
-rw-r--r--tests/glean/ttexgen.cpp371
-rw-r--r--tests/glean/ttexgen.h67
-rw-r--r--tests/glean/ttexrect.cpp217
-rw-r--r--tests/glean/ttexrect.h61
-rw-r--r--tests/glean/ttexture_srgb.cpp373
-rw-r--r--tests/glean/ttexture_srgb.h73
-rw-r--r--tests/glean/tvertattrib.cpp1620
-rw-r--r--tests/glean/tvertattrib.h93
-rw-r--r--tests/glean/tvertprog1.cpp1042
-rw-r--r--tests/glean/tvertprog1.h85
-rw-r--r--tests/glean/tvtxperf.cpp1424
-rw-r--r--tests/glean/tvtxperf.h147
-rw-r--r--tests/glean/unpack.cpp271
-rw-r--r--tests/glean/version.h47
-rw-r--r--tests/glean/winsys.cpp273
-rw-r--r--tests/glean/winsys.h117
-rw-r--r--tests/glean/wrtiff.cpp134
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