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 Mipmapping tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fTextureMipmapTests.hpp"
25
26#include "glsTextureTestUtil.hpp"
27#include "gluTexture.hpp"
28#include "gluTextureUtil.hpp"
29#include "gluPixelTransfer.hpp"
30#include "tcuTextureUtil.hpp"
31#include "tcuMatrix.hpp"
32#include "tcuMatrixUtil.hpp"
33#include "tcuTexLookupVerifier.hpp"
34#include "tcuVectorUtil.hpp"
35#include "deStringUtil.hpp"
36#include "deRandom.hpp"
37#include "deString.h"
38#include "glwFunctions.hpp"
39#include "glwEnums.hpp"
40
41using std::vector;
42using std::string;
43using namespace deqp::gls;
44
45namespace deqp
46{
47namespace gles3
48{
49namespace Functional
50{
51
52using std::string;
53using std::vector;
54using tcu::TestLog;
55using tcu::Vec2;
56using tcu::Vec3;
57using tcu::Vec4;
58using tcu::IVec4;
59using namespace gls::TextureTestUtil;
60
61static float getMinLodForCell (int cellNdx)
62{
63	static const float s_values[] =
64	{
65		1.0f,
66		3.5f,
67		2.0f,
68		-2.0f,
69		0.0f,
70		3.0f,
71		10.0f,
72		4.8f,
73		5.8f,
74		5.7f,
75		-1.9f,
76		4.0f,
77		6.5f,
78		7.1f,
79		-1e10,
80		1000.f
81	};
82	return s_values[cellNdx % DE_LENGTH_OF_ARRAY(s_values)];
83}
84
85static float getMaxLodForCell (int cellNdx)
86{
87	static const float s_values[] =
88	{
89		0.0f,
90		0.2f,
91		0.7f,
92		0.4f,
93		1.3f,
94		0.0f,
95		0.5f,
96		1.2f,
97		-2.0f,
98		1.0f,
99		0.1f,
100		0.3f,
101		2.7f,
102		1.2f,
103		10.0f,
104		-1000.f,
105		1e10f
106	};
107	return s_values[cellNdx % DE_LENGTH_OF_ARRAY(s_values)];
108}
109
110enum CoordType
111{
112	COORDTYPE_BASIC,		//!< texCoord = translateScale(position).
113	COORDTYPE_BASIC_BIAS,	//!< Like basic, but with bias values.
114	COORDTYPE_AFFINE,		//!< texCoord = translateScaleRotateShear(position).
115	COORDTYPE_PROJECTED,	//!< Projected coordinates, w != 1
116
117	COORDTYPE_LAST
118};
119
120// Texture2DMipmapCase
121
122class Texture2DMipmapCase : public tcu::TestCase
123{
124public:
125
126								Texture2DMipmapCase			(tcu::TestContext&			testCtx,
127															 glu::RenderContext&		renderCtx,
128															 const glu::ContextInfo&	renderCtxInfo,
129															 const char*				name,
130															 const char*				desc,
131															 CoordType					coordType,
132															 deUint32					minFilter,
133															 deUint32					wrapS,
134															 deUint32					wrapT,
135															 deUint32					format,
136															 deUint32					dataType,
137															 int						width,
138															 int						height);
139								~Texture2DMipmapCase		(void);
140
141	void						init						(void);
142	void						deinit						(void);
143	IterateResult				iterate						(void);
144
145private:
146								Texture2DMipmapCase			(const Texture2DMipmapCase& other);
147	Texture2DMipmapCase&		operator=					(const Texture2DMipmapCase& other);
148
149	glu::RenderContext&			m_renderCtx;
150	const glu::ContextInfo&		m_renderCtxInfo;
151
152	CoordType					m_coordType;
153	deUint32					m_minFilter;
154	deUint32					m_wrapS;
155	deUint32					m_wrapT;
156	deUint32					m_format;
157	deUint32					m_dataType;
158	int							m_width;
159	int							m_height;
160
161	glu::Texture2D*				m_texture;
162	TextureRenderer				m_renderer;
163};
164
165Texture2DMipmapCase::Texture2DMipmapCase (tcu::TestContext&			testCtx,
166										  glu::RenderContext&		renderCtx,
167										  const glu::ContextInfo&	renderCtxInfo,
168										  const char*				name,
169										  const char*				desc,
170										  CoordType					coordType,
171										  deUint32					minFilter,
172										  deUint32					wrapS,
173										  deUint32					wrapT,
174										  deUint32					format,
175										  deUint32					dataType,
176										  int						width,
177										  int						height)
178	: TestCase			(testCtx, name, desc)
179	, m_renderCtx		(renderCtx)
180	, m_renderCtxInfo	(renderCtxInfo)
181	, m_coordType		(coordType)
182	, m_minFilter		(minFilter)
183	, m_wrapS			(wrapS)
184	, m_wrapT			(wrapT)
185	, m_format			(format)
186	, m_dataType		(dataType)
187	, m_width			(width)
188	, m_height			(height)
189	, m_texture			(DE_NULL)
190	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
191{
192}
193
194Texture2DMipmapCase::~Texture2DMipmapCase (void)
195{
196	deinit();
197}
198
199void Texture2DMipmapCase::init (void)
200{
201	if (m_coordType == COORDTYPE_PROJECTED && m_renderCtx.getRenderTarget().getNumSamples() > 0)
202		throw tcu::NotSupportedError("Projected lookup validation not supported in multisample config");
203
204	m_texture = new glu::Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height);
205
206	int numLevels = deLog2Floor32(de::max(m_width, m_height))+1;
207
208	// Fill texture with colored grid.
209	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
210	{
211		deUint32	step		= 0xff / (numLevels-1);
212		deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
213		deUint32	dec			= 0xff - inc;
214		deUint32	rgb			= (inc << 16) | (dec << 8) | 0xff;
215		deUint32	color		= 0xff000000 | rgb;
216
217		m_texture->getRefTexture().allocLevel(levelNdx);
218		tcu::clear(m_texture->getRefTexture().getLevel(levelNdx), toVec4(tcu::RGBA(color)));
219	}
220}
221
222void Texture2DMipmapCase::deinit (void)
223{
224	delete m_texture;
225	m_texture = DE_NULL;
226
227	m_renderer.clear();
228}
229
230static void getBasicTexCoord2D (std::vector<float>& dst, int cellNdx)
231{
232	static const struct
233	{
234		Vec2 bottomLeft;
235		Vec2 topRight;
236	} s_basicCoords[] =
237	{
238		{ Vec2(-0.1f,  0.1f), Vec2( 0.8f,  1.0f) },
239		{ Vec2(-0.3f, -0.6f), Vec2( 0.7f,  0.4f) },
240		{ Vec2(-0.3f,  0.6f), Vec2( 0.7f, -0.9f) },
241		{ Vec2(-0.8f,  0.6f), Vec2( 0.7f, -0.9f) },
242
243		{ Vec2(-0.5f, -0.5f), Vec2( 1.5f,  1.5f) },
244		{ Vec2( 1.0f, -1.0f), Vec2(-1.3f,  1.0f) },
245		{ Vec2( 1.2f, -1.0f), Vec2(-1.3f,  1.6f) },
246		{ Vec2( 2.2f, -1.1f), Vec2(-1.3f,  0.8f) },
247
248		{ Vec2(-1.5f,  1.6f), Vec2( 1.7f, -1.4f) },
249		{ Vec2( 2.0f,  1.6f), Vec2( 2.3f, -1.4f) },
250		{ Vec2( 1.3f, -2.6f), Vec2(-2.7f,  2.9f) },
251		{ Vec2(-0.8f, -6.6f), Vec2( 6.0f, -0.9f) },
252
253		{ Vec2( -8.0f,   9.0f), Vec2(  8.3f,  -7.0f) },
254		{ Vec2(-16.0f,  10.0f), Vec2( 18.3f,  24.0f) },
255		{ Vec2( 30.2f,  55.0f), Vec2(-24.3f,  -1.6f) },
256		{ Vec2(-33.2f,  64.1f), Vec2( 32.1f, -64.1f) },
257	};
258
259	DE_ASSERT(de::inBounds(cellNdx, 0, DE_LENGTH_OF_ARRAY(s_basicCoords)));
260
261	const Vec2& bottomLeft	= s_basicCoords[cellNdx].bottomLeft;
262	const Vec2& topRight	= s_basicCoords[cellNdx].topRight;
263
264	computeQuadTexCoord2D(dst, bottomLeft, topRight);
265}
266
267static void getAffineTexCoord2D (std::vector<float>& dst, int cellNdx)
268{
269	// Use basic coords as base.
270	getBasicTexCoord2D(dst, cellNdx);
271
272	// Rotate based on cell index.
273	float		angle		= 2.0f*DE_PI * ((float)cellNdx / 16.0f);
274	tcu::Mat2	rotMatrix	= tcu::rotationMatrix(angle);
275
276	// Second and third row are sheared.
277	float		shearX		= de::inRange(cellNdx, 4, 11) ? (float)(15-cellNdx) / 16.0f : 0.0f;
278	tcu::Mat2	shearMatrix	= tcu::shearMatrix(tcu::Vec2(shearX, 0.0f));
279
280	tcu::Mat2	transform	= rotMatrix * shearMatrix;
281	Vec2		p0			= transform * Vec2(dst[0], dst[1]);
282	Vec2		p1			= transform * Vec2(dst[2], dst[3]);
283	Vec2		p2			= transform * Vec2(dst[4], dst[5]);
284	Vec2		p3			= transform * Vec2(dst[6], dst[7]);
285
286	dst[0] = p0.x();	dst[1] = p0.y();
287	dst[2] = p1.x();	dst[3] = p1.y();
288	dst[4] = p2.x();	dst[5] = p2.y();
289	dst[6] = p3.x();	dst[7] = p3.y();
290}
291
292Texture2DMipmapCase::IterateResult Texture2DMipmapCase::iterate (void)
293{
294	const glw::Functions&		gl					= m_renderCtx.getFunctions();
295
296	const tcu::Texture2D&		refTexture			= m_texture->getRefTexture();
297
298	const deUint32				magFilter			= GL_NEAREST;
299	const int					texWidth			= refTexture.getWidth();
300	const int					texHeight			= refTexture.getHeight();
301	const int					defViewportWidth	= texWidth*4;
302	const int					defViewportHeight	= texHeight*4;
303
304	const RandomViewport		viewport			(m_renderCtx.getRenderTarget(), defViewportWidth, defViewportHeight, deStringHash(getName()));
305	ReferenceParams				sampleParams		(TEXTURETYPE_2D);
306	vector<float>				texCoord;
307
308	const bool					isProjected			= m_coordType == COORDTYPE_PROJECTED;
309	const bool					useLodBias			= m_coordType == COORDTYPE_BASIC_BIAS;
310
311	tcu::Surface				renderedFrame		(viewport.width, viewport.height);
312
313	// Viewport is divided into 4x4 grid.
314	int							gridWidth			= 4;
315	int							gridHeight			= 4;
316	int							cellWidth			= viewport.width / gridWidth;
317	int							cellHeight			= viewport.height / gridHeight;
318
319	// Bail out if rendertarget is too small.
320	if (viewport.width < defViewportWidth/2 || viewport.height < defViewportHeight/2)
321		throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
322
323	// Sampling parameters.
324	sampleParams.sampler		= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, magFilter);
325	sampleParams.samplerType	= gls::TextureTestUtil::getSamplerType(m_texture->getRefTexture().getFormat());
326	sampleParams.flags			= (isProjected ? ReferenceParams::PROJECTED : 0) | (useLodBias ? ReferenceParams::USE_BIAS : 0);
327	sampleParams.lodMode		= LODMODE_EXACT; // Use ideal lod.
328
329	// Upload texture data.
330	m_texture->upload();
331
332	// Bind gradient texture and setup sampler parameters.
333	gl.bindTexture	(GL_TEXTURE_2D, m_texture->getGLTexture());
334	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		m_wrapS);
335	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		m_wrapT);
336	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
337	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	magFilter);
338
339	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
340
341	// Bias values.
342	static const float s_bias[] = { 1.0f, -2.0f, 0.8f, -0.5f, 1.5f, 0.9f, 2.0f, 4.0f };
343
344	// Projection values.
345	static const Vec4 s_projections[] =
346	{
347		Vec4(1.2f, 1.0f, 0.7f, 1.0f),
348		Vec4(1.3f, 0.8f, 0.6f, 2.0f),
349		Vec4(0.8f, 1.0f, 1.7f, 0.6f),
350		Vec4(1.2f, 1.0f, 1.7f, 1.5f)
351	};
352
353	// Render cells.
354	for (int gridY = 0; gridY < gridHeight; gridY++)
355	{
356		for (int gridX = 0; gridX < gridWidth; gridX++)
357		{
358			const int		curX		= cellWidth*gridX;
359			const int		curY		= cellHeight*gridY;
360			const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
361			const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
362			const int		cellNdx		= gridY*gridWidth + gridX;
363
364			// Compute texcoord.
365			switch (m_coordType)
366			{
367				case COORDTYPE_BASIC_BIAS:	// Fall-through.
368				case COORDTYPE_PROJECTED:
369				case COORDTYPE_BASIC:		getBasicTexCoord2D	(texCoord, cellNdx);	break;
370				case COORDTYPE_AFFINE:		getAffineTexCoord2D	(texCoord, cellNdx);	break;
371				default:					DE_ASSERT(DE_FALSE);
372			}
373
374			if (isProjected)
375				sampleParams.w = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
376
377			if (useLodBias)
378				sampleParams.bias = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
379
380			// Render with GL.
381			gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
382			m_renderer.renderQuad(0, &texCoord[0], sampleParams);
383		}
384	}
385
386	// Read result.
387	glu::readPixels(m_renderCtx, viewport.x, viewport.y, renderedFrame.getAccess());
388
389	// Compare and log.
390	{
391		const tcu::PixelFormat&	pixelFormat		= m_renderCtx.getRenderTarget().getPixelFormat();
392		const bool				isTrilinear		= m_minFilter == GL_NEAREST_MIPMAP_LINEAR || m_minFilter == GL_LINEAR_MIPMAP_LINEAR;
393		tcu::Surface			referenceFrame	(viewport.width, viewport.height);
394		tcu::Surface			errorMask		(viewport.width, viewport.height);
395		tcu::LookupPrecision	lookupPrec;
396		tcu::LodPrecision		lodPrec;
397		int						numFailedPixels	= 0;
398
399		lookupPrec.coordBits		= tcu::IVec3(20, 20, 0);
400		lookupPrec.uvwBits			= tcu::IVec3(16, 16, 0); // Doesn't really matter since pixels are unicolored.
401		lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(max(getBitsVec(pixelFormat) - (isTrilinear ? 2 : 1), tcu::IVec4(0)));
402		lookupPrec.colorMask		= getCompareMask(pixelFormat);
403		lodPrec.derivateBits		= 10;
404		lodPrec.lodBits				= isProjected ? 6 : 8;
405
406		for (int gridY = 0; gridY < gridHeight; gridY++)
407		{
408			for (int gridX = 0; gridX < gridWidth; gridX++)
409			{
410				const int		curX		= cellWidth*gridX;
411				const int		curY		= cellHeight*gridY;
412				const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
413				const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
414				const int		cellNdx		= gridY*gridWidth + gridX;
415
416				// Compute texcoord.
417				switch (m_coordType)
418				{
419					case COORDTYPE_BASIC_BIAS:	// Fall-through.
420					case COORDTYPE_PROJECTED:
421					case COORDTYPE_BASIC:		getBasicTexCoord2D	(texCoord, cellNdx);	break;
422					case COORDTYPE_AFFINE:		getAffineTexCoord2D	(texCoord, cellNdx);	break;
423					default:					DE_ASSERT(DE_FALSE);
424				}
425
426				if (isProjected)
427					sampleParams.w = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
428
429				if (useLodBias)
430					sampleParams.bias = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
431
432				// Render ideal result
433				sampleTexture(SurfaceAccess(referenceFrame, pixelFormat, curX, curY, curW, curH),
434							  refTexture, &texCoord[0], sampleParams);
435
436				// Compare this cell
437				numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
438															tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
439															tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
440															m_texture->getRefTexture(), &texCoord[0], sampleParams,
441															lookupPrec, lodPrec, m_testCtx.getWatchDog());
442			}
443		}
444
445		if (numFailedPixels > 0)
446			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
447
448		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
449							<< TestLog::Image("Rendered", "Rendered image", renderedFrame);
450
451		if (numFailedPixels > 0)
452		{
453			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
454								<< TestLog::Image("ErrorMask", "Error mask", errorMask);
455		}
456
457		m_testCtx.getLog() << TestLog::EndImageSet;
458
459		{
460			const bool isOk = numFailedPixels == 0;
461			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
462									isOk ? "Pass"				: "Image verification failed");
463		}
464	}
465
466	return STOP;
467}
468
469// TextureCubeMipmapCase
470
471class TextureCubeMipmapCase : public tcu::TestCase
472{
473public:
474
475								TextureCubeMipmapCase		(tcu::TestContext&			testCtx,
476															 glu::RenderContext&		renderCtx,
477															 const glu::ContextInfo&	renderCtxInfo,
478															 const char*				name,
479															 const char*				desc,
480															 CoordType					coordType,
481															 deUint32					minFilter,
482															 deUint32					wrapS,
483															 deUint32					wrapT,
484															 deUint32					format,
485															 deUint32					dataType,
486															 int						size);
487								~TextureCubeMipmapCase		(void);
488
489	void						init						(void);
490	void						deinit						(void);
491	IterateResult				iterate						(void);
492
493private:
494								TextureCubeMipmapCase		(const TextureCubeMipmapCase& other);
495	TextureCubeMipmapCase&		operator=					(const TextureCubeMipmapCase& other);
496
497	glu::RenderContext&			m_renderCtx;
498	const glu::ContextInfo&		m_renderCtxInfo;
499
500	CoordType					m_coordType;
501	deUint32					m_minFilter;
502	deUint32					m_wrapS;
503	deUint32					m_wrapT;
504	deUint32					m_format;
505	deUint32					m_dataType;
506	int							m_size;
507
508	glu::TextureCube*			m_texture;
509	TextureRenderer				m_renderer;
510};
511
512TextureCubeMipmapCase::TextureCubeMipmapCase (tcu::TestContext&			testCtx,
513											  glu::RenderContext&		renderCtx,
514											  const glu::ContextInfo&	renderCtxInfo,
515											  const char*				name,
516											  const char*				desc,
517											  CoordType					coordType,
518											  deUint32					minFilter,
519											  deUint32					wrapS,
520											  deUint32					wrapT,
521											  deUint32					format,
522											  deUint32					dataType,
523											  int						size)
524	: TestCase			(testCtx, name, desc)
525	, m_renderCtx		(renderCtx)
526	, m_renderCtxInfo	(renderCtxInfo)
527	, m_coordType		(coordType)
528	, m_minFilter		(minFilter)
529	, m_wrapS			(wrapS)
530	, m_wrapT			(wrapT)
531	, m_format			(format)
532	, m_dataType		(dataType)
533	, m_size			(size)
534	, m_texture			(DE_NULL)
535	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
536{
537}
538
539TextureCubeMipmapCase::~TextureCubeMipmapCase (void)
540{
541	deinit();
542}
543
544void TextureCubeMipmapCase::init (void)
545{
546	if (m_coordType == COORDTYPE_PROJECTED && m_renderCtx.getRenderTarget().getNumSamples() > 0)
547		throw tcu::NotSupportedError("Projected lookup validation not supported in multisample config");
548
549	m_texture = new glu::TextureCube(m_renderCtx, m_format, m_dataType, m_size);
550
551	int numLevels = deLog2Floor32(m_size)+1;
552
553	// Fill texture with colored grid.
554	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
555	{
556		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
557		{
558			deUint32	step		= 0xff / (numLevels-1);
559			deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
560			deUint32	dec			= 0xff - inc;
561			deUint32	rgb			= 0;
562
563			switch (faceNdx)
564			{
565				case 0: rgb = (inc << 16) | (dec << 8) | 255; break;
566				case 1: rgb = (255 << 16) | (inc << 8) | dec; break;
567				case 2: rgb = (dec << 16) | (255 << 8) | inc; break;
568				case 3: rgb = (dec << 16) | (inc << 8) | 255; break;
569				case 4: rgb = (255 << 16) | (dec << 8) | inc; break;
570				case 5: rgb = (inc << 16) | (255 << 8) | dec; break;
571			}
572
573			deUint32	color		= 0xff000000 | rgb;
574
575			m_texture->getRefTexture().allocLevel((tcu::CubeFace)faceNdx, levelNdx);
576			tcu::clear(m_texture->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)faceNdx), toVec4(tcu::RGBA(color)));
577		}
578	}
579}
580
581void TextureCubeMipmapCase::deinit (void)
582{
583	delete m_texture;
584	m_texture = DE_NULL;
585
586	m_renderer.clear();
587}
588
589static void randomPartition (vector<IVec4>& dst, de::Random& rnd, int x, int y, int width, int height)
590{
591	const int minWidth	= 8;
592	const int minHeight	= 8;
593
594	bool	partition		= rnd.getFloat() > 0.4f;
595	bool	partitionX		= partition && width > minWidth && rnd.getBool();
596	bool	partitionY		= partition && height > minHeight && !partitionX;
597
598	if (partitionX)
599	{
600		int split = width/2 + rnd.getInt(-width/4, +width/4);
601		randomPartition(dst, rnd, x, y, split, height);
602		randomPartition(dst, rnd, x+split, y, width-split, height);
603	}
604	else if (partitionY)
605	{
606		int split = height/2 + rnd.getInt(-height/4, +height/4);
607		randomPartition(dst, rnd, x, y, width, split);
608		randomPartition(dst, rnd, x, y+split, width, height-split);
609	}
610	else
611		dst.push_back(IVec4(x, y, width, height));
612}
613
614static void computeGridLayout (vector<IVec4>& dst, int width, int height)
615{
616	de::Random rnd(7);
617	randomPartition(dst, rnd, 0, 0, width, height);
618}
619
620TextureCubeMipmapCase::IterateResult TextureCubeMipmapCase::iterate (void)
621{
622	const deUint32			magFilter			= GL_NEAREST;
623	const int				texWidth			= m_texture->getRefTexture().getSize();
624	const int				texHeight			= m_texture->getRefTexture().getSize();
625	const int				defViewportWidth	= texWidth*2;
626	const int				defViewportHeight	= texHeight*2;
627
628	const glw::Functions&	gl					= m_renderCtx.getFunctions();
629	const RandomViewport	viewport			(m_renderCtx.getRenderTarget(), defViewportWidth, defViewportHeight, deStringHash(getName()));
630
631	const bool				isProjected			= m_coordType == COORDTYPE_PROJECTED;
632	const bool				useLodBias			= m_coordType == COORDTYPE_BASIC_BIAS;
633
634	vector<float>			texCoord;
635	tcu::Surface			renderedFrame		(viewport.width, viewport.height);
636
637	// Bail out if rendertarget is too small.
638	if (viewport.width < defViewportWidth/2 || viewport.height < defViewportHeight/2)
639		throw tcu::NotSupportedError("Too small viewport", "", __FILE__, __LINE__);
640
641	// Upload texture data.
642	m_texture->upload();
643
644	// Bind gradient texture and setup sampler parameters.
645	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, m_texture->getGLTexture());
646	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		m_wrapS);
647	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		m_wrapT);
648	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
649	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	magFilter);
650
651	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
652
653	// Compute grid.
654	vector<IVec4> gridLayout;
655	computeGridLayout(gridLayout, viewport.width, viewport.height);
656
657	// Bias values.
658	static const float s_bias[] = { 1.0f, -2.0f, 0.8f, -0.5f, 1.5f, 0.9f, 2.0f, 4.0f };
659
660	// Projection values \note Less agressive than in 2D case due to smaller quads.
661	static const Vec4 s_projections[] =
662	{
663		Vec4(1.2f, 1.0f, 0.7f, 1.0f),
664		Vec4(1.3f, 0.8f, 0.6f, 1.1f),
665		Vec4(0.8f, 1.0f, 1.2f, 0.8f),
666		Vec4(1.2f, 1.0f, 1.3f, 0.9f)
667	};
668
669	// Render with GL
670	for (int cellNdx = 0; cellNdx < (int)gridLayout.size(); cellNdx++)
671	{
672		const int			curX		= gridLayout[cellNdx].x();
673		const int			curY		= gridLayout[cellNdx].y();
674		const int			curW		= gridLayout[cellNdx].z();
675		const int			curH		= gridLayout[cellNdx].w();
676		const tcu::CubeFace	cubeFace	= (tcu::CubeFace)(cellNdx % tcu::CUBEFACE_LAST);
677		RenderParams		params		(TEXTURETYPE_CUBE);
678
679		DE_ASSERT(m_coordType != COORDTYPE_AFFINE); // Not supported.
680		computeQuadTexCoordCube(texCoord, cubeFace);
681
682		if (isProjected)
683		{
684			params.flags	|= ReferenceParams::PROJECTED;
685			params.w		 = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
686		}
687
688		if (useLodBias)
689		{
690			params.flags	|= ReferenceParams::USE_BIAS;
691			params.bias		 = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
692		}
693
694		// Render with GL.
695		gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
696		m_renderer.renderQuad(0, &texCoord[0], params);
697	}
698	GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
699
700	// Read result.
701	glu::readPixels(m_renderCtx, viewport.x, viewport.y, renderedFrame.getAccess());
702	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
703
704	// Render reference and compare
705	{
706		tcu::Surface			referenceFrame		(viewport.width, viewport.height);
707		tcu::Surface			errorMask			(viewport.width, viewport.height);
708		int						numFailedPixels		= 0;
709		ReferenceParams			params				(TEXTURETYPE_CUBE);
710		tcu::LookupPrecision	lookupPrec;
711		tcu::LodPrecision		lodPrec;
712
713		// Params for rendering reference
714		params.sampler					= glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, magFilter);
715		params.sampler.seamlessCubeMap	= true;
716		params.lodMode					= LODMODE_EXACT;
717
718		// Comparison parameters
719		lookupPrec.colorMask			= getCompareMask(m_renderCtx.getRenderTarget().getPixelFormat());
720		lookupPrec.colorThreshold		= tcu::computeFixedPointThreshold(max(getBitsVec(m_renderCtx.getRenderTarget().getPixelFormat())-2, IVec4(0)));
721		lookupPrec.coordBits			= isProjected ? tcu::IVec3(8) : tcu::IVec3(10);
722		lookupPrec.uvwBits				= tcu::IVec3(5,5,0);
723		lodPrec.derivateBits			= 10;
724		lodPrec.lodBits					= isProjected ? 3 : 6;
725
726		for (int cellNdx = 0; cellNdx < (int)gridLayout.size(); cellNdx++)
727		{
728			const int				curX		= gridLayout[cellNdx].x();
729			const int				curY		= gridLayout[cellNdx].y();
730			const int				curW		= gridLayout[cellNdx].z();
731			const int				curH		= gridLayout[cellNdx].w();
732			const tcu::CubeFace		cubeFace	= (tcu::CubeFace)(cellNdx % tcu::CUBEFACE_LAST);
733
734			DE_ASSERT(m_coordType != COORDTYPE_AFFINE); // Not supported.
735			computeQuadTexCoordCube(texCoord, cubeFace);
736
737			if (isProjected)
738			{
739				params.flags	|= ReferenceParams::PROJECTED;
740				params.w		 = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
741			}
742
743			if (useLodBias)
744			{
745				params.flags	|= ReferenceParams::USE_BIAS;
746				params.bias		 = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
747			}
748
749			// Render ideal reference.
750			{
751				SurfaceAccess idealDst(referenceFrame, m_renderCtx.getRenderTarget().getPixelFormat(), curX, curY, curW, curH);
752				sampleTexture(idealDst, m_texture->getRefTexture(), &texCoord[0], params);
753			}
754
755			// Compare this cell
756			numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
757														tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
758														tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
759														m_texture->getRefTexture(), &texCoord[0], params,
760														lookupPrec, lodPrec, m_testCtx.getWatchDog());
761		}
762
763		if (numFailedPixels > 0)
764			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
765
766		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
767						   << TestLog::Image("Rendered", "Rendered image", renderedFrame);
768
769		if (numFailedPixels > 0)
770		{
771			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
772							   << TestLog::Image("ErrorMask", "Error mask", errorMask);
773		}
774
775		m_testCtx.getLog() << TestLog::EndImageSet;
776
777		{
778			const bool isOk = numFailedPixels == 0;
779			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
780									isOk ? "Pass"				: "Image verification failed");
781		}
782	}
783
784	return STOP;
785}
786
787// Texture2DGenMipmapCase
788
789class Texture2DGenMipmapCase : public tcu::TestCase
790{
791public:
792
793								Texture2DGenMipmapCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* desc, deUint32 format, deUint32 dataType, deUint32 hint, int width, int height);
794								~Texture2DGenMipmapCase		(void);
795
796	void						init						(void);
797	void						deinit						(void);
798	IterateResult				iterate						(void);
799
800private:
801								Texture2DGenMipmapCase		(const Texture2DGenMipmapCase& other);
802	Texture2DGenMipmapCase&		operator=					(const Texture2DGenMipmapCase& other);
803
804	glu::RenderContext&			m_renderCtx;
805
806	deUint32					m_format;
807	deUint32					m_dataType;
808	deUint32					m_hint;
809	int							m_width;
810	int							m_height;
811
812	glu::Texture2D*				m_texture;
813	TextureRenderer				m_renderer;
814};
815
816Texture2DGenMipmapCase::Texture2DGenMipmapCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* desc, deUint32 format, deUint32 dataType, deUint32 hint, int width, int height)
817	: TestCase			(testCtx, name, desc)
818	, m_renderCtx		(renderCtx)
819	, m_format			(format)
820	, m_dataType		(dataType)
821	, m_hint			(hint)
822	, m_width			(width)
823	, m_height			(height)
824	, m_texture			(DE_NULL)
825	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
826{
827}
828
829Texture2DGenMipmapCase::~Texture2DGenMipmapCase (void)
830{
831	deinit();
832}
833
834void Texture2DGenMipmapCase::init (void)
835{
836	DE_ASSERT(!m_texture);
837	m_texture = new glu::Texture2D(m_renderCtx, m_format, m_dataType, m_width, m_height);
838}
839
840void Texture2DGenMipmapCase::deinit (void)
841{
842	delete m_texture;
843	m_texture = DE_NULL;
844
845	m_renderer.clear();
846}
847
848Texture2DGenMipmapCase::IterateResult Texture2DGenMipmapCase::iterate (void)
849{
850	const glw::Functions&	gl					= m_renderCtx.getFunctions();
851
852	const deUint32			minFilter			= GL_NEAREST_MIPMAP_NEAREST;
853	const deUint32			magFilter			= GL_NEAREST;
854	const deUint32			wrapS				= GL_CLAMP_TO_EDGE;
855	const deUint32			wrapT				= GL_CLAMP_TO_EDGE;
856
857	const int				numLevels			= deLog2Floor32(de::max(m_width, m_height))+1;
858	const tcu::Sampler		sampler				= glu::mapGLSampler(wrapS, wrapT, minFilter, magFilter);
859
860	tcu::Texture2D			resultTexture		(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), m_texture->getRefTexture().getWidth(), m_texture->getRefTexture().getHeight());
861
862	vector<float>			texCoord;
863
864	// Initialize texture level 0 with colored grid.
865	m_texture->getRefTexture().allocLevel(0);
866	tcu::fillWithGrid(m_texture->getRefTexture().getLevel(0), 8, tcu::Vec4(1.0f, 0.5f, 0.0f, 0.5f), tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
867
868	// Upload data and setup params.
869	m_texture->upload();
870
871	gl.bindTexture	(GL_TEXTURE_2D, m_texture->getGLTexture());
872	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		wrapS);
873	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		wrapT);
874	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	minFilter);
875	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	magFilter);
876	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
877
878	// Generate mipmap.
879	gl.hint(GL_GENERATE_MIPMAP_HINT, m_hint);
880	gl.generateMipmap(GL_TEXTURE_2D);
881	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenerateMipmap()");
882
883	// Use (0, 0) -> (1, 1) texture coordinates.
884	computeQuadTexCoord2D(texCoord, Vec2(0.0f, 0.0f), Vec2(1.0f, 1.0f));
885
886	// Fetch resulting texture by rendering.
887	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
888	{
889		const int				levelWidth		= de::max(1, m_width >> levelNdx);
890		const int				levelHeight		= de::max(1, m_height >> levelNdx);
891		const RandomViewport	viewport		(m_renderCtx.getRenderTarget(), levelWidth, levelHeight, deStringHash(getName()) + levelNdx);
892
893		gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
894		m_renderer.renderQuad(0, &texCoord[0], TEXTURETYPE_2D);
895
896		resultTexture.allocLevel(levelNdx);
897		glu::readPixels(m_renderCtx, viewport.x, viewport.y, resultTexture.getLevel(levelNdx));
898	}
899
900	// Compare results
901	{
902		const IVec4			framebufferBits		= max(getBitsVec(m_renderCtx.getRenderTarget().getPixelFormat())-2, IVec4(0));
903		const IVec4			formatBits			= tcu::getTextureFormatBitDepth(glu::mapGLTransferFormat(m_format, m_dataType));
904		const tcu::BVec4	formatMask			= greaterThan(formatBits, IVec4(0));
905		const IVec4			cmpBits				= select(min(framebufferBits, formatBits), framebufferBits, formatMask);
906		GenMipmapPrecision	comparePrec;
907
908		comparePrec.colorMask		= getCompareMask(m_renderCtx.getRenderTarget().getPixelFormat());
909		comparePrec.colorThreshold	= tcu::computeFixedPointThreshold(cmpBits);
910		comparePrec.filterBits		= tcu::IVec3(4, 4, 0);
911
912		const qpTestResult compareResult = compareGenMipmapResult(m_testCtx.getLog(), resultTexture, m_texture->getRefTexture(), comparePrec);
913
914		m_testCtx.setTestResult(compareResult, compareResult == QP_TEST_RESULT_PASS				? "Pass" :
915											   compareResult == QP_TEST_RESULT_QUALITY_WARNING	? "Low-quality method used"	:
916											   compareResult == QP_TEST_RESULT_FAIL				? "Image comparison failed"	: "");
917	}
918
919	return STOP;
920}
921
922// TextureCubeGenMipmapCase
923
924class TextureCubeGenMipmapCase : public tcu::TestCase
925{
926public:
927
928								TextureCubeGenMipmapCase		(tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* desc, deUint32 format, deUint32 dataType, deUint32 hint, int size);
929								~TextureCubeGenMipmapCase		(void);
930
931	void						init							(void);
932	void						deinit							(void);
933	IterateResult				iterate							(void);
934
935private:
936								TextureCubeGenMipmapCase		(const TextureCubeGenMipmapCase& other);
937	TextureCubeGenMipmapCase&	operator=						(const TextureCubeGenMipmapCase& other);
938
939	glu::RenderContext&			m_renderCtx;
940
941	deUint32					m_format;
942	deUint32					m_dataType;
943	deUint32					m_hint;
944	int							m_size;
945
946	glu::TextureCube*			m_texture;
947	TextureRenderer				m_renderer;
948};
949
950TextureCubeGenMipmapCase::TextureCubeGenMipmapCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* desc, deUint32 format, deUint32 dataType, deUint32 hint, int size)
951	: TestCase			(testCtx, name, desc)
952	, m_renderCtx		(renderCtx)
953	, m_format			(format)
954	, m_dataType		(dataType)
955	, m_hint			(hint)
956	, m_size			(size)
957	, m_texture			(DE_NULL)
958	, m_renderer		(renderCtx, testCtx, glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
959{
960}
961
962TextureCubeGenMipmapCase::~TextureCubeGenMipmapCase (void)
963{
964	deinit();
965}
966
967void TextureCubeGenMipmapCase::init (void)
968{
969	if (m_renderCtx.getRenderTarget().getWidth() < 3*m_size || m_renderCtx.getRenderTarget().getHeight() < 2*m_size)
970		throw tcu::NotSupportedError("Render target size must be at least (" + de::toString(3*m_size) + ", " + de::toString(2*m_size) + ")");
971
972	DE_ASSERT(!m_texture);
973	m_texture = new glu::TextureCube(m_renderCtx, m_format, m_dataType, m_size);
974}
975
976void TextureCubeGenMipmapCase::deinit (void)
977{
978	delete m_texture;
979	m_texture = DE_NULL;
980
981	m_renderer.clear();
982}
983
984TextureCubeGenMipmapCase::IterateResult TextureCubeGenMipmapCase::iterate (void)
985{
986	const glw::Functions&	gl					= m_renderCtx.getFunctions();
987
988	const deUint32			minFilter			= GL_NEAREST_MIPMAP_NEAREST;
989	const deUint32			magFilter			= GL_NEAREST;
990	const deUint32			wrapS				= GL_CLAMP_TO_EDGE;
991	const deUint32			wrapT				= GL_CLAMP_TO_EDGE;
992
993	tcu::TextureCube		resultTexture		(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), m_size);
994
995	const int				numLevels			= deLog2Floor32(m_size)+1;
996	tcu::Sampler			sampler				= glu::mapGLSampler(wrapS, wrapT, minFilter, magFilter);
997	vector<float>			texCoord;
998
999	sampler.seamlessCubeMap = true;
1000
1001	// Initialize texture level 0 with colored grid.
1002	for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
1003	{
1004		Vec4 ca, cb; // Grid colors.
1005
1006		switch (face)
1007		{
1008			case 0: ca = Vec4(1.0f, 0.3f, 0.0f, 0.7f); cb = Vec4(0.0f, 0.0f, 1.0f, 1.0f); break;
1009			case 1: ca = Vec4(0.0f, 1.0f, 0.5f, 0.5f); cb = Vec4(1.0f, 0.0f, 0.0f, 1.0f); break;
1010			case 2: ca = Vec4(0.7f, 0.0f, 1.0f, 0.3f); cb = Vec4(0.0f, 1.0f, 0.0f, 1.0f); break;
1011			case 3: ca = Vec4(0.0f, 0.3f, 1.0f, 1.0f); cb = Vec4(1.0f, 0.0f, 0.0f, 0.7f); break;
1012			case 4: ca = Vec4(1.0f, 0.0f, 0.5f, 1.0f); cb = Vec4(0.0f, 1.0f, 0.0f, 0.5f); break;
1013			case 5: ca = Vec4(0.7f, 1.0f, 0.0f, 1.0f); cb = Vec4(0.0f, 0.0f, 1.0f, 0.3f); break;
1014		}
1015
1016		m_texture->getRefTexture().allocLevel((tcu::CubeFace)face, 0);
1017		fillWithGrid(m_texture->getRefTexture().getLevelFace(0, (tcu::CubeFace)face), 8, ca, cb);
1018	}
1019
1020	// Upload data and setup params.
1021	m_texture->upload();
1022
1023	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, m_texture->getGLTexture());
1024	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		wrapS);
1025	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		wrapT);
1026	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	minFilter);
1027	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	magFilter);
1028	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
1029
1030	// Generate mipmap.
1031	gl.hint(GL_GENERATE_MIPMAP_HINT, m_hint);
1032	gl.generateMipmap(GL_TEXTURE_CUBE_MAP);
1033	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenerateMipmap()");
1034
1035	// Render all levels.
1036	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1037	{
1038		const int	levelWidth	= de::max(1, m_size >> levelNdx);
1039		const int	levelHeight	= de::max(1, m_size >> levelNdx);
1040
1041		for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
1042		{
1043			const RandomViewport	viewport	(m_renderCtx.getRenderTarget(), levelWidth*3, levelHeight*2, deStringHash(getName()) ^ deInt32Hash(levelNdx + faceNdx));
1044			const tcu::CubeFace		face		= tcu::CubeFace(faceNdx);
1045
1046			computeQuadTexCoordCube(texCoord, face);
1047
1048			gl.viewport(viewport.x, viewport.y, levelWidth, levelHeight);
1049			m_renderer.renderQuad(0, &texCoord[0], TEXTURETYPE_CUBE);
1050
1051			resultTexture.allocLevel(face, levelNdx);
1052			glu::readPixels(m_renderCtx, viewport.x, viewport.y, resultTexture.getLevelFace(levelNdx, face));
1053		}
1054	}
1055
1056	// Compare results
1057	{
1058		const IVec4			framebufferBits		= max(getBitsVec(m_renderCtx.getRenderTarget().getPixelFormat())-2, IVec4(0));
1059		const IVec4			formatBits			= tcu::getTextureFormatBitDepth(glu::mapGLTransferFormat(m_format, m_dataType));
1060		const tcu::BVec4	formatMask			= greaterThan(formatBits, IVec4(0));
1061		const IVec4			cmpBits				= select(min(framebufferBits, formatBits), framebufferBits, formatMask);
1062		GenMipmapPrecision	comparePrec;
1063
1064		comparePrec.colorMask		= getCompareMask(m_renderCtx.getRenderTarget().getPixelFormat());
1065		comparePrec.colorThreshold	= tcu::computeFixedPointThreshold(cmpBits);
1066		comparePrec.filterBits		= tcu::IVec3(4, 4, 0);
1067
1068		const qpTestResult compareResult = compareGenMipmapResult(m_testCtx.getLog(), resultTexture, m_texture->getRefTexture(), comparePrec);
1069
1070		m_testCtx.setTestResult(compareResult, compareResult == QP_TEST_RESULT_PASS				? "Pass" :
1071											   compareResult == QP_TEST_RESULT_QUALITY_WARNING	? "Low-quality method used"	:
1072											   compareResult == QP_TEST_RESULT_FAIL				? "Image comparison failed"	: "");
1073	}
1074
1075	return STOP;
1076}
1077
1078// Texture3DMipmapCase
1079
1080class Texture3DMipmapCase : public TestCase
1081{
1082public:
1083
1084								Texture3DMipmapCase			(Context&					context,
1085															 const char*				name,
1086															 const char*				desc,
1087															 CoordType					coordType,
1088															 deUint32					minFilter,
1089															 deUint32					wrapS,
1090															 deUint32					wrapT,
1091															 deUint32					wrapR,
1092															 deUint32					format,
1093															 int						width,
1094															 int						height,
1095															 int						depth);
1096								~Texture3DMipmapCase		(void);
1097
1098	void						init						(void);
1099	void						deinit						(void);
1100	IterateResult				iterate						(void);
1101
1102private:
1103								Texture3DMipmapCase			(const Texture3DMipmapCase& other);
1104	Texture3DMipmapCase&		operator=					(const Texture3DMipmapCase& other);
1105
1106	CoordType					m_coordType;
1107	deUint32					m_minFilter;
1108	deUint32					m_wrapS;
1109	deUint32					m_wrapT;
1110	deUint32					m_wrapR;
1111	deUint32					m_internalFormat;
1112	int							m_width;
1113	int							m_height;
1114	int							m_depth;
1115
1116	glu::Texture3D*						m_texture;
1117	TextureTestUtil::TextureRenderer	m_renderer;
1118};
1119
1120Texture3DMipmapCase::Texture3DMipmapCase (Context& context, const char* name, const char* desc, CoordType coordType, deUint32 minFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR, deUint32 format, int width, int height, int depth)
1121	: TestCase			(context, name, desc)
1122	, m_coordType		(coordType)
1123	, m_minFilter		(minFilter)
1124	, m_wrapS			(wrapS)
1125	, m_wrapT			(wrapT)
1126	, m_wrapR			(wrapR)
1127	, m_internalFormat	(format)
1128	, m_width			(width)
1129	, m_height			(height)
1130	, m_depth			(depth)
1131	, m_texture			(DE_NULL)
1132	, m_renderer		(context.getRenderContext(), m_context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
1133{
1134}
1135
1136Texture3DMipmapCase::~Texture3DMipmapCase (void)
1137{
1138	Texture3DMipmapCase::deinit();
1139}
1140
1141void Texture3DMipmapCase::init (void)
1142{
1143	const tcu::TextureFormat&		texFmt			= glu::mapGLInternalFormat(m_internalFormat);
1144	tcu::TextureFormatInfo			fmtInfo			= tcu::getTextureFormatInfo(texFmt);
1145	const tcu::Vec4&				cScale			= fmtInfo.lookupScale;
1146	const tcu::Vec4&				cBias			= fmtInfo.lookupBias;
1147	int								numLevels		= deLog2Floor32(de::max(de::max(m_width, m_height), m_depth))+1;
1148
1149	if (m_coordType == COORDTYPE_PROJECTED && m_context.getRenderTarget().getNumSamples() > 0)
1150		throw tcu::NotSupportedError("Projected lookup validation not supported in multisample config");
1151
1152	m_texture = new glu::Texture3D(m_context.getRenderContext(), m_internalFormat, m_width, m_height, m_depth);
1153
1154	// Fill texture with colored grid.
1155	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1156	{
1157		deUint32	step		= 0xff / (numLevels-1);
1158		deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
1159		deUint32	dec			= 0xff - inc;
1160		deUint32	rgb			= (0xff << 16) | (dec << 8) | inc;
1161		deUint32	color		= 0xff000000 | rgb;
1162
1163		m_texture->getRefTexture().allocLevel(levelNdx);
1164		tcu::clear(m_texture->getRefTexture().getLevel(levelNdx), tcu::RGBA(color).toVec()*cScale + cBias);
1165	}
1166
1167	m_texture->upload();
1168}
1169
1170void Texture3DMipmapCase::deinit (void)
1171{
1172	delete m_texture;
1173	m_texture = DE_NULL;
1174
1175	m_renderer.clear();
1176}
1177
1178static void getBasicTexCoord3D (std::vector<float>& dst, int cellNdx)
1179{
1180	static const struct
1181	{
1182		float sScale;
1183		float sBias;
1184		float tScale;
1185		float tBias;
1186		float rScale;
1187		float rBias;
1188	} s_params[] =
1189	{
1190	//		sScale	sBias	tScale	tBias	rScale	rBias
1191		{	 0.9f,	-0.1f,	 0.7f,	 0.3f,	 0.8f,	 0.9f	},
1192		{	 1.2f,	-0.1f,	 1.1f,	 0.3f,	 1.0f,	 0.9f	},
1193		{	 1.5f,	 0.7f,	 0.9f,	-0.3f,	 1.1f,	 0.1f	},
1194		{	 1.2f,	 0.7f,	-2.3f,	-0.3f,	 1.1f,	 0.2f	},
1195		{	 1.1f,	 0.8f,	-1.3f,	-0.3f,	 2.9f,	 0.9f	},
1196		{	 3.4f,	 0.8f,	 4.0f,	 0.0f,	-3.3f,	-1.0f	},
1197		{	-3.4f,	-0.1f,	-4.0f,	 0.0f,	-5.1f,	 1.0f	},
1198		{	-4.0f,	-0.1f,	 3.4f,	 0.1f,	 5.7f,	 0.0f	},
1199		{	-5.6f,	 0.0f,	 0.5f,	 1.2f,	 3.9f,	 4.0f	},
1200		{	 5.0f,	-2.0f,	 3.1f,	 1.2f,	 5.1f,	 0.2f	},
1201		{	 2.5f,	-2.0f,	 6.3f,	 3.0f,	 5.1f,	 0.2f	},
1202		{	-8.3f,	 0.0f,	 7.1f,	 3.0f,	 2.0f,	 0.2f	},
1203		{    3.8f,	 0.0f,	 9.7f,	 1.0f,	 7.0f,	 0.7f	},
1204		{	13.3f,	 0.0f,	 7.1f,	 3.0f,	 2.0f,	 0.2f	},
1205		{   16.0f,	 8.0f,	12.7f,	 1.0f,	17.1f,	 0.7f	},
1206		{	15.3f,	 0.0f,	20.1f,	 3.0f,	33.0f,	 3.2f	}
1207	};
1208
1209	float sScale	= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].sScale;
1210	float sBias		= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].sBias;
1211	float tScale	= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].tScale;
1212	float tBias		= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].tBias;
1213	float rScale	= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].rScale;
1214	float rBias		= s_params[cellNdx%DE_LENGTH_OF_ARRAY(s_params)].rBias;
1215
1216	dst.resize(3*4);
1217
1218	dst[0] = sBias;			dst[ 1] = tBias;			dst[ 2] = rBias;
1219	dst[3] = sBias;			dst[ 4] = tBias+tScale;		dst[ 5] = rBias+rScale*0.5f;
1220	dst[6] = sBias+sScale;	dst[ 7] = tBias;			dst[ 8] = rBias+rScale*0.5f;
1221	dst[9] = sBias+sScale;	dst[10] = tBias+tScale;		dst[11] = rBias+rScale;
1222}
1223
1224static void getAffineTexCoord3D (std::vector<float>& dst, int cellNdx)
1225{
1226	// Use basic coords as base.
1227	getBasicTexCoord3D(dst, cellNdx);
1228
1229	// Rotate based on cell index.
1230	float		angleX		= 0.0f + 2.0f*DE_PI * ((float)cellNdx / 16.0f);
1231	float		angleY		= 1.0f + 2.0f*DE_PI * ((float)cellNdx / 32.0f);
1232	tcu::Mat3	rotMatrix	= tcu::rotationMatrixX(angleX) * tcu::rotationMatrixY(angleY);
1233
1234	Vec3		p0			= rotMatrix * Vec3(dst[0], dst[ 1], dst[ 2]);
1235	Vec3		p1			= rotMatrix * Vec3(dst[3], dst[ 4], dst[ 5]);
1236	Vec3		p2			= rotMatrix * Vec3(dst[6], dst[ 7], dst[ 8]);
1237	Vec3		p3			= rotMatrix * Vec3(dst[9], dst[10], dst[11]);
1238
1239	dst[0] = p0.x();	dst[ 1] = p0.y();	dst[ 2] = p0.z();
1240	dst[3] = p1.x();	dst[ 4] = p1.y();	dst[ 5] = p1.z();
1241	dst[6] = p2.x();	dst[ 7] = p2.y();	dst[ 8] = p2.z();
1242	dst[9] = p3.x();	dst[10] = p3.y();	dst[11] = p3.z();
1243}
1244
1245Texture3DMipmapCase::IterateResult Texture3DMipmapCase::iterate (void)
1246{
1247	const glw::Functions&			gl					= m_context.getRenderContext().getFunctions();
1248
1249	const tcu::Texture3D&			refTexture			= m_texture->getRefTexture();
1250	const tcu::TextureFormat&		texFmt				= refTexture.getFormat();
1251	const tcu::TextureFormatInfo	fmtInfo				= tcu::getTextureFormatInfo(texFmt);
1252	const int						texWidth			= refTexture.getWidth();
1253	const int						texHeight			= refTexture.getHeight();
1254	const deUint32					magFilter			= GL_NEAREST;
1255
1256	const tcu::RenderTarget&		renderTarget		= m_context.getRenderContext().getRenderTarget();
1257	const RandomViewport			viewport			(renderTarget, texWidth*4, texHeight*4, deStringHash(getName()));
1258
1259	const bool						isProjected			= m_coordType == COORDTYPE_PROJECTED;
1260	const bool						useLodBias			= m_coordType == COORDTYPE_BASIC_BIAS;
1261
1262	// Viewport is divided into 4x4 grid.
1263	const int						gridWidth			= 4;
1264	const int						gridHeight			= 4;
1265	const int						cellWidth			= viewport.width / gridWidth;
1266	const int						cellHeight			= viewport.height / gridHeight;
1267
1268	ReferenceParams					sampleParams		(TEXTURETYPE_3D);
1269
1270	tcu::Surface					renderedFrame		(viewport.width, viewport.height);
1271	vector<float>					texCoord;
1272
1273	// Sampling parameters.
1274	sampleParams.sampler		= glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapR, m_minFilter, magFilter);
1275	sampleParams.samplerType	= gls::TextureTestUtil::getSamplerType(texFmt);
1276	sampleParams.colorBias		= fmtInfo.lookupBias;
1277	sampleParams.colorScale		= fmtInfo.lookupScale;
1278	sampleParams.flags			= (isProjected ? ReferenceParams::PROJECTED : 0) | (useLodBias ? ReferenceParams::USE_BIAS : 0);
1279
1280	// Bind texture and setup sampler parameters.
1281	gl.bindTexture	(GL_TEXTURE_3D, m_texture->getGLTexture());
1282	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,		m_wrapS);
1283	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,		m_wrapT);
1284	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,		m_wrapR);
1285	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
1286	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,	magFilter);
1287
1288	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
1289
1290	// Bias values.
1291	static const float s_bias[] = { 1.0f, -2.0f, 0.8f, -0.5f, 1.5f, 0.9f, 2.0f, 4.0f };
1292
1293	// Projection values.
1294	static const Vec4 s_projections[] =
1295	{
1296		Vec4(1.2f, 1.0f, 0.7f, 1.0f),
1297		Vec4(1.3f, 0.8f, 0.6f, 2.0f),
1298		Vec4(0.8f, 1.0f, 1.7f, 0.6f),
1299		Vec4(1.2f, 1.0f, 1.7f, 1.5f)
1300	};
1301
1302	// Render cells.
1303	for (int gridY = 0; gridY < gridHeight; gridY++)
1304	{
1305		for (int gridX = 0; gridX < gridWidth; gridX++)
1306		{
1307			const int		curX		= cellWidth*gridX;
1308			const int		curY		= cellHeight*gridY;
1309			const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
1310			const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
1311			const int		cellNdx		= gridY*gridWidth + gridX;
1312
1313			// Compute texcoord.
1314			switch (m_coordType)
1315			{
1316				case COORDTYPE_BASIC_BIAS:	// Fall-through.
1317				case COORDTYPE_PROJECTED:
1318				case COORDTYPE_BASIC:		getBasicTexCoord3D	(texCoord, cellNdx);	break;
1319				case COORDTYPE_AFFINE:		getAffineTexCoord3D	(texCoord, cellNdx);	break;
1320				default:					DE_ASSERT(DE_FALSE);
1321			}
1322
1323			// Set projection.
1324			if (isProjected)
1325				sampleParams.w = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
1326
1327			// Set LOD bias.
1328			if (useLodBias)
1329				sampleParams.bias = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
1330
1331			// Render with GL.
1332			gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
1333			m_renderer.renderQuad(0, &texCoord[0], sampleParams);
1334		}
1335	}
1336
1337	// Read result.
1338	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, renderedFrame.getAccess());
1339
1340	// Compare and log
1341	{
1342		const tcu::PixelFormat&	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
1343		const bool				isTrilinear		= m_minFilter == GL_NEAREST_MIPMAP_LINEAR || m_minFilter == GL_LINEAR_MIPMAP_LINEAR;
1344		tcu::Surface			referenceFrame	(viewport.width, viewport.height);
1345		tcu::Surface			errorMask		(viewport.width, viewport.height);
1346		tcu::LookupPrecision	lookupPrec;
1347		tcu::LodPrecision		lodPrec;
1348		int						numFailedPixels	= 0;
1349
1350		lookupPrec.coordBits		= tcu::IVec3(20, 20, 20);
1351		lookupPrec.uvwBits			= tcu::IVec3(16, 16, 16); // Doesn't really matter since pixels are unicolored.
1352		lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(max(getBitsVec(pixelFormat) - (isTrilinear ? 2 : 1), tcu::IVec4(0)));
1353		lookupPrec.colorMask		= getCompareMask(pixelFormat);
1354		lodPrec.derivateBits		= 10;
1355		lodPrec.lodBits				= isProjected ? 6 : 8;
1356
1357		for (int gridY = 0; gridY < gridHeight; gridY++)
1358		{
1359			for (int gridX = 0; gridX < gridWidth; gridX++)
1360			{
1361				const int		curX		= cellWidth*gridX;
1362				const int		curY		= cellHeight*gridY;
1363				const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
1364				const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
1365				const int		cellNdx		= gridY*gridWidth + gridX;
1366
1367				switch (m_coordType)
1368				{
1369					case COORDTYPE_BASIC_BIAS:	// Fall-through.
1370					case COORDTYPE_PROJECTED:
1371					case COORDTYPE_BASIC:		getBasicTexCoord3D	(texCoord, cellNdx);	break;
1372					case COORDTYPE_AFFINE:		getAffineTexCoord3D	(texCoord, cellNdx);	break;
1373					default:					DE_ASSERT(DE_FALSE);
1374				}
1375
1376				if (isProjected)
1377					sampleParams.w = s_projections[cellNdx % DE_LENGTH_OF_ARRAY(s_projections)];
1378
1379				if (useLodBias)
1380					sampleParams.bias = s_bias[cellNdx % DE_LENGTH_OF_ARRAY(s_bias)];
1381
1382				// Render ideal result
1383				sampleTexture(SurfaceAccess(referenceFrame, pixelFormat, curX, curY, curW, curH),
1384							  refTexture, &texCoord[0], sampleParams);
1385
1386				// Compare this cell
1387				numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
1388															tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
1389															tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
1390															m_texture->getRefTexture(), &texCoord[0], sampleParams,
1391															lookupPrec, lodPrec, m_testCtx.getWatchDog());
1392			}
1393		}
1394
1395		if (numFailedPixels > 0)
1396			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
1397
1398		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
1399							<< TestLog::Image("Rendered", "Rendered image", renderedFrame);
1400
1401		if (numFailedPixels > 0)
1402		{
1403			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
1404								<< TestLog::Image("ErrorMask", "Error mask", errorMask);
1405		}
1406
1407		m_testCtx.getLog() << TestLog::EndImageSet;
1408
1409		{
1410			const bool isOk = numFailedPixels == 0;
1411			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1412									isOk ? "Pass"				: "Image verification failed");
1413		}
1414	}
1415
1416	return STOP;
1417}
1418
1419// Texture2DLodControlCase + test cases
1420
1421class Texture2DLodControlCase : public TestCase
1422{
1423public:
1424
1425										Texture2DLodControlCase		(Context& context, const char* name, const char* desc, deUint32 minFilter);
1426										~Texture2DLodControlCase	(void);
1427
1428	void								init						(void);
1429	void								deinit						(void);
1430	IterateResult						iterate						(void);
1431
1432protected:
1433	virtual void						setTextureParams			(int cellNdx)							= DE_NULL;
1434	virtual void						getReferenceParams			(ReferenceParams& params, int cellNdx)	= DE_NULL;
1435
1436	const int							m_texWidth;
1437	const int							m_texHeight;
1438
1439private:
1440										Texture2DLodControlCase		(const Texture2DLodControlCase& other);
1441	Texture2DLodControlCase&			operator=					(const Texture2DLodControlCase& other);
1442
1443	deUint32							m_minFilter;
1444
1445	glu::Texture2D*						m_texture;
1446	TextureTestUtil::TextureRenderer	m_renderer;
1447};
1448
1449Texture2DLodControlCase::Texture2DLodControlCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1450	: TestCase		(context, name, desc)
1451	, m_texWidth	(64)
1452	, m_texHeight	(64)
1453	, m_minFilter	(minFilter)
1454	, m_texture		(DE_NULL)
1455	, m_renderer	(context.getRenderContext(), m_context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
1456{
1457}
1458
1459Texture2DLodControlCase::~Texture2DLodControlCase (void)
1460{
1461	Texture2DLodControlCase::deinit();
1462}
1463
1464void Texture2DLodControlCase::init (void)
1465{
1466	const deUint32	format		= GL_RGBA8;
1467	int				numLevels	= deLog2Floor32(de::max(m_texWidth, m_texHeight))+1;
1468
1469	m_texture = new glu::Texture2D(m_context.getRenderContext(), format, m_texWidth, m_texHeight);
1470
1471	// Fill texture with colored grid.
1472	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1473	{
1474		deUint32	step		= 0xff / (numLevels-1);
1475		deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
1476		deUint32	dec			= 0xff - inc;
1477		deUint32	rgb			= (inc << 16) | (dec << 8) | 0xff;
1478		deUint32	color		= 0xff000000 | rgb;
1479
1480		m_texture->getRefTexture().allocLevel(levelNdx);
1481		tcu::clear(m_texture->getRefTexture().getLevel(levelNdx), tcu::RGBA(color).toVec());
1482	}
1483}
1484
1485void Texture2DLodControlCase::deinit (void)
1486{
1487	delete m_texture;
1488	m_texture = DE_NULL;
1489
1490	m_renderer.clear();
1491}
1492
1493Texture2DLodControlCase::IterateResult Texture2DLodControlCase::iterate (void)
1494{
1495	const glw::Functions&		gl					= m_context.getRenderContext().getFunctions();
1496
1497	const deUint32				wrapS				= GL_REPEAT;
1498	const deUint32				wrapT				= GL_REPEAT;
1499	const deUint32				magFilter			= GL_NEAREST;
1500
1501	const tcu::Texture2D&		refTexture			= m_texture->getRefTexture();
1502	const int					texWidth			= refTexture.getWidth();
1503	const int					texHeight			= refTexture.getHeight();
1504
1505	const tcu::RenderTarget&	renderTarget		= m_context.getRenderContext().getRenderTarget();
1506	const RandomViewport		viewport			(renderTarget, texWidth*4, texHeight*4, deStringHash(getName()));
1507
1508	ReferenceParams				sampleParams		(gls::TextureTestUtil::TEXTURETYPE_2D, glu::mapGLSampler(wrapS, wrapT, m_minFilter, magFilter));
1509	vector<float>				texCoord;
1510	tcu::Surface				renderedFrame		(viewport.width, viewport.height);
1511
1512	// Viewport is divided into 4x4 grid.
1513	const int					gridWidth			= 4;
1514	const int					gridHeight			= 4;
1515	const int					cellWidth			= viewport.width / gridWidth;
1516	const int					cellHeight			= viewport.height / gridHeight;
1517
1518	// Upload texture data.
1519	m_texture->upload();
1520
1521	// Bind gradient texture and setup sampler parameters.
1522	gl.bindTexture	(GL_TEXTURE_2D, m_texture->getGLTexture());
1523	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,		wrapS);
1524	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,		wrapT);
1525	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
1526	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,	magFilter);
1527
1528	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
1529
1530	// Render cells.
1531	for (int gridY = 0; gridY < gridHeight; gridY++)
1532	{
1533		for (int gridX = 0; gridX < gridWidth; gridX++)
1534		{
1535			int				curX		= cellWidth*gridX;
1536			int				curY		= cellHeight*gridY;
1537			int				curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
1538			int				curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
1539			int				cellNdx		= gridY*gridWidth + gridX;
1540
1541			// Compute texcoord.
1542			getBasicTexCoord2D(texCoord, cellNdx);
1543
1544			// Render with GL.
1545			setTextureParams(cellNdx);
1546			gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
1547			m_renderer.renderQuad(0, &texCoord[0], sampleParams);
1548		}
1549	}
1550
1551	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, renderedFrame.getAccess());
1552	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
1553
1554	// Compare and log.
1555	{
1556		const tcu::PixelFormat&	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
1557		const bool				isTrilinear		= m_minFilter == GL_NEAREST_MIPMAP_LINEAR || m_minFilter == GL_LINEAR_MIPMAP_LINEAR;
1558		tcu::Surface			referenceFrame	(viewport.width, viewport.height);
1559		tcu::Surface			errorMask		(viewport.width, viewport.height);
1560		tcu::LookupPrecision	lookupPrec;
1561		tcu::LodPrecision		lodPrec;
1562		int						numFailedPixels	= 0;
1563
1564		lookupPrec.coordBits		= tcu::IVec3(20, 20, 0);
1565		lookupPrec.uvwBits			= tcu::IVec3(16, 16, 0); // Doesn't really matter since pixels are unicolored.
1566		lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(max(getBitsVec(pixelFormat) - (isTrilinear ? 2 : 1), tcu::IVec4(0)));
1567		lookupPrec.colorMask		= getCompareMask(pixelFormat);
1568		lodPrec.derivateBits		= 10;
1569		lodPrec.lodBits				= 8;
1570
1571		for (int gridY = 0; gridY < gridHeight; gridY++)
1572		{
1573			for (int gridX = 0; gridX < gridWidth; gridX++)
1574			{
1575				const int		curX		= cellWidth*gridX;
1576				const int		curY		= cellHeight*gridY;
1577				const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
1578				const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
1579				const int		cellNdx		= gridY*gridWidth + gridX;
1580
1581				getBasicTexCoord2D(texCoord, cellNdx);
1582				getReferenceParams(sampleParams, cellNdx);
1583
1584				// Render ideal result
1585				sampleTexture(SurfaceAccess(referenceFrame, pixelFormat, curX, curY, curW, curH),
1586							  refTexture, &texCoord[0], sampleParams);
1587
1588				// Compare this cell
1589				numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
1590															tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
1591															tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
1592															m_texture->getRefTexture(), &texCoord[0], sampleParams,
1593															lookupPrec, lodPrec, m_testCtx.getWatchDog());
1594			}
1595		}
1596
1597		if (numFailedPixels > 0)
1598			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
1599
1600		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
1601							<< TestLog::Image("Rendered", "Rendered image", renderedFrame);
1602
1603		if (numFailedPixels > 0)
1604		{
1605			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
1606								<< TestLog::Image("ErrorMask", "Error mask", errorMask);
1607		}
1608
1609		m_testCtx.getLog() << TestLog::EndImageSet;
1610
1611		{
1612			const bool isOk = numFailedPixels == 0;
1613			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1614									isOk ? "Pass"				: "Image verification failed");
1615		}
1616	}
1617
1618	return STOP;
1619}
1620
1621class Texture2DMinLodCase : public Texture2DLodControlCase
1622{
1623public:
1624	Texture2DMinLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1625		: Texture2DLodControlCase(context, name, desc, minFilter)
1626	{
1627	}
1628
1629protected:
1630	void setTextureParams (int cellNdx)
1631	{
1632		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1633		gl.texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, getMinLodForCell(cellNdx));
1634	}
1635
1636	void getReferenceParams (ReferenceParams& params, int cellNdx)
1637	{
1638		params.minLod = getMinLodForCell(cellNdx);
1639	}
1640};
1641
1642class Texture2DMaxLodCase : public Texture2DLodControlCase
1643{
1644public:
1645	Texture2DMaxLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1646		: Texture2DLodControlCase(context, name, desc, minFilter)
1647	{
1648	}
1649
1650protected:
1651	void setTextureParams (int cellNdx)
1652	{
1653		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1654		gl.texParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, getMaxLodForCell(cellNdx));
1655	}
1656
1657	void getReferenceParams (ReferenceParams& params, int cellNdx)
1658	{
1659		params.maxLod = getMaxLodForCell(cellNdx);
1660	}
1661};
1662
1663class Texture2DBaseLevelCase : public Texture2DLodControlCase
1664{
1665public:
1666	Texture2DBaseLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1667		: Texture2DLodControlCase(context, name, desc, minFilter)
1668	{
1669	}
1670
1671protected:
1672	int getBaseLevel (int cellNdx) const
1673	{
1674		const int	numLevels	= deLog2Floor32(de::max(m_texWidth, m_texHeight))+1;
1675		const int	baseLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0xac2f274a) % numLevels;
1676
1677		return baseLevel;
1678	}
1679
1680	void setTextureParams (int cellNdx)
1681	{
1682		const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
1683		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, getBaseLevel(cellNdx));
1684	}
1685
1686	void getReferenceParams (ReferenceParams& params, int cellNdx)
1687	{
1688		params.baseLevel = getBaseLevel(cellNdx);
1689	}
1690};
1691
1692class Texture2DMaxLevelCase : public Texture2DLodControlCase
1693{
1694public:
1695	Texture2DMaxLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1696		: Texture2DLodControlCase(context, name, desc, minFilter)
1697	{
1698	}
1699
1700protected:
1701	int getMaxLevel (int cellNdx) const
1702	{
1703		const int		numLevels	= deLog2Floor32(de::max(m_texWidth, m_texHeight))+1;
1704		const int		maxLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0x82cfa4e) % numLevels;
1705
1706		return maxLevel;
1707	}
1708
1709	void setTextureParams (int cellNdx)
1710	{
1711		const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
1712		gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, getMaxLevel(cellNdx));
1713	}
1714
1715	void getReferenceParams (ReferenceParams& params, int cellNdx)
1716	{
1717		params.maxLevel = getMaxLevel(cellNdx);
1718	}
1719};
1720
1721// TextureCubeLodControlCase + test cases
1722
1723class TextureCubeLodControlCase : public TestCase
1724{
1725public:
1726
1727										TextureCubeLodControlCase	(Context& context, const char* name, const char* desc, deUint32 minFilter);
1728										~TextureCubeLodControlCase	(void);
1729
1730	void								init						(void);
1731	void								deinit						(void);
1732	IterateResult						iterate						(void);
1733
1734protected:
1735	virtual void						setTextureParams			(int cellNdx)							= DE_NULL;
1736	virtual void						getReferenceParams			(ReferenceParams& params, int cellNdx)	= DE_NULL;
1737
1738	const int							m_texSize;
1739
1740private:
1741										TextureCubeLodControlCase	(const TextureCubeLodControlCase& other);
1742	TextureCubeLodControlCase&			operator=					(const TextureCubeLodControlCase& other);
1743
1744	deUint32							m_minFilter;
1745
1746	glu::TextureCube*					m_texture;
1747	TextureTestUtil::TextureRenderer	m_renderer;
1748};
1749
1750TextureCubeLodControlCase::TextureCubeLodControlCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1751	: TestCase			(context, name, desc)
1752	, m_texSize			(64)
1753	, m_minFilter		(minFilter)
1754	, m_texture			(DE_NULL)
1755	, m_renderer		(context.getRenderContext(), context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
1756{
1757}
1758
1759TextureCubeLodControlCase::~TextureCubeLodControlCase (void)
1760{
1761	deinit();
1762}
1763
1764void TextureCubeLodControlCase::init (void)
1765{
1766	const deUint32	format		= GL_RGBA8;
1767	const int		numLevels	= deLog2Floor32(m_texSize)+1;
1768
1769	m_texture = new glu::TextureCube(m_context.getRenderContext(), format, m_texSize);
1770
1771	// Fill texture with colored grid.
1772	for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
1773	{
1774		for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
1775		{
1776			deUint32	step		= 0xff / (numLevels-1);
1777			deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
1778			deUint32	dec			= 0xff - inc;
1779			deUint32	rgb			= 0;
1780
1781			switch (faceNdx)
1782			{
1783				case 0: rgb = (inc << 16) | (dec << 8) | 255; break;
1784				case 1: rgb = (255 << 16) | (inc << 8) | dec; break;
1785				case 2: rgb = (dec << 16) | (255 << 8) | inc; break;
1786				case 3: rgb = (dec << 16) | (inc << 8) | 255; break;
1787				case 4: rgb = (255 << 16) | (dec << 8) | inc; break;
1788				case 5: rgb = (inc << 16) | (255 << 8) | dec; break;
1789			}
1790
1791			deUint32	color		= 0xff000000 | rgb;
1792
1793			m_texture->getRefTexture().allocLevel((tcu::CubeFace)faceNdx, levelNdx);
1794			tcu::clear(m_texture->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)faceNdx), tcu::RGBA(color).toVec());
1795		}
1796	}
1797}
1798
1799void TextureCubeLodControlCase::deinit (void)
1800{
1801	delete m_texture;
1802	m_texture = DE_NULL;
1803
1804	m_renderer.clear();
1805}
1806
1807TextureCubeLodControlCase::IterateResult TextureCubeLodControlCase::iterate (void)
1808{
1809	const deUint32			wrapS				= GL_CLAMP_TO_EDGE;
1810	const deUint32			wrapT				= GL_CLAMP_TO_EDGE;
1811	const deUint32			magFilter			= GL_NEAREST;
1812
1813	const int				texWidth			= m_texture->getRefTexture().getSize();
1814	const int				texHeight			= m_texture->getRefTexture().getSize();
1815
1816	const int				defViewportWidth	= texWidth*2;
1817	const int				defViewportHeight	= texHeight*2;
1818
1819	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
1820	const RandomViewport	viewport			(m_context.getRenderTarget(), defViewportWidth, defViewportHeight, deStringHash(getName()));
1821
1822	vector<float>			texCoord;
1823
1824	tcu::Surface			renderedFrame		(viewport.width, viewport.height);
1825
1826	// Upload texture data.
1827	m_texture->upload();
1828
1829	// Bind gradient texture and setup sampler parameters.
1830	gl.bindTexture	(GL_TEXTURE_CUBE_MAP, m_texture->getGLTexture());
1831	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,		wrapS);
1832	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,		wrapT);
1833	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,	m_minFilter);
1834	gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,	magFilter);
1835
1836	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
1837
1838	// Compute grid.
1839	vector<tcu::IVec4> gridLayout;
1840	computeGridLayout(gridLayout, viewport.width, viewport.height);
1841
1842	for (int cellNdx = 0; cellNdx < (int)gridLayout.size(); cellNdx++)
1843	{
1844		const int			curX		= gridLayout[cellNdx].x();
1845		const int			curY		= gridLayout[cellNdx].y();
1846		const int			curW		= gridLayout[cellNdx].z();
1847		const int			curH		= gridLayout[cellNdx].w();
1848		const tcu::CubeFace	cubeFace	= (tcu::CubeFace)(cellNdx % tcu::CUBEFACE_LAST);
1849		RenderParams		params		(TEXTURETYPE_CUBE);
1850
1851		TextureTestUtil::computeQuadTexCoordCube(texCoord, cubeFace);
1852
1853		setTextureParams(cellNdx);
1854
1855		// Render with GL.
1856		gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
1857		m_renderer.renderQuad(0, &texCoord[0], params);
1858		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
1859	}
1860
1861	// Read result.
1862	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, renderedFrame.getAccess());
1863	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels");
1864
1865	// Render reference and compare
1866	{
1867		tcu::Surface			referenceFrame		(viewport.width, viewport.height);
1868		tcu::Surface			errorMask			(viewport.width, viewport.height);
1869		int						numFailedPixels		= 0;
1870		ReferenceParams			params				(TEXTURETYPE_CUBE);
1871		tcu::LookupPrecision	lookupPrec;
1872		tcu::LodPrecision		lodPrec;
1873
1874		// Params for rendering reference
1875		params.sampler					= glu::mapGLSampler(wrapS, wrapT, m_minFilter, magFilter);
1876		params.sampler.seamlessCubeMap	= true;
1877		params.lodMode					= LODMODE_EXACT;
1878
1879		// Comparison parameters
1880		lookupPrec.colorMask			= getCompareMask(m_context.getRenderTarget().getPixelFormat());
1881		lookupPrec.colorThreshold		= tcu::computeFixedPointThreshold(max(getBitsVec(m_context.getRenderTarget().getPixelFormat())-2, IVec4(0)));
1882		lookupPrec.coordBits			= tcu::IVec3(10);
1883		lookupPrec.uvwBits				= tcu::IVec3(5,5,0);
1884		lodPrec.derivateBits			= 10;
1885		lodPrec.lodBits					= 6;
1886
1887		for (int cellNdx = 0; cellNdx < (int)gridLayout.size(); cellNdx++)
1888		{
1889			const int				curX		= gridLayout[cellNdx].x();
1890			const int				curY		= gridLayout[cellNdx].y();
1891			const int				curW		= gridLayout[cellNdx].z();
1892			const int				curH		= gridLayout[cellNdx].w();
1893			const tcu::CubeFace		cubeFace	= (tcu::CubeFace)(cellNdx % tcu::CUBEFACE_LAST);
1894
1895			computeQuadTexCoordCube(texCoord, cubeFace);
1896			getReferenceParams(params, cellNdx);
1897
1898			// Render ideal reference.
1899			{
1900				SurfaceAccess idealDst(referenceFrame, m_context.getRenderTarget().getPixelFormat(), curX, curY, curW, curH);
1901				sampleTexture(idealDst, m_texture->getRefTexture(), &texCoord[0], params);
1902			}
1903
1904			// Compare this cell
1905			numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
1906														tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
1907														tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
1908														m_texture->getRefTexture(), &texCoord[0], params,
1909														lookupPrec, lodPrec, m_testCtx.getWatchDog());
1910		}
1911
1912		if (numFailedPixels > 0)
1913			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
1914
1915		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
1916						   << TestLog::Image("Rendered", "Rendered image", renderedFrame);
1917
1918		if (numFailedPixels > 0)
1919		{
1920			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
1921							   << TestLog::Image("ErrorMask", "Error mask", errorMask);
1922		}
1923
1924		m_testCtx.getLog() << TestLog::EndImageSet;
1925
1926		{
1927			const bool isOk = numFailedPixels == 0;
1928			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1929									isOk ? "Pass"				: "Image verification failed");
1930		}
1931	}
1932
1933	return STOP;
1934}
1935
1936class TextureCubeMinLodCase : public TextureCubeLodControlCase
1937{
1938public:
1939	TextureCubeMinLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1940		: TextureCubeLodControlCase(context, name, desc, minFilter)
1941	{
1942	}
1943
1944protected:
1945	void setTextureParams (int cellNdx)
1946	{
1947		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1948		gl.texParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, getMinLodForCell(cellNdx));
1949	}
1950
1951	void getReferenceParams (ReferenceParams& params, int cellNdx)
1952	{
1953		params.minLod = getMinLodForCell(cellNdx);
1954	}
1955};
1956
1957class TextureCubeMaxLodCase : public TextureCubeLodControlCase
1958{
1959public:
1960	TextureCubeMaxLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1961		: TextureCubeLodControlCase(context, name, desc, minFilter)
1962	{
1963	}
1964
1965protected:
1966	void setTextureParams (int cellNdx)
1967	{
1968		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1969		gl.texParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LOD, getMaxLodForCell(cellNdx));
1970	}
1971
1972	void getReferenceParams (ReferenceParams& params, int cellNdx)
1973	{
1974		params.maxLod = getMaxLodForCell(cellNdx);
1975	}
1976};
1977
1978class TextureCubeBaseLevelCase : public TextureCubeLodControlCase
1979{
1980public:
1981	TextureCubeBaseLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
1982		: TextureCubeLodControlCase(context, name, desc, minFilter)
1983	{
1984	}
1985
1986protected:
1987	int getBaseLevel (int cellNdx) const
1988	{
1989		const int	numLevels	= deLog2Floor32(m_texSize)+1;
1990		const int	baseLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0x23fae13) % numLevels;
1991
1992		return baseLevel;
1993	}
1994
1995	void setTextureParams (int cellNdx)
1996	{
1997		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
1998		gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, getBaseLevel(cellNdx));
1999	}
2000
2001	void getReferenceParams (ReferenceParams& params, int cellNdx)
2002	{
2003		params.baseLevel = getBaseLevel(cellNdx);
2004	}
2005};
2006
2007class TextureCubeMaxLevelCase : public TextureCubeLodControlCase
2008{
2009public:
2010	TextureCubeMaxLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2011		: TextureCubeLodControlCase(context, name, desc, minFilter)
2012	{
2013	}
2014
2015protected:
2016	int getMaxLevel (int cellNdx) const
2017	{
2018		const int	numLevels	= deLog2Floor32(m_texSize)+1;
2019		const int	maxLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0x974e21) % numLevels;
2020
2021		return maxLevel;
2022	}
2023
2024	void setTextureParams (int cellNdx)
2025	{
2026		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2027		gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, getMaxLevel(cellNdx));
2028	}
2029
2030	void getReferenceParams (ReferenceParams& params, int cellNdx)
2031	{
2032		params.maxLevel = getMaxLevel(cellNdx);
2033	}
2034};
2035
2036// Texture3DLodControlCase + test cases
2037
2038class Texture3DLodControlCase : public TestCase
2039{
2040public:
2041
2042										Texture3DLodControlCase		(Context& context, const char* name, const char* desc, deUint32 minFilter);
2043										~Texture3DLodControlCase	(void);
2044
2045	void								init						(void);
2046	void								deinit						(void);
2047	IterateResult						iterate						(void);
2048
2049protected:
2050	virtual void						setTextureParams			(int cellNdx)						= DE_NULL;
2051	virtual void						getReferenceParams			(ReferenceParams& params, int cellNdx)	= DE_NULL;
2052
2053	const int							m_texWidth;
2054	const int							m_texHeight;
2055	const int							m_texDepth;
2056
2057private:
2058										Texture3DLodControlCase		(const Texture3DLodControlCase& other);
2059	Texture3DLodControlCase&			operator=					(const Texture3DLodControlCase& other);
2060
2061	deUint32							m_minFilter;
2062
2063	glu::Texture3D*						m_texture;
2064	TextureTestUtil::TextureRenderer	m_renderer;
2065};
2066
2067Texture3DLodControlCase::Texture3DLodControlCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2068	: TestCase			(context, name, desc)
2069	, m_texWidth		(32)
2070	, m_texHeight		(32)
2071	, m_texDepth		(32)
2072	, m_minFilter		(minFilter)
2073	, m_texture			(DE_NULL)
2074	, m_renderer		(context.getRenderContext(), m_context.getTestContext(), glu::GLSL_VERSION_300_ES, glu::PRECISION_HIGHP)
2075{
2076}
2077
2078Texture3DLodControlCase::~Texture3DLodControlCase (void)
2079{
2080	Texture3DLodControlCase::deinit();
2081}
2082
2083void Texture3DLodControlCase::init (void)
2084{
2085	const deUint32					format			= GL_RGBA8;
2086	const tcu::TextureFormat&		texFmt			= glu::mapGLInternalFormat(format);
2087	tcu::TextureFormatInfo			fmtInfo			= tcu::getTextureFormatInfo(texFmt);
2088	const tcu::Vec4&				cScale			= fmtInfo.lookupScale;
2089	const tcu::Vec4&				cBias			= fmtInfo.lookupBias;
2090	int								numLevels		= deLog2Floor32(de::max(de::max(m_texWidth, m_texHeight), m_texDepth))+1;
2091
2092	m_texture = new glu::Texture3D(m_context.getRenderContext(), format, m_texWidth, m_texHeight, m_texDepth);
2093
2094	// Fill texture with colored grid.
2095	for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
2096	{
2097		deUint32	step		= 0xff / (numLevels-1);
2098		deUint32	inc			= deClamp32(step*levelNdx, 0x00, 0xff);
2099		deUint32	dec			= 0xff - inc;
2100		deUint32	rgb			= (inc << 16) | (dec << 8) | 0xff;
2101		deUint32	color		= 0xff000000 | rgb;
2102
2103		m_texture->getRefTexture().allocLevel(levelNdx);
2104		tcu::clear(m_texture->getRefTexture().getLevel(levelNdx), tcu::RGBA(color).toVec()*cScale + cBias);
2105	}
2106
2107	m_texture->upload();
2108}
2109
2110void Texture3DLodControlCase::deinit (void)
2111{
2112	delete m_texture;
2113	m_texture = DE_NULL;
2114
2115	m_renderer.clear();
2116}
2117
2118Texture3DLodControlCase::IterateResult Texture3DLodControlCase::iterate (void)
2119{
2120	const glw::Functions&			gl					= m_context.getRenderContext().getFunctions();
2121
2122	const deUint32					wrapS				= GL_CLAMP_TO_EDGE;
2123	const deUint32					wrapT				= GL_CLAMP_TO_EDGE;
2124	const deUint32					wrapR				= GL_CLAMP_TO_EDGE;
2125	const deUint32					magFilter			= GL_NEAREST;
2126	const tcu::Texture3D&			refTexture			= m_texture->getRefTexture();
2127	const tcu::TextureFormat&		texFmt				= refTexture.getFormat();
2128	const tcu::TextureFormatInfo	fmtInfo				= tcu::getTextureFormatInfo(texFmt);
2129	const int						texWidth			= refTexture.getWidth();
2130	const int						texHeight			= refTexture.getHeight();
2131
2132	const tcu::RenderTarget&		renderTarget		= m_context.getRenderContext().getRenderTarget();
2133	const RandomViewport			viewport			(renderTarget, texWidth*4, texHeight*4, deStringHash(getName()));
2134
2135	// Viewport is divided into 4x4 grid.
2136	const int						gridWidth			= 4;
2137	const int						gridHeight			= 4;
2138	const int						cellWidth			= viewport.width / gridWidth;
2139	const int						cellHeight			= viewport.height / gridHeight;
2140
2141	tcu::Surface					renderedFrame		(viewport.width, viewport.height);
2142	vector<float>					texCoord;
2143	ReferenceParams					sampleParams		(gls::TextureTestUtil::TEXTURETYPE_3D);
2144
2145	// Sampling parameters.
2146	sampleParams.sampler		= glu::mapGLSampler(wrapS, wrapT, wrapR, m_minFilter, magFilter);
2147	sampleParams.samplerType	= gls::TextureTestUtil::getSamplerType(texFmt);
2148	sampleParams.colorBias		= fmtInfo.lookupBias;
2149	sampleParams.colorScale		= fmtInfo.lookupScale;
2150
2151	// Bind texture and setup sampler parameters.
2152	gl.bindTexture	(GL_TEXTURE_3D, m_texture->getGLTexture());
2153	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S,		wrapS);
2154	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T,		wrapT);
2155	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R,		wrapR);
2156	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER,	m_minFilter);
2157	gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER,	magFilter);
2158
2159	GLU_EXPECT_NO_ERROR(gl.getError(), "After texture setup");
2160
2161	// Render cells.
2162	for (int gridY = 0; gridY < gridHeight; gridY++)
2163	{
2164		for (int gridX = 0; gridX < gridWidth; gridX++)
2165		{
2166			int		curX		= cellWidth*gridX;
2167			int		curY		= cellHeight*gridY;
2168			int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
2169			int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
2170			int		cellNdx		= gridY*gridWidth + gridX;
2171
2172			// Compute texcoord.
2173			getBasicTexCoord3D(texCoord, cellNdx);
2174
2175			setTextureParams(cellNdx);
2176
2177			// Render with GL.
2178			gl.viewport(viewport.x+curX, viewport.y+curY, curW, curH);
2179			m_renderer.renderQuad(0, &texCoord[0], sampleParams);
2180		}
2181	}
2182
2183	// Read result.
2184	glu::readPixels(m_context.getRenderContext(), viewport.x, viewport.y, renderedFrame.getAccess());
2185
2186	// Compare and log
2187	{
2188		const tcu::PixelFormat&	pixelFormat		= m_context.getRenderTarget().getPixelFormat();
2189		const bool				isTrilinear		= m_minFilter == GL_NEAREST_MIPMAP_LINEAR || m_minFilter == GL_LINEAR_MIPMAP_LINEAR;
2190		tcu::Surface			referenceFrame	(viewport.width, viewport.height);
2191		tcu::Surface			errorMask		(viewport.width, viewport.height);
2192		tcu::LookupPrecision	lookupPrec;
2193		tcu::LodPrecision		lodPrec;
2194		int						numFailedPixels	= 0;
2195
2196		lookupPrec.coordBits		= tcu::IVec3(20, 20, 20);
2197		lookupPrec.uvwBits			= tcu::IVec3(16, 16, 16); // Doesn't really matter since pixels are unicolored.
2198		lookupPrec.colorThreshold	= tcu::computeFixedPointThreshold(max(getBitsVec(pixelFormat) - (isTrilinear ? 2 : 1), tcu::IVec4(0)));
2199		lookupPrec.colorMask		= getCompareMask(pixelFormat);
2200		lodPrec.derivateBits		= 10;
2201		lodPrec.lodBits				= 8;
2202
2203		for (int gridY = 0; gridY < gridHeight; gridY++)
2204		{
2205			for (int gridX = 0; gridX < gridWidth; gridX++)
2206			{
2207				const int		curX		= cellWidth*gridX;
2208				const int		curY		= cellHeight*gridY;
2209				const int		curW		= gridX+1 == gridWidth ? (viewport.width-curX) : cellWidth;
2210				const int		curH		= gridY+1 == gridHeight ? (viewport.height-curY) : cellHeight;
2211				const int		cellNdx		= gridY*gridWidth + gridX;
2212
2213				getBasicTexCoord3D(texCoord, cellNdx);
2214				getReferenceParams(sampleParams, cellNdx);
2215
2216				// Render ideal result
2217				sampleTexture(SurfaceAccess(referenceFrame, pixelFormat, curX, curY, curW, curH),
2218							  refTexture, &texCoord[0], sampleParams);
2219
2220				// Compare this cell
2221				numFailedPixels += computeTextureLookupDiff(tcu::getSubregion(renderedFrame.getAccess(), curX, curY, curW, curH),
2222															tcu::getSubregion(referenceFrame.getAccess(), curX, curY, curW, curH),
2223															tcu::getSubregion(errorMask.getAccess(), curX, curY, curW, curH),
2224															m_texture->getRefTexture(), &texCoord[0], sampleParams,
2225															lookupPrec, lodPrec, m_testCtx.getWatchDog());
2226			}
2227		}
2228
2229		if (numFailedPixels > 0)
2230			m_testCtx.getLog() << TestLog::Message << "ERROR: Image verification failed, found " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2231
2232		m_testCtx.getLog() << TestLog::ImageSet("Result", "Verification result")
2233							<< TestLog::Image("Rendered", "Rendered image", renderedFrame);
2234
2235		if (numFailedPixels > 0)
2236		{
2237			m_testCtx.getLog() << TestLog::Image("Reference", "Ideal reference", referenceFrame)
2238								<< TestLog::Image("ErrorMask", "Error mask", errorMask);
2239		}
2240
2241		m_testCtx.getLog() << TestLog::EndImageSet;
2242
2243		{
2244			const bool isOk = numFailedPixels == 0;
2245			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
2246									isOk ? "Pass"				: "Image verification failed");
2247		}
2248	}
2249
2250	return STOP;
2251}
2252
2253class Texture3DMinLodCase : public Texture3DLodControlCase
2254{
2255public:
2256	Texture3DMinLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2257		: Texture3DLodControlCase(context, name, desc, minFilter)
2258	{
2259	}
2260
2261protected:
2262	void setTextureParams (int cellNdx)
2263	{
2264		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2265		gl.texParameterf(GL_TEXTURE_3D, GL_TEXTURE_MIN_LOD, getMinLodForCell(cellNdx));
2266	}
2267
2268	void getReferenceParams (ReferenceParams& params, int cellNdx)
2269	{
2270		params.minLod = getMinLodForCell(cellNdx);
2271	}
2272};
2273
2274class Texture3DMaxLodCase : public Texture3DLodControlCase
2275{
2276public:
2277	Texture3DMaxLodCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2278		: Texture3DLodControlCase(context, name, desc, minFilter)
2279	{
2280	}
2281
2282protected:
2283	void setTextureParams (int cellNdx)
2284	{
2285		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2286		gl.texParameterf(GL_TEXTURE_3D, GL_TEXTURE_MAX_LOD, getMaxLodForCell(cellNdx));
2287	}
2288
2289	void getReferenceParams (ReferenceParams& params, int cellNdx)
2290	{
2291		params.maxLod = getMaxLodForCell(cellNdx);
2292	}
2293};
2294
2295class Texture3DBaseLevelCase : public Texture3DLodControlCase
2296{
2297public:
2298	Texture3DBaseLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2299		: Texture3DLodControlCase(context, name, desc, minFilter)
2300	{
2301	}
2302
2303protected:
2304	int getBaseLevel (int cellNdx) const
2305	{
2306		const int	numLevels	= deLog2Floor32(de::max(m_texWidth, de::max(m_texHeight, m_texDepth)))+1;
2307		const int	baseLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0x7347e9) % numLevels;
2308
2309		return baseLevel;
2310	}
2311
2312	void setTextureParams (int cellNdx)
2313	{
2314		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2315		gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_BASE_LEVEL, getBaseLevel(cellNdx));
2316	}
2317
2318	void getReferenceParams (ReferenceParams& params, int cellNdx)
2319	{
2320		params.baseLevel = getBaseLevel(cellNdx);
2321	}
2322};
2323
2324class Texture3DMaxLevelCase : public Texture3DLodControlCase
2325{
2326public:
2327	Texture3DMaxLevelCase (Context& context, const char* name, const char* desc, deUint32 minFilter)
2328		: Texture3DLodControlCase(context, name, desc, minFilter)
2329	{
2330	}
2331
2332protected:
2333	int getMaxLevel (int cellNdx) const
2334	{
2335		const int	numLevels	= deLog2Floor32(de::max(m_texWidth, de::max(m_texHeight, m_texDepth)))+1;
2336		const int	maxLevel	= (deInt32Hash(cellNdx) ^ deStringHash(getName()) ^ 0x9111e7) % numLevels;
2337
2338		return maxLevel;
2339	}
2340
2341	void setTextureParams (int cellNdx)
2342	{
2343		const glw::Functions& gl = m_context.getRenderContext().getFunctions();
2344		gl.texParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, getMaxLevel(cellNdx));
2345	}
2346
2347	void getReferenceParams (ReferenceParams& params, int cellNdx)
2348	{
2349		params.maxLevel = getMaxLevel(cellNdx);
2350	}
2351};
2352
2353TextureMipmapTests::TextureMipmapTests (Context& context)
2354	: TestCaseGroup(context, "mipmap", "Mipmapping tests")
2355{
2356}
2357
2358TextureMipmapTests::~TextureMipmapTests (void)
2359{
2360}
2361
2362void TextureMipmapTests::init (void)
2363{
2364	tcu::TestCaseGroup* group2D		= new tcu::TestCaseGroup(m_testCtx, "2d",	"2D Texture Mipmapping");
2365	tcu::TestCaseGroup*	groupCube	= new tcu::TestCaseGroup(m_testCtx, "cube",	"Cube Map Mipmapping");
2366	tcu::TestCaseGroup*	group3D		= new tcu::TestCaseGroup(m_testCtx, "3d",	"3D Texture Mipmapping");
2367	addChild(group2D);
2368	addChild(groupCube);
2369	addChild(group3D);
2370
2371	static const struct
2372	{
2373		const char*		name;
2374		deUint32		mode;
2375	} wrapModes[] =
2376	{
2377		{ "clamp",		GL_CLAMP_TO_EDGE },
2378		{ "repeat",		GL_REPEAT },
2379		{ "mirror",		GL_MIRRORED_REPEAT }
2380	};
2381
2382	static const struct
2383	{
2384		const char*		name;
2385		deUint32		mode;
2386	} minFilterModes[] =
2387	{
2388		{ "nearest_nearest",	GL_NEAREST_MIPMAP_NEAREST	},
2389		{ "linear_nearest",		GL_LINEAR_MIPMAP_NEAREST	},
2390		{ "nearest_linear",		GL_NEAREST_MIPMAP_LINEAR	},
2391		{ "linear_linear",		GL_LINEAR_MIPMAP_LINEAR		}
2392	};
2393
2394	static const struct
2395	{
2396		CoordType		type;
2397		const char*		name;
2398		const char*		desc;
2399	} coordTypes[] =
2400	{
2401		{ COORDTYPE_BASIC,		"basic",		"Mipmapping with translated and scaled coordinates" },
2402		{ COORDTYPE_AFFINE,		"affine",		"Mipmapping with affine coordinate transform"		},
2403		{ COORDTYPE_PROJECTED,	"projected",	"Mipmapping with perspective projection"			}
2404	};
2405
2406	static const struct
2407	{
2408		const char*		name;
2409		deUint32		format;
2410		deUint32		dataType;
2411	} formats[] =
2412	{
2413		{ "a8",			GL_ALPHA,			GL_UNSIGNED_BYTE },
2414		{ "l8",			GL_LUMINANCE,		GL_UNSIGNED_BYTE },
2415		{ "la88",		GL_LUMINANCE_ALPHA,	GL_UNSIGNED_BYTE },
2416		{ "rgb565",		GL_RGB,				GL_UNSIGNED_SHORT_5_6_5 },
2417		{ "rgb888",		GL_RGB,				GL_UNSIGNED_BYTE },
2418		{ "rgba4444",	GL_RGBA,			GL_UNSIGNED_SHORT_4_4_4_4 },
2419		{ "rgba5551",	GL_RGBA,			GL_UNSIGNED_SHORT_5_5_5_1 },
2420		{ "rgba8888",	GL_RGBA,			GL_UNSIGNED_BYTE }
2421	};
2422
2423	static const struct
2424	{
2425		const char*		name;
2426		deUint32		hint;
2427	} genHints[] =
2428	{
2429		{ "fastest",	GL_FASTEST },
2430		{ "nicest",		GL_NICEST }
2431	};
2432
2433	static const struct
2434	{
2435		const char*		name;
2436		int				width;
2437		int				height;
2438	} tex2DSizes[] =
2439	{
2440		{ DE_NULL,		64, 64 }, // Default.
2441		{ "npot",		63, 57 },
2442		{ "non_square",	32, 64 }
2443	};
2444
2445	static const struct
2446	{
2447		const char*		name;
2448		int				width;
2449		int				height;
2450		int				depth;
2451	} tex3DSizes[] =
2452	{
2453		{ DE_NULL,		32, 32, 32 }, // Default.
2454		{ "npot",		33, 29, 27 }
2455	};
2456
2457	const int cubeMapSize = 64;
2458
2459	static const struct
2460	{
2461		CoordType		type;
2462		const char*		name;
2463		const char*		desc;
2464	} cubeCoordTypes[] =
2465	{
2466		{ COORDTYPE_BASIC,		"basic",		"Mipmapping with translated and scaled coordinates" },
2467		{ COORDTYPE_PROJECTED,	"projected",	"Mipmapping with perspective projection"			},
2468		{ COORDTYPE_BASIC_BIAS,	"bias",			"User-supplied bias value"							}
2469	};
2470
2471	// 2D cases.
2472	for (int coordType = 0; coordType < DE_LENGTH_OF_ARRAY(coordTypes); coordType++)
2473	{
2474		tcu::TestCaseGroup* coordTypeGroup = new tcu::TestCaseGroup(m_testCtx, coordTypes[coordType].name, coordTypes[coordType].desc);
2475		group2D->addChild(coordTypeGroup);
2476
2477		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2478		{
2479			for (int wrapMode = 0; wrapMode < DE_LENGTH_OF_ARRAY(wrapModes); wrapMode++)
2480			{
2481				// Add non_square variants to basic cases only.
2482				int sizeEnd = coordTypes[coordType].type == COORDTYPE_BASIC ? DE_LENGTH_OF_ARRAY(tex2DSizes) : 1;
2483
2484				for (int size = 0; size < sizeEnd; size++)
2485				{
2486					std::ostringstream name;
2487					name << minFilterModes[minFilter].name
2488						 << "_" << wrapModes[wrapMode].name;
2489
2490					if (tex2DSizes[size].name)
2491						name << "_" << tex2DSizes[size].name;
2492
2493					coordTypeGroup->addChild(new Texture2DMipmapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
2494																	 name.str().c_str(), "",
2495																	 coordTypes[coordType].type,
2496																	 minFilterModes[minFilter].mode,
2497																	 wrapModes[wrapMode].mode,
2498																	 wrapModes[wrapMode].mode,
2499																	 GL_RGBA, GL_UNSIGNED_BYTE,
2500																	 tex2DSizes[size].width, tex2DSizes[size].height));
2501				}
2502			}
2503		}
2504	}
2505
2506	// 2D bias variants.
2507	{
2508		tcu::TestCaseGroup* biasGroup = new tcu::TestCaseGroup(m_testCtx, "bias", "User-supplied bias value");
2509		group2D->addChild(biasGroup);
2510
2511		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2512			biasGroup->addChild(new Texture2DMipmapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
2513														minFilterModes[minFilter].name, "",
2514														COORDTYPE_BASIC_BIAS,
2515														minFilterModes[minFilter].mode,
2516														GL_REPEAT, GL_REPEAT,
2517														GL_RGBA, GL_UNSIGNED_BYTE,
2518														tex2DSizes[0].width, tex2DSizes[0].height));
2519	}
2520
2521	// 2D mipmap generation variants.
2522	{
2523		tcu::TestCaseGroup* genMipmapGroup = new tcu::TestCaseGroup(m_testCtx, "generate", "Mipmap generation tests");
2524		group2D->addChild(genMipmapGroup);
2525
2526		for (int format = 0; format < DE_LENGTH_OF_ARRAY(formats); format++)
2527		{
2528			for (int size = 0; size < DE_LENGTH_OF_ARRAY(tex2DSizes); size++)
2529			{
2530				for (int hint = 0; hint < DE_LENGTH_OF_ARRAY(genHints); hint++)
2531				{
2532					std::ostringstream name;
2533					name << formats[format].name;
2534
2535					if (tex2DSizes[size].name)
2536						name << "_" << tex2DSizes[size].name;
2537
2538					name << "_" << genHints[hint].name;
2539
2540					genMipmapGroup->addChild(new Texture2DGenMipmapCase(m_testCtx, m_context.getRenderContext(), name.str().c_str(), "",
2541																		formats[format].format, formats[format].dataType, genHints[hint].hint,
2542																		tex2DSizes[size].width, tex2DSizes[size].height));
2543				}
2544			}
2545		}
2546	}
2547
2548	// 2D LOD controls.
2549	{
2550		// MIN_LOD
2551		tcu::TestCaseGroup* minLodGroup = new tcu::TestCaseGroup(m_testCtx, "min_lod", "Lod control: min lod");
2552		group2D->addChild(minLodGroup);
2553
2554		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2555			minLodGroup->addChild(new Texture2DMinLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2556
2557		// MAX_LOD
2558		tcu::TestCaseGroup* maxLodGroup = new tcu::TestCaseGroup(m_testCtx, "max_lod", "Lod control: max lod");
2559		group2D->addChild(maxLodGroup);
2560
2561		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2562			maxLodGroup->addChild(new Texture2DMaxLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2563
2564		// BASE_LEVEL
2565		tcu::TestCaseGroup* baseLevelGroup = new tcu::TestCaseGroup(m_testCtx, "base_level", "Base level");
2566		group2D->addChild(baseLevelGroup);
2567
2568		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2569			baseLevelGroup->addChild(new Texture2DBaseLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2570
2571		// MAX_LEVEL
2572		tcu::TestCaseGroup* maxLevelGroup = new tcu::TestCaseGroup(m_testCtx, "max_level", "Max level");
2573		group2D->addChild(maxLevelGroup);
2574
2575		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2576			maxLevelGroup->addChild(new Texture2DMaxLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2577	}
2578
2579	// Cubemap cases.
2580	for (int coordType = 0; coordType < DE_LENGTH_OF_ARRAY(cubeCoordTypes); coordType++)
2581	{
2582		tcu::TestCaseGroup* coordTypeGroup = new tcu::TestCaseGroup(m_testCtx, cubeCoordTypes[coordType].name, cubeCoordTypes[coordType].desc);
2583		groupCube->addChild(coordTypeGroup);
2584
2585		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2586		{
2587			coordTypeGroup->addChild(new TextureCubeMipmapCase(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo(),
2588															   minFilterModes[minFilter].name, "",
2589															   cubeCoordTypes[coordType].type,
2590															   minFilterModes[minFilter].mode,
2591															   GL_CLAMP_TO_EDGE,
2592															   GL_CLAMP_TO_EDGE,
2593															   GL_RGBA, GL_UNSIGNED_BYTE, cubeMapSize));
2594		}
2595	}
2596
2597	// Cubemap mipmap generation variants.
2598	{
2599		tcu::TestCaseGroup* genMipmapGroup = new tcu::TestCaseGroup(m_testCtx, "generate", "Mipmap generation tests");
2600		groupCube->addChild(genMipmapGroup);
2601
2602		for (int format = 0; format < DE_LENGTH_OF_ARRAY(formats); format++)
2603		{
2604			for (int hint = 0; hint < DE_LENGTH_OF_ARRAY(genHints); hint++)
2605			{
2606				std::ostringstream name;
2607				name << formats[format].name
2608					 << "_" << genHints[hint].name;
2609
2610				genMipmapGroup->addChild(new TextureCubeGenMipmapCase(m_testCtx, m_context.getRenderContext(), name.str().c_str(), "", formats[format].format, formats[format].dataType, genHints[hint].hint, cubeMapSize));
2611			}
2612		}
2613	}
2614
2615	// Cubemap LOD controls.
2616	{
2617		// MIN_LOD
2618		tcu::TestCaseGroup* minLodGroup = new tcu::TestCaseGroup(m_testCtx, "min_lod", "Lod control: min lod");
2619		groupCube->addChild(minLodGroup);
2620
2621		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2622			minLodGroup->addChild(new TextureCubeMinLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2623
2624		// MAX_LOD
2625		tcu::TestCaseGroup* maxLodGroup = new tcu::TestCaseGroup(m_testCtx, "max_lod", "Lod control: max lod");
2626		groupCube->addChild(maxLodGroup);
2627
2628		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2629			maxLodGroup->addChild(new TextureCubeMaxLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2630
2631		// BASE_LEVEL
2632		tcu::TestCaseGroup* baseLevelGroup = new tcu::TestCaseGroup(m_testCtx, "base_level", "Base level");
2633		groupCube->addChild(baseLevelGroup);
2634
2635		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2636			baseLevelGroup->addChild(new TextureCubeBaseLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2637
2638		// MAX_LEVEL
2639		tcu::TestCaseGroup* maxLevelGroup = new tcu::TestCaseGroup(m_testCtx, "max_level", "Max level");
2640		groupCube->addChild(maxLevelGroup);
2641
2642		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2643			maxLevelGroup->addChild(new TextureCubeMaxLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2644	}
2645
2646	// 3D cases.
2647	for (int coordType = 0; coordType < DE_LENGTH_OF_ARRAY(coordTypes); coordType++)
2648	{
2649		tcu::TestCaseGroup* coordTypeGroup = new tcu::TestCaseGroup(m_testCtx, coordTypes[coordType].name, coordTypes[coordType].desc);
2650		group3D->addChild(coordTypeGroup);
2651
2652		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2653		{
2654			for (int wrapMode = 0; wrapMode < DE_LENGTH_OF_ARRAY(wrapModes); wrapMode++)
2655			{
2656				// Add other size variants to basic cases only.
2657				int sizeEnd = coordTypes[coordType].type == COORDTYPE_BASIC ? DE_LENGTH_OF_ARRAY(tex3DSizes) : 1;
2658
2659				for (int size = 0; size < sizeEnd; size++)
2660				{
2661					std::ostringstream name;
2662					name << minFilterModes[minFilter].name
2663						 << "_" << wrapModes[wrapMode].name;
2664
2665					if (tex3DSizes[size].name)
2666						name << "_" << tex3DSizes[size].name;
2667
2668					coordTypeGroup->addChild(new Texture3DMipmapCase(m_context,
2669																	 name.str().c_str(), "",
2670																	 coordTypes[coordType].type,
2671																	 minFilterModes[minFilter].mode,
2672																	 wrapModes[wrapMode].mode,
2673																	 wrapModes[wrapMode].mode,
2674																	 wrapModes[wrapMode].mode,
2675																	 GL_RGBA8,
2676																	 tex3DSizes[size].width, tex3DSizes[size].height, tex3DSizes[size].depth));
2677				}
2678			}
2679		}
2680	}
2681
2682	// 3D bias variants.
2683	{
2684		tcu::TestCaseGroup* biasGroup = new tcu::TestCaseGroup(m_testCtx, "bias", "User-supplied bias value");
2685		group3D->addChild(biasGroup);
2686
2687		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2688			biasGroup->addChild(new Texture3DMipmapCase(m_context,
2689														minFilterModes[minFilter].name, "",
2690														COORDTYPE_BASIC_BIAS,
2691														minFilterModes[minFilter].mode,
2692														GL_REPEAT, GL_REPEAT, GL_REPEAT,
2693														GL_RGBA8,
2694														tex3DSizes[0].width, tex3DSizes[0].height, tex3DSizes[0].depth));
2695	}
2696
2697	// 3D LOD controls.
2698	{
2699		// MIN_LOD
2700		tcu::TestCaseGroup* minLodGroup = new tcu::TestCaseGroup(m_testCtx, "min_lod", "Lod control: min lod");
2701		group3D->addChild(minLodGroup);
2702
2703		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2704			minLodGroup->addChild(new Texture3DMinLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2705
2706		// MAX_LOD
2707		tcu::TestCaseGroup* maxLodGroup = new tcu::TestCaseGroup(m_testCtx, "max_lod", "Lod control: max lod");
2708		group3D->addChild(maxLodGroup);
2709
2710		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2711			maxLodGroup->addChild(new Texture3DMaxLodCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2712
2713		// BASE_LEVEL
2714		tcu::TestCaseGroup* baseLevelGroup = new tcu::TestCaseGroup(m_testCtx, "base_level", "Base level");
2715		group3D->addChild(baseLevelGroup);
2716
2717		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2718			baseLevelGroup->addChild(new Texture3DBaseLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2719
2720		// MAX_LEVEL
2721		tcu::TestCaseGroup* maxLevelGroup = new tcu::TestCaseGroup(m_testCtx, "max_level", "Max level");
2722		group3D->addChild(maxLevelGroup);
2723
2724		for (int minFilter = 0; minFilter < DE_LENGTH_OF_ARRAY(minFilterModes); minFilter++)
2725			maxLevelGroup->addChild(new Texture3DMaxLevelCase(m_context, minFilterModes[minFilter].name, "", minFilterModes[minFilter].mode));
2726	}
2727}
2728
2729} // Functional
2730} // gles3
2731} // deqp
2732