1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 /* 19 * Hardware Composer stress test 20 * 21 * Performs a pseudo-random (prandom) sequence of operations to the 22 * Hardware Composer (HWC), for a specified number of passes or for 23 * a specified period of time. By default the period of time is FLT_MAX, 24 * so that the number of passes will take precedence. 25 * 26 * The passes are grouped together, where (pass / passesPerGroup) specifies 27 * which group a particular pass is in. This causes every passesPerGroup 28 * worth of sequential passes to be within the same group. Computationally 29 * intensive operations are performed just once at the beginning of a group 30 * of passes and then used by all the passes in that group. This is done 31 * so as to increase both the average and peak rate of graphic operations, 32 * by moving computationally intensive operations to the beginning of a group. 33 * In particular, at the start of each group of passes a set of 34 * graphic buffers are created, then used by the first and remaining 35 * passes of that group of passes. 36 * 37 * The per-group initialization of the graphic buffers is performed 38 * by a function called initFrames. This function creates an array 39 * of smart pointers to the graphic buffers, in the form of a vector 40 * of vectors. The array is accessed in row major order, so each 41 * row is a vector of smart pointers. All the pointers of a single 42 * row point to graphic buffers which use the same pixel format and 43 * have the same dimension, although it is likely that each one is 44 * filled with a different color. This is done so that after doing 45 * the first HWC prepare then set call, subsequent set calls can 46 * be made with each of the layer handles changed to a different 47 * graphic buffer within the same row. Since the graphic buffers 48 * in a particular row have the same pixel format and dimension, 49 * additional HWC set calls can be made, without having to perform 50 * an HWC prepare call. 51 * 52 * This test supports the following command-line options: 53 * 54 * -v Verbose 55 * -s num Starting pass 56 * -e num Ending pass 57 * -p num Execute the single pass specified by num 58 * -n num Number of set operations to perform after each prepare operation 59 * -t float Maximum time in seconds to execute the test 60 * -d float Delay in seconds performed after each set operation 61 * -D float Delay in seconds performed after the last pass is executed 62 * 63 * Typically the test is executed for a large range of passes. By default 64 * passes 0 through 99999 (100,000 passes) are executed. Although this test 65 * does not validate the generated image, at times it is useful to reexecute 66 * a particular pass and leave the displayed image on the screen for an 67 * extended period of time. This can be done either by setting the -s 68 * and -e options to the desired pass, along with a large value for -D. 69 * This can also be done via the -p option, again with a large value for 70 * the -D options. 71 * 72 * So far this test only contains code to create graphic buffers with 73 * a continuous solid color. Although this test is unable to validate the 74 * image produced, any image that contains other than rectangles of a solid 75 * color are incorrect. Note that the rectangles may use a transparent 76 * color and have a blending operation that causes the color in overlapping 77 * rectangles to be mixed. In such cases the overlapping portions may have 78 * a different color from the rest of the rectangle. 79 */ 80 81 #define LOG_TAG "hwcStressTest" 82 83 #include <algorithm> 84 #include <assert.h> 85 #include <cerrno> 86 #include <cmath> 87 #include <cstdlib> 88 #include <ctime> 89 #include <libgen.h> 90 #include <sched.h> 91 #include <sstream> 92 #include <stdint.h> 93 #include <string.h> 94 #include <unistd.h> 95 #include <vector> 96 97 #include <sys/syscall.h> 98 #include <sys/types.h> 99 #include <sys/wait.h> 100 101 #include <EGL/egl.h> 102 #include <EGL/eglext.h> 103 #include <GLES2/gl2.h> 104 #include <GLES2/gl2ext.h> 105 106 #include <ui/GraphicBuffer.h> 107 108 #include <utils/Log.h> 109 #include <testUtil.h> 110 111 #include <hardware/hwcomposer.h> 112 113 #include <glTestLib.h> 114 #include "hwcTestLib.h" 115 116 using namespace std; 117 using namespace android; 118 119 const float maxSizeRatio = 1.3; // Graphic buffers can be upto this munch 120 // larger than the default screen size 121 const unsigned int passesPerGroup = 10; // A group of passes all use the same 122 // graphic buffers 123 124 // Ratios at which rare and frequent conditions should be produced 125 const float rareRatio = 0.1; 126 const float freqRatio = 0.9; 127 128 // Defaults for command-line options 129 const bool defaultVerbose = false; 130 const unsigned int defaultStartPass = 0; 131 const unsigned int defaultEndPass = 99999; 132 const unsigned int defaultPerPassNumSet = 10; 133 const float defaultPerSetDelay = 0.0; // Default delay after each set 134 // operation. Default delay of 135 // zero used so as to perform the 136 // the set operations as quickly 137 // as possible. 138 const float defaultEndDelay = 2.0; // Default delay between completion of 139 // final pass and restart of framework 140 const float defaultDuration = FLT_MAX; // A fairly long time, so that 141 // range of passes will have 142 // precedence 143 144 // Command-line option settings 145 static bool verbose = defaultVerbose; 146 static unsigned int startPass = defaultStartPass; 147 static unsigned int endPass = defaultEndPass; 148 static unsigned int numSet = defaultPerPassNumSet; 149 static float perSetDelay = defaultPerSetDelay; 150 static float endDelay = defaultEndDelay; 151 static float duration = defaultDuration; 152 153 // Command-line mutual exclusion detection flags. 154 // Corresponding flag set true once an option is used. 155 bool eFlag, sFlag, pFlag; 156 157 #define MAXSTR 100 158 #define MAXCMD 200 159 #define BITSPERBYTE 8 // TODO: Obtain from <values.h>, once 160 // it has been added 161 162 #define CMD_STOP_FRAMEWORK "stop 2>&1" 163 #define CMD_START_FRAMEWORK "start 2>&1" 164 165 #define NUMA(a) (sizeof(a) / sizeof(a [0])) 166 #define MEMCLR(addr, size) do { \ 167 memset((addr), 0, (size)); \ 168 } while (0) 169 170 // File scope constants 171 const unsigned int blendingOps[] = { 172 HWC_BLENDING_NONE, 173 HWC_BLENDING_PREMULT, 174 HWC_BLENDING_COVERAGE, 175 }; 176 const unsigned int layerFlags[] = { 177 HWC_SKIP_LAYER, 178 }; 179 const vector<unsigned int> vecLayerFlags(layerFlags, 180 layerFlags + NUMA(layerFlags)); 181 182 const unsigned int transformFlags[] = { 183 HWC_TRANSFORM_FLIP_H, 184 HWC_TRANSFORM_FLIP_V, 185 HWC_TRANSFORM_ROT_90, 186 // ROT_180 & ROT_270 intentionally not listed, because they 187 // they are formed from combinations of the flags already listed. 188 }; 189 const vector<unsigned int> vecTransformFlags(transformFlags, 190 transformFlags + NUMA(transformFlags)); 191 192 // File scope globals 193 static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE | 194 GraphicBuffer::USAGE_SW_WRITE_RARELY; 195 static hwc_composer_device_1_t *hwcDevice; 196 static EGLDisplay dpy; 197 static EGLSurface surface; 198 static EGLint width, height; 199 static vector <vector <sp<GraphicBuffer> > > frames; 200 201 // File scope prototypes 202 void init(void); 203 void initFrames(unsigned int seed); 204 template <class T> vector<T> vectorRandSelect(const vector<T>& vec, size_t num); 205 template <class T> T vectorOr(const vector<T>& vec); 206 207 /* 208 * Main 209 * 210 * Performs the following high-level sequence of operations: 211 * 212 * 1. Command-line parsing 213 * 214 * 2. Initialization 215 * 216 * 3. For each pass: 217 * 218 * a. If pass is first pass or in a different group from the 219 * previous pass, initialize the array of graphic buffers. 220 * 221 * b. Create a HWC list with room to specify a prandomly 222 * selected number of layers. 223 * 224 * c. Select a subset of the rows from the graphic buffer array, 225 * such that there is a unique row to be used for each 226 * of the layers in the HWC list. 227 * 228 * d. Prandomly fill in the HWC list with handles 229 * selected from any of the columns of the selected row. 230 * 231 * e. Pass the populated list to the HWC prepare call. 232 * 233 * f. Pass the populated list to the HWC set call. 234 * 235 * g. If additional set calls are to be made, then for each 236 * additional set call, select a new set of handles and 237 * perform the set call. 238 */ 239 int 240 main(int argc, char *argv[]) 241 { 242 int rv, opt; 243 char *chptr; 244 unsigned int pass; 245 char cmd[MAXCMD]; 246 struct timeval startTime, currentTime, delta; 247 248 testSetLogCatTag(LOG_TAG); 249 250 // Parse command line arguments 251 while ((opt = getopt(argc, argv, "vp:d:D:n:s:e:t:?h")) != -1) { 252 switch (opt) { 253 case 'd': // Delay after each set operation 254 perSetDelay = strtod(optarg, &chptr); 255 if ((*chptr != '\0') || (perSetDelay < 0.0)) { 256 testPrintE("Invalid command-line specified per pass delay of: " 257 "%s", optarg); 258 exit(1); 259 } 260 break; 261 262 case 'D': // End of test delay 263 // Delay between completion of final pass and restart 264 // of framework 265 endDelay = strtod(optarg, &chptr); 266 if ((*chptr != '\0') || (endDelay < 0.0)) { 267 testPrintE("Invalid command-line specified end of test delay " 268 "of: %s", optarg); 269 exit(2); 270 } 271 break; 272 273 case 't': // Duration 274 duration = strtod(optarg, &chptr); 275 if ((*chptr != '\0') || (duration < 0.0)) { 276 testPrintE("Invalid command-line specified duration of: %s", 277 optarg); 278 exit(3); 279 } 280 break; 281 282 case 'n': // Num set operations per pass 283 numSet = strtoul(optarg, &chptr, 10); 284 if (*chptr != '\0') { 285 testPrintE("Invalid command-line specified num set per pass " 286 "of: %s", optarg); 287 exit(4); 288 } 289 break; 290 291 case 's': // Starting Pass 292 sFlag = true; 293 if (pFlag) { 294 testPrintE("Invalid combination of command-line options."); 295 testPrintE(" The -p option is mutually exclusive from the"); 296 testPrintE(" -s and -e options."); 297 exit(5); 298 } 299 startPass = strtoul(optarg, &chptr, 10); 300 if (*chptr != '\0') { 301 testPrintE("Invalid command-line specified starting pass " 302 "of: %s", optarg); 303 exit(6); 304 } 305 break; 306 307 case 'e': // Ending Pass 308 eFlag = true; 309 if (pFlag) { 310 testPrintE("Invalid combination of command-line options."); 311 testPrintE(" The -p option is mutually exclusive from the"); 312 testPrintE(" -s and -e options."); 313 exit(7); 314 } 315 endPass = strtoul(optarg, &chptr, 10); 316 if (*chptr != '\0') { 317 testPrintE("Invalid command-line specified ending pass " 318 "of: %s", optarg); 319 exit(8); 320 } 321 break; 322 323 case 'p': // Run a single specified pass 324 pFlag = true; 325 if (sFlag || eFlag) { 326 testPrintE("Invalid combination of command-line options."); 327 testPrintE(" The -p option is mutually exclusive from the"); 328 testPrintE(" -s and -e options."); 329 exit(9); 330 } 331 startPass = endPass = strtoul(optarg, &chptr, 10); 332 if (*chptr != '\0') { 333 testPrintE("Invalid command-line specified pass of: %s", 334 optarg); 335 exit(10); 336 } 337 break; 338 339 case 'v': // Verbose 340 verbose = true; 341 break; 342 343 case 'h': // Help 344 case '?': 345 default: 346 testPrintE(" %s [options]", basename(argv[0])); 347 testPrintE(" options:"); 348 testPrintE(" -p Execute specified pass"); 349 testPrintE(" -s Starting pass"); 350 testPrintE(" -e Ending pass"); 351 testPrintE(" -t Duration"); 352 testPrintE(" -d Delay after each set operation"); 353 testPrintE(" -D End of test delay"); 354 testPrintE(" -n Num set operations per pass"); 355 testPrintE(" -v Verbose"); 356 exit(((optopt == 0) || (optopt == '?')) ? 0 : 11); 357 } 358 } 359 if (endPass < startPass) { 360 testPrintE("Unexpected ending pass before starting pass"); 361 testPrintE(" startPass: %u endPass: %u", startPass, endPass); 362 exit(12); 363 } 364 if (argc != optind) { 365 testPrintE("Unexpected command-line postional argument"); 366 testPrintE(" %s [-s start_pass] [-e end_pass] [-t duration]", 367 basename(argv[0])); 368 exit(13); 369 } 370 testPrintI("duration: %g", duration); 371 testPrintI("startPass: %u", startPass); 372 testPrintI("endPass: %u", endPass); 373 testPrintI("numSet: %u", numSet); 374 375 // Stop framework 376 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK); 377 if (rv >= (signed) sizeof(cmd) - 1) { 378 testPrintE("Command too long for: %s", CMD_STOP_FRAMEWORK); 379 exit(14); 380 } 381 testExecCmd(cmd); 382 testDelay(1.0); // TODO - need means to query whether asyncronous stop 383 // framework operation has completed. For now, just wait 384 // a long time. 385 386 init(); 387 388 // For each pass 389 gettimeofday(&startTime, NULL); 390 for (pass = startPass; pass <= endPass; pass++) { 391 // Stop if duration of work has already been performed 392 gettimeofday(¤tTime, NULL); 393 delta = tvDelta(&startTime, ¤tTime); 394 if (tv2double(&delta) > duration) { break; } 395 396 // Regenerate a new set of test frames when this pass is 397 // either the first pass or is in a different group then 398 // the previous pass. A group of passes are passes that 399 // all have the same quotient when their pass number is 400 // divided by passesPerGroup. 401 if ((pass == startPass) 402 || ((pass / passesPerGroup) != ((pass - 1) / passesPerGroup))) { 403 initFrames(pass / passesPerGroup); 404 } 405 406 testPrintI("==== Starting pass: %u", pass); 407 408 // Cause deterministic sequence of prandom numbers to be 409 // generated for this pass. 410 srand48(pass); 411 412 hwc_display_contents_1_t *list; 413 list = hwcTestCreateLayerList(testRandMod(frames.size()) + 1); 414 if (list == NULL) { 415 testPrintE("hwcTestCreateLayerList failed"); 416 exit(20); 417 } 418 419 // Prandomly select a subset of frames to be used by this pass. 420 vector <vector <sp<GraphicBuffer> > > selectedFrames; 421 selectedFrames = vectorRandSelect(frames, list->numHwLayers); 422 423 // Any transform tends to create a layer that the hardware 424 // composer is unable to support and thus has to leave for 425 // SurfaceFlinger. Place heavy bias on specifying no transforms. 426 bool noTransform = testRandFract() > rareRatio; 427 428 for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { 429 unsigned int idx = testRandMod(selectedFrames[n1].size()); 430 sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; 431 hwc_layer_1_t *layer = &list->hwLayers[n1]; 432 layer->handle = gBuf->handle; 433 434 layer->blending = blendingOps[testRandMod(NUMA(blendingOps))]; 435 layer->flags = (testRandFract() > rareRatio) ? 0 436 : vectorOr(vectorRandSelect(vecLayerFlags, 437 testRandMod(vecLayerFlags.size() + 1))); 438 layer->transform = (noTransform || testRandFract() > rareRatio) ? 0 439 : vectorOr(vectorRandSelect(vecTransformFlags, 440 testRandMod(vecTransformFlags.size() + 1))); 441 layer->sourceCrop.left = testRandMod(gBuf->getWidth()); 442 layer->sourceCrop.top = testRandMod(gBuf->getHeight()); 443 layer->sourceCrop.right = layer->sourceCrop.left 444 + testRandMod(gBuf->getWidth() - layer->sourceCrop.left) + 1; 445 layer->sourceCrop.bottom = layer->sourceCrop.top 446 + testRandMod(gBuf->getHeight() - layer->sourceCrop.top) + 1; 447 layer->displayFrame.left = testRandMod(width); 448 layer->displayFrame.top = testRandMod(height); 449 layer->displayFrame.right = layer->displayFrame.left 450 + testRandMod(width - layer->displayFrame.left) + 1; 451 layer->displayFrame.bottom = layer->displayFrame.top 452 + testRandMod(height - layer->displayFrame.top) + 1; 453 454 // Increase the frequency that a scale factor of 1.0 from 455 // the sourceCrop to displayFrame occurs. This is the 456 // most common scale factor used by applications and would 457 // be rarely produced by this stress test without this 458 // logic. 459 if (testRandFract() <= freqRatio) { 460 // Only change to scale factor to 1.0 if both the 461 // width and height will fit. 462 int sourceWidth = layer->sourceCrop.right 463 - layer->sourceCrop.left; 464 int sourceHeight = layer->sourceCrop.bottom 465 - layer->sourceCrop.top; 466 if (((layer->displayFrame.left + sourceWidth) <= width) 467 && ((layer->displayFrame.top + sourceHeight) <= height)) { 468 layer->displayFrame.right = layer->displayFrame.left 469 + sourceWidth; 470 layer->displayFrame.bottom = layer->displayFrame.top 471 + sourceHeight; 472 } 473 } 474 475 layer->visibleRegionScreen.numRects = 1; 476 layer->visibleRegionScreen.rects = &layer->displayFrame; 477 } 478 479 // Perform prepare operation 480 if (verbose) { testPrintI("Prepare:"); hwcTestDisplayList(list); } 481 hwcDevice->prepare(hwcDevice, 1, &list); 482 if (verbose) { 483 testPrintI("Post Prepare:"); 484 hwcTestDisplayListPrepareModifiable(list); 485 } 486 487 // Turn off the geometry changed flag 488 list->flags &= ~HWC_GEOMETRY_CHANGED; 489 490 // Perform the set operation(s) 491 if (verbose) {testPrintI("Set:"); } 492 for (unsigned int n1 = 0; n1 < numSet; n1++) { 493 if (verbose) { hwcTestDisplayListHandles(list); } 494 list->dpy = dpy; 495 list->sur = surface; 496 hwcDevice->set(hwcDevice, 1, &list); 497 498 // Prandomly select a new set of handles 499 for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { 500 unsigned int idx = testRandMod(selectedFrames[n1].size()); 501 sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; 502 hwc_layer_1_t *layer = &list->hwLayers[n1]; 503 layer->handle = (native_handle_t *) gBuf->handle; 504 } 505 506 testDelay(perSetDelay); 507 } 508 509 hwcTestFreeLayerList(list); 510 testPrintI("==== Completed pass: %u", pass); 511 } 512 513 testDelay(endDelay); 514 515 // Start framework 516 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK); 517 if (rv >= (signed) sizeof(cmd) - 1) { 518 testPrintE("Command too long for: %s", CMD_START_FRAMEWORK); 519 exit(21); 520 } 521 testExecCmd(cmd); 522 523 testPrintI("Successfully completed %u passes", pass - startPass); 524 525 return 0; 526 } 527 528 void init(void) 529 { 530 srand48(0); // Defensively set pseudo random number generator. 531 // Should not need to set this, because a stress test 532 // sets the seed on each pass. Defensively set it here 533 // so that future code that uses pseudo random numbers 534 // before the first pass will be deterministic. 535 536 hwcTestInitDisplay(verbose, &dpy, &surface, &width, &height); 537 538 hwcTestOpenHwc(&hwcDevice); 539 } 540 541 /* 542 * Initialize Frames 543 * 544 * Creates an array of graphic buffers, within the global variable 545 * named frames. The graphic buffers are contained within a vector of 546 * vectors. All the graphic buffers in a particular row are of the same 547 * format and dimension. Each graphic buffer is uniformly filled with a 548 * prandomly selected color. It is likely that each buffer, even 549 * in the same row, will be filled with a unique color. 550 */ 551 void initFrames(unsigned int seed) 552 { 553 int rv; 554 const size_t maxRows = 5; 555 const size_t minCols = 2; // Need at least double buffering 556 const size_t maxCols = 4; // One more than triple buffering 557 558 if (verbose) { testPrintI("initFrames seed: %u", seed); } 559 srand48(seed); 560 size_t rows = testRandMod(maxRows) + 1; 561 562 frames.clear(); 563 frames.resize(rows); 564 565 for (unsigned int row = 0; row < rows; row++) { 566 // All frames within a row have to have the same format and 567 // dimensions. Width and height need to be >= 1. 568 unsigned int formatIdx = testRandMod(NUMA(hwcTestGraphicFormat)); 569 const struct hwcTestGraphicFormat *formatPtr 570 = &hwcTestGraphicFormat[formatIdx]; 571 int format = formatPtr->format; 572 573 // Pick width and height, which must be >= 1 and the size 574 // mod the wMod/hMod value must be equal to 0. 575 size_t w = (width * maxSizeRatio) * testRandFract(); 576 size_t h = (height * maxSizeRatio) * testRandFract(); 577 w = max(size_t(1u), w); 578 h = max(size_t(1u), h); 579 if ((w % formatPtr->wMod) != 0) { 580 w += formatPtr->wMod - (w % formatPtr->wMod); 581 } 582 if ((h % formatPtr->hMod) != 0) { 583 h += formatPtr->hMod - (h % formatPtr->hMod); 584 } 585 if (verbose) { 586 testPrintI(" frame %u width: %u height: %u format: %u %s", 587 row, w, h, format, hwcTestGraphicFormat2str(format)); 588 } 589 590 size_t cols = testRandMod((maxCols + 1) - minCols) + minCols; 591 frames[row].resize(cols); 592 for (unsigned int col = 0; col < cols; col++) { 593 ColorFract color(testRandFract(), testRandFract(), testRandFract()); 594 float alpha = testRandFract(); 595 596 frames[row][col] = new GraphicBuffer(w, h, format, texUsage); 597 if ((rv = frames[row][col]->initCheck()) != NO_ERROR) { 598 testPrintE("GraphicBuffer initCheck failed, rv: %i", rv); 599 testPrintE(" frame %u width: %u height: %u format: %u %s", 600 row, w, h, format, hwcTestGraphicFormat2str(format)); 601 exit(80); 602 } 603 604 hwcTestFillColor(frames[row][col].get(), color, alpha); 605 if (verbose) { 606 testPrintI(" buf: %p handle: %p color: %s alpha: %f", 607 frames[row][col].get(), frames[row][col]->handle, 608 string(color).c_str(), alpha); 609 } 610 } 611 } 612 } 613 614 /* 615 * Vector Random Select 616 * 617 * Prandomly selects and returns num elements from vec. 618 */ 619 template <class T> 620 vector<T> vectorRandSelect(const vector<T>& vec, size_t num) 621 { 622 vector<T> rv = vec; 623 624 while (rv.size() > num) { 625 rv.erase(rv.begin() + testRandMod(rv.size())); 626 } 627 628 return rv; 629 } 630 631 /* 632 * Vector Or 633 * 634 * Or's togethen the values of each element of vec and returns the result. 635 */ 636 template <class T> 637 T vectorOr(const vector<T>& vec) 638 { 639 T rv = 0; 640 641 for (size_t n1 = 0; n1 < vec.size(); n1++) { 642 rv |= vec[n1]; 643 } 644 645 return rv; 646 } 647