• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::__anon922096fb0111::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::__anon922096fb0111::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 	deUint32	 format; //!< Internal format.
152 	int			 width;
153 	int			 height;
154 	int			 depth;
155 	int			 numLevels;
156 	tcu::Sampler sampler;
157 
TextureSpecdeqp::Functional::__anon922096fb0111::TextureSpec158 	TextureSpec(void) : type(TEXTURETYPE_LAST), format(GL_NONE), width(0), height(0), depth(0), numLevels(0)
159 	{
160 	}
161 
TextureSpecdeqp::Functional::__anon922096fb0111::TextureSpec162 	TextureSpec(TextureType type_, deUint32 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::__anon922096fb0111::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), m_lookupParams(lookupParams)
293 	{
294 	}
295 
evaluate(ShaderEvalContext & ctx)296 	virtual void evaluate(ShaderEvalContext& ctx)
297 	{
298 		m_evalFunc(ctx, m_lookupParams);
299 	}
300 
301 private:
302 	TexEvalFunc			   m_evalFunc;
303 	const TexLookupParams& m_lookupParams;
304 };
305 
306 class TextureShadowLodTestCase : public ShaderRenderCase
307 {
308 public:
309 	TextureShadowLodTestCase(Context& context, const char* name, const char* desc, const TextureLookupSpec& lookup,
310 							 const TextureSpec& texture, TexEvalFunc evalFunc, bool isVertexCase);
311 	~TextureShadowLodTestCase(void);
312 
313 	void init(void);
314 	void deinit(void);
315 
316 protected:
317 	void setupUniforms(deUint32 programID, const tcu::Vec4& constCoords);
318 
319 private:
320 	void initTexture(void);
321 	void initShaderSources(void);
322 
323 	TextureLookupSpec m_lookupSpec;
324 	TextureSpec		  m_textureSpec;
325 
326 	TexLookupParams	m_lookupParams;
327 	TexLookupEvaluator m_evaluator;
328 
329 	glu::Texture2D*		   m_texture2D;
330 	glu::TextureCube*	  m_textureCube;
331 	glu::TextureCubeArray* m_textureCubeArray;
332 	glu::Texture2DArray*   m_texture2DArray;
333 };
334 
TextureShadowLodTestCase(Context & context,const char * name,const char * desc,const TextureLookupSpec & lookup,const TextureSpec & texture,TexEvalFunc evalFunc,bool isVertexCase)335 TextureShadowLodTestCase::TextureShadowLodTestCase(Context& context, const char* name, const char* desc,
336 												   const TextureLookupSpec& lookup, const TextureSpec& texture,
337 												   TexEvalFunc evalFunc, bool isVertexCase)
338 	: ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc,
339 					   isVertexCase, m_evaluator)
340 	, m_lookupSpec(lookup)
341 	, m_textureSpec(texture)
342 	, m_evaluator(evalFunc, m_lookupParams)
343 	, m_texture2D(DE_NULL)
344 	, m_textureCube(DE_NULL)
345 	, m_textureCubeArray(DE_NULL)
346 	, m_texture2DArray(DE_NULL)
347 {
348 }
349 
~TextureShadowLodTestCase(void)350 TextureShadowLodTestCase::~TextureShadowLodTestCase(void)
351 {
352 	delete m_texture2D;
353 	delete m_textureCube;
354 	delete m_textureCubeArray;
355 	delete m_texture2DArray;
356 }
357 
init(void)358 void TextureShadowLodTestCase::init(void)
359 {
360 	// Check extension and other features are supported with this context/platform.
361 	{
362 		glu::ContextInfo* info = glu::ContextInfo::create(m_renderCtx);
363 
364 		// First check if extension is available.
365 		if (!info->isExtensionSupported("GL_EXT_texture_shadow_lod"))
366 		{
367 			throw tcu::NotSupportedError("EXT_texture_shadow_lod is not supported on the platform");
368 		}
369 
370 		// Check that API support and that the various texture_cube_map_array extension is supported based on GL / ES versions.
371 
372 		if (glu::isContextTypeES(m_renderCtx.getType()))
373 		{
374 			// ES
375 			if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 0)))
376 			{
377 				throw tcu::NotSupportedError(
378 					"EXT_texture_shadow_lod is not supported due to minimum ES version requirements");
379 			}
380 
381 			// Check if cube map array is supported.  For ES, it is supported
382 			// as of ES 3.2, or with OES_texture_cube_map_array for
383 			// 3.1.
384 			if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
385 			{
386 				if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
387 					!(glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 1)) &&
388 					  (info->isExtensionSupported("GL_OES_texture_cube_map_array") ||
389 					   info->isExtensionSupported("GL_EXT_texture_cube_map_array"))))
390 				{
391 					throw tcu::NotSupportedError("GL_OES_texture_cube_map_array or GL_EXT_texture_cube_map_array is "
392 												 "required for this configuration and is not available.");
393 				}
394 			}
395 		}
396 		else
397 		{
398 			// GL
399 			if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(2, 0)))
400 			{
401 				throw tcu::NotSupportedError(
402 					"EXT_texture_shadow_lod is not supported due to minimum GL version requirements");
403 			}
404 
405 			// Check if cube map array is supported.  For GL, it is supported
406 			// as of GL 4.0 core, or with ARB_texture_cube_map_array prior.
407 			if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
408 			{
409 				if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)) &&
410 					!info->isExtensionSupported("GL_ARB_texture_cube_map_array"))
411 				{
412 					throw tcu::NotSupportedError(
413 						"ARB_texture_cube_map_array is required for this configuration and is not available.");
414 				}
415 			}
416 		}
417 	}
418 
419 	// Set up the user attributes.
420 	// The user attributes are set up as matrices where each row represents a component
421 	// in the attribute transformed against the vertex's interpolated position across the grid.
422 	{
423 		// Base coord scale & bias
424 		Vec4 s = m_lookupSpec.maxCoord - m_lookupSpec.minCoord;
425 		Vec4 b = m_lookupSpec.minCoord;
426 
427 		float baseCoordTrans[] = { s.x(),		 0.0f,		   0.f, b.x(),
428 								   0.f,			 s.y(),		   0.f, b.y(),
429 								   s.z() / 2.f,  -s.z() / 2.f, 0.f, s.z() / 2.f + b.z(),
430 								   -s.w() / 2.f, s.w() / 2.f,  0.f, s.w() / 2.f + b.w() };
431 
432 		//a_in0
433 		m_userAttribTransforms.push_back(tcu::Mat4(baseCoordTrans));
434 	}
435 
436 	bool hasLodBias = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
437 
438 	if (hasLodBias || m_lookupSpec.useSepRef)
439 	{
440 		float s		  = 0.0f;
441 		float b		  = 0.0f;
442 		float sepRefS = 0.0f;
443 		float sepRefB = 0.0f;
444 
445 		if (hasLodBias)
446 		{
447 			s = m_lookupSpec.maxLodBias - m_lookupSpec.minLodBias;
448 			b = m_lookupSpec.minLodBias;
449 		}
450 
451 		if (m_lookupSpec.useSepRef)
452 		{
453 			sepRefS = m_lookupSpec.maxSepRef - m_lookupSpec.minSepRef;
454 			sepRefB = m_lookupSpec.minSepRef;
455 		}
456 		float lodCoordTrans[] = { s / 2.0f, s / 2.0f, 0.f,  b,	sepRefS / 2.0f, sepRefS / 2.0f, 0.0f, sepRefB,
457 								  0.0f,		0.0f,	 0.0f, 0.0f, 0.0f,			  0.0f,			  0.0f, 0.0f };
458 
459 		//a_in1
460 		m_userAttribTransforms.push_back(tcu::Mat4(lodCoordTrans));
461 	}
462 
463 	initShaderSources();
464 	initTexture();
465 
466 	ShaderRenderCase::init();
467 }
468 
initTexture(void)469 void TextureShadowLodTestCase::initTexture(void)
470 {
471 	static const IVec4 texCubeSwz[] = { IVec4(0, 0, 1, 1), IVec4(1, 1, 0, 0), IVec4(0, 1, 0, 1),
472 										IVec4(1, 0, 1, 0), IVec4(0, 1, 1, 0), IVec4(1, 0, 0, 1) };
473 	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(texCubeSwz) == tcu::CUBEFACE_LAST);
474 
475 	tcu::TextureFormat	 texFmt		= glu::mapGLInternalFormat(m_textureSpec.format);
476 	tcu::TextureFormatInfo fmtInfo		= tcu::getTextureFormatInfo(texFmt);
477 	tcu::IVec2			   viewportSize = getViewportSize();
478 	bool isAutoLod = functionHasAutoLod(m_isVertexCase ? glu::SHADERTYPE_VERTEX : glu::SHADERTYPE_FRAGMENT,
479 										m_lookupSpec.function); // LOD can vary significantly
480 
481 	switch (m_textureSpec.type)
482 	{
483 	case TEXTURETYPE_2D:
484 	{
485 		float levelStep	= isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
486 		Vec4  cScale	   = fmtInfo.valueMax - fmtInfo.valueMin;
487 		Vec4  cBias		   = fmtInfo.valueMin;
488 		int   baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
489 
490 		m_texture2D = new glu::Texture2D(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.height);
491 		for (int level = 0; level < m_textureSpec.numLevels; level++)
492 		{
493 			float fA	 = float(level) * levelStep;
494 			float fB	 = 1.0f - fA;
495 			Vec4  colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
496 			Vec4  colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
497 
498 			m_texture2D->getRefTexture().allocLevel(level);
499 			tcu::fillWithGrid(m_texture2D->getRefTexture().getLevel(level), de::max(1, baseCellSize >> level), colorA,
500 							  colorB);
501 		}
502 		m_texture2D->upload();
503 
504 		// Compute LOD.
505 		float dudx =
506 			(m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
507 		float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
508 					 (float)viewportSize[1];
509 		m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
510 
511 		// Append to texture list.
512 		m_textures.push_back(TextureBinding(m_texture2D, m_textureSpec.sampler));
513 		break;
514 	}
515 
516 	case TEXTURETYPE_2D_ARRAY:
517 	{
518 		float layerStep = 1.0f / (float)m_textureSpec.depth;
519 		float levelStep =
520 			isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
521 		Vec4 cScale		  = fmtInfo.valueMax - fmtInfo.valueMin;
522 		Vec4 cBias		  = fmtInfo.valueMin;
523 		int  baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
524 
525 		m_texture2DArray = new glu::Texture2DArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width,
526 												   m_textureSpec.height, m_textureSpec.depth);
527 		for (int level = 0; level < m_textureSpec.numLevels; level++)
528 		{
529 			m_texture2DArray->getRefTexture().allocLevel(level);
530 			tcu::PixelBufferAccess levelAccess = m_texture2DArray->getRefTexture().getLevel(level);
531 
532 			for (int layer = 0; layer < levelAccess.getDepth(); layer++)
533 			{
534 				float fA	 = (float)layer * layerStep + (float)level * levelStep;
535 				float fB	 = 1.0f - fA;
536 				Vec4  colorA = cBias + cScale * Vec4(fA, fB, fA, fB);
537 				Vec4  colorB = cBias + cScale * Vec4(fB, fA, fB, fA);
538 
539 				tcu::fillWithGrid(
540 					tcu::getSubregion(levelAccess, 0, 0, layer, levelAccess.getWidth(), levelAccess.getHeight(), 1),
541 					de::max(1, baseCellSize >> level), colorA, colorB);
542 			}
543 		}
544 		m_texture2DArray->upload();
545 
546 		// Compute LOD.
547 		float dudx =
548 			(m_lookupSpec.maxCoord[0] - m_lookupSpec.minCoord[0]) * (float)m_textureSpec.width / (float)viewportSize[0];
549 		float dvdy = (m_lookupSpec.maxCoord[1] - m_lookupSpec.minCoord[1]) * (float)m_textureSpec.height /
550 					 (float)viewportSize[1];
551 		m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
552 
553 		// Append to texture list.
554 		m_textures.push_back(TextureBinding(m_texture2DArray, m_textureSpec.sampler));
555 		break;
556 	}
557 
558 	case TEXTURETYPE_CUBE_MAP:
559 	{
560 		float levelStep	= isAutoLod ? 0.0f : 1.0f / (float)de::max(1, m_textureSpec.numLevels - 1);
561 		Vec4  cScale	   = fmtInfo.valueMax - fmtInfo.valueMin;
562 		Vec4  cBias		   = fmtInfo.valueMin;
563 		Vec4  cCorner	  = cBias + cScale * 0.5f;
564 		int   baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
565 
566 		DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
567 		m_textureCube = new glu::TextureCube(m_renderCtx, m_textureSpec.format, m_textureSpec.width);
568 		for (int level = 0; level < m_textureSpec.numLevels; level++)
569 		{
570 			float fA = float(level) * levelStep;
571 			float fB = 1.0f - fA;
572 			Vec2  f(fA, fB);
573 
574 			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
575 			{
576 				const IVec4& swzA   = texCubeSwz[face];
577 				IVec4		 swzB   = 1 - swzA;
578 				Vec4		 colorA = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
579 				Vec4		 colorB = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
580 
581 				m_textureCube->getRefTexture().allocLevel((tcu::CubeFace)face, level);
582 				{
583 					const tcu::PixelBufferAccess access =
584 						m_textureCube->getRefTexture().getLevelFace(level, (tcu::CubeFace)face);
585 					const int lastPix = access.getWidth() - 1;
586 
587 					tcu::fillWithGrid(access, de::max(1, baseCellSize >> level), colorA, colorB);
588 
589 					// Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
590 					access.setPixel(cCorner, 0, 0);
591 					access.setPixel(cCorner, 0, lastPix);
592 					access.setPixel(cCorner, lastPix, 0);
593 					access.setPixel(cCorner, lastPix, lastPix);
594 				}
595 			}
596 		}
597 		m_textureCube->upload();
598 
599 		// Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
600 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
601 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
602 				  de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
603 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
604 				  de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
605 
606 		tcu::CubeFaceFloatCoords c00 =
607 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
608 		tcu::CubeFaceFloatCoords c10 =
609 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
610 		tcu::CubeFaceFloatCoords c01 =
611 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
612 		float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
613 		float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
614 
615 		m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
616 
617 		m_textures.push_back(TextureBinding(m_textureCube, m_textureSpec.sampler));
618 		break;
619 	}
620 
621 	case TEXTURETYPE_CUBE_MAP_ARRAY:
622 	{
623 		float layerStep = 1.0f / (float)m_textureSpec.depth;
624 		float levelStep =
625 			isAutoLod ? 0.0f : 1.0f / (float)(de::max(1, m_textureSpec.numLevels - 1) * m_textureSpec.depth);
626 		Vec4 cScale		  = fmtInfo.valueMax - fmtInfo.valueMin;
627 		Vec4 cBias		  = fmtInfo.valueMin;
628 		Vec4 cCorner	  = cBias + cScale * 0.5f;
629 		int  baseCellSize = de::min(m_textureSpec.width / 4, m_textureSpec.height / 4);
630 
631 		DE_ASSERT(m_textureSpec.width == m_textureSpec.height);
632 		// I think size here means width/height of cube tex
633 		m_textureCubeArray =
634 			new glu::TextureCubeArray(m_renderCtx, m_textureSpec.format, m_textureSpec.width, m_textureSpec.depth * 6);
635 		// mipmap level
636 		for (int level = 0; level < m_textureSpec.numLevels; level++)
637 		{
638 			m_textureCubeArray->getRefTexture().allocLevel(level);
639 			tcu::PixelBufferAccess levelAccess = m_textureCubeArray->getRefTexture().getLevel(level);
640 
641 			//array layer
642 			DE_ASSERT((levelAccess.getDepth() % 6) == 0);
643 
644 			for (int layer = 0; layer < levelAccess.getDepth() / 6; ++layer)
645 			{
646 				for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
647 				{
648 					float fA = (float)layer * layerStep + float(level) * levelStep;
649 					float fB = 1.0f - fA;
650 					Vec2  f(fA, fB);
651 
652 					const IVec4& swzA   = texCubeSwz[face];
653 					IVec4		 swzB   = 1 - swzA;
654 					Vec4		 colorA = cBias + cScale * f.swizzle(swzA[0], swzA[1], swzA[2], swzA[3]);
655 					Vec4		 colorB = cBias + cScale * f.swizzle(swzB[0], swzB[1], swzB[2], swzB[3]);
656 
657 					{
658 						const tcu::PixelBufferAccess access  = m_textureCubeArray->getRefTexture().getLevel(level);
659 						const int					 lastPix = access.getWidth() - 1;
660 
661 						int layerFaceNdx = face + layer * 6;
662 
663 						DE_ASSERT(levelAccess.getWidth() == levelAccess.getHeight());
664 						tcu::fillWithGrid(tcu::getSubregion(access, 0, 0, layerFaceNdx, levelAccess.getWidth(),
665 															levelAccess.getHeight(), 1),
666 										  de::max(1, baseCellSize >> level), colorA, colorB);
667 
668 						// Ensure all corners have identical colors in order to avoid dealing with ambiguous corner texel filtering
669 						access.setPixel(cCorner, 0, 0, layer);
670 						access.setPixel(cCorner, 0, lastPix, layer);
671 						access.setPixel(cCorner, lastPix, 0, layer);
672 						access.setPixel(cCorner, lastPix, lastPix, layer);
673 					}
674 				}
675 			}
676 		}
677 		m_textureCubeArray->upload();
678 
679 		// Compute LOD \note Assumes that only single side is accessed and R is constant major axis.
680 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[2] - m_lookupSpec.maxCoord[2]) < 0.005);
681 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[0]) < de::abs(m_lookupSpec.minCoord[2]) &&
682 				  de::abs(m_lookupSpec.maxCoord[0]) < de::abs(m_lookupSpec.minCoord[2]));
683 		DE_ASSERT(de::abs(m_lookupSpec.minCoord[1]) < de::abs(m_lookupSpec.minCoord[2]) &&
684 				  de::abs(m_lookupSpec.maxCoord[1]) < de::abs(m_lookupSpec.minCoord[2]));
685 
686 		tcu::CubeFaceFloatCoords c00 =
687 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
688 		tcu::CubeFaceFloatCoords c10 =
689 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.maxCoord[0], m_lookupSpec.minCoord[1], m_lookupSpec.minCoord[2]));
690 		tcu::CubeFaceFloatCoords c01 =
691 			tcu::getCubeFaceCoords(Vec3(m_lookupSpec.minCoord[0], m_lookupSpec.maxCoord[1], m_lookupSpec.minCoord[2]));
692 		float dudx = (c10.s - c00.s) * (float)m_textureSpec.width / (float)viewportSize[0];
693 		float dvdy = (c01.t - c00.t) * (float)m_textureSpec.height / (float)viewportSize[1];
694 
695 		m_lookupParams.lod = computeLodFromDerivates(DEFAULT_LOD_MODE, dudx, 0.0f, 0.0f, dvdy);
696 
697 		m_textures.push_back(TextureBinding(m_textureCubeArray, m_textureSpec.sampler));
698 		break;
699 	}
700 
701 	default:
702 		DE_ASSERT(DE_FALSE);
703 	}
704 
705 	// Set lookup scale & bias
706 	m_lookupParams.scale  = fmtInfo.lookupScale;
707 	m_lookupParams.bias   = fmtInfo.lookupBias;
708 	m_lookupParams.offset = m_lookupSpec.offset;
709 }
710 
initShaderSources(void)711 void TextureShadowLodTestCase::initShaderSources(void)
712 {
713 	Function	   function		   = m_lookupSpec.function;
714 	bool		   isVtxCase	   = m_isVertexCase;
715 	bool		   hasLodBias	  = functionHasLod(m_lookupSpec.function) || m_lookupSpec.useBias;
716 	int			   texCoordComps   = m_textureSpec.type == TEXTURETYPE_2D ? 2 : 3;
717 	int			   extraCoordComps = 1; // For shadow ref
718 	bool		   hasSepShadowRef = m_lookupSpec.useSepRef;
719 	glu::DataType  coordType	   = glu::getDataTypeFloatVec(texCoordComps + extraCoordComps);
720 	glu::Precision coordPrec	   = glu::PRECISION_HIGHP;
721 	const char*	coordTypeName   = glu::getDataTypeName(coordType);
722 	const char*	coordPrecName   = glu::getPrecisionName(coordPrec);
723 	glu::DataType  samplerType	 = glu::TYPE_LAST;
724 	const char*	baseFuncName	= DE_NULL;
725 
726 	switch (m_textureSpec.type)
727 	{
728 	case TEXTURETYPE_2D:
729 		samplerType = glu::TYPE_SAMPLER_2D_SHADOW;
730 		break;
731 	case TEXTURETYPE_CUBE_MAP:
732 		samplerType = glu::TYPE_SAMPLER_CUBE_SHADOW;
733 		break;
734 	case TEXTURETYPE_CUBE_MAP_ARRAY:
735 		samplerType = glu::TYPE_SAMPLER_CUBE_ARRAY_SHADOW;
736 		break;
737 	case TEXTURETYPE_2D_ARRAY:
738 		samplerType = glu::TYPE_SAMPLER_2D_ARRAY_SHADOW;
739 		break;
740 	default:
741 		DE_ASSERT(DE_FALSE);
742 	}
743 
744 	switch (m_lookupSpec.function)
745 	{
746 	case FUNCTION_TEXTURE:
747 		baseFuncName = "texture";
748 		break;
749 	case FUNCTION_TEXTURELOD:
750 		baseFuncName = "textureLod";
751 		break;
752 	default:
753 		DE_ASSERT(DE_FALSE);
754 	}
755 
756 	bool			 isGL		 = glu::isContextTypeGLCore(m_renderCtx.getType());
757 	glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(m_renderCtx.getType());
758 
759 	std::string shaderVersion = glu::getGLSLVersionDeclaration(glslVersion);
760 
761 	std::ostringstream  vert;
762 	std::ostringstream  frag;
763 	std::ostringstream& op = isVtxCase ? vert : frag;
764 
765 	std::string cubeMapArrayEXT = "";
766 
767 	// Check if we need to add the texture_cube_map_array extension.
768 	if (m_textureSpec.type == TEXTURETYPE_CUBE_MAP_ARRAY)
769 	{
770 		if (!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) &&
771 			!glu::contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 0)))
772 		{
773 			if (isGL)
774 			{
775 				cubeMapArrayEXT = "#extension GL_ARB_texture_cube_map_array : require\n";
776 			}
777 			else
778 			{
779 				glu::ContextInfo* info = glu::ContextInfo::create(m_renderCtx);
780 				if (info->isExtensionSupported("GL_EXT_texture_cube_map_array"))
781 				{
782 					cubeMapArrayEXT = "#extension GL_EXT_texture_cube_map_array : require\n";
783 				}
784 				else
785 				{
786 					cubeMapArrayEXT = "#extension GL_OES_texture_cube_map_array : require\n";
787 				}
788 			}
789 		}
790 	}
791 
792 	vert << shaderVersion << "\n"
793 		 << "#extension GL_EXT_texture_shadow_lod : require\n\n";
794 
795 	vert << cubeMapArrayEXT;
796 
797 	vert << "in highp vec4 a_position;\n"
798 		 << "in " << coordPrecName << " " << coordTypeName << " a_in0;\n";
799 
800 	if (hasLodBias || hasSepShadowRef)
801 	{
802 		vert << "in " << coordPrecName << " vec4 a_in1;\n";
803 	}
804 
805 	frag << shaderVersion << "\n"
806 		 << "#extension GL_EXT_texture_shadow_lod : require\n\n";
807 
808 	frag << cubeMapArrayEXT;
809 
810 	frag << "out mediump vec4 o_color;\n";
811 
812 	if (isVtxCase)
813 	{
814 		vert << "out mediump vec4 v_color;\n";
815 		frag << "in mediump vec4 v_color;\n";
816 	}
817 	else
818 	{
819 		vert << "out " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
820 		frag << "in " << coordPrecName << " " << coordTypeName << " v_texCoord;\n";
821 
822 		if (hasLodBias || hasSepShadowRef)
823 		{
824 			vert << "out " << coordPrecName << " vec4 v_lodShadowRef;\n";
825 			frag << "in " << coordPrecName << " vec4 v_lodShadowRef;\n";
826 		}
827 	}
828 
829 	// Uniforms
830 	op << "uniform highp " << glu::getDataTypeName(samplerType) << " u_sampler;\n"
831 	   << "uniform highp vec4 u_scale;\n"
832 	   << "uniform highp vec4 u_bias;\n";
833 
834 	vert << "\nvoid main()\n{\n"
835 		 << "\tgl_Position = a_position;\n";
836 	frag << "\nvoid main()\n{\n";
837 
838 	if (isVtxCase)
839 		vert << "\tv_color = ";
840 	else
841 		frag << "\to_color = ";
842 
843 	// Op.
844 	{
845 		const char* texCoord = isVtxCase ? "a_in0" : "v_texCoord";
846 		const char* lodBias  = isVtxCase ? "a_in1" : "v_lodShadowRef";
847 
848 		op << "vec4(" << baseFuncName;
849 		if (m_lookupSpec.useOffset)
850 			op << "Offset";
851 		op << "(u_sampler, ";
852 
853 		op << texCoord;
854 
855 		if (m_lookupSpec.useSepRef)
856 		{
857 			op << ", " << lodBias << ".y";
858 		}
859 
860 		if (functionHasLod(function))
861 		{
862 			op << ", " << lodBias << ".x";
863 		}
864 
865 		if (m_lookupSpec.useOffset)
866 		{
867 			int offsetComps = 2;
868 
869 			op << ", ivec" << offsetComps << "(";
870 			for (int ndx = 0; ndx < offsetComps; ndx++)
871 			{
872 				if (ndx != 0)
873 					op << ", ";
874 				op << m_lookupSpec.offset[ndx];
875 			}
876 			op << ")";
877 		}
878 
879 		if (m_lookupSpec.useBias)
880 			op << ", " << lodBias << ".x";
881 
882 		op << ")";
883 
884 		op << ", 0.0, 0.0, 1.0)";
885 
886 		op << ";\n";
887 	}
888 
889 	if (isVtxCase)
890 		frag << "\to_color = v_color;\n";
891 	else
892 	{
893 		vert << "\tv_texCoord = a_in0;\n";
894 
895 		if (hasLodBias || hasSepShadowRef)
896 		{
897 			vert << "\tv_lodShadowRef = a_in1;\n";
898 		}
899 	}
900 
901 	vert << "}\n";
902 	frag << "}\n";
903 
904 	m_vertShaderSource = vert.str();
905 	m_fragShaderSource = frag.str();
906 }
907 
deinit(void)908 void TextureShadowLodTestCase::deinit(void)
909 {
910 	ShaderRenderCase::deinit();
911 
912 	delete m_texture2D;
913 	delete m_textureCube;
914 	delete m_texture2DArray;
915 
916 	m_texture2D		 = DE_NULL;
917 	m_textureCube	= DE_NULL;
918 	m_texture2DArray = DE_NULL;
919 }
920 
setupUniforms(deUint32 programID,const tcu::Vec4 &)921 void TextureShadowLodTestCase::setupUniforms(deUint32 programID, const tcu::Vec4&)
922 {
923 	const glw::Functions& gl = m_renderCtx.getFunctions();
924 	gl.uniform1i(gl.getUniformLocation(programID, "u_sampler"), 0);
925 	gl.uniform4fv(gl.getUniformLocation(programID, "u_scale"), 1, m_lookupParams.scale.getPtr());
926 	gl.uniform4fv(gl.getUniformLocation(programID, "u_bias"), 1, m_lookupParams.bias.getPtr());
927 }
928 
TextureShadowLodTest(Context & context)929 TextureShadowLodTest::TextureShadowLodTest(Context& context)
930 	: TestCaseGroup(context, "ext_texture_shadow_lod", "Texture Access Function Tests")
931 {
932 }
933 
~TextureShadowLodTest(void)934 TextureShadowLodTest::~TextureShadowLodTest(void)
935 {
936 }
937 
938 enum CaseFlags
939 {
940 	VERTEX   = (1 << 0),
941 	FRAGMENT = (1 << 1),
942 	BOTH	 = VERTEX | FRAGMENT
943 };
944 
945 struct TexFuncCaseSpec
946 {
947 	const char*		  name;
948 	TextureLookupSpec lookupSpec;
949 	TextureSpec		  texSpec;
950 	TexEvalFunc		  evalFunc;
951 	deUint32		  flags;
952 };
953 
954 #define CASE_SPEC(NAME, FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF, MINSEPREF, \
955 				  MAXSEPREF, TEXSPEC, EVALFUNC, FLAGS)                                                              \
956 	{                                                                                                               \
957 		#NAME, TextureLookupSpec(FUNC, MINCOORD, MAXCOORD, USEBIAS, MINLOD, MAXLOD, USEOFFSET, OFFSET, USESEPREF,   \
958 								 MINSEPREF, MAXSEPREF),                                                             \
959 								 TEXSPEC, EVALFUNC, FLAGS                                                           \
960 	}
961 
createCaseGroup(TestCaseGroup * parent,const char * groupName,const char * groupDesc,const TexFuncCaseSpec * cases,int numCases)962 static void createCaseGroup(TestCaseGroup* parent, const char* groupName, const char* groupDesc,
963 							const TexFuncCaseSpec* cases, int numCases)
964 {
965 	tcu::TestCaseGroup* group = new tcu::TestCaseGroup(parent->getTestContext(), groupName, groupDesc);
966 	parent->addChild(group);
967 
968 	for (int ndx = 0; ndx < numCases; ndx++)
969 	{
970 		std::string name = cases[ndx].name;
971 		if (cases[ndx].flags & VERTEX)
972 			group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_vertex").c_str(), "",
973 														 cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
974 														 true));
975 		if (cases[ndx].flags & FRAGMENT)
976 			group->addChild(new TextureShadowLodTestCase(parent->getContext(), (name + "_fragment").c_str(), "",
977 														 cases[ndx].lookupSpec, cases[ndx].texSpec, cases[ndx].evalFunc,
978 														 false));
979 	}
980 }
981 
init(void)982 void TextureShadowLodTest::init(void)
983 {
984 	// Samplers
985 	static const tcu::Sampler samplerShadowNoMipmap(
986 		tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST,
987 		tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
988 		0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
989 	static const tcu::Sampler samplerShadowMipmap(
990 		tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST_MIPMAP_NEAREST,
991 		tcu::Sampler::NEAREST, 0.0f /* LOD threshold */, true /* normalized coords */, tcu::Sampler::COMPAREMODE_LESS,
992 		0 /* cmp channel */, tcu::Vec4(0.0f) /* border color */, true /* seamless cube map */);
993 
994 	// Default textures.
995 	//													Type							Format					W		H		D	L	Sampler
996 
997 	static const TextureSpec tex2DArrayShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
998 											  samplerShadowNoMipmap);
999 	static const TextureSpec texCubeShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 1,
1000 										   samplerShadowNoMipmap);
1001 	static const TextureSpec texCubeMipmapShadow(TEXTURETYPE_CUBE_MAP, GL_DEPTH_COMPONENT16, 256, 256, 1, 9,
1002 												 samplerShadowMipmap);
1003 	static const TextureSpec texCubeArrayShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 1,
1004 												samplerShadowNoMipmap);
1005 	static const TextureSpec texCubeArrayMipmapShadow(TEXTURETYPE_CUBE_MAP_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
1006 													  samplerShadowMipmap);
1007 	static const TextureSpec tex2DArrayMipmapShadow(TEXTURETYPE_2D_ARRAY, GL_DEPTH_COMPONENT16, 128, 128, 4, 8,
1008 													samplerShadowMipmap);
1009 
1010 	// texture() cases
1011 	static const TexFuncCaseSpec textureCases[] = {
1012 		//		  Name							Function			MinCoord							MaxCoord							Bias?	MinLod	MaxLod	Offset?	Offset		Format					EvalFunc				Flags
1013 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1014 				  false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayShadow, evalTexture2DArrayShadow,
1015 				  VERTEX),
1016 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1017 				  false, 0.0f, 0.0f, false, IVec3(0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
1018 				  evalTexture2DArrayShadow, FRAGMENT),
1019 		CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1020 				  Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, false, IVec3(0), false, 0.0f, 0.0f,
1021 				  tex2DArrayMipmapShadow, evalTexture2DArrayShadowBias, FRAGMENT),
1022 
1023 		CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1024 				  Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
1025 				  texCubeArrayShadow, evalTextureCubeArrayShadow, VERTEX),
1026 		CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1027 				  Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, 0.0f, 0.0f, false, IVec3(0), true, 0.0f, 1.0f,
1028 				  texCubeArrayMipmapShadow, evalTextureCubeArrayShadow, FRAGMENT),
1029 		CASE_SPEC(samplercubearrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1030 				  Vec4(1.0f, 1.0f, 1.01f, 3.5f), true, -2.0, 2.0f, false, IVec3(0), true, 0.0f, 1.0f,
1031 				  texCubeArrayMipmapShadow, evalTextureCubeArrayShadowBias, FRAGMENT),
1032 	};
1033 	createCaseGroup(this, "texture", "texture() Tests", textureCases, DE_LENGTH_OF_ARRAY(textureCases));
1034 
1035 	// textureOffset() cases
1036 	// \note _bias variants are not using mipmap thanks to wide allowed range for LOD computation
1037 	static const TexFuncCaseSpec textureOffsetCases[] = {
1038 		//		  Name							Function			MinCoord							MaxCoord							Bias?	MinLod	MaxLod	Offset?	Offset				Format					EvalFunc						Flags
1039 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1040 				  false, 0.0f, 0.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f, tex2DArrayShadow,
1041 				  evalTexture2DArrayShadowOffset, VERTEX),
1042 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f), Vec4(1.5f, 2.3f, 3.5f, 1.0f),
1043 				  false, 0.0f, 0.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f, tex2DArrayMipmapShadow,
1044 				  evalTexture2DArrayShadowOffset, FRAGMENT),
1045 		CASE_SPEC(sampler2darrayshadow_bias, FUNCTION_TEXTURE, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1046 				  Vec4(1.5f, 2.3f, 3.5f, 1.0f), true, -2.0f, 2.0f, true, IVec3(7, -8, 0), false, 0.0f, 0.0f,
1047 				  tex2DArrayMipmapShadow, evalTexture2DArrayShadowOffsetBias, FRAGMENT),
1048 	};
1049 	createCaseGroup(this, "textureoffset", "textureOffset() Tests", textureOffsetCases,
1050 					DE_LENGTH_OF_ARRAY(textureOffsetCases));
1051 
1052 	// textureLod() cases
1053 	static const TexFuncCaseSpec textureLodCases[] = {
1054 		//		  Name							Function				MinCoord							MaxCoord							Bias?	MinLod	MaxLod	Offset?	Offset		Format					EvalFunc				Flags
1055 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1056 				  Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
1057 				  tex2DArrayMipmapShadow, evalTexture2DArrayShadowLod, BOTH),
1058 		CASE_SPEC(samplercubeshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, 0.0f),
1059 				  Vec4(1.0f, 1.0f, 1.01f, 1.0f), false, -1.0f, 8.0f, false, IVec3(0), false, 0.0f, 0.0f,
1060 				  texCubeMipmapShadow, evalTextureCubeShadowLod, BOTH),
1061 		CASE_SPEC(samplercubearrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.0f, -1.0f, 1.01f, -0.5f),
1062 				  Vec4(1.0f, 1.0f, 1.01f, 3.5f), false, -1.0f, 8.0f, false, IVec3(0), true, 0.0f, 1.0f,
1063 				  texCubeArrayMipmapShadow, evalTextureCubeArrayShadowLod, FRAGMENT)
1064 	};
1065 	createCaseGroup(this, "texturelod", "textureLod() Tests", textureLodCases, DE_LENGTH_OF_ARRAY(textureLodCases));
1066 
1067 	// textureLodOffset() cases
1068 	static const TexFuncCaseSpec textureLodOffsetCases[] = {
1069 		//		  Name							Function				MinCoord							MaxCoord							Bias?	MinLod	MaxLod	Offset?	Offset				Format					EvalFunc						Flags
1070 		CASE_SPEC(sampler2darrayshadow, FUNCTION_TEXTURELOD, Vec4(-1.2f, -0.4f, -0.5f, 0.0f),
1071 				  Vec4(1.5f, 2.3f, 3.5f, 1.0f), false, -1.0f, 9.0f, true, IVec3(-8, 7, 0), false, 0.0f, 0.0f,
1072 				  tex2DArrayMipmapShadow, evalTexture2DArrayShadowLodOffset, BOTH),
1073 	};
1074 	createCaseGroup(this, "texturelodoffset", "textureLodOffset() Tests", textureLodOffsetCases,
1075 					DE_LENGTH_OF_ARRAY(textureLodOffsetCases));
1076 }
1077 } // namespace Functional
1078 } // namespace deqp
1079