• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
44 using tcu::Vec2;
45 using tcu::Vec3;
46 using tcu::Vec4;
47 using tcu::IVec2;
48 using tcu::Mat3;
49 using std::vector;
50 using std::string;
51 using namespace glw; // GL types
52 
53 namespace deqp
54 {
55 
56 using namespace gls::TextureTestUtil;
57 
58 namespace gles2
59 {
60 namespace Functional
61 {
62 
63 static const int VIEWPORT_WIDTH			= 128;
64 static const int VIEWPORT_HEIGHT		= 128;
65 
66 static const int TEXTURE_WIDTH_2D		= 128;
67 static 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.
70 static const int TEXTURE_WIDTH_CUBE		= 256;
71 static const int TEXTURE_HEIGHT_CUBE	= 256;
72 
73 static const int GRID_CELL_SIZE			= 8;
74 
75 static const GLenum s_testFormats[] =
76 {
77 	GL_RGB,
78 	GL_RGBA,
79 	GL_ALPHA,
80 	GL_LUMINANCE,
81 	GL_LUMINANCE_ALPHA
82 };
83 
84 static 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 
92 static const GLenum s_testWrapModes[] =
93 {
94 	GL_CLAMP_TO_EDGE,
95 	GL_REPEAT,
96 	GL_MIRRORED_REPEAT,
97 };
98 
99 static 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 
109 static const GLenum s_testNonMipmapMinFilters[] =
110 {
111 	GL_NEAREST,
112 	GL_LINEAR
113 };
114 
115 static const GLenum s_testMagFilters[] =
116 {
117 	GL_NEAREST,
118 	GL_LINEAR
119 };
120 
121 static 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 
generateMultiTexFragmentShader(int numUnits,const GLenum * unitTypes)131 static 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 
generateShaderProgramDeclaration(int numUnits,const GLenum * unitTypes)173 static 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().
calculateLodDerivateParts(const Mat3 & transformation)206 static 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
calculateLodMax(const Vec4 & derivateParts,const tcu::IVec2 & textureSize,const Vec2 & screenDerivate)220 static 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
calculateLodMin(const Vec4 & derivateParts,const tcu::IVec2 & textureSize,const Vec2 & screenDerivate)231 static 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 
241 class MultiTexShader : public sglr::ShaderProgram
242 {
243 public:
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 
249 private:
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 
MultiTexShader(deUint32 randSeed,int numUnits,const vector<GLenum> & unitTypes)259 MultiTexShader::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 
setUniforms(sglr::Context & ctx,deUint32 program) const397 void 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 
makeSafeLods(const vector<IVec2> & textureSizes,const IVec2 & viewportSize)412 void 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 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const453 void 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 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const464 void 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 
531 class TextureUnitCase : public TestCase
532 {
533 public:
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 
549 private:
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 
TextureUnitCase(Context & context,const char * name,const char * desc,int numUnits,CaseType caseType,deUint32 randSeed)581 TextureUnitCase::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_numTextures		(0)
587 	, m_numUnits		(0)
588 	, m_shader			(DE_NULL)
589 {
590 }
591 
~TextureUnitCase(void)592 TextureUnitCase::~TextureUnitCase (void)
593 {
594 	TextureUnitCase::deinit();
595 }
596 
deinit(void)597 void TextureUnitCase::deinit (void)
598 {
599 	for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
600 		delete *i;
601 	m_textures2d.clear();
602 
603 	for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
604 		delete *i;
605 	m_texturesCube.clear();
606 
607 	delete m_shader;
608 	m_shader = DE_NULL;
609 }
610 
init(void)611 void TextureUnitCase::init (void)
612 {
613 	m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
614 
615 	// Make the textures.
616 
617 	try
618 	{
619 		tcu::TestLog&	log	= m_testCtx.getLog();
620 		de::Random		rnd	(m_randSeed);
621 
622 		if (rnd.getFloat() < 0.7f)
623 			m_numTextures = m_numUnits;											// In most cases use one unit per texture.
624 		else
625 			m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits);	// Sometimes assign same texture to multiple units.
626 
627 		log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage;
628 
629 		m_textureTypes.reserve(m_numTextures);
630 		m_textureParams.reserve(m_numTextures);
631 		m_ndx2dOrCube.reserve(m_numTextures);
632 
633 		// Generate textures.
634 
635 		for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
636 		{
637 			// Either fixed or randomized target types (2d or cube), and randomized parameters for every texture.
638 
639 			TextureParameters	params;
640 			bool				is2d		= m_caseType == CASE_ONLY_2D	? true :
641 											  m_caseType == CASE_ONLY_CUBE	? false :
642 																			  rnd.getBool();
643 
644 			GLenum				type		= is2d ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP;
645 			const int			texWidth	= is2d ? TEXTURE_WIDTH_2D : TEXTURE_WIDTH_CUBE;
646 			const int			texHeight	= is2d ? TEXTURE_HEIGHT_2D : TEXTURE_HEIGHT_CUBE;
647 			bool				mipmaps		= (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight));
648 			int					numLevels	= mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1;
649 
650 			params.wrapModeS	= s_testWrapModes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
651 			params.wrapModeT	= s_testWrapModes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
652 			params.magFilter	= s_testMagFilters	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)];
653 			params.dataType		= s_testDataTypes	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testDataTypes) - 1)];
654 
655 			// Certain minification filters are only used when using mipmaps.
656 			if (mipmaps)
657 				params.minFilter = s_testMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)];
658 			else
659 				params.minFilter = s_testNonMipmapMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)];
660 
661 			// Format may depend on data type.
662 			if (params.dataType == GL_UNSIGNED_SHORT_5_6_5)
663 				params.format = GL_RGB;
664 			else if (params.dataType == GL_UNSIGNED_SHORT_4_4_4_4 || params.dataType == GL_UNSIGNED_SHORT_5_5_5_1)
665 				params.format = GL_RGBA;
666 			else
667 				params.format = s_testFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testFormats) - 1)];
668 
669 			m_textureTypes.push_back(type);
670 			m_textureParams.push_back(params);
671 
672 			// Create new texture.
673 
674 			if (is2d)
675 			{
676 				m_ndx2dOrCube.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d array.
677 				m_textures2d.push_back(new tcu::Texture2D(glu::mapGLTransferFormat(params.format, params.dataType), texWidth, texHeight, isES2Context(m_context.getRenderContext().getType())));
678 			}
679 			else
680 			{
681 				m_ndx2dOrCube.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube array.
682 				DE_ASSERT(texWidth == texHeight);
683 				m_texturesCube.push_back(new tcu::TextureCube(glu::mapGLTransferFormat(params.format, params.dataType), texWidth));
684 			}
685 
686 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(is2d ? m_textures2d.back()->getFormat() : m_texturesCube.back()->getFormat());
687 			Vec4					cBias		= fmtInfo.valueMin;
688 			Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
689 
690 			// Fill with grid texture.
691 
692 			int numFaces = is2d ? 1 : (int)tcu::CUBEFACE_LAST;
693 
694 			for (int face = 0; face < numFaces; face++)
695 			{
696 				deUint32 rgb	= rnd.getUint32() & 0x00ffffff;
697 				deUint32 alpha0	= 0xff000000;
698 				deUint32 alpha1	= 0xff000000;
699 
700 				if (params.format == GL_ALPHA) // \note This needs alpha to be visible.
701 				{
702 					alpha0 &= rnd.getUint32();
703 					alpha1 = ~alpha0;
704 				}
705 
706 				deUint32 colorA = alpha0 | rgb;
707 				deUint32 colorB = alpha1 | ~rgb;
708 
709 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
710 				{
711 					if (is2d)
712 						m_textures2d.back()->allocLevel(levelNdx);
713 					else
714 						m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
715 
716 					int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
717 
718 					tcu::PixelBufferAccess access = is2d ? m_textures2d.back()->getLevel(levelNdx) : m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face);
719 					tcu::fillWithGrid(access, curCellSize, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
720 				}
721 			}
722 		}
723 
724 		// Assign a texture index to each unit.
725 
726 		m_unitTextures.reserve(m_numUnits);
727 
728 		// \note Every texture is used at least once.
729 		for (int i = 0; i < m_numTextures; i++)
730 			m_unitTextures.push_back(i);
731 
732 		// Assign a random texture to remaining units.
733 		while ((int)m_unitTextures.size() < m_numUnits)
734 			m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
735 
736 		rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
737 
738 		// Create shader.
739 
740 		vector<GLenum> unitTypes;
741 		unitTypes.reserve(m_numUnits);
742 		for (int i = 0; i < m_numUnits; i++)
743 			unitTypes.push_back(m_textureTypes[m_unitTextures[i]]);
744 
745 		DE_ASSERT(m_shader == DE_NULL);
746 		m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes);
747 	}
748 	catch (const std::exception&)
749 	{
750 		// Clean up to save memory.
751 		TextureUnitCase::deinit();
752 		throw;
753 	}
754 }
755 
iterate(void)756 TextureUnitCase::IterateResult TextureUnitCase::iterate (void)
757 {
758 	glu::RenderContext&			renderCtx			= m_context.getRenderContext();
759 	const tcu::RenderTarget&	renderTarget		= renderCtx.getRenderTarget();
760 	tcu::TestLog&				log					= m_testCtx.getLog();
761 	de::Random					rnd					(m_randSeed);
762 
763 	int							viewportWidth		= deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
764 	int							viewportHeight		= deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
765 	int							viewportX			= rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
766 	int							viewportY			= rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
767 
768 	tcu::Surface				gles2Frame			(viewportWidth, viewportHeight);
769 	tcu::Surface				refFrame			(viewportWidth, viewportHeight);
770 
771 	{
772 		// First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
773 
774 		vector<IVec2> texSizes;
775 		texSizes.reserve(m_numUnits);
776 
777 		for (int i = 0; i < m_numUnits; i++)
778 		{
779 			int		texNdx			= m_unitTextures[i];
780 			int		texNdxInType	= m_ndx2dOrCube[texNdx];
781 			GLenum	type			= m_textureTypes[texNdx];
782 
783 			switch (type)
784 			{
785 				case GL_TEXTURE_2D:			texSizes.push_back(IVec2(m_textures2d[texNdxInType]->getWidth(),	m_textures2d[texNdxInType]->getHeight()));	break;
786 				case GL_TEXTURE_CUBE_MAP:	texSizes.push_back(IVec2(m_texturesCube[texNdxInType]->getSize(),	m_texturesCube[texNdxInType]->getSize()));	break;
787 				default:
788 					DE_ASSERT(DE_FALSE);
789 			}
790 		}
791 
792 		m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
793 	}
794 
795 	// Render using GLES2.
796 	{
797 		sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
798 
799 		render(context);
800 
801 		context.readPixels(gles2Frame, 0, 0, viewportWidth, viewportHeight);
802 	}
803 
804 	// Render reference image.
805 	{
806 		sglr::ReferenceContextBuffers	buffers	(tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight);
807 		sglr::ReferenceContext			context	(sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
808 
809 		render(context);
810 
811 		context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
812 	}
813 
814 	// Compare images.
815 	const float		threshold	= 0.001f;
816 	bool			isOk		= tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles2Frame, threshold, tcu::COMPARE_LOG_RESULT);
817 
818 	// Store test result.
819 	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
820 							isOk ? "Pass"				: "Image comparison failed");
821 
822 	return STOP;
823 }
824 
render(sglr::Context & context)825 void TextureUnitCase::render (sglr::Context& context)
826 {
827 	// Setup textures.
828 
829 	vector<deUint32>	textureGLNames;
830 	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.
831 
832 	textureGLNames.resize(m_numTextures);
833 	context.genTextures(m_numTextures, &textureGLNames[0]);
834 
835 	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
836 	{
837 		int texNdx = m_unitTextures[unitNdx];
838 
839 		// Bind texture to unit.
840 		context.activeTexture(GL_TEXTURE0 + unitNdx);
841 		context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
842 
843 		if (!isTextureSetUp[texNdx])
844 		{
845 			// Binding this texture for first time, so set parameters and data.
846 
847 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
848 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
849 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
850 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
851 
852 			if (m_textureTypes[texNdx] == GL_TEXTURE_2D)
853 			{
854 				int						ndx2d		= m_ndx2dOrCube[texNdx];
855 				const tcu::Texture2D*	texture		= m_textures2d[ndx2d];
856 				bool					mipmaps		= (deIsPowerOfTwo32(texture->getWidth()) && deIsPowerOfTwo32(texture->getHeight()));
857 				int						numLevels	= mipmaps ? deLog2Floor32(de::max(texture->getWidth(), texture->getHeight()))+1 : 1;
858 
859 				context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
860 
861 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
862 				{
863 					tcu::ConstPixelBufferAccess		access	= texture->getLevel(levelNdx);
864 					int								width	= access.getWidth();
865 					int								height	= access.getHeight();
866 
867 					DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
868 
869 					context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr());
870 				}
871 			}
872 			else
873 			{
874 				DE_ASSERT(m_textureTypes[texNdx] == GL_TEXTURE_CUBE_MAP);
875 
876 				int							ndxCube		= m_ndx2dOrCube[texNdx];
877 				const tcu::TextureCube*		texture		= m_texturesCube[ndxCube];
878 				bool						mipmaps		= deIsPowerOfTwo32(texture->getSize()) != DE_FALSE;
879 				int							numLevels	= mipmaps ? deLog2Floor32(texture->getSize())+1 : 1;
880 
881 				context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
882 
883 				for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
884 				{
885 					for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
886 					{
887 						tcu::ConstPixelBufferAccess		access	= texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
888 						int								width	= access.getWidth();
889 						int								height	= access.getHeight();
890 
891 						DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
892 
893 						context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].format, width, height, 0, m_textureParams[texNdx].format, m_textureParams[texNdx].dataType, access.getDataPtr());
894 					}
895 				}
896 			}
897 
898 			isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
899 		}
900 	}
901 
902 	GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
903 
904 	// Setup shader
905 
906 	deUint32 shaderID = context.createProgram(m_shader);
907 
908 	// Draw.
909 
910 	context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
911 	context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
912 	m_shader->setUniforms(context, shaderID);
913 	sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
914 	GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
915 
916 	// Delete previously generated texture names.
917 
918 	context.deleteTextures(m_numTextures, &textureGLNames[0]);
919 	GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
920 }
921 
TextureUnitTests(Context & context)922 TextureUnitTests::TextureUnitTests (Context& context)
923 	: TestCaseGroup(context, "units", "Texture Unit Usage Tests")
924 {
925 }
926 
~TextureUnitTests(void)927 TextureUnitTests::~TextureUnitTests (void)
928 {
929 }
930 
init(void)931 void TextureUnitTests::init (void)
932 {
933 	const int numTestsPerGroup = 10;
934 
935 	static const int unitCounts[] =
936 	{
937 		2,
938 		4,
939 		8,
940 		-1 // \note Negative stands for the implementation-specified maximum.
941 	};
942 
943 	for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
944 	{
945 		int numUnits = unitCounts[unitCountNdx];
946 
947 		string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
948 
949 		tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
950 		addChild(countGroup);
951 
952 		DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
953 
954 		for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
955 		{
956 			const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D	? "only_2d" :
957 											(TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE	? "only_cube" :
958 											(TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED		? "mixed" :
959 																													  DE_NULL;
960 			DE_ASSERT(caseTypeGroupName != DE_NULL);
961 
962 			tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
963 			countGroup->addChild(caseTypeGroup);
964 
965 			for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
966 				caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, (deUint32)deInt32Hash(testNdx)));
967 		}
968 	}
969 }
970 
971 } // Functional
972 } // gles2
973 } // deqp
974