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