diff options
Diffstat (limited to 'libs/surfaceflinger/Layer.cpp')
-rw-r--r-- | libs/surfaceflinger/Layer.cpp | 783 |
1 files changed, 386 insertions, 397 deletions
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 96395a8..f5a5a0b 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -14,26 +14,24 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> #include <cutils/properties.h> +#include <cutils/native_handle.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/StopWatch.h> +#include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> -#include <ui/EGLDisplaySurface.h> +#include <ui/Surface.h> #include "clz.h" #include "Layer.h" -#include "LayerBitmap.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" @@ -49,292 +47,375 @@ const char* const Layer::typeID = "Layer"; // --------------------------------------------------------------------------- -Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& c, int32_t i) : LayerBaseClient(flinger, display, c, i), mSecure(false), - mFrontBufferIndex(1), + mNoEGLImageForSwBuffers(false), mNeedsBlending(true), - mResizeTransactionDone(false), - mTextureName(-1U), mTextureWidth(0), mTextureHeight(0) + mNeedsDithering(false) { // no OpenGL operation is possible here, since we might not be // in the OpenGL thread. + mFrontBufferIndex = lcblk->getFrontBuffer(); } Layer::~Layer() { - client->free(clientIndex()); - // this should always be called from the OpenGL thread - if (mTextureName != -1U) { - //glDeleteTextures(1, &mTextureName); - deletedTextures.add(mTextureName); - } + destroy(); + // the actual buffers will be destroyed here } -void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) +void Layer::destroy() { - LayerBase::initStates(w,h,flags); - - if (flags & ISurfaceComposer::eDestroyBackbuffer) - lcblk->flags |= eNoCopyBack; + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + if (mTextures[i].name != -1U) { + glDeleteTextures(1, &mTextures[i].name); + mTextures[i].name = -1U; + } + if (mTextures[i].image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTextures[i].image); + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + Mutex::Autolock _l(mLock); + mBuffers[i].clear(); + mWidth = mHeight = 0; + } + mSurface.clear(); } -sp<LayerBaseClient::Surface> Layer::getSurface() const +sp<LayerBaseClient::Surface> Layer::createSurface() const { return mSurface; } -status_t Layer::setBuffers( Client* client, - uint32_t w, uint32_t h, +status_t Layer::ditch() +{ + // the layer is not on screen anymore. free as much resources as possible + destroy(); + return NO_ERROR; +} + +status_t Layer::setBuffers( uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { + // this surfaces pixel format 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); + // the display's pixel format + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + PixelFormatInfo displayInfo; + getPixelFormatInfo(hw.getFormat(), &displayInfo); + const uint32_t hwFlags = hw.getFlags(); - // 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; - } - } - + mFormat = format; + mWidth = w; + mHeight = h; 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); + mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); + + // we use the red index + int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); + int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); + mNeedsDithering = layerRedsize > displayRedSize; + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mBuffers[i] = new GraphicBuffer(); + } + mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); 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(); + Mutex::Autolock _l(mLock); + sp<GraphicBuffer> buffer(getFrontBufferLocked()); + int index = mFrontBufferIndex; + + // create the new texture name if needed + if (UNLIKELY(mTextures[index].name == -1U)) { + mTextures[index].name = createTexture(); + mTextures[index].width = 0; + mTextures[index].height = 0; } - const GGLSurface& t(frontBuffer().surface()); - loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight); -} +#ifdef EGL_ANDROID_image_native_buffer + if (mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { + if (mTextures[index].dirty) { + initializeEglImage(buffer, &mTextures[index]); + } + } else { + if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || + mHybridBuffer->height != buffer->height)) { + mHybridBuffer.clear(); + mHybridBuffer = new GraphicBuffer( + buffer->width, buffer->height, buffer->format, + GraphicBuffer::USAGE_SW_WRITE_OFTEN | + GraphicBuffer::USAGE_HW_TEXTURE); + initializeEglImage( + mHybridBuffer, &mTextures[0]); + } + + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + Texture* const texture(&mTextures[0]); + + glBindTexture(GL_TEXTURE_2D, texture->name); + + sp<GraphicBuffer> buf(mHybridBuffer); + void* vaddr; + res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); + if (res == NO_ERROR) { + int bpp = 0; + switch (t.format) { + case GGL_PIXEL_FORMAT_RGB_565: + case GGL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + case GGL_PIXEL_FORMAT_RGBA_8888: + case GGL_PIXEL_FORMAT_RGBX_8888: + bpp = 4; + break; + case GGL_PIXEL_FORMAT_YCbCr_422_SP: + case GGL_PIXEL_FORMAT_YCbCr_420_SP: + // just show the Y plane of YUV buffers + bpp = 1; + break; + default: + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, texture->name, t.format); + } + if (bpp) { + const Rect bounds(dirty.getBounds()); + size_t src_stride = t.stride; + size_t dst_stride = buf->stride; + if (src_stride == dst_stride && + bounds.width() == t.width && + bounds.height() == t.height) + { + memcpy(vaddr, t.data, t.height * t.stride * bpp); + } else { + GLubyte const * src = t.data + + (bounds.left + bounds.top * src_stride) * bpp; + GLubyte * dst = (GLubyte *)vaddr + + (bounds.left + bounds.top * dst_stride) * bpp; + const size_t length = bounds.width() * bpp; + size_t h = bounds.height(); + src_stride *= bpp; + dst_stride *= bpp; + while (h--) { + memcpy(dst, src, length); + dst += dst_stride; + src += src_stride; + } + } + } + buf->unlock(); + } + buffer->unlock(); + } + } + } else +#endif + { + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + loadTexture(&mTextures[0], dirty, t); + buffer->unlock(); + } + } +} void Layer::onDraw(const Region& clip) const { - if (UNLIKELY(mTextureName == -1LU)) { - //LOGW("Layer %p doesn't have a texture", this); + int index = mFrontBufferIndex; + if (mTextures[index].image == EGL_NO_IMAGE_KHR) + index = 0; + GLuint textureName = mTextures[index].name; + if (UNLIKELY(textureName == -1LU)) { // the texture has not been created yet, this Layer has // in fact never been drawn into. this happens frequently with // SurfaceView. clearWithOpenGL(clip); return; } + drawWithOpenGL(clip, mTextures[index]); +} - 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); +sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + + // this ensures our client doesn't go away while we're accessing + // the shared area. + sp<Client> ourClient(client.promote()); + if (ourClient == 0) { + // oops, the client is already gone + return buffer; + } + + /* + * This is called from the client's Surface::dequeue(). This can happen + * at any time, especially while we're in the middle of using the + * buffer 'index' as our front buffer. + * + * Make sure the buffer we're resizing is not the front buffer and has been + * dequeued. Once this condition is asserted, we are guaranteed that this + * buffer cannot become the front buffer under our feet, since we're called + * from Surface::dequeue() + */ + status_t err = lcblk->assertReallocate(index); + LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err)); + if (err != NO_ERROR) { + // the surface may have died + return buffer; } - if (!can_use_copybit || err) { - drawWithOpenGL(clip, mTextureName, t); + uint32_t w, h; + { // scope for the lock + Mutex::Autolock _l(mLock); + w = mWidth; + h = mHeight; + buffer = mBuffers[index]; + + // destroy() could have been called before we get here, we log it + // because it's uncommon, and the code below should handle it + LOGW_IF(buffer==0, + "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", + index, w, h); + + mBuffers[index].clear(); } -} -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); + const uint32_t effectiveUsage = getEffectiveUsage(usage); + if (buffer!=0 && buffer->getStrongCount() == 1) { + err = buffer->reallocate(w, h, mFormat, effectiveUsage); } 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? + // here we have to reallocate a new buffer because we could have a + // client in our process with a reference to it (eg: status bar), + // and we can't release the handle under its feet. + buffer.clear(); + buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage); + err = buffer->initCheck(); } - return err; -} -uint32_t Layer::doTransaction(uint32_t flags) -{ - const Layer::State& front(drawingState()); - const Layer::State& temp(currentState()); + if (err || buffer->handle == 0) { + LOGE_IF(err || buffer->handle == 0, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)", + this, index, w, h, strerror(-err)); + } else { + LOGD_IF(DEBUG_RESIZE, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d, handle=%p", + this, index, w, h, buffer->handle); + } - // 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. + if (err == NO_ERROR && buffer->handle != 0) { + Mutex::Autolock _l(mLock); + if (mWidth && mHeight) { + // and we have new buffer + mBuffers[index] = buffer; + // texture is now dirty... + mTextures[index].dirty = true; + } else { + // oops we got killed while we were allocating the buffer + buffer.clear(); + } + } + return buffer; +} +uint32_t Layer::getEffectiveUsage(uint32_t usage) const +{ /* - * 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 - } - } + * buffers used for software rendering, but h/w composition + * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE + * + * buffers used for h/w rendering and h/w composition + * are allocated with HW_RENDER | HW_TEXTURE + * + * buffers used with h/w rendering and either NPOT or no egl_image_ext + * are allocated with SW_READ_RARELY | HW_RENDER + * */ - // 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 (mSecure) { + // secure buffer, don't store it into the GPU + usage = GraphicBuffer::USAGE_SW_READ_OFTEN | + GraphicBuffer::USAGE_SW_WRITE_OFTEN; + } else { + // it's allowed to modify the usage flags here, but generally + // the requested flags should be honored. + if (mNoEGLImageForSwBuffers) { + if (usage & GraphicBuffer::USAGE_HW_MASK) { + // request EGLImage for h/w buffers only + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + } else { + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } } + return usage; +} - if (resizeFlags == eResizeRequested) { - // NOTE: asserting that clientBackBufferIndex!=mFrontBufferIndex - // here, would be wrong and misleading because by this point - // mFrontBufferIndex has not been updated yet. +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + if ((front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h)) { + // the size changed, we need to ask our client to request a new buffer 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(); - } - } + "resize (layer=%p), requested (%dx%d), " + "drawing (%d,%d), (%dx%d), (%dx%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h), + int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), + int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); + + // 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(); } } + + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; + + // record the new size, form this point on, when the client request a + // buffer, it'll get the new size. + setDrawingSize(temp.requested_w, temp.requested_h); + + // all buffers need reallocation + lcblk->reallocate(); } - + 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 @@ -346,54 +427,10 @@ uint32_t Layer::doTransaction(uint32_t flags) 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)); +void Layer::setDrawingSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + mWidth = w; + mHeight = h; } // ---------------------------------------------------------------------------- @@ -402,128 +439,61 @@ void Layer::setSizeChanged(uint32_t w, uint32_t h) 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); + ssize_t buf = lcblk->retireAndLock(); + if (buf < NO_ERROR) { + //LOGW("nothing to retire (%s)", strerror(-buf)); + // NOTE: here the buffer is locked because we will used + // for composition later in the loop return; } + + // we retired a buffer, which becomes the new front buffer + mFrontBufferIndex = buf; - 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)) { + // get the dirty region + sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); + const Region dirty(lcblk->getDirtyRegion(buf)); + mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); - 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; + const Layer::State& front(drawingState()); + if (newFrontBuffer->getWidth() == front.requested_w && + newFrontBuffer->getHeight() == front.requested_h) + { + if ((front.w != front.requested_w) || + (front.h != front.requested_h)) + { + // Here we pretend the transaction happened by updating the + // current and drawing states. Drawing state is only accessed + // in this thread, no need to have it locked + Layer::State& editDraw(mDrawingState); + editDraw.w = editDraw.requested_w; + editDraw.h = editDraw.requested_h; + + // We also need to update the current state so that we don't + // end-up doing too much work during the next transaction. + // NOTE: We actually don't need hold the transaction lock here + // because State::w and State::h are only accessed from + // this thread + Layer::State& editTemp(currentState()); + editTemp.w = editDraw.w; + editTemp.h = editDraw.h; + + // recompute visible region recomputeVisibleRegions = true; - this->contentDirty = true; } - } - reloadTexture(dirty); + // we now have the correct size, unfreeze the screen + mFreezeLock.clear(); + } - return dirty; -} + if (lcblk->getQueuedCount()) { + // signal an event if we have more buffers waiting + mFlinger->signalEvent(); + } -Point Layer::getPhysicalSize() const -{ - const LayerBitmap& front(frontBuffer()); - return Point(front.width(), front.height()); + if (!mPostedDirtyRegion.isEmpty()) { + reloadTexture( mPostedDirtyRegion ); + } } void Layer::unlockPageFlip( @@ -544,23 +514,42 @@ void Layer::unlockPageFlip( // 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); + status_t err = lcblk->unlock( mFrontBufferIndex ); + LOGE_IF(err!=NO_ERROR, + "layer %p, buffer=%d wasn't locked!", + this, mFrontBufferIndex); +} + +// --------------------------------------------------------------------------- + +Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner) + : Surface(flinger, id, owner->getIdentity(), owner) +{ +} + +Layer::SurfaceLayer::~SurfaceLayer() +{ } +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + sp<Layer> owner(getOwner()); + if (owner != 0) { + LOGE_IF(uint32_t(index)>=NUM_BUFFERS, + "getBuffer() index (%d) out of range", index); + if (uint32_t(index) < NUM_BUFFERS) { + buffer = owner->requestBuffer(index, usage); + } + } + return buffer; +} // --------------------------------------------------------------------------- |