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
44using tcu::Vec2;
45using tcu::Vec3;
46using tcu::Vec4;
47using tcu::IVec2;
48using tcu::IVec3;
49using tcu::Mat3;
50using tcu::Mat4;
51using std::vector;
52using std::string;
53using namespace glw; // GL types
54
55namespace deqp
56{
57
58using namespace gls::TextureTestUtil;
59
60namespace gles3
61{
62namespace Functional
63{
64
65static const int VIEWPORT_WIDTH				= 128;
66static const int VIEWPORT_HEIGHT			= 128;
67
68static const int TEXTURE_WIDTH_2D			= 128;
69static 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.
72static const int TEXTURE_WIDTH_CUBE			= 256;
73static const int TEXTURE_HEIGHT_CUBE		= 256;
74
75static const int TEXTURE_WIDTH_2D_ARRAY		= 64;
76static const int TEXTURE_HEIGHT_2D_ARRAY	= 64;
77static const int TEXTURE_LAYERS_2D_ARRAY	= 4;
78
79static const int TEXTURE_WIDTH_3D			= 32;
80static const int TEXTURE_HEIGHT_3D			= 32;
81static const int TEXTURE_DEPTH_3D			= 32;
82
83static const int GRID_CELL_SIZE				= 8;
84
85static 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
138static const GLenum s_testWrapModes[] =
139{
140	GL_CLAMP_TO_EDGE,
141	GL_REPEAT,
142	GL_MIRRORED_REPEAT,
143};
144
145static 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
155static const GLenum s_testNonMipmapMinFilters[] =
156{
157	GL_NEAREST,
158	GL_LINEAR
159};
160
161static const GLenum s_testNearestMinFilters[] =
162{
163	GL_NEAREST,
164	GL_NEAREST_MIPMAP_NEAREST
165};
166
167static const GLenum s_testMagFilters[] =
168{
169	GL_NEAREST,
170	GL_LINEAR
171};
172
173static 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).
184static 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
197static 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
248static 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().
286static 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
299static 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
315static 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
330class MultiTexShader : public sglr::ShaderProgram
331{
332public:
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
344private:
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
356MultiTexShader::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
543void 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
560void 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
608void 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
619void 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
684class TextureUnitCase : public TestCase
685{
686public:
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
704private:
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
743TextureUnitCase::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
752TextureUnitCase::~TextureUnitCase (void)
753{
754	TextureUnitCase::deinit();
755}
756
757void 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
779void 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
983TextureUnitCase::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
1054void 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
1078void 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
1105void 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
1131void 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
1157void 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
1224TextureUnitTests::TextureUnitTests (Context& context)
1225	: TestCaseGroup(context, "units", "Texture Unit Usage Tests")
1226{
1227}
1228
1229TextureUnitTests::~TextureUnitTests (void)
1230{
1231}
1232
1233void 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