Loading include/gui/BufferQueue.h +3 −2 Original line number Diff line number Diff line Loading @@ -182,8 +182,9 @@ public: mBuf(INVALID_BUFFER_SLOT) { mCrop.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. // mGraphicBuffer points to the buffer allocated for this slot, or is NULL // if the buffer in this slot has been acquired in the past (see // BufferSlot.mAcquireCalled). sp<GraphicBuffer> mGraphicBuffer; // mCrop is the current crop rectangle for this buffer slot. Loading include/gui/SurfaceTexture.h +38 −30 Original line number Diff line number Diff line Loading @@ -74,14 +74,13 @@ public: GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, const sp<BufferQueue> &bufferQueue = 0); // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. // // After calling this method the doGLFenceWait method must be called // before issuing OpenGL ES commands that access the texture contents. // This calls doGLFenceWait to ensure proper synchronization. status_t updateTexImage(); // setReleaseFence stores a fence file descriptor that will signal when the Loading Loading @@ -161,8 +160,7 @@ public: // doGLFenceWait inserts a wait command into the OpenGL ES command stream // to ensure that it is safe for future OpenGL ES commands to access the // current texture buffer. This must be called each time updateTexImage // is called before issuing OpenGL ES commands that access the texture. // current texture buffer. status_t doGLFenceWait() const; // isSynchronousMode returns whether the SurfaceTexture is currently in Loading Loading @@ -233,23 +231,33 @@ protected: virtual status_t releaseBufferLocked(int buf, EGLDisplay display, EGLSyncKHR eglFence); status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) { return releaseBufferLocked(buf, mEglDisplay, eglFence); } static bool isExternalFormat(uint32_t format); private: // this version of updateTexImage() takes a functor used to reject or not // the newly acquired buffer. // this API is TEMPORARY and intended to be used by SurfaceFlinger only, // which is why class Layer is made a friend of SurfaceTexture below. class BufferRejecter { friend class SurfaceTexture; virtual bool reject(const sp<GraphicBuffer>& buf, const BufferQueue::BufferItem& item) = 0; protected: virtual ~BufferRejecter() { } }; friend class Layer; status_t updateTexImage(BufferRejecter* rejecter, bool skipSync); // This releases the buffer in the slot referenced by mCurrentTexture, // then updates state to refer to the BufferItem, which must be a // newly-acquired buffer. status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item); // Binds mTexName and the current buffer to mTexTarget. Uses // mCurrentTexture if it's set, mCurrentTextureBuf if not. status_t bindTextureImage(); // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command // stream to ensure that it is safe for future OpenGL ES commands to // access the current texture buffer. status_t doGLFenceWaitLocked() const; // Gets the current EGLDisplay and EGLContext values, and compares them // to mEglDisplay and mEglContext. If the fields have been previously // set, the values must match; if not, the fields are set to the current // values. status_t checkAndUpdateEglStateLocked(); private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); Loading @@ -267,19 +275,19 @@ private: // mCurrentTextureBuf must not be NULL. void computeCurrentTransformMatrixLocked(); // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command // stream to ensure that it is safe for future OpenGL ES commands to // access the current texture buffer. This must be called each time // updateTexImage is called before issuing OpenGL ES commands that access // the texture. status_t doGLFenceWaitLocked() const; // 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); // Normally, when we bind a buffer to a texture target, we bind a buffer // that is referenced by an entry in mEglSlots. In some situations we // have a buffer in mCurrentTextureBuf, but no corresponding entry for // it in our slot array. bindUnslottedBuffer handles that situation by // binding the buffer without touching the EglSlots. status_t bindUnslottedBufferLocked(EGLDisplay dpy); // The default consumer usage flags that SurfaceTexture always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the SurfaceTexture user. In particular, SurfaceTexture will always Loading Loading @@ -344,8 +352,8 @@ private: // EGLSlot contains the information and object references that // SurfaceTexture maintains about a BufferQueue buffer slot. struct EGLSlot { EGLSlot() struct EglSlot { EglSlot() : mEglImage(EGL_NO_IMAGE_KHR), mEglFence(EGL_NO_SYNC_KHR) { } Loading Loading @@ -379,7 +387,7 @@ private: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS]; EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS]; // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, Loading libs/gui/BufferQueue.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ #include <private/gui/ComposerService.h> #include <utils/Log.h> #include <gui/SurfaceTexture.h> #include <utils/Trace.h> // Macros for including the BufferQueue name in log messages Loading libs/gui/SurfaceTexture.cpp +192 −138 Original line number Diff line number Diff line Loading @@ -154,7 +154,56 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) } status_t SurfaceTexture::updateTexImage() { return SurfaceTexture::updateTexImage(NULL, false); ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferQueue::BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. ST_LOGV("updateTexImage: no buffers were available"); glBindTexture(mTexTarget, mTexName); err = NO_ERROR; } else { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // Release the previous buffer. err = releaseAndUpdateLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); return err; } // Bind the new buffer to the GL texture. err = bindTextureImage(); if (err != NO_ERROR) { return err; } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); } status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { Loading @@ -165,21 +214,16 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { int slot = item->mBuf; if (item->mGraphicBuffer != NULL) { // This buffer has not been acquired before, so we must assume // that any EGLImage in mEglSlots is stale. if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage); mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; } if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", slot); // keep going } // Update the GL texture object. We may have to do this even when // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when // detaching from a context but the buffer has not been re-allocated. if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) { EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; } mEglSlots[slot].mEglImage = image; } return NO_ERROR; Loading @@ -187,106 +231,71 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display, EGLSyncKHR eglFence) { status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay, eglFence); status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); status_t SurfaceTexture::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) { status_t err = NO_ERROR; if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " ST_LOGE("releaseAndUpdate: 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) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return INVALID_OPERATION; // Confirm state. err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } mEglDisplay = dpy; mEglContext = ctx; BufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err == NO_ERROR) { int buf = item.mBuf; // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); glBindTexture(mTexTarget, mTexName); return NO_ERROR; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } EGLImageKHR image = mEglSlots[buf].mEglImage; glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; // If the mEglSlot entry is empty, create an EGLImage for the gralloc // buffer currently in the slot in ConsumerBase. // // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired), if we destroyed the // EGLImage when detaching from a context but the buffer has not been // re-allocated. if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); if (image == EGL_NO_IMAGE_KHR) { ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d", mEglDisplay, buf); return UNKNOWN_ERROR; } if (err == NO_ERROR) { err = syncForReleaseLocked(dpy); mEglSlots[buf].mEglImage = image; } // Do whatever sync ops we need to do before releasing the old slot. err = syncForReleaseLocked(mEglDisplay); if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = releaseBufferLocked(mCurrentTexture, dpy, status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] } } Loading @@ -298,28 +307,63 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; if (!skipSync) { // SurfaceFlinger needs to lazily perform GLES synchronization // only when it's actually going to use GLES for compositing. // Eventually SurfaceFlinger should have its own consumer class, // but for now we'll just hack it in to SurfaceTexture. // SurfaceFlinger is responsible for calling doGLFenceWait before // texturing from this SurfaceTexture. doGLFenceWaitLocked(); } computeCurrentTransformMatrixLocked(); } else { if (err < 0) { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); return err; } // We always bind the texture even if we don't update its contents. status_t SurfaceTexture::bindTextureImage() { if (mEglDisplay == EGL_NO_DISPLAY) { ALOGE("bindTextureImage: invalid display"); return INVALID_OPERATION; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("bindTextureImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); return OK; if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTextureBuf == NULL) { ST_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } return bindUnslottedBufferLocked(mEglDisplay); } else { EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; return err; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); return UNKNOWN_ERROR; } return NO_ERROR; } } status_t SurfaceTexture::checkAndUpdateEglStateLocked() { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; return NO_ERROR; } void SurfaceTexture::setReleaseFence(int fenceFd) { Loading Loading @@ -427,6 +471,25 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { // The EGLImageKHR that was associated with the slot was destroyed when // the SurfaceTexture was detached from the old context, so we need to // recreate it here. status_t err = bindUnslottedBufferLocked(dpy); if (err != NO_ERROR) { return err; } } mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; return OK; } status_t SurfaceTexture::bindUnslottedBufferLocked(EGLDisplay dpy) { ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p", mCurrentTexture, mCurrentTextureBuf.get()); // Create a temporary EGLImageKHR. EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; Loading @@ -438,7 +501,7 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { GLint error; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("attachToContext: error binding external texture image %p " ST_LOGE("bindUnslottedBuffer: error binding external texture image %p " "(slot %d): %#04x", image, mCurrentTexture, error); err = UNKNOWN_ERROR; } Loading @@ -450,18 +513,9 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { // gets acquired in updateTexImage. 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"); Loading services/surfaceflinger/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ GLExtensions.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ SurfaceFlingerConsumer.cpp \ SurfaceTextureLayer.cpp \ Transform.cpp \ Loading Loading
include/gui/BufferQueue.h +3 −2 Original line number Diff line number Diff line Loading @@ -182,8 +182,9 @@ public: mBuf(INVALID_BUFFER_SLOT) { mCrop.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot or is NULL // if no buffer has been allocated. // mGraphicBuffer points to the buffer allocated for this slot, or is NULL // if the buffer in this slot has been acquired in the past (see // BufferSlot.mAcquireCalled). sp<GraphicBuffer> mGraphicBuffer; // mCrop is the current crop rectangle for this buffer slot. Loading
include/gui/SurfaceTexture.h +38 −30 Original line number Diff line number Diff line Loading @@ -74,14 +74,13 @@ public: GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true, const sp<BufferQueue> &bufferQueue = 0); // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. // // This call may only be made while the OpenGL ES context to which the // target texture belongs is bound to the calling thread. // // After calling this method the doGLFenceWait method must be called // before issuing OpenGL ES commands that access the texture contents. // This calls doGLFenceWait to ensure proper synchronization. status_t updateTexImage(); // setReleaseFence stores a fence file descriptor that will signal when the Loading Loading @@ -161,8 +160,7 @@ public: // doGLFenceWait inserts a wait command into the OpenGL ES command stream // to ensure that it is safe for future OpenGL ES commands to access the // current texture buffer. This must be called each time updateTexImage // is called before issuing OpenGL ES commands that access the texture. // current texture buffer. status_t doGLFenceWait() const; // isSynchronousMode returns whether the SurfaceTexture is currently in Loading Loading @@ -233,23 +231,33 @@ protected: virtual status_t releaseBufferLocked(int buf, EGLDisplay display, EGLSyncKHR eglFence); status_t releaseBufferLocked(int buf, EGLSyncKHR eglFence) { return releaseBufferLocked(buf, mEglDisplay, eglFence); } static bool isExternalFormat(uint32_t format); private: // this version of updateTexImage() takes a functor used to reject or not // the newly acquired buffer. // this API is TEMPORARY and intended to be used by SurfaceFlinger only, // which is why class Layer is made a friend of SurfaceTexture below. class BufferRejecter { friend class SurfaceTexture; virtual bool reject(const sp<GraphicBuffer>& buf, const BufferQueue::BufferItem& item) = 0; protected: virtual ~BufferRejecter() { } }; friend class Layer; status_t updateTexImage(BufferRejecter* rejecter, bool skipSync); // This releases the buffer in the slot referenced by mCurrentTexture, // then updates state to refer to the BufferItem, which must be a // newly-acquired buffer. status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item); // Binds mTexName and the current buffer to mTexTarget. Uses // mCurrentTexture if it's set, mCurrentTextureBuf if not. status_t bindTextureImage(); // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command // stream to ensure that it is safe for future OpenGL ES commands to // access the current texture buffer. status_t doGLFenceWaitLocked() const; // Gets the current EGLDisplay and EGLContext values, and compares them // to mEglDisplay and mEglContext. If the fields have been previously // set, the values must match; if not, the fields are set to the current // values. status_t checkAndUpdateEglStateLocked(); private: // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); Loading @@ -267,19 +275,19 @@ private: // mCurrentTextureBuf must not be NULL. void computeCurrentTransformMatrixLocked(); // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command // stream to ensure that it is safe for future OpenGL ES commands to // access the current texture buffer. This must be called each time // updateTexImage is called before issuing OpenGL ES commands that access // the texture. status_t doGLFenceWaitLocked() const; // 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); // Normally, when we bind a buffer to a texture target, we bind a buffer // that is referenced by an entry in mEglSlots. In some situations we // have a buffer in mCurrentTextureBuf, but no corresponding entry for // it in our slot array. bindUnslottedBuffer handles that situation by // binding the buffer without touching the EglSlots. status_t bindUnslottedBufferLocked(EGLDisplay dpy); // The default consumer usage flags that SurfaceTexture always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the SurfaceTexture user. In particular, SurfaceTexture will always Loading Loading @@ -344,8 +352,8 @@ private: // EGLSlot contains the information and object references that // SurfaceTexture maintains about a BufferQueue buffer slot. struct EGLSlot { EGLSlot() struct EglSlot { EglSlot() : mEglImage(EGL_NO_IMAGE_KHR), mEglFence(EGL_NO_SYNC_KHR) { } Loading Loading @@ -379,7 +387,7 @@ private: // slot that has not yet been used. The buffer allocated to a slot will also // be replaced if the requested buffer usage or geometry differs from that // of the buffer allocated to a slot. EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS]; EglSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS]; // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, Loading
libs/gui/BufferQueue.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,6 @@ #include <private/gui/ComposerService.h> #include <utils/Log.h> #include <gui/SurfaceTexture.h> #include <utils/Trace.h> // Macros for including the BufferQueue name in log messages Loading
libs/gui/SurfaceTexture.cpp +192 −138 Original line number Diff line number Diff line Loading @@ -154,7 +154,56 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) } status_t SurfaceTexture::updateTexImage() { return SurfaceTexture::updateTexImage(NULL, false); ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferQueue::BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. ST_LOGV("updateTexImage: no buffers were available"); glBindTexture(mTexTarget, mTexName); err = NO_ERROR; } else { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // Release the previous buffer. err = releaseAndUpdateLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); return err; } // Bind the new buffer to the GL texture. err = bindTextureImage(); if (err != NO_ERROR) { return err; } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); } status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { Loading @@ -165,21 +214,16 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { int slot = item->mBuf; if (item->mGraphicBuffer != NULL) { // This buffer has not been acquired before, so we must assume // that any EGLImage in mEglSlots is stale. if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage); mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; } if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", slot); // keep going } // Update the GL texture object. We may have to do this even when // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when // detaching from a context but the buffer has not been re-allocated. if (mEglSlots[slot].mEglImage == EGL_NO_IMAGE_KHR) { EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; } mEglSlots[slot].mEglImage = image; } return NO_ERROR; Loading @@ -187,106 +231,71 @@ status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) { status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display, EGLSyncKHR eglFence) { status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay, eglFence); status_t err = ConsumerBase::releaseBufferLocked(buf, display, eglFence); mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR; return err; } status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); status_t SurfaceTexture::releaseAndUpdateLocked(const BufferQueue::BufferItem& item) { status_t err = NO_ERROR; if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " ST_LOGE("releaseAndUpdate: 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) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return INVALID_OPERATION; // Confirm state. err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } mEglDisplay = dpy; mEglContext = ctx; BufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err == NO_ERROR) { int buf = item.mBuf; // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); glBindTexture(mTexTarget, mTexName); return NO_ERROR; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } EGLImageKHR image = mEglSlots[buf].mEglImage; glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; // If the mEglSlot entry is empty, create an EGLImage for the gralloc // buffer currently in the slot in ConsumerBase. // // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired), if we destroyed the // EGLImage when detaching from a context but the buffer has not been // re-allocated. if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) { EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer); if (image == EGL_NO_IMAGE_KHR) { ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d", mEglDisplay, buf); return UNKNOWN_ERROR; } if (err == NO_ERROR) { err = syncForReleaseLocked(dpy); mEglSlots[buf].mEglImage = image; } // Do whatever sync ops we need to do before releasing the old slot. err = syncForReleaseLocked(mEglDisplay); if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); releaseBufferLocked(buf, mEglDisplay, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = releaseBufferLocked(mCurrentTexture, dpy, status_t status = releaseBufferLocked(mCurrentTexture, mEglDisplay, mEglSlots[mCurrentTexture].mEglFence); if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] } } Loading @@ -298,28 +307,63 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; if (!skipSync) { // SurfaceFlinger needs to lazily perform GLES synchronization // only when it's actually going to use GLES for compositing. // Eventually SurfaceFlinger should have its own consumer class, // but for now we'll just hack it in to SurfaceTexture. // SurfaceFlinger is responsible for calling doGLFenceWait before // texturing from this SurfaceTexture. doGLFenceWaitLocked(); } computeCurrentTransformMatrixLocked(); } else { if (err < 0) { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); return err; } // We always bind the texture even if we don't update its contents. status_t SurfaceTexture::bindTextureImage() { if (mEglDisplay == EGL_NO_DISPLAY) { ALOGE("bindTextureImage: invalid display"); return INVALID_OPERATION; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("bindTextureImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); return OK; if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTextureBuf == NULL) { ST_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } return bindUnslottedBufferLocked(mEglDisplay); } else { EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; return err; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); return UNKNOWN_ERROR; } return NO_ERROR; } } status_t SurfaceTexture::checkAndUpdateEglStateLocked() { EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("checkAndUpdateEglState: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("checkAndUpdateEglState: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; return NO_ERROR; } void SurfaceTexture::setReleaseFence(int fenceFd) { Loading Loading @@ -427,6 +471,25 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { // The EGLImageKHR that was associated with the slot was destroyed when // the SurfaceTexture was detached from the old context, so we need to // recreate it here. status_t err = bindUnslottedBufferLocked(dpy); if (err != NO_ERROR) { return err; } } mEglDisplay = dpy; mEglContext = ctx; mTexName = tex; mAttached = true; return OK; } status_t SurfaceTexture::bindUnslottedBufferLocked(EGLDisplay dpy) { ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p", mCurrentTexture, mCurrentTextureBuf.get()); // Create a temporary EGLImageKHR. EGLImageKHR image = createImage(dpy, mCurrentTextureBuf); if (image == EGL_NO_IMAGE_KHR) { return UNKNOWN_ERROR; Loading @@ -438,7 +501,7 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { GLint error; status_t err = OK; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("attachToContext: error binding external texture image %p " ST_LOGE("bindUnslottedBuffer: error binding external texture image %p " "(slot %d): %#04x", image, mCurrentTexture, error); err = UNKNOWN_ERROR; } Loading @@ -450,18 +513,9 @@ status_t SurfaceTexture::attachToContext(GLuint tex) { // gets acquired in updateTexImage. 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"); Loading
services/surfaceflinger/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \ GLExtensions.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ SurfaceFlingerConsumer.cpp \ SurfaceTextureLayer.cpp \ Transform.cpp \ Loading