am 55d3880e: am bb1e7d43: Merge changes I7e973a35,Ib3386fcc into ics-mr1
* commit '55d3880eed3450748eb7b97281e030902ee29c2a': SurfaceTexture: add EGL_KHR_fence_sync option SurfaceTexture: add a blit-to-FBO test
This commit is contained in:
commit
a4fbecd119
|
@ -60,10 +60,16 @@ public:
|
||||||
virtual void onFrameAvailable() = 0;
|
virtual void onFrameAvailable() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// tex indicates the name OpenGL texture to which images are to be streamed.
|
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
||||||
// This texture name cannot be changed once the SurfaceTexture is created.
|
// name of the OpenGL ES texture to which images are to be streamed. This
|
||||||
|
// texture name cannot be changed once the SurfaceTexture is created.
|
||||||
|
// allowSynchronousMode specifies whether or not synchronous mode can be
|
||||||
|
// enabled. texTarget specifies the OpenGL ES texture target to which the
|
||||||
|
// texture will be bound in updateTexImage. useFenceSync specifies whether
|
||||||
|
// fences should be used to synchronize access to buffers if that behavior
|
||||||
|
// is enabled at compile-time.
|
||||||
SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
|
SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
|
||||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES);
|
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true);
|
||||||
|
|
||||||
virtual ~SurfaceTexture();
|
virtual ~SurfaceTexture();
|
||||||
|
|
||||||
|
@ -276,7 +282,8 @@ private:
|
||||||
mTransform(0),
|
mTransform(0),
|
||||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||||
mTimestamp(0),
|
mTimestamp(0),
|
||||||
mFrameNumber(0) {
|
mFrameNumber(0),
|
||||||
|
mFence(EGL_NO_SYNC_KHR) {
|
||||||
mCrop.makeInvalid();
|
mCrop.makeInvalid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +356,11 @@ private:
|
||||||
// mFrameNumber is the number of the queued frame for this slot.
|
// mFrameNumber is the number of the queued frame for this slot.
|
||||||
uint64_t mFrameNumber;
|
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
|
// mSlots is the array of buffer slots that must be mirrored on the client
|
||||||
|
@ -472,6 +484,12 @@ private:
|
||||||
// It is set by the setName method.
|
// It is set by the setName method.
|
||||||
String8 mName;
|
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
|
// mMutex is the mutex used to prevent concurrent access to the member
|
||||||
// variables of SurfaceTexture objects. It must be locked whenever the
|
// variables of SurfaceTexture objects. It must be locked whenever the
|
||||||
// member variables are accessed.
|
// member variables are accessed.
|
||||||
|
|
|
@ -38,6 +38,12 @@
|
||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
#include <utils/String8.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
|
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
|
||||||
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
|
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
|
||||||
#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
|
#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
|
||||||
|
@ -45,6 +51,16 @@
|
||||||
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false
|
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false
|
||||||
#endif
|
#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
|
||||||
|
// allowing the buffer to be dequeued.
|
||||||
|
#ifdef USE_FENCE_SYNC
|
||||||
|
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
|
||||||
|
#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// Macros for including the SurfaceTexture name in log messages
|
// Macros for including the SurfaceTexture name in log messages
|
||||||
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
|
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||||
#define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
|
#define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||||
|
@ -101,7 +117,7 @@ static int32_t createProcessUniqueId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||||
GLenum texTarget) :
|
GLenum texTarget, bool useFenceSync) :
|
||||||
mDefaultWidth(1),
|
mDefaultWidth(1),
|
||||||
mDefaultHeight(1),
|
mDefaultHeight(1),
|
||||||
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
|
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
|
||||||
|
@ -118,6 +134,11 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||||
mAllowSynchronousMode(allowSynchronousMode),
|
mAllowSynchronousMode(allowSynchronousMode),
|
||||||
mConnectedApi(NO_CONNECTED_API),
|
mConnectedApi(NO_CONNECTED_API),
|
||||||
mAbandoned(false),
|
mAbandoned(false),
|
||||||
|
#ifdef USE_FENCE_SYNC
|
||||||
|
mUseFenceSync(useFenceSync),
|
||||||
|
#else
|
||||||
|
mUseFenceSync(false),
|
||||||
|
#endif
|
||||||
mTexTarget(texTarget),
|
mTexTarget(texTarget),
|
||||||
mFrameCounter(0) {
|
mFrameCounter(0) {
|
||||||
// Choose a name using the PID and a process-unique ID.
|
// Choose a name using the PID and a process-unique ID.
|
||||||
|
@ -263,195 +284,225 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Autolock lock(mMutex);
|
|
||||||
|
|
||||||
status_t returnFlags(OK);
|
status_t returnFlags(OK);
|
||||||
|
EGLDisplay dpy = EGL_NO_DISPLAY;
|
||||||
|
EGLSyncKHR fence = EGL_NO_SYNC_KHR;
|
||||||
|
|
||||||
int found = -1;
|
{ // Scope for the lock
|
||||||
int foundSync = -1;
|
Mutex::Autolock lock(mMutex);
|
||||||
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
|
int found = -1;
|
||||||
// needs to change.
|
int foundSync = -1;
|
||||||
//
|
int dequeuedCount = 0;
|
||||||
// The condition "number of buffers needs to change" is true if
|
bool tryAgain = true;
|
||||||
// - the client doesn't care about how many buffers there are
|
while (tryAgain) {
|
||||||
// - AND the actual number of buffer is different from what was
|
if (mAbandoned) {
|
||||||
// set in the last setBufferCountServer()
|
ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
|
||||||
// - OR -
|
return NO_INIT;
|
||||||
// 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
|
// We need to wait for the FIFO to drain if the number of buffer
|
||||||
LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
|
// needs to change.
|
||||||
"dequeueBuffer: buffer %d is both FREE and current!", i);
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
|
const int minBufferCountNeeded = mSynchronousMode ?
|
||||||
if (state == BufferSlot::FREE || i == mCurrentTexture) {
|
MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
|
||||||
foundSync = i;
|
|
||||||
if (i != mCurrentTexture) {
|
const bool numberOfBuffersNeedsToChange = !mClientBufferCount &&
|
||||||
found = i;
|
((mServerBufferCount != mBufferCount) ||
|
||||||
break;
|
(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++;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (state == BufferSlot::FREE) {
|
// if buffer is FREE it CANNOT be current
|
||||||
/** For Asynchronous mode, we need to return the oldest of free buffers
|
LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
|
||||||
* There is only one instance when the Framecounter overflows, this logic
|
"dequeueBuffer: buffer %d is both FREE and current!",
|
||||||
* might return the earlier buffer to client. Which is a negligible impact
|
i);
|
||||||
**/
|
|
||||||
if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
|
if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
|
||||||
|
if (state == BufferSlot::FREE || i == mCurrentTexture) {
|
||||||
foundSync = i;
|
foundSync = i;
|
||||||
found = 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
|
// clients are not allowed to dequeue more than one buffer
|
||||||
// if they didn't set a buffer count.
|
// if they didn't set a buffer count.
|
||||||
if (!mClientBufferCount && dequeuedCount) {
|
if (!mClientBufferCount && dequeuedCount) {
|
||||||
ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
|
ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
|
||||||
"setting the buffer count");
|
"setting the buffer count");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See whether a buffer has been queued since the last setBufferCount so
|
// See whether a buffer has been queued since the last
|
||||||
// we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below.
|
// setBufferCount so we know whether to perform the
|
||||||
bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
|
// MIN_UNDEQUEUED_BUFFERS check below.
|
||||||
if (bufferHasBeenQueued) {
|
bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT;
|
||||||
// make sure the client is not trying to dequeue more buffers
|
if (bufferHasBeenQueued) {
|
||||||
// than allowed.
|
// make sure the client is not trying to dequeue more buffers
|
||||||
const int avail = mBufferCount - (dequeuedCount+1);
|
// than allowed.
|
||||||
if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
|
const int avail = mBufferCount - (dequeuedCount+1);
|
||||||
ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
|
if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) {
|
||||||
"(dequeued=%d)",
|
ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
|
||||||
MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
|
"(dequeued=%d)",
|
||||||
dequeuedCount);
|
MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode),
|
||||||
return -EBUSY;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're in synchronous mode and didn't find a buffer, we need to wait
|
if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
|
||||||
// for some buffers to be consumed
|
// foundSync guaranteed to be != INVALID_BUFFER_SLOT
|
||||||
tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT);
|
found = foundSync;
|
||||||
if (tryAgain) {
|
|
||||||
mDequeueCondition.wait(mMutex);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (mSynchronousMode && found == INVALID_BUFFER_SLOT) {
|
if (found == INVALID_BUFFER_SLOT) {
|
||||||
// foundSync guaranteed to be != INVALID_BUFFER_SLOT
|
// This should not happen.
|
||||||
found = foundSync;
|
ST_LOGE("dequeueBuffer: no available buffer slots");
|
||||||
}
|
return -EBUSY;
|
||||||
|
|
||||||
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;
|
const int buf = found;
|
||||||
|
*outBuf = found;
|
||||||
|
|
||||||
|
const bool useDefaultSize = !w && !h;
|
||||||
|
if (useDefaultSize) {
|
||||||
|
// use the default size
|
||||||
|
w = mDefaultWidth;
|
||||||
|
h = mDefaultHeight;
|
||||||
}
|
}
|
||||||
mSlots[buf].mGraphicBuffer = graphicBuffer;
|
|
||||||
mSlots[buf].mRequestBufferCalled = false;
|
const bool updateFormat = (format != 0);
|
||||||
if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
|
if (!updateFormat) {
|
||||||
eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
|
// keep the current (or default) format
|
||||||
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
|
format = mPixelFormat;
|
||||||
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
|
|
||||||
}
|
}
|
||||||
if (mCurrentTexture == buf) {
|
|
||||||
// The current texture no longer references the buffer in this slot
|
// buffer is now in DEQUEUED (but can also be current at the same time,
|
||||||
// since we just allocated a new buffer.
|
// if we're in synchronous mode)
|
||||||
mCurrentTexture = INVALID_BUFFER_SLOT;
|
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;
|
||||||
}
|
}
|
||||||
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) {
|
||||||
|
LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError());
|
||||||
|
} else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
|
||||||
|
LOGE("dequeueBuffer: timeout waiting for fence");
|
||||||
|
}
|
||||||
|
eglDestroySyncKHR(dpy, fence);
|
||||||
|
}
|
||||||
|
|
||||||
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
|
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
|
||||||
mSlots[buf].mGraphicBuffer->handle, returnFlags);
|
mSlots[buf].mGraphicBuffer->handle, returnFlags);
|
||||||
|
|
||||||
return returnFlags;
|
return returnFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,8 +765,8 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
|
|
||||||
// Update the GL texture object.
|
// Update the GL texture object.
|
||||||
EGLImageKHR image = mSlots[buf].mEglImage;
|
EGLImageKHR image = mSlots[buf].mEglImage;
|
||||||
|
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||||
if (image == EGL_NO_IMAGE_KHR) {
|
if (image == EGL_NO_IMAGE_KHR) {
|
||||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
|
||||||
if (mSlots[buf].mGraphicBuffer == 0) {
|
if (mSlots[buf].mGraphicBuffer == 0) {
|
||||||
ST_LOGE("buffer at slot %d is null", buf);
|
ST_LOGE("buffer at slot %d is null", buf);
|
||||||
return BAD_VALUE;
|
return BAD_VALUE;
|
||||||
|
@ -748,16 +799,32 @@ status_t SurfaceTexture::updateTexImage() {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
|
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
|
||||||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
|
if (mUseFenceSync) {
|
||||||
mSlots[buf].mGraphicBuffer->handle);
|
EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR,
|
||||||
|
NULL);
|
||||||
|
if (fence == EGL_NO_SYNC_KHR) {
|
||||||
|
LOGE("updateTexImage: error creating fence: %#x",
|
||||||
|
eglGetError());
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
glFlush();
|
||||||
|
mSlots[mCurrentTexture].mFence = fence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||||
|
mCurrentTexture,
|
||||||
|
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
||||||
|
buf, mSlots[buf].mGraphicBuffer->handle);
|
||||||
|
|
||||||
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
|
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
|
||||||
// The current buffer becomes FREE if it was still in the queued
|
// The current buffer becomes FREE if it was still in the queued
|
||||||
// state. If it has already been given to the client
|
// state. If it has already been given to the client
|
||||||
// (synchronous mode), then it stays in DEQUEUED state.
|
// (synchronous mode), then it stays in DEQUEUED state.
|
||||||
if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
|
if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) {
|
||||||
mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
|
mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the SurfaceTexture state.
|
// Update the SurfaceTexture state.
|
||||||
|
|
|
@ -536,6 +536,20 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r,
|
||||||
|
uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
const size_t PIXEL_SIZE = 4;
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
for (int x = 0; x < h; x++) {
|
||||||
|
off_t offset = (y * stride + x) * PIXEL_SIZE;
|
||||||
|
buf[offset + 0] = r;
|
||||||
|
buf[offset + 1] = g;
|
||||||
|
buf[offset + 2] = b;
|
||||||
|
buf[offset + 3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
|
TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) {
|
||||||
const int texWidth = 64;
|
const int texWidth = 64;
|
||||||
const int texHeight = 66;
|
const int texHeight = 66;
|
||||||
|
@ -1616,4 +1630,101 @@ TEST_F(SurfaceTextureGLThreadToGLTest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SurfaceTextureFBOTest : public SurfaceTextureGLTest {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void SetUp() {
|
||||||
|
SurfaceTextureGLTest::SetUp();
|
||||||
|
|
||||||
|
glGenFramebuffers(1, &mFbo);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
|
||||||
|
glGenTextures(1, &mFboTex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mFboTex);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(),
|
||||||
|
getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_TEXTURE_2D, mFboTex, 0);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() {
|
||||||
|
SurfaceTextureGLTest::TearDown();
|
||||||
|
|
||||||
|
glDeleteTextures(1, &mFboTex);
|
||||||
|
glDeleteFramebuffers(1, &mFbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint mFbo;
|
||||||
|
GLuint mFboTex;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This test is intended to verify that proper synchronization is done when
|
||||||
|
// rendering into an FBO.
|
||||||
|
TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) {
|
||||||
|
const int texWidth = 64;
|
||||||
|
const int texHeight = 64;
|
||||||
|
|
||||||
|
ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
|
||||||
|
texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888));
|
||||||
|
ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
|
||||||
|
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN));
|
||||||
|
|
||||||
|
android_native_buffer_t* anb;
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
|
||||||
|
ASSERT_TRUE(anb != NULL);
|
||||||
|
|
||||||
|
sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
|
||||||
|
|
||||||
|
// Fill the buffer with green
|
||||||
|
uint8_t* img = NULL;
|
||||||
|
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
|
||||||
|
fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255,
|
||||||
|
0, 255);
|
||||||
|
buf->unlock();
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
|
||||||
|
|
||||||
|
ASSERT_EQ(NO_ERROR, mST->updateTexImage());
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
|
||||||
|
drawTexture();
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
SCOPED_TRACE(String8::format("frame %d", i).string());
|
||||||
|
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
|
||||||
|
ASSERT_TRUE(anb != NULL);
|
||||||
|
|
||||||
|
buf = new GraphicBuffer(anb, false);
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(),
|
||||||
|
buf->getNativeBuffer()));
|
||||||
|
|
||||||
|
// Fill the buffer with red
|
||||||
|
ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN,
|
||||||
|
(void**)(&img)));
|
||||||
|
fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0,
|
||||||
|
0, 255);
|
||||||
|
ASSERT_EQ(NO_ERROR, buf->unlock());
|
||||||
|
ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(),
|
||||||
|
buf->getNativeBuffer()));
|
||||||
|
|
||||||
|
ASSERT_EQ(NO_ERROR, mST->updateTexImage());
|
||||||
|
|
||||||
|
drawTexture();
|
||||||
|
|
||||||
|
EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
|
||||||
|
|
||||||
|
EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace android {
|
||||||
|
|
||||||
|
|
||||||
SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer)
|
SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer)
|
||||||
: SurfaceTexture(tex), mLayer(layer) {
|
: SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceTextureLayer::~SurfaceTextureLayer() {
|
SurfaceTextureLayer::~SurfaceTextureLayer() {
|
||||||
|
|
Loading…
Reference in New Issue