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