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