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