| /* |
| * 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 |