[Piglit] [PATCH] gl-3.0: add a test to stress the bound resource limits

Matthew McClure mcclurem at vmware.com
Fri Jul 11 15:30:58 PDT 2014


Okay fixed. If approved, I am new to the project and do not have commit access so I will need help checking this in.

Thanks,
Matthew

--- Begin Updated Patch ---

With this patch, we add a test that will stress the bound
resource limits as determined by the GL API getInteger
values. The bound resources are populated with floats given
a table of primes. Each contribution is multiplied into the
final expression, and uniquely represents the successful
reference of the resource's memory.

Reviewed-by: <needed>
---
 tests/all.py                              |    1 +
 tests/spec/gl-3.0/CMakeLists.gl.txt       |    1 +
 tests/spec/gl-3.0/bound-resource-limits.c | 1336 +++++++++++++++++++++++++++++
 3 files changed, 1338 insertions(+), 0 deletions(-)
 create mode 100644 tests/spec/gl-3.0/bound-resource-limits.c

diff --git a/tests/all.py b/tests/all.py
index f792a84..403f040 100644
--- a/tests/all.py
+++ b/tests/all.py
@@ -885,6 +885,7 @@ gl30['attribs'] = concurrent_test('attribs GL3')
 add_concurrent_test(gl30, 'bindfragdata-invalid-parameters')
 add_concurrent_test(gl30, 'bindfragdata-link-error')
 add_concurrent_test(gl30, 'bindfragdata-nonexistent-variable')
+gl30['bound-resource-limits'] = concurrent_test('gl-3.0-bound-resource-limits')
 add_concurrent_test(gl30, 'clearbuffer-depth')
 add_concurrent_test(gl30, 'clearbuffer-depth-stencil')
 add_plain_test(gl30, 'clearbuffer-display-lists')
diff --git a/tests/spec/gl-3.0/CMakeLists.gl.txt b/tests/spec/gl-3.0/CMakeLists.gl.txt
index 29c59ee..a802da3 100644
--- a/tests/spec/gl-3.0/CMakeLists.gl.txt
+++ b/tests/spec/gl-3.0/CMakeLists.gl.txt
@@ -9,6 +9,7 @@ link_libraries (
 	${OPENGL_glu_LIBRARY}
 )
 
+piglit_add_executable (gl-3.0-bound-resource-limits bound-resource-limits.c)
 piglit_add_executable (gl-3.0-minmax minmax.c)
 piglit_add_executable (gl-3.0-render-integer render-integer.c)
 piglit_add_executable (gl-3.0-required-sized-texture-formats required-sized-texture-formats.c)
diff --git a/tests/spec/gl-3.0/bound-resource-limits.c b/tests/spec/gl-3.0/bound-resource-limits.c
new file mode 100644
index 0000000..851c361
--- /dev/null
+++ b/tests/spec/gl-3.0/bound-resource-limits.c
@@ -0,0 +1,1336 @@
+/*
+ * Copyright 2014 VMware, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * Test resource limits given the maximum supported by the implementation.
+ *
+ * Each component of the fragment output is derived using the following
+ * expression (indexed at the scalar level):
+ *
+ *   result[i] = texture[i] * texture[i] * ... * attrib[i + l * numOutputs]
+ *
+ * Depending on the limits for vertex/fragment image units, the texture
+ * contribution will vary. See the generation of g_expected at the end of
+ * piglit_init.
+ *
+ * Since the scalar inputs are primes, multiplication will yield a unique
+ * result. Results can be diagnosed by evaluating for their missing factor(s).
+ *
+ * Matthew McClure
+ * 7 July 2014
+ */
+
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+        config.supports_gl_compat_version = 30;
+        config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+
+/*
+ * Definitions for the runtime behavior.
+ */
+#define GLSL_VERSION                    "#version 130"
+#define BUFFER_WIDTH                    32
+#define BUFFER_HEIGHT                   32
+#define MAX_SHADER_LINE_CHARS           256
+#define MAX_SHADER_TEXT_CHARS           16*1024
+#define MAX_COMPONENTS                  4
+#define MAX_EXPRESSION_OUTPUTS          1
+#define MAX_EXPRESSION_ARGUMENTS        2
+#define MAX_EXPRESSION_TEMPS            1
+#define TEMP_TEXT_SIZE                  (MAX_EXPRESSION_OUTPUTS +  \
+                                         MAX_EXPRESSION_ARGUMENTS +  \
+                                         MAX_EXPRESSION_ARGUMENTS)
+
+#define NUM_PRIMES                      512
+#define NUM_VERTICES                    3
+
+#define DEBUG_INPUT                     0x01
+#define DEBUG_READBACK                  0x02
+#define DEBUG_SHADERS                   0x04
+#define DEBUG_DRAW                      0x08
+#define DEBUG_DONT_CLAMP_MAX_VARYINGS   0x10
+
+/*
+ * Type definitions for our working set.
+ */
+enum {
+        DRAW_ARRAYS_VBO = 0,
+        DRAW_ELEMENTS_VBO = 1,
+        DRAW_IMMEDIATE = 2,
+};
+
+typedef struct MyVector4 {
+        float x, y, z, w;
+} MyVector4;
+
+typedef struct PackedTypeDesc {
+        char *typeName;
+        unsigned numComponents;
+        char *componentNames[MAX_COMPONENTS];
+        char *defaultValue;
+} PackedTypeDesc;
+
+typedef struct PackedDesc {
+        char *semanticName;
+        char *variableName;
+        bool isArray;
+        unsigned count;
+        const PackedTypeDesc *typeDesc;
+} PackedDesc;
+
+
+/*
+ * Global variables for the test's state.
+ */
+static GLint g_maxVaryingFloats = 0;
+static GLint g_maxVertexAttribs = 0;
+static GLint g_maxVertexTextureImageUnits = 0;
+static GLint g_maxTextureImageUnits = 0;
+static GLint g_maxCombinedTextureImageUnits = 0;
+
+static GLint g_maxAuxBuffers = 0;
+static GLint g_maxDrawBuffers = 0;
+static GLint g_maxColorAttachments = 0;
+
+static GLint g_debugMask = 0x0;
+static GLint g_drawMode = DRAW_IMMEDIATE;
+
+static char *g_fragColor = "gl_FragColor";
+static char *g_fragData = "gl_FragData";
+
+static const GLfloat g_primes[NUM_PRIMES] = {
+2.0, 3.0, 5.0, 7.0, 11.0, 13.0, 17.0, 19.0,
+23.0, 29.0, 31.0, 37.0, 41.0, 43.0, 47.0, 53.0,
+59.0, 61.0, 67.0, 71.0, 73.0, 79.0, 83.0, 89.0,
+97.0, 101.0, 103.0, 107.0, 109.0, 113.0, 127.0, 131.0,
+137.0, 139.0, 149.0, 151.0, 157.0, 163.0, 167.0, 173.0,
+179.0, 181.0, 191.0, 193.0, 197.0, 199.0, 211.0, 223.0,
+227.0, 229.0, 233.0, 239.0, 241.0, 251.0, 257.0, 263.0,
+269.0, 271.0, 277.0, 281.0, 283.0, 293.0, 307.0, 311.0,
+313.0, 317.0, 331.0, 337.0, 347.0, 349.0, 353.0, 359.0,
+367.0, 373.0, 379.0, 383.0, 389.0, 397.0, 401.0, 409.0,
+419.0, 421.0, 431.0, 433.0, 439.0, 443.0, 449.0, 457.0,
+461.0, 463.0, 467.0, 479.0, 487.0, 491.0, 499.0, 503.0,
+509.0, 521.0, 523.0, 541.0, 547.0, 557.0, 563.0, 569.0,
+571.0, 577.0, 587.0, 593.0, 599.0, 601.0, 607.0, 613.0,
+617.0, 619.0, 631.0, 641.0, 643.0, 647.0, 653.0, 659.0,
+661.0, 673.0, 677.0, 683.0, 691.0, 701.0, 709.0, 719.0,
+727.0, 733.0, 739.0, 743.0, 751.0, 757.0, 761.0, 769.0,
+773.0, 787.0, 797.0, 809.0, 811.0, 821.0, 823.0, 827.0,
+829.0, 839.0, 853.0, 857.0, 859.0, 863.0, 877.0, 881.0,
+883.0, 887.0, 907.0, 911.0, 919.0, 929.0, 937.0, 941.0,
+947.0, 953.0, 967.0, 971.0, 977.0, 983.0, 991.0, 997.0,
+1009.0, 1013.0, 1019.0, 1021.0, 1031.0, 1033.0, 1039.0, 1049.0,
+1051.0, 1061.0, 1063.0, 1069.0, 1087.0, 1091.0, 1093.0, 1097.0,
+1103.0, 1109.0, 1117.0, 1123.0, 1129.0, 1151.0, 1153.0, 1163.0,
+1171.0, 1181.0, 1187.0, 1193.0, 1201.0, 1213.0, 1217.0, 1223.0,
+1229.0, 1231.0, 1237.0, 1249.0, 1259.0, 1277.0, 1279.0, 1283.0,
+1289.0, 1291.0, 1297.0, 1301.0, 1303.0, 1307.0, 1319.0, 1321.0,
+1327.0, 1361.0, 1367.0, 1373.0, 1381.0, 1399.0, 1409.0, 1423.0,
+1427.0, 1429.0, 1433.0, 1439.0, 1447.0, 1451.0, 1453.0, 1459.0,
+1471.0, 1481.0, 1483.0, 1487.0, 1489.0, 1493.0, 1499.0, 1511.0,
+1523.0, 1531.0, 1543.0, 1549.0, 1553.0, 1559.0, 1567.0, 1571.0,
+1579.0, 1583.0, 1597.0, 1601.0, 1607.0, 1609.0, 1613.0, 1619.0,
+1621.0, 1627.0, 1637.0, 1657.0, 1663.0, 1667.0, 1669.0, 1693.0,
+1697.0, 1699.0, 1709.0, 1721.0, 1723.0, 1733.0, 1741.0, 1747.0,
+1753.0, 1759.0, 1777.0, 1783.0, 1787.0, 1789.0, 1801.0, 1811.0,
+1823.0, 1831.0, 1847.0, 1861.0, 1867.0, 1871.0, 1873.0, 1877.0,
+1879.0, 1889.0, 1901.0, 1907.0, 1913.0, 1931.0, 1933.0, 1949.0,
+1951.0, 1973.0, 1979.0, 1987.0, 1993.0, 1997.0, 1999.0, 2003.0,
+2011.0, 2017.0, 2027.0, 2029.0, 2039.0, 2053.0, 2063.0, 2069.0,
+2081.0, 2083.0, 2087.0, 2089.0, 2099.0, 2111.0, 2113.0, 2129.0,
+2131.0, 2137.0, 2141.0, 2143.0, 2153.0, 2161.0, 2179.0, 2203.0,
+2207.0, 2213.0, 2221.0, 2237.0, 2239.0, 2243.0, 2251.0, 2267.0,
+2269.0, 2273.0, 2281.0, 2287.0, 2293.0, 2297.0, 2309.0, 2311.0,
+2333.0, 2339.0, 2341.0, 2347.0, 2351.0, 2357.0, 2371.0, 2377.0,
+2381.0, 2383.0, 2389.0, 2393.0, 2399.0, 2411.0, 2417.0, 2423.0,
+2437.0, 2441.0, 2447.0, 2459.0, 2467.0, 2473.0, 2477.0, 2503.0,
+2521.0, 2531.0, 2539.0, 2543.0, 2549.0, 2551.0, 2557.0, 2579.0,
+2591.0, 2593.0, 2609.0, 2617.0, 2621.0, 2633.0, 2647.0, 2657.0,
+2659.0, 2663.0, 2671.0, 2677.0, 2683.0, 2687.0, 2689.0, 2693.0,
+2699.0, 2707.0, 2711.0, 2713.0, 2719.0, 2729.0, 2731.0, 2741.0,
+2749.0, 2753.0, 2767.0, 2777.0, 2789.0, 2791.0, 2797.0, 2801.0,
+2803.0, 2819.0, 2833.0, 2837.0, 2843.0, 2851.0, 2857.0, 2861.0,
+2879.0, 2887.0, 2897.0, 2903.0, 2909.0, 2917.0, 2927.0, 2939.0,
+2953.0, 2957.0, 2963.0, 2969.0, 2971.0, 2999.0, 3001.0, 3011.0,
+3019.0, 3023.0, 3037.0, 3041.0, 3049.0, 3061.0, 3067.0, 3079.0,
+3083.0, 3089.0, 3109.0, 3119.0, 3121.0, 3137.0, 3163.0, 3167.0,
+3169.0, 3181.0, 3187.0, 3191.0, 3203.0, 3209.0, 3217.0, 3221.0,
+3229.0, 3251.0, 3253.0, 3257.0, 3259.0, 3271.0, 3299.0, 3301.0,
+3307.0, 3313.0, 3319.0, 3323.0, 3329.0, 3331.0, 3343.0, 3347.0,
+3359.0, 3361.0, 3371.0, 3373.0, 3389.0, 3391.0, 3407.0, 3413.0,
+3433.0, 3449.0, 3457.0, 3461.0, 3463.0, 3467.0, 3469.0, 3491.0,
+3499.0, 3511.0, 3517.0, 3527.0, 3529.0, 3533.0, 3539.0, 3541.0,
+3547.0, 3557.0, 3559.0, 3571.0, 3581.0, 3583.0, 3593.0, 3607.0,
+3613.0, 3617.0, 3623.0, 3631.0, 3637.0, 3643.0, 3659.0, 3671.0,
+};
+
+static const PackedTypeDesc g_rgbaDesc = {
+        "vec4", 4, { ".r", ".g", ".b", ".a" },
+        "vec4(1.0, 1.0, 1.0, 1.0)"
+};
+
+static const PackedTypeDesc g_vec4Desc = {
+        "vec4", 4, { ".x", ".y", ".z", ".w" },
+        "vec4(1.0, 1.0, 1.0, 1.0)"
+};
+
+static const PackedTypeDesc g_floatDesc = {
+        "float", 1, { "" },
+        "1.0"
+};
+
+static const PackedTypeDesc g_sampler2DDesc = {
+        "sampler2D", 4, { "" },
+        "vec4(1.0, 1.0, 1.0, 1.0)"
+};
+
+static const char *g_vectorComponents[MAX_COMPONENTS] = {
+        ".x", ".y", ".z", ".w"
+};
+
+//static const MyVector4 one = { 1.0f, 1.0f, 1.0f, 1.0f };
+//static const MyVector4 zero = { 0.0f, 0.0f, 0.0f, 0.0f };
+
+static const MyVector4 g_positionBuffer[NUM_VERTICES] = {
+        { -1.0, -1.0, 0.0, 0.0 },
+        { -1.0, 1.0, 0.0, 0.0 },
+        { 1.0, 1.0, 0.0, 0.0 }
+};
+
+static GLuint g_elementVBO;
+static const GLushort g_elementBuffer[NUM_VERTICES] = { 0, 1, 2 };
+static const char *g_vertexShaderText =
+        "#version 110 \n"
+        " \n"
+        "void main() \n"
+        "{ \n"
+        "   gl_Position = gl_Vertex; \n"
+        "} \n";
+
+//static char *g_geometryShaderText = NULL;
+
+static const char *g_fragmentShaderText =
+        "#version 110 \n"
+        " \n"
+        "void main()\n"
+        "{ \n"
+        "    gl_FragData[0] = vec4(1.0, 2.0, 3.0, 4.0);\n"
+        "    gl_FragData[1] = vec4(5.0, 6.0, 7.0, 8.0);\n"
+        "} \n";
+
+static GLuint g_program = 0;
+static GLfloat *g_expected;
+
+
+/**
+ * Format a string which contains the requested declaration for the variable.
+ * The desc structure will describe the variable, i.e. type, if it is an array,
+ * and how large the array is.
+ *
+ * \param desc Descriptor of the variable referenced. See PackedDesc.
+ * \param tmpStrSize Temporary string size for safe string formatting.
+ * \param tmpStr Temporary staging string.
+ * \param strSize Size of the formatted string.
+ * \param str An allocated temporary string for this function to populate with
+ *            the reference expression.
+ * \return Pointer to str.
+ */
+
+static char *
+get_packed_decl(const PackedDesc *desc,
+                const unsigned tmpStrSize,
+                char *tmpStr,
+                const unsigned strSize,
+                char *str)
+{
+        int i = 0;
+
+        if (NULL == str) {
+                return "";
+        }
+
+        if (desc->isArray) {
+                snprintf(tmpStr, tmpStrSize,
+                         "%s %s %s%s%u%s;\n",
+                         desc->semanticName,
+                         desc->typeDesc->typeName,
+                         desc->variableName,
+                         desc->isArray ? "[" : "",
+                         desc->count,
+                         desc->isArray ? "]" : "");
+                strncat(str, tmpStr, strSize - strlen(str));
+        } else if (desc->count > 1) {
+                for (i = 0; i < desc->count; i++) {
+                        snprintf(tmpStr, tmpStrSize,
+                                 "%s %s %s%u;\n",
+                                 desc->semanticName,
+                                 desc->typeDesc->typeName,
+                                 desc->variableName, i);
+                        strncat(str, tmpStr, strSize - strlen(str));
+                }
+        } else {
+                snprintf(tmpStr, tmpStrSize,
+                         "%s %s %s;\n",
+                         desc->semanticName,
+                         desc->typeDesc->typeName,
+                         desc->variableName);
+                strncat(str, tmpStr, strSize - strlen(str));
+        }
+
+        return str;
+}
+
+
+/**
+ * Format a string which contains the requested referenced value. The desc
+ * structure will describe the variable which is sub-indexed by arrayIndex.
+ * To address individual components, a componentIndex translated to (x,y,z,w)
+ * is applied to the textual representation.
+ *
+ * \param desc Descriptor of the variable referenced. See PackedDesc.
+ * \param arrayIndex Sub-index into the variable described by desc.
+ * \param componentIndex Component of the variable, depends on the desc
+ *                       configuration for the variable.
+ * \param str An allocated temporary string for this function to populate with
+ *            the reference expression.
+ * \return Pointer to str.
+ */
+
+static char *
+get_packed_reference(const PackedDesc *desc,
+                     const unsigned arrayIndex,
+                     const unsigned componentIndex,
+                     const unsigned strSize,
+                     char *str)
+{
+        if (g_debugMask & DEBUG_INPUT) {
+                printf("desc=%p componentIndex=%u\n", desc, componentIndex);
+                printf("desc->typeDesc=%p\n", desc->typeDesc);
+                printf("desc->typeDesc->componentNames=%p\n",
+                       desc->typeDesc->componentNames);
+        }
+
+        if (componentIndex > desc->typeDesc->numComponents) {
+                snprintf(str, strSize, "%s", g_floatDesc.defaultValue);
+                return str;
+        }
+
+        if (arrayIndex > desc->count) {
+                snprintf(str, strSize, "%s%s",
+                         desc->typeDesc->defaultValue,
+                         desc->typeDesc->componentNames[componentIndex]);
+                return str;
+        }
+
+        if (desc->count > 1) {
+                snprintf(str, strSize, "%s%s%u%s%s",
+                         desc->variableName,
+                         desc->isArray ? "[" : "",
+                         arrayIndex,
+                         desc->isArray ? "]" : "",
+                         desc->typeDesc->componentNames[componentIndex]);
+        } else {
+                snprintf(str, strSize, "%s%s",
+                         desc->variableName,
+                         desc->typeDesc->componentNames[componentIndex]);
+        }
+
+        return str;
+}
+
+
+/**
+ * Set up an FBO for the test.
+ *
+ * \param colorTarget Color attachment index relative to GL_COLOR_ATTACHMENT0
+ * \param internalFormat Internal GL format for the pixel data.
+ * \param format GL format for the pixel data.
+ * \param formatType Data type for the pixel data. 
+ * \param width Width of the requested FBO backing store.
+ * \param height Height of the requested FBO backing store.
+ * \param fbo FBO object allocated to bind with this texture.
+ * \param pTexture A pointer to the GLuint identifier storage for the allocated
+ *                 texture.
+ * \return true if the setup succeeded without error
+ *         false otherwise.
+ */
+
+static bool
+setup_fbo_2d(const GLuint colorTarget,
+             const GLenum internalFormat,
+             const GLenum format,
+             const GLenum formatType,
+             const GLuint width,
+             const GLuint height,
+             const GLuint fbo,
+             GLuint *pTexture)
+{
+        GLenum status;
+
+        glGenTextures(1, pTexture);
+        glBindTexture(GL_TEXTURE_2D, *pTexture);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0,
+                     format, formatType, NULL);
+
+        /*
+         * Framebuffer object is implied to be bound.
+         */
+        glFramebufferTexture2D(GL_FRAMEBUFFER,
+                               GL_COLOR_ATTACHMENT0 + colorTarget,
+                               GL_TEXTURE_2D, *pTexture, 0);
+
+        if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                fprintf(stderr, "Failed to create FBO %u.\n", colorTarget);
+                piglit_report_result(PIGLIT_FAIL);
+                return false;
+        }
+
+        status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+        if (status != GL_FRAMEBUFFER_COMPLETE) {
+                fprintf(stderr, "Incomplete fbo for format %s.%s (status %s)\n",
+                        piglit_get_gl_enum_name(internalFormat),
+                        piglit_get_gl_enum_name(format),
+                        piglit_get_gl_enum_name(status));
+                piglit_report_result(PIGLIT_FAIL);
+                return false;
+        }
+
+        return true;
+}
+
+
+/**
+ * Set up the vertex buffer objects and element buffer objects for the
+ * test. To ensure constant values across the primitive, populate the same
+ * prime attribute value in each vertex.
+ *
+ * \return true if the setup succeeded without error
+ *         false otherwise.
+ */
+
+static bool
+setup_vertex_element_buffers(void)
+{
+        MyVector4 attrib[NUM_VERTICES];
+        GLuint buf;
+        GLuint attribLoc;
+        int i;
+
+        /*
+         * Setup the gl_Position attribute buffer.
+         */
+        glGenBuffers(1, &buf);
+        glBindBuffer(GL_ARRAY_BUFFER, buf);
+        glBufferData(GL_ARRAY_BUFFER, NUM_VERTICES*sizeof(MyVector4),
+                     g_positionBuffer, GL_STATIC_DRAW);
+
+        attribLoc = glGetAttribLocation(g_program, "InPosition");
+        glEnableVertexAttribArray(attribLoc);
+        glVertexAttribPointer(attribLoc, 4, GL_FLOAT, GL_FALSE, 0, 0);
+        glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+        /*
+         * Setup the vertex buffer objects.
+         */
+        for (i = 0; i < g_maxVertexAttribs - 1; i++) {
+                char strTemp[16];
+
+                if ((i + 1) * MAX_COMPONENTS < NUM_PRIMES) {
+                        attrib[0].x = attrib[1].x =
+                        attrib[2].x = g_primes[i*MAX_COMPONENTS + 0];
+                        attrib[0].y = attrib[1].y =
+                        attrib[2].y = g_primes[i*MAX_COMPONENTS + 1];
+                        attrib[0].z = attrib[1].z =
+                        attrib[2].z = g_primes[i*MAX_COMPONENTS + 2];
+                        attrib[0].w = attrib[1].w =
+                        attrib[2].w = g_primes[i*MAX_COMPONENTS + 3];
+                } else {
+                        attrib[0].x = attrib[1].x = attrib[2].x = 1.0;
+                        attrib[0].y = attrib[1].y = attrib[2].y = 1.0;
+                        attrib[0].z = attrib[1].z = attrib[2].z = 1.0;
+                        attrib[0].w = attrib[1].w = attrib[2].w = 1.0;
+                }
+
+                glGenBuffers(1, &buf);
+                glBindBuffer(GL_ARRAY_BUFFER, buf);
+                glBufferData(GL_ARRAY_BUFFER, sizeof(MyVector4)*NUM_VERTICES,
+                             attrib, GL_STATIC_DRAW);
+
+                snprintf(strTemp, sizeof(strTemp), "InValue%u", i);
+                attribLoc = glGetAttribLocation(g_program, strTemp);
+                glEnableVertexAttribArray(attribLoc);
+                glVertexAttribPointer(attribLoc, 4, GL_FLOAT, GL_FALSE, 0, 0);
+                glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                         fprintf(stderr, "Failed to create VBO %u.\n", i);
+                         piglit_report_result(PIGLIT_FAIL);
+                         return false;
+                }
+        }
+
+        /*
+         * Setup the element buffers.
+         */
+        glGenBuffers(1, &g_elementVBO);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_elementVBO);
+        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_elementBuffer),
+                     g_elementBuffer, GL_STATIC_DRAW);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+        if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                fprintf(stderr, "Failed to create IBO.\n");
+                piglit_report_result(PIGLIT_FAIL);
+                return false;
+        }
+
+        return true;
+}
+
+
+/**
+ * Build a GLSL shader for the given packedInput, packedUniform, packedOutput
+ * descriptors. Each GLSL shader will have inputs, uniforms, and outputs. To
+ * ensure all bound resources are made resident, this shader must use all the
+ * inputs, uniforms, and outputs requested by the describing PackedDesc
+ * structures. If the number of outputs are smaller than the number of inputs,
+ * the inputs will map as follows:
+ *
+ *   outputs[0] = uniforms[0] inputs[0] * ... * inputs[(i % numOutputs == 0)]
+ *
+ * where i >= numOutputs and represents a subsequent pass of inputs up
+ * until i == numInputs.
+ *
+ * If the number of outputs is larger than the number of inputs, the inputs
+ * and uniforms are referenced until exhausted. Once exhausted, an input
+ * or uniform will default to 1.0 to provide a multiplicative identity.
+ *
+ * \param packedInput A descriptor of the type and number of inputs to this
+ *                    shader.
+ * \param packedUniform A descriptor of the type and number of uniforms bound
+ *                      to the shader.
+ * \param packedOutput A descriptor of the type and number of outputs for this
+ *                     shader.
+ * \param outputDefaultSystemValue A required output for the GLSL shader.
+ *                                 i.e. gl_Position for a vertex shader.
+ * \param defaultSystemValueDecl Pre-defined output declaration.
+ * \param defaultSystemValue Pre-defined output value.
+ * \param pShaderText Output pointer for the allocated and generated shader
+ *                    text.
+ * \return true if the setup succeeded without error
+ *         false otherwise.
+ */
+
+static bool
+build_reduce_glsl_shader(const PackedDesc *packedInput,
+                         const PackedDesc *packedUniform,
+                         const PackedDesc *packedOutput,
+                         const char *outputDefaultSystemValue,
+                         const char *defaultSystemValueDecl,
+                         const char *defaultSystemValue,
+                         char **pShaderText)
+{
+        char *shaderTempText[TEMP_TEXT_SIZE];
+        char *shaderText = NULL;
+        unsigned shaderTextFree = 0;
+        unsigned numScalarInputs =
+                packedInput->count * packedInput->typeDesc->numComponents;
+        unsigned numScalarUniforms =
+                packedUniform->count * packedUniform->typeDesc->numComponents;
+        unsigned numScalarOutputs =
+                packedOutput->count * packedOutput->typeDesc->numComponents;
+
+        /* Prepare for reduction of input contributions to the output slots */
+        unsigned di_do = numScalarInputs / numScalarOutputs;
+        unsigned r = numScalarInputs % numScalarOutputs;
+        int i, j, k, l;
+
+        if (g_debugMask & DEBUG_INPUT) {
+                printf("di_do=%u r=%u\n", di_do, r);
+        }
+
+        for (i = 0; i < sizeof(shaderTempText) / sizeof(char *); i++) {
+                shaderTempText[i] = calloc(MAX_SHADER_LINE_CHARS, sizeof(char));
+
+                if (NULL == shaderTempText[i]) {
+                        fprintf(stderr,
+                                "Failed to allocate shader temporary text area\n");
+                        piglit_report_result(PIGLIT_FAIL);
+                        return false;
+                }
+        }
+
+        shaderText = calloc(MAX_SHADER_TEXT_CHARS, sizeof(char));
+        if (NULL == shaderText) {
+                fprintf(stderr, "Failed to allocate space for shader text\n");
+                piglit_report_result(PIGLIT_FAIL);
+                return false;
+        }
+
+        shaderTextFree = MAX_SHADER_TEXT_CHARS;
+
+        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+        "%s\n", GLSL_VERSION);
+        strncat(shaderText, shaderTempText[0], shaderTextFree);
+        shaderTextFree -= strlen(shaderTempText[0]);
+
+        /*
+         * Declare the default system value input.
+         */
+        if (outputDefaultSystemValue) {
+                snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                         "%s\n", defaultSystemValueDecl);
+                strncat(shaderText, shaderTempText[0], shaderTextFree);
+                shaderTextFree -= strlen(defaultSystemValueDecl);
+        }
+
+        /*
+         * Declare the input attributes.
+         */
+        get_packed_decl(packedInput,
+        MAX_SHADER_LINE_CHARS, shaderTempText[0],
+        MAX_SHADER_TEXT_CHARS, shaderText);
+
+        /*
+         * Declare the uniform samplers using the vertex texture functionality.
+         */
+        get_packed_decl(packedUniform,
+        MAX_SHADER_LINE_CHARS, shaderTempText[0],
+        MAX_SHADER_TEXT_CHARS, shaderText);
+
+        /*
+         * Declare the outputs.
+         */
+        if (packedOutput->semanticName) {
+                get_packed_decl(packedOutput,
+                                MAX_SHADER_LINE_CHARS, shaderTempText[0],
+                                MAX_SHADER_TEXT_CHARS, shaderText);
+        }
+
+        shaderTextFree = MAX_SHADER_TEXT_CHARS - strlen(shaderText);
+
+        /*
+         * Begin main program block.
+         */
+        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                 "void main()\n{\n");
+        strncat(shaderText, shaderTempText[0], shaderTextFree);
+        shaderTextFree -= strlen(shaderTempText[0]);
+
+        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                 "  vec4 texel;\n");
+        strncat(shaderText, shaderTempText[0], shaderTextFree);
+        shaderTextFree -= strlen(shaderTempText[0]);
+
+        /*
+         * Passthru the attributes to the outputs.
+         */
+        i = j = k = l = 0;
+
+        while (i < numScalarOutputs)  {
+                char *resultStr = " ", *srcStr = " ";
+
+                if ((i < packedUniform->count*packedUniform->typeDesc->numComponents) &&
+                    (j % packedUniform->typeDesc->numComponents) == 0) {
+                        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                                 "  texel = texture2D(Texture[%u], vec2(0.0, 0.0));\n",
+                                 i / packedUniform->typeDesc->numComponents);
+                        strncat(shaderText, shaderTempText[0], shaderTextFree);
+                        shaderTextFree -= strlen(shaderTempText[0]);
+                }
+
+                /*
+                 * Iterate through the contents of the texel
+                 */
+                resultStr = get_packed_reference(packedOutput,
+                                (i / packedOutput->typeDesc->numComponents),
+                                (i % packedOutput->typeDesc->numComponents),
+                                MAX_SHADER_LINE_CHARS,
+                                shaderTempText[1]);
+
+                /* Reset the temp string to avoid cumulative contributions. */
+                memset(shaderTempText[2], 0, MAX_SHADER_LINE_CHARS);
+
+                if (k < numScalarInputs) {
+                        /* Reduce the scalar multiple of the input contributions. */
+                        for (l = 0; l < di_do; l++) {
+                                srcStr = get_packed_reference(packedInput,
+                                                (i + (l * numScalarOutputs)) /
+                                                packedInput->typeDesc->numComponents,
+                                                (i + (l * numScalarOutputs)) %
+                                                packedInput->typeDesc->numComponents,
+                                                MAX_SHADER_LINE_CHARS,
+                                                shaderTempText[3]);
+
+                                if (l > 0) {
+                                        strncat(shaderTempText[2], " * ",
+                                                MAX_SHADER_LINE_CHARS - strlen(shaderTempText[2]));
+                                }
+                                strncat(shaderTempText[2], srcStr,
+                                        MAX_SHADER_LINE_CHARS - strlen(shaderTempText[2]));
+
+                                k++;
+                        }
+
+                        /* Reduce the remaining scalar contributions until exhausted */
+                        if (r) {
+                                srcStr = get_packed_reference(packedInput,
+                                                (i + (l * numScalarOutputs)) /
+                                                packedInput->typeDesc->numComponents,
+                                                (i + (l * numScalarOutputs)) %
+                                                packedInput->typeDesc->numComponents,
+                                                MAX_SHADER_LINE_CHARS,
+                                                shaderTempText[3]);
+                                if (l > 0) {
+                                        strncat(shaderTempText[2], " * ",
+                                                MAX_SHADER_LINE_CHARS - strlen(shaderTempText[2]));
+                                }
+                                strncat(shaderTempText[2], srcStr,
+                                        MAX_SHADER_LINE_CHARS - strlen(shaderTempText[2]));
+                                k++;
+                                r--;
+                        }
+                        srcStr = shaderTempText[2];
+                } else {
+                        snprintf(shaderTempText[2], MAX_SHADER_LINE_CHARS,
+                                 "%s", g_floatDesc.defaultValue);
+                        srcStr = shaderTempText[2];
+                }
+
+                if (j < numScalarUniforms) {
+                        if ((g_debugMask & DEBUG_INPUT) && outputDefaultSystemValue) {
+                                snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                                         "  %s = texel%s;\n", resultStr,
+                                         g_vectorComponents[j %
+                                                packedUniform->typeDesc->numComponents]);
+                        } else {
+                                snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                                         "  %s = texel%s * %s;\n", resultStr,
+                                         g_vectorComponents[j %
+                                                packedUniform->typeDesc->numComponents],
+                                         srcStr);
+                        }
+                        strncat(shaderText, shaderTempText[0], shaderTextFree);
+                        shaderTextFree -= strlen(shaderTempText[0]);
+                        j++;
+                } else {
+                        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                                 "  %s = %s%s * %s;\n", resultStr,
+                                 packedUniform->typeDesc->defaultValue,
+                                 g_vectorComponents[0],
+                                 srcStr);
+                        strncat(shaderText, shaderTempText[0], shaderTextFree);
+                        shaderTextFree -= strlen(shaderTempText[0]);
+                }
+
+                /* Increment to the next output. */
+                i++;
+        }
+
+        /*
+         * End the main program block.
+         */
+        if (outputDefaultSystemValue) {
+                snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS,
+                         "  %s = %s;\n",
+                         outputDefaultSystemValue, defaultSystemValue);
+                strncat(shaderText, shaderTempText[0], shaderTextFree);
+                shaderTextFree -= strlen(shaderTempText[0]);
+        }
+
+        snprintf(shaderTempText[0], MAX_SHADER_LINE_CHARS, "}\n");
+        strncat(shaderText, shaderTempText[0], shaderTextFree);
+        shaderTextFree -= strlen(shaderTempText[0]);
+
+        for (i = 0; i < sizeof(shaderTempText) / sizeof(char *); i++) {
+                if (shaderTempText[i]) {
+                        free(shaderTempText[i]);
+                }
+        }
+
+        *pShaderText = shaderText;
+        return true;
+}
+
+
+/**
+ * Core piglit_display callback. This function will bind the FBOs, Textures,
+ * VBOs, IBOs, and draw a primitive using the GLSL shader generated by
+ * build_reduce_glsl_shader.
+ *
+ * Once rendered, a pixel from each of the color attachments is read back and
+ * compared to the final expression value stored in g_expected given the
+ * associated color attachment index.
+ *
+ * \return PIGLIT_PASS if the setup succeeded without error
+ *         PIGLIT_FAIL otherwise.
+ */
+
+enum piglit_result
+piglit_display(void)
+{
+        GLuint vao = 0;
+        GLuint fbo = 0;
+        GLuint *fboTextures = NULL;
+        GLenum *colorBuffers = NULL;
+        GLuint attribLoc;
+        GLfloat result[4];
+        int i = 0;
+
+        /*
+         * Reserve memory for the FBO texture objects.
+         */
+        fboTextures = calloc(g_maxColorAttachments, sizeof(GLuint));
+
+        if (NULL == fboTextures) {
+                fprintf(stderr, "Failed to create FBO texture object container.\n");
+                goto fail;
+        }
+
+        /*
+         * Build the color attachments
+         */
+        colorBuffers = calloc(g_maxColorAttachments, sizeof(GLenum));
+
+        if (NULL == colorBuffers) {
+                fprintf(stderr, "Failed to create draw buffers descriptor.\n");
+                goto fail;
+        }
+
+        /*
+         * Generate an FBO container to hold the color attachment hierarchy.
+         */
+        glGenFramebuffers(1, &fbo);
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+        for (i = 0; i < g_maxColorAttachments; i++) {
+                setup_fbo_2d(i, GL_RGBA32F, GL_RGBA, GL_FLOAT,
+                             BUFFER_WIDTH, BUFFER_HEIGHT,
+                             fbo, &fboTextures[i]);
+                colorBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
+        }
+
+        /*
+         * Build the textures sampled by the shaders
+         */
+        for (i = 0; i < g_maxCombinedTextureImageUnits; i++) {
+                GLuint tex;
+                GLuint uniformLoc;
+                char strTemp[16];
+
+                glGenTextures(1, &tex);
+                glActiveTexture(GL_TEXTURE0 + i);
+                glBindTexture(GL_TEXTURE_2D, tex);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+                if (((i+1) * MAX_COMPONENTS) > sizeof(g_primes)) {
+                        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0,
+                                     GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+                } else {
+                        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0,
+                                     GL_RGBA, GL_FLOAT,
+                                     &g_primes[i * MAX_COMPONENTS]);
+                }
+
+                if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                        fprintf(stderr, "Failed to create texture %u.\n", i);
+                        goto fail;
+                }
+
+                snprintf(strTemp, sizeof(strTemp), "Texture[%u]", i);
+                uniformLoc = glGetUniformLocation(g_program, strTemp);
+                glUniform1i(uniformLoc, i);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                        fprintf(stderr, "Unable to assign texture %u uniform.\n", i);
+                        goto fail;
+                }
+        }
+
+        /*
+         * Setup the vertex and element buffers for drawing our points.
+         */
+        if (g_drawMode != DRAW_IMMEDIATE) {
+                glGenVertexArrays(1, &vao);
+                glBindVertexArray(vao);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                        fprintf(stderr, "Unable to create VAO.\n");
+                        goto fail;
+                }
+
+                setup_vertex_element_buffers();
+        }
+
+        /*
+         * Setup the raster state.
+         */
+        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        glDrawBuffers(g_maxColorAttachments, colorBuffers);
+
+        if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                fprintf(stderr, "Unable to assign draw buffers.\n");
+                goto fail;
+        }
+
+        glClearColor(0.0, 1.0, 0.0, 0.0);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        /*
+         * Bind the vertex array and enable each attribute.
+         */
+        if (g_drawMode != DRAW_IMMEDIATE) {
+                glBindVertexArray(vao);
+                attribLoc = glGetAttribLocation(g_program, "InPosition");
+                glEnableVertexAttribArray(attribLoc);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                        fprintf(stderr,
+                                "Unable to enable vertex array attribute %u.\n",
+                                attribLoc);
+                        goto fail;
+                }
+
+                /*
+                 * Enable the rest of the attributes.
+                 */
+                for (i = 0; i < g_maxVertexAttribs - 1; i++) {
+                        char strTemp[16];
+
+                        snprintf(strTemp, sizeof(strTemp), "InValue%u", i);
+                        attribLoc = glGetAttribLocation(g_program, strTemp);
+                        glEnableVertexAttribArray(attribLoc);
+
+                        if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                                fprintf(stderr,
+                                        "Unable to enable vertex array attribute %u.\n",
+                                        attribLoc);
+                                goto fail;
+                        }
+                }
+
+                if (g_drawMode == DRAW_ARRAYS_VBO) {
+                        if (g_debugMask & DEBUG_DRAW) {
+                                fprintf(stderr, "Draw mode DRAW_ARRAYS_VBO\n");
+                        }
+                        glDrawArrays(GL_TRIANGLES, 0, NUM_VERTICES);
+                }
+
+                if (g_drawMode == DRAW_ELEMENTS_VBO) {
+                        if (g_debugMask & DEBUG_DRAW) {
+                                fprintf(stderr,
+                                        "Draw mode DRAW_ELEMENTS_VBO\n");
+                        }
+                        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_elementVBO);
+                        glDrawElements(GL_TRIANGLES, NUM_VERTICES,
+                                       GL_UNSIGNED_SHORT, 0);
+                        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+                }
+
+                /*
+                 * Blindly reset all the attributes.
+                 */
+                for (i = 0; i < g_maxVertexAttribs; i++) {
+                        glDisableVertexAttribArray(i);
+                }
+        } else {
+                glBegin(GL_TRIANGLES);
+                glVertex3f(-1.0, -1.0, 0.0);
+                glVertex3f(-1.0, 1.0, 0.0);
+                glVertex3f(1.0, 1.0, 0.0);
+                glEnd();
+        }
+
+        if (!piglit_check_gl_error(GL_NO_ERROR))
+                goto fail;
+
+        /*
+         * Read back the FBO contents.
+         */
+
+        /*
+         * Disable color clamping so we don't encounter result
+         * collisions attempting to use a normalized color space.
+         *
+         * Requires OpenGL 3.0
+         */
+        glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
+
+        for (i = 0; i < g_maxColorAttachments; i++) {
+                if (g_debugMask & DEBUG_READBACK) {
+                        printf("GL_READ_FRAMEBUFFER <- fbo=%u\n", fbo);
+                }
+
+                glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR))
+                        goto fail;
+
+                glFramebufferTexture2D(GL_READ_FRAMEBUFFER,
+                                       GL_COLOR_ATTACHMENT0,
+                                       GL_TEXTURE_2D,
+                                       fboTextures[i],
+                                       0);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR))
+                        goto fail;
+
+                glPixelStorei(GL_PACK_ALIGNMENT, 1);
+                glReadBuffer(GL_COLOR_ATTACHMENT0);
+
+                if (!piglit_check_gl_error(GL_NO_ERROR))
+                        goto fail;
+
+                glReadPixels(0, BUFFER_HEIGHT - 1,
+                             1, 1, GL_RGBA, GL_FLOAT, result);
+
+                if ((g_expected[(i * MAX_COMPONENTS) + 0] != result[0]) ||
+                    (g_expected[(i * MAX_COMPONENTS) + 1] != result[1]) ||
+                    (g_expected[(i * MAX_COMPONENTS) + 2] != result[2]) ||
+                    (g_expected[(i * MAX_COMPONENTS) + 3] != result[3])) {
+                        fprintf(stderr, "GL_COLOR_ATTACHMENT%u: expected "
+                                        "(%f, %f, %f, %f) != (%f, %f, %f, %f)\n",
+                                i, g_expected[(i * MAX_COMPONENTS) + 0],
+                                g_expected[(i * MAX_COMPONENTS) + 1],
+                                g_expected[(i * MAX_COMPONENTS) + 2],
+                                g_expected[(i * MAX_COMPONENTS) + 3],
+                                result[0], result[1], result[2], result[3]);
+
+                        goto fail;
+                }
+        }
+
+        piglit_present_results();
+
+        /*
+         * Cleanup the allocations.
+         */
+        free(colorBuffers);
+        free(fboTextures);
+
+        piglit_report_result(PIGLIT_PASS);
+
+        return PIGLIT_PASS;
+
+fail:
+
+        if (colorBuffers)
+                free(colorBuffers);
+
+        if (fboTextures)
+                free(fboTextures);
+
+        piglit_report_result(PIGLIT_FAIL);
+
+        return PIGLIT_FAIL;
+}
+
+
+/**
+ * Core piglit_init callback. This function will query the implementation for
+ * the maximum number of supported resources. We attempt to create a GLSL
+ * shader which references all supported resources bindable within the GL 3.0
+ * specification.
+ *
+ * Each resource will be populated with a unique prime value. This function is
+ * also responsible for calculating the expected value and storing this in
+ * the g_expected array.
+ *
+ * Note: If the number of primes is insufficient to assign a unique prime to
+ *       each resource, we will exit with PIGLIT_SKIP.
+ *
+ * Results: PIGLIT_PASS if the setup succeeded without error
+ *          PIGLIT_FAIL otherwise.
+ */
+
+void
+piglit_init(int argc, char **argv)
+{
+        GLfloat *var;
+        PackedDesc vsInput = { "in", "InValue", false, 0, &g_vec4Desc };
+        PackedDesc vsUniform = { "uniform", "Texture", true, 0, &g_sampler2DDesc };
+        PackedDesc vsOutput = { "out", "Variable", true, 0, &g_floatDesc };
+        PackedDesc fsInput = { "in", "Variable", true, 0, &g_floatDesc };
+        PackedDesc fsUniform = { "uniform", "Texture", true, 0, &g_sampler2DDesc };
+        PackedDesc fsOutput = { NULL, g_fragData, true, 0, &g_rgbaDesc };
+        char shaderTempText[MAX_SHADER_LINE_CHARS];
+        char *vertexShaderText = (char *)g_vertexShaderText;
+        char *fragmentShaderText = (char *)g_fragmentShaderText;
+        int i = 0;
+
+        piglit_require_gl_version(30);
+
+        for (i = 1; i < argc; i++) {
+                if (strcmp(argv[i], "-drawArraysVBO") == 0) {
+                        g_drawMode = DRAW_ARRAYS_VBO;
+                }
+                if (strcmp(argv[i], "-drawElementsVBO") == 0) {
+                        g_drawMode = DRAW_ELEMENTS_VBO;
+                }
+                if (strcmp(argv[i], "-drawImmediate") == 0) {
+                        g_drawMode = DRAW_IMMEDIATE;
+                }
+                if (strcmp(argv[i], "-debugInput") == 0) {
+                        g_debugMask |= DEBUG_INPUT;
+                }
+                if (strcmp(argv[i], "-debugReadback") == 0) {
+                        g_debugMask |= DEBUG_READBACK;
+                }
+                if (strcmp(argv[i], "-debugShaders") == 0) {
+                        g_debugMask |= DEBUG_SHADERS;
+                }
+                if (strcmp(argv[i], "-debugDraw") == 0) {
+                        g_debugMask |= DEBUG_DRAW;
+                }
+                if (strcmp(argv[i], "-dontClampMaxVaryings") == 0) {
+                        g_debugMask |= DEBUG_DONT_CLAMP_MAX_VARYINGS;
+                }
+        }
+
+        /*
+         * Query the sampler capabilities.
+         */
+        glGetIntegerv(GL_MAX_VARYING_FLOATS, &g_maxVaryingFloats);
+        glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &g_maxVertexAttribs);
+        glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
+                      &g_maxVertexTextureImageUnits);
+        glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS,
+                      &g_maxTextureImageUnits);
+        glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+                      &g_maxCombinedTextureImageUnits);
+
+        printf("GL_MAX_VARYING_FLOATS: %d\n", g_maxVaryingFloats);
+        printf("GL_MAX_VERTEX_ATTRIBS: %d\n", g_maxVertexAttribs);
+        printf("GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %d\n",
+               g_maxVertexTextureImageUnits);
+        printf("GL_MAX_TEXTURE_IMAGE_UNITS: %d\n",
+               g_maxTextureImageUnits);
+        printf("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d\n",
+               g_maxCombinedTextureImageUnits);
+
+        /*
+         * Query the render target capabilities.
+         */
+        glGetIntegerv(GL_AUX_BUFFERS, &g_maxAuxBuffers);
+        glGetIntegerv(GL_MAX_DRAW_BUFFERS, &g_maxDrawBuffers);
+        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &g_maxColorAttachments);
+
+        if (g_maxColorAttachments == 1) {
+                fsOutput.variableName = g_fragColor;
+        }
+
+        printf("GL_AUX_BUFFERS: %d\n", g_maxAuxBuffers);
+        printf("GL_MAX_DRAW_BUFFERS: %d\n", g_maxDrawBuffers);
+        printf("GL_MAX_COLOR_ATTACHMENTS: %d\n", g_maxColorAttachments);
+
+        if ((g_maxColorAttachments * MAX_COMPONENTS > NUM_PRIMES) ||
+            (g_maxVaryingFloats > NUM_PRIMES) ||
+            (g_maxVertexAttribs * MAX_COMPONENTS > NUM_PRIMES) ||
+            (g_maxVertexTextureImageUnits * MAX_COMPONENTS > NUM_PRIMES) ||
+            (g_maxTextureImageUnits * MAX_COMPONENTS > NUM_PRIMES)) {
+                fprintf(stderr, "Unable to uniquely represent a result path.\n");
+                piglit_report_result(PIGLIT_SKIP);
+        }
+
+        /*
+         * Clamp the max varyings by default to work around large array
+         * issues with some GLSL implementations.
+         */
+        if ((g_debugMask & DEBUG_DONT_CLAMP_MAX_VARYINGS) == 0) {
+                g_maxVaryingFloats = MIN2(g_maxVaryingFloats, 32);
+                fprintf(stderr, "Clamped max varying floats to %u.\n",
+                g_maxVaryingFloats);
+        }
+
+        if (g_drawMode == DRAW_IMMEDIATE) {
+                g_maxVertexAttribs = 1;
+                fprintf(stderr,
+                        "Immediate mode selected, using only one vertex attrib.\n");
+        }
+
+        /*
+         * Build the shaders based upon the queried limits.
+         */
+        vsInput.count = g_maxVertexAttribs - 1;
+        vsUniform.count = g_maxVertexTextureImageUnits;
+        vsOutput.count = g_maxVaryingFloats;
+        if (!build_reduce_glsl_shader(&vsInput,
+                                      &vsUniform,
+                                      &vsOutput,
+                                      "gl_Position",
+                                      "in vec4 InPosition;",
+                                      "InPosition",
+                                      &vertexShaderText)) {
+                fprintf(stderr, "Failed to build GLSL vertex shader\n");
+                piglit_report_result(PIGLIT_SKIP);
+        }
+
+        if (g_debugMask & DEBUG_SHADERS) {
+                printf("vertexShaderText:\n%s", vertexShaderText);
+        }
+
+        fsInput.count = g_maxVaryingFloats;
+        fsUniform.count = g_maxTextureImageUnits;
+        fsOutput.count = g_maxColorAttachments;
+        if (!build_reduce_glsl_shader(&fsInput,
+                                      &fsUniform,
+                                      &fsOutput,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      &fragmentShaderText)) {
+                fprintf(stderr, "Failed to build GLSL vertex shader\n");
+                piglit_report_result(PIGLIT_SKIP);
+        }
+
+        if (g_debugMask & DEBUG_SHADERS) {
+                printf("fragmentShaderText:\n%s", fragmentShaderText);
+        }
+
+        /*
+         * Build the vertex and fragment shaders.
+         */
+        g_program = piglit_build_simple_program(vertexShaderText,
+                                                fragmentShaderText);
+        if (!g_program) {
+                fprintf(stderr, "Failed to compile/link program\n");
+                piglit_report_result(PIGLIT_SKIP);
+        }
+
+        snprintf(shaderTempText, sizeof(shaderTempText), "InPosition");
+        glBindAttribLocation(g_program, 0, shaderTempText);
+
+        for (i = 0; i < g_maxVertexAttribs - 1; i++) {
+                snprintf(shaderTempText, sizeof(shaderTempText), "InValue%u", i);
+                glBindAttribLocation(g_program, i+1, shaderTempText);
+        }
+
+        if (g_debugMask & DEBUG_SHADERS) {
+                printf("Linking program...\n");
+        }
+
+        glLinkProgram(g_program);
+        glUseProgram(g_program);
+
+        if (!piglit_check_gl_error(GL_NO_ERROR)) {
+                fprintf(stderr, "Failure to link shaders\n");
+                fprintf(stderr, "vertexShaderText:\n%s", vertexShaderText);
+                fprintf(stderr, "fragmentShaderText:\n%s", fragmentShaderText);
+                free(vertexShaderText);
+                free(fragmentShaderText);
+                piglit_report_result(PIGLIT_FAIL);
+        }
+
+        if (g_debugMask & DEBUG_SHADERS) {
+                printf("Using program %u...\n", g_program);
+        }
+
+        /* Delete the interim copies generated by build_reduce_glsl_shader */ 
+        free(vertexShaderText);
+        free(fragmentShaderText);
+
+        /*
+         * Calculate the expected results for the bound resource limits.
+         */
+        var = calloc(g_maxVaryingFloats, sizeof(GLfloat));
+
+        if (NULL == var) {
+                fprintf(stderr,
+                        "Failed to allocate temporary array for expected calculation.\n");
+                piglit_report_result(PIGLIT_FAIL);
+        }
+
+        g_expected = calloc(g_maxColorAttachments, sizeof(GLfloat)*MAX_COMPONENTS);
+
+        if (NULL == g_expected) {
+                fprintf(stderr, "Failed to allocate array for expected calculation.\n");
+                piglit_report_result(PIGLIT_FAIL);
+        }
+
+        /*
+         * Sampler coverage up to the total number of varying floats.
+         */
+        for (i = 0; i < g_maxVaryingFloats; i++) {
+                if ((i < g_maxVertexTextureImageUnits*MAX_COMPONENTS) &&
+                    (i < NUM_PRIMES)) {
+                        var[i] = g_primes[i];
+                } else {
+                        var[i] = 1.0;
+                }
+        }
+
+        /*
+         * Multiply in all vertex attributes.
+         */
+        i = 0;
+        while (i < (g_maxVertexAttribs-1)*MAX_COMPONENTS) {
+                if (i < NUM_PRIMES) {
+                        var[i % g_maxVaryingFloats] *= g_primes[i];
+                } else {
+                        var[i % g_maxVaryingFloats] *= 1.0;
+                }
+                i++;
+        }
+
+        /*
+         * Calculate the expected values for the FS stage.
+         *
+         * Sampler cover up to the total number of varying floats.
+         */
+        for (i = 0; i < g_maxColorAttachments*MAX_COMPONENTS; i++) {
+                if ((i < g_maxVertexTextureImageUnits*MAX_COMPONENTS) &&
+                    (i < NUM_PRIMES)) {
+                        g_expected[i] = g_primes[i];
+                } else {
+                        g_expected[i] = 1.0;
+                }
+        }
+
+        /*
+         * Multiply in all the varying contributions generated by the VS.
+         */
+        i = 0;
+        while (i < g_maxVaryingFloats) {
+                g_expected[i % (g_maxColorAttachments*MAX_COMPONENTS)] *= var[i];
+                i++;
+        }
+
+        for (i = 0; i < g_maxColorAttachments; i++) {
+                printf("g_expected[%u]=(%f, %f, %f, %f)\n", i,
+                       g_expected[(i * MAX_COMPONENTS) + 0],
+                       g_expected[(i * MAX_COMPONENTS) + 1],
+                       g_expected[(i * MAX_COMPONENTS) + 2],
+                       g_expected[(i * MAX_COMPONENTS) + 3]);
+        }
+
+        free(var);
+}
-- 
1.7.9

----- Original Message -----
From: "Brian Paul" <brianp at vmware.com>
To: "Matthew McClure" <mcclurem at vmware.com>
Cc: piglit at lists.freedesktop.org
Sent: Wednesday, July 9, 2014 4:34:52 PM
Subject: Re: [Piglit] [PATCH] gl-3.0: add a test to stress the bound resource limits

On 07/09/2014 03:10 PM, Matthew McClure wrote:
> I reproduced Brian's issue with the NVIDIA drivers. Below is a list of the configurations I ran:
>
> GeForce GTX 650
> * Ubuntu NVIDIA 304.88 - Allocation of Variable[GL_MAX_VARYING_FLOATS] succeeded, VBO usage succeeds.
> * Ubuntu NVIDIA 331.89 - Allocation of Variable[GL_MAX_VARYING_FLOATS] fails with the following message:
>
>       0(2) : error C5041: cannot locate suitable resource to bind variable "Variable". Possibly large array.
>
> GeForce GT 740M
> * Windows 8.1 NVIDIA 332.88 - Allocation of Variable[GL_MAX_VARYING_FLOATS] succeeded, VBO usage fails.
>
> Due to the issues above, I clamped the return value of GL_MAX_VARYING_FLOATS to 32 and only used immediate mode rendering with one attribute. A developer can disable varying floats clamping by specifying the command line argument "-dontClampMaxVaryings". A developer can also re-enable different draw paths using command line arguments "-drawArraysVBO" or "-drawElementsVBO".
>
> Below is an updated patch to address the issues, as they are not a blocking issue for the test to be useful.


OK, the test passes now with my nvidia driver, softpipe, llvmpipe and svga.

A couple more comments on the code.

1. Some of the static, initialized arrays could be const-qualified.  Not 
a big deal though.

2. The standard indentation practice is to use 8-space tabs.

-Brian


More information about the Piglit mailing list