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