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 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 165 DrawCallBatchingTest::~DrawCallBatchingTest (void) 166 { 167 deinit(); 168 } 169 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 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 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 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 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 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 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 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 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 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 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 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 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 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 963 DrawCallBatchingTests::DrawCallBatchingTests (Context& context) 964 : TestCaseGroup(context, "draw_call_batching", "Draw call batching performance tests.") 965 { 966 } 967 968 DrawCallBatchingTests::~DrawCallBatchingTests (void) 969 { 970 } 971 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