Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eeaa52bc authored by Courtney Goeltzenleuchter's avatar Courtney Goeltzenleuchter
Browse files

EGL: add color space validation

We want Android to support multiple color spaces as
that can be implemented entirely in the eglapi.cpp wrapper.
These additional dataspaces do not require special behavior
by the driver and may not recognize EGL_GL_COLORSPACE_DISPLAY_P3_EXT,
EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT, and related attributes.
Therefor, need to filter out those attributes before calling
the driver.
In the future there may be new extensions that do require
driver support. When that happens we'll need to update
stripColorSpaceAttribute to treat that correctly.

Test: Android CTS
      adb -d shell am start \
      -n com.drawelements.deqp/android.app.NativeActivity \
      -e cmdLine '"deqp --deqp-case=dEQP-EGL.functional.wide_color.* \
      --deqp-log-filename=/sdcard/dEQP-Log.qpa"'
Bug: 62424735
Change-Id: I0f0867feb73055100636d326213183ef1f052b7c
parent 3dc8acb2
Loading
Loading
Loading
Loading
+141 −51
Original line number Diff line number Diff line
@@ -473,6 +473,59 @@ static android_dataspace modifyBufferDataspace( android_dataspace dataSpace,
    return dataSpace;
}

// Return true if we stripped any EGL_GL_COLORSPACE_KHR attributes.
static EGLBoolean stripColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
                                           EGLint format,
                                           std::vector<EGLint>& stripped_attrib_list) {
    std::vector<EGLint> allowedColorSpaces;
    switch (format) {
        case HAL_PIXEL_FORMAT_RGBA_8888:
        case HAL_PIXEL_FORMAT_RGB_565:
            // driver okay with linear & sRGB for 8888, but can't handle
            // Display-P3 or other spaces.
            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR);
            allowedColorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR);
            break;

        case HAL_PIXEL_FORMAT_RGBA_FP16:
        case HAL_PIXEL_FORMAT_RGBA_1010102:
        default:
            // driver does not want to see colorspace attributes for 1010102 or fp16.
            // Future: if driver supports XXXX extension, we can pass down that colorspace
            break;
    }

    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;
                    }
                }
                if (!found) {
                    stripped = true;
                } else {
                    stripped_attrib_list.push_back(attr[0]);
                    stripped_attrib_list.push_back(attr[1]);
                }
            } else {
                stripped_attrib_list.push_back(attr[0]);
                stripped_attrib_list.push_back(attr[1]);
            }
        }
    }
    if (stripped) {
        stripped_attrib_list.push_back(EGL_NONE);
        stripped_attrib_list.push_back(EGL_NONE);
    }
    return stripped;
}

static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attrib_list,
                                         EGLint& colorSpace, android_dataspace& dataSpace) {
    colorSpace = EGL_GL_COLORSPACE_LINEAR_KHR;
@@ -514,35 +567,7 @@ static EGLBoolean getColorSpaceAttribute(egl_display_ptr dp, const EGLint* attri
    return true;
}

EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    clearError();

    egl_connection_t* cnx = NULL;
    egl_display_ptr dp = validate_display_connection(dpy, cnx);
    if (dp) {
        EGLDisplay iDpy = dp->disp.dpy;

        if (!window) {
            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
        }

        int value = 0;
        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
        if (!value) {
            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
        }

        int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
        if (result < 0) {
            ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
                    "failed (%#x) (already connected to another API?)",
                    window, result);
            return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
        }

void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, EGLint& format) {
    // Set the native window's buffers format to match what this config requests.
    // Whether to use sRGB gamma is not part of the EGLconfig, but is part
    // of our native format. So if sRGB gamma is requested, we have to
@@ -550,21 +575,30 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
    // format.

    EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_COLOR_COMPONENT_TYPE_EXT,
                                    &componentType);
    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType);

        EGLint format;
        EGLint colorSpace;
        android_dataspace dataSpace;
    EGLint a = 0;
    EGLint r, g, b;
    r = g = b = 0;
        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_RED_SIZE,   &r);
        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_GREEN_SIZE, &g);
        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_BLUE_SIZE,  &b);
        cnx->egl.eglGetConfigAttrib(iDpy, config, EGL_ALPHA_SIZE, &a);
    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
    cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
    EGLint colorDepth = r + g + b;

    // Today, the driver only understands sRGB and linear on 888X
    // formats. Strip other colorspaces from the attribute list and
    // only use them to set the dataspace via
    // native_window_set_buffers_dataspace
    // if pixel format is RGBX 8888
    //    TBD: Can test for future extensions that indicate that driver
    //    handles requested color space and we can let it through.
    //    allow SRGB and LINEAR. All others need to be stripped.
    // else if 565, 4444
    //    TBD: Can we assume these are supported if 8888 is?
    // else if FP16 or 1010102
    //    strip colorspace from attribs.
    // endif
    if (a == 0) {
        if (colorDepth <= 16) {
            format = HAL_PIXEL_FORMAT_RGB_565;
@@ -590,13 +624,55 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
            format = HAL_PIXEL_FORMAT_RGBA_FP16;
        }
    }
}

        // now select a corresponding sRGB format if needed
EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    clearError();

    egl_connection_t* cnx = NULL;
    egl_display_ptr dp = validate_display_connection(dpy, cnx);
    if (dp) {
        EGLDisplay iDpy = dp->disp.dpy;

        if (!window) {
            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
        }

        int value = 0;
        window->query(window, NATIVE_WINDOW_IS_VALID, &value);
        if (!value) {
            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
        }

        int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
        if (result < 0) {
            ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
                    "failed (%#x) (already connected to another API?)",
                    window, result);
            return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
        }

        EGLint format;
        getNativePixelFormat(iDpy, cnx, config, format);

        // now select correct colorspace and dataspace based on user's attribute list
        EGLint colorSpace;
        android_dataspace dataSpace;
        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
            ALOGE("error invalid colorspace: %d", colorSpace);
            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
        }

        std::vector<EGLint> strippedAttribList;
        if (stripColorSpaceAttribute(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();
        }

        if (format != 0) {
            int err = native_window_set_buffers_format(window, format);
            if (err != 0) {
@@ -671,15 +747,29 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,

    egl_connection_t* cnx = NULL;
    egl_display_ptr dp = validate_display_connection(dpy, cnx);
    if (dp) {
        EGLDisplay iDpy = dp->disp.dpy;
        EGLint format;
        getNativePixelFormat(iDpy, cnx, config, format);

        // now select correct colorspace and dataspace based on user's attribute list
        EGLint colorSpace;
        android_dataspace dataSpace;
    if (dp) {
        // now select a corresponding sRGB format if needed
        if (!getColorSpaceAttribute(dp, attrib_list, colorSpace, dataSpace)) {
            ALOGE("error invalid colorspace: %d", colorSpace);
            return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
        }

        // Pbuffers are not displayed so we don't need to store the
        // 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)) {
            // Had to modify the attribute list due to use of color space.
            // Use modified list from here on.
            attrib_list = strippedAttribList.data();
        }

        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
                dp->disp.dpy, config, attrib_list);
        if (surface != EGL_NO_SURFACE) {