summaryrefslogtreecommitdiffstats
path: root/opengl
diff options
context:
space:
mode:
authorLouis Huemiller <lhuemill@google.com>2010-11-22 18:05:30 -0800
committerLouis Huemiller <lhuemill@google.com>2010-11-29 14:08:57 -0800
commit98027ada3a55ddf32bbd0153de71849d5ca8b4df (patch)
treee48e574bb91ca015b91db4ee7a0bdf5e347a8779 /opengl
parent656a23ba2bfab62aec4e682a998f4f39a3ef0087 (diff)
downloadframeworks_base-98027ada3a55ddf32bbd0153de71849d5ca8b4df.zip
frameworks_base-98027ada3a55ddf32bbd0153de71849d5ca8b4df.tar.gz
frameworks_base-98027ada3a55ddf32bbd0153de71849d5ca8b4df.tar.bz2
Hardware Composer Stress Test
Change-Id: Ief08b22dacd052483f10754b3ab61aa09ca28b17
Diffstat (limited to 'opengl')
-rw-r--r--opengl/tests/hwc/Android.mk27
-rw-r--r--opengl/tests/hwc/hwc_stress.cpp1193
2 files changed, 1220 insertions, 0 deletions
diff --git a/opengl/tests/hwc/Android.mk b/opengl/tests/hwc/Android.mk
new file mode 100644
index 0000000..743dbf1
--- /dev/null
+++ b/opengl/tests/hwc/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= hwc_stress.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv2 \
+ libui \
+ libhardware \
+
+LOCAL_STATIC_LIBRARIES := \
+ libtestUtil \
+
+LOCAL_C_INCLUDES += \
+ system/extras/tests/include \
+ hardware/libhardware/include \
+
+LOCAL_MODULE:= hwc_stress
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativestresstest
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+include $(BUILD_NATIVE_TEST)
diff --git a/opengl/tests/hwc/hwc_stress.cpp b/opengl/tests/hwc/hwc_stress.cpp
new file mode 100644
index 0000000..d119734
--- /dev/null
+++ b/opengl/tests/hwc/hwc_stress.cpp
@@ -0,0 +1,1193 @@
+/*
+ * 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 <arpa/inet.h> // For ntohl() and htonl()
+
+#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>
+#include <ui/EGLUtils.h>
+
+#define LOG_TAG "hwcStressTest"
+#include <utils/Log.h>
+#include <testUtil.h>
+
+#include <hardware/hwcomposer.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
+const float rareRatio = 0.1; // Ratio at which rare conditions are produced.
+
+// 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 defaultPerPassDelay = 0.1;
+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 = defaultPerPassDelay;
+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)
+
+// Represent RGB color as fraction of color components.
+// Each of the color components are expected in the range [0.0, 1.0]
+class RGBColor {
+ public:
+ RGBColor(): _r(0.0), _g(0.0), _b(0.0) {};
+ RGBColor(float f): _r(f), _g(f), _b(f) {}; // Gray
+ RGBColor(float r, float g, float b): _r(r), _g(g), _b(b) {};
+ float r(void) const { return _r; }
+ float g(void) const { return _g; }
+ float b(void) const { return _b; }
+
+ private:
+ float _r;
+ float _g;
+ float _b;
+};
+
+// Represent YUV color as fraction of color components.
+// Each of the color components are expected in the range [0.0, 1.0]
+class YUVColor {
+ public:
+ YUVColor(): _y(0.0), _u(0.0), _v(0.0) {};
+ YUVColor(float f): _y(f), _u(0.0), _v(0.0) {}; // Gray
+ YUVColor(float y, float u, float v): _y(y), _u(u), _v(v) {};
+ float y(void) const { return _y; }
+ float u(void) const { return _u; }
+ float v(void) const { return _v; }
+
+ private:
+ float _y;
+ float _u;
+ float _v;
+};
+
+// File scope constants
+static const struct {
+ unsigned int format;
+ const char *desc;
+} graphicFormat[] = {
+ {HAL_PIXEL_FORMAT_RGBA_8888, "RGBA8888"},
+ {HAL_PIXEL_FORMAT_RGBX_8888, "RGBX8888"},
+// {HAL_PIXEL_FORMAT_RGB_888, "RGB888"}, // Known issue: 3198458
+ {HAL_PIXEL_FORMAT_RGB_565, "RGB565"},
+ {HAL_PIXEL_FORMAT_BGRA_8888, "BGRA8888"},
+ {HAL_PIXEL_FORMAT_RGBA_5551, "RGBA5551"},
+ {HAL_PIXEL_FORMAT_RGBA_4444, "RGBA4444"},
+// {HAL_PIXEL_FORMAT_YV12, "YV12"}, // Currently not supported by HWC
+};
+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 hw_module_t const *hwcModule;
+static hwc_composer_device_t *hwcDevice;
+static vector <vector <sp<GraphicBuffer> > > frames;
+static EGLDisplay dpy;
+static EGLContext context;
+static EGLSurface surface;
+static EGLint width, height;
+
+// File scope prototypes
+static void execCmd(const char *cmd);
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE);
+static void checkGlError(const char* op);
+static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config);
+static void printGLString(const char *name, GLenum s);
+static hwc_layer_list_t *createLayerList(size_t numLayers);
+static void freeLayerList(hwc_layer_list_t *list);
+static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans);
+static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans);
+void init(void);
+void initFrames(unsigned int seed);
+void displayList(hwc_layer_list_t *list);
+void displayListPrepareModifiable(hwc_layer_list_t *list);
+void displayListHandles(hwc_layer_list_t *list);
+const char *graphicFormat2str(unsigned int format);
+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);
+ }
+ execCmd(cmd);
+ testDelay(1.0); // TODO - needs 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(&currentTime, NULL);
+ delta = tvDelta(&startTime, &currentTime);
+ 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 = createLayerList(testRandMod(frames.size()) + 1);
+ if (list == NULL) {
+ testPrintE("createLayerList 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;
+ layer->visibleRegionScreen.numRects = 1;
+ layer->visibleRegionScreen.rects = &layer->displayFrame;
+ }
+
+ // Perform prepare operation
+ if (verbose) { testPrintI("Prepare:"); displayList(list); }
+ hwcDevice->prepare(hwcDevice, list);
+ if (verbose) {
+ testPrintI("Post Prepare:");
+ displayListPrepareModifiable(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) {displayListHandles(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);
+ }
+
+
+ freeLayerList(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);
+ }
+ execCmd(cmd);
+
+ testPrintI("Successfully completed %u passes", pass - startPass);
+
+ return 0;
+}
+
+/*
+ * Execute Command
+ *
+ * Executes the command pointed to by cmd. Output from the
+ * executed command is captured and sent to LogCat Info. Once
+ * the command has finished execution, it's exit status is captured
+ * and checked for an exit status of zero. Any other exit status
+ * causes diagnostic information to be printed and an immediate
+ * testcase failure.
+ */
+static void execCmd(const char *cmd)
+{
+ FILE *fp;
+ int rv;
+ int status;
+ char str[MAXSTR];
+
+ // Display command to be executed
+ testPrintI("cmd: %s", cmd);
+
+ // Execute the command
+ fflush(stdout);
+ if ((fp = popen(cmd, "r")) == NULL) {
+ testPrintE("execCmd popen failed, errno: %i", errno);
+ exit(30);
+ }
+
+ // Obtain and display each line of output from the executed command
+ while (fgets(str, sizeof(str), fp) != NULL) {
+ if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) {
+ str[strlen(str) - 1] = '\0';
+ }
+ testPrintI(" out: %s", str);
+ }
+
+ // Obtain and check return status of executed command.
+ // Fail on non-zero exit status
+ status = pclose(fp);
+ if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
+ testPrintE("Unexpected command failure");
+ testPrintE(" status: %#x", status);
+ if (WIFEXITED(status)) {
+ testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status));
+ }
+ if (WIFSIGNALED(status)) {
+ testPrintE("WTERMSIG: %i", WTERMSIG(status));
+ }
+ exit(31);
+ }
+}
+
+static void checkEglError(const char* op, EGLBoolean returnVal) {
+ if (returnVal != EGL_TRUE) {
+ testPrintE("%s() returned %d", op, returnVal);
+ }
+
+ for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+ = eglGetError()) {
+ testPrintE("after %s() eglError %s (0x%x)",
+ op, EGLUtils::strerror(error), error);
+ }
+}
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ testPrintE("after %s() glError (0x%x)", op, error);
+ }
+}
+
+static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) {
+
+#define X(VAL) {VAL, #VAL}
+ struct {EGLint attribute; const char* name;} names[] = {
+ X(EGL_BUFFER_SIZE),
+ X(EGL_ALPHA_SIZE),
+ X(EGL_BLUE_SIZE),
+ X(EGL_GREEN_SIZE),
+ X(EGL_RED_SIZE),
+ X(EGL_DEPTH_SIZE),
+ X(EGL_STENCIL_SIZE),
+ X(EGL_CONFIG_CAVEAT),
+ X(EGL_CONFIG_ID),
+ X(EGL_LEVEL),
+ X(EGL_MAX_PBUFFER_HEIGHT),
+ X(EGL_MAX_PBUFFER_PIXELS),
+ X(EGL_MAX_PBUFFER_WIDTH),
+ X(EGL_NATIVE_RENDERABLE),
+ X(EGL_NATIVE_VISUAL_ID),
+ X(EGL_NATIVE_VISUAL_TYPE),
+ X(EGL_SAMPLES),
+ X(EGL_SAMPLE_BUFFERS),
+ X(EGL_SURFACE_TYPE),
+ X(EGL_TRANSPARENT_TYPE),
+ X(EGL_TRANSPARENT_RED_VALUE),
+ X(EGL_TRANSPARENT_GREEN_VALUE),
+ X(EGL_TRANSPARENT_BLUE_VALUE),
+ X(EGL_BIND_TO_TEXTURE_RGB),
+ X(EGL_BIND_TO_TEXTURE_RGBA),
+ X(EGL_MIN_SWAP_INTERVAL),
+ X(EGL_MAX_SWAP_INTERVAL),
+ X(EGL_LUMINANCE_SIZE),
+ X(EGL_ALPHA_MASK_SIZE),
+ X(EGL_COLOR_BUFFER_TYPE),
+ X(EGL_RENDERABLE_TYPE),
+ X(EGL_CONFORMANT),
+ };
+#undef X
+
+ for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
+ EGLint value = -1;
+ EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value);
+ EGLint error = eglGetError();
+ if (returnVal && error == EGL_SUCCESS) {
+ testPrintI(" %s: %d (%#x)", names[j].name, value, value);
+ }
+ }
+ testPrintI("");
+}
+
+static void printGLString(const char *name, GLenum s)
+{
+ const char *v = (const char *) glGetString(s);
+
+ if (v == NULL) {
+ testPrintI("GL %s unknown", name);
+ } else {
+ testPrintI("GL %s = %s", name, v);
+ }
+}
+
+/*
+ * createLayerList
+ * dynamically creates layer list with numLayers worth
+ * of hwLayers entries.
+ */
+static hwc_layer_list_t *createLayerList(size_t numLayers)
+{
+ hwc_layer_list_t *list;
+
+ size_t size = sizeof(hwc_layer_list) + numLayers * sizeof(hwc_layer_t);
+ if ((list = (hwc_layer_list_t *) calloc(1, size)) == NULL) {
+ return NULL;
+ }
+ list->flags = HWC_GEOMETRY_CHANGED;
+ list->numHwLayers = numLayers;
+
+ return list;
+}
+
+/*
+ * freeLayerList
+ * Frees memory previous allocated via createLayerList().
+ */
+static void freeLayerList(hwc_layer_list_t *list)
+{
+ free(list);
+}
+
+static void fillColor(GraphicBuffer *gBuf, RGBColor color, float trans)
+{
+ unsigned char* buf = NULL;
+ status_t err;
+ unsigned int numPixels = gBuf->getWidth() * gBuf->getHeight();
+ uint32_t pixel;
+
+ // RGB 2 YUV conversion ratios
+ const struct rgb2yuvRatios {
+ int format;
+ float weightRed;
+ float weightBlu;
+ float weightGrn;
+ } rgb2yuvRatios[] = {
+ { HAL_PIXEL_FORMAT_YV12, 0.299, 0.114, 0.587 },
+ };
+
+ const struct rgbAttrib {
+ int format;
+ bool hostByteOrder;
+ size_t bytes;
+ size_t rOffset;
+ size_t rSize;
+ size_t gOffset;
+ size_t gSize;
+ size_t bOffset;
+ size_t bSize;
+ size_t aOffset;
+ size_t aSize;
+ } rgbAttributes[] = {
+ {HAL_PIXEL_FORMAT_RGBA_8888, false, 4, 0, 8, 8, 8, 16, 8, 24, 8},
+ {HAL_PIXEL_FORMAT_RGBX_8888, false, 4, 0, 8, 8, 8, 16, 8, 0, 0},
+ {HAL_PIXEL_FORMAT_RGB_888, false, 3, 0, 8, 8, 8, 16, 8, 0, 0},
+ {HAL_PIXEL_FORMAT_RGB_565, true, 2, 0, 5, 5, 6, 11, 5, 0, 0},
+ {HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8, 8, 8, 0, 8, 24, 8},
+ {HAL_PIXEL_FORMAT_RGBA_5551, true , 2, 0, 5, 5, 5, 10, 5, 15, 1},
+ {HAL_PIXEL_FORMAT_RGBA_4444, false, 2, 12, 4, 0, 4, 4, 4, 8, 4},
+ };
+
+ // If YUV format, convert color and pass work to YUV color fill
+ for (unsigned int n1 = 0; n1 < NUMA(rgb2yuvRatios); n1++) {
+ if (gBuf->getPixelFormat() == rgb2yuvRatios[n1].format) {
+ float wr = rgb2yuvRatios[n1].weightRed;
+ float wb = rgb2yuvRatios[n1].weightBlu;
+ float wg = rgb2yuvRatios[n1].weightGrn;
+ float y = wr * color.r() + wb * color.b() + wg * color.g();
+ float u = 0.5 * ((color.b() - y) / (1 - wb)) + 0.5;
+ float v = 0.5 * ((color.r() - y) / (1 - wr)) + 0.5;
+ YUVColor yuvColor(y, u, v);
+ fillColor(gBuf, yuvColor, trans);
+ return;
+ }
+ }
+
+ const struct rgbAttrib *attrib;
+ for (attrib = rgbAttributes; attrib < rgbAttributes + NUMA(rgbAttributes);
+ attrib++) {
+ if (attrib->format == gBuf->getPixelFormat()) { break; }
+ }
+ if (attrib >= rgbAttributes + NUMA(rgbAttributes)) {
+ testPrintE("fillColor rgb unsupported format of: %u",
+ gBuf->getPixelFormat());
+ exit(50);
+ }
+
+ pixel = htonl((uint32_t) (((1 << attrib->rSize) - 1) * color.r())
+ << ((sizeof(pixel) * BITSPERBYTE)
+ - (attrib->rOffset + attrib->rSize)));
+ pixel |= htonl((uint32_t) (((1 << attrib->gSize) - 1) * color.g())
+ << ((sizeof(pixel) * BITSPERBYTE)
+ - (attrib->gOffset + attrib->gSize)));
+ pixel |= htonl((uint32_t) (((1 << attrib->bSize) - 1) * color.b())
+ << ((sizeof(pixel) * BITSPERBYTE)
+ - (attrib->bOffset + attrib->bSize)));
+ if (attrib->aSize) {
+ pixel |= htonl((uint32_t) (((1 << attrib->aSize) - 1) * trans)
+ << ((sizeof(pixel) * BITSPERBYTE)
+ - (attrib->aOffset + attrib->aSize)));
+ }
+ if (attrib->hostByteOrder) {
+ pixel = ntohl(pixel);
+ pixel >>= sizeof(pixel) * BITSPERBYTE - attrib->bytes * BITSPERBYTE;
+ }
+
+ err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+ if (err != 0) {
+ testPrintE("fillColor rgb lock failed: %d", err);
+ exit(51);
+ }
+
+ for (unsigned int n1 = 0; n1 < numPixels; n1++) {
+ memmove(buf, &pixel, attrib->bytes);
+ buf += attrib->bytes;
+ }
+
+ err = gBuf->unlock();
+ if (err != 0) {
+ testPrintE("fillColor rgb unlock failed: %d", err);
+ exit(52);
+ }
+}
+
+static void fillColor(GraphicBuffer *gBuf, YUVColor color, float trans)
+{
+ unsigned char* buf = NULL;
+ status_t err;
+ unsigned int width = gBuf->getWidth();
+ unsigned int height = gBuf->getHeight();
+
+ const struct yuvAttrib {
+ int format;
+ size_t padWidth;
+ bool planar;
+ unsigned int uSubSampX;
+ unsigned int uSubSampY;
+ unsigned int vSubSampX;
+ unsigned int vSubSampY;
+ } yuvAttributes[] = {
+ { HAL_PIXEL_FORMAT_YV12, 16, true, 2, 2, 2, 2},
+ };
+
+ const struct yuvAttrib *attrib;
+ for (attrib = yuvAttributes; attrib < yuvAttributes + NUMA(yuvAttributes);
+ attrib++) {
+ if (attrib->format == gBuf->getPixelFormat()) { break; }
+ }
+ if (attrib >= yuvAttributes + NUMA(yuvAttributes)) {
+ testPrintE("fillColor yuv unsupported format of: %u",
+ gBuf->getPixelFormat());
+ exit(60);
+ }
+
+ assert(attrib->planar == true); // So far, only know how to handle planar
+
+ // If needed round width up to pad size
+ if (width % attrib->padWidth) {
+ width += attrib->padWidth - (width % attrib->padWidth);
+ }
+ assert((width % attrib->padWidth) == 0);
+
+ err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
+ if (err != 0) {
+ testPrintE("fillColor lock failed: %d", err);
+ exit(61);
+ }
+
+ // Fill in Y component
+ for (unsigned int x = 0; x < width; x++) {
+ for (unsigned int y = 0; y < height; y++) {
+ *buf++ = (x < gBuf->getWidth()) ? (255 * color.y()) : 0;
+ }
+ }
+
+ // Fill in U component
+ for (unsigned int x = 0; x < width; x += attrib->uSubSampX) {
+ for (unsigned int y = 0; y < height; y += attrib->uSubSampY) {
+ *buf++ = (x < gBuf->getWidth()) ? (255 * color.u()) : 0;
+ }
+ }
+
+ // Fill in V component
+ for (unsigned int x = 0; x < width; x += attrib->vSubSampX) {
+ for (unsigned int y = 0; y < height; y += attrib->vSubSampY) {
+ *buf++ = (x < gBuf->getWidth()) ? (255 * color.v()) : 0;
+ }
+ }
+
+ err = gBuf->unlock();
+ if (err != 0) {
+ testPrintE("fillColor unlock failed: %d", err);
+ exit(62);
+ }
+}
+
+void init(void)
+{
+ int rv;
+
+ EGLBoolean returnValue;
+ EGLConfig myConfig = {0};
+ EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLint sConfigAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE };
+ EGLint majorVersion, minorVersion;
+
+ checkEglError("<init>");
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ checkEglError("eglGetDisplay");
+ if (dpy == EGL_NO_DISPLAY) {
+ testPrintE("eglGetDisplay returned EGL_NO_DISPLAY");
+ exit(70);
+ }
+
+ returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+ checkEglError("eglInitialize", returnValue);
+ testPrintI("EGL version %d.%d", majorVersion, minorVersion);
+ if (returnValue != EGL_TRUE) {
+ testPrintE("eglInitialize failed");
+ exit(71);
+ }
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+ if (window == NULL) {
+ testPrintE("android_createDisplaySurface failed");
+ exit(72);
+ }
+ returnValue = EGLUtils::selectConfigForNativeWindow(dpy,
+ sConfigAttribs, window, &myConfig);
+ if (returnValue) {
+ testPrintE("EGLUtils::selectConfigForNativeWindow() returned %d",
+ returnValue);
+ exit(73);
+ }
+ checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+ testPrintI("Chose this configuration:");
+ printEGLConfiguration(dpy, myConfig);
+
+ surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+ checkEglError("eglCreateWindowSurface");
+ if (surface == EGL_NO_SURFACE) {
+ testPrintE("gelCreateWindowSurface failed.");
+ exit(74);
+ }
+
+ context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, contextAttribs);
+ checkEglError("eglCreateContext");
+ if (context == EGL_NO_CONTEXT) {
+ testPrintE("eglCreateContext failed");
+ exit(75);
+ }
+ returnValue = eglMakeCurrent(dpy, surface, surface, context);
+ checkEglError("eglMakeCurrent", returnValue);
+ if (returnValue != EGL_TRUE) {
+ testPrintE("eglMakeCurrent failed");
+ exit(76);
+ }
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &width);
+ checkEglError("eglQuerySurface");
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &height);
+ checkEglError("eglQuerySurface");
+
+ fprintf(stderr, "Window dimensions: %d x %d", width, height);
+
+ printGLString("Version", GL_VERSION);
+ printGLString("Vendor", GL_VENDOR);
+ printGLString("Renderer", GL_RENDERER);
+ printGLString("Extensions", GL_EXTENSIONS);
+
+ if ((rv = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwcModule)) != 0) {
+ testPrintE("hw_get_module failed, rv: %i", rv);
+ errno = -rv;
+ perror(NULL);
+ exit(77);
+ }
+ if ((rv = hwc_open(hwcModule, &hwcDevice)) != 0) {
+ testPrintE("hwc_open failed, rv: %i", rv);
+ errno = -rv;
+ perror(NULL);
+ exit(78);
+ }
+
+ testPrintI("");
+}
+
+/*
+ * Initialize Frames
+ *
+ * Creates an array of graphic buffers, within the global variable
+ * named frames. The graphic buffers are contained within a vector of
+ * verctors. 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)
+{
+ 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.
+ int format = graphicFormat[testRandMod(NUMA(graphicFormat))].format;
+ size_t w = (width * maxSizeRatio) * testRandFract();
+ size_t h = (height * maxSizeRatio) * testRandFract();
+ w = max(1u, w);
+ h = max(1u, h);
+ if (verbose) {
+ testPrintI(" frame %u width: %u height: %u format: %u %s",
+ row, w, h, format, graphicFormat2str(format));
+ }
+
+ size_t cols = testRandMod((maxCols + 1) - minCols) + minCols;
+ frames[row].resize(cols);
+ for (unsigned int col = 0; col < cols; col++) {
+ RGBColor color(testRandFract(), testRandFract(), testRandFract());
+ float transp = testRandFract();
+
+ frames[row][col] = new GraphicBuffer(w, h, format, texUsage);
+ fillColor(frames[row][col].get(), color, transp);
+ if (verbose) {
+ testPrintI(" buf: %p handle: %p color: <%f, %f, %f> "
+ "transp: %f",
+ frames[row][col].get(), frames[row][col]->handle,
+ color.r(), color.g(), color.b(), transp);
+ }
+ }
+ }
+}
+
+void displayList(hwc_layer_list_t *list)
+{
+ testPrintI(" flags: %#x%s", list->flags,
+ (list->flags & HWC_GEOMETRY_CHANGED) ? " GEOMETRY_CHANGED" : "");
+ testPrintI(" numHwLayers: %u", list->numHwLayers);
+
+ for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+ testPrintI(" layer %u compositionType: %#x%s%s", layer,
+ list->hwLayers[layer].compositionType,
+ (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
+ ? " FRAMEBUFFER" : "",
+ (list->hwLayers[layer].compositionType == HWC_OVERLAY)
+ ? " OVERLAY" : "");
+
+ testPrintI(" hints: %#x",
+ list->hwLayers[layer].hints,
+ (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
+ ? " TRIPLE_BUFFER" : "",
+ (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
+ ? " CLEAR_FB" : "");
+
+ testPrintI(" flags: %#x%s",
+ list->hwLayers[layer].flags,
+ (list->hwLayers[layer].flags & HWC_SKIP_LAYER)
+ ? " SKIP_LAYER" : "");
+
+ testPrintI(" handle: %p",
+ list->hwLayers[layer].handle);
+
+ // Intentionally skipped display of ROT_180 & ROT_270,
+ // which are formed from combinations of the other flags.
+ testPrintI(" transform: %#x%s%s%s",
+ list->hwLayers[layer].transform,
+ (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_H)
+ ? " FLIP_H" : "",
+ (list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_V)
+ ? " FLIP_V" : "",
+ (list->hwLayers[layer].transform & HWC_TRANSFORM_ROT_90)
+ ? " ROT_90" : "");
+
+ testPrintI(" blending: %#x",
+ list->hwLayers[layer].blending,
+ (list->hwLayers[layer].blending == HWC_BLENDING_NONE)
+ ? " NONE" : "",
+ (list->hwLayers[layer].blending == HWC_BLENDING_PREMULT)
+ ? " PREMULT" : "",
+ (list->hwLayers[layer].blending == HWC_BLENDING_COVERAGE)
+ ? " COVERAGE" : "");
+
+ testPrintI(" sourceCrop: [%i, %i, %i, %i]",
+ list->hwLayers[layer].sourceCrop.left,
+ list->hwLayers[layer].sourceCrop.top,
+ list->hwLayers[layer].sourceCrop.right,
+ list->hwLayers[layer].sourceCrop.bottom);
+
+ testPrintI(" displayFrame: [%i, %i, %i, %i]",
+ list->hwLayers[layer].displayFrame.left,
+ list->hwLayers[layer].displayFrame.top,
+ list->hwLayers[layer].displayFrame.right,
+ list->hwLayers[layer].displayFrame.bottom);
+ }
+}
+
+/*
+ * Display List Prepare Modifiable
+ *
+ * Displays the portions of a list that are meant to be modified by
+ * a prepare call.
+ */
+void displayListPrepareModifiable(hwc_layer_list_t *list)
+{
+ for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+ testPrintI(" layer %u compositionType: %#x%s%s", layer,
+ list->hwLayers[layer].compositionType,
+ (list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
+ ? " FRAMEBUFFER" : "",
+ (list->hwLayers[layer].compositionType == HWC_OVERLAY)
+ ? " OVERLAY" : "");
+ testPrintI(" hints: %#x%s%s",
+ list->hwLayers[layer].hints,
+ (list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
+ ? " TRIPLE_BUFFER" : "",
+ (list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
+ ? " CLEAR_FB" : "");
+ }
+}
+
+/*
+ * Display List Handles
+ *
+ * Displays the handles of all the graphic buffers in the list.
+ */
+void displayListHandles(hwc_layer_list_t *list)
+{
+ const unsigned int maxLayersPerLine = 6;
+
+ ostringstream str(" layers:");
+ for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
+ str << ' ' << list->hwLayers[layer].handle;
+ if (((layer % maxLayersPerLine) == (maxLayersPerLine - 1))
+ && (layer != list->numHwLayers - 1)) {
+ testPrintI("%s", str.str().c_str());
+ str.str(" ");
+ }
+ }
+ testPrintI("%s", str.str().c_str());
+}
+
+const char *graphicFormat2str(unsigned int format)
+{
+ const static char *unknown = "unknown";
+
+ for (unsigned int n1 = 0; n1 < NUMA(graphicFormat); n1++) {
+ if (format == graphicFormat[n1].format) {
+ return graphicFormat[n1].desc;
+ }
+ }
+
+ return unknown;
+}
+
+/*
+ * 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;
+}