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 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 "es3fTextureUnitTests.hpp"
27 #include "glsTextureTestUtil.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluContextInfo.hpp"
30 #include "gluTextureUtil.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuImageCompare.hpp"
33 #include "tcuMatrix.hpp"
34 #include "tcuRenderTarget.hpp"
35 #include "sglrContextUtil.hpp"
36 #include "sglrReferenceContext.hpp"
37 #include "sglrGLContext.hpp"
38 #include "deRandom.hpp"
39 #include "deStringUtil.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::IVec3;
49 using tcu::Mat3;
50 using tcu::Mat4;
51 using std::vector;
52 using std::string;
53 using namespace glw; // GL types
54 
55 namespace deqp
56 {
57 
58 using namespace gls::TextureTestUtil;
59 
60 namespace gles3
61 {
62 namespace Functional
63 {
64 
65 static const int VIEWPORT_WIDTH				= 128;
66 static const int VIEWPORT_HEIGHT			= 128;
67 
68 static const int TEXTURE_WIDTH_2D			= 128;
69 static const int TEXTURE_HEIGHT_2D			= 128;
70 
71 // \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time.
72 static const int TEXTURE_WIDTH_CUBE			= 256;
73 static const int TEXTURE_HEIGHT_CUBE		= 256;
74 
75 static const int TEXTURE_WIDTH_2D_ARRAY		= 64;
76 static const int TEXTURE_HEIGHT_2D_ARRAY	= 64;
77 static const int TEXTURE_LAYERS_2D_ARRAY	= 4;
78 
79 static const int TEXTURE_WIDTH_3D			= 32;
80 static const int TEXTURE_HEIGHT_3D			= 32;
81 static const int TEXTURE_DEPTH_3D			= 32;
82 
83 static const int GRID_CELL_SIZE				= 8;
84 
85 static const GLenum s_testSizedInternalFormats[] =
86 {
87 	GL_RGBA32F,
88 	GL_RGBA32I,
89 	GL_RGBA32UI,
90 	GL_RGBA16F,
91 	GL_RGBA16I,
92 	GL_RGBA16UI,
93 	GL_RGBA8,
94 	GL_RGBA8I,
95 	GL_RGBA8UI,
96 	GL_SRGB8_ALPHA8,
97 	GL_RGB10_A2,
98 	GL_RGB10_A2UI,
99 	GL_RGBA4,
100 	GL_RGB5_A1,
101 	GL_RGBA8_SNORM,
102 	GL_RGB8,
103 	GL_RGB565,
104 	GL_R11F_G11F_B10F,
105 	GL_RGB32F,
106 	GL_RGB32I,
107 	GL_RGB32UI,
108 	GL_RGB16F,
109 	GL_RGB16I,
110 	GL_RGB16UI,
111 	GL_RGB8_SNORM,
112 	GL_RGB8I,
113 	GL_RGB8UI,
114 	GL_SRGB8,
115 	GL_RGB9_E5,
116 	GL_RG32F,
117 	GL_RG32I,
118 	GL_RG32UI,
119 	GL_RG16F,
120 	GL_RG16I,
121 	GL_RG16UI,
122 	GL_RG8,
123 	GL_RG8I,
124 	GL_RG8UI,
125 	GL_RG8_SNORM,
126 	GL_R32F,
127 	GL_R32I,
128 	GL_R32UI,
129 	GL_R16F,
130 	GL_R16I,
131 	GL_R16UI,
132 	GL_R8,
133 	GL_R8I,
134 	GL_R8UI,
135 	GL_R8_SNORM
136 };
137 
138 static const GLenum s_testWrapModes[] =
139 {
140 	GL_CLAMP_TO_EDGE,
141 	GL_REPEAT,
142 	GL_MIRRORED_REPEAT,
143 };
144 
145 static const GLenum s_testMinFilters[] =
146 {
147 	GL_NEAREST,
148 	GL_LINEAR,
149 	GL_NEAREST_MIPMAP_NEAREST,
150 	GL_LINEAR_MIPMAP_NEAREST,
151 	GL_NEAREST_MIPMAP_LINEAR,
152 	GL_LINEAR_MIPMAP_LINEAR
153 };
154 
155 static const GLenum s_testNonMipmapMinFilters[] =
156 {
157 	GL_NEAREST,
158 	GL_LINEAR
159 };
160 
161 static const GLenum s_testNearestMinFilters[] =
162 {
163 	GL_NEAREST,
164 	GL_NEAREST_MIPMAP_NEAREST
165 };
166 
167 static const GLenum s_testMagFilters[] =
168 {
169 	GL_NEAREST,
170 	GL_LINEAR
171 };
172 
173 static const GLenum s_cubeFaceTargets[] =
174 {
175 	GL_TEXTURE_CUBE_MAP_POSITIVE_X,
176 	GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
177 	GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
178 	GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
179 	GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
180 	GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
181 };
182 
183 // Extend a 3x3 transformation matrix to an equivalent 4x4 transformation matrix (i.e. 1.0 in right-down cell, 0.0's in other new cells).
matExtend3To4(const Mat3 & mat)184 static Mat4 matExtend3To4 (const Mat3& mat)
185 {
186 	Mat4 res;
187 	for (int rowNdx = 0; rowNdx < 3; rowNdx++)
188 	{
189 		Vec3 row = mat.getRow(rowNdx);
190 		res.setRow(rowNdx, Vec4(row.x(), row.y(), row.z(), 0.0f));
191 	}
192 	res.setRow(3, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
193 
194 	return res;
195 }
196 
generateMultiTexFragmentShader(int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes)197 static string generateMultiTexFragmentShader (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
198 {
199 	// The fragment shader calculates the average of a set of textures.
200 
201 	string samplersStr;
202 	string matricesStr;
203 	string scalesStr;
204 	string biasesStr;
205 	string lookupsStr;
206 
207 	string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)";
208 
209 	for (int ndx = 0; ndx < numUnits; ndx++)
210 	{
211 		string ndxStr				= de::toString(ndx);
212 		string samplerName			= "u_sampler" + ndxStr;
213 		string transformationName	= "u_trans" + ndxStr;
214 		string scaleName			= "u_texScale" + ndxStr;
215 		string biasName				= "u_texBias" + ndxStr;
216 
217 		samplersStr += string("") + "uniform highp " + glu::getDataTypeName(samplerTypes[ndx]) + " " + samplerName + ";\n";
218 		matricesStr += "uniform highp mat4 " + transformationName + ";\n";
219 		scalesStr += "uniform highp vec4 " + scaleName + ";\n";
220 		biasesStr += "uniform highp vec4 " + biasName + ";\n";
221 
222 		string lookupCoord = transformationName + "*vec4(v_coord, 1.0, 1.0)";
223 
224 		if (unitTypes[ndx] == GL_TEXTURE_2D)
225 			lookupCoord = "vec2(" + lookupCoord + ")";
226 		else
227 			lookupCoord = "vec3(" + lookupCoord + ")";
228 
229 		lookupsStr += "\tcolor += " + colorMultiplier + "*(vec4(texture(" + samplerName + ", " + lookupCoord + "))*" + scaleName + " + " + biasName + ");\n";
230 	}
231 
232 	return "#version 300 es\n"
233 		   "layout(location = 0) out mediump vec4 o_color;\n" +
234 		   samplersStr +
235 		   matricesStr +
236 		   scalesStr +
237 		   biasesStr +
238 		   "in highp vec2 v_coord;\n"
239 		   "\n"
240 		   "void main (void)\n"
241 		   "{\n"
242 		   "	mediump vec4 color = vec4(0.0);\n" +
243 		   lookupsStr +
244 		   "	o_color = color;\n"
245 		   "}\n";
246 }
247 
generateShaderProgramDeclaration(int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes)248 static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
249 {
250 	sglr::pdec::ShaderProgramDeclaration decl;
251 
252 	decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT);
253 	decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT);
254 	decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT);
255 	decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT);
256 
257 	for (int ndx = 0; ndx < numUnits; ++ndx)
258 	{
259 		string samplerName			= "u_sampler" + de::toString(ndx);
260 		string transformationName	= "u_trans" + de::toString(ndx);
261 		string scaleName			= "u_texScale" + de::toString(ndx);
262 		string biasName				= "u_texBias" + de::toString(ndx);
263 
264 		decl << sglr::pdec::Uniform(samplerName, samplerTypes[ndx]);
265 		decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT4);
266 		decl << sglr::pdec::Uniform(scaleName, glu::TYPE_FLOAT_VEC4);
267 		decl << sglr::pdec::Uniform(biasName, glu::TYPE_FLOAT_VEC4);
268 	}
269 
270 	decl << sglr::pdec::VertexSource("#version 300 es\n"
271 									 "in highp vec4 a_position;\n"
272 									 "in highp vec2 a_coord;\n"
273 									 "out highp vec2 v_coord;\n"
274 									 "\n"
275 									 "void main (void)\n"
276 									 "{\n"
277 									 "	gl_Position = a_position;\n"
278 									 "	v_coord = a_coord;\n"
279 									 "}\n");
280 	decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes, samplerTypes));
281 
282 	return decl;
283 }
284 
285 // Calculates values that will be used in calculateLod().
calculateLodDerivateParts(const Mat4 & transformation)286 static tcu::Vector<tcu::Vec2, 3> calculateLodDerivateParts (const Mat4& transformation)
287 {
288 	// Calculate transformed coordinates of three screen corners.
289 	Vec3 trans00 = (transformation * Vec4(0.0f, 0.0f, 1.0f, 1.0f)).xyz();
290 	Vec3 trans01 = (transformation * Vec4(0.0f, 1.0f, 1.0f, 1.0f)).xyz();
291 	Vec3 trans10 = (transformation * Vec4(1.0f, 0.0f, 1.0f, 1.0f)).xyz();
292 
293 	return tcu::Vector<tcu::Vec2, 3>(Vec2(trans10.x() - trans00.x(), trans01.x() - trans00.x()),
294 									 Vec2(trans10.y() - trans00.y(), trans01.y() - trans00.y()),
295 									 Vec2(trans10.z() - trans00.z(), trans01.z() - trans00.z()));
296 }
297 
298 // Calculates the maximum allowed lod from derivates
calculateLodMax(const tcu::Vector<tcu::Vec2,3> & derivateParts,const tcu::IVec3 & textureSize,const Vec2 & screenDerivate)299 static float calculateLodMax(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
300 {
301 	float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
302 	float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
303 	float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
304 	float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
305 	float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
306 	float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
307 
308 	const float mu = de::max(de::abs(dudx), de::abs(dudy));
309 	const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
310 	const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
311 	return deFloatLog2(mu + mv + mw);
312 }
313 
314 // Calculates the minimum allowed lod from derivates
calculateLodMin(const tcu::Vector<tcu::Vec2,3> & derivateParts,const tcu::IVec3 & textureSize,const Vec2 & screenDerivate)315 static float calculateLodMin(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
316 {
317 	float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
318 	float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
319 	float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
320 	float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
321 	float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
322 	float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
323 
324 	const float mu = de::max(de::abs(dudx), de::abs(dudy));
325 	const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
326 	const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
327 	return deFloatLog2(de::max(mu, de::max(mv, mw)));
328 }
329 
330 class MultiTexShader : public sglr::ShaderProgram
331 {
332 public:
333 							MultiTexShader	(deUint32 randSeed,
334 											 int numUnits,
335 											 const vector<GLenum>& unitTypes,
336 											 const vector<glu::DataType>& samplerTypes,
337 											 const vector<Vec4>& texScales,
338 											 const vector<Vec4>& texBiases,
339 											 const vector<int>& num2dArrayLayers); // \note 2d array layer "coordinate" isn't normalized, so this is needed here.
340 
341 	void					setUniforms		(sglr::Context& context, deUint32 program) const;
342 	void					makeSafeLods	(const vector<IVec3>& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 .
343 
344 private:
345 	void					shadeVertices	(const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const;
346 	void					shadeFragments	(rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const;
347 
348 	int									m_numUnits;
349 	vector<GLenum>						m_unitTypes;		// 2d, cube map, 2d array or 3d.
350 	vector<Vec4>						m_texScales;
351 	vector<Vec4>						m_texBiases;
352 	vector<Mat4>						m_transformations;
353 	vector<tcu::Vector<tcu::Vec2, 3> >	m_lodDerivateParts;	// Parts of lod derivates; computed in init(), used in eval().
354 };
355 
MultiTexShader(deUint32 randSeed,int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes,const vector<Vec4> & texScales,const vector<Vec4> & texBiases,const vector<int> & num2dArrayLayers)356 MultiTexShader::MultiTexShader (deUint32 randSeed,
357 								int numUnits,
358 								const vector<GLenum>& unitTypes,
359 								const vector<glu::DataType>& samplerTypes,
360 								const vector<Vec4>& texScales,
361 								const vector<Vec4>& texBiases,
362 								const vector<int>& num2dArrayLayers)
363 		: sglr::ShaderProgram	(generateShaderProgramDeclaration(numUnits, unitTypes, samplerTypes))
364 		, m_numUnits		(numUnits)
365 		, m_unitTypes		(unitTypes)
366 		, m_texScales		(texScales)
367 		, m_texBiases		(texBiases)
368 {
369 	// 2d-to-cube-face transformations.
370 	// \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well.
371 	static const float s_cubeTransforms[][3*3] =
372 	{
373 		// Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1))
374 		{  0.0f,  0.0f, -1.0f,
375 		   0.0f, -2.0f,  1.0f,
376 		   2.0f,  0.0f, -1.0f },
377 		// Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1))
378 		{  0.0f,  0.0f,  1.0f,
379 		   0.0f, -2.0f,  1.0f,
380 		  -2.0f,  0.0f,  1.0f },
381 		// Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1))
382 		{  2.0f,  0.0f, -1.0f,
383 		   0.0f,  0.0f, -1.0f,
384 		   0.0f, -2.0f,  1.0f },
385 		// Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1))
386 		{  2.0f,  0.0f, -1.0f,
387 		   0.0f,  0.0f,  1.0f,
388 		   0.0f,  2.0f, -1.0f },
389 		// Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1)
390 		{ -2.0f,  0.0f,  1.0f,
391 		   0.0f, -2.0f,  1.0f,
392 		   0.0f,  0.0f, -1.0f },
393 		// Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1)
394 		{  2.0f,  0.0f, -1.0f,
395 		   0.0f, -2.0f,  1.0f,
396 		   0.0f,  0.0f,  1.0f }
397 	};
398 
399 	// Generate transformation matrices.
400 
401 	de::Random rnd(randSeed);
402 
403 	m_transformations.reserve(m_numUnits);
404 	m_lodDerivateParts.reserve(m_numUnits);
405 
406 	int tex2dArrayNdx = 0; // Keep track of 2d texture array index.
407 
408 	DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
409 
410 	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
411 	{
412 		if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
413 		{
414 			float rotAngle				= rnd.getFloat(0.0f, 2.0f*DE_PI);
415 			float xScaleFactor			= rnd.getFloat(0.7f, 1.5f);
416 			float yScaleFactor			= rnd.getFloat(0.7f, 1.5f);
417 			float xShearAmount			= rnd.getFloat(0.0f, 0.5f);
418 			float yShearAmount			= rnd.getFloat(0.0f, 0.5f);
419 			float xTranslationAmount	= rnd.getFloat(-0.5f, 0.5f);
420 			float yTranslationAmount	= rnd.getFloat(-0.5f, 0.5f);
421 
422 			static const float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations.
423 			{
424 				1.0f,  0.0f, -0.5f,
425 				0.0f,  1.0f, -0.5f,
426 				0.0f,  0.0f,  1.0f
427 			};
428 			float rotTransfData[3*3] =
429 			{
430 				deFloatCos(rotAngle),	-deFloatSin(rotAngle),	0.0f,
431 				deFloatSin(rotAngle),	deFloatCos(rotAngle),	0.0f,
432 				0.0f,					0.0f,					1.0f
433 			};
434 			float scaleTransfData[3*3] =
435 			{
436 				xScaleFactor,	0.0f,			0.0f,
437 				0.0f,			yScaleFactor,	0.0f,
438 				0.0f,			0.0f,			1.0f
439 			};
440 			float xShearTransfData[3*3] =
441 			{
442 				1.0f,			xShearAmount,	0.0f,
443 				0.0f,			1.0f,			0.0f,
444 				0.0f,			0.0f,			1.0f
445 			};
446 			float yShearTransfData[3*3] =
447 			{
448 				1.0f,			0.0f,			0.0f,
449 				yShearAmount,	1.0f,			0.0f,
450 				0.0f,			0.0f,			1.0f
451 			};
452 			float translationTransfData[3*3] =
453 			{
454 				1.0f,	0.0f,	xTranslationAmount,
455 				0.0f,	1.0f,	yTranslationAmount,
456 				0.0f,	0.0f,	1.0f
457 			};
458 
459 			Mat4 transformation = matExtend3To4(Mat3(tempOffsetData) *
460 												Mat3(translationTransfData) *
461 												Mat3(rotTransfData) *
462 												Mat3(scaleTransfData) *
463 												Mat3(xShearTransfData) *
464 												Mat3(yShearTransfData) *
465 												(Mat3(tempOffsetData) * (-1.0f)));
466 
467 			m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
468 			m_transformations.push_back(transformation);
469 		}
470 		else if (m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP)
471 		{
472 			DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms));
473 
474 			float planarTransData[3*3];
475 
476 			// 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.
477 
478 			for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++)
479 			{
480 				if (i == 0 || i == 4)
481 					planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling.
482 				else if (i == 8)
483 					planarTransData[i] = 1.0f;
484 				else
485 					planarTransData[i] = 0.0f;
486 			}
487 
488 			int		faceNdx			= rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1);
489 			Mat3	planarTrans		(planarTransData);												// Planar, face-agnostic transformation.
490 			Mat4	finalTrans		= matExtend3To4(Mat3(s_cubeTransforms[faceNdx]) * planarTrans);	// Final transformation from planar to cube map coordinates, including the transformation just generated.
491 			Mat4	planarTrans4x4	= matExtend3To4(planarTrans);
492 
493 			m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans4x4));
494 			m_transformations.push_back(finalTrans);
495 		}
496 		else
497 		{
498 			DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_3D || m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY);
499 
500 			float transData[4*4];
501 
502 			for (int i = 0; i < 4*4; i++)
503 			{
504 				float sign = rnd.getBool() ? 1.0f : -1.0f;
505 				transData[i] = rnd.getFloat(0.7f, 1.4f) * sign;
506 			}
507 
508 			Mat4 transformation(transData);
509 
510 			if (m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY)
511 			{
512 				// Z direction: Translate by 0.5 and scale by layer amount.
513 
514 				float numLayers = (float)num2dArrayLayers[tex2dArrayNdx];
515 
516 				static const float zTranslationTransfData[4*4] =
517 				{
518 					1.0f, 0.0f, 0.0f, 0.0f,
519 					0.0f, 1.0f, 0.0f, 0.0f,
520 					0.0f, 0.0f, 1.0f, 0.5f,
521 					0.0f, 0.0f, 0.0f, 1.0f
522 				};
523 
524 				float zScaleTransfData[4*4] =
525 				{
526 					1.0f,		0.0f,		0.0f,		0.0f,
527 					0.0f,		1.0f,		0.0f,		0.0f,
528 					0.0f,		0.0f,		numLayers,	0.0f,
529 					0.0f,		0.0f,		0.0f,		1.0f
530 				};
531 
532 				transformation = transformation * Mat4(zScaleTransfData) * Mat4(zTranslationTransfData);
533 
534 				tex2dArrayNdx++;
535 			}
536 
537 			m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
538 			m_transformations.push_back(Mat4(transformation));
539 		}
540 	}
541 }
542 
setUniforms(sglr::Context & ctx,deUint32 program) const543 void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const
544 {
545 	ctx.useProgram(program);
546 
547 	// Sampler and matrix uniforms.
548 
549 	for (int ndx = 0; ndx < m_numUnits; ndx++)
550 	{
551 		string			ndxStr		= de::toString(ndx);
552 
553 		ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx);
554 		ctx.uniformMatrix4fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]);
555 		ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texScale" + ndxStr).c_str()), 1, m_texScales[ndx].getPtr());
556 		ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texBias" + ndxStr).c_str()), 1, m_texBiases[ndx].getPtr());
557 	}
558 }
559 
makeSafeLods(const vector<IVec3> & textureSizes,const IVec2 & viewportSize)560 void MultiTexShader::makeSafeLods (const vector<IVec3>& textureSizes, const IVec2& viewportSize)
561 {
562 	DE_ASSERT((int)textureSizes.size() == m_numUnits);
563 
564 	static const float shrinkScaleMat2dData[3*3] =
565 	{
566 		0.95f,	0.0f,	0.0f,
567 		0.0f,	0.95f,	0.0f,
568 		0.0f,	0.0f,	1.0f
569 	};
570 	static const float shrinkScaleMat3dData[3*3] =
571 	{
572 		0.95f,	0.0f,	0.0f,
573 		0.0f,	0.95f,	0.0f,
574 		0.0f,	0.0f,	0.95f
575 	};
576 	Mat4 shrinkScaleMat2d = matExtend3To4(Mat3(shrinkScaleMat2dData));
577 	Mat4 shrinkScaleMat3d = matExtend3To4(Mat3(shrinkScaleMat3dData));
578 
579 	Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y());
580 
581 	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
582 	{
583 		// 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.
584 		for (;;)
585 		{
586 			const float threshold = 0.1f;
587 			const float epsilon	= 0.01f;
588 
589 			const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
590 			const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
591 
592 			const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1);
593 			const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1);
594 
595 			if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) ||
596 				de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) ||
597 				maxLevel != minLevel)
598 			{
599 				m_transformations[unitNdx] = (m_unitTypes[unitNdx] == GL_TEXTURE_3D ? shrinkScaleMat3d : shrinkScaleMat2d) * m_transformations[unitNdx];
600 				m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]);
601 			}
602 			else
603 				break;
604 		}
605 	}
606 }
607 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const608 void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
609 {
610 	for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
611 	{
612 		rr::VertexPacket& packet = *(packets[packetNdx]);
613 
614 		packet.position		= rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
615 		packet.outputs[0]	= rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
616 	}
617 }
618 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const619 void MultiTexShader::shadeFragments	(rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
620 {
621 	DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
622 	DE_ASSERT((int)m_transformations.size() == m_numUnits);
623 	DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits);
624 
625 	for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
626 	{
627 		rr::FragmentPacket& packet				= packets[packetNdx];
628 		const float			colorMultiplier		= 1.0f / (float)m_numUnits;
629 		Vec4				outColors[4]		= { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) };
630 
631 		for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
632 		{
633 			tcu::Vec4 texSamples[4];
634 
635 			// Read tex coords
636 			const tcu::Vec2 texCoords[4] =
637 			{
638 				rr::readTriangleVarying<float>(packet, context, 0, 0).xy(),
639 				rr::readTriangleVarying<float>(packet, context, 0, 1).xy(),
640 				rr::readTriangleVarying<float>(packet, context, 0, 2).xy(),
641 				rr::readTriangleVarying<float>(packet, context, 0, 3).xy(),
642 			};
643 
644 			// Transform
645 			tcu::Vec3 coords3D[4] =
646 			{
647 				(m_transformations[unitNdx] * Vec4(texCoords[0].x(), texCoords[0].y(), 1.0f, 1.0f)).xyz(),
648 				(m_transformations[unitNdx] * Vec4(texCoords[1].x(), texCoords[1].y(), 1.0f, 1.0f)).xyz(),
649 				(m_transformations[unitNdx] * Vec4(texCoords[2].x(), texCoords[2].y(), 1.0f, 1.0f)).xyz(),
650 				(m_transformations[unitNdx] * Vec4(texCoords[3].x(), texCoords[3].y(), 1.0f, 1.0f)).xyz(),
651 			};
652 
653 			// To 2D
654 			const tcu::Vec2 coords2D[4] =
655 			{
656 				coords3D[0].xy(),
657 				coords3D[1].xy(),
658 				coords3D[2].xy(),
659 				coords3D[3].xy(),
660 			};
661 
662 			// Sample
663 			switch (m_unitTypes[unitNdx])
664 			{
665 				case GL_TEXTURE_2D:			m_uniforms[4*unitNdx].sampler.tex2D->sample4(texSamples, coords2D);			break;
666 				case GL_TEXTURE_CUBE_MAP:	m_uniforms[4*unitNdx].sampler.texCube->sample4(texSamples, coords3D);		break;
667 				case GL_TEXTURE_2D_ARRAY:	m_uniforms[4*unitNdx].sampler.tex2DArray->sample4(texSamples, coords3D);	break;
668 				case GL_TEXTURE_3D:			m_uniforms[4*unitNdx].sampler.tex3D->sample4(texSamples, coords3D);			break;
669 				default:
670 					DE_ASSERT(DE_FALSE);
671 			}
672 
673 			// Add to sum
674 			for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
675 				outColors[fragNdx] += colorMultiplier * (texSamples[fragNdx]*m_texScales[unitNdx] + m_texBiases[unitNdx]);
676 		}
677 
678 		// output
679 		for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
680 			rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]);
681 	}
682 }
683 
684 class TextureUnitCase : public TestCase
685 {
686 public:
687 	enum CaseType
688 	{
689 		CASE_ONLY_2D = 0,
690 		CASE_ONLY_CUBE,
691 		CASE_ONLY_2D_ARRAY,
692 		CASE_ONLY_3D,
693 		CASE_MIXED,
694 
695 		CASE_LAST
696 	};
697 								TextureUnitCase		(Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed);
698 								~TextureUnitCase	(void);
699 
700 	void						init				(void);
701 	void						deinit				(void);
702 	IterateResult				iterate				(void);
703 
704 private:
705 	struct TextureParameters
706 	{
707 		GLenum internalFormat;
708 		GLenum wrapModeS;
709 		GLenum wrapModeT;
710 		GLenum wrapModeR;
711 		GLenum minFilter;
712 		GLenum magFilter;
713 	};
714 
715 									TextureUnitCase			(const TextureUnitCase& other);
716 	TextureUnitCase&				operator=				(const TextureUnitCase& other);
717 
718 	void							upload2dTexture			(int texNdx, sglr::Context& context);
719 	void							uploadCubeTexture		(int texNdx, sglr::Context& context);
720 	void							upload2dArrayTexture	(int texNdx, sglr::Context& context);
721 	void							upload3dTexture			(int texNdx, sglr::Context& context);
722 
723 	void							render					(sglr::Context& context);
724 
725 	const int						m_numUnitsParam;
726 	const CaseType					m_caseType;
727 	const deUint32					m_randSeed;
728 
729 	int								m_numTextures;	//!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units.
730 	int								m_numUnits;		//!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum
731 
732 	vector<GLenum>					m_textureTypes;
733 	vector<TextureParameters>		m_textureParams;
734 	vector<tcu::Texture2D*>			m_textures2d;
735 	vector<tcu::TextureCube*>		m_texturesCube;
736 	vector<tcu::Texture2DArray*>	m_textures2dArray;
737 	vector<tcu::Texture3D*>			m_textures3d;
738 	vector<int>						m_unitTextures;	//!< Which texture is used in a particular unit.
739 	vector<int>						m_ndxTexType;	//!< Index of a texture in m_textures2d, m_texturesCube, m_textures2dArray or m_textures3d, depending on texture type.
740 	MultiTexShader*					m_shader;
741 };
742 
TextureUnitCase(Context & context,const char * name,const char * desc,int numUnits,CaseType caseType,deUint32 randSeed)743 TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed)
744 	: TestCase			(context, tcu::NODETYPE_SELF_VALIDATE, name, desc)
745 	, m_numUnitsParam	(numUnits)
746 	, m_caseType		(caseType)
747 	, m_randSeed		(randSeed)
748 	, m_shader			(DE_NULL)
749 {
750 }
751 
~TextureUnitCase(void)752 TextureUnitCase::~TextureUnitCase (void)
753 {
754 	TextureUnitCase::deinit();
755 }
756 
deinit(void)757 void TextureUnitCase::deinit (void)
758 {
759 	for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
760 		delete *i;
761 	m_textures2d.clear();
762 
763 	for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
764 		delete *i;
765 	m_texturesCube.clear();
766 
767 	for (vector<tcu::Texture2DArray*>::iterator i = m_textures2dArray.begin(); i != m_textures2dArray.end(); i++)
768 		delete *i;
769 	m_textures2dArray.clear();
770 
771 	for (vector<tcu::Texture3D*>::iterator i = m_textures3d.begin(); i != m_textures3d.end(); i++)
772 		delete *i;
773 	m_textures3d.clear();
774 
775 	delete m_shader;
776 	m_shader = DE_NULL;
777 }
778 
init(void)779 void TextureUnitCase::init (void)
780 {
781 	m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
782 
783 	// Make the textures.
784 
785 	try
786 	{
787 		tcu::TestLog&	log	= m_testCtx.getLog();
788 		de::Random		rnd	(m_randSeed);
789 
790 		if (rnd.getFloat() < 0.7f)
791 			m_numTextures = m_numUnits;											// In most cases use one unit per texture.
792 		else
793 			m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits);	// Sometimes assign same texture to multiple units.
794 
795 		log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage;
796 
797 		m_textureTypes.reserve(m_numTextures);
798 		m_textureParams.reserve(m_numTextures);
799 		m_ndxTexType.reserve(m_numTextures);
800 
801 		// Generate textures.
802 
803 		for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
804 		{
805 			// Either fixed or randomized target types, and randomized parameters for every texture.
806 
807 			TextureParameters	params;
808 
809 			DE_STATIC_ASSERT(CASE_ONLY_2D == 0 && CASE_MIXED + 1 == CASE_LAST);
810 
811 			int						texType			= m_caseType == CASE_MIXED ? rnd.getInt(0, (int)CASE_MIXED - 1) : (int)m_caseType;
812 			bool					is2dTex			= texType == 0;
813 			bool					isCubeTex		= texType == 1;
814 			bool					is2dArrayTex	= texType == 2;
815 			bool					is3dTex			= texType == 3;
816 
817 			DE_ASSERT(is2dTex || isCubeTex || is2dArrayTex || is3dTex);
818 
819 			GLenum					type			= is2dTex		? GL_TEXTURE_2D		: isCubeTex ? GL_TEXTURE_CUBE_MAP	: is2dArrayTex ? GL_TEXTURE_2D_ARRAY		: GL_TEXTURE_3D;
820 			const int				texWidth		= is2dTex		? TEXTURE_WIDTH_2D	: isCubeTex ? TEXTURE_WIDTH_CUBE	: is2dArrayTex ? TEXTURE_WIDTH_2D_ARRAY		: TEXTURE_WIDTH_3D;
821 			const int				texHeight		= is2dTex		? TEXTURE_HEIGHT_2D	: isCubeTex ? TEXTURE_HEIGHT_CUBE	: is2dArrayTex ? TEXTURE_HEIGHT_2D_ARRAY	: TEXTURE_HEIGHT_3D;
822 
823 			const int				texDepth		= is3dTex ? TEXTURE_DEPTH_3D : 1;
824 			const int				texLayers		= is2dArrayTex ? TEXTURE_LAYERS_2D_ARRAY : 1;
825 
826 			bool					mipmaps			= (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight) && deIsPowerOfTwo32(texDepth));
827 			int						numLevels		= mipmaps ? deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth))+1 : 1;
828 
829 			params.internalFormat = s_testSizedInternalFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testSizedInternalFormats) - 1)];
830 
831 			bool					isFilterable	= glu::isGLInternalColorFormatFilterable(params.internalFormat);
832 
833 			params.wrapModeS = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
834 			params.wrapModeT = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
835 			params.wrapModeR = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
836 
837 			params.magFilter = isFilterable ? s_testMagFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)] : GL_NEAREST;
838 
839 			if (mipmaps)
840 				params.minFilter = isFilterable ?
841 					s_testMinFilters			[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)] :
842 					s_testNearestMinFilters		[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNearestMinFilters) - 1)];
843 			else
844 				params.minFilter = isFilterable ?
845 					s_testNonMipmapMinFilters	[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)] :
846 					GL_NEAREST;
847 
848 			m_textureTypes.push_back(type);
849 			m_textureParams.push_back(params);
850 
851 			// Create new texture.
852 
853 			tcu::TextureFormat texFormat = glu::mapGLInternalFormat((deUint32)params.internalFormat);
854 
855 			if (is2dTex)
856 			{
857 				m_ndxTexType.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d texture vector.
858 				m_textures2d.push_back(new tcu::Texture2D(texFormat, texWidth, texHeight));
859 			}
860 			else if (isCubeTex)
861 			{
862 				m_ndxTexType.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube texture vector.
863 				DE_ASSERT(texWidth == texHeight);
864 				m_texturesCube.push_back(new tcu::TextureCube(texFormat, texWidth));
865 			}
866 			else if (is2dArrayTex)
867 			{
868 				m_ndxTexType.push_back((int)m_textures2dArray.size()); // Remember the index this texture has in the 2d array texture vector.
869 				m_textures2dArray.push_back(new tcu::Texture2DArray(texFormat, texWidth, texHeight, texLayers));
870 			}
871 			else
872 			{
873 				m_ndxTexType.push_back((int)m_textures3d.size()); // Remember the index this texture has in the 3d vector.
874 				m_textures3d.push_back(new tcu::Texture3D(texFormat, texWidth, texHeight, texDepth));
875 			}
876 
877 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(texFormat);
878 			Vec4					cBias		= fmtInfo.valueMin;
879 			Vec4					cScale		= fmtInfo.valueMax-fmtInfo.valueMin;
880 
881 			// Fill with grid texture.
882 
883 			int numFaces = isCubeTex ? (int)tcu::CUBEFACE_LAST : 1;
884 
885 			for (int face = 0; face < numFaces; face++)
886 			{
887 				deUint32 rgb	= rnd.getUint32() & 0x00ffffff;
888 				deUint32 alpha	= 0xff000000;
889 
890 				deUint32 colorA = alpha | rgb;
891 				deUint32 colorB = alpha | ((~rgb) & 0x00ffffff);
892 
893 				for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
894 				{
895 					if (is2dTex)
896 						m_textures2d.back()->allocLevel(levelNdx);
897 					else if (isCubeTex)
898 						m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
899 					else if (is2dArrayTex)
900 						m_textures2dArray.back()->allocLevel(levelNdx);
901 					else
902 						m_textures3d.back()->allocLevel(levelNdx);
903 
904 					int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
905 
906 					tcu::PixelBufferAccess access = is2dTex			? m_textures2d.back()->getLevel(levelNdx)
907 												  : isCubeTex		? m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face)
908 												  : is2dArrayTex	? m_textures2dArray.back()->getLevel(levelNdx)
909 												  :					  m_textures3d.back()->getLevel(levelNdx);
910 
911 					tcu::fillWithGrid(access, curCellSize, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
912 				}
913 			}
914 		}
915 
916 		// Assign a texture index to each unit.
917 
918 		m_unitTextures.reserve(m_numUnits);
919 
920 		// \note Every texture is used at least once.
921 		for (int i = 0; i < m_numTextures; i++)
922 			m_unitTextures.push_back(i);
923 
924 		// Assign a random texture to remaining units.
925 		while ((int)m_unitTextures.size() < m_numUnits)
926 			m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
927 
928 		rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
929 
930 		// Generate information for shader.
931 
932 		vector<GLenum>			unitTypes;
933 		vector<Vec4>			texScales;
934 		vector<Vec4>			texBiases;
935 		vector<glu::DataType>	samplerTypes;
936 		vector<int>				num2dArrayLayers;
937 
938 		unitTypes.reserve(m_numUnits);
939 		texScales.reserve(m_numUnits);
940 		texBiases.reserve(m_numUnits);
941 		samplerTypes.reserve(m_numUnits);
942 		num2dArrayLayers.reserve(m_numUnits);
943 
944 		for (int i = 0; i < m_numUnits; i++)
945 		{
946 			int						texNdx		= m_unitTextures[i];
947 			GLenum					type		= m_textureTypes[texNdx];
948 			tcu::TextureFormat		fmt			= glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat);
949 			tcu::TextureFormatInfo	fmtInfo		= tcu::getTextureFormatInfo(fmt);
950 
951 			unitTypes.push_back(type);
952 
953 			if (type == GL_TEXTURE_2D_ARRAY)
954 				num2dArrayLayers.push_back(m_textures2dArray[m_ndxTexType[texNdx]]->getNumLayers());
955 
956 			texScales.push_back(fmtInfo.lookupScale);
957 			texBiases.push_back(fmtInfo.lookupBias);
958 
959 			switch (type)
960 			{
961 				case GL_TEXTURE_2D:			samplerTypes.push_back(glu::getSampler2DType(fmt));			break;
962 				case GL_TEXTURE_CUBE_MAP:	samplerTypes.push_back(glu::getSamplerCubeType(fmt));		break;
963 				case GL_TEXTURE_2D_ARRAY:	samplerTypes.push_back(glu::getSampler2DArrayType(fmt));	break;
964 				case GL_TEXTURE_3D:			samplerTypes.push_back(glu::getSampler3DType(fmt));			break;
965 				default:
966 					DE_ASSERT(DE_FALSE);
967 			}
968 		}
969 
970 		// Create shader.
971 
972 		DE_ASSERT(m_shader == DE_NULL);
973 		m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes, samplerTypes, texScales, texBiases, num2dArrayLayers);
974 	}
975 	catch (const std::exception&)
976 	{
977 		// Clean up to save memory.
978 		TextureUnitCase::deinit();
979 		throw;
980 	}
981 }
982 
iterate(void)983 TextureUnitCase::IterateResult TextureUnitCase::iterate (void)
984 {
985 	glu::RenderContext&			renderCtx			= m_context.getRenderContext();
986 	const tcu::RenderTarget&	renderTarget		= renderCtx.getRenderTarget();
987 	tcu::TestLog&				log					= m_testCtx.getLog();
988 	de::Random					rnd					(m_randSeed);
989 
990 	int							viewportWidth		= deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
991 	int							viewportHeight		= deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
992 	int							viewportX			= rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
993 	int							viewportY			= rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
994 
995 	tcu::Surface				gles3Frame			(viewportWidth, viewportHeight);
996 	tcu::Surface				refFrame			(viewportWidth, viewportHeight);
997 
998 	{
999 		// First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
1000 
1001 		vector<IVec3> texSizes;
1002 		texSizes.reserve(m_numUnits);
1003 
1004 		for (int i = 0; i < m_numUnits; i++)
1005 		{
1006 			int		texNdx			= m_unitTextures[i];
1007 			int		texNdxInType	= m_ndxTexType[texNdx];
1008 			GLenum	type			= m_textureTypes[texNdx];
1009 
1010 			switch (type)
1011 			{
1012 				case GL_TEXTURE_2D:			texSizes.push_back(IVec3(m_textures2d[texNdxInType]->getWidth(),		m_textures2d[texNdxInType]->getHeight(),		0));										break;
1013 				case GL_TEXTURE_CUBE_MAP:	texSizes.push_back(IVec3(m_texturesCube[texNdxInType]->getSize(),		m_texturesCube[texNdxInType]->getSize(),		0));										break;
1014 				case GL_TEXTURE_2D_ARRAY:	texSizes.push_back(IVec3(m_textures2dArray[texNdxInType]->getWidth(),	m_textures2dArray[texNdxInType]->getHeight(),	0));										break;
1015 				case GL_TEXTURE_3D:			texSizes.push_back(IVec3(m_textures3d[texNdxInType]->getWidth(),		m_textures3d[texNdxInType]->getHeight(),		m_textures3d[texNdxInType]->getDepth()));	break;
1016 				default:
1017 					DE_ASSERT(DE_FALSE);
1018 			}
1019 		}
1020 
1021 		m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
1022 	}
1023 
1024 	// Render using GLES3.
1025 	{
1026 		sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
1027 
1028 		render(context);
1029 
1030 		context.readPixels(gles3Frame, 0, 0, viewportWidth, viewportHeight);
1031 	}
1032 
1033 	// Render reference image.
1034 	{
1035 		sglr::ReferenceContextBuffers	buffers	(tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight);
1036 		sglr::ReferenceContext			context	(sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
1037 
1038 		render(context);
1039 
1040 		context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
1041 	}
1042 
1043 	// Compare images.
1044 	const float		threshold	= 0.001f;
1045 	bool			isOk		= tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles3Frame, threshold, tcu::COMPARE_LOG_RESULT);
1046 
1047 	// Store test result.
1048 	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
1049 							isOk ? "Pass"				: "Image comparison failed");
1050 
1051 	return STOP;
1052 }
1053 
upload2dTexture(int texNdx,sglr::Context & context)1054 void TextureUnitCase::upload2dTexture (int texNdx, sglr::Context& context)
1055 {
1056 	int						ndx2d		= m_ndxTexType[texNdx];
1057 	const tcu::Texture2D*	texture		= m_textures2d[ndx2d];
1058 	glu::TransferFormat		formatGl	= glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1059 
1060 	context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1061 
1062 	for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1063 	{
1064 		if (texture->isLevelEmpty(levelNdx))
1065 			continue;
1066 
1067 		tcu::ConstPixelBufferAccess		access	= texture->getLevel(levelNdx);
1068 		int								width	= access.getWidth();
1069 		int								height	= access.getHeight();
1070 
1071 		DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1072 
1073 		context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1074 		GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d texture image data");
1075 	}
1076 }
1077 
uploadCubeTexture(int texNdx,sglr::Context & context)1078 void TextureUnitCase::uploadCubeTexture (int texNdx, sglr::Context& context)
1079 {
1080 	int							ndxCube		= m_ndxTexType[texNdx];
1081 	const tcu::TextureCube*		texture		= m_texturesCube[ndxCube];
1082 	glu::TransferFormat			formatGl	= glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1083 
1084 	context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1085 
1086 	for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
1087 	{
1088 		for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1089 		{
1090 			if (texture->isLevelEmpty((tcu::CubeFace)face, levelNdx))
1091 				continue;
1092 
1093 			tcu::ConstPixelBufferAccess		access	= texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
1094 			int								width	= access.getWidth();
1095 			int								height	= access.getHeight();
1096 
1097 			DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1098 
1099 			context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1100 			GLU_EXPECT_NO_ERROR(context.getError(), "Set cube map image data");
1101 		}
1102 	}
1103 }
1104 
upload2dArrayTexture(int texNdx,sglr::Context & context)1105 void TextureUnitCase::upload2dArrayTexture (int texNdx, sglr::Context& context)
1106 {
1107 	int							ndx2dArray	= m_ndxTexType[texNdx];
1108 	const tcu::Texture2DArray*	texture		= m_textures2dArray[ndx2dArray];
1109 	glu::TransferFormat			formatGl	= glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1110 
1111 	context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1112 
1113 	for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1114 	{
1115 		if (texture->isLevelEmpty(levelNdx))
1116 			continue;
1117 
1118 		tcu::ConstPixelBufferAccess	access	= texture->getLevel(levelNdx);
1119 		int							width	= access.getWidth();
1120 		int							height	= access.getHeight();
1121 		int							layers	= access.getDepth();
1122 
1123 		DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1124 		DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
1125 
1126 		context.texImage3D(GL_TEXTURE_2D_ARRAY, levelNdx, m_textureParams[texNdx].internalFormat, width, height, layers, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1127 		GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d array texture image data");
1128 	}
1129 }
1130 
upload3dTexture(int texNdx,sglr::Context & context)1131 void TextureUnitCase::upload3dTexture (int texNdx, sglr::Context& context)
1132 {
1133 	int							ndx3d		= m_ndxTexType[texNdx];
1134 	const tcu::Texture3D*		texture		= m_textures3d[ndx3d];
1135 	glu::TransferFormat			formatGl	= glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1136 
1137 	context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1138 
1139 	for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1140 	{
1141 		if (texture->isLevelEmpty(levelNdx))
1142 			continue;
1143 
1144 		tcu::ConstPixelBufferAccess	access	= texture->getLevel(levelNdx);
1145 		int							width	= access.getWidth();
1146 		int							height	= access.getHeight();
1147 		int							depth	= access.getDepth();
1148 
1149 		DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
1150 		DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
1151 
1152 		context.texImage3D(GL_TEXTURE_3D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, depth, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1153 		GLU_EXPECT_NO_ERROR(context.getError(), "Set 3d texture image data");
1154 	}
1155 }
1156 
render(sglr::Context & context)1157 void TextureUnitCase::render (sglr::Context& context)
1158 {
1159 	// Setup textures.
1160 
1161 	vector<deUint32>	textureGLNames;
1162 	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.
1163 
1164 	textureGLNames.resize(m_numTextures);
1165 	context.genTextures(m_numTextures, &textureGLNames[0]);
1166 	GLU_EXPECT_NO_ERROR(context.getError(), "Generate textures");
1167 
1168 	for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
1169 	{
1170 		int texNdx = m_unitTextures[unitNdx];
1171 
1172 		// Bind texture to unit.
1173 		context.activeTexture(GL_TEXTURE0 + unitNdx);
1174 		GLU_EXPECT_NO_ERROR(context.getError(), "Set active texture");
1175 		context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
1176 		GLU_EXPECT_NO_ERROR(context.getError(), "Bind texture");
1177 
1178 		if (!isTextureSetUp[texNdx])
1179 		{
1180 			// Binding this texture for first time, so set parameters and data.
1181 
1182 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
1183 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
1184 			if (m_textureTypes[texNdx] == GL_TEXTURE_3D)
1185 				context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_R, m_textureParams[texNdx].wrapModeR);
1186 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
1187 			context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
1188 			GLU_EXPECT_NO_ERROR(context.getError(), "Set texture parameters");
1189 
1190 			switch (m_textureTypes[texNdx])
1191 			{
1192 				case GL_TEXTURE_2D:			upload2dTexture(texNdx, context);		break;
1193 				case GL_TEXTURE_CUBE_MAP:	uploadCubeTexture(texNdx, context);		break;
1194 				case GL_TEXTURE_2D_ARRAY:	upload2dArrayTexture(texNdx, context);	break;
1195 				case GL_TEXTURE_3D:			upload3dTexture(texNdx, context);		break;
1196 				default:
1197 					DE_ASSERT(DE_FALSE);
1198 			}
1199 
1200 			isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
1201 		}
1202 	}
1203 
1204 	GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
1205 
1206 	// Setup shader
1207 
1208 	deUint32 shaderID = context.createProgram(m_shader);
1209 
1210 	// Draw.
1211 
1212 	context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
1213 	context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
1214 	m_shader->setUniforms(context, shaderID);
1215 	sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
1216 	GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
1217 
1218 	// Delete previously generated texture names.
1219 
1220 	context.deleteTextures(m_numTextures, &textureGLNames[0]);
1221 	GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
1222 }
1223 
TextureUnitTests(Context & context)1224 TextureUnitTests::TextureUnitTests (Context& context)
1225 	: TestCaseGroup(context, "units", "Texture Unit Usage Tests")
1226 {
1227 }
1228 
~TextureUnitTests(void)1229 TextureUnitTests::~TextureUnitTests (void)
1230 {
1231 }
1232 
init(void)1233 void TextureUnitTests::init (void)
1234 {
1235 	const int numTestsPerGroup = 10;
1236 
1237 	static const int unitCounts[] =
1238 	{
1239 		2,
1240 		4,
1241 		8,
1242 		-1 // \note Negative stands for the implementation-specified maximum.
1243 	};
1244 
1245 	for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
1246 	{
1247 		int numUnits = unitCounts[unitCountNdx];
1248 
1249 		string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
1250 
1251 		tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
1252 		addChild(countGroup);
1253 
1254 		DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
1255 
1256 		for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
1257 		{
1258 			const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D		? "only_2d"
1259 										  : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE		? "only_cube"
1260 										  : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D_ARRAY	? "only_2d_array"
1261 										  : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_3D		? "only_3d"
1262 										  : (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED			? "mixed"
1263 										  : DE_NULL;
1264 
1265 			DE_ASSERT(caseTypeGroupName != DE_NULL);
1266 
1267 			tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
1268 			countGroup->addChild(caseTypeGroup);
1269 
1270 			for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
1271 				caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, deUint32Hash((deUint32)testNdx)));
1272 		}
1273 	}
1274 }
1275 
1276 } // Functional
1277 } // gles3
1278 } // deqp
1279