| /* |
| * Copyright (C) 2011 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 Commit Points |
| * |
| * Synopsis |
| * hwcCommit [options] graphicFormat ... |
| * options: |
| * -s [width, height] - Starting dimension |
| * -v - Verbose |
| * |
| * graphic formats: |
| * RGBA8888 (reference frame default) |
| * RGBX8888 |
| * RGB888 |
| * RGB565 |
| * BGRA8888 |
| * RGBA5551 |
| * RGBA4444 |
| * YV12 |
| * |
| * Description |
| * The Hardware Composer (HWC) Commit test is a benchmark that |
| * discovers the points at which the HWC will commit to rendering an |
| * overlay(s). Before rendering a set of overlays, the HWC is shown |
| * the list through a prepare call. During the prepare call the HWC |
| * is able to examine the list and specify which overlays it is able |
| * to handle. The overlays that it can't handle are typically composited |
| * by a higher level (e.g. Surface Flinger) and then the original list |
| * plus a composit of what HWC passed on are provided back to the HWC |
| * for rendering. |
| * |
| * Once an implementation of the HWC has been shipped, a regression would |
| * likely occur if a latter implementation started passing on conditions |
| * that it used to commit to. The primary purpose of this benchmark |
| * is the automated discovery of the commit points, where an implementation |
| * is on the edge between committing and not committing. These are commonly |
| * referred to as commit points. Between implementations changes to the |
| * commit points are allowed, as long as they improve what the HWC commits |
| * to. Once an implementation of the HWC is shipped, the commit points are |
| * not allowed to regress in future implementations. |
| * |
| * This benchmark takes a sampling and then adjusts until it finds a |
| * commit point. It doesn't exhaustively check all possible conditions, |
| * which do to the number of combinations would be impossible. Instead |
| * it starts its search from a starting dimension, that can be changed |
| * via the -s option. The search is also bounded by a set of search |
| * limits, that are hard-coded into a structure of constants named |
| * searchLimits. Results that happen to reach a searchLimit are prefixed |
| * with >=, so that it is known that the value could possibly be larger. |
| * |
| * Measurements are made for each of the graphic formats specified as |
| * positional parameters on the command-line. If no graphic formats |
| * are specified on the command line, then by default measurements are |
| * made and reported for each of the known graphic format. |
| */ |
| |
| #include <algorithm> |
| #include <assert.h> |
| #include <cerrno> |
| #include <cmath> |
| #include <cstdlib> |
| #include <ctime> |
| #include <iomanip> |
| #include <istream> |
| #include <libgen.h> |
| #include <list> |
| #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 "hwcCommitTest" |
| #include <utils/Log.h> |
| #include <testUtil.h> |
| |
| #include <hardware/hwcomposer.h> |
| |
| #include <glTestLib.h> |
| #include "hwcTestLib.h" |
| |
| using namespace std; |
| using namespace android; |
| |
| // Defaults |
| const HwcTestDim defaultStartDim = HwcTestDim(100, 100); |
| const bool defaultVerbose = false; |
| |
| const uint32_t defaultFormat = HAL_PIXEL_FORMAT_RGBA_8888; |
| const int32_t defaultTransform = 0; |
| const uint32_t defaultBlend = HWC_BLENDING_NONE; |
| const ColorFract defaultColor(0.5, 0.5, 0.5); |
| const float defaultAlpha = 1.0; // Opaque |
| const HwcTestDim defaultSourceDim(1, 1); |
| const struct hwc_rect defaultSourceCrop = {0, 0, 1, 1}; |
| const struct hwc_rect defaultDisplayFrame = {0, 0, 100, 100}; |
| |
| // Global Constants |
| const uint32_t printFieldWidth = 2; |
| const struct searchLimits { |
| uint32_t numOverlays; |
| HwcTestDim sourceCrop; |
| } searchLimits = { |
| 10, |
| HwcTestDim(3000, 2000), |
| }; |
| const struct transformType { |
| const char *desc; |
| uint32_t id; |
| } transformType[] = { |
| {"fliph", HWC_TRANSFORM_FLIP_H}, |
| {"flipv", HWC_TRANSFORM_FLIP_V}, |
| {"rot90", HWC_TRANSFORM_ROT_90}, |
| {"rot180", HWC_TRANSFORM_ROT_180}, |
| {"rot270", HWC_TRANSFORM_ROT_270}, |
| }; |
| const struct blendType { |
| const char *desc; |
| uint32_t id; |
| } blendType[] = { |
| {"none", HWC_BLENDING_NONE}, |
| {"premult", HWC_BLENDING_PREMULT}, |
| {"coverage", HWC_BLENDING_COVERAGE}, |
| }; |
| |
| // Defines |
| #define MAXCMD 200 |
| #define CMD_STOP_FRAMEWORK "stop 2>&1" |
| #define CMD_START_FRAMEWORK "start 2>&1" |
| |
| // Macros |
| #define NUMA(a) (sizeof(a) / sizeof(a [0])) // Num elements in an array |
| |
| // Local types |
| class Rectangle { |
| public: |
| Rectangle(uint32_t graphicFormat = defaultFormat, |
| HwcTestDim dfDim = HwcTestDim(1, 1), |
| HwcTestDim sDim = HwcTestDim(1, 1)); |
| void setSourceDim(HwcTestDim dim); |
| |
| uint32_t format; |
| uint32_t transform; |
| int32_t blend; |
| ColorFract color; |
| float alpha; |
| HwcTestDim sourceDim; |
| struct hwc_rect sourceCrop; |
| struct hwc_rect displayFrame; |
| }; |
| |
| class Range { |
| public: |
| Range(void) : _l(0), _u(0) {} |
| Range(uint32_t lower, uint32_t upper) : _l(lower), _u(upper) {} |
| uint32_t lower(void) { return _l; } |
| uint32_t upper(void) { return _u; } |
| |
| operator string(); |
| |
| private: |
| uint32_t _l; // lower |
| uint32_t _u; // upper |
| }; |
| |
| Range::operator string() |
| { |
| ostringstream out; |
| |
| out << '[' << _l << ", " << _u << ']'; |
| |
| return out.str(); |
| } |
| |
| class Rational { |
| public: |
| Rational(void) : _n(0), _d(1) {} |
| Rational(uint32_t n, uint32_t d) : _n(n), _d(d) {} |
| uint32_t numerator(void) { return _n; } |
| uint32_t denominator(void) { return _d; } |
| void setNumerator(uint32_t numerator) { _n = numerator; } |
| |
| bool operator==(const Rational& other) const; |
| bool operator!=(const Rational& other) const { return !(*this == other); } |
| bool operator<(const Rational& other) const; |
| bool operator>(const Rational& other) const { |
| return (!(*this == other) && !(*this < other)); |
| } |
| static void double2Rational(double f, Range nRange, Range dRange, |
| Rational& lower, Rational& upper); |
| |
| operator string() const; |
| operator double() const { return (double) _n / (double) _d; } |
| |
| |
| private: |
| uint32_t _n; |
| uint32_t _d; |
| }; |
| |
| // Globals |
| static const int texUsage = GraphicBuffer::USAGE_HW_TEXTURE | |
| GraphicBuffer::USAGE_SW_WRITE_RARELY; |
| static hwc_composer_device_1_t *hwcDevice; |
| static EGLDisplay dpy; |
| static EGLSurface surface; |
| static EGLint width, height; |
| static size_t maxHeadingLen; |
| static vector<string> formats; |
| |
| // Measurements |
| struct meas { |
| uint32_t format; |
| uint32_t startDimOverlays; |
| uint32_t maxNonOverlapping; |
| uint32_t maxOverlapping; |
| list<uint32_t> transforms; |
| list<uint32_t> blends; |
| struct displayFrame { |
| uint32_t minWidth; |
| uint32_t minHeight; |
| HwcTestDim minDim; |
| uint32_t maxWidth; |
| uint32_t maxHeight; |
| HwcTestDim maxDim; |
| } df; |
| struct sourceCrop { |
| uint32_t minWidth; |
| uint32_t minHeight; |
| HwcTestDim minDim; |
| uint32_t maxWidth; |
| uint32_t maxHeight; |
| HwcTestDim maxDim; |
| Rational hScale; |
| HwcTestDim hScaleBestDf; |
| HwcTestDim hScaleBestSc; |
| Rational vScale; |
| HwcTestDim vScaleBestDf; |
| HwcTestDim vScaleBestSc; |
| } sc; |
| vector<uint32_t> overlapBlendNone; |
| vector<uint32_t> overlapBlendPremult; |
| vector<uint32_t> overlapBlendCoverage; |
| }; |
| vector<meas> measurements; |
| |
| // Function prototypes |
| uint32_t numOverlays(list<Rectangle>& rectList); |
| uint32_t maxOverlays(uint32_t format, bool allowOverlap); |
| list<uint32_t> supportedTransforms(uint32_t format); |
| list<uint32_t> supportedBlends(uint32_t format); |
| uint32_t dfMinWidth(uint32_t format); |
| uint32_t dfMinHeight(uint32_t format); |
| uint32_t dfMaxWidth(uint32_t format); |
| uint32_t dfMaxHeight(uint32_t format); |
| HwcTestDim dfMinDim(uint32_t format); |
| HwcTestDim dfMaxDim(uint32_t format); |
| uint32_t scMinWidth(uint32_t format, const HwcTestDim& dfDim); |
| uint32_t scMinHeight(uint32_t format, const HwcTestDim& dfDim); |
| uint32_t scMaxWidth(uint32_t format, const HwcTestDim& dfDim); |
| uint32_t scMaxHeight(uint32_t format, const HwcTestDim& dfDim); |
| HwcTestDim scMinDim(uint32_t format, const HwcTestDim& dfDim); |
| HwcTestDim scMaxDim(uint32_t format, const HwcTestDim& dfDim); |
| Rational scHScale(uint32_t format, |
| const HwcTestDim& dfMin, const HwcTestDim& dfMax, |
| const HwcTestDim& scMin, const HwcTestDim& scMax, |
| HwcTestDim& outBestDf, HwcTestDim& outBestSc); |
| Rational scVScale(uint32_t format, |
| const HwcTestDim& dfMin, const HwcTestDim& dfMax, |
| const HwcTestDim& scMin, const HwcTestDim& scMax, |
| HwcTestDim& outBestDf, HwcTestDim& outBestSc); |
| uint32_t numOverlapping(uint32_t backgroundFormat, uint32_t foregroundFormat, |
| uint32_t backgroundBlend, uint32_t foregroundBlend); |
| string transformList2str(const list<uint32_t>& transformList); |
| string blendList2str(const list<uint32_t>& blendList); |
| void init(void); |
| void printFormatHeadings(size_t indent); |
| void printOverlapLine(size_t indent, const string formatStr, |
| const vector<uint32_t>& results); |
| void printSyntax(const char *cmd); |
| |
| // Command-line option settings |
| static bool verbose = defaultVerbose; |
| static HwcTestDim startDim = defaultStartDim; |
| |
| /* |
| * Main |
| * |
| * Performs the following high-level sequence of operations: |
| * |
| * 1. Command-line parsing |
| * |
| * 2. Form a list of command-line specified graphic formats. If |
| * no formats are specified, then form a list of all known formats. |
| * |
| * 3. Stop framework |
| * Only one user at a time is allowed to use the HWC. Surface |
| * Flinger uses the HWC and is part of the framework. Need to |
| * stop the framework so that Surface Flinger will stop using |
| * the HWC. |
| * |
| * 4. Initialization |
| * |
| * 5. For each graphic format in the previously formed list perform |
| * measurements on that format and report the results. |
| * |
| * 6. Start framework |
| */ |
| int |
| main(int argc, char *argv[]) |
| { |
| int rv, opt; |
| char *chptr; |
| bool error; |
| string str; |
| char cmd[MAXCMD]; |
| list<Rectangle> rectList; |
| |
| testSetLogCatTag(LOG_TAG); |
| |
| // Parse command line arguments |
| while ((opt = getopt(argc, argv, "s:v?h")) != -1) { |
| switch (opt) { |
| |
| case 's': // Start Dimension |
| // Use arguments until next starts with a dash |
| // or current ends with a > or ] |
| str = optarg; |
| while (optind < argc) { |
| if (*argv[optind] == '-') { break; } |
| char endChar = (str.length() > 1) ? str[str.length() - 1] : 0; |
| if ((endChar == '>') || (endChar == ']')) { break; } |
| str += " " + string(argv[optind++]); |
| } |
| { |
| istringstream in(str); |
| startDim = hwcTestParseDim(in, error); |
| // Any parse error or characters not used by parser |
| if (error |
| || (((unsigned int) in.tellg() != in.str().length()) |
| && (in.tellg() != (streampos) -1))) { |
| testPrintE("Invalid command-line specified start " |
| "dimension of: %s", str.c_str()); |
| exit(8); |
| } |
| } |
| break; |
| |
| case 'v': // Verbose |
| verbose = true; |
| break; |
| |
| case 'h': // Help |
| case '?': |
| default: |
| printSyntax(basename(argv[0])); |
| exit(((optopt == 0) || (optopt == '?')) ? 0 : 11); |
| } |
| } |
| |
| // Positional parameters |
| // Positional parameters provide the names of graphic formats that |
| // measurements are to be made on. Measurements are made on all |
| // known graphic formats when no positional parameters are provided. |
| if (optind == argc) { |
| // No command-line specified graphic formats |
| // Add all graphic formats to the list of formats to be measured |
| for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) { |
| formats.push_back(hwcTestGraphicFormat[n1].desc); |
| } |
| } else { |
| // Add names of command-line specified graphic formats to the |
| // list of formats to be tested |
| for (; argv[optind] != NULL; optind++) { |
| formats.push_back(argv[optind]); |
| } |
| } |
| |
| // Determine length of longest specified graphic format. |
| // This value is used for output formating |
| for (vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| maxHeadingLen = max(maxHeadingLen, it->length()); |
| } |
| |
| // 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 - needs means to query whether asynchronous stop |
| // framework operation has completed. For now, just wait |
| // a long time. |
| |
| testPrintI("startDim: %s", ((string) startDim).c_str()); |
| |
| init(); |
| |
| // For each of the graphic formats |
| for (vector<string>::iterator itFormat = formats.begin(); |
| itFormat != formats.end(); ++itFormat) { |
| |
| // Locate hwcTestLib structure that describes this format |
| const struct hwcTestGraphicFormat *format; |
| format = hwcTestGraphicFormatLookup((*itFormat).c_str()); |
| if (format == NULL) { |
| testPrintE("Unknown graphic format of: %s", (*itFormat).c_str()); |
| exit(1); |
| } |
| |
| // Display format header |
| testPrintI("format: %s", format->desc); |
| |
| // Create area to hold the measurements |
| struct meas meas; |
| struct meas *measPtr; |
| meas.format = format->format; |
| measurements.push_back(meas); |
| measPtr = &measurements[measurements.size() - 1]; |
| |
| // Start dimension num overlays |
| Rectangle rect(format->format, startDim); |
| rectList.clear(); |
| rectList.push_back(rect); |
| measPtr->startDimOverlays = numOverlays(rectList); |
| testPrintI(" startDimOverlays: %u", measPtr->startDimOverlays); |
| |
| // Skip the rest of the measurements, when the start dimension |
| // doesn't produce an overlay |
| if (measPtr->startDimOverlays == 0) { continue; } |
| |
| // Max Overlays |
| measPtr->maxNonOverlapping = maxOverlays(format->format, false); |
| testPrintI(" max nonOverlapping overlays: %s%u", |
| (measPtr->maxNonOverlapping == searchLimits.numOverlays) |
| ? ">= " : "", |
| measPtr->maxNonOverlapping); |
| measPtr->maxOverlapping = maxOverlays(format->format, true); |
| testPrintI(" max Overlapping overlays: %s%u", |
| (measPtr->maxOverlapping == searchLimits.numOverlays) |
| ? ">= " : "", |
| measPtr->maxOverlapping); |
| |
| // Transforms and blends |
| measPtr->transforms = supportedTransforms(format->format); |
| testPrintI(" transforms: %s", |
| transformList2str(measPtr->transforms).c_str()); |
| measPtr->blends = supportedBlends(format->format); |
| testPrintI(" blends: %s", |
| blendList2str(measPtr->blends).c_str()); |
| |
| // Display frame measurements |
| measPtr->df.minWidth = dfMinWidth(format->format); |
| testPrintI(" dfMinWidth: %u", measPtr->df.minWidth); |
| |
| measPtr->df.minHeight = dfMinHeight(format->format); |
| testPrintI(" dfMinHeight: %u", measPtr->df.minHeight); |
| |
| measPtr->df.maxWidth = dfMaxWidth(format->format); |
| testPrintI(" dfMaxWidth: %u", measPtr->df.maxWidth); |
| |
| measPtr->df.maxHeight = dfMaxHeight(format->format); |
| testPrintI(" dfMaxHeight: %u", measPtr->df.maxHeight); |
| |
| measPtr->df.minDim = dfMinDim(format->format); |
| testPrintI(" dfMinDim: %s", ((string) measPtr->df.minDim).c_str()); |
| |
| measPtr->df.maxDim = dfMaxDim(format->format); |
| testPrintI(" dfMaxDim: %s", ((string) measPtr->df.maxDim).c_str()); |
| |
| // Source crop measurements |
| measPtr->sc.minWidth = scMinWidth(format->format, measPtr->df.minDim); |
| testPrintI(" scMinWidth: %u", measPtr->sc.minWidth); |
| |
| measPtr->sc.minHeight = scMinHeight(format->format, measPtr->df.minDim); |
| testPrintI(" scMinHeight: %u", measPtr->sc.minHeight); |
| |
| measPtr->sc.maxWidth = scMaxWidth(format->format, measPtr->df.maxDim); |
| testPrintI(" scMaxWidth: %s%u", (measPtr->sc.maxWidth |
| == searchLimits.sourceCrop.width()) ? ">= " : "", |
| measPtr->sc.maxWidth); |
| |
| measPtr->sc.maxHeight = scMaxHeight(format->format, measPtr->df.maxDim); |
| testPrintI(" scMaxHeight: %s%u", (measPtr->sc.maxHeight |
| == searchLimits.sourceCrop.height()) ? ">= " : "", |
| measPtr->sc.maxHeight); |
| |
| measPtr->sc.minDim = scMinDim(format->format, measPtr->df.minDim); |
| testPrintI(" scMinDim: %s", ((string) measPtr->sc.minDim).c_str()); |
| |
| measPtr->sc.maxDim = scMaxDim(format->format, measPtr->df.maxDim); |
| testPrintI(" scMaxDim: %s%s", ((measPtr->sc.maxDim.width() |
| >= searchLimits.sourceCrop.width()) |
| || (measPtr->sc.maxDim.width() >= |
| searchLimits.sourceCrop.height())) ? ">= " : "", |
| ((string) measPtr->sc.maxDim).c_str()); |
| |
| measPtr->sc.hScale = scHScale(format->format, |
| measPtr->df.minDim, measPtr->df.maxDim, |
| measPtr->sc.minDim, measPtr->sc.maxDim, |
| measPtr->sc.hScaleBestDf, |
| measPtr->sc.hScaleBestSc); |
| testPrintI(" scHScale: %s%f", |
| (measPtr->sc.hScale |
| >= Rational(searchLimits.sourceCrop.width(), |
| measPtr->df.minDim.width())) ? ">= " : "", |
| (double) measPtr->sc.hScale); |
| testPrintI(" HScale Best Display Frame: %s", |
| ((string) measPtr->sc.hScaleBestDf).c_str()); |
| testPrintI(" HScale Best Source Crop: %s", |
| ((string) measPtr->sc.hScaleBestSc).c_str()); |
| |
| measPtr->sc.vScale = scVScale(format->format, |
| measPtr->df.minDim, measPtr->df.maxDim, |
| measPtr->sc.minDim, measPtr->sc.maxDim, |
| measPtr->sc.vScaleBestDf, |
| measPtr->sc.vScaleBestSc); |
| testPrintI(" scVScale: %s%f", |
| (measPtr->sc.vScale |
| >= Rational(searchLimits.sourceCrop.height(), |
| measPtr->df.minDim.height())) ? ">= " : "", |
| (double) measPtr->sc.vScale); |
| testPrintI(" VScale Best Display Frame: %s", |
| ((string) measPtr->sc.vScaleBestDf).c_str()); |
| testPrintI(" VScale Best Source Crop: %s", |
| ((string) measPtr->sc.vScaleBestSc).c_str()); |
| |
| // Overlap two graphic formats and different blends |
| // Results displayed after all overlap measurments with |
| // current format in the foreground |
| // TODO: make measurments with background blend other than |
| // none. All of these measurements are done with a |
| // background blend of HWC_BLENDING_NONE, with the |
| // blend type of the foregound being varied. |
| uint32_t foregroundFormat = format->format; |
| for (vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| uint32_t num; |
| |
| const struct hwcTestGraphicFormat *backgroundFormatPtr |
| = hwcTestGraphicFormatLookup((*it).c_str()); |
| uint32_t backgroundFormat = backgroundFormatPtr->format; |
| |
| num = numOverlapping(backgroundFormat, foregroundFormat, |
| HWC_BLENDING_NONE, HWC_BLENDING_NONE); |
| measPtr->overlapBlendNone.push_back(num); |
| |
| num = numOverlapping(backgroundFormat, foregroundFormat, |
| HWC_BLENDING_NONE, HWC_BLENDING_PREMULT); |
| measPtr->overlapBlendPremult.push_back(num); |
| |
| num = numOverlapping(backgroundFormat, foregroundFormat, |
| HWC_BLENDING_NONE, HWC_BLENDING_COVERAGE); |
| measPtr->overlapBlendCoverage.push_back(num); |
| } |
| |
| } |
| |
| // Display overlap results |
| size_t indent = 2; |
| testPrintI("overlapping blend: none"); |
| printFormatHeadings(indent); |
| for (vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| printOverlapLine(indent, *it, measurements[it |
| - formats.begin()].overlapBlendNone); |
| } |
| testPrintI(""); |
| |
| testPrintI("overlapping blend: premult"); |
| printFormatHeadings(indent); |
| for (vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| printOverlapLine(indent, *it, measurements[it |
| - formats.begin()].overlapBlendPremult); |
| } |
| testPrintI(""); |
| |
| testPrintI("overlapping blend: coverage"); |
| printFormatHeadings(indent); |
| for (vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| printOverlapLine(indent, *it, measurements[it |
| - formats.begin()].overlapBlendCoverage); |
| } |
| testPrintI(""); |
| |
| // 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); |
| |
| return 0; |
| } |
| |
| // Determine the maximum number of overlays that are all of the same format |
| // that the HWC will commit to. If allowOverlap is true, then the rectangles |
| // are laid out on a diagonal starting from the upper left corner. With |
| // each rectangle adjust one pixel to the right and one pixel down. |
| // When allowOverlap is false, the rectangles are tiled in column major |
| // order. Note, column major ordering is used so that the initial rectangles |
| // are all on different horizontal scan rows. It is common that hardware |
| // has limits on the number of objects it can handle on any single row. |
| uint32_t maxOverlays(uint32_t format, bool allowOverlap) |
| { |
| unsigned int max = 0; |
| |
| for (unsigned int numRects = 1; numRects <= searchLimits.numOverlays; |
| numRects++) { |
| list<Rectangle> rectList; |
| |
| for (unsigned int x = 0; |
| (x + startDim.width()) < (unsigned int) width; |
| x += (allowOverlap) ? 1 : startDim.width()) { |
| for (unsigned int y = 0; |
| (y + startDim.height()) < (unsigned int) height; |
| y += (allowOverlap) ? 1 : startDim.height()) { |
| Rectangle rect(format, startDim, startDim); |
| rect.displayFrame.left = x; |
| rect.displayFrame.top = y; |
| rect.displayFrame.right = x + startDim.width(); |
| rect.displayFrame.bottom = y + startDim.height(); |
| |
| rectList.push_back(rect); |
| |
| if (rectList.size() >= numRects) { break; } |
| } |
| if (rectList.size() >= numRects) { break; } |
| } |
| |
| uint32_t num = numOverlays(rectList); |
| if (num > max) { max = num; } |
| } |
| |
| return max; |
| } |
| |
| // Measures what transforms (i.e. flip horizontal, rotate 180) are |
| // supported by the specified format |
| list<uint32_t> supportedTransforms(uint32_t format) |
| { |
| list<uint32_t> rv; |
| list<Rectangle> rectList; |
| Rectangle rect(format, startDim); |
| |
| // For each of the transform types |
| for (unsigned int idx = 0; idx < NUMA(transformType); idx++) { |
| unsigned int id = transformType[idx].id; |
| |
| rect.transform = id; |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| |
| if (num == 1) { |
| rv.push_back(id); |
| } |
| } |
| |
| return rv; |
| } |
| |
| // Determines which types of blends (i.e. none, premult, coverage) are |
| // supported by the specified format |
| list<uint32_t> supportedBlends(uint32_t format) |
| { |
| list<uint32_t> rv; |
| list<Rectangle> rectList; |
| Rectangle rect(format, startDim); |
| |
| // For each of the blend types |
| for (unsigned int idx = 0; idx < NUMA(blendType); idx++) { |
| unsigned int id = blendType[idx].id; |
| |
| rect.blend = id; |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| |
| if (num == 1) { |
| rv.push_back(id); |
| } |
| } |
| |
| return rv; |
| } |
| |
| // Determines the minimum width of any display frame of the given format |
| // that the HWC will commit to. |
| uint32_t dfMinWidth(uint32_t format) |
| { |
| uint32_t w; |
| list<Rectangle> rectList; |
| |
| for (w = 1; w <= startDim.width(); w++) { |
| HwcTestDim dim(w, startDim.height()); |
| Rectangle rect(format, dim); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return w; |
| } |
| } |
| if (w > startDim.width()) { |
| testPrintE("Failed to locate display frame min width"); |
| exit(33); |
| } |
| |
| return w; |
| } |
| |
| // Display frame minimum height |
| uint32_t dfMinHeight(uint32_t format) |
| { |
| uint32_t h; |
| list<Rectangle> rectList; |
| |
| for (h = 1; h <= startDim.height(); h++) { |
| HwcTestDim dim(startDim.width(), h); |
| Rectangle rect(format, dim); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return h; |
| } |
| } |
| if (h > startDim.height()) { |
| testPrintE("Failed to locate display frame min height"); |
| exit(34); |
| } |
| |
| return h; |
| } |
| |
| // Display frame maximum width |
| uint32_t dfMaxWidth(uint32_t format) |
| { |
| uint32_t w; |
| list<Rectangle> rectList; |
| |
| for (w = width; w >= startDim.width(); w--) { |
| HwcTestDim dim(w, startDim.height()); |
| Rectangle rect(format, dim); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return w; |
| } |
| } |
| if (w < startDim.width()) { |
| testPrintE("Failed to locate display frame max width"); |
| exit(35); |
| } |
| |
| return w; |
| } |
| |
| // Display frame maximum height |
| uint32_t dfMaxHeight(uint32_t format) |
| { |
| uint32_t h; |
| |
| for (h = height; h >= startDim.height(); h--) { |
| HwcTestDim dim(startDim.width(), h); |
| Rectangle rect(format, dim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return h; |
| } |
| } |
| if (h < startDim.height()) { |
| testPrintE("Failed to locate display frame max height"); |
| exit(36); |
| } |
| |
| return h; |
| } |
| |
| // Determine the minimum number of pixels that the HWC will ever commit to. |
| // Note, this might be different that dfMinWidth * dfMinHeight, in that this |
| // function adjusts both the width and height from the starting dimension. |
| HwcTestDim dfMinDim(uint32_t format) |
| { |
| uint64_t bestMinPixels = 0; |
| HwcTestDim bestDim; |
| bool bestSet = false; // True when value has been assigned to |
| // bestMinPixels and bestDim |
| |
| bool origVerbose = verbose; // Temporarily turn off verbose |
| verbose = false; |
| for (uint32_t w = 1; w <= startDim.width(); w++) { |
| for (uint32_t h = 1; h <= startDim.height(); h++) { |
| if (bestSet && ((w > bestMinPixels) || (h > bestMinPixels))) { |
| break; |
| } |
| |
| HwcTestDim dim(w, h); |
| Rectangle rect(format, dim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| uint64_t pixels = dim.width() * dim.height(); |
| if (!bestSet || (pixels < bestMinPixels)) { |
| bestMinPixels = pixels; |
| bestDim = dim; |
| bestSet = true; |
| } |
| } |
| } |
| } |
| verbose = origVerbose; |
| |
| if (!bestSet) { |
| testPrintE("Unable to locate display frame min dimension"); |
| exit(20); |
| } |
| |
| return bestDim; |
| } |
| |
| // Display frame maximum dimension |
| HwcTestDim dfMaxDim(uint32_t format) |
| { |
| uint64_t bestMaxPixels = 0; |
| HwcTestDim bestDim; |
| bool bestSet = false; // True when value has been assigned to |
| // bestMaxPixels and bestDim; |
| |
| // Potentially increase benchmark performance by first checking |
| // for the common case of supporting a full display frame. |
| HwcTestDim dim(width, height); |
| Rectangle rect(format, dim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num == 1) { return dim; } |
| |
| // TODO: Use a binary search |
| bool origVerbose = verbose; // Temporarily turn off verbose |
| verbose = false; |
| for (uint32_t w = startDim.width(); w <= (uint32_t) width; w++) { |
| for (uint32_t h = startDim.height(); h <= (uint32_t) height; h++) { |
| if (bestSet && ((w * h) <= bestMaxPixels)) { continue; } |
| |
| HwcTestDim dim(w, h); |
| Rectangle rect(format, dim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| uint64_t pixels = dim.width() * dim.height(); |
| if (!bestSet || (pixels > bestMaxPixels)) { |
| bestMaxPixels = pixels; |
| bestDim = dim; |
| bestSet = true; |
| } |
| } |
| } |
| } |
| verbose = origVerbose; |
| |
| if (!bestSet) { |
| testPrintE("Unable to locate display frame max dimension"); |
| exit(21); |
| } |
| |
| return bestDim; |
| } |
| |
| // Source crop minimum width |
| uint32_t scMinWidth(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint32_t w; |
| list<Rectangle> rectList; |
| |
| // Source crop frame min width |
| for (w = 1; w <= dfDim.width(); w++) { |
| Rectangle rect(format, dfDim, HwcTestDim(w, dfDim.height())); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return w; |
| } |
| } |
| testPrintE("Failed to locate source crop min width"); |
| exit(35); |
| } |
| |
| // Source crop minimum height |
| uint32_t scMinHeight(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint32_t h; |
| list<Rectangle> rectList; |
| |
| for (h = 1; h <= dfDim.height(); h++) { |
| Rectangle rect(format, dfDim, HwcTestDim(dfDim.width(), h)); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return h; |
| } |
| } |
| testPrintE("Failed to locate source crop min height"); |
| exit(36); |
| } |
| |
| // Source crop maximum width |
| uint32_t scMaxWidth(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint32_t w; |
| list<Rectangle> rectList; |
| |
| for (w = searchLimits.sourceCrop.width(); w >= dfDim.width(); w--) { |
| Rectangle rect(format, dfDim, HwcTestDim(w, dfDim.height())); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return w; |
| } |
| } |
| testPrintE("Failed to locate source crop max width"); |
| exit(35); |
| } |
| |
| // Source crop maximum height |
| uint32_t scMaxHeight(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint32_t h; |
| list<Rectangle> rectList; |
| |
| for (h = searchLimits.sourceCrop.height(); h >= dfDim.height(); h--) { |
| Rectangle rect(format, dfDim, HwcTestDim(dfDim.width(), h)); |
| rectList.clear(); |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| return h; |
| } |
| } |
| testPrintE("Failed to locate source crop max height"); |
| exit(36); |
| } |
| |
| // Source crop minimum dimension |
| // Discovers the source crop with the least number of pixels that the |
| // HWC will commit to. Note, this may be different from scMinWidth |
| // * scMinHeight, in that this function searches for a combination of |
| // width and height. While the other routines always keep one of the |
| // dimensions equal to the corresponding start dimension. |
| HwcTestDim scMinDim(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint64_t bestMinPixels = 0; |
| HwcTestDim bestDim; |
| bool bestSet = false; // True when value has been assigned to |
| // bestMinPixels and bestDim |
| |
| bool origVerbose = verbose; // Temporarily turn off verbose |
| verbose = false; |
| for (uint32_t w = 1; w <= dfDim.width(); w++) { |
| for (uint32_t h = 1; h <= dfDim.height(); h++) { |
| if (bestSet && ((w > bestMinPixels) || (h > bestMinPixels))) { |
| break; |
| } |
| |
| HwcTestDim dim(w, h); |
| Rectangle rect(format, dfDim, HwcTestDim(w, h)); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| uint64_t pixels = dim.width() * dim.height(); |
| if (!bestSet || (pixels < bestMinPixels)) { |
| bestMinPixels = pixels; |
| bestDim = dim; |
| bestSet = true; |
| } |
| } |
| } |
| } |
| verbose = origVerbose; |
| |
| if (!bestSet) { |
| testPrintE("Unable to locate source crop min dimension"); |
| exit(20); |
| } |
| |
| return bestDim; |
| } |
| |
| // Source crop maximum dimension |
| HwcTestDim scMaxDim(uint32_t format, const HwcTestDim& dfDim) |
| { |
| uint64_t bestMaxPixels = 0; |
| HwcTestDim bestDim; |
| bool bestSet = false; // True when value has been assigned to |
| // bestMaxPixels and bestDim; |
| |
| // Potentially increase benchmark performance by first checking |
| // for the common case of supporting the maximum checked source size |
| HwcTestDim dim = searchLimits.sourceCrop; |
| Rectangle rect(format, dfDim, searchLimits.sourceCrop); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num == 1) { return dim; } |
| |
| // TODO: Use a binary search |
| bool origVerbose = verbose; // Temporarily turn off verbose |
| verbose = false; |
| for (uint32_t w = dfDim.width(); |
| w <= searchLimits.sourceCrop.width(); w++) { |
| for (uint32_t h = dfDim.height(); |
| h <= searchLimits.sourceCrop.height(); h++) { |
| if (bestSet && ((w * h) <= bestMaxPixels)) { continue; } |
| |
| HwcTestDim dim(w, h); |
| Rectangle rect(format, dfDim, dim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| if (num > 0) { |
| uint64_t pixels = dim.width() * dim.height(); |
| if (!bestSet || (pixels > bestMaxPixels)) { |
| bestMaxPixels = pixels; |
| bestDim = dim; |
| bestSet = true; |
| } |
| } |
| } |
| } |
| verbose = origVerbose; |
| |
| if (!bestSet) { |
| testPrintE("Unable to locate source crop max dimension"); |
| exit(21); |
| } |
| |
| return bestDim; |
| } |
| |
| // Source crop horizontal scale |
| // Determines the maximum factor by which the source crop can be larger |
| // that the display frame. The commit point is discovered through a |
| // binary search of rational numbers. The numerator in each of the |
| // rational numbers contains the dimension for the source crop, while |
| // the denominator specifies the dimension for the display frame. On |
| // each pass of the binary search the mid-point between the greatest |
| // point committed to (best) and the smallest point in which a commit |
| // has failed is calculated. This mid-point is then passed to a function |
| // named double2Rational, which determines the closest rational numbers |
| // just below and above the mid-point. By default the lower rational |
| // number is used for the scale factor on the next pass of the binary |
| // search. The upper value is only used when best is already equal |
| // to the lower value. This only occurs when the lower value has already |
| // been tried. |
| Rational scHScale(uint32_t format, |
| const HwcTestDim& dfMin, const HwcTestDim& dfMax, |
| const HwcTestDim& scMin, const HwcTestDim& scMax, |
| HwcTestDim& outBestDf, HwcTestDim& outBestSc) |
| { |
| HwcTestDim scDim, dfDim; // Source crop and display frame dimension |
| Rational best(0, 1), minBad; // Current bounds for a binary search |
| // MinGood is set below the lowest |
| // possible scale. The value of minBad, |
| // will be set by the first pass |
| // of the binary search. |
| |
| // Perform the passes of the binary search |
| bool firstPass = true; |
| do { |
| // On first pass try the maximum scale within the search limits |
| if (firstPass) { |
| // Try the maximum possible scale, within the search limits |
| scDim = HwcTestDim(searchLimits.sourceCrop.width(), scMin.height()); |
| dfDim = dfMin; |
| } else { |
| // Subsequent pass |
| // Halve the difference between best and minBad. |
| Rational lower, upper, selected; |
| |
| // Try the closest ratio halfway between minBood and minBad; |
| // TODO: Avoid rounding issue by using Rational type for |
| // midpoint. For now will use double, which should |
| // have more than sufficient resolution. |
| double mid = (double) best |
| + ((double) minBad - (double) best) / 2.0; |
| Rational::double2Rational(mid, |
| Range(scMin.width(), scMax.width()), |
| Range(dfMin.width(), dfMax.width()), |
| lower, upper); |
| if (((lower == best) && (upper == minBad))) { |
| return best; |
| } |
| |
| // Use lower value unless its already been tried |
| selected = (lower != best) ? lower : upper; |
| |
| // Assign the size of the source crop and display frame |
| // from the selected ratio of source crop to display frame. |
| scDim = HwcTestDim(selected.numerator(), scMin.height()); |
| dfDim = HwcTestDim(selected.denominator(), dfMin.height()); |
| } |
| |
| // See if the HWC will commit to this combination |
| Rectangle rect(format, dfDim, scDim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| |
| if (verbose) { |
| testPrintI(" scHscale num: %u scale: %f dfDim: %s scDim: %s", |
| num, (float) Rational(scDim.width(), dfDim.width()), |
| ((string) dfDim).c_str(), ((string) scDim).c_str()); |
| } |
| if (num == 1) { |
| // HWC committed to the combination |
| // This is the best scale factor seen so far. Report the |
| // dimensions to the caller, in case nothing better is seen. |
| outBestDf = dfDim; |
| outBestSc = scDim; |
| |
| // Success on the first pass means the largest possible scale |
| // is supported, in which case no need to search any further. |
| if (firstPass) { return Rational(scDim.width(), dfDim.width()); } |
| |
| // Update the lower bound of the binary search |
| best = Rational(scDim.width(), dfDim.width()); |
| } else { |
| // HWC didn't commit to this combination, so update the |
| // upper bound of the binary search. |
| minBad = Rational(scDim.width(), dfDim.width()); |
| } |
| |
| firstPass = false; |
| } while (best != minBad); |
| |
| return best; |
| } |
| |
| // Source crop vertical scale |
| // Determines the maximum factor by which the source crop can be larger |
| // that the display frame. The commit point is discovered through a |
| // binary search of rational numbers. The numerator in each of the |
| // rational numbers contains the dimension for the source crop, while |
| // the denominator specifies the dimension for the display frame. On |
| // each pass of the binary search the mid-point between the greatest |
| // point committed to (best) and the smallest point in which a commit |
| // has failed is calculated. This mid-point is then passed to a function |
| // named double2Rational, which determines the closest rational numbers |
| // just below and above the mid-point. By default the lower rational |
| // number is used for the scale factor on the next pass of the binary |
| // search. The upper value is only used when best is already equal |
| // to the lower value. This only occurs when the lower value has already |
| // been tried. |
| Rational scVScale(uint32_t format, |
| const HwcTestDim& dfMin, const HwcTestDim& dfMax, |
| const HwcTestDim& scMin, const HwcTestDim& scMax, |
| HwcTestDim& outBestDf, HwcTestDim& outBestSc) |
| { |
| HwcTestDim scDim, dfDim; // Source crop and display frame dimension |
| Rational best(0, 1), minBad; // Current bounds for a binary search |
| // MinGood is set below the lowest |
| // possible scale. The value of minBad, |
| // will be set by the first pass |
| // of the binary search. |
| |
| // Perform the passes of the binary search |
| bool firstPass = true; |
| do { |
| // On first pass try the maximum scale within the search limits |
| if (firstPass) { |
| // Try the maximum possible scale, within the search limits |
| scDim = HwcTestDim(scMin.width(), searchLimits.sourceCrop.height()); |
| dfDim = dfMin; |
| } else { |
| // Subsequent pass |
| // Halve the difference between best and minBad. |
| Rational lower, upper, selected; |
| |
| // Try the closest ratio halfway between minBood and minBad; |
| // TODO: Avoid rounding issue by using Rational type for |
| // midpoint. For now will use double, which should |
| // have more than sufficient resolution. |
| double mid = (double) best |
| + ((double) minBad - (double) best) / 2.0; |
| Rational::double2Rational(mid, |
| Range(scMin.height(), scMax.height()), |
| Range(dfMin.height(), dfMax.height()), |
| lower, upper); |
| if (((lower == best) && (upper == minBad))) { |
| return best; |
| } |
| |
| // Use lower value unless its already been tried |
| selected = (lower != best) ? lower : upper; |
| |
| // Assign the size of the source crop and display frame |
| // from the selected ratio of source crop to display frame. |
| scDim = HwcTestDim(scMin.width(), selected.numerator()); |
| dfDim = HwcTestDim(dfMin.width(), selected.denominator()); |
| } |
| |
| // See if the HWC will commit to this combination |
| Rectangle rect(format, dfDim, scDim); |
| list<Rectangle> rectList; |
| rectList.push_back(rect); |
| uint32_t num = numOverlays(rectList); |
| |
| if (verbose) { |
| testPrintI(" scHscale num: %u scale: %f dfDim: %s scDim: %s", |
| num, (float) Rational(scDim.height(), dfDim.height()), |
| ((string) dfDim).c_str(), ((string) scDim).c_str()); |
| } |
| if (num == 1) { |
| // HWC committed to the combination |
| // This is the best scale factor seen so far. Report the |
| // dimensions to the caller, in case nothing better is seen. |
| outBestDf = dfDim; |
| outBestSc = scDim; |
| |
| // Success on the first pass means the largest possible scale |
| // is supported, in which case no need to search any further. |
| if (firstPass) { return Rational(scDim.height(), dfDim.height()); } |
| |
| // Update the lower bound of the binary search |
| best = Rational(scDim.height(), dfDim.height()); |
| } else { |
| // HWC didn't commit to this combination, so update the |
| // upper bound of the binary search. |
| minBad = Rational(scDim.height(), dfDim.height()); |
| } |
| |
| firstPass = false; |
| } while (best != minBad); |
| |
| return best; |
| } |
| |
| uint32_t numOverlapping(uint32_t backgroundFormat, uint32_t foregroundFormat, |
| uint32_t backgroundBlend, uint32_t foregroundBlend) |
| { |
| list<Rectangle> rectList; |
| |
| Rectangle background(backgroundFormat, startDim, startDim); |
| background.blend = backgroundBlend; |
| rectList.push_back(background); |
| |
| // TODO: Handle cases where startDim is so small that adding 5 |
| // causes frames not to overlap. |
| // TODO: Handle cases where startDim is so large that adding 5 |
| // cause a portion or all of the foreground displayFrame |
| // to be off the display. |
| Rectangle foreground(foregroundFormat, startDim, startDim); |
| foreground.displayFrame.left += 5; |
| foreground.displayFrame.top += 5; |
| foreground.displayFrame.right += 5; |
| foreground.displayFrame.bottom += 5; |
| background.blend = foregroundBlend; |
| rectList.push_back(foreground); |
| |
| uint32_t num = numOverlays(rectList); |
| |
| return num; |
| } |
| |
| Rectangle::Rectangle(uint32_t graphicFormat, HwcTestDim dfDim, |
| HwcTestDim sDim) : |
| format(graphicFormat), transform(defaultTransform), |
| blend(defaultBlend), color(defaultColor), alpha(defaultAlpha), |
| sourceCrop(sDim), displayFrame(dfDim) |
| { |
| // Set source dimension |
| // Can't use a base initializer, because the setting of format |
| // must be done before setting the sourceDimension. |
| setSourceDim(sDim); |
| } |
| |
| void Rectangle::setSourceDim(HwcTestDim dim) |
| { |
| this->sourceDim = dim; |
| |
| const struct hwcTestGraphicFormat *attrib; |
| attrib = hwcTestGraphicFormatLookup(this->format); |
| if (attrib != NULL) { |
| if (sourceDim.width() % attrib->wMod) { |
| sourceDim.setWidth(sourceDim.width() + attrib->wMod |
| - (sourceDim.width() % attrib->wMod)); |
| } |
| if (sourceDim.height() % attrib->hMod) { |
| sourceDim.setHeight(sourceDim.height() + attrib->hMod |
| - (sourceDim.height() % attrib->hMod)); |
| } |
| } |
| } |
| |
| // Rational member functions |
| bool Rational::operator==(const Rational& other) const |
| { |
| if (((uint64_t) _n * other._d) |
| == ((uint64_t) _d * other._n)) { return true; } |
| |
| return false; |
| } |
| |
| bool Rational::operator<(const Rational& other) const |
| { |
| if (((uint64_t) _n * other._d) |
| < ((uint64_t) _d * other._n)) { return true; } |
| |
| return false; |
| } |
| |
| Rational::operator string() const |
| { |
| ostringstream out; |
| |
| out << _n << '/' << _d; |
| |
| return out.str(); |
| } |
| |
| void Rational::double2Rational(double f, Range nRange, Range dRange, |
| Rational& lower, Rational& upper) |
| { |
| Rational bestLower(nRange.lower(), dRange.upper()); |
| Rational bestUpper(nRange.upper(), dRange.lower()); |
| |
| // Search for a better solution |
| for (uint32_t d = dRange.lower(); d <= dRange.upper(); d++) { |
| Rational val(d * f, d); // Lower, because double to int cast truncates |
| |
| if ((val.numerator() < nRange.lower()) |
| || (val.numerator() > nRange.upper())) { continue; } |
| |
| if (((double) val > (double) bestLower) && ((double) val <= f)) { |
| bestLower = val; |
| } |
| |
| val.setNumerator(val.numerator() + 1); |
| if (val.numerator() > nRange.upper()) { continue; } |
| |
| if (((double) val < (double) bestUpper) && ((double) val >= f)) { |
| bestUpper = val; |
| } |
| } |
| |
| lower = bestLower; |
| upper = bestUpper; |
| } |
| |
| // Local functions |
| |
| // Num Overlays |
| // Given a list of rectangles, determine how many HWC will commit to render |
| uint32_t numOverlays(list<Rectangle>& rectList) |
| { |
| hwc_display_contents_1_t *hwcList; |
| list<sp<GraphicBuffer> > buffers; |
| |
| hwcList = hwcTestCreateLayerList(rectList.size()); |
| if (hwcList == NULL) { |
| testPrintE("numOverlays create hwcList failed"); |
| exit(30); |
| } |
| |
| hwc_layer_1_t *layer = &hwcList->hwLayers[0]; |
| for (std::list<Rectangle>::iterator it = rectList.begin(); |
| it != rectList.end(); ++it, ++layer) { |
| // Allocate the texture for the source frame |
| // and push it onto the buffers list, so that it |
| // stays in scope until a return from this function. |
| sp<GraphicBuffer> texture; |
| texture = new GraphicBuffer(it->sourceDim.width(), |
| it->sourceDim.height(), |
| it->format, texUsage); |
| buffers.push_back(texture); |
| |
| layer->handle = texture->handle; |
| layer->blending = it->blend; |
| layer->transform = it->transform; |
| layer->sourceCrop = it->sourceCrop; |
| layer->displayFrame = it->displayFrame; |
| |
| layer->visibleRegionScreen.numRects = 1; |
| layer->visibleRegionScreen.rects = &layer->displayFrame; |
| } |
| |
| // Perform prepare operation |
| if (verbose) { testPrintI("Prepare:"); hwcTestDisplayList(hwcList); } |
| hwcDevice->prepare(hwcDevice, 1, &hwcList); |
| if (verbose) { |
| testPrintI("Post Prepare:"); |
| hwcTestDisplayListPrepareModifiable(hwcList); |
| } |
| |
| // Count the number of overlays |
| uint32_t total = 0; |
| for (unsigned int n1 = 0; n1 < hwcList->numHwLayers; n1++) { |
| if (hwcList->hwLayers[n1].compositionType == HWC_OVERLAY) { |
| total++; |
| } |
| } |
| |
| // Free the layer list and graphic buffers |
| hwcTestFreeLayerList(hwcList); |
| |
| return total; |
| } |
| |
| string transformList2str(const list<uint32_t>& transformList) |
| { |
| ostringstream out; |
| |
| for (list<uint32_t>::const_iterator it = transformList.begin(); |
| it != transformList.end(); ++it) { |
| uint32_t id = *it; |
| |
| if (it != transformList.begin()) { |
| out << ", "; |
| } |
| out << id; |
| |
| for (unsigned int idx = 0; idx < NUMA(transformType); idx++) { |
| if (id == transformType[idx].id) { |
| out << " (" << transformType[idx].desc << ')'; |
| break; |
| } |
| } |
| } |
| |
| return out.str(); |
| } |
| |
| string blendList2str(const list<uint32_t>& blendList) |
| { |
| ostringstream out; |
| |
| for (list<uint32_t>::const_iterator it = blendList.begin(); |
| it != blendList.end(); ++it) { |
| uint32_t id = *it; |
| |
| if (it != blendList.begin()) { |
| out << ", "; |
| } |
| out << id; |
| |
| for (unsigned int idx = 0; idx < NUMA(blendType); idx++) { |
| if (id == blendType[idx].id) { |
| out << " (" << blendType[idx].desc << ')'; |
| break; |
| } |
| } |
| } |
| |
| return out.str(); |
| } |
| |
| void init(void) |
| { |
| srand48(0); |
| |
| hwcTestInitDisplay(verbose, &dpy, &surface, &width, &height); |
| |
| hwcTestOpenHwc(&hwcDevice); |
| } |
| |
| void printFormatHeadings(size_t indent) |
| { |
| for (size_t row = 0; row <= maxHeadingLen; row++) { |
| ostringstream line; |
| for(vector<string>::iterator it = formats.begin(); |
| it != formats.end(); ++it) { |
| if ((maxHeadingLen - row) <= it->length()) { |
| if (row != maxHeadingLen) { |
| char ch = (*it)[it->length() - (maxHeadingLen - row)]; |
| line << ' ' << setw(printFieldWidth) << ch; |
| } else { |
| line << ' ' << string(printFieldWidth, '-'); |
| } |
| } else { |
| line << ' ' << setw(printFieldWidth) << ""; |
| } |
| } |
| testPrintI("%*s%s", indent + maxHeadingLen, "", |
| line.str().c_str()); |
| } |
| } |
| |
| void printOverlapLine(size_t indent, const string formatStr, |
| const vector<uint32_t>& results) |
| { |
| ostringstream line; |
| |
| line << setw(indent + maxHeadingLen - formatStr.length()) << ""; |
| |
| line << formatStr; |
| |
| for (vector<uint32_t>::const_iterator it = results.begin(); |
| it != results.end(); ++it) { |
| line << ' ' << setw(printFieldWidth) << *it; |
| } |
| |
| testPrintI("%s", line.str().c_str()); |
| } |
| |
| void printSyntax(const char *cmd) |
| { |
| testPrintE(" %s [options] [graphicFormat] ...", |
| cmd); |
| testPrintE(" options:"); |
| testPrintE(" -s [width, height] - start dimension"); |
| testPrintE(" -v - Verbose"); |
| testPrintE(""); |
| testPrintE(" graphic formats:"); |
| for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) { |
| testPrintE(" %s", hwcTestGraphicFormat[n1].desc); |
| } |
| } |