Merge "Fix color change verification in dithering tests" into nougat-cts-dev am:...
[platform/upstream/VK-GL-CTS.git] / modules / gles3 / functional / es3fTextureUnitTests.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.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 "es3fTextureUnitTests.hpp"
27 #include "glsTextureTestUtil.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluContextInfo.hpp"
30 #include "gluTextureUtil.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuImageCompare.hpp"
33 #include "tcuMatrix.hpp"
34 #include "tcuRenderTarget.hpp"
35 #include "sglrContextUtil.hpp"
36 #include "sglrReferenceContext.hpp"
37 #include "sglrGLContext.hpp"
38 #include "deRandom.hpp"
39 #include "deStringUtil.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::IVec3;
49 using tcu::Mat3;
50 using tcu::Mat4;
51 using std::vector;
52 using std::string;
53 using namespace glw; // GL types
54
55 namespace deqp
56 {
57
58 using namespace gls::TextureTestUtil;
59
60 namespace gles3
61 {
62 namespace Functional
63 {
64
65 static const int VIEWPORT_WIDTH                         = 128;
66 static const int VIEWPORT_HEIGHT                        = 128;
67
68 static const int TEXTURE_WIDTH_2D                       = 128;
69 static const int TEXTURE_HEIGHT_2D                      = 128;
70
71 // \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time.
72 static const int TEXTURE_WIDTH_CUBE                     = 256;
73 static const int TEXTURE_HEIGHT_CUBE            = 256;
74
75 static const int TEXTURE_WIDTH_2D_ARRAY         = 64;
76 static const int TEXTURE_HEIGHT_2D_ARRAY        = 64;
77 static const int TEXTURE_LAYERS_2D_ARRAY        = 4;
78
79 static const int TEXTURE_WIDTH_3D                       = 32;
80 static const int TEXTURE_HEIGHT_3D                      = 32;
81 static const int TEXTURE_DEPTH_3D                       = 32;
82
83 static const int GRID_CELL_SIZE                         = 8;
84
85 static const GLenum s_testSizedInternalFormats[] =
86 {
87         GL_RGBA32F,
88         GL_RGBA32I,
89         GL_RGBA32UI,
90         GL_RGBA16F,
91         GL_RGBA16I,
92         GL_RGBA16UI,
93         GL_RGBA8,
94         GL_RGBA8I,
95         GL_RGBA8UI,
96         GL_SRGB8_ALPHA8,
97         GL_RGB10_A2,
98         GL_RGB10_A2UI,
99         GL_RGBA4,
100         GL_RGB5_A1,
101         GL_RGBA8_SNORM,
102         GL_RGB8,
103         GL_RGB565,
104         GL_R11F_G11F_B10F,
105         GL_RGB32F,
106         GL_RGB32I,
107         GL_RGB32UI,
108         GL_RGB16F,
109         GL_RGB16I,
110         GL_RGB16UI,
111         GL_RGB8_SNORM,
112         GL_RGB8I,
113         GL_RGB8UI,
114         GL_SRGB8,
115         GL_RGB9_E5,
116         GL_RG32F,
117         GL_RG32I,
118         GL_RG32UI,
119         GL_RG16F,
120         GL_RG16I,
121         GL_RG16UI,
122         GL_RG8,
123         GL_RG8I,
124         GL_RG8UI,
125         GL_RG8_SNORM,
126         GL_R32F,
127         GL_R32I,
128         GL_R32UI,
129         GL_R16F,
130         GL_R16I,
131         GL_R16UI,
132         GL_R8,
133         GL_R8I,
134         GL_R8UI,
135         GL_R8_SNORM
136 };
137
138 static const GLenum s_testWrapModes[] =
139 {
140         GL_CLAMP_TO_EDGE,
141         GL_REPEAT,
142         GL_MIRRORED_REPEAT,
143 };
144
145 static const GLenum s_testMinFilters[] =
146 {
147         GL_NEAREST,
148         GL_LINEAR,
149         GL_NEAREST_MIPMAP_NEAREST,
150         GL_LINEAR_MIPMAP_NEAREST,
151         GL_NEAREST_MIPMAP_LINEAR,
152         GL_LINEAR_MIPMAP_LINEAR
153 };
154
155 static const GLenum s_testNonMipmapMinFilters[] =
156 {
157         GL_NEAREST,
158         GL_LINEAR
159 };
160
161 static const GLenum s_testNearestMinFilters[] =
162 {
163         GL_NEAREST,
164         GL_NEAREST_MIPMAP_NEAREST
165 };
166
167 static const GLenum s_testMagFilters[] =
168 {
169         GL_NEAREST,
170         GL_LINEAR
171 };
172
173 static const GLenum s_cubeFaceTargets[] =
174 {
175         GL_TEXTURE_CUBE_MAP_POSITIVE_X,
176         GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
177         GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
178         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
179         GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
180         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
181 };
182
183 // Extend a 3x3 transformation matrix to an equivalent 4x4 transformation matrix (i.e. 1.0 in right-down cell, 0.0's in other new cells).
184 static Mat4 matExtend3To4 (const Mat3& mat)
185 {
186         Mat4 res;
187         for (int rowNdx = 0; rowNdx < 3; rowNdx++)
188         {
189                 Vec3 row = mat.getRow(rowNdx);
190                 res.setRow(rowNdx, Vec4(row.x(), row.y(), row.z(), 0.0f));
191         }
192         res.setRow(3, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
193
194         return res;
195 }
196
197 static string generateMultiTexFragmentShader (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
198 {
199         // The fragment shader calculates the average of a set of textures.
200
201         string samplersStr;
202         string matricesStr;
203         string scalesStr;
204         string biasesStr;
205         string lookupsStr;
206
207         string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)";
208
209         for (int ndx = 0; ndx < numUnits; ndx++)
210         {
211                 string ndxStr                           = de::toString(ndx);
212                 string samplerName                      = "u_sampler" + ndxStr;
213                 string transformationName       = "u_trans" + ndxStr;
214                 string scaleName                        = "u_texScale" + ndxStr;
215                 string biasName                         = "u_texBias" + ndxStr;
216
217                 samplersStr += string("") + "uniform highp " + glu::getDataTypeName(samplerTypes[ndx]) + " " + samplerName + ";\n";
218                 matricesStr += "uniform highp mat4 " + transformationName + ";\n";
219                 scalesStr += "uniform highp vec4 " + scaleName + ";\n";
220                 biasesStr += "uniform highp vec4 " + biasName + ";\n";
221
222                 string lookupCoord = transformationName + "*vec4(v_coord, 1.0, 1.0)";
223
224                 if (unitTypes[ndx] == GL_TEXTURE_2D)
225                         lookupCoord = "vec2(" + lookupCoord + ")";
226                 else
227                         lookupCoord = "vec3(" + lookupCoord + ")";
228
229                 lookupsStr += "\tcolor += " + colorMultiplier + "*(vec4(texture(" + samplerName + ", " + lookupCoord + "))*" + scaleName + " + " + biasName + ");\n";
230         }
231
232         return "#version 300 es\n"
233                    "layout(location = 0) out mediump vec4 o_color;\n" +
234                    samplersStr +
235                    matricesStr +
236                    scalesStr +
237                    biasesStr +
238                    "in highp vec2 v_coord;\n"
239                    "\n"
240                    "void main (void)\n"
241                    "{\n"
242                    "    mediump vec4 color = vec4(0.0);\n" +
243                    lookupsStr +
244                    "    o_color = color;\n"
245                    "}\n";
246 }
247
248 static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
249 {
250         sglr::pdec::ShaderProgramDeclaration decl;
251
252         decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT);
253         decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT);
254         decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT);
255         decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT);
256
257         for (int ndx = 0; ndx < numUnits; ++ndx)
258         {
259                 string samplerName                      = "u_sampler" + de::toString(ndx);
260                 string transformationName       = "u_trans" + de::toString(ndx);
261                 string scaleName                        = "u_texScale" + de::toString(ndx);
262                 string biasName                         = "u_texBias" + de::toString(ndx);
263
264                 decl << sglr::pdec::Uniform(samplerName, samplerTypes[ndx]);
265                 decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT4);
266                 decl << sglr::pdec::Uniform(scaleName, glu::TYPE_FLOAT_VEC4);
267                 decl << sglr::pdec::Uniform(biasName, glu::TYPE_FLOAT_VEC4);
268         }
269
270         decl << sglr::pdec::VertexSource("#version 300 es\n"
271                                                                          "in highp vec4 a_position;\n"
272                                                                          "in highp vec2 a_coord;\n"
273                                                                          "out highp vec2 v_coord;\n"
274                                                                          "\n"
275                                                                          "void main (void)\n"
276                                                                          "{\n"
277                                                                          "      gl_Position = a_position;\n"
278                                                                          "      v_coord = a_coord;\n"
279                                                                          "}\n");
280         decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes, samplerTypes));
281
282         return decl;
283 }
284
285 // Calculates values that will be used in calculateLod().
286 static tcu::Vector<tcu::Vec2, 3> calculateLodDerivateParts (const Mat4& transformation)
287 {
288         // Calculate transformed coordinates of three screen corners.
289         Vec3 trans00 = (transformation * Vec4(0.0f, 0.0f, 1.0f, 1.0f)).xyz();
290         Vec3 trans01 = (transformation * Vec4(0.0f, 1.0f, 1.0f, 1.0f)).xyz();
291         Vec3 trans10 = (transformation * Vec4(1.0f, 0.0f, 1.0f, 1.0f)).xyz();
292
293         return tcu::Vector<tcu::Vec2, 3>(Vec2(trans10.x() - trans00.x(), trans01.x() - trans00.x()),
294                                                                          Vec2(trans10.y() - trans00.y(), trans01.y() - trans00.y()),
295                                                                          Vec2(trans10.z() - trans00.z(), trans01.z() - trans00.z()));
296 }
297
298 // Calculates the maximum allowed lod from derivates
299 static float calculateLodMax(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
300 {
301         float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
302         float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
303         float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
304         float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
305         float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
306         float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
307
308         const float mu = de::max(de::abs(dudx), de::abs(dudy));
309         const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
310         const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
311         return deFloatLog2(mu + mv + mw);
312 }
313
314 // Calculates the minimum allowed lod from derivates
315 static float calculateLodMin(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
316 {
317         float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
318         float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
319         float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
320         float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
321         float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
322         float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
323
324         const float mu = de::max(de::abs(dudx), de::abs(dudy));
325         const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
326         const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
327         return deFloatLog2(de::max(mu, de::max(mv, mw)));
328 }
329
330 class MultiTexShader : public sglr::ShaderProgram
331 {
332 public:
333                                                         MultiTexShader  (deUint32 randSeed,
334                                                                                          int numUnits,
335                                                                                          const vector<GLenum>& unitTypes,
336                                                                                          const vector<glu::DataType>& samplerTypes,
337                                                                                          const vector<Vec4>& texScales,
338                                                                                          const vector<Vec4>& texBiases,
339                                                                                          const vector<int>& num2dArrayLayers); // \note 2d array layer "coordinate" isn't normalized, so this is needed here.
340
341         void                                    setUniforms             (sglr::Context& context, deUint32 program) const;
342         void                                    makeSafeLods    (const vector<IVec3>& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 .
343
344 private:
345         void                                    shadeVertices   (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const;
346         void                                    shadeFragments  (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const;
347
348         int                                                                     m_numUnits;
349         vector<GLenum>                                          m_unitTypes;            // 2d, cube map, 2d array or 3d.
350         vector<Vec4>                                            m_texScales;
351         vector<Vec4>                                            m_texBiases;
352         vector<Mat4>                                            m_transformations;
353         vector<tcu::Vector<tcu::Vec2, 3> >      m_lodDerivateParts;     // Parts of lod derivates; computed in init(), used in eval().
354 };
355
356 MultiTexShader::MultiTexShader (deUint32 randSeed,
357                                                                 int numUnits,
358                                                                 const vector<GLenum>& unitTypes,
359                                                                 const vector<glu::DataType>& samplerTypes,
360                                                                 const vector<Vec4>& texScales,
361                                                                 const vector<Vec4>& texBiases,
362                                                                 const vector<int>& num2dArrayLayers)
363                 : sglr::ShaderProgram   (generateShaderProgramDeclaration(numUnits, unitTypes, samplerTypes))
364                 , m_numUnits            (numUnits)
365                 , m_unitTypes           (unitTypes)
366                 , m_texScales           (texScales)
367                 , m_texBiases           (texBiases)
368 {
369         // 2d-to-cube-face transformations.
370         // \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well.
371         static const float s_cubeTransforms[][3*3] =
372         {
373                 // Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1))
374                 {  0.0f,  0.0f, -1.0f,
375                    0.0f, -2.0f,  1.0f,
376                    2.0f,  0.0f, -1.0f },
377                 // Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1))
378                 {  0.0f,  0.0f,  1.0f,
379                    0.0f, -2.0f,  1.0f,
380                   -2.0f,  0.0f,  1.0f },
381                 // Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1))
382                 {  2.0f,  0.0f, -1.0f,
383                    0.0f,  0.0f, -1.0f,
384                    0.0f, -2.0f,  1.0f },
385                 // Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1))
386                 {  2.0f,  0.0f, -1.0f,
387                    0.0f,  0.0f,  1.0f,
388                    0.0f,  2.0f, -1.0f },
389                 // Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1)
390                 { -2.0f,  0.0f,  1.0f,
391                    0.0f, -2.0f,  1.0f,
392                    0.0f,  0.0f, -1.0f },
393                 // Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1)
394                 {  2.0f,  0.0f, -1.0f,
395                    0.0f, -2.0f,  1.0f,
396                    0.0f,  0.0f,  1.0f }
397         };
398
399         // Generate transformation matrices.
400
401         de::Random rnd(randSeed);
402
403         m_transformations.reserve(m_numUnits);
404         m_lodDerivateParts.reserve(m_numUnits);
405
406         int tex2dArrayNdx = 0; // Keep track of 2d texture array index.
407
408         DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
409
410         for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
411         {
412                 if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
413                 {
414                         float rotAngle                          = rnd.getFloat(0.0f, 2.0f*DE_PI);
415                         float xScaleFactor                      = rnd.getFloat(0.7f, 1.5f);
416                         float yScaleFactor                      = rnd.getFloat(0.7f, 1.5f);
417                         float xShearAmount                      = rnd.getFloat(0.0f, 0.5f);
418                         float yShearAmount                      = rnd.getFloat(0.0f, 0.5f);
419                         float xTranslationAmount        = rnd.getFloat(-0.5f, 0.5f);
420                         float yTranslationAmount        = rnd.getFloat(-0.5f, 0.5f);
421
422                         static const float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations.
423                         {
424                                 1.0f,  0.0f, -0.5f,
425                                 0.0f,  1.0f, -0.5f,
426                                 0.0f,  0.0f,  1.0f
427                         };
428                         float rotTransfData[3*3] =
429                         {
430                                 deFloatCos(rotAngle),   -deFloatSin(rotAngle),  0.0f,
431                                 deFloatSin(rotAngle),   deFloatCos(rotAngle),   0.0f,
432                                 0.0f,                                   0.0f,                                   1.0f
433                         };
434                         float scaleTransfData[3*3] =
435                         {
436                                 xScaleFactor,   0.0f,                   0.0f,
437                                 0.0f,                   yScaleFactor,   0.0f,
438                                 0.0f,                   0.0f,                   1.0f
439                         };
440                         float xShearTransfData[3*3] =
441                         {
442                                 1.0f,                   xShearAmount,   0.0f,
443                                 0.0f,                   1.0f,                   0.0f,
444                                 0.0f,                   0.0f,                   1.0f
445                         };
446                         float yShearTransfData[3*3] =
447                         {
448                                 1.0f,                   0.0f,                   0.0f,
449                                 yShearAmount,   1.0f,                   0.0f,
450                                 0.0f,                   0.0f,                   1.0f
451                         };
452                         float translationTransfData[3*3] =
453                         {
454                                 1.0f,   0.0f,   xTranslationAmount,
455                                 0.0f,   1.0f,   yTranslationAmount,
456                                 0.0f,   0.0f,   1.0f
457                         };
458
459                         Mat4 transformation = matExtend3To4(Mat3(tempOffsetData) *
460                                                                                                 Mat3(translationTransfData) *
461                                                                                                 Mat3(rotTransfData) *
462                                                                                                 Mat3(scaleTransfData) *
463                                                                                                 Mat3(xShearTransfData) *
464                                                                                                 Mat3(yShearTransfData) *
465                                                                                                 (Mat3(tempOffsetData) * (-1.0f)));
466
467                         m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
468                         m_transformations.push_back(transformation);
469                 }
470                 else if (m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP)
471                 {
472                         DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms));
473
474                         float planarTransData[3*3];
475
476                         // 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.
477
478                         for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++)
479                         {
480                                 if (i == 0 || i == 4)
481                                         planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling.
482                                 else if (i == 8)
483                                         planarTransData[i] = 1.0f;
484                                 else
485                                         planarTransData[i] = 0.0f;
486                         }
487
488                         int             faceNdx                 = rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1);
489                         Mat3    planarTrans             (planarTransData);                                                                                              // Planar, face-agnostic transformation.
490                         Mat4    finalTrans              = matExtend3To4(Mat3(s_cubeTransforms[faceNdx]) * planarTrans); // Final transformation from planar to cube map coordinates, including the transformation just generated.
491                         Mat4    planarTrans4x4  = matExtend3To4(planarTrans);
492
493                         m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans4x4));
494                         m_transformations.push_back(finalTrans);
495                 }
496                 else
497                 {
498                         DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_3D || m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY);
499
500                         float transData[4*4];
501
502                         for (int i = 0; i < 4*4; i++)
503                         {
504                                 float sign = rnd.getBool() ? 1.0f : -1.0f;
505                                 transData[i] = rnd.getFloat(0.7f, 1.4f) * sign;
506                         }
507
508                         Mat4 transformation(transData);
509
510                         if (m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY)
511                         {
512                                 // Z direction: Translate by 0.5 and scale by layer amount.
513
514                                 float numLayers = (float)num2dArrayLayers[tex2dArrayNdx];
515
516                                 static const float zTranslationTransfData[4*4] =
517                                 {
518                                         1.0f, 0.0f, 0.0f, 0.0f,
519                                         0.0f, 1.0f, 0.0f, 0.0f,
520                                         0.0f, 0.0f, 1.0f, 0.5f,
521                                         0.0f, 0.0f, 0.0f, 1.0f
522                                 };
523
524                                 float zScaleTransfData[4*4] =
525                                 {
526                                         1.0f,           0.0f,           0.0f,           0.0f,
527                                         0.0f,           1.0f,           0.0f,           0.0f,
528                                         0.0f,           0.0f,           numLayers,      0.0f,
529                                         0.0f,           0.0f,           0.0f,           1.0f
530                                 };
531
532                                 transformation = transformation * Mat4(zScaleTransfData) * Mat4(zTranslationTransfData);
533
534                                 tex2dArrayNdx++;
535                         }
536
537                         m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
538                         m_transformations.push_back(Mat4(transformation));
539                 }
540         }
541 }
542
543 void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const
544 {
545         ctx.useProgram(program);
546
547         // Sampler and matrix uniforms.
548
549         for (int ndx = 0; ndx < m_numUnits; ndx++)
550         {
551                 string                  ndxStr          = de::toString(ndx);
552
553                 ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx);
554                 ctx.uniformMatrix4fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]);
555                 ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texScale" + ndxStr).c_str()), 1, m_texScales[ndx].getPtr());
556                 ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texBias" + ndxStr).c_str()), 1, m_texBiases[ndx].getPtr());
557         }
558 }
559
560 void MultiTexShader::makeSafeLods (const vector<IVec3>& textureSizes, const IVec2& viewportSize)
561 {
562         DE_ASSERT((int)textureSizes.size() == m_numUnits);
563
564         static const float shrinkScaleMat2dData[3*3] =
565         {
566                 0.95f,  0.0f,   0.0f,
567                 0.0f,   0.95f,  0.0f,
568                 0.0f,   0.0f,   1.0f
569         };
570         static const float shrinkScaleMat3dData[3*3] =
571         {
572                 0.95f,  0.0f,   0.0f,
573                 0.0f,   0.95f,  0.0f,
574                 0.0f,   0.0f,   0.95f
575         };
576         Mat4 shrinkScaleMat2d = matExtend3To4(Mat3(shrinkScaleMat2dData));
577         Mat4 shrinkScaleMat3d = matExtend3To4(Mat3(shrinkScaleMat3dData));
578
579         Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y());
580
581         for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
582         {
583                 // 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.
584                 for (;;)
585                 {
586                         const float threshold = 0.1f;
587                         const float epsilon     = 0.01f;
588
589                         const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
590                         const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
591
592                         const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1);
593                         const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1);
594
595                         if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) ||
596                                 de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) ||
597                                 maxLevel != minLevel)
598                         {
599                                 m_transformations[unitNdx] = (m_unitTypes[unitNdx] == GL_TEXTURE_3D ? shrinkScaleMat3d : shrinkScaleMat2d) * m_transformations[unitNdx];
600                                 m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]);
601                         }
602                         else
603                                 break;
604                 }
605         }
606 }
607
608 void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
609 {
610         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
611         {
612                 rr::VertexPacket& packet = *(packets[packetNdx]);
613
614                 packet.position         = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
615                 packet.outputs[0]       = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
616         }
617 }
618
619 void MultiTexShader::shadeFragments     (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
620 {
621         DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
622         DE_ASSERT((int)m_transformations.size() == m_numUnits);
623         DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits);
624
625         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
626         {
627                 rr::FragmentPacket& packet                              = packets[packetNdx];
628                 const float                     colorMultiplier         = 1.0f / (float)m_numUnits;
629                 Vec4                            outColors[4]            = { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) };
630
631                 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
632                 {
633                         tcu::Vec4 texSamples[4];
634
635                         // Read tex coords
636                         const tcu::Vec2 texCoords[4] =
637                         {
638                                 rr::readTriangleVarying<float>(packet, context, 0, 0).xy(),
639                                 rr::readTriangleVarying<float>(packet, context, 0, 1).xy(),
640                                 rr::readTriangleVarying<float>(packet, context, 0, 2).xy(),
641                                 rr::readTriangleVarying<float>(packet, context, 0, 3).xy(),
642                         };
643
644                         // Transform
645                         tcu::Vec3 coords3D[4] =
646                         {
647                                 (m_transformations[unitNdx] * Vec4(texCoords[0].x(), texCoords[0].y(), 1.0f, 1.0f)).xyz(),
648                                 (m_transformations[unitNdx] * Vec4(texCoords[1].x(), texCoords[1].y(), 1.0f, 1.0f)).xyz(),
649                                 (m_transformations[unitNdx] * Vec4(texCoords[2].x(), texCoords[2].y(), 1.0f, 1.0f)).xyz(),
650                                 (m_transformations[unitNdx] * Vec4(texCoords[3].x(), texCoords[3].y(), 1.0f, 1.0f)).xyz(),
651                         };
652
653                         // To 2D
654                         const tcu::Vec2 coords2D[4] =
655                         {
656                                 coords3D[0].xy(),
657                                 coords3D[1].xy(),
658                                 coords3D[2].xy(),
659                                 coords3D[3].xy(),
660                         };
661
662                         // Sample
663                         switch (m_unitTypes[unitNdx])
664                         {
665                                 case GL_TEXTURE_2D:                     m_uniforms[4*unitNdx].sampler.tex2D->sample4(texSamples, coords2D);                     break;
666                                 case GL_TEXTURE_CUBE_MAP:       m_uniforms[4*unitNdx].sampler.texCube->sample4(texSamples, coords3D);           break;
667                                 case GL_TEXTURE_2D_ARRAY:       m_uniforms[4*unitNdx].sampler.tex2DArray->sample4(texSamples, coords3D);        break;
668                                 case GL_TEXTURE_3D:                     m_uniforms[4*unitNdx].sampler.tex3D->sample4(texSamples, coords3D);                     break;
669                                 default:
670                                         DE_ASSERT(DE_FALSE);
671                         }
672
673                         // Add to sum
674                         for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
675                                 outColors[fragNdx] += colorMultiplier * (texSamples[fragNdx]*m_texScales[unitNdx] + m_texBiases[unitNdx]);
676                 }
677
678                 // output
679                 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
680                         rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]);
681         }
682 }
683
684 class TextureUnitCase : public TestCase
685 {
686 public:
687         enum CaseType
688         {
689                 CASE_ONLY_2D = 0,
690                 CASE_ONLY_CUBE,
691                 CASE_ONLY_2D_ARRAY,
692                 CASE_ONLY_3D,
693                 CASE_MIXED,
694
695                 CASE_LAST
696         };
697                                                                 TextureUnitCase         (Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed);
698                                                                 ~TextureUnitCase        (void);
699
700         void                                            init                            (void);
701         void                                            deinit                          (void);
702         IterateResult                           iterate                         (void);
703
704 private:
705         struct TextureParameters
706         {
707                 GLenum internalFormat;
708                 GLenum wrapModeS;
709                 GLenum wrapModeT;
710                 GLenum wrapModeR;
711                 GLenum minFilter;
712                 GLenum magFilter;
713         };
714
715                                                                         TextureUnitCase                 (const TextureUnitCase& other);
716         TextureUnitCase&                                operator=                               (const TextureUnitCase& other);
717
718         void                                                    upload2dTexture                 (int texNdx, sglr::Context& context);
719         void                                                    uploadCubeTexture               (int texNdx, sglr::Context& context);
720         void                                                    upload2dArrayTexture    (int texNdx, sglr::Context& context);
721         void                                                    upload3dTexture                 (int texNdx, sglr::Context& context);
722
723         void                                                    render                                  (sglr::Context& context);
724
725         const int                                               m_numUnitsParam;
726         const CaseType                                  m_caseType;
727         const deUint32                                  m_randSeed;
728
729         int                                                             m_numTextures;  //!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units.
730         int                                                             m_numUnits;             //!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum
731
732         vector<GLenum>                                  m_textureTypes;
733         vector<TextureParameters>               m_textureParams;
734         vector<tcu::Texture2D*>                 m_textures2d;
735         vector<tcu::TextureCube*>               m_texturesCube;
736         vector<tcu::Texture2DArray*>    m_textures2dArray;
737         vector<tcu::Texture3D*>                 m_textures3d;
738         vector<int>                                             m_unitTextures; //!< Which texture is used in a particular unit.
739         vector<int>                                             m_ndxTexType;   //!< Index of a texture in m_textures2d, m_texturesCube, m_textures2dArray or m_textures3d, depending on texture type.
740         MultiTexShader*                                 m_shader;
741 };
742
743 TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed)
744         : TestCase                      (context, tcu::NODETYPE_SELF_VALIDATE, name, desc)
745         , m_numUnitsParam       (numUnits)
746         , m_caseType            (caseType)
747         , m_randSeed            (randSeed)
748         , m_shader                      (DE_NULL)
749 {
750 }
751
752 TextureUnitCase::~TextureUnitCase (void)
753 {
754         TextureUnitCase::deinit();
755 }
756
757 void TextureUnitCase::deinit (void)
758 {
759         for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
760                 delete *i;
761         m_textures2d.clear();
762
763         for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
764                 delete *i;
765         m_texturesCube.clear();
766
767         for (vector<tcu::Texture2DArray*>::iterator i = m_textures2dArray.begin(); i != m_textures2dArray.end(); i++)
768                 delete *i;
769         m_textures2dArray.clear();
770
771         for (vector<tcu::Texture3D*>::iterator i = m_textures3d.begin(); i != m_textures3d.end(); i++)
772                 delete *i;
773         m_textures3d.clear();
774
775         delete m_shader;
776         m_shader = DE_NULL;
777 }
778
779 void TextureUnitCase::init (void)
780 {
781         m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
782
783         // Make the textures.
784
785         try
786         {
787                 tcu::TestLog&   log     = m_testCtx.getLog();
788                 de::Random              rnd     (m_randSeed);
789
790                 if (rnd.getFloat() < 0.7f)
791                         m_numTextures = m_numUnits;                                                                                     // In most cases use one unit per texture.
792                 else
793                         m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits);     // Sometimes assign same texture to multiple units.
794
795                 log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage;
796
797                 m_textureTypes.reserve(m_numTextures);
798                 m_textureParams.reserve(m_numTextures);
799                 m_ndxTexType.reserve(m_numTextures);
800
801                 // Generate textures.
802
803                 for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
804                 {
805                         // Either fixed or randomized target types, and randomized parameters for every texture.
806
807                         TextureParameters       params;
808
809                         DE_STATIC_ASSERT(CASE_ONLY_2D == 0 && CASE_MIXED + 1 == CASE_LAST);
810
811                         int                                             texType                 = m_caseType == CASE_MIXED ? rnd.getInt(0, (int)CASE_MIXED - 1) : (int)m_caseType;
812                         bool                                    is2dTex                 = texType == 0;
813                         bool                                    isCubeTex               = texType == 1;
814                         bool                                    is2dArrayTex    = texType == 2;
815                         bool                                    is3dTex                 = texType == 3;
816
817                         DE_ASSERT(is2dTex || isCubeTex || is2dArrayTex || is3dTex);
818
819                         GLenum                                  type                    = is2dTex               ? GL_TEXTURE_2D         : isCubeTex ? GL_TEXTURE_CUBE_MAP       : is2dArrayTex ? GL_TEXTURE_2D_ARRAY            : GL_TEXTURE_3D;
820                         const int                               texWidth                = is2dTex               ? TEXTURE_WIDTH_2D      : isCubeTex ? TEXTURE_WIDTH_CUBE        : is2dArrayTex ? TEXTURE_WIDTH_2D_ARRAY         : TEXTURE_WIDTH_3D;
821                         const int                               texHeight               = is2dTex               ? TEXTURE_HEIGHT_2D     : isCubeTex ? TEXTURE_HEIGHT_CUBE       : is2dArrayTex ? TEXTURE_HEIGHT_2D_ARRAY        : TEXTURE_HEIGHT_3D;
822
823                         const int                               texDepth                = is3dTex ? TEXTURE_DEPTH_3D : 1;
824                         const int                               texLayers               = is2dArrayTex ? TEXTURE_LAYERS_2D_ARRAY : 1;
825
826                         bool                                    mipmaps                 = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight) && deIsPowerOfTwo32(texDepth));
827                         int                                             numLevels               = mipmaps ? deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth))+1 : 1;
828
829                         params.internalFormat = s_testSizedInternalFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testSizedInternalFormats) - 1)];
830
831                         bool                                    isFilterable    = glu::isGLInternalColorFormatFilterable(params.internalFormat);
832
833                         params.wrapModeS = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
834                         params.wrapModeT = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
835                         params.wrapModeR = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
836
837                         params.magFilter = isFilterable ? s_testMagFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)] : GL_NEAREST;
838
839                         if (mipmaps)
840                                 params.minFilter = isFilterable ?
841                                         s_testMinFilters                        [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)] :
842                                         s_testNearestMinFilters         [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNearestMinFilters) - 1)];
843                         else
844                                 params.minFilter = isFilterable ?
845                                         s_testNonMipmapMinFilters       [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)] :
846                                         GL_NEAREST;
847
848                         m_textureTypes.push_back(type);
849                         m_textureParams.push_back(params);
850
851                         // Create new texture.
852
853                         tcu::TextureFormat texFormat = glu::mapGLInternalFormat((deUint32)params.internalFormat);
854
855                         if (is2dTex)
856                         {
857                                 m_ndxTexType.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d texture vector.
858                                 m_textures2d.push_back(new tcu::Texture2D(texFormat, texWidth, texHeight));
859                         }
860                         else if (isCubeTex)
861                         {
862                                 m_ndxTexType.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube texture vector.
863                                 DE_ASSERT(texWidth == texHeight);
864                                 m_texturesCube.push_back(new tcu::TextureCube(texFormat, texWidth));
865                         }
866                         else if (is2dArrayTex)
867                         {
868                                 m_ndxTexType.push_back((int)m_textures2dArray.size()); // Remember the index this texture has in the 2d array texture vector.
869                                 m_textures2dArray.push_back(new tcu::Texture2DArray(texFormat, texWidth, texHeight, texLayers));
870                         }
871                         else
872                         {
873                                 m_ndxTexType.push_back((int)m_textures3d.size()); // Remember the index this texture has in the 3d vector.
874                                 m_textures3d.push_back(new tcu::Texture3D(texFormat, texWidth, texHeight, texDepth));
875                         }
876
877                         tcu::TextureFormatInfo  fmtInfo         = tcu::getTextureFormatInfo(texFormat);
878                         Vec4                                    cBias           = fmtInfo.valueMin;
879                         Vec4                                    cScale          = fmtInfo.valueMax-fmtInfo.valueMin;
880
881                         // Fill with grid texture.
882
883                         int numFaces = isCubeTex ? (int)tcu::CUBEFACE_LAST : 1;
884
885                         for (int face = 0; face < numFaces; face++)
886                         {
887                                 deUint32 rgb    = rnd.getUint32() & 0x00ffffff;
888                                 deUint32 alpha  = 0xff000000;
889
890                                 deUint32 colorA = alpha | rgb;
891                                 deUint32 colorB = alpha | ((~rgb) & 0x00ffffff);
892
893                                 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
894                                 {
895                                         if (is2dTex)
896                                                 m_textures2d.back()->allocLevel(levelNdx);
897                                         else if (isCubeTex)
898                                                 m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
899                                         else if (is2dArrayTex)
900                                                 m_textures2dArray.back()->allocLevel(levelNdx);
901                                         else
902                                                 m_textures3d.back()->allocLevel(levelNdx);
903
904                                         int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
905
906                                         tcu::PixelBufferAccess access = is2dTex                 ? m_textures2d.back()->getLevel(levelNdx)
907                                                                                                   : isCubeTex           ? m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face)
908                                                                                                   : is2dArrayTex        ? m_textures2dArray.back()->getLevel(levelNdx)
909                                                                                                   :                                       m_textures3d.back()->getLevel(levelNdx);
910
911                                         tcu::fillWithGrid(access, curCellSize, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
912                                 }
913                         }
914                 }
915
916                 // Assign a texture index to each unit.
917
918                 m_unitTextures.reserve(m_numUnits);
919
920                 // \note Every texture is used at least once.
921                 for (int i = 0; i < m_numTextures; i++)
922                         m_unitTextures.push_back(i);
923
924                 // Assign a random texture to remaining units.
925                 while ((int)m_unitTextures.size() < m_numUnits)
926                         m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
927
928                 rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
929
930                 // Generate information for shader.
931
932                 vector<GLenum>                  unitTypes;
933                 vector<Vec4>                    texScales;
934                 vector<Vec4>                    texBiases;
935                 vector<glu::DataType>   samplerTypes;
936                 vector<int>                             num2dArrayLayers;
937
938                 unitTypes.reserve(m_numUnits);
939                 texScales.reserve(m_numUnits);
940                 texBiases.reserve(m_numUnits);
941                 samplerTypes.reserve(m_numUnits);
942                 num2dArrayLayers.reserve(m_numUnits);
943
944                 for (int i = 0; i < m_numUnits; i++)
945                 {
946                         int                                             texNdx          = m_unitTextures[i];
947                         GLenum                                  type            = m_textureTypes[texNdx];
948                         tcu::TextureFormat              fmt                     = glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat);
949                         tcu::TextureFormatInfo  fmtInfo         = tcu::getTextureFormatInfo(fmt);
950
951                         unitTypes.push_back(type);
952
953                         if (type == GL_TEXTURE_2D_ARRAY)
954                                 num2dArrayLayers.push_back(m_textures2dArray[m_ndxTexType[texNdx]]->getNumLayers());
955
956                         texScales.push_back(fmtInfo.lookupScale);
957                         texBiases.push_back(fmtInfo.lookupBias);
958
959                         switch (type)
960                         {
961                                 case GL_TEXTURE_2D:                     samplerTypes.push_back(glu::getSampler2DType(fmt));                     break;
962                                 case GL_TEXTURE_CUBE_MAP:       samplerTypes.push_back(glu::getSamplerCubeType(fmt));           break;
963                                 case GL_TEXTURE_2D_ARRAY:       samplerTypes.push_back(glu::getSampler2DArrayType(fmt));        break;
964                                 case GL_TEXTURE_3D:                     samplerTypes.push_back(glu::getSampler3DType(fmt));                     break;
965                                 default:
966                                         DE_ASSERT(DE_FALSE);
967                         }
968                 }
969
970                 // Create shader.
971
972                 DE_ASSERT(m_shader == DE_NULL);
973                 m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes, samplerTypes, texScales, texBiases, num2dArrayLayers);
974         }
975         catch (const std::exception&)
976         {
977                 // Clean up to save memory.
978                 TextureUnitCase::deinit();
979                 throw;
980         }
981 }
982
983 TextureUnitCase::IterateResult TextureUnitCase::iterate (void)
984 {
985         glu::RenderContext&                     renderCtx                       = m_context.getRenderContext();
986         const tcu::RenderTarget&        renderTarget            = renderCtx.getRenderTarget();
987         tcu::TestLog&                           log                                     = m_testCtx.getLog();
988         de::Random                                      rnd                                     (m_randSeed);
989
990         int                                                     viewportWidth           = deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
991         int                                                     viewportHeight          = deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
992         int                                                     viewportX                       = rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
993         int                                                     viewportY                       = rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
994
995         tcu::Surface                            gles3Frame                      (viewportWidth, viewportHeight);
996         tcu::Surface                            refFrame                        (viewportWidth, viewportHeight);
997
998         {
999                 // First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
1000
1001                 vector<IVec3> texSizes;
1002                 texSizes.reserve(m_numUnits);
1003
1004                 for (int i = 0; i < m_numUnits; i++)
1005                 {
1006                         int             texNdx                  = m_unitTextures[i];
1007                         int             texNdxInType    = m_ndxTexType[texNdx];
1008                         GLenum  type                    = m_textureTypes[texNdx];
1009
1010                         switch (type)
1011                         {
1012                                 case GL_TEXTURE_2D:                     texSizes.push_back(IVec3(m_textures2d[texNdxInType]->getWidth(),                m_textures2d[texNdxInType]->getHeight(),                0));                                                                            break;
1013                                 case GL_TEXTURE_CUBE_MAP:       texSizes.push_back(IVec3(m_texturesCube[texNdxInType]->getSize(),               m_texturesCube[texNdxInType]->getSize(),                0));                                                                            break;
1014                                 case GL_TEXTURE_2D_ARRAY:       texSizes.push_back(IVec3(m_textures2dArray[texNdxInType]->getWidth(),   m_textures2dArray[texNdxInType]->getHeight(),   0));                                                                            break;
1015                                 case GL_TEXTURE_3D:                     texSizes.push_back(IVec3(m_textures3d[texNdxInType]->getWidth(),                m_textures3d[texNdxInType]->getHeight(),                m_textures3d[texNdxInType]->getDepth()));       break;
1016                                 default:
1017                                         DE_ASSERT(DE_FALSE);
1018                         }
1019                 }
1020
1021                 m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
1022         }
1023
1024         // Render using GLES3.
1025         {
1026                 sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
1027
1028                 render(context);
1029
1030                 context.readPixels(gles3Frame, 0, 0, viewportWidth, viewportHeight);
1031         }
1032
1033         // Render reference image.
1034         {
1035                 sglr::ReferenceContextBuffers   buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight);
1036                 sglr::ReferenceContext                  context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
1037
1038                 render(context);
1039
1040                 context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
1041         }
1042
1043         // Compare images.
1044         const float             threshold       = 0.001f;
1045         bool                    isOk            = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles3Frame, threshold, tcu::COMPARE_LOG_RESULT);
1046
1047         // Store test result.
1048         m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS      : QP_TEST_RESULT_FAIL,
1049                                                         isOk ? "Pass"                           : "Image comparison failed");
1050
1051         return STOP;
1052 }
1053
1054 void TextureUnitCase::upload2dTexture (int texNdx, sglr::Context& context)
1055 {
1056         int                                             ndx2d           = m_ndxTexType[texNdx];
1057         const tcu::Texture2D*   texture         = m_textures2d[ndx2d];
1058         glu::TransferFormat             formatGl        = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1059
1060         context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1061
1062         for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1063         {
1064                 if (texture->isLevelEmpty(levelNdx))
1065                         continue;
1066
1067                 tcu::ConstPixelBufferAccess             access  = texture->getLevel(levelNdx);
1068                 int                                                             width   = access.getWidth();
1069                 int                                                             height  = access.getHeight();
1070
1071                 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1072
1073                 context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1074                 GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d texture image data");
1075         }
1076 }
1077
1078 void TextureUnitCase::uploadCubeTexture (int texNdx, sglr::Context& context)
1079 {
1080         int                                                     ndxCube         = m_ndxTexType[texNdx];
1081         const tcu::TextureCube*         texture         = m_texturesCube[ndxCube];
1082         glu::TransferFormat                     formatGl        = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1083
1084         context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1085
1086         for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
1087         {
1088                 for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1089                 {
1090                         if (texture->isLevelEmpty((tcu::CubeFace)face, levelNdx))
1091                                 continue;
1092
1093                         tcu::ConstPixelBufferAccess             access  = texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
1094                         int                                                             width   = access.getWidth();
1095                         int                                                             height  = access.getHeight();
1096
1097                         DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1098
1099                         context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1100                         GLU_EXPECT_NO_ERROR(context.getError(), "Set cube map image data");
1101                 }
1102         }
1103 }
1104
1105 void TextureUnitCase::upload2dArrayTexture (int texNdx, sglr::Context& context)
1106 {
1107         int                                                     ndx2dArray      = m_ndxTexType[texNdx];
1108         const tcu::Texture2DArray*      texture         = m_textures2dArray[ndx2dArray];
1109         glu::TransferFormat                     formatGl        = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1110
1111         context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1112
1113         for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1114         {
1115                 if (texture->isLevelEmpty(levelNdx))
1116                         continue;
1117
1118                 tcu::ConstPixelBufferAccess     access  = texture->getLevel(levelNdx);
1119                 int                                                     width   = access.getWidth();
1120                 int                                                     height  = access.getHeight();
1121                 int                                                     layers  = access.getDepth();
1122
1123                 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1124                 DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
1125
1126                 context.texImage3D(GL_TEXTURE_2D_ARRAY, levelNdx, m_textureParams[texNdx].internalFormat, width, height, layers, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1127                 GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d array texture image data");
1128         }
1129 }
1130
1131 void TextureUnitCase::upload3dTexture (int texNdx, sglr::Context& context)
1132 {
1133         int                                                     ndx3d           = m_ndxTexType[texNdx];
1134         const tcu::Texture3D*           texture         = m_textures3d[ndx3d];
1135         glu::TransferFormat                     formatGl        = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1136
1137         context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1138
1139         for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1140         {
1141                 if (texture->isLevelEmpty(levelNdx))
1142                         continue;
1143
1144                 tcu::ConstPixelBufferAccess     access  = texture->getLevel(levelNdx);
1145                 int                                                     width   = access.getWidth();
1146                 int                                                     height  = access.getHeight();
1147                 int                                                     depth   = access.getDepth();
1148
1149                 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1150                 DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
1151
1152                 context.texImage3D(GL_TEXTURE_3D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, depth, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1153                 GLU_EXPECT_NO_ERROR(context.getError(), "Set 3d texture image data");
1154         }
1155 }
1156
1157 void TextureUnitCase::render (sglr::Context& context)
1158 {
1159         // Setup textures.
1160
1161         vector<deUint32>        textureGLNames;
1162         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.
1163
1164         textureGLNames.resize(m_numTextures);
1165         context.genTextures(m_numTextures, &textureGLNames[0]);
1166         GLU_EXPECT_NO_ERROR(context.getError(), "Generate textures");
1167
1168         for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
1169         {
1170                 int texNdx = m_unitTextures[unitNdx];
1171
1172                 // Bind texture to unit.
1173                 context.activeTexture(GL_TEXTURE0 + unitNdx);
1174                 GLU_EXPECT_NO_ERROR(context.getError(), "Set active texture");
1175                 context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
1176                 GLU_EXPECT_NO_ERROR(context.getError(), "Bind texture");
1177
1178                 if (!isTextureSetUp[texNdx])
1179                 {
1180                         // Binding this texture for first time, so set parameters and data.
1181
1182                         context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
1183                         context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
1184                         if (m_textureTypes[texNdx] == GL_TEXTURE_3D)
1185                                 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_R, m_textureParams[texNdx].wrapModeR);
1186                         context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
1187                         context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
1188                         GLU_EXPECT_NO_ERROR(context.getError(), "Set texture parameters");
1189
1190                         switch (m_textureTypes[texNdx])
1191                         {
1192                                 case GL_TEXTURE_2D:                     upload2dTexture(texNdx, context);               break;
1193                                 case GL_TEXTURE_CUBE_MAP:       uploadCubeTexture(texNdx, context);             break;
1194                                 case GL_TEXTURE_2D_ARRAY:       upload2dArrayTexture(texNdx, context);  break;
1195                                 case GL_TEXTURE_3D:                     upload3dTexture(texNdx, context);               break;
1196                                 default:
1197                                         DE_ASSERT(DE_FALSE);
1198                         }
1199
1200                         isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
1201                 }
1202         }
1203
1204         GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
1205
1206         // Setup shader
1207
1208         deUint32 shaderID = context.createProgram(m_shader);
1209
1210         // Draw.
1211
1212         context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
1213         context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
1214         m_shader->setUniforms(context, shaderID);
1215         sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
1216         GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
1217
1218         // Delete previously generated texture names.
1219
1220         context.deleteTextures(m_numTextures, &textureGLNames[0]);
1221         GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
1222 }
1223
1224 TextureUnitTests::TextureUnitTests (Context& context)
1225         : TestCaseGroup(context, "units", "Texture Unit Usage Tests")
1226 {
1227 }
1228
1229 TextureUnitTests::~TextureUnitTests (void)
1230 {
1231 }
1232
1233 void TextureUnitTests::init (void)
1234 {
1235         const int numTestsPerGroup = 10;
1236
1237         static const int unitCounts[] =
1238         {
1239                 2,
1240                 4,
1241                 8,
1242                 -1 // \note Negative stands for the implementation-specified maximum.
1243         };
1244
1245         for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
1246         {
1247                 int numUnits = unitCounts[unitCountNdx];
1248
1249                 string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
1250
1251                 tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
1252                 addChild(countGroup);
1253
1254                 DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
1255
1256                 for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
1257                 {
1258                         const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D            ? "only_2d"
1259                                                                                   : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE              ? "only_cube"
1260                                                                                   : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D_ARRAY  ? "only_2d_array"
1261                                                                                   : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_3D                ? "only_3d"
1262                                                                                   : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED                  ? "mixed"
1263                                                                                   : DE_NULL;
1264
1265                         DE_ASSERT(caseTypeGroupName != DE_NULL);
1266
1267                         tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
1268                         countGroup->addChild(caseTypeGroup);
1269
1270                         for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
1271                                 caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, deUint32Hash((deUint32)testNdx)));
1272                 }
1273         }
1274 }
1275
1276 } // Functional
1277 } // gles3
1278 } // deqp