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