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