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

Commit 767fcf7e authored by Chia-I Wu's avatar Chia-I Wu
Browse files

surfaceflinger: add more sync operations to RenderEngine

Add RenderEngine::finish and RenderEngine::waitFence.  Rework flush
not to fall back to finish.

Test: SurfaceFlinger_test
Change-Id: I2e5738f72b4aa1186d45d23cab9055f96d90ff23
parent 767d7c9f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -89,6 +89,16 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt
        hasEGLExtension("EGL_KHR_no_config_context")) {
        mHasNoConfigContext = true;
    }

    if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
        mHasNativeFenceSync = true;
    }
    if (hasEGLExtension("EGL_KHR_fence_sync")) {
        mHasFenceSync = true;
    }
    if (hasEGLExtension("EGL_KHR_wait_sync")) {
        mHasWaitSync = true;
    }
}

char const* GLExtensions::getEGLVersion() const {
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,9 @@ class GLExtensions : public Singleton<GLExtensions> {
    friend class Singleton<GLExtensions>;

    bool mHasNoConfigContext = false;
    bool mHasNativeFenceSync = false;
    bool mHasFenceSync = false;
    bool mHasWaitSync = false;

    String8 mVendor;
    String8 mRenderer;
@@ -57,6 +60,9 @@ protected:

public:
    bool hasNoConfigContext() const { return mHasNoConfigContext; }
    bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
    bool hasFenceSync() const { return mHasFenceSync; }
    bool hasWaitSync() const { return mHasWaitSync; }

    void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
                           GLubyte const* extensions);
+82 −46
Original line number Diff line number Diff line
@@ -169,6 +169,88 @@ void RenderEngine::resetCurrentSurface() {
    eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}

base::unique_fd RenderEngine::flush() {
    if (!GLExtensions::getInstance().hasNativeFenceSync()) {
        return base::unique_fd();
    }

    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
    if (sync == EGL_NO_SYNC_KHR) {
        ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
        return base::unique_fd();
    }

    // native fence fd will not be populated until flush() is done.
    glFlush();

    // get the fence fd
    base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
    eglDestroySyncKHR(mEGLDisplay, sync);
    if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
        ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
    }

    return fenceFd;
}

bool RenderEngine::finish() {
    if (!GLExtensions::getInstance().hasFenceSync()) {
        ALOGW("no synchronization support");
        return false;
    }

    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
    if (sync == EGL_NO_SYNC_KHR) {
        ALOGW("failed to create EGL fence sync: %#x", eglGetError());
        return false;
    }

    EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                         2000000000 /*2 sec*/);
    EGLint error = eglGetError();
    eglDestroySyncKHR(mEGLDisplay, sync);
    if (result != EGL_CONDITION_SATISFIED_KHR) {
        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
            ALOGW("fence wait timed out");
        } else {
            ALOGW("error waiting on EGL fence: %#x", error);
        }
        return false;
    }

    return true;
}

bool RenderEngine::waitFence(base::unique_fd fenceFd) {
    if (!GLExtensions::getInstance().hasNativeFenceSync() ||
        !GLExtensions::getInstance().hasWaitSync()) {
        return false;
    }

    EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
    EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
    if (sync == EGL_NO_SYNC_KHR) {
        ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
        return false;
    }

    // fenceFd is now owned by EGLSync
    (void)fenceFd.release();

    // XXX: The spec draft is inconsistent as to whether this should return an
    // EGLint or void.  Ignore the return value for now, as it's not strictly
    // needed.
    eglWaitSyncKHR(mEGLDisplay, sync, 0);
    EGLint error = eglGetError();
    eglDestroySyncKHR(mEGLDisplay, sync);
    if (error != EGL_SUCCESS) {
        ALOGE("failed to wait for EGL native fence sync: %#x", error);
        return false;
    }

    return true;
}

void RenderEngine::checkErrors() const {
    do {
        // there could be more than one error flag
@@ -220,52 +302,6 @@ void RenderEngine::fillRegionWithColor(const Region& region, uint32_t height, fl
    drawMesh(mesh);
}

int RenderEngine::flush(bool wait) {
    // Attempt to create a sync khr object that can produce a sync point. If that
    // isn't available, create a non-dupable sync object in the fallback path and
    // wait on it directly.
    EGLSyncKHR sync;
    if (!wait) {
        EGLint syncFd = EGL_NO_NATIVE_FENCE_FD_ANDROID;

        sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
        if (sync != EGL_NO_SYNC_KHR) {
            // native fence fd will not be populated until flush() is done.
            glFlush();

            // get the sync fd
            syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
            if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
                ALOGW("failed to dup sync khr object");
            }

            eglDestroySyncKHR(mEGLDisplay, sync);
        }

        if (syncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
            return syncFd;
        }
    }

    // fallback or explicit wait
    sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
    if (sync != EGL_NO_SYNC_KHR) {
        EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                             2000000000 /*2 sec*/);
        EGLint eglErr = eglGetError();
        if (result == EGL_TIMEOUT_EXPIRED_KHR) {
            ALOGW("fence wait timed out");
        } else {
            ALOGW_IF(eglErr != EGL_SUCCESS, "error waiting on EGL fence: %#x", eglErr);
        }
        eglDestroySyncKHR(mEGLDisplay, sync);
    } else {
        ALOGW("error creating EGL fence: %#x", eglGetError());
    }

    return -1;
}

void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
    glClearColor(red, green, blue, alpha);
    glClear(GL_COLOR_BUFFER_BIT);
+14 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <Transform.h>
#include <android-base/unique_fd.h>
#include <gui/SurfaceControl.h>
#include <math/mat4.h>

@@ -82,9 +83,20 @@ public:
    // dump the extension strings. always call the base class.
    virtual void dump(String8& result);

    // synchronization

    // flush submits RenderEngine command stream for execution and returns a
    // native fence fd that is signaled when the execution has completed.  It
    // returns -1 on errors.
    base::unique_fd flush();
    // finish waits until RenderEngine command stream has been executed.  It
    // returns false on errors.
    bool finish();
    // waitFence inserts a wait on an external fence fd to RenderEngine
    // command stream.  It returns false on errors.
    bool waitFence(base::unique_fd fenceFd);

    // helpers
    // flush returns -1 or a valid native fence fd owned by the caller
    int flush(bool wait);
    void clearWithColor(float red, float green, float blue, float alpha);
    void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
                             float blue, float alpha);
+10 −3
Original line number Diff line number Diff line
@@ -4538,9 +4538,10 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
    // dependent on the context's EGLConfig.
    renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);

    *outSyncFd = getRenderEngine().flush(DEBUG_SCREENSHOTS);

    if (DEBUG_SCREENSHOTS) {
        getRenderEngine().finish();
        *outSyncFd = -1;

        const auto reqWidth = renderArea.getReqWidth();
        const auto reqHeight = renderArea.getReqHeight();

@@ -4548,6 +4549,12 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
        getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
        checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
        delete [] pixels;
    } else {
        base::unique_fd syncFd = getRenderEngine().flush();
        if (syncFd < 0) {
            getRenderEngine().finish();
        }
        *outSyncFd = syncFd.release();
    }

    return NO_ERROR;