Merge "Revert "SurfaceTexture: inherit from ConsumerBase"" into jb-mr1-dev
This commit is contained in:
commit
729f48082e
@ -189,6 +189,14 @@ protected:
|
|||||||
// if none is supplied
|
// if none is supplied
|
||||||
sp<BufferQueue> mBufferQueue;
|
sp<BufferQueue> mBufferQueue;
|
||||||
|
|
||||||
|
// mAttached indicates whether the ConsumerBase is currently attached to
|
||||||
|
// an OpenGL ES context. For legacy reasons, this is initialized to true,
|
||||||
|
// indicating that the ConsumerBase is considered to be attached to
|
||||||
|
// whatever context is current at the time of the first updateTexImage call.
|
||||||
|
// It is set to false by detachFromContext, and then set to true again by
|
||||||
|
// attachToContext.
|
||||||
|
bool mAttached;
|
||||||
|
|
||||||
// mMutex is the mutex used to prevent concurrent access to the member
|
// mMutex is the mutex used to prevent concurrent access to the member
|
||||||
// variables of ConsumerBase objects. It must be locked whenever the
|
// variables of ConsumerBase objects. It must be locked whenever the
|
||||||
// member variables are accessed.
|
// member variables are accessed.
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include <gui/ISurfaceTexture.h>
|
#include <gui/ISurfaceTexture.h>
|
||||||
#include <gui/BufferQueue.h>
|
#include <gui/BufferQueue.h>
|
||||||
#include <gui/ConsumerBase.h>
|
|
||||||
|
|
||||||
#include <ui/GraphicBuffer.h>
|
#include <ui/GraphicBuffer.h>
|
||||||
|
|
||||||
@ -40,9 +39,20 @@ namespace android {
|
|||||||
|
|
||||||
class String8;
|
class String8;
|
||||||
|
|
||||||
class SurfaceTexture : public ConsumerBase {
|
class SurfaceTexture : public virtual RefBase,
|
||||||
|
protected BufferQueue::ConsumerListener {
|
||||||
public:
|
public:
|
||||||
typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
|
struct FrameAvailableListener : public virtual RefBase {
|
||||||
|
// onFrameAvailable() is called 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
|
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
||||||
// name of the OpenGL ES texture to which images are to be streamed.
|
// name of the OpenGL ES texture to which images are to be streamed.
|
||||||
@ -72,6 +82,8 @@ public:
|
|||||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
|
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true,
|
||||||
const sp<BufferQueue> &bufferQueue = 0);
|
const sp<BufferQueue> &bufferQueue = 0);
|
||||||
|
|
||||||
|
virtual ~SurfaceTexture();
|
||||||
|
|
||||||
// updateTexImage sets the image contents of the target texture to that of
|
// updateTexImage sets the image contents of the target texture to that of
|
||||||
// the most recently queued buffer.
|
// the most recently queued buffer.
|
||||||
//
|
//
|
||||||
@ -120,6 +132,16 @@ public:
|
|||||||
// documented by the source.
|
// documented by the source.
|
||||||
int64_t getTimestamp();
|
int64_t getTimestamp();
|
||||||
|
|
||||||
|
// setFrameAvailableListener sets the listener object that will be notified
|
||||||
|
// when a new frame becomes available.
|
||||||
|
void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
|
||||||
|
|
||||||
|
// getAllocator retrieves the binder object that must be referenced as long
|
||||||
|
// as the GraphicBuffers dequeued from this SurfaceTexture are referenced.
|
||||||
|
// Holding this binder reference prevents SurfaceFlinger from freeing the
|
||||||
|
// buffers before the client is done with them.
|
||||||
|
sp<IBinder> getAllocator();
|
||||||
|
|
||||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||||
// requestBuffers when a with and height of zero is requested.
|
// requestBuffers when a with and height of zero is requested.
|
||||||
// A call to setDefaultBufferSize() may trigger requestBuffers() to
|
// A call to setDefaultBufferSize() may trigger requestBuffers() to
|
||||||
@ -158,6 +180,17 @@ public:
|
|||||||
// synchronous mode.
|
// synchronous mode.
|
||||||
bool isSynchronousMode() const;
|
bool isSynchronousMode() const;
|
||||||
|
|
||||||
|
// abandon frees all the buffers and puts the SurfaceTexture into the
|
||||||
|
// 'abandoned' state. Once put in this state the SurfaceTexture can never
|
||||||
|
// leave it. When in the 'abandoned' state, all methods of the
|
||||||
|
// ISurfaceTexture interface will fail with the NO_INIT error.
|
||||||
|
//
|
||||||
|
// Note that while calling this method causes all the buffers to be freed
|
||||||
|
// from the perspective of the the SurfaceTexture, if there are additional
|
||||||
|
// references on the buffers (e.g. if a buffer is referenced by a client or
|
||||||
|
// by OpenGL ES as a texture) then those buffer will remain allocated.
|
||||||
|
void abandon();
|
||||||
|
|
||||||
// set the name of the SurfaceTexture that will be used to identify it in
|
// set the name of the SurfaceTexture that will be used to identify it in
|
||||||
// log messages.
|
// log messages.
|
||||||
void setName(const String8& name);
|
void setName(const String8& name);
|
||||||
@ -171,9 +204,7 @@ public:
|
|||||||
|
|
||||||
// getBufferQueue returns the BufferQueue object to which this
|
// getBufferQueue returns the BufferQueue object to which this
|
||||||
// SurfaceTexture is connected.
|
// SurfaceTexture is connected.
|
||||||
sp<BufferQueue> getBufferQueue() const {
|
sp<BufferQueue> getBufferQueue() const;
|
||||||
return mBufferQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// detachFromContext detaches the SurfaceTexture from the calling thread's
|
// detachFromContext detaches the SurfaceTexture from the calling thread's
|
||||||
// current OpenGL ES context. This context must be the same as the context
|
// current OpenGL ES context. This context must be the same as the context
|
||||||
@ -202,25 +233,17 @@ public:
|
|||||||
// current at the time of the last call to detachFromContext.
|
// current at the time of the last call to detachFromContext.
|
||||||
status_t attachToContext(GLuint tex);
|
status_t attachToContext(GLuint tex);
|
||||||
|
|
||||||
|
// dump our state in a String
|
||||||
|
virtual void dump(String8& result) const;
|
||||||
|
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// abandonLocked overrides the ConsumerBase method to clear
|
// Implementation of the BufferQueue::ConsumerListener interface. These
|
||||||
// mCurrentTextureBuf in addition to the ConsumerBase behavior.
|
// calls are used to notify the SurfaceTexture of asynchronous events in the
|
||||||
virtual void abandonLocked();
|
// BufferQueue.
|
||||||
|
virtual void onFrameAvailable();
|
||||||
// dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
|
virtual void onBuffersReleased();
|
||||||
// specific info in addition to the ConsumerBase behavior.
|
|
||||||
virtual void dumpLocked(String8& result, const char* prefix, char* buffer,
|
|
||||||
size_t size) const;
|
|
||||||
|
|
||||||
// acquireBufferLocked overrides the ConsumerBase method to update the
|
|
||||||
// mEglSlots array in addition to the ConsumerBase behavior.
|
|
||||||
virtual status_t acquireBufferLocked(BufferQueue::BufferItem *item);
|
|
||||||
|
|
||||||
// releaseBufferLocked overrides the ConsumerBase method to update the
|
|
||||||
// mEglSlots array in addition to the ConsumerBase.
|
|
||||||
virtual status_t releaseBufferLocked(int buf, EGLDisplay display,
|
|
||||||
EGLSyncKHR eglFence, const sp<Fence>& fence);
|
|
||||||
|
|
||||||
static bool isExternalFormat(uint32_t format);
|
static bool isExternalFormat(uint32_t format);
|
||||||
|
|
||||||
@ -328,9 +351,11 @@ private:
|
|||||||
struct EGLSlot {
|
struct EGLSlot {
|
||||||
EGLSlot()
|
EGLSlot()
|
||||||
: mEglImage(EGL_NO_IMAGE_KHR),
|
: mEglImage(EGL_NO_IMAGE_KHR),
|
||||||
mEglFence(EGL_NO_SYNC_KHR) {
|
mFence(EGL_NO_SYNC_KHR) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sp<GraphicBuffer> mGraphicBuffer;
|
||||||
|
|
||||||
// mEglImage is the EGLImage created from mGraphicBuffer.
|
// mEglImage is the EGLImage created from mGraphicBuffer.
|
||||||
EGLImageKHR mEglImage;
|
EGLImageKHR mEglImage;
|
||||||
|
|
||||||
@ -338,7 +363,14 @@ private:
|
|||||||
// associated with this buffer slot may be dequeued. It is initialized
|
// associated with this buffer slot may be dequeued. It is initialized
|
||||||
// to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
|
// 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.
|
// on a compile-time option) set to a new sync object in updateTexImage.
|
||||||
EGLSyncKHR mEglFence;
|
EGLSyncKHR mFence;
|
||||||
|
|
||||||
|
// mReleaseFence is a fence which will signal when the buffer
|
||||||
|
// associated with this buffer slot is no longer being used by the
|
||||||
|
// consumer and can be overwritten. The buffer can be dequeued before
|
||||||
|
// the fence signals; the producer is responsible for delaying writes
|
||||||
|
// until it signals.
|
||||||
|
sp<Fence> mReleaseFence;
|
||||||
};
|
};
|
||||||
|
|
||||||
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
|
// mEglDisplay is the EGLDisplay with which this SurfaceTexture is currently
|
||||||
@ -360,7 +392,23 @@ private:
|
|||||||
// slot that has not yet been used. The buffer allocated to a slot will also
|
// 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
|
// be replaced if the requested buffer usage or geometry differs from that
|
||||||
// of the buffer allocated to a slot.
|
// of the buffer allocated to a slot.
|
||||||
EGLSlot mEglSlots[BufferQueue::NUM_BUFFER_SLOTS];
|
EGLSlot mEGLSlots[BufferQueue::NUM_BUFFER_SLOTS];
|
||||||
|
|
||||||
|
// 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 SurfaceTexture in log messages.
|
||||||
|
// It can be set by the setName method.
|
||||||
|
String8 mName;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
// mCurrentTexture is the buffer slot index of the buffer that is currently
|
// mCurrentTexture is the buffer slot index of the buffer that is currently
|
||||||
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
|
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
|
||||||
@ -370,13 +418,22 @@ private:
|
|||||||
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
|
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
|
||||||
int mCurrentTexture;
|
int mCurrentTexture;
|
||||||
|
|
||||||
// mAttached indicates whether the ConsumerBase is currently attached to
|
// The SurfaceTexture has-a BufferQueue and is responsible for creating this object
|
||||||
|
// if none is supplied
|
||||||
|
sp<BufferQueue> mBufferQueue;
|
||||||
|
|
||||||
|
// mAttached indicates whether the SurfaceTexture is currently attached to
|
||||||
// an OpenGL ES context. For legacy reasons, this is initialized to true,
|
// an OpenGL ES context. For legacy reasons, this is initialized to true,
|
||||||
// indicating that the ConsumerBase is considered to be attached to
|
// indicating that the SurfaceTexture is considered to be attached to
|
||||||
// whatever context is current at the time of the first updateTexImage call.
|
// whatever context is current at the time of the first updateTexImage call.
|
||||||
// It is set to false by detachFromContext, and then set to true again by
|
// It is set to false by detachFromContext, and then set to true again by
|
||||||
// attachToContext.
|
// attachToContext.
|
||||||
bool mAttached;
|
bool mAttached;
|
||||||
|
|
||||||
|
// mMutex is the mutex used to prevent concurrent access to the member
|
||||||
|
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||||
|
// member variables are accessed.
|
||||||
|
mutable Mutex mMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -53,8 +53,7 @@ static int32_t createProcessUniqueId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
|
ConsumerBase::ConsumerBase(const sp<BufferQueue>& bufferQueue) :
|
||||||
mAbandoned(false),
|
mBufferQueue(bufferQueue) {
|
||||||
mBufferQueue(bufferQueue) {
|
|
||||||
// Choose a name using the PID and a process-unique ID.
|
// Choose a name using the PID and a process-unique ID.
|
||||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||||
|
|
||||||
|
@ -26,8 +26,6 @@
|
|||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <GLES2/gl2ext.h>
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
#include <hardware/hardware.h>
|
|
||||||
|
|
||||||
#include <gui/IGraphicBufferAlloc.h>
|
#include <gui/IGraphicBufferAlloc.h>
|
||||||
#include <gui/ISurfaceComposer.h>
|
#include <gui/ISurfaceComposer.h>
|
||||||
#include <gui/SurfaceComposerClient.h>
|
#include <gui/SurfaceComposerClient.h>
|
||||||
@ -98,10 +96,14 @@ static float mtxRot270[16] = {
|
|||||||
|
|
||||||
static void mtxMul(float out[16], const float a[16], const float b[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,
|
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||||
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
|
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
|
||||||
ConsumerBase(bufferQueue == 0 ? new BufferQueue(allowSynchronousMode) : bufferQueue),
|
|
||||||
mCurrentTransform(0),
|
mCurrentTransform(0),
|
||||||
mCurrentTimestamp(0),
|
mCurrentTimestamp(0),
|
||||||
mFilteringEnabled(true),
|
mFilteringEnabled(true),
|
||||||
@ -114,15 +116,47 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
|||||||
mTexTarget(texTarget),
|
mTexTarget(texTarget),
|
||||||
mEglDisplay(EGL_NO_DISPLAY),
|
mEglDisplay(EGL_NO_DISPLAY),
|
||||||
mEglContext(EGL_NO_CONTEXT),
|
mEglContext(EGL_NO_CONTEXT),
|
||||||
|
mAbandoned(false),
|
||||||
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
|
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
|
||||||
mAttached(true)
|
mAttached(true)
|
||||||
{
|
{
|
||||||
|
// Choose a name using the PID and a process-unique ID.
|
||||||
|
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||||
ST_LOGV("SurfaceTexture");
|
ST_LOGV("SurfaceTexture");
|
||||||
|
if (bufferQueue == 0) {
|
||||||
|
ST_LOGV("Creating a new BufferQueue");
|
||||||
|
mBufferQueue = new BufferQueue(allowSynchronousMode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mBufferQueue = bufferQueue;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(mCurrentTransformMatrix, mtxIdentity,
|
memcpy(mCurrentTransformMatrix, mtxIdentity,
|
||||||
sizeof(mCurrentTransformMatrix));
|
sizeof(mCurrentTransformMatrix));
|
||||||
|
|
||||||
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
|
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
|
||||||
|
// reference once the ctor ends, as that would cause the refcount of 'this'
|
||||||
|
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
|
||||||
|
// that's what we create.
|
||||||
|
wp<BufferQueue::ConsumerListener> listener;
|
||||||
|
sp<BufferQueue::ConsumerListener> proxy;
|
||||||
|
listener = static_cast<BufferQueue::ConsumerListener*>(this);
|
||||||
|
proxy = new BufferQueue::ProxyConsumerListener(listener);
|
||||||
|
|
||||||
|
status_t err = mBufferQueue->consumerConnect(proxy);
|
||||||
|
if (err != NO_ERROR) {
|
||||||
|
ST_LOGE("SurfaceTexture: error connecting to BufferQueue: %s (%d)",
|
||||||
|
strerror(-err), err);
|
||||||
|
} else {
|
||||||
|
mBufferQueue->setConsumerName(mName);
|
||||||
|
mBufferQueue->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceTexture::~SurfaceTexture() {
|
||||||
|
ST_LOGV("~SurfaceTexture");
|
||||||
|
|
||||||
|
abandon();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
|
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
|
||||||
@ -143,42 +177,6 @@ status_t SurfaceTexture::updateTexImage() {
|
|||||||
return SurfaceTexture::updateTexImage(NULL);
|
return SurfaceTexture::updateTexImage(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t SurfaceTexture::acquireBufferLocked(BufferQueue::BufferItem *item) {
|
|
||||||
status_t err = ConsumerBase::acquireBufferLocked(item);
|
|
||||||
if (err != NO_ERROR) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int slot = item->mBuf;
|
|
||||||
if (item->mGraphicBuffer != NULL) {
|
|
||||||
if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
|
|
||||||
eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage);
|
|
||||||
mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
EGLImageKHR image = createImage(mEglDisplay, mSlots[slot].mGraphicBuffer);
|
|
||||||
if (image == EGL_NO_IMAGE_KHR) {
|
|
||||||
return UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
mEglSlots[slot].mEglImage = image;
|
|
||||||
|
|
||||||
return NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t SurfaceTexture::releaseBufferLocked(int buf, EGLDisplay display,
|
|
||||||
EGLSyncKHR eglFence, const sp<Fence>& fence) {
|
|
||||||
status_t err = ConsumerBase::releaseBufferLocked(buf, mEglDisplay,
|
|
||||||
eglFence, fence);
|
|
||||||
|
|
||||||
mEglSlots[mCurrentTexture].mEglFence = EGL_NO_SYNC_KHR;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
|
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
|
||||||
ATRACE_CALL();
|
ATRACE_CALL();
|
||||||
ST_LOGV("updateTexImage");
|
ST_LOGV("updateTexImage");
|
||||||
@ -219,65 +217,97 @@ status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
|
|||||||
|
|
||||||
// In asynchronous mode the list is guaranteed to be one buffer
|
// In asynchronous mode the list is guaranteed to be one buffer
|
||||||
// deep, while in synchronous mode we use the oldest buffer.
|
// deep, while in synchronous mode we use the oldest buffer.
|
||||||
err = acquireBufferLocked(&item);
|
err = mBufferQueue->acquireBuffer(&item);
|
||||||
if (err == NO_ERROR) {
|
if (err == NO_ERROR) {
|
||||||
int buf = item.mBuf;
|
int buf = item.mBuf;
|
||||||
|
// This buffer was newly allocated, so we need to clean up on our side
|
||||||
|
if (item.mGraphicBuffer != NULL) {
|
||||||
|
mEGLSlots[buf].mGraphicBuffer = 0;
|
||||||
|
if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
|
||||||
|
eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
|
||||||
|
mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
|
||||||
|
}
|
||||||
|
mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// we call the rejecter here, in case the caller has a reason to
|
// we call the rejecter here, in case the caller has a reason to
|
||||||
// not accept this buffer. this is used by SurfaceFlinger to
|
// not accept this buffer. this is used by SurfaceFlinger to
|
||||||
// reject buffers which have the wrong size
|
// reject buffers which have the wrong size
|
||||||
if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) {
|
if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) {
|
||||||
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
|
mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
|
||||||
glBindTexture(mTexTarget, mTexName);
|
glBindTexture(mTexTarget, mTexName);
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLint error;
|
// Update the GL texture object. We may have to do this even when
|
||||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
// item.mGraphicBuffer == NULL, if we destroyed the EGLImage when
|
||||||
ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
|
// detaching from a context but the buffer has not been re-allocated.
|
||||||
}
|
EGLImageKHR image = mEGLSlots[buf].mEglImage;
|
||||||
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
EGLImageKHR image = mEglSlots[buf].mEglImage;
|
if (mEGLSlots[buf].mGraphicBuffer == NULL) {
|
||||||
glBindTexture(mTexTarget, mTexName);
|
ST_LOGE("updateTexImage: buffer at slot %d is null", buf);
|
||||||
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
|
err = BAD_VALUE;
|
||||||
|
} else {
|
||||||
while ((error = glGetError()) != GL_NO_ERROR) {
|
image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer);
|
||||||
ST_LOGE("updateTexImage: error binding external texture image %p "
|
mEGLSlots[buf].mEglImage = image;
|
||||||
"(slot %d): %#04x", image, buf, error);
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
err = UNKNOWN_ERROR;
|
// NOTE: if dpy was invalid, createImage() is guaranteed to
|
||||||
|
// fail. so we'd end up here.
|
||||||
|
err = UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == NO_ERROR) {
|
if (err == NO_ERROR) {
|
||||||
err = syncForReleaseLocked(dpy);
|
GLint error;
|
||||||
|
while ((error = glGetError()) != GL_NO_ERROR) {
|
||||||
|
ST_LOGW("updateTexImage: clearing GL error: %#04x", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (err == NO_ERROR) {
|
||||||
|
err = syncForReleaseLocked(dpy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err != NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
// Release the buffer we just acquired. It's not safe to
|
// Release the buffer we just acquired. It's not safe to
|
||||||
// release the old buffer, so instead we just drop the new frame.
|
// release the old buffer, so instead we just drop the new frame.
|
||||||
releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
|
mBufferQueue->releaseBuffer(buf, dpy, EGL_NO_SYNC_KHR, item.mFence);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||||
mCurrentTexture,
|
mCurrentTexture,
|
||||||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
||||||
buf, mSlots[buf].mGraphicBuffer->handle);
|
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
|
||||||
|
|
||||||
// release old buffer
|
// release old buffer
|
||||||
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
status_t status = releaseBufferLocked(mCurrentTexture, dpy,
|
status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy,
|
||||||
mEglSlots[mCurrentTexture].mEglFence,
|
mEGLSlots[mCurrentTexture].mFence,
|
||||||
mSlots[mCurrentTexture].mFence);
|
mEGLSlots[mCurrentTexture].mReleaseFence);
|
||||||
if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
|
mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR;
|
||||||
ST_LOGE("updateTexImage: failed to release buffer: %s (%d)",
|
mEGLSlots[mCurrentTexture].mReleaseFence.clear();
|
||||||
strerror(-status), status);
|
if (status == BufferQueue::STALE_BUFFER_SLOT) {
|
||||||
|
freeBufferLocked(mCurrentTexture);
|
||||||
|
} else if (status != NO_ERROR) {
|
||||||
|
ST_LOGE("updateTexImage: released invalid buffer");
|
||||||
err = status;
|
err = status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the SurfaceTexture state.
|
// Update the SurfaceTexture state.
|
||||||
mCurrentTexture = buf;
|
mCurrentTexture = buf;
|
||||||
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
|
mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
|
||||||
mCurrentCrop = item.mCrop;
|
mCurrentCrop = item.mCrop;
|
||||||
mCurrentTransform = item.mTransform;
|
mCurrentTransform = item.mTransform;
|
||||||
mCurrentScalingMode = item.mScalingMode;
|
mCurrentScalingMode = item.mScalingMode;
|
||||||
@ -300,20 +330,20 @@ void SurfaceTexture::setReleaseFence(int fenceFd) {
|
|||||||
sp<Fence> fence(new Fence(fenceFd));
|
sp<Fence> fence(new Fence(fenceFd));
|
||||||
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
|
if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
|
||||||
return;
|
return;
|
||||||
if (!mSlots[mCurrentTexture].mFence.get()) {
|
if (!mEGLSlots[mCurrentTexture].mReleaseFence.get()) {
|
||||||
mSlots[mCurrentTexture].mFence = fence;
|
mEGLSlots[mCurrentTexture].mReleaseFence = fence;
|
||||||
} else {
|
} else {
|
||||||
sp<Fence> mergedFence = Fence::merge(
|
sp<Fence> mergedFence = Fence::merge(
|
||||||
String8("SurfaceTexture merged release"),
|
String8("SurfaceTexture merged release"),
|
||||||
mSlots[mCurrentTexture].mFence, fence);
|
mEGLSlots[mCurrentTexture].mReleaseFence, fence);
|
||||||
if (!mergedFence.get()) {
|
if (!mergedFence.get()) {
|
||||||
ST_LOGE("failed to merge release fences");
|
ST_LOGE("failed to merge release fences");
|
||||||
// synchronization is broken, the best we can do is hope fences
|
// synchronization is broken, the best we can do is hope fences
|
||||||
// signal in order so the new fence will act like a union
|
// signal in order so the new fence will act like a union
|
||||||
mSlots[mCurrentTexture].mFence = fence;
|
mEGLSlots[mCurrentTexture].mReleaseFence = fence;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mSlots[mCurrentTexture].mFence = mergedFence;
|
mEGLSlots[mCurrentTexture].mReleaseFence = mergedFence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,10 +390,10 @@ status_t SurfaceTexture::detachFromContext() {
|
|||||||
// SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
|
// SurfaceTexture gets attached to a new OpenGL ES context (and thus gets a
|
||||||
// new EGLDisplay).
|
// new EGLDisplay).
|
||||||
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||||
EGLImageKHR img = mEglSlots[i].mEglImage;
|
EGLImageKHR img = mEGLSlots[i].mEglImage;
|
||||||
if (img != EGL_NO_IMAGE_KHR) {
|
if (img != EGL_NO_IMAGE_KHR) {
|
||||||
eglDestroyImageKHR(mEglDisplay, img);
|
eglDestroyImageKHR(mEglDisplay, img);
|
||||||
mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
|
mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,7 +481,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
|
|||||||
ST_LOGV("syncForReleaseLocked");
|
ST_LOGV("syncForReleaseLocked");
|
||||||
|
|
||||||
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
if (mUseFenceSync && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||||
EGLSyncKHR fence = mEglSlots[mCurrentTexture].mEglFence;
|
EGLSyncKHR fence = mEGLSlots[mCurrentTexture].mFence;
|
||||||
if (fence != EGL_NO_SYNC_KHR) {
|
if (fence != EGL_NO_SYNC_KHR) {
|
||||||
// There is already a fence for the current slot. We need to wait
|
// There is already a fence for the current slot. We need to wait
|
||||||
// on that before replacing it with another fence to ensure that all
|
// on that before replacing it with another fence to ensure that all
|
||||||
@ -479,7 +509,7 @@ status_t SurfaceTexture::syncForReleaseLocked(EGLDisplay dpy) {
|
|||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
}
|
}
|
||||||
glFlush();
|
glFlush();
|
||||||
mEglSlots[mCurrentTexture].mEglFence = fence;
|
mEGLSlots[mCurrentTexture].mFence = fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@ -577,12 +607,10 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
|
|||||||
// only need to shrink by a half a pixel.
|
// only need to shrink by a half a pixel.
|
||||||
shrinkAmount = 0.5;
|
shrinkAmount = 0.5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// If we don't recognize the format, we must assume the
|
// If we don't recognize the format, we must assume the
|
||||||
// worst case (that we care about), which is YUV420.
|
// worst case (that we care about), which is YUV420.
|
||||||
shrinkAmount = 1.0;
|
shrinkAmount = 1.0;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,6 +650,13 @@ nsecs_t SurfaceTexture::getTimestamp() {
|
|||||||
return mCurrentTimestamp;
|
return mCurrentTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SurfaceTexture::setFrameAvailableListener(
|
||||||
|
const sp<FrameAvailableListener>& listener) {
|
||||||
|
ST_LOGV("setFrameAvailableListener");
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
mFrameAvailableListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
|
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
|
||||||
const sp<GraphicBuffer>& graphicBuffer) {
|
const sp<GraphicBuffer>& graphicBuffer) {
|
||||||
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
|
EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
|
||||||
@ -701,21 +736,35 @@ bool SurfaceTexture::isSynchronousMode() const {
|
|||||||
|
|
||||||
void SurfaceTexture::freeBufferLocked(int slotIndex) {
|
void SurfaceTexture::freeBufferLocked(int slotIndex) {
|
||||||
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
|
ST_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
|
||||||
|
mEGLSlots[slotIndex].mGraphicBuffer = 0;
|
||||||
if (slotIndex == mCurrentTexture) {
|
if (slotIndex == mCurrentTexture) {
|
||||||
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
||||||
}
|
}
|
||||||
EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
|
EGLImageKHR img = mEGLSlots[slotIndex].mEglImage;
|
||||||
if (img != EGL_NO_IMAGE_KHR) {
|
if (img != EGL_NO_IMAGE_KHR) {
|
||||||
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
|
ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
|
||||||
eglDestroyImageKHR(mEglDisplay, img);
|
eglDestroyImageKHR(mEglDisplay, img);
|
||||||
}
|
}
|
||||||
mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
|
mEGLSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceTexture::abandonLocked() {
|
void SurfaceTexture::abandon() {
|
||||||
ST_LOGV("abandonLocked");
|
ST_LOGV("abandon");
|
||||||
mCurrentTextureBuf.clear();
|
Mutex::Autolock lock(mMutex);
|
||||||
ConsumerBase::abandonLocked();
|
|
||||||
|
if (!mAbandoned) {
|
||||||
|
mAbandoned = true;
|
||||||
|
mCurrentTextureBuf.clear();
|
||||||
|
|
||||||
|
// destroy all egl buffers
|
||||||
|
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||||
|
freeBufferLocked(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disconnect from the BufferQueue
|
||||||
|
mBufferQueue->consumerDisconnect();
|
||||||
|
mBufferQueue.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceTexture::setName(const String8& name) {
|
void SurfaceTexture::setName(const String8& name) {
|
||||||
@ -747,18 +796,71 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) {
|
|||||||
return mBufferQueue->setSynchronousMode(enabled);
|
return mBufferQueue->setSynchronousMode(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SurfaceTexture::dumpLocked(String8& result, const char* prefix,
|
// Used for refactoring, should not be in final interface
|
||||||
char* buffer, size_t size) const
|
sp<BufferQueue> SurfaceTexture::getBufferQueue() const {
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
return mBufferQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurfaceTexture::onFrameAvailable() {
|
||||||
|
ST_LOGV("onFrameAvailable");
|
||||||
|
|
||||||
|
sp<FrameAvailableListener> listener;
|
||||||
|
{ // scope for the lock
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
listener = mFrameAvailableListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener != NULL) {
|
||||||
|
ST_LOGV("actually calling onFrameAvailable");
|
||||||
|
listener->onFrameAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurfaceTexture::onBuffersReleased() {
|
||||||
|
ST_LOGV("onBuffersReleased");
|
||||||
|
|
||||||
|
Mutex::Autolock lock(mMutex);
|
||||||
|
|
||||||
|
if (mAbandoned) {
|
||||||
|
// Nothing to do if we're already abandoned.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mask = 0;
|
||||||
|
mBufferQueue->getReleasedBuffers(&mask);
|
||||||
|
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
|
||||||
|
if (mask & (1 << i)) {
|
||||||
|
freeBufferLocked(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurfaceTexture::dump(String8& result) const
|
||||||
{
|
{
|
||||||
snprintf(buffer, size,
|
char buffer[1024];
|
||||||
"%smTexName=%d mCurrentTexture=%d\n"
|
dump(result, "", buffer, 1024);
|
||||||
"%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
|
}
|
||||||
prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
|
|
||||||
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
|
void SurfaceTexture::dump(String8& result, const char* prefix,
|
||||||
mCurrentTransform);
|
char* buffer, size_t SIZE) const
|
||||||
|
{
|
||||||
|
Mutex::Autolock _l(mMutex);
|
||||||
|
snprintf(buffer, SIZE, "%smTexName=%d, mAbandoned=%d\n", prefix, mTexName,
|
||||||
|
int(mAbandoned));
|
||||||
result.append(buffer);
|
result.append(buffer);
|
||||||
|
|
||||||
ConsumerBase::dumpLocked(result, prefix, buffer, size);
|
snprintf(buffer, SIZE,
|
||||||
|
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n",
|
||||||
|
prefix, mCurrentCrop.left,
|
||||||
|
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
|
||||||
|
mCurrentTransform, mCurrentTexture
|
||||||
|
);
|
||||||
|
result.append(buffer);
|
||||||
|
|
||||||
|
if (!mAbandoned) {
|
||||||
|
mBufferQueue->dump(result, prefix, buffer, SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtxMul(float out[16], const float a[16], const float b[16]) {
|
static void mtxMul(float out[16], const float a[16], const float b[16]) {
|
||||||
|
Loading…
Reference in New Issue
Block a user