b685c54283
Change-Id: I07efff54f2980dcb013935747b03e099b8f1181b
1031 lines
35 KiB
C++
1031 lines
35 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 Test Library
|
|
* Utility library functions for use by the Hardware Composer test cases
|
|
*/
|
|
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <arpa/inet.h> // For ntohl() and htonl()
|
|
|
|
#include "hwcTestLib.h"
|
|
|
|
#include "EGLUtils.h"
|
|
|
|
// Defines
|
|
#define NUMA(a) (sizeof(a) / sizeof(a [0]))
|
|
|
|
// Function Prototypes
|
|
static void printGLString(const char *name, GLenum s);
|
|
static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE);
|
|
static void checkGlError(const char* op);
|
|
static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config);
|
|
|
|
using namespace std;
|
|
using namespace android;
|
|
|
|
|
|
#define BITSPERBYTE 8 // TODO: Obtain from <values.h>, once
|
|
// it has been added
|
|
|
|
// Initialize Display
|
|
void hwcTestInitDisplay(bool verbose, EGLDisplay *dpy, EGLSurface *surface,
|
|
EGLint *width, EGLint *height)
|
|
{
|
|
static EGLContext context;
|
|
|
|
int rv;
|
|
|
|
EGLBoolean returnValue;
|
|
EGLConfig myConfig = {0};
|
|
EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
|
|
EGLint sConfigAttribs[] = {
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_NONE };
|
|
EGLint majorVersion, minorVersion;
|
|
|
|
checkEglError("<init>");
|
|
*dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
checkEglError("eglGetDisplay");
|
|
if (*dpy == EGL_NO_DISPLAY) {
|
|
testPrintE("eglGetDisplay returned EGL_NO_DISPLAY");
|
|
exit(70);
|
|
}
|
|
|
|
returnValue = eglInitialize(*dpy, &majorVersion, &minorVersion);
|
|
checkEglError("eglInitialize", returnValue);
|
|
if (verbose) {
|
|
testPrintI("EGL version %d.%d", majorVersion, minorVersion);
|
|
}
|
|
if (returnValue != EGL_TRUE) {
|
|
testPrintE("eglInitialize failed");
|
|
exit(71);
|
|
}
|
|
|
|
EGLNativeWindowType window = android_createDisplaySurface();
|
|
if (window == NULL) {
|
|
testPrintE("android_createDisplaySurface failed");
|
|
exit(72);
|
|
}
|
|
returnValue = EGLUtils::selectConfigForNativeWindow(*dpy,
|
|
sConfigAttribs, window, &myConfig);
|
|
if (returnValue) {
|
|
testPrintE("EGLUtils::selectConfigForNativeWindow() returned %d",
|
|
returnValue);
|
|
exit(73);
|
|
}
|
|
checkEglError("EGLUtils::selectConfigForNativeWindow");
|
|
|
|
if (verbose) {
|
|
testPrintI("Chose this configuration:");
|
|
printEGLConfiguration(*dpy, myConfig);
|
|
}
|
|
|
|
*surface = eglCreateWindowSurface(*dpy, myConfig, window, NULL);
|
|
checkEglError("eglCreateWindowSurface");
|
|
if (*surface == EGL_NO_SURFACE) {
|
|
testPrintE("gelCreateWindowSurface failed.");
|
|
exit(74);
|
|
}
|
|
|
|
context = eglCreateContext(*dpy, myConfig, EGL_NO_CONTEXT, contextAttribs);
|
|
checkEglError("eglCreateContext");
|
|
if (context == EGL_NO_CONTEXT) {
|
|
testPrintE("eglCreateContext failed");
|
|
exit(75);
|
|
}
|
|
returnValue = eglMakeCurrent(*dpy, *surface, *surface, context);
|
|
checkEglError("eglMakeCurrent", returnValue);
|
|
if (returnValue != EGL_TRUE) {
|
|
testPrintE("eglMakeCurrent failed");
|
|
exit(76);
|
|
}
|
|
eglQuerySurface(*dpy, *surface, EGL_WIDTH, width);
|
|
checkEglError("eglQuerySurface");
|
|
eglQuerySurface(*dpy, *surface, EGL_HEIGHT, height);
|
|
checkEglError("eglQuerySurface");
|
|
|
|
if (verbose) {
|
|
testPrintI("Window dimensions: %d x %d", *width, *height);
|
|
|
|
printGLString("Version", GL_VERSION);
|
|
printGLString("Vendor", GL_VENDOR);
|
|
printGLString("Renderer", GL_RENDERER);
|
|
printGLString("Extensions", GL_EXTENSIONS);
|
|
}
|
|
}
|
|
|
|
// Open Hardware Composer Device
|
|
void hwcTestOpenHwc(hwc_composer_device_1_t **hwcDevicePtr)
|
|
{
|
|
int rv;
|
|
hw_module_t const *hwcModule;
|
|
|
|
if ((rv = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwcModule)) != 0) {
|
|
testPrintE("hw_get_module failed, rv: %i", rv);
|
|
errno = -rv;
|
|
perror(NULL);
|
|
exit(77);
|
|
}
|
|
if ((rv = hwc_open_1(hwcModule, hwcDevicePtr)) != 0) {
|
|
testPrintE("hwc_open failed, rv: %i", rv);
|
|
errno = -rv;
|
|
perror(NULL);
|
|
exit(78);
|
|
}
|
|
}
|
|
|
|
// Color fraction class to string conversion
|
|
ColorFract::operator string()
|
|
{
|
|
ostringstream out;
|
|
|
|
out << '[' << this->c1() << ", "
|
|
<< this->c2() << ", "
|
|
<< this->c3() << ']';
|
|
|
|
return out.str();
|
|
}
|
|
|
|
// Dimension class to string conversion
|
|
HwcTestDim::operator string()
|
|
{
|
|
ostringstream out;
|
|
|
|
out << '[' << this->width() << ", "
|
|
<< this->height() << ']';
|
|
|
|
return out.str();
|
|
}
|
|
|
|
// Dimension class to hwc_rect conversion
|
|
HwcTestDim::operator hwc_rect() const
|
|
{
|
|
hwc_rect rect;
|
|
|
|
rect.left = rect.top = 0;
|
|
|
|
rect.right = this->_w;
|
|
rect.bottom = this->_h;
|
|
|
|
return rect;
|
|
}
|
|
|
|
// Hardware Composer rectangle to string conversion
|
|
string hwcTestRect2str(const struct hwc_rect& rect)
|
|
{
|
|
ostringstream out;
|
|
|
|
out << '[';
|
|
out << rect.left << ", ";
|
|
out << rect.top << ", ";
|
|
out << rect.right << ", ";
|
|
out << rect.bottom;
|
|
out << ']';
|
|
|
|
return out.str();
|
|
}
|
|
|
|
// Parse HWC rectangle description of form [left, top, right, bottom]
|
|
struct hwc_rect hwcTestParseHwcRect(istringstream& in, bool& error)
|
|
{
|
|
struct hwc_rect rect;
|
|
char chStart, ch;
|
|
|
|
// Defensively specify that an error occurred. Will clear
|
|
// error flag if all of parsing succeeds.
|
|
error = true;
|
|
|
|
// First character should be a [ or <
|
|
in >> chStart;
|
|
if (!in || ((chStart != '<') && (chStart != '['))) { return rect; }
|
|
|
|
// Left
|
|
in >> rect.left;
|
|
if (!in) { return rect; }
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return rect; }
|
|
|
|
// Top
|
|
in >> rect.top;
|
|
if (!in) { return rect; }
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return rect; }
|
|
|
|
// Right
|
|
in >> rect.right;
|
|
if (!in) { return rect; }
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return rect; }
|
|
|
|
// Bottom
|
|
in >> rect.bottom;
|
|
if (!in) { return rect; }
|
|
|
|
// Closing > or ]
|
|
in >> ch;
|
|
if (!in) { return rect; }
|
|
if (((chStart == '<') && (ch != '>'))
|
|
|| ((chStart == '[') && (ch != ']'))) { return rect; }
|
|
|
|
// Validate right and bottom are greater than left and top
|
|
if ((rect.right <= rect.left) || (rect.bottom <= rect.top)) { return rect; }
|
|
|
|
// Made It, clear error indicator
|
|
error = false;
|
|
|
|
return rect;
|
|
}
|
|
|
|
// Parse dimension of form [width, height]
|
|
HwcTestDim hwcTestParseDim(istringstream& in, bool& error)
|
|
{
|
|
HwcTestDim dim;
|
|
char chStart, ch;
|
|
uint32_t val;
|
|
|
|
// Defensively specify that an error occurred. Will clear
|
|
// error flag if all of parsing succeeds.
|
|
error = true;
|
|
|
|
// First character should be a [ or <
|
|
in >> chStart;
|
|
if (!in || ((chStart != '<') && (chStart != '['))) { return dim; }
|
|
|
|
// Width
|
|
in >> val;
|
|
if (!in) { return dim; }
|
|
dim.setWidth(val);
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return dim; }
|
|
|
|
// Height
|
|
in >> val;
|
|
if (!in) { return dim; }
|
|
dim.setHeight(val);
|
|
|
|
// Closing > or ]
|
|
in >> ch;
|
|
if (!in) { return dim; }
|
|
if (((chStart == '<') && (ch != '>'))
|
|
|| ((chStart == '[') && (ch != ']'))) { return dim; }
|
|
|
|
// Validate width and height greater than 0
|
|
if ((dim.width() <= 0) || (dim.height() <= 0)) { return dim; }
|
|
|
|
// Made It, clear error indicator
|
|
error = false;
|
|
return dim;
|
|
}
|
|
|
|
// Parse fractional color of form [0.##, 0.##, 0.##]
|
|
// Fractional values can be from 0.0 to 1.0 inclusive. Note, integer
|
|
// values of 0.0 and 1.0, which are non-fractional, are considered valid.
|
|
// They are an exception, all other valid inputs are fractions.
|
|
ColorFract hwcTestParseColor(istringstream& in, bool& error)
|
|
{
|
|
ColorFract color;
|
|
char chStart, ch;
|
|
float c1, c2, c3;
|
|
|
|
// Defensively specify that an error occurred. Will clear
|
|
// error flag if all of parsing succeeds.
|
|
error = true;
|
|
|
|
// First character should be a [ or <
|
|
in >> chStart;
|
|
if (!in || ((chStart != '<') && (chStart != '['))) { return color; }
|
|
|
|
// 1st Component
|
|
in >> c1;
|
|
if (!in) { return color; }
|
|
if ((c1 < 0.0) || (c1 > 1.0)) { return color; }
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return color; }
|
|
|
|
// 2nd Component
|
|
in >> c2;
|
|
if (!in) { return color; }
|
|
if ((c2 < 0.0) || (c2 > 1.0)) { return color; }
|
|
in >> ch;
|
|
if (!in || (ch != ',')) { return color; }
|
|
|
|
// 3rd Component
|
|
in >> c3;
|
|
if (!in) { return color; }
|
|
if ((c3 < 0.0) || (c3 > 1.0)) { return color; }
|
|
|
|
// Closing > or ]
|
|
in >> ch;
|
|
if (!in) { return color; }
|
|
if (((chStart == '<') && (ch != '>'))
|
|
|| ((chStart == '[') && (ch != ']'))) { return color; }
|
|
|
|
// Are all the components fractional
|
|
if ((c1 < 0.0) || (c1 > 1.0)
|
|
|| (c2 < 0.0) || (c2 > 1.0)
|
|
|| (c3 < 0.0) || (c3 > 1.0)) { return color; }
|
|
|
|
// Made It, clear error indicator
|
|
error = false;
|
|
|
|
return ColorFract(c1, c2, c3);
|
|
}
|
|
|
|
// Look up and return pointer to structure with the characteristics
|
|
// of the graphic format named by the desc parameter. Search failure
|
|
// indicated by the return of NULL.
|
|
const struct hwcTestGraphicFormat *hwcTestGraphicFormatLookup(const char *desc)
|
|
{
|
|
for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) {
|
|
if (string(desc) == string(hwcTestGraphicFormat[n1].desc)) {
|
|
return &hwcTestGraphicFormat[n1];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Look up and return pointer to structure with the characteristics
|
|
// of the graphic format specified by the id parameter. Search failure
|
|
// indicated by the return of NULL.
|
|
const struct hwcTestGraphicFormat *hwcTestGraphicFormatLookup(uint32_t id)
|
|
{
|
|
for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) {
|
|
if (id == hwcTestGraphicFormat[n1].format) {
|
|
return &hwcTestGraphicFormat[n1];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Given the integer ID of a graphic format, return a pointer to
|
|
// a string that describes the format.
|
|
const char *hwcTestGraphicFormat2str(uint32_t format)
|
|
{
|
|
const static char *unknown = "unknown";
|
|
|
|
for (unsigned int n1 = 0; n1 < NUMA(hwcTestGraphicFormat); n1++) {
|
|
if (format == hwcTestGraphicFormat[n1].format) {
|
|
return hwcTestGraphicFormat[n1].desc;
|
|
}
|
|
}
|
|
|
|
return unknown;
|
|
}
|
|
|
|
/*
|
|
* hwcTestCreateLayerList
|
|
* Dynamically creates layer list with numLayers worth
|
|
* of hwLayers entries.
|
|
*/
|
|
hwc_display_contents_1_t *hwcTestCreateLayerList(size_t numLayers)
|
|
{
|
|
hwc_display_contents_1_t *list;
|
|
|
|
size_t size = sizeof(hwc_display_contents_1_t) + numLayers * sizeof(hwc_layer_1_t);
|
|
if ((list = (hwc_display_contents_1_t *) calloc(1, size)) == NULL) {
|
|
return NULL;
|
|
}
|
|
list->flags = HWC_GEOMETRY_CHANGED;
|
|
list->numHwLayers = numLayers;
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* hwcTestFreeLayerList
|
|
* Frees memory previous allocated via hwcTestCreateLayerList().
|
|
*/
|
|
void hwcTestFreeLayerList(hwc_display_contents_1_t *list)
|
|
{
|
|
free(list);
|
|
}
|
|
|
|
// Display the settings of the layer list pointed to by list
|
|
void hwcTestDisplayList(hwc_display_contents_1_t *list)
|
|
{
|
|
testPrintI(" flags: %#x%s", list->flags,
|
|
(list->flags & HWC_GEOMETRY_CHANGED) ? " GEOMETRY_CHANGED" : "");
|
|
testPrintI(" numHwLayers: %u", list->numHwLayers);
|
|
|
|
for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
|
|
testPrintI(" layer %u compositionType: %#x%s%s", layer,
|
|
list->hwLayers[layer].compositionType,
|
|
(list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
|
|
? " FRAMEBUFFER" : "",
|
|
(list->hwLayers[layer].compositionType == HWC_OVERLAY)
|
|
? " OVERLAY" : "");
|
|
|
|
testPrintI(" hints: %#x",
|
|
list->hwLayers[layer].hints,
|
|
(list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
|
|
? " TRIPLE_BUFFER" : "",
|
|
(list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
|
|
? " CLEAR_FB" : "");
|
|
|
|
testPrintI(" flags: %#x%s",
|
|
list->hwLayers[layer].flags,
|
|
(list->hwLayers[layer].flags & HWC_SKIP_LAYER)
|
|
? " SKIP_LAYER" : "");
|
|
|
|
testPrintI(" handle: %p",
|
|
list->hwLayers[layer].handle);
|
|
|
|
// Intentionally skipped display of ROT_180 & ROT_270,
|
|
// which are formed from combinations of the other flags.
|
|
testPrintI(" transform: %#x%s%s%s",
|
|
list->hwLayers[layer].transform,
|
|
(list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_H)
|
|
? " FLIP_H" : "",
|
|
(list->hwLayers[layer].transform & HWC_TRANSFORM_FLIP_V)
|
|
? " FLIP_V" : "",
|
|
(list->hwLayers[layer].transform & HWC_TRANSFORM_ROT_90)
|
|
? " ROT_90" : "");
|
|
|
|
testPrintI(" blending: %#x%s%s%s",
|
|
list->hwLayers[layer].blending,
|
|
(list->hwLayers[layer].blending == HWC_BLENDING_NONE)
|
|
? " NONE" : "",
|
|
(list->hwLayers[layer].blending == HWC_BLENDING_PREMULT)
|
|
? " PREMULT" : "",
|
|
(list->hwLayers[layer].blending == HWC_BLENDING_COVERAGE)
|
|
? " COVERAGE" : "");
|
|
|
|
testPrintI(" sourceCrop: %s",
|
|
hwcTestRect2str(list->hwLayers[layer].sourceCrop).c_str());
|
|
testPrintI(" displayFrame: %s",
|
|
hwcTestRect2str(list->hwLayers[layer].displayFrame).c_str());
|
|
testPrintI(" scaleFactor: [%f, %f]",
|
|
(float) (list->hwLayers[layer].sourceCrop.right
|
|
- list->hwLayers[layer].sourceCrop.left)
|
|
/ (float) (list->hwLayers[layer].displayFrame.right
|
|
- list->hwLayers[layer].displayFrame.left),
|
|
(float) (list->hwLayers[layer].sourceCrop.bottom
|
|
- list->hwLayers[layer].sourceCrop.top)
|
|
/ (float) (list->hwLayers[layer].displayFrame.bottom
|
|
- list->hwLayers[layer].displayFrame.top));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Display List Prepare Modifiable
|
|
*
|
|
* Displays the portions of a list that are meant to be modified by
|
|
* a prepare call.
|
|
*/
|
|
void hwcTestDisplayListPrepareModifiable(hwc_display_contents_1_t *list)
|
|
{
|
|
uint32_t numOverlays = 0;
|
|
for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
|
|
if (list->hwLayers[layer].compositionType == HWC_OVERLAY) {
|
|
numOverlays++;
|
|
}
|
|
testPrintI(" layer %u compositionType: %#x%s%s", layer,
|
|
list->hwLayers[layer].compositionType,
|
|
(list->hwLayers[layer].compositionType == HWC_FRAMEBUFFER)
|
|
? " FRAMEBUFFER" : "",
|
|
(list->hwLayers[layer].compositionType == HWC_OVERLAY)
|
|
? " OVERLAY" : "");
|
|
testPrintI(" hints: %#x%s%s",
|
|
list->hwLayers[layer].hints,
|
|
(list->hwLayers[layer].hints & HWC_HINT_TRIPLE_BUFFER)
|
|
? " TRIPLE_BUFFER" : "",
|
|
(list->hwLayers[layer].hints & HWC_HINT_CLEAR_FB)
|
|
? " CLEAR_FB" : "");
|
|
}
|
|
testPrintI(" numOverlays: %u", numOverlays);
|
|
}
|
|
|
|
/*
|
|
* Display List Handles
|
|
*
|
|
* Displays the handles of all the graphic buffers in the list.
|
|
*/
|
|
void hwcTestDisplayListHandles(hwc_display_contents_1_t *list)
|
|
{
|
|
const unsigned int maxLayersPerLine = 6;
|
|
|
|
ostringstream str(" layers:");
|
|
for (unsigned int layer = 0; layer < list->numHwLayers; layer++) {
|
|
str << ' ' << list->hwLayers[layer].handle;
|
|
if (((layer % maxLayersPerLine) == (maxLayersPerLine - 1))
|
|
&& (layer != list->numHwLayers - 1)) {
|
|
testPrintI("%s", str.str().c_str());
|
|
str.str(" ");
|
|
}
|
|
}
|
|
testPrintI("%s", str.str().c_str());
|
|
}
|
|
|
|
// Returns a uint32_t that contains a format specific representation of a
|
|
// single pixel of the given color and alpha values.
|
|
uint32_t hwcTestColor2Pixel(uint32_t format, ColorFract color, float alpha)
|
|
{
|
|
const struct attrib {
|
|
uint32_t format;
|
|
bool hostByteOrder;
|
|
size_t bytes;
|
|
size_t c1Offset;
|
|
size_t c1Size;
|
|
size_t c2Offset;
|
|
size_t c2Size;
|
|
size_t c3Offset;
|
|
size_t c3Size;
|
|
size_t aOffset;
|
|
size_t aSize;
|
|
} attributes[] = {
|
|
{HAL_PIXEL_FORMAT_RGBA_8888, false, 4, 0, 8, 8, 8, 16, 8, 24, 8},
|
|
{HAL_PIXEL_FORMAT_RGBX_8888, false, 4, 0, 8, 8, 8, 16, 8, 0, 0},
|
|
{HAL_PIXEL_FORMAT_RGB_888, false, 3, 0, 8, 8, 8, 16, 8, 0, 0},
|
|
{HAL_PIXEL_FORMAT_RGB_565, true, 2, 0, 5, 5, 6, 11, 5, 0, 0},
|
|
{HAL_PIXEL_FORMAT_BGRA_8888, false, 4, 16, 8, 8, 8, 0, 8, 24, 8},
|
|
{HAL_PIXEL_FORMAT_RGBA_5551, true , 2, 0, 5, 5, 5, 10, 5, 15, 1},
|
|
{HAL_PIXEL_FORMAT_RGBA_4444, false, 2, 12, 4, 0, 4, 4, 4, 8, 4},
|
|
{HAL_PIXEL_FORMAT_YV12, true, 3, 16, 8, 8, 8, 0, 8, 0, 0},
|
|
};
|
|
|
|
const struct attrib *attrib;
|
|
for (attrib = attributes; attrib < attributes + NUMA(attributes);
|
|
attrib++) {
|
|
if (attrib->format == format) { break; }
|
|
}
|
|
if (attrib >= attributes + NUMA(attributes)) {
|
|
testPrintE("colorFract2Pixel unsupported format of: %u", format);
|
|
exit(80);
|
|
}
|
|
|
|
uint32_t pixel;
|
|
pixel = htonl((uint32_t) round((((1 << attrib->c1Size) - 1) * color.c1()))
|
|
<< ((sizeof(pixel) * BITSPERBYTE)
|
|
- (attrib->c1Offset + attrib->c1Size)));
|
|
pixel |= htonl((uint32_t) round((((1 << attrib->c2Size) - 1) * color.c2()))
|
|
<< ((sizeof(pixel) * BITSPERBYTE)
|
|
- (attrib->c2Offset + attrib->c2Size)));
|
|
pixel |= htonl((uint32_t) round((((1 << attrib->c3Size) - 1) * color.c3()))
|
|
<< ((sizeof(pixel) * BITSPERBYTE)
|
|
- (attrib->c3Offset + attrib->c3Size)));
|
|
if (attrib->aSize) {
|
|
pixel |= htonl((uint32_t) round((((1 << attrib->aSize) - 1) * alpha))
|
|
<< ((sizeof(pixel) * BITSPERBYTE)
|
|
- (attrib->aOffset + attrib->aSize)));
|
|
}
|
|
if (attrib->hostByteOrder) {
|
|
pixel = ntohl(pixel);
|
|
pixel >>= sizeof(pixel) * BITSPERBYTE - attrib->bytes * BITSPERBYTE;
|
|
}
|
|
|
|
return pixel;
|
|
}
|
|
|
|
// Sets the pixel at the given x and y coordinates to the color and alpha
|
|
// value given by pixel. The contents of pixel is format specific. It's
|
|
// value should come from a call to hwcTestColor2Pixel().
|
|
void hwcTestSetPixel(GraphicBuffer *gBuf, unsigned char *buf,
|
|
uint32_t x, uint32_t y, uint32_t pixel)
|
|
{
|
|
|
|
const struct attrib {
|
|
int format;
|
|
size_t bytes;
|
|
} attributes[] = {
|
|
{HAL_PIXEL_FORMAT_RGBA_8888, 4},
|
|
{HAL_PIXEL_FORMAT_RGBX_8888, 4},
|
|
{HAL_PIXEL_FORMAT_RGB_888, 3},
|
|
{HAL_PIXEL_FORMAT_RGB_565, 2},
|
|
{HAL_PIXEL_FORMAT_BGRA_8888, 4},
|
|
{HAL_PIXEL_FORMAT_RGBA_5551, 2},
|
|
{HAL_PIXEL_FORMAT_RGBA_4444, 2},
|
|
};
|
|
|
|
if (gBuf->getPixelFormat() == HAL_PIXEL_FORMAT_YV12) {
|
|
uint32_t yPlaneOffset, uPlaneOffset, vPlaneOffset;
|
|
uint32_t yPlaneStride = gBuf->getStride();
|
|
uint32_t uPlaneStride = ((gBuf->getStride() / 2) + 0xf) & ~0xf;
|
|
uint32_t vPlaneStride = uPlaneStride;
|
|
yPlaneOffset = 0;
|
|
vPlaneOffset = yPlaneOffset + yPlaneStride * gBuf->getHeight();
|
|
uPlaneOffset = vPlaneOffset
|
|
+ vPlaneStride * (gBuf->getHeight() / 2);
|
|
*(buf + yPlaneOffset + y * yPlaneStride + x) = pixel & 0xff;
|
|
*(buf + uPlaneOffset + (y / 2) * uPlaneStride + (x / 2))
|
|
= (pixel & 0xff00) >> 8;
|
|
*(buf + vPlaneOffset + (y / 2) * vPlaneStride + (x / 2))
|
|
= (pixel & 0xff0000) >> 16;
|
|
|
|
return;
|
|
}
|
|
|
|
const struct attrib *attrib;
|
|
for (attrib = attributes; attrib < attributes + NUMA(attributes);
|
|
attrib++) {
|
|
if (attrib->format == gBuf->getPixelFormat()) { break; }
|
|
}
|
|
if (attrib >= attributes + NUMA(attributes)) {
|
|
testPrintE("setPixel unsupported format of: %u",
|
|
gBuf->getPixelFormat());
|
|
exit(90);
|
|
}
|
|
|
|
memmove(buf + ((gBuf->getStride() * attrib->bytes) * y)
|
|
+ (attrib->bytes * x), &pixel, attrib->bytes);
|
|
}
|
|
|
|
// Fill a given graphic buffer with a uniform color and alpha
|
|
void hwcTestFillColor(GraphicBuffer *gBuf, ColorFract color, float alpha)
|
|
{
|
|
unsigned char* buf = NULL;
|
|
status_t err;
|
|
uint32_t pixel;
|
|
|
|
pixel = hwcTestColor2Pixel(gBuf->getPixelFormat(), color, alpha);
|
|
|
|
err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
|
|
if (err != 0) {
|
|
testPrintE("hwcTestFillColor lock failed: %d", err);
|
|
exit(100);
|
|
}
|
|
|
|
for (unsigned int x = 0; x < gBuf->getStride(); x++) {
|
|
for (unsigned int y = 0; y < gBuf->getHeight(); y++) {
|
|
uint32_t val = pixel;
|
|
hwcTestSetPixel(gBuf, buf, x, y, (x < gBuf->getWidth())
|
|
? pixel : testRand());
|
|
}
|
|
}
|
|
|
|
err = gBuf->unlock();
|
|
if (err != 0) {
|
|
testPrintE("hwcTestFillColor unlock failed: %d", err);
|
|
exit(101);
|
|
}
|
|
}
|
|
|
|
// Fill the given buffer with a horizontal blend of colors, with the left
|
|
// side color given by startColor and the right side color given by
|
|
// endColor. The startColor and endColor values are specified in the format
|
|
// given by colorFormat, which might be different from the format of the
|
|
// graphic buffer. When different, a color conversion is done when possible
|
|
// to the graphic format of the graphic buffer. A color of black is
|
|
// produced for cases where the conversion is impossible (e.g. out of gamut
|
|
// values).
|
|
void hwcTestFillColorHBlend(GraphicBuffer *gBuf, uint32_t colorFormat,
|
|
ColorFract startColor, ColorFract endColor)
|
|
{
|
|
status_t err;
|
|
unsigned char* buf = NULL;
|
|
const uint32_t width = gBuf->getWidth();
|
|
const uint32_t height = gBuf->getHeight();
|
|
const uint32_t stride = gBuf->getStride();
|
|
|
|
err = gBuf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&buf));
|
|
if (err != 0) {
|
|
testPrintE("hwcTestFillColorHBlend lock failed: %d", err);
|
|
exit(110);
|
|
}
|
|
|
|
for (unsigned int x = 0; x < stride; x++) {
|
|
uint32_t pixel;
|
|
if (x < width) {
|
|
ColorFract color(startColor.c1() + (endColor.c1() - startColor.c1())
|
|
* ((float) x / (float) (width - 1)),
|
|
startColor.c2() + (endColor.c2() - startColor.c2())
|
|
* ((float) x / (float) (width - 1)),
|
|
startColor.c3() + (endColor.c3() - startColor.c3())
|
|
* ((float) x / (float) (width - 1)));
|
|
|
|
// When formats differ, convert colors.
|
|
// Important to not convert when formats are the same, since
|
|
// out of gamut colors are always converted to black.
|
|
if (colorFormat != (uint32_t) gBuf->getPixelFormat()) {
|
|
hwcTestColorConvert(colorFormat, gBuf->getPixelFormat(), color);
|
|
}
|
|
pixel = hwcTestColor2Pixel(gBuf->getPixelFormat(), color, 1.0);
|
|
} else {
|
|
// Fill pad with random values
|
|
pixel = testRand();
|
|
}
|
|
|
|
for (unsigned int y = 0; y < height; y++) {
|
|
hwcTestSetPixel(gBuf, buf, x, y, pixel);
|
|
}
|
|
}
|
|
|
|
err = gBuf->unlock();
|
|
if (err != 0) {
|
|
testPrintE("hwcTestFillColorHBlend unlock failed: %d", err);
|
|
exit(111);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When possible, converts color specified as a full range value in
|
|
* the fromFormat, into an equivalent full range color in the toFormat.
|
|
* When conversion is impossible (e.g. out of gamut color) a color
|
|
* or black in the full range output format is produced. The input
|
|
* color is given as a fractional color in the parameter named color.
|
|
* The produced color is written over the same parameter used to
|
|
* provide the input color.
|
|
*
|
|
* Each graphic format has 3 color components and each of these
|
|
* components has both a full and in gamut range. This function uses
|
|
* a table that provides the full and in gamut ranges of each of the
|
|
* supported graphic formats. The full range is given by members named
|
|
* c[123]Min to c[123]Max, while the in gamut range is given by members
|
|
* named c[123]Low to c[123]High. In most cases the full and in gamut
|
|
* ranges are equivalent. This occurs when the c[123]Min == c[123]Low and
|
|
* c[123]High == c[123]Max.
|
|
*
|
|
* The input and produced colors are both specified as a fractional amount
|
|
* of the full range. The diagram below provides an overview of the
|
|
* conversion process. The main steps are:
|
|
*
|
|
* 1. Produce black if the input color is out of gamut.
|
|
*
|
|
* 2. Convert the in gamut color into the fraction of the fromFromat
|
|
* in gamut range.
|
|
*
|
|
* 3. Convert from the fraction of the in gamut from format range to
|
|
* the fraction of the in gamut to format range. Produce black
|
|
* if an equivalent color does not exists.
|
|
*
|
|
* 4. Covert from the fraction of the in gamut to format to the
|
|
* fraction of the full range to format.
|
|
*
|
|
* From Format To Format
|
|
* max high high max
|
|
* ----+ +-----------+
|
|
* high \ / \ high
|
|
* ------\-------------+ +-------->
|
|
* \
|
|
* \ +--- black --+
|
|
* \ / \
|
|
* \ / +-->
|
|
* low \ / low
|
|
* -------- ---+-- black --+
|
|
* min low low min
|
|
* ^ ^ ^ ^ ^
|
|
* | | | | |
|
|
* | | | | +-- fraction of full range
|
|
* | | | +-- fraction of valid range
|
|
* | | +-- fromFormat to toFormat color conversion
|
|
* | +-- fraction of valid range
|
|
* +-- fraction of full range
|
|
*/
|
|
void hwcTestColorConvert(uint32_t fromFormat, uint32_t toFormat,
|
|
ColorFract& color)
|
|
{
|
|
const struct attrib {
|
|
uint32_t format;
|
|
bool rgb;
|
|
bool yuv;
|
|
int c1Min, c1Low, c1High, c1Max;
|
|
int c2Min, c2Low, c2High, c2Max;
|
|
int c3Min, c3Low, c3High, c3Max;
|
|
} attributes[] = {
|
|
{HAL_PIXEL_FORMAT_RGBA_8888, true, false,
|
|
0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255},
|
|
{HAL_PIXEL_FORMAT_RGBX_8888, true, false,
|
|
0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255},
|
|
{HAL_PIXEL_FORMAT_RGB_888, true, false,
|
|
0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255},
|
|
{HAL_PIXEL_FORMAT_RGB_565, true, false,
|
|
0, 0, 31, 31, 0, 0, 63, 63, 0, 0, 31, 31},
|
|
{HAL_PIXEL_FORMAT_BGRA_8888, true, false,
|
|
0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255},
|
|
{HAL_PIXEL_FORMAT_RGBA_5551, true, false,
|
|
0, 0, 31, 31, 0, 0, 31, 31, 0, 0, 31, 31},
|
|
{HAL_PIXEL_FORMAT_RGBA_4444, true, false,
|
|
0, 0, 15, 15, 0, 0, 15, 15, 0, 0, 15, 15},
|
|
{HAL_PIXEL_FORMAT_YV12, false, true,
|
|
0, 16, 235, 255, 0, 16, 240, 255, 0, 16, 240, 255},
|
|
};
|
|
|
|
const struct attrib *fromAttrib;
|
|
for (fromAttrib = attributes; fromAttrib < attributes + NUMA(attributes);
|
|
fromAttrib++) {
|
|
if (fromAttrib->format == fromFormat) { break; }
|
|
}
|
|
if (fromAttrib >= attributes + NUMA(attributes)) {
|
|
testPrintE("hwcTestColorConvert unsupported from format of: %u",
|
|
fromFormat);
|
|
exit(120);
|
|
}
|
|
|
|
const struct attrib *toAttrib;
|
|
for (toAttrib = attributes; toAttrib < attributes + NUMA(attributes);
|
|
toAttrib++) {
|
|
if (toAttrib->format == toFormat) { break; }
|
|
}
|
|
if (toAttrib >= attributes + NUMA(attributes)) {
|
|
testPrintE("hwcTestColorConvert unsupported to format of: %u",
|
|
toFormat);
|
|
exit(121);
|
|
}
|
|
|
|
// Produce black if any of the from components are outside the
|
|
// valid color range
|
|
float c1Val = fromAttrib->c1Min
|
|
+ ((float) (fromAttrib->c1Max - fromAttrib->c1Min) * color.c1());
|
|
float c2Val = fromAttrib->c2Min
|
|
+ ((float) (fromAttrib->c2Max - fromAttrib->c2Min) * color.c2());
|
|
float c3Val = fromAttrib->c3Min
|
|
+ ((float) (fromAttrib->c3Max - fromAttrib->c3Min) * color.c3());
|
|
if ((c1Val < fromAttrib->c1Low) || (c1Val > fromAttrib->c1High)
|
|
|| (c2Val < fromAttrib->c2Low) || (c2Val > fromAttrib->c2High)
|
|
|| (c3Val < fromAttrib->c3Low) || (c3Val > fromAttrib->c3High)) {
|
|
|
|
// Return black
|
|
// Will use representation of black from RGBA8888 graphic format
|
|
// and recursively convert it to the requested graphic format.
|
|
color = ColorFract(0.0, 0.0, 0.0);
|
|
hwcTestColorConvert(HAL_PIXEL_FORMAT_RGBA_8888, toFormat, color);
|
|
return;
|
|
}
|
|
|
|
// Within from format, convert from fraction of full range
|
|
// to fraction of valid range
|
|
color = ColorFract((c1Val - fromAttrib->c1Low)
|
|
/ (fromAttrib->c1High - fromAttrib->c1Low),
|
|
(c2Val - fromAttrib->c2Low)
|
|
/ (fromAttrib->c2High - fromAttrib->c2Low),
|
|
(c3Val - fromAttrib->c3Low)
|
|
/ (fromAttrib->c3High - fromAttrib->c3Low));
|
|
|
|
// If needed perform RGB to YUV conversion
|
|
float wr = 0.2126, wg = 0.7152, wb = 0.0722; // ITU709 recommended constants
|
|
if (fromAttrib->rgb && toAttrib->yuv) {
|
|
float r = color.c1(), g = color.c2(), b = color.c3();
|
|
float y = wr * r + wg * g + wb * b;
|
|
float u = 0.5 * ((b - y) / (1.0 - wb)) + 0.5;
|
|
float v = 0.5 * ((r - y) / (1.0 - wr)) + 0.5;
|
|
|
|
// Produce black if color is outside the YUV gamut
|
|
if ((y < 0.0) || (y > 1.0)
|
|
|| (u < 0.0) || (u > 1.0)
|
|
|| (v < 0.0) || (v > 1.0)) {
|
|
y = 0.0;
|
|
u = v = 0.5;
|
|
}
|
|
|
|
color = ColorFract(y, u, v);
|
|
}
|
|
|
|
// If needed perform YUV to RGB conversion
|
|
// Equations determined from the ITU709 equations for RGB to YUV
|
|
// conversion, plus the following algebra:
|
|
//
|
|
// u = 0.5 * ((b - y) / (1.0 - wb)) + 0.5
|
|
// 0.5 * ((b - y) / (1.0 - wb)) = u - 0.5
|
|
// (b - y) / (1.0 - wb) = 2 * (u - 0.5)
|
|
// b - y = 2 * (u - 0.5) * (1.0 - wb)
|
|
// b = 2 * (u - 0.5) * (1.0 - wb) + y
|
|
//
|
|
// v = 0.5 * ((r -y) / (1.0 - wr)) + 0.5
|
|
// 0.5 * ((r - y) / (1.0 - wr)) = v - 0.5
|
|
// (r - y) / (1.0 - wr) = 2 * (v - 0.5)
|
|
// r - y = 2 * (v - 0.5) * (1.0 - wr)
|
|
// r = 2 * (v - 0.5) * (1.0 - wr) + y
|
|
//
|
|
// y = wr * r + wg * g + wb * b
|
|
// wr * r + wg * g + wb * b = y
|
|
// wg * g = y - wr * r - wb * b
|
|
// g = (y - wr * r - wb * b) / wg
|
|
if (fromAttrib->yuv && toAttrib->rgb) {
|
|
float y = color.c1(), u = color.c2(), v = color.c3();
|
|
float r = 2.0 * (v - 0.5) * (1.0 - wr) + y;
|
|
float b = 2.0 * (u - 0.5) * (1.0 - wb) + y;
|
|
float g = (y - wr * r - wb * b) / wg;
|
|
|
|
// Produce black if color is outside the RGB gamut
|
|
if ((r < 0.0) || (r > 1.0)
|
|
|| (g < 0.0) || (g > 1.0)
|
|
|| (b < 0.0) || (b > 1.0)) {
|
|
r = g = b = 0.0;
|
|
}
|
|
|
|
color = ColorFract(r, g, b);
|
|
}
|
|
|
|
// Within to format, convert from fraction of valid range
|
|
// to fraction of full range
|
|
c1Val = (toAttrib->c1Low
|
|
+ (float) (toAttrib->c1High - toAttrib->c1Low) * color.c1());
|
|
c2Val = (toAttrib->c1Low
|
|
+ (float) (toAttrib->c2High - toAttrib->c2Low) * color.c2());
|
|
c3Val = (toAttrib->c1Low
|
|
+ (float) (toAttrib->c3High - toAttrib->c3Low) * color.c3());
|
|
color = ColorFract((float) (c1Val - toAttrib->c1Min)
|
|
/ (float) (toAttrib->c1Max - toAttrib->c1Min),
|
|
(float) (c2Val - toAttrib->c2Min)
|
|
/ (float) (toAttrib->c2Max - toAttrib->c2Min),
|
|
(float) (c3Val - toAttrib->c3Min)
|
|
/ (float) (toAttrib->c3Max - toAttrib->c3Min));
|
|
}
|
|
|
|
// TODO: Use PrintGLString, CechckGlError, and PrintEGLConfiguration
|
|
// from libglTest
|
|
static void printGLString(const char *name, GLenum s)
|
|
{
|
|
const char *v = (const char *) glGetString(s);
|
|
|
|
if (v == NULL) {
|
|
testPrintI("GL %s unknown", name);
|
|
} else {
|
|
testPrintI("GL %s = %s", name, v);
|
|
}
|
|
}
|
|
|
|
static void checkEglError(const char* op, EGLBoolean returnVal)
|
|
{
|
|
if (returnVal != EGL_TRUE) {
|
|
testPrintE("%s() returned %d", op, returnVal);
|
|
}
|
|
|
|
for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
|
|
= eglGetError()) {
|
|
testPrintE("after %s() eglError %s (0x%x)",
|
|
op, EGLUtils::strerror(error), error);
|
|
}
|
|
}
|
|
|
|
static void checkGlError(const char* op)
|
|
{
|
|
for (GLint error = glGetError(); error; error
|
|
= glGetError()) {
|
|
testPrintE("after %s() glError (0x%x)", op, error);
|
|
}
|
|
}
|
|
|
|
static void printEGLConfiguration(EGLDisplay dpy, EGLConfig config)
|
|
{
|
|
|
|
#define X(VAL) {VAL, #VAL}
|
|
struct {EGLint attribute; const char* name;} names[] = {
|
|
X(EGL_BUFFER_SIZE),
|
|
X(EGL_ALPHA_SIZE),
|
|
X(EGL_BLUE_SIZE),
|
|
X(EGL_GREEN_SIZE),
|
|
X(EGL_RED_SIZE),
|
|
X(EGL_DEPTH_SIZE),
|
|
X(EGL_STENCIL_SIZE),
|
|
X(EGL_CONFIG_CAVEAT),
|
|
X(EGL_CONFIG_ID),
|
|
X(EGL_LEVEL),
|
|
X(EGL_MAX_PBUFFER_HEIGHT),
|
|
X(EGL_MAX_PBUFFER_PIXELS),
|
|
X(EGL_MAX_PBUFFER_WIDTH),
|
|
X(EGL_NATIVE_RENDERABLE),
|
|
X(EGL_NATIVE_VISUAL_ID),
|
|
X(EGL_NATIVE_VISUAL_TYPE),
|
|
X(EGL_SAMPLES),
|
|
X(EGL_SAMPLE_BUFFERS),
|
|
X(EGL_SURFACE_TYPE),
|
|
X(EGL_TRANSPARENT_TYPE),
|
|
X(EGL_TRANSPARENT_RED_VALUE),
|
|
X(EGL_TRANSPARENT_GREEN_VALUE),
|
|
X(EGL_TRANSPARENT_BLUE_VALUE),
|
|
X(EGL_BIND_TO_TEXTURE_RGB),
|
|
X(EGL_BIND_TO_TEXTURE_RGBA),
|
|
X(EGL_MIN_SWAP_INTERVAL),
|
|
X(EGL_MAX_SWAP_INTERVAL),
|
|
X(EGL_LUMINANCE_SIZE),
|
|
X(EGL_ALPHA_MASK_SIZE),
|
|
X(EGL_COLOR_BUFFER_TYPE),
|
|
X(EGL_RENDERABLE_TYPE),
|
|
X(EGL_CONFORMANT),
|
|
};
|
|
#undef X
|
|
|
|
for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) {
|
|
EGLint value = -1;
|
|
EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute,
|
|
&value);
|
|
EGLint error = eglGetError();
|
|
if (returnVal && error == EGL_SUCCESS) {
|
|
testPrintI(" %s: %d (%#x)", names[j].name, value, value);
|
|
}
|
|
}
|
|
testPrintI("");
|
|
}
|