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
39using std::vector;
40using std::string;
41
42namespace deqp
43{
44namespace gles3
45{
46namespace Functional
47{
48
49static const int	MAX_RENDER_WIDTH		= 128;
50static const int	MAX_RENDER_HEIGHT		= 128;
51
52static const int	QUAD_GRID_SIZE			= 127;
53
54// Attribute divisors for the attributes defining the color's RGB components.
55static const int	ATTRIB_DIVISOR_R		= 3;
56static const int	ATTRIB_DIVISOR_G		= 2;
57static const int	ATTRIB_DIVISOR_B		= 1;
58
59static 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.
62static const float	FLOAT_INT_SCALE			= 100.0f;
63static const float	FLOAT_INT_BIAS			= -50.0f;
64static const float	FLOAT_UINT_SCALE		= 100.0f;
65static const float	FLOAT_UINT_BIAS			= 0.0f;
66
67// \note Non-anonymous namespace needed; VarComp is used as a template parameter.
68namespace vcns
69{
70
71union VarComp
72{
73	float		f32;
74	deUint32	u32;
75	deInt32		i32;
76
77	VarComp(float v)	: f32(v) {}
78	VarComp(deUint32 v)	: u32(v) {}
79	VarComp(deInt32 v)	: i32(v) {}
80};
81DE_STATIC_ASSERT(sizeof(VarComp) == sizeof(deUint32));
82
83} // vcns
84
85using namespace vcns;
86
87class InstancedRenderingCase : public TestCase
88{
89public:
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
114private:
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
142InstancedRenderingCase::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
152InstancedRenderingCase::~InstancedRenderingCase (void)
153{
154	InstancedRenderingCase::deinit();
155}
156
157// Helper function that does biasing and scaling when converting float to integer.
158void 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
175void 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
428void InstancedRenderingCase::deinit (void)
429{
430	delete m_program;
431	m_program = DE_NULL;
432}
433
434InstancedRenderingCase::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
471void 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
505void 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
554void 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
611InstancedRenderingTests::InstancedRenderingTests (Context& context)
612	: TestCaseGroup(context, "instanced", "Instanced rendering tests")
613{
614}
615
616InstancedRenderingTests::~InstancedRenderingTests (void)
617{
618}
619
620void 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