1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2019 The Khronos Group Inc.
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
22  */ /*-------------------------------------------------------------------*/
23 
24 /*!
25  * \file esextcTextureShadowLodFunctionsTest.cpp
26  * \brief EXT_texture_shadow_lod extension testing
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "esextcTextureShadowLodFunctionsTest.hpp"
30 #include "deMath.h"
31 #include "glcShaderLibrary.hpp"
32 #include "glcShaderRenderCase.hpp"
33 #include "glsTextureTestUtil.hpp"
34 #include "gluPixelTransfer.hpp"
35 #include "gluStrUtil.hpp"
36 #include "gluTexture.hpp"
37 #include "gluTextureUtil.hpp"
38 #include "glwFunctions.hpp"
39 #include "tcuMatrix.hpp"
40 #include "tcuMatrixUtil.hpp"
41 #include "tcuTestLog.hpp"
42 #include "tcuTextureUtil.hpp"
43 
44 #include <sstream>
45 
46 #include "glwEnums.hpp"
47 #include "glwFunctions.hpp"
48 
49 namespace deqp
50 {
51 namespace Functional
52 {
53 namespace
54 {
55 
56 using glu::TextureTestUtil::computeLodFromDerivates;
57 
58 enum Function
59 {
60     FUNCTION_TEXTURE = 0, //!< texture(), textureOffset()
61     FUNCTION_TEXTURELOD,  // ...
62     FUNCTION_LAST
63 };
64 
65 // For texture(..., [bias]) functions.
functionHasAutoLod(glu::ShaderType shaderType,Function function)66 inline bool functionHasAutoLod(glu::ShaderType shaderType, Function function)
67 {
68     return (shaderType == glu::SHADERTYPE_FRAGMENT) && (function == FUNCTION_TEXTURE);
69 }
70 
71 // For textureLod* functions.
functionHasLod(Function function)72 inline bool functionHasLod(Function function)
73 {
74     return function == FUNCTION_TEXTURELOD;
75 }
76 
77 struct TextureLookupSpec
78 {
79     // The texture function to use.
80     Function function;
81 
82     // Min/max texture coordinates.
83     tcu::Vec4 minCoord;
84     tcu::Vec4 maxCoord;
85 
86     // Bias
87     bool useBias;
88 
89     // Bias or Lod for *Lod* functions
90     float minLodBias;
91     float maxLodBias;
92 
93     // For *Offset variants
94     bool useOffset;
95     tcu::IVec3 offset;
96 
97     // Do we require an additional shadow ref "compare"
98     // parameter in the texture function's parameter list? (used for
99     // shadow cube array textures).
100     bool useSepRef;
101     float minSepRef;
102     float maxSepRef;
103 
TextureLookupSpecdeqp::Functional::__anon58642bf20111::TextureLookupSpec104     TextureLookupSpec(void)
105         : function(FUNCTION_LAST)
106         , minCoord(0.0f)
107         , maxCoord(1.0f)
108         , useBias(false)
109         , minLodBias(0.0f)
110         , maxLodBias(0.0f)
111         , useOffset(false)
112         , offset(0)
113         , useSepRef(false)
114         , minSepRef(0.0f)
115         , maxSepRef(0.0f)
116     {
117     }
118 
TextureLookupSpecdeqp::Functional::__anon58642bf20111::TextureLookupSpec119     TextureLookupSpec(Function function_, const tcu::Vec4 &minCoord_, const tcu::Vec4 &maxCoord_, bool useBias_,
120                       float minLodBias_, float maxLodBias_, bool useOffset_, const tcu::IVec3 &offset_, bool useSepRef_,
121                       float minSepRef_, float maxSepRef_)
122         : function(function_)
123         , minCoord(minCoord_)
124         , maxCoord(maxCoord_)
125         , useBias(useBias_)
126         , minLodBias(minLodBias_)
127         , maxLodBias(maxLodBias_)
128         , useOffset(useOffset_)
129         , offset(offset_)
130         , useSepRef(useSepRef_)
131         , minSepRef(minSepRef_)
132         , maxSepRef(maxSepRef_)
133     {
134     }
135 };
136 
137 // Only shadow texture types contained in EXT_texture_shadow_lod will be tested.
138 enum TextureType
139 {
140     TEXTURETYPE_2D,
141     TEXTURETYPE_CUBE_MAP,
142     TEXTURETYPE_CUBE_MAP_ARRAY,
143     TEXTURETYPE_2D_ARRAY,
144 
145     TEXTURETYPE_LAST
146 };
147 
148 struct TextureSpec
149 {
150     TextureType type; //!< Texture type (2D, cubemap, ...)
151     uint32_t format;  //!< Internal format.
152     int width;
153     int height;
154     int depth;
155     int numLevels;
156     tcu::Sampler sampler;
157 
TextureSpecdeqp::Functional::__anon58642bf20111::TextureSpec158     TextureSpec(void) : type(TEXTURETYPE_LAST), format(GL_NONE), width(0), height(0), depth(0), numLevels(0)
159     {
160     }
161 
TextureSpecdeqp::Functional::__anon58642bf20111::TextureSpec162     TextureSpec(TextureType type_, uint32_t format_, int width_, int height_, int depth_, int numLevels_,
163                 const tcu::Sampler &sampler_)
164         : type(type_)
165         , format(format_)
166         , width(width_)
167         , height(height_)
168         , depth(depth_)
169         , numLevels(numLevels_)
170         , sampler(sampler_)
171     {
172     }
173 };
174 
175 struct TexLookupParams
176 {
177     float lod;
178     tcu::IVec3 offset;
179     tcu::Vec4 scale;
180     tcu::Vec4 bias;
181 
TexLookupParamsdeqp::Functional::__anon58642bf20111::TexLookupParams182     TexLookupParams(void) : lod(0.0f), offset(0), scale(1.0f), bias(0.0f)
183     {
184     }
185 };
186 
187 } // namespace
188 
189 using tcu::IVec2;
190 using tcu::IVec3;
191 using tcu::IVec4;
192 using tcu::Vec2;
193 using tcu::Vec3;
194 using tcu::Vec4;
195 
196 static const glu::TextureTestUtil::LodMode DEFAULT_LOD_MODE = glu::TextureTestUtil::LODMODE_EXACT;
197 
198 typedef void (*TexEvalFunc)(ShaderEvalContext &c, const TexLookupParams &lookupParams);
199 
texture2DShadow(const ShaderEvalContext & c,float ref,float s,float t,float lod)200 inline float texture2DShadow(const ShaderEvalContext &c, float ref, float s, float t, float lod)
201 {
202     return c.textures[0].tex2D->sampleCompare(c.textures[0].sampler, ref, s, t, lod);
203 }
texture2DArrayShadow(const ShaderEvalContext & c,float ref,float s,float t,float r,float lod)204 inline float texture2DArrayShadow(const ShaderEvalContext &c, float ref, float s, float t, float r, float lod)
205 {
206     return c.textures[0].tex2DArray->sampleCompare(c.textures[0].sampler, ref, s, t, r, lod);
207 }
textureCubeShadow(const ShaderEvalContext & c,float ref,float s,float t,float r,float lod)208 inline float textureCubeShadow(const ShaderEvalContext &c, float ref, float s, float t, float r, float lod)
209 {
210     return c.textures[0].texCube->sampleCompare(c.textures[0].sampler, ref, s, t, r, lod);
211 }
textureCubeArrayShadow(const ShaderEvalContext & c,float ref,float s,float t,float r,float q,float lod)212 inline float textureCubeArrayShadow(const ShaderEvalContext &c, float ref, float s, float t, float r, float q,
213                                     float lod)
214 {
215     return c.textures[0].texCubeArray->sampleCompare(c.textures[0].sampler, ref, s, t, r, q, lod);
216 }
texture2DShadowOffset(const ShaderEvalContext & c,float ref,float s,float t,float lod,IVec2 offset)217 inline float texture2DShadowOffset(const ShaderEvalContext &c, float ref, float s, float t, float lod, IVec2 offset)
218 {
219     return c.textures[0].tex2D->sampleCompareOffset(c.textures[0].sampler, ref, s, t, lod, offset);
220 }
texture2DArrayShadowOffset(const ShaderEvalContext & c,float ref,float s,float t,float r,float lod,IVec2 offset)221 inline float texture2DArrayShadowOffset(const ShaderEvalContext &c, float ref, float s, float t, float r, float lod,
222                                         IVec2 offset)
223 {
224     return c.textures[0].tex2DArray->sampleCompareOffset(c.textures[0].sampler, ref, s, t, r, lod, offset);
225 }
226 
227 // Shadow evaluation functions
evalTexture2DArrayShadow(ShaderEvalContext & c,const TexLookupParams & p)228 static void evalTexture2DArrayShadow(ShaderEvalContext &c, const TexLookupParams &p)
229 {
230     c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod);
231 }
evalTexture2DArrayShadowBias(ShaderEvalContext & c,const TexLookupParams & p)232 static void evalTexture2DArrayShadowBias(ShaderEvalContext &c, const TexLookupParams &p)
233 {
234     c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x());
235 }
evalTexture2DArrayShadowOffset(ShaderEvalContext & c,const TexLookupParams & p)236 static void evalTexture2DArrayShadowOffset(ShaderEvalContext &c, const TexLookupParams &p)
237 {
238     c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod,
239                                              p.offset.swizzle(0, 1));
240 }
evalTexture2DArrayShadowOffsetBias(ShaderEvalContext & c,const TexLookupParams & p)241 static void evalTexture2DArrayShadowOffsetBias(ShaderEvalContext &c, const TexLookupParams &p)
242 {
243     c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x(),
244                                              p.offset.swizzle(0, 1));
245 }
246 
evalTexture2DArrayShadowLod(ShaderEvalContext & c,const TexLookupParams & p)247 static void evalTexture2DArrayShadowLod(ShaderEvalContext &c, const TexLookupParams &p)
248 {
249     (void)p;
250     c.color.x() = texture2DArrayShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x());
251 }
evalTexture2DArrayShadowLodOffset(ShaderEvalContext & c,const TexLookupParams & p)252 static void evalTexture2DArrayShadowLodOffset(ShaderEvalContext &c, const TexLookupParams &p)
253 {
254     c.color.x() = texture2DArrayShadowOffset(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x(),
255                                              p.offset.swizzle(0, 1));
256 }
257 
evalTextureCubeShadow(ShaderEvalContext & c,const TexLookupParams & p)258 static void evalTextureCubeShadow(ShaderEvalContext &c, const TexLookupParams &p)
259 {
260     c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod);
261 }
evalTextureCubeShadowBias(ShaderEvalContext & c,const TexLookupParams & p)262 static void evalTextureCubeShadowBias(ShaderEvalContext &c, const TexLookupParams &p)
263 {
264     c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), p.lod + c.in[1].x());
265 }
evalTextureCubeShadowLod(ShaderEvalContext & c,const TexLookupParams & p)266 static void evalTextureCubeShadowLod(ShaderEvalContext &c, const TexLookupParams &p)
267 {
268     (void)p;
269     c.color.x() = textureCubeShadow(c, c.in[0].w(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[1].x());
270 }
271 
evalTextureCubeArrayShadow(ShaderEvalContext & c,const TexLookupParams & p)272 static void evalTextureCubeArrayShadow(ShaderEvalContext &c, const TexLookupParams &p)
273 {
274     c.color.x() = textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), p.lod);
275 }
evalTextureCubeArrayShadowBias(ShaderEvalContext & c,const TexLookupParams & p)276 static void evalTextureCubeArrayShadowBias(ShaderEvalContext &c, const TexLookupParams &p)
277 {
278     c.color.x() =
279         textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), p.lod + c.in[1].x());
280 }
evalTextureCubeArrayShadowLod(ShaderEvalContext & c,const TexLookupParams & p)281 static void evalTextureCubeArrayShadowLod(ShaderEvalContext &c, const TexLookupParams &p)
282 {
283     (void)p;
284     c.color.x() =
285         textureCubeArrayShadow(c, c.in[1].y(), c.in[0].x(), c.in[0].y(), c.in[0].z(), c.in[0].w(), c.in[1].x());
286 }
287 
288 class TexLookupEvaluator : public ShaderEvaluator
289 {
290 public:
TexLookupEvaluator(TexEvalFunc evalFunc,const TexLookupParams & lookupParams)291     TexLookupEvaluator(TexEvalFunc evalFunc, const TexLookupParams &lookupParams)
292         : m_evalFunc(evalFunc)
293         , m_lookupParams(lookupParams)
294     {
295     }
296 
evaluate(ShaderEvalContext & ctx)297     virtual void evaluate(ShaderEvalContext &ctx)
298     {
299         m_evalFunc(ctx, m_lookupParams);
300     }
301 
302 private:
303     TexEvalFunc m_evalFunc;
304     const TexLookupParams &m_lookupParams;
305 };
306 
307 class TextureShadowLodTestCase : public ShaderRenderCase
308 {
309 public:
310     TextureShadowLodTestCase(Context &context, const char *name, const char *desc, const TextureLookupSpec &lookup,
311                              const TextureSpec &texture, TexEvalFunc evalFunc, bool isVertexCase);
312     ~TextureShadowLodTestCase(void);
313 
314     void init(void);
315     void deinit(void);
316 
317 protected:
318     void setupUniforms(uint32_t programID, const tcu::Vec4 &constCoords);
319 
320 private:
321     void initTexture(void);
322     void initShaderSources(void);
323 
324     TextureLookupSpec m_lookupSpec;
325     TextureSpec m_textureSpec;
326 
327     TexLookupParams m_lookupParams;
328     TexLookupEvaluator m_evaluator;
329 
330     glu::Texture2D *m_texture2D;
331     glu::TextureCube *m_textureCube;
332     glu::TextureCubeArray *m_textureCubeArray;
333     glu::Texture2DArray *m_texture2DArray;
334 };
335 
TextureShadowLodTestCase(Context & context,const char * name,const char * desc,const TextureLookupSpec & lookup,const TextureSpec & texture,TexEvalFunc evalFunc,bool isVertexCase)336 TextureShadowLodTestCase::TextureShadowLodTestCase(Context &context, const char *name, const char *desc,
337                                                    const TextureLookupSpec &lookup, const TextureSpec &texture,
338                                                    TexEvalFunc evalFunc, bool isVertexCase)
339     : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc,
340                        isVertexCase, m_evaluator)
341     , m_lookupSpec(lookup)
342     , m_textureSpec(texture)
343     , m_evaluator(evalFunc, m_lookupParams)
344     , m_texture2D(DE_NULL)
345     , m_textureCube(DE_NULL)
346     , m_textureCubeArray(DE_NULL)
347     , m_texture2DArray(DE_NULL)
348 {
349 }
350 
~TextureShadowLodTestCase(void)351 TextureShadowLodTestCase::~TextureShadowLodTestCase(void)
352 {
353     delete m_texture2D;
354     delete m_textureCube;
355     delete m_textureCubeArray;
356     delete m_texture2DArray;
357 }
358 
init(void)359 void TextureShadowLodTestCase::init(void)
360 {
361     // Check extension and other features are supported with this context/platform.
362     {
363         glu::ContextInfo *info = glu::ContextInfo::create(m_renderCtx);
364 
365         // First check if extension is available.
366         if (!info->isExtensionSupported("GL_EXT_texture_shadow_lod"))
367         {
368             throw tcu::NotSupportedError("EXT_texture_shadow_lod is not supported on the platform");
369         }
370 
371         // Check that API support and that the various texture_cube_map_array extension is supported based on GL / ES versions.
372 
373         if (glu::isContextTypeES(m_renderCtx.getType()))
374         {
375             // ES
376             if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 0)))
377             {
378                 throw tcu::NotSupportedError(
379                     "EXT_texture_shadow_lod is not supported due to minimum ES version requirements");
380             }
381 
382             // Check if cube map array is supported.  For ES, it is supported
383             // as of ES 3.2, or with OES_texture_cube_map_array for
384             // 3.1.
385             if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
386             {
387                 if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
388                     !(glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 1)) &&
389                       (info->isExtensionSupported("GL_OES_texture_cube_map_array") ||
390                        info->isExtensionSupported("GL_EXT_texture_cube_map_array"))))
391                 {
392                     throw tcu::NotSupportedError("GL_OES_texture_cube_map_array or GL_EXT_texture_cube_map_array is "
393                                                  "required for this configuration and is not available.");
394                 }
395             }
396         }
397         else
398         {
399             // GL
400             if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(2, 0)))
401             {
402                 throw tcu::NotSupportedError(
403                     "EXT_texture_shadow_lod is not supported due to minimum GL version requirements");
404             }
405 
406             // Check if cube map array is supported.  For GL, it is supported
407             // as of GL 4.0 core, or with ARB_texture_cube_map_array prior.
408             if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
409             {
410                 if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)) &&
411                     !info->isExtensionSupported("GL_ARB_texture_cube_map_array"))
412                 {
413                     throw tcu::NotSupportedError(
414                         "ARB_texture_cube_map_array is required for this configuration and is not available.");
415                 }
416             }
417         }
418     }
419 
420     // Set up the user attributes.
421     // The user attributes are set up as matrices where each row represents a component
422     // in the attribute transformed against the vertex's interpolated position across the grid.
423     {
424         // Base coord scale & bias
425         Vec4 s = m_lookupSpec.maxCoord - m_lookupSpec.minCoord;
426         Vec4 b = m_lookupSpec.minCoord;
427 
428         float baseCoordTrans[] = {s.x(),        0.0f,         0.f, b.x(),
429                                   0.f,          s.y(),        0.f, b.y(),
430                                   s.z() / 2.f,  -s.z() / 2.f, 0.f, s.z() / 2.f + b.z(),
431                                   -s.w() / 2.f, s.w() / 2.f,  0.f, s.w() / 2.f + b.w()};
432 
433         //a_in0
434         m_userAttribTransforms.push_back(tcu::Mat4(baseCoordTrans));
435     }
436 
437     bool hasLodBias = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
438 
439     if (hasLodBias || m_lookupSpec.useSepRef)
440     {
441         float s       = 0.0f;
442         float b       = 0.0f;
443         float sepRefS = 0.0f;
444         float sepRefB = 0.0f;
445 
446         if (hasLodBias)
447         {
448             s = m_lookupSpec.maxLodBias - m_lookupSpec.minLodBias;
449             b = m_lookupSpec.minLodBias;
450         }
451 
452         if (m_lookupSpec.useSepRef)
453         {
454             sepRefS = m_lookupSpec.maxSepRef - m_lookupSpec.minSepRef;
455             sepRefB = m_lookupSpec.minSepRef;
456         }
457         float lodCoordTrans[] = {s / 2.0f, s / 2.0f, 0.f,  b,    sepRefS / 2.0f, sepRefS / 2.0f, 0.0f, sepRefB,
458                                  0.0f,     0.0f,     0.0f, 0.0f, 0.0f,           0.0f,           0.0f, 0.0f};
459 
460         //a_in1
461         m_userAttribTransforms.push_back(tcu::Mat4(lodCoordTrans));
462     }
463 
464     initShaderSources();
465     initTexture();
466 
467     ShaderRenderCase::init();
468 }
469 
initTexture(void)470 void TextureShadowLodTestCase::initTexture(void)
471 {
472     static const IVec4 texCubeSwz[] = {IVec4(0, 0, 1, 1), IVec4(1, 1, 0, 0), IVec4(0, 1, 0, 1),
473                                        IVec4(1, 0, 1, 0), IVec4(0, 1, 1, 0), IVec4(1, 0, 0, 1)};
474     DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(texCubeSwz) == tcu::CUBEFACE_LAST);
475 
476     tcu::TextureFormat texFmt      = glu::mapGLInternalFormat(m_textureSpec.format);
477     tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFmt);
478     tcu::IVec2 viewportSize        = getViewportSize();
479     bool isAutoLod = functionHasAutoLod(m_isVertexCase ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT,
480                                         m_lookupSpec.function); // LOD can vary significantly
481 
482     switch (m_textureSpec.type)
483     {
484     case TEXTURETYPE_2D:
485     {
486         float levelStep  = isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
487         Vec4 cScale      = fmtInfo.valueMax - fmtInfo.valueMin;
488         Vec4 cBias       = fmtInfo.valueMin;
489         int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
490 
491         m_texture2D = new glu::Texture2D(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.height);
492         for (int level = 0; level < m_textureSpec.numLevels; level++)
493         {
494             float fA    = float(level) * levelStep;
495             float fB    = 1.0f - fA;
496             Vec4 colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
497             Vec4 colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
498 
499             m_texture2D->getRefTexture().allocLevel(level);
500             tcu::fillWithGrid(m_texture2D->getRefTexture().getLevel(level), de::max(1, baseCellSize >> level), colorA,
501                               colorB);
502         }
503         m_texture2D->upload();
504 
505         // Compute LOD.
506         float dudx =
507             (m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
508         float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
509                      (float)viewportSize[1];
510         m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
511 
512         // Append to texture list.
513         m_textures.push_back(TextureBinding(m_texture2D, m_textureSpec.sampler));
514         break;
515     }
516 
517     case TEXTURETYPE_2D_ARRAY:
518     {
519         float layerStep = 1.0f / (float)m_textureSpec.depth;
520         float levelStep =
521             isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
522         Vec4 cScale      = fmtInfo.valueMax - fmtInfo.valueMin;
523         Vec4 cBias       = fmtInfo.valueMin;
524         int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
525 
526         m_texture2DArray = new glu::Texture2DArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width,
527                                                    m_textureSpec.height, m_textureSpec.depth);
528         for (int level = 0; level < m_textureSpec.numLevels; level++)
529         {
530             m_texture2DArray->getRefTexture().allocLevel(level);
531             tcu::PixelBufferAccess levelAccess = m_texture2DArray->getRefTexture().getLevel(level);
532 
533             for (int layer = 0; layer < levelAccess.getDepth(); layer++)
534             {
535                 float fA    = (float)layer * layerStep + (float)level * levelStep;
536                 float fB    = 1.0f - fA;
537                 Vec4 colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
538                 Vec4 colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
539 
540                 tcu::fillWithGrid(
541                     tcu::getSubregion(levelAccess, 0, 0, layer, levelAccess.getWidth(), levelAccess.getHeight(), 1),
542                     de::max(1, baseCellSize >> level), colorA, colorB);
543             }
544         }
545         m_texture2DArray->upload();
546 
547         // Compute LOD.
548         float dudx =
549             (m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
550         float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
551                      (float)viewportSize[1];
552         m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
553 
554         // Append to texture list.
555         m_textures.push_back(TextureBinding(m_texture2DArray, m_textureSpec.sampler));
556         break;
557     }
558 
559     case TEXTURETYPE_CUBE_MAP:
560     {
561         float levelStep  = isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
562         Vec4 cScale      = fmtInfo.valueMax - fmtInfo.valueMin;
563         Vec4 cBias       = fmtInfo.valueMin;
564         Vec4 cCorner     = cBias + cScale * 0.5f;
565         int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
566 
567         DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
568         m_textureCube = new glu::TextureCube(m_renderCtx, m_textureSpec.format, m_textureSpec.width);
569         for (int level = 0; level < m_textureSpec.numLevels; level++)
570         {
571             float fA = float(level) * levelStep;
572             float fB = 1.0f - fA;
573             Vec2 f(fA, fB);
574 
575             for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
576             {
577                 const IVec4 &swzA = texCubeSwz[face];
578                 IVec4 swzB        = 1 - swzA;
579                 Vec4 colorA       = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
580                 Vec4 colorB       = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
581 
582                 m_textureCube->getRefTexture().allocLevel((tcu::CubeFace)face, level);
583                 {
584                     const tcu::PixelBufferAccess access =
585                         m_textureCube->getRefTexture().getLevelFace(level, (tcu::CubeFace)face);
586                     const int lastPix = access.getWidth() - 1;
587 
588                     tcu::fillWithGrid(access, de::max(1, baseCellSize >> level), colorA, colorB);
589 
590                     // Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
591                     access.setPixel(cCorner, 0, 0);
592                     access.setPixel(cCorner, 0, lastPix);
593                     access.setPixel(cCorner, lastPix, 0);
594                     access.setPixel(cCorner, lastPix, lastPix);
595                 }
596             }
597         }
598         m_textureCube->upload();
599 
600         // Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
601         DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
602         DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
603                   de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
604         DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
605                   de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
606 
607         tcu::CubeFaceFloatCoords c00 =
608             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
609         tcu::CubeFaceFloatCoords c10 =
610             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
611         tcu::CubeFaceFloatCoords c01 =
612             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
613         float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
614         float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
615 
616         m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
617 
618         m_textures.push_back(TextureBinding(m_textureCube, m_textureSpec.sampler));
619         break;
620     }
621 
622     case TEXTURETYPE_CUBE_MAP_ARRAY:
623     {
624         float layerStep = 1.0f / (float)m_textureSpec.depth;
625         float levelStep =
626             isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
627         Vec4 cScale      = fmtInfo.valueMax - fmtInfo.valueMin;
628         Vec4 cBias       = fmtInfo.valueMin;
629         Vec4 cCorner     = cBias + cScale * 0.5f;
630         int baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
631 
632         DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
633         // I think size here means width/height of cube tex
634         m_textureCubeArray =
635             new glu::TextureCubeArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.depth * 6);
636         // mipmap level
637         for (int level = 0; level < m_textureSpec.numLevels; level++)
638         {
639             m_textureCubeArray->getRefTexture().allocLevel(level);
640             tcu::PixelBufferAccess levelAccess = m_textureCubeArray->getRefTexture().getLevel(level);
641 
642             //array layer
643             DE_ASSERT((levelAccess.getDepth() % 6) == 0);
644 
645             for (int layer = 0; layer < levelAccess.getDepth() / 6; ++layer)
646             {
647                 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
648                 {
649                     float fA = (float)layer * layerStep + float(level) * levelStep;
650                     float fB = 1.0f - fA;
651                     Vec2 f(fA, fB);
652 
653                     const IVec4 &swzA = texCubeSwz[face];
654                     IVec4 swzB        = 1 - swzA;
655                     Vec4 colorA       = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
656                     Vec4 colorB       = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
657 
658                     {
659                         const tcu::PixelBufferAccess access = m_textureCubeArray->getRefTexture().getLevel(level);
660                         const int lastPix                   = access.getWidth() - 1;
661 
662                         int layerFaceNdx = face + layer * 6;
663 
664                         DE_ASSERT(levelAccess.getWidth() == levelAccess.getHeight());
665                         tcu::fillWithGrid(tcu::getSubregion(access, 0, 0, layerFaceNdx, levelAccess.getWidth(),
666                                                             levelAccess.getHeight(), 1),
667                                           de::max(1, baseCellSize >> level), colorA, colorB);
668 
669                         // Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
670                         access.setPixel(cCorner, 0, 0, layer);
671                         access.setPixel(cCorner, 0, lastPix, layer);
672                         access.setPixel(cCorner, lastPix, 0, layer);
673                         access.setPixel(cCorner, lastPix, lastPix, layer);
674                     }
675                 }
676             }
677         }
678         m_textureCubeArray->upload();
679 
680         // Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
681         DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
682         DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
683                   de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
684         DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
685                   de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
686 
687         tcu::CubeFaceFloatCoords c00 =
688             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
689         tcu::CubeFaceFloatCoords c10 =
690             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
691         tcu::CubeFaceFloatCoords c01 =
692             tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
693         float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
694         float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
695 
696         m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
697 
698         m_textures.push_back(TextureBinding(m_textureCubeArray, m_textureSpec.sampler));
699         break;
700     }
701 
702     default:
703         DE_ASSERT(false);
704     }
705 
706     // Set lookup scale & bias
707     m_lookupParams.scale  = fmtInfo.lookupScale;
708     m_lookupParams.bias   = fmtInfo.lookupBias;
709     m_lookupParams.offset = m_lookupSpec.offset;
710 }
711 
initShaderSources(void)712 void TextureShadowLodTestCase::initShaderSources(void)
713 {
714     Function function         = m_lookupSpec.function;
715     bool isVtxCase            = m_isVertexCase;
716     bool hasLodBias           = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
717     int texCoordComps         = m_textureSpec.type == TEXTURETYPE_2D ? 2 : 3;
718     int extraCoordComps       = 1; // For shadow ref
719     bool hasSepShadowRef      = m_lookupSpec.useSepRef;
720     glu::DataType coordType   = glu::getDataTypeFloatVec(texCoordComps + extraCoordComps);
721     glu::Precision coordPrec  = glu::PRECISION_HIGHP;
722     const char *coordTypeName = glu::getDataTypeName(coordType);
723     const char *coordPrecName = glu::getPrecisionName(coordPrec);
724     glu::DataType samplerType = glu::TYPE_LAST;
725     const char *baseFuncName  = DE_NULL;
726 
727     switch (m_textureSpec.type)
728     {
729     case TEXTURETYPE_2D:
730         samplerType = glu::TYPE_SAMPLER_2D_SHADOW;
731         break;
732     case TEXTURETYPE_CUBE_MAP:
733         samplerType = glu::TYPE_SAMPLER_CUBE_SHADOW;
734         break;
735     case TEXTURETYPE_CUBE_MAP_ARRAY:
736         samplerType = glu::TYPE_SAMPLER_CUBE_ARRAY_SHADOW;
737         break;
738     case TEXTURETYPE_2D_ARRAY:
739         samplerType = glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
740         break;
741     default:
742         DE_ASSERT(false);
743     }
744 
745     switch (m_lookupSpec.function)
746     {
747     case FUNCTION_TEXTURE:
748         baseFuncName = "texture";
749         break;
750     case FUNCTION_TEXTURELOD:
751         baseFuncName = "textureLod";
752         break;
753     default:
754         DE_ASSERT(false);
755     }
756 
757     bool isGL                    = glu::isContextTypeGLCore(m_renderCtx.getType());
758     glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_renderCtx.getType());
759 
760     std::string shaderVersion = glu::getGLSLVersionDeclaration(glslVersion);
761 
762     std::ostringstream vert;
763     std::ostringstream frag;
764     std::ostringstream &op = isVtxCase ? vert : frag;
765 
766     std::string cubeMapArrayEXT = "";
767 
768     // Check if we need to add the texture_cube_map_array extension.
769     if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
770     {
771         if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
772             !glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)))
773         {
774             if (isGL)
775             {
776                 cubeMapArrayEXT = "#extension GL_ARB_texture_cube_map_array : require\n";
777             }
778             else
779             {
780                 glu::ContextInfo *info = glu::ContextInfo::create(m_renderCtx);
781                 if (info->isExtensionSupported("GL_EXT_texture_cube_map_array"))
782                 {
783                     cubeMapArrayEXT = "#extension GL_EXT_texture_cube_map_array : require\n";
784                 }
785                 else
786                 {
787                     cubeMapArrayEXT = "#extension GL_OES_texture_cube_map_array : require\n";
788                 }
789             }
790         }
791     }
792 
793     vert << shaderVersion << "\n"
794          << "#extension GL_EXT_texture_shadow_lod : require\n\n";
795 
796     vert << cubeMapArrayEXT;
797 
798     vert << "in highp vec4 a_position;\n"
799          << "in " << coordPrecName << " " << coordTypeName << " a_in0;\n";
800 
801     if (hasLodBias || hasSepShadowRef)
802     {
803         vert << "in " << coordPrecName << " vec4 a_in1;\n";
804     }
805 
806     frag << shaderVersion << "\n"
807          << "#extension GL_EXT_texture_shadow_lod : require\n\n";
808 
809     frag << cubeMapArrayEXT;
810 
811     frag << "out mediump vec4 o_color;\n";
812 
813     if (isVtxCase)
814     {
815         vert << "out mediump vec4 v_color;\n";
816         frag << "in mediump vec4 v_color;\n";
817     }
818     else
819     {
820         vert << "out " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
821         frag << "in " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
822 
823         if (hasLodBias || hasSepShadowRef)
824         {
825             vert << "out " << coordPrecName << " vec4 v_lodShadowRef;\n";
826             frag << "in " << coordPrecName << " vec4 v_lodShadowRef;\n";
827         }
828     }
829 
830     // Uniforms
831     op << "uniform highp " << glu::getDataTypeName(samplerType) << " u_sampler;\n"
832        << "uniform highp vec4 u_scale;\n"
833        << "uniform highp vec4 u_bias;\n";
834 
835     vert << "\nvoid main()\n{\n"
836          << "\tgl_Position = a_position;\n";
837     frag << "\nvoid main()\n{\n";
838 
839     if (isVtxCase)
840         vert << "\tv_color = ";
841     else
842         frag << "\to_color = ";
843 
844     // Op.
845     {
846         const char *texCoord = isVtxCase ? "a_in0" : "v_texCoord";
847         const char *lodBias  = isVtxCase ? "a_in1" : "v_lodShadowRef";
848 
849         op << "vec4(" << baseFuncName;
850         if (m_lookupSpec.useOffset)
851             op << "Offset";
852         op << "(u_sampler, ";
853 
854         op << texCoord;
855 
856         if (m_lookupSpec.useSepRef)
857         {
858             op << ", " << lodBias << ".y";
859         }
860 
861         if (functionHasLod(function))
862         {
863             op << ", " << lodBias << ".x";
864         }
865 
866         if (m_lookupSpec.useOffset)
867         {
868             int offsetComps = 2;
869 
870             op << ", ivec" << offsetComps << "(";
871             for (int ndx = 0; ndx < offsetComps; ndx++)
872             {
873                 if (ndx != 0)
874                     op << ", ";
875                 op << m_lookupSpec.offset[ndx];
876             }
877             op << ")";
878         }
879 
880         if (m_lookupSpec.useBias)
881             op << ", " << lodBias << ".x";
882 
883         op << ")";
884 
885         op << ", 0.0, 0.0, 1.0)";
886 
887         op << ";\n";
888     }
889 
890     if (isVtxCase)
891         frag << "\to_color = v_color;\n";
892     else
893     {
894         vert << "\tv_texCoord = a_in0;\n";
895 
896         if (hasLodBias || hasSepShadowRef)
897         {
898             vert << "\tv_lodShadowRef = a_in1;\n";
899         }
900     }
901 
902     vert << "}\n";
903     frag << "}\n";
904 
905     m_vertShaderSource = vert.str();
906     m_fragShaderSource = frag.str();
907 }
908 
deinit(void)909 void TextureShadowLodTestCase::deinit(void)
910 {
911     ShaderRenderCase::deinit();
912 
913     delete m_texture2D;
914     delete m_textureCube;
915     delete m_texture2DArray;
916 
917     m_texture2D      = DE_NULL;
918     m_textureCube    = DE_NULL;
919     m_texture2DArray = DE_NULL;
920 }
921 
setupUniforms(uint32_t programID,const tcu::Vec4 &)922 void TextureShadowLodTestCase::setupUniforms(uint32_t programID, const tcu::Vec4 &)
923 {
924     const glw::Functions &gl = m_renderCtx.getFunctions();
925     gl.uniform1i(gl.getUniformLocation(programID, "u_sampler"), 0);
926     gl.uniform4fv(gl.getUniformLocation(programID, "u_scale"), 1, m_lookupParams.scale.getPtr());
927     gl.uniform4fv(gl.getUniformLocation(programID, "u_bias"), 1, m_lookupParams.bias.getPtr());
928 }
929 
TextureShadowLodTest(Context & context)930 TextureShadowLodTest::TextureShadowLodTest(Context &context)
931     : TestCaseGroup(context, "ext_texture_shadow_lod", "Texture Access Function Tests")
932 {
933 }
934 
~TextureShadowLodTest(void)935 TextureShadowLodTest::~TextureShadowLodTest(void)
936 {
937 }
938 
939 enum CaseFlags
940 {
941     VERTEX   = (1 << 0),
942     FRAGMENT = (1 << 1),
943     BOTH     = VERTEX | FRAGMENT
944 };
945 
946 struct TexFuncCaseSpec
947 {
948     const char *name;
949     TextureLookupSpec lookupSpec;
950     TextureSpec texSpec;
951     TexEvalFunc evalFunc;
952     uint32_t flags;
953 };
954 
955 #define CASE_SPEC(NAME, FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF, MINSEPREF, \
956                   MAXSEPREF, TEXSPEC, EVALFUNC, FLAGS)                                                              \
957     {                                                                                                               \
958         #NAME,                                                                                                      \
959             TextureLookupSpec(FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF,      \
960                               MINSEPREF, MAXSEPREF),                                                                \
961             TEXSPEC, EVALFUNC, FLAGS                                                                                \
962     }
963 
createCaseGroup(TestCaseGroup * parent,const char * groupName,const char * groupDesc,const TexFuncCaseSpec * cases,int numCases)964 static void createCaseGroup(TestCaseGroup *parent, const char *groupName, const char *groupDesc,
965                             const TexFuncCaseSpec *cases, int numCases)
966 {
967     tcu::TestCaseGroup *group = new tcu::TestCaseGroup(parent->getTestContext(), groupName, groupDesc);
968     parent->addChild(group);
969 
970     for (int ndx = 0; ndx < numCases; ndx++)
971     {
972         std::string name = cases[ndx].name;
973         if (cases[ndx].flags & VERTEX)
974             group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_vertex").c_str(), "",
975                                                          cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
976                                                          true));
977         if (cases[ndx].flags & FRAGMENT)
978             group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_fragment").c_str(), "",
979                                                          cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
980                                                          false));
981     }
982 }
983 
init(void)984 void TextureShadowLodTest::init(void)
985 {
986     // Samplers
987     static const tcu::Sampler samplerShadowNoMipmap(
988         tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST,
989         tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
990         0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
991     static const tcu::Sampler samplerShadowMipmap(
992         tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST_MIPMAP_NEAREST,
993         tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
994         0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
995 
996     // Default textures.
997     //                                                    Type                            Format                    W        H        D    L    Sampler
998 
999     static const TextureSpec tex2DArrayShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
1000                                               samplerShadowNoMipmap);
1001     static const TextureSpec texCubeShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 1,
1002                                            samplerShadowNoMipmap);
1003     static const TextureSpec texCubeMipmapShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 9,
1004                                                  samplerShadowMipmap);
1005     static const TextureSpec texCubeArrayShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
1006                                                 samplerShadowNoMipmap);
1007     static const TextureSpec texCubeArrayMipmapShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
1008                                                       samplerShadowMipmap);
1009     static const TextureSpec tex2DArrayMipmapShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
1010                                                     samplerShadowMipmap);
1011 
1012     // texture() cases
1013     static const TexFuncCaseSpec textureCases[] = {
1014         //          Name                            Function            MinCoord                            MaxCoord                            Bias?    MinLod    MaxLod    Offset?    Offset        Format                    EvalFunc                Flags
1015         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1016                   false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayShadow, evalTexture2DArrayShadow,
1017                   VERTEX),
1018         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1019                   false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
1020                   evalTexture2DArrayShadow, FRAGMENT),
1021         CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1022                   Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, false, IVec3(0), false, 0.0f, 0.0f,
1023                   tex2DArrayMipmapShadow, evalTexture2DArrayShadowBias, FRAGMENT),
1024 
1025         CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1026                   Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
1027                   texCubeArrayShadow, evalTextureCubeArrayShadow, VERTEX),
1028         CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1029                   Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
1030                   texCubeArrayMipmapShadow, evalTextureCubeArrayShadow, FRAGMENT),
1031         CASE_SPEC(samplercubearrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1032                   Vec4(1.0f, 1.0f, 1.01f, 3.5f), true, -2.0, 2.0f, false, IVec3(0), true, 0.0f, 1.0f,
1033                   texCubeArrayMipmapShadow, evalTextureCubeArrayShadowBias, FRAGMENT),
1034     };
1035     createCaseGroup(this, "texture", "texture() Tests", textureCases, DE_LENGTH_OF_ARRAY(textureCases));
1036 
1037     // textureOffset() cases
1038     // \note _bias variants are not using mipmap thanks to wide allowed range for LOD computation
1039     static const TexFuncCaseSpec textureOffsetCases[] = {
1040         //          Name                            Function            MinCoord                            MaxCoord                            Bias?    MinLod    MaxLod    Offset?    Offset                Format                    EvalFunc                        Flags
1041         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1042                   false, 0.0f, 0.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f, tex2DArrayShadow,
1043                   evalTexture2DArrayShadowOffset, VERTEX),
1044         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1045                   false, 0.0f, 0.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
1046                   evalTexture2DArrayShadowOffset, FRAGMENT),
1047         CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1048                   Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f,
1049                   tex2DArrayMipmapShadow, evalTexture2DArrayShadowOffsetBias, FRAGMENT),
1050     };
1051     createCaseGroup(this, "textureoffset", "textureOffset() Tests", textureOffsetCases,
1052                     DE_LENGTH_OF_ARRAY(textureOffsetCases));
1053 
1054     // textureLod() cases
1055     static const TexFuncCaseSpec textureLodCases[] = {
1056         //          Name                            Function                MinCoord                            MaxCoord                            Bias?    MinLod    MaxLod    Offset?    Offset        Format                    EvalFunc                Flags
1057         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1058                   Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
1059                   tex2DArrayMipmapShadow, evalTexture2DArrayShadowLod, BOTH),
1060         CASE_SPEC(samplercubeshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, 0.0f),
1061                   Vec4(1.0f, 1.0f, 1.01f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
1062                   texCubeMipmapShadow, evalTextureCubeShadowLod, BOTH),
1063         CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1064                   Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, -1.0f, 8.0f, false, IVec3(0), true, 0.0f, 1.0f,
1065                   texCubeArrayMipmapShadow, evalTextureCubeArrayShadowLod, FRAGMENT)};
1066     createCaseGroup(this, "texturelod", "textureLod() Tests", textureLodCases, DE_LENGTH_OF_ARRAY(textureLodCases));
1067 
1068     // textureLodOffset() cases
1069     static const TexFuncCaseSpec textureLodOffsetCases[] = {
1070         //          Name                            Function                MinCoord                            MaxCoord                            Bias?    MinLod    MaxLod    Offset?    Offset                Format                    EvalFunc                        Flags
1071         CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1072                   Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 9.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f,
1073                   tex2DArrayMipmapShadow, evalTexture2DArrayShadowLodOffset, BOTH),
1074     };
1075     createCaseGroup(this, "texturelodoffset", "textureLodOffset() Tests", textureLodOffsetCases,
1076                     DE_LENGTH_OF_ARRAY(textureLodOffsetCases));
1077 }
1078 } // namespace Functional
1079 } // namespace deqp
1080