Merge "a window could get stuck to gpu composition"
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
deleted file mode 100644
index 8a2979a..0000000
--- a/include/binder/CursorWindow.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ANDROID__DATABASE_WINDOW_H
-#define _ANDROID__DATABASE_WINDOW_H
-
-#include <cutils/log.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <binder/Parcel.h>
-#include <utils/String8.h>
-
-#if LOG_NDEBUG
-
-#define IF_LOG_WINDOW() if (false)
-#define LOG_WINDOW(...)
-
-#else
-
-#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
-
-#endif
-
-namespace android {
-
-/**
- * This class stores a set of rows from a database in a buffer. The begining of the
- * window has first chunk of RowSlots, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
- * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * FieldSlot per column, which has the size, offset, and type of the data for that field.
- * Note that the data types come from sqlite3.h.
- *
- * Strings are stored in UTF-8.
- */
-class CursorWindow {
- CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly);
-
-public:
- /* Field types. */
- enum {
- FIELD_TYPE_NULL = 0,
- FIELD_TYPE_INTEGER = 1,
- FIELD_TYPE_FLOAT = 2,
- FIELD_TYPE_STRING = 3,
- FIELD_TYPE_BLOB = 4,
- };
-
- /* Opaque type that describes a field slot. */
- struct FieldSlot {
- private:
- int32_t type;
- union {
- double d;
- int64_t l;
- struct {
- uint32_t offset;
- uint32_t size;
- } buffer;
- } data;
-
- friend class CursorWindow;
- } __attribute((packed));
-
- ~CursorWindow();
-
- static status_t create(const String8& name, size_t size, CursorWindow** outCursorWindow);
- static status_t createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow);
-
- status_t writeToParcel(Parcel* parcel);
-
- inline String8 name() { return mName; }
- inline size_t size() { return mSize; }
- inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
- inline uint32_t getNumRows() { return mHeader->numRows; }
- inline uint32_t getNumColumns() { return mHeader->numColumns; }
-
- status_t clear();
- status_t setNumColumns(uint32_t numColumns);
-
- /**
- * Allocate a row slot and its directory.
- * The row is initialized will null entries for each field.
- */
- status_t allocRow();
- status_t freeLastRow();
-
- status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
- status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
- status_t putLong(uint32_t row, uint32_t column, int64_t value);
- status_t putDouble(uint32_t row, uint32_t column, double value);
- status_t putNull(uint32_t row, uint32_t column);
-
- /**
- * Gets the field slot at the specified row and column.
- * Returns null if the requested row or column is not in the window.
- */
- FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
-
- inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
- return fieldSlot->type;
- }
-
- inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
- return fieldSlot->data.l;
- }
-
- inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
- return fieldSlot->data.d;
- }
-
- inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
- size_t* outSizeIncludingNull) {
- *outSizeIncludingNull = fieldSlot->data.buffer.size;
- return static_cast<char*>(offsetToPtr(fieldSlot->data.buffer.offset));
- }
-
- inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
- *outSize = fieldSlot->data.buffer.size;
- return offsetToPtr(fieldSlot->data.buffer.offset);
- }
-
-private:
- static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
-
- struct Header {
- // Offset of the lowest unused byte in the window.
- uint32_t freeOffset;
-
- // Offset of the first row slot chunk.
- uint32_t firstChunkOffset;
-
- uint32_t numRows;
- uint32_t numColumns;
- };
-
- struct RowSlot {
- uint32_t offset;
- };
-
- struct RowSlotChunk {
- RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
- uint32_t nextChunkOffset;
- };
-
- String8 mName;
- int mAshmemFd;
- void* mData;
- size_t mSize;
- bool mReadOnly;
- Header* mHeader;
-
- inline void* offsetToPtr(uint32_t offset) {
- return static_cast<uint8_t*>(mData) + offset;
- }
-
- inline uint32_t offsetFromPtr(void* ptr) {
- return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData);
- }
-
- /**
- * Allocate a portion of the window. Returns the offset
- * of the allocation, or 0 if there isn't enough space.
- * If aligned is true, the allocation gets 4 byte alignment.
- */
- uint32_t alloc(size_t size, bool aligned = false);
-
- RowSlot* getRowSlot(uint32_t row);
- RowSlot* allocRowSlot();
-
- status_t putBlobOrString(uint32_t row, uint32_t column,
- const void* value, size_t size, int32_t type);
-};
-
-}; // namespace android
-
-#endif
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index dd1558c..8c21a28 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -40,6 +40,7 @@
};
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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
// mFrameCounter is the free running counter, incremented for every buffer queued
// with the surface Texture.
uint64_t mFrameCounter;
+
+ bool mBufferHasBeenQueued;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index dcab049..5531e53 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -153,8 +153,8 @@
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 @@
// 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;
+
};
// ----------------------------------------------------------------------------
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index 3a12e96..fd116b7 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -16,7 +16,6 @@
sources := \
Binder.cpp \
BpBinder.cpp \
- CursorWindow.cpp \
IInterface.cpp \
IMemory.cpp \
IPCThreadState.cpp \
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
deleted file mode 100644
index a6e5f71..0000000
--- a/libs/binder/CursorWindow.cpp
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2006-2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CursorWindow"
-
-#include <utils/Log.h>
-#include <binder/CursorWindow.h>
-
-#include <cutils/ashmem.h>
-#include <sys/mman.h>
-
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-namespace android {
-
-CursorWindow::CursorWindow(const String8& name, int ashmemFd,
- void* data, size_t size, bool readOnly) :
- mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
- mHeader = static_cast<Header*>(mData);
-}
-
-CursorWindow::~CursorWindow() {
- ::munmap(mData, mSize);
- ::close(mAshmemFd);
-}
-
-status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
- String8 ashmemName("CursorWindow: ");
- ashmemName.append(name);
-
- status_t result;
- int ashmemFd = ashmem_create_region(ashmemName.string(), size);
- if (ashmemFd < 0) {
- result = -errno;
- } else {
- result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
- if (result >= 0) {
- void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
- if (data == MAP_FAILED) {
- result = -errno;
- } else {
- result = ashmem_set_prot_region(ashmemFd, PROT_READ);
- if (result >= 0) {
- CursorWindow* window = new CursorWindow(name, ashmemFd,
- data, size, false /*readOnly*/);
- result = window->clear();
- if (!result) {
- LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
- window->mHeader->freeOffset,
- window->mHeader->numRows,
- window->mHeader->numColumns,
- window->mSize, window->mData);
- *outCursorWindow = window;
- return OK;
- }
- delete window;
- }
- }
- ::munmap(data, size);
- }
- ::close(ashmemFd);
- }
- *outCursorWindow = NULL;
- return result;
-}
-
-status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) {
- String8 name = parcel->readString8();
-
- status_t result;
- int ashmemFd = parcel->readFileDescriptor();
- if (ashmemFd == int(BAD_TYPE)) {
- result = BAD_TYPE;
- } else {
- ssize_t size = ashmem_get_size_region(ashmemFd);
- if (size < 0) {
- result = UNKNOWN_ERROR;
- } else {
- int dupAshmemFd = ::dup(ashmemFd);
- if (dupAshmemFd < 0) {
- result = -errno;
- } else {
- void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0);
- if (data == MAP_FAILED) {
- result = -errno;
- } else {
- CursorWindow* window = new CursorWindow(name, dupAshmemFd,
- data, size, true /*readOnly*/);
- LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
- window->mHeader->freeOffset,
- window->mHeader->numRows,
- window->mHeader->numColumns,
- window->mSize, window->mData);
- *outCursorWindow = window;
- return OK;
- }
- ::close(dupAshmemFd);
- }
- }
- }
- *outCursorWindow = NULL;
- return result;
-}
-
-status_t CursorWindow::writeToParcel(Parcel* parcel) {
- status_t status = parcel->writeString8(mName);
- if (!status) {
- status = parcel->writeDupFileDescriptor(mAshmemFd);
- }
- return status;
-}
-
-status_t CursorWindow::clear() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
- mHeader->firstChunkOffset = sizeof(Header);
- mHeader->numRows = 0;
- mHeader->numColumns = 0;
-
- RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset));
- firstChunk->nextChunkOffset = 0;
- return OK;
-}
-
-status_t CursorWindow::setNumColumns(uint32_t numColumns) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- uint32_t cur = mHeader->numColumns;
- if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
- ALOGE("Trying to go from %d columns to %d", cur, numColumns);
- return INVALID_OPERATION;
- }
- mHeader->numColumns = numColumns;
- return OK;
-}
-
-status_t CursorWindow::allocRow() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- // Fill in the row slot
- RowSlot* rowSlot = allocRowSlot();
- if (rowSlot == NULL) {
- return NO_MEMORY;
- }
-
- // Allocate the slots for the field directory
- size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
- uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
- if (!fieldDirOffset) {
- mHeader->numRows--;
- LOG_WINDOW("The row failed, so back out the new row accounting "
- "from allocRowSlot %d", mHeader->numRows);
- return NO_MEMORY;
- }
- FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset));
- memset(fieldDir, 0, fieldDirSize);
-
- LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
- mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
- rowSlot->offset = fieldDirOffset;
- return OK;
-}
-
-status_t CursorWindow::freeLastRow() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- if (mHeader->numRows > 0) {
- mHeader->numRows--;
- }
- return OK;
-}
-
-uint32_t CursorWindow::alloc(size_t size, bool aligned) {
- uint32_t padding;
- if (aligned) {
- // 4 byte alignment
- padding = (~mHeader->freeOffset + 1) & 3;
- } else {
- padding = 0;
- }
-
- uint32_t offset = mHeader->freeOffset + padding;
- uint32_t nextFreeOffset = offset + size;
- if (nextFreeOffset > mSize) {
- ALOGW("Window is full: requested allocation %d bytes, "
- "free space %d bytes, window size %d bytes",
- size, freeSpace(), mSize);
- return 0;
- }
-
- mHeader->freeOffset = nextFreeOffset;
- return offset;
-}
-
-CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
- uint32_t chunkPos = row;
- RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
- offsetToPtr(mHeader->firstChunkOffset));
- while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
- chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
- chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
- }
- return &chunk->slots[chunkPos];
-}
-
-CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
- uint32_t chunkPos = mHeader->numRows;
- RowSlotChunk* chunk = static_cast<RowSlotChunk*>(
- offsetToPtr(mHeader->firstChunkOffset));
- while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
- chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
- chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
- }
- if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
- if (!chunk->nextChunkOffset) {
- chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
- if (!chunk->nextChunkOffset) {
- return NULL;
- }
- }
- chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset));
- chunk->nextChunkOffset = 0;
- chunkPos = 0;
- }
- mHeader->numRows += 1;
- return &chunk->slots[chunkPos];
-}
-
-CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
- if (row >= mHeader->numRows || column >= mHeader->numColumns) {
- ALOGE("Failed to read row %d, column %d from a CursorWindow which "
- "has %d rows, %d columns.",
- row, column, mHeader->numRows, mHeader->numColumns);
- return NULL;
- }
- RowSlot* rowSlot = getRowSlot(row);
- if (!rowSlot) {
- ALOGE("Failed to find rowSlot for row %d.", row);
- return NULL;
- }
- FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset));
- return &fieldDir[column];
-}
-
-status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
- return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
-}
-
-status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
- size_t sizeIncludingNull) {
- return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
-}
-
-status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
- const void* value, size_t size, int32_t type) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- uint32_t offset = alloc(size);
- if (!offset) {
- return NO_MEMORY;
- }
-
- memcpy(offsetToPtr(offset), value, size);
-
- fieldSlot->type = type;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = size;
- return OK;
-}
-
-status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_INTEGER;
- fieldSlot->data.l = value;
- return OK;
-}
-
-status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_FLOAT;
- fieldSlot->data.d = value;
- return OK;
-}
-
-status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_NULL;
- fieldSlot->data.buffer.offset = 0;
- fieldSlot->data.buffer.size = 0;
- return OK;
-}
-
-}; // namespace android
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 261336a..d761680 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -15,6 +15,8 @@
*/
#define LOG_TAG "BufferQueue"
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
@@ -27,6 +29,8 @@
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
+#include <gui/SurfaceTexture.h>
+#include <utils/Trace.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 +38,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 +50,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 +71,17 @@
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 +127,23 @@
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 +185,7 @@
freeAllBuffersLocked();
mBufferCount = bufferCount;
mClientBufferCount = bufferCount;
- mCurrentTexture = INVALID_BUFFER_SLOT;
+ mBufferHasBeenQueued = false;
mQueue.clear();
mDequeueCondition.signal();
return OK;
@@ -168,6 +193,7 @@
int BufferQueue::query(int what, int* outValue)
{
+ ATRACE_CALL();
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -198,6 +224,7 @@
}
status_t BufferQueue::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+ ATRACE_CALL();
ST_LOGV("requestBuffer: slot=%d", slot);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -216,6 +243,7 @@
status_t BufferQueue::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) {
+ ATRACE_CALL();
ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
if ((w && !h) || (!w && h)) {
@@ -276,7 +304,7 @@
mBufferCount = mServerBufferCount;
if (mBufferCount < minBufferCountNeeded)
mBufferCount = minBufferCountNeeded;
- mCurrentTexture = INVALID_BUFFER_SLOT;
+ mBufferHasBeenQueued = false;
returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS;
}
@@ -290,19 +318,12 @@
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 +352,7 @@
// 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 +424,23 @@
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 +453,7 @@
ALOGE("dequeueBuffer: timeout waiting for fence");
}
eglDestroySyncKHR(dpy, fence);
+
}
ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf,
@@ -446,6 +463,7 @@
}
status_t BufferQueue::setSynchronousMode(bool enabled) {
+ ATRACE_CALL();
ST_LOGV("setSynchronousMode: enabled=%d", enabled);
Mutex::Autolock lock(mMutex);
@@ -478,6 +496,7 @@
status_t BufferQueue::queueBuffer(int buf, int64_t timestamp,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+ ATRACE_CALL();
ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
sp<FrameAvailableListener> listener;
@@ -496,9 +515,6 @@
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,11 +554,14 @@
mFrameCounter++;
mSlots[buf].mFrameNumber = mFrameCounter;
+ mBufferHasBeenQueued = true;
mDequeueCondition.signal();
*outWidth = mDefaultWidth;
*outHeight = mDefaultHeight;
*outTransform = 0;
+
+ ATRACE_INT(mConsumerName.string(), mQueue.size());
} // scope for the lock
// call back without lock held
@@ -553,6 +572,7 @@
}
void BufferQueue::cancelBuffer(int buf) {
+ ATRACE_CALL();
ST_LOGV("cancelBuffer: slot=%d", buf);
Mutex::Autolock lock(mMutex);
@@ -576,6 +596,7 @@
}
status_t BufferQueue::setCrop(const Rect& crop) {
+ ATRACE_CALL();
ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
crop.bottom);
@@ -589,6 +610,7 @@
}
status_t BufferQueue::setTransform(uint32_t transform) {
+ ATRACE_CALL();
ST_LOGV("setTransform: xform=%#x", transform);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -600,6 +622,7 @@
}
status_t BufferQueue::setScalingMode(int mode) {
+ ATRACE_CALL();
ST_LOGV("setScalingMode: mode=%d", mode);
switch (mode) {
@@ -618,6 +641,7 @@
status_t BufferQueue::connect(int api,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
+ ATRACE_CALL();
ST_LOGV("connect: api=%d", api);
Mutex::Autolock lock(mMutex);
@@ -647,10 +671,14 @@
err = -EINVAL;
break;
}
+
+ mBufferHasBeenQueued = false;
+
return err;
}
status_t BufferQueue::disconnect(int api) {
+ ATRACE_CALL();
ST_LOGV("disconnect: api=%d", api);
Mutex::Autolock lock(mMutex);
@@ -687,26 +715,188 @@
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);
+
+ ATRACE_INT(mConsumerName.string(), mQueue.size());
+ }
+ 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) {
+ ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ return setBufferCountServerLocked(bufferCount);
+}
+
void BufferQueue::freeAllBuffersExceptHeadLocked() {
ALOGW_IF(!mQueue.isEmpty(),
"freeAllBuffersExceptCurrentLocked called but mQueue is not empty");
@@ -715,7 +905,7 @@
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);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 5f0013c..b42aa34 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "SurfaceTexture"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#define GL_GLEXT_PROTOTYPES
@@ -36,6 +37,7 @@
#include <utils/Log.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension
// to synchronize access to the buffers. It will cause dequeueBuffer to stall,
@@ -96,6 +98,11 @@
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 +115,13 @@
#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,31 +130,22 @@
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() {
+ ATRACE_CALL();
ST_LOGV("updateTexImage");
Mutex::Autolock lock(mMutex);
@@ -151,23 +154,35 @@
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 +205,8 @@
failed = true;
}
if (failed) {
+ releaseBuffer(buf, mEGLSlots[buf].mEglDisplay,
+ mEGLSlots[buf].mFence);
return -EINVAL;
}
@@ -200,40 +217,37 @@
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 +313,7 @@
}
}
- 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 +385,7 @@
const sp<FrameAvailableListener>& listener) {
ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
- mFrameAvailableListener = listener;
+ BufferQueue::setFrameAvailableListener(listener);
}
EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
@@ -412,22 +426,33 @@
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 +465,19 @@
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]) {
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index dac54a8..f88dcaf 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -15,9 +15,11 @@
*/
#define LOG_TAG "SurfaceTextureClient"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -121,6 +123,7 @@
}
int SurfaceTextureClient::setSwapInterval(int interval) {
+ ATRACE_CALL();
// EGL specification states:
// interval is silently clamped to minimum and maximum implementation
// dependent values before being stored.
@@ -138,6 +141,7 @@
}
int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::dequeueBuffer");
Mutex::Autolock lock(mMutex);
int buf = -1;
@@ -167,6 +171,7 @@
}
int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::cancelBuffer");
Mutex::Autolock lock(mMutex);
int i = getSlotFromBufferLocked(buffer);
@@ -213,6 +218,7 @@
}
int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
@@ -236,6 +242,7 @@
}
int SurfaceTextureClient::query(int what, int* value) const {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::query");
{ // scope for the lock
Mutex::Autolock lock(mMutex);
@@ -404,6 +411,7 @@
int SurfaceTextureClient::connect(int api) {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::connect");
Mutex::Autolock lock(mMutex);
int err = mSurfaceTexture->connect(api,
@@ -415,6 +423,7 @@
}
int SurfaceTextureClient::disconnect(int api) {
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::disconnect");
Mutex::Autolock lock(mMutex);
freeAllBuffers();
@@ -441,6 +450,7 @@
int SurfaceTextureClient::setCrop(Rect const* rect)
{
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::setCrop");
Mutex::Autolock lock(mMutex);
@@ -459,6 +469,7 @@
int SurfaceTextureClient::setBufferCount(int bufferCount)
{
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::setBufferCount");
Mutex::Autolock lock(mMutex);
@@ -475,6 +486,7 @@
int SurfaceTextureClient::setBuffersDimensions(int w, int h)
{
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::setBuffersDimensions");
Mutex::Autolock lock(mMutex);
@@ -508,6 +520,7 @@
int SurfaceTextureClient::setScalingMode(int mode)
{
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::setScalingMode(%d)", mode);
Mutex::Autolock lock(mMutex);
// mode is validated on the server
@@ -520,6 +533,7 @@
int SurfaceTextureClient::setBuffersTransform(int transform)
{
+ ATRACE_CALL();
ALOGV("SurfaceTextureClient::setBuffersTransform");
Mutex::Autolock lock(mMutex);
status_t err = mSurfaceTexture->setTransform(transform);
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index a5dc832..a1bd82d 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
@@ -34,6 +36,7 @@
#include <utils/KeyedVector.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
+#include <utils/Trace.h>
#include "egl_impl.h"
#include "egl_tls.h"
@@ -348,6 +351,7 @@
}
void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
+ ATRACE_CALL();
clearError();
egl_display_t const * const dp = validate_display(dpy);
@@ -712,6 +716,7 @@
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
+ ATRACE_CALL();
clearError();
egl_display_t const * const dp = validate_display(dpy);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e96cf32..df7fe5c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
@@ -26,6 +28,7 @@
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/StopWatch.h>
+#include <utils/Trace.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
@@ -267,6 +270,8 @@
void Layer::onDraw(const Region& clip) const
{
+ ATRACE_CALL();
+
if (CC_UNLIKELY(mActiveBuffer == 0)) {
// the texture has not been created yet, this Layer has
// in fact never been drawn into. This happens frequently with
@@ -365,6 +370,8 @@
uint32_t Layer::doTransaction(uint32_t flags)
{
+ ATRACE_CALL();
+
const Layer::State& front(drawingState());
const Layer::State& temp(currentState());
@@ -418,6 +425,8 @@
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
+ ATRACE_CALL();
+
if (mQueuedFrames > 0) {
// if we've already called updateTexImage() without going through
@@ -546,6 +555,8 @@
void Layer::unlockPageFlip(
const Transform& planeTransform, Region& outDirtyRegion)
{
+ ATRACE_CALL();
+
Region postedRegion(mPostedDirtyRegion);
if (!postedRegion.isEmpty()) {
mPostedDirtyRegion.clear();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 62461bb..42ed4fa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
@@ -39,6 +41,7 @@
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/StopWatch.h>
+#include <utils/Trace.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/PixelFormat.h>
@@ -402,6 +405,7 @@
void SurfaceFlinger::onMessageReceived(int32_t what)
{
+ ATRACE_CALL();
switch (what) {
case MessageQueue::REFRESH: {
// case MessageQueue::INVALIDATE: {
@@ -737,6 +741,7 @@
void SurfaceFlinger::handlePageFlip()
{
+ ATRACE_CALL();
const DisplayHardware& hw = graphicPlane(0).displayHardware();
const Region screenRegion(hw.bounds());