xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fTextureShadowTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Shadow texture lookup tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fTextureShadowTests.hpp"
25 #include "gluTexture.hpp"
26 #include "gluPixelTransfer.hpp"
27 #include "gluTextureUtil.hpp"
28 #include "glsTextureTestUtil.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "tcuRenderTarget.hpp"
31 #include "tcuTexCompareVerifier.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "deString.h"
34 #include "deMath.h"
35 #include "deStringUtil.hpp"
36 #include "glwFunctions.hpp"
37 #include "glwEnums.hpp"
38 
39 namespace deqp
40 {
41 namespace gles3
42 {
43 namespace Functional
44 {
45 
46 using std::string;
47 using std::vector;
48 using tcu::TestLog;
49 using namespace deqp::gls::TextureTestUtil;
50 using namespace glu::TextureTestUtil;
51 
52 enum
53 {
54     TEX2D_VIEWPORT_WIDTH      = 64,
55     TEX2D_VIEWPORT_HEIGHT     = 64,
56     TEX2D_MIN_VIEWPORT_WIDTH  = 64,
57     TEX2D_MIN_VIEWPORT_HEIGHT = 64
58 };
59 
isFloatingPointDepthFormat(const tcu::TextureFormat & format)60 static bool isFloatingPointDepthFormat(const tcu::TextureFormat &format)
61 {
62     // Only two depth and depth-stencil formats are floating point
63     return (format.order == tcu::TextureFormat::D && format.type == tcu::TextureFormat::FLOAT) ||
64            (format.order == tcu::TextureFormat::DS && format.type == tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV);
65 }
66 
clampFloatingPointTexture(const tcu::PixelBufferAccess & access)67 static void clampFloatingPointTexture(const tcu::PixelBufferAccess &access)
68 {
69     DE_ASSERT(isFloatingPointDepthFormat(access.getFormat()));
70 
71     for (int z = 0; z < access.getDepth(); ++z)
72         for (int y = 0; y < access.getHeight(); ++y)
73             for (int x = 0; x < access.getWidth(); ++x)
74                 access.setPixDepth(de::clamp(access.getPixDepth(x, y, z), 0.0f, 1.0f), x, y, z);
75 }
76 
clampFloatingPointTexture(tcu::Texture2D & target)77 static void clampFloatingPointTexture(tcu::Texture2D &target)
78 {
79     for (int level = 0; level < target.getNumLevels(); ++level)
80         if (!target.isLevelEmpty(level))
81             clampFloatingPointTexture(target.getLevel(level));
82 }
83 
clampFloatingPointTexture(tcu::Texture2DArray & target)84 static void clampFloatingPointTexture(tcu::Texture2DArray &target)
85 {
86     for (int level = 0; level < target.getNumLevels(); ++level)
87         if (!target.isLevelEmpty(level))
88             clampFloatingPointTexture(target.getLevel(level));
89 }
90 
clampFloatingPointTexture(tcu::TextureCube & target)91 static void clampFloatingPointTexture(tcu::TextureCube &target)
92 {
93     for (int level = 0; level < target.getNumLevels(); ++level)
94         for (int face = tcu::CUBEFACE_NEGATIVE_X; face < tcu::CUBEFACE_LAST; ++face)
95             clampFloatingPointTexture(target.getLevelFace(level, (tcu::CubeFace)face));
96 }
97 
98 template <typename TextureType>
verifyTexCompareResult(tcu::TestContext & testCtx,const tcu::ConstPixelBufferAccess & result,const TextureType & src,const float * texCoord,const ReferenceParams & sampleParams,const tcu::TexComparePrecision & comparePrec,const tcu::LodPrecision & lodPrec,const tcu::PixelFormat & pixelFormat)99 bool verifyTexCompareResult(tcu::TestContext &testCtx, const tcu::ConstPixelBufferAccess &result,
100                             const TextureType &src, const float *texCoord, const ReferenceParams &sampleParams,
101                             const tcu::TexComparePrecision &comparePrec, const tcu::LodPrecision &lodPrec,
102                             const tcu::PixelFormat &pixelFormat)
103 {
104     tcu::TestLog &log = testCtx.getLog();
105     tcu::Surface reference(result.getWidth(), result.getHeight());
106     tcu::Surface errorMask(result.getWidth(), result.getHeight());
107     const tcu::IVec4 nonShadowBits     = tcu::max(getBitsVec(pixelFormat) - 1, tcu::IVec4(0));
108     const tcu::Vec3 nonShadowThreshold = tcu::computeFixedPointThreshold(nonShadowBits).swizzle(1, 2, 3);
109     int numFailedPixels;
110 
111     // sampleTexture() expects source image to be the same state as it would be in a GL implementation, that is
112     // the floating point depth values should be in [0, 1] range as data is clamped during texture upload. Since
113     // we don't have a separate "uploading" phase and just reuse the buffer we used for GL-upload, do the clamping
114     // here if necessary.
115 
116     if (isFloatingPointDepthFormat(src.getFormat()))
117     {
118         TextureType clampedSource(src);
119 
120         clampFloatingPointTexture(clampedSource);
121 
122         // sample clamped values
123 
124         sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), clampedSource, texCoord, sampleParams);
125         numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), clampedSource,
126                                                     texCoord, sampleParams, comparePrec, lodPrec, nonShadowThreshold);
127     }
128     else
129     {
130         // sample raw values (they are guaranteed to be in [0, 1] range as the format cannot represent any other values)
131 
132         sampleTexture(tcu::SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
133         numFailedPixels = computeTextureCompareDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord,
134                                                     sampleParams, comparePrec, lodPrec, nonShadowThreshold);
135     }
136 
137     if (numFailedPixels > 0)
138         log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!"
139             << TestLog::EndMessage;
140 
141     log << TestLog::ImageSet("VerifyResult", "Verification result")
142         << TestLog::Image("Rendered", "Rendered image", result);
143 
144     if (numFailedPixels > 0)
145     {
146         log << TestLog::Image("Reference", "Ideal reference image", reference)
147             << TestLog::Image("ErrorMask", "Error mask", errorMask);
148     }
149 
150     log << TestLog::EndImageSet;
151 
152     return numFailedPixels == 0;
153 }
154 
155 class Texture2DShadowCase : public TestCase
156 {
157 public:
158     Texture2DShadowCase(Context &context, const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter,
159                         uint32_t wrapS, uint32_t wrapT, uint32_t format, int width, int height, uint32_t compareFunc);
160     ~Texture2DShadowCase(void);
161 
162     void init(void);
163     void deinit(void);
164     IterateResult iterate(void);
165 
166 private:
167     Texture2DShadowCase(const Texture2DShadowCase &other);
168     Texture2DShadowCase &operator=(const Texture2DShadowCase &other);
169 
170     const uint32_t m_minFilter;
171     const uint32_t m_magFilter;
172     const uint32_t m_wrapS;
173     const uint32_t m_wrapT;
174     const uint32_t m_format;
175     const int m_width;
176     const int m_height;
177     const uint32_t m_compareFunc;
178 
179     struct FilterCase
180     {
181         const glu::Texture2D *texture;
182         tcu::Vec2 minCoord;
183         tcu::Vec2 maxCoord;
184         float ref;
185 
FilterCasedeqp::gles3::Functional::Texture2DShadowCase::FilterCase186         FilterCase(void) : texture(DE_NULL), ref(0.0f)
187         {
188         }
189 
FilterCasedeqp::gles3::Functional::Texture2DShadowCase::FilterCase190         FilterCase(const glu::Texture2D *tex_, const float ref_, const tcu::Vec2 &minCoord_, const tcu::Vec2 &maxCoord_)
191             : texture(tex_)
192             , minCoord(minCoord_)
193             , maxCoord(maxCoord_)
194             , ref(ref_)
195         {
196         }
197     };
198 
199     std::vector<glu::Texture2D *> m_textures;
200     std::vector<FilterCase> m_cases;
201 
202     TextureRenderer m_renderer;
203 
204     int m_caseNdx;
205 };
206 
Texture2DShadowCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t format,int width,int height,uint32_t compareFunc)207 Texture2DShadowCase::Texture2DShadowCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
208                                          uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t format, int width,
209                                          int height, uint32_t compareFunc)
210     : TestCase(context, name, desc)
211     , m_minFilter(minFilter)
212     , m_magFilter(magFilter)
213     , m_wrapS(wrapS)
214     , m_wrapT(wrapT)
215     , m_format(format)
216     , m_width(width)
217     , m_height(height)
218     , m_compareFunc(compareFunc)
219     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES,
220                  glu::PRECISION_HIGHP)
221     , m_caseNdx(0)
222 {
223 }
224 
~Texture2DShadowCase(void)225 Texture2DShadowCase::~Texture2DShadowCase(void)
226 {
227     deinit();
228 }
229 
init(void)230 void Texture2DShadowCase::init(void)
231 {
232     try
233     {
234         // Create 2 textures.
235         m_textures.reserve(2);
236         m_textures.push_back(new glu::Texture2D(m_context.getRenderContext(), m_format, m_width, m_height));
237         m_textures.push_back(new glu::Texture2D(m_context.getRenderContext(), m_format, m_width, m_height));
238 
239         int numLevels = m_textures[0]->getRefTexture().getNumLevels();
240 
241         // Fill first gradient texture.
242         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
243         {
244             m_textures[0]->getRefTexture().allocLevel(levelNdx);
245             tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx),
246                                             tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f));
247         }
248 
249         // Fill second with grid texture.
250         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
251         {
252             uint32_t step   = 0x00ffffff / numLevels;
253             uint32_t rgb    = step * levelNdx;
254             uint32_t colorA = 0xff000000 | rgb;
255             uint32_t colorB = 0xff000000 | ~rgb;
256 
257             m_textures[1]->getRefTexture().allocLevel(levelNdx);
258             tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec(),
259                               tcu::RGBA(colorB).toVec());
260         }
261 
262         // Upload.
263         for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
264             (*i)->upload();
265     }
266     catch (const std::exception &)
267     {
268         // Clean up to save memory.
269         Texture2DShadowCase::deinit();
270         throw;
271     }
272 
273     // Compute cases.
274     {
275         const float refInRangeUpper     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
276         const float refInRangeLower     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
277         const float refOutOfBoundsUpper = 1.1f; // !< lookup function should clamp values to [0, 1] range
278         const float refOutOfBoundsLower = -0.1f;
279 
280         const struct
281         {
282             int texNdx;
283             float ref;
284             float lodX;
285             float lodY;
286             float oX;
287             float oY;
288         } cases[] = {
289             {0, refInRangeUpper, 1.6f, 2.9f, -1.0f, -2.7f},
290             {0, refInRangeLower, -2.0f, -1.35f, -0.2f, 0.7f},
291             {1, refInRangeUpper, 0.14f, 0.275f, -1.5f, -1.1f},
292             {1, refInRangeLower, -0.92f, -2.64f, 0.4f, -0.1f},
293             {1, refOutOfBoundsUpper, -0.39f, -0.52f, 0.65f, 0.87f},
294             {1, refOutOfBoundsLower, -1.55f, 0.65f, 0.35f, 0.91f},
295         };
296 
297         const float viewportW = (float)de::min<int>(TEX2D_VIEWPORT_WIDTH, m_context.getRenderTarget().getWidth());
298         const float viewportH = (float)de::min<int>(TEX2D_VIEWPORT_HEIGHT, m_context.getRenderTarget().getHeight());
299 
300         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
301         {
302             const int texNdx = de::clamp(cases[caseNdx].texNdx, 0, (int)m_textures.size() - 1);
303             const float ref  = cases[caseNdx].ref;
304             const float lodX = cases[caseNdx].lodX;
305             const float lodY = cases[caseNdx].lodY;
306             const float oX   = cases[caseNdx].oX;
307             const float oY   = cases[caseNdx].oY;
308             const float sX   = deFloatExp2(lodX) * viewportW / float(m_textures[texNdx]->getRefTexture().getWidth());
309             const float sY   = deFloatExp2(lodY) * viewportH / float(m_textures[texNdx]->getRefTexture().getHeight());
310 
311             m_cases.push_back(FilterCase(m_textures[texNdx], ref, tcu::Vec2(oX, oY), tcu::Vec2(oX + sX, oY + sY)));
312         }
313     }
314 
315     m_caseNdx = 0;
316     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
317 }
318 
deinit(void)319 void Texture2DShadowCase::deinit(void)
320 {
321     for (std::vector<glu::Texture2D *>::iterator i = m_textures.begin(); i != m_textures.end(); i++)
322         delete *i;
323     m_textures.clear();
324 
325     m_renderer.clear();
326     m_cases.clear();
327 }
328 
iterate(void)329 Texture2DShadowCase::IterateResult Texture2DShadowCase::iterate(void)
330 {
331     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
332     const RandomViewport viewport(m_context.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT,
333                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
334     const FilterCase &curCase = m_cases[m_caseNdx];
335     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
336                                         string("Test ") + de::toString(m_caseNdx));
337     ReferenceParams sampleParams(TEXTURETYPE_2D);
338     tcu::Surface rendered(viewport.width, viewport.height);
339     vector<float> texCoord;
340 
341     if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
342         throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
343 
344     // Setup params for reference.
345     sampleParams.sampler         = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
346     sampleParams.sampler.compare = glu::mapGLCompareFunc(m_compareFunc);
347     sampleParams.samplerType     = SAMPLERTYPE_SHADOW;
348     sampleParams.lodMode         = LODMODE_EXACT;
349     sampleParams.ref             = curCase.ref;
350 
351     m_testCtx.getLog() << TestLog::Message << "Compare reference value =  " << sampleParams.ref << TestLog::EndMessage;
352 
353     // Compute texture coordinates.
354     m_testCtx.getLog() << TestLog::Message << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
355                        << TestLog::EndMessage;
356     computeQuadTexCoord2D(texCoord, curCase.minCoord, curCase.maxCoord);
357 
358     gl.bindTexture(GL_TEXTURE_2D, curCase.texture->getGLTexture());
359     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
360     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
361     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
362     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
363     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
364     gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, m_compareFunc);
365 
366     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
367     m_renderer.renderQuad(0, &texCoord[0], sampleParams);
368     glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
369 
370     {
371         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
372         tcu::LodPrecision lodPrecision;
373         tcu::TexComparePrecision texComparePrecision;
374 
375         lodPrecision.derivateBits         = 18;
376         lodPrecision.lodBits              = 6;
377         texComparePrecision.coordBits     = tcu::IVec3(20, 20, 0);
378         texComparePrecision.uvwBits       = tcu::IVec3(7, 7, 0);
379         texComparePrecision.pcfBits       = 5;
380         texComparePrecision.referenceBits = 16;
381         texComparePrecision.resultBits    = pixelFormat.redBits - 1;
382 
383         const bool isHighQuality =
384             verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
385                                    sampleParams, texComparePrecision, lodPrecision, pixelFormat);
386 
387         if (!isHighQuality)
388         {
389             m_testCtx.getLog() << TestLog::Message
390                                << "Warning: Verification assuming high-quality PCF filtering failed."
391                                << TestLog::EndMessage;
392 
393             lodPrecision.lodBits        = 4;
394             texComparePrecision.uvwBits = tcu::IVec3(4, 4, 0);
395             texComparePrecision.pcfBits = 0;
396 
397             const bool isOk =
398                 verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
399                                        sampleParams, texComparePrecision, lodPrecision, pixelFormat);
400 
401             if (!isOk)
402             {
403                 m_testCtx.getLog()
404                     << TestLog::Message
405                     << "ERROR: Verification against low precision requirements failed, failing test case."
406                     << TestLog::EndMessage;
407                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
408             }
409             else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
410                 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
411         }
412     }
413 
414     m_caseNdx += 1;
415     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
416 }
417 
418 class TextureCubeShadowCase : public TestCase
419 {
420 public:
421     TextureCubeShadowCase(Context &context, const char *name, const char *desc, uint32_t minFilter, uint32_t magFilter,
422                           uint32_t wrapS, uint32_t wrapT, uint32_t format, int size, uint32_t compareFunc);
423     ~TextureCubeShadowCase(void);
424 
425     void init(void);
426     void deinit(void);
427     IterateResult iterate(void);
428 
429 private:
430     TextureCubeShadowCase(const TextureCubeShadowCase &other);
431     TextureCubeShadowCase &operator=(const TextureCubeShadowCase &other);
432 
433     const uint32_t m_minFilter;
434     const uint32_t m_magFilter;
435     const uint32_t m_wrapS;
436     const uint32_t m_wrapT;
437 
438     const uint32_t m_format;
439     const int m_size;
440 
441     const uint32_t m_compareFunc;
442 
443     struct FilterCase
444     {
445         const glu::TextureCube *texture;
446         tcu::Vec2 bottomLeft;
447         tcu::Vec2 topRight;
448         float ref;
449 
FilterCasedeqp::gles3::Functional::TextureCubeShadowCase::FilterCase450         FilterCase(void) : texture(DE_NULL), ref(0.0f)
451         {
452         }
453 
FilterCasedeqp::gles3::Functional::TextureCubeShadowCase::FilterCase454         FilterCase(const glu::TextureCube *tex_, const float ref_, const tcu::Vec2 &bottomLeft_,
455                    const tcu::Vec2 &topRight_)
456             : texture(tex_)
457             , bottomLeft(bottomLeft_)
458             , topRight(topRight_)
459             , ref(ref_)
460         {
461         }
462     };
463 
464     glu::TextureCube *m_gradientTex;
465     glu::TextureCube *m_gridTex;
466     std::vector<FilterCase> m_cases;
467 
468     TextureRenderer m_renderer;
469 
470     int m_caseNdx;
471 };
472 
TextureCubeShadowCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t format,int size,uint32_t compareFunc)473 TextureCubeShadowCase::TextureCubeShadowCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
474                                              uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t format,
475                                              int size, uint32_t compareFunc)
476     : TestCase(context, name, desc)
477     , m_minFilter(minFilter)
478     , m_magFilter(magFilter)
479     , m_wrapS(wrapS)
480     , m_wrapT(wrapT)
481     , m_format(format)
482     , m_size(size)
483     , m_compareFunc(compareFunc)
484     , m_gradientTex(DE_NULL)
485     , m_gridTex(DE_NULL)
486     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES,
487                  glu::PRECISION_HIGHP)
488     , m_caseNdx(0)
489 {
490 }
491 
~TextureCubeShadowCase(void)492 TextureCubeShadowCase::~TextureCubeShadowCase(void)
493 {
494     TextureCubeShadowCase::deinit();
495 }
496 
init(void)497 void TextureCubeShadowCase::init(void)
498 {
499     try
500     {
501         DE_ASSERT(!m_gradientTex && !m_gridTex);
502 
503         int numLevels                  = deLog2Floor32(m_size) + 1;
504         tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_format);
505         tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
506         tcu::Vec4 cBias                = fmtInfo.valueMin;
507         tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
508 
509         // Create textures.
510         m_gradientTex = new glu::TextureCube(m_context.getRenderContext(), m_format, m_size);
511         m_gridTex     = new glu::TextureCube(m_context.getRenderContext(), m_format, m_size);
512 
513         // Fill first with gradient texture.
514         static const tcu::Vec4 gradients[tcu::CUBEFACE_LAST][2] = {
515             {tcu::Vec4(-1.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}, // negative x
516             {tcu::Vec4(0.0f, -1.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // positive x
517             {tcu::Vec4(-1.0f, 0.0f, -1.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // negative y
518             {tcu::Vec4(-1.0f, -1.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)},  // positive y
519             {tcu::Vec4(-1.0f, -1.0f, -1.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f)}, // negative z
520             {tcu::Vec4(0.0f, 0.0f, 0.0f, 2.0f), tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)}     // positive z
521         };
522         for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
523         {
524             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
525             {
526                 m_gradientTex->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
527                 tcu::fillWithComponentGradients(
528                     m_gradientTex->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face),
529                     gradients[face][0] * cScale + cBias, gradients[face][1] * cScale + cBias);
530             }
531         }
532 
533         // Fill second with grid texture.
534         for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
535         {
536             for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
537             {
538                 uint32_t step   = 0x00ffffff / (numLevels * tcu::CUBEFACE_LAST);
539                 uint32_t rgb    = step * levelNdx * face;
540                 uint32_t colorA = 0xff000000 | rgb;
541                 uint32_t colorB = 0xff000000 | ~rgb;
542 
543                 m_gridTex->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
544                 tcu::fillWithGrid(m_gridTex->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4,
545                                   tcu::RGBA(colorA).toVec() * cScale + cBias,
546                                   tcu::RGBA(colorB).toVec() * cScale + cBias);
547             }
548         }
549 
550         // Upload.
551         m_gradientTex->upload();
552         m_gridTex->upload();
553     }
554     catch (const std::exception &)
555     {
556         // Clean up to save memory.
557         TextureCubeShadowCase::deinit();
558         throw;
559     }
560 
561     // Compute cases
562     {
563         const float refInRangeUpper     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
564         const float refInRangeLower     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
565         const float refOutOfBoundsUpper = 1.1f;
566         const float refOutOfBoundsLower = -0.1f;
567         const bool singleSample         = m_context.getRenderTarget().getNumSamples() == 0;
568 
569         if (singleSample)
570             m_cases.push_back(FilterCase(m_gradientTex, refInRangeUpper, tcu::Vec2(-1.25f, -1.2f),
571                                          tcu::Vec2(1.2f, 1.25f))); // minification
572         else
573             m_cases.push_back(FilterCase(
574                 m_gradientTex, refInRangeUpper, tcu::Vec2(-1.19f, -1.3f),
575                 tcu::Vec2(1.1f, 1.35f))); // minification - w/ tuned coordinates to avoid hitting triangle edges
576 
577         m_cases.push_back(FilterCase(m_gradientTex, refInRangeLower, tcu::Vec2(0.8f, 0.8f),
578                                      tcu::Vec2(1.25f, 1.20f))); // magnification
579         m_cases.push_back(
580             FilterCase(m_gridTex, refInRangeUpper, tcu::Vec2(-1.19f, -1.3f), tcu::Vec2(1.1f, 1.35f))); // minification
581         m_cases.push_back(
582             FilterCase(m_gridTex, refInRangeLower, tcu::Vec2(-1.2f, -1.1f), tcu::Vec2(-0.8f, -0.8f))); // magnification
583         m_cases.push_back(FilterCase(m_gridTex, refOutOfBoundsUpper, tcu::Vec2(-0.61f, -0.1f),
584                                      tcu::Vec2(0.9f, 1.18f))); // reference value clamp, upper
585 
586         if (singleSample)
587             m_cases.push_back(FilterCase(m_gridTex, refOutOfBoundsLower, tcu::Vec2(-0.75f, 1.0f),
588                                          tcu::Vec2(0.05f, 0.75f))); // reference value clamp, lower
589         else
590             m_cases.push_back(FilterCase(m_gridTex, refOutOfBoundsLower, tcu::Vec2(-0.75f, 1.0f),
591                                          tcu::Vec2(0.25f, 0.75f))); // reference value clamp, lower
592     }
593 
594     m_caseNdx = 0;
595     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
596 }
597 
deinit(void)598 void TextureCubeShadowCase::deinit(void)
599 {
600     delete m_gradientTex;
601     delete m_gridTex;
602 
603     m_gradientTex = DE_NULL;
604     m_gridTex     = DE_NULL;
605 
606     m_renderer.clear();
607     m_cases.clear();
608 }
609 
getFaceDesc(const tcu::CubeFace face)610 static const char *getFaceDesc(const tcu::CubeFace face)
611 {
612     switch (face)
613     {
614     case tcu::CUBEFACE_NEGATIVE_X:
615         return "-X";
616     case tcu::CUBEFACE_POSITIVE_X:
617         return "+X";
618     case tcu::CUBEFACE_NEGATIVE_Y:
619         return "-Y";
620     case tcu::CUBEFACE_POSITIVE_Y:
621         return "+Y";
622     case tcu::CUBEFACE_NEGATIVE_Z:
623         return "-Z";
624     case tcu::CUBEFACE_POSITIVE_Z:
625         return "+Z";
626     default:
627         DE_ASSERT(false);
628         return DE_NULL;
629     }
630 }
631 
iterate(void)632 TextureCubeShadowCase::IterateResult TextureCubeShadowCase::iterate(void)
633 {
634     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
635     const int viewportSize   = 28;
636     const RandomViewport viewport(m_context.getRenderTarget(), viewportSize, viewportSize,
637                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
638     const tcu::ScopedLogSection iterSection(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
639                                             string("Test ") + de::toString(m_caseNdx));
640     const FilterCase &curCase = m_cases[m_caseNdx];
641     ReferenceParams sampleParams(TEXTURETYPE_CUBE);
642 
643     if (viewport.width < viewportSize || viewport.height < viewportSize)
644         throw tcu::NotSupportedError("Too small render target", DE_NULL, __FILE__, __LINE__);
645 
646     // Setup texture
647     gl.bindTexture(GL_TEXTURE_CUBE_MAP, curCase.texture->getGLTexture());
648     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, m_minFilter);
649     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, m_magFilter);
650     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, m_wrapS);
651     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, m_wrapT);
652     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
653     gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, m_compareFunc);
654 
655     // Other state
656     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
657 
658     // Params for reference computation.
659     sampleParams.sampler = glu::mapGLSampler(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, m_minFilter, m_magFilter);
660     sampleParams.sampler.seamlessCubeMap = true;
661     sampleParams.sampler.compare         = glu::mapGLCompareFunc(m_compareFunc);
662     sampleParams.samplerType             = SAMPLERTYPE_SHADOW;
663     sampleParams.lodMode                 = LODMODE_EXACT;
664     sampleParams.ref                     = curCase.ref;
665 
666     m_testCtx.getLog() << TestLog::Message << "Compare reference value =  " << sampleParams.ref << "\n"
667                        << "Coordinates: " << curCase.bottomLeft << " -> " << curCase.topRight << TestLog::EndMessage;
668 
669     for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
670     {
671         const tcu::CubeFace face = tcu::CubeFace(faceNdx);
672         tcu::Surface result(viewport.width, viewport.height);
673         vector<float> texCoord;
674 
675         computeQuadTexCoordCube(texCoord, face, curCase.bottomLeft, curCase.topRight);
676 
677         m_testCtx.getLog() << TestLog::Message << "Face " << getFaceDesc(face) << TestLog::EndMessage;
678 
679         // \todo Log texture coordinates.
680 
681         m_renderer.renderQuad(0, &texCoord[0], sampleParams);
682         GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
683 
684         glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, result.getAccess());
685         GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
686 
687         {
688             const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
689             tcu::LodPrecision lodPrecision;
690             tcu::TexComparePrecision texComparePrecision;
691 
692             lodPrecision.derivateBits         = 10;
693             lodPrecision.lodBits              = 5;
694             texComparePrecision.coordBits     = tcu::IVec3(10, 10, 10);
695             texComparePrecision.uvwBits       = tcu::IVec3(6, 6, 0);
696             texComparePrecision.pcfBits       = 5;
697             texComparePrecision.referenceBits = 16;
698             texComparePrecision.resultBits    = pixelFormat.redBits - 1;
699 
700             const bool isHighQuality =
701                 verifyTexCompareResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
702                                        sampleParams, texComparePrecision, lodPrecision, pixelFormat);
703 
704             if (!isHighQuality)
705             {
706                 m_testCtx.getLog() << TestLog::Message
707                                    << "Warning: Verification assuming high-quality PCF filtering failed."
708                                    << TestLog::EndMessage;
709 
710                 lodPrecision.lodBits        = 4;
711                 texComparePrecision.uvwBits = tcu::IVec3(4, 4, 0);
712                 texComparePrecision.pcfBits = 0;
713 
714                 const bool isOk =
715                     verifyTexCompareResult(m_testCtx, result.getAccess(), curCase.texture->getRefTexture(),
716                                            &texCoord[0], sampleParams, texComparePrecision, lodPrecision, pixelFormat);
717 
718                 if (!isOk)
719                 {
720                     m_testCtx.getLog()
721                         << TestLog::Message
722                         << "ERROR: Verification against low precision requirements failed, failing test case."
723                         << TestLog::EndMessage;
724                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
725                 }
726                 else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
727                     m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
728             }
729         }
730     }
731 
732     m_caseNdx += 1;
733     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
734 }
735 
736 class Texture2DArrayShadowCase : public TestCase
737 {
738 public:
739     Texture2DArrayShadowCase(Context &context, const char *name, const char *desc, uint32_t minFilter,
740                              uint32_t magFilter, uint32_t wrapS, uint32_t wrapT, uint32_t format, int width, int height,
741                              int numLayers, uint32_t compareFunc);
742     ~Texture2DArrayShadowCase(void);
743 
744     void init(void);
745     void deinit(void);
746     IterateResult iterate(void);
747 
748 private:
749     Texture2DArrayShadowCase(const Texture2DArrayShadowCase &other);
750     Texture2DArrayShadowCase &operator=(const Texture2DArrayShadowCase &other);
751 
752     const uint32_t m_minFilter;
753     const uint32_t m_magFilter;
754     const uint32_t m_wrapS;
755     const uint32_t m_wrapT;
756 
757     const uint32_t m_format;
758     const int m_width;
759     const int m_height;
760     const int m_numLayers;
761 
762     const uint32_t m_compareFunc;
763 
764     struct FilterCase
765     {
766         const glu::Texture2DArray *texture;
767         tcu::Vec3 minCoord;
768         tcu::Vec3 maxCoord;
769         float ref;
770 
FilterCasedeqp::gles3::Functional::Texture2DArrayShadowCase::FilterCase771         FilterCase(void) : texture(DE_NULL), ref(0.0f)
772         {
773         }
774 
FilterCasedeqp::gles3::Functional::Texture2DArrayShadowCase::FilterCase775         FilterCase(const glu::Texture2DArray *tex_, float ref_, const tcu::Vec3 &minCoord_, const tcu::Vec3 &maxCoord_)
776             : texture(tex_)
777             , minCoord(minCoord_)
778             , maxCoord(maxCoord_)
779             , ref(ref_)
780         {
781         }
782     };
783 
784     glu::Texture2DArray *m_gradientTex;
785     glu::Texture2DArray *m_gridTex;
786     std::vector<FilterCase> m_cases;
787 
788     TextureRenderer m_renderer;
789 
790     int m_caseNdx;
791 };
792 
Texture2DArrayShadowCase(Context & context,const char * name,const char * desc,uint32_t minFilter,uint32_t magFilter,uint32_t wrapS,uint32_t wrapT,uint32_t format,int width,int height,int numLayers,uint32_t compareFunc)793 Texture2DArrayShadowCase::Texture2DArrayShadowCase(Context &context, const char *name, const char *desc,
794                                                    uint32_t minFilter, uint32_t magFilter, uint32_t wrapS,
795                                                    uint32_t wrapT, uint32_t format, int width, int height,
796                                                    int numLayers, uint32_t compareFunc)
797     : TestCase(context, name, desc)
798     , m_minFilter(minFilter)
799     , m_magFilter(magFilter)
800     , m_wrapS(wrapS)
801     , m_wrapT(wrapT)
802     , m_format(format)
803     , m_width(width)
804     , m_height(height)
805     , m_numLayers(numLayers)
806     , m_compareFunc(compareFunc)
807     , m_gradientTex(DE_NULL)
808     , m_gridTex(DE_NULL)
809     , m_renderer(context.getRenderContext(), context.getTestContext().getLog(), glu::GLSL_VERSION_300_ES,
810                  glu::PRECISION_HIGHP)
811     , m_caseNdx(0)
812 {
813 }
814 
~Texture2DArrayShadowCase(void)815 Texture2DArrayShadowCase::~Texture2DArrayShadowCase(void)
816 {
817     Texture2DArrayShadowCase::deinit();
818 }
819 
init(void)820 void Texture2DArrayShadowCase::init(void)
821 {
822     try
823     {
824         tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_format);
825         tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
826         tcu::Vec4 cScale               = fmtInfo.valueMax - fmtInfo.valueMin;
827         tcu::Vec4 cBias                = fmtInfo.valueMin;
828         int numLevels                  = deLog2Floor32(de::max(m_width, m_height)) + 1;
829 
830         // Create textures.
831         m_gradientTex = new glu::Texture2DArray(m_context.getRenderContext(), m_format, m_width, m_height, m_numLayers);
832         m_gridTex     = new glu::Texture2DArray(m_context.getRenderContext(), m_format, m_width, m_height, m_numLayers);
833 
834         // Fill first gradient texture.
835         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
836         {
837             tcu::Vec4 gMin = tcu::Vec4(-0.5f, -0.5f, -0.5f, 2.0f) * cScale + cBias;
838             tcu::Vec4 gMax = tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f) * cScale + cBias;
839 
840             m_gradientTex->getRefTexture().allocLevel(levelNdx);
841             tcu::fillWithComponentGradients(m_gradientTex->getRefTexture().getLevel(levelNdx), gMin, gMax);
842         }
843 
844         // Fill second with grid texture.
845         for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
846         {
847             uint32_t step   = 0x00ffffff / numLevels;
848             uint32_t rgb    = step * levelNdx;
849             uint32_t colorA = 0xff000000 | rgb;
850             uint32_t colorB = 0xff000000 | ~rgb;
851 
852             m_gridTex->getRefTexture().allocLevel(levelNdx);
853             tcu::fillWithGrid(m_gridTex->getRefTexture().getLevel(levelNdx), 4,
854                               tcu::RGBA(colorA).toVec() * cScale + cBias, tcu::RGBA(colorB).toVec() * cScale + cBias);
855         }
856 
857         // Upload.
858         m_gradientTex->upload();
859         m_gridTex->upload();
860     }
861     catch (...)
862     {
863         // Clean up to save memory.
864         Texture2DArrayShadowCase::deinit();
865         throw;
866     }
867 
868     // Compute cases.
869     {
870         const float refInRangeUpper     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 1.0f : 0.5f;
871         const float refInRangeLower     = (m_compareFunc == GL_EQUAL || m_compareFunc == GL_NOTEQUAL) ? 0.0f : 0.5f;
872         const float refOutOfBoundsUpper = 1.1f; // !< lookup function should clamp values to [0, 1] range
873         const float refOutOfBoundsLower = -0.1f;
874 
875         const struct
876         {
877             int texNdx;
878             float ref;
879             float lodX;
880             float lodY;
881             float oX;
882             float oY;
883         } cases[] = {
884             {0, refInRangeUpper, 1.6f, 2.9f, -1.0f, -2.7f},
885             {0, refInRangeLower, -2.0f, -1.35f, -0.2f, 0.7f},
886             {1, refInRangeUpper, 0.14f, 0.275f, -1.5f, -1.1f},
887             {1, refInRangeLower, -0.92f, -2.64f, 0.4f, -0.1f},
888             {1, refOutOfBoundsUpper, -0.49f, -0.22f, 0.45f, 0.97f},
889             {1, refOutOfBoundsLower, -0.85f, 0.75f, 0.25f, 0.61f},
890         };
891 
892         const float viewportW = (float)de::min<int>(TEX2D_VIEWPORT_WIDTH, m_context.getRenderTarget().getWidth());
893         const float viewportH = (float)de::min<int>(TEX2D_VIEWPORT_HEIGHT, m_context.getRenderTarget().getHeight());
894 
895         const float minLayer = -0.5f;
896         const float maxLayer = (float)m_numLayers;
897 
898         for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); caseNdx++)
899         {
900             const glu::Texture2DArray *tex = cases[caseNdx].texNdx > 0 ? m_gridTex : m_gradientTex;
901             const float ref                = cases[caseNdx].ref;
902             const float lodX               = cases[caseNdx].lodX;
903             const float lodY               = cases[caseNdx].lodY;
904             const float oX                 = cases[caseNdx].oX;
905             const float oY                 = cases[caseNdx].oY;
906             const float sX                 = deFloatExp2(lodX) * viewportW / float(tex->getRefTexture().getWidth());
907             const float sY                 = deFloatExp2(lodY) * viewportH / float(tex->getRefTexture().getHeight());
908 
909             m_cases.push_back(FilterCase(tex, ref, tcu::Vec3(oX, oY, minLayer), tcu::Vec3(oX + sX, oY + sY, maxLayer)));
910         }
911     }
912 
913     m_caseNdx = 0;
914     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
915 }
916 
deinit(void)917 void Texture2DArrayShadowCase::deinit(void)
918 {
919     delete m_gradientTex;
920     delete m_gridTex;
921 
922     m_gradientTex = DE_NULL;
923     m_gridTex     = DE_NULL;
924 
925     m_renderer.clear();
926     m_cases.clear();
927 }
928 
iterate(void)929 Texture2DArrayShadowCase::IterateResult Texture2DArrayShadowCase::iterate(void)
930 {
931     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
932     const RandomViewport viewport(m_context.getRenderTarget(), TEX2D_VIEWPORT_WIDTH, TEX2D_VIEWPORT_HEIGHT,
933                                   deStringHash(getName()) ^ deInt32Hash(m_caseNdx));
934     const FilterCase &curCase = m_cases[m_caseNdx];
935     const tcu::ScopedLogSection section(m_testCtx.getLog(), string("Test") + de::toString(m_caseNdx),
936                                         string("Test ") + de::toString(m_caseNdx));
937     ReferenceParams sampleParams(TEXTURETYPE_2D_ARRAY);
938     tcu::Surface rendered(viewport.width, viewport.height);
939 
940     const float texCoord[] = {
941         curCase.minCoord.x(), curCase.minCoord.y(), curCase.minCoord.z(),
942         curCase.minCoord.x(), curCase.maxCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
943         curCase.maxCoord.x(), curCase.minCoord.y(), (curCase.minCoord.z() + curCase.maxCoord.z()) / 2.0f,
944         curCase.maxCoord.x(), curCase.maxCoord.y(), curCase.maxCoord.z()};
945 
946     if (viewport.width < TEX2D_MIN_VIEWPORT_WIDTH || viewport.height < TEX2D_MIN_VIEWPORT_HEIGHT)
947         throw tcu::NotSupportedError("Too small render target", "", __FILE__, __LINE__);
948 
949     // Setup params for reference.
950     sampleParams.sampler         = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
951     sampleParams.sampler.compare = glu::mapGLCompareFunc(m_compareFunc);
952     sampleParams.samplerType     = SAMPLERTYPE_SHADOW;
953     sampleParams.lodMode         = LODMODE_EXACT;
954     sampleParams.ref             = curCase.ref;
955 
956     m_testCtx.getLog() << TestLog::Message << "Compare reference value =  " << sampleParams.ref << "\n"
957                        << "Texture coordinates: " << curCase.minCoord << " -> " << curCase.maxCoord
958                        << TestLog::EndMessage;
959 
960     gl.bindTexture(GL_TEXTURE_2D_ARRAY, curCase.texture->getGLTexture());
961     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, m_minFilter);
962     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, m_magFilter);
963     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, m_wrapS);
964     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, m_wrapT);
965     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
966     gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, m_compareFunc);
967 
968     gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
969     m_renderer.renderQuad(0, &texCoord[0], sampleParams);
970     glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, rendered.getAccess());
971 
972     {
973         const tcu::PixelFormat pixelFormat = m_context.getRenderTarget().getPixelFormat();
974         tcu::LodPrecision lodPrecision;
975         tcu::TexComparePrecision texComparePrecision;
976 
977         lodPrecision.derivateBits         = 18;
978         lodPrecision.lodBits              = 6;
979         texComparePrecision.coordBits     = tcu::IVec3(20, 20, 20);
980         texComparePrecision.uvwBits       = tcu::IVec3(7, 7, 7);
981         texComparePrecision.pcfBits       = 5;
982         texComparePrecision.referenceBits = 16;
983         texComparePrecision.resultBits    = pixelFormat.redBits - 1;
984 
985         const bool isHighQuality =
986             verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
987                                    sampleParams, texComparePrecision, lodPrecision, pixelFormat);
988 
989         if (!isHighQuality)
990         {
991             m_testCtx.getLog() << TestLog::Message
992                                << "Warning: Verification assuming high-quality PCF filtering failed."
993                                << TestLog::EndMessage;
994 
995             lodPrecision.lodBits        = 4;
996             texComparePrecision.uvwBits = tcu::IVec3(4, 4, 4);
997             texComparePrecision.pcfBits = 0;
998 
999             const bool isOk =
1000                 verifyTexCompareResult(m_testCtx, rendered.getAccess(), curCase.texture->getRefTexture(), &texCoord[0],
1001                                        sampleParams, texComparePrecision, lodPrecision, pixelFormat);
1002 
1003             if (!isOk)
1004             {
1005                 m_testCtx.getLog()
1006                     << TestLog::Message
1007                     << "ERROR: Verification against low precision requirements failed, failing test case."
1008                     << TestLog::EndMessage;
1009                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1010             }
1011             else if (m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
1012                 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Low-quality result");
1013         }
1014     }
1015 
1016     m_caseNdx += 1;
1017     return m_caseNdx < (int)m_cases.size() ? CONTINUE : STOP;
1018 }
1019 
TextureShadowTests(Context & context)1020 TextureShadowTests::TextureShadowTests(Context &context)
1021     : TestCaseGroup(context, "shadow", "Shadow texture lookup tests")
1022 {
1023 }
1024 
~TextureShadowTests(void)1025 TextureShadowTests::~TextureShadowTests(void)
1026 {
1027 }
1028 
init(void)1029 void TextureShadowTests::init(void)
1030 {
1031     static const struct
1032     {
1033         const char *name;
1034         uint32_t format;
1035     } formats[] = {{"depth_component16", GL_DEPTH_COMPONENT16},
1036                    {"depth_component32f", GL_DEPTH_COMPONENT32F},
1037                    {"depth24_stencil8", GL_DEPTH24_STENCIL8}};
1038 
1039     static const struct
1040     {
1041         const char *name;
1042         uint32_t minFilter;
1043         uint32_t magFilter;
1044     } filters[] = {{"nearest", GL_NEAREST, GL_NEAREST},
1045                    {"linear", GL_LINEAR, GL_LINEAR},
1046                    {"nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR},
1047                    {"linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
1048                    {"nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR},
1049                    {"linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}};
1050 
1051     static const struct
1052     {
1053         const char *name;
1054         uint32_t func;
1055     } compareFuncs[] = {
1056         {"less_or_equal", GL_LEQUAL}, {"greater_or_equal", GL_GEQUAL}, {"less", GL_LESS},     {"greater", GL_GREATER},
1057         {"equal", GL_EQUAL},          {"not_equal", GL_NOTEQUAL},      {"always", GL_ALWAYS}, {"never", GL_NEVER}};
1058 
1059     // 2D cases.
1060     {
1061         tcu::TestCaseGroup *group2D = new tcu::TestCaseGroup(m_testCtx, "2d", "2D texture shadow lookup tests");
1062         addChild(group2D);
1063 
1064         for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
1065         {
1066             tcu::TestCaseGroup *filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
1067             group2D->addChild(filterGroup);
1068 
1069             for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
1070             {
1071                 for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
1072                 {
1073                     uint32_t minFilter   = filters[filterNdx].minFilter;
1074                     uint32_t magFilter   = filters[filterNdx].magFilter;
1075                     uint32_t format      = formats[formatNdx].format;
1076                     uint32_t compareFunc = compareFuncs[compareNdx].func;
1077                     const uint32_t wrapS = GL_REPEAT;
1078                     const uint32_t wrapT = GL_REPEAT;
1079                     const int width      = 32;
1080                     const int height     = 64;
1081                     string name          = string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
1082 
1083                     filterGroup->addChild(new Texture2DShadowCase(m_context, name.c_str(), "", minFilter, magFilter,
1084                                                                   wrapS, wrapT, format, width, height, compareFunc));
1085                 }
1086             }
1087         }
1088     }
1089 
1090     // Cubemap cases.
1091     {
1092         tcu::TestCaseGroup *groupCube =
1093             new tcu::TestCaseGroup(m_testCtx, "cube", "Cube map texture shadow lookup tests");
1094         addChild(groupCube);
1095 
1096         for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
1097         {
1098             tcu::TestCaseGroup *filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
1099             groupCube->addChild(filterGroup);
1100 
1101             for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
1102             {
1103                 for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
1104                 {
1105                     uint32_t minFilter   = filters[filterNdx].minFilter;
1106                     uint32_t magFilter   = filters[filterNdx].magFilter;
1107                     uint32_t format      = formats[formatNdx].format;
1108                     uint32_t compareFunc = compareFuncs[compareNdx].func;
1109                     const uint32_t wrapS = GL_REPEAT;
1110                     const uint32_t wrapT = GL_REPEAT;
1111                     const int size       = 32;
1112                     string name          = string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
1113 
1114                     filterGroup->addChild(new TextureCubeShadowCase(m_context, name.c_str(), "", minFilter, magFilter,
1115                                                                     wrapS, wrapT, format, size, compareFunc));
1116                 }
1117             }
1118         }
1119     }
1120 
1121     // 2D array cases.
1122     {
1123         tcu::TestCaseGroup *group2DArray =
1124             new tcu::TestCaseGroup(m_testCtx, "2d_array", "2D texture array shadow lookup tests");
1125         addChild(group2DArray);
1126 
1127         for (int filterNdx = 0; filterNdx < DE_LENGTH_OF_ARRAY(filters); filterNdx++)
1128         {
1129             tcu::TestCaseGroup *filterGroup = new tcu::TestCaseGroup(m_testCtx, filters[filterNdx].name, "");
1130             group2DArray->addChild(filterGroup);
1131 
1132             for (int compareNdx = 0; compareNdx < DE_LENGTH_OF_ARRAY(compareFuncs); compareNdx++)
1133             {
1134                 for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++)
1135                 {
1136                     uint32_t minFilter   = filters[filterNdx].minFilter;
1137                     uint32_t magFilter   = filters[filterNdx].magFilter;
1138                     uint32_t format      = formats[formatNdx].format;
1139                     uint32_t compareFunc = compareFuncs[compareNdx].func;
1140                     const uint32_t wrapS = GL_REPEAT;
1141                     const uint32_t wrapT = GL_REPEAT;
1142                     const int width      = 32;
1143                     const int height     = 64;
1144                     const int numLayers  = 8;
1145                     string name          = string(compareFuncs[compareNdx].name) + "_" + formats[formatNdx].name;
1146 
1147                     filterGroup->addChild(new Texture2DArrayShadowCase(m_context, name.c_str(), "", minFilter,
1148                                                                        magFilter, wrapS, wrapT, format, width, height,
1149                                                                        numLayers, compareFunc));
1150                 }
1151             }
1152         }
1153     }
1154 }
1155 
1156 } // namespace Functional
1157 } // namespace gles3
1158 } // namespace deqp
1159