/* * 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.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 30; config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA; config.khr_no_error_support = PIGLIT_NO_ERRORS; 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 - 1; 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); }