single buffer mode for BufferQueue
Bug: 9891035 Change-Id: Id1ab5f911a6dc4c1d8235e65775b3d3635231ad4
This commit is contained in:
parent
bf5b849ec7
commit
ad678e18b6
@ -340,6 +340,13 @@ public:
|
||||
// The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
|
||||
status_t setDefaultMaxBufferCount(int bufferCount);
|
||||
|
||||
// disableAsyncBuffer disables the extra buffer used in async mode
|
||||
// (when both producer and consumer have set their "isControlledByApp"
|
||||
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
|
||||
//
|
||||
// This can only be called before consumerConnect().
|
||||
status_t disableAsyncBuffer();
|
||||
|
||||
// setMaxAcquiredBufferCount sets the maximum number of buffers that can
|
||||
// be acquired by the consumer at one time (default 1). This call will
|
||||
// fail if a producer is connected to the BufferQueue.
|
||||
@ -364,6 +371,7 @@ public:
|
||||
// NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform).
|
||||
status_t setTransformHint(uint32_t hint);
|
||||
|
||||
|
||||
private:
|
||||
// freeBufferLocked frees the GraphicBuffer and sync resources for the
|
||||
// given slot.
|
||||
@ -559,10 +567,14 @@ private:
|
||||
bool mConsumerControlledByApp;
|
||||
|
||||
// mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block.
|
||||
// this flag is set durring connect() when both consumer and producer are controlled
|
||||
// this flag is set during connect() when both consumer and producer are controlled
|
||||
// by the application.
|
||||
bool mDequeueBufferCannotBlock;
|
||||
|
||||
// mUseAsyncBuffer whether an extra buffer is used in async mode to prevent
|
||||
// dequeueBuffer() from ever blocking.
|
||||
bool mUseAsyncBuffer;
|
||||
|
||||
// mConnectedApi indicates the producer API that is currently connected
|
||||
// to this BufferQueue. It defaults to NO_CONNECTED_API (= 0), and gets
|
||||
// updated by the connect and disconnect methods.
|
||||
|
@ -98,6 +98,13 @@ public:
|
||||
// This calls doGLFenceWait to ensure proper synchronization.
|
||||
status_t updateTexImage();
|
||||
|
||||
// releaseTexImage releases the texture acquired in updateTexImage().
|
||||
// This is intended to be used in single buffer mode.
|
||||
//
|
||||
// This call may only be made while the OpenGL ES context to which the
|
||||
// target texture belongs is bound to the calling thread.
|
||||
status_t releaseTexImage();
|
||||
|
||||
// setReleaseFence stores a fence that will signal when the current buffer
|
||||
// is no longer being read. This fence will be returned to the producer
|
||||
// when the current buffer is released by updateTexImage(). Multiple
|
||||
@ -251,7 +258,7 @@ protected:
|
||||
// This releases the buffer in the slot referenced by mCurrentTexture,
|
||||
// then updates state to refer to the BufferItem, which must be a
|
||||
// newly-acquired buffer.
|
||||
status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item);
|
||||
status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
|
||||
|
||||
// Binds mTexName and the current buffer to mTexTarget. Uses
|
||||
// mCurrentTexture if it's set, mCurrentTextureBuf if not. If the
|
||||
@ -416,6 +423,10 @@ private:
|
||||
// It is set to false by detachFromContext, and then set to true again by
|
||||
// attachToContext.
|
||||
bool mAttached;
|
||||
|
||||
// mReleasedTexImageBuffer is a dummy buffer used when in single buffer
|
||||
// mode and releaseTexImage() has been called
|
||||
sp<GraphicBuffer> mReleasedTexImageBuffer;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -72,6 +72,7 @@ BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) :
|
||||
mOverrideMaxBufferCount(0),
|
||||
mConsumerControlledByApp(false),
|
||||
mDequeueBufferCannotBlock(false),
|
||||
mUseAsyncBuffer(true),
|
||||
mConnectedApi(NO_CONNECTED_API),
|
||||
mAbandoned(false),
|
||||
mFrameCounter(0),
|
||||
@ -100,7 +101,8 @@ BufferQueue::~BufferQueue() {
|
||||
}
|
||||
|
||||
status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) {
|
||||
if (count < 2 || count > NUM_BUFFER_SLOTS)
|
||||
const int minBufferCount = mUseAsyncBuffer ? 2 : 1;
|
||||
if (count < minBufferCount || count > NUM_BUFFER_SLOTS)
|
||||
return BAD_VALUE;
|
||||
|
||||
mDefaultMaxBufferCount = count;
|
||||
@ -1033,6 +1035,17 @@ status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) {
|
||||
return setDefaultMaxBufferCountLocked(bufferCount);
|
||||
}
|
||||
|
||||
status_t BufferQueue::disableAsyncBuffer() {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mMutex);
|
||||
if (mConsumerListener != NULL) {
|
||||
ST_LOGE("disableAsyncBuffer: consumer already connected!");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
mUseAsyncBuffer = false;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
|
||||
ATRACE_CALL();
|
||||
Mutex::Autolock lock(mMutex);
|
||||
@ -1049,8 +1062,17 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
|
||||
}
|
||||
|
||||
int BufferQueue::getMinUndequeuedBufferCount(bool async) const {
|
||||
return (mDequeueBufferCannotBlock || async) ?
|
||||
mMaxAcquiredBufferCount+1 : mMaxAcquiredBufferCount;
|
||||
// if dequeueBuffer is allowed to error out, we don't have to
|
||||
// add an extra buffer.
|
||||
if (!mUseAsyncBuffer)
|
||||
return mMaxAcquiredBufferCount;
|
||||
|
||||
// we're in async mode, or we want to prevent the app to
|
||||
// deadlock itself, we throw-in an extra buffer to guarantee it.
|
||||
if (mDequeueBufferCannotBlock || async)
|
||||
return mMaxAcquiredBufferCount+1;
|
||||
|
||||
return mMaxAcquiredBufferCount;
|
||||
}
|
||||
|
||||
int BufferQueue::getMinMaxBufferCountLocked(bool async) const {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
#include <hardware/hardware.h>
|
||||
|
||||
@ -49,6 +50,12 @@ namespace android {
|
||||
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
|
||||
static const struct {
|
||||
size_t width, height;
|
||||
char const* bits;
|
||||
} kDebugData = { 11, 8,
|
||||
"__X_____X_____X___X_____XXXXXXX___XX_XXX_XX_XXXXXXXXXXXX_XXXXXXX_XX_X_____X_X___XX_XX___" };
|
||||
|
||||
// Transform matrices
|
||||
static float mtxIdentity[16] = {
|
||||
1, 0, 0, 0,
|
||||
@ -154,7 +161,7 @@ status_t GLConsumer::updateTexImage() {
|
||||
}
|
||||
|
||||
// Release the previous buffer.
|
||||
err = releaseAndUpdateLocked(item);
|
||||
err = updateAndReleaseLocked(item);
|
||||
if (err != NO_ERROR) {
|
||||
// We always bind the texture.
|
||||
glBindTexture(mTexTarget, mTexName);
|
||||
@ -165,6 +172,80 @@ status_t GLConsumer::updateTexImage() {
|
||||
return bindTextureImageLocked();
|
||||
}
|
||||
|
||||
|
||||
status_t GLConsumer::releaseTexImage() {
|
||||
ATRACE_CALL();
|
||||
ST_LOGV("releaseTexImage");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
|
||||
if (mAbandoned) {
|
||||
ST_LOGE("releaseTexImage: GLConsumer is abandoned!");
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
// Make sure the EGL state is the same as in previous calls.
|
||||
status_t err = checkAndUpdateEglStateLocked();
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Update the GLConsumer state.
|
||||
int buf = mCurrentTexture;
|
||||
if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
|
||||
|
||||
ST_LOGV("releaseTexImage:(slot=%d", buf);
|
||||
|
||||
// Do whatever sync ops we need to do before releasing the slot.
|
||||
err = syncForReleaseLocked(mEglDisplay);
|
||||
if (err != NO_ERROR) {
|
||||
ST_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
|
||||
mEglDisplay, EGL_NO_SYNC_KHR);
|
||||
if (err < NO_ERROR) {
|
||||
ST_LOGE("releaseTexImage: failed to release buffer: %s (%d)",
|
||||
strerror(-err), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (CC_UNLIKELY(mReleasedTexImageBuffer == NULL)) {
|
||||
// The first time, create the debug texture in case the application
|
||||
// continues to use it.
|
||||
sp<GraphicBuffer> buffer = new GraphicBuffer(11, 8, PIXEL_FORMAT_RGBA_8888,
|
||||
GraphicBuffer::USAGE_SW_WRITE_RARELY);
|
||||
uint32_t* bits;
|
||||
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
|
||||
size_t w = buffer->getStride();
|
||||
size_t h = buffer->getHeight();
|
||||
memset(bits, 0, w*h*4);
|
||||
for (size_t y=0 ; y<kDebugData.height ; y++) {
|
||||
for (size_t x=0 ; x<kDebugData.width ; x++) {
|
||||
bits[x] = (kDebugData.bits[y*11+x] == 'X') ? 0xFF000000 : 0xFFFFFFFF;
|
||||
}
|
||||
bits += w;
|
||||
}
|
||||
buffer->unlock();
|
||||
mReleasedTexImageBuffer = buffer;
|
||||
}
|
||||
|
||||
mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
|
||||
mCurrentTextureBuf = mReleasedTexImageBuffer;
|
||||
mCurrentCrop.makeInvalid();
|
||||
mCurrentTransform = 0;
|
||||
mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
|
||||
mCurrentTimestamp = 0;
|
||||
mCurrentFence = Fence::NO_FENCE;
|
||||
|
||||
// bind a dummy texture
|
||||
glBindTexture(mTexTarget, mTexName);
|
||||
bindUnslottedBufferLocked(mEglDisplay);
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
|
||||
nsecs_t presentWhen) {
|
||||
status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen);
|
||||
@ -202,12 +283,12 @@ status_t GLConsumer::releaseBufferLocked(int buf,
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
|
||||
status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
|
||||
{
|
||||
status_t err = NO_ERROR;
|
||||
|
||||
if (!mAttached) {
|
||||
ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL "
|
||||
ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL "
|
||||
"ES context");
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
@ -230,7 +311,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
|
||||
if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
|
||||
EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d",
|
||||
ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
|
||||
mEglDisplay, buf);
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
@ -249,7 +330,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
|
||||
return err;
|
||||
}
|
||||
|
||||
ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||
ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
|
||||
mCurrentTexture,
|
||||
mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
|
||||
buf, mSlots[buf].mGraphicBuffer->handle);
|
||||
@ -259,8 +340,8 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
|
||||
status_t status = releaseBufferLocked(
|
||||
mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
|
||||
mEglSlots[mCurrentTexture].mEglFence);
|
||||
if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
|
||||
ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
|
||||
if (status < NO_ERROR) {
|
||||
ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
|
||||
strerror(-status), status);
|
||||
err = status;
|
||||
// keep going, with error raised [?]
|
||||
|
@ -108,7 +108,7 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>&
|
||||
// Release the previous buffer.
|
||||
err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
|
||||
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
|
||||
if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) {
|
||||
if (err < NO_ERROR) {
|
||||
ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
|
||||
return err;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter)
|
||||
}
|
||||
|
||||
// Release the previous buffer.
|
||||
err = releaseAndUpdateLocked(item);
|
||||
err = updateAndReleaseLocked(item);
|
||||
if (err != NO_ERROR) {
|
||||
return err;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user