diff options
author | Daniel Lam <dalam@google.com> | 2012-01-22 15:26:27 -0800 |
---|---|---|
committer | Daniel Lam <dalam@google.com> | 2012-02-07 13:57:01 -0800 |
commit | 70e80aacf98414a6acee0d0e5eb1a7b5df7daccb (patch) | |
tree | c9872a1478e025b50fffb06a53e687963566805c | |
parent | e7171effdf9ef0700782520187c0a292b0a74759 (diff) | |
download | frameworks_base-70e80aacf98414a6acee0d0e5eb1a7b5df7daccb.zip frameworks_base-70e80aacf98414a6acee0d0e5eb1a7b5df7daccb.tar.gz frameworks_base-70e80aacf98414a6acee0d0e5eb1a7b5df7daccb.tar.bz2 |
Refactored ISurfaceTexture calls from SurfaceTexture into BufferQueue.
Change-Id: I514f6b802f6b49c9ae27bed37bf0b9d23da03c9a
-rw-r--r-- | include/gui/BufferQueue.h | 343 | ||||
-rw-r--r-- | include/gui/SurfaceTexture.h | 298 | ||||
-rw-r--r-- | libs/gui/Android.mk | 1 | ||||
-rw-r--r-- | libs/gui/BufferQueue.cpp | 722 | ||||
-rw-r--r-- | libs/gui/SurfaceTexture.cpp | 674 |
5 files changed, 1072 insertions, 966 deletions
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h new file mode 100644 index 0000000..991a181 --- /dev/null +++ b/include/gui/BufferQueue.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_GUI_BUFFERQUEUE_H +#define ANDROID_GUI_BUFFERQUEUE_H + +#include <EGL/egl.h> + +#include <gui/ISurfaceTexture.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class BufferQueue : public BnSurfaceTexture { +public: + enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + enum { + MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, + MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + }; + enum { NUM_BUFFER_SLOTS = 32 }; + enum { NO_CONNECTED_API = 0 }; + + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called from queueBuffer() each time an + // additional frame becomes available for consumption. This means that + // frames that are queued while in asynchronous mode only trigger the + // callback if no previous frames are pending. Frames queued while in + // synchronous mode always trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + // BufferQueue manages a pool of gralloc memory slots to be used + // by producers and consumers. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. + BufferQueue(bool allowSynchronousMode = true); + virtual ~BufferQueue(); + + // setBufferCount updates the number of available buffer slots. After + // calling this all buffer slots are both unallocated and owned by the + // BufferQueue object (i.e. they are not owned by the client). + virtual status_t setBufferCount(int bufferCount); + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); + + // dequeueBuffer gets the next buffer slot index for the client to use. If a + // buffer slot is available then that slot index is written to the location + // pointed to by the buf argument and a status of OK is returned. If no + // slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. + virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); + + // queueBuffer returns a filled buffer to the BufferQueue. In addition, a + // timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are client-dependent and should be documented by the + // client. + virtual status_t queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + virtual void cancelBuffer(int buf); + virtual status_t setCrop(const Rect& reg); + virtual status_t setTransform(uint32_t transform); + virtual status_t setScalingMode(int mode); + + // setSynchronousMode set whether dequeueBuffer is synchronous or + // asynchronous. In synchronous mode, dequeueBuffer blocks until + // a buffer is available, the currently bound buffer can be dequeued and + // queued buffers will be retired in order. + // The default mode is asynchronous. + virtual status_t setSynchronousMode(bool enabled); + + // connect attempts to connect a producer client API to the BufferQueue. + // This must be called before any other ISurfaceTexture methods are called + // except for getAllocator. + // + // This method will fail if the connect was previously called on the + // BufferQueue and no corresponding disconnect call was made. + virtual status_t connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); + + // disconnect attempts to disconnect a producer client API from the + // BufferQueue. Calling this method will cause any subsequent calls to other + // ISurfaceTexture methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the BufferQueue is not currently + // connected to the specified client API. + virtual status_t disconnect(int api); + +protected: + + // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) + // for the given slot. + void freeBufferLocked(int index); + + // freeAllBuffersLocked frees the resources (both GraphicBuffer and + // EGLImage) for all slots. + void freeAllBuffersLocked(); + + // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer + // and EGLImage) for all slots except the head of mQueue + void freeAllBuffersExceptHeadLocked(); + + // drainQueueLocked drains the buffer queue if we're in synchronous mode + // returns immediately otherwise. It returns NO_INIT if the BufferQueue + // became abandoned or disconnected during this call. + status_t drainQueueLocked(); + + // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in + // synchronous mode and free all buffers. In asynchronous mode, all buffers + // are freed except the current buffer. + status_t drainQueueAndFreeBuffersLocked(); + + status_t setBufferCountServerLocked(int bufferCount); + + enum { INVALID_BUFFER_SLOT = -1 }; + + struct BufferSlot { + + BufferSlot() + : mEglImage(EGL_NO_IMAGE_KHR), + mEglDisplay(EGL_NO_DISPLAY), + mBufferState(BufferSlot::FREE), + mRequestBufferCalled(false), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mFrameNumber(0), + mFence(EGL_NO_SYNC_KHR) { + mCrop.makeInvalid(); + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp<GraphicBuffer> mGraphicBuffer; + + // mEglImage is the EGLImage created from mGraphicBuffer. + EGLImageKHR mEglImage; + + // mEglDisplay is the EGLDisplay used to create mEglImage. + EGLDisplay mEglDisplay; + + // BufferState represents the different states in which a buffer slot + // can be. + enum BufferState { + // FREE indicates that the buffer is not currently being used and + // will not be used in the future until it gets dequeued and + // subsequently queued by the client. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // client, but has not yet been queued or canceled. The buffer is + // considered 'owned' by the client, and the server should not use + // it for anything. + // + // Note that when in synchronous-mode (mSynchronousMode == true), + // the buffer that's currently attached to the texture may be + // dequeued by the client. That means that the current buffer can + // be in either the DEQUEUED or QUEUED state. In asynchronous mode, + // however, the current buffer is always in the QUEUED state. + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been queued by the client, + // and has not since been made available for the client to dequeue. + // Attaching the buffer to the texture does NOT transition the + // buffer away from the QUEUED state. However, in Synchronous mode + // the current buffer may be dequeued by the client under some + // circumstances. See the note about the current buffer in the + // documentation for DEQUEUED. + QUEUED = 2, + }; + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the client did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching client bugs. + bool mRequestBufferCalled; + + // mCrop is the current crop rectangle for this buffer slot. This gets + // set to mNextCrop each time queueBuffer gets called for this buffer. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. This + // gets set to mNextTransform each time queueBuffer gets called for this + // slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. This + // gets set to mNextScalingMode each time queueBuffer gets called for + // this slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // 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 + // side. This allows buffer ownership to be transferred between the client + // and server without sending a GraphicBuffer over binder. The entire array + // is initialized to NULL at construction time, and buffers are allocated + // for a slot when requestBuffer is called with that slot's index. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in requestBuffers() if a width and height of zero is specified. + uint32_t mDefaultHeight; + + // mPixelFormat holds the pixel format of allocated buffers. It is used + // in requestBuffers() if a format of zero is specified. + uint32_t mPixelFormat; + + // mBufferCount is the number of buffer slots that the client and server + // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed + // by calling setBufferCount or setBufferCountServer + int mBufferCount; + + // mClientBufferCount is the number of buffer slots requested by the client. + // The default is zero, which means the client doesn't care how many buffers + // there is. + int mClientBufferCount; + + // mServerBufferCount buffer count requested by the server-side + int mServerBufferCount; + + // mCurrentTexture is the buffer slot index of the buffer that is currently + // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, + // indicating that no buffer slot is currently bound to the texture. Note, + // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean + // that no buffer is bound to the texture. A call to setBufferCount will + // reset mCurrentTexture to INVALID_BUFFER_SLOT. + int mCurrentTexture; + + // mNextCrop is the crop rectangle that will be used for the next buffer + // that gets queued. It is set by calling setCrop. + Rect mNextCrop; + + // mNextTransform is the transform identifier that will be used for the next + // buffer that gets queued. It is set by calling setTransform. + uint32_t mNextTransform; + + // mNextScalingMode is the scaling mode that will be used for the next + // buffers that get queued. It is set by calling setScalingMode. + int mNextScalingMode; + + // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to + // allocate new GraphicBuffer objects. + sp<IGraphicBufferAlloc> mGraphicBufferAlloc; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // mSynchronousMode whether we're in synchronous mode or not + bool mSynchronousMode; + + // mAllowSynchronousMode whether we allow synchronous mode or not + const bool mAllowSynchronousMode; + + // mConnectedApi indicates the API that is currently connected to this + // BufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + + // mDequeueCondition condition used for dequeueBuffer in synchronous mode + mutable Condition mDequeueCondition; + + // mQueue is a FIFO of queued buffers used in synchronous mode + typedef Vector<int> Fifo; + Fifo mQueue; + + // mAbandoned indicates that the BufferQueue will no longer be used to + // consume images buffers pushed to it using the ISurfaceTexture interface. + // It is initialized to false, and set to true in the abandon method. A + // BufferQueue that has been abandoned will return the NO_INIT error from + // all ISurfaceTexture methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the BufferQueue in log messages. + // It is set by the setName method. + String8 mName; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of BufferQueue objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // mFrameCounter is the free running counter, incremented for every buffer queued + // with the surface Texture. + uint64_t mFrameCounter; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_BUFFERQUEUE_H diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index a8c7672..4318f0f 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -23,6 +23,7 @@ #include <GLES2/gl2ext.h> #include <gui/ISurfaceTexture.h> +#include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> @@ -35,30 +36,11 @@ namespace android { // ---------------------------------------------------------------------------- -class IGraphicBufferAlloc; + class String8; -class SurfaceTexture : public BnSurfaceTexture { +class SurfaceTexture : public BufferQueue { public: - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; - enum { - MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1, - MIN_SYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS - }; - enum { NUM_BUFFER_SLOTS = 32 }; - enum { NO_CONNECTED_API = 0 }; - - struct FrameAvailableListener : public virtual RefBase { - // onFrameAvailable() is called from queueBuffer() each time an - // additional frame becomes available for consumption. This means that - // frames that are queued while in asynchronous mode only trigger the - // callback if no previous frames are pending. Frames queued while in - // synchronous mode always trigger the callback. - // - // This is called without any lock held and can be called concurrently - // by multiple threads. - virtual void onFrameAvailable() = 0; - }; // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the // name of the OpenGL ES texture to which images are to be streamed. This @@ -73,65 +55,8 @@ public: virtual ~SurfaceTexture(); - // setBufferCount updates the number of available buffer slots. After - // calling this all buffer slots are both unallocated and owned by the - // SurfaceTexture object (i.e. they are not owned by the client). - virtual status_t setBufferCount(int bufferCount); - - virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf); - - // dequeueBuffer gets the next buffer slot index for the client to use. If a - // buffer slot is available then that slot index is written to the location - // pointed to by the buf argument and a status of OK is returned. If no - // slot is available then a status of -EBUSY is returned and buf is - // unmodified. - // The width and height parameters must be no greater than the minimum of - // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). - // An error due to invalid dimensions might not be reported until - // updateTexImage() is called. - virtual status_t dequeueBuffer(int *buf, uint32_t width, uint32_t height, - uint32_t format, uint32_t usage); - - // queueBuffer returns a filled buffer to the SurfaceTexture. In addition, a - // timestamp must be provided for the buffer. The timestamp is in - // nanoseconds, and must be monotonically increasing. Its other semantics - // (zero point, etc) are client-dependent and should be documented by the - // client. - virtual status_t queueBuffer(int buf, int64_t timestamp, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - virtual void cancelBuffer(int buf); - virtual status_t setCrop(const Rect& reg); - virtual status_t setTransform(uint32_t transform); - virtual status_t setScalingMode(int mode); - virtual int query(int what, int* value); - // setSynchronousMode set whether dequeueBuffer is synchronous or - // asynchronous. In synchronous mode, dequeueBuffer blocks until - // a buffer is available, the currently bound buffer can be dequeued and - // queued buffers will be retired in order. - // The default mode is asynchronous. - virtual status_t setSynchronousMode(bool enabled); - - // connect attempts to connect a client API to the SurfaceTexture. This - // must be called before any other ISurfaceTexture methods are called except - // for getAllocator. - // - // This method will fail if the connect was previously called on the - // SurfaceTexture and no corresponding disconnect call was made. - virtual status_t connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform); - - // disconnect attempts to disconnect a client API from the SurfaceTexture. - // Calling this method will cause any subsequent calls to other - // ISurfaceTexture methods to fail except for getAllocator and connect. - // Successfully calling connect after this will allow the other methods to - // succeed again. - // - // This method will fail if the the SurfaceTexture is not currently - // connected to the specified client API. - virtual status_t disconnect(int api); - // updateTexImage sets the image contents of the target texture to that of // the most recently queued buffer. // @@ -233,28 +158,6 @@ public: protected: - // freeBufferLocked frees the resources (both GraphicBuffer and EGLImage) - // for the given slot. - void freeBufferLocked(int index); - - // freeAllBuffersLocked frees the resources (both GraphicBuffer and - // EGLImage) for all slots. - void freeAllBuffersLocked(); - - // freeAllBuffersExceptHeadLocked frees the resources (both GraphicBuffer - // and EGLImage) for all slots except the head of mQueue - void freeAllBuffersExceptHeadLocked(); - - // drainQueueLocked drains the buffer queue if we're in synchronous mode - // returns immediately otherwise. return NO_INIT if SurfaceTexture - // became abandoned or disconnected during this call. - status_t drainQueueLocked(); - - // drainQueueAndFreeBuffersLocked drains the buffer queue if we're in - // synchronous mode and free all buffers. In asynchronous mode, all buffers - // are freed except the current buffer. - status_t drainQueueAndFreeBuffersLocked(); - static bool isExternalFormat(uint32_t format); private: @@ -263,146 +166,11 @@ private: EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer); - status_t setBufferCountServerLocked(int bufferCount); - // computeCurrentTransformMatrix computes the transform matrix for the // current texture. It uses mCurrentTransform and the current GraphicBuffer // to compute this matrix and stores it in mCurrentTransformMatrix. void computeCurrentTransformMatrix(); - enum { INVALID_BUFFER_SLOT = -1 }; - - struct BufferSlot { - - BufferSlot() - : mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mBufferState(BufferSlot::FREE), - mRequestBufferCalled(false), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mFrameNumber(0), - mFence(EGL_NO_SYNC_KHR) { - mCrop.makeInvalid(); - } - - // mGraphicBuffer points to the buffer allocated for this slot or is NULL - // if no buffer has been allocated. - sp<GraphicBuffer> mGraphicBuffer; - - // mEglImage is the EGLImage created from mGraphicBuffer. - EGLImageKHR mEglImage; - - // mEglDisplay is the EGLDisplay used to create mEglImage. - EGLDisplay mEglDisplay; - - // BufferState represents the different states in which a buffer slot - // can be. - enum BufferState { - // FREE indicates that the buffer is not currently being used and - // will not be used in the future until it gets dequeued and - // subsequently queued by the client. - FREE = 0, - - // DEQUEUED indicates that the buffer has been dequeued by the - // client, but has not yet been queued or canceled. The buffer is - // considered 'owned' by the client, and the server should not use - // it for anything. - // - // Note that when in synchronous-mode (mSynchronousMode == true), - // the buffer that's currently attached to the texture may be - // dequeued by the client. That means that the current buffer can - // be in either the DEQUEUED or QUEUED state. In asynchronous mode, - // however, the current buffer is always in the QUEUED state. - DEQUEUED = 1, - - // QUEUED indicates that the buffer has been queued by the client, - // and has not since been made available for the client to dequeue. - // Attaching the buffer to the texture does NOT transition the - // buffer away from the QUEUED state. However, in Synchronous mode - // the current buffer may be dequeued by the client under some - // circumstances. See the note about the current buffer in the - // documentation for DEQUEUED. - QUEUED = 2, - }; - - // mBufferState is the current state of this buffer slot. - BufferState mBufferState; - - // mRequestBufferCalled is used for validating that the client did - // call requestBuffer() when told to do so. Technically this is not - // needed but useful for debugging and catching client bugs. - bool mRequestBufferCalled; - - // mCrop is the current crop rectangle for this buffer slot. This gets - // set to mNextCrop each time queueBuffer gets called for this buffer. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. This - // gets set to mNextTransform each time queueBuffer gets called for this - // slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. This - // gets set to mNextScalingMode each time queueBuffer gets called for - // this slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // 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 - // side. This allows buffer ownership to be transferred between the client - // and server without sending a GraphicBuffer over binder. The entire array - // is initialized to NULL at construction time, and buffers are allocated - // for a slot when requestBuffer is called with that slot's index. - BufferSlot mSlots[NUM_BUFFER_SLOTS]; - - // mDefaultWidth holds the default width of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultWidth; - - // mDefaultHeight holds the default height of allocated buffers. It is used - // in requestBuffers() if a width and height of zero is specified. - uint32_t mDefaultHeight; - - // mPixelFormat holds the pixel format of allocated buffers. It is used - // in requestBuffers() if a format of zero is specified. - uint32_t mPixelFormat; - - // mBufferCount is the number of buffer slots that the client and server - // must maintain. It defaults to MIN_ASYNC_BUFFER_SLOTS and can be changed - // by calling setBufferCount or setBufferCountServer - int mBufferCount; - - // mClientBufferCount is the number of buffer slots requested by the client. - // The default is zero, which means the client doesn't care how many buffers - // there is. - int mClientBufferCount; - - // mServerBufferCount buffer count requested by the server-side - int mServerBufferCount; - - // mCurrentTexture is the buffer slot index of the buffer that is currently - // bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT, - // indicating that no buffer slot is currently bound to the texture. Note, - // however, that a value of INVALID_BUFFER_SLOT does not necessarily mean - // that no buffer is bound to the texture. A call to setBufferCount will - // reset mCurrentTexture to INVALID_BUFFER_SLOT. - int mCurrentTexture; - // 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. @@ -429,72 +197,17 @@ private: // gets set each time updateTexImage is called. int64_t mCurrentTimestamp; - // mNextCrop is the crop rectangle that will be used for the next buffer - // that gets queued. It is set by calling setCrop. - Rect mNextCrop; - - // mNextTransform is the transform identifier that will be used for the next - // buffer that gets queued. It is set by calling setTransform. - uint32_t mNextTransform; - - // mNextScalingMode is the scaling mode that will be used for the next - // buffers that get queued. It is set by calling setScalingMode. - int mNextScalingMode; - // 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; - // mGraphicBufferAlloc is the connection to SurfaceFlinger that is used to - // allocate new GraphicBuffer objects. - sp<IGraphicBufferAlloc> mGraphicBufferAlloc; - - // mFrameAvailableListener is the listener object that will be called when a - // new frame becomes available. If it is not NULL it will be called from - // queueBuffer. - sp<FrameAvailableListener> mFrameAvailableListener; - - // mSynchronousMode whether we're in synchronous mode or not - bool mSynchronousMode; - - // mAllowSynchronousMode whether we allow synchronous mode or not - const bool mAllowSynchronousMode; - - // mConnectedApi indicates the API that is currently connected to this - // SurfaceTexture. It defaults to NO_CONNECTED_API (= 0), and gets updated - // by the connect and disconnect methods. - int mConnectedApi; - - // mDequeueCondition condition used for dequeueBuffer in synchronous mode - mutable Condition mDequeueCondition; - - // mQueue is a FIFO of queued buffers used in synchronous mode - typedef Vector<int> Fifo; - Fifo mQueue; - - // mAbandoned indicates that the SurfaceTexture will no longer be used to - // consume images buffers pushed to it using the ISurfaceTexture interface. - // It is initialized to false, and set to true in the abandon method. A - // SurfaceTexture that has been abandoned will return the NO_INIT error from - // all ISurfaceTexture methods capable of returning an error. - bool mAbandoned; - - // mName is a string used to identify the SurfaceTexture in log messages. - // 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. - mutable Mutex mMutex; - // mTexTarget is the GL texture target with which the GL texture object is // associated. It is set in the constructor and never changed. It is // almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android @@ -504,11 +217,6 @@ private: // browser's tile cache exceeds. const GLenum mTexTarget; - // mFrameCounter is the free running counter, incremented for every buffer queued - // with the surface Texture. - uint64_t mFrameCounter; - - }; // ---------------------------------------------------------------------------- diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index b6f5b9e..2f4ac62 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BitTube.cpp \ + BufferQueue.cpp \ DisplayEventReceiver.cpp \ IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp new file mode 100644 index 0000000..c7e2c0f --- /dev/null +++ b/libs/gui/BufferQueue.cpp @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferQueue" + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <gui/BufferQueue.h> +#include <private/gui/ComposerService.h> +#include <surfaceflinger/ISurfaceComposer.h> + +#include <utils/Log.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" +#else +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false +#endif + +// Macros for including the BufferQueue name in log messages +#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +BufferQueue::BufferQueue( bool allowSynchronousMode ) : + mDefaultWidth(1), + mDefaultHeight(1), + mPixelFormat(PIXEL_FORMAT_RGBA_8888), + mBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mClientBufferCount(0), + mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), + mCurrentTexture(INVALID_BUFFER_SLOT), + mNextTransform(0), + mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mSynchronousMode(false), + mAllowSynchronousMode(allowSynchronousMode), + mConnectedApi(NO_CONNECTED_API), + mAbandoned(false), + mFrameCounter(0) +{ + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + ST_LOGV("BufferQueue"); + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); + mNextCrop.makeInvalid(); +} + +BufferQueue::~BufferQueue() { + ST_LOGV("~BufferQueue"); +} + +status_t BufferQueue::setBufferCountServerLocked(int bufferCount) { + if (bufferCount > NUM_BUFFER_SLOTS) + return BAD_VALUE; + + // special-case, nothing to do + if (bufferCount == mBufferCount) + return OK; + + if (!mClientBufferCount && + bufferCount >= mBufferCount) { + // easy, we just have more buffers + mBufferCount = bufferCount; + mServerBufferCount = bufferCount; + mDequeueCondition.signal(); + } else { + // we're here because we're either + // - reducing the number of available buffers + // - or there is a client-buffer-count in effect + + // less than 2 buffers is never allowed + if (bufferCount < 2) + return BAD_VALUE; + + // when there is non client-buffer-count in effect, the client is not + // allowed to dequeue more than one buffer at a time, + // so the next time they dequeue a buffer, we know that they don't + // own one. the actual resizing will happen during the next + // dequeueBuffer. + + mServerBufferCount = bufferCount; + } + return OK; +} + +status_t BufferQueue::setBufferCount(int bufferCount) { + ST_LOGV("setBufferCount: count=%d", bufferCount); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (bufferCount > NUM_BUFFER_SLOTS) { + ST_LOGE("setBufferCount: bufferCount larger than slots available"); + return BAD_VALUE; + } + + // Error out if the user has dequeued buffers + for (int i=0 ; i<mBufferCount ; i++) { + if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { + ST_LOGE("setBufferCount: client owns some buffers"); + return -EINVAL; + } + } + + const int minBufferSlots = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + if (bufferCount == 0) { + mClientBufferCount = 0; + bufferCount = (mServerBufferCount >= minBufferSlots) ? + mServerBufferCount : minBufferSlots; + return setBufferCountServerLocked(bufferCount); + } + + if (bufferCount < minBufferSlots) { + ST_LOGE("setBufferCount: requested buffer count (%d) is less than " + "minimum (%d)", bufferCount, minBufferSlots); + return BAD_VALUE; + } + + // here we're guaranteed that the client doesn't have dequeued buffers + // and will release all of its buffer references. + freeAllBuffersLocked(); + mBufferCount = bufferCount; + mClientBufferCount = bufferCount; + mCurrentTexture = INVALID_BUFFER_SLOT; + mQueue.clear(); + mDequeueCondition.signal(); + return OK; +} + +status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) { + ST_LOGV("requestBuffer: slot=%d", slot); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (slot < 0 || mBufferCount <= slot) { + ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, slot); + return BAD_VALUE; + } + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, + uint32_t format, uint32_t usage) { + ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); + + if ((w && !h) || (!w && h)) { + ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); + return BAD_VALUE; + } + + 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; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; + } + + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } + + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + } + + // if buffer is FREE it CANNOT be current + ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { + foundSync = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* 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; + } + } + } + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + 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. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } + + // 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); + } + } + + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; + } + + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + const int buf = found; + *outBuf = found; + + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } + + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + 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); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + 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) { + ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("dequeueBuffer: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, fence); + } + + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t BufferQueue::setSynchronousMode(bool enabled) { + ST_LOGV("setSynchronousMode: enabled=%d", enabled); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + + status_t err = OK; + if (!mAllowSynchronousMode && enabled) + return err; + + if (!enabled) { + // going to asynchronous mode, drain the queue + err = drainQueueLocked(); + if (err != NO_ERROR) + return err; + } + + if (mSynchronousMode != enabled) { + // - if we're going to asynchronous mode, the queue is guaranteed to be + // empty here + // - if the client set the number of buffers, we're guaranteed that + // we have at least 3 (because we don't allow less) + mSynchronousMode = enabled; + mDequeueCondition.signal(); + } + return err; +} + +status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); + + sp<FrameAvailableListener> listener; + + { // scope for the lock + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("queueBuffer: slot %d is not owned by the client " + "(state=%d)", buf, mSlots[buf].mBufferState); + return -EINVAL; + } else if (buf == mCurrentTexture) { + ST_LOGE("queueBuffer: slot %d is current!", buf); + return -EINVAL; + } else if (!mSlots[buf].mRequestBufferCalled) { + ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " + "buffer", buf); + return -EINVAL; + } + + if (mSynchronousMode) { + // In synchronous mode we queue all buffers in a FIFO. + mQueue.push_back(buf); + + // Synchronous mode always signals that an additional frame should + // be consumed. + listener = mFrameAvailableListener; + } else { + // In asynchronous mode we only keep the most recent buffer. + if (mQueue.empty()) { + mQueue.push_back(buf); + + // Asynchronous mode only signals that a frame should be + // consumed if no previous frame was pending. If a frame were + // pending then the consumer would have already been notified. + listener = mFrameAvailableListener; + } else { + Fifo::iterator front(mQueue.begin()); + // buffer currently queued is freed + mSlots[*front].mBufferState = BufferSlot::FREE; + // and we record the new buffer index in the queued list + *front = buf; + } + } + + mSlots[buf].mBufferState = BufferSlot::QUEUED; + mSlots[buf].mCrop = mNextCrop; + mSlots[buf].mTransform = mNextTransform; + mSlots[buf].mScalingMode = mNextScalingMode; + mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + + mDequeueCondition.signal(); + + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } // scope for the lock + + // call back without lock held + if (listener != 0) { + listener->onFrameAvailable(); + } + return OK; +} + +void BufferQueue::cancelBuffer(int buf) { + ST_LOGV("cancelBuffer: slot=%d", buf); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGW("cancelBuffer: BufferQueue has been abandoned!"); + return; + } + + if (buf < 0 || buf >= mBufferCount) { + ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return; + } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", + buf, mSlots[buf].mBufferState); + return; + } + mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; + mDequeueCondition.signal(); +} + +status_t BufferQueue::setCrop(const Rect& crop) { + ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, + crop.bottom); + + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setCrop: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextCrop = crop; + return OK; +} + +status_t BufferQueue::setTransform(uint32_t transform) { + ST_LOGV("setTransform: xform=%#x", transform); + Mutex::Autolock lock(mMutex); + if (mAbandoned) { + ST_LOGE("setTransform: BufferQueue has been abandoned!"); + return NO_INIT; + } + mNextTransform = transform; + return OK; +} + +status_t BufferQueue::setScalingMode(int mode) { + ST_LOGV("setScalingMode: mode=%d", mode); + + switch (mode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + break; + default: + ST_LOGE("unknown scaling mode: %d", mode); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mNextScalingMode = mode; + return OK; +} + +status_t BufferQueue::connect(int api, + uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { + ST_LOGV("connect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("connect: BufferQueue has been abandoned!"); + return NO_INIT; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi != NO_CONNECTED_API) { + ST_LOGE("connect: already connected (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } else { + mConnectedApi = api; + *outWidth = mDefaultWidth; + *outHeight = mDefaultHeight; + *outTransform = 0; + } + break; + default: + err = -EINVAL; + break; + } + return err; +} + +status_t BufferQueue::disconnect(int api) { + ST_LOGV("disconnect: api=%d", api); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; + } + + int err = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + drainQueueAndFreeBuffersLocked(); + mConnectedApi = NO_CONNECTED_API; + mNextCrop.makeInvalid(); + mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mNextTransform = 0; + mDequeueCondition.signal(); + } else { + ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } + break; + default: + ST_LOGE("disconnect: unknown API %d", api); + err = -EINVAL; + break; + } + return err; +} + +void BufferQueue::freeBufferLocked(int i) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; + if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + } +} + +void BufferQueue::freeAllBuffersLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersLocked called but mQueue is not empty"); + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } +} + +void BufferQueue::freeAllBuffersExceptHeadLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); + int head = -1; + if (!mQueue.empty()) { + Fifo::iterator front(mQueue.begin()); + head = *front; + } + mCurrentTexture = INVALID_BUFFER_SLOT; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (i != head) { + freeBufferLocked(i); + } + } +} + +status_t BufferQueue::drainQueueLocked() { + while (mSynchronousMode && !mQueue.isEmpty()) { + mDequeueCondition.wait(mMutex); + if (mAbandoned) { + ST_LOGE("drainQueueLocked: BufferQueue has been abandoned!"); + return NO_INIT; + } + if (mConnectedApi == NO_CONNECTED_API) { + ST_LOGE("drainQueueLocked: BufferQueue is not connected!"); + return NO_INIT; + } + } + return NO_ERROR; +} + +status_t BufferQueue::drainQueueAndFreeBuffersLocked() { + status_t err = drainQueueLocked(); + if (err == NO_ERROR) { + if (mSynchronousMode) { + freeAllBuffersLocked(); + } else { + freeAllBuffersExceptHeadLocked(); + } + } + return err; +} + +}; // namespace android diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 3abe84a..be1bcd1 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -38,19 +38,6 @@ #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" -#else -#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 @@ -110,44 +97,22 @@ static float mtxRot270[16] = { static void mtxMul(float out[16], const float a[16], const float b[16]); -// Get an ID that's unique within this process. -static int32_t createProcessUniqueId() { - static volatile int32_t globalCounter = 0; - return android_atomic_inc(&globalCounter); -} SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, GLenum texTarget, bool useFenceSync) : - mDefaultWidth(1), - mDefaultHeight(1), - mPixelFormat(PIXEL_FORMAT_RGBA_8888), - mBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mClientBufferCount(0), - mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS), - mCurrentTexture(INVALID_BUFFER_SLOT), + BufferQueue(allowSynchronousMode), mCurrentTransform(0), mCurrentTimestamp(0), - mNextTransform(0), - mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTexName(tex), - mSynchronousMode(false), - 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. - mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + mTexTarget(texTarget) +{ ST_LOGV("SurfaceTexture"); - sp<ISurfaceComposer> composer(ComposerService::getComposerService()); - mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); - mNextCrop.makeInvalid(); memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); } @@ -157,91 +122,11 @@ SurfaceTexture::~SurfaceTexture() { freeAllBuffersLocked(); } -status_t SurfaceTexture::setBufferCountServerLocked(int bufferCount) { - if (bufferCount > NUM_BUFFER_SLOTS) - return BAD_VALUE; - - // special-case, nothing to do - if (bufferCount == mBufferCount) - return OK; - - if (!mClientBufferCount && - bufferCount >= mBufferCount) { - // easy, we just have more buffers - mBufferCount = bufferCount; - mServerBufferCount = bufferCount; - mDequeueCondition.signal(); - } else { - // we're here because we're either - // - reducing the number of available buffers - // - or there is a client-buffer-count in effect - - // less than 2 buffers is never allowed - if (bufferCount < 2) - return BAD_VALUE; - - // when there is non client-buffer-count in effect, the client is not - // allowed to dequeue more than one buffer at a time, - // so the next time they dequeue a buffer, we know that they don't - // own one. the actual resizing will happen during the next - // dequeueBuffer. - - mServerBufferCount = bufferCount; - } - return OK; -} - status_t SurfaceTexture::setBufferCountServer(int bufferCount) { Mutex::Autolock lock(mMutex); return setBufferCountServerLocked(bufferCount); } -status_t SurfaceTexture::setBufferCount(int bufferCount) { - ST_LOGV("setBufferCount: count=%d", bufferCount); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setBufferCount: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (bufferCount > NUM_BUFFER_SLOTS) { - ST_LOGE("setBufferCount: bufferCount larger than slots available"); - return BAD_VALUE; - } - - // Error out if the user has dequeued buffers - for (int i=0 ; i<mBufferCount ; i++) { - if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { - ST_LOGE("setBufferCount: client owns some buffers"); - return -EINVAL; - } - } - - const int minBufferSlots = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - if (bufferCount == 0) { - mClientBufferCount = 0; - bufferCount = (mServerBufferCount >= minBufferSlots) ? - mServerBufferCount : minBufferSlots; - return setBufferCountServerLocked(bufferCount); - } - - if (bufferCount < minBufferSlots) { - ST_LOGE("setBufferCount: requested buffer count (%d) is less than " - "minimum (%d)", bufferCount, minBufferSlots); - return BAD_VALUE; - } - - // here we're guaranteed that the client doesn't have dequeued buffers - // and will release all of its buffer references. - freeAllBuffersLocked(); - mBufferCount = bufferCount; - mClientBufferCount = bufferCount; - mCurrentTexture = INVALID_BUFFER_SLOT; - mQueue.clear(); - mDequeueCondition.signal(); - return OK; -} status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { @@ -258,496 +143,6 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) return OK; } -status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - ST_LOGV("requestBuffer: slot=%d", slot); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (slot < 0 || mBufferCount <= slot) { - ST_LOGE("requestBuffer: slot index out of range [0, %d]: %d", - mBufferCount, slot); - return BAD_VALUE; - } - mSlots[slot].mRequestBufferCalled = true; - *buf = mSlots[slot].mGraphicBuffer; - return NO_ERROR; -} - -status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, - uint32_t format, uint32_t usage) { - ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); - - if ((w && !h) || (!w && h)) { - ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); - return BAD_VALUE; - } - - 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; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } - - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } - - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; - } - - // if buffer is FREE it CANNOT be current - ALOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", - i); - - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } - } - } else { - if (state == BufferSlot::FREE) { - /* 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; - } - } - } - } - - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " - "setting the buffer count"); - 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. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; - } - } - - // 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); - } - } - - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } - - if (found == INVALID_BUFFER_SLOT) { - // This should not happen. - ST_LOGE("dequeueBuffer: no available buffer slots"); - return -EBUSY; - } - - const int buf = found; - *outBuf = found; - - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } - - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; - } - - // buffer is now in DEQUEUED (but can also be current at the same time, - // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; - } - 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); - mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - } - if (mCurrentTexture == buf) { - // The current texture no longer references the buffer in this slot - // since we just allocated a new buffer. - mCurrentTexture = INVALID_BUFFER_SLOT; - } - 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) { - ALOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); - } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { - ALOGE("dequeueBuffer: timeout waiting for fence"); - } - eglDestroySyncKHR(dpy, fence); - } - - ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, - mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); - - return returnFlags; -} - -status_t SurfaceTexture::setSynchronousMode(bool enabled) { - ST_LOGV("setSynchronousMode: enabled=%d", enabled); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("setSynchronousMode: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - status_t err = OK; - if (!mAllowSynchronousMode && enabled) - return err; - - if (!enabled) { - // going to asynchronous mode, drain the queue - err = drainQueueLocked(); - if (err != NO_ERROR) - return err; - } - - if (mSynchronousMode != enabled) { - // - if we're going to asynchronous mode, the queue is guaranteed to be - // empty here - // - if the client set the number of buffers, we're guaranteed that - // we have at least 3 (because we don't allow less) - mSynchronousMode = enabled; - mDequeueCondition.signal(); - } - return err; -} - -status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); - - sp<FrameAvailableListener> listener; - - { // scope for the lock - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("queueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return -EINVAL; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("queueBuffer: slot %d is not owned by the client " - "(state=%d)", buf, mSlots[buf].mBufferState); - return -EINVAL; - } else if (buf == mCurrentTexture) { - ST_LOGE("queueBuffer: slot %d is current!", buf); - return -EINVAL; - } else if (!mSlots[buf].mRequestBufferCalled) { - ST_LOGE("queueBuffer: slot %d was enqueued without requesting a " - "buffer", buf); - return -EINVAL; - } - - if (mSynchronousMode) { - // In synchronous mode we queue all buffers in a FIFO. - mQueue.push_back(buf); - - // Synchronous mode always signals that an additional frame should - // be consumed. - listener = mFrameAvailableListener; - } else { - // In asynchronous mode we only keep the most recent buffer. - if (mQueue.empty()) { - mQueue.push_back(buf); - - // Asynchronous mode only signals that a frame should be - // consumed if no previous frame was pending. If a frame were - // pending then the consumer would have already been notified. - listener = mFrameAvailableListener; - } else { - Fifo::iterator front(mQueue.begin()); - // buffer currently queued is freed - mSlots[*front].mBufferState = BufferSlot::FREE; - // and we record the new buffer index in the queued list - *front = buf; - } - } - - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mCrop = mNextCrop; - mSlots[buf].mTransform = mNextTransform; - mSlots[buf].mScalingMode = mNextScalingMode; - mSlots[buf].mTimestamp = timestamp; - mFrameCounter++; - mSlots[buf].mFrameNumber = mFrameCounter; - - mDequeueCondition.signal(); - - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } // scope for the lock - - // call back without lock held - if (listener != 0) { - listener->onFrameAvailable(); - } - return OK; -} - -void SurfaceTexture::cancelBuffer(int buf) { - ST_LOGV("cancelBuffer: slot=%d", buf); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); - return; - } - - if (buf < 0 || buf >= mBufferCount) { - ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); - return; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { - ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", - buf, mSlots[buf].mBufferState); - return; - } - mSlots[buf].mBufferState = BufferSlot::FREE; - mSlots[buf].mFrameNumber = 0; - mDequeueCondition.signal(); -} - -status_t SurfaceTexture::setCrop(const Rect& crop) { - ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, - crop.bottom); - - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setCrop: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextCrop = crop; - return OK; -} - -status_t SurfaceTexture::setTransform(uint32_t transform) { - ST_LOGV("setTransform: xform=%#x", transform); - Mutex::Autolock lock(mMutex); - if (mAbandoned) { - ST_LOGE("setTransform: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - mNextTransform = transform; - return OK; -} - -status_t SurfaceTexture::connect(int api, - uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("connect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - ST_LOGE("connect: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi != NO_CONNECTED_API) { - ST_LOGE("connect: already connected (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } else { - mConnectedApi = api; - *outWidth = mDefaultWidth; - *outHeight = mDefaultHeight; - *outTransform = 0; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::disconnect(int api) { - ST_LOGV("disconnect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // it is not really an error to disconnect after the surface - // has been abandoned, it should just be a no-op. - return NO_ERROR; - } - - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); - mConnectedApi = NO_CONNECTED_API; - mNextCrop.makeInvalid(); - mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; - mNextTransform = 0; - mDequeueCondition.signal(); - } else { - ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", - mConnectedApi, api); - err = -EINVAL; - } - break; - default: - ST_LOGE("disconnect: unknown API %d", api); - err = -EINVAL; - break; - } - return err; -} - -status_t SurfaceTexture::setScalingMode(int mode) { - ST_LOGV("setScalingMode: mode=%d", mode); - - switch (mode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - break; - default: - ST_LOGE("unknown scaling mode: %d", mode); - return BAD_VALUE; - } - - Mutex::Autolock lock(mMutex); - mNextScalingMode = mode; - return OK; -} - status_t SurfaceTexture::updateTexImage() { ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -980,69 +375,6 @@ void SurfaceTexture::setFrameAvailableListener( mFrameAvailableListener = listener; } -void SurfaceTexture::freeBufferLocked(int i) { - mSlots[i].mGraphicBuffer = 0; - mSlots[i].mBufferState = BufferSlot::FREE; - mSlots[i].mFrameNumber = 0; - if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); - mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[i].mEglDisplay = EGL_NO_DISPLAY; - } -} - -void SurfaceTexture::freeAllBuffersLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersLocked called but mQueue is not empty"); - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - freeBufferLocked(i); - } -} - -void SurfaceTexture::freeAllBuffersExceptHeadLocked() { - ALOGW_IF(!mQueue.isEmpty(), - "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); - int head = -1; - if (!mQueue.empty()) { - Fifo::iterator front(mQueue.begin()); - head = *front; - } - mCurrentTexture = INVALID_BUFFER_SLOT; - for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (i != head) { - freeBufferLocked(i); - } - } -} - -status_t SurfaceTexture::drainQueueLocked() { - while (mSynchronousMode && !mQueue.isEmpty()) { - mDequeueCondition.wait(mMutex); - if (mAbandoned) { - ST_LOGE("drainQueueLocked: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - if (mConnectedApi == NO_CONNECTED_API) { - ST_LOGE("drainQueueLocked: SurfaceTexture is not connected!"); - return NO_INIT; - } - } - return NO_ERROR; -} - -status_t SurfaceTexture::drainQueueAndFreeBuffersLocked() { - status_t err = drainQueueLocked(); - if (err == NO_ERROR) { - if (mSynchronousMode) { - freeAllBuffersLocked(); - } else { - freeAllBuffersExceptHeadLocked(); - } - } - return err; -} - EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); |