xref: /aosp_15_r20/external/deqp/modules/gles2/functional/es2fTextureFilteringTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 2.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Texture filtering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es2fTextureFilteringTests.hpp"
25 #include "glsTextureTestUtil.hpp"
26 #include "gluTexture.hpp"
27 #include "gluStrUtil.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluPixelTransfer.hpp"
30 #include "tcuTestLog.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuTexLookupVerifier.hpp"
33 #include "tcuVectorUtil.hpp"
34 #include "deStringUtil.hpp"
35 #include "glwFunctions.hpp"
36 #include "glwEnums.hpp"
37 
38 namespace deqp
39 {
40 namespace gles2
41 {
42 namespace Functional
43 {
44 
45 using std::string;
46 using std::vector;
47 using tcu::Sampler;
48 using tcu::TestLog;
49 using namespace glu;
50 using namespace gls::TextureTestUtil;
51 using namespace glu::TextureTestUtil;
52 
53 enum
54 {
55     VIEWPORT_WIDTH      = 64,
56     VIEWPORT_HEIGHT     = 64,
57     MIN_VIEWPORT_WIDTH  = 64,
58     MIN_VIEWPORT_HEIGHT = 64
59 };
60 
61 class Texture2DFilteringCase : public tcu::TestCase
62 {
63 public:
64     Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
65                            const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
66                            uint32_t wrapT, uint32_t format, uint32_t dataType, int width, int height);
67     Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
68                            const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
69                            uint32_t wrapT, const std::vector<std::string> &filenames);
70     ~Texture2DFilteringCase(void);
71 
72     void init(void);
73     void deinit(void);
74     IterateResult iterate(void);
75 
76 private:
77     Texture2DFilteringCase(const Texture2DFilteringCase &other);
78     Texture2DFilteringCase &operator=(const Texture2DFilteringCase &other);
79 
80     glu::RenderContext &m_renderCtx;
81     const glu::ContextInfo &m_renderCtxInfo;
82 
83     const uint32_t m_minFilter;
84     const uint32_t m_magFilter;
85     const uint32_t m_wrapS;
86     const uint32_t m_wrapT;
87 
88     const uint32_t m_format;
89     const uint32_t m_dataType;
90     const int m_width;
91     const int m_height;
92 
93     const std::vector<std::string> m_filenames;
94 
95     struct FilterCase
96     {
97         const glu::Texture2D *texture;
98         tcu::Vec2 minCoord;
99         tcu::Vec2 maxCoord;
100 
FilterCasedeqp::gles2::Functional::Texture2DFilteringCase::FilterCase101         FilterCase(void) : texture(DE_NULL)
102         {
103         }
104 
FilterCasedeqp::gles2::Functional::Texture2DFilteringCase::FilterCase105         FilterCase(const glu::Texture2D *tex_, const tcu::Vec2 &minCoord_, const tcu::Vec2 &maxCoord_)
106             : texture(tex_)
107             , minCoord(minCoord_)
108             , maxCoord(maxCoord_)
109         {
110         }
111     };
112 
113     std::vector<glu::Texture2D *> m_textures;
114     std::vector<FilterCase> m_cases;
115 
116     TextureRenderer m_renderer;
117 
118     int m_caseNdx;
119 };
120 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t format,uint32_t dataType,int width,int height)121 Texture2DFilteringCase::Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
122                                                const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
123                                                uint32_t minFilter, uint32_t magFilter, uint32_t wrapS, uint32_t wrapT,
124                                                uint32_t format, uint32_t dataType, int width, int height)
125     : TestCase(testCtx, name, desc)
126     , m_renderCtx(renderCtx)
127     , m_renderCtxInfo(ctxInfo)
128     , m_minFilter(minFilter)
129     , m_magFilter(magFilter)
130     , m_wrapS(wrapS)
131     , m_wrapT(wrapT)
132     , m_format(format)
133     , m_dataType(dataType)
134     , m_width(width)
135     , m_height(height)
136     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
137     , m_caseNdx(0)
138 {
139 }
140 
Texture2DFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,const std::vector<std::string> & filenames)141 Texture2DFilteringCase::Texture2DFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
142                                                const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
143                                                uint32_t minFilter, uint32_t magFilter, uint32_t wrapS, uint32_t wrapT,
144                                                const std::vector<std::string> &filenames)
145     : TestCase(testCtx, name, desc)
146     , m_renderCtx(renderCtx)
147     , m_renderCtxInfo(ctxInfo)
148     , m_minFilter(minFilter)
149     , m_magFilter(magFilter)
150     , m_wrapS(wrapS)
151     , m_wrapT(wrapT)
152     , m_format(GL_NONE)
153     , m_dataType(GL_NONE)
154     , m_width(0)
155     , m_height(0)
156     , m_filenames(filenames)
157     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
158     , m_caseNdx(0)
159 {
160 }
161 
~Texture2DFilteringCase(void)162 Texture2DFilteringCase::~Texture2DFilteringCase(void)
163 {
164     deinit();
165 }
166 
init(void)167 void Texture2DFilteringCase::init(void)
168 {
169     try
170     {
171         if (!m_filenames.empty())
172         {
173             m_textures.reserve(1);
174             m_textures.push_back(glu::Texture2D::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(),
175                                                         (int)m_filenames.size(), m_filenames));
176         }
177         else
178         {
179             // Create 2 textures.
180             m_textures.reserve(2);
181             for (int ndx = 0; ndx < 2; ndx++)
182                 m_textures.push_back(new glu::Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height));
183 
184             bool mipmaps                   = deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
185             int numLevels                  = mipmaps ? deLog2Floor32(de::max(m_width, m_height)) + 1 : 1;
186             tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
187             tcu::Vec4 cBias                = fmtInfo.valueMin;
188             tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
189 
190             // Fill first gradient texture.
191             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
192             {
193                 tcu::Vec4 gMin = tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f) * cScale + cBias;
194                 tcu::Vec4 gMax = tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) * cScale + cBias;
195 
196                 m_textures[0]->getRefTexture().allocLevel(levelNdx);
197                 tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
198             }
199 
200             // Fill second with grid texture.
201             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
202             {
203                 uint32_t step   = 0x00ffffff / numLevels;
204                 uint32_t rgb    = step * levelNdx;
205                 uint32_t colorA = 0xff000000 | rgb;
206                 uint32_t colorB = 0xff000000 | ~rgb;
207 
208                 m_textures[1]->getRefTexture().allocLevel(levelNdx);
209                 tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4,
210                                   tcu::RGBA(colorA).toVec() * cScale + cBias,
211                                   tcu::RGBA(colorB).toVec() * cScale + cBias);
212             }
213 
214             // Upload.
215             for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
216                 (*i)->upload();
217         }
218 
219         // Compute cases.
220         {
221             const struct
222             {
223                 int texNdx;
224                 float lodX;
225                 float lodY;
226                 float oX;
227                 float oY;
228             } cases[] = {
229                 {0, 1.6f, 2.9f, -1.0f, -2.7f},
230                 {0, -2.0f, -1.35f, -0.2f, 0.7f},
231                 {1, 0.14f, 0.275f, -1.5f, -1.1f},
232                 {1, -0.92f, -2.64f, 0.4f, -0.1f},
233             };
234 
235             const float viewportW = (float)de::min<int>(VIEWPORT_WIDTH, m_renderCtx.getRenderTarget().getWidth());
236             const float viewportH = (float)de::min<int>(VIEWPORT_HEIGHT, m_renderCtx.getRenderTarget().getHeight());
237 
238             for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
239             {
240                 const int texNdx = de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size() - 1);
241                 const float lodX = cases[caseNdx].lodX;
242                 const float lodY = cases[caseNdx].lodY;
243                 const float oX   = cases[caseNdx].oX;
244                 const float oY   = cases[caseNdx].oY;
245                 const float sX = deFloatExp2(lodX) * viewportW / float(m_textures[texNdx]->getRefTexture().getWidth());
246                 const float sY = deFloatExp2(lodY) * viewportH / float(m_textures[texNdx]->getRefTexture().getHeight());
247 
248                 m_cases.push_back(FilterCase(m_textures[texNdx], tcu::Vec2(oX, oY), tcu::Vec2(oX + sX, oY + sY)));
249             }
250         }
251 
252         m_caseNdx = 0;
253         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
254     }
255     catch (...)
256     {
257         // Clean up to save memory.
258         Texture2DFilteringCase::deinit();
259         throw;
260     }
261 }
262 
deinit(void)263 void Texture2DFilteringCase::deinit(void)
264 {
265     for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
266         delete *i;
267     m_textures.clear();
268 
269     m_renderer.clear();
270     m_cases.clear();
271 }
272 
iterate(void)273 Texture2DFilteringCase::IterateResult Texture2DFilteringCase::iterate(void)
274 {
275     const glw::Functions &gl = m_renderCtx.getFunctions();
276     const RandomViewport viewport(m_renderCtx.getRenderTarget(), VIEWPORT_WIDTH, VIEWPORT_HEIGHT,
277                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
278     const tcu::TextureFormat texFmt      = m_textures[0]->getRefTexture().getFormat();
279     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
280     const FilterCase &curCase            = m_cases[m_caseNdx];
281     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
282                                         string("Test ") + de::toString(m_caseNdx));
283     ReferenceParams refParams(TEXTURETYPE_2D);
284     tcu::Surface rendered(viewport.width, viewport.height);
285     vector<float> texCoord;
286 
287     if (viewport.width < MIN_VIEWPORT_WIDTH || viewport.height < MIN_VIEWPORT_HEIGHT)
288         throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
289 
290     // Setup params for reference.
291     refParams.sampler     = mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
292     refParams.samplerType = getSamplerType(texFmt);
293     refParams.lodMode     = LODMODE_EXACT;
294     refParams.colorBias   = fmtInfo.lookupBias;
295     refParams.colorScale  = fmtInfo.lookupScale;
296 
297     // Compute texture coordinates.
298     m_testCtx.getLog() << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
299                        << TestLog::EndMessage;
300     computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
301 
302     gl.bindTexture(GL_TEXTURE_2D, curCase.texture->getGLTexture());
303     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
304     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
305     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
306     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
307 
308     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
309     m_renderer.renderQuad(0, &texCoord[0], refParams);
310     glu::readPixels(m_renderCtx, viewport.x, viewport.y, rendered.getAccess());
311 
312     {
313         const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
314         const tcu::PixelFormat pixelFormat = m_renderCtx.getRenderTarget().getPixelFormat();
315         const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
316                                                  tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
317         tcu::LodPrecision lodPrecision;
318         tcu::LookupPrecision lookupPrecision;
319 
320         lodPrecision.derivateBits      = 7;
321         lodPrecision.lodBits           = 4;
322         lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / refParams.colorScale;
323         lookupPrecision.coordBits      = tcu::IVec3(9, 9, 0); // mediump interpolation
324         lookupPrecision.uvwBits        = tcu::IVec3(5, 5, 0);
325         lookupPrecision.colorMask      = getCompareMask(pixelFormat);
326 
327         const bool isOk = verifyTextureResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(),
328                                               &texCoord[0], refParams, lookupPrecision, lodPrecision, pixelFormat);
329 
330         if (!isOk)
331             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
332     }
333 
334     m_caseNdx += 1;
335     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
336 }
337 
338 class TextureCubeFilteringCase : public tcu::TestCase
339 {
340 public:
341     TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
342                              const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
343                              uint32_t wrapT, uint32_t format, uint32_t dataType, int width, int height);
344     TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo,
345                              const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
346                              uint32_t wrapT, const std::vector<std::string> &filenames);
347     ~TextureCubeFilteringCase(void);
348 
349     void init(void);
350     void deinit(void);
351     IterateResult iterate(void);
352 
353 private:
354     TextureCubeFilteringCase(const TextureCubeFilteringCase &other);
355     TextureCubeFilteringCase &operator=(const TextureCubeFilteringCase &other);
356 
357     glu::RenderContext &m_renderCtx;
358     const glu::ContextInfo &m_renderCtxInfo;
359 
360     const uint32_t m_minFilter;
361     const uint32_t m_magFilter;
362     const uint32_t m_wrapS;
363     const uint32_t m_wrapT;
364 
365     const uint32_t m_format;
366     const uint32_t m_dataType;
367     const int m_width;
368     const int m_height;
369 
370     const std::vector<std::string> m_filenames;
371 
372     struct FilterCase
373     {
374         const glu::TextureCube *texture;
375         tcu::Vec2 bottomLeft;
376         tcu::Vec2 topRight;
377 
FilterCasedeqp::gles2::Functional::TextureCubeFilteringCase::FilterCase378         FilterCase(void) : texture(DE_NULL)
379         {
380         }
381 
FilterCasedeqp::gles2::Functional::TextureCubeFilteringCase::FilterCase382         FilterCase(const glu::TextureCube *tex_, const tcu::Vec2 &bottomLeft_, const tcu::Vec2 &topRight_)
383             : texture(tex_)
384             , bottomLeft(bottomLeft_)
385             , topRight(topRight_)
386         {
387         }
388     };
389 
390     std::vector<glu::TextureCube *> m_textures;
391     std::vector<FilterCase> m_cases;
392 
393     TextureRenderer m_renderer;
394 
395     int m_caseNdx;
396 };
397 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t format,uint32_t dataType,int width,int height)398 TextureCubeFilteringCase::TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
399                                                    const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
400                                                    uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
401                                                    uint32_t wrapT, uint32_t format, uint32_t dataType, int width,
402                                                    int height)
403     : TestCase(testCtx, name, desc)
404     , m_renderCtx(renderCtx)
405     , m_renderCtxInfo(ctxInfo)
406     , m_minFilter(minFilter)
407     , m_magFilter(magFilter)
408     , m_wrapS(wrapS)
409     , m_wrapT(wrapT)
410     , m_format(format)
411     , m_dataType(dataType)
412     , m_width(width)
413     , m_height(height)
414     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
415     , m_caseNdx(0)
416 {
417 }
418 
TextureCubeFilteringCase(tcu::TestContext & testCtx,glu::RenderContext & renderCtx,const glu::ContextInfo & ctxInfo,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,const std::vector<std::string> & filenames)419 TextureCubeFilteringCase::TextureCubeFilteringCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx,
420                                                    const glu::ContextInfo &ctxInfo, const char *name, const char *desc,
421                                                    uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
422                                                    uint32_t wrapT, const std::vector<std::string> &filenames)
423     : TestCase(testCtx, name, desc)
424     , m_renderCtx(renderCtx)
425     , m_renderCtxInfo(ctxInfo)
426     , m_minFilter(minFilter)
427     , m_magFilter(magFilter)
428     , m_wrapS(wrapS)
429     , m_wrapT(wrapT)
430     , m_format(GL_NONE)
431     , m_dataType(GL_NONE)
432     , m_width(0)
433     , m_height(0)
434     , m_filenames(filenames)
435     , m_renderer(renderCtx, testCtx.getLog(), glu::GLSL_VERSION_100_ES, glu::PRECISION_MEDIUMP)
436     , m_caseNdx(0)
437 {
438 }
439 
~TextureCubeFilteringCase(void)440 TextureCubeFilteringCase::~TextureCubeFilteringCase(void)
441 {
442     deinit();
443 }
444 
init(void)445 void TextureCubeFilteringCase::init(void)
446 {
447     try
448     {
449         if (!m_filenames.empty())
450         {
451             m_textures.reserve(1);
452             m_textures.push_back(glu::TextureCube::create(m_renderCtx, m_renderCtxInfo, m_testCtx.getArchive(),
453                                                           (int)m_filenames.size() / 6, m_filenames));
454         }
455         else
456         {
457             DE_ASSERT(m_width == m_height);
458             m_textures.reserve(2);
459             for (int ndx = 0; ndx < 2; ndx++)
460                 m_textures.push_back(new glu::TextureCube(m_renderCtx, m_format, m_dataType, m_width));
461 
462             const bool mipmaps             = deIsPowerOfTwo32(m_width) && deIsPowerOfTwo32(m_height);
463             const int numLevels            = mipmaps ? deLog2Floor32(de::max(m_width, m_height)) + 1 : 1;
464             tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
465             tcu::Vec4 cBias                = fmtInfo.valueMin;
466             tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
467 
468             // Fill first with gradient texture.
469             static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] = {
470                 {tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative x
471                 {tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // positive x
472                 {tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative y
473                 {tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // positive y
474                 {tcu::Vec4(0.0f, 0.0f, 0.0f, 0.5f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)}, // negative z
475                 {tcu::Vec4(0.5f, 0.5f, 0.5f, 1.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}  // positive z
476             };
477             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
478             {
479                 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
480                 {
481                     m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
482                     tcu::fillWithComponentGradients(
483                         m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face),
484                         gradients[face][0] * cScale + cBias, gradients[face][1] * cScale + cBias);
485                 }
486             }
487 
488             // Fill second with grid texture.
489             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
490             {
491                 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
492                 {
493                     uint32_t step   = 0x00ffffff / (numLevels * tcu::CUBEFACE_LAST);
494                     uint32_t rgb    = step * levelNdx * face;
495                     uint32_t colorA = 0xff000000 | rgb;
496                     uint32_t colorB = 0xff000000 | ~rgb;
497 
498                     m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
499                     tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4,
500                                       tcu::RGBA(colorA).toVec() * cScale + cBias,
501                                       tcu::RGBA(colorB).toVec() * cScale + cBias);
502                 }
503             }
504 
505             // Upload.
506             for (std::vector<glu::TextureCube *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
507                 (*i)->upload();
508         }
509 
510         // Compute cases
511         {
512             const glu::TextureCube *tex0 = m_textures[0];
513             const glu::TextureCube *tex1 = m_textures.size() > 1 ? m_textures[1] : tex0;
514 
515             // \note Coordinates are chosen so that they only sample face interior. ES3 has changed edge sampling behavior
516             //         and hw is not expected to implement both modes.
517             m_cases.push_back(FilterCase(tex0, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f))); // minification
518             m_cases.push_back(FilterCase(tex0, tcu::Vec2(0.5f, 0.65f), tcu::Vec2(0.8f, 0.8f)));  // magnification
519             m_cases.push_back(FilterCase(tex1, tcu::Vec2(-0.8f, -0.8f), tcu::Vec2(0.8f, 0.8f))); // minification
520             m_cases.push_back(FilterCase(tex1, tcu::Vec2(0.2f, 0.2f), tcu::Vec2(0.6f, 0.5f)));   // magnification
521         }
522 
523         m_caseNdx = 0;
524         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
525     }
526     catch (...)
527     {
528         // Clean up to save memory.
529         TextureCubeFilteringCase::deinit();
530         throw;
531     }
532 }
533 
deinit(void)534 void TextureCubeFilteringCase::deinit(void)
535 {
536     for (std::vector<glu::TextureCube *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
537         delete *i;
538     m_textures.clear();
539 
540     m_renderer.clear();
541     m_cases.clear();
542 }
543 
getFaceDesc(const tcu::CubeFace face)544 static const char *getFaceDesc(const tcu::CubeFace face)
545 {
546     switch (face)
547     {
548     case tcu::CUBEFACE_NEGATIVE_X:
549         return "-X";
550     case tcu::CUBEFACE_POSITIVE_X:
551         return "+X";
552     case tcu::CUBEFACE_NEGATIVE_Y:
553         return "-Y";
554     case tcu::CUBEFACE_POSITIVE_Y:
555         return "+Y";
556     case tcu::CUBEFACE_NEGATIVE_Z:
557         return "-Z";
558     case tcu::CUBEFACE_POSITIVE_Z:
559         return "+Z";
560     default:
561         DE_ASSERT(false);
562         return DE_NULL;
563     }
564 }
565 
iterate(void)566 TextureCubeFilteringCase::IterateResult TextureCubeFilteringCase::iterate(void)
567 {
568     const glw::Functions &gl = m_renderCtx.getFunctions();
569     const int viewportSize   = 28;
570     const RandomViewport viewport(m_renderCtx.getRenderTarget(), viewportSize, viewportSize,
571                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
572     const tcu::ScopedLogSection iterSection(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
573                                             string("Test ") + de::toString(m_caseNdx));
574     const FilterCase &curCase            = m_cases[m_caseNdx];
575     const tcu::TextureFormat &texFmt     = curCase.texture->getRefTexture().getFormat();
576     const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
577     ReferenceParams sampleParams(TEXTURETYPE_CUBE);
578 
579     if (viewport.width < viewportSize || viewport.height < viewportSize)
580         throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
581 
582     // Setup texture
583     gl.bindTexture(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
584     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, m_minFilter);
585     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, m_magFilter);
586     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, m_wrapS);
587     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, m_wrapT);
588 
589     // Other state
590     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
591 
592     // Params for reference computation.
593     sampleParams.sampler                 = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
594     sampleParams.sampler.seamlessCubeMap = true;
595     sampleParams.samplerType             = getSamplerType(texFmt);
596     sampleParams.colorBias               = fmtInfo.lookupBias;
597     sampleParams.colorScale              = fmtInfo.lookupScale;
598     sampleParams.lodMode                 = LODMODE_EXACT;
599 
600     m_testCtx.getLog() << TestLog::Message << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight
601                        << TestLog::EndMessage;
602 
603     for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
604     {
605         const tcu::CubeFace face = tcu::CubeFace(faceNdx);
606         tcu::Surface result(viewport.width, viewport.height);
607         vector<float> texCoord;
608 
609         computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
610 
611         m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
612 
613         // \todo Log texture coordinates.
614 
615         m_renderer.renderQuad(0, &texCoord[0], sampleParams);
616         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
617 
618         glu::readPixels(m_renderCtx, viewport.x, viewport.y, result.getAccess());
619         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
620 
621         {
622             const bool isNearestOnly           = m_minFilter == GL_NEAREST && m_magFilter == GL_NEAREST;
623             const tcu::PixelFormat pixelFormat = m_renderCtx.getRenderTarget().getPixelFormat();
624             const tcu::IVec4 colorBits         = max(getBitsVec(pixelFormat) - (isNearestOnly ? 1 : 2),
625                                                      tcu::IVec4(0)); // 1 inaccurate bit if nearest only, 2 otherwise
626             tcu::LodPrecision lodPrecision;
627             tcu::LookupPrecision lookupPrecision;
628 
629             lodPrecision.derivateBits      = 5;
630             lodPrecision.lodBits           = 3;
631             lookupPrecision.colorThreshold = tcu::computeFixedPointThreshold(colorBits) / sampleParams.colorScale;
632             lookupPrecision.coordBits      = tcu::IVec3(9, 9, 9); // mediump interpolation
633             lookupPrecision.uvwBits        = tcu::IVec3(3, 3, 0);
634             lookupPrecision.colorMask      = getCompareMask(pixelFormat);
635 
636             const bool isOk =
637                 verifyTextureResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
638                                     sampleParams, lookupPrecision, lodPrecision, pixelFormat);
639 
640             if (!isOk)
641                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
642         }
643     }
644 
645     m_caseNdx += 1;
646     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
647 }
648 
TextureFilteringTests(Context & context)649 TextureFilteringTests::TextureFilteringTests(Context &context)
650     : TestCaseGroup(context, "filtering", "Texture Filtering Tests")
651 {
652 }
653 
~TextureFilteringTests(void)654 TextureFilteringTests::~TextureFilteringTests(void)
655 {
656 }
657 
init(void)658 void TextureFilteringTests::init(void)
659 {
660     tcu::TestCaseGroup *group2D   = new tcu::TestCaseGroup(m_testCtx, "2d", "2D Texture Filtering");
661     tcu::TestCaseGroup *groupCube = new tcu::TestCaseGroup(m_testCtx, "cube", "Cube Map Filtering");
662     addChild(group2D);
663     addChild(groupCube);
664 
665     static const struct
666     {
667         const char *name;
668         uint32_t mode;
669     } wrapModes[] = {{"clamp", GL_CLAMP_TO_EDGE}, {"repeat", GL_REPEAT}, {"mirror", GL_MIRRORED_REPEAT}};
670 
671     static const struct
672     {
673         const char *name;
674         uint32_t mode;
675     } minFilterModes[] = {{"nearest", GL_NEAREST},
676                           {"linear", GL_LINEAR},
677                           {"nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST},
678                           {"linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST},
679                           {"nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR},
680                           {"linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR}};
681 
682     static const struct
683     {
684         const char *name;
685         uint32_t mode;
686     } magFilterModes[] = {{"nearest", GL_NEAREST}, {"linear", GL_LINEAR}};
687 
688     static const struct
689     {
690         const char *name;
691         int width;
692         int height;
693     } sizes2D[] = {{"pot", 32, 64}, {"npot", 31, 55}};
694 
695     static const struct
696     {
697         const char *name;
698         int width;
699         int height;
700     } sizesCube[] = {{"pot", 64, 64}, {"npot", 63, 63}};
701 
702     static const struct
703     {
704         const char *name;
705         uint32_t format;
706         uint32_t dataType;
707     } formats[] = {{"rgba8888", GL_RGBA, GL_UNSIGNED_BYTE},
708                    {"rgb888", GL_RGB, GL_UNSIGNED_BYTE},
709                    {"rgba4444", GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4},
710                    {"l8", GL_LUMINANCE, GL_UNSIGNED_BYTE}};
711 
712 #define FOR_EACH(ITERATOR, ARRAY, BODY)                                      \
713     for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++) \
714     BODY
715 
716     // 2D cases.
717     FOR_EACH(minFilter, minFilterModes,
718              FOR_EACH(magFilter, magFilterModes,
719                       FOR_EACH(wrapMode, wrapModes,
720                                FOR_EACH(format, formats, FOR_EACH(size, sizes2D, {
721                                             bool isMipmap = minFilterModes[minFilter].mode != GL_NEAREST &&
722                                                             minFilterModes[minFilter].mode != GL_LINEAR;
723                                             bool isClamp      = wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
724                                             bool isRepeat     = wrapModes[wrapMode].mode == GL_REPEAT;
725                                             bool isMagNearest = magFilterModes[magFilter].mode == GL_NEAREST;
726                                             bool isPotSize    = deIsPowerOfTwo32(sizes2D[size].width) &&
727                                                              deIsPowerOfTwo32(sizes2D[size].height);
728 
729                                             if ((isMipmap || !isClamp) && !isPotSize)
730                                                 continue; // Not supported.
731 
732                                             if ((format != 0) && !(!isMipmap || (isRepeat && isMagNearest)))
733                                                 continue; // Skip.
734 
735                                             string name = string("") + minFilterModes[minFilter].name + "_" +
736                                                           magFilterModes[magFilter].name + "_" +
737                                                           wrapModes[wrapMode].name + "_" + formats[format].name;
738 
739                                             if (!isMipmap)
740                                                 name += string("_") + sizes2D[size].name;
741 
742                                             group2D->addChild(new Texture2DFilteringCase(
743                                                 m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
744                                                 name.c_str(), "", minFilterModes[minFilter].mode,
745                                                 magFilterModes[magFilter].mode, wrapModes[wrapMode].mode,
746                                                 wrapModes[wrapMode].mode, formats[format].format,
747                                                 formats[format].dataType, sizes2D[size].width, sizes2D[size].height));
748                                         })))))
749 
750     // 2D ETC1 texture cases.
751     {
752         std::vector<std::string> filenames;
753         for (int i = 0; i <= 7; i++)
754             filenames.push_back(string("data/etc1/photo_helsinki_mip_") + de::toString(i) + ".pkm");
755 
756         FOR_EACH(minFilter, minFilterModes,
757                  FOR_EACH(magFilter, magFilterModes, FOR_EACH(wrapMode, wrapModes, {
758                               string name = string("") + minFilterModes[minFilter].name + "_" +
759                                             magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name + "_etc1";
760 
761                               group2D->addChild(new Texture2DFilteringCase(
762                                   m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
763                                   minFilterModes[minFilter].mode, magFilterModes[magFilter].mode,
764                                   wrapModes[wrapMode].mode, wrapModes[wrapMode].mode, filenames));
765                           })))
766     }
767 
768     // Cubemap cases.
769     FOR_EACH(minFilter, minFilterModes,
770              FOR_EACH(magFilter, magFilterModes,
771                       FOR_EACH(wrapMode, wrapModes,
772                                FOR_EACH(format, formats, FOR_EACH(size, sizesCube, {
773                                             bool isMipmap = minFilterModes[minFilter].mode != GL_NEAREST &&
774                                                             minFilterModes[minFilter].mode != GL_LINEAR;
775                                             bool isClamp      = wrapModes[wrapMode].mode == GL_CLAMP_TO_EDGE;
776                                             bool isRepeat     = wrapModes[wrapMode].mode == GL_REPEAT;
777                                             bool isMagNearest = magFilterModes[magFilter].mode == GL_NEAREST;
778                                             bool isPotSize    = deIsPowerOfTwo32(sizesCube[size].width) &&
779                                                              deIsPowerOfTwo32(sizesCube[size].height);
780 
781                                             if ((isMipmap || !isClamp) && !isPotSize)
782                                                 continue; // Not supported.
783 
784                                             if (format != 0 && !(!isMipmap || (isRepeat && isMagNearest)))
785                                                 continue; // Skip.
786 
787                                             string name = string("") + minFilterModes[minFilter].name + "_" +
788                                                           magFilterModes[magFilter].name + "_" +
789                                                           wrapModes[wrapMode].name + "_" + formats[format].name;
790 
791                                             if (!isMipmap)
792                                                 name += string("_") + sizesCube[size].name;
793 
794                                             groupCube->addChild(new TextureCubeFilteringCase(
795                                                 m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
796                                                 name.c_str(), "", minFilterModes[minFilter].mode,
797                                                 magFilterModes[magFilter].mode, wrapModes[wrapMode].mode,
798                                                 wrapModes[wrapMode].mode, formats[format].format,
799                                                 formats[format].dataType, sizesCube[size].width,
800                                                 sizesCube[size].height));
801                                         })))))
802 
803     // Cubemap ETC1 cases
804     {
805         static const char *faceExt[] = {"neg_x", "pos_x", "neg_y", "pos_y", "neg_z", "pos_z"};
806 
807         const int numLevels = 7;
808         vector<string> filenames;
809         for (int level = 0; level < numLevels; level++)
810             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
811                 filenames.push_back(string("data/etc1/skybox_") + faceExt[face] + "_mip_" + de::toString(level) +
812                                     ".pkm");
813 
814         FOR_EACH(minFilter, minFilterModes, FOR_EACH(magFilter, magFilterModes, {
815                      string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name +
816                                    "_clamp_etc1";
817 
818                      groupCube->addChild(new TextureCubeFilteringCase(
819                          m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(), name.c_str(), "",
820                          minFilterModes[minFilter].mode, magFilterModes[magFilter].mode, GL_CLAMP_TO_EDGE,
821                          GL_CLAMP_TO_EDGE, filenames));
822                  }))
823     }
824 }
825 
826 } // namespace Functional
827 } // namespace gles2
828 } // namespace deqp
829