5880cc5738
The acquire and release fences aren't yet used; this is just support for the new version and temporary backwards compatibility for older versions. Change-Id: Ia5ccc05a97c86f649042b9a35e11042fa0187e84
1562 lines
53 KiB
C++
1562 lines
53 KiB
C++
/*
|
|
* 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_layer_list_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, 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);
|
|
}
|
|
}
|