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 Instanced rendering tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fInstancedRenderingTests.hpp"
25 #include "gluPixelTransfer.hpp"
26 #include "gluShaderProgram.hpp"
27 #include "gluShaderUtil.hpp"
28 #include "tcuTestLog.hpp"
29 #include "tcuSurface.hpp"
30 #include "tcuImageCompare.hpp"
31 #include "tcuVector.hpp"
32 #include "tcuRenderTarget.hpp"
33 #include "deRandom.hpp"
34 #include "deStringUtil.hpp"
35 #include "deString.h"
36 
37 #include "glw.h"
38 
39 using std::vector;
40 using std::string;
41 
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Functional
47 {
48 
49 static const int	MAX_RENDER_WIDTH		= 128;
50 static const int	MAX_RENDER_HEIGHT		= 128;
51 
52 static const int	QUAD_GRID_SIZE			= 127;
53 
54 // Attribute divisors for the attributes defining the color's RGB components.
55 static const int	ATTRIB_DIVISOR_R		= 3;
56 static const int	ATTRIB_DIVISOR_G		= 2;
57 static const int	ATTRIB_DIVISOR_B		= 1;
58 
59 static const int	OFFSET_COMPONENTS		= 3; // \note Affects whether a float or a vecN is used in shader, but only first component is non-zero.
60 
61 // Scale and bias values when converting float to integer, when attribute is of integer type.
62 static const float	FLOAT_INT_SCALE			= 100.0f;
63 static const float	FLOAT_INT_BIAS			= -50.0f;
64 static const float	FLOAT_UINT_SCALE		= 100.0f;
65 static const float	FLOAT_UINT_BIAS			= 0.0f;
66 
67 // \note Non-anonymous namespace needed; VarComp is used as a template parameter.
68 namespace vcns
69 {
70 
71 union VarComp
72 {
73 	float		f32;
74 	deUint32	u32;
75 	deInt32		i32;
76 
VarComp(float v)77 	VarComp(float v)	: f32(v) {}
VarComp(deUint32 v)78 	VarComp(deUint32 v)	: u32(v) {}
VarComp(deInt32 v)79 	VarComp(deInt32 v)	: i32(v) {}
80 };
81 DE_STATIC_ASSERT(sizeof(VarComp) == sizeof(deUint32));
82 
83 } // vcns
84 
85 using namespace vcns;
86 
87 class InstancedRenderingCase : public TestCase
88 {
89 public:
90 	enum DrawFunction
91 	{
92 		FUNCTION_DRAW_ARRAYS_INSTANCED = 0,
93 		FUNCTION_DRAW_ELEMENTS_INSTANCED,
94 
95 		FUNCTION_LAST
96 	};
97 
98 	enum InstancingType
99 	{
100 		TYPE_INSTANCE_ID = 0,
101 		TYPE_ATTRIB_DIVISOR,
102 		TYPE_MIXED,
103 
104 		TYPE_LAST
105 	};
106 
107 								InstancedRenderingCase	(Context& context, const char* name, const char* description, DrawFunction function, InstancingType instancingType, glu::DataType rgbAttrType, int numInstances);
108 								~InstancedRenderingCase	(void);
109 
110 	void						init					(void);
111 	void						deinit					(void);
112 	IterateResult				iterate					(void);
113 
114 private:
115 								InstancedRenderingCase	(const InstancedRenderingCase& other);
116 	InstancedRenderingCase&		operator=				(const InstancedRenderingCase& other);
117 
118 	void						pushVarCompAttrib		(vector<VarComp>& vec, float val);
119 
120 	void						setupVarAttribPointer	(const void* attrPtr, int startLocation, int divisor);
121 	void						setupAndRender			(void);
122 	void						computeReference		(tcu::Surface& dst);
123 
124 	DrawFunction				m_function;
125 	InstancingType				m_instancingType;
126 	glu::DataType				m_rgbAttrType;			// \note Instance attribute types, color components only. Position offset attribute is always float/vecN.
127 	int							m_numInstances;
128 
129 	vector<float>				m_gridVertexPositions;	// X and Y components per vertex.
130 	vector<deUint16>			m_gridIndices;			// \note Only used if m_function is FUNCTION_DRAW_ELEMENTS_INSTANCED.
131 
132 	// \note Some or all of the following instance attribute parameters may be unused with TYPE_INSTANCE_ID or TYPE_MIXED.
133 	vector<float>				m_instanceOffsets;		// Position offsets. OFFSET_COMPONENTS components per offset.
134 	// Attribute data for float, int or uint (or respective vector types) color components.
135 	vector<VarComp>				m_instanceColorR;
136 	vector<VarComp>				m_instanceColorG;
137 	vector<VarComp>				m_instanceColorB;
138 
139 	glu::ShaderProgram*			m_program;
140 };
141 
InstancedRenderingCase(Context & context,const char * name,const char * description,DrawFunction function,InstancingType instancingType,glu::DataType rgbAttrType,int numInstances)142 InstancedRenderingCase::InstancedRenderingCase (Context& context, const char* name, const char* description, DrawFunction function, InstancingType instancingType, glu::DataType rgbAttrType, int numInstances)
143 	: TestCase			(context, name, description)
144 	, m_function		(function)
145 	, m_instancingType	(instancingType)
146 	, m_rgbAttrType		(rgbAttrType)
147 	, m_numInstances	(numInstances)
148 	, m_program			(DE_NULL)
149 {
150 }
151 
~InstancedRenderingCase(void)152 InstancedRenderingCase::~InstancedRenderingCase (void)
153 {
154 	InstancedRenderingCase::deinit();
155 }
156 
157 // Helper function that does biasing and scaling when converting float to integer.
pushVarCompAttrib(vector<VarComp> & vec,float val)158 void InstancedRenderingCase::pushVarCompAttrib (vector<VarComp>& vec, float val)
159 {
160 	bool	isFloatCase	= glu::isDataTypeFloatOrVec(m_rgbAttrType);
161 	bool	isIntCase	= glu::isDataTypeIntOrIVec(m_rgbAttrType);
162 	bool	isUintCase	= glu::isDataTypeUintOrUVec(m_rgbAttrType);
163 	bool	isMatCase	= glu::isDataTypeMatrix(m_rgbAttrType);
164 
165 	if (isFloatCase || isMatCase)
166 		vec.push_back(VarComp(val));
167 	else if (isIntCase)
168 		vec.push_back(VarComp((deInt32)(val*FLOAT_INT_SCALE + FLOAT_INT_BIAS)));
169 	else if (isUintCase)
170 		vec.push_back(VarComp((deUint32)(val*FLOAT_UINT_SCALE + FLOAT_UINT_BIAS)));
171 	else
172 		DE_ASSERT(DE_FALSE);
173 }
174 
init(void)175 void InstancedRenderingCase::init (void)
176 {
177 	bool	isFloatCase			= glu::isDataTypeFloatOrVec(m_rgbAttrType);
178 	bool	isIntCase			= glu::isDataTypeIntOrIVec(m_rgbAttrType);
179 	bool	isUintCase			= glu::isDataTypeUintOrUVec(m_rgbAttrType);
180 	bool	isMatCase			= glu::isDataTypeMatrix(m_rgbAttrType);
181 	int		typeSize			= glu::getDataTypeScalarSize(m_rgbAttrType);
182 	bool	isScalarCase		= typeSize == 1;
183 	string	swizzleFirst		= isScalarCase ? "" : ".x";
184 	string	typeName			= glu::getDataTypeName(m_rgbAttrType);
185 
186 	string	floatIntScaleStr	= "(" + de::floatToString(FLOAT_INT_SCALE, 3) + ")";
187 	string	floatIntBiasStr		= "(" + de::floatToString(FLOAT_INT_BIAS, 3) + ")";
188 	string	floatUintScaleStr	= "(" + de::floatToString(FLOAT_UINT_SCALE, 3) + ")";
189 	string	floatUintBiasStr	= "(" + de::floatToString(FLOAT_UINT_BIAS, 3) + ")";
190 
191 	DE_ASSERT(isFloatCase || isIntCase || isUintCase || isMatCase);
192 
193 	// Generate shader.
194 	// \note For case TYPE_MIXED, vertex position offset and color red component get their values from instance id, while green and blue get their values from instanced attributes.
195 
196 	string numInstancesStr = de::toString(m_numInstances) + ".0";
197 
198 	string instanceAttribs;
199 	string posExpression;
200 	string colorRExpression;
201 	string colorGExpression;
202 	string colorBExpression;
203 
204 	if (m_instancingType == TYPE_INSTANCE_ID || m_instancingType == TYPE_MIXED)
205 	{
206 		posExpression = "a_position + vec4(float(gl_InstanceID) * 2.0 / " + numInstancesStr + ", 0.0, 0.0, 0.0)";
207 		colorRExpression = "float(gl_InstanceID)/" + numInstancesStr;
208 
209 		if (m_instancingType == TYPE_INSTANCE_ID)
210 		{
211 			colorGExpression = "float(gl_InstanceID)*2.0/" + numInstancesStr;
212 			colorBExpression = "1.0 - float(gl_InstanceID)/" + numInstancesStr;
213 		}
214 	}
215 
216 	if (m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED)
217 	{
218 		if (m_instancingType == TYPE_ATTRIB_DIVISOR)
219 		{
220 			posExpression = "a_position + vec4(a_instanceOffset";
221 
222 			DE_STATIC_ASSERT(OFFSET_COMPONENTS >= 1 && OFFSET_COMPONENTS <= 4);
223 
224 			for (int i = 0; i < 4-OFFSET_COMPONENTS; i++)
225 				posExpression += ", 0.0";
226 			posExpression += ")";
227 
228 			if (isFloatCase)
229 				colorRExpression = "a_instanceR" + swizzleFirst;
230 			else if (isIntCase)
231 				colorRExpression = "(float(a_instanceR" + swizzleFirst + ") - " + floatIntBiasStr + ") / " + floatIntScaleStr;
232 			else if (isUintCase)
233 				colorRExpression = "(float(a_instanceR" + swizzleFirst + ") - " + floatUintBiasStr + ") / " + floatUintScaleStr;
234 			else if (isMatCase)
235 				colorRExpression = "a_instanceR[0][0]";
236 			else
237 				DE_ASSERT(DE_FALSE);
238 
239 			instanceAttribs += "in highp " + (OFFSET_COMPONENTS == 1 ? string("float") : "vec" + de::toString(OFFSET_COMPONENTS)) + " a_instanceOffset;\n";
240 			instanceAttribs += "in mediump " + typeName + " a_instanceR;\n";
241 		}
242 
243 		if (isFloatCase)
244 		{
245 			colorGExpression = "a_instanceG" + swizzleFirst;
246 			colorBExpression = "a_instanceB" + swizzleFirst;
247 		}
248 		else if (isIntCase)
249 		{
250 			colorGExpression = "(float(a_instanceG" + swizzleFirst + ") - " + floatIntBiasStr + ") / " + floatIntScaleStr;
251 			colorBExpression = "(float(a_instanceB" + swizzleFirst + ") - " + floatIntBiasStr + ") / " + floatIntScaleStr;
252 		}
253 		else if (isUintCase)
254 		{
255 			colorGExpression = "(float(a_instanceG" + swizzleFirst + ") - " + floatUintBiasStr + ") / " + floatUintScaleStr;
256 			colorBExpression = "(float(a_instanceB" + swizzleFirst + ") - " + floatUintBiasStr + ") / " + floatUintScaleStr;
257 		}
258 		else if (isMatCase)
259 		{
260 			colorGExpression = "a_instanceG[0][0]";
261 			colorBExpression = "a_instanceB[0][0]";
262 		}
263 		else
264 			DE_ASSERT(DE_FALSE);
265 
266 		instanceAttribs += "in mediump " + typeName + " a_instanceG;\n";
267 		instanceAttribs += "in mediump " + typeName + " a_instanceB;\n";
268 	}
269 
270 	DE_ASSERT(!posExpression.empty());
271 	DE_ASSERT(!colorRExpression.empty());
272 	DE_ASSERT(!colorGExpression.empty());
273 	DE_ASSERT(!colorBExpression.empty());
274 
275 	std::string vertShaderSourceStr =
276 		"#version 300 es\n"
277 		"in highp vec4 a_position;\n" +
278 		instanceAttribs +
279 		"out mediump vec4 v_color;\n"
280 		"\n"
281 		"void main()\n"
282 		"{\n"
283 		"	gl_Position = " + posExpression + ";\n"
284 		"	v_color.r = " + colorRExpression + ";\n"
285 		"	v_color.g = " + colorGExpression + ";\n"
286 		"	v_color.b = " + colorBExpression + ";\n"
287 		"	v_color.a = 1.0;\n"
288 		"}\n";
289 
290 	static const char* fragShaderSource =
291 		"#version 300 es\n"
292 		"layout(location = 0) out mediump vec4 o_color;\n"
293 		"in mediump vec4 v_color;\n"
294 		"\n"
295 		"void main()\n"
296 		"{\n"
297 		"	o_color = v_color;\n"
298 		"}\n";
299 
300 	// Create shader program and log it.
301 
302 	DE_ASSERT(!m_program);
303 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSourceStr, fragShaderSource));
304 
305 	tcu::TestLog& log = m_testCtx.getLog();
306 
307 	log << *m_program;
308 
309 	if(!m_program->isOk())
310 		TCU_FAIL("Failed to compile shader");
311 
312 	// Vertex shader attributes.
313 
314 	if (m_function == FUNCTION_DRAW_ELEMENTS_INSTANCED)
315 	{
316 		// Vertex positions. Positions form a vertical bar of width <screen width>/<number of instances>.
317 
318 		for (int y = 0; y < QUAD_GRID_SIZE + 1; y++)
319 			for (int x = 0; x < QUAD_GRID_SIZE + 1; x++)
320 			{
321 				float fx = -1.0f + (float)x / (float)QUAD_GRID_SIZE * 2.0f / (float)m_numInstances;
322 				float fy = -1.0f + (float)y / (float)QUAD_GRID_SIZE * 2.0f;
323 
324 				m_gridVertexPositions.push_back(fx);
325 				m_gridVertexPositions.push_back(fy);
326 			}
327 
328 		// Indices.
329 
330 		for (int y = 0; y < QUAD_GRID_SIZE; y++)
331 			for (int x = 0; x < QUAD_GRID_SIZE; x++)
332 			{
333 				int ndx00 = y*(QUAD_GRID_SIZE + 1) + x;
334 				int ndx10 = y*(QUAD_GRID_SIZE + 1) + x + 1;
335 				int ndx01 = (y + 1)*(QUAD_GRID_SIZE + 1) + x;
336 				int ndx11 = (y + 1)*(QUAD_GRID_SIZE + 1) + x + 1;
337 
338 				// Lower-left triangle of a quad.
339 				m_gridIndices.push_back(ndx00);
340 				m_gridIndices.push_back(ndx10);
341 				m_gridIndices.push_back(ndx01);
342 
343 				// Upper-right triangle of a quad.
344 				m_gridIndices.push_back(ndx11);
345 				m_gridIndices.push_back(ndx01);
346 				m_gridIndices.push_back(ndx10);
347 			}
348 	}
349 	else
350 	{
351 		DE_ASSERT(m_function == FUNCTION_DRAW_ARRAYS_INSTANCED);
352 
353 		// Vertex positions. Positions form a vertical bar of width <screen width>/<number of instances>.
354 
355 		for (int y = 0; y < QUAD_GRID_SIZE; y++)
356 			for (int x = 0; x < QUAD_GRID_SIZE; x++)
357 			{
358 				float fx0 = -1.0f + (float)(x+0) / (float)QUAD_GRID_SIZE * 2.0f / (float)m_numInstances;
359 				float fx1 = -1.0f + (float)(x+1) / (float)QUAD_GRID_SIZE * 2.0f / (float)m_numInstances;
360 				float fy0 = -1.0f + (float)(y+0) / (float)QUAD_GRID_SIZE * 2.0f;
361 				float fy1 = -1.0f + (float)(y+1) / (float)QUAD_GRID_SIZE * 2.0f;
362 
363 				// Vertices of a quad's lower-left triangle: (fx0, fy0), (fx1, fy0) and (fx0, fy1)
364 				m_gridVertexPositions.push_back(fx0);
365 				m_gridVertexPositions.push_back(fy0);
366 				m_gridVertexPositions.push_back(fx1);
367 				m_gridVertexPositions.push_back(fy0);
368 				m_gridVertexPositions.push_back(fx0);
369 				m_gridVertexPositions.push_back(fy1);
370 
371 				// Vertices of a quad's upper-right triangle: (fx1, fy1), (fx0, fy1) and (fx1, fy0)
372 				m_gridVertexPositions.push_back(fx1);
373 				m_gridVertexPositions.push_back(fy1);
374 				m_gridVertexPositions.push_back(fx0);
375 				m_gridVertexPositions.push_back(fy1);
376 				m_gridVertexPositions.push_back(fx1);
377 				m_gridVertexPositions.push_back(fy0);
378 			}
379 	}
380 
381 	// Instanced attributes: position offset and color RGB components.
382 
383 	if (m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED)
384 	{
385 		if (m_instancingType == TYPE_ATTRIB_DIVISOR)
386 		{
387 			// Offsets are such that the vertical bars are drawn next to each other.
388 			for (int i = 0; i < m_numInstances; i++)
389 			{
390 				m_instanceOffsets.push_back((float)i * 2.0f / (float)m_numInstances);
391 
392 				DE_STATIC_ASSERT(OFFSET_COMPONENTS >= 1 && OFFSET_COMPONENTS <= 4);
393 
394 				for (int j = 0; j < OFFSET_COMPONENTS-1; j++)
395 					m_instanceOffsets.push_back(0.0f);
396 			}
397 
398 			int rInstances = m_numInstances / ATTRIB_DIVISOR_R + (m_numInstances % ATTRIB_DIVISOR_R == 0 ? 0 : 1);
399 			for (int i = 0; i < rInstances; i++)
400 			{
401 				pushVarCompAttrib(m_instanceColorR, (float)i / (float)rInstances);
402 
403 				for (int j = 0; j < typeSize - 1; j++)
404 					pushVarCompAttrib(m_instanceColorR, 0.0f);
405 			}
406 		}
407 
408 		int gInstances = m_numInstances / ATTRIB_DIVISOR_G + (m_numInstances % ATTRIB_DIVISOR_G == 0 ? 0 : 1);
409 		for (int i = 0; i < gInstances; i++)
410 		{
411 			pushVarCompAttrib(m_instanceColorG, (float)i*2.0f / (float)gInstances);
412 
413 			for (int j = 0; j < typeSize - 1; j++)
414 				pushVarCompAttrib(m_instanceColorG, 0.0f);
415 		}
416 
417 		int bInstances = m_numInstances / ATTRIB_DIVISOR_B + (m_numInstances % ATTRIB_DIVISOR_B == 0 ? 0 : 1);
418 		for (int i = 0; i < bInstances; i++)
419 		{
420 			pushVarCompAttrib(m_instanceColorB, 1.0f - (float)i / (float)bInstances);
421 
422 			for (int j = 0; j < typeSize - 1; j++)
423 				pushVarCompAttrib(m_instanceColorB, 0.0f);
424 		}
425 	}
426 }
427 
deinit(void)428 void InstancedRenderingCase::deinit (void)
429 {
430 	delete m_program;
431 	m_program = DE_NULL;
432 }
433 
iterate(void)434 InstancedRenderingCase::IterateResult InstancedRenderingCase::iterate (void)
435 {
436 	int							width			= deMin32(m_context.getRenderTarget().getWidth(), MAX_RENDER_WIDTH);
437 	int							height			= deMin32(m_context.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT);
438 
439 	int							xOffsetMax		= m_context.getRenderTarget().getWidth() - width;
440 	int							yOffsetMax		= m_context.getRenderTarget().getHeight() - height;
441 
442 	de::Random					rnd				(deStringHash(getName()));
443 
444 	int							xOffset			= rnd.getInt(0, xOffsetMax);
445 	int							yOffset			= rnd.getInt(0, yOffsetMax);
446 	tcu::Surface				referenceImg	(width, height);
447 	tcu::Surface				resultImg		(width, height);
448 
449 	// Draw result.
450 
451 	glViewport(xOffset, yOffset, width, height);
452 
453 	setupAndRender();
454 
455 	glu::readPixels(m_context.getRenderContext(), xOffset, yOffset, resultImg.getAccess());
456 
457 	// Compute reference.
458 
459 	computeReference(referenceImg);
460 
461 	// Compare.
462 
463 	bool testOk = tcu::fuzzyCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", referenceImg, resultImg, 0.05f, tcu::COMPARE_LOG_RESULT);
464 
465 	m_testCtx.setTestResult(testOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
466 							testOk ? "Pass"					: "Fail");
467 
468 	return STOP;
469 }
470 
setupVarAttribPointer(const void * attrPtr,int location,int divisor)471 void InstancedRenderingCase::setupVarAttribPointer (const void* attrPtr, int location, int divisor)
472 {
473 	bool	isFloatCase		= glu::isDataTypeFloatOrVec(m_rgbAttrType);
474 	bool	isIntCase		= glu::isDataTypeIntOrIVec(m_rgbAttrType);
475 	bool	isUintCase		= glu::isDataTypeUintOrUVec(m_rgbAttrType);
476 	bool	isMatCase		= glu::isDataTypeMatrix(m_rgbAttrType);
477 	int		typeSize		= glu::getDataTypeScalarSize(m_rgbAttrType);
478 	int		numSlots		= isMatCase ? glu::getDataTypeMatrixNumColumns(m_rgbAttrType) : 1; // Matrix uses as many attribute slots as it has columns.
479 
480 	for (int slotNdx = 0; slotNdx < numSlots; slotNdx++)
481 	{
482 		int curLoc = location + slotNdx;
483 
484 		glEnableVertexAttribArray(curLoc);
485 		glVertexAttribDivisor(curLoc, divisor);
486 
487 		if (isFloatCase)
488 			glVertexAttribPointer(curLoc, typeSize, GL_FLOAT, GL_FALSE, 0, attrPtr);
489 		else if (isIntCase)
490 			glVertexAttribIPointer(curLoc, typeSize, GL_INT, 0, attrPtr);
491 		else if (isUintCase)
492 			glVertexAttribIPointer(curLoc, typeSize, GL_UNSIGNED_INT, 0, attrPtr);
493 		else if (isMatCase)
494 		{
495 			int numRows = glu::getDataTypeMatrixNumRows(m_rgbAttrType);
496 			int numCols = glu::getDataTypeMatrixNumColumns(m_rgbAttrType);
497 
498 			glVertexAttribPointer(curLoc, numRows, GL_FLOAT, GL_FALSE, numCols*numRows*sizeof(float), attrPtr);
499 		}
500 		else
501 			DE_ASSERT(DE_FALSE);
502 	}
503 }
504 
setupAndRender(void)505 void InstancedRenderingCase::setupAndRender (void)
506 {
507 	deUint32 program = m_program->getProgram();
508 
509 	glUseProgram(program);
510 
511 	{
512 		// Setup attributes.
513 
514 		// Position attribute is non-instanced.
515 		int positionLoc = glGetAttribLocation(program, "a_position");
516 		glEnableVertexAttribArray(positionLoc);
517 		glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, &m_gridVertexPositions[0]);
518 
519 		if (m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED)
520 		{
521 			if (m_instancingType == TYPE_ATTRIB_DIVISOR)
522 			{
523 				// Position offset attribute is instanced with separate offset for every instance.
524 				int offsetLoc = glGetAttribLocation(program, "a_instanceOffset");
525 				glEnableVertexAttribArray(offsetLoc);
526 				glVertexAttribDivisor(offsetLoc, 1);
527 				glVertexAttribPointer(offsetLoc, OFFSET_COMPONENTS, GL_FLOAT, GL_FALSE, 0, &m_instanceOffsets[0]);
528 
529 				int rLoc = glGetAttribLocation(program, "a_instanceR");
530 				setupVarAttribPointer((void*)&m_instanceColorR[0].u32, rLoc, ATTRIB_DIVISOR_R);
531 			}
532 
533 			int gLoc = glGetAttribLocation(program, "a_instanceG");
534 			setupVarAttribPointer((void*)&m_instanceColorG[0].u32, gLoc, ATTRIB_DIVISOR_G);
535 
536 			int bLoc = glGetAttribLocation(program, "a_instanceB");
537 			setupVarAttribPointer((void*)&m_instanceColorB[0].u32, bLoc, ATTRIB_DIVISOR_B);
538 		}
539 	}
540 
541 	// Draw using appropriate function.
542 
543 	if (m_function == FUNCTION_DRAW_ARRAYS_INSTANCED)
544 	{
545 		const int numPositionComponents = 2;
546 		glDrawArraysInstanced(GL_TRIANGLES, 0, ((int)m_gridVertexPositions.size() / numPositionComponents), m_numInstances);
547 	}
548 	else
549 		glDrawElementsInstanced(GL_TRIANGLES, (int)m_gridIndices.size(), GL_UNSIGNED_SHORT, &m_gridIndices[0], m_numInstances);
550 
551 	glUseProgram(0);
552 }
553 
computeReference(tcu::Surface & dst)554 void InstancedRenderingCase::computeReference (tcu::Surface& dst)
555 {
556 	int wid = dst.getWidth();
557 	int hei = dst.getHeight();
558 
559 	// Draw a rectangle (vertical bar) for each instance.
560 
561 	for (int instanceNdx = 0; instanceNdx < m_numInstances; instanceNdx++)
562 	{
563 		int xStart		= instanceNdx * wid / m_numInstances;
564 		int xEnd		= (instanceNdx + 1) * wid / m_numInstances;
565 
566 		// Emulate attribute divisors if that is the case.
567 
568 		int clrNdxR		= m_instancingType == TYPE_ATTRIB_DIVISOR									? instanceNdx / ATTRIB_DIVISOR_R : instanceNdx;
569 		int clrNdxG		= m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED	? instanceNdx / ATTRIB_DIVISOR_G : instanceNdx;
570 		int clrNdxB		= m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED	? instanceNdx / ATTRIB_DIVISOR_B : instanceNdx;
571 
572 		int rInstances	= m_instancingType == TYPE_ATTRIB_DIVISOR									? m_numInstances / ATTRIB_DIVISOR_R + (m_numInstances % ATTRIB_DIVISOR_R == 0 ? 0 : 1) : m_numInstances;
573 		int gInstances	= m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED	? m_numInstances / ATTRIB_DIVISOR_G + (m_numInstances % ATTRIB_DIVISOR_G == 0 ? 0 : 1) : m_numInstances;
574 		int bInstances	= m_instancingType == TYPE_ATTRIB_DIVISOR || m_instancingType == TYPE_MIXED	? m_numInstances / ATTRIB_DIVISOR_B + (m_numInstances % ATTRIB_DIVISOR_B == 0 ? 0 : 1) : m_numInstances;
575 
576 		// Calculate colors.
577 
578 		float r = (float)clrNdxR / (float)rInstances;
579 		float g = (float)clrNdxG * 2.0f / (float)gInstances;
580 		float b = 1.0f - (float)clrNdxB / (float)bInstances;
581 
582 		// Convert to integer and back if shader inputs are integers.
583 
584 		if (glu::isDataTypeIntOrIVec(m_rgbAttrType))
585 		{
586 			deInt32 intR = (deInt32)(r*FLOAT_INT_SCALE + FLOAT_INT_BIAS);
587 			deInt32 intG = (deInt32)(g*FLOAT_INT_SCALE + FLOAT_INT_BIAS);
588 			deInt32 intB = (deInt32)(b*FLOAT_INT_SCALE + FLOAT_INT_BIAS);
589 			r = (float)(intR - FLOAT_INT_BIAS) / FLOAT_INT_SCALE;
590 			g = (float)(intG - FLOAT_INT_BIAS) / FLOAT_INT_SCALE;
591 			b = (float)(intB - FLOAT_INT_BIAS) / FLOAT_INT_SCALE;
592 		}
593 		else if(glu::isDataTypeUintOrUVec(m_rgbAttrType))
594 		{
595 			deUint32 uintR = (deInt32)(r*FLOAT_UINT_SCALE + FLOAT_UINT_BIAS);
596 			deUint32 uintG = (deInt32)(g*FLOAT_UINT_SCALE + FLOAT_UINT_BIAS);
597 			deUint32 uintB = (deInt32)(b*FLOAT_UINT_SCALE + FLOAT_UINT_BIAS);
598 			r = (float)(uintR - FLOAT_UINT_BIAS) / FLOAT_UINT_SCALE;
599 			g = (float)(uintG - FLOAT_UINT_BIAS) / FLOAT_UINT_SCALE;
600 			b = (float)(uintB - FLOAT_UINT_BIAS) / FLOAT_UINT_SCALE;
601 		}
602 
603 		// Draw rectangle.
604 
605 		for (int y = 0; y < hei; y++)
606 			for (int x = xStart; x < xEnd; x++)
607 				dst.setPixel(x, y, tcu::RGBA(tcu::Vec4(r, g, b, 1.0f)));
608 	}
609 }
610 
InstancedRenderingTests(Context & context)611 InstancedRenderingTests::InstancedRenderingTests (Context& context)
612 	: TestCaseGroup(context, "instanced", "Instanced rendering tests")
613 {
614 }
615 
~InstancedRenderingTests(void)616 InstancedRenderingTests::~InstancedRenderingTests (void)
617 {
618 }
619 
init(void)620 void InstancedRenderingTests::init (void)
621 {
622 	// Cases testing function, instancing method and instance count.
623 
624 	static const int instanceCounts[] = { 1, 2, 4, 20 };
625 
626 	for (int function = 0; function < (int)InstancedRenderingCase::FUNCTION_LAST; function++)
627 	{
628 		const char* functionName = function == (int)InstancedRenderingCase::FUNCTION_DRAW_ARRAYS_INSTANCED		? "draw_arrays_instanced"
629 								 : function == (int)InstancedRenderingCase::FUNCTION_DRAW_ELEMENTS_INSTANCED	? "draw_elements_instanced"
630 								 : DE_NULL;
631 
632 		const char* functionDesc = function == (int)InstancedRenderingCase::FUNCTION_DRAW_ARRAYS_INSTANCED		? "Use glDrawArraysInstanced()"
633 								 : function == (int)InstancedRenderingCase::FUNCTION_DRAW_ELEMENTS_INSTANCED	? "Use glDrawElementsInstanced()"
634 								 : DE_NULL;
635 
636 		DE_ASSERT(functionName != DE_NULL);
637 		DE_ASSERT(functionDesc != DE_NULL);
638 
639 		TestCaseGroup* functionGroup = new TestCaseGroup(m_context, functionName, functionDesc);
640 		addChild(functionGroup);
641 
642 		for (int instancingType = 0; instancingType < (int)InstancedRenderingCase::TYPE_LAST; instancingType++)
643 		{
644 			const char* instancingTypeName = instancingType == (int)InstancedRenderingCase::TYPE_INSTANCE_ID	? "instance_id"
645 										   : instancingType == (int)InstancedRenderingCase::TYPE_ATTRIB_DIVISOR	? "attribute_divisor"
646 										   : instancingType == (int)InstancedRenderingCase::TYPE_MIXED			? "mixed"
647 										   : DE_NULL;
648 
649 			const char* instancingTypeDesc = instancingType == (int)InstancedRenderingCase::TYPE_INSTANCE_ID	? "Use gl_InstanceID for instancing"
650 										   : instancingType == (int)InstancedRenderingCase::TYPE_ATTRIB_DIVISOR	? "Use vertex attribute divisors for instancing"
651 										   : instancingType == (int)InstancedRenderingCase::TYPE_MIXED			? "Use both gl_InstanceID and vertex attribute divisors for instancing"
652 										   : DE_NULL;
653 
654 			DE_ASSERT(instancingTypeName != DE_NULL);
655 			DE_ASSERT(instancingTypeDesc != DE_NULL);
656 
657 			TestCaseGroup* instancingTypeGroup = new TestCaseGroup(m_context, instancingTypeName, instancingTypeDesc);
658 			functionGroup->addChild(instancingTypeGroup);
659 
660 			for (int countNdx = 0; countNdx < DE_LENGTH_OF_ARRAY(instanceCounts); countNdx++)
661 			{
662 				std::string countName = de::toString(instanceCounts[countNdx]) + "_instances";
663 
664 				instancingTypeGroup->addChild(new InstancedRenderingCase(m_context, countName.c_str(), "",
665 																		 (InstancedRenderingCase::DrawFunction)function,
666 																		 (InstancedRenderingCase::InstancingType)instancingType,
667 																		 glu::TYPE_FLOAT,
668 																		 instanceCounts[countNdx]));
669 			}
670 		}
671 	}
672 
673 	// Data type specific cases.
674 
675 	static const glu::DataType s_testTypes[] =
676 	{
677 		glu::TYPE_FLOAT,
678 		glu::TYPE_FLOAT_VEC2,
679 		glu::TYPE_FLOAT_VEC3,
680 		glu::TYPE_FLOAT_VEC4,
681 		glu::TYPE_FLOAT_MAT2,
682 		glu::TYPE_FLOAT_MAT2X3,
683 		glu::TYPE_FLOAT_MAT2X4,
684 		glu::TYPE_FLOAT_MAT3X2,
685 		glu::TYPE_FLOAT_MAT3,
686 		glu::TYPE_FLOAT_MAT3X4,
687 		glu::TYPE_FLOAT_MAT4X2,
688 		glu::TYPE_FLOAT_MAT4X3,
689 		glu::TYPE_FLOAT_MAT4,
690 
691 		glu::TYPE_INT,
692 		glu::TYPE_INT_VEC2,
693 		glu::TYPE_INT_VEC3,
694 		glu::TYPE_INT_VEC4,
695 
696 		glu::TYPE_UINT,
697 		glu::TYPE_UINT_VEC2,
698 		glu::TYPE_UINT_VEC3,
699 		glu::TYPE_UINT_VEC4
700 	};
701 
702 	const int typeTestNumInstances = 4;
703 
704 	TestCaseGroup* typesGroup = new TestCaseGroup(m_context, "types", "Tests for instanced attributes of particular data types");
705 	addChild(typesGroup);
706 
707 	for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_testTypes); typeNdx++)
708 	{
709 		glu::DataType type = s_testTypes[typeNdx];
710 
711 		typesGroup->addChild(new InstancedRenderingCase(m_context, glu::getDataTypeName(type), "",
712 														InstancedRenderingCase::FUNCTION_DRAW_ARRAYS_INSTANCED,
713 														InstancedRenderingCase::TYPE_ATTRIB_DIVISOR,
714 														type,
715 														typeTestNumInstances));
716 	}
717 }
718 
719 } // Functional
720 } // gles3
721 } // deqp
722