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