/* * 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 #include #include #include #include #include #include #include #include #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 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) && (mFlinger->getGPU() != 0)) { // 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 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(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