summaryrefslogtreecommitdiffstats
path: root/libs/ui
diff options
context:
space:
mode:
authorMathias Agopian <mathias@google.com>2009-09-07 16:32:45 -0700
committerMathias Agopian <mathias@google.com>2009-09-07 16:32:45 -0700
commit9779b221e999583ff89e0dfc40e56398737adbb3 (patch)
tree76b185d252b95b05e8d74e7a1644b843f8839725 /libs/ui
parenta4eb91da03bd785bc91bed0d25a9efaa9baba1c1 (diff)
downloadframeworks_base-9779b221e999583ff89e0dfc40e56398737adbb3.zip
frameworks_base-9779b221e999583ff89e0dfc40e56398737adbb3.tar.gz
frameworks_base-9779b221e999583ff89e0dfc40e56398737adbb3.tar.bz2
fix [2068105] implement queueBuffer/lockBuffer/dequeueBuffer properly
Rewrote SurfaceFlinger's buffer management from the ground-up. The design now support an arbitrary number of buffers per surface, however the current implementation is limited to four. Currently only 2 buffers are used in practice. The main new feature is to be able to dequeue all buffers at once (very important when there are only two). A client can dequeue all buffers until there are none available, it can lock all buffers except the last one that is used for composition. The client will block then, until a new buffer is enqueued. The current implementation requires that buffers are locked in the same order they are dequeued and enqueued in the same order they are locked. Only one buffer can be locked at a time. eg. Allowed sequence: DQ, DQ, LOCK, Q, LOCK, Q eg. Forbidden sequence: DQ, DQ, LOCK, LOCK, Q, Q
Diffstat (limited to 'libs/ui')
-rw-r--r--libs/ui/Android.mk2
-rw-r--r--libs/ui/ISurface.cpp10
-rw-r--r--libs/ui/SharedBufferStack.cpp352
-rw-r--r--libs/ui/Surface.cpp374
-rw-r--r--libs/ui/SurfaceBuffer.cpp134
-rw-r--r--libs/ui/SurfaceComposerClient.cpp184
6 files changed, 648 insertions, 408 deletions
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 93c7263..348dd68 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -23,7 +23,9 @@ LOCAL_SRC_FILES:= \
PixelFormat.cpp \
Rect.cpp \
Region.cpp \
+ SharedBufferStack.cpp \
Surface.cpp \
+ SurfaceBuffer.cpp \
SurfaceComposerClient.cpp \
SurfaceFlingerSynchro.cpp
diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp
index b78e8b5..a2dbe7f 100644
--- a/libs/ui/ISurface.cpp
+++ b/libs/ui/ISurface.cpp
@@ -71,12 +71,13 @@ public:
{
}
- virtual sp<SurfaceBuffer> getBuffer(int usage)
+ virtual sp<SurfaceBuffer> requestBuffer(int bufferIdx, int usage)
{
Parcel data, reply;
data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
+ data.writeInt32(bufferIdx);
data.writeInt32(usage);
- remote()->transact(GET_BUFFER, data, &reply);
+ remote()->transact(REQUEST_BUFFER, data, &reply);
sp<SurfaceBuffer> buffer = new SurfaceBuffer(reply);
return buffer;
}
@@ -134,10 +135,11 @@ status_t BnSurface::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
- case GET_BUFFER: {
+ case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurface, data, reply);
+ int bufferIdx = data.readInt32();
int usage = data.readInt32();
- sp<SurfaceBuffer> buffer(getBuffer(usage));
+ sp<SurfaceBuffer> buffer(requestBuffer(bufferIdx, usage));
return SurfaceBuffer::writeToParcel(reply, buffer.get());
}
case REGISTER_BUFFERS: {
diff --git a/libs/ui/SharedBufferStack.cpp b/libs/ui/SharedBufferStack.cpp
new file mode 100644
index 0000000..5995af5
--- /dev/null
+++ b/libs/ui/SharedBufferStack.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#define LOG_TAG "SharedBufferStack"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <private/ui/SharedBufferStack.h>
+
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#define DEBUG_ATOMICS 0
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+SharedClient::SharedClient()
+ : lock(Mutex::SHARED)
+{
+}
+
+SharedClient::~SharedClient() {
+}
+
+
+// these functions are used by the clients
+status_t SharedClient::validate(size_t i) const {
+ if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX))
+ return BAD_INDEX;
+ return surfaces[i].status;
+}
+
+uint32_t SharedClient::getIdentity(size_t token) const {
+ return uint32_t(surfaces[token].identity);
+}
+
+status_t SharedClient::setIdentity(size_t token, uint32_t identity) {
+ if (token >= NUM_LAYERS_MAX)
+ return BAD_INDEX;
+ surfaces[token].identity = identity;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+
+SharedBufferStack::SharedBufferStack()
+ : inUse(-1), identity(-1), status(NO_ERROR)
+{
+}
+
+status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+
+ // in the current implementation we only send a single rectangle
+ const Rect bounds(dirty.getBounds());
+ FlatRegion& reg(dirtyRegion[buffer]);
+ reg.count = 1;
+ reg.rects[0] = uint16_t(bounds.left);
+ reg.rects[1] = uint16_t(bounds.top);
+ reg.rects[2] = uint16_t(bounds.right);
+ reg.rects[3] = uint16_t(bounds.bottom);
+ return NO_ERROR;
+}
+
+Region SharedBufferStack::getDirtyRegion(int buffer) const
+{
+ Region res;
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return res;
+
+ const FlatRegion& reg(dirtyRegion[buffer]);
+ res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3]));
+ return res;
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
+ int surface, int num)
+ : mSharedClient(sharedClient),
+ mSharedStack(sharedClient->surfaces + surface),
+ mNumBuffers(num)
+{
+}
+
+SharedBufferBase::~SharedBufferBase()
+{
+}
+
+uint32_t SharedBufferBase::getIdentity()
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.identity;
+}
+
+size_t SharedBufferBase::getFrontBuffer() const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return size_t( stack.head );
+}
+
+String8 SharedBufferBase::dump(char const* prefix) const
+{
+ const size_t SIZE = 1024;
+ char buffer[SIZE];
+ String8 result;
+ SharedBufferStack& stack( *mSharedStack );
+ snprintf(buffer, SIZE,
+ "%s[ head=%2d, available=%2d, queued=%2d ] "
+ "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
+ prefix, stack.head, stack.available, stack.queued,
+ stack.reallocMask, stack.inUse, stack.identity, stack.status);
+ result.append(buffer);
+ return result;
+}
+
+
+// ============================================================================
+// conditions and updates
+// ============================================================================
+
+SharedBufferClient::DequeueCondition::DequeueCondition(
+ SharedBufferClient* sbc) : ConditionBase(sbc) {
+}
+bool SharedBufferClient::DequeueCondition::operator()() {
+ return stack.available > 0;
+}
+
+SharedBufferClient::LockCondition::LockCondition(
+ SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
+}
+bool SharedBufferClient::LockCondition::operator()() {
+ return (buf != stack.head ||
+ (stack.queued > 0 && stack.inUse != buf));
+}
+
+SharedBufferServer::ReallocateCondition::ReallocateCondition(
+ SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
+}
+bool SharedBufferServer::ReallocateCondition::operator()() {
+ // TODO: we should also check that buf has been dequeued
+ return (buf != stack.head);
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
+}
+ssize_t SharedBufferClient::QueueUpdate::operator()() {
+ android_atomic_inc(&stack.queued);
+ return NO_ERROR;
+}
+
+SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
+}
+ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ android_atomic_inc(&stack.available);
+ return NO_ERROR;
+}
+
+SharedBufferServer::UnlockUpdate::UnlockUpdate(
+ SharedBufferBase* sbb, int lockedBuffer)
+ : UpdateBase(sbb), lockedBuffer(lockedBuffer) {
+}
+ssize_t SharedBufferServer::UnlockUpdate::operator()() {
+ if (stack.inUse != lockedBuffer) {
+ LOGE("unlocking %d, but currently locked buffer is %d",
+ lockedBuffer, stack.inUse);
+ return BAD_VALUE;
+ }
+ android_atomic_write(-1, &stack.inUse);
+ return NO_ERROR;
+}
+
+SharedBufferServer::RetireUpdate::RetireUpdate(
+ SharedBufferBase* sbb, int numBuffers)
+ : UpdateBase(sbb), numBuffers(numBuffers) {
+}
+ssize_t SharedBufferServer::RetireUpdate::operator()() {
+ // head is only written in this function, which is single-thread.
+ int32_t head = stack.head;
+
+ // Preventively lock the current buffer before updating queued.
+ android_atomic_write(head, &stack.inUse);
+
+ // Decrement the number of queued buffers
+ int32_t queued;
+ do {
+ queued = stack.queued;
+ if (queued == 0) {
+ return NOT_ENOUGH_DATA;
+ }
+ } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
+
+ // update the head pointer
+ head = ((head+1 >= numBuffers) ? 0 : head+1);
+
+ // lock the buffer before advancing head, which automatically unlocks
+ // the buffer we preventively locked upon entering this function
+ android_atomic_write(head, &stack.inUse);
+
+ // advance head
+ android_atomic_write(head, &stack.head);
+
+ // now that head has moved, we can increment the number of available buffers
+ android_atomic_inc(&stack.available);
+ return head;
+}
+
+// ============================================================================
+
+SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
+ int surface, int num)
+ : SharedBufferBase(sharedClient, surface, num), tail(0)
+{
+}
+
+ssize_t SharedBufferClient::dequeue()
+{
+ //LOGD("[%d] about to dequeue a buffer",
+ // mSharedStack->identity);
+ DequeueCondition condition(this);
+ status_t err = waitForCondition(condition);
+ if (err != NO_ERROR)
+ return ssize_t(err);
+
+
+ SharedBufferStack& stack( *mSharedStack );
+ // NOTE: 'stack.available' is part of the conditions, however
+ // decrementing it, never changes any conditions, so we don't need
+ // to do this as part of an update.
+ if (android_atomic_dec(&stack.available) == 0) {
+ LOGW("dequeue probably called from multiple threads!");
+ }
+
+ int dequeued = tail;
+ tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
+ LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s",
+ dequeued, tail, dump("").string());
+ return dequeued;
+}
+
+status_t SharedBufferClient::undoDequeue(int buf)
+{
+ UndoDequeueUpdate update(this);
+ status_t err = updateCondition( update );
+ return err;
+}
+
+status_t SharedBufferClient::lock(int buf)
+{
+ LockCondition condition(this, buf);
+ status_t err = waitForCondition(condition);
+ return err;
+}
+
+status_t SharedBufferClient::queue(int buf)
+{
+ QueueUpdate update(this);
+ status_t err = updateCondition( update );
+ LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string());
+ return err;
+}
+
+bool SharedBufferClient::needNewBuffer(int buffer) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ const uint32_t mask = 1<<buffer;
+ return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;
+}
+
+status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.setDirtyRegion(buffer, reg);
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
+ int surface, int num)
+ : SharedBufferBase(sharedClient, surface, num)
+{
+ mSharedStack->head = num-1;
+ mSharedStack->available = num;
+ mSharedStack->queued = 0;
+ mSharedStack->reallocMask = 0;
+ memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion));
+}
+
+ssize_t SharedBufferServer::retireAndLock()
+{
+ RetireUpdate update(this, mNumBuffers);
+ ssize_t buf = updateCondition( update );
+ LOGD_IF(DEBUG_ATOMICS, "retire=%d, %s", int(buf), dump("").string());
+ return buf;
+}
+
+status_t SharedBufferServer::unlock(int buffer)
+{
+ UnlockUpdate update(this, buffer);
+ status_t err = updateCondition( update );
+ return err;
+}
+
+status_t SharedBufferServer::reallocate()
+{
+ SharedBufferStack& stack( *mSharedStack );
+ uint32_t mask = (1<<mNumBuffers)-1;
+ android_atomic_or(mask, &stack.reallocMask);
+ return NO_ERROR;
+}
+
+status_t SharedBufferServer::assertReallocate(int buffer)
+{
+ ReallocateCondition condition(this, buffer);
+ status_t err = waitForCondition(condition);
+ return err;
+}
+
+Region SharedBufferServer::getDirtyRegion(int buffer) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getDirtyRegion(buffer);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp
index 474308a..c3fbea2 100644
--- a/libs/ui/Surface.cpp
+++ b/libs/ui/Surface.cpp
@@ -25,6 +25,7 @@
#include <utils/Errors.h>
#include <utils/threads.h>
+#include <utils/CallStack.h>
#include <binder/IPCThreadState.h>
#include <binder/IMemory.h>
#include <utils/Log.h>
@@ -38,102 +39,12 @@
#include <pixelflinger/pixelflinger.h>
-#include <private/ui/SharedState.h>
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/LayerState.h>
#include <private/ui/SurfaceBuffer.h>
namespace android {
-// ============================================================================
-// SurfaceBuffer
-// ============================================================================
-
-SurfaceBuffer::SurfaceBuffer()
- : BASE(), mOwner(false), mBufferMapper(BufferMapper::get())
-{
- width =
- height =
- stride =
- format =
- usage = 0;
- handle = NULL;
-}
-
-SurfaceBuffer::SurfaceBuffer(const Parcel& data)
- : BASE(), mOwner(true), mBufferMapper(BufferMapper::get())
-{
- // we own the handle in this case
- width = data.readInt32();
- if (width < 0) {
- width = height = stride = format = usage = 0;
- handle = 0;
- } else {
- height = data.readInt32();
- stride = data.readInt32();
- format = data.readInt32();
- usage = data.readInt32();
- handle = data.readNativeHandle();
- }
-}
-
-SurfaceBuffer::~SurfaceBuffer()
-{
- if (handle && mOwner) {
- native_handle_close(handle);
- native_handle_delete(const_cast<native_handle*>(handle));
- }
-}
-
-status_t SurfaceBuffer::lock(uint32_t usage, void** vaddr)
-{
- const Rect lockBounds(width, height);
- status_t res = lock(usage, lockBounds, vaddr);
- return res;
-}
-
-status_t SurfaceBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr)
-{
- if (rect.left < 0 || rect.right > this->width ||
- rect.top < 0 || rect.bottom > this->height) {
- LOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
- rect.left, rect.top, rect.right, rect.bottom,
- this->width, this->height);
- return BAD_VALUE;
- }
- status_t res = getBufferMapper().lock(handle, usage, rect, vaddr);
- return res;
-}
-
-status_t SurfaceBuffer::unlock()
-{
- status_t res = getBufferMapper().unlock(handle);
- return res;
-}
-
-status_t SurfaceBuffer::writeToParcel(Parcel* reply,
- android_native_buffer_t const* buffer)
-{
- if (buffer == NULL)
- return BAD_VALUE;
-
- if (buffer->width < 0 || buffer->height < 0)
- return BAD_VALUE;
-
- status_t err = NO_ERROR;
- if (buffer->handle == NULL) {
- // this buffer doesn't have a handle
- reply->writeInt32(NO_MEMORY);
- } else {
- reply->writeInt32(buffer->width);
- reply->writeInt32(buffer->height);
- reply->writeInt32(buffer->stride);
- reply->writeInt32(buffer->format);
- reply->writeInt32(buffer->usage);
- err = reply->writeNativeHandle(buffer->handle);
- }
- return err;
-}
-
// ----------------------------------------------------------------------
static status_t copyBlt(
@@ -324,7 +235,7 @@ status_t SurfaceControl::setFreezeTint(uint32_t tint) {
return client->setFreezeTint(mToken, tint);
}
-status_t SurfaceControl::validate(per_client_cblk_t const* cblk) const
+status_t SurfaceControl::validate(SharedClient const* cblk) const
{
if (mToken<0 || mClient==0) {
LOGE("invalid token (%d, identity=%u) or client (%p)",
@@ -341,9 +252,10 @@ status_t SurfaceControl::validate(per_client_cblk_t const* cblk) const
mToken, mIdentity, err, strerror(-err));
return err;
}
- if (mIdentity != uint32_t(cblk->layers[mToken].identity)) {
+ uint32_t identity = cblk->getIdentity(mToken);
+ if (mIdentity != identity) {
LOGE("using an invalid surface id=%d, identity=%u should be %d",
- mToken, mIdentity, cblk->layers[mToken].identity);
+ mToken, mIdentity, identity);
return NO_INIT;
}
return NO_ERROR;
@@ -398,14 +310,17 @@ Surface::Surface(const sp<SurfaceControl>& surface)
: mClient(surface->mClient), mSurface(surface->mSurface),
mToken(surface->mToken), mIdentity(surface->mIdentity),
mFormat(surface->mFormat), mFlags(surface->mFlags),
- mBufferMapper(BufferMapper::get()),
+ mBufferMapper(BufferMapper::get()), mSharedBufferClient(NULL),
mWidth(surface->mWidth), mHeight(surface->mHeight)
{
+ mSharedBufferClient = new SharedBufferClient(
+ mClient->mControl, mToken, 2);
+
init();
}
Surface::Surface(const Parcel& parcel)
- : mBufferMapper(BufferMapper::get())
+ : mBufferMapper(BufferMapper::get()), mSharedBufferClient(NULL)
{
sp<IBinder> clientBinder = parcel.readStrongBinder();
mSurface = interface_cast<ISurface>(parcel.readStrongBinder());
@@ -416,9 +331,14 @@ Surface::Surface(const Parcel& parcel)
mFormat = parcel.readInt32();
mFlags = parcel.readInt32();
- if (clientBinder != NULL)
+ // FIXME: what does that mean if clientBinder is NULL here?
+ if (clientBinder != NULL) {
mClient = SurfaceComposerClient::clientForConnection(clientBinder);
+ mSharedBufferClient = new SharedBufferClient(
+ mClient->mControl, mToken, 2);
+ }
+
init();
}
@@ -442,6 +362,7 @@ void Surface::init()
// be default we request a hardware surface
mUsage = GRALLOC_USAGE_HW_RENDER;
mUsageChanged = true;
+ mNeedFullUpdate = false;
}
Surface::~Surface()
@@ -458,6 +379,7 @@ Surface::~Surface()
// happen without delay, since these resources are quite heavy.
mClient.clear();
mSurface.clear();
+ delete mSharedBufferClient;
IPCThreadState::self()->flushCommands();
}
@@ -473,7 +395,7 @@ bool Surface::isValid() {
return mToken>=0 && mClient!=0;
}
-status_t Surface::validate(per_client_cblk_t const* cblk) const
+status_t Surface::validate(SharedClient const* cblk) const
{
sp<SurfaceComposerClient> client(getClient());
if (mToken<0 || mClient==0) {
@@ -491,9 +413,10 @@ status_t Surface::validate(per_client_cblk_t const* cblk) const
mToken, mIdentity, err, strerror(-err));
return err;
}
- if (mIdentity != uint32_t(cblk->layers[mToken].identity)) {
+ uint32_t identity = cblk->getIdentity(mToken);
+ if (mIdentity != identity) {
LOGE("using an invalid surface id=%d, identity=%u should be %d",
- mToken, mIdentity, cblk->layers[mToken].identity);
+ mToken, mIdentity, identity);
return NO_INIT;
}
return NO_ERROR;
@@ -511,42 +434,36 @@ bool Surface::isSameSurface(
// ----------------------------------------------------------------------------
-int Surface::setSwapInterval(android_native_window_t* window, int interval)
-{
+int Surface::setSwapInterval(android_native_window_t* window, int interval) {
return 0;
}
int Surface::dequeueBuffer(android_native_window_t* window,
- android_native_buffer_t** buffer)
-{
+ android_native_buffer_t** buffer) {
Surface* self = getSelf(window);
return self->dequeueBuffer(buffer);
}
int Surface::lockBuffer(android_native_window_t* window,
- android_native_buffer_t* buffer)
-{
+ android_native_buffer_t* buffer) {
Surface* self = getSelf(window);
return self->lockBuffer(buffer);
}
int Surface::queueBuffer(android_native_window_t* window,
- android_native_buffer_t* buffer)
-{
+ android_native_buffer_t* buffer) {
Surface* self = getSelf(window);
return self->queueBuffer(buffer);
}
int Surface::query(android_native_window_t* window,
- int what, int* value)
-{
+ int what, int* value) {
Surface* self = getSelf(window);
return self->query(what, value);
}
int Surface::perform(android_native_window_t* window,
- int operation, ...)
-{
+ int operation, ...) {
va_list args;
va_start(args, operation);
Surface* self = getSelf(window);
@@ -557,8 +474,7 @@ int Surface::perform(android_native_window_t* window,
// ----------------------------------------------------------------------------
-status_t Surface::dequeueBuffer(sp<SurfaceBuffer>* buffer)
-{
+status_t Surface::dequeueBuffer(sp<SurfaceBuffer>* buffer) {
android_native_buffer_t* out;
status_t err = dequeueBuffer(&out);
if (err == NO_ERROR) {
@@ -567,70 +483,49 @@ status_t Surface::dequeueBuffer(sp<SurfaceBuffer>* buffer)
return err;
}
-status_t Surface::lockBuffer(const sp<SurfaceBuffer>& buffer)
-{
- return lockBuffer(buffer.get());
-}
-
-status_t Surface::queueBuffer(const sp<SurfaceBuffer>& buffer)
-{
- return queueBuffer(buffer.get());
-}
-
// ----------------------------------------------------------------------------
+
int Surface::dequeueBuffer(android_native_buffer_t** buffer)
{
- // FIXME: dequeueBuffer() needs proper implementation
-
- Mutex::Autolock _l(mSurfaceLock);
-
sp<SurfaceComposerClient> client(getClient());
- per_client_cblk_t* const cblk = client->mControl;
- status_t err = validate(cblk);
+ status_t err = validate(client->mControl);
if (err != NO_ERROR)
return err;
- SurfaceID index(mToken);
-
- int32_t backIdx = cblk->lock_layer(size_t(index),
- per_client_cblk_t::BLOCKING);
-
- if (backIdx < 0)
- return status_t(backIdx);
-
- mBackbufferIndex = backIdx;
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- volatile const surface_info_t* const back = lcblk->surface + backIdx;
-
- const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]);
-
- if (backBuffer==0 &&
- !((back->flags & surface_info_t::eNeedNewBuffer) || mUsageChanged)) {
- LOGW("dequeueBuffer: backbuffer is null, but eNeedNewBuffer "
- "is not set, fetching a buffer anyways...");
+ ssize_t bufIdx = mSharedBufferClient->dequeue();
+ if (bufIdx < 0) {
+ LOGE("error dequeuing a buffer (%s)", strerror(bufIdx));
+ return bufIdx;
}
-
- if ((back->flags & surface_info_t::eNeedNewBuffer) ||mUsageChanged ||
- backBuffer==0)
- {
- mUsageChanged = false;
- err = getBufferLocked(backIdx, mUsage);
+
+ // FIXME: in case of failure below, we need to undo the dequeue
+
+ uint32_t usage;
+ const bool usageChanged = getUsage(&usage);
+ const sp<SurfaceBuffer>& backBuffer(mBuffers[bufIdx]);
+ if ((backBuffer == 0) || usageChanged ||
+ mSharedBufferClient->needNewBuffer(bufIdx)) {
+ err = getBufferLocked(bufIdx, usage);
+ LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)",
+ bufIdx, usage, strerror(-err));
if (err == NO_ERROR) {
// reset the width/height with the what we get from the buffer
- const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]);
mWidth = uint32_t(backBuffer->width);
mHeight = uint32_t(backBuffer->height);
}
}
+ // if we still don't have a buffer here, we probably ran out of memory
+ if (!err && backBuffer==0) {
+ err = NO_MEMORY;
+ }
+
if (err == NO_ERROR) {
- if (backBuffer != 0) {
- mDirtyRegion.set(backBuffer->width, backBuffer->height);
- *buffer = backBuffer.get();
- } else {
- err = NO_MEMORY;
- }
+ mDirtyRegion.set(backBuffer->width, backBuffer->height);
+ *buffer = backBuffer.get();
+ } else {
+ mSharedBufferClient->undoDequeue(bufIdx);
}
return err;
@@ -638,25 +533,21 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer)
int Surface::lockBuffer(android_native_buffer_t* buffer)
{
- Mutex::Autolock _l(mSurfaceLock);
-
sp<SurfaceComposerClient> client(getClient());
- per_client_cblk_t* const cblk = client->mControl;
- status_t err = validate(cblk);
+ status_t err = validate(client->mControl);
if (err != NO_ERROR)
return err;
- // FIXME: lockBuffer() needs proper implementation
- return 0;
+ int32_t bufIdx = SurfaceBuffer::getSelf(buffer)->getIndex();
+ err = mSharedBufferClient->lock(bufIdx);
+ LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
+ return err;
}
int Surface::queueBuffer(android_native_buffer_t* buffer)
{
- Mutex::Autolock _l(mSurfaceLock);
-
sp<SurfaceComposerClient> client(getClient());
- per_client_cblk_t* const cblk = client->mControl;
- status_t err = validate(cblk);
+ status_t err = validate(client->mControl);
if (err != NO_ERROR)
return err;
@@ -664,30 +555,30 @@ int Surface::queueBuffer(android_native_buffer_t* buffer)
mDirtyRegion.set(mSwapRectangle);
}
- // transmit the dirty region
- SurfaceID index(mToken);
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- _send_dirty_region(lcblk, mDirtyRegion);
+ int32_t bufIdx = SurfaceBuffer::getSelf(buffer)->getIndex();
+ mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
+ err = mSharedBufferClient->queue(bufIdx);
+ LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
- uint32_t newstate = cblk->unlock_layer_and_post(size_t(index));
- if (!(newstate & eNextFlipPending))
+ if (err == NO_ERROR) {
+ // FIXME: can we avoid this IPC if we know there is one pending?
client->signalServer();
-
- return NO_ERROR;
+ }
+ return err;
}
int Surface::query(int what, int* value)
{
switch (what) {
- case NATIVE_WINDOW_WIDTH:
- *value = int(mWidth);
- return NO_ERROR;
- case NATIVE_WINDOW_HEIGHT:
- *value = int(mHeight);
- return NO_ERROR;
- case NATIVE_WINDOW_FORMAT:
- *value = int(mFormat);
- return NO_ERROR;
+ case NATIVE_WINDOW_WIDTH:
+ *value = int(mWidth);
+ return NO_ERROR;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = int(mHeight);
+ return NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = int(mFormat);
+ return NO_ERROR;
}
return BAD_VALUE;
}
@@ -715,6 +606,17 @@ void Surface::setUsage(uint32_t reqUsage)
}
}
+bool Surface::getUsage(uint32_t* usage)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ *usage = mUsage;
+ if (mUsageChanged) {
+ mUsageChanged = false;
+ return true;
+ }
+ return false;
+}
+
// ----------------------------------------------------------------------------
status_t Surface::lock(SurfaceInfo* info, bool blocking) {
@@ -723,43 +625,55 @@ status_t Surface::lock(SurfaceInfo* info, bool blocking) {
status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{
+ if (mApiLock.tryLock() != NO_ERROR) {
+ LOGE("calling Surface::lock() from different threads!");
+ CallStack stack;
+ stack.update();
+ stack.dump("Surface::lock called from different threads");
+ return WOULD_BLOCK;
+ }
+
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
sp<SurfaceBuffer> backBuffer;
status_t err = dequeueBuffer(&backBuffer);
+ LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
if (err == NO_ERROR) {
- err = lockBuffer(backBuffer);
+ err = lockBuffer(backBuffer.get());
+ LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)",
+ backBuffer->getIndex(), strerror(-err));
if (err == NO_ERROR) {
// we handle copy-back here...
-
+
const Rect bounds(backBuffer->width, backBuffer->height);
Region scratch(bounds);
Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);
- sp<SurfaceComposerClient> client(getClient());
- per_client_cblk_t* const cblk = client->mControl;
- layer_cblk_t* const lcblk = &(cblk->layers[SurfaceID(mToken)]);
- volatile const surface_info_t* const back = lcblk->surface + mBackbufferIndex;
- if (back->flags & surface_info_t::eBufferDirty) {
- // content is meaningless in this case and the whole surface
- // needs to be redrawn.
+ if (mNeedFullUpdate) {
+ // reset newDirtyRegion to bounds when a buffer is reallocated
+ // it would be better if this information was associated with
+ // the buffer and made available to outside of Surface.
+ // This will do for now though.
+ mNeedFullUpdate = false;
newDirtyRegion.set(bounds);
} else {
newDirtyRegion.andSelf(bounds);
- const sp<SurfaceBuffer>& frontBuffer(mBuffers[1-mBackbufferIndex]);
- if (frontBuffer !=0 &&
- backBuffer->width == frontBuffer->width &&
- backBuffer->height == frontBuffer->height &&
- !(lcblk->flags & eNoCopyBack))
- {
- const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
- if (!copyback.isEmpty() && frontBuffer!=0) {
- // copy front to back
- copyBlt(backBuffer, frontBuffer, copyback);
- }
+ }
+
+ const sp<SurfaceBuffer>& frontBuffer(mPostedBuffer);
+ if (frontBuffer !=0 &&
+ backBuffer->width == frontBuffer->width &&
+ backBuffer->height == frontBuffer->height &&
+ !(mFlags & ISurfaceComposer::eDestroyBackbuffer))
+ {
+ const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
+ if (!copyback.isEmpty() && frontBuffer!=0) {
+ // copy front to back
+ copyBlt(backBuffer, frontBuffer, copyback);
}
}
+
mDirtyRegion = newDirtyRegion;
mOldDirtyRegion = newDirtyRegion;
@@ -768,8 +682,8 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr);
- LOGW_IF(res, "failed locking buffer %d (%p)",
- mBackbufferIndex, backBuffer->handle);
+ LOGW_IF(res, "failed locking buffer (handle = %p)",
+ backBuffer->handle);
mLockedBuffer = backBuffer;
other->w = backBuffer->width;
@@ -780,36 +694,29 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
other->bits = vaddr;
}
}
+ mApiLock.unlock();
return err;
}
status_t Surface::unlockAndPost()
{
- if (mLockedBuffer == 0)
+ if (mLockedBuffer == 0) {
+ LOGE("unlockAndPost failed, no locked buffer");
return BAD_VALUE;
+ }
- status_t res = mLockedBuffer->unlock();
- LOGW_IF(res, "failed unlocking buffer %d (%p)",
- mBackbufferIndex, mLockedBuffer->handle);
+ status_t err = mLockedBuffer->unlock();
+ LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
- status_t err = queueBuffer(mLockedBuffer);
+ err = queueBuffer(mLockedBuffer.get());
+ LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)",
+ mLockedBuffer->getIndex(), strerror(-err));
+
+ mPostedBuffer = mLockedBuffer;
mLockedBuffer = 0;
return err;
}
-void Surface::_send_dirty_region(
- layer_cblk_t* lcblk, const Region& dirty)
-{
- const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift;
- flat_region_t* flat_region = lcblk->region + index;
- status_t err = dirty.write(flat_region, sizeof(flat_region_t));
- if (err < NO_ERROR) {
- // region doesn't fit, use the bounds
- const Region reg(dirty.bounds());
- reg.write(flat_region, sizeof(flat_region_t));
- }
-}
-
void Surface::setSwapRectangle(const Rect& r) {
Mutex::Autolock _l(mSurfaceLock);
mSwapRectangle = r;
@@ -829,15 +736,22 @@ status_t Surface::getBufferLocked(int index, int usage)
currentBuffer.clear();
}
- sp<SurfaceBuffer> buffer = s->getBuffer(usage);
- LOGE_IF(buffer==0, "ISurface::getBuffer() returned NULL");
+ sp<SurfaceBuffer> buffer = s->requestBuffer(index, usage);
+ LOGE_IF(buffer==0,
+ "ISurface::getBuffer(%d, %08x) returned NULL",
+ index, usage);
if (buffer != 0) { // this should never happen by construction
+ LOGE_IF(buffer->handle == NULL,
+ "requestBuffer(%d, %08x) returned a buffer with a null handle",
+ index, usage);
if (buffer->handle != NULL) {
err = getBufferMapper().registerBuffer(buffer->handle);
LOGW_IF(err, "registerBuffer(...) failed %d (%s)",
err, strerror(-err));
if (err == NO_ERROR) {
currentBuffer = buffer;
+ currentBuffer->setIndex(index);
+ mNeedFullUpdate = true;
}
}
}
diff --git a/libs/ui/SurfaceBuffer.cpp b/libs/ui/SurfaceBuffer.cpp
new file mode 100644
index 0000000..0510bc1
--- /dev/null
+++ b/libs/ui/SurfaceBuffer.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "SurfaceBuffer"
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+
+#include <ui/BufferMapper.h>
+#include <ui/Rect.h>
+#include <private/ui/SurfaceBuffer.h>
+
+namespace android {
+
+// ============================================================================
+// SurfaceBuffer
+// ============================================================================
+
+SurfaceBuffer::SurfaceBuffer()
+ : BASE(), mOwner(false), mBufferMapper(BufferMapper::get()), mIndex(-1)
+{
+ width =
+ height =
+ stride =
+ format =
+ usage = 0;
+ handle = NULL;
+}
+
+SurfaceBuffer::SurfaceBuffer(const Parcel& data)
+ : BASE(), mOwner(true), mBufferMapper(BufferMapper::get())
+{
+ // we own the handle in this case
+ width = data.readInt32();
+ if (width < 0) {
+ width = height = stride = format = usage = 0;
+ handle = 0;
+ } else {
+ height = data.readInt32();
+ stride = data.readInt32();
+ format = data.readInt32();
+ usage = data.readInt32();
+ handle = data.readNativeHandle();
+ }
+}
+
+SurfaceBuffer::~SurfaceBuffer()
+{
+ if (handle && mOwner) {
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle*>(handle));
+ }
+}
+
+status_t SurfaceBuffer::lock(uint32_t usage, void** vaddr)
+{
+ const Rect lockBounds(width, height);
+ status_t res = lock(usage, lockBounds, vaddr);
+ return res;
+}
+
+status_t SurfaceBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr)
+{
+ if (rect.left < 0 || rect.right > this->width ||
+ rect.top < 0 || rect.bottom > this->height) {
+ LOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
+ rect.left, rect.top, rect.right, rect.bottom,
+ this->width, this->height);
+ return BAD_VALUE;
+ }
+ status_t res = getBufferMapper().lock(handle, usage, rect, vaddr);
+ return res;
+}
+
+status_t SurfaceBuffer::unlock()
+{
+ status_t res = getBufferMapper().unlock(handle);
+ return res;
+}
+
+status_t SurfaceBuffer::writeToParcel(Parcel* reply,
+ android_native_buffer_t const* buffer)
+{
+ if (buffer == NULL)
+ return BAD_VALUE;
+
+ if (buffer->width < 0 || buffer->height < 0)
+ return BAD_VALUE;
+
+ status_t err = NO_ERROR;
+ if (buffer->handle == NULL) {
+ // this buffer doesn't have a handle
+ reply->writeInt32(NO_MEMORY);
+ } else {
+ reply->writeInt32(buffer->width);
+ reply->writeInt32(buffer->height);
+ reply->writeInt32(buffer->stride);
+ reply->writeInt32(buffer->format);
+ reply->writeInt32(buffer->usage);
+ err = reply->writeNativeHandle(buffer->handle);
+ }
+ return err;
+}
+
+
+void SurfaceBuffer::setIndex(int index) {
+ mIndex = index;
+}
+
+int SurfaceBuffer::getIndex() const {
+ return mIndex;
+}
+
+
+}; // namespace android
+
diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp
index d2cef78..8401cb6 100644
--- a/libs/ui/SurfaceComposerClient.cpp
+++ b/libs/ui/SurfaceComposerClient.cpp
@@ -40,8 +40,8 @@
#include <ui/SurfaceComposerClient.h>
#include <ui/Rect.h>
-#include <private/ui/SharedState.h>
#include <private/ui/LayerState.h>
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/SurfaceFlingerSynchro.h>
#define VERBOSE(...) ((void)0)
@@ -103,169 +103,6 @@ static volatile surface_flinger_cblk_t const * get_cblk()
// ---------------------------------------------------------------------------
-// these functions are used by the clients
-status_t per_client_cblk_t::validate(size_t i) const {
- if (uint32_t(i) >= NUM_LAYERS_MAX)
- return BAD_INDEX;
- if (layers[i].swapState & eInvalidSurface)
- return NO_MEMORY;
- return NO_ERROR;
-}
-
-int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags)
-{
- int32_t index;
- uint32_t state;
- int timeout = 0;
- status_t result;
- layer_cblk_t * const layer = layers + i;
- const bool blocking = flags & BLOCKING;
- const bool inspect = flags & INSPECT;
-
- do {
- state = layer->swapState;
-
- if (UNLIKELY((state&(eFlipRequested|eNextFlipPending)) == eNextFlipPending)) {
- LOGE("eNextFlipPending set but eFlipRequested not set, "
- "layer=%d (lcblk=%p), state=%08x",
- int(i), layer, int(state));
- return INVALID_OPERATION;
- }
-
- if (UNLIKELY(state&eLocked)) {
- LOGE("eLocked set when entering lock_layer(), "
- "layer=%d (lcblk=%p), state=%08x",
- int(i), layer, int(state));
- return WOULD_BLOCK;
- }
-
-
- if (state & (eFlipRequested | eNextFlipPending | eResizeRequested
- | eInvalidSurface))
- {
- int32_t resizeIndex;
- Mutex::Autolock _l(lock);
- // might block for a very short amount of time
- // will never cause the server to block (trylock())
-
- goto start_loop_here;
-
- // We block the client if:
- // eNextFlipPending: we've used both buffers already, so we need to
- // wait for one to become availlable.
- // eResizeRequested: the buffer we're going to acquire is being
- // resized. Block until it is done.
- // eFlipRequested && eBusy: the buffer we're going to acquire is
- // currently in use by the server.
- // eInvalidSurface: this is a special case, we don't block in this
- // case, we just return an error.
-
- while((state & (eNextFlipPending|eInvalidSurface)) ||
- (state & ((resizeIndex) ? eResizeBuffer1 : eResizeBuffer0)) ||
- ((state & (eFlipRequested|eBusy)) == (eFlipRequested|eBusy)) )
- {
- if (state & eInvalidSurface)
- return NO_MEMORY;
-
- if (!blocking)
- return WOULD_BLOCK;
-
- timeout = 0;
- result = cv.waitRelative(lock, seconds(1));
- if (__builtin_expect(result!=NO_ERROR, false)) {
- const int newState = layer->swapState;
- LOGW( "lock_layer timed out (is the CPU pegged?) "
- "layer=%d, lcblk=%p, state=%08x (was %08x)",
- int(i), layer, newState, int(state));
- timeout = newState != int(state);
- }
-
- start_loop_here:
- state = layer->swapState;
- resizeIndex = (state&eIndex) ^ ((state&eFlipRequested)>>1);
- }
-
- LOGW_IF(timeout,
- "lock_layer() timed out but didn't appear to need "
- "to be locked and we recovered "
- "(layer=%d, lcblk=%p, state=%08x)",
- int(i), layer, int(state));
- }
-
- // eFlipRequested is not set and cannot be set by another thread: it's
- // safe to use the first buffer without synchronization.
-
- // Choose the index depending on eFlipRequested.
- // When it's set, choose the 'other' buffer.
- index = (state&eIndex) ^ ((state&eFlipRequested)>>1);
-
- // make sure this buffer is valid
- status_t err = layer->surface[index].status;
- if (err < 0) {
- return err;
- }
-
- if (inspect) {
- // we just want to inspect this layer. don't lock it.
- goto done;
- }
-
- // last thing before we're done, we need to atomically lock the state
- } while (android_atomic_cmpxchg(state, state|eLocked, &(layer->swapState)));
-
- VERBOSE("locked layer=%d (lcblk=%p), buffer=%d, state=0x%08x",
- int(i), layer, int(index), int(state));
-
- // store the index of the locked buffer (for client use only)
- layer->flags &= ~eBufferIndex;
- layer->flags |= ((index << eBufferIndexShift) & eBufferIndex);
-
-done:
- return index;
-}
-
-uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i)
-{
- // atomically set eFlipRequested and clear eLocked and optionally
- // set eNextFlipPending if eFlipRequested was already set
-
- layer_cblk_t * const layer = layers + i;
- int32_t oldvalue, newvalue;
- do {
- oldvalue = layer->swapState;
- // get current value
-
- newvalue = oldvalue & ~eLocked;
- // clear eLocked
-
- newvalue |= eFlipRequested;
- // set eFlipRequested
-
- if (oldvalue & eFlipRequested)
- newvalue |= eNextFlipPending;
- // if eFlipRequested was already set, set eNextFlipPending
-
- } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState)));
-
- VERBOSE("request pageflip for layer=%d, buffer=%d, state=0x%08x",
- int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift),
- int(newvalue));
-
- // from this point, the server can kick in at any time and use the first
- // buffer, so we cannot use it anymore, and we must use the 'other'
- // buffer instead (or wait if it is not available yet, see lock_layer).
-
- return newvalue;
-}
-
-void per_client_cblk_t::unlock_layer(size_t i)
-{
- layer_cblk_t * const layer = layers + i;
- android_atomic_and(~eLocked, &layer->swapState);
-}
-
-// ---------------------------------------------------------------------------
-
static inline int compare_type( const layer_state_t& lhs,
const layer_state_t& rhs) {
if (lhs.surface < rhs.surface) return -1;
@@ -315,7 +152,7 @@ void SurfaceComposerClient::_init(
mControlMemory = mClient->getControlBlock();
mSignalServer = new SurfaceFlingerSynchro(sm);
- mControl = static_cast<per_client_cblk_t *>(mControlMemory->getBase());
+ mControl = static_cast<SharedClient *>(mControlMemory->getBase());
}
SurfaceComposerClient::~SurfaceComposerClient()
@@ -539,18 +376,17 @@ void SurfaceComposerClient::closeGlobalTransaction()
const size_t N = clients.size();
VERBOSE("closeGlobalTransaction (%ld clients)", N);
- if (N == 1) {
- clients[0]->closeTransaction();
- } else {
- const sp<ISurfaceComposer>& sm(_get_surface_manager());
- sm->openGlobalTransaction();
- for (size_t i=0; i<N; i++) {
- clients[i]->closeTransaction();
- }
- sm->closeGlobalTransaction();
+
+ const sp<ISurfaceComposer>& sm(_get_surface_manager());
+ sm->openGlobalTransaction();
+ for (size_t i=0; i<N; i++) {
+ clients[i]->closeTransaction();
}
+ sm->closeGlobalTransaction();
+
}
+
status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
{
const sp<ISurfaceComposer>& sm(_get_surface_manager());