diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/gui/Android.mk | 1 | ||||
-rw-r--r-- | libs/gui/ISurface.cpp | 47 | ||||
-rw-r--r-- | libs/gui/ISurfaceComposer.cpp | 25 | ||||
-rw-r--r-- | libs/gui/ISurfaceComposerClient.cpp | 56 | ||||
-rw-r--r-- | libs/gui/SharedBufferStack.cpp | 714 | ||||
-rw-r--r-- | libs/gui/Surface.cpp | 634 | ||||
-rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 17 | ||||
-rw-r--r-- | libs/gui/SurfaceTexture.cpp | 12 | ||||
-rw-r--r-- | libs/gui/SurfaceTextureClient.cpp | 6 | ||||
-rw-r--r-- | libs/gui/tests/SurfaceTextureClient_test.cpp | 114 | ||||
-rw-r--r-- | libs/gui/tests/SurfaceTexture_test.cpp | 283 | ||||
-rw-r--r-- | libs/utils/Android.mk | 1 | ||||
-rw-r--r-- | libs/utils/BlobCache.cpp | 232 | ||||
-rw-r--r-- | libs/utils/RefBase.cpp | 34 | ||||
-rw-r--r-- | libs/utils/Threads.cpp | 13 | ||||
-rw-r--r-- | libs/utils/tests/Android.mk | 1 | ||||
-rw-r--r-- | libs/utils/tests/BlobCache_test.cpp | 257 |
17 files changed, 1005 insertions, 1442 deletions
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index b5737ff..4070eba 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -16,7 +16,6 @@ LOCAL_SRC_FILES:= \ ISurfaceComposerClient.cpp \ IGraphicBufferAlloc.cpp \ LayerState.cpp \ - SharedBufferStack.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ diff --git a/libs/gui/ISurface.cpp b/libs/gui/ISurface.cpp index 23b90af..96155d7 100644 --- a/libs/gui/ISurface.cpp +++ b/libs/gui/ISurface.cpp @@ -22,9 +22,7 @@ #include <binder/Parcel.h> -#include <ui/GraphicBuffer.h> - -#include <surfaceflinger/Surface.h> +#include <gui/ISurfaceTexture.h> #include <surfaceflinger/ISurface.h> namespace android { @@ -39,30 +37,11 @@ public: { } - virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) - { + virtual sp<ISurfaceTexture> getSurfaceTexture() const { Parcel data, reply; data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(bufferIdx); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(format); - data.writeInt32(usage); - remote()->transact(REQUEST_BUFFER, data, &reply); - sp<GraphicBuffer> buffer = new GraphicBuffer(); - reply.read(*buffer); - return buffer; - } - - virtual status_t setBufferCount(int bufferCount) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(bufferCount); - remote()->transact(SET_BUFFER_COUNT, data, &reply); - status_t err = reply.readInt32(); - return err; + remote()->transact(GET_SURFACE_TEXTURE, data, &reply); + return interface_cast<ISurfaceTexture>(reply.readStrongBinder()); } }; @@ -74,23 +53,9 @@ status_t BnSurface::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { - case REQUEST_BUFFER: { - CHECK_INTERFACE(ISurface, data, reply); - int bufferIdx = data.readInt32(); - uint32_t w = data.readInt32(); - uint32_t h = data.readInt32(); - uint32_t format = data.readInt32(); - uint32_t usage = data.readInt32(); - sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, usage)); - if (buffer == NULL) - return BAD_VALUE; - return reply->write(*buffer); - } - case SET_BUFFER_COUNT: { + case GET_SURFACE_TEXTURE: { CHECK_INTERFACE(ISurface, data, reply); - int bufferCount = data.readInt32(); - status_t err = setBufferCount(bufferCount); - reply->writeInt32(err); + reply->writeStrongBinder( getSurfaceTexture()->asBinder() ); return NO_ERROR; } default: diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 8951c3f..40450a3 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -57,15 +57,6 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual sp<ISurfaceComposerClient> createClientConnection() - { - uint32_t n; - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CREATE_CLIENT_CONNECTION, data, &reply); - return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); - } - virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() { uint32_t n; @@ -174,13 +165,6 @@ public: return reply.readInt32(); } - virtual void signal() const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); - } - virtual bool authenticateSurface(const sp<ISurface>& surface) const { Parcel data, reply; @@ -229,11 +213,6 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); } break; - case CREATE_CLIENT_CONNECTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> b = createClientConnection()->asBinder(); - reply->writeStrongBinder(b); - } break; case CREATE_GRAPHIC_BUFFER_ALLOC: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); @@ -270,10 +249,6 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case SIGNAL: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - signal(); - } break; case GET_CBLK: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = getCblk()->asBinder(); diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index ea38e08..8d83392 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -50,9 +50,7 @@ namespace android { enum { - GET_CBLK = IBinder::FIRST_CALL_TRANSACTION, - GET_TOKEN, - CREATE_SURFACE, + CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, DESTROY_SURFACE, SET_STATE }; @@ -65,23 +63,6 @@ public: { } - virtual sp<IMemoryHeap> getControlBlock() const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - remote()->transact(GET_CBLK, data, &reply); - return interface_cast<IMemoryHeap>(reply.readStrongBinder()); - } - - virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor()); - data.writeStrongBinder(sur->asBinder()); - remote()->transact(GET_TOKEN, data, &reply); - return reply.readInt32(); - } - virtual sp<ISurface> createSurface( surface_data_t* params, const String8& name, DisplayID display, @@ -131,41 +112,6 @@ IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClie status_t BnSurfaceComposerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - // codes that don't require permission check - - switch(code) { - case GET_CBLK: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<IMemoryHeap> ctl(getControlBlock()); - reply->writeStrongBinder(ctl->asBinder()); - return NO_ERROR; - } break; - case GET_TOKEN: { - CHECK_INTERFACE(ISurfaceComposerClient, data, reply); - sp<ISurface> sur = interface_cast<ISurface>(data.readStrongBinder()); - ssize_t token = getTokenForSurface(sur); - reply->writeInt32(token); - return NO_ERROR; - } break; - } - - // these must be checked - - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int uid = ipc->getCallingUid(); - const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - LOGE("Permission Denial: " - "can't openGlobalTransaction pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - } - switch(code) { case CREATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposerClient, data, reply); diff --git a/libs/gui/SharedBufferStack.cpp b/libs/gui/SharedBufferStack.cpp deleted file mode 100644 index 7505d53..0000000 --- a/libs/gui/SharedBufferStack.cpp +++ /dev/null @@ -1,714 +0,0 @@ -/* - * 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/surfaceflinger/SharedBufferStack.h> - -#include <ui/Rect.h> -#include <ui/Region.h> - -#define DEBUG_ATOMICS 0 - -namespace android { -// ---------------------------------------------------------------------------- - -SharedClient::SharedClient() - : lock(Mutex::SHARED), cv(Condition::SHARED) -{ -} - -SharedClient::~SharedClient() { -} - - -// these functions are used by the clients -status_t SharedClient::validate(size_t i) const { - if (uint32_t(i) >= uint32_t(SharedBufferStack::NUM_LAYERS_MAX)) - return BAD_INDEX; - return surfaces[i].status; -} - -// ---------------------------------------------------------------------------- - - -SharedBufferStack::SharedBufferStack() -{ -} - -void SharedBufferStack::init(int32_t i) -{ - status = NO_ERROR; - identity = i; -} - -status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - - buffers[buffer].crop.l = uint16_t(crop.left); - buffers[buffer].crop.t = uint16_t(crop.top); - buffers[buffer].crop.r = uint16_t(crop.right); - buffers[buffer].crop.b = uint16_t(crop.bottom); - return NO_ERROR; -} - -status_t SharedBufferStack::setTransform(int buffer, uint8_t transform) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - buffers[buffer].transform = transform; - return NO_ERROR; -} - -status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return BAD_INDEX; - - FlatRegion& reg(buffers[buffer].dirtyRegion); - if (dirty.isEmpty()) { - reg.count = 0; - return NO_ERROR; - } - - size_t count; - Rect const* r = dirty.getArray(&count); - if (count > FlatRegion::NUM_RECT_MAX) { - const Rect bounds(dirty.getBounds()); - reg.count = 1; - reg.rects[0].l = uint16_t(bounds.left); - reg.rects[0].t = uint16_t(bounds.top); - reg.rects[0].r = uint16_t(bounds.right); - reg.rects[0].b = uint16_t(bounds.bottom); - } else { - reg.count = count; - for (size_t i=0 ; i<count ; i++) { - reg.rects[i].l = uint16_t(r[i].left); - reg.rects[i].t = uint16_t(r[i].top); - reg.rects[i].r = uint16_t(r[i].right); - reg.rects[i].b = uint16_t(r[i].bottom); - } - } - return NO_ERROR; -} - -Region SharedBufferStack::getDirtyRegion(int buffer) const -{ - Region res; - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return res; - - const FlatRegion& reg(buffers[buffer].dirtyRegion); - if (reg.count > FlatRegion::NUM_RECT_MAX) - return res; - - if (reg.count == 1) { - const Rect r( - reg.rects[0].l, - reg.rects[0].t, - reg.rects[0].r, - reg.rects[0].b); - res.set(r); - } else { - for (size_t i=0 ; i<reg.count ; i++) { - const Rect r( - reg.rects[i].l, - reg.rects[i].t, - reg.rects[i].r, - reg.rects[i].b); - res.orSelf(r); - } - } - return res; -} - -Rect SharedBufferStack::getCrop(int buffer) const -{ - Rect res(-1, -1); - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return res; - res.left = buffers[buffer].crop.l; - res.top = buffers[buffer].crop.t; - res.right = buffers[buffer].crop.r; - res.bottom = buffers[buffer].crop.b; - return res; -} - -uint32_t SharedBufferStack::getTransform(int buffer) const -{ - if (uint32_t(buffer) >= NUM_BUFFER_MAX) - return 0; - return buffers[buffer].transform; -} - - -// ---------------------------------------------------------------------------- - -SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, - int surface, int32_t identity) - : mSharedClient(sharedClient), - mSharedStack(sharedClient->surfaces + surface), - mIdentity(identity) -{ -} - -SharedBufferBase::~SharedBufferBase() -{ -} - -status_t SharedBufferBase::getStatus() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.status; -} - -int32_t SharedBufferBase::getIdentity() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.identity; -} - -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, identity=%d, status=%d", - prefix, stack.head, stack.available, stack.queued, - stack.reallocMask, stack.identity, stack.status); - result.append(buffer); - result.append("\n"); - return result; -} - -status_t SharedBufferBase::waitForCondition(const ConditionBase& condition) -{ - const SharedBufferStack& stack( *mSharedStack ); - SharedClient& client( *mSharedClient ); - const nsecs_t TIMEOUT = s2ns(1); - const int identity = mIdentity; - - Mutex::Autolock _l(client.lock); - while ((condition()==false) && - (stack.identity == identity) && - (stack.status == NO_ERROR)) - { - status_t err = client.cv.waitRelative(client.lock, TIMEOUT); - // handle errors and timeouts - if (CC_UNLIKELY(err != NO_ERROR)) { - if (err == TIMED_OUT) { - if (condition()) { - LOGE("waitForCondition(%s) timed out (identity=%d), " - "but condition is true! We recovered but it " - "shouldn't happen." , condition.name(), stack.identity); - break; - } else { - LOGW("waitForCondition(%s) timed out " - "(identity=%d, status=%d). " - "CPU may be pegged. trying again.", condition.name(), - stack.identity, stack.status); - } - } else { - LOGE("waitForCondition(%s) error (%s) ", - condition.name(), strerror(-err)); - return err; - } - } - } - return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status; -} -// ============================================================================ -// conditions and updates -// ============================================================================ - -SharedBufferClient::DequeueCondition::DequeueCondition( - SharedBufferClient* sbc) : ConditionBase(sbc) { -} -bool SharedBufferClient::DequeueCondition::operator()() const { - return stack.available > 0; -} - -SharedBufferClient::LockCondition::LockCondition( - SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) { -} -bool SharedBufferClient::LockCondition::operator()() const { - // NOTE: if stack.head is messed up, we could crash the client - // or cause some drawing artifacts. This is okay, as long as it is - // limited to the client. - return (buf != stack.index[stack.head]); -} - -SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition( - SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs), - mNumBuffers(numBuffers) { -} -bool SharedBufferServer::BuffersAvailableCondition::operator()() const { - return stack.available == mNumBuffers; -} - -// ---------------------------------------------------------------------------- - -SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) - : UpdateBase(sbb) { -} -ssize_t SharedBufferClient::QueueUpdate::operator()() { - android_atomic_inc(&stack.queued); - return NO_ERROR; -} - -SharedBufferClient::DequeueUpdate::DequeueUpdate(SharedBufferBase* sbb) - : UpdateBase(sbb) { -} -ssize_t SharedBufferClient::DequeueUpdate::operator()() { - if (android_atomic_dec(&stack.available) == 0) { - LOGW("dequeue probably called from multiple threads!"); - } - return NO_ERROR; -} - -SharedBufferClient::CancelUpdate::CancelUpdate(SharedBufferBase* sbb, - int tail, int buf) - : UpdateBase(sbb), tail(tail), buf(buf) { -} -ssize_t SharedBufferClient::CancelUpdate::operator()() { - stack.index[tail] = buf; - android_atomic_inc(&stack.available); - return NO_ERROR; -} - -SharedBufferServer::RetireUpdate::RetireUpdate( - SharedBufferBase* sbb, int numBuffers) - : UpdateBase(sbb), numBuffers(numBuffers) { -} -ssize_t SharedBufferServer::RetireUpdate::operator()() { - int32_t head = stack.head; - if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - // 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)); - - // lock the buffer before advancing head, which automatically unlocks - // the buffer we preventively locked upon entering this function - - head = (head + 1) % numBuffers; - const int8_t headBuf = stack.index[head]; - stack.headBuf = headBuf; - - // head is only modified here, so we don't need to use cmpxchg - 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; -} - -SharedBufferServer::StatusUpdate::StatusUpdate( - SharedBufferBase* sbb, status_t status) - : UpdateBase(sbb), status(status) { -} - -ssize_t SharedBufferServer::StatusUpdate::operator()() { - android_atomic_write(status, &stack.status); - return NO_ERROR; -} - -// ============================================================================ - -SharedBufferClient::SharedBufferClient(SharedClient* sharedClient, - int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, identity), - mNumBuffers(num), tail(0) -{ - SharedBufferStack& stack( *mSharedStack ); - tail = computeTail(); - queued_head = stack.head; -} - -int32_t SharedBufferClient::computeTail() const -{ - SharedBufferStack& stack( *mSharedStack ); - return (mNumBuffers + stack.head - stack.available + 1) % mNumBuffers; -} - -ssize_t SharedBufferClient::dequeue() -{ - SharedBufferStack& stack( *mSharedStack ); - - RWLock::AutoRLock _rd(mLock); - - const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); - - //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); - - DequeueUpdate update(this); - updateCondition( update ); - - int dequeued = stack.index[tail]; - tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); - LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", - dequeued, tail, dump("").string()); - - mDequeueTime[dequeued] = dequeueTime; - - return dequeued; -} - -status_t SharedBufferClient::undoDequeue(int buf) -{ - return cancel(buf); -} - -status_t SharedBufferClient::cancel(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - // calculate the new position of the tail index (essentially tail--) - int localTail = (tail + mNumBuffers - 1) % mNumBuffers; - CancelUpdate update(this, localTail, buf); - status_t err = updateCondition( update ); - if (err == NO_ERROR) { - tail = localTail; - } - return err; -} - -status_t SharedBufferClient::lock(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - SharedBufferStack& stack( *mSharedStack ); - LockCondition condition(this, buf); - status_t err = waitForCondition(condition); - return err; -} - -status_t SharedBufferClient::queue(int buf) -{ - RWLock::AutoRLock _rd(mLock); - - SharedBufferStack& stack( *mSharedStack ); - - queued_head = (queued_head + 1) % mNumBuffers; - stack.index[queued_head] = buf; - - QueueUpdate update(this); - status_t err = updateCondition( update ); - LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); - - const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); - stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); - - return err; -} - -bool SharedBufferClient::needNewBuffer(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - const uint32_t mask = 1<<(31-buf); - return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0; -} - -status_t SharedBufferClient::setCrop(int buf, const Rect& crop) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setCrop(buf, crop); -} - -status_t SharedBufferClient::setTransform(int buf, uint32_t transform) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setTransform(buf, uint8_t(transform)); -} - -status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.setDirtyRegion(buf, reg); -} - -status_t SharedBufferClient::setBufferCount( - int bufferCount, const SetBufferCountCallback& ipc) -{ - SharedBufferStack& stack( *mSharedStack ); - if (uint32_t(bufferCount) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - if (uint32_t(bufferCount) < SharedBufferStack::NUM_BUFFER_MIN) - return BAD_VALUE; - - RWLock::AutoWLock _wr(mLock); - - status_t err = ipc(bufferCount); - if (err == NO_ERROR) { - mNumBuffers = bufferCount; - queued_head = (stack.head + stack.queued) % mNumBuffers; - tail = computeTail(); - } - return err; -} - -// ---------------------------------------------------------------------------- - -SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, - int surface, int num, int32_t identity) - : SharedBufferBase(sharedClient, surface, identity), - mNumBuffers(num) -{ - mSharedStack->init(identity); - mSharedStack->token = surface; - mSharedStack->head = num-1; - mSharedStack->available = num; - mSharedStack->queued = 0; - mSharedStack->reallocMask = 0; - memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); - for (int i=0 ; i<num ; i++) { - mBufferList.add(i); - mSharedStack->index[i] = i; - } -} - -SharedBufferServer::~SharedBufferServer() -{ -} - -ssize_t SharedBufferServer::retireAndLock() -{ - RWLock::AutoRLock _l(mLock); - - RetireUpdate update(this, mNumBuffers); - ssize_t buf = updateCondition( update ); - if (buf >= 0) { - if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - SharedBufferStack& stack( *mSharedStack ); - buf = stack.index[buf]; - LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", - int(buf), dump("").string()); - } - return buf; -} - -void SharedBufferServer::setStatus(status_t status) -{ - if (status < NO_ERROR) { - StatusUpdate update(this, status); - updateCondition( update ); - } -} - -status_t SharedBufferServer::reallocateAll() -{ - RWLock::AutoRLock _l(mLock); - - SharedBufferStack& stack( *mSharedStack ); - uint32_t mask = mBufferList.getMask(); - android_atomic_or(mask, &stack.reallocMask); - return NO_ERROR; -} - -status_t SharedBufferServer::reallocateAllExcept(int buffer) -{ - RWLock::AutoRLock _l(mLock); - - SharedBufferStack& stack( *mSharedStack ); - BufferList temp(mBufferList); - temp.remove(buffer); - uint32_t mask = temp.getMask(); - android_atomic_or(mask, &stack.reallocMask); - return NO_ERROR; -} - -int32_t SharedBufferServer::getQueuedCount() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.queued; -} - -Region SharedBufferServer::getDirtyRegion(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getDirtyRegion(buf); -} - -Rect SharedBufferServer::getCrop(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getCrop(buf); -} - -uint32_t SharedBufferServer::getTransform(int buf) const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.getTransform(buf); -} - -/* - * NOTE: this is not thread-safe on the server-side, meaning - * 'head' cannot move during this operation. The client-side - * can safely operate an usual. - * - */ -status_t SharedBufferServer::resize(int newNumBuffers) -{ - if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN || - (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) { - return BAD_VALUE; - } - - RWLock::AutoWLock _l(mLock); - - if (newNumBuffers < mNumBuffers) { - return shrink(newNumBuffers); - } else { - return grow(newNumBuffers); - } -} - -status_t SharedBufferServer::grow(int newNumBuffers) -{ - SharedBufferStack& stack( *mSharedStack ); - const int numBuffers = mNumBuffers; - const int extra = newNumBuffers - numBuffers; - - // read the head, make sure it's valid - int32_t head = stack.head; - if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) - return BAD_VALUE; - - int base = numBuffers; - int32_t avail = stack.available; - int tail = head - avail + 1; - - if (tail >= 0) { - int8_t* const index = const_cast<int8_t*>(stack.index); - const int nb = numBuffers - head; - memmove(&index[head + extra], &index[head], nb); - base = head; - // move head 'extra' ahead, this doesn't impact stack.index[head]; - stack.head = head + extra; - } - stack.available += extra; - - // fill the new free space with unused buffers - BufferList::const_iterator curr(mBufferList.free_begin()); - for (int i=0 ; i<extra ; i++) { - stack.index[base+i] = *curr; - mBufferList.add(*curr); - ++curr; - } - - mNumBuffers = newNumBuffers; - return NO_ERROR; -} - -status_t SharedBufferServer::shrink(int newNumBuffers) -{ - SharedBufferStack& stack( *mSharedStack ); - - // Shrinking is only supported if there are no buffers currently dequeued. - int32_t avail = stack.available; - int32_t queued = stack.queued; - if (avail + queued != mNumBuffers) { - return INVALID_OPERATION; - } - - // Wait for any queued buffers to be displayed. - BuffersAvailableCondition condition(this, mNumBuffers); - status_t err = waitForCondition(condition); - if (err < 0) { - return err; - } - - // Reset head to index 0 and make it refer to buffer 0. The same renaming - // (head -> 0) is done in the BufferManager. - int32_t head = stack.head; - int8_t* index = const_cast<int8_t*>(stack.index); - for (int8_t i = 0; i < newNumBuffers; i++) { - index[i] = i; - } - stack.head = 0; - stack.headBuf = 0; - - // Free the buffers from the end of the list that are no longer needed. - for (int i = newNumBuffers; i < mNumBuffers; i++) { - mBufferList.remove(i); - } - - // Tell the client to reallocate all the buffers. - reallocateAll(); - - mNumBuffers = newNumBuffers; - stack.available = newNumBuffers; - - return NO_ERROR; -} - -SharedBufferStack::Statistics SharedBufferServer::getStats() const -{ - SharedBufferStack& stack( *mSharedStack ); - return stack.stats; -} - -// --------------------------------------------------------------------------- -status_t SharedBufferServer::BufferList::add(int value) -{ - if (uint32_t(value) >= mCapacity) - return BAD_VALUE; - uint32_t mask = 1<<(31-value); - if (mList & mask) - return ALREADY_EXISTS; - mList |= mask; - return NO_ERROR; -} - -status_t SharedBufferServer::BufferList::remove(int value) -{ - if (uint32_t(value) >= mCapacity) - return BAD_VALUE; - uint32_t mask = 1<<(31-value); - if (!(mList & mask)) - return NAME_NOT_FOUND; - mList &= ~mask; - return NO_ERROR; -} - - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 0c5767b..4d1d923 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -21,13 +21,15 @@ #include <sys/types.h> #include <sys/stat.h> -#include <utils/Errors.h> -#include <utils/threads.h> #include <utils/CallStack.h> +#include <utils/Errors.h> #include <utils/Log.h> +#include <utils/threads.h> -#include <binder/IPCThreadState.h> #include <binder/IMemory.h> +#include <binder/IPCThreadState.h> + +#include <gui/SurfaceTextureClient.h> #include <ui/DisplayInfo.h> #include <ui/GraphicBuffer.h> @@ -35,12 +37,11 @@ #include <ui/GraphicLog.h> #include <ui/Rect.h> -#include <surfaceflinger/Surface.h> #include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> #include <surfaceflinger/SurfaceComposerClient.h> -#include <private/surfaceflinger/SharedBufferStack.h> #include <private/surfaceflinger/LayerState.h> namespace android { @@ -273,58 +274,10 @@ sp<Surface> SurfaceControl::getSurface() const // Surface // ============================================================================ -class SurfaceClient : public Singleton<SurfaceClient> -{ - // all these attributes are constants - sp<ISurfaceComposer> mComposerService; - sp<ISurfaceComposerClient> mClient; - status_t mStatus; - SharedClient* mControl; - sp<IMemoryHeap> mControlMemory; - - SurfaceClient() - : Singleton<SurfaceClient>(), mStatus(NO_INIT) - { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - mComposerService = sf; - mClient = sf->createClientConnection(); - if (mClient != NULL) { - mControlMemory = mClient->getControlBlock(); - if (mControlMemory != NULL) { - mControl = static_cast<SharedClient *>( - mControlMemory->getBase()); - if (mControl) { - mStatus = NO_ERROR; - } - } - } - } - friend class Singleton<SurfaceClient>; -public: - status_t initCheck() const { - return mStatus; - } - SharedClient* getSharedClient() const { - return mControl; - } - ssize_t getTokenForSurface(const sp<ISurface>& sur) const { - // TODO: we could cache a few tokens here to avoid an IPC - return mClient->getTokenForSurface(sur); - } - void signalServer() const { - mComposerService->signal(); - } -}; - -ANDROID_SINGLETON_STATIC_INSTANCE(SurfaceClient); - // --------------------------------------------------------------------------- Surface::Surface(const sp<SurfaceControl>& surface) - : mBufferMapper(GraphicBufferMapper::get()), - mClient(SurfaceClient::getInstance()), - mSharedBufferClient(NULL), - mInitCheck(NO_INIT), + : mInitCheck(NO_INIT), mSurface(surface->mSurface), mIdentity(surface->mIdentity), mFormat(surface->mFormat), mFlags(surface->mFlags), @@ -334,10 +287,7 @@ Surface::Surface(const sp<SurfaceControl>& surface) } Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) - : mBufferMapper(GraphicBufferMapper::get()), - mClient(SurfaceClient::getInstance()), - mSharedBufferClient(NULL), - mInitCheck(NO_INIT) + : mInitCheck(NO_INIT) { mSurface = interface_cast<ISurface>(ref); mIdentity = parcel.readInt32(); @@ -382,7 +332,6 @@ status_t Surface::writeToParcel( } - Mutex Surface::sCachedSurfacesLock; DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces; @@ -422,32 +371,29 @@ void Surface::init() ANativeWindow::query = query; ANativeWindow::perform = perform; - DisplayInfo dinfo; - SurfaceComposerClient::getDisplayInfo(0, &dinfo); - const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; - const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; - // FIXME: set real values here - const_cast<int&>(ANativeWindow::minSwapInterval) = 1; - const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; - const_cast<uint32_t&>(ANativeWindow::flags) = 0; - - mNextBufferTransform = 0; - mConnected = 0; - mSwapRectangle.makeInvalid(); - mNextBufferCrop = Rect(0,0); - // two buffers by default - mBuffers.setCapacity(2); - mBuffers.insertAt(0, 2); - - if (mSurface != 0 && mClient.initCheck() == NO_ERROR) { - int32_t token = mClient.getTokenForSurface(mSurface); - if (token >= 0) { - mSharedBufferClient = new SharedBufferClient( - mClient.getSharedClient(), token, 2, mIdentity); - mInitCheck = mClient.getSharedClient()->validate(token); - } else { - LOGW("Not initializing the shared buffer client because token = %d", - token); + if (mSurface != NULL) { + sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture()); + LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface"); + if (surfaceTexture != NULL) { + mSurfaceTextureClient = new SurfaceTextureClient(surfaceTexture); + mSurfaceTextureClient->setUsage(GraphicBuffer::USAGE_HW_RENDER); + } + + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + const_cast<float&>(ANativeWindow::xdpi) = dinfo.xdpi; + const_cast<float&>(ANativeWindow::ydpi) = dinfo.ydpi; + + const_cast<int&>(ANativeWindow::minSwapInterval) = + mSurfaceTextureClient->minSwapInterval; + + const_cast<int&>(ANativeWindow::maxSwapInterval) = + mSurfaceTextureClient->maxSwapInterval; + + const_cast<uint32_t&>(ANativeWindow::flags) = 0; + + if (mSurfaceTextureClient != 0) { + mInitCheck = NO_ERROR; } } } @@ -456,9 +402,8 @@ Surface::~Surface() { // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. - mBuffers.clear(); + mSurfaceTextureClient.clear(); mSurface.clear(); - delete mSharedBufferClient; IPCThreadState::self()->flushCommands(); } @@ -473,32 +418,6 @@ status_t Surface::validate(bool inCancelBuffer) const LOGE("invalid token (identity=%u)", mIdentity); return mInitCheck; } - - // verify the identity of this surface - uint32_t identity = mSharedBufferClient->getIdentity(); - if (mIdentity != identity) { - LOGE("[Surface] using an invalid surface, " - "identity=%u should be %d", - mIdentity, identity); - CallStack stack; - stack.update(); - stack.dump("Surface"); - return BAD_INDEX; - } - - // check the surface didn't become invalid - status_t err = mSharedBufferClient->getStatus(); - if (err != NO_ERROR) { - if (!inCancelBuffer) { - LOGE("surface (identity=%u) is invalid, err=%d (%s)", - mIdentity, err, strerror(-err)); - CallStack stack; - stack.update(); - stack.dump("Surface"); - } - return err; - } - return NO_ERROR; } @@ -509,7 +428,8 @@ sp<IBinder> Surface::asBinder() const { // ---------------------------------------------------------------------------- int Surface::setSwapInterval(ANativeWindow* window, int interval) { - return 0; + Surface* self = getSelf(window); + return self->setSwapInterval(interval); } int Surface::dequeueBuffer(ANativeWindow* window, @@ -554,383 +474,52 @@ int Surface::perform(ANativeWindow* window, // ---------------------------------------------------------------------------- -bool Surface::needNewBuffer(int bufIdx, - uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const -{ - Mutex::Autolock _l(mSurfaceLock); - - // Always call needNewBuffer(), since it clears the needed buffers flags - bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); - bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); - bool newNeewBuffer = needNewBuffer || !validBuffer; - if (newNeewBuffer) { - mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); - } - return newNeewBuffer; +int Surface::setSwapInterval(int interval) { + return mSurfaceTextureClient->setSwapInterval(interval); } -int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_APP_DEQUEUE_BEFORE, mIdentity, -1); - - ssize_t bufIdx = mSharedBufferClient->dequeue(); - - logger.log(GraphicLog::SF_APP_DEQUEUE_AFTER, mIdentity, bufIdx); - - if (bufIdx < 0) { - LOGE("error dequeuing a buffer (%s)", strerror(bufIdx)); - return bufIdx; - } - - // grow the buffer array if needed - const size_t size = mBuffers.size(); - const size_t needed = bufIdx+1; - if (size < needed) { - mBuffers.insertAt(size, needed-size); - } - - uint32_t w, h, format, usage; - if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { - err = getBufferLocked(bufIdx, w, h, format, usage); - LOGE_IF(err, "getBufferLocked(%ld, %u, %u, %u, %08x) failed (%s)", - bufIdx, w, h, format, usage, strerror(-err)); - if (err == NO_ERROR) { - // reset the width/height with the what we get from the buffer - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - 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 - const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); - if (!err && backBuffer==0) { - err = NO_MEMORY; - } - +int Surface::dequeueBuffer(ANativeWindowBuffer** buffer) { + status_t err = mSurfaceTextureClient->dequeueBuffer(buffer); if (err == NO_ERROR) { - mDirtyRegion.set(backBuffer->width, backBuffer->height); - *buffer = backBuffer.get(); - } else { - mSharedBufferClient->undoDequeue(bufIdx); + mDirtyRegion.set(buffer[0]->width, buffer[0]->height); } - return err; } -int Surface::cancelBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(true); - switch (err) { - case NO_ERROR: - // no error, common case - break; - case BAD_INDEX: - // legitimate errors here - return err; - default: - // other errors happen because the surface is now invalid, - // for instance because it has been destroyed. In this case, - // we just fail silently (canceling a buffer is not technically - // an error at this point) - return NO_ERROR; - } - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - err = mSharedBufferClient->cancel(bufIdx); - - LOGE_IF(err, "error canceling buffer %d (%s)", bufIdx, strerror(-err)); - return err; +int Surface::cancelBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->cancelBuffer(buffer); } - -int Surface::lockBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_APP_LOCK_BEFORE, mIdentity, bufIdx); - - err = mSharedBufferClient->lock(bufIdx); - - logger.log(GraphicLog::SF_APP_LOCK_AFTER, mIdentity, bufIdx); - - LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err)); - return err; +int Surface::lockBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->lockBuffer(buffer); } -int Surface::queueBuffer(ANativeWindowBuffer* buffer) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - if (mSwapRectangle.isValid()) { - mDirtyRegion.set(mSwapRectangle); - } - - int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); - - GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx); - - mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); - mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); - mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); - err = mSharedBufferClient->queue(bufIdx); - LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); - - if (err == NO_ERROR) { - // TODO: can we avoid this IPC if we know there is one pending? - mClient.signalServer(); - } - return err; +int Surface::queueBuffer(ANativeWindowBuffer* buffer) { + return mSurfaceTextureClient->queueBuffer(buffer); } -int Surface::query(int what, int* value) const -{ +int Surface::query(int what, int* value) const { 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); + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: + // TODO: this is not needed anymore + *value = 1; return NO_ERROR; - case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: - *value = MIN_UNDEQUEUED_BUFFERS; - return NO_ERROR; - case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - *value = sf->authenticateSurface(mSurface) ? 1 : 0; - return NO_ERROR; - } case NATIVE_WINDOW_CONCRETE_TYPE: + // TODO: this is not needed anymore *value = NATIVE_WINDOW_SURFACE; return NO_ERROR; } - return BAD_VALUE; -} - -int Surface::perform(int operation, va_list args) -{ - status_t err = validate(); - if (err != NO_ERROR) - return err; - - int res = NO_ERROR; - switch (operation) { - case NATIVE_WINDOW_SET_USAGE: - dispatch_setUsage( args ); - break; - case NATIVE_WINDOW_CONNECT: - res = dispatch_connect( args ); - break; - case NATIVE_WINDOW_DISCONNECT: - res = dispatch_disconnect( args ); - break; - case NATIVE_WINDOW_SET_CROP: - res = dispatch_crop( args ); - break; - case NATIVE_WINDOW_SET_BUFFER_COUNT: - res = dispatch_set_buffer_count( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: - res = dispatch_set_buffers_geometry( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: - res = dispatch_set_buffers_transform( args ); - break; - case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: - res = dispatch_set_buffers_timestamp( args ); - break; - default: - res = NAME_NOT_FOUND; - break; - } - return res; -} - -void Surface::dispatch_setUsage(va_list args) { - int usage = va_arg(args, int); - setUsage( usage ); -} -int Surface::dispatch_connect(va_list args) { - int api = va_arg(args, int); - return connect( api ); -} -int Surface::dispatch_disconnect(va_list args) { - int api = va_arg(args, int); - return disconnect( api ); -} -int Surface::dispatch_crop(va_list args) { - android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); - return crop( reinterpret_cast<Rect const*>(rect) ); -} -int Surface::dispatch_set_buffer_count(va_list args) { - size_t bufferCount = va_arg(args, size_t); - return setBufferCount(bufferCount); -} -int Surface::dispatch_set_buffers_geometry(va_list args) { - int w = va_arg(args, int); - int h = va_arg(args, int); - int f = va_arg(args, int); - return setBuffersGeometry(w, h, f); -} - -int Surface::dispatch_set_buffers_transform(va_list args) { - int transform = va_arg(args, int); - return setBuffersTransform(transform); -} - -int Surface::dispatch_set_buffers_timestamp(va_list args) { - int64_t timestamp = va_arg(args, int64_t); - return setBuffersTimestamp(timestamp); -} - -void Surface::setUsage(uint32_t reqUsage) -{ - Mutex::Autolock _l(mSurfaceLock); - mBufferInfo.set(reqUsage); -} - -int Surface::connect(int api) -{ - Mutex::Autolock _l(mSurfaceLock); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnected) { - err = -EINVAL; - } else { - mConnected = api; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -int Surface::disconnect(int api) -{ - Mutex::Autolock _l(mSurfaceLock); - int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - if (mConnected == api) { - mConnected = 0; - } else { - err = -EINVAL; - } - break; - default: - err = -EINVAL; - break; - } - return err; -} - -int Surface::crop(Rect const* rect) -{ - Mutex::Autolock _l(mSurfaceLock); - // TODO: validate rect size - - if (rect == NULL || rect->isEmpty()) { - mNextBufferCrop = Rect(0,0); - } else { - mNextBufferCrop = *rect; - } - - return NO_ERROR; + return mSurfaceTextureClient->query(what, value); } -int Surface::setBufferCount(int bufferCount) -{ - sp<ISurface> s(mSurface); - if (s == 0) return NO_INIT; - - class SetBufferCountIPC : public SharedBufferClient::SetBufferCountCallback { - sp<ISurface> surface; - virtual status_t operator()(int bufferCount) const { - return surface->setBufferCount(bufferCount); - } - public: - SetBufferCountIPC(const sp<ISurface>& surface) : surface(surface) { } - } ipc(s); - - status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); - LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", - bufferCount, strerror(-err)); - - if (err == NO_ERROR) { - // Clear out any references to the old buffers. - mBuffers.clear(); - } - - return err; -} - -int Surface::setBuffersGeometry(int w, int h, int format) -{ - if (w<0 || h<0 || format<0) - return BAD_VALUE; - - if ((w && !h) || (!w && h)) - return BAD_VALUE; - - Mutex::Autolock _l(mSurfaceLock); - if (mConnected == NATIVE_WINDOW_API_EGL) { - return INVALID_OPERATION; - } - - mBufferInfo.set(w, h, format); - if (format != 0) { - // we update the format of the surface as reported by query(). - // this is to allow applications to change the format of a surface's - // buffer, and have it reflected in EGL; which is needed for - // EGLConfig validation. - mFormat = format; - } - - mNextBufferCrop = Rect(0,0); - - return NO_ERROR; -} - -int Surface::setBuffersTransform(int transform) -{ - Mutex::Autolock _l(mSurfaceLock); - mNextBufferTransform = transform; - return NO_ERROR; -} - -int Surface::setBuffersTimestamp(int64_t timestamp) -{ - // Surface doesn't really have anything meaningful to do with timestamps - // so they'll just be dropped here. - return NO_ERROR; +int Surface::perform(int operation, va_list args) { + return mSurfaceTextureClient->perform(operation, args); } // ---------------------------------------------------------------------------- -int Surface::getConnectedApi() const -{ - Mutex::Autolock _l(mSurfaceLock); - return mConnected; +int Surface::getConnectedApi() const { + return mSurfaceTextureClient->getConnectedApi(); } // ---------------------------------------------------------------------------- @@ -967,16 +556,17 @@ status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) } // we're intending to do software rendering from this point - setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + mSurfaceTextureClient->setUsage( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); ANativeWindowBuffer* out; - status_t err = dequeueBuffer(&out); + status_t err = mSurfaceTextureClient->dequeueBuffer(&out); LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err)); if (err == NO_ERROR) { sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); - err = lockBuffer(backBuffer.get()); - LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)", - getBufferIndex(backBuffer), strerror(-err)); + err = mSurfaceTextureClient->lockBuffer(backBuffer.get()); + LOGE_IF(err, "lockBuffer (handle=%p) failed (%s)", + backBuffer->handle, strerror(-err)); if (err == NO_ERROR) { const Rect bounds(backBuffer->width, backBuffer->height); const Region boundsRegion(bounds); @@ -1043,110 +633,14 @@ status_t Surface::unlockAndPost() status_t err = mLockedBuffer->unlock(); LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle); - err = queueBuffer(mLockedBuffer.get()); - LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)", - getBufferIndex(mLockedBuffer), strerror(-err)); + err = mSurfaceTextureClient->queueBuffer(mLockedBuffer.get()); + LOGE_IF(err, "queueBuffer (handle=%p) failed (%s)", + mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; return err; } -void Surface::setSwapRectangle(const Rect& r) { - Mutex::Autolock _l(mSurfaceLock); - mSwapRectangle = r; -} - -int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const -{ - int idx = buffer->getIndex(); - if (idx < 0) { - // The buffer doesn't have an index set. See if the handle the same as - // one of the buffers for which we do know the index. This can happen - // e.g. if GraphicBuffer is used to wrap an ANativeWindowBuffer that - // was dequeued from an ANativeWindow. - for (size_t i = 0; i < mBuffers.size(); i++) { - if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) { - idx = mBuffers[i]->getIndex(); - break; - } - } - } - return idx; -} - -status_t Surface::getBufferLocked(int index, - uint32_t w, uint32_t h, uint32_t format, uint32_t usage) -{ - sp<ISurface> s(mSurface); - if (s == 0) return NO_INIT; - - status_t err = NO_MEMORY; - - // free the current buffer - sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); - if (currentBuffer != 0) { - currentBuffer.clear(); - } - - sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); - LOGE_IF(buffer==0, - "ISurface::getBuffer(%d, %08x) returned NULL", - index, usage); - if (buffer != 0) { // this should always happen by construction - LOGE_IF(buffer->handle == NULL, - "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " - "returned a buffer with a null handle", - mIdentity, index, w, h, format, usage); - err = mSharedBufferClient->getStatus(); - LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); - if (!err && buffer->handle != NULL) { - currentBuffer = buffer; - currentBuffer->setIndex(index); - } else { - err = err<0 ? err : status_t(NO_MEMORY); - } - } - return err; -} - -// ---------------------------------------------------------------------------- -Surface::BufferInfo::BufferInfo() - : mWidth(0), mHeight(0), mFormat(0), - mUsage(GRALLOC_USAGE_HW_RENDER), mDirty(0) -{ -} - -void Surface::BufferInfo::set(uint32_t w, uint32_t h, uint32_t format) { - if ((mWidth != w) || (mHeight != h) || (mFormat != format)) { - mWidth = w; - mHeight = h; - mFormat = format; - mDirty |= GEOMETRY; - } -} - -void Surface::BufferInfo::set(uint32_t usage) { - mUsage = usage; -} - -void Surface::BufferInfo::get(uint32_t *pWidth, uint32_t *pHeight, - uint32_t *pFormat, uint32_t *pUsage) const { - *pWidth = mWidth; - *pHeight = mHeight; - *pFormat = mFormat; - *pUsage = mUsage; -} - -bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { - // make sure we AT LEAST have the usage flags we want - if (mDirty || buffer==0 || - ((buffer->usage & mUsage) != mUsage)) { - mDirty = 0; - return false; - } - return true; -} - // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index a1ff2c1..1678711 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -20,19 +20,20 @@ #include <sys/types.h> #include <utils/Errors.h> -#include <utils/threads.h> -#include <utils/SortedVector.h> #include <utils/Log.h> #include <utils/Singleton.h> +#include <utils/SortedVector.h> +#include <utils/String8.h> +#include <utils/threads.h> -#include <binder/IServiceManager.h> #include <binder/IMemory.h> +#include <binder/IServiceManager.h> #include <ui/DisplayInfo.h> +#include <surfaceflinger/ISurface.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/ISurfaceComposerClient.h> -#include <surfaceflinger/ISurface.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <private/surfaceflinger/LayerState.h> @@ -217,7 +218,7 @@ void SurfaceComposerClient::dispose() status_t SurfaceComposerClient::getDisplayInfo( DisplayID dpy, DisplayInfo* info) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); @@ -235,7 +236,7 @@ status_t SurfaceComposerClient::getDisplayInfo( ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -244,7 +245,7 @@ ssize_t SurfaceComposerClient::getDisplayWidth(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; @@ -253,7 +254,7 @@ ssize_t SurfaceComposerClient::getDisplayHeight(DisplayID dpy) ssize_t SurfaceComposerClient::getDisplayOrientation(DisplayID dpy) { - if (uint32_t(dpy)>=SharedBufferStack::NUM_DISPLAY_MAX) + if (uint32_t(dpy)>=NUM_DISPLAY_MAX) return BAD_VALUE; volatile surface_flinger_cblk_t const * cblk = get_cblk(); volatile display_cblk_t const * dcblk = cblk->displays + dpy; diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index ee97dcf..3cecdb4 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -96,6 +96,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) : sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); + memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix)); } SurfaceTexture::~SurfaceTexture() { @@ -270,7 +271,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } - if (state == BufferSlot::FREE || i == mCurrentTexture) { + if (state == BufferSlot::FREE /*|| i == mCurrentTexture*/) { foundSync = i; if (i != mCurrentTexture) { found = i; @@ -547,6 +548,7 @@ status_t SurfaceTexture::updateTexImage() { mCurrentCrop = mSlots[buf].mCrop; mCurrentTransform = mSlots[buf].mTransform; mCurrentTimestamp = mSlots[buf].mTimestamp; + computeCurrentTransformMatrix(); mDequeueCondition.signal(); } else { // We always bind the texture even if we don't update its contents. @@ -596,8 +598,12 @@ GLenum SurfaceTexture::getCurrentTextureTarget() const { } void SurfaceTexture::getTransformMatrix(float mtx[16]) { - LOGV("SurfaceTexture::getTransformMatrix"); Mutex::Autolock lock(mMutex); + memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); +} + +void SurfaceTexture::computeCurrentTransformMatrix() { + LOGV("SurfaceTexture::computeCurrentTransformMatrix"); float xform[16]; for (int i = 0; i < 16; i++) { @@ -684,7 +690,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) { // coordinate of 0, so SurfaceTexture must behave the same way. We don't // want to expose this to applications, however, so we must add an // additional vertical flip to the transform after all the other transforms. - mtxMul(mtx, mtxFlipV, mtxBeforeFlipV); + mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV); } nsecs_t SurfaceTexture::getTimestamp() { diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index c20fcf2..22b0852 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -181,6 +181,12 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { int SurfaceTextureClient::query(int what, int* value) const { LOGV("SurfaceTextureClient::query"); switch (what) { + case NATIVE_WINDOW_FORMAT: + if (mReqFormat) { + *value = mReqFormat; + return NO_ERROR; + } + break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: // TODO: this is not needed anymore *value = 0; diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 2f704c8..e1a85f3 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -505,13 +505,121 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + thread->run(); ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - thread->run(); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + //ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + //ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); thread->bufferDequeued(); thread->requestExitAndWait(); } +TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + st->getTransformMatrix(mtx); + + EXPECT_EQ(1.f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-1.f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(1.f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); +} + +TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers + st->getTransformMatrix(mtx); + + EXPECT_EQ(1.f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-1.f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(1.f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); +} + +TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { + sp<ANativeWindow> anw(mSTC); + sp<SurfaceTexture> st(mST); + android_native_buffer_t* buf[3]; + float mtx[16] = {}; + android_native_rect_t crop; + crop.left = 0; + crop.top = 0; + crop.right = 5; + crop.bottom = 5; + + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0)); + ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); + ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop)); + ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, st->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers + st->getTransformMatrix(mtx); + + // This accounts for the 1 texel shrink for each edge that's included in the + // transform matrix to avoid texturing outside the crop region. + EXPECT_EQ(.5f, mtx[0]); + EXPECT_EQ(0.f, mtx[1]); + EXPECT_EQ(0.f, mtx[2]); + EXPECT_EQ(0.f, mtx[3]); + + EXPECT_EQ(0.f, mtx[4]); + EXPECT_EQ(-.5f, mtx[5]); + EXPECT_EQ(0.f, mtx[6]); + EXPECT_EQ(0.f, mtx[7]); + + EXPECT_EQ(0.f, mtx[8]); + EXPECT_EQ(0.f, mtx[9]); + EXPECT_EQ(1.f, mtx[10]); + EXPECT_EQ(0.f, mtx[11]); + + EXPECT_EQ(0.f, mtx[12]); + EXPECT_EQ(.5f, mtx[13]); + EXPECT_EQ(0.f, mtx[14]); + EXPECT_EQ(1.f, mtx[15]); } + +} // namespace android diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 8747ba5..56c1702 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 + #include <gtest/gtest.h> #include <gui/SurfaceTexture.h> #include <gui/SurfaceTextureClient.h> #include <ui/GraphicBuffer.h> #include <utils/String8.h> +#include <utils/threads.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/Surface.h> @@ -270,8 +273,12 @@ protected: *outPgm = program; } + static int abs(int value) { + return value > 0 ? value : -value; + } + ::testing::AssertionResult checkPixel(int x, int y, int r, - int g, int b, int a) { + int g, int b, int a, int tolerance=2) { GLubyte pixel[4]; String8 msg; glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); @@ -285,22 +292,22 @@ protected: return ::testing::AssertionFailure( ::testing::Message(msg.string())); } - if (r >= 0 && GLubyte(r) != pixel[0]) { + if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { msg += String8::format("r(%d isn't %d)", pixel[0], r); } - if (g >= 0 && GLubyte(g) != pixel[1]) { + if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("g(%d isn't %d)", pixel[1], g); } - if (b >= 0 && GLubyte(b) != pixel[2]) { + if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } msg += String8::format("b(%d isn't %d)", pixel[2], b); } - if (a >= 0 && GLubyte(a) != pixel[3]) { + if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { if (!msg.isEmpty()) { msg += " "; } @@ -618,4 +625,270 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { } } +/* + * This test is for testing GL -> GL texture streaming via SurfaceTexture. It + * contains functionality to create a producer thread that will perform GL + * rendering to an ANativeWindow that feeds frames to a SurfaceTexture. + * Additionally it supports interlocking the producer and consumer threads so + * that a specific sequence of calls can be deterministically created by the + * test. + * + * The intended usage is as follows: + * + * TEST_F(...) { + * class PT : public ProducerThread { + * virtual void render() { + * ... + * swapBuffers(); + * } + * }; + * + * runProducerThread(new PT()); + * + * // The order of these calls will vary from test to test and may include + * // multiple frames and additional operations (e.g. GL rendering from the + * // texture). + * fc->waitForFrame(); + * mST->updateTexImage(); + * fc->finishFrame(); + * } + * + */ +class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +protected: + + // ProducerThread is an abstract base class to simplify the creation of + // OpenGL ES frame producer threads. + class ProducerThread : public Thread { + public: + virtual ~ProducerThread() { + } + + void setEglObjects(EGLDisplay producerEglDisplay, + EGLSurface producerEglSurface, + EGLContext producerEglContext) { + mProducerEglDisplay = producerEglDisplay; + mProducerEglSurface = producerEglSurface; + mProducerEglContext = producerEglContext; + } + + virtual bool threadLoop() { + eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext); + render(); + eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + return false; + } + + protected: + virtual void render() = 0; + + void swapBuffers() { + eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface); + } + + EGLDisplay mProducerEglDisplay; + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; + }; + + // FrameCondition is a utility class for interlocking between the producer + // and consumer threads. The FrameCondition object should be created and + // destroyed in the consumer thread only. The consumer thread should set + // the FrameCondition as the FrameAvailableListener of the SurfaceTexture, + // and should call both waitForFrame and finishFrame once for each expected + // frame. + // + // This interlocking relies on the fact that onFrameAvailable gets called + // synchronously from SurfaceTexture::queueBuffer. + class FrameCondition : public SurfaceTexture::FrameAvailableListener { + public: + // waitForFrame waits for the next frame to arrive. This should be + // called from the consumer thread once for every frame expected by the + // test. + void waitForFrame() { + LOGV("+waitForFrame"); + Mutex::Autolock lock(mMutex); + status_t result = mFrameAvailableCondition.wait(mMutex); + LOGV("-waitForFrame"); + } + + // Allow the producer to return from its swapBuffers call and continue + // on to produce the next frame. This should be called by the consumer + // thread once for every frame expected by the test. + void finishFrame() { + LOGV("+finishFrame"); + Mutex::Autolock lock(mMutex); + mFrameFinishCondition.signal(); + LOGV("-finishFrame"); + } + + // This should be called by SurfaceTexture on the producer thread. + virtual void onFrameAvailable() { + LOGV("+onFrameAvailable"); + Mutex::Autolock lock(mMutex); + mFrameAvailableCondition.signal(); + mFrameFinishCondition.wait(mMutex); + LOGV("-onFrameAvailable"); + } + + protected: + Mutex mMutex; + Condition mFrameAvailableCondition; + Condition mFrameFinishCondition; + }; + + SurfaceTextureGLToGLTest(): + mProducerEglSurface(EGL_NO_SURFACE), + mProducerEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + EGLConfig myConfig = {0}; + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); + + mProducerEglContext = eglCreateContext(mEglDisplay, myConfig, + EGL_NO_CONTEXT, getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); + + mFC = new FrameCondition(); + mST->setFrameAvailableListener(mFC); + } + + virtual void TearDown() { + if (mProducerThread != NULL) { + mProducerThread->requestExitAndWait(); + } + if (mProducerEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mProducerEglContext); + } + if (mProducerEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mProducerEglSurface); + } + mProducerThread.clear(); + mFC.clear(); + } + + void runProducerThread(const sp<ProducerThread> producerThread) { + ASSERT_TRUE(mProducerThread == NULL); + mProducerThread = producerThread; + producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, + mProducerEglContext); + producerThread->run(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; + sp<ProducerThread> mProducerThread; + sp<FrameCondition> mFC; +}; + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) { + class PT : public ProducerThread { + virtual void render() { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); + } + }; + + runProducerThread(new PT()); + + mFC->waitForFrame(); + mST->updateTexImage(); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! +} + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageAfterFrameFinishedWorks) { + class PT : public ProducerThread { + virtual void render() { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); + } + }; + + runProducerThread(new PT()); + + mFC->waitForFrame(); + mFC->finishFrame(); + mST->updateTexImage(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! +} + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) { + enum { NUM_ITERATIONS = 1024 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + LOGV("+swapBuffers"); + swapBuffers(); + LOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + LOGV("+updateTexImage"); + mST->updateTexImage(); + LOGV("-updateTexImage"); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } } + +// XXX: This test is disabled because it causes hangs on some devices. +TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) { + enum { NUM_ITERATIONS = 1024 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + LOGV("+swapBuffers"); + swapBuffers(); + LOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + mFC->finishFrame(); + LOGV("+updateTexImage"); + mST->updateTexImage(); + LOGV("-updateTexImage"); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } +} + +} // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index e8d40ba..093189c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ Debug.cpp \ diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp new file mode 100644 index 0000000..1298fa7 --- /dev/null +++ b/libs/utils/BlobCache.cpp @@ -0,0 +1,232 @@ +/* + ** Copyright 2011, 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 "BlobCache" +//#define LOG_NDEBUG 0 + +#include <stdlib.h> +#include <string.h> + +#include <utils/BlobCache.h> +#include <utils/Log.h> + +namespace android { + +BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): + mMaxKeySize(maxKeySize), + mMaxValueSize(maxValueSize), + mMaxTotalSize(maxTotalSize), + mTotalSize(0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mRandState[0] = (now >> 0) & 0xFFFF; + mRandState[1] = (now >> 16) & 0xFFFF; + mRandState[2] = (now >> 32) & 0xFFFF; + LOGV("initializing random seed using %lld", now); +} + +void BlobCache::set(const void* key, size_t keySize, const void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("set: not caching because the key is too large: %d (limit: %d)", + keySize, mMaxKeySize); + return; + } + if (mMaxValueSize < valueSize) { + LOGV("set: not caching because the value is too large: %d (limit: %d)", + valueSize, mMaxValueSize); + return; + } + if (mMaxTotalSize < keySize + valueSize) { + LOGV("set: not caching because the combined key/value size is too " + "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); + return; + } + if (keySize == 0) { + LOGW("set: not caching because keySize is 0"); + return; + } + if (valueSize <= 0) { + LOGW("set: not caching because valueSize is 0"); + return; + } + + Mutex::Autolock lock(mMutex); + sp<Blob> dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + + while (true) { + + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + // Create a new cache entry. + sp<Blob> keyBlob(new Blob(key, keySize, true)); + sp<Blob> valueBlob(new Blob(value, valueSize, true)); + size_t newTotalSize = mTotalSize + keySize + valueSize; + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new key/value pair because the " + "total cache size limit would be exceeded: %d " + "(limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); + mTotalSize = newTotalSize; + LOGV("set: created new cache entry with %d byte key and %d byte value", + keySize, valueSize); + } else { + // Update the existing cache entry. + sp<Blob> valueBlob(new Blob(value, valueSize, true)); + sp<Blob> oldValueBlob(mCacheEntries[index].getValue()); + size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize(); + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new value because the total cache " + "size limit would be exceeded: %d (limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.editItemAt(index).setValue(valueBlob); + mTotalSize = newTotalSize; + LOGV("set: updated existing cache entry with %d byte key and %d byte " + "value", keySize, valueSize); + } + break; + } +} + +size_t BlobCache::get(const void* key, size_t keySize, void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("get: not searching because the key is too large: %d (limit %d)", + keySize, mMaxKeySize); + return 0; + } + Mutex::Autolock lock(mMutex); + sp<Blob> dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + LOGV("get: no cache entry found for key of size %d", keySize); + return 0; + } + + // The key was found. Return the value if the caller's buffer is large + // enough. + sp<Blob> valueBlob(mCacheEntries[index].getValue()); + size_t valueBlobSize = valueBlob->getSize(); + if (valueBlobSize <= valueSize) { + LOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + memcpy(value, valueBlob->getData(), valueBlobSize); + } else { + LOGV("get: caller's buffer is too small for value: %d (needs %d)", + valueSize, valueBlobSize); + } + return valueBlobSize; +} + +void BlobCache::clean() { + // Remove a random cache entry until the total cache size gets below half + // the maximum total cache size. + while (mTotalSize > mMaxTotalSize / 2) { + size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size())); + const CacheEntry& entry(mCacheEntries[i]); + mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); + mCacheEntries.removeAt(i); + } +} + +bool BlobCache::isCleanable() const { + return mTotalSize > mMaxTotalSize / 2; +} + +BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): + mData(copyData ? malloc(size) : data), + mSize(size), + mOwnsData(copyData) { + if (copyData) { + memcpy(const_cast<void*>(mData), data, size); + } +} + +BlobCache::Blob::~Blob() { + if (mOwnsData) { + free(const_cast<void*>(mData)); + } +} + +bool BlobCache::Blob::operator<(const Blob& rhs) const { + if (mSize == rhs.mSize) { + return memcmp(mData, rhs.mData, mSize) < 0; + } else { + return mSize < rhs.mSize; + } +} + +const void* BlobCache::Blob::getData() const { + return mData; +} + +size_t BlobCache::Blob::getSize() const { + return mSize; +} + +BlobCache::CacheEntry::CacheEntry() { +} + +BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value): + mKey(key), + mValue(value) { +} + +BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce): + mKey(ce.mKey), + mValue(ce.mValue) { +} + +bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const { + return *mKey < *rhs.mKey; +} + +const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) { + mKey = rhs.mKey; + mValue = rhs.mValue; + return *this; +} + +sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const { + return mKey; +} + +sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const { + return mValue; +} + +void BlobCache::CacheEntry::setValue(const sp<Blob>& value) { + mValue = value; +} + +} // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 58e0811..8db2009 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -49,6 +49,11 @@ namespace android { // --------------------------------------------------------------------------- +RefBase::Destroyer::~Destroyer() { +} + +// --------------------------------------------------------------------------- + class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -56,7 +61,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - + Destroyer* mDestroyer; #if !DEBUG_REFS @@ -65,6 +70,7 @@ public: , mWeak(0) , mBase(base) , mFlags(0) + , mDestroyer(0) { } @@ -345,10 +351,6 @@ void RefBase::incStrong(const void* id) const const_cast<RefBase*>(this)->onFirstRef(); } -void RefBase::destroy() const { - delete this; -} - void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; @@ -361,7 +363,11 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast<RefBase*>(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - destroy(); + if (refs->mDestroyer) { + refs->mDestroyer->destroy(this); + } else { + delete this; + } } } refs->decWeak(id); @@ -394,7 +400,9 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } - +void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { + mRefs->mDestroyer = destroyer; +} RefBase* RefBase::weakref_type::refBase() const { @@ -420,7 +428,11 @@ void RefBase::weakref_type::decWeak(const void* id) if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); @@ -430,7 +442,11 @@ void RefBase::weakref_type::decWeak(const void* id) impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } } diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 8b5da0e..c748228 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -168,6 +168,9 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, return 0; } + // Note that *threadID is directly available to the parent only, as it is + // assigned after the child starts. Use memory barrier / lock if the child + // or other threads also need access. if (threadId != NULL) { *threadId = (android_thread_id_t)thread; // XXX: this is not portable } @@ -718,7 +721,6 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } - // The new thread wakes up at _threadLoop, but immediately blocks on mLock if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! @@ -742,11 +744,6 @@ int Thread::_threadLoop(void* user) { Thread* const self = static_cast<Thread*>(user); - // force a memory barrier before reading any fields, in particular mHoldSelf - { - Mutex::Autolock _l(self->mLock); - } - sp<Thread> strong(self->mHoldSelf); wp<Thread> weak(strong); self->mHoldSelf.clear(); @@ -816,6 +813,7 @@ void Thread::requestExit() status_t Thread::requestExitAndWait() { + Mutex::Autolock _l(mLock); if (mThread == getThreadId()) { LOGW( "Thread (this=%p): don't call waitForExit() from this " @@ -825,9 +823,8 @@ status_t Thread::requestExitAndWait() return WOULD_BLOCK; } - requestExit(); + mExitPending = true; - Mutex::Autolock _l(mLock); while (mRunning == true) { mThreadExitedCondition.wait(mLock); } diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 72d4876..87ad98e 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -6,6 +6,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ + BlobCache_test.cpp \ ObbFile_test.cpp \ Looper_test.cpp \ String8_test.cpp \ diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp new file mode 100644 index 0000000..653ea5e --- /dev/null +++ b/libs/utils/tests/BlobCache_test.cpp @@ -0,0 +1,257 @@ +/* + ** Copyright 2011, 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. + */ + +#include <gtest/gtest.h> + +#include <utils/BlobCache.h> + +namespace android { + +class BlobCacheTest : public ::testing::Test { +protected: + enum { + MAX_KEY_SIZE = 6, + MAX_VALUE_SIZE = 8, + MAX_TOTAL_SIZE = 13, + }; + + virtual void SetUp() { + mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); + } + + virtual void TearDown() { + mBC.clear(); + } + + sp<BlobCache> mBC; +}; + +TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { + char buf[2] = { 0xee, 0xee }; + mBC->set("ab", 2, "cd", 2); + mBC->set("ef", 2, "gh", 2); + ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); + ASSERT_EQ('c', buf[0]); + ASSERT_EQ('d', buf[1]); + ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); + ASSERT_EQ('g', buf[0]); + ASSERT_EQ('h', buf[1]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { + char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ('e', buf[1]); + ASSERT_EQ('f', buf[2]); + ASSERT_EQ('g', buf[3]); + ASSERT_EQ('h', buf[4]); + ASSERT_EQ(0xee, buf[5]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { + char buf[3] = { 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); +} + +TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); +} + +TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, "ijkl", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('i', buf[0]); + ASSERT_EQ('j', buf[1]); + ASSERT_EQ('k', buf[2]); + ASSERT_EQ('l', buf[3]); +} + +TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { + char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { + char key[MAX_KEY_SIZE+1]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE+1; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { + char buf[MAX_VALUE_SIZE+1]; + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + SCOPED_TRACE(i); + ASSERT_EQ(0xee, buf[i]); + } +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { + // Check a testing assumptions + ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { + char key[MAX_KEY_SIZE]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); + ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); + ASSERT_EQ('w', buf[0]); + ASSERT_EQ('x', buf[1]); + ASSERT_EQ('y', buf[2]); + ASSERT_EQ('z', buf[3]); +} + +TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { + char buf[MAX_VALUE_SIZE]; + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, + MAX_VALUE_SIZE)); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + SCOPED_TRACE(i); + ASSERT_EQ('b', buf[i]); + } +} + +TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { + // Check a testing assumption + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, bufSize); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { + char buf[1] = { 0xee }; + mBC->set("x", 1, "y", 1); + ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); + ASSERT_EQ('y', buf[0]); +} + +TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { + for (int i = 0; i < 256; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + int numCached = 0; + for (int i = 0; i < 256; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); +} + +TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + // Insert one more entry, causing a cache overflow. + { + uint8_t k = maxEntries; + mBC->set(&k, 1, "x", 1); + } + // Count the number of entries in the cache. + int numCached = 0; + for (int i = 0; i < maxEntries+1; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_EQ(maxEntries/2 + 1, numCached); +} + +} // namespace android |