Loading include/gui/SurfaceTexture.h +64 −6 Original line number Diff line number Diff line Loading @@ -55,8 +55,7 @@ public: }; // 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. // name of the OpenGL ES texture to which images are to be streamed. // 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 Loading @@ -64,6 +63,21 @@ public: // is enabled at compile-time. A custom bufferQueue can be specified // if behavior for queue/dequeue/connect etc needs to be customized. // Otherwise a default BufferQueue will be created and used. // // For legacy reasons, the SurfaceTexture is created in a state where it is // considered attached to an OpenGL ES context for the purposes of the // attachToContext and detachFromContext methods. However, despite being // considered "attached" to a context, the specific OpenGL ES context // doesn't get latched until the first call to updateTexImage. After that // point, all calls to updateTexImage must be made with the same OpenGL ES // context current. // // A SurfaceTexture may be detached from one OpenGL ES context and then // attached to a different context using the detachFromContext and // attachToContext methods, respectively. The intention of these methods is // purely to allow a SurfaceTexture to be transferred from one consumer // context to another. If such a transfer is not needed there is no // requirement that either of these methods be called. SurfaceTexture(GLuint tex, bool allowSynchronousMode = true, GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, const sp<BufferQueue> &bufferQueue = 0); Loading Loading @@ -175,8 +189,37 @@ public: virtual status_t connect(int api, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); // getBufferQueue returns the BufferQueue object to which this // SurfaceTexture is connected. sp<BufferQueue> getBufferQueue() const; // detachFromContext detaches the SurfaceTexture from the calling thread's // current OpenGL ES context. This context must be the same as the context // that was current for previous calls to updateTexImage. // // Detaching a SurfaceTexture from an OpenGL ES context will result in the // deletion of the OpenGL ES texture object into which the images were being // streamed. After a SurfaceTexture has been detached from the OpenGL ES // context calls to updateTexImage will fail returning INVALID_OPERATION // until the SurfaceTexture is attached to a new OpenGL ES context using the // attachToContext method. status_t detachFromContext(); // attachToContext attaches a SurfaceTexture that is currently in the // 'detached' state to the current OpenGL ES context. A SurfaceTexture is // in the 'detached' state iff detachFromContext has successfully been // called and no calls to attachToContext have succeeded since the last // detachFromContext call. Calls to attachToContext made on a // SurfaceTexture that is not in the 'detached' state will result in an // INVALID_OPERATION error. // // The tex argument specifies the OpenGL ES texture object name in the // new context into which the image contents will be streamed. A successful // call to attachToContext will result in this texture object being bound to // the texture target and populated with the image contents that were // current at the time of the last call to detachFromContext. status_t attachToContext(GLuint tex); // dump our state in a String virtual void dump(String8& result) const; virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; Loading Loading @@ -209,6 +252,12 @@ private: // to compute this matrix and stores it in mCurrentTransformMatrix. void computeCurrentTransformMatrix(); // syncForReleaseLocked performs the synchronization needed to release the // current slot from an OpenGL ES context. If needed it will set the // current slot's fence to guard against a producer accessing the buffer // before the outstanding accesses have completed. status_t syncForReleaseLocked(EGLDisplay dpy); // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. Loading Loading @@ -237,8 +286,8 @@ private: // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; // and can be changed with a call to attachToContext. GLuint mTexName; // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before Loading Loading @@ -277,13 +326,14 @@ private: // 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. // current display when updateTexImage is called for the first time and when // attachToContext is called. 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. // time and when attachToContext is called. EGLContext mEglContext; // mEGLSlots stores the buffers that have been allocated by the BufferQueue Loading Loading @@ -323,6 +373,14 @@ private: // if none is supplied sp<BufferQueue> mBufferQueue; // mAttached indicates whether the SurfaceTexture is currently attached to // an OpenGL ES context. For legacy reasons, this is initialized to true, // indicating that the SurfaceTexture is considered to be attached to // whatever context is current at the time of the first updateTexImage call. // It is set to false by detachFromContext, and then set to true again by // attachToContext. bool mAttached; // 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. Loading libs/gui/SurfaceTexture.cpp +198 −36 Original line number Diff line number Diff line Loading @@ -118,7 +118,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), mAbandoned(false), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); Loading Loading @@ -176,21 +177,29 @@ status_t SurfaceTexture::updateTexImage() { Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture"); ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return -EINVAL; return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return -EINVAL; return INVALID_OPERATION; } mEglDisplay = dpy; Loading @@ -216,7 +225,7 @@ status_t SurfaceTexture::updateTexImage() { EGLImageKHR image = mEGLSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { if (item.mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); ST_LOGE("updateTexImage: buffer at slot %d is null", buf); return BAD_VALUE; } image = createImage(dpy, item.mGraphicBuffer); Loading @@ -224,7 +233,7 @@ status_t SurfaceTexture::updateTexImage() { if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. return -EINVAL; return UNKNOWN_ERROR; } } Loading @@ -236,31 +245,23 @@ status_t SurfaceTexture::updateTexImage() { glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); bool failed = false; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("error binding external texture image %p (slot %d): %#04x", image, buf, error); failed = true; } if (failed) { mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); return -EINVAL; ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; } if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (mUseFenceSync) { EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ALOGE("updateTexImage: error creating fence: %#x", eglGetError()); mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); return -EINVAL; } glFlush(); mEGLSlots[mCurrentTexture].mFence = fence; if (err == OK) { err = syncForReleaseLocked(dpy); } if (err != OK) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", Loading @@ -268,9 +269,12 @@ status_t SurfaceTexture::updateTexImage() { mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); // release old buffer // Release the old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { mBufferQueue->releaseBuffer(mCurrentTexture, dpy, mEGLSlots[mCurrentTexture].mFence); mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR; } // Update the SurfaceTexture state. mCurrentTexture = buf; Loading @@ -280,10 +284,6 @@ status_t SurfaceTexture::updateTexImage() { mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; computeCurrentTransformMatrix(); // Now that we've passed the point at which failures can happen, // it's safe to remove the buffer from the front of the queue. } else { // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); Loading @@ -292,6 +292,168 @@ status_t SurfaceTexture::updateTexImage() { return OK; } status_t SurfaceTexture::detachFromContext() { ATRACE_CALL(); ST_LOGV("detachFromContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("detachFromContext: abandoned SurfaceTexture"); return NO_INIT; } if (!mAttached) { ST_LOGE("detachFromContext: SurfaceTexture is not attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { ST_LOGE("detachFromContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { ST_LOGE("detachFromContext: invalid current EGLContext"); return INVALID_OPERATION; } if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { status_t err = syncForReleaseLocked(dpy); if (err != OK) { return err; } glDeleteTextures(1, &mTexName); } mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; mAttached = false; return OK; } status_t SurfaceTexture::attachToContext(GLuint tex) { ATRACE_CALL(); ST_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("attachToContext: abandoned SurfaceTexture"); return NO_INIT; } if (mAttached) { ST_LOGE("attachToContext: SurfaceTexture is already attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (dpy == EGL_NO_DISPLAY) { ST_LOGE("attachToContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (ctx == EGL_NO_CONTEXT) { ST_LOGE("attachToContext: invalid current EGLContext"); return INVALID_OPERATION; } // We need to bind the texture regardless of whether there's a current // buffer. glBindTexture(mTexTarget, tex); if (mCurrentTextureBuf != NULL) { // If the current buffer is no longer associated with a slot, then it // doesn't have an EGLImage. In that case we create one now, but we also // destroy it once we've used it to attach the buffer to the OpenGL ES // texture. bool imageNeedsDestroy = false; EGLImageKHR image = EGL_NO_IMAGE_KHR; if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { image = mEGLSlots[mCurrentTexture].mEglImage; imageNeedsDestroy = false; } else { image = createImage(dpy, mCurrentTextureBuf); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } imageNeedsDestroy = true; } // Attach the current buffer to the GL texture. glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); GLint error; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("attachToContext: error binding external texture image %p " "(slot %d): %#04x", image, mCurrentTexture, error); err = UNKNOWN_ERROR; } if (imageNeedsDestroy) { eglDestroyImageKHR(dpy, image); } if (err != OK) { return err; } } mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; return OK; } status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to wait // on that before replacing it with another fence to ensure that all // outstanding buffer accesses have completed before the producer // accesses it. EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); if (result == EGL_FALSE) { ST_LOGE("syncForReleaseLocked: error waiting for previous " "fence: %#x", eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("syncForReleaseLocked: timeout waiting for previous " "fence"); return TIMED_OUT; } eglDestroySyncKHR(dpy, fence); } // Create a fence for the outstanding accesses in the current OpenGL ES // context. fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ST_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEGLSlots[mCurrentTexture].mFence = fence; } return OK; } bool SurfaceTexture::isExternalFormat(uint32_t format) { switch (format) { Loading Loading
include/gui/SurfaceTexture.h +64 −6 Original line number Diff line number Diff line Loading @@ -55,8 +55,7 @@ public: }; // 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. // name of the OpenGL ES texture to which images are to be streamed. // 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 Loading @@ -64,6 +63,21 @@ public: // is enabled at compile-time. A custom bufferQueue can be specified // if behavior for queue/dequeue/connect etc needs to be customized. // Otherwise a default BufferQueue will be created and used. // // For legacy reasons, the SurfaceTexture is created in a state where it is // considered attached to an OpenGL ES context for the purposes of the // attachToContext and detachFromContext methods. However, despite being // considered "attached" to a context, the specific OpenGL ES context // doesn't get latched until the first call to updateTexImage. After that // point, all calls to updateTexImage must be made with the same OpenGL ES // context current. // // A SurfaceTexture may be detached from one OpenGL ES context and then // attached to a different context using the detachFromContext and // attachToContext methods, respectively. The intention of these methods is // purely to allow a SurfaceTexture to be transferred from one consumer // context to another. If such a transfer is not needed there is no // requirement that either of these methods be called. SurfaceTexture(GLuint tex, bool allowSynchronousMode = true, GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, const sp<BufferQueue> &bufferQueue = 0); Loading Loading @@ -175,8 +189,37 @@ public: virtual status_t connect(int api, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); // getBufferQueue returns the BufferQueue object to which this // SurfaceTexture is connected. sp<BufferQueue> getBufferQueue() const; // detachFromContext detaches the SurfaceTexture from the calling thread's // current OpenGL ES context. This context must be the same as the context // that was current for previous calls to updateTexImage. // // Detaching a SurfaceTexture from an OpenGL ES context will result in the // deletion of the OpenGL ES texture object into which the images were being // streamed. After a SurfaceTexture has been detached from the OpenGL ES // context calls to updateTexImage will fail returning INVALID_OPERATION // until the SurfaceTexture is attached to a new OpenGL ES context using the // attachToContext method. status_t detachFromContext(); // attachToContext attaches a SurfaceTexture that is currently in the // 'detached' state to the current OpenGL ES context. A SurfaceTexture is // in the 'detached' state iff detachFromContext has successfully been // called and no calls to attachToContext have succeeded since the last // detachFromContext call. Calls to attachToContext made on a // SurfaceTexture that is not in the 'detached' state will result in an // INVALID_OPERATION error. // // The tex argument specifies the OpenGL ES texture object name in the // new context into which the image contents will be streamed. A successful // call to attachToContext will result in this texture object being bound to // the texture target and populated with the image contents that were // current at the time of the last call to detachFromContext. status_t attachToContext(GLuint tex); // dump our state in a String virtual void dump(String8& result) const; virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const; Loading Loading @@ -209,6 +252,12 @@ private: // to compute this matrix and stores it in mCurrentTransformMatrix. void computeCurrentTransformMatrix(); // syncForReleaseLocked performs the synchronization needed to release the // current slot from an OpenGL ES context. If needed it will set the // current slot's fence to guard against a producer accessing the buffer // before the outstanding accesses have completed. status_t syncForReleaseLocked(EGLDisplay dpy); // mCurrentTextureBuf is the graphic buffer of the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. Loading Loading @@ -237,8 +286,8 @@ private: // mTexName is the name of the OpenGL texture to which streamed images will // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; // and can be changed with a call to attachToContext. GLuint mTexName; // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync // extension should be used to prevent buffers from being dequeued before Loading Loading @@ -277,13 +326,14 @@ private: // 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. // current display when updateTexImage is called for the first time and when // attachToContext is called. 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. // time and when attachToContext is called. EGLContext mEglContext; // mEGLSlots stores the buffers that have been allocated by the BufferQueue Loading Loading @@ -323,6 +373,14 @@ private: // if none is supplied sp<BufferQueue> mBufferQueue; // mAttached indicates whether the SurfaceTexture is currently attached to // an OpenGL ES context. For legacy reasons, this is initialized to true, // indicating that the SurfaceTexture is considered to be attached to // whatever context is current at the time of the first updateTexImage call. // It is set to false by detachFromContext, and then set to true again by // attachToContext. bool mAttached; // 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. Loading
libs/gui/SurfaceTexture.cpp +198 −36 Original line number Diff line number Diff line Loading @@ -118,7 +118,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), mAbandoned(false), mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT) mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT), mAttached(true) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); Loading Loading @@ -176,21 +177,29 @@ status_t SurfaceTexture::updateTexImage() { Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture"); ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return -EINVAL; return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return -EINVAL; return INVALID_OPERATION; } mEglDisplay = dpy; Loading @@ -216,7 +225,7 @@ status_t SurfaceTexture::updateTexImage() { EGLImageKHR image = mEGLSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { if (item.mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); ST_LOGE("updateTexImage: buffer at slot %d is null", buf); return BAD_VALUE; } image = createImage(dpy, item.mGraphicBuffer); Loading @@ -224,7 +233,7 @@ status_t SurfaceTexture::updateTexImage() { if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. return -EINVAL; return UNKNOWN_ERROR; } } Loading @@ -236,31 +245,23 @@ status_t SurfaceTexture::updateTexImage() { glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); bool failed = false; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("error binding external texture image %p (slot %d): %#04x", image, buf, error); failed = true; } if (failed) { mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); return -EINVAL; ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; } if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (mUseFenceSync) { EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ALOGE("updateTexImage: error creating fence: %#x", eglGetError()); mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); return -EINVAL; } glFlush(); mEGLSlots[mCurrentTexture].mFence = fence; if (err == OK) { err = syncForReleaseLocked(dpy); } if (err != OK) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", Loading @@ -268,9 +269,12 @@ status_t SurfaceTexture::updateTexImage() { mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); // release old buffer // Release the old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { mBufferQueue->releaseBuffer(mCurrentTexture, dpy, mEGLSlots[mCurrentTexture].mFence); mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR; } // Update the SurfaceTexture state. mCurrentTexture = buf; Loading @@ -280,10 +284,6 @@ status_t SurfaceTexture::updateTexImage() { mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; computeCurrentTransformMatrix(); // Now that we've passed the point at which failures can happen, // it's safe to remove the buffer from the front of the queue. } else { // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); Loading @@ -292,6 +292,168 @@ status_t SurfaceTexture::updateTexImage() { return OK; } status_t SurfaceTexture::detachFromContext() { ATRACE_CALL(); ST_LOGV("detachFromContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("detachFromContext: abandoned SurfaceTexture"); return NO_INIT; } if (!mAttached) { ST_LOGE("detachFromContext: SurfaceTexture is not attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) { ST_LOGE("detachFromContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) { ST_LOGE("detachFromContext: invalid current EGLContext"); return INVALID_OPERATION; } if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) { status_t err = syncForReleaseLocked(dpy); if (err != OK) { return err; } glDeleteTextures(1, &mTexName); } mEglDisplay = EGL_NO_DISPLAY; mEglContext = EGL_NO_CONTEXT; mAttached = false; return OK; } status_t SurfaceTexture::attachToContext(GLuint tex) { ATRACE_CALL(); ST_LOGV("attachToContext"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("attachToContext: abandoned SurfaceTexture"); return NO_INIT; } if (mAttached) { ST_LOGE("attachToContext: SurfaceTexture is already attached to a " "context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if (dpy == EGL_NO_DISPLAY) { ST_LOGE("attachToContext: invalid current EGLDisplay"); return INVALID_OPERATION; } if (ctx == EGL_NO_CONTEXT) { ST_LOGE("attachToContext: invalid current EGLContext"); return INVALID_OPERATION; } // We need to bind the texture regardless of whether there's a current // buffer. glBindTexture(mTexTarget, tex); if (mCurrentTextureBuf != NULL) { // If the current buffer is no longer associated with a slot, then it // doesn't have an EGLImage. In that case we create one now, but we also // destroy it once we've used it to attach the buffer to the OpenGL ES // texture. bool imageNeedsDestroy = false; EGLImageKHR image = EGL_NO_IMAGE_KHR; if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { image = mEGLSlots[mCurrentTexture].mEglImage; imageNeedsDestroy = false; } else { image = createImage(dpy, mCurrentTextureBuf); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; } imageNeedsDestroy = true; } // Attach the current buffer to the GL texture. glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); GLint error; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("attachToContext: error binding external texture image %p " "(slot %d): %#04x", image, mCurrentTexture, error); err = UNKNOWN_ERROR; } if (imageNeedsDestroy) { eglDestroyImageKHR(dpy, image); } if (err != OK) { return err; } } mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; return OK; } status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) { ST_LOGV("syncForReleaseLocked"); if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence; if (fence != EGL_NO_SYNC_KHR) { // There is already a fence for the current slot. We need to wait // on that before replacing it with another fence to ensure that all // outstanding buffer accesses have completed before the producer // accesses it. EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); if (result == EGL_FALSE) { ST_LOGE("syncForReleaseLocked: error waiting for previous " "fence: %#x", eglGetError()); return UNKNOWN_ERROR; } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("syncForReleaseLocked: timeout waiting for previous " "fence"); return TIMED_OUT; } eglDestroySyncKHR(dpy, fence); } // Create a fence for the outstanding accesses in the current OpenGL ES // context. fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { ST_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); return UNKNOWN_ERROR; } glFlush(); mEGLSlots[mCurrentTexture].mFence = fence; } return OK; } bool SurfaceTexture::isExternalFormat(uint32_t format) { switch (format) { Loading