/* * 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 <utils/Errors.h> #include <utils/Log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <GLES/gl.h> #include <GLES/glext.h> #include <hardware/hardware.h> #include "clz.h" #include "LayerBase.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" #include "TextureManager.h" namespace android { // --------------------------------------------------------------------------- int32_t LayerBase::sSequence = 1; LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), sequence(uint32_t(android_atomic_inc(&sSequence))), mFlinger(flinger), mNeedsFiltering(false), mOrientation(0), mLeft(0), mTop(0), mTransactionFlags(0), mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), mInvalidate(0) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); mBufferCrop.makeInvalid(); mBufferTransform = 0; } LayerBase::~LayerBase() { } void LayerBase::setName(const String8& name) { mName = name; } String8 LayerBase::getName() const { return mName; } const GraphicPlane& LayerBase::graphicPlane(int dpy) const { return mFlinger->graphicPlane(dpy); } GraphicPlane& LayerBase::graphicPlane(int dpy) { return mFlinger->graphicPlane(dpy); } void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags) { uint32_t layerFlags = 0; if (flags & ISurfaceComposer::eHidden) layerFlags = ISurfaceComposer::eLayerHidden; if (flags & ISurfaceComposer::eNonPremultiplied) mPremultipliedAlpha = false; mCurrentState.z = 0; mCurrentState.w = w; mCurrentState.h = h; mCurrentState.requested_w = w; mCurrentState.requested_h = h; mCurrentState.alpha = 0xFF; mCurrentState.flags = layerFlags; mCurrentState.sequence = 0; mCurrentState.transform.set(0, 0); // drawing state & current state are identical mDrawingState = mCurrentState; } void LayerBase::commitTransaction() { mDrawingState = mCurrentState; } void LayerBase::forceVisibilityTransaction() { // this can be called without SurfaceFlinger.mStateLock, but if we // can atomically increment the sequence number, it doesn't matter. android_atomic_inc(&mCurrentState.sequence); requestTransaction(); } bool LayerBase::requestTransaction() { int32_t old = setTransactionFlags(eTransactionNeeded); return ((old & eTransactionNeeded) == 0); } uint32_t LayerBase::getTransactionFlags(uint32_t flags) { return android_atomic_and(~flags, &mTransactionFlags) & flags; } uint32_t LayerBase::setTransactionFlags(uint32_t flags) { return android_atomic_or(flags, &mTransactionFlags); } bool LayerBase::setPosition(int32_t x, int32_t y) { if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false; mCurrentState.sequence++; mCurrentState.transform.set(x, y); requestTransaction(); return true; } bool LayerBase::setLayer(uint32_t z) { if (mCurrentState.z == z) return false; mCurrentState.sequence++; mCurrentState.z = z; requestTransaction(); return true; } bool LayerBase::setSize(uint32_t w, uint32_t h) { if (mCurrentState.requested_w == w && mCurrentState.requested_h == h) return false; mCurrentState.requested_w = w; mCurrentState.requested_h = h; requestTransaction(); return true; } bool LayerBase::setAlpha(uint8_t alpha) { if (mCurrentState.alpha == alpha) return false; mCurrentState.sequence++; mCurrentState.alpha = alpha; requestTransaction(); return true; } bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { mCurrentState.sequence++; mCurrentState.transform.set( matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); requestTransaction(); return true; } bool LayerBase::setTransparentRegionHint(const Region& transparent) { mCurrentState.sequence++; mCurrentState.transparentRegion = transparent; requestTransaction(); return true; } bool LayerBase::setFlags(uint8_t flags, uint8_t mask) { const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); if (mCurrentState.flags == newFlags) return false; mCurrentState.sequence++; mCurrentState.flags = newFlags; requestTransaction(); return true; } Rect LayerBase::visibleBounds() const { return mTransformedBounds; } void LayerBase::setVisibleRegion(const Region& visibleRegion) { // always called from main thread visibleRegionScreen = visibleRegion; } void LayerBase::setCoveredRegion(const Region& coveredRegion) { // always called from main thread coveredRegionScreen = coveredRegion; } uint32_t LayerBase::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)) { // resize the layer, set the physical size to the requested size Layer::State& editTemp(currentState()); editTemp.w = temp.requested_w; editTemp.h = temp.requested_h; } if ((front.w != temp.w) || (front.h != temp.h)) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } if (temp.sequence != front.sequence) { // invalidate and recompute the visible regions if needed flags |= eVisibleRegion; this->contentDirty = true; // we may use linear filtering, if the matrix scales us const uint8_t type = temp.transform.getType(); mNeedsFiltering = (!temp.transform.preserveRects() || (type >= Transform::SCALE)); } // Commit the transaction commitTransaction(); return flags; } void LayerBase::validateVisibility(const Transform& planeTransform) { const Layer::State& s(drawingState()); const Transform tr(planeTransform * s.transform); const bool transformed = tr.transformed(); uint32_t w = s.w; uint32_t h = s.h; tr.transform(mVertices[0], 0, 0); tr.transform(mVertices[1], 0, h); tr.transform(mVertices[2], w, h); tr.transform(mVertices[3], w, 0); if (UNLIKELY(transformed)) { // NOTE: here we could also punt if we have too many rectangles // in the transparent region if (tr.preserveRects()) { // transform the transparent region transparentRegionScreen = tr.transform(s.transparentRegion); } else { // transformation too complex, can't do the transparent region // optimization. transparentRegionScreen.clear(); } } else { transparentRegionScreen = s.transparentRegion; } // cache a few things... mOrientation = tr.getOrientation(); mTransformedBounds = tr.makeBounds(w, h); mLeft = tr.tx(); mTop = tr.ty(); } void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) { } void LayerBase::unlockPageFlip( const Transform& planeTransform, Region& outDirtyRegion) { if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { outDirtyRegion.orSelf(visibleRegionScreen); } } void LayerBase::invalidate() { if ((android_atomic_or(1, &mInvalidate)&1) == 0) { mFlinger->signalEvent(); } } void LayerBase::drawRegion(const Region& reg) const { Region::const_iterator it = reg.begin(); Region::const_iterator const end = reg.end(); if (it != end) { Rect r; const DisplayHardware& hw(graphicPlane(0).displayHardware()); const int32_t fbWidth = hw.getWidth(); const int32_t fbHeight = hw.getHeight(); const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, { fbWidth, fbHeight }, { 0, fbHeight } }; glVertexPointer(2, GL_SHORT, 0, vertices); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } } } void LayerBase::setGeometry(hwc_layer_t* hwcl) { hwcl->flags |= HWC_SKIP_LAYER; } void LayerBase::setPerFrameData(hwc_layer_t* hwcl) { hwcl->compositionType = HWC_FRAMEBUFFER; hwcl->handle = NULL; } void LayerBase::draw(const Region& clip) const { // reset GL state glEnable(GL_SCISSOR_TEST); onDraw(clip); } void LayerBase::drawForSreenShot() const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); onDraw( Region(hw.bounds()) ); } void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); glColor4f(red,green,blue,alpha); TextureManager::deactivateTextures(); glDisable(GL_BLEND); glDisable(GL_DITHER); Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); glEnable(GL_SCISSOR_TEST); glVertexPointer(2, GL_FLOAT, 0, mVertices); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } } void LayerBase::clearWithOpenGL(const Region& clip) const { clearWithOpenGL(clip,0,0,0,0); } template <typename T> static inline void swap(T& a, T& b) { T t(a); a = b; b = t; } void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); const State& s(drawingState()); // bind our texture TextureManager::activateTexture(texture, needsFiltering()); uint32_t width = texture.width; uint32_t height = texture.height; GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; if (UNLIKELY(s.alpha < 0xFF)) { const GLfloat alpha = s.alpha * (1.0f/255.0f); if (mPremultipliedAlpha) { glColor4f(alpha, alpha, alpha, alpha); } else { glColor4f(1, 1, 1, alpha); } glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } else { glColor4f(1, 1, 1, 1); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); if (needsBlending()) { glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } } /* * compute texture coordinates * here, we handle NPOT, cropping and buffer transformations */ GLfloat cl, ct, cr, cb; if (!mBufferCrop.isEmpty()) { // source is cropped const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width; const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height; cl = mBufferCrop.left * us; ct = mBufferCrop.top * vs; cr = mBufferCrop.right * us; cb = mBufferCrop.bottom * vs; } else { cl = 0; ct = 0; cr = (texture.NPOTAdjust ? texture.wScale : 1.0f); cb = (texture.NPOTAdjust ? texture.hScale : 1.0f); } /* * For the buffer transformation, we apply the rotation last. * Since we're transforming the texture-coordinates, we need * to apply the inverse of the buffer transformation: * inverse( FLIP_V -> FLIP_H -> ROT_90 ) * <=> inverse( ROT_90 * FLIP_H * FLIP_V ) * = inverse(FLIP_V) * inverse(FLIP_H) * inverse(ROT_90) * = FLIP_V * FLIP_H * ROT_270 * <=> ROT_270 -> FLIP_H -> FLIP_V * * The rotation is performed first, in the texture coordinate space. * */ struct TexCoords { GLfloat u; GLfloat v; }; enum { // name of the corners in the texture map LB = 0, // left-bottom LT = 1, // left-top RT = 2, // right-top RB = 3 // right-bottom }; // vertices in screen space int vLT = LB; int vLB = LT; int vRB = RT; int vRT = RB; // the texture's source is rotated uint32_t transform = mBufferTransform; if (transform & HAL_TRANSFORM_ROT_90) { vLT = RB; vLB = LB; vRB = LT; vRT = RT; } if (transform & HAL_TRANSFORM_FLIP_V) { swap(vLT, vLB); swap(vRT, vRB); } if (transform & HAL_TRANSFORM_FLIP_H) { swap(vLT, vRT); swap(vLB, vRB); } TexCoords texCoords[4]; texCoords[vLT].u = cl; texCoords[vLT].v = ct; texCoords[vLB].u = cl; texCoords[vLB].v = cb; texCoords[vRB].u = cr; texCoords[vRB].v = cb; texCoords[vRT].u = cr; texCoords[vRT].v = ct; if (needsDithering()) { glEnable(GL_DITHER); } else { glDisable(GL_DITHER); } glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, mVertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); Region::const_iterator it = clip.begin(); Region::const_iterator const end = clip.end(); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } glDisableClientState(GL_TEXTURE_COORD_ARRAY); } void LayerBase::setBufferCrop(const Rect& crop) { if (mBufferCrop != crop) { mBufferCrop = crop; mFlinger->invalidateHwcGeometry(); } } void LayerBase::setBufferTransform(uint32_t transform) { if (mBufferTransform != transform) { mBufferTransform = transform; mFlinger->invalidateHwcGeometry(); } } void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); snprintf(buffer, SIZE, "+ %s %p\n" " " "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", getTypeId(), this, s.z, tx(), ty(), s.w, s.h, needsBlending(), needsDithering(), contentDirty, s.alpha, s.flags, s.transform[0][0], s.transform[0][1], s.transform[1][0], s.transform[1][1]); result.append(buffer); } void LayerBase::shortDump(String8& result, char* scratch, size_t size) const { LayerBase::dump(result, scratch, size); } // --------------------------------------------------------------------------- int32_t LayerBaseClient::sIdentity = 1; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client) : LayerBase(flinger, display), mHasSurface(false), mClientRef(client), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { } LayerBaseClient::~LayerBaseClient() { sp<Client> c(mClientRef.promote()); if (c != 0) { c->detachLayer(this); } } sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() { sp<Surface> s; Mutex::Autolock _l(mLock); LOG_ALWAYS_FATAL_IF(mHasSurface, "LayerBaseClient::getSurface() has already been called"); mHasSurface = true; s = createSurface(); mClientSurfaceBinder = s->asBinder(); return s; } wp<IBinder> LayerBaseClient::getSurfaceBinder() const { return mClientSurfaceBinder; } sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const { return new Surface(mFlinger, mIdentity, const_cast<LayerBaseClient *>(this)); } void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const { LayerBase::dump(result, buffer, SIZE); sp<Client> client(mClientRef.promote()); snprintf(buffer, SIZE, " name=%s\n" " client=%p, identity=%u\n", getName().string(), client.get(), getIdentity()); result.append(buffer); } void LayerBaseClient::shortDump(String8& result, char* scratch, size_t size) const { LayerBaseClient::dump(result, scratch, size); } // --------------------------------------------------------------------------- LayerBaseClient::Surface::Surface( const sp<SurfaceFlinger>& flinger, int identity, const sp<LayerBaseClient>& owner) : mFlinger(flinger), mIdentity(identity), mOwner(owner) { } LayerBaseClient::Surface::~Surface() { /* * This is a good place to clean-up all client resources */ // destroy client resources sp<LayerBaseClient> layer = getOwner(); if (layer != 0) { mFlinger->destroySurface(layer); } } sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const { sp<LayerBaseClient> owner(mOwner.promote()); return owner; } status_t LayerBaseClient::Surface::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { return BnSurface::onTransact(code, data, reply, flags); } sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { return NULL; } status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) { return INVALID_OPERATION; } // --------------------------------------------------------------------------- }; // namespace android