|  | /* | 
|  | * Copyright (C) 2010 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | * | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Hardware Composer stress test | 
|  | * | 
|  | * Performs a pseudo-random (prandom) sequence of operations to the | 
|  | * Hardware Composer (HWC), for a specified number of passes or for | 
|  | * a specified period of time.  By default the period of time is FLT_MAX, | 
|  | * so that the number of passes will take precedence. | 
|  | * | 
|  | * The passes are grouped together, where (pass / passesPerGroup) specifies | 
|  | * which group a particular pass is in.  This causes every passesPerGroup | 
|  | * worth of sequential passes to be within the same group.  Computationally | 
|  | * intensive operations are performed just once at the beginning of a group | 
|  | * of passes and then used by all the passes in that group.  This is done | 
|  | * so as to increase both the average and peak rate of graphic operations, | 
|  | * by moving computationally intensive operations to the beginning of a group. | 
|  | * In particular, at the start of each group of passes a set of | 
|  | * graphic buffers are created, then used by the first and remaining | 
|  | * passes of that group of passes. | 
|  | * | 
|  | * The per-group initialization of the graphic buffers is performed | 
|  | * by a function called initFrames.  This function creates an array | 
|  | * of smart pointers to the graphic buffers, in the form of a vector | 
|  | * of vectors.  The array is accessed in row major order, so each | 
|  | * row is a vector of smart pointers.  All the pointers of a single | 
|  | * row point to graphic buffers which use the same pixel format and | 
|  | * have the same dimension, although it is likely that each one is | 
|  | * filled with a different color.  This is done so that after doing | 
|  | * the first HWC prepare then set call, subsequent set calls can | 
|  | * be made with each of the layer handles changed to a different | 
|  | * graphic buffer within the same row.  Since the graphic buffers | 
|  | * in a particular row have the same pixel format and dimension, | 
|  | * additional HWC set calls can be made, without having to perform | 
|  | * an HWC prepare call. | 
|  | * | 
|  | * This test supports the following command-line options: | 
|  | * | 
|  | *   -v        Verbose | 
|  | *   -s num    Starting pass | 
|  | *   -e num    Ending pass | 
|  | *   -p num    Execute the single pass specified by num | 
|  | *   -n num    Number of set operations to perform after each prepare operation | 
|  | *   -t float  Maximum time in seconds to execute the test | 
|  | *   -d float  Delay in seconds performed after each set operation | 
|  | *   -D float  Delay in seconds performed after the last pass is executed | 
|  | * | 
|  | * Typically the test is executed for a large range of passes.  By default | 
|  | * passes 0 through 99999 (100,000 passes) are executed.  Although this test | 
|  | * does not validate the generated image, at times it is useful to reexecute | 
|  | * a particular pass and leave the displayed image on the screen for an | 
|  | * extended period of time.  This can be done either by setting the -s | 
|  | * and -e options to the desired pass, along with a large value for -D. | 
|  | * This can also be done via the -p option, again with a large value for | 
|  | * the -D options. | 
|  | * | 
|  | * So far this test only contains code to create graphic buffers with | 
|  | * a continuous solid color.  Although this test is unable to validate the | 
|  | * image produced, any image that contains other than rectangles of a solid | 
|  | * color are incorrect.  Note that the rectangles may use a transparent | 
|  | * color and have a blending operation that causes the color in overlapping | 
|  | * rectangles to be mixed.  In such cases the overlapping portions may have | 
|  | * a different color from the rest of the rectangle. | 
|  | */ | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <assert.h> | 
|  | #include <cerrno> | 
|  | #include <cmath> | 
|  | #include <cstdlib> | 
|  | #include <ctime> | 
|  | #include <libgen.h> | 
|  | #include <sched.h> | 
|  | #include <sstream> | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <vector> | 
|  |  | 
|  | #include <sys/syscall.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/wait.h> | 
|  |  | 
|  | #include <EGL/egl.h> | 
|  | #include <EGL/eglext.h> | 
|  | #include <GLES2/gl2.h> | 
|  | #include <GLES2/gl2ext.h> | 
|  |  | 
|  | #include <ui/FramebufferNativeWindow.h> | 
|  | #include <ui/GraphicBuffer.h> | 
|  |  | 
|  | #define LOG_TAG "hwcStressTest" | 
|  | #include <utils/Log.h> | 
|  | #include <testUtil.h> | 
|  |  | 
|  | #include <hardware/hwcomposer.h> | 
|  |  | 
|  | #include <glTestLib.h> | 
|  | #include "hwcTestLib.h" | 
|  |  | 
|  | using namespace std; | 
|  | using namespace android; | 
|  |  | 
|  | const float maxSizeRatio = 1.3;  // Graphic buffers can be upto this munch | 
|  | // larger than the default screen size | 
|  | const unsigned int passesPerGroup = 10; // A group of passes all use the same | 
|  | // graphic buffers | 
|  |  | 
|  | // Ratios at which rare and frequent conditions should be produced | 
|  | const float rareRatio = 0.1; | 
|  | const float freqRatio = 0.9; | 
|  |  | 
|  | // Defaults for command-line options | 
|  | const bool defaultVerbose = false; | 
|  | const unsigned int defaultStartPass = 0; | 
|  | const unsigned int defaultEndPass = 99999; | 
|  | const unsigned int defaultPerPassNumSet = 10; | 
|  | const float defaultPerSetDelay = 0.0; // Default delay after each set | 
|  | // operation.  Default delay of | 
|  | // zero used so as to perform the | 
|  | // the set operations as quickly | 
|  | // as possible. | 
|  | const float defaultEndDelay = 2.0; // Default delay between completion of | 
|  | // final pass and restart of framework | 
|  | const float defaultDuration = FLT_MAX; // A fairly long time, so that | 
|  | // range of passes will have | 
|  | // precedence | 
|  |  | 
|  | // Command-line option settings | 
|  | static bool verbose = defaultVerbose; | 
|  | static unsigned int startPass = defaultStartPass; | 
|  | static unsigned int endPass = defaultEndPass; | 
|  | static unsigned int numSet = defaultPerPassNumSet; | 
|  | static float perSetDelay = defaultPerSetDelay; | 
|  | static float endDelay = defaultEndDelay; | 
|  | static float duration = defaultDuration; | 
|  |  | 
|  | // Command-line mutual exclusion detection flags. | 
|  | // Corresponding flag set true once an option is used. | 
|  | bool eFlag, sFlag, pFlag; | 
|  |  | 
|  | #define MAXSTR               100 | 
|  | #define MAXCMD               200 | 
|  | #define BITSPERBYTE            8 // TODO: Obtain from <values.h>, once | 
|  | // it has been added | 
|  |  | 
|  | #define CMD_STOP_FRAMEWORK   "stop 2>&1" | 
|  | #define CMD_START_FRAMEWORK  "start 2>&1" | 
|  |  | 
|  | #define NUMA(a) (sizeof(a) / sizeof(a [0])) | 
|  | #define MEMCLR(addr, size) do { \ | 
|  | memset((addr), 0, (size)); \ | 
|  | } while (0) | 
|  |  | 
|  | // File scope constants | 
|  | const unsigned int blendingOps[] = { | 
|  | HWC_BLENDING_NONE, | 
|  | HWC_BLENDING_PREMULT, | 
|  | HWC_BLENDING_COVERAGE, | 
|  | }; | 
|  | const unsigned int layerFlags[] = { | 
|  | HWC_SKIP_LAYER, | 
|  | }; | 
|  | const vector<unsigned int> vecLayerFlags(layerFlags, | 
|  | layerFlags + NUMA(layerFlags)); | 
|  |  | 
|  | const unsigned int transformFlags[] = { | 
|  | HWC_TRANSFORM_FLIP_H, | 
|  | HWC_TRANSFORM_FLIP_V, | 
|  | HWC_TRANSFORM_ROT_90, | 
|  | // ROT_180 & ROT_270 intentionally not listed, because they | 
|  | // they are formed from combinations of the flags already listed. | 
|  | }; | 
|  | const vector<unsigned int> vecTransformFlags(transformFlags, | 
|  | transformFlags + NUMA(transformFlags)); | 
|  |  | 
|  | // File scope globals | 
|  | static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE | | 
|  | GraphicBuffer::USAGE_SW_WRITE_RARELY; | 
|  | static hwc_composer_device_t *hwcDevice; | 
|  | static EGLDisplay dpy; | 
|  | static EGLSurface surface; | 
|  | static EGLint width, height; | 
|  | static vector <vector <sp<GraphicBuffer> > > frames; | 
|  |  | 
|  | // File scope prototypes | 
|  | void init(void); | 
|  | void initFrames(unsigned int seed); | 
|  | template <class T> vector<T> vectorRandSelect(const vector<T>& vec, size_t num); | 
|  | template <class T> T vectorOr(const vector<T>& vec); | 
|  |  | 
|  | /* | 
|  | * Main | 
|  | * | 
|  | * Performs the following high-level sequence of operations: | 
|  | * | 
|  | *   1. Command-line parsing | 
|  | * | 
|  | *   2. Initialization | 
|  | * | 
|  | *   3. For each pass: | 
|  | * | 
|  | *        a. If pass is first pass or in a different group from the | 
|  | *           previous pass, initialize the array of graphic buffers. | 
|  | * | 
|  | *        b. Create a HWC list with room to specify a prandomly | 
|  | *           selected number of layers. | 
|  | * | 
|  | *        c. Select a subset of the rows from the graphic buffer array, | 
|  | *           such that there is a unique row to be used for each | 
|  | *           of the layers in the HWC list. | 
|  | * | 
|  | *        d. Prandomly fill in the HWC list with handles | 
|  | *           selected from any of the columns of the selected row. | 
|  | * | 
|  | *        e. Pass the populated list to the HWC prepare call. | 
|  | * | 
|  | *        f. Pass the populated list to the HWC set call. | 
|  | * | 
|  | *        g. If additional set calls are to be made, then for each | 
|  | *           additional set call, select a new set of handles and | 
|  | *           perform the set call. | 
|  | */ | 
|  | int | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int rv, opt; | 
|  | char *chptr; | 
|  | unsigned int pass; | 
|  | char cmd[MAXCMD]; | 
|  | struct timeval startTime, currentTime, delta; | 
|  |  | 
|  | testSetLogCatTag(LOG_TAG); | 
|  |  | 
|  | // Parse command line arguments | 
|  | while ((opt = getopt(argc, argv, "vp:d:D:n:s:e:t:?h")) != -1) { | 
|  | switch (opt) { | 
|  | case 'd': // Delay after each set operation | 
|  | perSetDelay = strtod(optarg, &chptr); | 
|  | if ((*chptr != '\0') || (perSetDelay < 0.0)) { | 
|  | testPrintE("Invalid command-line specified per pass delay of: " | 
|  | "%s", optarg); | 
|  | exit(1); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'D': // End of test delay | 
|  | // Delay between completion of final pass and restart | 
|  | // of framework | 
|  | endDelay = strtod(optarg, &chptr); | 
|  | if ((*chptr != '\0') || (endDelay < 0.0)) { | 
|  | testPrintE("Invalid command-line specified end of test delay " | 
|  | "of: %s", optarg); | 
|  | exit(2); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 't': // Duration | 
|  | duration = strtod(optarg, &chptr); | 
|  | if ((*chptr != '\0') || (duration < 0.0)) { | 
|  | testPrintE("Invalid command-line specified duration of: %s", | 
|  | optarg); | 
|  | exit(3); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'n': // Num set operations per pass | 
|  | numSet = strtoul(optarg, &chptr, 10); | 
|  | if (*chptr != '\0') { | 
|  | testPrintE("Invalid command-line specified num set per pass " | 
|  | "of: %s", optarg); | 
|  | exit(4); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 's': // Starting Pass | 
|  | sFlag = true; | 
|  | if (pFlag) { | 
|  | testPrintE("Invalid combination of command-line options."); | 
|  | testPrintE("  The -p option is mutually exclusive from the"); | 
|  | testPrintE("  -s and -e options."); | 
|  | exit(5); | 
|  | } | 
|  | startPass = strtoul(optarg, &chptr, 10); | 
|  | if (*chptr != '\0') { | 
|  | testPrintE("Invalid command-line specified starting pass " | 
|  | "of: %s", optarg); | 
|  | exit(6); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'e': // Ending Pass | 
|  | eFlag = true; | 
|  | if (pFlag) { | 
|  | testPrintE("Invalid combination of command-line options."); | 
|  | testPrintE("  The -p option is mutually exclusive from the"); | 
|  | testPrintE("  -s and -e options."); | 
|  | exit(7); | 
|  | } | 
|  | endPass = strtoul(optarg, &chptr, 10); | 
|  | if (*chptr != '\0') { | 
|  | testPrintE("Invalid command-line specified ending pass " | 
|  | "of: %s", optarg); | 
|  | exit(8); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'p': // Run a single specified pass | 
|  | pFlag = true; | 
|  | if (sFlag || eFlag) { | 
|  | testPrintE("Invalid combination of command-line options."); | 
|  | testPrintE("  The -p option is mutually exclusive from the"); | 
|  | testPrintE("  -s and -e options."); | 
|  | exit(9); | 
|  | } | 
|  | startPass = endPass = strtoul(optarg, &chptr, 10); | 
|  | if (*chptr != '\0') { | 
|  | testPrintE("Invalid command-line specified pass of: %s", | 
|  | optarg); | 
|  | exit(10); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'v': // Verbose | 
|  | verbose = true; | 
|  | break; | 
|  |  | 
|  | case 'h': // Help | 
|  | case '?': | 
|  | default: | 
|  | testPrintE("  %s [options]", basename(argv[0])); | 
|  | testPrintE("    options:"); | 
|  | testPrintE("      -p Execute specified pass"); | 
|  | testPrintE("      -s Starting pass"); | 
|  | testPrintE("      -e Ending pass"); | 
|  | testPrintE("      -t Duration"); | 
|  | testPrintE("      -d Delay after each set operation"); | 
|  | testPrintE("      -D End of test delay"); | 
|  | testPrintE("      -n Num set operations per pass"); | 
|  | testPrintE("      -v Verbose"); | 
|  | exit(((optopt == 0) || (optopt == '?')) ? 0 : 11); | 
|  | } | 
|  | } | 
|  | if (endPass < startPass) { | 
|  | testPrintE("Unexpected ending pass before starting pass"); | 
|  | testPrintE("  startPass: %u endPass: %u", startPass, endPass); | 
|  | exit(12); | 
|  | } | 
|  | if (argc != optind) { | 
|  | testPrintE("Unexpected command-line postional argument"); | 
|  | testPrintE("  %s [-s start_pass] [-e end_pass] [-t duration]", | 
|  | basename(argv[0])); | 
|  | exit(13); | 
|  | } | 
|  | testPrintI("duration: %g", duration); | 
|  | testPrintI("startPass: %u", startPass); | 
|  | testPrintI("endPass: %u", endPass); | 
|  | testPrintI("numSet: %u", numSet); | 
|  |  | 
|  | // Stop framework | 
|  | rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK); | 
|  | if (rv >= (signed) sizeof(cmd) - 1) { | 
|  | testPrintE("Command too long for: %s", CMD_STOP_FRAMEWORK); | 
|  | exit(14); | 
|  | } | 
|  | testExecCmd(cmd); | 
|  | testDelay(1.0); // TODO - need means to query whether asyncronous stop | 
|  | // framework operation has completed.  For now, just wait | 
|  | // a long time. | 
|  |  | 
|  | init(); | 
|  |  | 
|  | // For each pass | 
|  | gettimeofday(&startTime, NULL); | 
|  | for (pass = startPass; pass <= endPass; pass++) { | 
|  | // Stop if duration of work has already been performed | 
|  | gettimeofday(¤tTime, NULL); | 
|  | delta = tvDelta(&startTime, ¤tTime); | 
|  | if (tv2double(&delta) > duration) { break; } | 
|  |  | 
|  | // Regenerate a new set of test frames when this pass is | 
|  | // either the first pass or is in a different group then | 
|  | // the previous pass.  A group of passes are passes that | 
|  | // all have the same quotient when their pass number is | 
|  | // divided by passesPerGroup. | 
|  | if ((pass == startPass) | 
|  | || ((pass / passesPerGroup) != ((pass - 1) / passesPerGroup))) { | 
|  | initFrames(pass / passesPerGroup); | 
|  | } | 
|  |  | 
|  | testPrintI("==== Starting pass: %u", pass); | 
|  |  | 
|  | // Cause deterministic sequence of prandom numbers to be | 
|  | // generated for this pass. | 
|  | srand48(pass); | 
|  |  | 
|  | hwc_layer_list_t *list; | 
|  | list = hwcTestCreateLayerList(testRandMod(frames.size()) + 1); | 
|  | if (list == NULL) { | 
|  | testPrintE("hwcTestCreateLayerList failed"); | 
|  | exit(20); | 
|  | } | 
|  |  | 
|  | // Prandomly select a subset of frames to be used by this pass. | 
|  | vector <vector <sp<GraphicBuffer> > > selectedFrames; | 
|  | selectedFrames = vectorRandSelect(frames, list->numHwLayers); | 
|  |  | 
|  | // Any transform tends to create a layer that the hardware | 
|  | // composer is unable to support and thus has to leave for | 
|  | // SurfaceFlinger.  Place heavy bias on specifying no transforms. | 
|  | bool noTransform = testRandFract() > rareRatio; | 
|  |  | 
|  | for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { | 
|  | unsigned int idx = testRandMod(selectedFrames[n1].size()); | 
|  | sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; | 
|  | hwc_layer_t *layer = &list->hwLayers[n1]; | 
|  | layer->handle = gBuf->handle; | 
|  |  | 
|  | layer->blending = blendingOps[testRandMod(NUMA(blendingOps))]; | 
|  | layer->flags = (testRandFract() > rareRatio) ? 0 | 
|  | : vectorOr(vectorRandSelect(vecLayerFlags, | 
|  | testRandMod(vecLayerFlags.size() + 1))); | 
|  | layer->transform = (noTransform || testRandFract() > rareRatio) ? 0 | 
|  | : vectorOr(vectorRandSelect(vecTransformFlags, | 
|  | testRandMod(vecTransformFlags.size() + 1))); | 
|  | layer->sourceCrop.left = testRandMod(gBuf->getWidth()); | 
|  | layer->sourceCrop.top = testRandMod(gBuf->getHeight()); | 
|  | layer->sourceCrop.right = layer->sourceCrop.left | 
|  | + testRandMod(gBuf->getWidth() - layer->sourceCrop.left) + 1; | 
|  | layer->sourceCrop.bottom = layer->sourceCrop.top | 
|  | + testRandMod(gBuf->getHeight() - layer->sourceCrop.top) + 1; | 
|  | layer->displayFrame.left = testRandMod(width); | 
|  | layer->displayFrame.top = testRandMod(height); | 
|  | layer->displayFrame.right = layer->displayFrame.left | 
|  | + testRandMod(width - layer->displayFrame.left) + 1; | 
|  | layer->displayFrame.bottom = layer->displayFrame.top | 
|  | + testRandMod(height - layer->displayFrame.top) + 1; | 
|  |  | 
|  | // Increase the frequency that a scale factor of 1.0 from | 
|  | // the sourceCrop to displayFrame occurs.  This is the | 
|  | // most common scale factor used by applications and would | 
|  | // be rarely produced by this stress test without this | 
|  | // logic. | 
|  | if (testRandFract() <= freqRatio) { | 
|  | // Only change to scale factor to 1.0 if both the | 
|  | // width and height will fit. | 
|  | int sourceWidth = layer->sourceCrop.right | 
|  | - layer->sourceCrop.left; | 
|  | int sourceHeight = layer->sourceCrop.bottom | 
|  | - layer->sourceCrop.top; | 
|  | if (((layer->displayFrame.left + sourceWidth) <= width) | 
|  | && ((layer->displayFrame.top + sourceHeight) <= height)) { | 
|  | layer->displayFrame.right = layer->displayFrame.left | 
|  | + sourceWidth; | 
|  | layer->displayFrame.bottom = layer->displayFrame.top | 
|  | + sourceHeight; | 
|  | } | 
|  | } | 
|  |  | 
|  | layer->visibleRegionScreen.numRects = 1; | 
|  | layer->visibleRegionScreen.rects = &layer->displayFrame; | 
|  | } | 
|  |  | 
|  | // Perform prepare operation | 
|  | if (verbose) { testPrintI("Prepare:"); hwcTestDisplayList(list); } | 
|  | hwcDevice->prepare(hwcDevice, list); | 
|  | if (verbose) { | 
|  | testPrintI("Post Prepare:"); | 
|  | hwcTestDisplayListPrepareModifiable(list); | 
|  | } | 
|  |  | 
|  | // Turn off the geometry changed flag | 
|  | list->flags &= ~HWC_GEOMETRY_CHANGED; | 
|  |  | 
|  | // Perform the set operation(s) | 
|  | if (verbose) {testPrintI("Set:"); } | 
|  | for (unsigned int n1 = 0; n1 < numSet; n1++) { | 
|  | if (verbose) { hwcTestDisplayListHandles(list); } | 
|  | hwcDevice->set(hwcDevice, dpy, surface, list); | 
|  |  | 
|  | // Prandomly select a new set of handles | 
|  | for (unsigned int n1 = 0; n1 < list->numHwLayers; n1++) { | 
|  | unsigned int idx = testRandMod(selectedFrames[n1].size()); | 
|  | sp<GraphicBuffer> gBuf = selectedFrames[n1][idx]; | 
|  | hwc_layer_t *layer = &list->hwLayers[n1]; | 
|  | layer->handle = (native_handle_t *) gBuf->handle; | 
|  | } | 
|  |  | 
|  | testDelay(perSetDelay); | 
|  | } | 
|  |  | 
|  | hwcTestFreeLayerList(list); | 
|  | testPrintI("==== Completed pass: %u", pass); | 
|  | } | 
|  |  | 
|  | testDelay(endDelay); | 
|  |  | 
|  | // Start framework | 
|  | rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK); | 
|  | if (rv >= (signed) sizeof(cmd) - 1) { | 
|  | testPrintE("Command too long for: %s", CMD_START_FRAMEWORK); | 
|  | exit(21); | 
|  | } | 
|  | testExecCmd(cmd); | 
|  |  | 
|  | testPrintI("Successfully completed %u passes", pass - startPass); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void init(void) | 
|  | { | 
|  | srand48(0); // Defensively set pseudo random number generator. | 
|  | // Should not need to set this, because a stress test | 
|  | // sets the seed on each pass.  Defensively set it here | 
|  | // so that future code that uses pseudo random numbers | 
|  | // before the first pass will be deterministic. | 
|  |  | 
|  | hwcTestInitDisplay(verbose, &dpy, &surface, &width, &height); | 
|  |  | 
|  | hwcTestOpenHwc(&hwcDevice); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialize Frames | 
|  | * | 
|  | * Creates an array of graphic buffers, within the global variable | 
|  | * named frames.  The graphic buffers are contained within a vector of | 
|  | * vectors.  All the graphic buffers in a particular row are of the same | 
|  | * format and dimension.  Each graphic buffer is uniformly filled with a | 
|  | * prandomly selected color.  It is likely that each buffer, even | 
|  | * in the same row, will be filled with a unique color. | 
|  | */ | 
|  | void initFrames(unsigned int seed) | 
|  | { | 
|  | int rv; | 
|  | const size_t maxRows = 5; | 
|  | const size_t minCols = 2;  // Need at least double buffering | 
|  | const size_t maxCols = 4;  // One more than triple buffering | 
|  |  | 
|  | if (verbose) { testPrintI("initFrames seed: %u", seed); } | 
|  | srand48(seed); | 
|  | size_t rows = testRandMod(maxRows) + 1; | 
|  |  | 
|  | frames.clear(); | 
|  | frames.resize(rows); | 
|  |  | 
|  | for (unsigned int row = 0; row < rows; row++) { | 
|  | // All frames within a row have to have the same format and | 
|  | // dimensions.  Width and height need to be >= 1. | 
|  | unsigned int formatIdx = testRandMod(NUMA(hwcTestGraphicFormat)); | 
|  | const struct hwcTestGraphicFormat *formatPtr | 
|  | = &hwcTestGraphicFormat[formatIdx]; | 
|  | int format = formatPtr->format; | 
|  |  | 
|  | // Pick width and height, which must be >= 1 and the size | 
|  | // mod the wMod/hMod value must be equal to 0. | 
|  | size_t w = (width * maxSizeRatio) * testRandFract(); | 
|  | size_t h = (height * maxSizeRatio) * testRandFract(); | 
|  | w = max(1u, w); | 
|  | h = max(1u, h); | 
|  | if ((w % formatPtr->wMod) != 0) { | 
|  | w += formatPtr->wMod - (w % formatPtr->wMod); | 
|  | } | 
|  | if ((h % formatPtr->hMod) != 0) { | 
|  | h += formatPtr->hMod - (h % formatPtr->hMod); | 
|  | } | 
|  | if (verbose) { | 
|  | testPrintI("  frame %u width: %u height: %u format: %u %s", | 
|  | row, w, h, format, hwcTestGraphicFormat2str(format)); | 
|  | } | 
|  |  | 
|  | size_t cols = testRandMod((maxCols + 1) - minCols) + minCols; | 
|  | frames[row].resize(cols); | 
|  | for (unsigned int col = 0; col < cols; col++) { | 
|  | ColorFract color(testRandFract(), testRandFract(), testRandFract()); | 
|  | float alpha = testRandFract(); | 
|  |  | 
|  | frames[row][col] = new GraphicBuffer(w, h, format, texUsage); | 
|  | if ((rv = frames[row][col]->initCheck()) != NO_ERROR) { | 
|  | testPrintE("GraphicBuffer initCheck failed, rv: %i", rv); | 
|  | testPrintE("  frame %u width: %u height: %u format: %u %s", | 
|  | row, w, h, format, hwcTestGraphicFormat2str(format)); | 
|  | exit(80); | 
|  | } | 
|  |  | 
|  | hwcTestFillColor(frames[row][col].get(), color, alpha); | 
|  | if (verbose) { | 
|  | testPrintI("    buf: %p handle: %p color: %s alpha: %f", | 
|  | frames[row][col].get(), frames[row][col]->handle, | 
|  | string(color).c_str(), alpha); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Vector Random Select | 
|  | * | 
|  | * Prandomly selects and returns num elements from vec. | 
|  | */ | 
|  | template <class T> | 
|  | vector<T> vectorRandSelect(const vector<T>& vec, size_t num) | 
|  | { | 
|  | vector<T> rv = vec; | 
|  |  | 
|  | while (rv.size() > num) { | 
|  | rv.erase(rv.begin() + testRandMod(rv.size())); | 
|  | } | 
|  |  | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Vector Or | 
|  | * | 
|  | * Or's togethen the values of each element of vec and returns the result. | 
|  | */ | 
|  | template <class T> | 
|  | T vectorOr(const vector<T>& vec) | 
|  | { | 
|  | T rv = 0; | 
|  |  | 
|  | for (size_t n1 = 0; n1 < vec.size(); n1++) { | 
|  | rv |= vec[n1]; | 
|  | } | 
|  |  | 
|  | return rv; | 
|  | } |