diff options
author | 2017-11-16 14:27:48 -0700 | |
---|---|---|
committer | 2018-01-24 15:09:35 -0500 | |
commit | 12ffe0966f5969a16a90e7ead838b21b96283c16 (patch) | |
tree | 0084ae2464ec70ee1b0144f36c7b16980ed998e0 | |
parent | 47f36ab4bd04e164799e040b52f368e5cd256cb5 (diff) |
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
-rw-r--r-- | opengl/libs/EGL/eglApi.cpp | 123 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_display.cpp | 17 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_object.cpp | 121 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_object.h | 9 | ||||
-rw-r--r-- | opengl/tests/EGLTest/EGL_test.cpp | 280 |
5 files changed, 519 insertions, 31 deletions
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 94dfe6a9de..484e0baffc 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -468,12 +468,17 @@ static android_dataspace modifyBufferDataspace(android_dataspace dataSpace, 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 @@ static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* att 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 @@ static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, NativeWindowType wi // 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 @@ void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig confi } } +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 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, } 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 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, 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 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, // 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 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, 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 3c1edd148a..0f36614ac1 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -214,6 +214,23 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { "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 72b4823ea0..13b94b6361 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -63,7 +63,9 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWind win(win), cnx(cnx), connected(true), - colorSpace(colorSpace) { + colorSpace(colorSpace), + smpte2086_metadata({}), + cta861_3_metadata({}) { if (win) { win->incStrong(this); } @@ -86,6 +88,123 @@ void egl_surface_t::disconnect() { } } +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 7c3075c9fc..a9020a17ee 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -137,6 +137,13 @@ public: 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 @@ private: 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 b67a053021..8bb74a2b29 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -53,7 +53,20 @@ using namespace android::hardware::configstore::V1_0; 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 @@ TEST_F(EGLTest, EGLDisplayP31010102) { 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 @@ TEST_F(EGLTest, EGLConfigFP16) { 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 @@ TEST_F(EGLTest, EGLConfigFP16) { 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, EGLConfigFP16) { 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 @@ TEST_F(EGLTest, EGLConfig1010102) { 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, |