| /* |
| * 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. |
| */ |
| |
| #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/PixelFormat.h> |
| #include <ui/Surface.h> |
| |
| #include "clz.h" |
| #include "Layer.h" |
| #include "LayerBitmap.h" |
| #include "SurfaceFlinger.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) |
| { |
| // no OpenGL operation is possible here, since we might not be |
| // in the OpenGL thread. |
| } |
| |
| Layer::~Layer() |
| { |
| destroy(); |
| // the actual buffers will be destroyed here |
| } |
| |
| void Layer::destroy() |
| { |
| for (int 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; |
| } |
| } |
| } |
| |
| 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::createSurface() const |
| { |
| return mSurface; |
| } |
| |
| status_t Layer::ditch() |
| { |
| // the layer is not on screen anymore. free as much resources as possible |
| mSurface.clear(); |
| destroy(); |
| return NO_ERROR; |
| } |
| |
| 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; |
| |
| uint32_t bufferFlags = 0; |
| if (flags & ISurfaceComposer::eGPU) |
| bufferFlags |= Buffer::GPU; |
| |
| if (flags & ISurfaceComposer::eSecure) |
| bufferFlags |= Buffer::SECURE; |
| |
| /* FIXME we need this code for msm7201A |
| if (bufferFlags & Buffer::GPU) { |
| // FIXME: this is msm7201A specific, as its GPU only supports |
| // BGRA_8888. |
| if (format == PIXEL_FORMAT_RGBA_8888) { |
| format = PIXEL_FORMAT_BGRA_8888; |
| } |
| } |
| */ |
| |
| mSecure = (bufferFlags & Buffer::SECURE) ? true : false; |
| mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; |
| for (int i=0 ; i<2 ; i++) { |
| err = mBuffers[i].init(lcblk->surface + i, w, h, format, bufferFlags); |
| if (err != NO_ERROR) { |
| return err; |
| } |
| } |
| mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); |
| return NO_ERROR; |
| } |
| |
| void Layer::reloadTexture(const Region& dirty) |
| { |
| const sp<Buffer>& buffer(frontBuffer().getBuffer()); |
| if (LIKELY(mFlags & DisplayHardware::DIRECT_TEXTURE)) { |
| int index = mFrontBufferIndex; |
| if (LIKELY(!mTextures[index].dirty)) { |
| glBindTexture(GL_TEXTURE_2D, mTextures[index].name); |
| } else { |
| // we need to recreate the texture |
| EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); |
| |
| // create the new texture name if needed |
| if (UNLIKELY(mTextures[index].name == -1U)) { |
| mTextures[index].name = createTexture(); |
| } else { |
| glBindTexture(GL_TEXTURE_2D, mTextures[index].name); |
| } |
| |
| // free the previous image |
| if (mTextures[index].image != EGL_NO_IMAGE_KHR) { |
| eglDestroyImageKHR(dpy, mTextures[index].image); |
| mTextures[index].image = EGL_NO_IMAGE_KHR; |
| } |
| |
| // construct an EGL_NATIVE_BUFFER_ANDROID |
| android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); |
| |
| // create the new EGLImageKHR |
| const EGLint attrs[] = { |
| EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, |
| EGL_NONE, EGL_NONE |
| }; |
| mTextures[index].image = eglCreateImageKHR( |
| dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, |
| (EGLClientBuffer)clientBuf, attrs); |
| |
| LOGE_IF(mTextures[index].image == EGL_NO_IMAGE_KHR, |
| "eglCreateImageKHR() failed. err=0x%4x", |
| eglGetError()); |
| |
| if (mTextures[index].image != EGL_NO_IMAGE_KHR) { |
| glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, |
| (GLeglImageOES)mTextures[index].image); |
| GLint error = glGetError(); |
| if (UNLIKELY(error != GL_NO_ERROR)) { |
| // this failed, for instance, because we don't support |
| // NPOT. |
| // FIXME: do something! |
| mFlags &= ~DisplayHardware::DIRECT_TEXTURE; |
| } else { |
| // Everything went okay! |
| mTextures[index].dirty = false; |
| } |
| } |
| } |
| } else { |
| GGLSurface t; |
| status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_RARELY); |
| LOGE_IF(res, "error %d (%s) locking buffer %p", |
| res, strerror(res), buffer.get()); |
| if (res == NO_ERROR) { |
| if (UNLIKELY(mTextures[0].name == -1U)) { |
| mTextures[0].name = createTexture(); |
| } |
| loadTexture(dirty, mTextures[0].name, t, |
| mTextures[0].width, mTextures[0].height); |
| buffer->unlock(); |
| } |
| } |
| } |
| |
| |
| void Layer::onDraw(const Region& clip) const |
| { |
| const int index = (mFlags & DisplayHardware::DIRECT_TEXTURE) ? |
| mFrontBufferIndex : 0; |
| GLuint textureName = mTextures[index].name; |
| |
| if (UNLIKELY(textureName == -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; |
| } |
| |
| GGLSurface t; |
| sp<const Buffer> buffer(frontBuffer().getBuffer()); |
| drawWithOpenGL(clip, textureName, buffer); |
| } |
| |
| sp<SurfaceBuffer> Layer::peekBuffer() |
| { |
| /* |
| * This is called from the client's Surface::lock(), after it locked |
| * the surface successfully. We're therefore guaranteed that the |
| * back-buffer is not in use by ourselves. |
| * Of course, we need to validate all this, which is not trivial. |
| * |
| * FIXME: A resize could happen at any time here. What to do about this? |
| * - resize() form post() |
| * - resize() from doTransaction() |
| * |
| * We'll probably need an internal lock for this. |
| * |
| * |
| * TODO: We need to make sure that post() doesn't swap |
| * the buffers under us. |
| */ |
| |
| // it's okay to read swapState for the purpose of figuring out the |
| // backbuffer index, which cannot change (since the app has locked it). |
| const uint32_t state = lcblk->swapState; |
| const int32_t backBufferIndex = layer_cblk_t::backBuffer(state); |
| |
| // get rid of the EGL image, since we shouldn't need it anymore |
| // (note that we're in a different thread than where it is being used) |
| if (mTextures[backBufferIndex].image != EGL_NO_IMAGE_KHR) { |
| EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); |
| eglDestroyImageKHR(dpy, mTextures[backBufferIndex].image); |
| mTextures[backBufferIndex].image = EGL_NO_IMAGE_KHR; |
| } |
| |
| LayerBitmap& layerBitmap(mBuffers[backBufferIndex]); |
| sp<SurfaceBuffer> buffer = layerBitmap.allocate(); |
| |
| LOGD_IF(DEBUG_RESIZE, |
| "Layer::getBuffer(this=%p), index=%d, (%d,%d), (%d,%d)", |
| this, backBufferIndex, |
| layerBitmap.getWidth(), |
| layerBitmap.getHeight(), |
| layerBitmap.getBuffer()->getWidth(), |
| layerBitmap.getBuffer()->getHeight()); |
| |
| if (UNLIKELY(buffer == 0)) { |
| // XXX: what to do, what to do? |
| } else { |
| // texture is now dirty... |
| mTextures[backBufferIndex].dirty = true; |
| // ... so it the visible region (because we consider the surface's |
| // buffer size for visibility calculations) |
| forceVisibilityTransaction(); |
| mFlinger->setTransactionFlags(eTraversalNeeded); |
| } |
| return buffer; |
| } |
| |
| 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. resize() 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].getWidth()), int(mBuffers[0].getHeight()), |
| int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); |
| // 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].getWidth()), int(mBuffers[0].getHeight()), |
| int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); |
| |
| 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) |
| * this is called from post() or from doTransaction() |
| */ |
| |
| 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.getWidth() != width) || |
| (clientBackBuffer.getHeight() != 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].getWidth()), int(mBuffers[0].getHeight()), |
| int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); |
| |
| // this can happen when changing the size back and forth quickly |
| status_t err = NO_ERROR; |
| if (backbufferChanged) { |
| |
| LOGD_IF(DEBUG_RESIZE, |
| "resize (layer=%p), requested (%dx%d), " |
| "index=%d, (%dx%d), (%dx%d)", |
| this, int(width), int(height), int(clientBackBufferIndex), |
| int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), |
| int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); |
| |
| err = mBuffers[clientBackBufferIndex].setSize(width, height); |
| if (UNLIKELY(err != NO_ERROR)) { |
| // This really should never happen |
| LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", |
| clientBackBufferIndex, width, height, err, strerror(err)); |
| // couldn't reallocate the surface |
| android_atomic_write(eInvalidSurface, &lcblk->swapState); |
| } |
| } |
| 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; |
| |
| /* NOTE: it's safe to set this flag here because this is only touched |
| * from LayerBitmap::allocate(), which by construction cannot happen |
| * while we're in post(). |
| */ |
| lcblk->surface[index].flags &= ~surface_info_t::eBufferDirty; |
| |
| // ... post the new front-buffer |
| Region dirty(lcblk->region + index); |
| dirty.andSelf(frontBuffer().getBounds()); |
| |
| //LOGD("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].getWidth()), int(mBuffers[0].getHeight()), |
| int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); |
| |
| // 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 |
| { |
| sp<const Buffer> front(frontBuffer().getBuffer()); |
| return Point(front->getWidth(), front->getHeight()); |
| } |
| |
| 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); |
| } |
| |
| // --------------------------------------------------------------------------- |
| |
| Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, |
| SurfaceID id, const sp<Layer>& owner) |
| : Surface(flinger, id, owner->getIdentity(), owner) |
| { |
| } |
| |
| Layer::SurfaceLayer::~SurfaceLayer() |
| { |
| } |
| |
| sp<SurfaceBuffer> Layer::SurfaceLayer::getBuffer() |
| { |
| sp<SurfaceBuffer> buffer = 0; |
| sp<Layer> owner(getOwner()); |
| if (owner != 0) { |
| buffer = owner->peekBuffer(); |
| } |
| return buffer; |
| } |
| |
| // --------------------------------------------------------------------------- |
| |
| |
| }; // namespace android |