1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Texture unit usage tests.
22 *
23 * \todo [2012-07-12 nuutti] Come up with a good way to make these tests faster.
24 *//*--------------------------------------------------------------------*/
25
26#include "es2fTextureUnitTests.hpp"
27#include "glsTextureTestUtil.hpp"
28#include "gluTextureUtil.hpp"
29#include "gluContextInfo.hpp"
30#include "tcuTextureUtil.hpp"
31#include "tcuImageCompare.hpp"
32#include "tcuMatrix.hpp"
33#include "tcuRenderTarget.hpp"
34#include "sglrContextUtil.hpp"
35#include "sglrReferenceContext.hpp"
36#include "sglrGLContext.hpp"
37#include "deMath.h"
38#include "deStringUtil.hpp"
39#include "deRandom.hpp"
40
41#include "glwEnums.hpp"
42#include "glwFunctions.hpp"
43
44using tcu::Vec2;
45using tcu::Vec3;
46using tcu::Vec4;
47using tcu::IVec2;
48using tcu::Mat3;
49using std::vector;
50using std::string;
51using namespace glw; // GL types
52
53namespace deqp
54{
55
56using namespace gls::TextureTestUtil;
57
58namespace gles2
59{
60namespace Functional
61{
62
63static const int VIEWPORT_WIDTH			= 128;
64static const int VIEWPORT_HEIGHT		= 128;
65
66static const int TEXTURE_WIDTH_2D		= 128;
67static const int TEXTURE_HEIGHT_2D		= 128;
68
69// \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time.
70static const int TEXTURE_WIDTH_CUBE		= 256;
71static const int TEXTURE_HEIGHT_CUBE	= 256;
72
73static const int GRID_CELL_SIZE			= 8;
74
75static const GLenum s_testFormats[] =
76{
77	GL_RGB,
78	GL_RGBA,
79	GL_ALPHA,
80	GL_LUMINANCE,
81	GL_LUMINANCE_ALPHA
82};
83
84static const GLenum s_testDataTypes[] =
85{
86	GL_UNSIGNED_BYTE,
87	GL_UNSIGNED_SHORT_5_6_5,
88	GL_UNSIGNED_SHORT_4_4_4_4,
89	GL_UNSIGNED_SHORT_5_5_5_1,
90};
91
92static const GLenum s_testWrapModes[] =
93{
94	GL_CLAMP_TO_EDGE,
95	GL_REPEAT,
96	GL_MIRRORED_REPEAT,
97};
98
99static const GLenum s_testMinFilters[] =
100{
101	GL_NEAREST,
102	GL_LINEAR,
103	GL_NEAREST_MIPMAP_NEAREST,
104	GL_LINEAR_MIPMAP_NEAREST,
105	GL_NEAREST_MIPMAP_LINEAR,
106	GL_LINEAR_MIPMAP_LINEAR
107};
108
109static const GLenum s_testNonMipmapMinFilters[] =
110{
111	GL_NEAREST,
112	GL_LINEAR
113};
114
115static const GLenum s_testMagFilters[] =
116{
117	GL_NEAREST,
118	GL_LINEAR
119};
120
121static const GLenum s_cubeFaceTargets[] =
122{
123	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
124	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
125	GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
126	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
127	GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
128	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
129};
130
131static string generateMultiTexFragmentShader(int numUnits, const GLenum* unitTypes)
132{
133	// The fragment shader calculates the average of a set of textures.
134
135	string samplersStr;
136	string matricesStr;
137	string lookupsStr;
138
139	string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)";
140
141	for (int ndx = 0; ndx < numUnits; ndx++)
142	{
143		string			ndxStr				= de::toString(ndx);
144		string			samplerName			= "u_sampler" + ndxStr;
145		string			transformationName	= "u_trans" + ndxStr;
146		const char*		samplerType			= unitTypes[ndx] == GL_TEXTURE_2D ? "sampler2D" : "samplerCube";
147		const char*		lookupFunc			= unitTypes[ndx] == GL_TEXTURE_2D ? "texture2D" : "textureCube";
148
149		samplersStr += string("") + "uniform mediump " + samplerType + " " + samplerName + ";\n";
150		matricesStr += "uniform mediump mat3 " + transformationName + ";\n";
151
152		string lookupCoord = transformationName + "*vec3(v_coord, 1.0)";
153
154		if (unitTypes[ndx] == GL_TEXTURE_2D)
155			lookupCoord = "vec2(" + lookupCoord + ")";
156
157		lookupsStr += "\tcolor += " + colorMultiplier + "*" + lookupFunc + "(" + samplerName + ", " + lookupCoord + ");\n";
158	}
159
160	return
161		samplersStr +
162		matricesStr +
163		"varying mediump vec2 v_coord;\n"
164		"\n"
165		"void main (void)\n"
166		"{\n"
167		"	mediump vec4 color = vec4(0.0);\n" +
168		lookupsStr +
169		"	gl_FragColor = color;\n"
170		"}\n";
171}
172
173static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const GLenum* unitTypes)
174{
175	sglr::pdec::ShaderProgramDeclaration decl;
176
177	decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT);
178	decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT);
179	decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT);
180	decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT);
181
182	for (int ndx = 0; ndx < numUnits; ++ndx)
183	{
184		string	samplerName			= "u_sampler" + de::toString(ndx);
185		string	transformationName	= "u_trans" + de::toString(ndx);
186
187		decl << sglr::pdec::Uniform(samplerName, (unitTypes[ndx] == GL_TEXTURE_2D) ? (glu::TYPE_SAMPLER_2D) : (glu::TYPE_SAMPLER_CUBE));
188		decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT3);
189	}
190
191	decl << sglr::pdec::VertexSource("attribute highp vec4 a_position;\n"
192									 "attribute mediump vec2 a_coord;\n"
193									 "varying mediump vec2 v_coord;\n"
194									 "\n"
195									 "void main (void)\n"
196									 "{\n"
197									 "	gl_Position = a_position;\n"
198									 "	v_coord = a_coord;\n"
199									 "}\n");
200	decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes));
201
202	return decl;
203}
204
205// Calculates values to be used in calculateLod().
206static Vec4 calculateLodDerivateParts(const Mat3& transformation)
207{
208	// Calculate transformed coordinates of three corners.
209	Vec2 trans00 = (transformation * Vec3(0.0f, 0.0f, 1.0f)).xy();
210	Vec2 trans01 = (transformation * Vec3(0.0f, 1.0f, 1.0f)).xy();
211	Vec2 trans10 = (transformation * Vec3(1.0f, 0.0f, 1.0f)).xy();
212
213	return Vec4(trans10.x() - trans00.x(),
214				trans01.x() - trans00.x(),
215				trans10.y() - trans00.y(),
216				trans01.y() - trans00.y());
217}
218
219// Calculates the maximum allowed lod from derivates
220static float calculateLodMax(const Vec4& derivateParts, const tcu::IVec2& textureSize, const Vec2& screenDerivate)
221{
222	float dudx = derivateParts.x() * (float)textureSize.x() * screenDerivate.x();
223	float dudy = derivateParts.y() * (float)textureSize.x() * screenDerivate.y();
224	float dvdx = derivateParts.z() * (float)textureSize.y() * screenDerivate.x();
225	float dvdy = derivateParts.w() * (float)textureSize.y() * screenDerivate.y();
226
227	return deFloatLog2(de::max(de::abs(dudx), de::abs(dudy)) + de::max(de::abs(dvdx), de::abs(dvdy)));
228}
229
230// Calculates the minimum allowed lod from derivates
231static float calculateLodMin(const Vec4& derivateParts, const tcu::IVec2& textureSize, const Vec2& screenDerivate)
232{
233	float dudx = derivateParts.x() * (float)textureSize.x() * screenDerivate.x();
234	float dudy = derivateParts.y() * (float)textureSize.x() * screenDerivate.y();
235	float dvdx = derivateParts.z() * (float)textureSize.y() * screenDerivate.x();
236	float dvdy = derivateParts.w() * (float)textureSize.y() * screenDerivate.y();
237
238	return deFloatLog2(de::max(de::max(de::abs(dudx), de::abs(dudy)), de::max(de::abs(dvdx), de::abs(dvdy))));
239}
240
241class MultiTexShader : public sglr::ShaderProgram
242{
243public:
244							MultiTexShader	(deUint32 randSeed, int numUnits, const vector<GLenum>& unitTypes);
245
246	void					setUniforms		(sglr::Context& context, deUint32 program) const;
247	void					makeSafeLods	(const vector<IVec2>& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 .
248
249private:
250	void					shadeVertices	(const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const;
251	void					shadeFragments	(rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const;
252
253	int						m_numUnits;
254	vector<GLenum>			m_unitTypes;		// 2d or cube map.
255	vector<Mat3>			m_transformations;
256	vector<Vec4>			m_lodDerivateParts;	// Parts of lod derivates; computed in init(), used in eval().
257};
258
259MultiTexShader::MultiTexShader (deUint32 randSeed, int numUnits, const vector<GLenum>& unitTypes)
260	: sglr::ShaderProgram	(generateShaderProgramDeclaration(numUnits, &unitTypes[0]))
261	, m_numUnits			(numUnits)
262	, m_unitTypes			(unitTypes)
263{
264	// 2d-to-cube-face transformations.
265	// \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well.
266	static const float s_cubeTransforms[][3*3] =
267	{
268		// Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1))
269		{  0.0f,  0.0f, -1.0f,
270		   0.0f, -2.0f,  1.0f,
271		   2.0f,  0.0f, -1.0f },
272		// Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1))
273		{  0.0f,  0.0f,  1.0f,
274		   0.0f, -2.0f,  1.0f,
275		  -2.0f,  0.0f,  1.0f },
276		// Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1))
277		{  2.0f,  0.0f, -1.0f,
278		   0.0f,  0.0f, -1.0f,
279		   0.0f, -2.0f,  1.0f },
280		// Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1))
281		{  2.0f,  0.0f, -1.0f,
282		   0.0f,  0.0f,  1.0f,
283		   0.0f,  2.0f, -1.0f },
284		// Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1)
285		{ -2.0f,  0.0f,  1.0f,
286		   0.0f, -2.0f,  1.0f,
287		   0.0f,  0.0f, -1.0f },
288		// Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1)
289		{  2.0f,  0.0f, -1.0f,
290		   0.0f, -2.0f,  1.0f,
291		   0.0f,  0.0f,  1.0f }
292	};
293
294	// Generate transformation matrices.
295
296	de::Random rnd(randSeed);
297
298	m_transformations.reserve(m_numUnits);
299	m_lodDerivateParts.reserve(m_numUnits);
300
301	DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
302
303	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
304	{
305		if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
306		{
307			float rotAngle				= rnd.getFloat(0.0f, 2.0f*DE_PI);
308			float xScaleFactor			= rnd.getFloat(0.7f, 1.5f);
309			float yScaleFactor			= rnd.getFloat(0.7f, 1.5f);
310			float xShearAmount			= rnd.getFloat(0.0f, 0.5f);
311			float yShearAmount			= rnd.getFloat(0.0f, 0.5f);
312			float xTranslationAmount	= rnd.getFloat(-0.5f, 0.5f);
313			float yTranslationAmount	= rnd.getFloat(-0.5f, 0.5f);
314
315			float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations.
316			{
317				1.0f,  0.0f, -0.5f,
318				0.0f,  1.0f, -0.5f,
319				0.0f,  0.0f,  1.0f
320			};
321			float rotTransfData[3*3] =
322			{
323				deFloatCos(rotAngle),	-deFloatSin(rotAngle),	0.0f,
324				deFloatSin(rotAngle),	deFloatCos(rotAngle),	0.0f,
325				0.0f,					0.0f,					1.0f
326			};
327			float scaleTransfData[3*3] =
328			{
329				xScaleFactor,	0.0f,			0.0f,
330				0.0f,			yScaleFactor,	0.0f,
331				0.0f,			0.0f,			1.0f
332			};
333			float xShearTransfData[3*3] =
334			{
335				1.0f,			xShearAmount,	0.0f,
336				0.0f,			1.0f,			0.0f,
337				0.0f,			0.0f,			1.0f
338			};
339			float yShearTransfData[3*3] =
340			{
341				1.0f,			0.0f,			0.0f,
342				yShearAmount,	1.0f,			0.0f,
343				0.0f,			0.0f,			1.0f
344			};
345			float translationTransfData[3*3] =
346			{
347				1.0f,	0.0f,	xTranslationAmount,
348				0.0f,	1.0f,	yTranslationAmount,
349				0.0f,	0.0f,	1.0f
350			};
351
352			Mat3 transformation =
353				Mat3(tempOffsetData) *
354				Mat3(translationTransfData) *
355				Mat3(rotTransfData) *
356				Mat3(scaleTransfData) *
357				Mat3(xShearTransfData) *
358				Mat3(yShearTransfData) *
359				(Mat3(tempOffsetData) * (-1.0f));
360
361			// Calculate parts of lod derivates.
362			m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
363
364			m_transformations.push_back(transformation);
365		}
366		else
367		{
368			DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP);
369			DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms));
370
371			float planarTransData[3*3];
372
373			// In case of a cube map, we only want to render one face, so the transformation needs to be restricted - only enlarging scaling is done.
374
375			for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++)
376			{
377				if (i == 0 || i == 4)
378					planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling.
379				else if (i == 8)
380					planarTransData[i] = 1.0f;
381				else
382					planarTransData[i] = 0.0f;
383			}
384
385			int		faceNdx			= rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1);
386			Mat3	planarTrans		(planarTransData);									// Planar, face-agnostic transformation.
387			Mat3	finalTrans		= Mat3(s_cubeTransforms[faceNdx]) * planarTrans;	// Final transformation from planar to cube map coordinates, including the transformation just generated.
388
389			// Calculate parts of lod derivates.
390			m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans));
391
392			m_transformations.push_back(finalTrans);
393		}
394	}
395}
396
397void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const
398{
399	ctx.useProgram(program);
400
401	// Sampler and matrix uniforms.
402
403	for (int ndx = 0; ndx < m_numUnits; ndx++)
404	{
405		string			ndxStr		= de::toString(ndx);
406
407		ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx);
408		ctx.uniformMatrix3fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]);
409	}
410}
411
412void MultiTexShader::makeSafeLods (const vector<IVec2>& textureSizes, const IVec2& viewportSize)
413{
414	DE_ASSERT((int)textureSizes.size() == m_numUnits);
415
416	static const float shrinkScaleMatData[3*3] =
417	{
418		0.95f,	0.0f,	0.0f,
419		0.0f,	0.95f,	0.0f,
420		0.0f,	0.0f,	1.0f
421	};
422	Mat3 shrinkScaleMat(shrinkScaleMatData);
423
424	Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y());
425
426	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
427	{
428		// As long as LOD is too close to 0.0 or is positive and too close to a something-and-a-half (0.5, 1.5, 2.5 etc) or allowed lod range could round to different levels, zoom in a little to get a safer LOD.
429		for (;;)
430		{
431			const float threshold = 0.1f;
432			const float epsilon	= 0.01f;
433
434			const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
435			const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
436
437			const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1);
438			const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1);
439
440			if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) ||
441				de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) ||
442				maxLevel != minLevel)
443			{
444				m_transformations[unitNdx] = shrinkScaleMat * m_transformations[unitNdx];
445				m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]);
446			}
447			else
448				break;
449		}
450	}
451}
452
453void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
454{
455	for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
456	{
457		rr::VertexPacket& packet = *(packets[packetNdx]);
458
459		packet.position		= rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
460		packet.outputs[0]	= rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
461	}
462}
463
464void MultiTexShader::shadeFragments	(rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
465{
466	DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
467	DE_ASSERT((int)m_transformations.size() == m_numUnits);
468	DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits);
469
470	for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
471	{
472		rr::FragmentPacket& packet				= packets[packetNdx];
473		const float			colorMultiplier		= 1.0f / (float)m_numUnits;
474		Vec4				outColors[4]		= { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) };
475
476		for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
477		{
478			tcu::Vec4 texSamples[4];
479
480			// Read tex coords
481			const tcu::Vec2 texCoords[4] =
482			{
483				rr::readTriangleVarying<float>(packet, context, 0, 0).xy(),
484				rr::readTriangleVarying<float>(packet, context, 0, 1).xy(),
485				rr::readTriangleVarying<float>(packet, context, 0, 2).xy(),
486				rr::readTriangleVarying<float>(packet, context, 0, 3).xy(),
487			};
488
489			if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
490			{
491				// Transform
492				const tcu::Vec2 transformedTexCoords[4] =
493				{
494					(m_transformations[unitNdx] * Vec3(texCoords[0].x(), texCoords[0].y(), 1.0f)).xy(),
495					(m_transformations[unitNdx] * Vec3(texCoords[1].x(), texCoords[1].y(), 1.0f)).xy(),
496					(m_transformations[unitNdx] * Vec3(texCoords[2].x(), texCoords[2].y(), 1.0f)).xy(),
497					(m_transformations[unitNdx] * Vec3(texCoords[3].x(), texCoords[3].y(), 1.0f)).xy(),
498				};
499
500				// Sample
501				m_uniforms[2*unitNdx].sampler.tex2D->sample4(texSamples, transformedTexCoords);
502			}
503			else
504			{
505				DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP);
506
507				// Transform
508				const tcu::Vec3 transformedTexCoords[4] =
509				{
510					m_transformations[unitNdx] * Vec3(texCoords[0].x(), texCoords[0].y(), 1.0f),
511					m_transformations[unitNdx] * Vec3(texCoords[1].x(), texCoords[1].y(), 1.0f),
512					m_transformations[unitNdx] * Vec3(texCoords[2].x(), texCoords[2].y(), 1.0f),
513					m_transformations[unitNdx] * Vec3(texCoords[3].x(), texCoords[3].y(), 1.0f),
514				};
515
516				// Sample
517				m_uniforms[2*unitNdx].sampler.texCube->sample4(texSamples, transformedTexCoords);
518			}
519
520			// Add to sum
521			for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
522				outColors[fragNdx] += colorMultiplier * texSamples[fragNdx];
523		}
524
525		// output
526		for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
527			rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]);
528	}
529}
530
531class TextureUnitCase : public TestCase
532{
533public:
534	enum CaseType
535	{
536		CASE_ONLY_2D = 0,
537		CASE_ONLY_CUBE,
538		CASE_MIXED,
539
540		CASE_LAST
541	};
542								TextureUnitCase		(Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed);
543								~TextureUnitCase	(void);
544
545	void						init				(void);
546	void						deinit				(void);
547	IterateResult				iterate				(void);
548
549private:
550	struct TextureParameters
551	{
552		GLenum format;
553		GLenum dataType;
554		GLenum wrapModeS;
555		GLenum wrapModeT;
556		GLenum minFilter;
557		GLenum magFilter;
558	};
559
560								TextureUnitCase		(const TextureUnitCase& other);
561	TextureUnitCase&			operator=			(const TextureUnitCase& other);
562
563	void						render				(sglr::Context& context);
564
565	const int					m_numUnitsParam;
566	const CaseType				m_caseType;
567	const deUint32				m_randSeed;
568
569	int							m_numTextures;	//!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units.
570	int							m_numUnits;		//!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum
571
572	vector<GLenum>				m_textureTypes;
573	vector<TextureParameters>	m_textureParams;
574	vector<tcu::Texture2D*>		m_textures2d;
575	vector<tcu::TextureCube*>	m_texturesCube;
576	vector<int>					m_unitTextures;	//!< Which texture is used in a particular unit.
577	vector<int>					m_ndx2dOrCube;	//!< Index of a texture in either m_textures2d or m_texturesCube, depending on texture type.
578	MultiTexShader*				m_shader;
579};
580
581TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed)
582	: TestCase			(context, tcu::NODETYPE_SELF_VALIDATE, name, desc)
583	, m_numUnitsParam	(numUnits)
584	, m_caseType		(caseType)
585	, m_randSeed		(randSeed)
586	, m_shader			(DE_NULL)
587{
588}
589
590TextureUnitCase::~TextureUnitCase (void)
591{
592	TextureUnitCase::deinit();
593}
594
595void TextureUnitCase::deinit (void)
596{
597	for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
598		delete *i;
599	m_textures2d.clear();
600
601	for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
602		delete *i;
603	m_texturesCube.clear();
604
605	delete m_shader;
606	m_shader = DE_NULL;
607}
608
609void TextureUnitCase::init (void)
610{
611	m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
612
613	// Make the textures.
614
615	try
616	{
617		tcu::TestLog&	log	= m_testCtx.getLog();
618		de::Random		rnd	(m_randSeed);
619
620		if (rnd.getFloat() < 0.7f)
621			m_numTextures = m_numUnits;											// In most cases use one unit per texture.
622		else
623			m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits);	// Sometimes assign same texture to multiple units.
624
625		log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage;
626
627		m_textureTypes.reserve(m_numTextures);
628		m_textureParams.reserve(m_numTextures);
629		m_ndx2dOrCube.reserve(m_numTextures);
630
631		// Generate textures.
632
633		for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
634		{
635			// Either fixed or randomized target types (2d or cube), and randomized parameters for every texture.
636
637			TextureParameters	params;
638			bool				is2d		= m_caseType == CASE_ONLY_2D	? true :
639											  m_caseType == CASE_ONLY_CUBE	? false :
640																			  rnd.getBool();
641
642			GLenum				type		= is2d ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP;
643			const int			texWidth	= is2d ? TEXTURE_WIDTH_2D : TEXTURE_WIDTH_CUBE;
644			const int			texHeight	= is2d ? TEXTURE_HEIGHT_2D : TEXTURE_HEIGHT_CUBE;
645			bool				mipmaps		= (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight));
646			int					numLevels	= mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1;
647
648			params.wrapModeS	= s_testWrapModes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
649			params.wrapModeT	= s_testWrapModes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
650			params.magFilter	= s_testMagFilters	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)];
651			params.dataType		= s_testDataTypes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testDataTypes) - 1)];
652
653			// Certain minification filters are only used when using mipmaps.
654			if (mipmaps)
655				params.minFilter = s_testMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)];
656			else
657				params.minFilter = s_testNonMipmapMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)];
658
659			// Format may depend on data type.
660			if (params.dataType == GL_UNSIGNED_SHORT_5_6_5)
661				params.format = GL_RGB;
662			else if (params.dataType == GL_UNSIGNED_SHORT_4_4_4_4 || params.dataType == GL_UNSIGNED_SHORT_5_5_5_1)
663				params.format = GL_RGBA;
664			else
665				params.format = s_testFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testFormats) - 1)];
666
667			m_textureTypes.push_back(type);
668			m_textureParams.push_back(params);
669
670			// Create new texture.
671
672			if (is2d)
673			{
674				m_ndx2dOrCube.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d array.
675				m_textures2d.push_back(new tcu::Texture2D(glu::mapGLTransferFormat(params.format, params.dataType), texWidth, texHeight));
676			}
677			else
678			{
679				m_ndx2dOrCube.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube array.
680				DE_ASSERT(texWidth == texHeight);
681				m_texturesCube.push_back(new tcu::TextureCube(glu::mapGLTransferFormat(params.format, params.dataType), texWidth));
682			}
683
684			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(is2d ? m_textures2d.back()->getFormat() : m_texturesCube.back()->getFormat());
685			Vec4					cBias		= fmtInfo.valueMin;
686			Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
687
688			// Fill with grid texture.
689
690			int numFaces = is2d ? 1 : (int)tcu::CUBEFACE_LAST;
691
692			for (int face = 0; face < numFaces; face++)
693			{
694				deUint32 rgb	= rnd.getUint32() & 0x00ffffff;
695				deUint32 alpha0	= 0xff000000;
696				deUint32 alpha1	= 0xff000000;
697
698				if (params.format == GL_ALPHA) // \note This needs alpha to be visible.
699				{
700					alpha0 &= rnd.getUint32();
701					alpha1 = ~alpha0;
702				}
703
704				deUint32 colorA = alpha0 | rgb;
705				deUint32 colorB = alpha1 | ~rgb;
706
707				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
708				{
709					if (is2d)
710						m_textures2d.back()->allocLevel(levelNdx);
711					else
712						m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
713
714					int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
715
716					tcu::PixelBufferAccess access = is2d ? m_textures2d.back()->getLevel(levelNdx) : m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face);
717					tcu::fillWithGrid(access, curCellSize, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
718				}
719			}
720		}
721
722		// Assign a texture index to each unit.
723
724		m_unitTextures.reserve(m_numUnits);
725
726		// \note Every texture is used at least once.
727		for (int i = 0; i < m_numTextures; i++)
728			m_unitTextures.push_back(i);
729
730		// Assign a random texture to remaining units.
731		while ((int)m_unitTextures.size() < m_numUnits)
732			m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
733
734		rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
735
736		// Create shader.
737
738		vector<GLenum> unitTypes;
739		unitTypes.reserve(m_numUnits);
740		for (int i = 0; i < m_numUnits; i++)
741			unitTypes.push_back(m_textureTypes[m_unitTextures[i]]);
742
743		DE_ASSERT(m_shader == DE_NULL);
744		m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes);
745	}
746	catch (const std::exception&)
747	{
748		// Clean up to save memory.
749		TextureUnitCase::deinit();
750		throw;
751	}
752}
753
754TextureUnitCase::IterateResult TextureUnitCase::iterate (void)
755{
756	glu::RenderContext&			renderCtx			= m_context.getRenderContext();
757	const tcu::RenderTarget&	renderTarget		= renderCtx.getRenderTarget();
758	tcu::TestLog&				log					= m_testCtx.getLog();
759	de::Random					rnd					(m_randSeed);
760
761	int							viewportWidth		= deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
762	int							viewportHeight		= deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
763	int							viewportX			= rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
764	int							viewportY			= rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
765
766	tcu::Surface				gles2Frame			(viewportWidth, viewportHeight);
767	tcu::Surface				refFrame			(viewportWidth, viewportHeight);
768
769	{
770		// First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
771
772		vector<IVec2> texSizes;
773		texSizes.reserve(m_numUnits);
774
775		for (int i = 0; i < m_numUnits; i++)
776		{
777			int		texNdx			= m_unitTextures[i];
778			int		texNdxInType	= m_ndx2dOrCube[texNdx];
779			GLenum	type			= m_textureTypes[texNdx];
780
781			switch (type)
782			{
783				case GL_TEXTURE_2D:			texSizes.push_back(IVec2(m_textures2d[texNdxInType]->getWidth(),	m_textures2d[texNdxInType]->getHeight()));	break;
784				case GL_TEXTURE_CUBE_MAP:	texSizes.push_back(IVec2(m_texturesCube[texNdxInType]->getSize(),	m_texturesCube[texNdxInType]->getSize()));	break;
785				default:
786					DE_ASSERT(DE_FALSE);
787			}
788		}
789
790		m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
791	}
792
793	// Render using GLES2.
794	{
795		sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
796
797		render(context);
798
799		context.readPixels(gles2Frame, 0, 0, viewportWidth, viewportHeight);
800	}
801
802	// Render reference image.
803	{
804		sglr::ReferenceContextBuffers	buffers	(tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight);
805		sglr::ReferenceContext			context	(sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
806
807		render(context);
808
809		context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
810	}
811
812	// Compare images.
813	const float		threshold	= 0.001f;
814	bool			isOk		= tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles2Frame, threshold, tcu::COMPARE_LOG_RESULT);
815
816	// Store test result.
817	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
818							isOk ? "Pass"				: "Image comparison failed");
819
820	return STOP;
821}
822
823void TextureUnitCase::render (sglr::Context& context)
824{
825	// Setup textures.
826
827	vector<deUint32>	textureGLNames;
828	vector<bool>		isTextureSetUp(m_numTextures, false); // \note Same texture may be bound to multiple units, but we only want to set up parameters and data once per texture.
829
830	textureGLNames.resize(m_numTextures);
831	context.genTextures(m_numTextures, &textureGLNames[0]);
832
833	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
834	{
835		int texNdx = m_unitTextures[unitNdx];
836
837		// Bind texture to unit.
838		context.activeTexture(GL_TEXTURE0 + unitNdx);
839		context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
840
841		if (!isTextureSetUp[texNdx])
842		{
843			// Binding this texture for first time, so set parameters and data.
844
845			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
846			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
847			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
848			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
849
850			if (m_textureTypes[texNdx] == GL_TEXTURE_2D)
851			{
852				int						ndx2d		= m_ndx2dOrCube[texNdx];
853				const tcu::Texture2D*	texture		= m_textures2d[ndx2d];
854				bool					mipmaps		= (deIsPowerOfTwo32(texture->getWidth()) && deIsPowerOfTwo32(texture->getHeight()));
855				int						numLevels	= mipmaps ? deLog2Floor32(de::max(texture->getWidth(), texture->getHeight()))+1 : 1;
856
857				context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
858
859				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
860				{
861					tcu::ConstPixelBufferAccess		access	= texture->getLevel(levelNdx);
862					int								width	= access.getWidth();
863					int								height	= access.getHeight();
864
865					DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
866
867					context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr());
868				}
869			}
870			else
871			{
872				DE_ASSERT(m_textureTypes[texNdx] == GL_TEXTURE_CUBE_MAP);
873
874				int							ndxCube		= m_ndx2dOrCube[texNdx];
875				const tcu::TextureCube*		texture		= m_texturesCube[ndxCube];
876				bool						mipmaps		= deIsPowerOfTwo32(texture->getSize()) != DE_FALSE;
877				int							numLevels	= mipmaps ? deLog2Floor32(texture->getSize())+1 : 1;
878
879				context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
880
881				for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
882				{
883					for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
884					{
885						tcu::ConstPixelBufferAccess		access	= texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
886						int								width	= access.getWidth();
887						int								height	= access.getHeight();
888
889						DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
890
891						context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr());
892					}
893				}
894			}
895
896			isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
897		}
898	}
899
900	GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
901
902	// Setup shader
903
904	deUint32 shaderID = context.createProgram(m_shader);
905
906	// Draw.
907
908	context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
909	context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
910	m_shader->setUniforms(context, shaderID);
911	sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
912	GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
913
914	// Delete previously generated texture names.
915
916	context.deleteTextures(m_numTextures, &textureGLNames[0]);
917	GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
918}
919
920TextureUnitTests::TextureUnitTests (Context& context)
921	: TestCaseGroup(context, "units", "Texture Unit Usage Tests")
922{
923}
924
925TextureUnitTests::~TextureUnitTests (void)
926{
927}
928
929void TextureUnitTests::init (void)
930{
931	const int numTestsPerGroup = 10;
932
933	static const int unitCounts[] =
934	{
935		2,
936		4,
937		8,
938		-1 // \note Negative stands for the implementation-specified maximum.
939	};
940
941	for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
942	{
943		int numUnits = unitCounts[unitCountNdx];
944
945		string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
946
947		tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
948		addChild(countGroup);
949
950		DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
951
952		for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
953		{
954			const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D	? "only_2d" :
955											(TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE	? "only_cube" :
956											(TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED		? "mixed" :
957																													  DE_NULL;
958			DE_ASSERT(caseTypeGroupName != DE_NULL);
959
960			tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
961			countGroup->addChild(caseTypeGroup);
962
963			for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
964				caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, (deUint32)deInt32Hash(testNdx)));
965		}
966	}
967}
968
969} // Functional
970} // gles2
971} // deqp
972