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

Commit 86edf4f6 authored by Jamie Gennis's avatar Jamie Gennis
Browse files

SurfaceTexture: add EGL_KHR_fence_sync option

This change adds a compile-time option for SurfaceTexture to use the
EGL_KHR_fence_sync extension to synchronize access to Gralloc buffers.

Bug: 5122031
Change-Id: I7e973a358631fff5308acf377581b811911fe790
parent fe27e2f4
Loading
Loading
Loading
Loading
+22 −4
Original line number Diff line number Diff line
@@ -60,10 +60,16 @@ public:
        virtual void onFrameAvailable() = 0;
    };

    // tex indicates the name OpenGL texture to which images are to be streamed.
    // This texture name cannot be changed once the SurfaceTexture is created.
    // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
    // name of the OpenGL ES texture to which images are to be streamed. This
    // texture name cannot be changed once the SurfaceTexture is created.
    // allowSynchronousMode specifies whether or not synchronous mode can be
    // enabled. texTarget specifies the OpenGL ES texture target to which the
    // texture will be bound in updateTexImage. useFenceSync specifies whether
    // fences should be used to synchronize access to buffers if that behavior
    // is enabled at compile-time.
    SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
            GLenum texTarget = GL_TEXTURE_EXTERNAL_OES);
            GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true);

    virtual ~SurfaceTexture();

@@ -276,7 +282,8 @@ private:
              mTransform(0),
              mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
              mTimestamp(0),
              mFrameNumber(0) {
              mFrameNumber(0),
              mFence(EGL_NO_SYNC_KHR) {
            mCrop.makeInvalid();
        }

@@ -349,6 +356,11 @@ private:
        // mFrameNumber is the number of the queued frame for this slot.
        uint64_t mFrameNumber;

        // mFence is the EGL sync object that must signal before the buffer
        // associated with this buffer slot may be dequeued. It is initialized
        // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
        // on a compile-time option) set to a new sync object in updateTexImage.
        EGLSyncKHR mFence;
    };

    // mSlots is the array of buffer slots that must be mirrored on the client
@@ -472,6 +484,12 @@ private:
    // It is set by the setName method.
    String8 mName;

    // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
    // extension should be used to prevent buffers from being dequeued before
    // it's safe for them to be written. It gets set at construction time and
    // never changes.
    const bool mUseFenceSync;

    // mMutex is the mutex used to prevent concurrent access to the member
    // variables of SurfaceTexture objects. It must be locked whenever the
    // member variables are accessed.
+233 −166
Original line number Diff line number Diff line
@@ -36,6 +36,12 @@
#include <utils/Log.h>
#include <utils/String8.h>

// This compile option causes SurfaceTexture to return the buffer that is currently
// attached to the GL texture from dequeueBuffer when no other buffers are
// available.  It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do
// implicit cross-process synchronization to prevent the buffer from being
// written to before the buffer has (a) been detached from the GL texture and
// (b) all GL reads from the buffer have completed.
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
@@ -43,6 +49,16 @@
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
#endif

// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension
// to synchronize access to the buffers.  It will cause dequeueBuffer to stall,
// waiting for the GL reads for the buffer being dequeued to complete before
// allowing the buffer to be dequeued.
#ifdef USE_FENCE_SYNC
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible"
#endif
#endif

// Macros for including the SurfaceTexture name in log messages
#define ST_LOGV(x, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
@@ -99,7 +115,7 @@ static int32_t createProcessUniqueId() {
}

SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
        GLenum texTarget) :
        GLenum texTarget, bool useFenceSync) :
    mDefaultWidth(1),
    mDefaultHeight(1),
    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
@@ -116,6 +132,11 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
    mAllowSynchronousMode(allowSynchronousMode),
    mConnectedApi(NO_CONNECTED_API),
    mAbandoned(false),
#ifdef USE_FENCE_SYNC
    mUseFenceSync(useFenceSync),
#else
    mUseFenceSync(false),
#endif
    mTexTarget(texTarget),
    mFrameCounter(0) {
    // Choose a name using the PID and a process-unique ID.
@@ -261,9 +282,12 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
        return BAD_VALUE;
    }

    Mutex::Autolock lock(mMutex);

    status_t returnFlags(OK);
    EGLDisplay dpy = EGL_NO_DISPLAY;
    EGLSyncKHR fence = EGL_NO_SYNC_KHR;

    { // Scope for the lock
        Mutex::Autolock lock(mMutex);

        int found = -1;
        int foundSync = -1;
@@ -327,7 +351,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,

                // if buffer is FREE it CANNOT be current
                LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
                    "dequeueBuffer: buffer %d is both FREE and current!", i);
                        "dequeueBuffer: buffer %d is both FREE and current!",
                        i);

                if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
                    if (state == BufferSlot::FREE || i == mCurrentTexture) {
@@ -339,11 +364,14 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
                    }
                } else {
                    if (state == BufferSlot::FREE) {
                    /** For Asynchronous mode, we need to return the oldest of free buffers
                    * There is only one instance when the Framecounter overflows, this logic
                    * might return the earlier buffer to client. Which is a negligible impact
                    **/
                    if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
                        /* We return the oldest of the free buffers to avoid
                         * stalling the producer if possible.  This is because
                         * the consumer may still have pending reads of the
                         * buffers in flight.
                         */
                        bool isOlder = mSlots[i].mFrameNumber <
                                mSlots[found].mFrameNumber;
                        if (found < 0 || isOlder) {
                            foundSync = i;
                            found = i;
                        }
@@ -359,8 +387,9 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
                return -EINVAL;
            }

        // See whether a buffer has been queued since the last setBufferCount so
        // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
            // See whether a buffer has been queued since the last
            // setBufferCount so we know whether to perform the
            // MIN_UNDEQUEUED_BUFFERS check below.
            bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
            if (bufferHasBeenQueued) {
                // make sure the client is not trying to dequeue more buffers
@@ -375,8 +404,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
                }
            }

        // we're in synchronous mode and didn't find a buffer, we need to wait
        // for some buffers to be consumed
            // we're in synchronous mode and didn't find a buffer, we need to
            // wait for some buffers to be consumed
            tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
            if (tryAgain) {
                mDequeueCondition.wait(mMutex);
@@ -436,8 +465,10 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
            }
            mSlots[buf].mGraphicBuffer = graphicBuffer;
            mSlots[buf].mRequestBufferCalled = false;
            mSlots[buf].mFence = EGL_NO_SYNC_KHR;
            if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
            eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
                eglDestroyImageKHR(mSlots[buf].mEglDisplay,
                        mSlots[buf].mEglImage);
                mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
                mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
            }
@@ -448,8 +479,28 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
            }
            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
        }

        dpy = mSlots[buf].mEglDisplay;
        fence = mSlots[buf].mFence;
        mSlots[buf].mFence = EGL_NO_SYNC_KHR;
    }

    if (fence != EGL_NO_SYNC_KHR) {
        EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
        // If something goes wrong, log the error, but return the buffer without
        // synchronizing access to it.  It's too late at this point to abort the
        // dequeue operation.
        if (result == EGL_FALSE) {
            LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
            LOGE("dequeueBuffer: timeout waiting for fence");
        }
        eglDestroySyncKHR(dpy, fence);
    }

    ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
            mSlots[buf].mGraphicBuffer->handle, returnFlags);

    return returnFlags;
}

@@ -712,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() {

        // Update the GL texture object.
        EGLImageKHR image = mSlots[buf].mEglImage;
        if (image == EGL_NO_IMAGE_KHR) {
        EGLDisplay dpy = eglGetCurrentDisplay();
        if (image == EGL_NO_IMAGE_KHR) {
            if (mSlots[buf].mGraphicBuffer == 0) {
                ST_LOGE("buffer at slot %d is null", buf);
                return BAD_VALUE;
@@ -746,17 +797,33 @@ status_t SurfaceTexture::updateTexImage() {
            return -EINVAL;
        }

        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
                mSlots[buf].mGraphicBuffer->handle);
        if (mCurrentTexture != INVALID_BUFFER_SLOT) {
            if (mUseFenceSync) {
                EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR,
                        NULL);
                if (fence == EGL_NO_SYNC_KHR) {
                    LOGE("updateTexImage: error creating fence: %#x",
                            eglGetError());
                    return -EINVAL;
                }
                glFlush();
                mSlots[mCurrentTexture].mFence = fence;
            }
        }

        ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
                mCurrentTexture,
                mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
                buf, mSlots[buf].mGraphicBuffer->handle);

        if (mCurrentTexture != INVALID_BUFFER_SLOT) {
            // The current buffer becomes FREE if it was still in the queued
            // state. If it has already been given to the client
            // (synchronous mode), then it stays in DEQUEUED state.
            if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
            if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) {
                mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
            }
        }

        // Update the SurfaceTexture state.
        mCurrentTexture = buf;
+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ namespace android {


SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer)
    : SurfaceTexture(tex), mLayer(layer) {
    : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) {
}

SurfaceTextureLayer::~SurfaceTextureLayer() {