| /* |
| * 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 <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include <utils/Errors.h> |
| #include <utils/threads.h> |
| #include <utils/IPCThreadState.h> |
| #include <utils/IMemory.h> |
| #include <utils/Log.h> |
| |
| #include <ui/DisplayInfo.h> |
| #include <ui/BufferMapper.h> |
| #include <ui/EGLNativeWindowSurface.h> |
| #include <ui/ISurface.h> |
| #include <ui/Surface.h> |
| #include <ui/SurfaceComposerClient.h> |
| #include <ui/Rect.h> |
| |
| #include <EGL/android_natives.h> |
| |
| #include <private/ui/SharedState.h> |
| #include <private/ui/LayerState.h> |
| |
| #include <pixelflinger/pixelflinger.h> |
| |
| namespace android { |
| |
| // ============================================================================ |
| // SurfaceBuffer |
| // ============================================================================ |
| |
| template<class SurfaceBuffer> Mutex Singleton<SurfaceBuffer>::sLock; |
| template<> SurfaceBuffer* Singleton<SurfaceBuffer>::sInstance(0); |
| |
| SurfaceBuffer::SurfaceBuffer() |
| : BASE(), handle(0), mOwner(false) |
| { |
| width = |
| height = |
| stride = |
| format = |
| usage = 0; |
| android_native_buffer_t::getHandle = getHandle; |
| } |
| |
| SurfaceBuffer::SurfaceBuffer(const Parcel& data) |
| : BASE(), handle(0), mOwner(true) |
| { |
| // we own the handle in this case |
| width = data.readInt32(); |
| height = data.readInt32(); |
| stride = data.readInt32(); |
| format = data.readInt32(); |
| usage = data.readInt32(); |
| handle = data.readNativeHandle(); |
| android_native_buffer_t::getHandle = getHandle; |
| } |
| |
| SurfaceBuffer::~SurfaceBuffer() |
| { |
| if (handle && mOwner) { |
| native_handle_close(handle); |
| native_handle_delete(const_cast<native_handle*>(handle)); |
| } |
| } |
| |
| int SurfaceBuffer::getHandle(android_native_buffer_t const * base, |
| buffer_handle_t* handle) |
| { |
| *handle = getSelf(base)->handle; |
| return 0; |
| } |
| |
| status_t SurfaceBuffer::writeToParcel(Parcel* reply, |
| android_native_buffer_t const* buffer) |
| { |
| buffer_handle_t handle; |
| status_t err = buffer->getHandle(buffer, &handle); |
| if (err < 0) { |
| return err; |
| } |
| reply->writeInt32(buffer->width); |
| reply->writeInt32(buffer->height); |
| reply->writeInt32(buffer->stride); |
| reply->writeInt32(buffer->format); |
| reply->writeInt32(buffer->usage); |
| reply->writeNativeHandle(handle); |
| return NO_ERROR; |
| } |
| |
| // ---------------------------------------------------------------------- |
| |
| static void copyBlt(const android_native_buffer_t* dst, |
| const android_native_buffer_t* src, const Region& reg) |
| { |
| Region::iterator iterator(reg); |
| if (iterator) { |
| // NOTE: dst and src must be the same format |
| Rect r; |
| const size_t bpp = bytesPerPixel(src->format); |
| const size_t dbpr = dst->stride * bpp; |
| const size_t sbpr = src->stride * bpp; |
| while (iterator.iterate(&r)) { |
| ssize_t h = r.bottom - r.top; |
| if (h) { |
| size_t size = (r.right - r.left) * bpp; |
| uint8_t* s = (GGLubyte*)src->bits + |
| (r.left + src->stride * r.top) * bpp; |
| uint8_t* d = (GGLubyte*)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); |
| } |
| } |
| } |
| } |
| |
| // ============================================================================ |
| // Surface |
| // ============================================================================ |
| |
| Surface::Surface(const sp<SurfaceComposerClient>& client, |
| const sp<ISurface>& surface, |
| const ISurfaceFlingerClient::surface_data_t& data, |
| uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, |
| bool owner) |
| : mClient(client), mSurface(surface), |
| mToken(data.token), mIdentity(data.identity), |
| mFormat(format), mFlags(flags), mOwner(owner) |
| { |
| android_native_window_t::connect = connect; |
| android_native_window_t::disconnect = disconnect; |
| android_native_window_t::setSwapInterval = setSwapInterval; |
| android_native_window_t::setSwapRectangle = setSwapRectangle; |
| android_native_window_t::dequeueBuffer = dequeueBuffer; |
| android_native_window_t::lockBuffer = lockBuffer; |
| android_native_window_t::queueBuffer = queueBuffer; |
| |
| mSwapRectangle.makeInvalid(); |
| |
| DisplayInfo dinfo; |
| SurfaceComposerClient::getDisplayInfo(0, &dinfo); |
| const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; |
| const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi; |
| // FIXME: set real values here |
| const_cast<int&>(android_native_window_t::minSwapInterval) = 1; |
| const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; |
| const_cast<uint32_t&>(android_native_window_t::flags) = 0; |
| } |
| |
| Surface::Surface(Surface const* rhs) |
| : mOwner(false) |
| { |
| // FIXME: we really should get rid of this ctor. the memcpy below |
| //should be safe for now, but android_native_window_t is not supposed |
| // to be clonable. |
| memcpy( static_cast<android_native_window_t*>(this), |
| static_cast<android_native_window_t const *>(rhs), |
| sizeof(android_native_window_t)); |
| |
| mToken = rhs->mToken; |
| mIdentity = rhs->mIdentity; |
| mClient = rhs->mClient; |
| mSurface = rhs->mSurface; |
| mBuffers[0] = rhs->mBuffers[0]; |
| mBuffers[1] = rhs->mBuffers[1]; |
| mFormat = rhs->mFormat; |
| mFlags = rhs->mFlags; |
| mSwapRectangle.makeInvalid(); |
| } |
| |
| Surface::~Surface() |
| { |
| if (mOwner && mToken>=0 && mClient!=0) { |
| mClient->destroySurface(mToken); |
| if (mBuffers[0] != 0) { |
| BufferMapper::get().unmap(mBuffers[0]->getHandle(), this); |
| } |
| if (mBuffers[1] != 0) { |
| BufferMapper::get().unmap(mBuffers[1]->getHandle(), this); |
| } |
| } |
| mClient.clear(); |
| mSurface.clear(); |
| IPCThreadState::self()->flushCommands(); |
| } |
| |
| sp<Surface> Surface::dup() const |
| { |
| // FIXME: we should get rid of Surface::dup() |
| Surface const * r = this; |
| if (this && mOwner) { |
| // the only reason we need to do this is because of Java's garbage |
| // collector: because we're creating a copy of the Surface |
| // instead of a reference, we can guarantee that when our last |
| // reference goes away, the real surface will be deleted. |
| // Without this hack (the code is correct too), we'd have to |
| // wait for a GC for the surface to go away. |
| r = new Surface(this); |
| } |
| return const_cast<Surface*>(r); |
| } |
| |
| status_t Surface::validate(per_client_cblk_t const* cblk) const |
| { |
| if (cblk == 0) { |
| LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); |
| return NO_INIT; |
| } |
| status_t err = cblk->validate(mToken); |
| if (err != NO_ERROR) { |
| LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", |
| mToken, mIdentity, err, strerror(-err)); |
| return err; |
| } |
| if (mIdentity != uint32_t(cblk->layers[mToken].identity)) { |
| LOGE("using an invalid surface id=%d, identity=%u should be %d", |
| mToken, mIdentity, cblk->layers[mToken].identity); |
| return NO_INIT; |
| } |
| return NO_ERROR; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| int Surface::setSwapRectangle(android_native_window_t* window, |
| int l, int t, int w, int h) |
| { |
| Surface* self = getSelf(window); |
| self->setSwapRectangle(Rect(l, t, l+w, t+h)); |
| return 0; |
| } |
| |
| void Surface::connect(android_native_window_t* window) |
| { |
| } |
| |
| void Surface::disconnect(android_native_window_t* window) |
| { |
| } |
| |
| int Surface::setSwapInterval(android_native_window_t* window, int interval) |
| { |
| return 0; |
| } |
| |
| int Surface::dequeueBuffer(android_native_window_t* window, |
| android_native_buffer_t** buffer) |
| { |
| Surface* self = getSelf(window); |
| return self->dequeueBuffer(buffer); |
| } |
| |
| int Surface::lockBuffer(android_native_window_t* window, |
| android_native_buffer_t* buffer) |
| { |
| Surface* self = getSelf(window); |
| return self->lockBuffer(buffer); |
| } |
| |
| int Surface::queueBuffer(android_native_window_t* window, |
| android_native_buffer_t* buffer) |
| { |
| Surface* self = getSelf(window); |
| return self->queueBuffer(buffer); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| int Surface::dequeueBuffer(android_native_buffer_t** buffer) |
| { |
| // FIXME: dequeueBuffer() needs proper implementation |
| |
| Mutex::Autolock _l(mSurfaceLock); |
| |
| per_client_cblk_t* const cblk = mClient->mControl; |
| status_t err = validate(cblk); |
| if (err != NO_ERROR) |
| return err; |
| |
| SurfaceID index(mToken); |
| |
| int32_t backIdx = cblk->lock_layer(size_t(index), |
| per_client_cblk_t::BLOCKING); |
| |
| if (backIdx < 0) |
| return status_t(backIdx); |
| |
| mBackbufferIndex = backIdx; |
| layer_cblk_t* const lcblk = &(cblk->layers[index]); |
| |
| volatile const surface_info_t* const back = lcblk->surface + backIdx; |
| if (back->flags & surface_info_t::eNeedNewBuffer) { |
| getBufferLocked(backIdx); |
| } |
| |
| const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]); |
| *buffer = backBuffer.get(); |
| |
| return NO_ERROR; |
| } |
| |
| int Surface::lockBuffer(android_native_buffer_t* buffer) |
| { |
| // FIXME: lockBuffer() needs proper implementation |
| return 0; |
| } |
| |
| int Surface::queueBuffer(android_native_buffer_t* buffer) |
| { |
| Mutex::Autolock _l(mSurfaceLock); |
| |
| per_client_cblk_t* const cblk = mClient->mControl; |
| status_t err = validate(cblk); |
| if (err != NO_ERROR) |
| return err; |
| |
| // transmit the dirty region |
| const Region dirty(swapRectangle()); |
| SurfaceID index(mToken); |
| layer_cblk_t* const lcblk = &(cblk->layers[index]); |
| _send_dirty_region(lcblk, dirty); |
| |
| uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); |
| if (!(newstate & eNextFlipPending)) |
| mClient->signalServer(); |
| |
| return NO_ERROR; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| status_t Surface::lock(SurfaceInfo* info, bool blocking) { |
| return Surface::lock(info, NULL, blocking); |
| } |
| |
| status_t Surface::lock(SurfaceInfo* other, Region* dirty, bool blocking) |
| { |
| // FIXME: needs some locking here |
| android_native_buffer_t* backBuffer; |
| status_t err = dequeueBuffer(&backBuffer); |
| if (err == NO_ERROR) { |
| err = lockBuffer(backBuffer); |
| if (err == NO_ERROR) { |
| backBuffer->common.incRef(&backBuffer->common); |
| mLockedBuffer = backBuffer; |
| other->w = backBuffer->width; |
| other->h = backBuffer->height; |
| other->s = backBuffer->stride; |
| other->usage = backBuffer->usage; |
| other->format = backBuffer->format; |
| other->bits = backBuffer->bits; |
| |
| // we handle copy-back here... |
| |
| const Rect bounds(backBuffer->width, backBuffer->height); |
| Region newDirtyRegion; |
| |
| per_client_cblk_t* const cblk = mClient->mControl; |
| layer_cblk_t* const lcblk = &(cblk->layers[SurfaceID(mToken)]); |
| volatile const surface_info_t* const back = lcblk->surface + mBackbufferIndex; |
| if (back->flags & surface_info_t::eBufferDirty) { |
| // content is meaningless in this case and the whole surface |
| // needs to be redrawn. |
| newDirtyRegion.set(bounds); |
| if (dirty) { |
| *dirty = newDirtyRegion; |
| } |
| } else |
| { |
| if (dirty) { |
| dirty->andSelf(Region(bounds)); |
| newDirtyRegion = *dirty; |
| } else { |
| newDirtyRegion.set(bounds); |
| } |
| Region copyback; |
| if (!(lcblk->flags & eNoCopyBack)) { |
| const Region previousDirtyRegion(dirtyRegion()); |
| copyback = previousDirtyRegion.subtract(newDirtyRegion); |
| } |
| const sp<SurfaceBuffer>& frontBuffer(mBuffers[1-mBackbufferIndex]); |
| if (!copyback.isEmpty() && frontBuffer!=0) { |
| // copy front to back |
| copyBlt(backBuffer, frontBuffer.get(), copyback); |
| } |
| } |
| setDirtyRegion(newDirtyRegion); |
| |
| |
| Rect lockBounds(backBuffer->width, backBuffer->height); |
| if (dirty) { |
| lockBounds = dirty->bounds(); |
| } |
| buffer_handle_t handle; |
| backBuffer->getHandle(backBuffer, &handle); |
| status_t res = BufferMapper::get().lock(handle, |
| GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, |
| lockBounds); |
| LOGW_IF(res, "failed locking buffer %d (%p)", |
| mBackbufferIndex, handle); |
| setSwapRectangle(lockBounds); |
| } |
| } |
| return err; |
| } |
| |
| status_t Surface::unlockAndPost() |
| { |
| // FIXME: needs some locking here |
| |
| if (mLockedBuffer == 0) |
| return BAD_VALUE; |
| |
| buffer_handle_t handle; |
| mLockedBuffer->getHandle(mLockedBuffer, &handle); |
| status_t res = BufferMapper::get().unlock(handle); |
| LOGW_IF(res, "failed unlocking buffer %d (%p)", |
| mBackbufferIndex, handle); |
| |
| const Rect dirty(dirtyRegion().bounds()); |
| setSwapRectangle(dirty); |
| status_t err = queueBuffer(mLockedBuffer); |
| mLockedBuffer->common.decRef(&mLockedBuffer->common); |
| mLockedBuffer = 0; |
| return err; |
| } |
| |
| void Surface::_send_dirty_region( |
| layer_cblk_t* lcblk, const Region& dirty) |
| { |
| const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; |
| flat_region_t* flat_region = lcblk->region + index; |
| status_t err = dirty.write(flat_region, sizeof(flat_region_t)); |
| if (err < NO_ERROR) { |
| // region doesn't fit, use the bounds |
| const Region reg(dirty.bounds()); |
| reg.write(flat_region, sizeof(flat_region_t)); |
| } |
| } |
| |
| |
| status_t Surface::setLayer(int32_t layer) { |
| return mClient->setLayer(this, layer); |
| } |
| status_t Surface::setPosition(int32_t x, int32_t y) { |
| return mClient->setPosition(this, x, y); |
| } |
| status_t Surface::setSize(uint32_t w, uint32_t h) { |
| return mClient->setSize(this, w, h); |
| } |
| status_t Surface::hide() { |
| return mClient->hide(this); |
| } |
| status_t Surface::show(int32_t layer) { |
| return mClient->show(this, layer); |
| } |
| status_t Surface::freeze() { |
| return mClient->freeze(this); |
| } |
| status_t Surface::unfreeze() { |
| return mClient->unfreeze(this); |
| } |
| status_t Surface::setFlags(uint32_t flags, uint32_t mask) { |
| return mClient->setFlags(this, flags, mask); |
| } |
| status_t Surface::setTransparentRegionHint(const Region& transparent) { |
| return mClient->setTransparentRegionHint(this, transparent); |
| } |
| status_t Surface::setAlpha(float alpha) { |
| return mClient->setAlpha(this, alpha); |
| } |
| status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { |
| return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy); |
| } |
| status_t Surface::setFreezeTint(uint32_t tint) { |
| return mClient->setFreezeTint(this, tint); |
| } |
| |
| Region Surface::dirtyRegion() const { |
| return mDirtyRegion; |
| } |
| void Surface::setDirtyRegion(const Region& region) const { |
| mDirtyRegion = region; |
| } |
| const Rect& Surface::swapRectangle() const { |
| return mSwapRectangle; |
| } |
| void Surface::setSwapRectangle(const Rect& r) { |
| mSwapRectangle = r; |
| } |
| |
| sp<Surface> Surface::readFromParcel(Parcel* parcel) |
| { |
| sp<SurfaceComposerClient> client; |
| ISurfaceFlingerClient::surface_data_t data; |
| sp<IBinder> clientBinder= parcel->readStrongBinder(); |
| sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder()); |
| data.token = parcel->readInt32(); |
| data.identity = parcel->readInt32(); |
| PixelFormat format = parcel->readInt32(); |
| uint32_t flags = parcel->readInt32(); |
| |
| if (clientBinder != NULL) |
| client = SurfaceComposerClient::clientForConnection(clientBinder); |
| |
| return new Surface(client, surface, data, 0, 0, format, flags, false); |
| } |
| |
| status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) |
| { |
| uint32_t flags=0; |
| uint32_t format=0; |
| SurfaceID token = -1; |
| uint32_t identity = 0; |
| sp<SurfaceComposerClient> client; |
| sp<ISurface> sur; |
| if (surface->isValid()) { |
| token = surface->mToken; |
| identity = surface->mIdentity; |
| client = surface->mClient; |
| sur = surface->mSurface; |
| format = surface->mFormat; |
| flags = surface->mFlags; |
| } |
| parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); |
| parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); |
| parcel->writeInt32(token); |
| parcel->writeInt32(identity); |
| parcel->writeInt32(format); |
| parcel->writeInt32(flags); |
| return NO_ERROR; |
| } |
| |
| bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs) |
| { |
| if (lhs == 0 || rhs == 0) |
| return false; |
| return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); |
| } |
| |
| status_t Surface::getBufferLocked(int index) |
| { |
| status_t err = NO_MEMORY; |
| sp<SurfaceBuffer> buffer = mSurface->getBuffer(); |
| LOGE_IF(buffer==0, "ISurface::getBuffer() returned NULL"); |
| if (buffer != 0) { |
| sp<SurfaceBuffer>& currentBuffer(mBuffers[index]); |
| if (currentBuffer != 0) { |
| BufferMapper::get().unmap(currentBuffer->getHandle(), this); |
| currentBuffer.clear(); |
| } |
| err = BufferMapper::get().map(buffer->getHandle(), &buffer->bits, this); |
| LOGW_IF(err, "map(...) failed %d (%s)", err, strerror(-err)); |
| if (err == NO_ERROR) { |
| currentBuffer = buffer; |
| } |
| } |
| return err; |
| } |
| |
| }; // namespace android |
| |