1/*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 2.0 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Texture unit usage tests. 22 * 23 * \todo [2012-07-12 nuutti] Come up with a good way to make these tests faster. 24 *//*--------------------------------------------------------------------*/ 25 26#include "es2fTextureUnitTests.hpp" 27#include "glsTextureTestUtil.hpp" 28#include "gluTextureUtil.hpp" 29#include "gluContextInfo.hpp" 30#include "tcuTextureUtil.hpp" 31#include "tcuImageCompare.hpp" 32#include "tcuMatrix.hpp" 33#include "tcuRenderTarget.hpp" 34#include "sglrContextUtil.hpp" 35#include "sglrReferenceContext.hpp" 36#include "sglrGLContext.hpp" 37#include "deMath.h" 38#include "deStringUtil.hpp" 39#include "deRandom.hpp" 40 41#include "glwEnums.hpp" 42#include "glwFunctions.hpp" 43 44using tcu::Vec2; 45using tcu::Vec3; 46using tcu::Vec4; 47using tcu::IVec2; 48using tcu::Mat3; 49using std::vector; 50using std::string; 51using namespace glw; // GL types 52 53namespace deqp 54{ 55 56using namespace gls::TextureTestUtil; 57 58namespace gles2 59{ 60namespace Functional 61{ 62 63static const int VIEWPORT_WIDTH = 128; 64static const int VIEWPORT_HEIGHT = 128; 65 66static const int TEXTURE_WIDTH_2D = 128; 67static const int TEXTURE_HEIGHT_2D = 128; 68 69// \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time. 70static const int TEXTURE_WIDTH_CUBE = 256; 71static const int TEXTURE_HEIGHT_CUBE = 256; 72 73static const int GRID_CELL_SIZE = 8; 74 75static const GLenum s_testFormats[] = 76{ 77 GL_RGB, 78 GL_RGBA, 79 GL_ALPHA, 80 GL_LUMINANCE, 81 GL_LUMINANCE_ALPHA 82}; 83 84static const GLenum s_testDataTypes[] = 85{ 86 GL_UNSIGNED_BYTE, 87 GL_UNSIGNED_SHORT_5_6_5, 88 GL_UNSIGNED_SHORT_4_4_4_4, 89 GL_UNSIGNED_SHORT_5_5_5_1, 90}; 91 92static const GLenum s_testWrapModes[] = 93{ 94 GL_CLAMP_TO_EDGE, 95 GL_REPEAT, 96 GL_MIRRORED_REPEAT, 97}; 98 99static const GLenum s_testMinFilters[] = 100{ 101 GL_NEAREST, 102 GL_LINEAR, 103 GL_NEAREST_MIPMAP_NEAREST, 104 GL_LINEAR_MIPMAP_NEAREST, 105 GL_NEAREST_MIPMAP_LINEAR, 106 GL_LINEAR_MIPMAP_LINEAR 107}; 108 109static const GLenum s_testNonMipmapMinFilters[] = 110{ 111 GL_NEAREST, 112 GL_LINEAR 113}; 114 115static const GLenum s_testMagFilters[] = 116{ 117 GL_NEAREST, 118 GL_LINEAR 119}; 120 121static const GLenum s_cubeFaceTargets[] = 122{ 123 GL_TEXTURE_CUBE_MAP_POSITIVE_X, 124 GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 125 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 126 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 127 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 128 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 129}; 130 131static string generateMultiTexFragmentShader(int numUnits, const GLenum* unitTypes) 132{ 133 // The fragment shader calculates the average of a set of textures. 134 135 string samplersStr; 136 string matricesStr; 137 string lookupsStr; 138 139 string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)"; 140 141 for (int ndx = 0; ndx < numUnits; ndx++) 142 { 143 string ndxStr = de::toString(ndx); 144 string samplerName = "u_sampler" + ndxStr; 145 string transformationName = "u_trans" + ndxStr; 146 const char* samplerType = unitTypes[ndx] == GL_TEXTURE_2D ? "sampler2D" : "samplerCube"; 147 const char* lookupFunc = unitTypes[ndx] == GL_TEXTURE_2D ? "texture2D" : "textureCube"; 148 149 samplersStr += string("") + "uniform mediump " + samplerType + " " + samplerName + ";\n"; 150 matricesStr += "uniform mediump mat3 " + transformationName + ";\n"; 151 152 string lookupCoord = transformationName + "*vec3(v_coord, 1.0)"; 153 154 if (unitTypes[ndx] == GL_TEXTURE_2D) 155 lookupCoord = "vec2(" + lookupCoord + ")"; 156 157 lookupsStr += "\tcolor += " + colorMultiplier + "*" + lookupFunc + "(" + samplerName + ", " + lookupCoord + ");\n"; 158 } 159 160 return 161 samplersStr + 162 matricesStr + 163 "varying mediump vec2 v_coord;\n" 164 "\n" 165 "void main (void)\n" 166 "{\n" 167 " mediump vec4 color = vec4(0.0);\n" + 168 lookupsStr + 169 " gl_FragColor = color;\n" 170 "}\n"; 171} 172 173static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const GLenum* unitTypes) 174{ 175 sglr::pdec::ShaderProgramDeclaration decl; 176 177 decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT); 178 decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT); 179 decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT); 180 decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT); 181 182 for (int ndx = 0; ndx < numUnits; ++ndx) 183 { 184 string samplerName = "u_sampler" + de::toString(ndx); 185 string transformationName = "u_trans" + de::toString(ndx); 186 187 decl << sglr::pdec::Uniform(samplerName, (unitTypes[ndx] == GL_TEXTURE_2D) ? (glu::TYPE_SAMPLER_2D) : (glu::TYPE_SAMPLER_CUBE)); 188 decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT3); 189 } 190 191 decl << sglr::pdec::VertexSource("attribute highp vec4 a_position;\n" 192 "attribute mediump vec2 a_coord;\n" 193 "varying mediump vec2 v_coord;\n" 194 "\n" 195 "void main (void)\n" 196 "{\n" 197 " gl_Position = a_position;\n" 198 " v_coord = a_coord;\n" 199 "}\n"); 200 decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes)); 201 202 return decl; 203} 204 205// Calculates values to be used in calculateLod(). 206static Vec4 calculateLodDerivateParts(const Mat3& transformation) 207{ 208 // Calculate transformed coordinates of three corners. 209 Vec2 trans00 = (transformation * Vec3(0.0f, 0.0f, 1.0f)).xy(); 210 Vec2 trans01 = (transformation * Vec3(0.0f, 1.0f, 1.0f)).xy(); 211 Vec2 trans10 = (transformation * Vec3(1.0f, 0.0f, 1.0f)).xy(); 212 213 return Vec4(trans10.x() - trans00.x(), 214 trans01.x() - trans00.x(), 215 trans10.y() - trans00.y(), 216 trans01.y() - trans00.y()); 217} 218 219// Calculates the maximum allowed lod from derivates 220static float calculateLodMax(const Vec4& derivateParts, const tcu::IVec2& textureSize, const Vec2& screenDerivate) 221{ 222 float dudx = derivateParts.x() * (float)textureSize.x() * screenDerivate.x(); 223 float dudy = derivateParts.y() * (float)textureSize.x() * screenDerivate.y(); 224 float dvdx = derivateParts.z() * (float)textureSize.y() * screenDerivate.x(); 225 float dvdy = derivateParts.w() * (float)textureSize.y() * screenDerivate.y(); 226 227 return deFloatLog2(de::max(de::abs(dudx), de::abs(dudy)) + de::max(de::abs(dvdx), de::abs(dvdy))); 228} 229 230// Calculates the minimum allowed lod from derivates 231static float calculateLodMin(const Vec4& derivateParts, const tcu::IVec2& textureSize, const Vec2& screenDerivate) 232{ 233 float dudx = derivateParts.x() * (float)textureSize.x() * screenDerivate.x(); 234 float dudy = derivateParts.y() * (float)textureSize.x() * screenDerivate.y(); 235 float dvdx = derivateParts.z() * (float)textureSize.y() * screenDerivate.x(); 236 float dvdy = derivateParts.w() * (float)textureSize.y() * screenDerivate.y(); 237 238 return deFloatLog2(de::max(de::max(de::abs(dudx), de::abs(dudy)), de::max(de::abs(dvdx), de::abs(dvdy)))); 239} 240 241class MultiTexShader : public sglr::ShaderProgram 242{ 243public: 244 MultiTexShader (deUint32 randSeed, int numUnits, const vector<GLenum>& unitTypes); 245 246 void setUniforms (sglr::Context& context, deUint32 program) const; 247 void makeSafeLods (const vector<IVec2>& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 . 248 249private: 250 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const; 251 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const; 252 253 int m_numUnits; 254 vector<GLenum> m_unitTypes; // 2d or cube map. 255 vector<Mat3> m_transformations; 256 vector<Vec4> m_lodDerivateParts; // Parts of lod derivates; computed in init(), used in eval(). 257}; 258 259MultiTexShader::MultiTexShader (deUint32 randSeed, int numUnits, const vector<GLenum>& unitTypes) 260 : sglr::ShaderProgram (generateShaderProgramDeclaration(numUnits, &unitTypes[0])) 261 , m_numUnits (numUnits) 262 , m_unitTypes (unitTypes) 263{ 264 // 2d-to-cube-face transformations. 265 // \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well. 266 static const float s_cubeTransforms[][3*3] = 267 { 268 // Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1)) 269 { 0.0f, 0.0f, -1.0f, 270 0.0f, -2.0f, 1.0f, 271 2.0f, 0.0f, -1.0f }, 272 // Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1)) 273 { 0.0f, 0.0f, 1.0f, 274 0.0f, -2.0f, 1.0f, 275 -2.0f, 0.0f, 1.0f }, 276 // Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1)) 277 { 2.0f, 0.0f, -1.0f, 278 0.0f, 0.0f, -1.0f, 279 0.0f, -2.0f, 1.0f }, 280 // Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1)) 281 { 2.0f, 0.0f, -1.0f, 282 0.0f, 0.0f, 1.0f, 283 0.0f, 2.0f, -1.0f }, 284 // Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1) 285 { -2.0f, 0.0f, 1.0f, 286 0.0f, -2.0f, 1.0f, 287 0.0f, 0.0f, -1.0f }, 288 // Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1) 289 { 2.0f, 0.0f, -1.0f, 290 0.0f, -2.0f, 1.0f, 291 0.0f, 0.0f, 1.0f } 292 }; 293 294 // Generate transformation matrices. 295 296 de::Random rnd(randSeed); 297 298 m_transformations.reserve(m_numUnits); 299 m_lodDerivateParts.reserve(m_numUnits); 300 301 DE_ASSERT((int)m_unitTypes.size() == m_numUnits); 302 303 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) 304 { 305 if (m_unitTypes[unitNdx] == GL_TEXTURE_2D) 306 { 307 float rotAngle = rnd.getFloat(0.0f, 2.0f*DE_PI); 308 float xScaleFactor = rnd.getFloat(0.7f, 1.5f); 309 float yScaleFactor = rnd.getFloat(0.7f, 1.5f); 310 float xShearAmount = rnd.getFloat(0.0f, 0.5f); 311 float yShearAmount = rnd.getFloat(0.0f, 0.5f); 312 float xTranslationAmount = rnd.getFloat(-0.5f, 0.5f); 313 float yTranslationAmount = rnd.getFloat(-0.5f, 0.5f); 314 315 float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations. 316 { 317 1.0f, 0.0f, -0.5f, 318 0.0f, 1.0f, -0.5f, 319 0.0f, 0.0f, 1.0f 320 }; 321 float rotTransfData[3*3] = 322 { 323 deFloatCos(rotAngle), -deFloatSin(rotAngle), 0.0f, 324 deFloatSin(rotAngle), deFloatCos(rotAngle), 0.0f, 325 0.0f, 0.0f, 1.0f 326 }; 327 float scaleTransfData[3*3] = 328 { 329 xScaleFactor, 0.0f, 0.0f, 330 0.0f, yScaleFactor, 0.0f, 331 0.0f, 0.0f, 1.0f 332 }; 333 float xShearTransfData[3*3] = 334 { 335 1.0f, xShearAmount, 0.0f, 336 0.0f, 1.0f, 0.0f, 337 0.0f, 0.0f, 1.0f 338 }; 339 float yShearTransfData[3*3] = 340 { 341 1.0f, 0.0f, 0.0f, 342 yShearAmount, 1.0f, 0.0f, 343 0.0f, 0.0f, 1.0f 344 }; 345 float translationTransfData[3*3] = 346 { 347 1.0f, 0.0f, xTranslationAmount, 348 0.0f, 1.0f, yTranslationAmount, 349 0.0f, 0.0f, 1.0f 350 }; 351 352 Mat3 transformation = 353 Mat3(tempOffsetData) * 354 Mat3(translationTransfData) * 355 Mat3(rotTransfData) * 356 Mat3(scaleTransfData) * 357 Mat3(xShearTransfData) * 358 Mat3(yShearTransfData) * 359 (Mat3(tempOffsetData) * (-1.0f)); 360 361 // Calculate parts of lod derivates. 362 m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation)); 363 364 m_transformations.push_back(transformation); 365 } 366 else 367 { 368 DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP); 369 DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms)); 370 371 float planarTransData[3*3]; 372 373 // 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. 374 375 for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++) 376 { 377 if (i == 0 || i == 4) 378 planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling. 379 else if (i == 8) 380 planarTransData[i] = 1.0f; 381 else 382 planarTransData[i] = 0.0f; 383 } 384 385 int faceNdx = rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1); 386 Mat3 planarTrans (planarTransData); // Planar, face-agnostic transformation. 387 Mat3 finalTrans = Mat3(s_cubeTransforms[faceNdx]) * planarTrans; // Final transformation from planar to cube map coordinates, including the transformation just generated. 388 389 // Calculate parts of lod derivates. 390 m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans)); 391 392 m_transformations.push_back(finalTrans); 393 } 394 } 395} 396 397void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const 398{ 399 ctx.useProgram(program); 400 401 // Sampler and matrix uniforms. 402 403 for (int ndx = 0; ndx < m_numUnits; ndx++) 404 { 405 string ndxStr = de::toString(ndx); 406 407 ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx); 408 ctx.uniformMatrix3fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]); 409 } 410} 411 412void MultiTexShader::makeSafeLods (const vector<IVec2>& textureSizes, const IVec2& viewportSize) 413{ 414 DE_ASSERT((int)textureSizes.size() == m_numUnits); 415 416 static const float shrinkScaleMatData[3*3] = 417 { 418 0.95f, 0.0f, 0.0f, 419 0.0f, 0.95f, 0.0f, 420 0.0f, 0.0f, 1.0f 421 }; 422 Mat3 shrinkScaleMat(shrinkScaleMatData); 423 424 Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y()); 425 426 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) 427 { 428 // 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. 429 for (;;) 430 { 431 const float threshold = 0.1f; 432 const float epsilon = 0.01f; 433 434 const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate); 435 const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate); 436 437 const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1); 438 const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1); 439 440 if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) || 441 de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) || 442 maxLevel != minLevel) 443 { 444 m_transformations[unitNdx] = shrinkScaleMat * m_transformations[unitNdx]; 445 m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]); 446 } 447 else 448 break; 449 } 450 } 451} 452 453void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const 454{ 455 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 456 { 457 rr::VertexPacket& packet = *(packets[packetNdx]); 458 459 packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx); 460 packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx); 461 } 462} 463 464void MultiTexShader::shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const 465{ 466 DE_ASSERT((int)m_unitTypes.size() == m_numUnits); 467 DE_ASSERT((int)m_transformations.size() == m_numUnits); 468 DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits); 469 470 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 471 { 472 rr::FragmentPacket& packet = packets[packetNdx]; 473 const float colorMultiplier = 1.0f / (float)m_numUnits; 474 Vec4 outColors[4] = { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) }; 475 476 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) 477 { 478 tcu::Vec4 texSamples[4]; 479 480 // Read tex coords 481 const tcu::Vec2 texCoords[4] = 482 { 483 rr::readTriangleVarying<float>(packet, context, 0, 0).xy(), 484 rr::readTriangleVarying<float>(packet, context, 0, 1).xy(), 485 rr::readTriangleVarying<float>(packet, context, 0, 2).xy(), 486 rr::readTriangleVarying<float>(packet, context, 0, 3).xy(), 487 }; 488 489 if (m_unitTypes[unitNdx] == GL_TEXTURE_2D) 490 { 491 // Transform 492 const tcu::Vec2 transformedTexCoords[4] = 493 { 494 (m_transformations[unitNdx] * Vec3(texCoords[0].x(), texCoords[0].y(), 1.0f)).xy(), 495 (m_transformations[unitNdx] * Vec3(texCoords[1].x(), texCoords[1].y(), 1.0f)).xy(), 496 (m_transformations[unitNdx] * Vec3(texCoords[2].x(), texCoords[2].y(), 1.0f)).xy(), 497 (m_transformations[unitNdx] * Vec3(texCoords[3].x(), texCoords[3].y(), 1.0f)).xy(), 498 }; 499 500 // Sample 501 m_uniforms[2*unitNdx].sampler.tex2D->sample4(texSamples, transformedTexCoords); 502 } 503 else 504 { 505 DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP); 506 507 // Transform 508 const tcu::Vec3 transformedTexCoords[4] = 509 { 510 m_transformations[unitNdx] * Vec3(texCoords[0].x(), texCoords[0].y(), 1.0f), 511 m_transformations[unitNdx] * Vec3(texCoords[1].x(), texCoords[1].y(), 1.0f), 512 m_transformations[unitNdx] * Vec3(texCoords[2].x(), texCoords[2].y(), 1.0f), 513 m_transformations[unitNdx] * Vec3(texCoords[3].x(), texCoords[3].y(), 1.0f), 514 }; 515 516 // Sample 517 m_uniforms[2*unitNdx].sampler.texCube->sample4(texSamples, transformedTexCoords); 518 } 519 520 // Add to sum 521 for (int fragNdx = 0; fragNdx < 4; ++fragNdx) 522 outColors[fragNdx] += colorMultiplier * texSamples[fragNdx]; 523 } 524 525 // output 526 for (int fragNdx = 0; fragNdx < 4; ++fragNdx) 527 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]); 528 } 529} 530 531class TextureUnitCase : public TestCase 532{ 533public: 534 enum CaseType 535 { 536 CASE_ONLY_2D = 0, 537 CASE_ONLY_CUBE, 538 CASE_MIXED, 539 540 CASE_LAST 541 }; 542 TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed); 543 ~TextureUnitCase (void); 544 545 void init (void); 546 void deinit (void); 547 IterateResult iterate (void); 548 549private: 550 struct TextureParameters 551 { 552 GLenum format; 553 GLenum dataType; 554 GLenum wrapModeS; 555 GLenum wrapModeT; 556 GLenum minFilter; 557 GLenum magFilter; 558 }; 559 560 TextureUnitCase (const TextureUnitCase& other); 561 TextureUnitCase& operator= (const TextureUnitCase& other); 562 563 void render (sglr::Context& context); 564 565 const int m_numUnitsParam; 566 const CaseType m_caseType; 567 const deUint32 m_randSeed; 568 569 int m_numTextures; //!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units. 570 int m_numUnits; //!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum 571 572 vector<GLenum> m_textureTypes; 573 vector<TextureParameters> m_textureParams; 574 vector<tcu::Texture2D*> m_textures2d; 575 vector<tcu::TextureCube*> m_texturesCube; 576 vector<int> m_unitTextures; //!< Which texture is used in a particular unit. 577 vector<int> m_ndx2dOrCube; //!< Index of a texture in either m_textures2d or m_texturesCube, depending on texture type. 578 MultiTexShader* m_shader; 579}; 580 581TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed) 582 : TestCase (context, tcu::NODETYPE_SELF_VALIDATE, name, desc) 583 , m_numUnitsParam (numUnits) 584 , m_caseType (caseType) 585 , m_randSeed (randSeed) 586 , m_shader (DE_NULL) 587{ 588} 589 590TextureUnitCase::~TextureUnitCase (void) 591{ 592 TextureUnitCase::deinit(); 593} 594 595void TextureUnitCase::deinit (void) 596{ 597 for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++) 598 delete *i; 599 m_textures2d.clear(); 600 601 for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++) 602 delete *i; 603 m_texturesCube.clear(); 604 605 delete m_shader; 606 m_shader = DE_NULL; 607} 608 609void TextureUnitCase::init (void) 610{ 611 m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS); 612 613 // Make the textures. 614 615 try 616 { 617 tcu::TestLog& log = m_testCtx.getLog(); 618 de::Random rnd (m_randSeed); 619 620 if (rnd.getFloat() < 0.7f) 621 m_numTextures = m_numUnits; // In most cases use one unit per texture. 622 else 623 m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits); // Sometimes assign same texture to multiple units. 624 625 log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage; 626 627 m_textureTypes.reserve(m_numTextures); 628 m_textureParams.reserve(m_numTextures); 629 m_ndx2dOrCube.reserve(m_numTextures); 630 631 // Generate textures. 632 633 for (int texNdx = 0; texNdx < m_numTextures; texNdx++) 634 { 635 // Either fixed or randomized target types (2d or cube), and randomized parameters for every texture. 636 637 TextureParameters params; 638 bool is2d = m_caseType == CASE_ONLY_2D ? true : 639 m_caseType == CASE_ONLY_CUBE ? false : 640 rnd.getBool(); 641 642 GLenum type = is2d ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP; 643 const int texWidth = is2d ? TEXTURE_WIDTH_2D : TEXTURE_WIDTH_CUBE; 644 const int texHeight = is2d ? TEXTURE_HEIGHT_2D : TEXTURE_HEIGHT_CUBE; 645 bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight)); 646 int numLevels = mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1; 647 648 params.wrapModeS = s_testWrapModes [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)]; 649 params.wrapModeT = s_testWrapModes [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)]; 650 params.magFilter = s_testMagFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)]; 651 params.dataType = s_testDataTypes [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testDataTypes) - 1)]; 652 653 // Certain minification filters are only used when using mipmaps. 654 if (mipmaps) 655 params.minFilter = s_testMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)]; 656 else 657 params.minFilter = s_testNonMipmapMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)]; 658 659 // Format may depend on data type. 660 if (params.dataType == GL_UNSIGNED_SHORT_5_6_5) 661 params.format = GL_RGB; 662 else if (params.dataType == GL_UNSIGNED_SHORT_4_4_4_4 || params.dataType == GL_UNSIGNED_SHORT_5_5_5_1) 663 params.format = GL_RGBA; 664 else 665 params.format = s_testFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testFormats) - 1)]; 666 667 m_textureTypes.push_back(type); 668 m_textureParams.push_back(params); 669 670 // Create new texture. 671 672 if (is2d) 673 { 674 m_ndx2dOrCube.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d array. 675 m_textures2d.push_back(new tcu::Texture2D(glu::mapGLTransferFormat(params.format, params.dataType), texWidth, texHeight)); 676 } 677 else 678 { 679 m_ndx2dOrCube.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube array. 680 DE_ASSERT(texWidth == texHeight); 681 m_texturesCube.push_back(new tcu::TextureCube(glu::mapGLTransferFormat(params.format, params.dataType), texWidth)); 682 } 683 684 tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(is2d ? m_textures2d.back()->getFormat() : m_texturesCube.back()->getFormat()); 685 Vec4 cBias = fmtInfo.valueMin; 686 Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; 687 688 // Fill with grid texture. 689 690 int numFaces = is2d ? 1 : (int)tcu::CUBEFACE_LAST; 691 692 for (int face = 0; face < numFaces; face++) 693 { 694 deUint32 rgb = rnd.getUint32() & 0x00ffffff; 695 deUint32 alpha0 = 0xff000000; 696 deUint32 alpha1 = 0xff000000; 697 698 if (params.format == GL_ALPHA) // \note This needs alpha to be visible. 699 { 700 alpha0 &= rnd.getUint32(); 701 alpha1 = ~alpha0; 702 } 703 704 deUint32 colorA = alpha0 | rgb; 705 deUint32 colorB = alpha1 | ~rgb; 706 707 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) 708 { 709 if (is2d) 710 m_textures2d.back()->allocLevel(levelNdx); 711 else 712 m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx); 713 714 int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps. 715 716 tcu::PixelBufferAccess access = is2d ? m_textures2d.back()->getLevel(levelNdx) : m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face); 717 tcu::fillWithGrid(access, curCellSize, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); 718 } 719 } 720 } 721 722 // Assign a texture index to each unit. 723 724 m_unitTextures.reserve(m_numUnits); 725 726 // \note Every texture is used at least once. 727 for (int i = 0; i < m_numTextures; i++) 728 m_unitTextures.push_back(i); 729 730 // Assign a random texture to remaining units. 731 while ((int)m_unitTextures.size() < m_numUnits) 732 m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1)); 733 734 rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end()); 735 736 // Create shader. 737 738 vector<GLenum> unitTypes; 739 unitTypes.reserve(m_numUnits); 740 for (int i = 0; i < m_numUnits; i++) 741 unitTypes.push_back(m_textureTypes[m_unitTextures[i]]); 742 743 DE_ASSERT(m_shader == DE_NULL); 744 m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes); 745 } 746 catch (const std::exception&) 747 { 748 // Clean up to save memory. 749 TextureUnitCase::deinit(); 750 throw; 751 } 752} 753 754TextureUnitCase::IterateResult TextureUnitCase::iterate (void) 755{ 756 glu::RenderContext& renderCtx = m_context.getRenderContext(); 757 const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget(); 758 tcu::TestLog& log = m_testCtx.getLog(); 759 de::Random rnd (m_randSeed); 760 761 int viewportWidth = deMin32(VIEWPORT_WIDTH, renderTarget.getWidth()); 762 int viewportHeight = deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight()); 763 int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth); 764 int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight); 765 766 tcu::Surface gles2Frame (viewportWidth, viewportHeight); 767 tcu::Surface refFrame (viewportWidth, viewportHeight); 768 769 { 770 // First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods(). 771 772 vector<IVec2> texSizes; 773 texSizes.reserve(m_numUnits); 774 775 for (int i = 0; i < m_numUnits; i++) 776 { 777 int texNdx = m_unitTextures[i]; 778 int texNdxInType = m_ndx2dOrCube[texNdx]; 779 GLenum type = m_textureTypes[texNdx]; 780 781 switch (type) 782 { 783 case GL_TEXTURE_2D: texSizes.push_back(IVec2(m_textures2d[texNdxInType]->getWidth(), m_textures2d[texNdxInType]->getHeight())); break; 784 case GL_TEXTURE_CUBE_MAP: texSizes.push_back(IVec2(m_texturesCube[texNdxInType]->getSize(), m_texturesCube[texNdxInType]->getSize())); break; 785 default: 786 DE_ASSERT(DE_FALSE); 787 } 788 } 789 790 m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight)); 791 } 792 793 // Render using GLES2. 794 { 795 sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight)); 796 797 render(context); 798 799 context.readPixels(gles2Frame, 0, 0, viewportWidth, viewportHeight); 800 } 801 802 // Render reference image. 803 { 804 sglr::ReferenceContextBuffers buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight); 805 sglr::ReferenceContext context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer()); 806 807 render(context); 808 809 context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight); 810 } 811 812 // Compare images. 813 const float threshold = 0.001f; 814 bool isOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles2Frame, threshold, tcu::COMPARE_LOG_RESULT); 815 816 // Store test result. 817 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 818 isOk ? "Pass" : "Image comparison failed"); 819 820 return STOP; 821} 822 823void TextureUnitCase::render (sglr::Context& context) 824{ 825 // Setup textures. 826 827 vector<deUint32> textureGLNames; 828 vector<bool> 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. 829 830 textureGLNames.resize(m_numTextures); 831 context.genTextures(m_numTextures, &textureGLNames[0]); 832 833 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++) 834 { 835 int texNdx = m_unitTextures[unitNdx]; 836 837 // Bind texture to unit. 838 context.activeTexture(GL_TEXTURE0 + unitNdx); 839 context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]); 840 841 if (!isTextureSetUp[texNdx]) 842 { 843 // Binding this texture for first time, so set parameters and data. 844 845 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS); 846 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT); 847 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter); 848 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter); 849 850 if (m_textureTypes[texNdx] == GL_TEXTURE_2D) 851 { 852 int ndx2d = m_ndx2dOrCube[texNdx]; 853 const tcu::Texture2D* texture = m_textures2d[ndx2d]; 854 bool mipmaps = (deIsPowerOfTwo32(texture->getWidth()) && deIsPowerOfTwo32(texture->getHeight())); 855 int numLevels = mipmaps ? deLog2Floor32(de::max(texture->getWidth(), texture->getHeight()))+1 : 1; 856 857 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); 858 859 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) 860 { 861 tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx); 862 int width = access.getWidth(); 863 int height = access.getHeight(); 864 865 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); 866 867 context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr()); 868 } 869 } 870 else 871 { 872 DE_ASSERT(m_textureTypes[texNdx] == GL_TEXTURE_CUBE_MAP); 873 874 int ndxCube = m_ndx2dOrCube[texNdx]; 875 const tcu::TextureCube* texture = m_texturesCube[ndxCube]; 876 bool mipmaps = deIsPowerOfTwo32(texture->getSize()) != DE_FALSE; 877 int numLevels = mipmaps ? deLog2Floor32(texture->getSize())+1 : 1; 878 879 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1); 880 881 for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++) 882 { 883 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) 884 { 885 tcu::ConstPixelBufferAccess access = texture->getLevelFace(levelNdx, (tcu::CubeFace)face); 886 int width = access.getWidth(); 887 int height = access.getHeight(); 888 889 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width); 890 891 context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr()); 892 } 893 } 894 } 895 896 isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later. 897 } 898 } 899 900 GLU_EXPECT_NO_ERROR(context.getError(), "Set textures"); 901 902 // Setup shader 903 904 deUint32 shaderID = context.createProgram(m_shader); 905 906 // Draw. 907 908 context.clearColor(0.125f, 0.25f, 0.5f, 1.0f); 909 context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 910 m_shader->setUniforms(context, shaderID); 911 sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f)); 912 GLU_EXPECT_NO_ERROR(context.getError(), "Draw"); 913 914 // Delete previously generated texture names. 915 916 context.deleteTextures(m_numTextures, &textureGLNames[0]); 917 GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures"); 918} 919 920TextureUnitTests::TextureUnitTests (Context& context) 921 : TestCaseGroup(context, "units", "Texture Unit Usage Tests") 922{ 923} 924 925TextureUnitTests::~TextureUnitTests (void) 926{ 927} 928 929void TextureUnitTests::init (void) 930{ 931 const int numTestsPerGroup = 10; 932 933 static const int unitCounts[] = 934 { 935 2, 936 4, 937 8, 938 -1 // \note Negative stands for the implementation-specified maximum. 939 }; 940 941 for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++) 942 { 943 int numUnits = unitCounts[unitCountNdx]; 944 945 string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units"; 946 947 tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), ""); 948 addChild(countGroup); 949 950 DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0); 951 952 for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++) 953 { 954 const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D ? "only_2d" : 955 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE ? "only_cube" : 956 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED ? "mixed" : 957 DE_NULL; 958 DE_ASSERT(caseTypeGroupName != DE_NULL); 959 960 tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, ""); 961 countGroup->addChild(caseTypeGroup); 962 963 for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++) 964 caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, (deUint32)deInt32Hash(testNdx))); 965 } 966 } 967} 968 969} // Functional 970} // gles2 971} // deqp 972