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