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