xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fTextureFilteringTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 filtering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fTextureFilteringTests.hpp"
25 #include "glsTextureTestUtil.hpp"
26 #include "gluPixelTransfer.hpp"
27 #include "gluTexture.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "tcuImageCompare.hpp"
31 #include "tcuTexLookupVerifier.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "deStringUtil.hpp"
34 #include "deString.h"
35 #include "glwFunctions.hpp"
36 #include "glwEnums.hpp"
37 #include "gluContextInfo.hpp"
38 #include "deUniquePtr.hpp"
39 
40 using de::MovePtr;
41 using glu::ContextInfo;
42 
43 namespace deqp
44 {
45 namespace gles3
46 {
47 namespace Functional
48 {
49 
50 using std::string;
51 using std::vector;
52 using tcu::TestLog;
53 using namespace gls::TextureTestUtil;
54 using namespace glu::TextureTestUtil;
55 
56 enum
57 {
58     TEX2D_VIEWPORT_WIDTH      = 64,
59     TEX2D_VIEWPORT_HEIGHT     = 64,
60     TEX2D_MIN_VIEWPORT_WIDTH  = 64,
61     TEX2D_MIN_VIEWPORT_HEIGHT = 64,
62 
63     TEX3D_VIEWPORT_WIDTH      = 64,
64     TEX3D_VIEWPORT_HEIGHT     = 64,
65     TEX3D_MIN_VIEWPORT_WIDTH  = 64,
66     TEX3D_MIN_VIEWPORT_HEIGHT = 64
67 };
68 
69 namespace
70 {
71 
checkSupport(const glu::ContextInfo & info,uint32_t internalFormat)72 void checkSupport(const glu::ContextInfo &info, uint32_t internalFormat)
73 {
74     if (internalFormat == GL_SR8_EXT && !info.isExtensionSupported("GL_EXT_texture_sRGB_R8"))
75         TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_R8 is not supported.");
76 
77     if (internalFormat == GL_SRG8_EXT && !info.isExtensionSupported("GL_EXT_texture_sRGB_RG8"))
78         TCU_THROW(NotSupportedError, "GL_EXT_texture_sRGB_RG8 is not supported.");
79 }
80 
81 } // namespace
82 
83 class Texture2DFilteringCase : public tcu::TestCase
84 {
85 public:
86     Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
87                            const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
88                            uint32_t wrapT, uint32_t internalFormat, int width, int height);
89     Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
90                            const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
91                            uint32_t wrapT, const std::vector<std::string> &filenames);
92     ~Texture2DFilteringCase(void);
93 
94     void init(void);
95     void deinit(void);
96     IterateResult iterate(void);
97 
98 private:
99     Texture2DFilteringCase(const Texture2DFilteringCase &other);
100     Texture2DFilteringCase &operator=(const Texture2DFilteringCase &other);
101 
102     glu::RenderContext &m_renderCtx;
103     const glu::ContextInfo &m_renderCtxInfo;
104 
105     const uint32_t m_minFilter;
106     const uint32_t m_magFilter;
107     const uint32_t m_wrapS;
108     const uint32_t m_wrapT;
109 
110     const uint32_t m_internalFormat;
111     const int m_width;
112     const int m_height;
113 
114     const std::vector<std::string> m_filenames;
115 
116     struct FilterCase
117     {
118         const glu::Texture2D *texture;
119         tcu::Vec2 minCoord;
120         tcu::Vec2 maxCoord;
121 
FilterCasedeqp::gles3::Functional::Texture2DFilteringCase::FilterCase122         FilterCase(void) : texture(DE_NULL)
123         {
124         }
125 
FilterCasedeqp::gles3::Functional::Texture2DFilteringCase::FilterCase126         FilterCase(const glu::Texture2D *tex_, const tcu::Vec2 &minCoord_, const tcu::Vec2 &maxCoord_)
127             : texture(tex_)
128             , minCoord(minCoord_)
129             , maxCoord(maxCoord_)
130         {
131         }
132     };
133 
134     std::vector<glu::Texture2D *> m_textures;
135     std::vector<FilterCase> m_cases;
136 
137     TextureRenderer m_renderer;
138 
139     int m_caseNdx;
140 };
141 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t internalFormat,int width,int height)142 Texture2DFilteringCase::Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
143                                                const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
144                                                uint32_t minFilter, uint32_t magFilter, uint32_t wrapS, uint32_t wrapT,
145                                                uint32_t internalFormat, int width, int height)
146     : TestCase(testCtx, name, desc)
147     , m_renderCtx(renderCtx)
148     , m_renderCtxInfo(ctxInfo)
149     , m_minFilter(minFilter)
150     , m_magFilter(magFilter)
151     , m_wrapS(wrapS)
152     , m_wrapT(wrapT)
153     , m_internalFormat(internalFormat)
154     , m_width(width)
155     , m_height(height)
156     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
157     , m_caseNdx(0)
158 {
159 }
160 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,const std::vector<std::string> & filenames)161 Texture2DFilteringCase::Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
162                                                const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
163                                                uint32_t minFilter, uint32_t magFilter, uint32_t wrapS, uint32_t wrapT,
164                                                const std::vector<std::string> &filenames)
165     : TestCase(testCtx, name, desc)
166     , m_renderCtx(renderCtx)
167     , m_renderCtxInfo(ctxInfo)
168     , m_minFilter(minFilter)
169     , m_magFilter(magFilter)
170     , m_wrapS(wrapS)
171     , m_wrapT(wrapT)
172     , m_internalFormat(GL_NONE)
173     , m_width(0)
174     , m_height(0)
175     , m_filenames(filenames)
176     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
177     , m_caseNdx(0)
178 {
179 }
180 
~Texture2DFilteringCase(void)181 Texture2DFilteringCase::~Texture2DFilteringCase(void)
182 {
183     deinit();
184 }
185 
init(void)186 void Texture2DFilteringCase::init(void)
187 {
188     checkSupport(m_renderCtxInfo, m_internalFormat);
189 
190     try
191     {
192         if (!m_filenames.empty())
193         {
194             m_textures.reserve(1);
195             m_textures.push_back(glu::Texture2D::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(),
196                                                         (int)m_filenames.size(), m_filenames));
197         }
198         else
199         {
200             // Create 2 textures.
201             m_textures.reserve(2);
202             for (int ndx = 0; ndx < 2; ndx++)
203                 m_textures.push_back(new glu::Texture2D(m_renderCtx, m_internalFormat, m_width, m_height));
204 
205             const bool mipmaps  = true;
206             const int numLevels = mipmaps ? deLog2Floor32(de::max(m_width, m_height)) + 1 : 1;
207             const tcu::TextureFormatInfo fmtInfo =
208                 tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
209             const tcu::Vec4 cBias  = fmtInfo.valueMin;
210             const tcu::Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
211 
212             // Fill first gradient texture.
213             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
214             {
215                 tcu::Vec4 gMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f) * cScale + cBias;
216                 tcu::Vec4 gMax = tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) * cScale + cBias;
217 
218                 m_textures[0]->getRefTexture().allocLevel(levelNdx);
219                 tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
220             }
221 
222             // Fill second with grid texture.
223             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
224             {
225                 uint32_t step   = 0x00ffffff / numLevels;
226                 uint32_t rgb    = step * levelNdx;
227                 uint32_t colorA = 0xff000000 | rgb;
228                 uint32_t colorB = 0xff000000 | ~rgb;
229 
230                 m_textures[1]->getRefTexture().allocLevel(levelNdx);
231                 tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4,
232                                   tcu::RGBA(colorA).toVec() * cScale + cBias,
233                                   tcu::RGBA(colorB).toVec() * cScale + cBias);
234             }
235 
236             // Upload.
237             for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
238                 (*i)->upload();
239         }
240 
241         // Compute cases.
242         {
243             const struct
244             {
245                 int texNdx;
246                 float lodX;
247                 float lodY;
248                 float oX;
249                 float oY;
250             } cases[] = {
251                 {0, 1.6f, 2.9f, -1.0f, -2.7f},
252                 {0, -2.0f, -1.35f, -0.2f, 0.7f},
253                 {1, 0.14f, 0.275f, -1.5f, -1.1f},
254                 {1, -0.92f, -2.64f, 0.4f, -0.1f},
255             };
256 
257             const float viewportW = (float)de::min<int>(TEX2D_VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
258             const float viewportH =
259                 (float)de::min<int>(TEX2D_VIEWPORT_HEIGHT, m_renderCtx.getRenderTarget().getHeight());
260 
261             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
262             {
263                 const int texNdx = de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size() - 1);
264                 const float lodX = cases[caseNdx].lodX;
265                 const float lodY = cases[caseNdx].lodY;
266                 const float oX   = cases[caseNdx].oX;
267                 const float oY   = cases[caseNdx].oY;
268                 const float sX = deFloatExp2(lodX) * viewportW / float(m_textures[texNdx]->getRefTexture().getWidth());
269                 const float sY = deFloatExp2(lodY) * viewportH / float(m_textures[texNdx]->getRefTexture().getHeight());
270 
271                 m_cases.push_back(FilterCase(m_textures[texNdx], tcu::Vec2(oX, oY), tcu::Vec2(oX + sX, oY + sY)));
272             }
273         }
274 
275         m_caseNdx = 0;
276         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
277     }
278     catch (...)
279     {
280         // Clean up to save memory.
281         Texture2DFilteringCase::deinit();
282         throw;
283     }
284 }
285 
deinit(void)286 void Texture2DFilteringCase::deinit(void)
287 {
288     for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
289         delete *i;
290     m_textures.clear();
291 
292     m_renderer.clear();
293     m_cases.clear();
294 }
295 
iterate(void)296 Texture2DFilteringCase::IterateResult Texture2DFilteringCase::iterate(void)
297 {
298     const glw::Functions &gl = m_renderCtx.getFunctions();
299     const RandomViewport viewport(m_renderCtx.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT,
300                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
301     const tcu::TextureFormat texFmt      = m_textures[0]->getRefTexture().getFormat();
302     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
303     const FilterCase &curCase            = m_cases[m_caseNdx];
304     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
305                                         string("Test ") + de::toString(m_caseNdx));
306     ReferenceParams refParams(TEXTURETYPE_2D);
307     tcu::Surface rendered(viewport.width, viewport.height);
308     vector<float> texCoord;
309 
310     if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
311         throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
312 
313     // Setup params for reference.
314     refParams.sampler     = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
315     refParams.samplerType = getSamplerType(texFmt);
316     refParams.lodMode     = LODMODE_EXACT;
317     refParams.colorBias   = fmtInfo.lookupBias;
318     refParams.colorScale  = fmtInfo.lookupScale;
319 
320     // Compute texture coordinates.
321     m_testCtx.getLog() << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
322                        << TestLog::EndMessage;
323     computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
324 
325     gl.bindTexture(GL_TEXTURE_2D, curCase.texture->getGLTexture());
326     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
327     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
328     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
329     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
330 
331     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
332     m_renderer.renderQuad(0, &texCoord[0], refParams);
333     glu::readPixels(m_renderCtx, viewport.x, viewport.y, rendered.getAccess());
334 
335     {
336         const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
337         const tcu::PixelFormat pixelFormat = m_renderCtx.getRenderTarget().getPixelFormat();
338         const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
339                                                  tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
340         tcu::LodPrecision lodPrecision;
341         tcu::LookupPrecision lookupPrecision;
342 
343         lodPrecision.derivateBits      = 18;
344         lodPrecision.lodBits           = 6;
345         lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
346         lookupPrecision.coordBits      = tcu::IVec3(20, 20, 0);
347         lookupPrecision.uvwBits        = tcu::IVec3(7, 7, 0);
348         lookupPrecision.colorMask      = getCompareMask(pixelFormat);
349 
350         const bool isHighQuality =
351             verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
352                                 refParams, lookupPrecision, lodPrecision, pixelFormat);
353 
354         if (!isHighQuality)
355         {
356             // Evaluate against lower precision requirements.
357             lodPrecision.lodBits    = 4;
358             lookupPrecision.uvwBits = tcu::IVec3(4, 4, 0);
359 
360             m_testCtx.getLog()
361                 << TestLog::Message
362                 << "Warning: Verification against high precision requirements failed, trying with lower requirements."
363                 << TestLog::EndMessage;
364 
365             const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
366                                                   &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
367 
368             if (!isOk)
369             {
370                 m_testCtx.getLog()
371                     << TestLog::Message
372                     << "ERROR: Verification against low precision requirements failed, failing test case."
373                     << TestLog::EndMessage;
374                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
375             }
376             else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
377                 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
378         }
379     }
380 
381     m_caseNdx += 1;
382     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
383 }
384 
385 class TextureCubeFilteringCase : public tcu::TestCase
386 {
387 public:
388     TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
389                              const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
390                              uint32_t wrapT, bool onlySampleFaceInterior, uint32_t internalFormat, int width,
391                              int height);
392     TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
393                              const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
394                              uint32_t wrapT, bool onlySampleFaceInterior, const std::vector<std::string> &filenames);
395     ~TextureCubeFilteringCase(void);
396 
397     void init(void);
398     void deinit(void);
399     IterateResult iterate(void);
400 
401 private:
402     TextureCubeFilteringCase(const TextureCubeFilteringCase &other);
403     TextureCubeFilteringCase &operator=(const TextureCubeFilteringCase &other);
404 
405     glu::RenderContext &m_renderCtx;
406     const glu::ContextInfo &m_renderCtxInfo;
407 
408     const uint32_t m_minFilter;
409     const uint32_t m_magFilter;
410     const uint32_t m_wrapS;
411     const uint32_t m_wrapT;
412     const bool m_onlySampleFaceInterior; //!< If true, we avoid sampling anywhere near a face's edges.
413 
414     const uint32_t m_internalFormat;
415     const int m_width;
416     const int m_height;
417 
418     const std::vector<std::string> m_filenames;
419 
420     struct FilterCase
421     {
422         const glu::TextureCube *texture;
423         tcu::Vec2 bottomLeft;
424         tcu::Vec2 topRight;
425 
FilterCasedeqp::gles3::Functional::TextureCubeFilteringCase::FilterCase426         FilterCase(void) : texture(DE_NULL)
427         {
428         }
429 
FilterCasedeqp::gles3::Functional::TextureCubeFilteringCase::FilterCase430         FilterCase(const glu::TextureCube *tex_, const tcu::Vec2 &bottomLeft_, const tcu::Vec2 &topRight_)
431             : texture(tex_)
432             , bottomLeft(bottomLeft_)
433             , topRight(topRight_)
434         {
435         }
436     };
437 
438     std::vector<glu::TextureCube *> m_textures;
439     std::vector<FilterCase> m_cases;
440 
441     TextureRenderer m_renderer;
442 
443     int m_caseNdx;
444 };
445 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,bool onlySampleFaceInterior,uint32_t internalFormat,int width,int height)446 TextureCubeFilteringCase::TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
447                                                    const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
448                                                    uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
449                                                    uint32_t wrapT, bool onlySampleFaceInterior, uint32_t internalFormat,
450                                                    int width, int height)
451     : TestCase(testCtx, name, desc)
452     , m_renderCtx(renderCtx)
453     , m_renderCtxInfo(ctxInfo)
454     , m_minFilter(minFilter)
455     , m_magFilter(magFilter)
456     , m_wrapS(wrapS)
457     , m_wrapT(wrapT)
458     , m_onlySampleFaceInterior(onlySampleFaceInterior)
459     , m_internalFormat(internalFormat)
460     , m_width(width)
461     , m_height(height)
462     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
463     , m_caseNdx(0)
464 {
465 }
466 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,bool onlySampleFaceInterior,const std::vector<std::string> & filenames)467 TextureCubeFilteringCase::TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
468                                                    const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
469                                                    uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
470                                                    uint32_t wrapT, bool onlySampleFaceInterior,
471                                                    const std::vector<std::string> &filenames)
472     : TestCase(testCtx, name, desc)
473     , m_renderCtx(renderCtx)
474     , m_renderCtxInfo(ctxInfo)
475     , m_minFilter(minFilter)
476     , m_magFilter(magFilter)
477     , m_wrapS(wrapS)
478     , m_wrapT(wrapT)
479     , m_onlySampleFaceInterior(onlySampleFaceInterior)
480     , m_internalFormat(GL_NONE)
481     , m_width(0)
482     , m_height(0)
483     , m_filenames(filenames)
484     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
485     , m_caseNdx(0)
486 {
487 }
488 
~TextureCubeFilteringCase(void)489 TextureCubeFilteringCase::~TextureCubeFilteringCase(void)
490 {
491     deinit();
492 }
493 
init(void)494 void TextureCubeFilteringCase::init(void)
495 {
496     checkSupport(m_renderCtxInfo, m_internalFormat);
497 
498     try
499     {
500         if (!m_filenames.empty())
501         {
502             m_textures.reserve(1);
503             m_textures.push_back(glu::TextureCube::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(),
504                                                           (int)m_filenames.size() / 6, m_filenames));
505         }
506         else
507         {
508             DE_ASSERT(m_width == m_height);
509             m_textures.reserve(2);
510             for (int ndx = 0; ndx < 2; ndx++)
511                 m_textures.push_back(new glu::TextureCube(m_renderCtx, m_internalFormat, m_width));
512 
513             const int numLevels            = deLog2Floor32(de::max(m_width, m_height)) + 1;
514             tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
515             tcu::Vec4 cBias                = fmtInfo.valueMin;
516             tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
517 
518             // Fill first with gradient texture.
519             static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] = {
520                 {tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative x
521                 {tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // positive x
522                 {tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative y
523                 {tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // positive y
524                 {tcu::Vec4(0.0f, 0.0f, 0.0f, 0.5f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)}, // negative z
525                 {tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}  // positive z
526             };
527             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
528             {
529                 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
530                 {
531                     m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
532                     tcu::fillWithComponentGradients(
533                         m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face),
534                         gradients[face][0] * cScale + cBias, gradients[face][1] * cScale + cBias);
535                 }
536             }
537 
538             // Fill second with grid texture.
539             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
540             {
541                 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
542                 {
543                     uint32_t step   = 0x00ffffff / (numLevels * tcu::CUBEFACE_LAST);
544                     uint32_t rgb    = step * levelNdx * face;
545                     uint32_t colorA = 0xff000000 | rgb;
546                     uint32_t colorB = 0xff000000 | ~rgb;
547 
548                     m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
549                     tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4,
550                                       tcu::RGBA(colorA).toVec() * cScale + cBias,
551                                       tcu::RGBA(colorB).toVec() * cScale + cBias);
552                 }
553             }
554 
555             // Upload.
556             for (std::vector<glu::TextureCube *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
557                 (*i)->upload();
558         }
559 
560         // Compute cases
561         {
562             const glu::TextureCube *tex0 = m_textures[0];
563             const glu::TextureCube *tex1 = m_textures.size() > 1 ? m_textures[1] : tex0;
564 
565             if (m_onlySampleFaceInterior)
566             {
567                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f))); // minification
568                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f, 0.8f)));  // magnification
569                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f))); // minification
570                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f, 0.5f)));   // magnification
571             }
572             else
573             {
574                 if (m_renderCtx.getRenderTarget().getNumSamples() == 0)
575                     m_cases.push_back(
576                         FilterCase(tex0, tcu::Vec2(-1.25f, -1.2f), tcu::Vec2(1.2f, 1.25f))); // minification
577                 else
578                     m_cases.push_back(FilterCase(
579                         tex0, tcu::Vec2(-1.19f, -1.3f),
580                         tcu::Vec2(
581                             1.1f,
582                             1.35f))); // minification - w/ tweak to avoid hitting triangle edges with face switchpoint
583 
584                 m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.8f, 0.8f), tcu::Vec2(1.25f, 1.20f)));   // magnification
585                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f))); // minification
586                 m_cases.push_back(FilterCase(tex1, tcu::Vec2(-1.2f, -1.1f), tcu::Vec2(-0.8f, -0.8f))); // magnification
587             }
588         }
589 
590         m_caseNdx = 0;
591         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
592     }
593     catch (...)
594     {
595         // Clean up to save memory.
596         TextureCubeFilteringCase::deinit();
597         throw;
598     }
599 }
600 
deinit(void)601 void TextureCubeFilteringCase::deinit(void)
602 {
603     for (std::vector<glu::TextureCube *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
604         delete *i;
605     m_textures.clear();
606 
607     m_renderer.clear();
608     m_cases.clear();
609 }
610 
getFaceDesc(const tcu::CubeFace face)611 static const char *getFaceDesc(const tcu::CubeFace face)
612 {
613     switch (face)
614     {
615     case tcu::CUBEFACE_NEGATIVE_X:
616         return "-X";
617     case tcu::CUBEFACE_POSITIVE_X:
618         return "+X";
619     case tcu::CUBEFACE_NEGATIVE_Y:
620         return "-Y";
621     case tcu::CUBEFACE_POSITIVE_Y:
622         return "+Y";
623     case tcu::CUBEFACE_NEGATIVE_Z:
624         return "-Z";
625     case tcu::CUBEFACE_POSITIVE_Z:
626         return "+Z";
627     default:
628         DE_ASSERT(false);
629         return DE_NULL;
630     }
631 }
632 
iterate(void)633 TextureCubeFilteringCase::IterateResult TextureCubeFilteringCase::iterate(void)
634 {
635     const glw::Functions &gl = m_renderCtx.getFunctions();
636     const int viewportSize   = 28;
637     const RandomViewport viewport(m_renderCtx.getRenderTarget(), viewportSize, viewportSize,
638                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
639     const tcu::ScopedLogSection iterSection(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
640                                             string("Test ") + de::toString(m_caseNdx));
641     const FilterCase &curCase            = m_cases[m_caseNdx];
642     const tcu::TextureFormat &texFmt     = curCase.texture->getRefTexture().getFormat();
643     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
644     ReferenceParams sampleParams(TEXTURETYPE_CUBE);
645 
646     if (viewport.width < viewportSize || viewport.height < viewportSize)
647         throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
648 
649     // Setup texture
650     gl.bindTexture(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
651     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, m_minFilter);
652     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, m_magFilter);
653     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, m_wrapS);
654     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, m_wrapT);
655 
656     // Other state
657     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
658 
659     // Params for reference computation.
660     sampleParams.sampler = glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
661     sampleParams.sampler.seamlessCubeMap = true;
662     sampleParams.samplerType             = getSamplerType(texFmt);
663     sampleParams.colorBias               = fmtInfo.lookupBias;
664     sampleParams.colorScale              = fmtInfo.lookupScale;
665     sampleParams.lodMode                 = LODMODE_EXACT;
666 
667     m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight
668                        << TestLog::EndMessage;
669 
670     for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
671     {
672         const tcu::CubeFace face = tcu::CubeFace(faceNdx);
673         tcu::Surface result(viewport.width, viewport.height);
674         vector<float> texCoord;
675 
676         computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
677 
678         m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
679 
680         // \todo Log texture coordinates.
681 
682         m_renderer.renderQuad(0, &texCoord[0], sampleParams);
683         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
684 
685         glu::readPixels(m_renderCtx, viewport.x, viewport.y, result.getAccess());
686         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
687 
688         {
689             const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
690             const tcu::PixelFormat pixelFormat = m_renderCtx.getRenderTarget().getPixelFormat();
691             const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
692                                                      tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
693             tcu::LodPrecision lodPrecision;
694             tcu::LookupPrecision lookupPrecision;
695 
696             lodPrecision.derivateBits      = 10;
697             lodPrecision.lodBits           = 5;
698             lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / sampleParams.colorScale;
699             lookupPrecision.coordBits      = tcu::IVec3(10, 10, 10);
700             lookupPrecision.uvwBits        = tcu::IVec3(6, 6, 0);
701             lookupPrecision.colorMask      = getCompareMask(pixelFormat);
702 
703             const bool isHighQuality =
704                 verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
705                                     sampleParams, lookupPrecision, lodPrecision, pixelFormat);
706 
707             if (!isHighQuality)
708             {
709                 // Evaluate against lower precision requirements.
710                 lodPrecision.lodBits    = 4;
711                 lookupPrecision.uvwBits = tcu::IVec3(4, 4, 0);
712 
713                 m_testCtx.getLog() << TestLog::Message
714                                    << "Warning: Verification against high precision requirements failed, trying with "
715                                       "lower requirements."
716                                    << TestLog::EndMessage;
717 
718                 const bool isOk =
719                     verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
720                                         sampleParams, lookupPrecision, lodPrecision, pixelFormat);
721 
722                 if (!isOk)
723                 {
724                     m_testCtx.getLog()
725                         << TestLog::Message
726                         << "ERROR: Verification against low precision requirements failed, failing test case."
727                         << TestLog::EndMessage;
728                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
729                 }
730                 else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
731                     m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
732             }
733         }
734     }
735 
736     m_caseNdx += 1;
737     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
738 }
739 
740 // 2D array filtering
741 
742 class Texture2DArrayFilteringCase : public TestCase
743 {
744 public:
745     Texture2DArrayFilteringCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
746                                 uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t internalFormat, int width,
747                                 int height, int numLayers);
748     ~Texture2DArrayFilteringCase(void);
749 
750     void init(void);
751     void deinit(void);
752     IterateResult iterate(void);
753 
754 private:
755     Texture2DArrayFilteringCase(const Texture2DArrayFilteringCase &);
756     Texture2DArrayFilteringCase &operator=(const Texture2DArrayFilteringCase &);
757 
758     const uint32_t m_minFilter;
759     const uint32_t m_magFilter;
760     const uint32_t m_wrapS;
761     const uint32_t m_wrapT;
762 
763     const uint32_t m_internalFormat;
764     const int m_width;
765     const int m_height;
766     const int m_numLayers;
767 
768     struct FilterCase
769     {
770         const glu::Texture2DArray *texture;
771         tcu::Vec2 lod;
772         tcu::Vec2 offset;
773         tcu::Vec2 layerRange;
774 
FilterCasedeqp::gles3::Functional::Texture2DArrayFilteringCase::FilterCase775         FilterCase(void) : texture(DE_NULL)
776         {
777         }
778 
FilterCasedeqp::gles3::Functional::Texture2DArrayFilteringCase::FilterCase779         FilterCase(const glu::Texture2DArray *tex_, const tcu::Vec2 &lod_, const tcu::Vec2 &offset_,
780                    const tcu::Vec2 &layerRange_)
781             : texture(tex_)
782             , lod(lod_)
783             , offset(offset_)
784             , layerRange(layerRange_)
785         {
786         }
787     };
788 
789     glu::Texture2DArray *m_gradientTex;
790     glu::Texture2DArray *m_gridTex;
791 
792     TextureRenderer m_renderer;
793 
794     std::vector<FilterCase> m_cases;
795     int m_caseNdx;
796 };
797 
Texture2DArrayFilteringCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t internalFormat,int width,int height,int numLayers)798 Texture2DArrayFilteringCase::Texture2DArrayFilteringCase(Context &context, const char *name, const char *desc,
799                                                          uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
800                                                          uint32_t wrapT, uint32_t internalFormat, int width, int height,
801                                                          int numLayers)
802     : TestCase(context, name, desc)
803     , m_minFilter(minFilter)
804     , m_magFilter(magFilter)
805     , m_wrapS(wrapS)
806     , m_wrapT(wrapT)
807     , m_internalFormat(internalFormat)
808     , m_width(width)
809     , m_height(height)
810     , m_numLayers(numLayers)
811     , m_gradientTex(DE_NULL)
812     , m_gridTex(DE_NULL)
813     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES,
814                  glu::PRECISION_HIGHP)
815     , m_caseNdx(0)
816 {
817 }
818 
~Texture2DArrayFilteringCase(void)819 Texture2DArrayFilteringCase::~Texture2DArrayFilteringCase(void)
820 {
821     Texture2DArrayFilteringCase::deinit();
822 }
823 
init(void)824 void Texture2DArrayFilteringCase::init(void)
825 {
826     checkSupport(m_context.getContextInfo(), m_internalFormat);
827 
828     try
829     {
830         const tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_internalFormat);
831         const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
832         const tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
833         const tcu::Vec4 cBias                = fmtInfo.valueMin;
834         const int numLevels                  = deLog2Floor32(de::max(m_width, m_height)) + 1;
835 
836         // Create textures.
837         m_gradientTex =
838             new glu::Texture2DArray(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_numLayers);
839         m_gridTex =
840             new glu::Texture2DArray(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_numLayers);
841 
842         const tcu::IVec4 levelSwz[] = {
843             tcu::IVec4(0, 1, 2, 3),
844             tcu::IVec4(2, 1, 3, 0),
845             tcu::IVec4(3, 0, 1, 2),
846             tcu::IVec4(1, 3, 2, 0),
847         };
848 
849         // Fill first gradient texture (gradient direction varies between layers).
850         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
851         {
852             m_gradientTex->getRefTexture().allocLevel(levelNdx);
853 
854             const tcu::PixelBufferAccess levelBuf = m_gradientTex->getRefTexture().getLevel(levelNdx);
855 
856             for (int layerNdx = 0; layerNdx < m_numLayers; layerNdx++)
857             {
858                 const tcu::IVec4 swz = levelSwz[layerNdx % DE_LENGTH_OF_ARRAY(levelSwz)];
859                 const tcu::Vec4 gMin =
860                     tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f).swizzle(swz[0], swz[1], swz[2], swz[3]) * cScale + cBias;
861                 const tcu::Vec4 gMax =
862                     tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f).swizzle(swz[0], swz[1], swz[2], swz[3]) * cScale + cBias;
863 
864                 tcu::fillWithComponentGradients(
865                     tcu::getSubregion(levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), gMin,
866                     gMax);
867             }
868         }
869 
870         // Fill second with grid texture (each layer has unique colors).
871         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
872         {
873             m_gridTex->getRefTexture().allocLevel(levelNdx);
874 
875             const tcu::PixelBufferAccess levelBuf = m_gridTex->getRefTexture().getLevel(levelNdx);
876 
877             for (int layerNdx = 0; layerNdx < m_numLayers; layerNdx++)
878             {
879                 const uint32_t step   = 0x00ffffff / (numLevels * m_numLayers - 1);
880                 const uint32_t rgb    = step * (levelNdx + layerNdx * numLevels);
881                 const uint32_t colorA = 0xff000000 | rgb;
882                 const uint32_t colorB = 0xff000000 | ~rgb;
883 
884                 tcu::fillWithGrid(
885                     tcu::getSubregion(levelBuf, 0, 0, layerNdx, levelBuf.getWidth(), levelBuf.getHeight(), 1), 4,
886                     tcu::RGBA(colorA).toVec() * cScale + cBias, tcu::RGBA(colorB).toVec() * cScale + cBias);
887             }
888         }
889 
890         // Upload.
891         m_gradientTex->upload();
892         m_gridTex->upload();
893 
894         // Test cases
895         m_cases.push_back(FilterCase(m_gradientTex, tcu::Vec2(1.5f, 2.8f), tcu::Vec2(-1.0f, -2.7f),
896                                      tcu::Vec2(-0.5f, float(m_numLayers) + 0.5f)));
897         m_cases.push_back(FilterCase(m_gridTex, tcu::Vec2(0.2f, 0.175f), tcu::Vec2(-2.0f, -3.7f),
898                                      tcu::Vec2(-0.5f, float(m_numLayers) + 0.5f)));
899         m_cases.push_back(FilterCase(m_gridTex, tcu::Vec2(-0.8f, -2.3f), tcu::Vec2(0.2f, -0.1f),
900                                      tcu::Vec2(float(m_numLayers) + 0.5f, -0.5f)));
901 
902         // Level rounding - only in single-sample configs as multisample configs may produce smooth transition at the middle.
903         if (m_context.getRenderTarget().getNumSamples() == 0)
904             m_cases.push_back(FilterCase(m_gradientTex, tcu::Vec2(-2.0f, -1.5f), tcu::Vec2(-0.1f, 0.9f),
905                                          tcu::Vec2(1.50001f, 1.49999f)));
906 
907         m_caseNdx = 0;
908         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
909     }
910     catch (...)
911     {
912         // Clean up to save memory.
913         Texture2DArrayFilteringCase::deinit();
914         throw;
915     }
916 }
917 
deinit(void)918 void Texture2DArrayFilteringCase::deinit(void)
919 {
920     delete m_gradientTex;
921     delete m_gridTex;
922 
923     m_gradientTex = DE_NULL;
924     m_gridTex     = DE_NULL;
925 
926     m_renderer.clear();
927     m_cases.clear();
928 }
929 
iterate(void)930 Texture2DArrayFilteringCase::IterateResult Texture2DArrayFilteringCase::iterate(void)
931 {
932     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
933     const RandomViewport viewport(m_context.getRenderTarget(), TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT,
934                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
935     const FilterCase &curCase            = m_cases[m_caseNdx];
936     const tcu::TextureFormat texFmt      = curCase.texture->getRefTexture().getFormat();
937     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
938     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
939                                         string("Test ") + de::toString(m_caseNdx));
940     ReferenceParams refParams(TEXTURETYPE_2D_ARRAY);
941     tcu::Surface rendered(viewport.width, viewport.height);
942     tcu::Vec3 texCoord[4];
943 
944     if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT)
945         throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
946 
947     // Setup params for reference.
948     refParams.sampler     = glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapT, m_minFilter, m_magFilter);
949     refParams.samplerType = getSamplerType(texFmt);
950     refParams.lodMode     = LODMODE_EXACT;
951     refParams.colorBias   = fmtInfo.lookupBias;
952     refParams.colorScale  = fmtInfo.lookupScale;
953 
954     // Compute texture coordinates.
955     m_testCtx.getLog() << TestLog::Message << "Approximate lod per axis = " << curCase.lod
956                        << ", offset = " << curCase.offset << TestLog::EndMessage;
957 
958     {
959         const float lodX = curCase.lod.x();
960         const float lodY = curCase.lod.y();
961         const float oX   = curCase.offset.x();
962         const float oY   = curCase.offset.y();
963         const float sX   = deFloatExp2(lodX) * float(viewport.width) / float(m_gradientTex->getRefTexture().getWidth());
964         const float sY = deFloatExp2(lodY) * float(viewport.height) / float(m_gradientTex->getRefTexture().getHeight());
965         const float l0 = curCase.layerRange.x();
966         const float l1 = curCase.layerRange.y();
967 
968         texCoord[0] = tcu::Vec3(oX, oY, l0);
969         texCoord[1] = tcu::Vec3(oX, oY + sY, l0 * 0.5f + l1 * 0.5f);
970         texCoord[2] = tcu::Vec3(oX + sX, oY, l0 * 0.5f + l1 * 0.5f);
971         texCoord[3] = tcu::Vec3(oX + sX, oY + sY, l1);
972     }
973 
974     gl.bindTexture(GL_TEXTURE_2D_ARRAY, curCase.texture->getGLTexture());
975     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, m_minFilter);
976     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, m_magFilter);
977     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, m_wrapS);
978     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, m_wrapT);
979 
980     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
981     m_renderer.renderQuad(0, (const float *)&texCoord[0], refParams);
982     glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
983 
984     {
985         const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
986         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
987         const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
988                                                  tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
989         tcu::LodPrecision lodPrecision;
990         tcu::LookupPrecision lookupPrecision;
991 
992         lodPrecision.derivateBits      = 18;
993         lodPrecision.lodBits           = 6;
994         lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
995         lookupPrecision.coordBits      = tcu::IVec3(20, 20, 20);
996         lookupPrecision.uvwBits        = tcu::IVec3(7, 7, 0);
997         lookupPrecision.colorMask      = getCompareMask(pixelFormat);
998 
999         const bool isHighQuality =
1000             verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1001                                 (const float *)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1002 
1003         if (!isHighQuality)
1004         {
1005             // Evaluate against lower precision requirements.
1006             lodPrecision.lodBits    = 4;
1007             lookupPrecision.uvwBits = tcu::IVec3(4, 4, 0);
1008 
1009             m_testCtx.getLog()
1010                 << TestLog::Message
1011                 << "Warning: Verification against high precision requirements failed, trying with lower requirements."
1012                 << TestLog::EndMessage;
1013 
1014             const bool isOk =
1015                 verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1016                                     (const float *)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1017 
1018             if (!isOk)
1019             {
1020                 m_testCtx.getLog()
1021                     << TestLog::Message
1022                     << "ERROR: Verification against low precision requirements failed, failing test case."
1023                     << TestLog::EndMessage;
1024                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1025             }
1026             else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1027                 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
1028         }
1029     }
1030 
1031     m_caseNdx += 1;
1032     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
1033 }
1034 
1035 // 3D filtering
1036 
1037 class Texture3DFilteringCase : public TestCase
1038 {
1039 public:
1040     Texture3DFilteringCase(Context &context, const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter,
1041                            uint32_t wrapS, uint32_t wrapT, uint32_t wrapR, uint32_t internalFormat, int width,
1042                            int height, int depth);
1043     ~Texture3DFilteringCase(void);
1044 
1045     void init(void);
1046     void deinit(void);
1047     IterateResult iterate(void);
1048 
1049 private:
1050     Texture3DFilteringCase(const Texture3DFilteringCase &other);
1051     Texture3DFilteringCase &operator=(const Texture3DFilteringCase &other);
1052 
1053     const uint32_t m_minFilter;
1054     const uint32_t m_magFilter;
1055     const uint32_t m_wrapS;
1056     const uint32_t m_wrapT;
1057     const uint32_t m_wrapR;
1058 
1059     const uint32_t m_internalFormat;
1060     const int m_width;
1061     const int m_height;
1062     const int m_depth;
1063 
1064     struct FilterCase
1065     {
1066         const glu::Texture3D *texture;
1067         tcu::Vec3 lod;
1068         tcu::Vec3 offset;
1069 
FilterCasedeqp::gles3::Functional::Texture3DFilteringCase::FilterCase1070         FilterCase(void) : texture(DE_NULL)
1071         {
1072         }
1073 
FilterCasedeqp::gles3::Functional::Texture3DFilteringCase::FilterCase1074         FilterCase(const glu::Texture3D *tex_, const tcu::Vec3 &lod_, const tcu::Vec3 &offset_)
1075             : texture(tex_)
1076             , lod(lod_)
1077             , offset(offset_)
1078         {
1079         }
1080     };
1081 
1082     glu::Texture3D *m_gradientTex;
1083     glu::Texture3D *m_gridTex;
1084 
1085     TextureRenderer m_renderer;
1086 
1087     std::vector<FilterCase> m_cases;
1088     int m_caseNdx;
1089 };
1090 
Texture3DFilteringCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t wrapR,uint32_t internalFormat,int width,int height,int depth)1091 Texture3DFilteringCase::Texture3DFilteringCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
1092                                                uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t wrapR,
1093                                                uint32_t internalFormat, int width, int height, int depth)
1094     : TestCase(context, name, desc)
1095     , m_minFilter(minFilter)
1096     , m_magFilter(magFilter)
1097     , m_wrapS(wrapS)
1098     , m_wrapT(wrapT)
1099     , m_wrapR(wrapR)
1100     , m_internalFormat(internalFormat)
1101     , m_width(width)
1102     , m_height(height)
1103     , m_depth(depth)
1104     , m_gradientTex(DE_NULL)
1105     , m_gridTex(DE_NULL)
1106     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES,
1107                  glu::PRECISION_HIGHP)
1108     , m_caseNdx(0)
1109 {
1110 }
1111 
~Texture3DFilteringCase(void)1112 Texture3DFilteringCase::~Texture3DFilteringCase(void)
1113 {
1114     Texture3DFilteringCase::deinit();
1115 }
1116 
init(void)1117 void Texture3DFilteringCase::init(void)
1118 {
1119     checkSupport(m_context.getContextInfo(), m_internalFormat);
1120 
1121     try
1122     {
1123         const tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_internalFormat);
1124         const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
1125         const tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
1126         const tcu::Vec4 cBias                = fmtInfo.valueMin;
1127         const int numLevels                  = deLog2Floor32(de::max(de::max(m_width, m_height), m_depth)) + 1;
1128 
1129         // Create textures.
1130         m_gradientTex = new glu::Texture3D(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_depth);
1131         m_gridTex     = new glu::Texture3D(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_depth);
1132 
1133         // Fill first gradient texture.
1134         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1135         {
1136             tcu::Vec4 gMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f) * cScale + cBias;
1137             tcu::Vec4 gMax = tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) * cScale + cBias;
1138 
1139             m_gradientTex->getRefTexture().allocLevel(levelNdx);
1140             tcu::fillWithComponentGradients(m_gradientTex->getRefTexture().getLevel(levelNdx), gMin, gMax);
1141         }
1142 
1143         // Fill second with grid texture.
1144         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1145         {
1146             uint32_t step   = 0x00ffffff / numLevels;
1147             uint32_t rgb    = step * levelNdx;
1148             uint32_t colorA = 0xff000000 | rgb;
1149             uint32_t colorB = 0xff000000 | ~rgb;
1150 
1151             m_gridTex->getRefTexture().allocLevel(levelNdx);
1152             tcu::fillWithGrid(m_gridTex->getRefTexture().getLevel(levelNdx), 4,
1153                               tcu::RGBA(colorA).toVec() * cScale + cBias, tcu::RGBA(colorB).toVec() * cScale + cBias);
1154         }
1155 
1156         // Upload.
1157         m_gradientTex->upload();
1158         m_gridTex->upload();
1159 
1160         // Test cases
1161         m_cases.push_back(FilterCase(m_gradientTex, tcu::Vec3(1.5f, 2.8f, 1.0f), tcu::Vec3(-1.0f, -2.7f, -2.275f)));
1162         m_cases.push_back(FilterCase(m_gradientTex, tcu::Vec3(-2.0f, -1.5f, -1.8f), tcu::Vec3(-0.1f, 0.9f, -0.25f)));
1163         m_cases.push_back(FilterCase(m_gridTex, tcu::Vec3(0.2f, 0.175f, 0.3f), tcu::Vec3(-2.0f, -3.7f, -1.825f)));
1164         m_cases.push_back(FilterCase(m_gridTex, tcu::Vec3(-0.8f, -2.3f, -2.5f), tcu::Vec3(0.2f, -0.1f, 1.325f)));
1165 
1166         m_caseNdx = 0;
1167         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1168     }
1169     catch (...)
1170     {
1171         // Clean up to save memory.
1172         Texture3DFilteringCase::deinit();
1173         throw;
1174     }
1175 }
1176 
deinit(void)1177 void Texture3DFilteringCase::deinit(void)
1178 {
1179     delete m_gradientTex;
1180     delete m_gridTex;
1181 
1182     m_gradientTex = DE_NULL;
1183     m_gridTex     = DE_NULL;
1184 
1185     m_renderer.clear();
1186     m_cases.clear();
1187 }
1188 
iterate(void)1189 Texture3DFilteringCase::IterateResult Texture3DFilteringCase::iterate(void)
1190 {
1191     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1192     const RandomViewport viewport(m_context.getRenderTarget(), TEX3D_VIEWPORT_WIDTH, TEX3D_VIEWPORT_HEIGHT,
1193                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
1194     const FilterCase &curCase            = m_cases[m_caseNdx];
1195     const tcu::TextureFormat texFmt      = curCase.texture->getRefTexture().getFormat();
1196     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
1197     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
1198                                         string("Test ") + de::toString(m_caseNdx));
1199     ReferenceParams refParams(TEXTURETYPE_3D);
1200     tcu::Surface rendered(viewport.width, viewport.height);
1201     tcu::Vec3 texCoord[4];
1202 
1203     if (viewport.width < TEX3D_MIN_VIEWPORT_WIDTH || viewport.height < TEX3D_MIN_VIEWPORT_HEIGHT)
1204         throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
1205 
1206     // Setup params for reference.
1207     refParams.sampler     = glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapR, m_minFilter, m_magFilter);
1208     refParams.samplerType = getSamplerType(texFmt);
1209     refParams.lodMode     = LODMODE_EXACT;
1210     refParams.colorBias   = fmtInfo.lookupBias;
1211     refParams.colorScale  = fmtInfo.lookupScale;
1212 
1213     // Compute texture coordinates.
1214     m_testCtx.getLog() << TestLog::Message << "Approximate lod per axis = " << curCase.lod
1215                        << ", offset = " << curCase.offset << TestLog::EndMessage;
1216 
1217     {
1218         const float lodX = curCase.lod.x();
1219         const float lodY = curCase.lod.y();
1220         const float lodZ = curCase.lod.z();
1221         const float oX   = curCase.offset.x();
1222         const float oY   = curCase.offset.y();
1223         const float oZ   = curCase.offset.z();
1224         const float sX   = deFloatExp2(lodX) * float(viewport.width) / float(m_gradientTex->getRefTexture().getWidth());
1225         const float sY = deFloatExp2(lodY) * float(viewport.height) / float(m_gradientTex->getRefTexture().getHeight());
1226         const float sZ = deFloatExp2(lodZ) * float(de::max(viewport.width, viewport.height)) /
1227                          float(m_gradientTex->getRefTexture().getDepth());
1228 
1229         texCoord[0] = tcu::Vec3(oX, oY, oZ);
1230         texCoord[1] = tcu::Vec3(oX, oY + sY, oZ + sZ * 0.5f);
1231         texCoord[2] = tcu::Vec3(oX + sX, oY, oZ + sZ * 0.5f);
1232         texCoord[3] = tcu::Vec3(oX + sX, oY + sY, oZ + sZ);
1233     }
1234 
1235     gl.bindTexture(GL_TEXTURE_3D, curCase.texture->getGLTexture());
1236     gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, m_minFilter);
1237     gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, m_magFilter);
1238     gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, m_wrapS);
1239     gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, m_wrapT);
1240     gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, m_wrapR);
1241 
1242     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
1243     m_renderer.renderQuad(0, (const float *)&texCoord[0], refParams);
1244     glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
1245 
1246     {
1247         const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
1248         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
1249         const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
1250                                                  tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
1251         tcu::LodPrecision lodPrecision;
1252         tcu::LookupPrecision lookupPrecision;
1253 
1254         lodPrecision.derivateBits      = 18;
1255         lodPrecision.lodBits           = 6;
1256         lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
1257         lookupPrecision.coordBits      = tcu::IVec3(20, 20, 20);
1258         lookupPrecision.uvwBits        = tcu::IVec3(7, 7, 7);
1259         lookupPrecision.colorMask      = getCompareMask(pixelFormat);
1260 
1261         const bool isHighQuality =
1262             verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1263                                 (const float *)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1264 
1265         if (!isHighQuality)
1266         {
1267             // Evaluate against lower precision requirements.
1268             lodPrecision.lodBits    = 4;
1269             lookupPrecision.uvwBits = tcu::IVec3(4, 4, 4);
1270 
1271             m_testCtx.getLog()
1272                 << TestLog::Message
1273                 << "Warning: Verification against high precision requirements failed, trying with lower requirements."
1274                 << TestLog::EndMessage;
1275 
1276             const bool isOk =
1277                 verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
1278                                     (const float *)&texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
1279 
1280             if (!isOk)
1281             {
1282                 m_testCtx.getLog()
1283                     << TestLog::Message
1284                     << "ERROR: Verification against low precision requirements failed, failing test case."
1285                     << TestLog::EndMessage;
1286                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1287             }
1288             else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1289                 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality filtering result");
1290         }
1291     }
1292 
1293     m_caseNdx += 1;
1294     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
1295 }
1296 
TextureFilteringTests(Context & context)1297 TextureFilteringTests::TextureFilteringTests(Context &context)
1298     : TestCaseGroup(context, "filtering", "Texture Filtering Tests")
1299 {
1300 }
1301 
~TextureFilteringTests(void)1302 TextureFilteringTests::~TextureFilteringTests(void)
1303 {
1304 }
1305 
init(void)1306 void TextureFilteringTests::init(void)
1307 {
1308     static const struct
1309     {
1310         const char *name;
1311         uint32_t mode;
1312     } wrapModes[] = {{"clamp", GL_CLAMP_TO_EDGE}, {"repeat", GL_REPEAT}, {"mirror", GL_MIRRORED_REPEAT}};
1313 
1314     static const struct
1315     {
1316         const char *name;
1317         uint32_t mode;
1318     } minFilterModes[] = {{"nearest", GL_NEAREST},
1319                           {"linear", GL_LINEAR},
1320                           {"nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST},
1321                           {"linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST},
1322                           {"nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR},
1323                           {"linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR}};
1324 
1325     static const struct
1326     {
1327         const char *name;
1328         uint32_t mode;
1329     } magFilterModes[] = {{"nearest", GL_NEAREST}, {"linear", GL_LINEAR}};
1330 
1331     static const struct
1332     {
1333         int width;
1334         int height;
1335     } sizes2D[] = {{4, 8}, {32, 64}, {128, 128}, {3, 7}, {31, 55}, {127, 99}};
1336 
1337     static const struct
1338     {
1339         int width;
1340         int height;
1341     } sizesCube[] = {{8, 8}, {64, 64}, {128, 128}, {7, 7}, {63, 63}};
1342 
1343     static const struct
1344     {
1345         int width;
1346         int height;
1347         int numLayers;
1348     } sizes2DArray[] = {{4, 8, 8}, {32, 64, 16}, {128, 32, 64}, {3, 7, 5}, {63, 63, 63}};
1349 
1350     static const struct
1351     {
1352         int width;
1353         int height;
1354         int depth;
1355     } sizes3D[] = {{4, 8, 8}, {32, 64, 16}, {128, 32, 64}, {3, 7, 5}, {63, 63, 63}};
1356 
1357     static const struct
1358     {
1359         const char *name;
1360         uint32_t format;
1361     } filterableFormatsByType[] = {{"rgba16f", GL_RGBA16F},
1362                                    {"r11f_g11f_b10f", GL_R11F_G11F_B10F},
1363                                    {"rgb9_e5", GL_RGB9_E5},
1364                                    {"rgba8", GL_RGBA8},
1365                                    {"rgba8_snorm", GL_RGBA8_SNORM},
1366                                    {"rgb565", GL_RGB565},
1367                                    {"rgba4", GL_RGBA4},
1368                                    {"rgb5_a1", GL_RGB5_A1},
1369                                    {"srgb8_alpha8", GL_SRGB8_ALPHA8},
1370                                    {"srgb_r8", GL_SR8_EXT},
1371                                    {"srgb_rg8", GL_SRG8_EXT},
1372                                    {"rgb10_a2", GL_RGB10_A2}};
1373 
1374     // 2D texture filtering.
1375     {
1376         tcu::TestCaseGroup *group2D = new tcu::TestCaseGroup(m_testCtx, "2d", "2D Texture Filtering");
1377         addChild(group2D);
1378 
1379         // Formats.
1380         tcu::TestCaseGroup *formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "2D Texture Formats");
1381         group2D->addChild(formatsGroup);
1382         for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1383         {
1384             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1385             {
1386                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1387                 const char *filterName = minFilterModes[filterNdx].name;
1388                 uint32_t format        = filterableFormatsByType[fmtNdx].format;
1389                 const char *formatName = filterableFormatsByType[fmtNdx].name;
1390                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1391                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1392                 string name            = string(formatName) + "_" + filterName;
1393                 uint32_t wrapS         = GL_REPEAT;
1394                 uint32_t wrapT         = GL_REPEAT;
1395                 int width              = 64;
1396                 int height             = 64;
1397 
1398                 formatsGroup->addChild(new Texture2DFilteringCase(
1399                     m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "", minFilter,
1400                     magFilter, wrapS, wrapT, format, width, height));
1401             }
1402         }
1403 
1404         // ETC1 format.
1405         {
1406             std::vector<std::string> filenames;
1407             for (int i = 0; i <= 7; i++)
1408                 filenames.push_back(string("data/etc1/photo_helsinki_mip_") + de::toString(i) + ".pkm");
1409 
1410             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1411             {
1412                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1413                 const char *filterName = minFilterModes[filterNdx].name;
1414                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1415                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1416                 string name            = string("etc1_rgb8_") + filterName;
1417                 uint32_t wrapS         = GL_REPEAT;
1418                 uint32_t wrapT         = GL_REPEAT;
1419 
1420                 formatsGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(),
1421                                                                   m_context.getContextInfo(), name.c_str(), "",
1422                                                                   minFilter, magFilter, wrapS, wrapT, filenames));
1423             }
1424         }
1425 
1426         // Sizes.
1427         tcu::TestCaseGroup *sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1428         group2D->addChild(sizesGroup);
1429         for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes2D); sizeNdx++)
1430         {
1431             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1432             {
1433                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1434                 const char *filterName = minFilterModes[filterNdx].name;
1435                 uint32_t format        = GL_RGBA8;
1436                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1437                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1438                 uint32_t wrapS         = GL_REPEAT;
1439                 uint32_t wrapT         = GL_REPEAT;
1440                 int width              = sizes2D[sizeNdx].width;
1441                 int height             = sizes2D[sizeNdx].height;
1442                 string name            = de::toString(width) + "x" + de::toString(height) + "_" + filterName;
1443 
1444                 sizesGroup->addChild(new Texture2DFilteringCase(m_testCtx, m_context.getRenderContext(),
1445                                                                 m_context.getContextInfo(), name.c_str(), "", minFilter,
1446                                                                 magFilter, wrapS, wrapT, format, width, height));
1447             }
1448         }
1449 
1450         // Wrap modes.
1451         tcu::TestCaseGroup *combinationsGroup =
1452             new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1453         group2D->addChild(combinationsGroup);
1454         for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1455         {
1456             for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1457             {
1458                 for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1459                 {
1460                     for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1461                     {
1462                         uint32_t minFilter = minFilterModes[minFilterNdx].mode;
1463                         uint32_t magFilter = magFilterModes[magFilterNdx].mode;
1464                         uint32_t format    = GL_RGBA8;
1465                         uint32_t wrapS     = wrapModes[wrapSNdx].mode;
1466                         uint32_t wrapT     = wrapModes[wrapTNdx].mode;
1467                         int width          = 63;
1468                         int height         = 57;
1469                         string name        = string(minFilterModes[minFilterNdx].name) + "_" +
1470                                       magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" +
1471                                       wrapModes[wrapTNdx].name;
1472 
1473                         combinationsGroup->addChild(new Texture2DFilteringCase(
1474                             m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
1475                             minFilter, magFilter, wrapS, wrapT, format, width, height));
1476                     }
1477                 }
1478             }
1479         }
1480     }
1481 
1482     // Cube map texture filtering.
1483     {
1484         tcu::TestCaseGroup *groupCube = new tcu::TestCaseGroup(m_testCtx, "cube", "Cube Map Texture Filtering");
1485         addChild(groupCube);
1486 
1487         // Formats.
1488         tcu::TestCaseGroup *formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "2D Texture Formats");
1489         groupCube->addChild(formatsGroup);
1490         for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1491         {
1492             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1493             {
1494                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1495                 const char *filterName = minFilterModes[filterNdx].name;
1496                 uint32_t format        = filterableFormatsByType[fmtNdx].format;
1497                 const char *formatName = filterableFormatsByType[fmtNdx].name;
1498                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1499                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1500                 string name            = string(formatName) + "_" + filterName;
1501                 uint32_t wrapS         = GL_REPEAT;
1502                 uint32_t wrapT         = GL_REPEAT;
1503                 int width              = 64;
1504                 int height             = 64;
1505 
1506                 formatsGroup->addChild(new TextureCubeFilteringCase(
1507                     m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "", minFilter,
1508                     magFilter, wrapS, wrapT, false /* always sample exterior as well */, format, width, height));
1509             }
1510         }
1511 
1512         // ETC1 format.
1513         {
1514             static const char *faceExt[] = {"neg_x", "pos_x", "neg_y", "pos_y", "neg_z", "pos_z"};
1515 
1516             const int numLevels = 7;
1517             vector<string> filenames;
1518             for (int level = 0; level < numLevels; level++)
1519                 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
1520                     filenames.push_back(string("data/etc1/skybox_") + faceExt[face] + "_mip_" + de::toString(level) +
1521                                         ".pkm");
1522 
1523             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1524             {
1525                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1526                 const char *filterName = minFilterModes[filterNdx].name;
1527                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1528                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1529                 string name            = string("etc1_rgb8_") + filterName;
1530                 uint32_t wrapS         = GL_REPEAT;
1531                 uint32_t wrapT         = GL_REPEAT;
1532 
1533                 formatsGroup->addChild(new TextureCubeFilteringCase(
1534                     m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "", minFilter,
1535                     magFilter, wrapS, wrapT, false /* always sample exterior as well */, filenames));
1536             }
1537         }
1538 
1539         // Sizes.
1540         tcu::TestCaseGroup *sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1541         groupCube->addChild(sizesGroup);
1542         for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizesCube); sizeNdx++)
1543         {
1544             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1545             {
1546                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1547                 const char *filterName = minFilterModes[filterNdx].name;
1548                 uint32_t format        = GL_RGBA8;
1549                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1550                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1551                 uint32_t wrapS         = GL_REPEAT;
1552                 uint32_t wrapT         = GL_REPEAT;
1553                 int width              = sizesCube[sizeNdx].width;
1554                 int height             = sizesCube[sizeNdx].height;
1555                 string name            = de::toString(width) + "x" + de::toString(height) + "_" + filterName;
1556 
1557                 sizesGroup->addChild(new TextureCubeFilteringCase(
1558                     m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "", minFilter,
1559                     magFilter, wrapS, wrapT, false, format, width, height));
1560             }
1561         }
1562 
1563         // Filter/wrap mode combinations.
1564         tcu::TestCaseGroup *combinationsGroup =
1565             new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1566         groupCube->addChild(combinationsGroup);
1567         for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1568         {
1569             for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1570             {
1571                 for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1572                 {
1573                     for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1574                     {
1575                         uint32_t minFilter = minFilterModes[minFilterNdx].mode;
1576                         uint32_t magFilter = magFilterModes[magFilterNdx].mode;
1577                         uint32_t format    = GL_RGBA8;
1578                         uint32_t wrapS     = wrapModes[wrapSNdx].mode;
1579                         uint32_t wrapT     = wrapModes[wrapTNdx].mode;
1580                         int width          = 63;
1581                         int height         = 63;
1582                         string name        = string(minFilterModes[minFilterNdx].name) + "_" +
1583                                       magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" +
1584                                       wrapModes[wrapTNdx].name;
1585 
1586                         combinationsGroup->addChild(new TextureCubeFilteringCase(
1587                             m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
1588                             minFilter, magFilter, wrapS, wrapT, false, format, width, height));
1589                     }
1590                 }
1591             }
1592         }
1593 
1594         // Cases with no visible cube edges.
1595         tcu::TestCaseGroup *onlyFaceInteriorGroup =
1596             new tcu::TestCaseGroup(m_testCtx, "no_edges_visible", "Don't sample anywhere near a face's edges");
1597         groupCube->addChild(onlyFaceInteriorGroup);
1598 
1599         for (int isLinearI = 0; isLinearI <= 1; isLinearI++)
1600         {
1601             bool isLinear   = isLinearI != 0;
1602             uint32_t filter = isLinear ? GL_LINEAR : GL_NEAREST;
1603 
1604             onlyFaceInteriorGroup->addChild(new TextureCubeFilteringCase(
1605                 m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), isLinear ? "linear" : "nearest",
1606                 "", filter, filter, GL_REPEAT, GL_REPEAT, true, GL_RGBA8, 63, 63));
1607         }
1608     }
1609 
1610     // 2D array texture filtering.
1611     {
1612         tcu::TestCaseGroup *const group2DArray =
1613             new tcu::TestCaseGroup(m_testCtx, "2d_array", "2D Array Texture Filtering");
1614         addChild(group2DArray);
1615 
1616         // Formats.
1617         tcu::TestCaseGroup *const formatsGroup =
1618             new tcu::TestCaseGroup(m_testCtx, "formats", "2D Array Texture Formats");
1619         group2DArray->addChild(formatsGroup);
1620         for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1621         {
1622             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1623             {
1624                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1625                 const char *filterName = minFilterModes[filterNdx].name;
1626                 uint32_t format        = filterableFormatsByType[fmtNdx].format;
1627                 const char *formatName = filterableFormatsByType[fmtNdx].name;
1628                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1629                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1630                 string name            = string(formatName) + "_" + filterName;
1631                 uint32_t wrapS         = GL_REPEAT;
1632                 uint32_t wrapT         = GL_REPEAT;
1633                 int width              = 128;
1634                 int height             = 128;
1635                 int numLayers          = 8;
1636 
1637                 formatsGroup->addChild(new Texture2DArrayFilteringCase(
1638                     m_context, name.c_str(), "", minFilter, magFilter, wrapS, wrapT, format, width, height, numLayers));
1639             }
1640         }
1641 
1642         // Sizes.
1643         tcu::TestCaseGroup *sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1644         group2DArray->addChild(sizesGroup);
1645         for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes2DArray); sizeNdx++)
1646         {
1647             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1648             {
1649                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1650                 const char *filterName = minFilterModes[filterNdx].name;
1651                 uint32_t format        = GL_RGBA8;
1652                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1653                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1654                 uint32_t wrapS         = GL_REPEAT;
1655                 uint32_t wrapT         = GL_REPEAT;
1656                 int width              = sizes2DArray[sizeNdx].width;
1657                 int height             = sizes2DArray[sizeNdx].height;
1658                 int numLayers          = sizes2DArray[sizeNdx].numLayers;
1659                 string name =
1660                     de::toString(width) + "x" + de::toString(height) + "x" + de::toString(numLayers) + "_" + filterName;
1661 
1662                 sizesGroup->addChild(new Texture2DArrayFilteringCase(m_context, name.c_str(), "", minFilter, magFilter,
1663                                                                      wrapS, wrapT, format, width, height, numLayers));
1664             }
1665         }
1666 
1667         // Wrap modes.
1668         tcu::TestCaseGroup *const combinationsGroup =
1669             new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1670         group2DArray->addChild(combinationsGroup);
1671         for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1672         {
1673             for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1674             {
1675                 for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1676                 {
1677                     for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1678                     {
1679                         uint32_t minFilter = minFilterModes[minFilterNdx].mode;
1680                         uint32_t magFilter = magFilterModes[magFilterNdx].mode;
1681                         uint32_t format    = GL_RGBA8;
1682                         uint32_t wrapS     = wrapModes[wrapSNdx].mode;
1683                         uint32_t wrapT     = wrapModes[wrapTNdx].mode;
1684                         int width          = 123;
1685                         int height         = 107;
1686                         int numLayers      = 7;
1687                         string name        = string(minFilterModes[minFilterNdx].name) + "_" +
1688                                       magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" +
1689                                       wrapModes[wrapTNdx].name;
1690 
1691                         combinationsGroup->addChild(new Texture2DArrayFilteringCase(m_context, name.c_str(), "",
1692                                                                                     minFilter, magFilter, wrapS, wrapT,
1693                                                                                     format, width, height, numLayers));
1694                     }
1695                 }
1696             }
1697         }
1698     }
1699 
1700     // 3D texture filtering.
1701     {
1702         tcu::TestCaseGroup *group3D = new tcu::TestCaseGroup(m_testCtx, "3d", "3D Texture Filtering");
1703         addChild(group3D);
1704 
1705         // Formats.
1706         tcu::TestCaseGroup *formatsGroup = new tcu::TestCaseGroup(m_testCtx, "formats", "3D Texture Formats");
1707         group3D->addChild(formatsGroup);
1708         for (int fmtNdx = 0; fmtNdx < DE_LENGTH_OF_ARRAY(filterableFormatsByType); fmtNdx++)
1709         {
1710             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1711             {
1712                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1713                 const char *filterName = minFilterModes[filterNdx].name;
1714                 uint32_t format        = filterableFormatsByType[fmtNdx].format;
1715                 const char *formatName = filterableFormatsByType[fmtNdx].name;
1716                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1717                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1718                 string name            = string(formatName) + "_" + filterName;
1719                 uint32_t wrapS         = GL_REPEAT;
1720                 uint32_t wrapT         = GL_REPEAT;
1721                 uint32_t wrapR         = GL_REPEAT;
1722                 int width              = 64;
1723                 int height             = 64;
1724                 int depth              = 64;
1725 
1726                 formatsGroup->addChild(new Texture3DFilteringCase(m_context, name.c_str(), "", minFilter, magFilter,
1727                                                                   wrapS, wrapT, wrapR, format, width, height, depth));
1728             }
1729         }
1730 
1731         // Sizes.
1732         tcu::TestCaseGroup *sizesGroup = new tcu::TestCaseGroup(m_testCtx, "sizes", "Texture Sizes");
1733         group3D->addChild(sizesGroup);
1734         for (int sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes3D); sizeNdx++)
1735         {
1736             for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); filterNdx++)
1737             {
1738                 uint32_t minFilter     = minFilterModes[filterNdx].mode;
1739                 const char *filterName = minFilterModes[filterNdx].name;
1740                 uint32_t format        = GL_RGBA8;
1741                 bool isMipmap          = minFilter != GL_NEAREST && minFilter != GL_LINEAR;
1742                 uint32_t magFilter     = isMipmap ? GL_LINEAR : minFilter;
1743                 uint32_t wrapS         = GL_REPEAT;
1744                 uint32_t wrapT         = GL_REPEAT;
1745                 uint32_t wrapR         = GL_REPEAT;
1746                 int width              = sizes3D[sizeNdx].width;
1747                 int height             = sizes3D[sizeNdx].height;
1748                 int depth              = sizes3D[sizeNdx].depth;
1749                 string name =
1750                     de::toString(width) + "x" + de::toString(height) + "x" + de::toString(depth) + "_" + filterName;
1751 
1752                 sizesGroup->addChild(new Texture3DFilteringCase(m_context, name.c_str(), "", minFilter, magFilter,
1753                                                                 wrapS, wrapT, wrapR, format, width, height, depth));
1754             }
1755         }
1756 
1757         // Wrap modes.
1758         tcu::TestCaseGroup *combinationsGroup =
1759             new tcu::TestCaseGroup(m_testCtx, "combinations", "Filter and wrap mode combinations");
1760         group3D->addChild(combinationsGroup);
1761         for (int minFilterNdx = 0; minFilterNdx < DE_LENGTH_OF_ARRAY(minFilterModes); minFilterNdx++)
1762         {
1763             for (int magFilterNdx = 0; magFilterNdx < DE_LENGTH_OF_ARRAY(magFilterModes); magFilterNdx++)
1764             {
1765                 for (int wrapSNdx = 0; wrapSNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapSNdx++)
1766                 {
1767                     for (int wrapTNdx = 0; wrapTNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapTNdx++)
1768                     {
1769                         for (int wrapRNdx = 0; wrapRNdx < DE_LENGTH_OF_ARRAY(wrapModes); wrapRNdx++)
1770                         {
1771                             uint32_t minFilter = minFilterModes[minFilterNdx].mode;
1772                             uint32_t magFilter = magFilterModes[magFilterNdx].mode;
1773                             uint32_t format    = GL_RGBA8;
1774                             uint32_t wrapS     = wrapModes[wrapSNdx].mode;
1775                             uint32_t wrapT     = wrapModes[wrapTNdx].mode;
1776                             uint32_t wrapR     = wrapModes[wrapRNdx].mode;
1777                             int width          = 63;
1778                             int height         = 57;
1779                             int depth          = 67;
1780                             string name        = string(minFilterModes[minFilterNdx].name) + "_" +
1781                                           magFilterModes[magFilterNdx].name + "_" + wrapModes[wrapSNdx].name + "_" +
1782                                           wrapModes[wrapTNdx].name + "_" + wrapModes[wrapRNdx].name;
1783 
1784                             combinationsGroup->addChild(
1785                                 new Texture3DFilteringCase(m_context, name.c_str(), "", minFilter, magFilter, wrapS,
1786                                                            wrapT, wrapR, format, width, height, depth));
1787                         }
1788                     }
1789                 }
1790             }
1791         }
1792     }
1793 }
1794 
1795 } // namespace Functional
1796 } // namespace gles3
1797 } // namespace deqp
1798