summaryrefslogtreecommitdiffstats
path: root/libs/surfaceflinger/Layer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/surfaceflinger/Layer.cpp')
-rw-r--r--libs/surfaceflinger/Layer.cpp568
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