1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.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 Draw call batching performance tests
22 *//*--------------------------------------------------------------------*/
23
24 #include "es2pDrawCallBatchingTests.hpp"
25
26 #include "gluShaderProgram.hpp"
27 #include "gluRenderContext.hpp"
28
29 #include "glwDefs.hpp"
30 #include "glwFunctions.hpp"
31 #include "glwEnums.hpp"
32
33 #include "tcuTestLog.hpp"
34
35 #include "deRandom.hpp"
36 #include "deStringUtil.hpp"
37
38 #include "deFile.h"
39 #include "deString.h"
40 #include "deClock.h"
41 #include "deThread.h"
42
43 #include <cmath>
44 #include <vector>
45 #include <string>
46 #include <sstream>
47
48 using tcu::TestLog;
49
50 using namespace glw;
51
52 using std::string;
53 using std::vector;
54
55 namespace deqp
56 {
57 namespace gles2
58 {
59 namespace Performance
60 {
61
62 namespace
63 {
64 const int CALIBRATION_SAMPLE_COUNT = 34;
65
66 class DrawCallBatchingTest : public tcu::TestCase
67 {
68 public:
69 struct TestSpec
70 {
71 bool useStaticBuffer;
72 int staticAttributeCount;
73
74 bool useDynamicBuffer;
75 int dynamicAttributeCount;
76
77 int triangleCount;
78 int drawCallCount;
79
80 bool useDrawElements;
81 bool useIndexBuffer;
82 bool dynamicIndices;
83 };
84
85 DrawCallBatchingTest(Context &context, const char *name, const char *description, const TestSpec &spec);
86 ~DrawCallBatchingTest(void);
87
88 void init(void);
89 void deinit(void);
90 IterateResult iterate(void);
91
92 private:
93 enum State
94 {
95 STATE_LOG_INFO = 0,
96 STATE_WARMUP_BATCHED,
97 STATE_WARMUP_UNBATCHED,
98 STATE_CALC_CALIBRATION,
99 STATE_SAMPLE
100 };
101
102 State m_state;
103
104 glu::RenderContext &m_renderCtx;
105 de::Random m_rnd;
106 int m_sampleIteration;
107
108 int m_unbatchedSampleCount;
109 int m_batchedSampleCount;
110
111 TestSpec m_spec;
112
113 glu::ShaderProgram *m_program;
114
115 vector<uint8_t> m_dynamicIndexData;
116 vector<uint8_t> m_staticIndexData;
117
118 vector<GLuint> m_unbatchedDynamicIndexBuffers;
119 GLuint m_batchedDynamicIndexBuffer;
120
121 GLuint m_unbatchedStaticIndexBuffer;
122 GLuint m_batchedStaticIndexBuffer;
123
124 vector<vector<int8_t>> m_staticAttributeDatas;
125 vector<vector<int8_t>> m_dynamicAttributeDatas;
126
127 vector<GLuint> m_batchedStaticBuffers;
128 vector<GLuint> m_unbatchedStaticBuffers;
129
130 vector<GLuint> m_batchedDynamicBuffers;
131 vector<vector<GLuint>> m_unbatchedDynamicBuffers;
132
133 vector<uint64_t> m_unbatchedSamplesUs;
134 vector<uint64_t> m_batchedSamplesUs;
135
136 void logTestInfo(void);
137
138 uint64_t renderUnbatched(void);
139 uint64_t renderBatched(void);
140
141 void createIndexData(void);
142 void createIndexBuffer(void);
143
144 void createShader(void);
145 void createAttributeDatas(void);
146 void createArrayBuffers(void);
147 };
148
DrawCallBatchingTest(Context & context,const char * name,const char * description,const TestSpec & spec)149 DrawCallBatchingTest::DrawCallBatchingTest(Context &context, const char *name, const char *description,
150 const TestSpec &spec)
151 : tcu::TestCase(context.getTestContext(), tcu::NODETYPE_PERFORMANCE, name, description)
152 , m_state(STATE_LOG_INFO)
153 , m_renderCtx(context.getRenderContext())
154 , m_rnd(deStringHash(name))
155 , m_sampleIteration(0)
156 , m_unbatchedSampleCount(CALIBRATION_SAMPLE_COUNT)
157 , m_batchedSampleCount(CALIBRATION_SAMPLE_COUNT)
158 , m_spec(spec)
159 , m_program(NULL)
160 , m_batchedDynamicIndexBuffer(0)
161 , m_unbatchedStaticIndexBuffer(0)
162 , m_batchedStaticIndexBuffer(0)
163 {
164 }
165
~DrawCallBatchingTest(void)166 DrawCallBatchingTest::~DrawCallBatchingTest(void)
167 {
168 deinit();
169 }
170
createIndexData(void)171 void DrawCallBatchingTest::createIndexData(void)
172 {
173 if (m_spec.dynamicIndices)
174 {
175 for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
176 {
177 for (int triangleNdx = 0; triangleNdx < m_spec.triangleCount; triangleNdx++)
178 {
179 m_dynamicIndexData.push_back(uint8_t(triangleNdx * 3));
180 m_dynamicIndexData.push_back(uint8_t(triangleNdx * 3 + 1));
181 m_dynamicIndexData.push_back(uint8_t(triangleNdx * 3 + 2));
182 }
183 }
184 }
185 else
186 {
187 for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
188 {
189 for (int triangleNdx = 0; triangleNdx < m_spec.triangleCount; triangleNdx++)
190 {
191 m_staticIndexData.push_back(uint8_t(triangleNdx * 3));
192 m_staticIndexData.push_back(uint8_t(triangleNdx * 3 + 1));
193 m_staticIndexData.push_back(uint8_t(triangleNdx * 3 + 2));
194 }
195 }
196 }
197 }
198
createShader(void)199 void DrawCallBatchingTest::createShader(void)
200 {
201 std::ostringstream vertexShader;
202 std::ostringstream fragmentShader;
203
204 for (int attributeNdx = 0; attributeNdx < m_spec.staticAttributeCount; attributeNdx++)
205 vertexShader << "attribute mediump vec4 a_static" << attributeNdx << ";\n";
206
207 if (m_spec.staticAttributeCount > 0 && m_spec.dynamicAttributeCount > 0)
208 vertexShader << "\n";
209
210 for (int attributeNdx = 0; attributeNdx < m_spec.dynamicAttributeCount; attributeNdx++)
211 vertexShader << "attribute mediump vec4 a_dyn" << attributeNdx << ";\n";
212
213 vertexShader << "\n"
214 << "varying mediump vec4 v_color;\n"
215 << "\n"
216 << "void main (void)\n"
217 << "{\n";
218
219 vertexShader << "\tv_color = ";
220
221 bool first = true;
222
223 for (int attributeNdx = 0; attributeNdx < m_spec.staticAttributeCount; attributeNdx++)
224 {
225 if (!first)
226 vertexShader << " + ";
227 first = false;
228
229 vertexShader << "a_static" << attributeNdx;
230 }
231
232 for (int attributeNdx = 0; attributeNdx < m_spec.dynamicAttributeCount; attributeNdx++)
233 {
234 if (!first)
235 vertexShader << " + ";
236 first = false;
237
238 vertexShader << "a_dyn" << attributeNdx;
239 }
240
241 vertexShader << ";\n";
242
243 if (m_spec.dynamicAttributeCount > 0)
244 vertexShader << "\tgl_Position = a_dyn0;\n";
245 else
246 vertexShader << "\tgl_Position = a_static0;\n";
247
248 vertexShader << "}";
249
250 fragmentShader << "varying mediump vec4 v_color;\n"
251 << "\n"
252 << "void main(void)\n"
253 << "{\n"
254 << "\tgl_FragColor = v_color;\n"
255 << "}\n";
256
257 m_program = new glu::ShaderProgram(m_renderCtx, glu::ProgramSources() << glu::VertexSource(vertexShader.str())
258 << glu::FragmentSource(fragmentShader.str()));
259
260 m_testCtx.getLog() << (*m_program);
261 TCU_CHECK(m_program->isOk());
262 }
263
createAttributeDatas(void)264 void DrawCallBatchingTest::createAttributeDatas(void)
265 {
266 // Generate data for static attributes
267 for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
268 {
269 vector<int8_t> data;
270
271 if (m_spec.dynamicAttributeCount == 0 && attribute == 0)
272 {
273 data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
274
275 for (int i = 0; i < m_spec.triangleCount * m_spec.drawCallCount; i++)
276 {
277 int sign = (m_spec.triangleCount % 2 == 1 || i % 2 == 0 ? 1 : -1);
278
279 data.push_back(int8_t(-127 * sign));
280 data.push_back(int8_t(-127 * sign));
281 data.push_back(0);
282 data.push_back(127);
283
284 data.push_back(int8_t(127 * sign));
285 data.push_back(int8_t(-127 * sign));
286 data.push_back(0);
287 data.push_back(127);
288
289 data.push_back(int8_t(127 * sign));
290 data.push_back(int8_t(127 * sign));
291 data.push_back(0);
292 data.push_back(127);
293 }
294 }
295 else
296 {
297 data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
298
299 for (int i = 0; i < 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount; i++)
300 data.push_back((int8_t)m_rnd.getUint32());
301 }
302
303 m_staticAttributeDatas.push_back(data);
304 }
305
306 // Generate data for dynamic attributes
307 for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
308 {
309 vector<int8_t> data;
310
311 if (attribute == 0)
312 {
313 data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
314
315 for (int i = 0; i < m_spec.triangleCount * m_spec.drawCallCount; i++)
316 {
317 int sign = (i % 2 == 0 ? 1 : -1);
318
319 data.push_back(int8_t(-127 * sign));
320 data.push_back(int8_t(-127 * sign));
321 data.push_back(0);
322 data.push_back(127);
323
324 data.push_back(int8_t(127 * sign));
325 data.push_back(int8_t(-127 * sign));
326 data.push_back(0);
327 data.push_back(127);
328
329 data.push_back(int8_t(127 * sign));
330 data.push_back(int8_t(127 * sign));
331 data.push_back(0);
332 data.push_back(127);
333 }
334 }
335 else
336 {
337 data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
338
339 for (int i = 0; i < 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount; i++)
340 data.push_back((int8_t)m_rnd.getUint32());
341 }
342
343 m_dynamicAttributeDatas.push_back(data);
344 }
345 }
346
createArrayBuffers(void)347 void DrawCallBatchingTest::createArrayBuffers(void)
348 {
349 const glw::Functions &gl = m_renderCtx.getFunctions();
350
351 if (m_spec.useStaticBuffer)
352 {
353 // Upload static attributes for batched
354 for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
355 {
356 GLuint buffer;
357
358 gl.genBuffers(1, &buffer);
359 gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
360 gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount,
361 &(m_staticAttributeDatas[attribute][0]), GL_STATIC_DRAW);
362 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
363 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating static buffer failed");
364
365 m_batchedStaticBuffers.push_back(buffer);
366 }
367
368 // Upload static attributes for unbatched
369 for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
370 {
371 GLuint buffer;
372
373 gl.genBuffers(1, &buffer);
374 gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
375 gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount, &(m_staticAttributeDatas[attribute][0]),
376 GL_STATIC_DRAW);
377 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
378 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating static buffer failed");
379
380 m_unbatchedStaticBuffers.push_back(buffer);
381 }
382 }
383
384 if (m_spec.useDynamicBuffer)
385 {
386 // Upload dynamic attributes for batched
387 for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
388 {
389 GLuint buffer;
390
391 gl.genBuffers(1, &buffer);
392 gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
393 gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount,
394 &(m_dynamicAttributeDatas[attribute][0]), GL_STATIC_DRAW);
395 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
396 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic buffer failed");
397
398 m_batchedDynamicBuffers.push_back(buffer);
399 }
400
401 // Upload dynamic attributes for unbatched
402 for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
403 {
404 vector<GLuint> buffers;
405
406 for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
407 {
408 GLuint buffer;
409
410 gl.genBuffers(1, &buffer);
411 gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
412 gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount,
413 &(m_dynamicAttributeDatas[attribute][0]), GL_STATIC_DRAW);
414 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
415 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic buffer failed");
416
417 buffers.push_back(buffer);
418 }
419
420 m_unbatchedDynamicBuffers.push_back(buffers);
421 }
422 }
423 }
424
createIndexBuffer(void)425 void DrawCallBatchingTest::createIndexBuffer(void)
426 {
427 const glw::Functions &gl = m_renderCtx.getFunctions();
428
429 if (m_spec.dynamicIndices)
430 {
431 for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
432 {
433 GLuint buffer;
434
435 gl.genBuffers(1, &buffer);
436 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
437 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount,
438 &(m_dynamicIndexData[drawNdx * m_spec.triangleCount * 3]), GL_STATIC_DRAW);
439 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
440 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
441
442 m_unbatchedDynamicIndexBuffers.push_back(buffer);
443 }
444
445 {
446 GLuint buffer;
447
448 gl.genBuffers(1, &buffer);
449 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
450 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount * m_spec.drawCallCount,
451 &(m_dynamicIndexData[0]), GL_STATIC_DRAW);
452 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
453 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
454
455 m_batchedDynamicIndexBuffer = buffer;
456 }
457 }
458 else
459 {
460 {
461 GLuint buffer;
462
463 gl.genBuffers(1, &buffer);
464 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
465 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount * m_spec.drawCallCount,
466 &(m_staticIndexData[0]), GL_STATIC_DRAW);
467 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
468 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
469
470 m_batchedStaticIndexBuffer = buffer;
471 }
472
473 {
474 GLuint buffer;
475
476 gl.genBuffers(1, &buffer);
477 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
478 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount, &(m_staticIndexData[0]), GL_STATIC_DRAW);
479 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
480 GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
481
482 m_unbatchedStaticIndexBuffer = buffer;
483 }
484 }
485 }
486
init(void)487 void DrawCallBatchingTest::init(void)
488 {
489 createShader();
490 createAttributeDatas();
491 createArrayBuffers();
492
493 if (m_spec.useDrawElements)
494 {
495 createIndexData();
496
497 if (m_spec.useIndexBuffer)
498 createIndexBuffer();
499 }
500 }
501
deinit(void)502 void DrawCallBatchingTest::deinit(void)
503 {
504 const glw::Functions &gl = m_renderCtx.getFunctions();
505
506 delete m_program;
507 m_program = NULL;
508
509 m_dynamicIndexData = vector<uint8_t>();
510 m_staticIndexData = vector<uint8_t>();
511
512 if (!m_unbatchedDynamicIndexBuffers.empty())
513 {
514 gl.deleteBuffers((GLsizei)m_unbatchedDynamicIndexBuffers.size(), &(m_unbatchedDynamicIndexBuffers[0]));
515 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
516
517 m_unbatchedDynamicIndexBuffers = vector<GLuint>();
518 }
519
520 if (m_batchedDynamicIndexBuffer)
521 {
522 gl.deleteBuffers((GLsizei)1, &m_batchedDynamicIndexBuffer);
523 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
524
525 m_batchedDynamicIndexBuffer = 0;
526 }
527
528 if (m_unbatchedStaticIndexBuffer)
529 {
530 gl.deleteBuffers((GLsizei)1, &m_unbatchedStaticIndexBuffer);
531 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
532
533 m_unbatchedStaticIndexBuffer = 0;
534 }
535
536 if (m_batchedStaticIndexBuffer)
537 {
538 gl.deleteBuffers((GLsizei)1, &m_batchedStaticIndexBuffer);
539 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
540
541 m_batchedStaticIndexBuffer = 0;
542 }
543
544 m_staticAttributeDatas = vector<vector<int8_t>>();
545 m_dynamicAttributeDatas = vector<vector<int8_t>>();
546
547 if (!m_batchedStaticBuffers.empty())
548 {
549 gl.deleteBuffers((GLsizei)m_batchedStaticBuffers.size(), &(m_batchedStaticBuffers[0]));
550 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
551
552 m_batchedStaticBuffers = vector<GLuint>();
553 }
554
555 if (!m_unbatchedStaticBuffers.empty())
556 {
557 gl.deleteBuffers((GLsizei)m_unbatchedStaticBuffers.size(), &(m_unbatchedStaticBuffers[0]));
558 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
559
560 m_unbatchedStaticBuffers = vector<GLuint>();
561 }
562
563 if (!m_batchedDynamicBuffers.empty())
564 {
565 gl.deleteBuffers((GLsizei)m_batchedDynamicBuffers.size(), &(m_batchedDynamicBuffers[0]));
566 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
567
568 m_batchedDynamicBuffers = vector<GLuint>();
569 }
570
571 for (int i = 0; i < (int)m_unbatchedDynamicBuffers.size(); i++)
572 {
573 gl.deleteBuffers((GLsizei)m_unbatchedDynamicBuffers[i].size(), &(m_unbatchedDynamicBuffers[i][0]));
574 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
575 }
576
577 m_unbatchedDynamicBuffers = vector<vector<GLuint>>();
578
579 m_unbatchedSamplesUs = vector<uint64_t>();
580 m_batchedSamplesUs = vector<uint64_t>();
581 }
582
renderUnbatched(void)583 uint64_t DrawCallBatchingTest::renderUnbatched(void)
584 {
585 const glw::Functions &gl = m_renderCtx.getFunctions();
586 uint64_t beginUs = 0;
587 uint64_t endUs = 0;
588 vector<GLint> dynamicAttributeLocations;
589
590 gl.viewport(0, 0, 32, 32);
591 gl.useProgram(m_program->getProgram());
592
593 // Setup static buffers
594 for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
595 {
596 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
597
598 gl.enableVertexAttribArray(location);
599
600 if (m_spec.useStaticBuffer)
601 {
602 gl.bindBuffer(GL_ARRAY_BUFFER, m_unbatchedStaticBuffers[attribNdx]);
603 gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, NULL);
604 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
605 }
606 else
607 gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, &(m_staticAttributeDatas[attribNdx][0]));
608 }
609
610 // Get locations of dynamic attributes
611 for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
612 {
613 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_dyn" + de::toString(attribNdx)).c_str());
614
615 gl.enableVertexAttribArray(location);
616 dynamicAttributeLocations.push_back(location);
617 }
618
619 if (m_spec.useDrawElements && m_spec.useIndexBuffer && !m_spec.dynamicIndices)
620 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unbatchedStaticIndexBuffer);
621
622 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup initial state for rendering.");
623
624 gl.finish();
625
626 beginUs = deGetMicroseconds();
627
628 for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
629 {
630 for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
631 {
632 if (m_spec.useDynamicBuffer)
633 {
634 gl.bindBuffer(GL_ARRAY_BUFFER, m_unbatchedDynamicBuffers[attribNdx][drawNdx]);
635 gl.vertexAttribPointer(dynamicAttributeLocations[attribNdx], 4, GL_BYTE, GL_TRUE, 0, NULL);
636 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
637 }
638 else
639 gl.vertexAttribPointer(dynamicAttributeLocations[attribNdx], 4, GL_BYTE, GL_TRUE, 0,
640 &(m_dynamicAttributeDatas[attribNdx][m_spec.triangleCount * 3 * drawNdx * 4]));
641 }
642
643 if (m_spec.useDrawElements)
644 {
645 if (m_spec.useIndexBuffer)
646 {
647 if (m_spec.dynamicIndices)
648 {
649 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unbatchedDynamicIndexBuffers[drawNdx]);
650 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, NULL);
651 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
652 }
653 else
654 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, NULL);
655 }
656 else
657 {
658 if (m_spec.dynamicIndices)
659 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE,
660 &(m_dynamicIndexData[drawNdx * m_spec.triangleCount * 3]));
661 else
662 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, &(m_staticIndexData[0]));
663 }
664 }
665 else
666 gl.drawArrays(GL_TRIANGLES, 0, 3 * m_spec.triangleCount);
667 }
668
669 gl.finish();
670
671 endUs = deGetMicroseconds();
672
673 GLU_EXPECT_NO_ERROR(gl.getError(), "Unbatched rendering failed");
674
675 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
676
677 for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
678 {
679 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
680 gl.disableVertexAttribArray(location);
681 }
682
683 for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
684 gl.disableVertexAttribArray(dynamicAttributeLocations[attribNdx]);
685
686 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to reset state after unbatched rendering");
687
688 return endUs - beginUs;
689 }
690
renderBatched(void)691 uint64_t DrawCallBatchingTest::renderBatched(void)
692 {
693 const glw::Functions &gl = m_renderCtx.getFunctions();
694 uint64_t beginUs = 0;
695 uint64_t endUs = 0;
696 vector<GLint> dynamicAttributeLocations;
697
698 gl.viewport(0, 0, 32, 32);
699 gl.useProgram(m_program->getProgram());
700
701 // Setup static buffers
702 for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
703 {
704 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
705
706 gl.enableVertexAttribArray(location);
707
708 if (m_spec.useStaticBuffer)
709 {
710 gl.bindBuffer(GL_ARRAY_BUFFER, m_batchedStaticBuffers[attribNdx]);
711 gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, NULL);
712 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
713 }
714 else
715 gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, &(m_staticAttributeDatas[attribNdx][0]));
716 }
717
718 // Get locations of dynamic attributes
719 for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
720 {
721 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_dyn" + de::toString(attribNdx)).c_str());
722
723 gl.enableVertexAttribArray(location);
724 dynamicAttributeLocations.push_back(location);
725 }
726
727 if (m_spec.useDrawElements && m_spec.useIndexBuffer && !m_spec.dynamicIndices)
728 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_batchedStaticIndexBuffer);
729
730 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup initial state for rendering.");
731
732 gl.finish();
733
734 beginUs = deGetMicroseconds();
735
736 for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
737 {
738 if (m_spec.useDynamicBuffer)
739 {
740 gl.bindBuffer(GL_ARRAY_BUFFER, m_batchedDynamicBuffers[attribute]);
741 gl.vertexAttribPointer(dynamicAttributeLocations[attribute], 4, GL_BYTE, GL_TRUE, 0, NULL);
742 gl.bindBuffer(GL_ARRAY_BUFFER, 0);
743 }
744 else
745 gl.vertexAttribPointer(dynamicAttributeLocations[attribute], 4, GL_BYTE, GL_TRUE, 0,
746 &(m_dynamicAttributeDatas[attribute][0]));
747 }
748
749 if (m_spec.useDrawElements)
750 {
751 if (m_spec.useIndexBuffer)
752 {
753 if (m_spec.dynamicIndices)
754 {
755 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_batchedDynamicIndexBuffer);
756 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, NULL);
757 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
758 }
759 else
760 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, NULL);
761 }
762 else
763 {
764 if (m_spec.dynamicIndices)
765 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE,
766 &(m_dynamicIndexData[0]));
767 else
768 gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE,
769 &(m_staticIndexData[0]));
770 }
771 }
772 else
773 gl.drawArrays(GL_TRIANGLES, 0, 3 * m_spec.triangleCount * m_spec.drawCallCount);
774
775 gl.finish();
776
777 endUs = deGetMicroseconds();
778
779 GLU_EXPECT_NO_ERROR(gl.getError(), "Batched rendering failed");
780
781 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
782
783 for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
784 {
785 GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
786 gl.disableVertexAttribArray(location);
787 }
788
789 for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
790 gl.disableVertexAttribArray(dynamicAttributeLocations[attribNdx]);
791
792 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to reset state after batched rendering");
793
794 return endUs - beginUs;
795 }
796
797 struct Statistics
798 {
799 double mean;
800 double standardDeviation;
801 double standardErrorOfMean;
802 };
803
calculateStats(const vector<uint64_t> & samples)804 Statistics calculateStats(const vector<uint64_t> &samples)
805 {
806 double mean = 0.0;
807
808 for (int i = 0; i < (int)samples.size(); i++)
809 mean += (double)samples[i];
810
811 mean /= (double)samples.size();
812
813 double standardDeviation = 0.0;
814
815 for (int i = 0; i < (int)samples.size(); i++)
816 {
817 double x = (double)samples[i];
818 standardDeviation += (x - mean) * (x - mean);
819 }
820
821 standardDeviation /= (double)samples.size();
822 standardDeviation = std::sqrt(standardDeviation);
823
824 double standardErrorOfMean = standardDeviation / std::sqrt((double)samples.size());
825
826 Statistics stats;
827
828 stats.mean = mean;
829 stats.standardDeviation = standardDeviation;
830 stats.standardErrorOfMean = standardErrorOfMean;
831
832 return stats;
833 }
834
logTestInfo(void)835 void DrawCallBatchingTest::logTestInfo(void)
836 {
837 TestLog &log = m_testCtx.getLog();
838 tcu::ScopedLogSection section(log, "Test info", "Test info");
839
840 log << TestLog::Message << "Rendering using " << (m_spec.useDrawElements ? "glDrawElements()" : "glDrawArrays()")
841 << "." << TestLog::EndMessage;
842
843 if (m_spec.useDrawElements)
844 log << TestLog::Message << "Using " << (m_spec.dynamicIndices ? "dynamic " : "") << "indices from "
845 << (m_spec.useIndexBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
846
847 if (m_spec.staticAttributeCount > 0)
848 log << TestLog::Message << "Using " << m_spec.staticAttributeCount << " static attribute"
849 << (m_spec.staticAttributeCount > 1 ? "s" : "") << " from "
850 << (m_spec.useStaticBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
851
852 if (m_spec.dynamicAttributeCount > 0)
853 log << TestLog::Message << "Using " << m_spec.dynamicAttributeCount << " dynamic attribute"
854 << (m_spec.dynamicAttributeCount > 1 ? "s" : "") << " from "
855 << (m_spec.useDynamicBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
856
857 log << TestLog::Message << "Rendering " << m_spec.drawCallCount << " draw calls with " << m_spec.triangleCount
858 << " triangles per call." << TestLog::EndMessage;
859 }
860
iterate(void)861 tcu::TestCase::IterateResult DrawCallBatchingTest::iterate(void)
862 {
863 if (m_state == STATE_LOG_INFO)
864 {
865 logTestInfo();
866 m_state = STATE_WARMUP_BATCHED;
867 }
868 else if (m_state == STATE_WARMUP_BATCHED)
869 {
870 renderBatched();
871 m_state = STATE_WARMUP_UNBATCHED;
872 }
873 else if (m_state == STATE_WARMUP_UNBATCHED)
874 {
875 renderUnbatched();
876 m_state = STATE_SAMPLE;
877 }
878 else if (m_state == STATE_SAMPLE)
879 {
880 if ((int)m_unbatchedSamplesUs.size() < m_unbatchedSampleCount &&
881 ((double)m_unbatchedSamplesUs.size() / ((double)m_unbatchedSampleCount) <
882 (double)m_batchedSamplesUs.size() / ((double)m_batchedSampleCount) ||
883 (int)m_batchedSamplesUs.size() >= m_batchedSampleCount))
884 m_unbatchedSamplesUs.push_back(renderUnbatched());
885 else if ((int)m_batchedSamplesUs.size() < m_batchedSampleCount)
886 m_batchedSamplesUs.push_back(renderBatched());
887 else
888 m_state = STATE_CALC_CALIBRATION;
889 }
890 else if (m_state == STATE_CALC_CALIBRATION)
891 {
892 TestLog &log = m_testCtx.getLog();
893
894 tcu::ScopedLogSection section(log, ("Sampling iteration " + de::toString(m_sampleIteration)).c_str(),
895 ("Sampling iteration " + de::toString(m_sampleIteration)).c_str());
896 const double targetSEM = 0.02;
897 const double limitSEM = 0.025;
898
899 Statistics unbatchedStats = calculateStats(m_unbatchedSamplesUs);
900 Statistics batchedStats = calculateStats(m_batchedSamplesUs);
901
902 log << TestLog::Message << "Batched samples; Count: " << m_batchedSamplesUs.size()
903 << ", Mean: " << batchedStats.mean << "us, Standard deviation: " << batchedStats.standardDeviation
904 << "us, Standard error of mean: " << batchedStats.standardErrorOfMean << "us("
905 << (batchedStats.standardErrorOfMean / batchedStats.mean) << ")" << TestLog::EndMessage;
906 log << TestLog::Message << "Unbatched samples; Count: " << m_unbatchedSamplesUs.size()
907 << ", Mean: " << unbatchedStats.mean << "us, Standard deviation: " << unbatchedStats.standardDeviation
908 << "us, Standard error of mean: " << unbatchedStats.standardErrorOfMean << "us("
909 << (unbatchedStats.standardErrorOfMean / unbatchedStats.mean) << ")" << TestLog::EndMessage;
910
911 if (m_sampleIteration > 2 ||
912 (m_sampleIteration > 0 && (unbatchedStats.standardErrorOfMean / unbatchedStats.mean) +
913 (batchedStats.standardErrorOfMean / batchedStats.mean) <=
914 2.0 * limitSEM))
915 {
916 if (m_sampleIteration > 2)
917 log << TestLog::Message << "Maximum iteration count reached." << TestLog::EndMessage;
918
919 log << TestLog::Message << "Standard errors in target range." << TestLog::EndMessage;
920 log << TestLog::Message << "Batched/Unbatched ratio: " << (batchedStats.mean / unbatchedStats.mean)
921 << TestLog::EndMessage;
922
923 m_testCtx.setTestResult(QP_TEST_RESULT_PASS,
924 de::floatToString((float)(batchedStats.mean / unbatchedStats.mean), 1).c_str());
925 return STOP;
926 }
927 else
928 {
929 if ((unbatchedStats.standardErrorOfMean / unbatchedStats.mean) > targetSEM)
930 log << TestLog::Message << "Unbatched standard error of mean outside of range." << TestLog::EndMessage;
931
932 if ((batchedStats.standardErrorOfMean / batchedStats.mean) > targetSEM)
933 log << TestLog::Message << "Batched standard error of mean outside of range." << TestLog::EndMessage;
934
935 if (unbatchedStats.standardDeviation > 0.0)
936 {
937 double x = (unbatchedStats.standardDeviation / unbatchedStats.mean) / targetSEM;
938 m_unbatchedSampleCount = std::max((int)m_unbatchedSamplesUs.size(), (int)(x * x));
939 }
940 else
941 m_unbatchedSampleCount = (int)m_unbatchedSamplesUs.size();
942
943 if (batchedStats.standardDeviation > 0.0)
944 {
945 double x = (batchedStats.standardDeviation / batchedStats.mean) / targetSEM;
946 m_batchedSampleCount = std::max((int)m_batchedSamplesUs.size(), (int)(x * x));
947 }
948 else
949 m_batchedSampleCount = (int)m_batchedSamplesUs.size();
950
951 m_batchedSamplesUs.clear();
952 m_unbatchedSamplesUs.clear();
953
954 m_sampleIteration++;
955 m_state = STATE_SAMPLE;
956 }
957 }
958 else
959 DE_ASSERT(false);
960
961 return CONTINUE;
962 }
963
specToName(const DrawCallBatchingTest::TestSpec & spec)964 string specToName(const DrawCallBatchingTest::TestSpec &spec)
965 {
966 std::ostringstream stream;
967
968 DE_ASSERT(!spec.useStaticBuffer || spec.staticAttributeCount > 0);
969 DE_ASSERT(!spec.useDynamicBuffer || spec.dynamicAttributeCount > 0);
970
971 if (spec.staticAttributeCount > 0)
972 stream << spec.staticAttributeCount << "_static_";
973
974 if (spec.useStaticBuffer)
975 stream << (spec.staticAttributeCount == 1 ? "buffer_" : "buffers_");
976
977 if (spec.dynamicAttributeCount > 0)
978 stream << spec.dynamicAttributeCount << "_dynamic_";
979
980 if (spec.useDynamicBuffer)
981 stream << (spec.dynamicAttributeCount == 1 ? "buffer_" : "buffers_");
982
983 stream << spec.triangleCount << "_triangles";
984
985 return stream.str();
986 }
987
specToDescrpition(const DrawCallBatchingTest::TestSpec & spec)988 string specToDescrpition(const DrawCallBatchingTest::TestSpec &spec)
989 {
990 DE_UNREF(spec);
991 return "Test performance of batched rendering against non-batched rendering.";
992 }
993
994 } // namespace
995
DrawCallBatchingTests(Context & context)996 DrawCallBatchingTests::DrawCallBatchingTests(Context &context)
997 : TestCaseGroup(context, "draw_call_batching", "Draw call batching performance tests.")
998 {
999 }
1000
~DrawCallBatchingTests(void)1001 DrawCallBatchingTests::~DrawCallBatchingTests(void)
1002 {
1003 }
1004
init(void)1005 void DrawCallBatchingTests::init(void)
1006 {
1007 int drawCallCounts[] = {10, 100};
1008
1009 int triangleCounts[] = {2, 10};
1010
1011 int staticAttributeCounts[] = {1, 0, 4, 8, 0};
1012
1013 int dynamicAttributeCounts[] = {0, 1, 4, 0, 8};
1014
1015 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(staticAttributeCounts) == DE_LENGTH_OF_ARRAY(dynamicAttributeCounts));
1016
1017 for (int drawType = 0; drawType < 2; drawType++)
1018 {
1019 bool drawElements = (drawType == 1);
1020
1021 for (int indexBufferNdx = 0; indexBufferNdx < 2; indexBufferNdx++)
1022 {
1023 bool useIndexBuffer = (indexBufferNdx == 1);
1024
1025 if (useIndexBuffer && !drawElements)
1026 continue;
1027
1028 for (int dynamicIndexNdx = 0; dynamicIndexNdx < 2; dynamicIndexNdx++)
1029 {
1030 bool dynamicIndices = (dynamicIndexNdx == 1);
1031
1032 if (dynamicIndices && !drawElements)
1033 continue;
1034
1035 if (dynamicIndices && !useIndexBuffer)
1036 continue;
1037
1038 TestCaseGroup *drawTypeGroup = new TestCaseGroup(
1039 m_context,
1040 (string(dynamicIndices ? "dynamic_" : "") + (useIndexBuffer ? "buffer_" : "") +
1041 (drawElements ? "draw_elements" : "draw_arrays"))
1042 .c_str(),
1043 (string("Test batched rendering with ") + (drawElements ? "draw_elements" : "draw_arrays"))
1044 .c_str());
1045
1046 addChild(drawTypeGroup);
1047
1048 for (int drawCallCountNdx = 0; drawCallCountNdx < DE_LENGTH_OF_ARRAY(drawCallCounts);
1049 drawCallCountNdx++)
1050 {
1051 int drawCallCount = drawCallCounts[drawCallCountNdx];
1052
1053 TestCaseGroup *callCountGroup = new TestCaseGroup(
1054 m_context, (de::toString(drawCallCount) + (drawCallCount == 1 ? "_draw" : "_draws")).c_str(),
1055 ("Test batched rendering performance with " + de::toString(drawCallCount) + " draw calls.")
1056 .c_str());
1057 TestCaseGroup *attributeCount1Group =
1058 new TestCaseGroup(m_context, "1_attribute", "Test draw call batching with 1 attribute.");
1059 TestCaseGroup *attributeCount8Group =
1060 new TestCaseGroup(m_context, "8_attributes", "Test draw call batching with 8 attributes.");
1061
1062 callCountGroup->addChild(attributeCount1Group);
1063 callCountGroup->addChild(attributeCount8Group);
1064
1065 drawTypeGroup->addChild(callCountGroup);
1066
1067 for (int attributeCountNdx = 0; attributeCountNdx < DE_LENGTH_OF_ARRAY(dynamicAttributeCounts);
1068 attributeCountNdx++)
1069 {
1070 TestCaseGroup *attributeCountGroup = NULL;
1071
1072 int staticAttributeCount = staticAttributeCounts[attributeCountNdx];
1073 int dynamicAttributeCount = dynamicAttributeCounts[attributeCountNdx];
1074
1075 if (staticAttributeCount + dynamicAttributeCount == 1)
1076 attributeCountGroup = attributeCount1Group;
1077 else if (staticAttributeCount + dynamicAttributeCount == 8)
1078 attributeCountGroup = attributeCount8Group;
1079 else
1080 DE_ASSERT(false);
1081
1082 for (int triangleCountNdx = 0; triangleCountNdx < DE_LENGTH_OF_ARRAY(triangleCounts);
1083 triangleCountNdx++)
1084 {
1085 int triangleCount = triangleCounts[triangleCountNdx];
1086
1087 for (int dynamicBufferNdx = 0; dynamicBufferNdx < 2; dynamicBufferNdx++)
1088 {
1089 bool useDynamicBuffer = (dynamicBufferNdx != 0);
1090
1091 for (int staticBufferNdx = 0; staticBufferNdx < 2; staticBufferNdx++)
1092 {
1093 bool useStaticBuffer = (staticBufferNdx != 0);
1094
1095 DrawCallBatchingTest::TestSpec spec;
1096
1097 spec.useStaticBuffer = useStaticBuffer;
1098 spec.staticAttributeCount = staticAttributeCount;
1099
1100 spec.useDynamicBuffer = useDynamicBuffer;
1101 spec.dynamicAttributeCount = dynamicAttributeCount;
1102
1103 spec.drawCallCount = drawCallCount;
1104 spec.triangleCount = triangleCount;
1105
1106 spec.useDrawElements = drawElements;
1107 spec.useIndexBuffer = useIndexBuffer;
1108 spec.dynamicIndices = dynamicIndices;
1109
1110 if (spec.useStaticBuffer && spec.staticAttributeCount == 0)
1111 continue;
1112
1113 if (spec.useDynamicBuffer && spec.dynamicAttributeCount == 0)
1114 continue;
1115
1116 attributeCountGroup->addChild(new DrawCallBatchingTest(
1117 m_context, specToName(spec).c_str(), specToDescrpition(spec).c_str(), spec));
1118 }
1119 }
1120 }
1121 }
1122 }
1123 }
1124 }
1125 }
1126 }
1127
1128 } // namespace Performance
1129 } // namespace gles2
1130 } // namespace deqp
1131