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