Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Vertex texture tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es3fVertexTextureTests.hpp"
     25 #include "glsTextureTestUtil.hpp"
     26 #include "gluTexture.hpp"
     27 #include "gluPixelTransfer.hpp"
     28 #include "gluTextureUtil.hpp"
     29 #include "tcuVector.hpp"
     30 #include "tcuMatrix.hpp"
     31 #include "tcuTextureUtil.hpp"
     32 #include "tcuImageCompare.hpp"
     33 #include "deRandom.hpp"
     34 #include "deString.h"
     35 
     36 #include <string>
     37 #include <vector>
     38 
     39 #include <limits>
     40 
     41 #include "glw.h"
     42 
     43 using tcu::TestLog;
     44 using tcu::Vec2;
     45 using tcu::Vec3;
     46 using tcu::Vec4;
     47 using tcu::IVec2;
     48 using tcu::IVec3;
     49 using tcu::IVec4;
     50 using tcu::Mat3;
     51 using std::string;
     52 using std::vector;
     53 
     54 namespace deqp
     55 {
     56 
     57 using namespace gls::TextureTestUtil;
     58 
     59 using gls::TextureTestUtil::TEXTURETYPE_2D;
     60 using gls::TextureTestUtil::TEXTURETYPE_CUBE;
     61 using gls::TextureTestUtil::TEXTURETYPE_2D_ARRAY;
     62 using gls::TextureTestUtil::TEXTURETYPE_3D;
     63 
     64 namespace gles3
     65 {
     66 namespace Functional
     67 {
     68 
     69 static const int WIDTH_2D_ARRAY				= 128;
     70 static const int HEIGHT_2D_ARRAY			= 128;
     71 static const int LAYERS_2D_ARRAY			= 8;
     72 
     73 static const int WIDTH_3D					= 64;
     74 static const int HEIGHT_3D					= 64;
     75 static const int DEPTH_3D					= 64;
     76 
     77 // The 2D case draws four images.
     78 static const int MAX_2D_RENDER_WIDTH		= 128*2;
     79 static const int MAX_2D_RENDER_HEIGHT		= 128*2;
     80 
     81 // The cube map case draws four 3-by-2 image groups.
     82 static const int MAX_CUBE_RENDER_WIDTH		= 28*2*3;
     83 static const int MAX_CUBE_RENDER_HEIGHT		= 28*2*2;
     84 
     85 static const int MAX_2D_ARRAY_RENDER_WIDTH	= 128*2;
     86 static const int MAX_2D_ARRAY_RENDER_HEIGHT	= 128*2;
     87 
     88 static const int MAX_3D_RENDER_WIDTH		= 128*2;
     89 static const int MAX_3D_RENDER_HEIGHT		= 128*2;
     90 
     91 static const int GRID_SIZE_2D				= 127;
     92 static const int GRID_SIZE_CUBE				= 63;
     93 static const int GRID_SIZE_2D_ARRAY			= 127;
     94 static const int GRID_SIZE_3D				= 127;
     95 
     96 // Helpers for making texture coordinates "safe", i.e. move them further from coordinate bounary.
     97 
     98 // Moves x towards the closest K+targetFraction, where K is an integer.
     99 // E.g. moveTowardsFraction(x, 0.5f) moves x away from integer boundaries.
    100 static inline float moveTowardsFraction (float x, float targetFraction)
    101 {
    102 	const float strictness = 0.5f;
    103 	DE_ASSERT(0.0f < strictness && strictness <= 1.0f);
    104 	DE_ASSERT(de::inBounds(targetFraction, 0.0f, 1.0f));
    105 	const float y = x + 0.5f - targetFraction;
    106 	return deFloatFloor(y) + deFloatFrac(y)*(1.0f-strictness) + strictness*0.5f - 0.5f + targetFraction;
    107 }
    108 
    109 static inline float safeCoord (float raw, int scale, float fraction)
    110 {
    111 	const float scaleFloat = (float)scale;
    112 	return moveTowardsFraction(raw*scaleFloat, fraction) / scaleFloat;
    113 }
    114 
    115 template <int Size>
    116 static inline tcu::Vector<float, Size> safeCoords (const tcu::Vector<float, Size>& raw, const tcu::Vector<int, Size>& scale, const tcu::Vector<float, Size>& fraction)
    117 {
    118 	tcu::Vector<float, Size> result;
    119 	for (int i = 0; i < Size; i++)
    120 		result[i] = safeCoord(raw[i], scale[i], fraction[i]);
    121 	return result;
    122 }
    123 
    124 static inline Vec2 safe2DTexCoords (const Vec2& raw, const IVec2& textureSize)
    125 {
    126 	return safeCoords(raw, textureSize, Vec2(0.5f));
    127 }
    128 
    129 static inline Vec3 safe2DArrayTexCoords (const Vec3& raw, const IVec3& textureSize)
    130 {
    131 	return safeCoords(raw, textureSize, Vec3(0.5f, 0.5f, 0.0f));
    132 }
    133 
    134 static inline Vec3 safe3DTexCoords (const Vec3& raw, const IVec3& textureSize)
    135 {
    136 	return safeCoords(raw, textureSize, Vec3(0.5f));
    137 }
    138 
    139 namespace
    140 {
    141 
    142 struct Rect
    143 {
    144 			Rect	(int x_, int y_, int w_, int h_) : x(x_), y(y_), w(w_), h(h_) {}
    145 	IVec2	pos		(void) const { return IVec2(x, y); }
    146 	IVec2	size	(void) const { return IVec2(w, h); }
    147 
    148 	int		x;
    149 	int		y;
    150 	int		w;
    151 	int		h;
    152 };
    153 
    154 template <TextureType> struct TexTypeTcuClass;
    155 template <> struct TexTypeTcuClass<TEXTURETYPE_2D>			{ typedef tcu::Texture2D		t; };
    156 template <> struct TexTypeTcuClass<TEXTURETYPE_CUBE>		{ typedef tcu::TextureCube		t; };
    157 template <> struct TexTypeTcuClass<TEXTURETYPE_2D_ARRAY>	{ typedef tcu::Texture2DArray	t; };
    158 template <> struct TexTypeTcuClass<TEXTURETYPE_3D>			{ typedef tcu::Texture3D		t; };
    159 
    160 template <TextureType> struct TexTypeSizeDims;
    161 template <> struct TexTypeSizeDims<TEXTURETYPE_2D>			{ enum { V = 2 }; };
    162 template <> struct TexTypeSizeDims<TEXTURETYPE_CUBE>		{ enum { V = 2 }; };
    163 template <> struct TexTypeSizeDims<TEXTURETYPE_2D_ARRAY>	{ enum { V = 3 }; };
    164 template <> struct TexTypeSizeDims<TEXTURETYPE_3D>			{ enum { V = 3 }; };
    165 
    166 template <TextureType> struct TexTypeCoordDims;
    167 template <> struct TexTypeCoordDims<TEXTURETYPE_2D>			{ enum { V = 2 }; };
    168 template <> struct TexTypeCoordDims<TEXTURETYPE_CUBE>		{ enum { V = 3 }; };
    169 template <> struct TexTypeCoordDims<TEXTURETYPE_2D_ARRAY>	{ enum { V = 3 }; };
    170 template <> struct TexTypeCoordDims<TEXTURETYPE_3D>			{ enum { V = 3 }; };
    171 
    172 template <TextureType TexType> struct TexTypeSizeIVec		{ typedef tcu::Vector<int,		TexTypeSizeDims<TexType>::V>	t; };
    173 template <TextureType TexType> struct TexTypeCoordVec		{ typedef tcu::Vector<float,	TexTypeCoordDims<TexType>::V>	t; };
    174 
    175 template <TextureType> struct TexTypeCoordParams;
    176 
    177 template <> struct
    178 TexTypeCoordParams<TEXTURETYPE_2D>
    179 {
    180 	Vec2 scale;
    181 	Vec2 bias;
    182 
    183 	TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_) : scale(scale_), bias(bias_) {}
    184 };
    185 
    186 template <> struct
    187 TexTypeCoordParams<TEXTURETYPE_CUBE>
    188 {
    189 	Vec2			scale;
    190 	Vec2			bias;
    191 	tcu::CubeFace	face;
    192 
    193 	TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_, tcu::CubeFace face_) : scale(scale_), bias(bias_), face(face_) {}
    194 };
    195 
    196 template <> struct
    197 TexTypeCoordParams<TEXTURETYPE_2D_ARRAY>
    198 {
    199 	Mat3 transform;
    200 
    201 	TexTypeCoordParams (const Mat3& transform_) : transform(transform_) {}
    202 };
    203 
    204 template <> struct
    205 TexTypeCoordParams<TEXTURETYPE_3D>
    206 {
    207 	Mat3 transform;
    208 
    209 	TexTypeCoordParams (const Mat3& transform_) : transform(transform_) {}
    210 };
    211 
    212 /*--------------------------------------------------------------------*//*!
    213  * \brief Quad grid class containing position and texture coordinate data.
    214  *
    215  * A quad grid of size S means a grid consisting of S*S quads (S rows and
    216  * S columns). The quads are rectangles with main axis aligned sides, and
    217  * each consists of two triangles. Note that although there are only
    218  * (S+1)*(S+1) distinct vertex positions, there are S*S*4 distinct vertices
    219  * because we want texture coordinates to be constant across the vertices
    220  * of a quad (to avoid interpolation issues), and thus each quad needs its
    221  * own 4 vertices.
    222  *
    223  * Pointers returned by get*Ptr() are suitable for gl calls such as
    224  * glVertexAttribPointer() (for position and tex coord) or glDrawElements()
    225  * (for indices).
    226  *//*--------------------------------------------------------------------*/
    227 template <TextureType TexType>
    228 class PosTexCoordQuadGrid
    229 {
    230 private:
    231 	enum { TEX_COORD_DIMS = TexTypeCoordDims <TexType>::V };
    232 	typedef typename TexTypeCoordVec<TexType>::t	TexCoordVec;
    233 	typedef typename TexTypeSizeIVec<TexType>::t	TexSizeIVec;
    234 	typedef TexTypeCoordParams<TexType>				TexCoordParams;
    235 
    236 public:
    237 							PosTexCoordQuadGrid		(int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
    238 
    239 	int						getSize					(void) const { return m_gridSize; }
    240 	Vec4					getQuadLDRU				(int col, int row) const; //!< Vec4(leftX, downY, rightX, upY)
    241 	const TexCoordVec&		getQuadTexCoord			(int col, int row) const;
    242 
    243 	int						getNumIndices			(void) const { return m_gridSize*m_gridSize*3*2; }
    244 	const float*			getPositionPtr			(void) const { DE_STATIC_ASSERT(sizeof(Vec2) == 2*sizeof(float)); return (float*)&m_positions[0]; }
    245 	const float*			getTexCoordPtr			(void) const { DE_STATIC_ASSERT(sizeof(TexCoordVec) == TEX_COORD_DIMS*(int)sizeof(float)); return (float*)&m_texCoords[0]; }
    246 	const deUint16*			getIndexPtr				(void) const { return &m_indices[0]; }
    247 
    248 private:
    249 	void					initializeTexCoords		(const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
    250 
    251 	const int				m_gridSize;
    252 	vector<Vec2>			m_positions;
    253 	vector<TexCoordVec>		m_texCoords;
    254 	vector<deUint16>		m_indices;
    255 };
    256 
    257 template <TextureType TexType>
    258 Vec4 PosTexCoordQuadGrid<TexType>::getQuadLDRU (int col, int row) const
    259 {
    260 	int ndx00 = (row*m_gridSize + col) * 4;
    261 	int ndx11 = ndx00 + 3;
    262 
    263 	return Vec4(m_positions[ndx00].x(),
    264 				m_positions[ndx00].y(),
    265 				m_positions[ndx11].x(),
    266 				m_positions[ndx11].y());
    267 }
    268 
    269 template <TextureType TexType>
    270 const typename TexTypeCoordVec<TexType>::t& PosTexCoordQuadGrid<TexType>::getQuadTexCoord (int col, int row) const
    271 {
    272 	return m_texCoords[(row*m_gridSize + col) * 4];
    273 }
    274 
    275 template <TextureType TexType>
    276 PosTexCoordQuadGrid<TexType>::PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    277 	: m_gridSize(gridSize)
    278 {
    279 	DE_ASSERT(m_gridSize > 0 && m_gridSize*m_gridSize <= (int)std::numeric_limits<deUint16>::max() + 1);
    280 
    281 	const float gridSizeFloat = (float)m_gridSize;
    282 
    283 	m_positions.reserve(m_gridSize*m_gridSize*4);
    284 	m_indices.reserve(m_gridSize*m_gridSize*3*2);
    285 
    286 	for (int y = 0; y < m_gridSize; y++)
    287 	for (int x = 0; x < m_gridSize; x++)
    288 	{
    289 		float fx0 = (float)(x+0) / gridSizeFloat;
    290 		float fx1 = (float)(x+1) / gridSizeFloat;
    291 		float fy0 = (float)(y+0) / gridSizeFloat;
    292 		float fy1 = (float)(y+1) / gridSizeFloat;
    293 
    294 		Vec2 quadVertices[4] = { Vec2(fx0, fy0), Vec2(fx1, fy0), Vec2(fx0, fy1), Vec2(fx1, fy1) };
    295 
    296 		int firstNdx = (int)m_positions.size();
    297 
    298 		for (int i = 0; i < DE_LENGTH_OF_ARRAY(quadVertices); i++)
    299 			m_positions.push_back(safeCoords(quadVertices[i], renderSize, Vec2(0.0f)) * 2.0f - 1.0f);
    300 
    301 		m_indices.push_back(firstNdx + 0);
    302 		m_indices.push_back(firstNdx + 1);
    303 		m_indices.push_back(firstNdx + 2);
    304 
    305 		m_indices.push_back(firstNdx + 1);
    306 		m_indices.push_back(firstNdx + 3);
    307 		m_indices.push_back(firstNdx + 2);
    308 	}
    309 
    310 	m_texCoords.reserve(m_gridSize*m_gridSize*4);
    311 	initializeTexCoords(textureSize, texCoordParams, useSafeTexCoords);
    312 
    313 	DE_ASSERT((int)m_positions.size() == m_gridSize*m_gridSize*4);
    314 	DE_ASSERT((int)m_indices.size() == m_gridSize*m_gridSize*3*2);
    315 	DE_ASSERT((int)m_texCoords.size() == m_gridSize*m_gridSize*4);
    316 }
    317 
    318 template <>
    319 void PosTexCoordQuadGrid<TEXTURETYPE_2D>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    320 {
    321 	DE_ASSERT(m_texCoords.empty());
    322 
    323 	const float gridSizeFloat = (float)m_gridSize;
    324 
    325 	for (int y = 0; y < m_gridSize; y++)
    326 	for (int x = 0; x < m_gridSize; x++)
    327 	{
    328 		Vec2 rawCoord = Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) * texCoordParams.scale + texCoordParams.bias;
    329 
    330 		for (int i = 0; i < 4; i++)
    331 			m_texCoords.push_back(useSafeTexCoords ? safe2DTexCoords(rawCoord, textureSize) : rawCoord);
    332 	}
    333 }
    334 
    335 template <>
    336 void PosTexCoordQuadGrid<TEXTURETYPE_CUBE>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    337 {
    338 	DE_ASSERT(m_texCoords.empty());
    339 
    340 	const float		gridSizeFloat	= (float)m_gridSize;
    341 	vector<float>	texBoundaries;
    342 	computeQuadTexCoordCube(texBoundaries, texCoordParams.face);
    343 	const Vec3		coordA			= Vec3(texBoundaries[0], texBoundaries[1], texBoundaries[2]);
    344 	const Vec3		coordB			= Vec3(texBoundaries[3], texBoundaries[4], texBoundaries[5]);
    345 	const Vec3		coordC			= Vec3(texBoundaries[6], texBoundaries[7], texBoundaries[8]);
    346 	const Vec3		coordAB			= coordB - coordA;
    347 	const Vec3		coordAC			= coordC - coordA;
    348 
    349 	for (int y = 0; y < m_gridSize; y++)
    350 	for (int x = 0; x < m_gridSize; x++)
    351 	{
    352 		const Vec2 rawFaceCoord		= texCoordParams.scale * Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) + texCoordParams.bias;
    353 		const Vec2 safeFaceCoord	= useSafeTexCoords ? safe2DTexCoords(rawFaceCoord, textureSize) : rawFaceCoord;
    354 		const Vec3 texCoord			= coordA + coordAC*safeFaceCoord.x() + coordAB*safeFaceCoord.y();
    355 
    356 		for (int i = 0; i < 4; i++)
    357 			m_texCoords.push_back(texCoord);
    358 	}
    359 }
    360 
    361 template <>
    362 void PosTexCoordQuadGrid<TEXTURETYPE_2D_ARRAY>::initializeTexCoords (const IVec3& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    363 {
    364 	DE_ASSERT(m_texCoords.empty());
    365 
    366 	const float gridSizeFloat = (float)m_gridSize;
    367 
    368 	for (int y = 0; y < m_gridSize; y++)
    369 	for (int x = 0; x < m_gridSize; x++)
    370 	{
    371 		const Vec3 rawCoord = texCoordParams.transform * Vec3((float)x / gridSizeFloat, (float)y / gridSizeFloat, 1.0f);
    372 
    373 		for (int i = 0; i < 4; i++)
    374 			m_texCoords.push_back(useSafeTexCoords ? safe2DArrayTexCoords(rawCoord, textureSize) : rawCoord);
    375 	}
    376 }
    377 
    378 template <>
    379 void PosTexCoordQuadGrid<TEXTURETYPE_3D>::initializeTexCoords (const IVec3& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    380 {
    381 	DE_ASSERT(m_texCoords.empty());
    382 
    383 	const float gridSizeFloat = (float)m_gridSize;
    384 
    385 	for (int y = 0; y < m_gridSize; y++)
    386 	for (int x = 0; x < m_gridSize; x++)
    387 	{
    388 		Vec3 rawCoord = texCoordParams.transform * Vec3((float)x / gridSizeFloat, (float)y / gridSizeFloat, 1.0f);
    389 
    390 		for (int i = 0; i < 4; i++)
    391 			m_texCoords.push_back(useSafeTexCoords ? safe3DTexCoords(rawCoord, textureSize) : rawCoord);
    392 	}
    393 }
    394 
    395 } // anonymous
    396 
    397 static inline bool isLevelNearest (deUint32 filter)
    398 {
    399 	return filter == GL_NEAREST || filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_NEAREST_MIPMAP_LINEAR;
    400 }
    401 
    402 static inline IVec2 getTextureSize (const glu::Texture2D& tex)
    403 {
    404 	const tcu::Texture2D& ref = tex.getRefTexture();
    405 	return IVec2(ref.getWidth(), ref.getHeight());
    406 }
    407 
    408 static inline IVec2 getTextureSize (const glu::TextureCube& tex)
    409 {
    410 	const tcu::TextureCube& ref = tex.getRefTexture();
    411 	return IVec2(ref.getSize(), ref.getSize());
    412 }
    413 
    414 static inline IVec3 getTextureSize (const glu::Texture2DArray& tex)
    415 {
    416 	const tcu::Texture2DArray& ref = tex.getRefTexture();
    417 	return IVec3(ref.getWidth(), ref.getHeight(), ref.getNumLayers());
    418 }
    419 
    420 static inline IVec3 getTextureSize (const glu::Texture3D& tex)
    421 {
    422 	const tcu::Texture3D& ref = tex.getRefTexture();
    423 	return IVec3(ref.getWidth(), ref.getHeight(), ref.getDepth());
    424 }
    425 
    426 template <TextureType TexType>
    427 static void setPixelColors (const vector<Vec4>& quadColors, const Rect& region, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst)
    428 {
    429 	const int gridSize = grid.getSize();
    430 
    431 	for (int y = 0; y < gridSize; y++)
    432 	for (int x = 0; x < gridSize; x++)
    433 	{
    434 		const Vec4	color	= quadColors[y*gridSize + x];
    435 		const Vec4	ldru	= grid.getQuadLDRU(x, y) * 0.5f + 0.5f; // [-1, 1] -> [0, 1]
    436 		const int	ix0		= deCeilFloatToInt32(ldru.x() * (float)region.w - 0.5f);
    437 		const int	ix1		= deCeilFloatToInt32(ldru.z() * (float)region.w - 0.5f);
    438 		const int	iy0		= deCeilFloatToInt32(ldru.y() * (float)region.h - 0.5f);
    439 		const int	iy1		= deCeilFloatToInt32(ldru.w() * (float)region.h - 0.5f);
    440 
    441 		for (int iy = iy0; iy < iy1; iy++)
    442 		for (int ix = ix0; ix < ix1; ix++)
    443 		{
    444 			DE_ASSERT(deInBounds32(ix + region.x, 0, dst.getWidth()));
    445 			DE_ASSERT(deInBounds32(iy + region.y, 0, dst.getHeight()));
    446 
    447 			dst.setPixel(ix + region.x, iy + region.y, toRGBA(color));
    448 		}
    449 	}
    450 }
    451 
    452 static inline Vec4 sample (const tcu::Texture2D&		tex, const Vec2& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), lod); }
    453 static inline Vec4 sample (const tcu::TextureCube&		tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); }
    454 static inline Vec4 sample (const tcu::Texture2DArray&	tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); }
    455 static inline Vec4 sample (const tcu::Texture3D&		tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); }
    456 
    457 template <TextureType TexType>
    458 void computeReference (const typename TexTypeTcuClass<TexType>::t& texture, float lod, const tcu::Sampler& sampler, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst, const Rect& dstRegion)
    459 {
    460 	const int		gridSize	= grid.getSize();
    461 	vector<Vec4>	quadColors	(gridSize*gridSize);
    462 
    463 	for (int y = 0; y < gridSize; y++)
    464 	for (int x = 0; x < gridSize; x++)
    465 	{
    466 		const int										ndx		= y*gridSize + x;
    467 		const typename TexTypeCoordVec<TexType>::t&		coord	= grid.getQuadTexCoord(x, y);
    468 
    469 		quadColors[ndx] = sample(texture, coord, lod, sampler);
    470 	}
    471 
    472 	setPixelColors(quadColors, dstRegion, grid, dst);
    473 }
    474 
    475 static bool compareImages (const glu::RenderContext& renderCtx, tcu::TestLog& log, const tcu::Surface& ref, const tcu::Surface& res)
    476 {
    477 	DE_ASSERT(renderCtx.getRenderTarget().getNumSamples() == 0);
    478 
    479 	const tcu::RGBA threshold = renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(15,15,15,15);
    480 	return tcu::pixelThresholdCompare(log, "Result", "Image compare result", ref, res, threshold, tcu::COMPARE_LOG_RESULT);
    481 }
    482 
    483 class Vertex2DTextureCase : public TestCase
    484 {
    485 public:
    486 								Vertex2DTextureCase		(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
    487 								~Vertex2DTextureCase	(void);
    488 
    489 	void						init					(void);
    490 	void						deinit					(void);
    491 	IterateResult				iterate					(void);
    492 
    493 private:
    494 	typedef PosTexCoordQuadGrid<TEXTURETYPE_2D> Grid;
    495 
    496 								Vertex2DTextureCase		(const Vertex2DTextureCase& other);
    497 	Vertex2DTextureCase&		operator=				(const Vertex2DTextureCase& other);
    498 
    499 	float						calculateLod			(const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
    500 	void						setupShaderInputs		(int textureNdx, float lod, const Grid& grid) const;
    501 	void						renderCell				(int textureNdx, float lod, const Grid& grid) const;
    502 	void						computeReferenceCell	(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
    503 
    504 	const deUint32				m_minFilter;
    505 	const deUint32				m_magFilter;
    506 	const deUint32				m_wrapS;
    507 	const deUint32				m_wrapT;
    508 
    509 	const glu::ShaderProgram*	m_program;
    510 	glu::Texture2D*				m_textures[2];	// 2 textures, a gradient texture and a grid texture.
    511 };
    512 
    513 Vertex2DTextureCase::Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
    514 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
    515 	, m_minFilter			(minFilter)
    516 	, m_magFilter			(magFilter)
    517 	, m_wrapS				(wrapS)
    518 	, m_wrapT				(wrapT)
    519 	, m_program				(DE_NULL)
    520 {
    521 	m_textures[0] = DE_NULL;
    522 	m_textures[1] = DE_NULL;
    523 }
    524 
    525 Vertex2DTextureCase::~Vertex2DTextureCase(void)
    526 {
    527 	Vertex2DTextureCase::deinit();
    528 }
    529 
    530 void Vertex2DTextureCase::init (void)
    531 {
    532 	const char* const vertexShader =
    533 		"#version 300 es\n"
    534 		"in highp vec2 a_position;\n"
    535 		"in highp vec2 a_texCoord;\n"
    536 		"uniform highp sampler2D u_texture;\n"
    537 		"uniform highp float u_lod;\n"
    538 		"out mediump vec4 v_color;\n"
    539 		"\n"
    540 		"void main()\n"
    541 		"{\n"
    542 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
    543 		"	v_color = textureLod(u_texture, a_texCoord, u_lod);\n"
    544 		"}\n";
    545 
    546 	const char* const fragmentShader =
    547 		"#version 300 es\n"
    548 		"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
    549 		"in mediump vec4 v_color;\n"
    550 		"\n"
    551 		"void main()\n"
    552 		"{\n"
    553 		"	dEQP_FragColor = v_color;\n"
    554 		"}\n";
    555 
    556 	if (m_context.getRenderTarget().getNumSamples() != 0)
    557 		throw tcu::NotSupportedError("MSAA config not supported by this test");
    558 
    559 	DE_ASSERT(!m_program);
    560 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
    561 
    562 	if(!m_program->isOk())
    563 	{
    564 		m_testCtx.getLog() << *m_program;
    565 
    566 		GLint maxVertexTextures;
    567 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
    568 
    569 		if (maxVertexTextures < 1)
    570 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
    571 		else
    572 			TCU_FAIL("Failed to compile shader");
    573 	}
    574 
    575 	// Make the textures.
    576 	try
    577 	{
    578 		// Compute suitable power-of-two sizes (for mipmaps).
    579 		const int texWidth		= 1 << deLog2Ceil32(MAX_2D_RENDER_WIDTH / 2);
    580 		const int texHeight		= 1 << deLog2Ceil32(MAX_2D_RENDER_HEIGHT / 2);
    581 
    582 		for (int i = 0; i < 2; i++)
    583 		{
    584 			DE_ASSERT(!m_textures[i]);
    585 			m_textures[i] = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight);
    586 		}
    587 
    588 		const bool						mipmaps		= (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight));
    589 		const int						numLevels	= mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1;
    590 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    591 		const Vec4						cBias		= fmtInfo.valueMin;
    592 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    593 
    594 		// Fill first with gradient texture.
    595 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    596 		{
    597 			const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
    598 			const Vec4 gMax = Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
    599 
    600 			m_textures[0]->getRefTexture().allocLevel(levelNdx);
    601 			tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
    602 		}
    603 
    604 		// Fill second with grid texture.
    605 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    606 		{
    607 			const deUint32 step		= 0x00ffffff / numLevels;
    608 			const deUint32 rgb		= step*levelNdx;
    609 			const deUint32 colorA	= 0xff000000 | rgb;
    610 			const deUint32 colorB	= 0xff000000 | ~rgb;
    611 
    612 			m_textures[1]->getRefTexture().allocLevel(levelNdx);
    613 			tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
    614 		}
    615 
    616 		// Upload.
    617 		for (int i = 0; i < 2; i++)
    618 			m_textures[i]->upload();
    619 	}
    620 	catch (const std::exception&)
    621 	{
    622 		// Clean up to save memory.
    623 		Vertex2DTextureCase::deinit();
    624 		throw;
    625 	}
    626 }
    627 
    628 void Vertex2DTextureCase::deinit (void)
    629 {
    630 	for (int i = 0; i < 2; i++)
    631 	{
    632 		delete m_textures[i];
    633 		m_textures[i] = DE_NULL;
    634 	}
    635 
    636 	delete m_program;
    637 	m_program = DE_NULL;
    638 }
    639 
    640 float Vertex2DTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
    641 {
    642 	const tcu::Texture2D&		refTexture	= m_textures[textureNdx]->getRefTexture();
    643 	const Vec2					srcSize		= Vec2((float)refTexture.getWidth(), (float)refTexture.getHeight());
    644 	const Vec2					sizeRatio	= texScale*srcSize / dstSize;
    645 
    646 	// \note In this particular case dv/dx and du/dy are zero, simplifying the expression.
    647 	return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
    648 }
    649 
    650 Vertex2DTextureCase::IterateResult Vertex2DTextureCase::iterate (void)
    651 {
    652 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_RENDER_WIDTH);
    653 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_RENDER_HEIGHT);
    654 
    655 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
    656 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
    657 
    658 	de::Random	rnd					(deStringHash(getName()));
    659 
    660 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
    661 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
    662 
    663 	glUseProgram(m_program->getProgram());
    664 
    665 	// Divide viewport into 4 cells.
    666 	const int leftWidth		= viewportWidth / 2;
    667 	const int rightWidth	= viewportWidth - leftWidth;
    668 	const int bottomHeight	= viewportHeight / 2;
    669 	const int topHeight		= viewportHeight - bottomHeight;
    670 
    671 	// Clear.
    672 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
    673 	glClear(GL_COLOR_BUFFER_BIT);
    674 
    675 	// Texture scaling and offsetting vectors.
    676 	const Vec2 texMinScale		(+1.8f, +1.8f);
    677 	const Vec2 texMinOffset		(-0.3f, -0.2f);
    678 	const Vec2 texMagScale		(+0.3f, +0.3f);
    679 	const Vec2 texMagOffset		(+0.9f, +0.8f);
    680 
    681 	// Surface for the reference image.
    682 	tcu::Surface refImage(viewportWidth, viewportHeight);
    683 
    684 	{
    685 		const struct Render
    686 		{
    687 			const Rect	region;
    688 			int			textureNdx;
    689 			const Vec2	texCoordScale;
    690 			const Vec2	texCoordOffset;
    691 			Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
    692 		} renders[] =
    693 		{
    694 			Render(Rect(0,				0,				leftWidth,	bottomHeight),	0, texMinScale, texMinOffset),
    695 			Render(Rect(leftWidth,		0,				rightWidth,	bottomHeight),	0, texMagScale, texMagOffset),
    696 			Render(Rect(0,				bottomHeight,	leftWidth,	topHeight),		1, texMinScale, texMinOffset),
    697 			Render(Rect(leftWidth,		bottomHeight,	rightWidth,	topHeight),		1, texMagScale, texMagOffset)
    698 		};
    699 
    700 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
    701 		{
    702 			const Render&	rend				= renders[renderNdx];
    703 			const float		lod					= calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
    704 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
    705 			const Grid		grid				(GRID_SIZE_2D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
    706 												 TexTypeCoordParams<TEXTURETYPE_2D>(rend.texCoordScale, rend.texCoordOffset), useSafeTexCoords);
    707 
    708 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
    709 			renderCell				(rend.textureNdx, lod, grid);
    710 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
    711 		}
    712 	}
    713 
    714 	// Read back rendered results.
    715 	tcu::Surface resImage(viewportWidth, viewportHeight);
    716 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
    717 
    718 	glUseProgram(0);
    719 
    720 	// Compare and log.
    721 	{
    722 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
    723 
    724 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    725 								isOk ? "Pass"				: "Image comparison failed");
    726 	}
    727 
    728 	return STOP;
    729 }
    730 
    731 void Vertex2DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
    732 {
    733 	const deUint32 programID = m_program->getProgram();
    734 
    735 	// SETUP ATTRIBUTES.
    736 
    737 	{
    738 		const int positionLoc = glGetAttribLocation(programID, "a_position");
    739 		if (positionLoc != -1)
    740 		{
    741 			glEnableVertexAttribArray(positionLoc);
    742 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
    743 		}
    744 	}
    745 
    746 	{
    747 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
    748 		if (texCoordLoc != -1)
    749 		{
    750 			glEnableVertexAttribArray(texCoordLoc);
    751 			glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
    752 		}
    753 	}
    754 
    755 	// SETUP UNIFORMS.
    756 
    757 	{
    758 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
    759 		if (lodLoc != -1)
    760 			glUniform1f(lodLoc, lod);
    761 	}
    762 
    763 	glActiveTexture(GL_TEXTURE0);
    764 	glBindTexture(GL_TEXTURE_2D, m_textures[textureNdx]->getGLTexture());
    765 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		m_wrapS);
    766 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		m_wrapT);
    767 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
    768 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
    769 
    770 	{
    771 		const int texLoc = glGetUniformLocation(programID, "u_texture");
    772 		if (texLoc != -1)
    773 			glUniform1i(texLoc, 0);
    774 	}
    775 }
    776 
    777 // Renders one sub-image with given parameters.
    778 void Vertex2DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
    779 {
    780 	setupShaderInputs(textureNdx, lod, grid);
    781 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
    782 }
    783 
    784 void Vertex2DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
    785 {
    786 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion);
    787 }
    788 
    789 class VertexCubeTextureCase : public TestCase
    790 {
    791 public:
    792 								VertexCubeTextureCase	(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
    793 								~VertexCubeTextureCase	(void);
    794 
    795 	void						init					(void);
    796 	void						deinit					(void);
    797 	IterateResult				iterate					(void);
    798 
    799 private:
    800 	typedef PosTexCoordQuadGrid<TEXTURETYPE_CUBE> Grid;
    801 
    802 								VertexCubeTextureCase	(const VertexCubeTextureCase& other);
    803 	VertexCubeTextureCase&		operator=				(const VertexCubeTextureCase& other);
    804 
    805 	float						calculateLod			(const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
    806 	void						setupShaderInputs		(int textureNdx, float lod, const Grid& grid) const;
    807 	void						renderCell				(int textureNdx, float lod, const Grid& grid) const;
    808 	void						computeReferenceCell	(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
    809 
    810 	const deUint32				m_minFilter;
    811 	const deUint32				m_magFilter;
    812 	const deUint32				m_wrapS;
    813 	const deUint32				m_wrapT;
    814 
    815 	const glu::ShaderProgram*	m_program;
    816 	glu::TextureCube*			m_textures[2];	// 2 textures, a gradient texture and a grid texture.
    817 };
    818 
    819 VertexCubeTextureCase::VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
    820 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
    821 	, m_minFilter			(minFilter)
    822 	, m_magFilter			(magFilter)
    823 	, m_wrapS				(wrapS)
    824 	, m_wrapT				(wrapT)
    825 	, m_program				(DE_NULL)
    826 {
    827 	m_textures[0] = DE_NULL;
    828 	m_textures[1] = DE_NULL;
    829 }
    830 
    831 VertexCubeTextureCase::~VertexCubeTextureCase(void)
    832 {
    833 	VertexCubeTextureCase::deinit();
    834 }
    835 
    836 void VertexCubeTextureCase::init (void)
    837 {
    838 	const char* const vertexShader =
    839 		"#version 300 es\n"
    840 		"in highp vec2 a_position;\n"
    841 		"in highp vec3 a_texCoord;\n"
    842 		"uniform highp samplerCube u_texture;\n"
    843 		"uniform highp float u_lod;\n"
    844 		"out mediump vec4 v_color;\n"
    845 		"\n"
    846 		"void main()\n"
    847 		"{\n"
    848 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
    849 		"	v_color = textureLod(u_texture, a_texCoord, u_lod);\n"
    850 		"}\n";
    851 
    852 	const char* const fragmentShader =
    853 		"#version 300 es\n"
    854 		"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
    855 		"in mediump vec4 v_color;\n"
    856 		"\n"
    857 		"void main()\n"
    858 		"{\n"
    859 		"	dEQP_FragColor = v_color;\n"
    860 		"}\n";
    861 
    862 	if (m_context.getRenderTarget().getNumSamples() != 0)
    863 		throw tcu::NotSupportedError("MSAA config not supported by this test");
    864 
    865 	DE_ASSERT(!m_program);
    866 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
    867 
    868 	if(!m_program->isOk())
    869 	{
    870 		m_testCtx.getLog() << *m_program;
    871 
    872 		GLint maxVertexTextures;
    873 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
    874 
    875 		if (maxVertexTextures < 1)
    876 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
    877 		else
    878 			TCU_FAIL("Failed to compile shader");
    879 	}
    880 
    881 	// Make the textures.
    882 	try
    883 	{
    884 		// Compute suitable power-of-two sizes (for mipmaps).
    885 		const int texWidth		= 1 << deLog2Ceil32(MAX_CUBE_RENDER_WIDTH / 3 / 2);
    886 		const int texHeight		= 1 << deLog2Ceil32(MAX_CUBE_RENDER_HEIGHT / 2 / 2);
    887 
    888 		DE_ASSERT(texWidth == texHeight);
    889 		DE_UNREF(texHeight);
    890 
    891 		for (int i = 0; i < 2; i++)
    892 		{
    893 			DE_ASSERT(!m_textures[i]);
    894 			m_textures[i] = new glu::TextureCube(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth);
    895 		}
    896 
    897 		const bool						mipmaps		= deIsPowerOfTwo32(texWidth) != DE_FALSE;
    898 		const int						numLevels	= mipmaps ? deLog2Floor32(texWidth)+1 : 1;
    899 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    900 		const Vec4						cBias		= fmtInfo.valueMin;
    901 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    902 
    903 		// Fill first with gradient texture.
    904 		static const Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    905 		{
    906 			{ Vec4(-1.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    907 			{ Vec4( 0.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    908 			{ Vec4(-1.0f,  0.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    909 			{ Vec4(-1.0f, -1.0f,  0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    910 			{ Vec4(-1.0f, -1.0f, -1.0f, 0.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    911 			{ Vec4( 0.0f,  0.0f,  0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    912 		};
    913 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    914 		{
    915 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    916 			{
    917 				m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    918 				tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
    919 			}
    920 		}
    921 
    922 		// Fill second with grid texture.
    923 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    924 		{
    925 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    926 			{
    927 				const deUint32 step		= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
    928 				const deUint32 rgb		= step*levelNdx*face;
    929 				const deUint32 colorA	= 0xff000000 | rgb;
    930 				const deUint32 colorB	= 0xff000000 | ~rgb;
    931 
    932 				m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    933 				tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
    934 			}
    935 		}
    936 
    937 		// Upload.
    938 		for (int i = 0; i < 2; i++)
    939 			m_textures[i]->upload();
    940 	}
    941 	catch (const std::exception&)
    942 	{
    943 		// Clean up to save memory.
    944 		VertexCubeTextureCase::deinit();
    945 		throw;
    946 	}
    947 }
    948 
    949 void VertexCubeTextureCase::deinit (void)
    950 {
    951 	for (int i = 0; i < 2; i++)
    952 	{
    953 		delete m_textures[i];
    954 		m_textures[i] = DE_NULL;
    955 	}
    956 
    957 	delete m_program;
    958 	m_program = DE_NULL;
    959 }
    960 
    961 float VertexCubeTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
    962 {
    963 	const tcu::TextureCube&		refTexture	= m_textures[textureNdx]->getRefTexture();
    964 	const Vec2					srcSize		= Vec2((float)refTexture.getSize(), (float)refTexture.getSize());
    965 	const Vec2					sizeRatio	= texScale*srcSize / dstSize;
    966 
    967 	// \note In this particular case, dv/dx and du/dy are zero, simplifying the expression.
    968 	return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
    969 }
    970 
    971 VertexCubeTextureCase::IterateResult VertexCubeTextureCase::iterate (void)
    972 {
    973 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_CUBE_RENDER_WIDTH);
    974 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_CUBE_RENDER_HEIGHT);
    975 
    976 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
    977 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
    978 
    979 	de::Random	rnd					(deStringHash(getName()));
    980 
    981 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
    982 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
    983 
    984 	glUseProgram(m_program->getProgram());
    985 
    986 	// Divide viewport into 4 areas.
    987 	const int leftWidth		= viewportWidth / 2;
    988 	const int rightWidth	= viewportWidth - leftWidth;
    989 	const int bottomHeight	= viewportHeight / 2;
    990 	const int topHeight		= viewportHeight - bottomHeight;
    991 
    992 	// Clear.
    993 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
    994 	glClear(GL_COLOR_BUFFER_BIT);
    995 
    996 	// Texture scaling and offsetting vectors.
    997 	const Vec2 texMinScale		(1.0f, 1.0f);
    998 	const Vec2 texMinOffset		(0.0f, 0.0f);
    999 	const Vec2 texMagScale		(0.3f, 0.3f);
   1000 	const Vec2 texMagOffset		(0.5f, 0.3f);
   1001 
   1002 	// Surface for the reference image.
   1003 	tcu::Surface refImage(viewportWidth, viewportHeight);
   1004 
   1005 	// Each of the four areas is divided into 6 cells.
   1006 	const int defCellWidth	= viewportWidth / 2 / 3;
   1007 	const int defCellHeight	= viewportHeight / 2 / 2;
   1008 
   1009 	for (int i = 0; i < tcu::CUBEFACE_LAST; i++)
   1010 	{
   1011 		const int	cellOffsetX			= defCellWidth * (i % 3);
   1012 		const int	cellOffsetY			= defCellHeight * (i / 3);
   1013 		const bool	isRightmostCell		= i == 2 || i == 5;
   1014 		const bool	isTopCell			= i >= 3;
   1015 		const int	leftCellWidth		= isRightmostCell	? leftWidth		- cellOffsetX : defCellWidth;
   1016 		const int	rightCellWidth		= isRightmostCell	? rightWidth	- cellOffsetX : defCellWidth;
   1017 		const int	bottomCellHeight	= isTopCell			? bottomHeight	- cellOffsetY : defCellHeight;
   1018 		const int	topCellHeight		= isTopCell			? topHeight		- cellOffsetY : defCellHeight;
   1019 
   1020 		const struct Render
   1021 		{
   1022 			const Rect	region;
   1023 			int			textureNdx;
   1024 			const Vec2	texCoordScale;
   1025 			const Vec2	texCoordOffset;
   1026 			Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
   1027 		} renders[] =
   1028 		{
   1029 			Render(Rect(cellOffsetX + 0,			cellOffsetY + 0,				leftCellWidth,	bottomCellHeight),	0, texMinScale, texMinOffset),
   1030 			Render(Rect(cellOffsetX + leftWidth,	cellOffsetY + 0,				rightCellWidth,	bottomCellHeight),	0, texMagScale, texMagOffset),
   1031 			Render(Rect(cellOffsetX + 0,			cellOffsetY + bottomHeight,		leftCellWidth,	topCellHeight),		1, texMinScale, texMinOffset),
   1032 			Render(Rect(cellOffsetX + leftWidth,	cellOffsetY + bottomHeight,		rightCellWidth,	topCellHeight),		1, texMagScale, texMagOffset)
   1033 		};
   1034 
   1035 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
   1036 		{
   1037 			const Render&	rend				= renders[renderNdx];
   1038 			const float		lod					= calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
   1039 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
   1040 			const Grid		grid				(GRID_SIZE_CUBE, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
   1041 												 TexTypeCoordParams<TEXTURETYPE_CUBE>(rend.texCoordScale, rend.texCoordOffset, (tcu::CubeFace)i), useSafeTexCoords);
   1042 
   1043 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
   1044 			renderCell				(rend.textureNdx, lod, grid);
   1045 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
   1046 		}
   1047 	}
   1048 
   1049 	// Read back rendered results.
   1050 	tcu::Surface resImage(viewportWidth, viewportHeight);
   1051 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
   1052 
   1053 	glUseProgram(0);
   1054 
   1055 	// Compare and log.
   1056 	{
   1057 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
   1058 
   1059 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
   1060 								isOk ? "Pass"				: "Image comparison failed");
   1061 	}
   1062 
   1063 	return STOP;
   1064 }
   1065 
   1066 void VertexCubeTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
   1067 {
   1068 	const deUint32 programID = m_program->getProgram();
   1069 
   1070 	// SETUP ATTRIBUTES.
   1071 
   1072 	{
   1073 		const int positionLoc = glGetAttribLocation(programID, "a_position");
   1074 		if (positionLoc != -1)
   1075 		{
   1076 			glEnableVertexAttribArray(positionLoc);
   1077 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
   1078 		}
   1079 	}
   1080 
   1081 	{
   1082 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
   1083 		if (texCoordLoc != -1)
   1084 		{
   1085 			glEnableVertexAttribArray(texCoordLoc);
   1086 			glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
   1087 		}
   1088 	}
   1089 
   1090 	// SETUP UNIFORMS.
   1091 
   1092 	{
   1093 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
   1094 		if (lodLoc != -1)
   1095 			glUniform1f(lodLoc, lod);
   1096 	}
   1097 
   1098 	glActiveTexture(GL_TEXTURE0);
   1099 	glBindTexture(GL_TEXTURE_CUBE_MAP, m_textures[textureNdx]->getGLTexture());
   1100 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
   1101 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
   1102 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
   1103 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
   1104 
   1105 	{
   1106 		const int texLoc = glGetUniformLocation(programID, "u_texture");
   1107 		if (texLoc != -1)
   1108 			glUniform1i(texLoc, 0);
   1109 	}
   1110 }
   1111 
   1112 // Renders one cube face with given parameters.
   1113 void VertexCubeTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
   1114 {
   1115 	setupShaderInputs(textureNdx, lod, grid);
   1116 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
   1117 }
   1118 
   1119 // Computes reference for one cube face with given parameters.
   1120 void VertexCubeTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
   1121 {
   1122 	tcu::Sampler sampler = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
   1123 	sampler.seamlessCubeMap = true;
   1124 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, sampler, grid, dst, dstRegion);
   1125 }
   1126 
   1127 class Vertex2DArrayTextureCase : public TestCase
   1128 {
   1129 public:
   1130 								Vertex2DArrayTextureCase	(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
   1131 								~Vertex2DArrayTextureCase	(void);
   1132 
   1133 	void						init						(void);
   1134 	void						deinit						(void);
   1135 	IterateResult				iterate						(void);
   1136 
   1137 private:
   1138 	typedef PosTexCoordQuadGrid<TEXTURETYPE_2D_ARRAY> Grid;
   1139 
   1140 								Vertex2DArrayTextureCase	(const Vertex2DArrayTextureCase& other);
   1141 	Vertex2DArrayTextureCase&	operator=					(const Vertex2DArrayTextureCase& other);
   1142 
   1143 	float						calculateLod				(const Mat3& transf, const Vec2& dstSize, int textureNdx) const;
   1144 	void						setupShaderInputs			(int textureNdx, float lod, const Grid& grid) const;
   1145 	void						renderCell					(int textureNdx, float lod, const Grid& grid) const;
   1146 	void						computeReferenceCell		(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
   1147 
   1148 	const deUint32				m_minFilter;
   1149 	const deUint32				m_magFilter;
   1150 	const deUint32				m_wrapS;
   1151 	const deUint32				m_wrapT;
   1152 
   1153 	const glu::ShaderProgram*	m_program;
   1154 	glu::Texture2DArray*		m_textures[2];	// 2 textures, a gradient texture and a grid texture.
   1155 };
   1156 
   1157 Vertex2DArrayTextureCase::Vertex2DArrayTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
   1158 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
   1159 	, m_minFilter			(minFilter)
   1160 	, m_magFilter			(magFilter)
   1161 	, m_wrapS				(wrapS)
   1162 	, m_wrapT				(wrapT)
   1163 	, m_program				(DE_NULL)
   1164 {
   1165 	m_textures[0] = DE_NULL;
   1166 	m_textures[1] = DE_NULL;
   1167 }
   1168 
   1169 Vertex2DArrayTextureCase::~Vertex2DArrayTextureCase(void)
   1170 {
   1171 	Vertex2DArrayTextureCase::deinit();
   1172 }
   1173 
   1174 void Vertex2DArrayTextureCase::init (void)
   1175 {
   1176 	const char* const vertexShaderSource =
   1177 		"#version 300 es\n"
   1178 		"in highp vec2 a_position;\n"
   1179 		"in highp vec3 a_texCoord;\n"
   1180 		"uniform highp sampler2DArray u_texture;\n"
   1181 		"uniform highp float u_lod;\n"
   1182 		"out mediump vec4 v_color;\n"
   1183 		"\n"
   1184 		"void main()\n"
   1185 		"{\n"
   1186 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
   1187 		"	v_color = textureLod(u_texture, a_texCoord, u_lod);\n"
   1188 		"}\n";
   1189 
   1190 	const char* const fragmentShaderSource =
   1191 		"#version 300 es\n"
   1192 		"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
   1193 		"in mediump vec4 v_color;\n"
   1194 		"\n"
   1195 		"void main()\n"
   1196 		"{\n"
   1197 		"	dEQP_FragColor = v_color;\n"
   1198 		"}\n";
   1199 
   1200 	if (m_context.getRenderTarget().getNumSamples() != 0)
   1201 		throw tcu::NotSupportedError("MSAA config not supported by this test");
   1202 
   1203 	// Create shader.
   1204 
   1205 	DE_ASSERT(!m_program);
   1206 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
   1207 
   1208 	if(!m_program->isOk())
   1209 	{
   1210 		m_testCtx.getLog() << *m_program;
   1211 
   1212 		GLint maxVertexTextures;
   1213 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
   1214 
   1215 		if (maxVertexTextures < 1)
   1216 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
   1217 		else
   1218 			TCU_FAIL("Failed to compile shader");
   1219 	}
   1220 
   1221 	// Make the textures.
   1222 
   1223 	try
   1224 	{
   1225 		const int texWidth	= WIDTH_2D_ARRAY;
   1226 		const int texHeight	= HEIGHT_2D_ARRAY;
   1227 		const int texLayers	= LAYERS_2D_ARRAY;
   1228 
   1229 		for (int i = 0; i < 2; i++)
   1230 		{
   1231 			DE_ASSERT(!m_textures[i]);
   1232 			m_textures[i] = new glu::Texture2DArray(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight, texLayers);
   1233 		}
   1234 
   1235 		const int						numLevels	= deLog2Floor32(de::max(texWidth, texHeight)) + 1;
   1236 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
   1237 		const Vec4						cBias		= fmtInfo.valueMin;
   1238 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
   1239 
   1240 		// Fill first with gradient texture.
   1241 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
   1242 		{
   1243 			const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
   1244 			const Vec4 gMax = Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
   1245 
   1246 			m_textures[0]->getRefTexture().allocLevel(levelNdx);
   1247 			tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
   1248 		}
   1249 
   1250 		// Fill second with grid texture.
   1251 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
   1252 		{
   1253 			const deUint32 step		= 0x00ffffff / numLevels;
   1254 			const deUint32 rgb		= step*levelNdx;
   1255 			const deUint32 colorA	= 0xff000000 | rgb;
   1256 			const deUint32 colorB	= 0xff000000 | ~rgb;
   1257 
   1258 			m_textures[1]->getRefTexture().allocLevel(levelNdx);
   1259 			tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
   1260 		}
   1261 
   1262 		// Upload.
   1263 		for (int i = 0; i < 2; i++)
   1264 			m_textures[i]->upload();
   1265 	}
   1266 	catch (const std::exception&)
   1267 	{
   1268 		// Clean up to save memory.
   1269 		Vertex2DArrayTextureCase::deinit();
   1270 		throw;
   1271 	}
   1272 }
   1273 
   1274 void Vertex2DArrayTextureCase::deinit (void)
   1275 {
   1276 	for (int i = 0; i < 2; i++)
   1277 	{
   1278 		delete m_textures[i];
   1279 		m_textures[i] = DE_NULL;
   1280 	}
   1281 
   1282 	delete m_program;
   1283 	m_program = DE_NULL;
   1284 }
   1285 
   1286 float Vertex2DArrayTextureCase::calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const
   1287 {
   1288 	const tcu::Texture2DArray&	refTexture	= m_textures[textureNdx]->getRefTexture();
   1289 	const int					texWidth	= refTexture.getWidth();
   1290 	const int					texHeight	= refTexture.getHeight();
   1291 
   1292 	// Calculate transformed coordinates of three screen corners.
   1293 	const Vec2					trans00		= (transf * Vec3(0.0f, 0.0f, 1.0f)).xy();
   1294 	const Vec2					trans01		= (transf * Vec3(0.0f, 1.0f, 1.0f)).xy();
   1295 	const Vec2					trans10		= (transf * Vec3(1.0f, 0.0f, 1.0f)).xy();
   1296 
   1297 	// Derivates.
   1298 	const float dudx = (trans10.x() - trans00.x()) * (float)texWidth / dstSize.x();
   1299 	const float dudy = (trans01.x() - trans00.x()) * (float)texWidth / dstSize.y();
   1300 	const float dvdx = (trans10.y() - trans00.y()) * (float)texHeight / dstSize.x();
   1301 	const float dvdy = (trans01.y() - trans00.y()) * (float)texHeight / dstSize.y();
   1302 
   1303 	return deFloatLog2(deFloatSqrt(de::max(dudx*dudx + dvdx*dvdx, dudy*dudy + dvdy*dvdy)));
   1304 }
   1305 
   1306 Vertex2DArrayTextureCase::IterateResult Vertex2DArrayTextureCase::iterate (void)
   1307 {
   1308 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_ARRAY_RENDER_WIDTH);
   1309 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_ARRAY_RENDER_HEIGHT);
   1310 
   1311 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
   1312 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
   1313 
   1314 	de::Random	rnd					(deStringHash(getName()));
   1315 
   1316 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
   1317 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
   1318 
   1319 	glUseProgram(m_program->getProgram());
   1320 
   1321 	// Divide viewport into 4 cells.
   1322 	const int leftWidth		= viewportWidth / 2;
   1323 	const int rightWidth	= viewportWidth - leftWidth;
   1324 	const int bottomHeight	= viewportHeight / 2;
   1325 	const int topHeight		= viewportHeight - bottomHeight;
   1326 
   1327 	// Clear.
   1328 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
   1329 	glClear(GL_COLOR_BUFFER_BIT);
   1330 
   1331 	// Shear by layer count to get all layers visible.
   1332 	static const float layerShearTransfData[] =
   1333 	{
   1334 		1.0f,					0.0f, 0.0f,
   1335 		0.0f,					1.0f, 0.0f,
   1336 		(float)LAYERS_2D_ARRAY, 0.0f, 0.0f
   1337 	};
   1338 
   1339 	// Minification and magnification transformations.
   1340 	static const float texMinTransfData[] =
   1341 	{
   1342 		2.1f,  0.0f, -0.3f,
   1343 		0.0f,  2.1f, -0.3f,
   1344 		0.0f,  0.0f,  1.0f
   1345 	};
   1346 	static const float texMagTransfData[] =
   1347 	{
   1348 		0.4f,  0.0f,  0.8f,
   1349 		0.0f,  0.4f,  0.8f,
   1350 		0.0f,  0.0f,  1.0f
   1351 	};
   1352 
   1353 	// Transformation matrices for minification and magnification.
   1354 	const Mat3 texMinTransf = Mat3(layerShearTransfData) * Mat3(texMinTransfData);
   1355 	const Mat3 texMagTransf = Mat3(layerShearTransfData) * Mat3(texMagTransfData);
   1356 
   1357 	// Surface for the reference image.
   1358 	tcu::Surface refImage(viewportWidth, viewportHeight);
   1359 
   1360 	{
   1361 		const struct Render
   1362 		{
   1363 			const Rect	region;
   1364 			int			textureNdx;
   1365 			const Mat3	texTransform;
   1366 			Render (const Rect& r, int tN, const Mat3& tT) : region(r), textureNdx(tN), texTransform(tT) {}
   1367 		} renders[] =
   1368 		{
   1369 			Render(Rect(0,				0,				leftWidth,	bottomHeight),	0, texMinTransf),
   1370 			Render(Rect(leftWidth,		0,				rightWidth,	bottomHeight),	0, texMagTransf),
   1371 			Render(Rect(0,				bottomHeight,	leftWidth,	topHeight),		1, texMinTransf),
   1372 			Render(Rect(leftWidth,		bottomHeight,	rightWidth,	topHeight),		1, texMagTransf)
   1373 		};
   1374 
   1375 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
   1376 		{
   1377 			const Render&	rend				= renders[renderNdx];
   1378 			const float		lod					= calculateLod(rend.texTransform, rend.region.size().asFloat(), rend.textureNdx);
   1379 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
   1380 			const Grid		grid				(GRID_SIZE_2D_ARRAY, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
   1381 												 TexTypeCoordParams<TEXTURETYPE_2D_ARRAY>(rend.texTransform), useSafeTexCoords);
   1382 
   1383 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
   1384 			renderCell				(rend.textureNdx, lod, grid);
   1385 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
   1386 		}
   1387 	}
   1388 
   1389 	// Read back rendered results.
   1390 	tcu::Surface resImage(viewportWidth, viewportHeight);
   1391 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
   1392 
   1393 	glUseProgram(0);
   1394 
   1395 	// Compare and log.
   1396 	{
   1397 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
   1398 
   1399 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
   1400 								isOk ? "Pass"				: "Image comparison failed");
   1401 	}
   1402 
   1403 	return STOP;
   1404 }
   1405 
   1406 void Vertex2DArrayTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
   1407 {
   1408 	const deUint32 programID = m_program->getProgram();
   1409 
   1410 	// SETUP ATTRIBUTES.
   1411 
   1412 	{
   1413 		const int positionLoc = glGetAttribLocation(programID, "a_position");
   1414 		if (positionLoc != -1)
   1415 		{
   1416 			glEnableVertexAttribArray(positionLoc);
   1417 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
   1418 		}
   1419 	}
   1420 
   1421 	{
   1422 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
   1423 		if (texCoordLoc != -1)
   1424 		{
   1425 			glEnableVertexAttribArray(texCoordLoc);
   1426 			glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
   1427 		}
   1428 	}
   1429 
   1430 	// SETUP UNIFORMS.
   1431 
   1432 	{
   1433 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
   1434 		if (lodLoc != -1)
   1435 			glUniform1f(lodLoc, lod);
   1436 	}
   1437 
   1438 	glActiveTexture(GL_TEXTURE0);
   1439 	glBindTexture(GL_TEXTURE_2D_ARRAY, m_textures[textureNdx]->getGLTexture());
   1440 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,		m_wrapS);
   1441 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,		m_wrapT);
   1442 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,	m_minFilter);
   1443 	glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER,	m_magFilter);
   1444 
   1445 	{
   1446 		const int texLoc = glGetUniformLocation(programID, "u_texture");
   1447 		if (texLoc != -1)
   1448 			glUniform1i(texLoc, 0);
   1449 	}
   1450 }
   1451 
   1452 // Renders one sub-image with given parameters.
   1453 void Vertex2DArrayTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
   1454 {
   1455 	setupShaderInputs(textureNdx, lod, grid);
   1456 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
   1457 }
   1458 
   1459 // Computes reference for one sub-image with given parameters.
   1460 void Vertex2DArrayTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
   1461 {
   1462 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion);
   1463 }
   1464 
   1465 class Vertex3DTextureCase : public TestCase
   1466 {
   1467 public:
   1468 								Vertex3DTextureCase		(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR);
   1469 								~Vertex3DTextureCase	(void);
   1470 
   1471 	void						init					(void);
   1472 	void						deinit					(void);
   1473 	IterateResult				iterate					(void);
   1474 
   1475 private:
   1476 	typedef PosTexCoordQuadGrid<TEXTURETYPE_3D> Grid;
   1477 
   1478 								Vertex3DTextureCase		(const Vertex3DTextureCase& other);
   1479 	Vertex3DTextureCase&		operator=				(const Vertex3DTextureCase& other);
   1480 
   1481 	float						calculateLod			(const Mat3& transf, const Vec2& dstSize, int textureNdx) const;
   1482 	void						setupShaderInputs		(int textureNdx, float lod, const Grid& grid) const;
   1483 	void						renderCell				(int textureNdx, float lod, const Grid& grid) const;
   1484 	void						computeReferenceCell	(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
   1485 
   1486 	const deUint32				m_minFilter;
   1487 	const deUint32				m_magFilter;
   1488 	const deUint32				m_wrapS;
   1489 	const deUint32				m_wrapT;
   1490 	const deUint32				m_wrapR;
   1491 
   1492 	const glu::ShaderProgram*	m_program;
   1493 	glu::Texture3D*				m_textures[2];	// 2 textures, a gradient texture and a grid texture.
   1494 };
   1495 
   1496 Vertex3DTextureCase::Vertex3DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR)
   1497 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
   1498 	, m_minFilter			(minFilter)
   1499 	, m_magFilter			(magFilter)
   1500 	, m_wrapS				(wrapS)
   1501 	, m_wrapT				(wrapT)
   1502 	, m_wrapR				(wrapR)
   1503 	, m_program				(DE_NULL)
   1504 {
   1505 	m_textures[0] = DE_NULL;
   1506 	m_textures[1] = DE_NULL;
   1507 }
   1508 
   1509 Vertex3DTextureCase::~Vertex3DTextureCase(void)
   1510 {
   1511 	Vertex3DTextureCase::deinit();
   1512 }
   1513 
   1514 void Vertex3DTextureCase::init (void)
   1515 {
   1516 	const char* const vertexShaderSource =
   1517 		"#version 300 es\n"
   1518 		"in highp vec2 a_position;\n"
   1519 		"in highp vec3 a_texCoord;\n"
   1520 		"uniform highp sampler3D u_texture;\n"
   1521 		"uniform highp float u_lod;\n"
   1522 		"out mediump vec4 v_color;\n"
   1523 		"\n"
   1524 		"void main()\n"
   1525 		"{\n"
   1526 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
   1527 		"	v_color = textureLod(u_texture, a_texCoord, u_lod);\n"
   1528 		"}\n";
   1529 
   1530 	const char* const fragmentShaderSource =
   1531 		"#version 300 es\n"
   1532 		"layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
   1533 		"in mediump vec4 v_color;\n"
   1534 		"\n"
   1535 		"void main()\n"
   1536 		"{\n"
   1537 		"	dEQP_FragColor = v_color;\n"
   1538 		"}\n";
   1539 
   1540 	if (m_context.getRenderTarget().getNumSamples() != 0)
   1541 		throw tcu::NotSupportedError("MSAA config not supported by this test");
   1542 
   1543 	// Create shader.
   1544 
   1545 	DE_ASSERT(!m_program);
   1546 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource));
   1547 
   1548 	if(!m_program->isOk())
   1549 	{
   1550 		m_testCtx.getLog() << *m_program;
   1551 
   1552 		GLint maxVertexTextures;
   1553 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
   1554 
   1555 		if (maxVertexTextures < 1)
   1556 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
   1557 		else
   1558 			TCU_FAIL("Failed to compile shader");
   1559 	}
   1560 
   1561 	// Make the textures.
   1562 
   1563 	try
   1564 	{
   1565 		const int texWidth	= WIDTH_3D;
   1566 		const int texHeight	= HEIGHT_3D;
   1567 		const int texDepth	= DEPTH_3D;
   1568 
   1569 		for (int i = 0; i < 2; i++)
   1570 		{
   1571 			DE_ASSERT(!m_textures[i]);
   1572 			m_textures[i] = new glu::Texture3D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight, texDepth);
   1573 		}
   1574 
   1575 		const int						numLevels	= deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth)) + 1;
   1576 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
   1577 		const Vec4						cBias		= fmtInfo.valueMin;
   1578 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
   1579 
   1580 		// Fill first with gradient texture.
   1581 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
   1582 		{
   1583 			const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
   1584 			const Vec4 gMax = Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
   1585 
   1586 			m_textures[0]->getRefTexture().allocLevel(levelNdx);
   1587 			tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
   1588 		}
   1589 
   1590 		// Fill second with grid texture.
   1591 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
   1592 		{
   1593 			const deUint32 step		= 0x00ffffff / numLevels;
   1594 			const deUint32 rgb		= step*levelNdx;
   1595 			const deUint32 colorA	= 0xff000000 | rgb;
   1596 			const deUint32 colorB	= 0xff000000 | ~rgb;
   1597 
   1598 			m_textures[1]->getRefTexture().allocLevel(levelNdx);
   1599 			tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
   1600 		}
   1601 
   1602 		// Upload.
   1603 		for (int i = 0; i < 2; i++)
   1604 			m_textures[i]->upload();
   1605 	}
   1606 	catch (const std::exception&)
   1607 	{
   1608 		// Clean up to save memory.
   1609 		Vertex3DTextureCase::deinit();
   1610 		throw;
   1611 	}
   1612 }
   1613 
   1614 void Vertex3DTextureCase::deinit (void)
   1615 {
   1616 	for (int i = 0; i < 2; i++)
   1617 	{
   1618 		delete m_textures[i];
   1619 		m_textures[i] = DE_NULL;
   1620 	}
   1621 
   1622 	delete m_program;
   1623 	m_program = DE_NULL;
   1624 }
   1625 
   1626 float Vertex3DTextureCase::calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const
   1627 {
   1628 	const tcu::Texture3D&	refTexture	= m_textures[textureNdx]->getRefTexture();
   1629 	const int				srcWidth	= refTexture.getWidth();
   1630 	const int				srcHeight	= refTexture.getHeight();
   1631 	const int				srcDepth	= refTexture.getDepth();
   1632 
   1633 	// Calculate transformed coordinates of three screen corners.
   1634 	const Vec3				trans00		= transf * Vec3(0.0f, 0.0f, 1.0f);
   1635 	const Vec3				trans01		= transf * Vec3(0.0f, 1.0f, 1.0f);
   1636 	const Vec3				trans10		= transf * Vec3(1.0f, 0.0f, 1.0f);
   1637 
   1638 	// Derivates.
   1639 	const float dudx = (trans10.x() - trans00.x()) * (float)srcWidth / dstSize.x();
   1640 	const float dudy = (trans01.x() - trans00.x()) * (float)srcWidth / dstSize.y();
   1641 	const float dvdx = (trans10.y() - trans00.y()) * (float)srcHeight / dstSize.x();
   1642 	const float dvdy = (trans01.y() - trans00.y()) * (float)srcHeight / dstSize.y();
   1643 	const float dwdx = (trans10.z() - trans00.z()) * (float)srcDepth / dstSize.x();
   1644 	const float dwdy = (trans01.z() - trans00.z()) * (float)srcDepth / dstSize.y();
   1645 
   1646 	return deFloatLog2(deFloatSqrt(de::max(dudx*dudx + dvdx*dvdx + dwdx*dwdx, dudy*dudy + dvdy*dvdy + dwdy*dwdy)));
   1647 }
   1648 
   1649 Vertex3DTextureCase::IterateResult Vertex3DTextureCase::iterate (void)
   1650 {
   1651 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_3D_RENDER_WIDTH);
   1652 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_3D_RENDER_HEIGHT);
   1653 
   1654 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
   1655 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
   1656 
   1657 	de::Random	rnd					(deStringHash(getName()));
   1658 
   1659 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
   1660 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
   1661 
   1662 	glUseProgram(m_program->getProgram());
   1663 
   1664 	// Divide viewport into 4 cells.
   1665 	const int leftWidth		= viewportWidth / 2;
   1666 	const int rightWidth		= viewportWidth - leftWidth;
   1667 	const int bottomHeight	= viewportHeight / 2;
   1668 	const int topHeight		= viewportHeight - bottomHeight;
   1669 
   1670 	// Clear.
   1671 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
   1672 	glClear(GL_COLOR_BUFFER_BIT);
   1673 
   1674 	// Shear to get all slices visible.
   1675 	static const float depthShearTransfData[] =
   1676 	{
   1677 		1.0f, 0.0f, 0.0f,
   1678 		0.0f, 1.0f, 0.0f,
   1679 		1.0f, 1.0f, 0.0f
   1680 	};
   1681 
   1682 	// Minification and magnification transformations.
   1683 	static const float texMinTransfData[] =
   1684 	{
   1685 		2.2f,  0.0f, -0.3f,
   1686 		0.0f,  2.2f, -0.3f,
   1687 		0.0f,  0.0f,  1.0f
   1688 	};
   1689 	static const float texMagTransfData[] =
   1690 	{
   1691 		0.4f,  0.0f,  0.8f,
   1692 		0.0f,  0.4f,  0.8f,
   1693 		0.0f,  0.0f,  1.0f
   1694 	};
   1695 
   1696 	// Transformation matrices for minification and magnification.
   1697 	const Mat3 texMinTransf = Mat3(depthShearTransfData) * Mat3(texMinTransfData);
   1698 	const Mat3 texMagTransf = Mat3(depthShearTransfData) * Mat3(texMagTransfData);
   1699 
   1700 	// Surface for the reference image.
   1701 	tcu::Surface refImage(viewportWidth, viewportHeight);
   1702 
   1703 	{
   1704 		const struct Render
   1705 		{
   1706 			const Rect	region;
   1707 			int			textureNdx;
   1708 			const Mat3		texTransform;
   1709 			Render (const Rect& r, int tN, const Mat3& tT) : region(r), textureNdx(tN), texTransform(tT) {}
   1710 		} renders[] =
   1711 		{
   1712 			Render(Rect(0,				0,				leftWidth,	bottomHeight),	0, texMinTransf),
   1713 			Render(Rect(leftWidth,		0,				rightWidth,	bottomHeight),	0, texMagTransf),
   1714 			Render(Rect(0,				bottomHeight,	leftWidth,	topHeight),		1, texMinTransf),
   1715 			Render(Rect(leftWidth,		bottomHeight,	rightWidth,	topHeight),		1, texMagTransf)
   1716 		};
   1717 
   1718 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
   1719 		{
   1720 			const Render&	rend				= renders[renderNdx];
   1721 			const float		lod					= calculateLod(rend.texTransform, rend.region.size().asFloat(), rend.textureNdx);
   1722 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
   1723 			const Grid		grid				(GRID_SIZE_3D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
   1724 												 TexTypeCoordParams<TEXTURETYPE_3D>(rend.texTransform), useSafeTexCoords);
   1725 
   1726 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
   1727 			renderCell				(rend.textureNdx, lod, grid);
   1728 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
   1729 		}
   1730 	}
   1731 
   1732 	// Read back rendered results.
   1733 	tcu::Surface resImage(viewportWidth, viewportHeight);
   1734 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
   1735 
   1736 	glUseProgram(0);
   1737 
   1738 	// Compare and log.
   1739 	{
   1740 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
   1741 
   1742 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
   1743 								isOk ? "Pass"				: "Image comparison failed");
   1744 	}
   1745 
   1746 	return STOP;
   1747 }
   1748 
   1749 void Vertex3DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
   1750 {
   1751 	const deUint32 programID = m_program->getProgram();
   1752 
   1753 	// SETUP ATTRIBUTES.
   1754 
   1755 	{
   1756 		const int positionLoc = glGetAttribLocation(programID, "a_position");
   1757 		if (positionLoc != -1)
   1758 		{
   1759 			glEnableVertexAttribArray(positionLoc);
   1760 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
   1761 		}
   1762 	}
   1763 
   1764 	{
   1765 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
   1766 		if (texCoordLoc != -1)
   1767 		{
   1768 			glEnableVertexAttribArray(texCoordLoc);
   1769 			glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
   1770 		}
   1771 	}
   1772 
   1773 	// SETUP UNIFORMS.
   1774 
   1775 	{
   1776 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
   1777 		if (lodLoc != -1)
   1778 			glUniform1f(lodLoc, lod);
   1779 	}
   1780 
   1781 	glActiveTexture(GL_TEXTURE0);
   1782 	glBindTexture(GL_TEXTURE_3D, m_textures[textureNdx]->getGLTexture());
   1783 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,		m_wrapS);
   1784 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,		m_wrapT);
   1785 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,		m_wrapR);
   1786 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
   1787 	glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
   1788 
   1789 	{
   1790 		const int texLoc = glGetUniformLocation(programID, "u_texture");
   1791 		if (texLoc != -1)
   1792 			glUniform1i(texLoc, 0);
   1793 	}
   1794 }
   1795 
   1796 // Renders one sub-image with given parameters.
   1797 void Vertex3DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
   1798 {
   1799 	setupShaderInputs(textureNdx, lod, grid);
   1800 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
   1801 }
   1802 
   1803 // Computes reference for one sub-image with given parameters.
   1804 void Vertex3DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
   1805 {
   1806 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapR, m_minFilter, m_magFilter), grid, dst, dstRegion);
   1807 }
   1808 
   1809 VertexTextureTests::VertexTextureTests (Context& context)
   1810 	: TestCaseGroup(context, "vertex", "Vertex Texture Tests")
   1811 {
   1812 }
   1813 
   1814 VertexTextureTests::~VertexTextureTests(void)
   1815 {
   1816 }
   1817 
   1818 void VertexTextureTests::init (void)
   1819 {
   1820 	// 2D and cube map groups, and their filtering and wrap sub-groups.
   1821 	TestCaseGroup* const group2D				= new TestCaseGroup(m_context, "2d",			"2D Vertex Texture Tests");
   1822 	TestCaseGroup* const groupCube				= new TestCaseGroup(m_context, "cube",			"Cube Map Vertex Texture Tests");
   1823 	TestCaseGroup* const group2DArray			= new TestCaseGroup(m_context, "2d_array",		"2D Array Vertex Texture Tests");
   1824 	TestCaseGroup* const group3D				= new TestCaseGroup(m_context, "3d",			"3D Vertex Texture Tests");
   1825 	TestCaseGroup* const filteringGroup2D		= new TestCaseGroup(m_context, "filtering",		"2D Vertex Texture Filtering Tests");
   1826 	TestCaseGroup* const wrapGroup2D			= new TestCaseGroup(m_context, "wrap",			"2D Vertex Texture Wrap Tests");
   1827 	TestCaseGroup* const filteringGroupCube		= new TestCaseGroup(m_context, "filtering",		"Cube Map Vertex Texture Filtering Tests");
   1828 	TestCaseGroup* const wrapGroupCube			= new TestCaseGroup(m_context, "wrap",			"Cube Map Vertex Texture Wrap Tests");
   1829 	TestCaseGroup* const filteringGroup2DArray	= new TestCaseGroup(m_context, "filtering",		"2D Array Vertex Texture Filtering Tests");
   1830 	TestCaseGroup* const wrapGroup2DArray		= new TestCaseGroup(m_context, "wrap",			"2D Array Vertex Texture Wrap Tests");
   1831 	TestCaseGroup* const filteringGroup3D		= new TestCaseGroup(m_context, "filtering",		"3D Vertex Texture Filtering Tests");
   1832 	TestCaseGroup* const wrapGroup3D			= new TestCaseGroup(m_context, "wrap",			"3D Vertex Texture Wrap Tests");
   1833 
   1834 	group2D->addChild(filteringGroup2D);
   1835 	group2D->addChild(wrapGroup2D);
   1836 	groupCube->addChild(filteringGroupCube);
   1837 	groupCube->addChild(wrapGroupCube);
   1838 	group2DArray->addChild(filteringGroup2DArray);
   1839 	group2DArray->addChild(wrapGroup2DArray);
   1840 	group3D->addChild(filteringGroup3D);
   1841 	group3D->addChild(wrapGroup3D);
   1842 
   1843 	addChild(group2D);
   1844 	addChild(groupCube);
   1845 	addChild(group2DArray);
   1846 	addChild(group3D);
   1847 
   1848 	static const struct
   1849 	{
   1850 		const char*		name;
   1851 		GLenum			mode;
   1852 	} wrapModes[] =
   1853 	{
   1854 		{ "clamp",		GL_CLAMP_TO_EDGE	},
   1855 		{ "repeat",		GL_REPEAT			},
   1856 		{ "mirror",		GL_MIRRORED_REPEAT	}
   1857 	};
   1858 
   1859 	static const struct
   1860 	{
   1861 		const char*		name;
   1862 		GLenum			mode;
   1863 	} minFilterModes[] =
   1864 	{
   1865 		{ "nearest",				GL_NEAREST					},
   1866 		{ "linear",					GL_LINEAR					},
   1867 		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
   1868 		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
   1869 		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
   1870 		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
   1871 	};
   1872 
   1873 	static const struct
   1874 	{
   1875 		const char*		name;
   1876 		GLenum			mode;
   1877 	} magFilterModes[] =
   1878 	{
   1879 		{ "nearest",	GL_NEAREST	},
   1880 		{ "linear",		GL_LINEAR	}
   1881 	};
   1882 
   1883 #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
   1884 	for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++)	\
   1885 		BODY
   1886 
   1887 	// 2D cases.
   1888 
   1889 	FOR_EACH(minFilter,		minFilterModes,
   1890 	FOR_EACH(magFilter,		magFilterModes,
   1891 	FOR_EACH(wrapMode,		wrapModes,
   1892 		{
   1893 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1894 
   1895 			filteringGroup2D->addChild(new Vertex2DTextureCase(m_context,
   1896 															   name.c_str(), "",
   1897 															   minFilterModes[minFilter].mode,
   1898 															   magFilterModes[magFilter].mode,
   1899 															   wrapModes[wrapMode].mode,
   1900 															   wrapModes[wrapMode].mode));
   1901 		})));
   1902 
   1903 	FOR_EACH(wrapSMode,		wrapModes,
   1904 	FOR_EACH(wrapTMode,		wrapModes,
   1905 		{
   1906 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
   1907 
   1908 			wrapGroup2D->addChild(new Vertex2DTextureCase(m_context,
   1909 														  name.c_str(), "",
   1910 														  GL_LINEAR_MIPMAP_LINEAR,
   1911 														  GL_LINEAR,
   1912 														  wrapModes[wrapSMode].mode,
   1913 														  wrapModes[wrapTMode].mode));
   1914 		}));
   1915 
   1916 	// Cube map cases.
   1917 
   1918 	FOR_EACH(minFilter,		minFilterModes,
   1919 	FOR_EACH(magFilter,		magFilterModes,
   1920 	FOR_EACH(wrapMode,		wrapModes,
   1921 		{
   1922 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1923 
   1924 			filteringGroupCube->addChild(new VertexCubeTextureCase(m_context,
   1925 																   name.c_str(), "",
   1926 																   minFilterModes[minFilter].mode,
   1927 																   magFilterModes[magFilter].mode,
   1928 																   wrapModes[wrapMode].mode,
   1929 																   wrapModes[wrapMode].mode));
   1930 		})));
   1931 
   1932 	FOR_EACH(wrapSMode,		wrapModes,
   1933 	FOR_EACH(wrapTMode,		wrapModes,
   1934 		{
   1935 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
   1936 
   1937 			wrapGroupCube->addChild(new VertexCubeTextureCase(m_context,
   1938 															  name.c_str(), "",
   1939 															  GL_LINEAR_MIPMAP_LINEAR,
   1940 															  GL_LINEAR,
   1941 															  wrapModes[wrapSMode].mode,
   1942 															  wrapModes[wrapTMode].mode));
   1943 		}));
   1944 
   1945 	// 2D array cases.
   1946 
   1947 	FOR_EACH(minFilter,		minFilterModes,
   1948 	FOR_EACH(magFilter,		magFilterModes,
   1949 	FOR_EACH(wrapMode,		wrapModes,
   1950 		{
   1951 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1952 
   1953 			filteringGroup2DArray->addChild(new Vertex2DArrayTextureCase(m_context,
   1954 																		 name.c_str(), "",
   1955 																		 minFilterModes[minFilter].mode,
   1956 																		 magFilterModes[magFilter].mode,
   1957 																		 wrapModes[wrapMode].mode,
   1958 																		 wrapModes[wrapMode].mode));
   1959 		})));
   1960 
   1961 	FOR_EACH(wrapSMode,		wrapModes,
   1962 	FOR_EACH(wrapTMode,		wrapModes,
   1963 		{
   1964 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
   1965 
   1966 			wrapGroup2DArray->addChild(new Vertex2DArrayTextureCase(m_context,
   1967 																	name.c_str(), "",
   1968 																	GL_LINEAR_MIPMAP_LINEAR,
   1969 																	GL_LINEAR,
   1970 																	wrapModes[wrapSMode].mode,
   1971 																	wrapModes[wrapTMode].mode));
   1972 		}));
   1973 
   1974 	// 3D cases.
   1975 
   1976 	FOR_EACH(minFilter,		minFilterModes,
   1977 	FOR_EACH(magFilter,		magFilterModes,
   1978 	FOR_EACH(wrapMode,		wrapModes,
   1979 		{
   1980 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1981 
   1982 			filteringGroup3D->addChild(new Vertex3DTextureCase(m_context,
   1983 															   name.c_str(), "",
   1984 															   minFilterModes[minFilter].mode,
   1985 															   magFilterModes[magFilter].mode,
   1986 															   wrapModes[wrapMode].mode,
   1987 															   wrapModes[wrapMode].mode,
   1988 															   wrapModes[wrapMode].mode));
   1989 		})));
   1990 
   1991 	FOR_EACH(wrapSMode,		wrapModes,
   1992 	FOR_EACH(wrapTMode,		wrapModes,
   1993 	FOR_EACH(wrapRMode,		wrapModes,
   1994 		{
   1995 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name + "_" + wrapModes[wrapRMode].name;
   1996 
   1997 			wrapGroup3D->addChild(new Vertex3DTextureCase(m_context,
   1998 														  name.c_str(), "",
   1999 														  GL_LINEAR_MIPMAP_LINEAR,
   2000 														  GL_LINEAR,
   2001 														  wrapModes[wrapSMode].mode,
   2002 														  wrapModes[wrapTMode].mode,
   2003 														  wrapModes[wrapRMode].mode));
   2004 		})));
   2005 }
   2006 
   2007 } // Functional
   2008 } // gles3
   2009 } // deqp
   2010