summaryrefslogtreecommitdiff
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
Initial commit
-rw-r--r--CMakeLists.txt8
-rw-r--r--HACKING85
-rw-r--r--README146
-rw-r--r--framework/__init__.py22
-rw-r--r--framework/core.py350
-rwxr-xr-xpiglit-run.py101
-rwxr-xr-xpiglit-summary-html.py414
-rw-r--r--templates/index.css89
-rw-r--r--templates/index.html40
-rw-r--r--templates/index_group.html6
-rw-r--r--templates/index_group_testrun.html1
-rw-r--r--templates/index_groupgroup.html1
-rw-r--r--templates/index_test.html5
-rw-r--r--templates/index_test_testrun.html1
-rw-r--r--templates/index_testrun.html1
-rw-r--r--templates/index_testrunb.html1
-rw-r--r--templates/result.css35
-rw-r--r--templates/result.html28
-rw-r--r--templates/result_detail.html4
-rw-r--r--templates/result_list.html3
-rw-r--r--templates/result_listitem.html1
-rw-r--r--templates/result_mstring.html1
-rw-r--r--tests/CMakeLists.txt5
-rw-r--r--tests/all.tests97
-rw-r--r--tests/bugs/CMakeLists.txt21
-rw-r--r--tests/bugs/fdo10370.c178
-rw-r--r--tests/bugs/fdo9833.c51
-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
-rw-r--r--tests/mesa/CMakeLists.txt3
-rw-r--r--tests/mesa/tests/CMakeLists.txt23
-rw-r--r--tests/mesa/tests/afsmultiarb.c469
-rw-r--r--tests/mesa/tests/antialias.c229
-rw-r--r--tests/mesa/tests/api_speed.c146
-rw-r--r--tests/mesa/tests/arbfpspec.c192
-rw-r--r--tests/mesa/tests/arbfptest1.c210
-rw-r--r--tests/mesa/tests/arbfptexture.c153
-rw-r--r--tests/mesa/tests/arbfptrig.c156
-rw-r--r--tests/mesa/tests/arbnpot-mipmap.c184
-rw-r--r--tests/mesa/tests/arbnpot.c174
-rw-r--r--tests/mesa/tests/arbvptest1.c164
-rw-r--r--tests/mesa/tests/arbvptest3.c127
-rw-r--r--tests/mesa/tests/arbvptorus.c186
-rw-r--r--tests/mesa/tests/arbvpwarpmesh.c246
-rw-r--r--tests/mesa/tests/auxbuffer.c499
-rw-r--r--tests/mesa/tests/blendminmax.c209
-rw-r--r--tests/mesa/tests/blendsquare.c178
-rw-r--r--tests/mesa/tests/bufferobj.c371
-rw-r--r--tests/mesa/tests/bug_3050.c162
-rw-r--r--tests/mesa/tests/bug_3101.c128
-rw-r--r--tests/mesa/tests/bug_3195.c275
-rw-r--r--tests/mesa/tests/copypixrate.c259
-rw-r--r--tests/mesa/tests/crossbar.c305
-rw-r--r--tests/mesa/tests/cva.c164
-rw-r--r--tests/mesa/tests/debugger.c733
-rw-r--r--tests/mesa/tests/dinoshade.c914
-rw-r--r--tests/mesa/tests/ext422square.c258
-rw-r--r--tests/mesa/tests/fbotest1.c206
-rw-r--r--tests/mesa/tests/fbotest2.c199
-rw-r--r--tests/mesa/tests/fbotexture.c407
-rw-r--r--tests/mesa/tests/floattex.c169
-rw-r--r--tests/mesa/tests/fog.c199
-rw-r--r--tests/mesa/tests/fogcoord.c102
-rw-r--r--tests/mesa/tests/fptest1.c225
-rw-r--r--tests/mesa/tests/fptexture.c151
-rw-r--r--tests/mesa/tests/getprocaddress.c530
-rw-r--r--tests/mesa/tests/interleave.c406
-rw-r--r--tests/mesa/tests/invert.c195
-rw-r--r--tests/mesa/tests/jkrahntest.c181
-rw-r--r--tests/mesa/tests/manytex.c382
-rw-r--r--tests/mesa/tests/mipmap_limits.c252
-rw-r--r--tests/mesa/tests/multipal.c377
-rw-r--r--tests/mesa/tests/multitexarray.c238
-rw-r--r--tests/mesa/tests/multiwindow.c169
-rw-r--r--tests/mesa/tests/no_s3tc.c97
-rw-r--r--tests/mesa/tests/packedpixels.c342
-rw-r--r--tests/mesa/tests/pbo.c296
-rw-r--r--tests/mesa/tests/prog_parameter.c285
-rw-r--r--tests/mesa/tests/projtex.c1028
-rw-r--r--tests/mesa/tests/readrate.c285
-rw-r--r--tests/mesa/tests/seccolor.c145
-rw-r--r--tests/mesa/tests/sharedtex.c440
-rw-r--r--tests/mesa/tests/stencil_wrap.c257
-rw-r--r--tests/mesa/tests/stencilwrap.c281
-rw-r--r--tests/mesa/tests/subtexrate.c350
-rw-r--r--tests/mesa/tests/tex1d.c139
-rw-r--r--tests/mesa/tests/texcmp.c414
-rw-r--r--tests/mesa/tests/texcompress2.c273
-rw-r--r--tests/mesa/tests/texfilt.c398
-rw-r--r--tests/mesa/tests/texgenmix.c640
-rw-r--r--tests/mesa/tests/texline.c269
-rw-r--r--tests/mesa/tests/texobjshare.c219
-rw-r--r--tests/mesa/tests/texrect.c360
-rw-r--r--tests/mesa/tests/texwrap.c304
-rw-r--r--tests/mesa/tests/vao-01.c177
-rw-r--r--tests/mesa/tests/vao-02.c205
-rw-r--r--tests/mesa/tests/vparray.c294
-rw-r--r--tests/mesa/tests/vpeval.c231
-rw-r--r--tests/mesa/tests/vptest1.c170
-rw-r--r--tests/mesa/tests/vptest2.c151
-rw-r--r--tests/mesa/tests/vptest3.c120
-rw-r--r--tests/mesa/tests/vptorus.c174
-rw-r--r--tests/mesa/tests/vpwarpmesh.c236
-rw-r--r--tests/mesa/tests/yuvrect.c193
-rw-r--r--tests/mesa/tests/yuvsquare.c232
-rw-r--r--tests/mesa/tests/zreaddraw.c116
-rw-r--r--tests/mesa/util/CMakeLists.txt21
-rw-r--r--tests/mesa/util/dumpstate.c1958
-rw-r--r--tests/mesa/util/errcheck.c27
-rw-r--r--tests/mesa/util/glstate.c506
-rw-r--r--tests/mesa/util/glstate.h53
-rw-r--r--tests/mesa/util/glutskel.c139
-rw-r--r--tests/mesa/util/idproj.c26
-rw-r--r--tests/mesa/util/imagesgi.cpp369
-rw-r--r--tests/mesa/util/imagesgi.h55
-rw-r--r--tests/mesa/util/matrix.c181
-rw-r--r--tests/mesa/util/mwmborder.c91
-rw-r--r--tests/mesa/util/readtex.c454
-rw-r--r--tests/mesa/util/readtex.h26
-rw-r--r--tests/mesa/util/showbuffer.c192
-rw-r--r--tests/mesa/util/showbuffer.h36
-rw-r--r--tests/mesa/util/trackball.c338
-rw-r--r--tests/mesa/util/trackball.h84
-rw-r--r--tests/mesa/util/winpos.c43
-rw-r--r--tests/r300.tests26
-rw-r--r--tests/sanity.tests10
-rw-r--r--tests/shaders/CMakeLists.txt24
-rw-r--r--tests/shaders/fp-fragment-position.c498
-rw-r--r--tests/shaders/fp-incomplete-tex.c367
-rw-r--r--tests/shaders/fp-kil.c465
-rw-r--r--tests/shaders/fp-lit-mask.c282
-rw-r--r--tests/shaders/trinity-fp1.c438
238 files changed, 62894 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..d04be207c
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,8 @@
+
+project (piglit)
+
+include(${CMAKE_ROOT}/Modules/FindOpenGL.cmake)
+include(${CMAKE_ROOT}/Modules/FindTIFF.cmake)
+include(${CMAKE_ROOT}/Modules/FindGLUT.cmake)
+
+add_subdirectory (tests)
diff --git a/HACKING b/HACKING
new file mode 100644
index 000000000..46c78c5a2
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,85 @@
+
+
+\ Initial design decisions
+ -------------------------
+
+Before I started working on Piglit, I asked around for OpenGL testing methods.
+There were basically two kinds of tests:
+
+1. Glean, which is fully automatic and intends to test the letter of the
+ OpenGL specification (at least partially).
+
+2. Manual tests using Mesa samples or third-party software.
+
+The weakness of Glean is that it is not flexible, not pragmatic enough for
+driver development. For example, it tests for precision requirements in
+blending, where a driver just cannot reasonably improve on what the hardware
+delivers. As a driver developer, one wants to consider a test successful
+when it reaches the optimal results that the hardware can give, even when
+these results may be non-compliant.
+
+Manual tests are not well repeatable. They require a considerable amount of
+work on the part of the developer, so most of the time they aren't done at all.
+On the other hand, those manual tests have sometimes been created to test for
+a particular weakness in implementations, so they may be very suitable to
+detect future, similar weaknesses.
+
+Due to these weaknesses, the test coverage of open source OpenGL drivers
+is suboptimal at best. My goal for Piglit is to create a useful test system
+that helps driver developers in improving driver quality.
+
+With that in mind, my sub-goals are:
+
+1. Combine the strengths of the two kinds of tests (Glean, manual tests)
+ into a single framework.
+
+2. Provide concise, human readable summaries of the test results, with the
+ option to increase the detail of the report when desired.
+
+3. Allow easy visualization of regressions.
+
+4. Allow easy detection of performance regressions.
+
+I briefly considered extending Glean, but then decided for creating an
+entirely new project. The most important reasons are:
+
+1. I do not want to pollute the very clean philosophy behind Glean.
+
+2. The model behind Glean is that one process runs all the tests.
+ During driver development, one often gets bugs that cause tests to crash.
+ This means that one failed test can disrupt the entire test batch.
+ I want to use a more robust model, where each test runs in its own process.
+ This model does not easily fit onto Glean.
+
+3. The amount of code duplication and forking overhead is minimal because
+ a) I can use Glean as a "subroutine", so no code is actually duplicated,
+ there's just a tiny amount of additional Python glue code.
+ b) It's unlikely that this project makes significant changes to Glean
+ that need to be merged upstream.
+
+4. While it remains reasonable to use C++ for the actual OpenGL tests,
+ it is easier to use a higher level language like Python for the framework
+ (summary processing etc.)
+
+
+
+
+\ Ugly Things (or: Coding style)
+ -------------------------------
+
+As a rule of thumb, coding style should be preserved in test code taken from
+other projects, as long as that code is self-contained.
+
+Apart from that, the following rules are cast in stone:
+
+1. Use tabulators for indentation
+2. Use spaces for alignment
+3. No whitespace at the end of a line
+
+See http://electroly.com/mt/archives/000002.html for a well-written rationale.
+
+Use whatever tabulator size you want:
+If you adhere to the rules above, the tab size does not matter. Tab size 4
+is recommended because it keeps the line lengths reasonable, but in the end,
+that's purely a matter of personal taste.
+
diff --git a/README b/README
new file mode 100644
index 000000000..bf927e5f2
--- /dev/null
+++ b/README
@@ -0,0 +1,146 @@
+
+Piglit
+------
+1. About
+2. Setup
+3. How to run tests
+4. How to write tests
+5. Todo
+
+
+1. About
+--------
+
+Piglit is a collection of automated tests for OpenGL implementations.
+
+The goal of Piglit is to help improve the quality of open source
+OpenGL drivers by providing developers with a simple means to
+perform regression tests.
+
+The original tests have been taken from
+- Glean ( http://glean.sf.net/ ) and
+- Mesa ( http://www.mesa3d.org/ )
+
+
+2. Setup
+--------
+
+First of all, you need to make sure that the following are installed:
+
+ - Python 2.4 or greater
+ - cmake (http://www.cmake.org)
+ - GL, glu and glut libraries and development packages (i.e. headers)
+ - X11 libraries and development packages (i.e. headers)
+ - libtiff
+
+Now configure the build system:
+
+ $ ccmake .
+
+This will start cmake's configuration tool, just follow the onscreen
+instructions. The default settings should be fine, but I recommend you:
+ - Press 'c' once (this will also check for dependencies) and then
+ - Set "CMAKE_BUILD_TYPE" to "Debug"
+Now you can press 'c' again and then 'g' to generate the build system.
+Now build everything:
+
+ $ make
+
+
+
+3. How to run tests
+-------------------
+
+Make sure that everything is set up correctly:
+
+ $ ./piglit-run.py tests/sanity.tests results/sanity.results
+
+This will run some minimal tests. Use
+
+ $ ./piglit-run.py
+
+to learn more about the command's syntax. Have a look into the tests/
+directory to see what test profiles are available:
+
+ $ cd tests
+ $ ls *.tests
+ ...
+ $ cd ..
+
+To create some nice formatted test summaries, run
+
+ $ ./piglit-summary-html.py summary/sanity results/sanity.results
+
+Hint: You can combine multiple test results into a single summary.
+ During development, you can use this to watch for regressions:
+
+ $ ./piglit-summary-html.py summary/compare results/baseline.results results/current.results
+
+ You can combine as many testruns as you want this way(in theory;
+ the HTML layout becomes awkward when the number of testruns increases)
+
+Have a look at the results with a browser:
+
+ $ xdg-open summary/sanity/index.html
+
+The summary shows the 'status' of a test:
+
+ pass This test has completed successfully.
+
+ warn The test completed successfully, but something unexpected happened.
+ Look at the details for more information.
+
+ fail The test failed.
+
+ skip The test was skipped.
+
+[Note: Once performance tests are implemented, 'fail' will mean that the test
+ rendered incorrectly or didn't complete, while 'warn' will indicate a
+ performance regression]
+[Note: For performance tests, result and status will be different concepts.
+ While status is always restricted to one of the four values above,
+ the result can contain a performance number like frames per second]
+
+
+4. How to write tests
+---------------------
+
+Every test is run as a separate process. This minimizes the impact that
+severe bugs like memory corruption have on the testing process.
+
+Therefore, tests can be implemented in an arbitrary standalone language.
+I recommend C, C++ and Python, as these are the languages that are already
+used in Piglit.
+
+All new tests must be added to the all.tests profile. The test profiles
+are simply Python scripts. There are currently two supported test types:
+
+ PlainExecTest
+ This test starts a new process and watches the process output (stdout and
+ stderr). Lines that start with "PIGLIT:" are collected and interpreted as
+ a Python dictionary that contains test result details.
+
+ GleanTest
+ This is a test that is only used to integrate Glean tests
+
+Additional test types (e.g. for automatic image comparison) would have to
+be added to core.py.
+
+Rules of thumb:
+ Test process that exit with a nonzero returncode are considered to have
+ failed.
+
+ Output on stderr causes a warning.
+
+
+5. Todo
+-------
+
+ Get automated tests into widespread use ;)
+
+ Automate and integrate tests and demos from Mesa
+ Add code that automatically tests whether the test has rendered correctly
+
+ Performance regression tests
+ Ideally, this should be done by summarizing / comparing a history of
+ test results
diff --git a/framework/__init__.py b/framework/__init__.py
new file mode 100644
index 000000000..51a5fed78
--- /dev/null
+++ b/framework/__init__.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+#
+# 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:
+#
+# 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.
diff --git a/framework/core.py b/framework/core.py
new file mode 100644
index 000000000..053c75bec
--- /dev/null
+++ b/framework/core.py
@@ -0,0 +1,350 @@
+#!/usr/bin/python
+#
+# 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:
+#
+# 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.
+
+# Piglit core
+
+import errno
+import os
+import re
+import subprocess
+import sys
+import traceback
+
+__all__ = [
+ 'Environment',
+ 'loadTestProfile',
+ 'testPathToResultName',
+ 'GroupResult',
+ 'TestResult'
+]
+
+
+#############################################################################
+##### Helper functions
+#############################################################################
+
+# Ensure the given directory exists
+def checkDir(dirname, failifexists):
+ exists = True
+ try:
+ os.stat(dirname)
+ except OSError, e:
+ if e.errno == errno.ENOENT or e.errno == errno.ENOTDIR:
+ exists = False
+
+ if exists and failifexists:
+ print >>sys.stderr, "%(dirname)s exists already.\nUse --overwrite if you want to overwrite it.\n" % locals()
+ exit(1)
+
+ try:
+ os.makedirs(dirname)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+def testPathToResultName(path):
+ elems = filter(lambda s: len(s) > 0, path.split('/'))
+ pyname = 'testrun.results' + "".join(map(lambda s: "['"+s+"']", elems))
+ return pyname
+
+
+#############################################################################
+##### Result classes
+#############################################################################
+
+
+class TestResult(dict):
+ def __init__(self, *args):
+ dict.__init__(self)
+
+ assert(len(args) == 0 or len(args) == 2)
+
+ if len(args) == 2:
+ for k in args[0]:
+ self.__setattr__(k, args[0][k])
+
+ self.update(args[1])
+
+ def __repr__(self):
+ attrnames = set(dir(self)) - set(dir(self.__class__()))
+ return '%(class)s(%(dir)s,%(dict)s)' % {
+ 'class': self.__class__.__name__,
+ 'dir': dict([(k, self.__getattribute__(k)) for k in attrnames]),
+ 'dict': dict.__repr__(self)
+ }
+
+
+class GroupResult(dict):
+ def __init__(self, *args):
+ dict.__init__(self)
+
+ assert(len(args) == 0 or len(args) == 2)
+
+ if len(args) == 2:
+ for k in args[0]:
+ self.__setattr__(k, args[0][k])
+
+ self.update(args[1])
+
+ def __repr__(self):
+ attrnames = set(dir(self)) - set(dir(self.__class__()))
+ return '%(class)s(%(dir)s,%(dict)s)' % {
+ 'class': self.__class__.__name__,
+ 'dir': dict([(k, self.__getattribute__(k)) for k in attrnames]),
+ 'dict': dict.__repr__(self)
+ }
+
+class TestrunResult:
+ def __init__(self, *args):
+ self.name = ''
+ self.results = GroupResult()
+
+
+
+#############################################################################
+##### Generic Test classes
+#############################################################################
+
+class Environment:
+ def __init__(self):
+ self.file = sys.stdout
+ self.execute = True
+ self.filter = []
+
+class Test:
+ ignoreErrors = []
+
+ def doRun(self, env, path):
+ # Filter
+ if len(env.filter) > 0:
+ if not True in map(lambda f: f.search(path) != None, env.filter):
+ return None
+
+ # Run the test
+ if env.execute:
+ try:
+ print "Test: %(path)s" % locals()
+ result = self.run()
+ if 'result' not in result:
+ result['result'] = 'fail'
+ if not isinstance(result, TestResult):
+ result = TestResult({}, result)
+ result['result'] = 'warn'
+ result['note'] = 'Result not returned as an instance of TestResult'
+ except:
+ result = TestResult()
+ result['result'] = 'fail'
+ result['exception'] = str(sys.exc_info()[0]) + str(sys.exc_info()[1])
+ result['traceback'] = '@@@' + "".join(traceback.format_tb(sys.exc_info()[2]))
+
+ print " result: %(result)s" % { 'result': result['result'] }
+
+ varname = testPathToResultName(path)
+ print >>env.file, "%(varname)s = %(result)s" % locals()
+ else:
+ print "Dry-run: %(path)s" % locals()
+
+ # Default handling for stderr messages
+ def handleErr(self, results, err):
+ errors = filter(lambda s: len(s) > 0, map(lambda s: s.strip(), err.split('\n')))
+
+ ignored = []
+ for s in errors:
+ ignore = False
+ for pattern in Test.ignoreErrors:
+ if type(pattern) == str:
+ if s.find(pattern) >= 0:
+ ignore = True
+ break
+ else:
+ if pattern.search(s):
+ ignore = True
+ break
+ if ignore:
+ ignored.append(s)
+
+ errors = [s for s in errors if s not in ignored]
+
+ if len(errors) > 0:
+ results['errors'] = errors
+
+ if results['result'] == 'pass':
+ results['result'] = 'warn'
+
+ if len(ignored) > 0:
+ results['errors_ignored'] = ignored
+
+
+class Group(dict):
+ def doRun(self, env, path):
+ print >>env.file, "%s = GroupResult()" % (testPathToResultName(path))
+ for sub in self:
+ spath = sub
+ if len(path) > 0:
+ spath = path + '/' + spath
+ self[sub].doRun(env, spath)
+
+#############################################################################
+##### PlainExecTest: Simply run an executable
+##### Expect one line prefixed PIGLIT: in the output, which contains a
+##### result dictionary. The plain output is appended to this dictionary
+#############################################################################
+class PlainExecTest(Test):
+ def __init__(self, command):
+ self.command = command
+
+ def run(self):
+ proc = subprocess.Popen(
+ self.command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ out,err = proc.communicate()
+
+ outlines = out.split('\n')
+ outpiglit = map(lambda s: s[7:], filter(lambda s: s.startswith('PIGLIT:'), outlines))
+
+ results = TestResult()
+
+ if len(outpiglit) > 0:
+ try:
+ results.update(eval(''.join(outpiglit), {}))
+ out = '\n'.join(filter(lambda s: not s.startswith('PIGLIT:'), outlines))
+ except:
+ results['result'] = 'fail'
+ results['note'] = 'Failed to parse result string'
+
+ if 'result' not in results:
+ results['result'] = 'fail'
+
+ if proc.returncode != 0:
+ results['result'] = 'fail'
+ results['note'] = 'Returncode was %d' % (proc.returncode)
+
+ self.handleErr(results, err)
+
+ results['info'] = "@@@Returncode: %d\n\nErrors:\n%s\n\nOutput:\n%s" % (proc.returncode, err, out)
+ results['returncode'] = proc.returncode
+
+ return results
+
+
+
+#############################################################################
+##### GleanTest: Execute a sub-test of Glean
+#############################################################################
+def gleanExecutable():
+ return "./tests/glean/glean"
+
+def gleanResultDir():
+ return "./results/glean/"
+
+class GleanTest(Test):
+ globalParams = []
+
+ def __init__(self, name):
+ self.name = name
+ self.env = {}
+
+ def run(self):
+ results = TestResult()
+
+ fullenv = os.environ.copy()
+ for e in self.env:
+ fullenv[e] = str(self.env[e])
+
+ checkDir(gleanResultDir()+self.name, False)
+
+ glean = subprocess.Popen(
+ [gleanExecutable(), "-o", "-r", gleanResultDir()+self.name,
+ "--ignore-prereqs",
+ "-v", "-v", "-v",
+ "-t", "+"+self.name] + GleanTest.globalParams,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=fullenv
+ )
+
+ out, err = glean.communicate()
+
+ results['result'] = 'pass'
+ if glean.returncode != 0 or out.find('FAIL') >= 0:
+ results['result'] = 'fail'
+
+ results['returncode'] = glean.returncode
+
+ self.handleErr(results, err)
+
+ results['info'] = "@@@Returncode: %d\n\nErrors:\n%s\n\nOutput:\n%s" % (glean.returncode, err, out)
+
+ return results
+
+
+#############################################################################
+##### Loaders
+#############################################################################
+
+def loadTestProfile(filename):
+ try:
+ ns = {
+ '__file__': filename,
+ '__dir__': os.path.dirname(filename),
+ 'Test': Test,
+ 'Group': Group,
+ 'GleanTest': GleanTest,
+ 'gleanExecutable': gleanExecutable,
+ 'PlainExecTest': PlainExecTest
+ }
+ execfile(filename, ns)
+ return ns['tests']
+ except:
+ traceback.print_exc()
+ raise FatalError('Could not read tests profile')
+
+def loadTestResults(filename):
+ try:
+ ns = {
+ '__file__': filename,
+ 'GroupResult': GroupResult,
+ 'TestResult': TestResult,
+ 'TestrunResult': TestrunResult
+ }
+ execfile(filename, ns)
+
+ # BACKWARDS COMPATIBILITY
+ if 'testrun' not in ns:
+ testrun = TestrunResult()
+ testrun.results.update(ns['results'])
+ if 'name' in ns:
+ testrun.name = ns['name']
+ ns['testrun'] = testrun
+ # END BACKWARDS COMPATIBILITY
+
+ testrun = ns['testrun']
+ if len(testrun.name) == 0:
+ testrun.name = filename
+
+ return testrun
+ except:
+ traceback.print_exc()
+ raise FatalError('Could not read tests results')
diff --git a/piglit-run.py b/piglit-run.py
new file mode 100755
index 000000000..c9a3e6d47
--- /dev/null
+++ b/piglit-run.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+#
+# 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:
+#
+# 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.
+
+
+from getopt import getopt, GetoptError
+import os
+import os.path
+import re
+import sys
+
+import framework.core as core
+
+
+
+#############################################################################
+##### Main program
+#############################################################################
+def usage():
+ USAGE = """\
+Usage: %(progName)s [options] [profile.tests] [profile.results]
+
+Options:
+ -h, --help Show this message
+ -d, --dry-run Do not execute the tests
+ -t regexp, --tests=regexp Run only matching tests (can be used more
+ than once)
+ -n name, --name=name Name of the testrun
+
+Example:
+ %(progName)s tests/all.tests results/all.results
+ Run all tests, store the results in results/all.results
+
+ %(progName)s -t basic tests/all.tests results/all.results
+ Run all tests whose path contains the word 'basic'
+
+ %(progName)s -t ^glean/ -t tex tests/all.tests results/all.results
+ Run all tests that are in the 'glean' group or whose path contains
+ the substring 'tex'
+"""
+ print USAGE % {'progName': sys.argv[0]}
+ exit(1)
+
+def main():
+ env = core.Environment()
+
+ try:
+ options, args = getopt(sys.argv[1:], "hdt:n:", [ "help", "dry-run", "tests=", "name=" ])
+ except GetoptError:
+ usage()
+
+ OptionName = ''
+
+ for name,value in options:
+ if name in ('-h', '--help'):
+ usage()
+ elif name in ('-d', '--dry-run'):
+ env.execute = False
+ elif name in ('-t', '--tests'):
+ env.filter[:0] = [re.compile(value)]
+ elif name in ('-n', '--name'):
+ OptionName = value
+
+ if len(args) != 2:
+ usage()
+
+ profileFilename = args[0]
+ resultsFilename = args[1]
+
+ core.checkDir(os.path.dirname(resultsFilename), False)
+
+ profile = core.loadTestProfile(profileFilename)
+ env.file = open(resultsFilename, "w")
+ print >>env.file, """
+testrun = TestrunResult()
+testrun.name = %(name)s
+""" % { 'name': repr(OptionName) }
+ profile.doRun(env, '')
+ env.file.close()
+
+if __name__ == "__main__":
+ main()
diff --git a/piglit-summary-html.py b/piglit-summary-html.py
new file mode 100755
index 000000000..edad4f673
--- /dev/null
+++ b/piglit-summary-html.py
@@ -0,0 +1,414 @@
+#!/usr/bin/python
+#
+# 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:
+#
+# 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.
+
+from getopt import getopt, GetoptError
+import errno
+import os
+import sys
+
+import framework.core as core
+
+#############################################################################
+##### Auxiliary functions
+#############################################################################
+
+def testPathToHtmlFilename(path):
+ return filter(lambda s: s.isalnum() or s == '_', path.replace('/', '__')) + '.html'
+
+
+#############################################################################
+##### Annotation preprocessing
+#############################################################################
+
+class PassVector:
+ def __init__(self,p,w,f,s):
+ self.passnr = p
+ self.warnnr = w
+ self.failnr = f
+ self.skipnr = s
+
+ def add(self,o):
+ self.passnr += o.passnr
+ self.warnnr += o.warnnr
+ self.failnr += o.failnr
+ self.skipnr += o.skipnr
+
+def annotateOneTest(path, results):
+ """\
+Result is an array containing one test result.
+"""
+ result = ''
+ if 'result' in results:
+ result = results['result']
+
+ vectormap = {
+ 'pass': PassVector(1,0,0,0),
+ 'warn': PassVector(0,1,0,0),
+ 'fail': PassVector(0,0,1,0),
+ 'skip': PassVector(0,0,0,1)
+ }
+
+ if result not in vectormap:
+ result = 'warn'
+
+ results.status = result
+ results.passvector = vectormap[result]
+ results.path = path
+
+ return results
+
+
+def annotateTest(path, results):
+ """\
+Result is an array containing corresponding test results, one per test run
+"""
+ for j in range(len(results)):
+ results[j] = annotateOneTest(path, results[j])
+
+ stati = set([r.status for r in results])
+ changes = len(stati) > 1
+ problems = len(stati - set(['pass', 'skip'])) > 0
+ for r in results:
+ r.changes = changes
+ r.problems = problems
+
+
+def annotateGroup(path, results):
+ """\
+Result is an array containing corresponding GroupResults, one per test run
+"""
+ groupnames = set()
+ testnames = set()
+
+ changes = False
+ problems = False
+
+ for r in results:
+ r.passvector = PassVector(0,0,0,0)
+ r.path = path
+
+ for name in r:
+ if type(name) != str:
+ continue
+
+ if isinstance(r[name], core.GroupResult):
+ groupnames.add(name)
+ else:
+ testnames.add(name)
+
+ for name in groupnames:
+ children = []
+
+ for r in results:
+ if name not in r:
+ r[name] = core.GroupResult()
+
+ children.append(r[name])
+
+ spath = name
+ if len(path) > 0:
+ spath = path + '/' + spath
+
+ annotateGroup(spath, children)
+
+ changes = changes or results[0][name].changes
+ problems = problems or results[0][name].problems
+
+ for r in results:
+ r.passvector.add(r[name].passvector)
+
+
+ for name in testnames:
+ children = []
+
+ for r in results:
+ if name not in r:
+ r[name] = core.TestResult({}, { 'result': 'skip' })
+
+ # BACKWARDS COMPATIBILITY
+ if not isinstance(r[name], core.TestResult):
+ r[name] = core.TestResult({}, r[name])
+ # END BACKWARDS COMPATIBILITY
+
+ children.append(r[name])
+
+ spath = name
+ if len(path) > 0:
+ spath = path + '/' + spath
+
+ annotateTest(spath, children)
+
+ changes = changes or results[0][name].changes
+ problems = problems or results[0][name].problems
+
+ for r in results:
+ r.passvector.add(r[name].passvector)
+
+ for r in results:
+ r.changes = changes
+ r.problems = problems
+
+
+#############################################################################
+##### HTML output
+#############################################################################
+
+def readfile(filename):
+ f = open(filename, "r")
+ s = f.read()
+ f.close()
+ return s
+
+def writefile(filename, text):
+ f = open(filename, "w")
+ f.write(text)
+ f.close()
+
+Result = readfile('templates/result.html')
+ResultDetail = readfile('templates/result_detail.html')
+ResultList = readfile('templates/result_list.html')
+ResultListItem = readfile('templates/result_listitem.html')
+ResultMString = readfile('templates/result_mstring.html')
+
+Index = readfile('templates/index.html')
+IndexTestrun = readfile('templates/index_testrun.html')
+IndexTestrunB = readfile('templates/index_testrunb.html')
+IndexGroup = readfile('templates/index_group.html')
+IndexGroupTestrun = readfile('templates/index_group_testrun.html')
+IndexGroupGroup = readfile('templates/index_groupgroup.html')
+IndexTest = readfile('templates/index_test.html')
+IndexTestTestrun = readfile('templates/index_test_testrun.html')
+
+SummaryPages = {
+ 'all': 'index.html',
+ 'changes': 'changes.html',
+ 'problems': 'problems.html'
+}
+
+def buildDetailValue(detail):
+ if type(detail) == list:
+ items = ''
+
+ for d in detail:
+ items = items + ResultListItem % { 'detail': buildDetailValue(d) }
+
+ return ResultList % { 'items': items }
+ elif type(detail) == str and detail[0:3] == '@@@':
+ return ResultMString % { 'detail': detail[3:] }
+
+ return str(detail)
+
+
+def buildDetails(testResult):
+ details = []
+ for name in testResult:
+ if type(name) != str or name == 'result':
+ continue
+
+ value = buildDetailValue(testResult[name])
+ details += [(name,value)]
+
+ details.sort(lambda a,b: len(a[1])-len(b[1]))
+
+ text = ''
+ alternate = 'a'
+ for name,value in details:
+ text += ResultDetail % locals()
+
+ if alternate == 'a':
+ alternate = 'b'
+ else:
+ alternate = 'a'
+
+ return text
+
+
+def writeResultHtml(testResult, filename):
+ path = testResult.path
+ name = testResult.path.split('/')[-1]
+ status = testResult.status
+
+ if 'result' in testResult:
+ result = testResult['result']
+ else:
+ result = '?'
+
+ details = buildDetails(testResult)
+
+ writefile(filename, Result % locals())
+
+
+def recursiveWriteResultHtml(results, summaryDir):
+ for n in results:
+ if type(n) != str:
+ continue
+
+ if isinstance(results[n], core.GroupResult):
+ recursiveWriteResultHtml(results[n], summaryDir)
+ else:
+ writeResultHtml(results[n], summaryDir + '/' + testPathToHtmlFilename(results[n].path))
+
+
+def buildTestSummary(indent, alternate, name, test):
+ tenindent = 10 - indent
+ testruns = "".join([IndexTestTestrun % {
+ 'alternate': alternate,
+ 'status': t.status,
+ 'link': r.codename + '/' + testPathToHtmlFilename(t.path)
+ } for r,t in test])
+
+ return IndexTest % locals()
+
+
+def buildGroupSummaryTestrun(results, group):
+ passnr = group.passvector.passnr
+ warnnr = group.passvector.warnnr
+ failnr = group.passvector.failnr
+ skipnr = group.passvector.skipnr
+ totalnr = passnr + warnnr + failnr # do not count skips
+
+ if failnr > 0:
+ status = 'fail'
+ elif warnnr > 0:
+ status = 'warn'
+ elif passnr > 0:
+ status = 'pass'
+ else:
+ status = 'skip'
+
+ return IndexGroupTestrun % locals()
+
+
+def buildGroupSummary(indent, name, results, showcurrent):
+ """\
+testruns is an array of pairs (results,group), where results is the
+entire testrun record and group is the group we're currently printing.
+"""
+ tenindent = 10 - indent
+
+ items = ''
+ alternate = 'a'
+ names = filter(lambda k: type(k) == str, results[0][1])
+
+ if showcurrent == 'changes':
+ names = filter(lambda n: results[0][1][n].changes, names)
+ elif showcurrent == 'problems':
+ names = filter(lambda n: results[0][1][n].problems, names)
+
+ names.sort()
+ for n in names:
+ if isinstance(results[0][1][n], core.GroupResult):
+ items = items + IndexGroupGroup % {
+ 'group': buildGroupSummary(indent+1, n, [(r,g[n]) for r,g in results], showcurrent)
+ }
+ else:
+ items = items + buildTestSummary(indent+1, alternate, n, [(r,g[n]) for r,g in results])
+
+ if alternate == 'a':
+ alternate = 'b'
+ else:
+ alternate = 'a'
+
+ testruns = "".join([buildGroupSummaryTestrun(r,g) for r,g in results])
+
+ return IndexGroup % locals()
+
+
+def writeSummaryHtml(results, summaryDir, showcurrent):
+ """\
+results is an array containing the top-level results dictionarys.
+"""
+ def link(to):
+ if to == showcurrent:
+ return to
+ else:
+ page = SummaryPages[to]
+ return '<a href="%(page)s">%(to)s</a>' % locals()
+
+ group = buildGroupSummary(1, 'Total', [(r,r.results) for r in results], showcurrent)
+ testruns = "".join([IndexTestrun % r.__dict__ for r in results])
+ testrunsb = "".join([IndexTestrunB % r.__dict__ for r in results])
+
+ tolist = SummaryPages.keys()
+ tolist.sort()
+ showlinks = " | ".join([link(to) for to in tolist])
+
+ writefile(summaryDir + '/' + SummaryPages[showcurrent], Index % locals())
+
+
+#############################################################################
+##### Main program
+#############################################################################
+def usage():
+ USAGE = """\
+Usage: %(progName)s [options] [summary-dir] [test.results]...
+
+Options:
+ -h, --help Show this message
+ -o, --overwrite Overwrite existing directories
+
+Example:
+ %(progName)s summary/mysum results/all.results
+"""
+ print USAGE % {'progName': sys.argv[0]}
+ exit(1)
+
+
+def main():
+ try:
+ options, args = getopt(sys.argv[1:], "ho", [ "help", "overwrite" ])
+ except GetoptError:
+ usage()
+
+ OptionOverwrite = False
+ for name,value in options:
+ if name == "-h" or name == "--help":
+ usage()
+ elif name == "-o" or name == "--overwrite":
+ OptionOverwrite = True
+
+ if len(args) < 2:
+ usage()
+
+ summaryDir = args[0]
+ resultFilenames = args[1:]
+
+ core.checkDir(summaryDir, not OptionOverwrite)
+
+ results = [core.loadTestResults(name) for name in resultFilenames]
+
+ annotateGroup('', [r.results for r in results])
+ for r in results:
+ r.codename = filter(lambda s: s.isalnum(), r.name)
+ core.checkDir(summaryDir + '/' + r.codename, False)
+ recursiveWriteResultHtml(r.results, summaryDir + '/' + r.codename)
+
+ writefile(summaryDir + '/result.css', readfile('templates/result.css'))
+ writefile(summaryDir + '/index.css', readfile('templates/index.css'))
+ writeSummaryHtml(results, summaryDir, 'all')
+ writeSummaryHtml(results, summaryDir, 'problems')
+ writeSummaryHtml(results, summaryDir, 'changes')
+
+
+if __name__ == "__main__":
+ main()
diff --git a/templates/index.css b/templates/index.css
new file mode 100644
index 000000000..3e10f8d28
--- /dev/null
+++ b/templates/index.css
@@ -0,0 +1,89 @@
+
+table {
+ border: 0pt;
+ border-collapse: collapse;
+}
+
+tr {
+ padding: 4pt;
+}
+
+td {
+ padding: 4pt;
+}
+
+.title {
+ background-color: #c8c838;
+}
+
+.head {
+ background-color: #c8c838
+}
+
+.a {
+ background-color: #ffff95
+}
+
+.b {
+ background-color: #e1e183
+}
+
+.skip {
+ text-align: right;
+ background-color: #b0b0b0;
+}
+
+.warn {
+ text-align: right;
+ background-color: #ff9020;
+}
+
+.fail {
+ text-align: right;
+ background-color: #ff2020;
+}
+
+.pass {
+ text-align: right;
+ background-color: #20ff20;
+}
+
+.skipa {
+ text-align: right;
+ background-color: #d0d0d0;
+}
+
+.warna {
+ text-align: right;
+ background-color: #ffc050;
+}
+
+.faila {
+ text-align: right;
+ background-color: #ff5050;
+}
+
+.passa {
+ text-align: right;
+ background-color: #50ff50;
+}
+
+.skipb {
+ text-align: right;
+ background-color: #c0c0c0;
+}
+
+.warnb {
+ text-align: right;
+ background-color: #ffa040;
+}
+
+.failb {
+ text-align: right;
+ background-color: #ff4040;
+}
+
+.passb {
+ text-align: right;
+ background-color: #40ff40;
+}
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 000000000..d6bf392d3
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ <title>Result summary </title>
+ <link rel="stylesheet" href="index.css"/>
+ </head>
+ <body>
+ <h1>Result summary</h1>
+ <p>
+ Currently showing: %(showcurrent)s
+ </p>
+ <p>
+ Show: %(showlinks)s
+ </p>
+ <table width="95%%">
+ <colgroup>
+ <!-- 9 columns for indent -->
+ <col width="5pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+ <col width="20pt" />
+
+ <!-- remaining name column -->
+ <col />
+
+ <!-- status column -->
+ %(testruns)s
+ </colgroup>
+ <tr>
+ <td colspan="10" />
+ %(testrunsb)s
+ </tr>
+ %(group)s
+ </table>
+ </body>
+</html>
diff --git a/templates/index_group.html b/templates/index_group.html
new file mode 100644
index 000000000..9c72bcff7
--- /dev/null
+++ b/templates/index_group.html
@@ -0,0 +1,6 @@
+<tr>
+ <td colspan="%(indent)s">&#160;</td>
+ <td class="head" colspan="%(tenindent)s"><b>%(name)s</b></td>
+ %(testruns)s
+</tr>
+%(items)s
diff --git a/templates/index_group_testrun.html b/templates/index_group_testrun.html
new file mode 100644
index 000000000..07b53421d
--- /dev/null
+++ b/templates/index_group_testrun.html
@@ -0,0 +1 @@
+<td class="%(status)s"><b>%(passnr)d/%(totalnr)d</b></td>
diff --git a/templates/index_groupgroup.html b/templates/index_groupgroup.html
new file mode 100644
index 000000000..53db60700
--- /dev/null
+++ b/templates/index_groupgroup.html
@@ -0,0 +1 @@
+%(group)s
diff --git a/templates/index_test.html b/templates/index_test.html
new file mode 100644
index 000000000..0c3bdde6a
--- /dev/null
+++ b/templates/index_test.html
@@ -0,0 +1,5 @@
+<tr>
+ <td colspan="%(indent)s">&#160;</td>
+ <td class="%(alternate)s" colspan="%(tenindent)s">%(name)s</td>
+ %(testruns)s
+</tr>
diff --git a/templates/index_test_testrun.html b/templates/index_test_testrun.html
new file mode 100644
index 000000000..43de898bf
--- /dev/null
+++ b/templates/index_test_testrun.html
@@ -0,0 +1 @@
+<td class="%(status)s%(alternate)s"><a href="%(link)s">%(status)s</a></td>
diff --git a/templates/index_testrun.html b/templates/index_testrun.html
new file mode 100644
index 000000000..1dc68ffee
--- /dev/null
+++ b/templates/index_testrun.html
@@ -0,0 +1 @@
+<col width="50pt" />
diff --git a/templates/index_testrunb.html b/templates/index_testrunb.html
new file mode 100644
index 000000000..b4ef7ecca
--- /dev/null
+++ b/templates/index_testrunb.html
@@ -0,0 +1 @@
+<td class="head"><b>%(name)s</b></td>
diff --git a/templates/result.css b/templates/result.css
new file mode 100644
index 000000000..7c6a557ce
--- /dev/null
+++ b/templates/result.css
@@ -0,0 +1,35 @@
+
+td {
+ padding: 4pt;
+}
+
+th {
+ padding: 4pt;
+}
+
+table {
+ border: 0pt;
+ border-collapse: collapse;
+}
+
+.head {
+ background-color: #c8c838
+}
+
+.a {
+ background-color: #ffff95
+}
+
+.b {
+ background-color: #e1e183
+}
+
+.bara {
+ vertical-align: top;
+ background-color: #ffff85;
+}
+
+.barb {
+ vertical-align: top;
+ background-color: #d1d173;
+}
diff --git a/templates/result.html b/templates/result.html
new file mode 100644
index 000000000..d6167e7b0
--- /dev/null
+++ b/templates/result.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <title>%(path)s - Details</title>
+ <link rel="stylesheet" href="../result.css"/>
+ </head>
+ <body>
+ <h1>Results for %(path)s</h1>
+ <h2>Overview</h2>
+ <p>
+ <b>Status:</b> %(status)s<br/>
+ <b>Result:</b> %(result)s<br/>
+ </p>
+ <p>
+ <a href="../index.html">Back to summary</a>
+ </p>
+ <h2>Details</h2>
+ <table>
+ <tr class="head">
+ <th>Detail</th>
+ <th>Value</th>
+ </tr>
+ %(details)s
+ </table>
+ <p>
+ <a href="../index.html">Back to summary</a>
+ </p>
+ </body>
+</html>
diff --git a/templates/result_detail.html b/templates/result_detail.html
new file mode 100644
index 000000000..d735d0da0
--- /dev/null
+++ b/templates/result_detail.html
@@ -0,0 +1,4 @@
+<tr>
+ <td class="bar%(alternate)s">%(name)s</td>
+ <td class="%(alternate)s">%(value)s</td>
+</tr>
diff --git a/templates/result_list.html b/templates/result_list.html
new file mode 100644
index 000000000..af215d481
--- /dev/null
+++ b/templates/result_list.html
@@ -0,0 +1,3 @@
+<ul>
+%(items)s
+</ul>
diff --git a/templates/result_listitem.html b/templates/result_listitem.html
new file mode 100644
index 000000000..12ef6ebfd
--- /dev/null
+++ b/templates/result_listitem.html
@@ -0,0 +1 @@
+<li>%(detail)s</li>
diff --git a/templates/result_mstring.html b/templates/result_mstring.html
new file mode 100644
index 000000000..b4c91d8a6
--- /dev/null
+++ b/templates/result_mstring.html
@@ -0,0 +1 @@
+<pre>%(detail)s</pre>
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 000000000..8cbc961ef
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,5 @@
+
+add_subdirectory (bugs)
+add_subdirectory (glean)
+add_subdirectory (mesa)
+add_subdirectory (shaders)
diff --git a/tests/all.tests b/tests/all.tests
new file mode 100644
index 000000000..931ec3665
--- /dev/null
+++ b/tests/all.tests
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# All tests that come with piglit, using default settings
+
+import re
+import subprocess
+
+######
+# Glean sub-tests
+glean_fragprog1 = Group()
+
+out,err = subprocess.Popen(
+ [gleanExecutable(), "-t", "+fragProg1", "--listdetails"],
+ stdout=subprocess.PIPE
+).communicate()
+
+st = 0
+for line in out.split('\n'):
+ if st == 0:
+ if line == "DETAILS for fragProg1":
+ st = 1
+ elif st == 1:
+ if line == "END DETAILS":
+ break
+
+ line = line.strip()
+ line = filter(lambda s: s.isalnum() or s in ' ()-_', line)
+
+ t = GleanTest('fragProg1')
+ t.env['GLEAN_FRAGPROG'] = line
+ glean_fragprog1[line] = t
+
+
+######
+# Collecting all tests
+glean = Group()
+glean['basic'] = GleanTest('basic')
+glean['makeCurrent'] = GleanTest('makeCurrent')
+glean['blendFunc'] = GleanTest('blendFunc')
+glean['depthStencil'] = GleanTest('depthStencil')
+glean['fpexceptions'] = GleanTest('fpexceptions')
+glean['fragProg1'] = GleanTest('fragProg1') # also add entire tests, because of interdependency bugs that only occur when several different FPs are used
+glean['getString'] = GleanTest('getString')
+glean['logicOp'] = GleanTest('logicOp')
+glean['maskedClear'] = GleanTest('maskedClear')
+glean['orthoPosRandTris'] = GleanTest('orthoPosRandTris')
+glean['orthoPosRandRects'] = GleanTest('orthoPosRandRects')
+glean['orthoPosTinyQuads'] = GleanTest('orthoPosTinyQuads')
+glean['orthoPosHLines'] = GleanTest('orthoPosHLines')
+glean['orthoPosVLines'] = GleanTest('orthoPosVLines')
+glean['orthoPosPoints'] = GleanTest('orthoPosPoints')
+glean['paths'] = GleanTest('paths')
+glean['polygonOffset'] = GleanTest('polygonOffset')
+glean['pixelFormats'] = GleanTest('pixelFormats')
+glean['pointAtten'] = GleanTest('pointAtten')
+glean['exactRGBA'] = GleanTest('exactRGBA')
+glean['readPixSanity'] = GleanTest('readPixSanity')
+glean['rgbTriStrip'] = GleanTest('rgbTriStrip')
+glean['scissor'] = GleanTest('scissor')
+glean['teapot'] = GleanTest('teapot')
+glean['texCombine'] = GleanTest('texCombine')
+glean['texCube'] = GleanTest('texCube')
+glean['texEnv'] = GleanTest('texEnv')
+glean['texgen'] = GleanTest('texgen')
+glean['texRect'] = GleanTest('texRect')
+glean['texture_srgb'] = GleanTest('texture_srgb')
+glean['vertattrib'] = GleanTest('vertattrib')
+glean['vertProg1'] = GleanTest('vertProg1')
+
+mesa = Group()
+mesa['crossbar'] = PlainExecTest([__dir__ + '/mesa/tests/crossbar', '-auto'])
+
+shaders = Group()
+shaders['trinity-fp1'] = PlainExecTest([__dir__ + '/shaders/trinity-fp1', '-auto'])
+shaders['fp-lit-mask'] = PlainExecTest([__dir__ + '/shaders/fp-lit-mask', '-auto'])
+shaders['fp-fragment-position'] = PlainExecTest([__dir__ + '/shaders/fp-fragment-position', '-auto'])
+shaders['fp-kil'] = PlainExecTest([__dir__ + '/shaders/fp-kil', '-auto'])
+shaders['fp-incomplete-tex'] = PlainExecTest([__dir__ + '/shaders/fp-incomplete-tex', '-auto'])
+shaders['glean-fragProg1'] = glean_fragprog1
+
+bugs = Group()
+bugs['fdo9833'] = PlainExecTest([__dir__ + '/bugs/fdo9833', '-auto'])
+bugs['fdo10370'] = PlainExecTest([__dir__ + '/bugs/fdo10370', '-auto'])
+
+tests = Group()
+tests['bugs'] = bugs
+tests['glean'] = glean
+tests['mesa'] = mesa
+tests['shaders'] = shaders
+
+#############
+# Some Mesa diagnostic messages that we should probably ignore
+Test.ignoreErrors.append("couldn't open libtxc_dxtn.so")
+Test.ignoreErrors.append(re.compile("Mesa: .*build"))
+Test.ignoreErrors.append(re.compile("Mesa: CPU.*"))
+Test.ignoreErrors.append(re.compile("Mesa: .*cpu detected."))
+Test.ignoreErrors.append(re.compile("Mesa: Test.*"))
+Test.ignoreErrors.append(re.compile("Mesa: Yes.*"))
diff --git a/tests/bugs/CMakeLists.txt b/tests/bugs/CMakeLists.txt
new file mode 100644
index 000000000..3f282581b
--- /dev/null
+++ b/tests/bugs/CMakeLists.txt
@@ -0,0 +1,21 @@
+
+include_directories(
+ ${OPENGL_INCLUDE_PATH}
+ ${GLUT_INCLUDE_DIR}
+ ${piglit_SOURCE_DIR}/tests/mesa/util
+)
+
+link_directories (
+ ${piglit_SOURCE_DIR}/tests/mesa/util
+)
+
+link_libraries (
+ ${OPENGL_gl_LIBRARY}
+ ${OPENGL_glu_LIBRARY}
+ ${GLUT_glut_LIBRARY}
+ ${TIFF_LIBRARY}
+ mesautil
+)
+
+add_executable (fdo9833 fdo9833.c)
+add_executable (fdo10370 fdo10370.c)
diff --git a/tests/bugs/fdo10370.c b/tests/bugs/fdo10370.c
new file mode 100644
index 000000000..bfbfc4b24
--- /dev/null
+++ b/tests/bugs/fdo10370.c
@@ -0,0 +1,178 @@
+/*
+ * Test case from fdo bug #10370
+ * http://bugs.freedesktop.org/show_bug.cgi?id=10370
+ */
+
+#include "GL/glut.h"
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static int Automatic = 0;
+
+#define WIN_WIDTH 128
+#define WIN_HEIGHT 128
+#define BITMAP_WIDTH 1
+#define BITMAP_HEIGHT 1
+#define ALIGN 1
+GLfloat read_buf[4 * BITMAP_WIDTH * BITMAP_HEIGHT];
+static GLfloat r_map[] = { 0, 1 };
+static GLfloat g_map[] = { 0, 0 };
+static GLfloat b_map[] = { 1, 0 };
+static GLfloat a_map[] = { 1, 1 };
+static GLubyte data[] = { 0x8f, 0xff, 0x7f, 0x70 };
+
+static GLuint tex_name;
+
+void init()
+{
+ int i, j;
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glTranslatef(-1.0, -1.0, 0.0);
+ glScalef(2.0/WIN_WIDTH, 2.0/WIN_HEIGHT, 1.0);
+
+ glDisable(GL_DITHER);
+ glClearColor(1, 1, 1, 1);
+ glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
+
+ glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 2, r_map);
+ glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 2, g_map);
+ glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 2, b_map);
+ glPixelMapfv(GL_PIXEL_MAP_I_TO_A, 2, a_map);
+
+ glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
+
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, ALIGN);
+
+ glGenTextures(1, &tex_name);
+ glBindTexture(GL_TEXTURE_2D, tex_name);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+}
+
+
+
+void display()
+{
+ int i, j, k, col, pixel;
+ GLubyte zero_data = 0;
+ GLfloat expected[4];
+ float dmax = 0.0;
+
+ memset(read_buf, 0xff, sizeof(read_buf)); //reset
+
+ for (k = 0; k < (sizeof(data)/sizeof(GLubyte)); k ++) {
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ glNewList(1, GL_COMPILE_AND_EXECUTE);
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, tex_name);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ BITMAP_WIDTH, BITMAP_HEIGHT, 0,
+ GL_COLOR_INDEX, GL_BITMAP, &data[k]);
+
+ glBegin(GL_POLYGON);
+ glTexCoord2f(0,0); glVertex2f(0, 0);
+ glTexCoord2f(1,0); glVertex2f(BITMAP_WIDTH, 0);
+ glTexCoord2f(1,1); glVertex2f(BITMAP_WIDTH, BITMAP_HEIGHT);
+ glTexCoord2f(0,1); glVertex2f(0, BITMAP_HEIGHT);
+ glEnd();
+ glDisable(GL_TEXTURE_2D);
+ glEndList();
+ glFlush();
+
+ glReadPixels(0, 0, BITMAP_WIDTH, BITMAP_HEIGHT,
+ GL_RGBA, GL_FLOAT, read_buf);
+
+ printf("data[0x%x], ", data[k]);
+ if (data[k] & 0x80) {
+ printf("foreground: expected RGBA (%1.1f, %1.1f %1.1f %1.1f)\n",
+ r_map[1], g_map[1], b_map[1], a_map[1]);
+ expected[0] = r_map[1];
+ expected[1] = g_map[1];
+ expected[2] = b_map[1];
+ expected[3] = a_map[1];
+ } else {
+ printf("background: expected RGBA (%1.1f, %1.1f %1.1f %1.1f)\n",
+ r_map[0], g_map[0], b_map[0], a_map[0]);
+ expected[0] = r_map[0];
+ expected[1] = g_map[0];
+ expected[2] = b_map[0];
+ expected[3] = a_map[0];
+ }
+
+ printf("First execution, Readback RGBA:\n");
+ for (i = 0; i < BITMAP_HEIGHT; i ++) {
+ for (j = 0; j < BITMAP_WIDTH; j ++) {
+ pixel = j + i*BITMAP_WIDTH;
+ printf("pixel[%d, %d]: %1.1f %1.1f %1.1f %1.1f\n", j, i,
+ read_buf[pixel*4], read_buf[pixel*4+1],
+ read_buf[pixel*4+2], read_buf[pixel*4+3]);
+
+ for(col = 0; col < 4; ++col) {
+ float delta = read_buf[pixel*4+col] - expected[col];
+ if (delta > dmax) dmax = delta;
+ else if (-delta > dmax) dmax = -delta;
+ }
+ }
+ }
+
+ /* 2nd time execution from call list */
+ glCallList(1);
+ glDeleteLists(1,1);
+
+ memset(read_buf, 0xff, sizeof(read_buf)); //reset
+ glReadPixels(0, 0, BITMAP_WIDTH, BITMAP_HEIGHT,
+ GL_RGBA, GL_FLOAT, read_buf);
+
+ printf("CallList execution, Readback RGBA:\n");
+ for (i = 0; i < BITMAP_HEIGHT; i ++) {
+ for (j = 0; j < BITMAP_WIDTH; j ++) {
+ pixel = j + i*BITMAP_WIDTH;
+ printf("pixel[%d, %d]: %1.1f %1.1f %1.1f %1.1f\n", j, i,
+ read_buf[pixel*4], read_buf[pixel*4+1],
+ read_buf[pixel*4+2], read_buf[pixel*4+3]);
+ for(col = 0; col < 4; ++col) {
+ float delta = read_buf[pixel*4+col] - expected[col];
+ if (delta > dmax) dmax = delta;
+ else if (-delta > dmax) dmax = -delta;
+ }
+ }
+ }
+ printf("------------------------------------\n");
+ } //end for(k)
+
+ printf("max delta: %f\n", dmax);
+
+ if (Automatic) {
+ if (dmax > 0.02)
+ printf("PIGLIT: {'result': 'fail' }\n");
+ else
+ printf("PIGLIT: {'result': 'pass' }\n");
+
+ exit(0);
+ }
+}
+
+
+int main(int argc, char**argv)
+{
+ glutInit(&argc, argv);
+ if (argc == 2 && !strcmp(argv[1], "-auto"))
+ Automatic = 1;
+ glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
+ glutInitWindowSize (WIN_WIDTH, WIN_HEIGHT);
+ glutInitWindowPosition (100, 100);
+ glutCreateWindow ("fdo10370");
+ init();
+ glutDisplayFunc(display);
+ glutMainLoop();
+}
diff --git a/tests/bugs/fdo9833.c b/tests/bugs/fdo9833.c
new file mode 100644
index 000000000..c14c0a657
--- /dev/null
+++ b/tests/bugs/fdo9833.c
@@ -0,0 +1,51 @@
+/**
+ * Test case from fd.o bug #9833.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=9833
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <GL/glut.h>
+
+static int Automatic = 0;
+
+static void display(void)
+{
+ static int goterrors = 0;
+ static int frame = 0;
+ GLuint error;
+
+ frame++;
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glPushAttrib(GL_TEXTURE_BIT);
+ while ( (error = glGetError()) != GL_NO_ERROR) {
+ fprintf(stderr, "OpenGL error 0x%0x occured after glPushAttrib!\n", error);
+ goterrors++;
+ }
+
+
+ glPopAttrib();
+ while ( (error = glGetError()) != GL_NO_ERROR) {
+ fprintf(stderr, "OpenGL error 0x%0x occured after glPopAttrib!\n", error);
+ goterrors++;
+ }
+
+ if (Automatic && frame > 2) {
+ printf("PIGLIT: {'result': '%s' }\n", goterrors ? "fail" : "pass");
+ exit(0);
+ }
+
+ glutPostRedisplay();
+}
+
+int main(int argc, char **argv)
+{
+ glutInit(&argc, argv);
+ if (argc == 2 && !strcmp(argv[1], "-auto"))
+ Automatic = 1;
+ glutCreateWindow("fdo9833");
+ glutDisplayFunc(display);
+ glutMainLoop();
+ return 0;
+}
diff --git a/tests/glean/CMakeLists.txt b/tests/glean/CMakeLists.txt
new file mode 100644
index 000000000..9156f1a8d
--- /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 000000000..af317bd9d
--- /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 000000000..44fe56b80
--- /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 000000000..b7bbc0665
--- /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 000000000..88f9a81b1
--- /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 000000000..a5f24f925
--- /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 000000000..9227bc6fe
--- /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 000000000..89c6d5833
--- /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 000000000..f158921ef
--- /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 000000000..9e81d9656
--- /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 000000000..d1447cd0a
--- /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 000000000..1bf08f0c9
--- /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 000000000..e8142a508
--- /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 000000000..31678b281
--- /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 000000000..ab8ab2dc5
--- /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 000000000..34e735668
--- /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 000000000..89ca4ae9a
--- /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 000000000..342bd4768
--- /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 000000000..94db42c5f
--- /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 000000000..18136ca82
--- /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 000000000..83942f4f7
--- /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 000000000..adfdc2247
--- /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 000000000..84891f141
--- /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 000000000..87e24dfc6
--- /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 000000000..c40b5db5b
--- /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 000000000..6d8aee4b3
--- /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 000000000..0590a5fd9
--- /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 000000000..9bda4e484
--- /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 000000000..ad476d37c
--- /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 000000000..11617ef47
--- /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 000000000..b47684d72
--- /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 000000000..8cc95b3d9
--- /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 000000000..eaea51507
--- /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 000000000..5e5068f26
--- /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 000000000..87c8c41a6
--- /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 000000000..d9eb019f6
--- /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 000000000..476754278
--- /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 000000000..b4fab0233
--- /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 000000000..2d3af683a
--- /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 000000000..94e91796e
--- /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 000000000..f53a64160
--- /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 000000000..6aedd0253
--- /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 000000000..835bf7a3b
--- /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 000000000..87d0934d7
--- /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 000000000..5a83902c3
--- /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 000000000..b943f53d5
--- /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 000000000..11f3bd7e7
--- /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 000000000..f691d2cf8
--- /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 000000000..9c915ecc0
--- /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 000000000..02bd16c51
--- /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 000000000..1d6e78c47
--- /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 000000000..7a93eda1e
--- /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 000000000..fee9a0c67
--- /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 000000000..632a5af29
--- /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 000000000..b36e7a6f9
--- /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 000000000..22c3347d5
--- /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 000000000..d0465b5db
--- /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 000000000..549946033
--- /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 000000000..41a80c5af
--- /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 000000000..06a80cfce
--- /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 000000000..0e670b0a6
--- /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 000000000..b8fd6f9ec
--- /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 000000000..d03fd1553
--- /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 000000000..42cac0389
--- /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 000000000..4ecad0430
--- /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 000000000..a50cfeabf
--- /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"