Merge "Removed dependecies between BufferQueue and SurfaceTexture"
This commit is contained in:
commit
8b82aff67c
@ -40,6 +40,7 @@ public:
|
||||
};
|
||||
enum { NUM_BUFFER_SLOTS = 32 };
|
||||
enum { NO_CONNECTED_API = 0 };
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
|
||||
struct FrameAvailableListener : public virtual RefBase {
|
||||
// onFrameAvailable() is called from queueBuffer() each time an
|
||||
@ -119,8 +120,91 @@ public:
|
||||
// connected to the specified client API.
|
||||
virtual status_t disconnect(int api);
|
||||
|
||||
protected:
|
||||
// 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;
|
||||
|
||||
// public facing structure for BufferSlot
|
||||
struct BufferItem {
|
||||
|
||||
BufferItem()
|
||||
:
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mFrameNumber(0),
|
||||
mBuf(INVALID_BUFFER_SLOT) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mCrop is the current crop rectangle for this buffer slot. This gets
|
||||
// set to mNextCrop each time queueBuffer gets called for this buffer.
|
||||
Rect mCrop;
|
||||
|
||||
// mTransform is the current transform flags for this buffer slot. This
|
||||
// gets set to mNextTransform each time queueBuffer gets called for this
|
||||
// slot.
|
||||
uint32_t mTransform;
|
||||
|
||||
// mScalingMode is the current scaling mode for this buffer slot. This
|
||||
// gets set to mNextScalingMode each time queueBuffer gets called for
|
||||
// this slot.
|
||||
uint32_t mScalingMode;
|
||||
|
||||
// mTimestamp is the current timestamp for this buffer slot. This gets
|
||||
// to set by queueBuffer each time this slot is queued.
|
||||
int64_t mTimestamp;
|
||||
|
||||
// mFrameNumber is the number of the queued frame for this slot.
|
||||
uint64_t mFrameNumber;
|
||||
|
||||
// buf is the slot index of this buffer
|
||||
int mBuf;
|
||||
|
||||
};
|
||||
|
||||
// The following public functions is the consumer facing interface
|
||||
|
||||
// acquire consumes a buffer by transferring its ownership to a consumer.
|
||||
// buffer contains the GraphicBuffer and its corresponding information.
|
||||
// buffer.mGraphicsBuffer will be NULL when the buffer has been already
|
||||
// acquired by the consumer.
|
||||
|
||||
status_t acquire(BufferItem *buffer);
|
||||
|
||||
// releaseBuffer releases a buffer slot from the consumer back to the
|
||||
// BufferQueue pending a fence sync.
|
||||
status_t releaseBuffer(int buf, EGLDisplay display, EGLSyncKHR fence);
|
||||
|
||||
// consumerDisconnect disconnects a consumer from the BufferQueue. All
|
||||
// buffers will be freed.
|
||||
status_t consumerDisconnect();
|
||||
|
||||
// setDefaultBufferSize is used to set the size of buffers returned by
|
||||
// requestBuffers when a with and height of zero is requested.
|
||||
status_t setDefaultBufferSize(uint32_t w, uint32_t h);
|
||||
|
||||
// setBufferCountServer set the buffer count. If the client has requested
|
||||
// a buffer count using setBufferCount, the server-buffer count will
|
||||
// take effect once the client sets the count back to zero.
|
||||
status_t setBufferCountServer(int bufferCount);
|
||||
|
||||
// isSynchronousMode returns whether the SurfaceTexture is currently in
|
||||
// synchronous mode.
|
||||
bool isSynchronousMode() const;
|
||||
|
||||
// setConsumerName sets the name used in logging
|
||||
void setConsumerName(const String8& name);
|
||||
|
||||
// setFrameAvailableListener sets the listener object that will be notified
|
||||
// when a new frame becomes available.
|
||||
void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
|
||||
|
||||
|
||||
private:
|
||||
// freeBufferLocked frees the resources (both GraphicBuffer and EGLImage)
|
||||
// for the given slot.
|
||||
void freeBufferLocked(int index);
|
||||
@ -145,20 +229,18 @@ protected:
|
||||
|
||||
status_t setBufferCountServerLocked(int bufferCount);
|
||||
|
||||
enum { INVALID_BUFFER_SLOT = -1 };
|
||||
|
||||
struct BufferSlot {
|
||||
|
||||
BufferSlot()
|
||||
: mEglImage(EGL_NO_IMAGE_KHR),
|
||||
mEglDisplay(EGL_NO_DISPLAY),
|
||||
: mEglDisplay(EGL_NO_DISPLAY),
|
||||
mBufferState(BufferSlot::FREE),
|
||||
mRequestBufferCalled(false),
|
||||
mTransform(0),
|
||||
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mTimestamp(0),
|
||||
mFrameNumber(0),
|
||||
mFence(EGL_NO_SYNC_KHR) {
|
||||
mFence(EGL_NO_SYNC_KHR),
|
||||
mAcquireCalled(false) {
|
||||
mCrop.makeInvalid();
|
||||
}
|
||||
|
||||
@ -166,9 +248,6 @@ protected:
|
||||
// if no buffer has been allocated.
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mEglImage is the EGLImage created from mGraphicBuffer.
|
||||
EGLImageKHR mEglImage;
|
||||
|
||||
// mEglDisplay is the EGLDisplay used to create mEglImage.
|
||||
EGLDisplay mEglDisplay;
|
||||
|
||||
@ -178,6 +257,7 @@ protected:
|
||||
// FREE indicates that the buffer is not currently being used and
|
||||
// will not be used in the future until it gets dequeued and
|
||||
// subsequently queued by the client.
|
||||
// aka "owned by BufferQueue, ready to be dequeued"
|
||||
FREE = 0,
|
||||
|
||||
// DEQUEUED indicates that the buffer has been dequeued by the
|
||||
@ -190,6 +270,7 @@ protected:
|
||||
// dequeued by the client. That means that the current buffer can
|
||||
// be in either the DEQUEUED or QUEUED state. In asynchronous mode,
|
||||
// however, the current buffer is always in the QUEUED state.
|
||||
// aka "owned by producer, ready to be queued"
|
||||
DEQUEUED = 1,
|
||||
|
||||
// QUEUED indicates that the buffer has been queued by the client,
|
||||
@ -199,7 +280,11 @@ protected:
|
||||
// the current buffer may be dequeued by the client under some
|
||||
// circumstances. See the note about the current buffer in the
|
||||
// documentation for DEQUEUED.
|
||||
// aka "owned by BufferQueue, ready to be acquired"
|
||||
QUEUED = 2,
|
||||
|
||||
// aka "owned by consumer, ready to be released"
|
||||
ACQUIRED = 3
|
||||
};
|
||||
|
||||
// mBufferState is the current state of this buffer slot.
|
||||
@ -236,6 +321,9 @@ protected:
|
||||
// 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;
|
||||
|
||||
// Indicates whether this buffer has been seen by a consumer yet
|
||||
bool mAcquireCalled;
|
||||
};
|
||||
|
||||
// mSlots is the array of buffer slots that must be mirrored on the client
|
||||
@ -245,7 +333,6 @@ protected:
|
||||
// for a slot when requestBuffer is called with that slot's index.
|
||||
BufferSlot mSlots[NUM_BUFFER_SLOTS];
|
||||
|
||||
|
||||
// mDefaultWidth holds the default width of allocated buffers. It is used
|
||||
// in requestBuffers() if a width and height of zero is specified.
|
||||
uint32_t mDefaultWidth;
|
||||
@ -271,14 +358,6 @@ protected:
|
||||
// mServerBufferCount buffer count requested by the server-side
|
||||
int mServerBufferCount;
|
||||
|
||||
// mCurrentTexture is the buffer slot index of the buffer that is currently
|
||||
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
|
||||
// indicating that no buffer slot is currently bound to the texture. Note,
|
||||
// however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
|
||||
// that no buffer is bound to the texture. A call to setBufferCount will
|
||||
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
|
||||
int mCurrentTexture;
|
||||
|
||||
// mNextCrop is the crop rectangle that will be used for the next buffer
|
||||
// that gets queued. It is set by calling setCrop.
|
||||
Rect mNextCrop;
|
||||
@ -327,7 +406,7 @@ protected:
|
||||
|
||||
// mName is a string used to identify the BufferQueue in log messages.
|
||||
// It is set by the setName method.
|
||||
String8 mName;
|
||||
String8 mConsumerName;
|
||||
|
||||
// mMutex is the mutex used to prevent concurrent access to the member
|
||||
// variables of BufferQueue objects. It must be locked whenever the
|
||||
@ -337,6 +416,8 @@ protected:
|
||||
// mFrameCounter is the free running counter, incremented for every buffer queued
|
||||
// with the surface Texture.
|
||||
uint64_t mFrameCounter;
|
||||
|
||||
bool mBufferHasBeenQueued;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -153,8 +153,8 @@ public:
|
||||
void setName(const String8& name);
|
||||
|
||||
// dump our state in a String
|
||||
void dump(String8& result) const;
|
||||
void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
||||
virtual void dump(String8& result) const;
|
||||
virtual void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
|
||||
|
||||
protected:
|
||||
|
||||
@ -217,6 +217,56 @@ private:
|
||||
// browser's tile cache exceeds.
|
||||
const GLenum mTexTarget;
|
||||
|
||||
// SurfaceTexture maintains EGL information about GraphicBuffers that corresponds
|
||||
// directly with BufferQueue's buffers
|
||||
struct EGLSlot {
|
||||
EGLSlot()
|
||||
: mEglImage(EGL_NO_IMAGE_KHR),
|
||||
mEglDisplay(EGL_NO_DISPLAY),
|
||||
mFence(EGL_NO_SYNC_KHR) {
|
||||
}
|
||||
|
||||
sp<GraphicBuffer> mGraphicBuffer;
|
||||
|
||||
// mEglImage is the EGLImage created from mGraphicBuffer.
|
||||
EGLImageKHR mEglImage;
|
||||
|
||||
// mEglDisplay is the EGLDisplay used to create mEglImage.
|
||||
EGLDisplay mEglDisplay;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
EGLSlot mEGLSlots[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;
|
||||
|
||||
// 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;
|
||||
|
||||
// mCurrentTexture is the buffer slot index of the buffer that is currently
|
||||
// bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
|
||||
// indicating that no buffer slot is currently bound to the texture. Note,
|
||||
// however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
|
||||
// that no buffer is bound to the texture. A call to setBufferCount will
|
||||
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
|
||||
int mCurrentTexture;
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#define LOG_TAG "BufferQueue"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
@ -27,6 +28,7 @@
|
||||
#include <private/gui/ComposerService.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <gui/SurfaceTexture.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
|
||||
@ -34,6 +36,10 @@
|
||||
// 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.
|
||||
|
||||
// During refactoring, do not support dequeuing the current buffer
|
||||
#undef ALLOW_DEQUEUE_CURRENT_BUFFER
|
||||
|
||||
#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
|
||||
#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
|
||||
#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
|
||||
@ -42,11 +48,11 @@
|
||||
#endif
|
||||
|
||||
// Macros for including the BufferQueue name in log messages
|
||||
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
|
||||
|
||||
namespace android {
|
||||
|
||||
@ -63,17 +69,17 @@ BufferQueue::BufferQueue( bool allowSynchronousMode ) :
|
||||
mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
|
||||
mClientBufferCount(0),
|
||||
mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
|
||||
mCurrentTexture(INVALID_BUFFER_SLOT),
|
||||
mNextTransform(0),
|
||||
mNextScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
|
||||
mSynchronousMode(false),
|
||||
mAllowSynchronousMode(allowSynchronousMode),
|
||||
mConnectedApi(NO_CONNECTED_API),
|
||||
mAbandoned(false),
|
||||
mFrameCounter(0)
|
||||
mFrameCounter(0),
|
||||
mBufferHasBeenQueued(false)
|
||||
{
|
||||
// Choose a name using the PID and a process-unique ID.
|
||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||
mConsumerName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||
|
||||
ST_LOGV("BufferQueue");
|
||||
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
|
||||
@ -119,6 +125,23 @@ status_t BufferQueue::setBufferCountServerLocked(int bufferCount) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool BufferQueue::isSynchronousMode() const {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return mSynchronousMode;
|
||||
}
|
||||
|
||||
void BufferQueue::setConsumerName(const String8& name) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mConsumerName = name;
|
||||
}
|
||||
|
||||
void BufferQueue::setFrameAvailableListener(
|
||||
const sp<FrameAvailableListener>& listener) {
|
||||
ST_LOGV("setFrameAvailableListener");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mFrameAvailableListener = listener;
|
||||
}
|
||||
|
||||
status_t BufferQueue::setBufferCount(int bufferCount) {
|
||||
ST_LOGV("setBufferCount: count=%d", bufferCount);
|
||||
Mutex::Autolock lock(mMutex);
|
||||
@ -160,7 +183,7 @@ status_t BufferQueue::setBufferCount(int bufferCount) {
|
||||
freeAllBuffersLocked();
|
||||
mBufferCount = bufferCount;
|
||||
mClientBufferCount = bufferCount;
|
||||
mCurrentTexture = INVALID_BUFFER_SLOT;
|
||||
mBufferHasBeenQueued = false;
|
||||
mQueue.clear();
|
||||
mDequeueCondition.signal();
|
||||
return OK;
|
||||
@ -276,7 +299,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
mBufferCount = mServerBufferCount;
|
||||
if (mBufferCount < minBufferCountNeeded)
|
||||
mBufferCount = minBufferCountNeeded;
|
||||
mCurrentTexture = INVALID_BUFFER_SLOT;
|
||||
mBufferHasBeenQueued = false;
|
||||
returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
|
||||
}
|
||||
|
||||
@ -290,19 +313,12 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
dequeuedCount++;
|
||||
}
|
||||
|
||||
// if buffer is FREE it CANNOT be current
|
||||
ALOGW_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;
|
||||
if (i != mCurrentTexture) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// this logic used to be if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER)
|
||||
// but dequeuing the current buffer is disabled.
|
||||
if (false) {
|
||||
// This functionality has been temporarily removed so
|
||||
// BufferQueue and SurfaceTexture can be refactored into
|
||||
// separate objects
|
||||
} else {
|
||||
if (state == BufferSlot::FREE) {
|
||||
/* We return the oldest of the free buffers to avoid
|
||||
@ -331,8 +347,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
// 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) {
|
||||
if (mBufferHasBeenQueued) {
|
||||
// make sure the client is not trying to dequeue more buffers
|
||||
// than allowed.
|
||||
const int avail = mBufferCount - (dequeuedCount+1);
|
||||
@ -404,27 +419,23 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
if (updateFormat) {
|
||||
mPixelFormat = format;
|
||||
}
|
||||
|
||||
mSlots[buf].mAcquireCalled = false;
|
||||
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;
|
||||
}
|
||||
mSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
|
||||
|
||||
|
||||
|
||||
|
||||
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
|
||||
}
|
||||
|
||||
dpy = mSlots[buf].mEglDisplay;
|
||||
fence = mSlots[buf].mFence;
|
||||
mSlots[buf].mFence = EGL_NO_SYNC_KHR;
|
||||
}
|
||||
} // end lock scope
|
||||
|
||||
if (fence != EGL_NO_SYNC_KHR) {
|
||||
EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
|
||||
@ -437,6 +448,7 @@ status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
|
||||
ALOGE("dequeueBuffer: timeout waiting for fence");
|
||||
}
|
||||
eglDestroySyncKHR(dpy, fence);
|
||||
|
||||
}
|
||||
|
||||
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
|
||||
@ -496,9 +508,6 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
|
||||
ST_LOGE("queueBuffer: slot %d is not owned by the client "
|
||||
"(state=%d)", buf, mSlots[buf].mBufferState);
|
||||
return -EINVAL;
|
||||
} else if (buf == mCurrentTexture) {
|
||||
ST_LOGE("queueBuffer: slot %d is current!", buf);
|
||||
return -EINVAL;
|
||||
} else if (!mSlots[buf].mRequestBufferCalled) {
|
||||
ST_LOGE("queueBuffer: slot %d was enqueued without requesting a "
|
||||
"buffer", buf);
|
||||
@ -538,6 +547,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
|
||||
mFrameCounter++;
|
||||
mSlots[buf].mFrameNumber = mFrameCounter;
|
||||
|
||||
mBufferHasBeenQueued = true;
|
||||
mDequeueCondition.signal();
|
||||
|
||||
*outWidth = mDefaultWidth;
|
||||
@ -647,6 +657,9 @@ status_t BufferQueue::connect(int api,
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mBufferHasBeenQueued = false;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -687,26 +700,185 @@ status_t BufferQueue::disconnect(int api) {
|
||||
return err;
|
||||
}
|
||||
|
||||
void BufferQueue::dump(String8& result) const
|
||||
{
|
||||
char buffer[1024];
|
||||
BufferQueue::dump(result, "", buffer, 1024);
|
||||
}
|
||||
|
||||
void BufferQueue::dump(String8& result, const char* prefix,
|
||||
char* buffer, size_t SIZE) const
|
||||
{
|
||||
Mutex::Autolock _l(mMutex);
|
||||
snprintf(buffer, SIZE,
|
||||
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x}\n"
|
||||
,prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
|
||||
mNextCrop.bottom, mNextTransform
|
||||
);
|
||||
result.append(buffer);
|
||||
|
||||
String8 fifo;
|
||||
int fifoSize = 0;
|
||||
Fifo::const_iterator i(mQueue.begin());
|
||||
while (i != mQueue.end()) {
|
||||
snprintf(buffer, SIZE, "%02d ", *i++);
|
||||
fifoSize++;
|
||||
fifo.append(buffer);
|
||||
}
|
||||
|
||||
snprintf(buffer, SIZE,
|
||||
"%s-BufferQueue mBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
|
||||
"mPixelFormat=%d, FIFO(%d)={%s}\n",
|
||||
prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
|
||||
mDefaultHeight, mPixelFormat, fifoSize, fifo.string());
|
||||
result.append(buffer);
|
||||
|
||||
|
||||
struct {
|
||||
const char * operator()(int state) const {
|
||||
switch (state) {
|
||||
case BufferSlot::DEQUEUED: return "DEQUEUED";
|
||||
case BufferSlot::QUEUED: return "QUEUED";
|
||||
case BufferSlot::FREE: return "FREE";
|
||||
case BufferSlot::ACQUIRED: return "ACQUIRED";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
} stateName;
|
||||
|
||||
for (int i=0 ; i<mBufferCount ; i++) {
|
||||
const BufferSlot& slot(mSlots[i]);
|
||||
snprintf(buffer, SIZE,
|
||||
"%s%s[%02d] "
|
||||
"state=%-8s, crop=[%d,%d,%d,%d], "
|
||||
"transform=0x%02x, timestamp=%lld",
|
||||
prefix, (slot.mBufferState == BufferSlot::ACQUIRED)?">":" ", i,
|
||||
stateName(slot.mBufferState),
|
||||
slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
|
||||
slot.mCrop.bottom, slot.mTransform, slot.mTimestamp
|
||||
);
|
||||
result.append(buffer);
|
||||
|
||||
const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
|
||||
if (buf != NULL) {
|
||||
snprintf(buffer, SIZE,
|
||||
", %p [%4ux%4u:%4u,%3X]",
|
||||
buf->handle, buf->width, buf->height, buf->stride,
|
||||
buf->format);
|
||||
result.append(buffer);
|
||||
}
|
||||
result.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueue::freeBufferLocked(int i) {
|
||||
mSlots[i].mGraphicBuffer = 0;
|
||||
mSlots[i].mBufferState = BufferSlot::FREE;
|
||||
mSlots[i].mFrameNumber = 0;
|
||||
if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage);
|
||||
mSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mSlots[i].mEglDisplay = EGL_NO_DISPLAY;
|
||||
mSlots[i].mAcquireCalled = false;
|
||||
|
||||
// destroy fence as BufferQueue now takes ownership
|
||||
if (mSlots[i].mFence != EGL_NO_SYNC_KHR) {
|
||||
eglDestroySyncKHR(mSlots[i].mEglDisplay, mSlots[i].mFence);
|
||||
mSlots[i].mFence = EGL_NO_SYNC_KHR;
|
||||
}
|
||||
}
|
||||
|
||||
void BufferQueue::freeAllBuffersLocked() {
|
||||
ALOGW_IF(!mQueue.isEmpty(),
|
||||
"freeAllBuffersLocked called but mQueue is not empty");
|
||||
mCurrentTexture = INVALID_BUFFER_SLOT;
|
||||
mQueue.clear();
|
||||
mBufferHasBeenQueued = false;
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
freeBufferLocked(i);
|
||||
}
|
||||
}
|
||||
|
||||
status_t BufferQueue::acquire(BufferItem *buffer) {
|
||||
Mutex::Autolock _l(mMutex);
|
||||
// check if queue is empty
|
||||
// In asynchronous mode the list is guaranteed to be one buffer
|
||||
// deep, while in synchronous mode we use the oldest buffer.
|
||||
if (!mQueue.empty()) {
|
||||
Fifo::iterator front(mQueue.begin());
|
||||
int buf = *front;
|
||||
|
||||
if (mSlots[buf].mAcquireCalled) {
|
||||
buffer->mGraphicBuffer = NULL;
|
||||
}
|
||||
else {
|
||||
buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer;
|
||||
}
|
||||
buffer->mCrop = mSlots[buf].mCrop;
|
||||
buffer->mTransform = mSlots[buf].mTransform;
|
||||
buffer->mScalingMode = mSlots[buf].mScalingMode;
|
||||
buffer->mFrameNumber = mSlots[buf].mFrameNumber;
|
||||
buffer->mBuf = buf;
|
||||
mSlots[buf].mAcquireCalled = true;
|
||||
|
||||
mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
|
||||
mQueue.erase(front);
|
||||
}
|
||||
else {
|
||||
return -EINVAL; //should be a better return code
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,
|
||||
EGLSyncKHR fence) {
|
||||
Mutex::Autolock _l(mMutex);
|
||||
|
||||
if (buf == INVALID_BUFFER_SLOT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mSlots[buf].mEglDisplay = display;
|
||||
mSlots[buf].mFence = fence;
|
||||
|
||||
// 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[buf].mBufferState == BufferSlot::QUEUED
|
||||
|| mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
|
||||
mSlots[buf].mBufferState = BufferSlot::FREE;
|
||||
}
|
||||
mDequeueCondition.signal();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t BufferQueue::consumerDisconnect() {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
// Once the SurfaceTexture disconnects, the BufferQueue
|
||||
// is considered abandoned
|
||||
mAbandoned = true;
|
||||
freeAllBuffersLocked();
|
||||
mDequeueCondition.signal();
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h)
|
||||
{
|
||||
ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
|
||||
if (!w || !h) {
|
||||
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
|
||||
w, h);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mDefaultWidth = w;
|
||||
mDefaultHeight = h;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t BufferQueue::setBufferCountServer(int bufferCount) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return setBufferCountServerLocked(bufferCount);
|
||||
}
|
||||
|
||||
void BufferQueue::freeAllBuffersExceptHeadLocked() {
|
||||
ALOGW_IF(!mQueue.isEmpty(),
|
||||
"freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
|
||||
@ -715,7 +887,7 @@ void BufferQueue::freeAllBuffersExceptHeadLocked() {
|
||||
Fifo::iterator front(mQueue.begin());
|
||||
head = *front;
|
||||
}
|
||||
mCurrentTexture = INVALID_BUFFER_SLOT;
|
||||
mBufferHasBeenQueued = false;
|
||||
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
if (i != head) {
|
||||
freeBufferLocked(i);
|
||||
|
@ -96,6 +96,11 @@ static float mtxRot270[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,
|
||||
GLenum texTarget, bool useFenceSync) :
|
||||
@ -108,8 +113,13 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||
#else
|
||||
mUseFenceSync(false),
|
||||
#endif
|
||||
mTexTarget(texTarget)
|
||||
mTexTarget(texTarget),
|
||||
mAbandoned(false),
|
||||
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
|
||||
{
|
||||
// Choose a name using the PID and a process-unique ID.
|
||||
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
|
||||
BufferQueue::setConsumerName(mName);
|
||||
|
||||
ST_LOGV("SurfaceTexture");
|
||||
memcpy(mCurrentTransformMatrix, mtxIdentity,
|
||||
@ -118,28 +128,18 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
|
||||
|
||||
SurfaceTexture::~SurfaceTexture() {
|
||||
ST_LOGV("~SurfaceTexture");
|
||||
freeAllBuffersLocked();
|
||||
abandon();
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::setBufferCountServer(int bufferCount) {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return setBufferCountServerLocked(bufferCount);
|
||||
return BufferQueue::setBufferCountServer(bufferCount);
|
||||
}
|
||||
|
||||
|
||||
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
|
||||
{
|
||||
ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
|
||||
if (!w || !h) {
|
||||
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
|
||||
w, h);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mDefaultWidth = w;
|
||||
mDefaultHeight = h;
|
||||
return OK;
|
||||
return BufferQueue::setDefaultBufferSize(w, h);
|
||||
}
|
||||
|
||||
status_t SurfaceTexture::updateTexImage() {
|
||||
@ -151,23 +151,35 @@ status_t SurfaceTexture::updateTexImage() {
|
||||
return NO_INIT;
|
||||
}
|
||||
|
||||
BufferItem item;
|
||||
|
||||
// In asynchronous mode the list is guaranteed to be one buffer
|
||||
// deep, while in synchronous mode we use the oldest buffer.
|
||||
if (!mQueue.empty()) {
|
||||
Fifo::iterator front(mQueue.begin());
|
||||
int buf = *front;
|
||||
if (acquire(&item) == NO_ERROR) {
|
||||
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(mEGLSlots[buf].mEglDisplay,
|
||||
mEGLSlots[buf].mEglImage);
|
||||
mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mEGLSlots[buf].mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer;
|
||||
}
|
||||
|
||||
// Update the GL texture object.
|
||||
EGLImageKHR image = mSlots[buf].mEglImage;
|
||||
EGLImageKHR image = mEGLSlots[buf].mEglImage;
|
||||
EGLDisplay dpy = eglGetCurrentDisplay();
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
if (mSlots[buf].mGraphicBuffer == 0) {
|
||||
if (item.mGraphicBuffer == 0) {
|
||||
ST_LOGE("buffer at slot %d is null", buf);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
image = createImage(dpy, mSlots[buf].mGraphicBuffer);
|
||||
mSlots[buf].mEglImage = image;
|
||||
mSlots[buf].mEglDisplay = dpy;
|
||||
image = createImage(dpy, item.mGraphicBuffer);
|
||||
mEGLSlots[buf].mEglImage = image;
|
||||
mEGLSlots[buf].mEglDisplay = dpy;
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
// NOTE: if dpy was invalid, createImage() is guaranteed to
|
||||
// fail. so we'd end up here.
|
||||
@ -190,6 +202,8 @@ status_t SurfaceTexture::updateTexImage() {
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
|
||||
mEGLSlots[buf].mFence);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -200,40 +214,37 @@ status_t SurfaceTexture::updateTexImage() {
|
||||
if (fence == EGL_NO_SYNC_KHR) {
|
||||
ALOGE("updateTexImage: error creating fence: %#x",
|
||||
eglGetError());
|
||||
releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
|
||||
mEGLSlots[buf].mFence);
|
||||
return -EINVAL;
|
||||
}
|
||||
glFlush();
|
||||
mSlots[mCurrentTexture].mFence = fence;
|
||||
mEGLSlots[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);
|
||||
buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0);
|
||||
|
||||
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) {
|
||||
mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE;
|
||||
}
|
||||
}
|
||||
// release old buffer
|
||||
releaseBuffer(mCurrentTexture,
|
||||
mEGLSlots[mCurrentTexture].mEglDisplay,
|
||||
mEGLSlots[mCurrentTexture].mFence);
|
||||
|
||||
// Update the SurfaceTexture state.
|
||||
mCurrentTexture = buf;
|
||||
mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
|
||||
mCurrentCrop = mSlots[buf].mCrop;
|
||||
mCurrentTransform = mSlots[buf].mTransform;
|
||||
mCurrentScalingMode = mSlots[buf].mScalingMode;
|
||||
mCurrentTimestamp = mSlots[buf].mTimestamp;
|
||||
mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
|
||||
mCurrentCrop = item.mCrop;
|
||||
mCurrentTransform = item.mTransform;
|
||||
mCurrentScalingMode = item.mScalingMode;
|
||||
mCurrentTimestamp = item.mTimestamp;
|
||||
computeCurrentTransformMatrix();
|
||||
|
||||
// Now that we've passed the point at which failures can happen,
|
||||
// it's safe to remove the buffer from the front of the queue.
|
||||
mQueue.erase(front);
|
||||
mDequeueCondition.signal();
|
||||
|
||||
} else {
|
||||
// We always bind the texture even if we don't update its contents.
|
||||
glBindTexture(mTexTarget, mTexName);
|
||||
@ -299,7 +310,7 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
|
||||
}
|
||||
}
|
||||
|
||||
sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer);
|
||||
sp<GraphicBuffer>& buf(mCurrentTextureBuf);
|
||||
float tx, ty, sx, sy;
|
||||
if (!mCurrentCrop.isEmpty()) {
|
||||
// In order to prevent bilinear sampling at the of the crop rectangle we
|
||||
@ -371,7 +382,7 @@ void SurfaceTexture::setFrameAvailableListener(
|
||||
const sp<FrameAvailableListener>& listener) {
|
||||
ST_LOGV("setFrameAvailableListener");
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mFrameAvailableListener = listener;
|
||||
BufferQueue::setFrameAvailableListener(listener);
|
||||
}
|
||||
|
||||
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
|
||||
@ -412,22 +423,33 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const {
|
||||
|
||||
bool SurfaceTexture::isSynchronousMode() const {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
return mSynchronousMode;
|
||||
return BufferQueue::isSynchronousMode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SurfaceTexture::abandon() {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
mQueue.clear();
|
||||
mAbandoned = true;
|
||||
mCurrentTextureBuf.clear();
|
||||
freeAllBuffersLocked();
|
||||
mDequeueCondition.signal();
|
||||
|
||||
// destroy all egl buffers
|
||||
for (int i =0; i < NUM_BUFFER_SLOTS; i++) {
|
||||
mEGLSlots[i].mGraphicBuffer = 0;
|
||||
if (mEGLSlots[i].mEglImage != EGL_NO_IMAGE_KHR) {
|
||||
eglDestroyImageKHR(mEGLSlots[i].mEglDisplay,
|
||||
mEGLSlots[i].mEglImage);
|
||||
mEGLSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
|
||||
mEGLSlots[i].mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// disconnect from the BufferQueue
|
||||
BufferQueue::consumerDisconnect();
|
||||
}
|
||||
|
||||
void SurfaceTexture::setName(const String8& name) {
|
||||
Mutex::Autolock _l(mMutex);
|
||||
mName = name;
|
||||
BufferQueue::setConsumerName(name);
|
||||
}
|
||||
|
||||
void SurfaceTexture::dump(String8& result) const
|
||||
@ -440,68 +462,19 @@ void SurfaceTexture::dump(String8& result, const char* prefix,
|
||||
char* buffer, size_t SIZE) const
|
||||
{
|
||||
Mutex::Autolock _l(mMutex);
|
||||
snprintf(buffer, SIZE,
|
||||
"%smBufferCount=%d, mSynchronousMode=%d, default-size=[%dx%d], "
|
||||
"mPixelFormat=%d, mTexName=%d\n",
|
||||
prefix, mBufferCount, mSynchronousMode, mDefaultWidth,
|
||||
mDefaultHeight, mPixelFormat, mTexName);
|
||||
snprintf(buffer, SIZE, "%smTexName=%d\n", prefix, mTexName);
|
||||
result.append(buffer);
|
||||
|
||||
String8 fifo;
|
||||
int fifoSize = 0;
|
||||
Fifo::const_iterator i(mQueue.begin());
|
||||
while (i != mQueue.end()) {
|
||||
snprintf(buffer, SIZE, "%02d ", *i++);
|
||||
fifoSize++;
|
||||
fifo.append(buffer);
|
||||
}
|
||||
|
||||
snprintf(buffer, SIZE,
|
||||
"%scurrent: {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n"
|
||||
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, FIFO(%d)={%s}}\n"
|
||||
,
|
||||
prefix, mCurrentCrop.left,
|
||||
"%snext : {crop=[%d,%d,%d,%d], transform=0x%02x, current=%d}\n"
|
||||
,prefix, mCurrentCrop.left,
|
||||
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
|
||||
mCurrentTransform, mCurrentTexture,
|
||||
prefix, mNextCrop.left, mNextCrop.top, mNextCrop.right,
|
||||
mNextCrop.bottom, mNextTransform, fifoSize, fifo.string()
|
||||
mCurrentTransform, mCurrentTexture
|
||||
);
|
||||
result.append(buffer);
|
||||
|
||||
struct {
|
||||
const char * operator()(int state) const {
|
||||
switch (state) {
|
||||
case BufferSlot::DEQUEUED: return "DEQUEUED";
|
||||
case BufferSlot::QUEUED: return "QUEUED";
|
||||
case BufferSlot::FREE: return "FREE";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
} stateName;
|
||||
|
||||
for (int i=0 ; i<mBufferCount ; i++) {
|
||||
const BufferSlot& slot(mSlots[i]);
|
||||
snprintf(buffer, SIZE,
|
||||
"%s%s[%02d] "
|
||||
"state=%-8s, crop=[%d,%d,%d,%d], "
|
||||
"transform=0x%02x, timestamp=%lld",
|
||||
prefix, (i==mCurrentTexture)?">":" ", i,
|
||||
stateName(slot.mBufferState),
|
||||
slot.mCrop.left, slot.mCrop.top, slot.mCrop.right,
|
||||
slot.mCrop.bottom, slot.mTransform, slot.mTimestamp
|
||||
);
|
||||
result.append(buffer);
|
||||
|
||||
const sp<GraphicBuffer>& buf(slot.mGraphicBuffer);
|
||||
if (buf != NULL) {
|
||||
snprintf(buffer, SIZE,
|
||||
", %p [%4ux%4u:%4u,%3X]",
|
||||
buf->handle, buf->width, buf->height, buf->stride,
|
||||
buf->format);
|
||||
result.append(buffer);
|
||||
}
|
||||
result.append("\n");
|
||||
}
|
||||
BufferQueue::dump(result, prefix, buffer, SIZE);
|
||||
}
|
||||
|
||||
static void mtxMul(float out[16], const float a[16], const float b[16]) {
|
||||
|
Loading…
Reference in New Issue
Block a user