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