Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 2.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Vertex texture tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es2fVertexTextureTests.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 "tcuTexVerifierUtil.hpp"
     33 #include "tcuImageCompare.hpp"
     34 #include "deRandom.hpp"
     35 #include "deString.h"
     36 #include "deMath.h"
     37 
     38 #include <string>
     39 #include <vector>
     40 
     41 #include <limits>
     42 
     43 #include "glw.h"
     44 
     45 using tcu::TestLog;
     46 using tcu::Vec2;
     47 using tcu::Vec3;
     48 using tcu::Vec4;
     49 using tcu::IVec2;
     50 using tcu::IVec3;
     51 using tcu::IVec4;
     52 using tcu::Mat3;
     53 using std::string;
     54 using std::vector;
     55 
     56 namespace deqp
     57 {
     58 
     59 using namespace gls::TextureTestUtil;
     60 using namespace glu::TextureTestUtil;
     61 
     62 using glu::TextureTestUtil::TEXTURETYPE_2D;
     63 using glu::TextureTestUtil::TEXTURETYPE_CUBE;
     64 
     65 namespace gles2
     66 {
     67 namespace Functional
     68 {
     69 
     70 // The 2D case draws four images.
     71 static const int MAX_2D_RENDER_WIDTH		= 128*2;
     72 static const int MAX_2D_RENDER_HEIGHT		= 128*2;
     73 
     74 // The cube map case draws four 3-by-2 image groups.
     75 static const int MAX_CUBE_RENDER_WIDTH		= 28*2*3;
     76 static const int MAX_CUBE_RENDER_HEIGHT		= 28*2*2;
     77 
     78 static const int GRID_SIZE_2D				= 127;
     79 static const int GRID_SIZE_CUBE				= 63;
     80 
     81 // Helpers for making texture coordinates "safe", i.e. move them further from coordinate bounary.
     82 
     83 // Moves x towards the closest K+targetFraction, where K is an integer.
     84 // E.g. moveTowardsFraction(x, 0.5f) moves x away from integer boundaries.
     85 static inline float moveTowardsFraction (float x, float targetFraction)
     86 {
     87 	const float strictness = 0.5f;
     88 	DE_ASSERT(0.0f < strictness && strictness <= 1.0f);
     89 	DE_ASSERT(de::inBounds(targetFraction, 0.0f, 1.0f));
     90 	const float y = x + 0.5f - targetFraction;
     91 	return deFloatFloor(y) + deFloatFrac(y)*(1.0f-strictness) + strictness*0.5f - 0.5f + targetFraction;
     92 }
     93 
     94 static inline float safeCoord (float raw, int scale, float fraction)
     95 {
     96 	const float scaleFloat = (float)scale;
     97 	return moveTowardsFraction(raw*scaleFloat, fraction) / scaleFloat;
     98 }
     99 
    100 template <int Size>
    101 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)
    102 {
    103 	tcu::Vector<float, Size> result;
    104 	for (int i = 0; i < Size; i++)
    105 		result[i] = safeCoord(raw[i], scale[i], fraction[i]);
    106 	return result;
    107 }
    108 
    109 static inline Vec2 safe2DTexCoords (const Vec2& raw, const IVec2& textureSize)
    110 {
    111 	return safeCoords(raw, textureSize, Vec2(0.5f));
    112 }
    113 
    114 namespace
    115 {
    116 
    117 struct Rect
    118 {
    119 			Rect	(int x_, int y_, int w_, int h_) : x(x_), y(y_), w(w_), h(h_) {}
    120 	IVec2	pos		(void) const { return IVec2(x, y); }
    121 	IVec2	size	(void) const { return IVec2(w, h); }
    122 
    123 	int		x;
    124 	int		y;
    125 	int		w;
    126 	int		h;
    127 };
    128 
    129 template <TextureType> struct TexTypeTcuClass;
    130 template <> struct TexTypeTcuClass<TEXTURETYPE_2D>			{ typedef tcu::Texture2D		t; };
    131 template <> struct TexTypeTcuClass<TEXTURETYPE_CUBE>		{ typedef tcu::TextureCube		t; };
    132 
    133 template <TextureType> struct TexTypeSizeDims;
    134 template <> struct TexTypeSizeDims<TEXTURETYPE_2D>			{ enum { V = 2 }; };
    135 template <> struct TexTypeSizeDims<TEXTURETYPE_CUBE>		{ enum { V = 2 }; };
    136 
    137 template <TextureType> struct TexTypeCoordDims;
    138 template <> struct TexTypeCoordDims<TEXTURETYPE_2D>			{ enum { V = 2 }; };
    139 template <> struct TexTypeCoordDims<TEXTURETYPE_CUBE>		{ enum { V = 3 }; };
    140 
    141 template <TextureType TexType> struct TexTypeSizeIVec		{ typedef tcu::Vector<int,		TexTypeSizeDims<TexType>::V>	t; };
    142 template <TextureType TexType> struct TexTypeCoordVec		{ typedef tcu::Vector<float,	TexTypeCoordDims<TexType>::V>	t; };
    143 
    144 template <TextureType> struct TexTypeCoordParams;
    145 
    146 template <> struct
    147 TexTypeCoordParams<TEXTURETYPE_2D>
    148 {
    149 	Vec2 scale;
    150 	Vec2 bias;
    151 
    152 	TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_) : scale(scale_), bias(bias_) {}
    153 };
    154 
    155 template <> struct
    156 TexTypeCoordParams<TEXTURETYPE_CUBE>
    157 {
    158 	Vec2			scale;
    159 	Vec2			bias;
    160 	tcu::CubeFace	face;
    161 
    162 	TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_, tcu::CubeFace face_) : scale(scale_), bias(bias_), face(face_) {}
    163 };
    164 
    165 /*--------------------------------------------------------------------*//*!
    166  * \brief Quad grid class containing position and texture coordinate data.
    167  *
    168  * A quad grid of size S means a grid consisting of S*S quads (S rows and
    169  * S columns). The quads are rectangles with main axis aligned sides, and
    170  * each consists of two triangles. Note that although there are only
    171  * (S+1)*(S+1) distinct vertex positions, there are S*S*4 distinct vertices
    172  * because we want texture coordinates to be constant across the vertices
    173  * of a quad (to avoid interpolation issues), and thus each quad needs its
    174  * own 4 vertices.
    175  *
    176  * Pointers returned by get*Ptr() are suitable for gl calls such as
    177  * glVertexAttribPointer() (for position and tex coord) or glDrawElements()
    178  * (for indices).
    179  *//*--------------------------------------------------------------------*/
    180 template <TextureType TexType>
    181 class PosTexCoordQuadGrid
    182 {
    183 private:
    184 	enum { TEX_COORD_DIMS = TexTypeCoordDims <TexType>::V };
    185 	typedef typename TexTypeCoordVec<TexType>::t	TexCoordVec;
    186 	typedef typename TexTypeSizeIVec<TexType>::t	TexSizeIVec;
    187 	typedef TexTypeCoordParams<TexType>				TexCoordParams;
    188 
    189 public:
    190 							PosTexCoordQuadGrid		(int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
    191 
    192 	int						getSize					(void) const { return m_gridSize; }
    193 	Vec4					getQuadLDRU				(int col, int row) const; //!< Vec4(leftX, downY, rightX, upY)
    194 	const TexCoordVec&		getQuadTexCoord			(int col, int row) const;
    195 
    196 	int						getNumIndices			(void) const { return m_gridSize*m_gridSize*3*2; }
    197 	const float*			getPositionPtr			(void) const { DE_STATIC_ASSERT(sizeof(Vec2) == 2*sizeof(float)); return (float*)&m_positions[0]; }
    198 	const float*			getTexCoordPtr			(void) const { DE_STATIC_ASSERT(sizeof(TexCoordVec) == TEX_COORD_DIMS*(int)sizeof(float)); return (float*)&m_texCoords[0]; }
    199 	const deUint16*			getIndexPtr				(void) const { return &m_indices[0]; }
    200 
    201 private:
    202 	void					initializeTexCoords		(const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
    203 
    204 	const int				m_gridSize;
    205 	vector<Vec2>			m_positions;
    206 	vector<TexCoordVec>		m_texCoords;
    207 	vector<deUint16>		m_indices;
    208 };
    209 
    210 template <TextureType TexType>
    211 Vec4 PosTexCoordQuadGrid<TexType>::getQuadLDRU (int col, int row) const
    212 {
    213 	int ndx00 = (row*m_gridSize + col) * 4;
    214 	int ndx11 = ndx00 + 3;
    215 
    216 	return Vec4(m_positions[ndx00].x(),
    217 				m_positions[ndx00].y(),
    218 				m_positions[ndx11].x(),
    219 				m_positions[ndx11].y());
    220 }
    221 
    222 template <TextureType TexType>
    223 const typename TexTypeCoordVec<TexType>::t& PosTexCoordQuadGrid<TexType>::getQuadTexCoord (int col, int row) const
    224 {
    225 	return m_texCoords[(row*m_gridSize + col) * 4];
    226 }
    227 
    228 template <TextureType TexType>
    229 PosTexCoordQuadGrid<TexType>::PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    230 	: m_gridSize(gridSize)
    231 {
    232 	DE_ASSERT(m_gridSize > 0 && m_gridSize*m_gridSize <= (int)std::numeric_limits<deUint16>::max() + 1);
    233 
    234 	const float gridSizeFloat = (float)m_gridSize;
    235 
    236 	m_positions.reserve(m_gridSize*m_gridSize*4);
    237 	m_indices.reserve(m_gridSize*m_gridSize*3*2);
    238 
    239 	for (int y = 0; y < m_gridSize; y++)
    240 	for (int x = 0; x < m_gridSize; x++)
    241 	{
    242 		float fx0 = (float)(x+0) / gridSizeFloat;
    243 		float fx1 = (float)(x+1) / gridSizeFloat;
    244 		float fy0 = (float)(y+0) / gridSizeFloat;
    245 		float fy1 = (float)(y+1) / gridSizeFloat;
    246 
    247 		Vec2 quadVertices[4] = { Vec2(fx0, fy0), Vec2(fx1, fy0), Vec2(fx0, fy1), Vec2(fx1, fy1) };
    248 
    249 		int firstNdx = (int)m_positions.size();
    250 
    251 		for (int i = 0; i < DE_LENGTH_OF_ARRAY(quadVertices); i++)
    252 			m_positions.push_back(safeCoords(quadVertices[i], renderSize, Vec2(0.0f)) * 2.0f - 1.0f);
    253 
    254 		m_indices.push_back(deUint16(firstNdx + 0));
    255 		m_indices.push_back(deUint16(firstNdx + 1));
    256 		m_indices.push_back(deUint16(firstNdx + 2));
    257 
    258 		m_indices.push_back(deUint16(firstNdx + 1));
    259 		m_indices.push_back(deUint16(firstNdx + 3));
    260 		m_indices.push_back(deUint16(firstNdx + 2));
    261 	}
    262 
    263 	m_texCoords.reserve(m_gridSize*m_gridSize*4);
    264 	initializeTexCoords(textureSize, texCoordParams, useSafeTexCoords);
    265 
    266 	DE_ASSERT((int)m_positions.size() == m_gridSize*m_gridSize*4);
    267 	DE_ASSERT((int)m_indices.size() == m_gridSize*m_gridSize*3*2);
    268 	DE_ASSERT((int)m_texCoords.size() == m_gridSize*m_gridSize*4);
    269 }
    270 
    271 template <>
    272 void PosTexCoordQuadGrid<TEXTURETYPE_2D>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    273 {
    274 	DE_ASSERT(m_texCoords.empty());
    275 
    276 	const float gridSizeFloat = (float)m_gridSize;
    277 
    278 	for (int y = 0; y < m_gridSize; y++)
    279 	for (int x = 0; x < m_gridSize; x++)
    280 	{
    281 		Vec2 rawCoord = Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) * texCoordParams.scale + texCoordParams.bias;
    282 
    283 		for (int i = 0; i < 4; i++)
    284 			m_texCoords.push_back(useSafeTexCoords ? safe2DTexCoords(rawCoord, textureSize) : rawCoord);
    285 	}
    286 }
    287 
    288 template <>
    289 void PosTexCoordQuadGrid<TEXTURETYPE_CUBE>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
    290 {
    291 	DE_ASSERT(m_texCoords.empty());
    292 
    293 	const float		gridSizeFloat	= (float)m_gridSize;
    294 	vector<float>	texBoundaries;
    295 	computeQuadTexCoordCube(texBoundaries, texCoordParams.face);
    296 	const Vec3		coordA			= Vec3(texBoundaries[0], texBoundaries[1], texBoundaries[2]);
    297 	const Vec3		coordB			= Vec3(texBoundaries[3], texBoundaries[4], texBoundaries[5]);
    298 	const Vec3		coordC			= Vec3(texBoundaries[6], texBoundaries[7], texBoundaries[8]);
    299 	const Vec3		coordAB			= coordB - coordA;
    300 	const Vec3		coordAC			= coordC - coordA;
    301 
    302 	for (int y = 0; y < m_gridSize; y++)
    303 	for (int x = 0; x < m_gridSize; x++)
    304 	{
    305 		const Vec2 rawFaceCoord		= texCoordParams.scale * Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) + texCoordParams.bias;
    306 		const Vec2 safeFaceCoord	= useSafeTexCoords ? safe2DTexCoords(rawFaceCoord, textureSize) : rawFaceCoord;
    307 		const Vec3 texCoord			= coordA + coordAC*safeFaceCoord.x() + coordAB*safeFaceCoord.y();
    308 
    309 		for (int i = 0; i < 4; i++)
    310 			m_texCoords.push_back(texCoord);
    311 	}
    312 }
    313 
    314 } // anonymous
    315 
    316 static inline bool isLevelNearest (deUint32 filter)
    317 {
    318 	return filter == GL_NEAREST || filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_NEAREST_MIPMAP_LINEAR;
    319 }
    320 
    321 static inline IVec2 getTextureSize (const glu::Texture2D& tex)
    322 {
    323 	const tcu::Texture2D& ref = tex.getRefTexture();
    324 	return IVec2(ref.getWidth(), ref.getHeight());
    325 }
    326 
    327 static inline IVec2 getTextureSize (const glu::TextureCube& tex)
    328 {
    329 	const tcu::TextureCube& ref = tex.getRefTexture();
    330 	return IVec2(ref.getSize(), ref.getSize());
    331 }
    332 
    333 template <TextureType TexType>
    334 static void setPixelColors (const vector<Vec4>& quadColors, const Rect& region, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst)
    335 {
    336 	const int gridSize = grid.getSize();
    337 
    338 	for (int y = 0; y < gridSize; y++)
    339 	for (int x = 0; x < gridSize; x++)
    340 	{
    341 		const Vec4	color	= quadColors[y*gridSize + x];
    342 		const Vec4	ldru	= grid.getQuadLDRU(x, y) * 0.5f + 0.5f; // [-1, 1] -> [0, 1]
    343 		const int	ix0		= deCeilFloatToInt32(ldru.x() * (float)region.w - 0.5f);
    344 		const int	ix1		= deCeilFloatToInt32(ldru.z() * (float)region.w - 0.5f);
    345 		const int	iy0		= deCeilFloatToInt32(ldru.y() * (float)region.h - 0.5f);
    346 		const int	iy1		= deCeilFloatToInt32(ldru.w() * (float)region.h - 0.5f);
    347 
    348 		for (int iy = iy0; iy < iy1; iy++)
    349 		for (int ix = ix0; ix < ix1; ix++)
    350 		{
    351 			DE_ASSERT(deInBounds32(ix + region.x, 0, dst.getWidth()));
    352 			DE_ASSERT(deInBounds32(iy + region.y, 0, dst.getHeight()));
    353 
    354 			dst.setPixel(ix + region.x, iy + region.y, tcu::RGBA(color));
    355 		}
    356 	}
    357 }
    358 
    359 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); }
    360 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); }
    361 
    362 template <TextureType TexType>
    363 void computeReference (const typename TexTypeTcuClass<TexType>::t& texture, float lod, const tcu::Sampler& sampler, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst, const Rect& dstRegion)
    364 {
    365 	const int		gridSize	= grid.getSize();
    366 	vector<Vec4>	quadColors	(gridSize*gridSize);
    367 
    368 	for (int y = 0; y < gridSize; y++)
    369 	for (int x = 0; x < gridSize; x++)
    370 	{
    371 		const int										ndx		= y*gridSize + x;
    372 		const typename TexTypeCoordVec<TexType>::t&		coord	= grid.getQuadTexCoord(x, y);
    373 
    374 		quadColors[ndx] = sample(texture, coord, lod, sampler);
    375 	}
    376 
    377 	setPixelColors(quadColors, dstRegion, grid, dst);
    378 }
    379 
    380 static bool compareImages (const glu::RenderContext& renderCtx, tcu::TestLog& log, const tcu::Surface& ref, const tcu::Surface& res)
    381 {
    382 	DE_ASSERT(renderCtx.getRenderTarget().getNumSamples() == 0);
    383 
    384 	const tcu::RGBA threshold = renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(15,15,15,15);
    385 	return tcu::pixelThresholdCompare(log, "Result", "Image compare result", ref, res, threshold, tcu::COMPARE_LOG_RESULT);
    386 }
    387 
    388 class Vertex2DTextureCase : public TestCase
    389 {
    390 public:
    391 								Vertex2DTextureCase		(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
    392 								~Vertex2DTextureCase	(void);
    393 
    394 	void						init					(void);
    395 	void						deinit					(void);
    396 	IterateResult				iterate					(void);
    397 
    398 private:
    399 	typedef PosTexCoordQuadGrid<TEXTURETYPE_2D> Grid;
    400 
    401 								Vertex2DTextureCase		(const Vertex2DTextureCase& other);
    402 	Vertex2DTextureCase&		operator=				(const Vertex2DTextureCase& other);
    403 
    404 	float						calculateLod			(const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
    405 	void						setupShaderInputs		(int textureNdx, float lod, const Grid& grid) const;
    406 	void						renderCell				(int textureNdx, float lod, const Grid& grid) const;
    407 	void						computeReferenceCell	(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
    408 
    409 	const deUint32				m_minFilter;
    410 	const deUint32				m_magFilter;
    411 	const deUint32				m_wrapS;
    412 	const deUint32				m_wrapT;
    413 
    414 	const glu::ShaderProgram*	m_program;
    415 	glu::Texture2D*				m_textures[2];	// 2 textures, a gradient texture and a grid texture.
    416 };
    417 
    418 Vertex2DTextureCase::Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
    419 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
    420 	, m_minFilter			(minFilter)
    421 	, m_magFilter			(magFilter)
    422 	, m_wrapS				(wrapS)
    423 	, m_wrapT				(wrapT)
    424 	, m_program				(DE_NULL)
    425 {
    426 	m_textures[0] = DE_NULL;
    427 	m_textures[1] = DE_NULL;
    428 }
    429 
    430 Vertex2DTextureCase::~Vertex2DTextureCase(void)
    431 {
    432 	Vertex2DTextureCase::deinit();
    433 }
    434 
    435 void Vertex2DTextureCase::init (void)
    436 {
    437 	const char* const vertexShader =
    438 		"attribute highp vec2 a_position;\n"
    439 		"attribute highp vec2 a_texCoord;\n"
    440 		"uniform highp sampler2D u_texture;\n"
    441 		"uniform highp float u_lod;\n"
    442 		"varying mediump vec4 v_color;\n"
    443 		"\n"
    444 		"void main()\n"
    445 		"{\n"
    446 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
    447 		"	v_color = texture2DLod(u_texture, a_texCoord, u_lod);\n"
    448 		"}\n";
    449 
    450 	const char* const fragmentShader =
    451 		"varying mediump vec4 v_color;\n"
    452 		"\n"
    453 		"void main()\n"
    454 		"{\n"
    455 		"	gl_FragColor = v_color;\n"
    456 		"}\n";
    457 
    458 	if (m_context.getRenderTarget().getNumSamples() != 0)
    459 		throw tcu::NotSupportedError("MSAA config not supported by this test");
    460 
    461 	DE_ASSERT(!m_program);
    462 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
    463 
    464 	if(!m_program->isOk())
    465 	{
    466 		m_testCtx.getLog() << *m_program;
    467 
    468 		GLint maxVertexTextures;
    469 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
    470 
    471 		if (maxVertexTextures < 1)
    472 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
    473 		else
    474 			TCU_FAIL("Failed to compile shader");
    475 	}
    476 
    477 	// Make the textures.
    478 	try
    479 	{
    480 		// Compute suitable power-of-two sizes (for mipmaps).
    481 		const int texWidth		= 1 << deLog2Ceil32(MAX_2D_RENDER_WIDTH / 2);
    482 		const int texHeight		= 1 << deLog2Ceil32(MAX_2D_RENDER_HEIGHT / 2);
    483 
    484 		for (int i = 0; i < 2; i++)
    485 		{
    486 			DE_ASSERT(!m_textures[i]);
    487 			m_textures[i] = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight);
    488 		}
    489 
    490 		const bool						mipmaps		= (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight));
    491 		const int						numLevels	= mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1;
    492 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    493 		const Vec4						cBias		= fmtInfo.valueMin;
    494 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    495 
    496 		// Fill first with gradient texture.
    497 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    498 		{
    499 			const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
    500 			const Vec4 gMax = Vec4( 1.0f,  1.0f,  1.0f, 0.0f)*cScale + cBias;
    501 
    502 			m_textures[0]->getRefTexture().allocLevel(levelNdx);
    503 			tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
    504 		}
    505 
    506 		// Fill second with grid texture.
    507 		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    508 		{
    509 			const deUint32 step		= 0x00ffffff / numLevels;
    510 			const deUint32 rgb		= step*levelNdx;
    511 			const deUint32 colorA	= 0xff000000 | rgb;
    512 			const deUint32 colorB	= 0xff000000 | ~rgb;
    513 
    514 			m_textures[1]->getRefTexture().allocLevel(levelNdx);
    515 			tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
    516 		}
    517 
    518 		// Upload.
    519 		for (int i = 0; i < 2; i++)
    520 			m_textures[i]->upload();
    521 	}
    522 	catch (const std::exception&)
    523 	{
    524 		// Clean up to save memory.
    525 		Vertex2DTextureCase::deinit();
    526 		throw;
    527 	}
    528 }
    529 
    530 void Vertex2DTextureCase::deinit (void)
    531 {
    532 	for (int i = 0; i < 2; i++)
    533 	{
    534 		delete m_textures[i];
    535 		m_textures[i] = DE_NULL;
    536 	}
    537 
    538 	delete m_program;
    539 	m_program = DE_NULL;
    540 }
    541 
    542 float Vertex2DTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
    543 {
    544 	const tcu::Texture2D&		refTexture	= m_textures[textureNdx]->getRefTexture();
    545 	const Vec2					srcSize		= Vec2((float)refTexture.getWidth(), (float)refTexture.getHeight());
    546 	const Vec2					sizeRatio	= texScale*srcSize / dstSize;
    547 
    548 	// \note In this particular case dv/dx and du/dy are zero, simplifying the expression.
    549 	return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
    550 }
    551 
    552 Vertex2DTextureCase::IterateResult Vertex2DTextureCase::iterate (void)
    553 {
    554 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_RENDER_WIDTH);
    555 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_RENDER_HEIGHT);
    556 
    557 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
    558 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
    559 
    560 	de::Random	rnd					(deStringHash(getName()));
    561 
    562 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
    563 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
    564 
    565 	glUseProgram(m_program->getProgram());
    566 
    567 	// Divide viewport into 4 cells.
    568 	const int leftWidth		= viewportWidth / 2;
    569 	const int rightWidth	= viewportWidth - leftWidth;
    570 	const int bottomHeight	= viewportHeight / 2;
    571 	const int topHeight		= viewportHeight - bottomHeight;
    572 
    573 	// Clear.
    574 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
    575 	glClear(GL_COLOR_BUFFER_BIT);
    576 
    577 	// Texture scaling and offsetting vectors.
    578 	const Vec2 texMinScale		(+1.8f, +1.8f);
    579 	const Vec2 texMinOffset		(-0.3f, -0.2f);
    580 	const Vec2 texMagScale		(+0.3f, +0.3f);
    581 	const Vec2 texMagOffset		(+0.9f, +0.8f);
    582 
    583 	// Surface for the reference image.
    584 	tcu::Surface refImage(viewportWidth, viewportHeight);
    585 
    586 	{
    587 		const struct Render
    588 		{
    589 			const Rect	region;
    590 			int			textureNdx;
    591 			const Vec2	texCoordScale;
    592 			const Vec2	texCoordOffset;
    593 			Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
    594 		} renders[] =
    595 		{
    596 			Render(Rect(0,				0,				leftWidth,	bottomHeight),	0, texMinScale, texMinOffset),
    597 			Render(Rect(leftWidth,		0,				rightWidth,	bottomHeight),	0, texMagScale, texMagOffset),
    598 			Render(Rect(0,				bottomHeight,	leftWidth,	topHeight),		1, texMinScale, texMinOffset),
    599 			Render(Rect(leftWidth,		bottomHeight,	rightWidth,	topHeight),		1, texMagScale, texMagOffset)
    600 		};
    601 
    602 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
    603 		{
    604 			const Render&	rend				= renders[renderNdx];
    605 			const float		lod					= calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
    606 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
    607 			const Grid		grid				(GRID_SIZE_2D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
    608 												 TexTypeCoordParams<TEXTURETYPE_2D>(rend.texCoordScale, rend.texCoordOffset), useSafeTexCoords);
    609 
    610 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
    611 			renderCell				(rend.textureNdx, lod, grid);
    612 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
    613 		}
    614 	}
    615 
    616 	// Read back rendered results.
    617 	tcu::Surface resImage(viewportWidth, viewportHeight);
    618 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
    619 
    620 	glUseProgram(0);
    621 
    622 	// Compare and log.
    623 	{
    624 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
    625 
    626 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    627 								isOk ? "Pass"				: "Image comparison failed");
    628 	}
    629 
    630 	return STOP;
    631 }
    632 
    633 void Vertex2DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
    634 {
    635 	const deUint32 programID = m_program->getProgram();
    636 
    637 	// SETUP ATTRIBUTES.
    638 
    639 	{
    640 		const int positionLoc = glGetAttribLocation(programID, "a_position");
    641 		if (positionLoc != -1)
    642 		{
    643 			glEnableVertexAttribArray(positionLoc);
    644 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
    645 		}
    646 	}
    647 
    648 	{
    649 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
    650 		if (texCoordLoc != -1)
    651 		{
    652 			glEnableVertexAttribArray(texCoordLoc);
    653 			glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
    654 		}
    655 	}
    656 
    657 	// SETUP UNIFORMS.
    658 
    659 	{
    660 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
    661 		if (lodLoc != -1)
    662 			glUniform1f(lodLoc, lod);
    663 	}
    664 
    665 	glActiveTexture(GL_TEXTURE0);
    666 	glBindTexture(GL_TEXTURE_2D, m_textures[textureNdx]->getGLTexture());
    667 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		m_wrapS);
    668 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		m_wrapT);
    669 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
    670 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	m_magFilter);
    671 
    672 	{
    673 		const int texLoc = glGetUniformLocation(programID, "u_texture");
    674 		if (texLoc != -1)
    675 			glUniform1i(texLoc, 0);
    676 	}
    677 }
    678 
    679 // Renders one sub-image with given parameters.
    680 void Vertex2DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
    681 {
    682 	setupShaderInputs(textureNdx, lod, grid);
    683 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
    684 }
    685 
    686 void Vertex2DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
    687 {
    688 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion);
    689 }
    690 
    691 class VertexCubeTextureCase : public TestCase
    692 {
    693 public:
    694 								VertexCubeTextureCase	(Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
    695 								~VertexCubeTextureCase	(void);
    696 
    697 	void						init					(void);
    698 	void						deinit					(void);
    699 	IterateResult				iterate					(void);
    700 
    701 private:
    702 	typedef PosTexCoordQuadGrid<TEXTURETYPE_CUBE> Grid;
    703 
    704 								VertexCubeTextureCase	(const VertexCubeTextureCase& other);
    705 	VertexCubeTextureCase&		operator=				(const VertexCubeTextureCase& other);
    706 
    707 	float						calculateLod			(const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
    708 	void						setupShaderInputs		(int textureNdx, float lod, const Grid& grid) const;
    709 	void						renderCell				(int textureNdx, float lod, const Grid& grid) const;
    710 	void						computeReferenceCell	(int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
    711 
    712 	const deUint32				m_minFilter;
    713 	const deUint32				m_magFilter;
    714 	const deUint32				m_wrapS;
    715 	const deUint32				m_wrapT;
    716 
    717 	const glu::ShaderProgram*	m_program;
    718 	glu::TextureCube*			m_textures[2];	// 2 textures, a gradient texture and a grid texture.
    719 };
    720 
    721 VertexCubeTextureCase::VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
    722 	: TestCase				(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
    723 	, m_minFilter			(minFilter)
    724 	, m_magFilter			(magFilter)
    725 	, m_wrapS				(wrapS)
    726 	, m_wrapT				(wrapT)
    727 	, m_program				(DE_NULL)
    728 {
    729 	m_textures[0] = DE_NULL;
    730 	m_textures[1] = DE_NULL;
    731 }
    732 
    733 VertexCubeTextureCase::~VertexCubeTextureCase(void)
    734 {
    735 	VertexCubeTextureCase::deinit();
    736 }
    737 
    738 void VertexCubeTextureCase::init (void)
    739 {
    740 	const char* const vertexShader =
    741 		"attribute highp vec2 a_position;\n"
    742 		"attribute highp vec3 a_texCoord;\n"
    743 		"uniform highp samplerCube u_texture;\n"
    744 		"uniform highp float u_lod;\n"
    745 		"varying mediump vec4 v_color;\n"
    746 		"\n"
    747 		"void main()\n"
    748 		"{\n"
    749 		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
    750 		"	v_color = textureCubeLod(u_texture, a_texCoord, u_lod);\n"
    751 		"}\n";
    752 
    753 	const char* const fragmentShader =
    754 		"varying mediump vec4 v_color;\n"
    755 		"\n"
    756 		"void main()\n"
    757 		"{\n"
    758 		"	gl_FragColor = v_color;\n"
    759 		"}\n";
    760 
    761 	if (m_context.getRenderTarget().getNumSamples() != 0)
    762 		throw tcu::NotSupportedError("MSAA config not supported by this test");
    763 
    764 	DE_ASSERT(!m_program);
    765 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
    766 
    767 	if(!m_program->isOk())
    768 	{
    769 		m_testCtx.getLog() << *m_program;
    770 
    771 		GLint maxVertexTextures;
    772 		glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
    773 
    774 		if (maxVertexTextures < 1)
    775 			throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
    776 		else
    777 			TCU_FAIL("Failed to compile shader");
    778 	}
    779 
    780 	// Make the textures.
    781 	try
    782 	{
    783 		// Compute suitable power-of-two sizes (for mipmaps).
    784 		const int texWidth		= 1 << deLog2Ceil32(MAX_CUBE_RENDER_WIDTH / 3 / 2);
    785 		const int texHeight		= 1 << deLog2Ceil32(MAX_CUBE_RENDER_HEIGHT / 2 / 2);
    786 
    787 		DE_ASSERT(texWidth == texHeight);
    788 		DE_UNREF(texHeight);
    789 
    790 		for (int i = 0; i < 2; i++)
    791 		{
    792 			DE_ASSERT(!m_textures[i]);
    793 			m_textures[i] = new glu::TextureCube(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth);
    794 		}
    795 
    796 		const bool						mipmaps		= deIsPowerOfTwo32(texWidth) != DE_FALSE;
    797 		const int						numLevels	= mipmaps ? deLog2Floor32(texWidth)+1 : 1;
    798 		const tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
    799 		const Vec4						cBias		= fmtInfo.valueMin;
    800 		const Vec4						cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
    801 
    802 		// Fill first with gradient texture.
    803 		static const Vec4 gradients[tcu::CUBEFACE_LAST][2] =
    804 		{
    805 			{ Vec4(-1.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
    806 			{ Vec4( 0.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
    807 			{ Vec4(-1.0f,  0.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
    808 			{ Vec4(-1.0f, -1.0f,  0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
    809 			{ Vec4(-1.0f, -1.0f, -1.0f, 0.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
    810 			{ Vec4( 0.0f,  0.0f,  0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }  // positive z
    811 		};
    812 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    813 		{
    814 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    815 			{
    816 				m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    817 				tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
    818 			}
    819 		}
    820 
    821 		// Fill second with grid texture.
    822 		for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
    823 		{
    824 			for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
    825 			{
    826 				const deUint32 step		= 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
    827 				const deUint32 rgb		= step*levelNdx*face;
    828 				const deUint32 colorA	= 0xff000000 | rgb;
    829 				const deUint32 colorB	= 0xff000000 | ~rgb;
    830 
    831 				m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
    832 				tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
    833 			}
    834 		}
    835 
    836 		// Upload.
    837 		for (int i = 0; i < 2; i++)
    838 			m_textures[i]->upload();
    839 	}
    840 	catch (const std::exception&)
    841 	{
    842 		// Clean up to save memory.
    843 		VertexCubeTextureCase::deinit();
    844 		throw;
    845 	}
    846 }
    847 
    848 void VertexCubeTextureCase::deinit (void)
    849 {
    850 	for (int i = 0; i < 2; i++)
    851 	{
    852 		delete m_textures[i];
    853 		m_textures[i] = DE_NULL;
    854 	}
    855 
    856 	delete m_program;
    857 	m_program = DE_NULL;
    858 }
    859 
    860 float VertexCubeTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
    861 {
    862 	const tcu::TextureCube&		refTexture	= m_textures[textureNdx]->getRefTexture();
    863 	const Vec2					srcSize		= Vec2((float)refTexture.getSize(), (float)refTexture.getSize());
    864 	const Vec2					sizeRatio	= texScale*srcSize / dstSize;
    865 
    866 	// \note In this particular case, dv/dx and du/dy are zero, simplifying the expression.
    867 	return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
    868 }
    869 
    870 VertexCubeTextureCase::IterateResult VertexCubeTextureCase::iterate (void)
    871 {
    872 	const int	viewportWidth		= deMin32(m_context.getRenderTarget().getWidth(), MAX_CUBE_RENDER_WIDTH);
    873 	const int	viewportHeight		= deMin32(m_context.getRenderTarget().getHeight(), MAX_CUBE_RENDER_HEIGHT);
    874 
    875 	const int	viewportXOffsetMax	= m_context.getRenderTarget().getWidth() - viewportWidth;
    876 	const int	viewportYOffsetMax	= m_context.getRenderTarget().getHeight() - viewportHeight;
    877 
    878 	de::Random	rnd					(deStringHash(getName()));
    879 
    880 	const int	viewportXOffset		= rnd.getInt(0, viewportXOffsetMax);
    881 	const int	viewportYOffset		= rnd.getInt(0, viewportYOffsetMax);
    882 
    883 	glUseProgram(m_program->getProgram());
    884 
    885 	// Divide viewport into 4 areas.
    886 	const int leftWidth		= viewportWidth / 2;
    887 	const int rightWidth	= viewportWidth - leftWidth;
    888 	const int bottomHeight	= viewportHeight / 2;
    889 	const int topHeight		= viewportHeight - bottomHeight;
    890 
    891 	// Clear.
    892 	glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
    893 	glClear(GL_COLOR_BUFFER_BIT);
    894 
    895 	// Texture scaling and offsetting vectors.
    896 	const Vec2 texMinScale		(1.0f, 1.0f);
    897 	const Vec2 texMinOffset		(0.0f, 0.0f);
    898 	const Vec2 texMagScale		(0.3f, 0.3f);
    899 	const Vec2 texMagOffset		(0.5f, 0.3f);
    900 
    901 	// Surface for the reference image.
    902 	tcu::Surface refImage(viewportWidth, viewportHeight);
    903 
    904 	// Each of the four areas is divided into 6 cells.
    905 	const int defCellWidth	= viewportWidth / 2 / 3;
    906 	const int defCellHeight	= viewportHeight / 2 / 2;
    907 
    908 	for (int i = 0; i < tcu::CUBEFACE_LAST; i++)
    909 	{
    910 		const int	cellOffsetX			= defCellWidth * (i % 3);
    911 		const int	cellOffsetY			= defCellHeight * (i / 3);
    912 		const bool	isRightmostCell		= i == 2 || i == 5;
    913 		const bool	isTopCell			= i >= 3;
    914 		const int	leftCellWidth		= isRightmostCell	? leftWidth		- cellOffsetX : defCellWidth;
    915 		const int	rightCellWidth		= isRightmostCell	? rightWidth	- cellOffsetX : defCellWidth;
    916 		const int	bottomCellHeight	= isTopCell			? bottomHeight	- cellOffsetY : defCellHeight;
    917 		const int	topCellHeight		= isTopCell			? topHeight		- cellOffsetY : defCellHeight;
    918 
    919 		const struct Render
    920 		{
    921 			const Rect	region;
    922 			int			textureNdx;
    923 			const Vec2	texCoordScale;
    924 			const Vec2	texCoordOffset;
    925 			Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
    926 		} renders[] =
    927 		{
    928 			Render(Rect(cellOffsetX + 0,			cellOffsetY + 0,				leftCellWidth,	bottomCellHeight),	0, texMinScale, texMinOffset),
    929 			Render(Rect(cellOffsetX + leftWidth,	cellOffsetY + 0,				rightCellWidth,	bottomCellHeight),	0, texMagScale, texMagOffset),
    930 			Render(Rect(cellOffsetX + 0,			cellOffsetY + bottomHeight,		leftCellWidth,	topCellHeight),		1, texMinScale, texMinOffset),
    931 			Render(Rect(cellOffsetX + leftWidth,	cellOffsetY + bottomHeight,		rightCellWidth,	topCellHeight),		1, texMagScale, texMagOffset)
    932 		};
    933 
    934 		for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
    935 		{
    936 			const Render&	rend				= renders[renderNdx];
    937 			const float		lod					= calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
    938 			const bool		useSafeTexCoords	= isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
    939 			const Grid		grid				(GRID_SIZE_CUBE, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
    940 												 TexTypeCoordParams<TEXTURETYPE_CUBE>(rend.texCoordScale, rend.texCoordOffset, (tcu::CubeFace)i), useSafeTexCoords);
    941 
    942 			glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
    943 			renderCell				(rend.textureNdx, lod, grid);
    944 			computeReferenceCell	(rend.textureNdx, lod, grid, refImage, rend.region);
    945 		}
    946 	}
    947 
    948 	// Read back rendered results.
    949 	tcu::Surface resImage(viewportWidth, viewportHeight);
    950 	glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
    951 
    952 	glUseProgram(0);
    953 
    954 	// Compare and log.
    955 	{
    956 		const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
    957 
    958 		m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
    959 								isOk ? "Pass"				: "Image comparison failed");
    960 	}
    961 
    962 	return STOP;
    963 }
    964 
    965 void VertexCubeTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
    966 {
    967 	const deUint32 programID = m_program->getProgram();
    968 
    969 	// SETUP ATTRIBUTES.
    970 
    971 	{
    972 		const int positionLoc = glGetAttribLocation(programID, "a_position");
    973 		if (positionLoc != -1)
    974 		{
    975 			glEnableVertexAttribArray(positionLoc);
    976 			glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
    977 		}
    978 	}
    979 
    980 	{
    981 		const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
    982 		if (texCoordLoc != -1)
    983 		{
    984 			glEnableVertexAttribArray(texCoordLoc);
    985 			glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
    986 		}
    987 	}
    988 
    989 	// SETUP UNIFORMS.
    990 
    991 	{
    992 		const int lodLoc = glGetUniformLocation(programID, "u_lod");
    993 		if (lodLoc != -1)
    994 			glUniform1f(lodLoc, lod);
    995 	}
    996 
    997 	glActiveTexture(GL_TEXTURE0);
    998 	glBindTexture(GL_TEXTURE_CUBE_MAP, m_textures[textureNdx]->getGLTexture());
    999 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
   1000 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
   1001 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
   1002 	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	m_magFilter);
   1003 
   1004 	{
   1005 		const int texLoc = glGetUniformLocation(programID, "u_texture");
   1006 		if (texLoc != -1)
   1007 			glUniform1i(texLoc, 0);
   1008 	}
   1009 }
   1010 
   1011 // Renders one cube face with given parameters.
   1012 void VertexCubeTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
   1013 {
   1014 	setupShaderInputs(textureNdx, lod, grid);
   1015 	glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
   1016 }
   1017 
   1018 // Computes reference for one cube face with given parameters.
   1019 void VertexCubeTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
   1020 {
   1021 	tcu::Sampler sampler = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
   1022 	computeReference(m_textures[textureNdx]->getRefTexture(), lod, sampler, grid, dst, dstRegion);
   1023 }
   1024 
   1025 VertexTextureTests::VertexTextureTests (Context& context)
   1026 	: TestCaseGroup(context, "vertex", "Vertex Texture Tests")
   1027 {
   1028 }
   1029 
   1030 VertexTextureTests::~VertexTextureTests(void)
   1031 {
   1032 }
   1033 
   1034 void VertexTextureTests::init (void)
   1035 {
   1036 	// 2D and cube map groups, and their filtering and wrap sub-groups.
   1037 	TestCaseGroup* const group2D				= new TestCaseGroup(m_context, "2d",			"2D Vertex Texture Tests");
   1038 	TestCaseGroup* const groupCube				= new TestCaseGroup(m_context, "cube",			"Cube Map Vertex Texture Tests");
   1039 	TestCaseGroup* const filteringGroup2D		= new TestCaseGroup(m_context, "filtering",		"2D Vertex Texture Filtering Tests");
   1040 	TestCaseGroup* const wrapGroup2D			= new TestCaseGroup(m_context, "wrap",			"2D Vertex Texture Wrap Tests");
   1041 	TestCaseGroup* const filteringGroupCube		= new TestCaseGroup(m_context, "filtering",		"Cube Map Vertex Texture Filtering Tests");
   1042 	TestCaseGroup* const wrapGroupCube			= new TestCaseGroup(m_context, "wrap",			"Cube Map Vertex Texture Wrap Tests");
   1043 
   1044 	group2D->addChild(filteringGroup2D);
   1045 	group2D->addChild(wrapGroup2D);
   1046 	groupCube->addChild(filteringGroupCube);
   1047 	groupCube->addChild(wrapGroupCube);
   1048 
   1049 	addChild(group2D);
   1050 	addChild(groupCube);
   1051 
   1052 	static const struct
   1053 	{
   1054 		const char*		name;
   1055 		GLenum			mode;
   1056 	} wrapModes[] =
   1057 	{
   1058 		{ "clamp",		GL_CLAMP_TO_EDGE	},
   1059 		{ "repeat",		GL_REPEAT			},
   1060 		{ "mirror",		GL_MIRRORED_REPEAT	}
   1061 	};
   1062 
   1063 	static const struct
   1064 	{
   1065 		const char*		name;
   1066 		GLenum			mode;
   1067 	} minFilterModes[] =
   1068 	{
   1069 		{ "nearest",				GL_NEAREST					},
   1070 		{ "linear",					GL_LINEAR					},
   1071 		{ "nearest_mipmap_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
   1072 		{ "linear_mipmap_nearest",	GL_LINEAR_MIPMAP_NEAREST	},
   1073 		{ "nearest_mipmap_linear",	GL_NEAREST_MIPMAP_LINEAR	},
   1074 		{ "linear_mipmap_linear",	GL_LINEAR_MIPMAP_LINEAR		}
   1075 	};
   1076 
   1077 	static const struct
   1078 	{
   1079 		const char*		name;
   1080 		GLenum			mode;
   1081 	} magFilterModes[] =
   1082 	{
   1083 		{ "nearest",	GL_NEAREST	},
   1084 		{ "linear",		GL_LINEAR	}
   1085 	};
   1086 
   1087 #define FOR_EACH(ITERATOR, ARRAY, BODY)	\
   1088 	for (int (ITERATOR) = 0; (ITERATOR) < DE_LENGTH_OF_ARRAY(ARRAY); (ITERATOR)++)	\
   1089 		BODY
   1090 
   1091 	// 2D cases.
   1092 
   1093 	FOR_EACH(minFilter,		minFilterModes,
   1094 	FOR_EACH(magFilter,		magFilterModes,
   1095 	FOR_EACH(wrapMode,		wrapModes,
   1096 		{
   1097 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1098 
   1099 			filteringGroup2D->addChild(new Vertex2DTextureCase(m_context,
   1100 															   name.c_str(), "",
   1101 															   minFilterModes[minFilter].mode,
   1102 															   magFilterModes[magFilter].mode,
   1103 															   wrapModes[wrapMode].mode,
   1104 															   wrapModes[wrapMode].mode));
   1105 		})));
   1106 
   1107 	FOR_EACH(wrapSMode,		wrapModes,
   1108 	FOR_EACH(wrapTMode,		wrapModes,
   1109 		{
   1110 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
   1111 
   1112 			wrapGroup2D->addChild(new Vertex2DTextureCase(m_context,
   1113 														  name.c_str(), "",
   1114 														  GL_LINEAR_MIPMAP_LINEAR,
   1115 														  GL_LINEAR,
   1116 														  wrapModes[wrapSMode].mode,
   1117 														  wrapModes[wrapTMode].mode));
   1118 		}));
   1119 
   1120 	// Cube map cases.
   1121 
   1122 	FOR_EACH(minFilter,		minFilterModes,
   1123 	FOR_EACH(magFilter,		magFilterModes,
   1124 	FOR_EACH(wrapMode,		wrapModes,
   1125 		{
   1126 			const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
   1127 
   1128 			filteringGroupCube->addChild(new VertexCubeTextureCase(m_context,
   1129 																   name.c_str(), "",
   1130 																   minFilterModes[minFilter].mode,
   1131 																   magFilterModes[magFilter].mode,
   1132 																   wrapModes[wrapMode].mode,
   1133 																   wrapModes[wrapMode].mode));
   1134 		})));
   1135 
   1136 	FOR_EACH(wrapSMode,		wrapModes,
   1137 	FOR_EACH(wrapTMode,		wrapModes,
   1138 		{
   1139 			const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
   1140 
   1141 			wrapGroupCube->addChild(new VertexCubeTextureCase(m_context,
   1142 															  name.c_str(), "",
   1143 															  GL_LINEAR_MIPMAP_LINEAR,
   1144 															  GL_LINEAR,
   1145 															  wrapModes[wrapSMode].mode,
   1146 															  wrapModes[wrapTMode].mode));
   1147 		}));
   1148 }
   1149 
   1150 } // Functional
   1151 } // gles2
   1152 } // deqp
   1153