/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Texture unit usage tests. * * \todo [2012-07-12 nuutti] Come up with a good way to make these tests faster. *//*--------------------------------------------------------------------*/ #include "es3fTextureUnitTests.hpp" #include "glsTextureTestUtil.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "gluTextureUtil.hpp" #include "tcuTextureUtil.hpp" #include "tcuImageCompare.hpp" #include "tcuMatrix.hpp" #include "tcuRenderTarget.hpp" #include "sglrContextUtil.hpp" #include "sglrReferenceContext.hpp" #include "sglrGLContext.hpp" #include "deMath.h" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using tcu::IVec2; using tcu::IVec3; using tcu::Mat3; using tcu::Mat4; using std::vector; using std::string; using namespace glw; // GL types namespace deqp { using namespace gls::TextureTestUtil; namespace gles3 { namespace Functional { static const int VIEWPORT_WIDTH = 128; static const int VIEWPORT_HEIGHT = 128; static const int TEXTURE_WIDTH_2D = 128; static const int TEXTURE_HEIGHT_2D = 128; // \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time. static const int TEXTURE_WIDTH_CUBE = 256; static const int TEXTURE_HEIGHT_CUBE = 256; static const int TEXTURE_WIDTH_2D_ARRAY = 64; static const int TEXTURE_HEIGHT_2D_ARRAY = 64; static const int TEXTURE_LAYERS_2D_ARRAY = 4; static const int TEXTURE_WIDTH_3D = 32; static const int TEXTURE_HEIGHT_3D = 32; static const int TEXTURE_DEPTH_3D = 32; static const int GRID_CELL_SIZE = 8; static const GLenum s_testSizedInternalFormats[] = { GL_RGBA32F, GL_RGBA32I, GL_RGBA32UI, GL_RGBA16F, GL_RGBA16I, GL_RGBA16UI, GL_RGBA8, GL_RGBA8I, GL_RGBA8UI, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGB10_A2UI, GL_RGBA4, GL_RGB5_A1, GL_RGBA8_SNORM, GL_RGB8, GL_RGB565, GL_R11F_G11F_B10F, GL_RGB32F, GL_RGB32I, GL_RGB32UI, GL_RGB16F, GL_RGB16I, GL_RGB16UI, GL_RGB8_SNORM, GL_RGB8I, GL_RGB8UI, GL_SRGB8, GL_RGB9_E5, GL_RG32F, GL_RG32I, GL_RG32UI, GL_RG16F, GL_RG16I, GL_RG16UI, GL_RG8, GL_RG8I, GL_RG8UI, GL_RG8_SNORM, GL_R32F, GL_R32I, GL_R32UI, GL_R16F, GL_R16I, GL_R16UI, GL_R8, GL_R8I, GL_R8UI, GL_R8_SNORM }; static const GLenum s_testWrapModes[] = { GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT, }; static const GLenum s_testMinFilters[] = { GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR }; static const GLenum s_testNonMipmapMinFilters[] = { GL_NEAREST, GL_LINEAR }; static const GLenum s_testNearestMinFilters[] = { GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST }; static const GLenum s_testMagFilters[] = { GL_NEAREST, GL_LINEAR }; static const GLenum s_cubeFaceTargets[] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; // Extend a 3x3 transformation matrix to an equivalent 4x4 transformation matrix (i.e. 1.0 in right-down cell, 0.0's in other new cells). static Mat4 matExtend3To4 (const Mat3& mat) { Mat4 res; for (int rowNdx = 0; rowNdx < 3; rowNdx++) { Vec3 row = mat.getRow(rowNdx); res.setRow(rowNdx, Vec4(row.x(), row.y(), row.z(), 0.0f)); } res.setRow(3, Vec4(0.0f, 0.0f, 0.0f, 1.0f)); return res; } static string generateMultiTexFragmentShader (int numUnits, const vector& unitTypes, const vector& samplerTypes) { // The fragment shader calculates the average of a set of textures. string samplersStr; string matricesStr; string scalesStr; string biasesStr; string lookupsStr; string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)"; for (int ndx = 0; ndx < numUnits; ndx++) { string ndxStr = de::toString(ndx); string samplerName = "u_sampler" + ndxStr; string transformationName = "u_trans" + ndxStr; string scaleName = "u_texScale" + ndxStr; string biasName = "u_texBias" + ndxStr; samplersStr += string("") + "uniform highp " + glu::getDataTypeName(samplerTypes[ndx]) + " " + samplerName + ";\n"; matricesStr += "uniform highp mat4 " + transformationName + ";\n"; scalesStr += "uniform highp vec4 " + scaleName + ";\n"; biasesStr += "uniform highp vec4 " + biasName + ";\n"; string lookupCoord = transformationName + "*vec4(v_coord, 1.0, 1.0)"; if (unitTypes[ndx] == GL_TEXTURE_2D) lookupCoord = "vec2(" + lookupCoord + ")"; else lookupCoord = "vec3(" + lookupCoord + ")"; lookupsStr += "\tcolor += " + colorMultiplier + "*(vec4(texture(" + samplerName + ", " + lookupCoord + "))*" + scaleName + " + " + biasName + ");\n"; } return "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" + samplersStr + matricesStr + scalesStr + biasesStr + "in highp vec2 v_coord;\n" "\n" "void main (void)\n" "{\n" " mediump vec4 color = vec4(0.0);\n" + lookupsStr + " o_color = color;\n" "}\n"; } static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const vector& unitTypes, const vector& samplerTypes) { sglr::pdec::ShaderProgramDeclaration decl; decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT); decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT); decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT); decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT); for (int ndx = 0; ndx < numUnits; ++ndx) { string samplerName = "u_sampler" + de::toString(ndx); string transformationName = "u_trans" + de::toString(ndx); string scaleName = "u_texScale" + de::toString(ndx); string biasName = "u_texBias" + de::toString(ndx); decl << sglr::pdec::Uniform(samplerName, samplerTypes[ndx]); decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT4); decl << sglr::pdec::Uniform(scaleName, glu::TYPE_FLOAT_VEC4); decl << sglr::pdec::Uniform(biasName, glu::TYPE_FLOAT_VEC4); } decl << sglr::pdec::VertexSource("#version 300 es\n" "in highp vec4 a_position;\n" "in highp vec2 a_coord;\n" "out highp vec2 v_coord;\n" "\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_coord = a_coord;\n" "}\n"); decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes, samplerTypes)); return decl; } // Calculates values that will be used in calculateLod(). static tcu::Vector calculateLodDerivateParts (const Mat4& transformation) { // Calculate transformed coordinates of three screen corners. Vec3 trans00 = (transformation * Vec4(0.0f, 0.0f, 1.0f, 1.0f)).xyz(); Vec3 trans01 = (transformation * Vec4(0.0f, 1.0f, 1.0f, 1.0f)).xyz(); Vec3 trans10 = (transformation * Vec4(1.0f, 0.0f, 1.0f, 1.0f)).xyz(); return tcu::Vector(Vec2(trans10.x() - trans00.x(), trans01.x() - trans00.x()), Vec2(trans10.y() - trans00.y(), trans01.y() - trans00.y()), Vec2(trans10.z() - trans00.z(), trans01.z() - trans00.z())); } // Calculates the maximum allowed lod from derivates static float calculateLodMax(const tcu::Vector& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate) { float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x(); float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y(); float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x(); float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y(); float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x(); float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y(); const float mu = de::max(de::abs(dudx), de::abs(dudy)); const float mv = de::max(de::abs(dvdx), de::abs(dvdy)); const float mw = de::max(de::abs(dwdx), de::abs(dwdy)); return deFloatLog2(mu + mv + mw); } // Calculates the minimum allowed lod from derivates static float calculateLodMin(const tcu::Vector& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate) { float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x(); float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y(); float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x(); float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y(); float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x(); float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y(); const float mu = de::max(de::abs(dudx), de::abs(dudy)); const float mv = de::max(de::abs(dvdx), de::abs(dvdy)); const float mw = de::max(de::abs(dwdx), de::abs(dwdy)); return deFloatLog2(de::max(mu, de::max(mv, mw))); } class MultiTexShader : public sglr::ShaderProgram { public: MultiTexShader (deUint32 randSeed, int numUnits, const vector& unitTypes, const vector& samplerTypes, const vector& texScales, const vector& texBiases, const vector& num2dArrayLayers); // \note 2d array layer "coordinate" isn't normalized, so this is needed here. void setUniforms (sglr::Context& context, deUint32 program) const; void makeSafeLods (const vector& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 . private: void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const; void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const; int m_numUnits; vector m_unitTypes; // 2d, cube map, 2d array or 3d. vector m_texScales; vector m_texBiases; vector m_transformations; vector > m_lodDerivateParts; // Parts of lod derivates; computed in init(), used in eval(). }; MultiTexShader::MultiTexShader (deUint32 randSeed, int numUnits, const vector& unitTypes, const vector& samplerTypes, const vector& texScales, const vector& texBiases, const vector& num2dArrayLayers) : sglr::ShaderProgram (generateShaderProgramDeclaration(numUnits, unitTypes, samplerTypes)) , m_numUnits (numUnits) , m_unitTypes (unitTypes) , m_texScales (texScales) , m_texBiases (texBiases) { // 2d-to-cube-face transformations. // \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well. static const float s_cubeTransforms[][3*3] = { // Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1)) { 0.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f, 2.0f, 0.0f, -1.0f }, // Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1)) { 0.0f, 0.0f, 1.0f, 0.0f, -2.0f, 1.0f, -2.0f, 0.0f, 1.0f }, // Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1)) { 2.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f }, // Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1)) { 2.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, -1.0f }, // Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1) { -2.0f, 0.0f, 1.0f, 0.0f, -2.0f, 1.0f, 0.0f, 0.0f, -1.0f }, // Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1) { 2.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f, 0.0f, 0.0f, 1.0f } }; // Generate transformation matrices. de::Random rnd(randSeed); m_transformations.reserve(m_numUnits); m_lodDerivateParts.reserve(m_numUnits); int tex2dArrayNdx = 0; // Keep track of 2d texture array index. DE_ASSERT((int)m_unitTypes.size() == m_numUnits); for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) { if (m_unitTypes[unitNdx] == GL_TEXTURE_2D) { float rotAngle = rnd.getFloat(0.0f, 2.0f*DE_PI); float xScaleFactor = rnd.getFloat(0.7f, 1.5f); float yScaleFactor = rnd.getFloat(0.7f, 1.5f); float xShearAmount = rnd.getFloat(0.0f, 0.5f); float yShearAmount = rnd.getFloat(0.0f, 0.5f); float xTranslationAmount = rnd.getFloat(-0.5f, 0.5f); float yTranslationAmount = rnd.getFloat(-0.5f, 0.5f); static const float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations. { 1.0f, 0.0f, -0.5f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 1.0f }; float rotTransfData[3*3] = { deFloatCos(rotAngle), -deFloatSin(rotAngle), 0.0f, deFloatSin(rotAngle), deFloatCos(rotAngle), 0.0f, 0.0f, 0.0f, 1.0f }; float scaleTransfData[3*3] = { xScaleFactor, 0.0f, 0.0f, 0.0f, yScaleFactor, 0.0f, 0.0f, 0.0f, 1.0f }; float xShearTransfData[3*3] = { 1.0f, xShearAmount, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; float yShearTransfData[3*3] = { 1.0f, 0.0f, 0.0f, yShearAmount, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; float translationTransfData[3*3] = { 1.0f, 0.0f, xTranslationAmount, 0.0f, 1.0f, yTranslationAmount, 0.0f, 0.0f, 1.0f }; Mat4 transformation = matExtend3To4(Mat3(tempOffsetData) * Mat3(translationTransfData) * Mat3(rotTransfData) * Mat3(scaleTransfData) * Mat3(xShearTransfData) * Mat3(yShearTransfData) * (Mat3(tempOffsetData) * (-1.0f))); m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation)); m_transformations.push_back(transformation); } else if (m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP) { DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms)); float planarTransData[3*3]; // In case of a cube map, we only want to render one face, so the transformation needs to be restricted - only enlarging scaling is done. for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++) { if (i == 0 || i == 4) planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling. else if (i == 8) planarTransData[i] = 1.0f; else planarTransData[i] = 0.0f; } int faceNdx = rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1); Mat3 planarTrans (planarTransData); // Planar, face-agnostic transformation. Mat4 finalTrans = matExtend3To4(Mat3(s_cubeTransforms[faceNdx]) * planarTrans); // Final transformation from planar to cube map coordinates, including the transformation just generated. Mat4 planarTrans4x4 = matExtend3To4(planarTrans); m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans4x4)); m_transformations.push_back(finalTrans); } else { DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_3D || m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY); float transData[4*4]; for (int i = 0; i < 4*4; i++) { float sign = rnd.getBool() ? 1.0f : -1.0f; transData[i] = rnd.getFloat(0.7f, 1.4f) * sign; } Mat4 transformation(transData); if (m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY) { // Z direction: Translate by 0.5 and scale by layer amount. float numLayers = (float)num2dArrayLayers[tex2dArrayNdx]; static const float zTranslationTransfData[4*4] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f }; float zScaleTransfData[4*4] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, numLayers, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; transformation = transformation * Mat4(zScaleTransfData) * Mat4(zTranslationTransfData); tex2dArrayNdx++; } m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation)); m_transformations.push_back(Mat4(transformation)); } } } void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const { ctx.useProgram(program); // Sampler and matrix uniforms. for (int ndx = 0; ndx < m_numUnits; ndx++) { string ndxStr = de::toString(ndx); ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx); ctx.uniformMatrix4fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]); ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texScale" + ndxStr).c_str()), 1, m_texScales[ndx].getPtr()); ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texBias" + ndxStr).c_str()), 1, m_texBiases[ndx].getPtr()); } } void MultiTexShader::makeSafeLods (const vector& textureSizes, const IVec2& viewportSize) { DE_ASSERT((int)textureSizes.size() == m_numUnits); static const float shrinkScaleMat2dData[3*3] = { 0.95f, 0.0f, 0.0f, 0.0f, 0.95f, 0.0f, 0.0f, 0.0f, 1.0f }; static const float shrinkScaleMat3dData[3*3] = { 0.95f, 0.0f, 0.0f, 0.0f, 0.95f, 0.0f, 0.0f, 0.0f, 0.95f }; Mat4 shrinkScaleMat2d = matExtend3To4(Mat3(shrinkScaleMat2dData)); Mat4 shrinkScaleMat3d = matExtend3To4(Mat3(shrinkScaleMat3dData)); Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y()); for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) { // As long as LOD is too close to 0.0 or is positive and too close to a something-and-a-half (0.5, 1.5, 2.5 etc) or allowed lod range could round to different levels, zoom in a little to get a safer LOD. for (;;) { const float threshold = 0.1f; const float epsilon = 0.01f; const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate); const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate); const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1); const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1); if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) || de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) || maxLevel != minLevel) { m_transformations[unitNdx] = (m_unitTypes[unitNdx] == GL_TEXTURE_3D ? shrinkScaleMat3d : shrinkScaleMat2d) * m_transformations[unitNdx]; m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]); } else break; } } } void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const { for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { rr::VertexPacket& packet = *(packets[packetNdx]); packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx); packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx); } } void MultiTexShader::shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const { DE_ASSERT((int)m_unitTypes.size() == m_numUnits); DE_ASSERT((int)m_transformations.size() == m_numUnits); DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits); for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { rr::FragmentPacket& packet = packets[packetNdx]; const float colorMultiplier = 1.0f / (float)m_numUnits; Vec4 outColors[4] = { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) }; for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) { tcu::Vec4 texSamples[4]; // Read tex coords const tcu::Vec2 texCoords[4] = { rr::readTriangleVarying(packet, context, 0, 0).xy(), rr::readTriangleVarying(packet, context, 0, 1).xy(), rr::readTriangleVarying(packet, context, 0, 2).xy(), rr::readTriangleVarying(packet, context, 0, 3).xy(), }; // Transform tcu::Vec3 coords3D[4] = { (m_transformations[unitNdx] * Vec4(texCoords[0].x(), texCoords[0].y(), 1.0f, 1.0f)).xyz(), (m_transformations[unitNdx] * Vec4(texCoords[1].x(), texCoords[1].y(), 1.0f, 1.0f)).xyz(), (m_transformations[unitNdx] * Vec4(texCoords[2].x(), texCoords[2].y(), 1.0f, 1.0f)).xyz(), (m_transformations[unitNdx] * Vec4(texCoords[3].x(), texCoords[3].y(), 1.0f, 1.0f)).xyz(), }; // To 2D const tcu::Vec2 coords2D[4] = { coords3D[0].xy(), coords3D[1].xy(), coords3D[2].xy(), coords3D[3].xy(), }; // Sample switch (m_unitTypes[unitNdx]) { case GL_TEXTURE_2D: m_uniforms[4*unitNdx].sampler.tex2D->sample4(texSamples, coords2D); break; case GL_TEXTURE_CUBE_MAP: m_uniforms[4*unitNdx].sampler.texCube->sample4(texSamples, coords3D); break; case GL_TEXTURE_2D_ARRAY: m_uniforms[4*unitNdx].sampler.tex2DArray->sample4(texSamples, coords3D); break; case GL_TEXTURE_3D: m_uniforms[4*unitNdx].sampler.tex3D->sample4(texSamples, coords3D); break; default: DE_ASSERT(DE_FALSE); } // Add to sum for (int fragNdx = 0; fragNdx < 4; ++fragNdx) outColors[fragNdx] += colorMultiplier * (texSamples[fragNdx]*m_texScales[unitNdx] + m_texBiases[unitNdx]); } // output for (int fragNdx = 0; fragNdx < 4; ++fragNdx) rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]); } } class TextureUnitCase : public TestCase { public: enum CaseType { CASE_ONLY_2D = 0, CASE_ONLY_CUBE, CASE_ONLY_2D_ARRAY, CASE_ONLY_3D, CASE_MIXED, CASE_LAST }; TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed); ~TextureUnitCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: struct TextureParameters { GLenum internalFormat; GLenum wrapModeS; GLenum wrapModeT; GLenum wrapModeR; GLenum minFilter; GLenum magFilter; }; TextureUnitCase (const TextureUnitCase& other); TextureUnitCase& operator= (const TextureUnitCase& other); void upload2dTexture (int texNdx, sglr::Context& context); void uploadCubeTexture (int texNdx, sglr::Context& context); void upload2dArrayTexture (int texNdx, sglr::Context& context); void upload3dTexture (int texNdx, sglr::Context& context); void render (sglr::Context& context); const int m_numUnitsParam; const CaseType m_caseType; const deUint32 m_randSeed; int m_numTextures; //!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units. int m_numUnits; //!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum vector m_textureTypes; vector m_textureParams; vector m_textures2d; vector m_texturesCube; vector m_textures2dArray; vector m_textures3d; vector m_unitTextures; //!< Which texture is used in a particular unit. vector m_ndxTexType; //!< Index of a texture in m_textures2d, m_texturesCube, m_textures2dArray or m_textures3d, depending on texture type. MultiTexShader* m_shader; }; TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed) : TestCase (context, tcu::NODETYPE_SELF_VALIDATE, name, desc) , m_numUnitsParam (numUnits) , m_caseType (caseType) , m_randSeed (randSeed) , m_shader (DE_NULL) { } TextureUnitCase::~TextureUnitCase (void) { TextureUnitCase::deinit(); } void TextureUnitCase::deinit (void) { for (vector::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++) delete *i; m_textures2d.clear(); for (vector::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++) delete *i; m_texturesCube.clear(); for (vector::iterator i = m_textures2dArray.begin(); i != m_textures2dArray.end(); i++) delete *i; m_textures2dArray.clear(); for (vector::iterator i = m_textures3d.begin(); i != m_textures3d.end(); i++) delete *i; m_textures3d.clear(); delete m_shader; m_shader = DE_NULL; } void TextureUnitCase::init (void) { m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS); // Make the textures. try { tcu::TestLog& log = m_testCtx.getLog(); de::Random rnd (m_randSeed); if (rnd.getFloat() < 0.7f) m_numTextures = m_numUnits; // In most cases use one unit per texture. else m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits); // Sometimes assign same texture to multiple units. log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage; m_textureTypes.reserve(m_numTextures); m_textureParams.reserve(m_numTextures); m_ndxTexType.reserve(m_numTextures); // Generate textures. for (int texNdx = 0; texNdx < m_numTextures; texNdx++) { // Either fixed or randomized target types, and randomized parameters for every texture. TextureParameters params; DE_STATIC_ASSERT(CASE_ONLY_2D == 0 && CASE_MIXED + 1 == CASE_LAST); int texType = m_caseType == CASE_MIXED ? rnd.getInt(0, (int)CASE_MIXED - 1) : (int)m_caseType; bool is2dTex = texType == 0; bool isCubeTex = texType == 1; bool is2dArrayTex = texType == 2; bool is3dTex = texType == 3; DE_ASSERT(is2dTex || isCubeTex || is2dArrayTex || is3dTex); GLenum type = is2dTex ? GL_TEXTURE_2D : isCubeTex ? GL_TEXTURE_CUBE_MAP : is2dArrayTex ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_3D; const int texWidth = is2dTex ? TEXTURE_WIDTH_2D : isCubeTex ? TEXTURE_WIDTH_CUBE : is2dArrayTex ? TEXTURE_WIDTH_2D_ARRAY : TEXTURE_WIDTH_3D; const int texHeight = is2dTex ? TEXTURE_HEIGHT_2D : isCubeTex ? TEXTURE_HEIGHT_CUBE : is2dArrayTex ? TEXTURE_HEIGHT_2D_ARRAY : TEXTURE_HEIGHT_3D; const int texDepth = is3dTex ? TEXTURE_DEPTH_3D : 1; const int texLayers = is2dArrayTex ? TEXTURE_LAYERS_2D_ARRAY : 1; bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight) && deIsPowerOfTwo32(texDepth)); int numLevels = mipmaps ? deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth))+1 : 1; params.internalFormat = s_testSizedInternalFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testSizedInternalFormats) - 1)]; bool isFilterable = glu::isGLInternalColorFormatFilterable(params.internalFormat); params.wrapModeS = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)]; params.wrapModeT = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)]; params.wrapModeR = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)]; params.magFilter = isFilterable ? s_testMagFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)] : GL_NEAREST; if (mipmaps) params.minFilter = isFilterable ? s_testMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)] : s_testNearestMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNearestMinFilters) - 1)]; else params.minFilter = isFilterable ? s_testNonMipmapMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)] : GL_NEAREST; m_textureTypes.push_back(type); m_textureParams.push_back(params); // Create new texture. tcu::TextureFormat texFormat = glu::mapGLInternalFormat((deUint32)params.internalFormat); if (is2dTex) { m_ndxTexType.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d texture vector. m_textures2d.push_back(new tcu::Texture2D(texFormat, texWidth, texHeight)); } else if (isCubeTex) { m_ndxTexType.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube texture vector. DE_ASSERT(texWidth == texHeight); m_texturesCube.push_back(new tcu::TextureCube(texFormat, texWidth)); } else if (is2dArrayTex) { m_ndxTexType.push_back((int)m_textures2dArray.size()); // Remember the index this texture has in the 2d array texture vector. m_textures2dArray.push_back(new tcu::Texture2DArray(texFormat, texWidth, texHeight, texLayers)); } else { m_ndxTexType.push_back((int)m_textures3d.size()); // Remember the index this texture has in the 3d vector. m_textures3d.push_back(new tcu::Texture3D(texFormat, texWidth, texHeight, texDepth)); } tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFormat); Vec4 cBias = fmtInfo.valueMin; Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; // Fill with grid texture. int numFaces = isCubeTex ? (int)tcu::CUBEFACE_LAST : 1; for (int face = 0; face < numFaces; face++) { deUint32 rgb = rnd.getUint32() & 0x00ffffff; deUint32 alpha = 0xff000000; deUint32 colorA = alpha | rgb; deUint32 colorB = alpha | ((~rgb) & 0x00ffffff); for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) { if (is2dTex) m_textures2d.back()->allocLevel(levelNdx); else if (isCubeTex) m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx); else if (is2dArrayTex) m_textures2dArray.back()->allocLevel(levelNdx); else m_textures3d.back()->allocLevel(levelNdx); int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps. tcu::PixelBufferAccess access = is2dTex ? m_textures2d.back()->getLevel(levelNdx) : isCubeTex ? m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face) : is2dArrayTex ? m_textures2dArray.back()->getLevel(levelNdx) : m_textures3d.back()->getLevel(levelNdx); tcu::fillWithGrid(access, curCellSize, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); } } } // Assign a texture index to each unit. m_unitTextures.reserve(m_numUnits); // \note Every texture is used at least once. for (int i = 0; i < m_numTextures; i++) m_unitTextures.push_back(i); // Assign a random texture to remaining units. while ((int)m_unitTextures.size() < m_numUnits) m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1)); rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end()); // Generate information for shader. vector unitTypes; vector texScales; vector texBiases; vector samplerTypes; vector num2dArrayLayers; unitTypes.reserve(m_numUnits); texScales.reserve(m_numUnits); texBiases.reserve(m_numUnits); samplerTypes.reserve(m_numUnits); num2dArrayLayers.reserve(m_numUnits); for (int i = 0; i < m_numUnits; i++) { int texNdx = m_unitTextures[i]; GLenum type = m_textureTypes[texNdx]; tcu::TextureFormat fmt = glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat); tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(fmt); unitTypes.push_back(type); if (type == GL_TEXTURE_2D_ARRAY) num2dArrayLayers.push_back(m_textures2dArray[m_ndxTexType[texNdx]]->getNumLayers()); texScales.push_back(fmtInfo.lookupScale); texBiases.push_back(fmtInfo.lookupBias); switch (type) { case GL_TEXTURE_2D: samplerTypes.push_back(glu::getSampler2DType(fmt)); break; case GL_TEXTURE_CUBE_MAP: samplerTypes.push_back(glu::getSamplerCubeType(fmt)); break; case GL_TEXTURE_2D_ARRAY: samplerTypes.push_back(glu::getSampler2DArrayType(fmt)); break; case GL_TEXTURE_3D: samplerTypes.push_back(glu::getSampler3DType(fmt)); break; default: DE_ASSERT(DE_FALSE); } } // Create shader. DE_ASSERT(m_shader == DE_NULL); m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes, samplerTypes, texScales, texBiases, num2dArrayLayers); } catch (const std::exception&) { // Clean up to save memory. TextureUnitCase::deinit(); throw; } } TextureUnitCase::IterateResult TextureUnitCase::iterate (void) { glu::RenderContext& renderCtx = m_context.getRenderContext(); const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget(); tcu::TestLog& log = m_testCtx.getLog(); de::Random rnd (m_randSeed); int viewportWidth = deMin32(VIEWPORT_WIDTH, renderTarget.getWidth()); int viewportHeight = deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight()); int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth); int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight); tcu::Surface gles3Frame (viewportWidth, viewportHeight); tcu::Surface refFrame (viewportWidth, viewportHeight); { // First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods(). vector texSizes; texSizes.reserve(m_numUnits); for (int i = 0; i < m_numUnits; i++) { int texNdx = m_unitTextures[i]; int texNdxInType = m_ndxTexType[texNdx]; GLenum type = m_textureTypes[texNdx]; switch (type) { case GL_TEXTURE_2D: texSizes.push_back(IVec3(m_textures2d[texNdxInType]->getWidth(), m_textures2d[texNdxInType]->getHeight(), 0)); break; case GL_TEXTURE_CUBE_MAP: texSizes.push_back(IVec3(m_texturesCube[texNdxInType]->getSize(), m_texturesCube[texNdxInType]->getSize(), 0)); break; case GL_TEXTURE_2D_ARRAY: texSizes.push_back(IVec3(m_textures2dArray[texNdxInType]->getWidth(), m_textures2dArray[texNdxInType]->getHeight(), 0)); break; case GL_TEXTURE_3D: texSizes.push_back(IVec3(m_textures3d[texNdxInType]->getWidth(), m_textures3d[texNdxInType]->getHeight(), m_textures3d[texNdxInType]->getDepth())); break; default: DE_ASSERT(DE_FALSE); } } m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight)); } // Render using GLES3. { sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight)); render(context); context.readPixels(gles3Frame, 0, 0, viewportWidth, viewportHeight); } // Render reference image. { sglr::ReferenceContextBuffers buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight); sglr::ReferenceContext context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); render(context); context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight); } // Compare images. const float threshold = 0.001f; bool isOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles3Frame, threshold, tcu::COMPARE_LOG_RESULT); // Store test result. m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Image comparison failed"); return STOP; } void TextureUnitCase::upload2dTexture (int texNdx, sglr::Context& context) { int ndx2d = m_ndxTexType[texNdx]; const tcu::Texture2D* texture = m_textures2d[ndx2d]; glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat)); context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++) { if (texture->isLevelEmpty(levelNdx)) continue; tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx); int width = access.getWidth(); int height = access.getHeight(); DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr()); GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d texture image data"); } } void TextureUnitCase::uploadCubeTexture (int texNdx, sglr::Context& context) { int ndxCube = m_ndxTexType[texNdx]; const tcu::TextureCube* texture = m_texturesCube[ndxCube]; glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat)); context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++) { for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++) { if (texture->isLevelEmpty((tcu::CubeFace)face, levelNdx)) continue; tcu::ConstPixelBufferAccess access = texture->getLevelFace(levelNdx, (tcu::CubeFace)face); int width = access.getWidth(); int height = access.getHeight(); DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr()); GLU_EXPECT_NO_ERROR(context.getError(), "Set cube map image data"); } } } void TextureUnitCase::upload2dArrayTexture (int texNdx, sglr::Context& context) { int ndx2dArray = m_ndxTexType[texNdx]; const tcu::Texture2DArray* texture = m_textures2dArray[ndx2dArray]; glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat)); context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++) { if (texture->isLevelEmpty(levelNdx)) continue; tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx); int width = access.getWidth(); int height = access.getHeight(); int layers = access.getDepth(); DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height); context.texImage3D(GL_TEXTURE_2D_ARRAY, levelNdx, m_textureParams[texNdx].internalFormat, width, height, layers, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr()); GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d array texture image data"); } } void TextureUnitCase::upload3dTexture (int texNdx, sglr::Context& context) { int ndx3d = m_ndxTexType[texNdx]; const tcu::Texture3D* texture = m_textures3d[ndx3d]; glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat)); context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++) { if (texture->isLevelEmpty(levelNdx)) continue; tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx); int width = access.getWidth(); int height = access.getHeight(); int depth = access.getDepth(); DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height); context.texImage3D(GL_TEXTURE_3D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, depth, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr()); GLU_EXPECT_NO_ERROR(context.getError(), "Set 3d texture image data"); } } void TextureUnitCase::render (sglr::Context& context) { // Setup textures. vector textureGLNames; vector isTextureSetUp(m_numTextures, false); // \note Same texture may be bound to multiple units, but we only want to set up parameters and data once per texture. textureGLNames.resize(m_numTextures); context.genTextures(m_numTextures, &textureGLNames[0]); GLU_EXPECT_NO_ERROR(context.getError(), "Generate textures"); for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) { int texNdx = m_unitTextures[unitNdx]; // Bind texture to unit. context.activeTexture(GL_TEXTURE0 + unitNdx); GLU_EXPECT_NO_ERROR(context.getError(), "Set active texture"); context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]); GLU_EXPECT_NO_ERROR(context.getError(), "Bind texture"); if (!isTextureSetUp[texNdx]) { // Binding this texture for first time, so set parameters and data. context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS); context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT); if (m_textureTypes[texNdx] == GL_TEXTURE_3D) context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_R, m_textureParams[texNdx].wrapModeR); context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter); context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter); GLU_EXPECT_NO_ERROR(context.getError(), "Set texture parameters"); switch (m_textureTypes[texNdx]) { case GL_TEXTURE_2D: upload2dTexture(texNdx, context); break; case GL_TEXTURE_CUBE_MAP: uploadCubeTexture(texNdx, context); break; case GL_TEXTURE_2D_ARRAY: upload2dArrayTexture(texNdx, context); break; case GL_TEXTURE_3D: upload3dTexture(texNdx, context); break; default: DE_ASSERT(DE_FALSE); } isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later. } } GLU_EXPECT_NO_ERROR(context.getError(), "Set textures"); // Setup shader deUint32 shaderID = context.createProgram(m_shader); // Draw. context.clearColor(0.125f, 0.25f, 0.5f, 1.0f); context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); m_shader->setUniforms(context, shaderID); sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); GLU_EXPECT_NO_ERROR(context.getError(), "Draw"); // Delete previously generated texture names. context.deleteTextures(m_numTextures, &textureGLNames[0]); GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures"); } TextureUnitTests::TextureUnitTests (Context& context) : TestCaseGroup(context, "units", "Texture Unit Usage Tests") { } TextureUnitTests::~TextureUnitTests (void) { } void TextureUnitTests::init (void) { const int numTestsPerGroup = 10; static const int unitCounts[] = { 2, 4, 8, -1 // \note Negative stands for the implementation-specified maximum. }; for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++) { int numUnits = unitCounts[unitCountNdx]; string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units"; tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), ""); addChild(countGroup); DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0); for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++) { const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D ? "only_2d" : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE ? "only_cube" : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D_ARRAY ? "only_2d_array" : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_3D ? "only_3d" : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED ? "mixed" : DE_NULL; DE_ASSERT(caseTypeGroupName != DE_NULL); tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, ""); countGroup->addChild(caseTypeGroup); for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++) caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, deUint32Hash((deUint32)testNdx))); } } } } // Functional } // gles3 } // deqp