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

Commit ce561372 authored by Jamie Gennis's avatar Jamie Gennis
Browse files

libgui: have ST::updateTexImage check the GL ctx

This change adds a check to SurfaceTexture::updateTexImage to verify
that the current GL context is the same as the one that was used for
previous updateTexImage calls.

Change-Id: If02d2f787bcfdb528046dc9ddf6665f8a90e1bf4
parent fa5b40eb
Loading
Loading
Loading
Loading
+11 −4
Original line number Diff line number Diff line
@@ -260,7 +260,6 @@ private:
    struct EGLSlot {
        EGLSlot()
        : mEglImage(EGL_NO_IMAGE_KHR),
          mEglDisplay(EGL_NO_DISPLAY),
          mFence(EGL_NO_SYNC_KHR) {
        }

@@ -269,9 +268,6 @@ private:
        // mEglImage is the EGLImage created from mGraphicBuffer.
        EGLImageKHR mEglImage;

        // mEglDisplay is the EGLDisplay used to create mEglImage.
        EGLDisplay mEglDisplay;

        // 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
@@ -279,6 +275,17 @@ private:
        EGLSyncKHR mFence;
    };

    // mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
    // associated.  It is intialized to EGL_NO_DISPLAY and gets set to the
    // current display when updateTexImage is called for the first time.
    EGLDisplay mEglDisplay;

    // mEglContext is the OpenGL ES context with which this SurfaceTexture is
    // currently associated.  It is initialized to EGL_NO_CONTEXT and gets set
    // to the current GL context when updateTexImage is called for the first
    // time.
    EGLContext mEglContext;

    // mEGLSlots stores the buffers that have been allocated by the BufferQueue
    // for each buffer slot.  It is initialized to null pointers, and gets
    // filled in with the result of BufferQueue::acquire when the
+23 −12
Original line number Diff line number Diff line
@@ -115,6 +115,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
    mUseFenceSync(false),
#endif
    mTexTarget(texTarget),
    mEglDisplay(EGL_NO_DISPLAY),
    mEglContext(EGL_NO_CONTEXT),
    mAbandoned(false),
    mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
{
@@ -178,6 +180,22 @@ status_t SurfaceTexture::updateTexImage() {
        return NO_INIT;
    }

    EGLDisplay dpy = eglGetCurrentDisplay();
    EGLContext ctx = eglGetCurrentContext();

    if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
        ST_LOGE("updateTexImage: invalid current EGLDisplay");
        return -EINVAL;
    }

    if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
        ST_LOGE("updateTexImage: invalid current EGLContext");
        return -EINVAL;
    }

    mEglDisplay = dpy;
    mEglContext = ctx;

    BufferQueue::BufferItem item;

    // In asynchronous mode the list is guaranteed to be one buffer
@@ -188,17 +206,14 @@ status_t SurfaceTexture::updateTexImage() {
        if (item.mGraphicBuffer != NULL) {
            mEGLSlots[buf].mGraphicBuffer = 0;
            if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
                eglDestroyImageKHR(mEGLSlots[buf].mEglDisplay,
                        mEGLSlots[buf].mEglImage);
                eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
                mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
                mEGLSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
            }
            mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
        }

        // Update the GL texture object.
        EGLImageKHR image = mEGLSlots[buf].mEglImage;
        EGLDisplay dpy = eglGetCurrentDisplay();
        if (image == EGL_NO_IMAGE_KHR) {
            if (item.mGraphicBuffer == 0) {
                ST_LOGE("buffer at slot %d is null", buf);
@@ -206,7 +221,6 @@ status_t SurfaceTexture::updateTexImage() {
            }
            image = createImage(dpy, item.mGraphicBuffer);
            mEGLSlots[buf].mEglImage = image;
            mEGLSlots[buf].mEglDisplay = dpy;
            if (image == EGL_NO_IMAGE_KHR) {
                // NOTE: if dpy was invalid, createImage() is guaranteed to
                // fail. so we'd end up here.
@@ -229,8 +243,7 @@ status_t SurfaceTexture::updateTexImage() {
            failed = true;
        }
        if (failed) {
            mBufferQueue->releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
                    mEGLSlots[buf].mFence);
            mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence);
            return -EINVAL;
        }

@@ -241,7 +254,7 @@ status_t SurfaceTexture::updateTexImage() {
                if (fence == EGL_NO_SYNC_KHR) {
                    ALOGE("updateTexImage: error creating fence: %#x",
                            eglGetError());
                    mBufferQueue->releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
                    mBufferQueue->releaseBuffer(buf, dpy,
                            mEGLSlots[buf].mFence);
                    return -EINVAL;
                }
@@ -256,8 +269,7 @@ status_t SurfaceTexture::updateTexImage() {
                buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);

        // release old buffer
        mBufferQueue->releaseBuffer(mCurrentTexture,
                mEGLSlots[mCurrentTexture].mEglDisplay,
        mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
                mEGLSlots[mCurrentTexture].mFence);

        // Update the SurfaceTexture state.
@@ -459,10 +471,9 @@ void SurfaceTexture::freeBufferLocked(int slotIndex) {
    if (mEGLSlots[slotIndex].mEglImage != EGL_NO_IMAGE_KHR) {
        EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
        if (img != EGL_NO_IMAGE_KHR) {
            eglDestroyImageKHR(mEGLSlots[slotIndex].mEglDisplay, img);
            eglDestroyImageKHR(mEglDisplay, img);
        }
        mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
        mEGLSlots[slotIndex].mEglDisplay = EGL_NO_DISPLAY;
    }
}

+66 −34
Original line number Diff line number Diff line
@@ -560,6 +560,27 @@ void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r,
    }
}

// Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern
// using the CPU.  This assumes that the ANativeWindow is already configured to
// allow this to be done (e.g. the format is set to RGBA8).
//
// Calls to this function should be wrapped in an ASSERT_NO_FATAL_FAILURE().
void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) {
    android_native_buffer_t* anb;
    ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &anb));
    ASSERT_TRUE(anb != NULL);

    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
    ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf->getNativeBuffer()));

    uint8_t* img = NULL;
    ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
            (void**)(&img)));
    fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
    ASSERT_EQ(NO_ERROR, buf->unlock());
    ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer()));
}

TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
    const int texWidth = 64;
    const int texHeight = 66;
@@ -873,19 +894,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferNpot) {
    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));

    android_native_buffer_t* anb;
    ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    ASSERT_TRUE(anb != NULL);

    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
    ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));

    // Fill the buffer with the a checkerboard pattern
    uint8_t* img = NULL;
    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
    fillRGBA8Buffer(img, texWidth, texHeight, buf->getStride());
    buf->unlock();
    ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));

    mST->updateTexImage();

@@ -927,19 +936,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) {
    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));

    android_native_buffer_t* anb;
    ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
    ASSERT_TRUE(anb != NULL);

    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
    ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));

    // Fill the buffer with the a checkerboard pattern
    uint8_t* img = NULL;
    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
    fillRGBA8Buffer(img, texWidth, texHeight, buf->getStride());
    buf->unlock();
    ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));

    mST->updateTexImage();

@@ -1095,18 +1092,12 @@ protected:
    virtual void SetUp() {
        SurfaceTextureGLTest::SetUp();

        EGLConfig myConfig = {0};
        EGLint numConfigs = 0;
        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
                1, &numConfigs));
        ASSERT_EQ(EGL_SUCCESS, eglGetError());

        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                mANW.get(), NULL);
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);

        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
        mProducerEglContext = eglCreateContext(mEglDisplay, mGlConfig,
                EGL_NO_CONTEXT, getContextAttribs());
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
@@ -1742,4 +1733,45 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
    EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255));
}

class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest {
protected:
    SurfaceTextureMultiContextGLTest():
            mSecondEglContext(EGL_NO_CONTEXT) {
    }

    virtual void SetUp() {
        SurfaceTextureGLTest::SetUp();

        mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig,
                EGL_NO_CONTEXT, getContextAttribs());
        ASSERT_EQ(EGL_SUCCESS, eglGetError());
        ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext);
    }

    virtual void TearDown() {
        if (mSecondEglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(mEglDisplay, mSecondEglContext);
        }
        SurfaceTextureGLTest::TearDown();
    }

    EGLContext mSecondEglContext;
};

TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) {
    sp<FrameWaiter> fw(new FrameWaiter);
    mST->setFrameAvailableListener(fw);

    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));

    // Latch the texture contents on the primary context.
    mST->updateTexImage();

    // Attempt to latch the texture on the secondary context.
    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
            mSecondEglContext));
    ASSERT_EQ(EGL_SUCCESS, eglGetError());
    ASSERT_EQ(-EINVAL, mST->updateTexImage());
}

} // namespace android