1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program EGL 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 Memory object allocation stress tests 22 *//*--------------------------------------------------------------------*/ 23 24 #include "teglMemoryStressTests.hpp" 25 26 #include "tcuTestLog.hpp" 27 #include "tcuCommandLine.hpp" 28 29 #include "deRandom.hpp" 30 #include "deClock.h" 31 #include "deString.h" 32 33 #include "gluDefs.hpp" 34 #include "glwFunctions.hpp" 35 #include "glwDefs.hpp" 36 #include "glwEnums.hpp" 37 38 #include "egluUtil.hpp" 39 40 #include "eglwLibrary.hpp" 41 #include "eglwEnums.hpp" 42 43 #include <vector> 44 #include <string> 45 46 using std::vector; 47 using std::string; 48 using tcu::TestLog; 49 50 using namespace eglw; 51 52 namespace deqp 53 { 54 namespace egl 55 { 56 57 namespace 58 { 59 60 enum ObjectType 61 { 62 OBJECTTYPE_PBUFFER = (1<<0), 63 OBJECTTYPE_CONTEXT = (1<<1), 64 65 // OBJECTTYPE_WINDOW, 66 // OBJECTTYPE_PIXMAP, 67 }; 68 69 class MemoryAllocator 70 { 71 public: 72 MemoryAllocator (EglTestContext& eglTestCtx, EGLDisplay display, EGLConfig config, int seed, ObjectType types, int minWidth, int minHeight, int maxWidth, int maxHeight, bool use); 73 ~MemoryAllocator (void); 74 75 bool allocateUntilFailure (void); 76 int getAllocationCount (void) const { return (int)(m_pbuffers.size() + m_contexts.size()); } 77 int getContextCount (void) const { return (int)m_contexts.size(); } 78 int getPBufferCount (void) const { return (int)m_pbuffers.size(); } 79 const string& getErrorString (void) const { return m_errorString; } 80 81 private: 82 void allocatePBuffer (void); 83 void allocateContext (void); 84 85 EglTestContext& m_eglTestCtx; 86 EGLDisplay m_display; 87 EGLConfig m_config; 88 glw::Functions m_gl; 89 90 de::Random m_rnd; 91 bool m_failed; 92 string m_errorString; 93 94 ObjectType m_types; 95 int m_minWidth; 96 int m_minHeight; 97 int m_maxWidth; 98 int m_maxHeight; 99 bool m_use; 100 101 vector<EGLSurface> m_pbuffers; 102 vector<EGLContext> m_contexts; 103 }; 104 105 MemoryAllocator::MemoryAllocator (EglTestContext& eglTestCtx, EGLDisplay display, EGLConfig config, int seed, ObjectType types, int minWidth, int minHeight, int maxWidth, int maxHeight, bool use) 106 : m_eglTestCtx (eglTestCtx) 107 , m_display (display) 108 , m_config (config) 109 110 , m_rnd (seed) 111 , m_failed (false) 112 113 , m_types (types) 114 , m_minWidth (minWidth) 115 , m_minHeight (minHeight) 116 , m_maxWidth (maxWidth) 117 , m_maxHeight (maxHeight) 118 , m_use (use) 119 { 120 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 121 } 122 123 MemoryAllocator::~MemoryAllocator (void) 124 { 125 const Library& egl = m_eglTestCtx.getLibrary(); 126 127 for (vector<EGLSurface>::const_iterator iter = m_pbuffers.begin(); iter != m_pbuffers.end(); ++iter) 128 egl.destroySurface(m_display, *iter); 129 130 m_pbuffers.clear(); 131 132 for (vector<EGLContext>::const_iterator iter = m_contexts.begin(); iter != m_contexts.end(); ++iter) 133 egl.destroyContext(m_display, *iter); 134 135 m_contexts.clear(); 136 } 137 138 bool MemoryAllocator::allocateUntilFailure (void) 139 { 140 const deUint64 timeLimitUs = 10000000; // 10s 141 deUint64 beginTimeUs = deGetMicroseconds(); 142 vector<ObjectType> types; 143 144 if ((m_types & OBJECTTYPE_CONTEXT) != 0) 145 types.push_back(OBJECTTYPE_CONTEXT); 146 147 if ((m_types & OBJECTTYPE_PBUFFER) != 0) 148 types.push_back(OBJECTTYPE_PBUFFER); 149 150 // If objects should be used. Create one of both at beginning to allow using them. 151 if (m_contexts.size() == 0 && m_pbuffers.size() == 0 && m_use) 152 { 153 allocateContext(); 154 allocatePBuffer(); 155 } 156 157 while (!m_failed) 158 { 159 ObjectType type = m_rnd.choose<ObjectType>(types.begin(), types.end()); 160 161 switch (type) 162 { 163 case OBJECTTYPE_PBUFFER: 164 allocatePBuffer(); 165 break; 166 167 case OBJECTTYPE_CONTEXT: 168 allocateContext(); 169 break; 170 171 default: 172 DE_ASSERT(false); 173 } 174 175 if (deGetMicroseconds() - beginTimeUs > timeLimitUs) 176 return true; 177 } 178 179 return false; 180 } 181 182 void MemoryAllocator::allocatePBuffer (void) 183 { 184 // Reserve space for new allocations 185 try 186 { 187 m_pbuffers.reserve(m_pbuffers.size() + 1); 188 } 189 catch (const std::bad_alloc&) 190 { 191 m_errorString = "std::bad_alloc when allocating more space for testcase. Out of host memory."; 192 m_failed = true; 193 return; 194 } 195 196 // Allocate pbuffer 197 try 198 { 199 const Library& egl = m_eglTestCtx.getLibrary(); 200 const EGLint width = m_rnd.getInt(m_minWidth, m_maxWidth); 201 const EGLint height = m_rnd.getInt(m_minHeight, m_maxHeight); 202 const EGLint attribList[] = 203 { 204 EGL_WIDTH, width, 205 EGL_HEIGHT, height, 206 EGL_NONE 207 }; 208 209 EGLSurface surface = egl.createPbufferSurface(m_display, m_config, attribList); 210 EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface"); 211 212 DE_ASSERT(surface != EGL_NO_SURFACE); 213 214 m_pbuffers.push_back(surface); 215 216 if (m_use && m_contexts.size() > 0) 217 { 218 EGLContext context = m_rnd.choose<EGLContext>(m_contexts.begin(), m_contexts.end()); 219 const float red = m_rnd.getFloat(); 220 const float green = m_rnd.getFloat(); 221 const float blue = m_rnd.getFloat(); 222 const float alpha = m_rnd.getFloat(); 223 224 EGLU_CHECK_CALL(egl, makeCurrent(m_display, surface, surface, context)); 225 226 m_gl.clearColor(red, green, blue, alpha); 227 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClearColor()"); 228 229 m_gl.clear(GL_COLOR_BUFFER_BIT); 230 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClear()"); 231 232 EGLU_CHECK_CALL(egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 233 } 234 } 235 catch (const eglu::Error& error) 236 { 237 if (error.getError() == EGL_BAD_ALLOC) 238 { 239 m_errorString = "eglCreatePbufferSurface returned EGL_BAD_ALLOC"; 240 m_failed = true; 241 return; 242 } 243 else 244 throw; 245 } 246 } 247 248 void MemoryAllocator::allocateContext (void) 249 { 250 // Reserve space for new allocations 251 try 252 { 253 m_contexts.reserve(m_contexts.size() + 1); 254 } 255 catch (const std::bad_alloc&) 256 { 257 m_errorString = "std::bad_alloc when allocating more space for testcase. Out of host memory."; 258 m_failed = true; 259 return; 260 } 261 262 // Allocate context 263 try 264 { 265 const Library& egl = m_eglTestCtx.getLibrary(); 266 const EGLint attribList[] = 267 { 268 EGL_CONTEXT_CLIENT_VERSION, 2, 269 EGL_NONE 270 }; 271 272 EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API)); 273 EGLContext context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attribList); 274 EGLU_CHECK_MSG(egl, "eglCreateContext"); 275 276 DE_ASSERT(context != EGL_NO_CONTEXT); 277 278 m_contexts.push_back(context); 279 280 if (m_use && m_pbuffers.size() > 0) 281 { 282 EGLSurface surface = m_rnd.choose<EGLSurface>(m_pbuffers.begin(), m_pbuffers.end()); 283 const float red = m_rnd.getFloat(); 284 const float green = m_rnd.getFloat(); 285 const float blue = m_rnd.getFloat(); 286 const float alpha = m_rnd.getFloat(); 287 288 EGLU_CHECK_CALL(egl, makeCurrent(m_display, surface, surface, context)); 289 290 m_gl.clearColor(red, green, blue, alpha); 291 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClearColor()"); 292 293 m_gl.clear(GL_COLOR_BUFFER_BIT); 294 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClear()"); 295 296 EGLU_CHECK_CALL(egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 297 } 298 } 299 catch (const eglu::Error& error) 300 { 301 if (error.getError() == EGL_BAD_ALLOC) 302 { 303 m_errorString = "eglCreateContext returned EGL_BAD_ALLOC"; 304 m_failed = true; 305 return; 306 } 307 else 308 throw; 309 } 310 } 311 312 } // anonymous 313 314 class MemoryStressCase : public TestCase 315 { 316 public: 317 struct Spec 318 { 319 ObjectType types; 320 int minWidth; 321 int minHeight; 322 int maxWidth; 323 int maxHeight; 324 bool use; 325 }; 326 327 MemoryStressCase (EglTestContext& eglTestCtx, Spec spec, const char* name, const char* description); 328 void init (void); 329 void deinit (void); 330 IterateResult iterate (void); 331 332 private: 333 Spec m_spec; 334 vector<int> m_allocationCounts; 335 MemoryAllocator* m_allocator; 336 337 int m_iteration; 338 int m_iterationCount; 339 int m_seed; 340 EGLDisplay m_display; 341 EGLConfig m_config; 342 }; 343 344 MemoryStressCase::MemoryStressCase (EglTestContext& eglTestCtx, Spec spec, const char* name, const char* description) 345 : TestCase (eglTestCtx, name, description) 346 , m_spec (spec) 347 , m_allocator (NULL) 348 , m_iteration (0) 349 , m_iterationCount (10) 350 , m_seed (deStringHash(name)) 351 , m_display (EGL_NO_DISPLAY) 352 , m_config (DE_NULL) 353 { 354 } 355 356 void MemoryStressCase::init (void) 357 { 358 const Library& egl = m_eglTestCtx.getLibrary(); 359 EGLint configCount = 0; 360 const EGLint attribList[] = 361 { 362 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 363 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 364 EGL_NONE 365 }; 366 367 if (!m_testCtx.getCommandLine().isOutOfMemoryTestEnabled()) 368 { 369 m_testCtx.getLog() << TestLog::Message << "Tests that exhaust memory are disabled, use --deqp-test-oom=enable command line option to enable." << TestLog::EndMessage; 370 throw tcu::NotSupportedError("OOM tests disabled"); 371 } 372 373 m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); 374 375 EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount)); 376 377 TCU_CHECK(configCount != 0); 378 } 379 380 void MemoryStressCase::deinit (void) 381 { 382 delete m_allocator; 383 m_allocator = DE_NULL; 384 385 if (m_display != EGL_NO_DISPLAY) 386 { 387 m_eglTestCtx.getLibrary().terminate(m_display); 388 m_display = EGL_NO_DISPLAY; 389 } 390 } 391 392 TestCase::IterateResult MemoryStressCase::iterate (void) 393 { 394 TestLog& log = m_testCtx.getLog(); 395 396 if (m_iteration < m_iterationCount) 397 { 398 try 399 { 400 if (!m_allocator) 401 m_allocator = new MemoryAllocator(m_eglTestCtx, m_display, m_config, m_seed, m_spec.types, m_spec.minWidth, m_spec.minHeight, m_spec.maxWidth, m_spec.maxHeight, m_spec.use); 402 403 if (m_allocator->allocateUntilFailure()) 404 { 405 log << TestLog::Message << "Couldn't exhaust memory before timeout. Allocated " << m_allocator->getAllocationCount() << " objects." << TestLog::EndMessage; 406 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 407 408 delete m_allocator; 409 m_allocator = NULL; 410 411 return STOP; 412 } 413 414 log << TestLog::Message << "Iteration " << m_iteration << ": Allocated " << m_allocator->getAllocationCount() << " objects; " << m_allocator->getContextCount() << " contexts, " << m_allocator->getPBufferCount() << " PBuffers." << TestLog::EndMessage; 415 log << TestLog::Message << "Got expected error: " << m_allocator->getErrorString() << TestLog::EndMessage; 416 m_allocationCounts.push_back(m_allocator->getAllocationCount()); 417 418 delete m_allocator; 419 m_allocator = NULL; 420 421 m_iteration++; 422 423 return CONTINUE; 424 } catch (...) 425 { 426 log << TestLog::Message << "Iteration " << m_iteration << ": Allocated " << m_allocator->getAllocationCount() << " objects; " << m_allocator->getContextCount() << " contexts, " << m_allocator->getPBufferCount() << " PBuffers." << TestLog::EndMessage; 427 log << TestLog::Message << "Unexpected error" << TestLog::EndMessage; 428 throw; 429 } 430 } 431 else 432 { 433 // Analyze number of passed allocations. 434 int min = m_allocationCounts[0]; 435 int max = m_allocationCounts[0]; 436 437 float threshold = 50.0f; 438 439 for (int allocNdx = 0; allocNdx < (int)m_allocationCounts.size(); allocNdx++) 440 { 441 min = deMin32(m_allocationCounts[allocNdx], min); 442 max = deMax32(m_allocationCounts[allocNdx], max); 443 } 444 445 if (min == 0 && max != 0) 446 { 447 log << TestLog::Message << "Allocation count zero" << TestLog::EndMessage; 448 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 449 } 450 else 451 { 452 float change = (float)(min - max) / ((float)(max)); 453 454 if (change > threshold) 455 { 456 log << TestLog::Message << "Allocated objects max: " << max << ", min: " << min << ", difference: " << change << "% threshold: " << threshold << "%" << TestLog::EndMessage; 457 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Allocation count variation"); 458 } 459 else 460 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 461 } 462 463 return STOP; 464 } 465 } 466 467 MemoryStressTests::MemoryStressTests (EglTestContext& eglTestCtx) 468 : TestCaseGroup(eglTestCtx, "memory", "Memory allocation stress tests") 469 { 470 } 471 472 void MemoryStressTests::init (void) 473 { 474 // Check small pbuffers 256x256 475 { 476 MemoryStressCase::Spec spec; 477 478 spec.types = OBJECTTYPE_PBUFFER; 479 spec.minWidth = 256; 480 spec.minHeight = 256; 481 spec.maxWidth = 256; 482 spec.maxHeight = 256; 483 spec.use = false; 484 485 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_256x256", "PBuffer allocation stress tests")); 486 } 487 488 // Check small pbuffers 256x256 and use them 489 { 490 MemoryStressCase::Spec spec; 491 492 spec.types = OBJECTTYPE_PBUFFER; 493 spec.minWidth = 256; 494 spec.minHeight = 256; 495 spec.maxWidth = 256; 496 spec.maxHeight = 256; 497 spec.use = true; 498 499 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_256x256_use", "PBuffer allocation stress tests")); 500 } 501 502 // Check big pbuffers 1024x1024 503 { 504 MemoryStressCase::Spec spec; 505 506 spec.types = OBJECTTYPE_PBUFFER; 507 spec.minWidth = 1024; 508 spec.minHeight = 1024; 509 spec.maxWidth = 1024; 510 spec.maxHeight = 1024; 511 spec.use = false; 512 513 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_1024x1024", "PBuffer allocation stress tests")); 514 } 515 516 // Check big pbuffers 1024x1024 and use them 517 { 518 MemoryStressCase::Spec spec; 519 520 spec.types = OBJECTTYPE_PBUFFER; 521 spec.minWidth = 1024; 522 spec.minHeight = 1024; 523 spec.maxWidth = 1024; 524 spec.maxHeight = 1024; 525 spec.use = true; 526 527 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_1024x1024_use", "PBuffer allocation stress tests")); 528 } 529 530 // Check different sized pbuffers 531 { 532 MemoryStressCase::Spec spec; 533 534 spec.types = OBJECTTYPE_PBUFFER; 535 spec.minWidth = 64; 536 spec.minHeight = 64; 537 spec.maxWidth = 1024; 538 spec.maxHeight = 1024; 539 spec.use = false; 540 541 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer", "PBuffer allocation stress tests")); 542 } 543 544 // Check different sized pbuffers and use them 545 { 546 MemoryStressCase::Spec spec; 547 548 spec.types = OBJECTTYPE_PBUFFER; 549 spec.minWidth = 64; 550 spec.minHeight = 64; 551 spec.maxWidth = 1024; 552 spec.maxHeight = 1024; 553 spec.use = true; 554 555 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_use", "PBuffer allocation stress tests")); 556 } 557 558 // Check contexts 559 { 560 MemoryStressCase::Spec spec; 561 562 spec.types = OBJECTTYPE_CONTEXT; 563 spec.minWidth = 1024; 564 spec.minHeight = 1024; 565 spec.maxWidth = 1024; 566 spec.maxHeight = 1024; 567 spec.use = false; 568 569 addChild(new MemoryStressCase(m_eglTestCtx, spec, "context", "Context allocation stress tests")); 570 } 571 572 // Check contexts and use them 573 { 574 MemoryStressCase::Spec spec; 575 576 spec.types = OBJECTTYPE_CONTEXT; 577 spec.minWidth = 1024; 578 spec.minHeight = 1024; 579 spec.maxWidth = 1024; 580 spec.maxHeight = 1024; 581 spec.use = true; 582 583 addChild(new MemoryStressCase(m_eglTestCtx, spec, "context_use", "Context allocation stress tests")); 584 } 585 586 // Check contexts and pbuffers 587 { 588 MemoryStressCase::Spec spec; 589 590 spec.types = (ObjectType)(OBJECTTYPE_PBUFFER|OBJECTTYPE_CONTEXT); 591 spec.minWidth = 64; 592 spec.minHeight = 64; 593 spec.maxWidth = 1024; 594 spec.maxHeight = 1024; 595 spec.use = false; 596 597 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_context", "PBuffer and context allocation stress tests")); 598 } 599 600 // Check contexts and pbuffers and use 601 { 602 MemoryStressCase::Spec spec; 603 604 spec.types = (ObjectType)(OBJECTTYPE_PBUFFER|OBJECTTYPE_CONTEXT); 605 spec.minWidth = 64; 606 spec.minHeight = 64; 607 spec.maxWidth = 1024; 608 spec.maxHeight = 1024; 609 spec.use = true; 610 611 addChild(new MemoryStressCase(m_eglTestCtx, spec, "pbuffer_context_use", "PBuffer and context allocation stress tests")); 612 } 613 } 614 615 } // egl 616 } // deqp 617