am bb1e7d43: Merge changes I7e973a35,Ib3386fcc into ics-mr1
* commit 'bb1e7d4324ff43491c526c77f405bbbf0cece611': SurfaceTexture: add EGL_KHR_fence_sync option SurfaceTexture: add a blit-to-FBO test
This commit is contained in:
commit
53233c52d0
@ -60,10 +60,16 @@ public:
|
||||
virtual void onFrameAvailable() = 0;
|
||||
};
|
||||
|
||||
// tex indicates the name OpenGL texture to which images are to be streamed.
|
||||
// This texture name cannot be changed once the SurfaceTexture is created.
|
||||
// SurfaceTexture constructs a new SurfaceTexture object. tex indicates the
|
||||
// name of the OpenGL ES texture to which images are to be streamed. This
|
||||
// texture name cannot be changed once the SurfaceTexture is created.
|
||||
// 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,
|
||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES);
|
||||
GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true);
|
||||
|
||||
virtual ~SurfaceTexture();
|
||||
|
||||
@ -276,7 +282,8 @@ private:
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mFrameNumber(0) {
|
||||
mFrameNumber(0),
|
||||
mFence(EGL_NO_SYNC_KHR) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
@ -349,6 +356,11 @@ private:
|
||||
// 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
|
||||
@ -472,6 +484,12 @@ private:
|
||||
// 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.
|
||||
|
@ -36,6 +36,12 @@
|
||||
#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"
|
||||
@ -43,6 +49,16 @@
|
||||
#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
|
||||
// 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
|
||||
#define ST_LOGV(x, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
@ -99,7 +115,7 @@ static int32_t createProcessUniqueId() {
|
||||
}
|
||||
|
||||
SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||
GLenum texTarget) :
|
||||
GLenum texTarget, bool useFenceSync) :
|
||||
mDefaultWidth(1),
|
||||
mDefaultHeight(1),
|
||||
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
|
||||
@ -116,6 +132,11 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||
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.
|
||||
@ -261,195 +282,225 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
status_t returnFlags(OK);
|
||||
EGLDisplay dpy = EGL_NO_DISPLAY;
|
||||
EGLSyncKHR fence = EGL_NO_SYNC_KHR;
|
||||
|
||||
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;
|
||||
}
|
||||
{ // Scope for the lock
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
// 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++;
|
||||
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;
|
||||
}
|
||||
|
||||
// if buffer is FREE it CANNOT be current
|
||||
LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
|
||||
"dequeueBuffer: buffer %d is both FREE and current!", i);
|
||||
// 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.
|
||||
|
||||
if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
|
||||
if (state == BufferSlot::FREE || i == mCurrentTexture) {
|
||||
foundSync = i;
|
||||
if (i != mCurrentTexture) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
} else {
|
||||
if (state == BufferSlot::FREE) {
|
||||
/** For Asynchronous mode, we need to return the oldest of free buffers
|
||||
* There is only one instance when the Framecounter overflows, this logic
|
||||
* might return the earlier buffer to client. Which is a negligible impact
|
||||
**/
|
||||
if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
|
||||
|
||||
// if buffer is FREE it CANNOT be current
|
||||
LOGW_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;
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (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 (found == INVALID_BUFFER_SLOT) {
|
||||
// This should not happen.
|
||||
ST_LOGE("dequeueBuffer: no available buffer slots");
|
||||
return -EBUSY;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
|
||||
const bool updateFormat = (format != 0);
|
||||
if (!updateFormat) {
|
||||
// keep the current (or default) format
|
||||
format = mPixelFormat;
|
||||
}
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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,
|
||||
mSlots[buf].mGraphicBuffer->handle, returnFlags);
|
||||
|
||||
return returnFlags;
|
||||
}
|
||||
|
||||
@ -712,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() {
|
||||
|
||||
// Update the GL texture object.
|
||||
EGLImageKHR image = mSlots[buf].mEglImage;
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
if (mSlots[buf].mGraphicBuffer == 0) {
|
||||
ST_LOGE("buffer at slot %d is null", buf);
|
||||
return BAD_VALUE;
|
||||
@ -746,16 +797,32 @@ status_t SurfaceTexture::updateTexImage() {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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 (mUseFenceSync) {
|
||||
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) {
|
||||
// The current buffer becomes FREE if it was still in the queued
|
||||
// state. If it has already been given to the client
|
||||
// (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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const int texWidth = 64;
|
||||
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
|
||||
|
@ -28,7 +28,7 @@ namespace android {
|
||||
|
||||
|
||||
SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer)
|
||||
: SurfaceTexture(tex), mLayer(layer) {
|
||||
: SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) {
|
||||
}
|
||||
|
||||
SurfaceTextureLayer::~SurfaceTextureLayer() {
|
||||
|
Loading…
Reference in New Issue
Block a user