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;
|
||||
};
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -38,6 +38,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"
|
||||
|
@ -45,6 +51,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, ...) ALOGV("[%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,
|
||||
GLenum texTarget) :
|
||||
GLenum texTarget, bool useFenceSync) :
|
||||
mDefaultWidth(1),
|
||||
mDefaultHeight(1),
|
||||
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
|
||||
|
@ -118,6 +134,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.
|
||||
|
@ -263,9 +284,12 @@ 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;
|
||||
|
||||
{ // Scope for the lock
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
int found = -1;
|
||||
int foundSync = -1;
|
||||
|
@ -329,7 +353,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
|
||||
// if buffer is FREE it CANNOT be current
|
||||
LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
|
||||
"dequeueBuffer: buffer %d is both FREE and current!", i);
|
||||
"dequeueBuffer: buffer %d is both FREE and current!",
|
||||
i);
|
||||
|
||||
if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
|
||||
if (state == BufferSlot::FREE || i == mCurrentTexture) {
|
||||
|
@ -341,11 +366,14 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
}
|
||||
} 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) {
|
||||
/* 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;
|
||||
}
|
||||
|
@ -361,8 +389,9 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
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.
|
||||
// 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
|
||||
|
@ -377,8 +406,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
}
|
||||
}
|
||||
|
||||
// we're in synchronous mode and didn't find a buffer, we need to wait
|
||||
// for some buffers to be consumed
|
||||
// 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);
|
||||
|
@ -438,8 +467,10 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
}
|
||||
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);
|
||||
eglDestroyImageKHR(mSlots[buf].mEglDisplay,
|
||||
mSlots[buf].mEglImage);
|
||||
mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
|
@ -450,8 +481,28 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -714,8 +765,8 @@ status_t SurfaceTexture::updateTexImage() {
|
|||
|
||||
// Update the GL texture object.
|
||||
EGLImageKHR image = mSlots[buf].mEglImage;
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
if (mSlots[buf].mGraphicBuffer == 0) {
|
||||
ST_LOGE("buffer at slot %d is null", buf);
|
||||
return BAD_VALUE;
|
||||
|
@ -748,17 +799,33 @@ 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.
|
||||
mCurrentTexture = buf;
|
||||
|
|
|
@ -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