diff options
Diffstat (limited to 'libs/gui/Surface.cpp')
-rw-r--r-- | libs/gui/Surface.cpp | 1152 |
1 files changed, 1152 insertions, 0 deletions
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp new file mode 100644 index 0000000..0dfbf01 --- /dev/null +++ b/libs/gui/Surface.cpp @@ -0,0 +1,1152 @@ +/* + * 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 "Surface" + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <utils/Errors.h> +#include <utils/threads.h> +#include <utils/CallStack.h> +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/IMemory.h> + +#include <ui/DisplayInfo.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicBufferMapper.h> +#include <ui/GraphicLog.h> +#include <ui/Rect.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <private/surfaceflinger/SharedBufferStack.h> +#include <private/surfaceflinger/LayerState.h> + +namespace android { + +// ---------------------------------------------------------------------- + +static status_t copyBlt( + const sp<GraphicBuffer>& dst, + const sp<GraphicBuffer>& src, + const Region& reg) +{ + // src and dst with, height and format must be identical. no verification + // is done here. + status_t err; + uint8_t const * src_bits = NULL; + err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); + LOGE_IF(err, "error locking src buffer %s", strerror(-err)); + + uint8_t* dst_bits = NULL; + err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); + LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); + + Region::const_iterator head(reg.begin()); + Region::const_iterator tail(reg.end()); + if (head != tail && src_bits && dst_bits) { + const size_t bpp = bytesPerPixel(src->format); + const size_t dbpr = dst->stride * bpp; + const size_t sbpr = src->stride * bpp; + + while (head != tail) { + const Rect& r(*head++); + ssize_t h = r.height(); + if (h <= 0) continue; + size_t size = r.width() * bpp; + uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; + uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + + if (src_bits) + src->unlock(); + + if (dst_bits) + dst->unlock(); + + return err; +} + +// ============================================================================ +// SurfaceControl +// ============================================================================ + +SurfaceControl::SurfaceControl( + const sp<SurfaceComposerClient>& client, + const sp<ISurface>& surface, + const ISurfaceComposerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) + : mClient(client), mSurface(surface), + mToken(data.token), mIdentity(data.identity), + mWidth(data.width), mHeight(data.height), mFormat(data.format), + mFlags(flags) +{ +} + +SurfaceControl::~SurfaceControl() +{ + destroy(); +} + +void SurfaceControl::destroy() +{ + if (isValid()) { + mClient->destroySurface(mToken); + } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mClient.clear(); + mSurface.clear(); + IPCThreadState::self()->flushCommands(); +} + +void SurfaceControl::clear() +{ + // here, the window manager tells us explicitly that we should destroy + // the surface's resource. Soon after this call, it will also release + // its last reference (which will call the dtor); however, it is possible + // that a client living in the same process still holds references which + // would delay the call to the dtor -- that is why we need this explicit + // "clear()" call. + destroy(); +} + +bool SurfaceControl::isSameSurface( + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); +} + +status_t SurfaceControl::setLayer(int32_t layer) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setLayer(mToken, layer); +} +status_t SurfaceControl::setPosition(int32_t x, int32_t y) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setPosition(mToken, x, y); +} +status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setSize(mToken, w, h); +} +status_t SurfaceControl::hide() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->hide(mToken); +} +status_t SurfaceControl::show(int32_t layer) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->show(mToken, layer); +} +status_t SurfaceControl::freeze() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->freeze(mToken); +} +status_t SurfaceControl::unfreeze() { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->unfreeze(mToken); +} +status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setFlags(mToken, flags, mask); +} +status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setTransparentRegionHint(mToken, transparent); +} +status_t SurfaceControl::setAlpha(float alpha) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setAlpha(mToken, alpha); +} +status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); +} +status_t SurfaceControl::setFreezeTint(uint32_t tint) { + status_t err = validate(); + if (err < 0) return err; + const sp<SurfaceComposerClient>& client(mClient); + return client->setFreezeTint(mToken, tint); +} + +status_t SurfaceControl::validate() const +{ + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return NO_INIT; + } + return NO_ERROR; +} + +status_t SurfaceControl::writeSurfaceToParcel( + const sp<SurfaceControl>& control, Parcel* parcel) +{ + sp<ISurface> sur; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t format = 0; + uint32_t flags = 0; + if (SurfaceControl::isValid(control)) { + sur = control->mSurface; + identity = control->mIdentity; + width = control->mWidth; + height = control->mHeight; + format = control->mFormat; + flags = control->mFlags; + } + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; +} + +sp<Surface> SurfaceControl::getSurface() const +{ + Mutex::Autolock _l(mLock); + if (mSurfaceData == 0) { + mSurfaceData = new Surface(const_cast<SurfaceControl*>(this)); + } + return mSurfaceData; +} + +// ============================================================================ +// 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), + mSurface(surface->mSurface), + mIdentity(surface->mIdentity), + mFormat(surface->mFormat), mFlags(surface->mFlags), + mWidth(surface->mWidth), mHeight(surface->mHeight) +{ + init(); +} + +Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref) + : mBufferMapper(GraphicBufferMapper::get()), + mClient(SurfaceClient::getInstance()), + mSharedBufferClient(NULL), + mInitCheck(NO_INIT) +{ + mSurface = interface_cast<ISurface>(ref); + mIdentity = parcel.readInt32(); + mWidth = parcel.readInt32(); + mHeight = parcel.readInt32(); + mFormat = parcel.readInt32(); + mFlags = parcel.readInt32(); + init(); +} + +status_t Surface::writeToParcel( + const sp<Surface>& surface, Parcel* parcel) +{ + sp<ISurface> sur; + uint32_t identity = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t format = 0; + uint32_t flags = 0; + if (Surface::isValid(surface)) { + sur = surface->mSurface; + identity = surface->mIdentity; + width = surface->mWidth; + height = surface->mHeight; + format = surface->mFormat; + flags = surface->mFlags; + } else if (surface != 0 && surface->mSurface != 0) { + LOGW("Parceling invalid surface with non-NULL ISurface as NULL: " + "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, " + "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d", + surface->mSurface.get(), surface->mIdentity, surface->mWidth, + surface->mHeight, surface->mFormat, surface->mFlags, + surface->mInitCheck); + } + parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); + parcel->writeInt32(identity); + parcel->writeInt32(width); + parcel->writeInt32(height); + parcel->writeInt32(format); + parcel->writeInt32(flags); + return NO_ERROR; + +} + + +Mutex Surface::sCachedSurfacesLock; +DefaultKeyedVector<wp<IBinder>, wp<Surface> > Surface::sCachedSurfaces; + +sp<Surface> Surface::readFromParcel(const Parcel& data) { + Mutex::Autolock _l(sCachedSurfacesLock); + sp<IBinder> binder(data.readStrongBinder()); + sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote(); + if (surface == 0) { + surface = new Surface(data, binder); + sCachedSurfaces.add(binder, surface); + } + if (surface->mSurface == 0) { + surface = 0; + } + cleanCachedSurfacesLocked(); + return surface; +} + +// Remove the stale entries from the surface cache. This should only be called +// with sCachedSurfacesLock held. +void Surface::cleanCachedSurfacesLocked() { + for (int i = sCachedSurfaces.size()-1; i >= 0; --i) { + wp<Surface> s(sCachedSurfaces.valueAt(i)); + if (s == 0 || s.promote() == 0) { + sCachedSurfaces.removeItemsAt(i); + } + } +} + +void Surface::init() +{ + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::cancelBuffer = cancelBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + 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); + } + } +} + +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(); + mSurface.clear(); + delete mSharedBufferClient; + IPCThreadState::self()->flushCommands(); +} + +bool Surface::isValid() { + return mInitCheck == NO_ERROR; +} + +status_t Surface::validate(bool inCancelBuffer) const +{ + // check that we initialized ourself properly + if (mInitCheck != NO_ERROR) { + 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; +} + +sp<ISurface> Surface::getISurface() const { + return mSurface; +} + +// ---------------------------------------------------------------------------- + +int Surface::setSwapInterval(ANativeWindow* window, int interval) { + return 0; +} + +int Surface::dequeueBuffer(ANativeWindow* window, + android_native_buffer_t** buffer) { + Surface* self = getSelf(window); + return self->dequeueBuffer(buffer); +} + +int Surface::cancelBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->cancelBuffer(buffer); +} + +int Surface::lockBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->lockBuffer(buffer); +} + +int Surface::queueBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + Surface* self = getSelf(window); + return self->queueBuffer(buffer); +} + +int Surface::query(ANativeWindow* window, + int what, int* value) { + Surface* self = getSelf(window); + return self->query(what, value); +} + +int Surface::perform(ANativeWindow* window, + int operation, ...) { + va_list args; + va_start(args, operation); + Surface* self = getSelf(window); + int res = self->perform(operation, args); + va_end(args); + return res; +} + +// ---------------------------------------------------------------------------- + +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::dequeueBuffer(android_native_buffer_t** 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; + } + + if (err == NO_ERROR) { + mDirtyRegion.set(backBuffer->width, backBuffer->height); + *buffer = backBuffer.get(); + } else { + mSharedBufferClient->undoDequeue(bufIdx); + } + + return err; +} + +int Surface::cancelBuffer(android_native_buffer_t* 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::lockBuffer(android_native_buffer_t* 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::queueBuffer(android_native_buffer_t* 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::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_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: + *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; +} + +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::getConnectedApi() const +{ + Mutex::Autolock _l(mSurfaceLock); + return mConnected; +} + +// ---------------------------------------------------------------------------- + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) +{ + if (getConnectedApi()) { + LOGE("Surface::lock(%p) failed. Already connected to another API", + (ANativeWindow*)this); + CallStack stack; + stack.update(); + stack.dump(""); + return INVALID_OPERATION; + } + + if (mApiLock.tryLock() != NO_ERROR) { + LOGE("calling Surface::lock from different threads!"); + CallStack stack; + stack.update(); + stack.dump(""); + return WOULD_BLOCK; + } + + /* Here we're holding mApiLock */ + + if (mLockedBuffer != 0) { + LOGE("Surface::lock failed, already locked"); + mApiLock.unlock(); + return INVALID_OPERATION; + } + + // we're intending to do software rendering from this point + setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + + android_native_buffer_t* out; + status_t err = 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)); + if (err == NO_ERROR) { + const Rect bounds(backBuffer->width, backBuffer->height); + const Region boundsRegion(bounds); + Region scratch(boundsRegion); + Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + newDirtyRegion &= boundsRegion; + + // figure out if we can copy the frontbuffer back + const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); + const bool canCopyBack = (frontBuffer != 0 && + backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + backBuffer->format == frontBuffer->format && + !(mFlags & ISurfaceComposer::eDestroyBackbuffer)); + + // the dirty region we report to surfaceflinger is the one + // given by the user (as opposed to the one *we* return to the + // user). + mDirtyRegion = newDirtyRegion; + + if (canCopyBack) { + // copy the area that is invalid and not repainted this round + const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty()) + copyBlt(backBuffer, frontBuffer, copyback); + } else { + // if we can't copy-back anything, modify the user's dirty + // region to make sure they redraw the whole buffer + newDirtyRegion = boundsRegion; + } + + // keep track of the are of the buffer that is "clean" + // (ie: that will be redrawn) + mOldDirtyRegion = newDirtyRegion; + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + LOGW_IF(res, "failed locking buffer (handle = %p)", + backBuffer->handle); + + mLockedBuffer = backBuffer; + other->w = backBuffer->width; + other->h = backBuffer->height; + other->s = backBuffer->stride; + other->usage = backBuffer->usage; + other->format = backBuffer->format; + other->bits = vaddr; + } + } + mApiLock.unlock(); + return err; +} + +status_t Surface::unlockAndPost() +{ + if (mLockedBuffer == 0) { + LOGE("Surface::unlockAndPost failed, no locked buffer"); + return INVALID_OPERATION; + } + + 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)); + + 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 android_native_buffer_t 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 |