Add new framebuffer fetch extension tests am: 2a609fb223
[platform/upstream/VK-GL-CTS.git] / modules / gles2 / functional / es2fTextureUnitTests.cpp
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 tcu::Vec2;
45 using tcu::Vec3;
46 using tcu::Vec4;
47 using tcu::IVec2;
48 using tcu::Mat3;
49 using std::vector;
50 using std::string;
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[] =
76 {
77         GL_RGB,
78         GL_RGBA,
79         GL_ALPHA,
80         GL_LUMINANCE,
81         GL_LUMINANCE_ALPHA
82 };
83
84 static 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
92 static const GLenum s_testWrapModes[] =
93 {
94         GL_CLAMP_TO_EDGE,
95         GL_REPEAT,
96         GL_MIRRORED_REPEAT,
97 };
98
99 static 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
109 static const GLenum s_testNonMipmapMinFilters[] =
110 {
111         GL_NEAREST,
112         GL_LINEAR
113 };
114
115 static const GLenum s_testMagFilters[] =
116 {
117         GL_NEAREST,
118         GL_LINEAR
119 };
120
121 static 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
131 static 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
173 static 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().
206 static 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
220 static 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
231 static 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
241 class MultiTexShader : public sglr::ShaderProgram
242 {
243 public:
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
249 private:
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
259 MultiTexShader::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
397 void 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
412 void 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
453 void 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
464 void 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
531 class TextureUnitCase : public TestCase
532 {
533 public:
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
549 private:
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
581 TextureUnitCase::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
590 TextureUnitCase::~TextureUnitCase (void)
591 {
592         TextureUnitCase::deinit();
593 }
594
595 void 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
609 void 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
754 TextureUnitCase::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
823 void 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
920 TextureUnitTests::TextureUnitTests (Context& context)
921         : TestCaseGroup(context, "units", "Texture Unit Usage Tests")
922 {
923 }
924
925 TextureUnitTests::~TextureUnitTests (void)
926 {
927 }
928
929 void 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