xref: /aosp_15_r20/external/deqp/modules/gles31/functional/es31fTextureFilteringTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.1 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 filtering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fTextureFilteringTests.hpp"
25 
26 #include "glsTextureTestUtil.hpp"
27 
28 #include "gluPixelTransfer.hpp"
29 #include "gluTexture.hpp"
30 #include "gluTextureUtil.hpp"
31 
32 #include "tcuCommandLine.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuImageCompare.hpp"
35 #include "tcuTexLookupVerifier.hpp"
36 #include "tcuVectorUtil.hpp"
37 
38 #include "deStringUtil.hpp"
39 #include "deString.h"
40 
41 #include "glwFunctions.hpp"
42 #include "glwEnums.hpp"
43 
44 namespace deqp
45 {
46 namespace gles31
47 {
48 namespace Functional
49 {
50 
51 using std::string;
52 using std::vector;
53 using tcu::TestLog;
54 using namespace gls::TextureTestUtil;
55 using namespace glu::TextureTestUtil;
56 
getFaceDesc(const tcu::CubeFace face)57 static const char *getFaceDesc(const tcu::CubeFace face)
58 {
59     switch (face)
60     {
61     case tcu::CUBEFACE_NEGATIVE_X:
62         return "-X";
63     case tcu::CUBEFACE_POSITIVE_X:
64         return "+X";
65     case tcu::CUBEFACE_NEGATIVE_Y:
66         return "-Y";
67     case tcu::CUBEFACE_POSITIVE_Y:
68         return "+Y";
69     case tcu::CUBEFACE_NEGATIVE_Z:
70         return "-Z";
71     case tcu::CUBEFACE_POSITIVE_Z:
72         return "+Z";
73     default:
74         DE_ASSERT(false);
75         return DE_NULL;
76     }
77 }
78 
logCubeArrayTexCoords(TestLog & log,vector<float> & texCoord)79 static void logCubeArrayTexCoords(TestLog &log, vector<float> &texCoord)
80 {
81     const size_t numVerts = texCoord.size() / 4;
82 
83     DE_ASSERT(texCoord.size() % 4 == 0);
84 
85     for (size_t vertNdx = 0; vertNdx < numVerts; vertNdx++)
86     {
87         const size_t coordNdx = vertNdx * 4;
88 
89         const float u = texCoord[coordNdx + 0];
90         const float v = texCoord[coordNdx + 1];
91         const float w = texCoord[coordNdx + 2];
92         const float q = texCoord[coordNdx + 3];
93 
94         log << TestLog::Message << vertNdx << ": (" << u << ", " << v << ", " << w << ", " << q << ")"
95             << TestLog::EndMessage;
96     }
97 }
98 
99 // Cube map array filtering
100 
101 class TextureCubeArrayFilteringCase : public TestCase
102 {
103 public:
104     TextureCubeArrayFilteringCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
105                                   uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t internalFormat, int size,
106                                   int depth, bool onlySampleFaceInterior = false);
107 
108     ~TextureCubeArrayFilteringCase(void);
109 
110     void init(void);
111     void deinit(void);
112     IterateResult iterate(void);
113 
114 private:
115     TextureCubeArrayFilteringCase(const TextureCubeArrayFilteringCase &);
116     TextureCubeArrayFilteringCase &operator=(const TextureCubeArrayFilteringCase &);
117 
118     const uint32_t m_minFilter;
119     const uint32_t m_magFilter;
120     const uint32_t m_wrapS;
121     const uint32_t m_wrapT;
122 
123     const uint32_t m_internalFormat;
124     const int m_size;
125     const int m_depth;
126 
127     const bool m_onlySampleFaceInterior; //!< If true, we avoid sampling anywhere near a face's edges.
128 
129     struct FilterCase
130     {
131         const glu::TextureCubeArray *texture;
132         tcu::Vec2 bottomLeft;
133         tcu::Vec2 topRight;
134         tcu::Vec2 layerRange;
135 
FilterCasedeqp::gles31::Functional::TextureCubeArrayFilteringCase::FilterCase136         FilterCase(void) : texture(DE_NULL)
137         {
138         }
139 
FilterCasedeqp::gles31::Functional::TextureCubeArrayFilteringCase::FilterCase140         FilterCase(const glu::TextureCubeArray *tex_, const tcu::Vec2 &bottomLeft_, const tcu::Vec2 &topRight_,
141                    const tcu::Vec2 &layerRange_)
142             : texture(tex_)
143             , bottomLeft(bottomLeft_)
144             , topRight(topRight_)
145             , layerRange(layerRange_)
146         {
147         }
148     };
149 
150     glu::TextureCubeArray *m_gradientTex;
151     glu::TextureCubeArray *m_gridTex;
152 
153     TextureRenderer m_renderer;
154 
155     std::vector<FilterCase> m_cases;
156     int m_caseNdx;
157 };
158 
TextureCubeArrayFilteringCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t internalFormat,int size,int depth,bool onlySampleFaceInterior)159 TextureCubeArrayFilteringCase::TextureCubeArrayFilteringCase(Context &context, const char *name, const char *desc,
160                                                              uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
161                                                              uint32_t wrapT, uint32_t internalFormat, int size,
162                                                              int depth, bool onlySampleFaceInterior)
163     : TestCase(context, name, desc)
164     , m_minFilter(minFilter)
165     , m_magFilter(magFilter)
166     , m_wrapS(wrapS)
167     , m_wrapT(wrapT)
168     , m_internalFormat(internalFormat)
169     , m_size(size)
170     , m_depth(depth)
171     , m_onlySampleFaceInterior(onlySampleFaceInterior)
172     , m_gradientTex(DE_NULL)
173     , m_gridTex(DE_NULL)
174     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_310_ES,
175                  glu::PRECISION_HIGHP)
176     , m_caseNdx(0)
177 {
178 }
179 
~TextureCubeArrayFilteringCase(void)180 TextureCubeArrayFilteringCase::~TextureCubeArrayFilteringCase(void)
181 {
182     TextureCubeArrayFilteringCase::deinit();
183 }
184 
init(void)185 void TextureCubeArrayFilteringCase::init(void)
186 {
187     auto ctxType            = m_context.getRenderContext().getType();
188     const bool isES32orGL45 = glu::contextSupports(ctxType, glu::ApiType::es(3, 2)) ||
189                               glu::contextSupports(ctxType, glu::ApiType::core(4, 5));
190 
191     if (!isES32orGL45 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_cube_map_array"))
192         throw tcu::NotSupportedError("GL_EXT_texture_cube_map_array not supported");
193 
194     if (m_internalFormat == GL_SR8_EXT && !(m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_sRGB_R8")))
195         TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_R8 not supported");
196 
197     if (m_internalFormat == GL_SRG8_EXT &&
198         !(m_context.getContextInfo().isExtensionSupported("GL_EXT_texture_sRGB_RG8")))
199         TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_RG8 not supported");
200 
201     try
202     {
203         const glw::Functions &gl             = m_context.getRenderContext().getFunctions();
204         const tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_internalFormat);
205         const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
206         const tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
207         const tcu::Vec4 cBias                = fmtInfo.valueMin;
208         const int numLevels                  = deLog2Floor32(m_size) + 1;
209         const int numLayers                  = m_depth / 6;
210 
211         if (!isContextTypeES(ctxType))
212             gl.enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
213 
214         // Create textures.
215         m_gradientTex = new glu::TextureCubeArray(m_context.getRenderContext(), m_internalFormat, m_size, m_depth);
216         m_gridTex     = new glu::TextureCubeArray(m_context.getRenderContext(), m_internalFormat, m_size, m_depth);
217 
218         const tcu::IVec4 levelSwz[] = {
219             tcu::IVec4(0, 1, 2, 3),
220             tcu::IVec4(2, 1, 3, 0),
221             tcu::IVec4(3, 0, 1, 2),
222             tcu::IVec4(1, 3, 2, 0),
223         };
224 
225         // Fill first gradient texture (gradient direction varies between layers).
226         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
227         {
228             m_gradientTex->getRefTexture().allocLevel(levelNdx);
229 
230             const tcu::PixelBufferAccess levelBuf = m_gradientTex->getRefTexture().getLevel(levelNdx);
231 
232             for (int layerFaceNdx = 0; layerFaceNdx < m_depth; layerFaceNdx++)
233             {
234                 const tcu::IVec4 swz = levelSwz[layerFaceNdx % DE_LENGTH_OF_ARRAY(levelSwz)];
235                 const tcu::Vec4 gMin =
236                     tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f).swizzle(swz[0], swz[1], swz[2], swz[3]) * cScale + cBias;
237                 const tcu::Vec4 gMax =
238                     tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f).swizzle(swz[0], swz[1], swz[2], swz[3]) * cScale + cBias;
239 
240                 tcu::fillWithComponentGradients(
241                     tcu::getSubregion(levelBuf, 0, 0, layerFaceNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), gMin,
242                     gMax);
243             }
244         }
245 
246         // Fill second with grid texture (each layer has unique colors).
247         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
248         {
249             m_gridTex->getRefTexture().allocLevel(levelNdx);
250 
251             const tcu::PixelBufferAccess levelBuf = m_gridTex->getRefTexture().getLevel(levelNdx);
252 
253             for (int layerFaceNdx = 0; layerFaceNdx < m_depth; layerFaceNdx++)
254             {
255                 const uint32_t step   = 0x00ffffff / (numLevels * m_depth - 1);
256                 const uint32_t rgb    = step * (levelNdx + layerFaceNdx * numLevels);
257                 const uint32_t colorA = 0xff000000 | rgb;
258                 const uint32_t colorB = 0xff000000 | ~rgb;
259 
260                 tcu::fillWithGrid(
261                     tcu::getSubregion(levelBuf, 0, 0, layerFaceNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), 4,
262                     tcu::RGBA(colorA).toVec() * cScale + cBias, tcu::RGBA(colorB).toVec() * cScale + cBias);
263             }
264         }
265 
266         // Upload.
267         m_gradientTex->upload();
268         m_gridTex->upload();
269 
270         // Test cases
271         {
272             const glu::TextureCubeArray *const tex0 = m_gradientTex;
273             const glu::TextureCubeArray *const tex1 = m_gridTex;
274 
275             if (m_onlySampleFaceInterior)
276             {
277                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f),
278                                              tcu::Vec2(-0.5f, float(numLayers) + 0.5f))); // minification
279                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f, 0.8f),
280                                              tcu::Vec2(-0.5f, float(numLayers) + 0.5f))); // magnification
281                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f),
282                                              tcu::Vec2(float(numLayers) + 0.5f, -0.5f))); // minification
283                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f, 0.5f),
284                                              tcu::Vec2(float(numLayers) + 0.5f, -0.5f))); // magnification
285             }
286             else
287             {
288                 const bool isSingleSample = (m_context.getRenderTarget().getNumSamples() == 0);
289 
290                 // minification - w/ tweak to avoid hitting triangle edges with a face switchpoint in multisample configs
291                 if (isSingleSample)
292                     m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.25f, -1.2f), tcu::Vec2(1.2f, 1.25f),
293                                                  tcu::Vec2(-0.5f, float(numLayers) + 0.5f)));
294                 else
295                     m_cases.push_back(FilterCase(tex0, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f),
296                                                  tcu::Vec2(-0.5f, float(numLayers) + 0.5f)));
297 
298                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.8f, 0.8f), tcu::Vec2(1.25f, 1.20f),
299                                              tcu::Vec2(-0.5f, float(numLayers) + 0.5f))); // magnification
300                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f),
301                                              tcu::Vec2(float(numLayers) + 0.5f, -0.5f))); // minification
302                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.2f, -1.1f), tcu::Vec2(-0.8f, -0.8f),
303                                              tcu::Vec2(float(numLayers) + 0.5f, -0.5f))); // magnification
304 
305                 // Layer rounding - only in single-sample configs as multisample configs may produce smooth transition at the middle.
306                 if (isSingleSample && (numLayers > 1))
307                     m_cases.push_back(FilterCase(tex0, tcu::Vec2(-2.0f, -1.5f), tcu::Vec2(-0.1f, 0.9f),
308                                                  tcu::Vec2(1.50001f, 1.49999f)));
309             }
310         }
311 
312         m_caseNdx = 0;
313         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
314     }
315     catch (...)
316     {
317         // Clean up to save memory.
318         TextureCubeArrayFilteringCase::deinit();
319         throw;
320     }
321 }
322 
deinit(void)323 void TextureCubeArrayFilteringCase::deinit(void)
324 {
325     delete m_gradientTex;
326     delete m_gridTex;
327 
328     m_gradientTex = DE_NULL;
329     m_gridTex     = DE_NULL;
330 
331     m_renderer.clear();
332     m_cases.clear();
333 
334     if (!isContextTypeES(m_context.getRenderContext().getType()))
335         m_context.getRenderContext().getFunctions().disable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
336 }
337 
iterate(void)338 TextureCubeArrayFilteringCase::IterateResult TextureCubeArrayFilteringCase::iterate(void)
339 {
340     TestLog &log                        = m_testCtx.getLog();
341     const glu::RenderContext &renderCtx = m_context.getRenderContext();
342     const glw::Functions &gl            = renderCtx.getFunctions();
343     const int viewportSize              = 28;
344     const uint32_t randomSeed =
345         deStringHash(getName()) ^ deInt32Hash(m_caseNdx) ^ m_testCtx.getCommandLine().getBaseSeed();
346     const RandomViewport viewport(m_context.getRenderTarget(), viewportSize, viewportSize, randomSeed);
347     const FilterCase &curCase            = m_cases[m_caseNdx];
348     const tcu::TextureFormat texFmt      = curCase.texture->getRefTexture().getFormat();
349     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
350     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
351                                         string("Test ") + de::toString(m_caseNdx));
352     ReferenceParams refParams(TEXTURETYPE_CUBE_ARRAY);
353 
354     if (viewport.width < viewportSize || viewport.height < viewportSize)
355         throw tcu::NotSupportedError("Render target too small", "", __FILE__, __LINE__);
356 
357     // Params for reference computation.
358     refParams.sampler                 = glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
359     refParams.sampler.seamlessCubeMap = true;
360     refParams.samplerType             = getSamplerType(texFmt);
361     refParams.colorBias               = fmtInfo.lookupBias;
362     refParams.colorScale              = fmtInfo.lookupScale;
363     refParams.lodMode                 = LODMODE_EXACT;
364 
365     gl.bindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, curCase.texture->getGLTexture());
366     gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, m_minFilter);
367     gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, m_magFilter);
368     gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, m_wrapS);
369     gl.texParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, m_wrapT);
370 
371     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
372 
373     m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight
374                        << TestLog::EndMessage;
375 
376     for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
377     {
378         const tcu::CubeFace face = tcu::CubeFace(faceNdx);
379         tcu::Surface result(viewport.width, viewport.height);
380         vector<float> texCoord;
381 
382         computeQuadTexCoordCubeArray(texCoord, face, curCase.bottomLeft, curCase.topRight, curCase.layerRange);
383 
384         log << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
385 
386         log << TestLog::Message << "Texture coordinates:" << TestLog::EndMessage;
387 
388         logCubeArrayTexCoords(log, texCoord);
389 
390         m_renderer.renderQuad(0, &texCoord[0], refParams);
391         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
392 
393         glu::readPixels(renderCtx, viewport.x, viewport.y, result.getAccess());
394         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
395 
396         {
397             const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
398             const tcu::PixelFormat pixelFormat = renderCtx.getRenderTarget().getPixelFormat();
399             const tcu::IVec4 coordBits         = tcu::IVec4(10);
400             const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
401                                                      tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
402             tcu::LodPrecision lodPrecision;
403             tcu::LookupPrecision lookupPrecision;
404 
405             lodPrecision.derivateBits      = 10;
406             lodPrecision.lodBits           = 5;
407             lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
408             lookupPrecision.coordBits      = coordBits.toWidth<3>();
409             lookupPrecision.uvwBits        = tcu::IVec3(6);
410             lookupPrecision.colorMask      = getCompareMask(pixelFormat);
411 
412             const bool isHighQuality =
413                 verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
414                                     refParams, lookupPrecision, coordBits, lodPrecision, pixelFormat);
415 
416             if (!isHighQuality)
417             {
418                 // Evaluate against lower precision requirements.
419                 lodPrecision.lodBits    = 4;
420                 lookupPrecision.uvwBits = tcu::IVec3(4);
421 
422                 m_testCtx.getLog() << TestLog::Message
423                                    << "Warning: Verification against high precision requirements failed, trying with "
424                                       "lower requirements."
425                                    << TestLog::EndMessage;
426 
427                 const bool isOk =
428                     verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
429                                         refParams, lookupPrecision, coordBits, lodPrecision, pixelFormat);
430 
431                 if (!isOk)
432                 {
433                     m_testCtx.getLog()
434                         << TestLog::Message
435                         << "ERROR: Verification against low precision requirements failed, failing test case."
436                         << TestLog::EndMessage;
437                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
438                 }
439                 else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
440                     m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
441             }
442         }
443     }
444 
445     m_caseNdx += 1;
446     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
447 }
448 
TextureFilteringTests(Context & context)449 TextureFilteringTests::TextureFilteringTests(Context &context)
450     : TestCaseGroup(context, "filtering", "Texture Filtering Tests")
451 {
452 }
453 
~TextureFilteringTests(void)454 TextureFilteringTests::~TextureFilteringTests(void)
455 {
456 }
457 
init(void)458 void TextureFilteringTests::init(void)
459 {
460     static const struct
461     {
462         const char *name;
463         uint32_t mode;
464     } wrapModes[] = {{"clamp", GL_CLAMP_TO_EDGE}, {"repeat", GL_REPEAT}, {"mirror", GL_MIRRORED_REPEAT}};
465 
466     static const struct
467     {
468         const char *name;
469         uint32_t mode;
470     } minFilterModes[] = {{"nearest", GL_NEAREST},
471                           {"linear", GL_LINEAR},
472                           {"nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST},
473                           {"linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST},
474                           {"nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR},
475                           {"linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR}};
476 
477     static const struct
478     {
479         const char *name;
480         uint32_t mode;
481     } magFilterModes[] = {{"nearest", GL_NEAREST}, {"linear", GL_LINEAR}};
482 
483     static const struct
484     {
485         int size;
486         int depth;
487     } sizesCubeArray[] = {{8, 6}, {64, 12}, {128, 12}, {7, 12}, {63, 18}};
488 
489     static const struct
490     {
491         const char *name;
492         uint32_t format;
493     } filterableFormatsByType[] = {{"rgba16f", GL_RGBA16F},
494                                    {"r11f_g11f_b10f", GL_R11F_G11F_B10F},
495                                    {"rgb9_e5", GL_RGB9_E5},
496                                    {"rgba8", GL_RGBA8},
497                                    {"rgba8_snorm", GL_RGBA8_SNORM},
498                                    {"rgb565", GL_RGB565},
499                                    {"rgba4", GL_RGBA4},
500                                    {"rgb5_a1", GL_RGB5_A1},
501                                    {"sr8", GL_SR8_EXT},
502                                    {"srg8", GL_SRG8_EXT},
503                                    {"srgb8_alpha8", GL_SRGB8_ALPHA8},
504                                    {"rgb10_a2", GL_RGB10_A2}};
505 
506     // Cube map array texture filtering.
507     {
508         tcu::TestCaseGroup *const groupCubeArray =
509             new tcu::TestCaseGroup(m_testCtx, "cube_array", "Cube Map Array Texture Filtering");
510         addChild(groupCubeArray);
511 
512         // Formats.
513         {
514             tcu::TestCaseGroup *const formatsGroup =
515                 new tcu::TestCaseGroup(m_testCtx, "formats", "Cube Map Array Texture Formats");
516             groupCubeArray->addChild(formatsGroup);
517 
518             for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
519             {
520                 for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
521                 {
522                     const uint32_t minFilter = minFilterModes[filterNdx].mode;
523                     const char *filterName   = minFilterModes[filterNdx].name;
524                     const uint32_t format    = filterableFormatsByType[fmtNdx].format;
525                     const char *formatName   = filterableFormatsByType[fmtNdx].name;
526                     const bool isMipmap      = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
527                     const uint32_t magFilter = isMipmap ? GL_LINEAR : minFilter;
528                     const string name        = string(formatName) + "_" + filterName;
529                     const uint32_t wrapS     = GL_REPEAT;
530                     const uint32_t wrapT     = GL_REPEAT;
531                     const int size           = 64;
532                     const int depth          = 12;
533 
534                     formatsGroup->addChild(new TextureCubeArrayFilteringCase(
535                         m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, size, depth));
536                 }
537             }
538         }
539 
540         // Sizes.
541         {
542             tcu::TestCaseGroup *const sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
543             groupCubeArray->addChild(sizesGroup);
544 
545             for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizesCubeArray); sizeNdx++)
546             {
547                 for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
548                 {
549                     const uint32_t minFilter = minFilterModes[filterNdx].mode;
550                     const char *filterName   = minFilterModes[filterNdx].name;
551                     const uint32_t format    = GL_RGBA8;
552                     const bool isMipmap      = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
553                     const uint32_t magFilter = isMipmap ? GL_LINEAR : minFilter;
554                     const uint32_t wrapS     = GL_REPEAT;
555                     const uint32_t wrapT     = GL_REPEAT;
556                     const int size           = sizesCubeArray[sizeNdx].size;
557                     const int depth          = sizesCubeArray[sizeNdx].depth;
558                     const string name =
559                         de::toString(size) + "x" + de::toString(size) + "x" + de::toString(depth) + "_" + filterName;
560 
561                     sizesGroup->addChild(new TextureCubeArrayFilteringCase(
562                         m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, size, depth));
563                 }
564             }
565         }
566 
567         // Wrap modes.
568         {
569             tcu::TestCaseGroup *const combinationsGroup =
570                 new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
571             groupCubeArray->addChild(combinationsGroup);
572 
573             for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
574             {
575                 for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
576                 {
577                     for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
578                     {
579                         for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
580                         {
581                             const uint32_t minFilter = minFilterModes[minFilterNdx].mode;
582                             const uint32_t magFilter = magFilterModes[magFilterNdx].mode;
583                             const uint32_t format    = GL_RGBA8;
584                             const uint32_t wrapS     = wrapModes[wrapSNdx].mode;
585                             const uint32_t wrapT     = wrapModes[wrapTNdx].mode;
586                             const int size           = 63;
587                             const int depth          = 12;
588                             const string name        = string(minFilterModes[minFilterNdx].name) + "_" +
589                                                 magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name +
590                                                 "_" + wrapModes[wrapTNdx].name;
591 
592                             combinationsGroup->addChild(new TextureCubeArrayFilteringCase(
593                                 m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, size, depth));
594                         }
595                     }
596                 }
597             }
598         }
599 
600         // Cases with no visible cube edges.
601         {
602             tcu::TestCaseGroup *const onlyFaceInteriorGroup =
603                 new tcu::TestCaseGroup(m_testCtx, "no_edges_visible", "Don't sample anywhere near a face's edges");
604             groupCubeArray->addChild(onlyFaceInteriorGroup);
605 
606             for (int isLinearI = 0; isLinearI <= 1; isLinearI++)
607             {
608                 const bool isLinear   = isLinearI != 0;
609                 const uint32_t filter = isLinear ? GL_LINEAR : GL_NEAREST;
610 
611                 onlyFaceInteriorGroup->addChild(
612                     new TextureCubeArrayFilteringCase(m_context, isLinear ? "linear" : "nearest", "", filter, filter,
613                                                       GL_REPEAT, GL_REPEAT, GL_RGBA8, 63, 12, true));
614             }
615         }
616     }
617 }
618 
619 } // namespace Functional
620 } // namespace gles31
621 } // namespace deqp
622