diff options
Diffstat (limited to 'libs/surfaceflinger/Layer.cpp')
-rw-r--r-- | libs/surfaceflinger/Layer.cpp | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp new file mode 100644 index 0000000..f65d669 --- /dev/null +++ b/libs/surfaceflinger/Layer.cpp @@ -0,0 +1,568 @@ +/* + * 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 "SurfaceFlinger" + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/properties.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <ui/PixelFormat.h> +#include <ui/EGLDisplaySurface.h> + +#include "clz.h" +#include "Layer.h" +#include "LayerBitmap.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + + +#define DEBUG_RESIZE 0 + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; +const char* const Layer::typeID = "Layer"; + +// --------------------------------------------------------------------------- + +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) + : LayerBaseClient(flinger, display, c, i), + mSecure(false), + mFrontBufferIndex(1), + mNeedsBlending(true), + mResizeTransactionDone(false), + mTextureName(-1U), mTextureWidth(0), mTextureHeight(0) +{ + // no OpenGL operation is possible here, since we might not be + // in the OpenGL thread. +} + +Layer::~Layer() +{ + client->free(clientIndex()); + // this should always be called from the OpenGL thread + if (mTextureName != -1U) { + //glDeleteTextures(1, &mTextureName); + deletedTextures.add(mTextureName); + } +} + +void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBase::initStates(w,h,flags); + + if (flags & ISurfaceComposer::eDestroyBackbuffer) + lcblk->flags |= eNoCopyBack; +} + +sp<LayerBaseClient::Surface> Layer::getSurface() const +{ + return mSurface; +} + +status_t Layer::setBuffers( Client* client, + uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags) +{ + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err) return err; + + // TODO: if eHardware is explicitly requested, we should fail + // on systems where we can't allocate memory that can be used with + // DMA engines for instance. + + // FIXME: we always ask for hardware for now (this should come from copybit) + flags |= ISurfaceComposer::eHardware; + + const uint32_t memory_flags = flags & + (ISurfaceComposer::eGPU | + ISurfaceComposer::eHardware | + ISurfaceComposer::eSecure); + + // pixel-alignment. the final alignment may be bigger because + // we always force a 4-byte aligned bpr. + uint32_t alignment = 1; + + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this value should come from the h/w + alignment = 8; + // FIXME: this is msm7201A specific, as its GPU only supports + // BGRA_8888. + if (format == PIXEL_FORMAT_RGBA_8888) { + format = PIXEL_FORMAT_BGRA_8888; + } + } + + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; + sp<MemoryDealer> allocators[2]; + for (int i=0 ; i<2 ; i++) { + allocators[i] = client->createAllocator(memory_flags); + if (allocators[i] == 0) + return NO_MEMORY; + mBuffers[i].init(allocators[i]); + int err = mBuffers[i].setBits(w, h, alignment, format, LayerBitmap::SECURE_BITS); + if (err != NO_ERROR) + return err; + mBuffers[i].clear(); // clear the bits for security + mBuffers[i].getInfo(lcblk->surface + i); + } + + mSurface = new Surface(clientIndex(), + allocators[0]->getMemoryHeap(), + allocators[1]->getMemoryHeap(), + mIdentity); + + return NO_ERROR; +} + +void Layer::reloadTexture(const Region& dirty) +{ + if (UNLIKELY(mTextureName == -1U)) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + mTextureName = createTexture(); + } + const GGLSurface& t(frontBuffer().surface()); + loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight); +} + + +void Layer::onDraw(const Region& clip) const +{ + if (UNLIKELY(mTextureName == -1LU)) { + //LOGW("Layer %p doesn't have a texture", this); + // the texture has not been created yet, this Layer has + // in fact never been drawn into. this happens frequently with + // SurfaceView. + clearWithOpenGL(clip); + return; + } + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const LayerBitmap& front(frontBuffer()); + const GGLSurface& t(front.surface()); + + status_t err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + // StopWatch watch("copybit"); + const State& s(drawingState()); + + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); + + copybit_image_t src; + front.getBitmapSurface(&src); + copybit_rect_t srect = { 0, 0, t.width, t.height }; + + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, + s.flags & ISurfaceComposer::eLayerDither ? + COPYBIT_ENABLE : COPYBIT_DISABLE); + + region_iterator it(clip); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + + if (!can_use_copybit || err) { + drawWithOpenGL(clip, mTextureName, t); + } +} + +status_t Layer::reallocateBuffer(int32_t index, uint32_t w, uint32_t h) +{ + LOGD_IF(DEBUG_RESIZE, + "reallocateBuffer (layer=%p), " + "requested (%dx%d), " + "index=%d, (%dx%d), (%dx%d)", + this, + int(w), int(h), + int(index), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + status_t err = mBuffers[index].resize(w, h); + if (err == NO_ERROR) { + mBuffers[index].getInfo(lcblk->surface + index); + } else { + LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", + index, w, h, err, strerror(err)); + // XXX: what to do, what to do? We could try to free some + // hidden surfaces, instead of killing this one? + } + return err; +} + +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + // the test front.{w|h} != temp.{w|h} is not enough because it is possible + // that the size changed back to its previous value before the buffer + // was resized (in the eLocked case below), in which case, we still + // need to execute the code below so the clients have a chance to be + // release. resze() deals with the fact that the size can be the same. + + /* + * Various states we could be in... + + resize = state & eResizeRequested; + if (backbufferChanged) { + if (resize == 0) { + // ERROR, the resized buffer doesn't have its resize flag set + } else if (resize == mask) { + // ERROR one of the buffer has already been resized + } else if (resize == mask ^ eResizeRequested) { + // ERROR, the resized buffer doesn't have its resize flag set + } else if (resize == eResizeRequested) { + // OK, Normal case, proceed with resize + } + } else { + if (resize == 0) { + // OK, nothing special, do nothing + } else if (resize == mask) { + // restarted transaction, do nothing + } else if (resize == mask ^ eResizeRequested) { + // restarted transaction, do nothing + } else if (resize == eResizeRequested) { + // OK, size reset to previous value, proceed with resize + } + } + */ + + // Index of the back buffer + const bool backbufferChanged = (front.w != temp.w) || (front.h != temp.h); + const uint32_t state = lcblk->swapState; + const int32_t clientBackBufferIndex = layer_cblk_t::backBuffer(state); + const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; + uint32_t resizeFlags = state & eResizeRequested; + + if (UNLIKELY(backbufferChanged && (resizeFlags != eResizeRequested))) { + LOGE( "backbuffer size changed, but both resize flags are not set! " + "(layer=%p), state=%08x, requested (%dx%d), drawing (%d,%d), " + "index=%d, (%dx%d), (%dx%d)", + this, state, + int(temp.w), int(temp.h), + int(drawingState().w), int(drawingState().h), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + // if we get there we're pretty screwed. the only reasonable + // thing to do is to pretend we should do the resize since + // backbufferChanged is set (this also will give a chance to + // client to get unblocked) + resizeFlags = eResizeRequested; + } + + if (resizeFlags == eResizeRequested) { + // NOTE: asserting that clientBackBufferIndex!=mFrontBufferIndex + // here, would be wrong and misleading because by this point + // mFrontBufferIndex has not been updated yet. + + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), state=%08x, " + "requested (%dx%d), " + "drawing (%d,%d), " + "index=%d, (%dx%d), (%dx%d)", + this, state, + int(temp.w), int(temp.h), + int(drawingState().w), int(drawingState().h), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + if (state & eLocked) { + // if the buffer is locked, we can't resize anything because + // - the backbuffer is currently in use by the user + // - the front buffer is being shown + // We just act as if the transaction didn't happen and we + // reschedule it later... + flags |= eRestartTransaction; + } else { + // This buffer needs to be resized + status_t err = + resize(clientBackBufferIndex, temp.w, temp.h, "transaction"); + if (err == NO_ERROR) { + const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; + android_atomic_and(~mask, &(lcblk->swapState)); + // since a buffer became available, we can let the client go... + mFlinger->scheduleBroadcast(client); + mResizeTransactionDone = true; + + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } + } + } + } + } + + if (temp.sequence != front.sequence) { + if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) { + // this surface is now hidden, so it shouldn't hold a freeze lock + // (it may never redraw, which is fine if it is hidden) + mFreezeLock.clear(); + } + } + + return LayerBase::doTransaction(flags); +} + +status_t Layer::resize( + int32_t clientBackBufferIndex, + uint32_t width, uint32_t height, + const char* what) +{ + /* + * handle resize (backbuffer and frontbuffer reallocation) + */ + + const LayerBitmap& clientBackBuffer(mBuffers[clientBackBufferIndex]); + + // if the new (transaction) size is != from the the backbuffer + // then we need to reallocate the backbuffer + bool backbufferChanged = (clientBackBuffer.width() != width) || + (clientBackBuffer.height() != height); + + LOGD_IF(!backbufferChanged, + "(%s) eResizeRequested (layer=%p), but size not changed: " + "requested (%dx%d), drawing (%d,%d), current (%d,%d)," + "state=%08lx, index=%d, (%dx%d), (%dx%d)", + what, this, + int(width), int(height), + int(drawingState().w), int(drawingState().h), + int(currentState().w), int(currentState().h), + long(lcblk->swapState), + int(clientBackBufferIndex), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + // this can happen when changing the size back and forth quickly + status_t err = NO_ERROR; + if (backbufferChanged) { + err = reallocateBuffer(clientBackBufferIndex, width, height); + } + if (UNLIKELY(err != NO_ERROR)) { + // couldn't reallocate the surface + android_atomic_write(eInvalidSurface, &lcblk->swapState); + memset(lcblk->surface+clientBackBufferIndex, 0, sizeof(surface_info_t)); + } + return err; +} + +void Layer::setSizeChanged(uint32_t w, uint32_t h) +{ + LOGD_IF(DEBUG_RESIZE, + "setSizeChanged w=%d, h=%d (old: w=%d, h=%d)", + w, h, mCurrentState.w, mCurrentState.h); + android_atomic_or(eResizeRequested, &(lcblk->swapState)); +} + +// ---------------------------------------------------------------------------- +// pageflip handling... +// ---------------------------------------------------------------------------- + +void Layer::lockPageFlip(bool& recomputeVisibleRegions) +{ + uint32_t state = android_atomic_or(eBusy, &(lcblk->swapState)); + // preemptively block the client, because he might set + // eFlipRequested at any time and want to use this buffer + // for the next frame. This will be unset below if it + // turns out we didn't need it. + + uint32_t mask = eInvalidSurface | eFlipRequested | eResizeRequested; + if (!(state & mask)) + return; + + if (UNLIKELY(state & eInvalidSurface)) { + // if eInvalidSurface is set, this means the surface + // became invalid during a transaction (NO_MEMORY for instance) + mFlinger->scheduleBroadcast(client); + return; + } + + if (UNLIKELY(state & eFlipRequested)) { + uint32_t oldState; + mPostedDirtyRegion = post(&oldState, recomputeVisibleRegions); + if (oldState & eNextFlipPending) { + // Process another round (we know at least a buffer + // is ready for that client). + mFlinger->signalEvent(); + } + } +} + +Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) +{ + // atomically swap buffers and (re)set eFlipRequested + int32_t oldValue, newValue; + layer_cblk_t * const lcblk = this->lcblk; + do { + oldValue = lcblk->swapState; + // get the current value + + LOG_ASSERT(oldValue&eFlipRequested, + "eFlipRequested not set, yet we're flipping! (state=0x%08lx)", + long(oldValue)); + + newValue = (oldValue ^ eIndex); + // swap buffers + + newValue &= ~(eFlipRequested | eNextFlipPending); + // clear eFlipRequested and eNextFlipPending + + if (oldValue & eNextFlipPending) + newValue |= eFlipRequested; + // if eNextFlipPending is set (second buffer already has something + // in it) we need to reset eFlipRequested because the client + // might never do it + + } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); + *previousSate = oldValue; + + const int32_t index = (newValue & eIndex) ^ 1; + mFrontBufferIndex = index; + + // ... post the new front-buffer + Region dirty(lcblk->region + index); + dirty.andSelf(frontBuffer().bounds()); + + //LOGI("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n", + // oldValue, newValue, mFrontBufferIndex); + //dirty.dump("dirty"); + + if (UNLIKELY(oldValue & eResizeRequested)) { + + LOGD_IF(DEBUG_RESIZE, + "post (layer=%p), state=%08x, " + "index=%d, (%dx%d), (%dx%d)", + this, newValue, + int(1-index), + int(mBuffers[0].width()), int(mBuffers[0].height()), + int(mBuffers[1].width()), int(mBuffers[1].height())); + + // here, we just posted the surface and we have resolved + // the front/back buffer indices. The client is blocked, so + // it cannot start using the new backbuffer. + + // If the backbuffer was resized in THIS round, we actually cannot + // resize the frontbuffer because it has *just* been drawn (and we + // would have nothing to draw). In this case we just skip the resize + // it'll happen after the next page flip or during the next + // transaction. + + const uint32_t mask = (1-index) ? eResizeBuffer1 : eResizeBuffer0; + if (mResizeTransactionDone && (newValue & mask)) { + // Resize the layer's second buffer only if the transaction + // happened. It may not have happened yet if eResizeRequested + // was set immediately after the "transactionRequested" test, + // in which case the drawing state's size would be wrong. + mFreezeLock.clear(); + const Layer::State& s(drawingState()); + if (resize(1-index, s.w, s.h, "post") == NO_ERROR) { + do { + oldValue = lcblk->swapState; + if ((oldValue & eResizeRequested) == eResizeRequested) { + // ugh, another resize was requested since we processed + // the first buffer, don't free the client, and let + // the next transaction handle everything. + break; + } + newValue = oldValue & ~mask; + } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); + } + mResizeTransactionDone = false; + recomputeVisibleRegions = true; + this->contentDirty = true; + } + } + + reloadTexture(dirty); + + return dirty; +} + +Point Layer::getPhysicalSize() const +{ + const LayerBitmap& front(frontBuffer()); + return Point(front.width(), front.height()); +} + +void Layer::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + Region dirtyRegion(mPostedDirtyRegion); + if (!dirtyRegion.isEmpty()) { + mPostedDirtyRegion.clear(); + // The dirty region is given in the layer's coordinate space + // transform the dirty region by the surface's transformation + // and the global transformation. + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + dirtyRegion = tr.transform(dirtyRegion); + + // At this point, the dirty region is in screen space. + // Make sure it's constrained by the visible region (which + // is in screen space as well). + dirtyRegion.andSelf(visibleRegionScreen); + outDirtyRegion.orSelf(dirtyRegion); + + // client could be blocked, so signal them so they get a + // chance to reevaluate their condition. + mFlinger->scheduleBroadcast(client); + } +} + +void Layer::finishPageFlip() +{ + if (LIKELY(!(lcblk->swapState & eInvalidSurface))) { + LOGE_IF(!(lcblk->swapState & eBusy), + "layer %p wasn't locked!", this); + android_atomic_and(~eBusy, &(lcblk->swapState)); + } + mFlinger->scheduleBroadcast(client); +} + + +// --------------------------------------------------------------------------- + + +}; // namespace android |