Add HDR dataspaces to EGL
bug: 63710530
Test: adb shell /data/nativetest/EGL_test/EGL_test
Test: adb -d shell am start -n \
com.drawelements.deqp/android.app.NativeActivity \
-e cmdLine '"deqp --deqp-case=dEQP-EGL.functional.hdr_color.* \
--deqp-log-filename=/sdcard/dEQP-Log.qpa"'
Change-Id: I52c43539806c901c674f037489d502d771080a30
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 94dfe6a..484e0ba 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -468,12 +468,17 @@
return HAL_DATASPACE_V0_SCRGB;
} else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
return HAL_DATASPACE_V0_SCRGB_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) {
+ return HAL_DATASPACE_BT2020_LINEAR;
+ } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) {
+ return HAL_DATASPACE_BT2020_PQ;
}
return dataSpace;
}
-// Return true if we stripped any EGL_GL_COLORSPACE_KHR attributes.
-static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
+// Return true if we stripped any EGL_GL_COLORSPACE_KHR or HDR metadata attributes.
+// Protect devices from attributes they don't recognize that are managed by Android
+static EGLBoolean stripAttributes(egl_display_ptr dp, const EGLint* attrib_list,
EGLint format,
std::vector<EGLint>& stripped_attrib_list) {
std::vector<EGLint> allowedColorSpaces;
@@ -494,33 +499,64 @@
break;
}
+ if (!attrib_list) return false;
+
bool stripped = false;
- if (attrib_list && dp->haveExtension("EGL_KHR_gl_colorspace")) {
- for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
- if (attr[0] == EGL_GL_COLORSPACE_KHR) {
- EGLint colorSpace = attr[1];
- bool found = false;
- // Verify that color space is allowed
- for (auto it : allowedColorSpaces) {
- if (colorSpace == it) {
- found = true;
+ for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
+ switch (attr[0]) {
+ case EGL_GL_COLORSPACE_KHR: {
+ EGLint colorSpace = attr[1];
+ bool found = false;
+ // Verify that color space is allowed
+ for (auto it : allowedColorSpaces) {
+ if (colorSpace == it) {
+ found = true;
+ }
+ }
+ if (found && dp->haveExtension("EGL_KHR_gl_colorspace")) {
+ stripped = true;
+ } else {
+ stripped_attrib_list.push_back(attr[0]);
+ stripped_attrib_list.push_back(attr[1]);
}
}
- if (!found) {
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+ case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+ case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+ case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+ case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+ if (dp->haveExtension("EGL_EXT_surface_SMPTE2086_metadata")) {
stripped = true;
} else {
stripped_attrib_list.push_back(attr[0]);
stripped_attrib_list.push_back(attr[1]);
}
- } else {
+ break;
+ case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+ case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+ if (dp->haveExtension("EGL_EXT_surface_CTA861_3_metadata")) {
+ stripped = true;
+ } else {
+ stripped_attrib_list.push_back(attr[0]);
+ stripped_attrib_list.push_back(attr[1]);
+ }
+ break;
+ default:
stripped_attrib_list.push_back(attr[0]);
stripped_attrib_list.push_back(attr[1]);
- }
+ break;
}
}
+
+ // Make sure there is at least one attribute
if (stripped) {
stripped_attrib_list.push_back(EGL_NONE);
- stripped_attrib_list.push_back(EGL_NONE);
}
return stripped;
}
@@ -544,10 +580,10 @@
// is available, so no need to verify.
found = true;
verify = false;
- } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_linear &&
+ } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT &&
dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_linear")) {
found = true;
- } else if (colorSpace == EGL_EXT_gl_colorspace_bt2020_pq &&
+ } else if (colorSpace == EGL_GL_COLORSPACE_BT2020_PQ_EXT &&
dp->haveExtension("EGL_EXT_gl_colorspace_bt2020_pq")) {
found = true;
} else if (colorSpace == EGL_GL_COLORSPACE_SCRGB_EXT &&
@@ -664,10 +700,43 @@
}
}
+EGLBoolean setSurfaceMetadata(egl_surface_t* s, NativeWindowType window,
+ const EGLint *attrib_list) {
+ // set any HDR metadata
+ bool smpte2086 = false;
+ bool cta8613 = false;
+ if (attrib_list == nullptr) return EGL_TRUE;
+
+ for (const EGLint* attr = attrib_list; attr[0] != EGL_NONE; attr += 2) {
+ smpte2086 |= s->setSmpte2086Attribute(attr[0], attr[1]);
+ cta8613 |= s->setCta8613Attribute(attr[0], attr[1]);
+ }
+ if (smpte2086) {
+ int err = native_window_set_buffers_smpte2086_metadata(window, s->getSmpte2086Metadata());
+ if (err != 0) {
+ ALOGE("error setting native window smpte2086 metadata: %s (%d)",
+ strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return EGL_FALSE;
+ }
+ }
+ if (cta8613) {
+ int err = native_window_set_buffers_cta861_3_metadata(window, s->getCta8613Metadata());
+ if (err != 0) {
+ ALOGE("error setting native window CTS 861.3 metadata: %s (%d)",
+ strerror(-err), err);
+ native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ return EGL_FALSE;
+ }
+ }
+ return EGL_TRUE;
+}
+
EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
NativeWindowType window,
const EGLint *attrib_list)
{
+ const EGLint *origAttribList = attrib_list;
clearError();
egl_connection_t* cnx = NULL;
@@ -705,7 +774,7 @@
}
std::vector<EGLint> strippedAttribList;
- if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+ if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
// Had to modify the attribute list due to use of color space.
// Use modified list from here on.
attrib_list = strippedAttribList.data();
@@ -741,7 +810,11 @@
if (surface != EGL_NO_SURFACE) {
egl_surface_t* s =
new egl_surface_t(dp.get(), config, window, surface, colorSpace, cnx);
- return s;
+
+ if (setSurfaceMetadata(s, window, origAttribList)) {
+ return s;
+ }
+ eglDestroySurface(dpy, s);
}
// EGLSurface creation failed
@@ -802,7 +875,7 @@
// colorspace. We do need to filter out color spaces the
// driver doesn't know how to process.
std::vector<EGLint> strippedAttribList;
- if (stripColorSpaceAttribute(dp, attrib_list, format, strippedAttribList)) {
+ if (stripAttributes(dp, attrib_list, format, strippedAttribList)) {
// Had to modify the attribute list due to use of color space.
// Use modified list from here on.
attrib_list = strippedAttribList.data();
@@ -850,12 +923,14 @@
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
egl_surface_t const * const s = get_surface(surface);
- if (attribute == EGL_GL_COLORSPACE_KHR) {
- *value = s->getColorSpace();
+ if (s->getColorSpaceAttribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getSmpte2086Attribute(attribute, value)) {
+ return EGL_TRUE;
+ } else if (s->getCta8613Attribute(attribute, value)) {
return EGL_TRUE;
}
- return s->cnx->egl.eglQuerySurface(
- dp->disp.dpy, s->surface, attribute, value);
+ return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value);
}
void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 3c1edd1..0f36614 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -214,6 +214,23 @@
"EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
}
+ bool hasHdrBoardConfig =
+ getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+
+ if (hasHdrBoardConfig) {
+ // hasHDRBoardConfig indicates the system is capable of supporting HDR content.
+ // Typically that means there is an HDR capable display attached, but could be
+ // support for attaching an HDR display. In either case, advertise support for
+ // HDR color spaces.
+ mExtensionString.append(
+ "EGL_EXT_gl_colorspace_bt2020_linear EGL_EXT_gl_colorspace_bt2020_pq ");
+ }
+
+ // Always advertise HDR metadata extensions since it's okay for an application
+ // to specify such information even though it may not be used by the system.
+ mExtensionString.append(
+ "EGL_EXT_surface_SMPTE2086_metadata EGL_EXT_surface_CTA861_3_metadata ");
+
char const* start = gExtensionString;
do {
// length of the extension name
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 72b4823..13b94b6 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -63,7 +63,9 @@
win(win),
cnx(cnx),
connected(true),
- colorSpace(colorSpace) {
+ colorSpace(colorSpace),
+ smpte2086_metadata({}),
+ cta861_3_metadata({}) {
if (win) {
win->incStrong(this);
}
@@ -86,6 +88,123 @@
}
}
+EGLBoolean egl_surface_t::setSmpte2086Attribute(EGLint attribute, EGLint value) {
+ switch (attribute) {
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+ smpte2086_metadata.displayPrimaryRed.x = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+ smpte2086_metadata.displayPrimaryRed.y = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+ smpte2086_metadata.displayPrimaryGreen.x = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+ smpte2086_metadata.displayPrimaryGreen.y = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+ smpte2086_metadata.displayPrimaryBlue.x = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+ smpte2086_metadata.displayPrimaryBlue.y = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+ smpte2086_metadata.whitePoint.x = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+ smpte2086_metadata.whitePoint.y = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+ smpte2086_metadata.maxLuminance = value;
+ return EGL_TRUE;
+ case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+ smpte2086_metadata.minLuminance = value;
+ return EGL_TRUE;
+ }
+ return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::setCta8613Attribute(EGLint attribute, EGLint value) {
+ switch (attribute) {
+ case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+ cta861_3_metadata.maxContentLightLevel = value;
+ return EGL_TRUE;
+ case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+ cta861_3_metadata.maxFrameAverageLightLevel = value;
+ return EGL_TRUE;
+ }
+ return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const {
+ if (attribute == EGL_GL_COLORSPACE_KHR) {
+ *value = colorSpace;
+ return EGL_TRUE;
+ }
+ return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const {
+ switch (attribute) {
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryRed.x);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryRed.y);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryGreen.x);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryGreen.y);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryBlue.x);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.displayPrimaryBlue.y);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_WHITE_POINT_X_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.whitePoint.x);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_WHITE_POINT_Y_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.whitePoint.y);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_MAX_LUMINANCE_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.maxLuminance);
+ return EGL_TRUE;
+ break;
+ case EGL_SMPTE2086_MIN_LUMINANCE_EXT:
+ *value = *reinterpret_cast<const int*>(&smpte2086_metadata.minLuminance);
+ return EGL_TRUE;
+ break;
+ }
+ return EGL_FALSE;
+}
+
+EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const {
+ switch (attribute) {
+ case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT:
+ *value = *reinterpret_cast<const int*>(&cta861_3_metadata.maxContentLightLevel);
+ return EGL_TRUE;
+ break;
+ case EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT:
+ *value = *reinterpret_cast<const int*>(&cta861_3_metadata.maxFrameAverageLightLevel);
+ return EGL_TRUE;
+ break;
+ }
+ return EGL_FALSE;
+}
+
void egl_surface_t::terminate() {
disconnect();
egl_object_t::terminate();
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 7c3075c..a9020a1 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -137,6 +137,13 @@
ANativeWindow* getNativeWindow() { return win; }
ANativeWindow* getNativeWindow() const { return win; }
EGLint getColorSpace() const { return colorSpace; }
+ EGLBoolean setSmpte2086Attribute(EGLint attribute, EGLint value);
+ EGLBoolean setCta8613Attribute(EGLint attribute, EGLint value);
+ EGLBoolean getColorSpaceAttribute(EGLint attribute, EGLint* value) const;
+ EGLBoolean getSmpte2086Attribute(EGLint attribute, EGLint* value) const;
+ EGLBoolean getCta8613Attribute(EGLint attribute, EGLint* value) const;
+ const android_smpte2086_metadata* getSmpte2086Metadata() const { return &smpte2086_metadata; }
+ const android_cta861_3_metadata* getCta8613Metadata() const { return &cta861_3_metadata; }
// Try to keep the order of these fields and size unchanged. It's not public API, but
// it's not hard to imagine native games accessing them.
@@ -150,6 +157,8 @@
bool connected;
void disconnect();
EGLint colorSpace;
+ android_smpte2086_metadata smpte2086_metadata;
+ android_cta861_3_metadata cta861_3_metadata;
};
class egl_context_t: public egl_object_t {
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index b67a053..8bb74a2 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -53,7 +53,20 @@
static bool hasWideColorDisplay =
getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+static bool hasHdrDisplay =
+ getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+
+union FlexAttribute {
+ EGLint uint_value;
+ float float_value;
+};
+
class EGLTest : public ::testing::Test {
+public:
+ void get8BitConfig(EGLConfig& config);
+ void addOptionalWindowMetadata(std::vector<EGLint>& attrs);
+ void checkOptionalWindowMetadata(EGLSurface eglSurface);
+
protected:
EGLDisplay mEglDisplay;
@@ -365,6 +378,261 @@
EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
}
+void EGLTest::get8BitConfig(EGLConfig& config) {
+ EGLint numConfigs;
+ EGLBoolean success;
+
+ // Use 8-bit to keep focus on colorspace aspect
+ const EGLint attrs[] = {
+ // clang-format off
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FIXED_EXT,
+ EGL_NONE,
+ // clang-format on
+ };
+ success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(1, numConfigs);
+
+ EGLint components[4];
+ EGLint value;
+ eglGetConfigAttrib(mEglDisplay, config, EGL_CONFIG_ID, &value);
+
+ success = eglGetConfigAttrib(mEglDisplay, config, EGL_RED_SIZE, &components[0]);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ success = eglGetConfigAttrib(mEglDisplay, config, EGL_GREEN_SIZE, &components[1]);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ success = eglGetConfigAttrib(mEglDisplay, config, EGL_BLUE_SIZE, &components[2]);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ success = eglGetConfigAttrib(mEglDisplay, config, EGL_ALPHA_SIZE, &components[3]);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Verify component sizes on config match what was asked for.
+ EXPECT_EQ(components[0], 8);
+ EXPECT_EQ(components[1], 8);
+ EXPECT_EQ(components[2], 8);
+ EXPECT_EQ(components[3], 8);
+}
+
+void EGLTest::addOptionalWindowMetadata(std::vector<EGLint>& attrs) {
+ FlexAttribute data;
+ if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_SMPTE2086_metadata")) {
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT);
+ data.float_value = 0.640;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT);
+ data.float_value = 0.330;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT);
+ data.float_value = 0.290;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT);
+ data.float_value = 0.600;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT);
+ data.float_value = 0.150;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT);
+ data.float_value = 0.060;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_WHITE_POINT_X_EXT);
+ data.float_value = 0.3127;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_WHITE_POINT_Y_EXT);
+ data.float_value = 0.3290;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_MAX_LUMINANCE_EXT);
+ data.float_value = 300.0;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_SMPTE2086_MIN_LUMINANCE_EXT);
+ data.float_value = 0.7;
+ attrs.push_back(data.uint_value);
+ }
+
+ if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_CTA861_3_metadata")) {
+ attrs.push_back(EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT);
+ data.float_value = 300.0;
+ attrs.push_back(data.uint_value);
+ attrs.push_back(EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT);
+ data.float_value = 75.0;
+ attrs.push_back(data.uint_value);
+ }
+}
+
+void EGLTest::checkOptionalWindowMetadata(EGLSurface eglSurface) {
+ EGLBoolean success;
+ EGLint value;
+ FlexAttribute expected;
+
+ if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_SMPTE2086_metadata")) {
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.640;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_RY_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.330;
+ ASSERT_EQ(expected.uint_value, value);
+ ASSERT_EQ(0, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_GX_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.290;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_GY_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.600;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_BX_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.150;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_DISPLAY_PRIMARY_BY_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.060;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_WHITE_POINT_X_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.3127;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_WHITE_POINT_Y_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.3290;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_MAX_LUMINANCE_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 300.0;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_SMPTE2086_MIN_LUMINANCE_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 0.7;
+ ASSERT_EQ(expected.uint_value, value);
+ }
+
+ if (hasEglExtension(mEglDisplay, "EGL_EXT_surface_CTA861_3_metadata")) {
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 300.0;
+ ASSERT_EQ(expected.uint_value, value);
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ expected.float_value = 75.0;
+ ASSERT_EQ(expected.uint_value, value);
+ }
+}
+
+TEST_F(EGLTest, EGLBT2020Linear) {
+ EGLConfig config;
+ EGLBoolean success;
+
+ if (!hasHdrDisplay) {
+ // skip this test if device does not have HDR display
+ RecordProperty("hasHdrDisplay", false);
+ return;
+ }
+
+ // Test that bt2020 linear extension exists
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_bt2020_linear"))
+ << "EGL_EXT_gl_colorspace_bt2020_linear extension not available";
+
+ ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
+
+ struct DummyConsumer : public BnConsumerListener {
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+ };
+
+ // Create a EGLSurface
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ consumer->consumerConnect(new DummyConsumer, false);
+ sp<Surface> mSTC = new Surface(producer);
+ sp<ANativeWindow> mANW = mSTC;
+
+ std::vector<EGLint> winAttrs;
+ winAttrs.push_back(EGL_GL_COLORSPACE_KHR);
+ winAttrs.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+
+ ASSERT_NO_FATAL_FAILURE(addOptionalWindowMetadata(winAttrs));
+
+ winAttrs.push_back(EGL_NONE);
+
+ EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs.data());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+ EGLint value;
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_GL_COLORSPACE_BT2020_PQ_EXT, value);
+
+ ASSERT_NO_FATAL_FAILURE(checkOptionalWindowMetadata(eglSurface));
+
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
+TEST_F(EGLTest, EGLBT2020PQ) {
+ EGLConfig config;
+ EGLBoolean success;
+
+ if (!hasHdrDisplay) {
+ // skip this test if device does not have HDR display
+ RecordProperty("hasHdrDisplay", false);
+ return;
+ }
+
+ // Test that bt2020-pq extension exists
+ ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_bt2020_pq"))
+ << "EGL_EXT_gl_colorspace_bt2020_pq extension not available";
+
+ ASSERT_NO_FATAL_FAILURE(get8BitConfig(config));
+
+ struct DummyConsumer : public BnConsumerListener {
+ void onFrameAvailable(const BufferItem& /* item */) override {}
+ void onBuffersReleased() override {}
+ void onSidebandStreamChanged() override {}
+ };
+
+ // Create a EGLSurface
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ consumer->consumerConnect(new DummyConsumer, false);
+ sp<Surface> mSTC = new Surface(producer);
+ sp<ANativeWindow> mANW = mSTC;
+ std::vector<EGLint> winAttrs;
+ winAttrs.push_back(EGL_GL_COLORSPACE_KHR);
+ winAttrs.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT);
+
+ ASSERT_NO_FATAL_FAILURE(addOptionalWindowMetadata(winAttrs));
+
+ winAttrs.push_back(EGL_NONE);
+
+ EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs.data());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+ EGLint value;
+ success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+ ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+ ASSERT_EQ(EGL_GL_COLORSPACE_BT2020_PQ_EXT, value);
+
+ ASSERT_NO_FATAL_FAILURE(checkOptionalWindowMetadata(eglSurface));
+
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
TEST_F(EGLTest, EGLConfigFP16) {
EGLint numConfigs;
EGLConfig config;
@@ -372,13 +640,13 @@
if (!hasWideColorDisplay) {
// skip this test if device does not have wide-color display
- std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl;
+ RecordProperty("hasWideColorDisplay", false);
return;
}
ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_pixel_format_float"));
- EGLint attrs[] = {
+ const EGLint attrs[] = {
// clang-format off
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
@@ -387,7 +655,7 @@
EGL_BLUE_SIZE, 16,
EGL_ALPHA_SIZE, 16,
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
- EGL_NONE, EGL_NONE
+ EGL_NONE,
// clang-format on
};
success = eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs);
@@ -437,7 +705,7 @@
TEST_F(EGLTest, EGLNoConfigContext) {
if (!hasWideColorDisplay) {
// skip this test if device does not have wide-color display
- std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl;
+ RecordProperty("hasWideColorDisplay", false);
return;
}
@@ -475,11 +743,11 @@
if (!hasWideColorDisplay) {
// skip this test if device does not have wide-color display
- std::cerr << "[ ] Device does not support wide-color, test skipped" << std::endl;
+ RecordProperty("hasWideColorDisplay", false);
return;
}
- EGLint attrs[] = {
+ const EGLint attrs[] = {
// clang-format off
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,