diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /libs/surfaceflinger | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'libs/surfaceflinger')
44 files changed, 9994 insertions, 0 deletions
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk new file mode 100644 index 0000000..496e271 --- /dev/null +++ b/libs/surfaceflinger/Android.mk @@ -0,0 +1,49 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + clz.cpp.arm \ + DisplayHardware/DisplayHardware.cpp \ + DisplayHardware/DisplayHardwareBase.cpp \ + GPUHardware/GPUHardware.cpp \ + BootAnimation.cpp \ + BlurFilter.cpp.arm \ + CPUGauge.cpp \ + Layer.cpp \ + LayerBase.cpp \ + LayerBuffer.cpp \ + LayerBlur.cpp \ + LayerBitmap.cpp \ + LayerDim.cpp \ + LayerOrientationAnim.cpp \ + OrientationAnimation.cpp \ + SurfaceFlinger.cpp \ + Tokenizer.cpp \ + Transform.cpp \ + VRamHeap.cpp + + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt + endif +endif + +LOCAL_SHARED_LIBRARIES := \ + libhardware \ + libutils \ + libcutils \ + libui \ + libcorecg \ + libsgl \ + libpixelflinger \ + libEGL \ + libGLESv1_CM + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) + +LOCAL_MODULE:= libsurfaceflinger + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/surfaceflinger/Barrier.h b/libs/surfaceflinger/Barrier.h new file mode 100644 index 0000000..e2bcf6a --- /dev/null +++ b/libs/surfaceflinger/Barrier.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ANDROID_BARRIER_H +#define ANDROID_BARRIER_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> + +namespace android { + +class Barrier +{ +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + void open() { + // gcc memory barrier, this makes sure all memory writes + // have been issued by gcc. On an SMP system we'd need a real + // h/w barrier. + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +}; // namespace android + +#endif // ANDROID_BARRIER_H diff --git a/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp new file mode 100644 index 0000000..5dc0ba0 --- /dev/null +++ b/libs/surfaceflinger/BlurFilter.cpp @@ -0,0 +1,326 @@ +/* +** +** Copyright 2006, 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 <stdio.h> +#include <string.h> +#include <stdint.h> +#include <utils/Errors.h> + +#include <pixelflinger/pixelflinger.h> + +#include "clz.h" + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +namespace android { + +#if BYTE_ORDER == LITTLE_ENDIAN +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return v; +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return v; +} +#else +inline uint32_t BLUR_RGBA_TO_HOST(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +inline uint32_t BLUR_HOST_TO_RGBA(uint32_t v) { + return (v<<24) | (v>>24) | ((v<<8)&0xff0000) | ((v>>8)&0xff00); +} +#endif + +const int BLUR_DITHER_BITS = 6; // dither weights stored on 6 bits +const int BLUR_DITHER_ORDER_SHIFT= 3; +const int BLUR_DITHER_ORDER = (1<<BLUR_DITHER_ORDER_SHIFT); +const int BLUR_DITHER_SIZE = BLUR_DITHER_ORDER * BLUR_DITHER_ORDER; +const int BLUR_DITHER_MASK = BLUR_DITHER_ORDER-1; + +static const uint8_t gDitherMatrix[BLUR_DITHER_SIZE] = { + 0, 32, 8, 40, 2, 34, 10, 42, + 48, 16, 56, 24, 50, 18, 58, 26, + 12, 44, 4, 36, 14, 46, 6, 38, + 60, 28, 52, 20, 62, 30, 54, 22, + 3, 35, 11, 43, 1, 33, 9, 41, + 51, 19, 59, 27, 49, 17, 57, 25, + 15, 47, 7, 39, 13, 45, 5, 37, + 63, 31, 55, 23, 61, 29, 53, 21 +}; + + +template <int FACTOR = 0> +struct BlurColor565 +{ + typedef uint16_t type; + int r, g, b; + inline BlurColor565() { } + inline BlurColor565(uint16_t v) { + r = v >> 11; + g = (v >> 5) & 0x3E; + b = v & 0x1F; + } + inline void clear() { r=g=b=0; } + inline uint16_t to(int shift, int last, int dither) const { + int R = r; + int G = g; + int B = b; + if (UNLIKELY(last)) { + if (FACTOR>0) { + int L = (R+G+B)>>1; + R += (((L>>1) - R) * FACTOR) >> 8; + G += (((L ) - G) * FACTOR) >> 8; + B += (((L>>1) - B) * FACTOR) >> 8; + } + R += (dither << shift) >> BLUR_DITHER_BITS; + G += (dither << shift) >> BLUR_DITHER_BITS; + B += (dither << shift) >> BLUR_DITHER_BITS; + } + R >>= shift; + G >>= shift; + B >>= shift; + return (R<<11) | (G<<5) | B; + } + inline BlurColor565& operator += (const BlurColor565& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor565& operator -= (const BlurColor565& rhs) { + r -= rhs.r; + g -= rhs.g; + b -= rhs.b; + return *this; + } +}; + +struct BlurGray565 +{ + typedef uint16_t type; + int l; + inline BlurGray565() { } + inline BlurGray565(uint16_t v) { + int r = v >> 11; + int g = (v >> 5) & 0x3F; + int b = v & 0x1F; + l = (r + g + b + 1)>>1; + } + inline void clear() { l=0; } + inline uint16_t to(int shift, int last, int dither) const { + int L = l; + if (UNLIKELY(last)) { + L += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= shift; + return ((L>>1)<<11) | (L<<5) | (L>>1); + } + inline BlurGray565& operator += (const BlurGray565& rhs) { + l += rhs.l; + return *this; + } + inline BlurGray565& operator -= (const BlurGray565& rhs) { + l -= rhs.l; + return *this; + } +}; + +struct BlurGray8888 +{ + typedef uint32_t type; + int l, a; + inline BlurGray8888() { } + inline BlurGray8888(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + int r = v & 0xFF; + int g = (v >> 8) & 0xFF; + int b = (v >> 16) & 0xFF; + a = v >> 24; + l = r + g + g + b; + } + inline void clear() { l=a=0; } + inline uint32_t to(int shift, int last, int dither) const { + int L = l; + int A = a; + if (UNLIKELY(last)) { + L += (dither << (shift+2)) >> BLUR_DITHER_BITS; + A += (dither << shift) >> BLUR_DITHER_BITS; + } + L >>= (shift+2); + A >>= shift; + return BLUR_HOST_TO_RGBA((A<<24) | (L<<16) | (L<<8) | L); + } + inline BlurGray8888& operator += (const BlurGray8888& rhs) { + l += rhs.l; + a += rhs.a; + return *this; + } + inline BlurGray8888& operator -= (const BlurGray8888& rhs) { + l -= rhs.l; + a -= rhs.a; + return *this; + } +}; + + +template<typename PIXEL> +static status_t blurFilter( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat) +{ + typedef typename PIXEL::type TYPE; + + const int shift = 31 - clz(kernelSizeUser); + const int areaShift = shift*2; + const int kernelSize = 1<<shift; + const int kernelHalfSize = kernelSize/2; + const int mask = kernelSize-1; + const int w = src->width; + const int h = src->height; + const uint8_t* ditherMatrix = gDitherMatrix; + + // we need a temporary buffer to store one line of blurred columns + // as well as kernelSize lines of source pixels organized as a ring buffer. + void* const temporary_buffer = malloc( + (w + kernelSize) * sizeof(PIXEL) + + (src->stride * kernelSize) * sizeof(TYPE)); + if (!temporary_buffer) + return NO_MEMORY; + + PIXEL* const sums = (PIXEL*)temporary_buffer; + TYPE* const scratch = (TYPE*)(sums + w + kernelSize); + + // Apply the blur 'repeat' times, this is used to approximate + // gaussian blurs. 3 times gives good results. + for (int k=0 ; k<repeat ; k++) { + + // Clear the columns sums for this round + memset(sums, 0, (w + kernelSize) * sizeof(PIXEL)); + TYPE* head; + TYPE pixel; + PIXEL current; + + // Since we're going to override the source data we need + // to copy it in a temporary buffer. Only kernelSize lines are + // required. But since we start in the center of the kernel, + // we only copy half of the data, and fill the rest with zeros + // (assuming black/transparent pixels). + memcpy( scratch + src->stride*kernelHalfSize, + src->data, + src->stride*kernelHalfSize*sizeof(TYPE)); + + // sum half of each column, because we assume the first half is + // zeros (black/transparent). + for (int y=0 ; y<kernelHalfSize ; y++) { + head = (TYPE*)src->data + y*src->stride; + for (int x=0 ; x<w ; x++) + sums[x] += PIXEL( *head++ ); + } + + for (int y=0 ; y<h ; y++) { + TYPE* fb = (TYPE*)dst->data + y*dst->stride; + + // compute the dither matrix line + uint8_t const * ditherY = ditherMatrix + + (y & BLUR_DITHER_MASK)*BLUR_DITHER_ORDER; + + // Horizontal blur pass on the columns sums + int count, dither, x=0; + PIXEL const * out= sums; + PIXEL const * in = sums; + current.clear(); + + count = kernelHalfSize; + do { + current += *in; + in++; + } while (--count); + + count = kernelHalfSize; + do { + current += *in; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++; + } while (--count); + + count = w-kernelSize; + do { + current += *in; + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + in++, out++; + } while (--count); + + count = kernelHalfSize; + do { + current -= *out; + dither = *(ditherY + ((x++)&BLUR_DITHER_MASK)); + *fb++ = current.to(areaShift, k==repeat-1, dither); + out++; + } while (--count); + + // vertical blur pass, subtract the oldest line from each columns + // and add a new line. Subtract or add zeros at the top + // and bottom edges. + TYPE* const tail = scratch + (y & mask) * src->stride; + if (y >= kernelHalfSize) { + for (int x=0 ; x<w ; x++) + sums[x] -= PIXEL( tail[x] ); + } + if (y < h-kernelSize) { + memcpy( tail, + (TYPE*)src->data + (y+kernelHalfSize)*src->stride, + src->stride*sizeof(TYPE)); + for (int x=0 ; x<w ; x++) + sums[x] += PIXEL( tail[x] ); + } + } + + // The subsequent passes are always done in-place. + src = dst; + } + + free(temporary_buffer); + + return NO_ERROR; +} + +template status_t blurFilter< BlurColor565<0x80> >( + GGLSurface const* dst, + GGLSurface const* src, + int kernelSizeUser, + int repeat); + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat) +{ + return blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); +} + +} // namespace android + +//err = blur< BlurColor565<0x80> >(dst, src, kernelSizeUser, repeat); +//err = blur<BlurGray565>(dst, src, kernelSizeUser, repeat); +//err = blur<BlurGray8888>(dst, src, kernelSizeUser, repeat); diff --git a/libs/surfaceflinger/BlurFilter.h b/libs/surfaceflinger/BlurFilter.h new file mode 100644 index 0000000..294db43 --- /dev/null +++ b/libs/surfaceflinger/BlurFilter.h @@ -0,0 +1,35 @@ +/* +** +** Copyright 2006, 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. +*/ + +#ifndef ANDROID_BLUR_FILTER_H +#define ANDROID_BLUR_FILTER_H + +#include <stdint.h> +#include <utils/Errors.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +status_t blurFilter( + GGLSurface const* image, + int kernelSizeUser, + int repeat); + +} // namespace android + +#endif // ANDROID_BLUR_FILTER_H diff --git a/libs/surfaceflinger/BootAnimation.cpp b/libs/surfaceflinger/BootAnimation.cpp new file mode 100644 index 0000000..2b30336 --- /dev/null +++ b/libs/surfaceflinger/BootAnimation.cpp @@ -0,0 +1,403 @@ +/* + * 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 "BootAnimation" + +#include <stdint.h> +#include <sys/types.h> +#include <math.h> +#include <fcntl.h> +#include <utils/misc.h> + +#include <utils/threads.h> +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/AssetManager.h> + +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/DisplayInfo.h> +#include <ui/ISurfaceComposer.h> +#include <ui/ISurfaceFlingerClient.h> +#include <ui/EGLNativeWindowSurface.h> + +#include <core/SkBitmap.h> +#include <images/SkImageDecoder.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <EGL/eglext.h> + +#include "BootAnimation.h" + +namespace android { + +// --------------------------------------------------------------------------- + +BootAnimation::BootAnimation(const sp<ISurfaceComposer>& composer) : + Thread(false) { + mSession = SurfaceComposerClient::clientForConnection( + composer->createConnection()->asBinder()); +} + +BootAnimation::~BootAnimation() { +} + +void BootAnimation::onFirstRef() { + run("BootAnimation", PRIORITY_DISPLAY); +} + +const sp<SurfaceComposerClient>& BootAnimation::session() const { + return mSession; +} + +status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, + const char* name) { + Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); + if (!asset) + return NO_INIT; + SkBitmap bitmap; + SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), + &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); + asset->close(); + delete asset; + + // ensure we can call getPixels(). No need to call unlock, since the + // bitmap will go out of scope when we return from this method. + bitmap.lockPixels(); + + const int w = bitmap.width(); + const int h = bitmap.height(); + const void* p = bitmap.getPixels(); + + GLint crop[4] = { 0, h, w, -h }; + texture->w = w; + texture->h = h; + + glGenTextures(1, &texture->name); + glBindTexture(GL_TEXTURE_2D, texture->name); + + switch (bitmap.getConfig()) { + case SkBitmap::kA8_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, + GL_UNSIGNED_BYTE, p); + break; + case SkBitmap::kARGB_4444_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4, p); + break; + case SkBitmap::kARGB_8888_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, p); + break; + case SkBitmap::kRGB_565_Config: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, p); + break; + default: + break; + } + + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + return NO_ERROR; +} + +status_t BootAnimation::readyToRun() { + mAssets.addDefaultAssets(); + + DisplayInfo dinfo; + status_t status = session()->getDisplayInfo(0, &dinfo); + if (status) + return -1; + + // create the native surface + sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h, + PIXEL_FORMAT_RGB_565); + session()->openTransaction(); + s->setLayer(0x40000000); + session()->closeTransaction(); + + // initialize opengl and egl + const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE }; + EGLint w, h, dummy; + EGLint numConfigs; + EGLConfig config; + EGLSurface surface; + EGLContext context; + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglChooseConfig(display, attribs, &config, 1, &numConfigs); + + mNativeWindowSurface = new EGLNativeWindowSurface(s); + surface = eglCreateWindowSurface(display, config, + mNativeWindowSurface.get(), NULL); + + context = eglCreateContext(display, config, NULL, NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &w); + eglQuerySurface(display, surface, EGL_HEIGHT, &h); + eglMakeCurrent(display, surface, surface, context); + mDisplay = display; + mContext = context; + mSurface = surface; + mWidth = w; + mHeight = h; + mFlingerSurface = s; + + // initialize GL + glShadeModel(GL_FLAT); + glEnable(GL_DITHER); + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + return NO_ERROR; +} + +void BootAnimation::requestExit() { + mBarrier.open(); + Thread::requestExit(); +} + +bool BootAnimation::threadLoop() { + bool r = android(); + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(mDisplay, mContext); + eglDestroySurface(mDisplay, mSurface); + mNativeWindowSurface.clear(); + return r; +} + +bool BootAnimation::android() { + initTexture(&mAndroid[0], mAssets, "images/android_320x480.png"); + initTexture(&mAndroid[1], mAssets, "images/boot_robot.png"); + initTexture(&mAndroid[2], mAssets, "images/boot_robot_glow.png"); + + // erase screen + glDisable(GL_SCISSOR_TEST); + glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); + + // clear screen + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mDisplay, mSurface); + + // wait ~1s + usleep(800000); + + // fade in + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + const int steps = 8; + for (int i = 1; i < steps; i++) { + float fade = i / float(steps); + glColor4f(1, 1, 1, fade * fade); + glClear(GL_COLOR_BUFFER_BIT); + glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h); + eglSwapBuffers(mDisplay, mSurface); + } + + // draw last frame + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + glDrawTexiOES(0, 0, 0, mAndroid[0].w, mAndroid[0].h); + eglSwapBuffers(mDisplay, mSurface); + + // update rect for the robot + const int x = mWidth - mAndroid[1].w - 33; + const int y = (mHeight - mAndroid[1].h) / 2 - 1; + const Rect updateRect(x, y, x + mAndroid[1].w, y + mAndroid[1].h); + + // draw and update only what we need + mNativeWindowSurface->setSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); + + glEnable(GL_SCISSOR_TEST); + glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), + updateRect.height()); + + const nsecs_t startTime = systemTime(); + do { + // glow speed and shape + nsecs_t time = systemTime() - startTime; + float t = ((4.0f / (360.0f * us2ns(16667))) * time); + t = t - floorf(t); + const float fade = 0.5f + 0.5f * sinf(t * 2 * M_PI); + + // fade the glow in and out + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, mAndroid[2].name); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f(fade, fade, fade, fade); + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, + updateRect.width(), updateRect.height()); + + // draw the robot + glEnable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDrawTexiOES(updateRect.left, mHeight - updateRect.bottom, 0, + updateRect.width(), updateRect.height()); + + // make sure sleep a lot to not take too much CPU away from + // the boot process. With this "glow" animation there is no + // visible difference. + usleep(16667 * 4); + + eglSwapBuffers(mDisplay, mSurface); + } while (!exitPending()); + + glDeleteTextures(1, &mAndroid[0].name); + glDeleteTextures(1, &mAndroid[1].name); + glDeleteTextures(1, &mAndroid[2].name); + return false; +} + +bool BootAnimation::cylon() { + // initialize the textures... + initTexture(&mLeftTrail, mAssets, "images/cylon_left.png"); + initTexture(&mRightTrail, mAssets, "images/cylon_right.png"); + initTexture(&mBrightSpot, mAssets, "images/cylon_dot.png"); + + int w = mWidth; + int h = mHeight; + + const Point c(w / 2, h / 2); + const GLint amplitude = 60; + const int scx = c.x - amplitude - mBrightSpot.w / 2; + const int scy = c.y - mBrightSpot.h / 2; + const int scw = amplitude * 2 + mBrightSpot.w; + const int sch = mBrightSpot.h; + const Rect updateRect(scx, h - scy - sch, scx + scw, h - scy); + + // erase screen + glDisable(GL_SCISSOR_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mDisplay, mSurface); + + glClear(GL_COLOR_BUFFER_BIT); + + mNativeWindowSurface->setSwapRectangle(updateRect.left, + updateRect.top, updateRect.width(), updateRect.height()); + + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + // clear the screen to white + Point p; + float t = 0; + float alpha = 1.0f; + const nsecs_t startTime = systemTime(); + nsecs_t fadeTime = 0; + + do { + // Set scissor in interesting area + glScissor(scx, scy, scw, sch); + + // erase screen + glClear(GL_COLOR_BUFFER_BIT); + + // compute wave + const float a = (t * 2 * M_PI) - M_PI / 2; + const float sn = sinf(a); + const float cs = cosf(a); + GLint x = GLint(amplitude * sn); + float derivative = cs; + + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + if (derivative > 0) { + // vanishing trail... + p.x = (-amplitude + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + float fade = 2.0f * (0.5f - t); + //fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); + glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); + + // trail... + p.x = (x + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + fade = t < 0.25f ? t * 4.0f : 1.0f; + fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mRightTrail.name); + glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); + } else { + // vanishing trail.. + p.x = (amplitude + c.x) - (mRightTrail.w + mBrightSpot.w / 2) + 16; + p.y = c.y - mRightTrail.h / 2; + float fade = 2.0f * (0.5f - (t - 0.5f)); + //fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mRightTrail.name); + glDrawTexiOES(p.x, p.y, 0, mRightTrail.w, mRightTrail.h); + + // trail... + p.x = (x + c.x) - mBrightSpot.w / 2; + p.y = c.y - mLeftTrail.h / 2; + fade = t < 0.5f + 0.25f ? (t - 0.5f) * 4.0f : 1.0f; + fade *= fade; + glColor4f(fade, fade, fade, fade); + glBindTexture(GL_TEXTURE_2D, mLeftTrail.name); + glDrawTexiOES(p.x, p.y, 0, mLeftTrail.w, mLeftTrail.h); + } + + const Point p(x + c.x - mBrightSpot.w / 2, c.y - mBrightSpot.h / 2); + glBindTexture(GL_TEXTURE_2D, mBrightSpot.name); + glColor4f(1, 0.5, 0.5, 1); + glDrawTexiOES(p.x, p.y, 0, mBrightSpot.w, mBrightSpot.h); + + // update animation + nsecs_t time = systemTime() - startTime; + t = ((4.0f / (360.0f * us2ns(16667))) * time); + t = t - floorf(t); + + eglSwapBuffers(mDisplay, mSurface); + + if (exitPending()) { + if (fadeTime == 0) { + fadeTime = time; + } + time -= fadeTime; + alpha = 1.0f - ((float(time) * 6.0f) / float(s2ns(1))); + + session()->openTransaction(); + mFlingerSurface->setAlpha(alpha * alpha); + session()->closeTransaction(); + } + } while (alpha > 0); + + // cleanup + glFinish(); + glDeleteTextures(1, &mLeftTrail.name); + glDeleteTextures(1, &mRightTrail.name); + glDeleteTextures(1, &mBrightSpot.name); + return false; +} + +// --------------------------------------------------------------------------- + +} +; // namespace android diff --git a/libs/surfaceflinger/BootAnimation.h b/libs/surfaceflinger/BootAnimation.h new file mode 100644 index 0000000..b20cea0 --- /dev/null +++ b/libs/surfaceflinger/BootAnimation.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ANDROID_BOOTANIMATION_H +#define ANDROID_BOOTANIMATION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/AssetManager.h> + +#include <ui/ISurfaceComposer.h> +#include <ui/SurfaceComposerClient.h> + +#include <EGL/egl.h> +#include <GLES/gl.h> + +#include "Barrier.h" + +class SkBitmap; + +namespace android { + +class AssetManager; +class EGLNativeWindowSurface; + +// --------------------------------------------------------------------------- + +class BootAnimation : public Thread +{ +public: + BootAnimation(const sp<ISurfaceComposer>& composer); + virtual ~BootAnimation(); + + const sp<SurfaceComposerClient>& session() const; + virtual void requestExit(); + +private: + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + struct Texture { + GLint w; + GLint h; + GLuint name; + }; + + status_t initTexture(Texture* texture, AssetManager& asset, const char* name); + bool android(); + bool cylon(); + + sp<SurfaceComposerClient> mSession; + AssetManager mAssets; + Texture mLeftTrail; + Texture mRightTrail; + Texture mBrightSpot; + Texture mAndroid[3]; + int mWidth; + int mHeight; + EGLDisplay mDisplay; + EGLDisplay mContext; + EGLDisplay mSurface; + sp<Surface> mFlingerSurface; + sp<EGLNativeWindowSurface> mNativeWindowSurface; + Barrier mBarrier; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_BOOTANIMATION_H diff --git a/libs/surfaceflinger/CPUGauge.cpp b/libs/surfaceflinger/CPUGauge.cpp new file mode 100644 index 0000000..74a9270 --- /dev/null +++ b/libs/surfaceflinger/CPUGauge.cpp @@ -0,0 +1,171 @@ +/* + * 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 "CPUGauge" + +#include <stdint.h> +#include <limits.h> +#include <sys/types.h> +#include <math.h> + +#include <utils/threads.h> +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/DisplayInfo.h> +#include <ui/ISurfaceComposer.h> +#include <ui/ISurfaceFlingerClient.h> + +#include <pixelflinger/pixelflinger.h> + +#include "CPUGauge.h" + +namespace android { + +CPUGauge::CPUGauge( const sp<ISurfaceComposer>& composer, + nsecs_t interval, + int clock, + int refclock) + : Thread(false), + mInterval(interval), mClock(clock), mRefClock(refclock), + mReferenceTime(0), + mReferenceWorkingTime(0), mCpuUsage(0), + mRefIdleTime(0), mIdleTime(0) +{ + mFd = fopen("/proc/stat", "r"); + setvbuf(mFd, NULL, _IONBF, 0); + + mSession = SurfaceComposerClient::clientForConnection( + composer->createConnection()->asBinder()); +} + +CPUGauge::~CPUGauge() +{ + fclose(mFd); +} + +const sp<SurfaceComposerClient>& CPUGauge::session() const +{ + return mSession; +} + +void CPUGauge::onFirstRef() +{ + run("CPU Gauge"); +} + +status_t CPUGauge::readyToRun() +{ + LOGI("Starting CPU gauge..."); + return NO_ERROR; +} + +bool CPUGauge::threadLoop() +{ + DisplayInfo dinfo; + session()->getDisplayInfo(0, &dinfo); + sp<Surface> s(session()->createSurface(getpid(), 0, dinfo.w, 4, PIXEL_FORMAT_OPAQUE)); + session()->openTransaction(); + s->setLayer(INT_MAX); + session()->closeTransaction(); + + static const GGLfixed colors[4][4] = { + { 0x00000, 0x10000, 0x00000, 0x10000 }, + { 0x10000, 0x10000, 0x00000, 0x10000 }, + { 0x10000, 0x00000, 0x00000, 0x10000 }, + { 0x00000, 0x00000, 0x00000, 0x10000 }, + }; + + GGLContext* gl; + gglInit(&gl); + gl->activeTexture(gl, 0); + gl->disable(gl, GGL_TEXTURE_2D); + gl->disable(gl, GGL_BLEND); + + const int w = dinfo.w; + + while(!exitPending()) + { + mLock.lock(); + const float cpuUsage = this->cpuUsage(); + const float totalCpuUsage = 1.0f - idle(); + mLock.unlock(); + + Surface::SurfaceInfo info; + s->lock(&info); + GGLSurface fb; + fb.version = sizeof(GGLSurface); + fb.width = info.w; + fb.height = info.h; + fb.stride = info.w; + fb.format = info.format; + fb.data = (GGLubyte*)info.bits; + + gl->colorBuffer(gl, &fb); + gl->color4xv(gl, colors[3]); + gl->recti(gl, 0, 0, w, 4); + gl->color4xv(gl, colors[2]); // red + gl->recti(gl, 0, 0, int(totalCpuUsage*w), 2); + gl->color4xv(gl, colors[0]); // green + gl->recti(gl, 0, 2, int(cpuUsage*w), 4); + + s->unlockAndPost(); + + usleep(ns2us(mInterval)); + } + + gglUninit(gl); + return false; +} + +void CPUGauge::sample() +{ + if (mLock.tryLock() == NO_ERROR) { + const nsecs_t now = systemTime(mRefClock); + const nsecs_t referenceTime = now-mReferenceTime; + if (referenceTime >= mInterval) { + const float reftime = 1.0f / referenceTime; + const nsecs_t nowWorkingTime = systemTime(mClock); + + char buf[256]; + fgets(buf, 256, mFd); + rewind(mFd); + char *str = buf+5; + char const * const usermode = strsep(&str, " "); (void)usermode; + char const * const usernice = strsep(&str, " "); (void)usernice; + char const * const systemmode = strsep(&str, " ");(void)systemmode; + char const * const idle = strsep(&str, " "); + const nsecs_t nowIdleTime = atoi(idle) * 10000000LL; + mIdleTime = float(nowIdleTime - mRefIdleTime) * reftime; + mRefIdleTime = nowIdleTime; + + const nsecs_t workingTime = nowWorkingTime - mReferenceWorkingTime; + const float newCpuUsage = float(workingTime) * reftime; + if (mCpuUsage != newCpuUsage) { + mCpuUsage = newCpuUsage; + mReferenceWorkingTime = nowWorkingTime; + mReferenceTime = now; + } + } + mLock.unlock(); + } +} + + +}; // namespace android diff --git a/libs/surfaceflinger/CPUGauge.h b/libs/surfaceflinger/CPUGauge.h new file mode 100644 index 0000000..5bb53c0 --- /dev/null +++ b/libs/surfaceflinger/CPUGauge.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef ANDROID_CPUGAUGE_H +#define ANDROID_CPUGAUGE_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Timers.h> + +#include <ui/SurfaceComposerClient.h> + +namespace android { + +class CPUGauge : public Thread +{ +public: + CPUGauge( const sp<ISurfaceComposer>& composer, + nsecs_t interval=s2ns(1), + int clock=SYSTEM_TIME_THREAD, + int refclock=SYSTEM_TIME_MONOTONIC); + + ~CPUGauge(); + + const sp<SurfaceComposerClient>& session() const; + + void sample(); + + inline float cpuUsage() const { return mCpuUsage; } + inline float idle() const { return mIdleTime; } + +private: + virtual void onFirstRef(); + virtual status_t readyToRun(); + virtual bool threadLoop(); + + Mutex mLock; + + sp<SurfaceComposerClient> mSession; + + const nsecs_t mInterval; + const int mClock; + const int mRefClock; + + nsecs_t mReferenceTime; + nsecs_t mReferenceWorkingTime; + float mCpuUsage; + nsecs_t mRefIdleTime; + float mIdleTime; + FILE* mFd; +}; + + +}; // namespace android + +#endif // ANDROID_CPUGAUGE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp new file mode 100644 index 0000000..f14d7e9 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -0,0 +1,353 @@ +/* + * 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 <stdio.h> +#include <string.h> +#include <math.h> + +#include <cutils/properties.h> + +#include <utils/Log.h> + +#include <ui/EGLDisplaySurface.h> + +#include <GLES/gl.h> +#include <EGL/eglext.h> + + +#include "DisplayHardware/DisplayHardware.h" + +#include <hardware/copybit.h> +#include <hardware/overlay.h> + +using namespace android; + +static __attribute__((noinline)) +const char *egl_strerror(EGLint err) +{ + switch (err){ + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "UNKNOWN"; + } +} + +static __attribute__((noinline)) +void checkGLErrors() +{ + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + LOGE("GL error 0x%04x", int(error)); +} + +static __attribute__((noinline)) +void checkEGLErrors(const char* token) +{ + EGLint error = eglGetError(); + // GLESonGL seems to be returning 0 when there is no errors? + if (error && error != EGL_SUCCESS) + LOGE("%s error 0x%04x (%s)", + token, int(error), egl_strerror(error)); +} + + +/* + * Initialize the display to the specified values. + * + */ + +DisplayHardware::DisplayHardware( + const sp<SurfaceFlinger>& flinger, + uint32_t dpy) + : DisplayHardwareBase(flinger, dpy) +{ + init(dpy); +} + +DisplayHardware::~DisplayHardware() +{ + fini(); +} + +float DisplayHardware::getDpiX() const { return mDpiX; } +float DisplayHardware::getDpiY() const { return mDpiY; } +float DisplayHardware::getDensity() const { return mDensity; } +float DisplayHardware::getRefreshRate() const { return mRefreshRate; } +int DisplayHardware::getWidth() const { return mWidth; } +int DisplayHardware::getHeight() const { return mHeight; } +PixelFormat DisplayHardware::getFormat() const { return mFormat; } + +void DisplayHardware::init(uint32_t dpy) +{ + // initialize EGL + const EGLint attribs[] = { + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_DEPTH_SIZE, 0, + EGL_NONE + }; + EGLint w, h, dummy; + EGLint numConfigs, n; + EGLConfig config; + EGLSurface surface; + EGLContext context; + mFlags = 0; + + // TODO: all the extensions below should be queried through + // eglGetProcAddress(). + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(display, NULL, NULL); + eglGetConfigs(display, NULL, 0, &numConfigs); + eglChooseConfig(display, attribs, &config, 1, &n); + + /* + * Gather EGL extensions + */ + + const char* const egl_extensions = eglQueryString( + display, EGL_EXTENSIONS); + + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + LOGI("version : %s", eglQueryString(display, EGL_VERSION)); + LOGI("extensions: %s", egl_extensions); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + + // TODO: get this from the devfb driver (probably should be HAL module) + mFlags |= SWAP_RECTANGLE_EXTENSION; + + // TODO: get the real "update_on_demand" behavior (probably should be HAL module) + mFlags |= UPDATE_ON_DEMAND; + + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { + if (dummy == EGL_SLOW_CONFIG) + mFlags |= SLOW_CONFIG; + } + + /* + * Create our main surface + */ + + mDisplaySurface = new EGLDisplaySurface(); + + surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL); + //checkEGLErrors("eglCreateDisplaySurfaceANDROID"); + + if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { + if (dummy == EGL_BUFFER_PRESERVED) { + mFlags |= BUFFER_PRESERVED; + } + } + + GLint value = EGL_UNKNOWN; + eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value); + if (value == EGL_UNKNOWN) { + mDpiX = 160.0f; + } else { + mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING; + } + value = EGL_UNKNOWN; + eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value); + if (value == EGL_UNKNOWN) { + mDpiY = 160.0f; + } else { + mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING; + } + mRefreshRate = 60.f; // TODO: get the real refresh rate + + + char property[PROPERTY_VALUE_MAX]; + if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { + LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); + strcpy(property, "160"); + } + mDensity = atoi(property) * (1.0f/160.0f); + + + /* + * Create our OpenGL ES context + */ + + context = eglCreateContext(display, config, NULL, NULL); + //checkEGLErrors("eglCreateContext"); + + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); + + + /* + * Gather OpenGL ES extensions + */ + + eglMakeCurrent(display, surface, surface, context); + const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); + LOGI("OpenGL informations:"); + LOGI("vendor : %s", glGetString(GL_VENDOR)); + LOGI("renderer : %s", glGetString(GL_RENDERER)); + LOGI("version : %s", glGetString(GL_VERSION)); + LOGI("extensions: %s", gl_extensions); + + if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { + mFlags |= NPOT_EXTENSION; + } + if (strstr(gl_extensions, "GL_OES_draw_texture")) { + mFlags |= DRAW_TEXTURE_EXTENSION; + } + if (strstr(gl_extensions, "GL_ANDROID_direct_texture")) { + mFlags |= DIRECT_TEXTURE; + } + + // Unbind the context from this thread + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = GGL_PIXEL_FORMAT_RGB_565; + + hw_module_t const* module; + + mBlitEngine = NULL; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } + + mOverlayEngine = NULL; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } +} + +/* + * Clean up. Throw out our local state. + * + * (It's entirely possible we'll never get here, since this is meant + * for real hardware, which doesn't restart.) + */ + +void DisplayHardware::fini() +{ + eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mDisplay); + copybit_close(mBlitEngine); + overlay_control_close(mOverlayEngine); +} + +void DisplayHardware::releaseScreen() const +{ + DisplayHardwareBase::releaseScreen(); +} + +void DisplayHardware::acquireScreen() const +{ + DisplayHardwareBase::acquireScreen(); +} + +void DisplayHardware::getDisplaySurface(copybit_image_t* img) const +{ + img->w = mDisplaySurface->stride; + img->h = mDisplaySurface->height; + img->format = mDisplaySurface->format; + img->offset = mDisplaySurface->offset; + img->base = (void*)mDisplaySurface->base; + img->fd = mDisplaySurface->fd; +} + +void DisplayHardware::getDisplaySurface(GGLSurface* fb) const +{ + fb->version= sizeof(GGLSurface); + fb->width = mDisplaySurface->width; + fb->height = mDisplaySurface->height; + fb->stride = mDisplaySurface->stride; + fb->format = mDisplaySurface->format; + fb->data = (GGLubyte*)mDisplaySurface->base + mDisplaySurface->offset; +} + +uint32_t DisplayHardware::getPageFlipCount() const { + return mDisplaySurface->getPageFlipCount(); +} + +/* + * "Flip" the front and back buffers. + */ + +void DisplayHardware::flip(const Region& dirty) const +{ + checkGLErrors(); + + EGLDisplay dpy = mDisplay; + EGLSurface surface = mSurface; + + Region newDirty(dirty); + newDirty.andSelf(Rect(mWidth, mHeight)); + + if (mFlags & BUFFER_PRESERVED) { + const Region copyback(mDirty.subtract(newDirty)); + mDirty = newDirty; + mDisplaySurface->copyFrontToBack(copyback); + } + + if (mFlags & SWAP_RECTANGLE_EXTENSION) { + const Rect& b(newDirty.bounds()); + mDisplaySurface->setSwapRectangle( + b.left, b.top, b.width(), b.height()); + } + + eglSwapBuffers(dpy, surface); + checkEGLErrors("eglSwapBuffers"); + + // for debugging + //glClearColor(1,0,0,0); + //glClear(GL_COLOR_BUFFER_BIT); +} + +uint32_t DisplayHardware::getFlags() const +{ + return mFlags; +} + +void DisplayHardware::makeCurrent() const +{ + eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); +} + +void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const { + mDisplaySurface->copyFrontToImage(front); +} + +void DisplayHardware::copyBackToImage(const copybit_image_t& front) const { + mDisplaySurface->copyBackToImage(front); +} diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h new file mode 100644 index 0000000..550a4d1 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef ANDROID_DISPLAY_HARDWARE_H +#define ANDROID_DISPLAY_HARDWARE_H + +#include <stdlib.h> + +#include <ui/PixelFormat.h> +#include <ui/Region.h> + +#include <EGL/egl.h> + +#include "DisplayHardware/DisplayHardwareBase.h" + +struct overlay_control_device_t; +struct copybit_device_t; +struct copybit_image_t; +struct copybit_t; + +namespace android { + +class EGLDisplaySurface; + +class DisplayHardware : public DisplayHardwareBase +{ +public: + enum { + DIRECT_TEXTURE = 0x00000002, + SWAP_RECTANGLE_EXTENSION= 0x00000004, + COPY_BITS_EXTENSION = 0x00000008, + NPOT_EXTENSION = 0x00000100, + DRAW_TEXTURE_EXTENSION = 0x00000200, + BUFFER_PRESERVED = 0x00010000, + UPDATE_ON_DEMAND = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + }; + + DisplayHardware( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); + + ~DisplayHardware(); + + void releaseScreen() const; + void acquireScreen() const; + + // Flip the front and back buffers if the back buffer is "dirty". Might + // be instantaneous, might involve copying the frame buffer around. + void flip(const Region& dirty) const; + + float getDpiX() const; + float getDpiY() const; + float getRefreshRate() const; + float getDensity() const; + int getWidth() const; + int getHeight() const; + PixelFormat getFormat() const; + uint32_t getFlags() const; + void makeCurrent() const; + + uint32_t getPageFlipCount() const; + void getDisplaySurface(copybit_image_t* img) const; + void getDisplaySurface(GGLSurface* fb) const; + EGLDisplay getEGLDisplay() const { return mDisplay; } + copybit_device_t* getBlitEngine() const { return mBlitEngine; } + overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } + + void copyFrontToImage(const copybit_image_t& front) const; + void copyBackToImage(const copybit_image_t& front) const; + + Rect bounds() const { + return Rect(mWidth, mHeight); + } + +private: + void init(uint32_t displayIndex) __attribute__((noinline)); + void fini() __attribute__((noinline)); + + EGLDisplay mDisplay; + EGLSurface mSurface; + EGLContext mContext; + EGLConfig mConfig; + float mDpiX; + float mDpiY; + float mRefreshRate; + float mDensity; + int mWidth; + int mHeight; + PixelFormat mFormat; + uint32_t mFlags; + mutable Region mDirty; + sp<EGLDisplaySurface> mDisplaySurface; + copybit_device_t* mBlitEngine; + overlay_control_device_t* mOverlayEngine; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp new file mode 100644 index 0000000..f75e5c2 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -0,0 +1,403 @@ +/* + * 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 <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/resource.h> + +#include <linux/unistd.h> + +#include <utils/Log.h> + +#include "DisplayHardware/DisplayHardwareBase.h" +#include "SurfaceFlinger.h" + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- +namespace android { + +static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep"; +static char const * kWakeFileName = "/sys/power/wait_for_fb_wake"; +static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep"; +static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake"; + +// This dir exists if the framebuffer console is present, either built into +// the kernel or loaded as a module. +static char const * const kFbconSysDir = "/sys/class/graphics/fbcon"; + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThreadBase::DisplayEventThreadBase( + const sp<SurfaceFlinger>& flinger) + : Thread(false), mFlinger(flinger) { +} + +DisplayHardwareBase::DisplayEventThreadBase::~DisplayEventThreadBase() { +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayEventThread::DisplayEventThread( + const sp<SurfaceFlinger>& flinger) + : DisplayEventThreadBase(flinger) +{ +} + +DisplayHardwareBase::DisplayEventThread::~DisplayEventThread() +{ +} + +bool DisplayHardwareBase::DisplayEventThread::threadLoop() +{ + int err = 0; + char buf; + int fd; + + fd = open(kSleepFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno)); + if (err >= 0) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) { + mBarrier.close(); + flinger->screenReleased(0); + mBarrier.wait(); + } + } + fd = open(kWakeFileName, O_RDONLY, 0); + do { + err = read(fd, &buf, 1); + } while (err < 0 && errno == EINTR); + close(fd); + LOGW_IF(err<0, "ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno)); + if (err >= 0) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + return true; +} + +status_t DisplayHardwareBase::DisplayEventThread::releaseScreen() const +{ + mBarrier.open(); + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::readyToRun() +{ + if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) { + if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) { + LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName); + return NO_INIT; + } + kSleepFileName = kOldSleepFileName; + kWakeFileName = kOldWakeFileName; + } + return NO_ERROR; +} + +status_t DisplayHardwareBase::DisplayEventThread::initCheck() const +{ + return (((access(kSleepFileName, R_OK) == 0 && + access(kWakeFileName, R_OK) == 0) || + (access(kOldSleepFileName, R_OK) == 0 && + access(kOldWakeFileName, R_OK) == 0)) && + access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +pid_t DisplayHardwareBase::ConsoleManagerThread::sSignalCatcherPid = 0; + +DisplayHardwareBase::ConsoleManagerThread::ConsoleManagerThread( + const sp<SurfaceFlinger>& flinger) + : DisplayEventThreadBase(flinger), consoleFd(-1) +{ + sSignalCatcherPid = 0; + + // create a new console + char const * const ttydev = "/dev/tty0"; + int fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open %s", ttydev); + this->consoleFd = -errno; + return; + } + + // to make sure that we are in text mode + int res = ioctl(fd, KDSETMODE, (void*) KD_TEXT); + if (res<0) { + LOGE("ioctl(%d, KDSETMODE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + } + + // get the current console + struct vt_stat vs; + res = ioctl(fd, VT_GETSTATE, &vs); + if (res<0) { + LOGE("ioctl(%d, VT_GETSTATE, ...) failed, res %d (%s)", + fd, res, strerror(errno)); + this->consoleFd = -errno; + return; + } + + // switch to console 7 (which is what X normaly uses) + int vtnum = 7; + do { + res = ioctl(fd, VT_ACTIVATE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_ACTIVATE, ...) failed, %d (%s) for %d", + fd, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)vtnum); + } while(res < 0 && errno == EINTR); + if (res<0) { + LOGE("ioctl(%d, VT_WAITACTIVE, ...) failed, %d %d %s for %d", + fd, res, errno, strerror(errno), vtnum); + this->consoleFd = -errno; + return; + } + + // open the new console + close(fd); + fd = open(ttydev, O_RDWR | O_SYNC); + if (fd<0) { + LOGE("Can't open new console %s", ttydev); + this->consoleFd = -errno; + return; + } + + /* disable console line buffer, echo, ... */ + struct termios ttyarg; + ioctl(fd, TCGETS , &ttyarg); + ttyarg.c_iflag = 0; + ttyarg.c_lflag = 0; + ioctl(fd, TCSETS , &ttyarg); + + // set up signals so we're notified when the console changes + // we can't use SIGUSR1 because it's used by the java-vm + vm.mode = VT_PROCESS; + vm.waitv = 0; + vm.relsig = SIGUSR2; + vm.acqsig = SIGUNUSED; + vm.frsig = 0; + + struct sigaction act; + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.relsig, &act, NULL); + + sigemptyset(&act.sa_mask); + act.sa_handler = sigHandler; + act.sa_flags = 0; + sigaction(vm.acqsig, &act, NULL); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + // switch to graphic mode + res = ioctl(fd, KDSETMODE, (void*)KD_GRAPHICS); + LOGW_IF(res<0, + "ioctl(%d, KDSETMODE, KD_GRAPHICS) failed, res %d", fd, res); + + this->prev_vt_num = vs.v_active; + this->vt_num = vtnum; + this->consoleFd = fd; +} + +DisplayHardwareBase::ConsoleManagerThread::~ConsoleManagerThread() +{ + if (this->consoleFd >= 0) { + int fd = this->consoleFd; + int prev_vt_num = this->prev_vt_num; + int res; + ioctl(fd, KDSETMODE, (void*)KD_TEXT); + do { + res = ioctl(fd, VT_ACTIVATE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + do { + res = ioctl(fd, VT_WAITACTIVE, (void*)prev_vt_num); + } while(res < 0 && errno == EINTR); + close(fd); + char const * const ttydev = "/dev/tty0"; + fd = open(ttydev, O_RDWR | O_SYNC); + ioctl(fd, VT_DISALLOCATE, 0); + close(fd); + } +} + +status_t DisplayHardwareBase::ConsoleManagerThread::readyToRun() +{ + if (this->consoleFd >= 0) { + sSignalCatcherPid = gettid(); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + sigprocmask(SIG_BLOCK, &mask, NULL); + + int res = ioctl(this->consoleFd, VT_SETMODE, &vm); + if (res<0) { + LOGE("ioctl(%d, VT_SETMODE, ...) failed, %d (%s)", + this->consoleFd, errno, strerror(errno)); + } + return NO_ERROR; + } + return this->consoleFd; +} + +void DisplayHardwareBase::ConsoleManagerThread::requestExit() +{ + Thread::requestExit(); + if (sSignalCatcherPid != 0) { + // wake the thread up + kill(sSignalCatcherPid, SIGINT); + // wait for it... + } +} + +void DisplayHardwareBase::ConsoleManagerThread::sigHandler(int sig) +{ + // resend the signal to our signal catcher thread + LOGW("received signal %d in thread %d, resending to %d", + sig, gettid(), sSignalCatcherPid); + + // we absolutely need the delays below because without them + // our main thread never gets a chance to handle the signal. + usleep(10000); + kill(sSignalCatcherPid, sig); + usleep(10000); +} + +status_t DisplayHardwareBase::ConsoleManagerThread::releaseScreen() const +{ + int fd = this->consoleFd; + int err = ioctl(fd, VT_RELDISP, (void*)1); + LOGE_IF(err<0, "ioctl(%d, VT_RELDISP, 1) failed %d (%s)", + fd, errno, strerror(errno)); + return (err<0) ? (-errno) : status_t(NO_ERROR); +} + +bool DisplayHardwareBase::ConsoleManagerThread::threadLoop() +{ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, vm.relsig); + sigaddset(&mask, vm.acqsig); + + int sig = 0; + sigwait(&mask, &sig); + + if (sig == vm.relsig) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + //LOGD("About to give-up screen, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenReleased(0); + } else if (sig == vm.acqsig) { + sp<SurfaceFlinger> flinger = mFlinger.promote(); + //LOGD("Screen about to return, flinger = %p", flinger.get()); + if (flinger != 0) + flinger->screenAcquired(0); + } + + return true; +} + +status_t DisplayHardwareBase::ConsoleManagerThread::initCheck() const +{ + return consoleFd >= 0 ? NO_ERROR : NO_INIT; +} + +// ---------------------------------------------------------------------------- + +DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex) + : mCanDraw(true) +{ + mDisplayEventThread = new DisplayEventThread(flinger); + if (mDisplayEventThread->initCheck() != NO_ERROR) { + // fall-back on the console + mDisplayEventThread = new ConsoleManagerThread(flinger); + } +} + +DisplayHardwareBase::~DisplayHardwareBase() +{ + // request exit + mDisplayEventThread->requestExitAndWait(); +} + + +bool DisplayHardwareBase::canDraw() const +{ + return mCanDraw; +} + +void DisplayHardwareBase::releaseScreen() const +{ + status_t err = mDisplayEventThread->releaseScreen(); + if (err >= 0) { + //LOGD("screen given-up"); + mCanDraw = false; + } +} + +void DisplayHardwareBase::acquireScreen() const +{ + status_t err = mDisplayEventThread->acquireScreen(); + if (err >= 0) { + //LOGD("screen returned"); + mCanDraw = true; + } +} + +}; // namespace android diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h new file mode 100644 index 0000000..8369bb8 --- /dev/null +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ANDROID_DISPLAY_HARDWARE_BASE_H +#define ANDROID_DISPLAY_HARDWARE_BASE_H + +#include <stdint.h> +#include <utils/RefBase.h> +#include <utils/threads.h> +#include <linux/kd.h> +#include <linux/vt.h> +#include "Barrier.h" + +namespace android { + +class SurfaceFlinger; + +class DisplayHardwareBase +{ +public: + DisplayHardwareBase( + const sp<SurfaceFlinger>& flinger, + uint32_t displayIndex); + + ~DisplayHardwareBase(); + + // console managment + void releaseScreen() const; + void acquireScreen() const; + bool canDraw() const; + +private: + class DisplayEventThreadBase : public Thread { + protected: + wp<SurfaceFlinger> mFlinger; + public: + DisplayEventThreadBase(const sp<SurfaceFlinger>& flinger); + virtual ~DisplayEventThreadBase(); + virtual void onFirstRef() { + run("DisplayEventThread", PRIORITY_URGENT_DISPLAY); + } + virtual status_t acquireScreen() const { return NO_ERROR; }; + virtual status_t releaseScreen() const { return NO_ERROR; }; + virtual status_t initCheck() const = 0; + }; + + class DisplayEventThread : public DisplayEventThreadBase + { + mutable Barrier mBarrier; + public: + DisplayEventThread(const sp<SurfaceFlinger>& flinger); + virtual ~DisplayEventThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + class ConsoleManagerThread : public DisplayEventThreadBase + { + int consoleFd; + int vt_num; + int prev_vt_num; + vt_mode vm; + static void sigHandler(int sig); + static pid_t sSignalCatcherPid; + public: + ConsoleManagerThread(const sp<SurfaceFlinger>& flinger); + virtual ~ConsoleManagerThread(); + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void requestExit(); + virtual status_t releaseScreen() const; + virtual status_t initCheck() const; + }; + + sp<DisplayEventThreadBase> mDisplayEventThread; + mutable int mCanDraw; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_BASE_H diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp new file mode 100644 index 0000000..eb75f99 --- /dev/null +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2008 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 <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <utils/IBinder.h> +#include <utils/MemoryDealer.h> +#include <utils/MemoryBase.h> +#include <utils/MemoryHeapPmem.h> +#include <utils/MemoryHeapBase.h> +#include <utils/IPCThreadState.h> +#include <utils/StopWatch.h> + +#include <ui/ISurfaceComposer.h> + +#include "VRamHeap.h" +#include "GPUHardware.h" + +#if HAVE_ANDROID_OS +#include <linux/android_pmem.h> +#endif + +#include "GPUHardware/GPUHardware.h" + + +/* + * Manage the GPU. This implementation is very specific to the G1. + * There are no abstraction here. + * + * All this code will soon go-away and be replaced by a new architecture + * for managing graphics accelerators. + * + * In the meantime, it is conceptually possible to instantiate a + * GPUHardwareInterface for another GPU (see GPUFactory at the bottom + * of this file); practically... doubtful. + * + */ + +namespace android { + +// --------------------------------------------------------------------------- + +class GPUClientHeap; +class GPUAreaHeap; + +class GPUHardware : public GPUHardwareInterface, public IBinder::DeathRecipient +{ +public: + static const int GPU_RESERVED_SIZE; + static const int GPUR_SIZE; + + GPUHardware(); + virtual ~GPUHardware(); + + virtual void revoke(int pid); + virtual sp<MemoryDealer> request(int pid); + virtual status_t request(int pid, + const sp<IGPUCallback>& callback, + ISurfaceComposer::gpu_info_t* gpu); + + virtual status_t friendlyRevoke(); + virtual void unconditionalRevoke(); + + virtual pid_t getOwner() const { return mOwner; } + + // used for debugging only... + virtual sp<SimpleBestFitAllocator> getAllocator() const; + +private: + + + enum { + NO_OWNER = -1, + }; + + struct GPUArea { + sp<GPUAreaHeap> heap; + sp<MemoryHeapPmem> clientHeap; + sp<IMemory> map(); + }; + + struct Client { + pid_t pid; + GPUArea smi; + GPUArea ebi; + GPUArea reg; + void createClientHeaps(); + void revokeAllHeaps(); + }; + + Client& getClientLocked(pid_t pid); + status_t requestLocked(int pid); + void releaseLocked(); + void takeBackGPULocked(); + void registerCallbackLocked(const sp<IGPUCallback>& callback, + Client& client); + + virtual void binderDied(const wp<IBinder>& who); + + mutable Mutex mLock; + sp<GPUAreaHeap> mSMIHeap; + sp<GPUAreaHeap> mEBIHeap; + sp<GPUAreaHeap> mREGHeap; + + KeyedVector<pid_t, Client> mClients; + DefaultKeyedVector< wp<IBinder>, pid_t > mRegisteredClients; + + pid_t mOwner; + + sp<MemoryDealer> mCurrentAllocator; + sp<IGPUCallback> mCallback; + + sp<SimpleBestFitAllocator> mAllocator; + + Condition mCondition; +}; + +// size reserved for GPU surfaces +// 1200 KB fits exactly: +// - two 320*480 16-bits double-buffered surfaces +// - one 320*480 32-bits double-buffered surface +// - one 320*240 16-bits double-buffered, 4x anti-aliased surface +const int GPUHardware::GPU_RESERVED_SIZE = 1200 * 1024; +const int GPUHardware::GPUR_SIZE = 1 * 1024 * 1024; + +// --------------------------------------------------------------------------- + +/* + * GPUHandle is a special IMemory given to the client. It represents their + * handle to the GPU. Once they give it up, they loose GPU access, or if + * they explicitly revoke their access through the binder code 1000. + * In both cases, this triggers a callback to revoke() + * first, and then actually powers down the chip. + * + * In the case of a misbehaving app, GPUHardware can ask for an immediate + * release of the GPU to the target process which should answer by calling + * code 1000 on GPUHandle. If it doesn't in a timely manner, the GPU will + * be revoked from under their feet. + * + * We should never hold a strong reference on GPUHandle. In practice this + * shouldn't be a big issue though because clients should use code 1000 and + * not rely on the dtor being called. + * + */ + +class GPUClientHeap : public MemoryHeapPmem +{ +public: + GPUClientHeap(const wp<GPUHardware>& gpu, + const sp<MemoryHeapBase>& heap) + : MemoryHeapPmem(heap), mGPU(gpu) { } +protected: + wp<GPUHardware> mGPU; +}; + +class GPUAreaHeap : public MemoryHeapBase +{ +public: + GPUAreaHeap(const wp<GPUHardware>& gpu, + const char* const vram, size_t size=0, size_t reserved=0) + : MemoryHeapBase(vram, size), mGPU(gpu) { + if (base() != MAP_FAILED) { + if (reserved == 0) + reserved = virtualSize(); + mAllocator = new SimpleBestFitAllocator(reserved); + } + } + virtual sp<MemoryHeapPmem> createClientHeap() { + sp<MemoryHeapBase> parentHeap(this); + return new GPUClientHeap(mGPU, parentHeap); + } + virtual const sp<SimpleBestFitAllocator>& getAllocator() const { + return mAllocator; + } +private: + sp<SimpleBestFitAllocator> mAllocator; +protected: + wp<GPUHardware> mGPU; +}; + +class GPURegisterHeap : public GPUAreaHeap +{ +public: + GPURegisterHeap(const sp<GPUHardware>& gpu) + : GPUAreaHeap(gpu, "/dev/hw3d", GPUHardware::GPUR_SIZE) { } + virtual sp<MemoryHeapPmem> createClientHeap() { + sp<MemoryHeapBase> parentHeap(this); + return new MemoryHeapRegs(mGPU, parentHeap); + } +private: + class MemoryHeapRegs : public GPUClientHeap { + public: + MemoryHeapRegs(const wp<GPUHardware>& gpu, + const sp<MemoryHeapBase>& heap) + : GPUClientHeap(gpu, heap) { } + sp<MemoryHeapPmem::MemoryPmem> createMemory(size_t offset, size_t size); + virtual void revoke(); + private: + class GPUHandle : public MemoryHeapPmem::MemoryPmem { + public: + GPUHandle(const sp<GPUHardware>& gpu, + const sp<MemoryHeapPmem>& heap) + : MemoryHeapPmem::MemoryPmem(heap), + mGPU(gpu), mOwner(gpu->getOwner()) { } + virtual ~GPUHandle(); + virtual sp<IMemoryHeap> getMemory( + ssize_t* offset, size_t* size) const; + virtual void revoke() { }; + virtual status_t onTransact( + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + private: + void revokeNotification(); + wp<GPUHardware> mGPU; + pid_t mOwner; + }; + }; +}; + +GPURegisterHeap::MemoryHeapRegs::GPUHandle::~GPUHandle() { + //LOGD("GPUHandle %p released, revoking GPU", this); + revokeNotification(); +} +void GPURegisterHeap::MemoryHeapRegs::GPUHandle::revokeNotification() { + sp<GPUHardware> hw(mGPU.promote()); + if (hw != 0) { + hw->revoke(mOwner); + } +} +sp<IMemoryHeap> GPURegisterHeap::MemoryHeapRegs::GPUHandle::getMemory( + ssize_t* offset, size_t* size) const +{ + sp<MemoryHeapPmem> heap = getHeap(); + if (offset) *offset = 0; + if (size) *size = heap !=0 ? heap->virtualSize() : 0; + return heap; +} +status_t GPURegisterHeap::MemoryHeapRegs::GPUHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + status_t err = BnMemory::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION && code == 1000) { + int callingPid = IPCThreadState::self()->getCallingPid(); + //LOGD("pid %d voluntarily revoking gpu", callingPid); + if (callingPid == mOwner) { + revokeNotification(); + // we've revoked the GPU, don't do it again later when we + // are destroyed. + mGPU.clear(); + } else { + LOGW("%d revoking someone else's gpu? (owner=%d)", + callingPid, mOwner); + } + err = NO_ERROR; + } + return err; +} + +// --------------------------------------------------------------------------- + + +sp<MemoryHeapPmem::MemoryPmem> GPURegisterHeap::MemoryHeapRegs::createMemory( + size_t offset, size_t size) +{ + sp<GPUHandle> memory; + sp<GPUHardware> gpu = mGPU.promote(); + if (heapID()>0 && gpu!=0) { +#if HAVE_ANDROID_OS + /* this is where the GPU is powered on and the registers are mapped + * in the client */ + //LOGD("ioctl(HW3D_GRANT_GPU)"); + int err = ioctl(heapID(), HW3D_GRANT_GPU, base()); + if (err) { + // it can happen if the master heap has been closed already + // in which case the GPU already is revoked (app crash for + // instance). + LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", + strerror(errno), heapID(), base()); + } + memory = new GPUHandle(gpu, this); +#endif + } + return memory; +} + +void GPURegisterHeap::MemoryHeapRegs::revoke() +{ + MemoryHeapPmem::revoke(); +#if HAVE_ANDROID_OS + if (heapID() > 0) { + //LOGD("ioctl(HW3D_REVOKE_GPU)"); + int err = ioctl(heapID(), HW3D_REVOKE_GPU, base()); + LOGE_IF(err, "HW3D_REVOKE_GPU failed (%s), mFD=%d, base=%p", + strerror(errno), heapID(), base()); + } +#endif +} + +/*****************************************************************************/ + +GPUHardware::GPUHardware() + : mOwner(NO_OWNER) +{ +} + +GPUHardware::~GPUHardware() +{ +} + +status_t GPUHardware::requestLocked(int pid) +{ + const int self_pid = getpid(); + if (pid == self_pid) { + // can't use GPU from surfaceflinger's process + return PERMISSION_DENIED; + } + + if (mOwner != pid) { + if (mREGHeap != 0) { + if (mOwner != NO_OWNER) { + // someone already has the gpu. + takeBackGPULocked(); + releaseLocked(); + } + } else { + // first time, initialize the stuff. + if (mSMIHeap == 0) + mSMIHeap = new GPUAreaHeap(this, "/dev/pmem_gpu0"); + if (mEBIHeap == 0) + mEBIHeap = new GPUAreaHeap(this, + "/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE); + mREGHeap = new GPURegisterHeap(this); + mAllocator = mEBIHeap->getAllocator(); + if (mAllocator == NULL) { + // something went terribly wrong. + mSMIHeap.clear(); + mEBIHeap.clear(); + mREGHeap.clear(); + return INVALID_OPERATION; + } + } + Client& client = getClientLocked(pid); + mCurrentAllocator = new MemoryDealer(client.ebi.clientHeap, mAllocator); + mOwner = pid; + } + return NO_ERROR; +} + +sp<MemoryDealer> GPUHardware::request(int pid) +{ + sp<MemoryDealer> dealer; + Mutex::Autolock _l(mLock); + Client* client; + LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); + if (requestLocked(pid) == NO_ERROR) { + dealer = mCurrentAllocator; + LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); + } + return dealer; +} + +status_t GPUHardware::request(int pid, const sp<IGPUCallback>& callback, + ISurfaceComposer::gpu_info_t* gpu) +{ + if (callback == 0) + return BAD_VALUE; + + sp<IMemory> gpuHandle; + LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner); + Mutex::Autolock _l(mLock); + status_t err = requestLocked(pid); + if (err == NO_ERROR) { + // it's guaranteed to be there, be construction + Client& client = mClients.editValueFor(pid); + registerCallbackLocked(callback, client); + gpu->count = 2; + gpu->regions[0].region = client.smi.map(); + gpu->regions[1].region = client.ebi.map(); + gpu->regs = client.reg.map(); + gpu->regions[0].reserved = 0; + gpu->regions[1].reserved = GPU_RESERVED_SIZE; + if (gpu->regs != 0) { + //LOGD("gpu core granted to pid %d, handle base=%p", + // mOwner, gpu->regs->pointer()); + } + mCallback = callback; + } else { + LOGW("couldn't grant gpu core to pid %d", pid); + } + return err; +} + +void GPUHardware::revoke(int pid) +{ + Mutex::Autolock _l(mLock); + if (mOwner > 0) { + if (pid != mOwner) { + LOGW("GPU owned by %d, revoke from %d", mOwner, pid); + return; + } + //LOGD("revoke pid=%d, owner=%d", pid, mOwner); + // mOwner could be <0 if the same process acquired the GPU + // several times without releasing it first. + mCondition.signal(); + releaseLocked(); + } +} + +status_t GPUHardware::friendlyRevoke() +{ + Mutex::Autolock _l(mLock); + //LOGD("friendlyRevoke owner=%d", mOwner); + takeBackGPULocked(); + releaseLocked(); + return NO_ERROR; +} + +void GPUHardware::takeBackGPULocked() +{ + sp<IGPUCallback> callback = mCallback; + mCallback.clear(); + if (callback != 0) { + callback->gpuLost(); // one-way + mCondition.waitRelative(mLock, ms2ns(250)); + } +} + +void GPUHardware::releaseLocked() +{ + //LOGD("revoking gpu from pid %d", mOwner); + if (mOwner != NO_OWNER) { + // this may fail because the client might have died, and have + // been removed from the list. + ssize_t index = mClients.indexOfKey(mOwner); + if (index >= 0) { + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); + } + mOwner = NO_OWNER; + mCurrentAllocator.clear(); + mCallback.clear(); + } +} + +GPUHardware::Client& GPUHardware::getClientLocked(pid_t pid) +{ + ssize_t index = mClients.indexOfKey(pid); + if (index < 0) { + Client client; + client.pid = pid; + client.smi.heap = mSMIHeap; + client.ebi.heap = mEBIHeap; + client.reg.heap = mREGHeap; + index = mClients.add(pid, client); + } + Client& client(mClients.editValueAt(index)); + client.createClientHeaps(); + return client; +} + +// ---------------------------------------------------------------------------- +// for debugging / testing ... + +sp<SimpleBestFitAllocator> GPUHardware::getAllocator() const { + Mutex::Autolock _l(mLock); + return mAllocator; +} + +void GPUHardware::unconditionalRevoke() +{ + Mutex::Autolock _l(mLock); + releaseLocked(); +} + +// --------------------------------------------------------------------------- + +sp<IMemory> GPUHardware::GPUArea::map() { + sp<IMemory> memory; + if (clientHeap != 0 && heap != 0) { + memory = clientHeap->mapMemory(0, heap->virtualSize()); + } + return memory; +} + +void GPUHardware::Client::createClientHeaps() +{ + if (smi.clientHeap == 0) + smi.clientHeap = smi.heap->createClientHeap(); + if (ebi.clientHeap == 0) + ebi.clientHeap = ebi.heap->createClientHeap(); + if (reg.clientHeap == 0) + reg.clientHeap = reg.heap->createClientHeap(); +} + +void GPUHardware::Client::revokeAllHeaps() +{ + if (smi.clientHeap != 0) + smi.clientHeap->revoke(); + if (ebi.clientHeap != 0) + ebi.clientHeap->revoke(); + if (reg.clientHeap != 0) + reg.clientHeap->revoke(); +} + +void GPUHardware::registerCallbackLocked(const sp<IGPUCallback>& callback, + Client& client) +{ + sp<IBinder> binder = callback->asBinder(); + if (mRegisteredClients.add(binder, client.pid) >= 0) { + binder->linkToDeath(this); + } +} + +void GPUHardware::binderDied(const wp<IBinder>& who) +{ + Mutex::Autolock _l(mLock); + pid_t pid = mRegisteredClients.valueFor(who); + if (pid != 0) { + ssize_t index = mClients.indexOfKey(pid); + if (index >= 0) { + //LOGD("*** removing client at %d", index); + Client& client(mClients.editValueAt(index)); + client.revokeAllHeaps(); // not really needed in theory + mClients.removeItemsAt(index); + if (mClients.size() == 0) { + //LOGD("*** was last client closing everything"); + mCallback.clear(); + mAllocator.clear(); + mCurrentAllocator.clear(); + mSMIHeap.clear(); + mREGHeap.clear(); + + // NOTE: we cannot clear the EBI heap because surfaceflinger + // itself may be using it, since this is where surfaces + // are allocated. if we're in the middle of compositing + // a surface (even if its process just died), we cannot + // rip the heap under our feet. + + mOwner = NO_OWNER; + } + } + } +} + +// --------------------------------------------------------------------------- + +sp<GPUHardwareInterface> GPUFactory::getGPU() +{ + return new GPUHardware(); +} + +// --------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/GPUHardware/GPUHardware.h new file mode 100644 index 0000000..3354528 --- /dev/null +++ b/libs/surfaceflinger/GPUHardware/GPUHardware.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_GPU_HARDWARE_H +#define ANDROID_GPU_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/RefBase.h> +#include <utils/threads.h> +#include <utils/KeyedVector.h> + +#include <ui/ISurfaceComposer.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class IGPUCallback; + +class GPUHardwareInterface : public virtual RefBase +{ +public: + virtual void revoke(int pid) = 0; + virtual sp<MemoryDealer> request(int pid) = 0; + virtual status_t request(int pid, const sp<IGPUCallback>& callback, + ISurfaceComposer::gpu_info_t* gpu) = 0; + + virtual status_t friendlyRevoke() = 0; + + // used for debugging only... + virtual sp<SimpleBestFitAllocator> getAllocator() const = 0; + virtual pid_t getOwner() const = 0; + virtual void unconditionalRevoke() = 0; +}; + +// --------------------------------------------------------------------------- + +class GPUFactory +{ +public: + // the gpu factory + static sp<GPUHardwareInterface> getGPU(); +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GPU_HARDWARE_H diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp new file mode 100644 index 0000000..f65d669 --- /dev/null +++ b/libs/surfaceflinger/Layer.cpp @@ -0,0 +1,568 @@ +/* + * 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 diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h new file mode 100644 index 0000000..2867f2b --- /dev/null +++ b/libs/surfaceflinger/Layer.h @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_H +#define ANDROID_LAYER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/PixelFormat.h> + +#include <private/ui/SharedState.h> +#include <private/ui/LayerState.h> + +#include <pixelflinger/pixelflinger.h> + +#include "LayerBitmap.h" +#include "LayerBase.h" +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class LayerBitmap; +class MemoryDealer; +class FreezeLock; + +// --------------------------------------------------------------------------- + +class Layer : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + Layer(SurfaceFlinger* flinger, DisplayID display, + Client* c, int32_t i); + + virtual ~Layer(); + + inline PixelFormat pixelFormat() const { + return frontBuffer().pixelFormat(); + } + + status_t setBuffers( Client* client, + uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); + + virtual void onDraw(const Region& clip) const; + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + virtual void setSizeChanged(uint32_t w, uint32_t h); + virtual uint32_t doTransaction(uint32_t transactionFlags); + virtual Point getPhysicalSize() const; + virtual void lockPageFlip(bool& recomputeVisibleRegions); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual void finishPageFlip(); + virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool isSecure() const { return mSecure; } + virtual GLuint getTextureName() const { return mTextureName; } + virtual sp<Surface> getSurface() const; + + const LayerBitmap& getBuffer(int i) const { return mBuffers[i]; } + LayerBitmap& getBuffer(int i) { return mBuffers[i]; } + + // only for debugging + const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + +private: + inline const LayerBitmap& + frontBuffer() const { return getBuffer(mFrontBufferIndex); } + inline LayerBitmap& + frontBuffer() { return getBuffer(mFrontBufferIndex); } + inline const LayerBitmap& + backBuffer() const { return getBuffer(1-mFrontBufferIndex); } + inline LayerBitmap& + backBuffer() { return getBuffer(1-mFrontBufferIndex); } + + void reloadTexture(const Region& dirty); + + status_t resize(int32_t index, uint32_t w, uint32_t h, const char* what); + Region post(uint32_t* oldState, bool& recomputeVisibleRegions); + status_t reallocateBuffer(int32_t index, uint32_t w, uint32_t h); + + sp<Surface> mSurface; + + bool mSecure; + LayerBitmap mBuffers[2]; + int32_t mFrontBufferIndex; + bool mNeedsBlending; + bool mResizeTransactionDone; + Region mPostedDirtyRegion; + sp<FreezeLock> mFreezeLock; + + GLuint mTextureName; + GLuint mTextureWidth; + GLuint mTextureHeight; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_H diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp new file mode 100644 index 0000000..0cf53f7 --- /dev/null +++ b/libs/surfaceflinger/LayerBase.cpp @@ -0,0 +1,740 @@ +/* + * 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 <utils/Errors.h> +#include <utils/Log.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "LayerBase.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + + +// We don't honor the premultiplied alpha flags, which means that +// premultiplied surface may be composed using a non-premultiplied +// equation. We do this because it may be a lot faster on some hardware +// The correct value is HONOR_PREMULTIPLIED_ALPHA = 1 +#define HONOR_PREMULTIPLIED_ALPHA 0 + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBase::typeInfo = 1; +const char* const LayerBase::typeID = "LayerBase"; + +const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; +const char* const LayerBaseClient::typeID = "LayerBaseClient"; + +// --------------------------------------------------------------------------- + +Vector<GLuint> LayerBase::deletedTextures; + +int32_t LayerBase::sIdentity = 0; + +LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) + : dpy(display), contentDirty(false), + mFlinger(flinger), + mTransformed(false), + mOrientation(0), + mCanUseCopyBit(false), + mTransactionFlags(0), + mPremultipliedAlpha(true), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))), + mInvalidate(0) +{ + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + mFlags = hw.getFlags(); +} + +LayerBase::~LayerBase() +{ +} + +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.alpha = 0xFF; + mCurrentState.flags = layerFlags; + mCurrentState.sequence = 0; + mCurrentState.transform.set(0, 0); + + // drawing state & current state are identical + mDrawingState = mCurrentState; +} + +void LayerBase::commitTransaction(bool skipSize) { + const uint32_t w = mDrawingState.w; + const uint32_t h = mDrawingState.h; + mDrawingState = mCurrentState; + if (skipSize) { + mDrawingState.w = w; + mDrawingState.h = h; + } +} +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); +} + +void LayerBase::setSizeChanged(uint32_t w, uint32_t h) { +} + +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.w == w && mCurrentState.h == h) + return false; + setSizeChanged(w, h); + mCurrentState.w = w; + mCurrentState.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) { + // TODO: check the matrix has changed + mCurrentState.sequence++; + mCurrentState.transform.set( + matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); + requestTransaction(); + return true; +} +bool LayerBase::setTransparentRegionHint(const Region& transparent) { + // TODO: check the region has changed + 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 (temp.sequence != front.sequence) { + // invalidate and recompute the visible regions if needed + flags |= eVisibleRegion; + this->contentDirty = true; + } + + // Commit the transaction + commitTransaction(flags & eRestartTransaction); + return flags; +} + +Point LayerBase::getPhysicalSize() const +{ + const Layer::State& front(drawingState()); + return Point(front.w, front.h); +} + +void LayerBase::validateVisibility(const Transform& planeTransform) +{ + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + const bool transformed = tr.transformed(); + + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + 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); + mTransformed = transformed; + mLeft = tr.tx(); + mTop = tr.ty(); + + // see if we can/should use 2D h/w with the new configuration + mCanUseCopyBit = false; + copybit_device_t* copybit = mFlinger->getBlitEngine(); + if (copybit) { + const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG); + const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS); + mCanUseCopyBit = true; + if ((mOrientation < 0) && (step > 1)) { + // arbitrary orientations not supported + mCanUseCopyBit = false; + } else if ((mOrientation > 0) && (step > 90)) { + // 90 deg rotations not supported + mCanUseCopyBit = false; + } else if ((tr.getType() & SkMatrix::kScale_Mask) && (scaleBits < 12)) { + // arbitrary scaling not supported + mCanUseCopyBit = false; + } +#if HONOR_PREMULTIPLIED_ALPHA + else if (needsBlending() && mPremultipliedAlpha) { + // pre-multiplied alpha not supported + mCanUseCopyBit = false; + } +#endif + else { + // here, we determined we can use copybit + if (tr.getType() & SkMatrix::kScale_Mask) { + // and we have scaling + if (!transparentRegionScreen.isRect()) { + // we punt because blending is cheap (h/w) and the region is + // complex, which may causes artifacts when copying + // scaled content + transparentRegionScreen.clear(); + } + } + } + } +} + +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::finishPageFlip() +{ +} + +void LayerBase::invalidate() +{ + if ((android_atomic_or(1, &mInvalidate)&1) == 0) { + mFlinger->signalEvent(); + } +} + +void LayerBase::drawRegion(const Region& reg) const +{ + Region::iterator iterator(reg); + if (iterator) { + 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 (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::draw(const Region& inClip) const +{ + // invalidate the region we'll update + Region clip(inClip); // copy-on-write, so no-op most of the time + + // Remove the transparent area from the clipping region + const State& s = drawingState(); + if (LIKELY(!s.transparentRegion.isEmpty())) { + clip.subtract(transparentRegionScreen); + if (clip.isEmpty()) { + // usually this won't happen because this should be taken care of + // by SurfaceFlinger::computeVisibleRegions() + return; + } + } + + // reset GL state + glEnable(GL_SCISSOR_TEST); + + onDraw(clip); + + /* + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0x8000, 0, 0x10000); + drawRegion(transparentRegionScreen); + glDisable(GL_BLEND); + */ +} + +GLuint LayerBase::createTexture() const +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (mFlags & DisplayHardware::SLOW_CONFIG) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + return textureName; +} + +void LayerBase::clearWithOpenGL(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + glColor4x(0,0,0,0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + Rect r; + Region::iterator iterator(clip); + if (iterator) { + glEnable(GL_SCISSOR_TEST); + glVertexPointer(2, GL_FIXED, 0, mVertices); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::drawWithOpenGL(const Region& clip, + GLint textureName, const GGLSurface& t, int transform) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + const State& s(drawingState()); + + // bind our texture + validateTexture(textureName); + glEnable(GL_TEXTURE_2D); + + // Dithering... + if (s.flags & ISurfaceComposer::eLayerDither) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } + + if (UNLIKELY(s.alpha < 0xFF)) { + // We have an alpha-modulation. We need to modulate all + // texture components by alpha because we're always using + // premultiplied alpha. + + // If the texture doesn't have an alpha channel we can + // use REPLACE and switch to non premultiplied alpha + // blending (SRCA/ONE_MINUS_SRCA). + + GLenum env, src; + if (needsBlending()) { + env = GL_MODULATE; + src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + } else { + env = GL_REPLACE; + src = GL_SRC_ALPHA; + } + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(alpha, alpha, alpha, alpha); + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); + } else { + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + if (needsBlending()) { + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + } + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) + { + //StopWatch watch("GL transformed"); + Region::iterator iterator(clip); + if (iterator) { + // always use high-quality filtering with fast configurations + bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG); + if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + const GLfixed texCoords[4][2] = { + { 0, 0 }, + { 0, 0x10000 }, + { 0x10000, 0x10000 }, + { 0x10000, 0 } + }; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + if (transform == HAL_TRANSFORM_ROT_90) { + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + } + + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + // find the smallest power-of-two that will accommodate our surface + GLuint tw = 1 << (31 - clz(t.width)); + GLuint th = 1 << (31 - clz(t.height)); + if (tw < t.width) tw <<= 1; + if (th < t.height) th <<= 1; + // this divide should be relatively fast because it's + // a power-of-two (optimized path in libgcc) + GLfloat ws = GLfloat(t.width) /tw; + GLfloat hs = GLfloat(t.height)/th; + glScalef(ws, hs, 1.0f); + } + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, texCoords); + + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + } else { + Region::iterator iterator(clip); + if (iterator) { + Rect r; + GLint crop[4] = { 0, t.height, t.width, -t.height }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + int x = tx(); + int y = ty(); + y = fbHeight - (y + t.height); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, t.width, t.height); + } + } + } +} + +void LayerBase::validateTexture(GLint textureName) const +{ + glBindTexture(GL_TEXTURE_2D, textureName); + // TODO: reload the texture if needed + // this is currently done in loadTexture() below +} + +void LayerBase::loadTexture(const Region& dirty, + GLint textureName, const GGLSurface& t, + GLuint& textureWidth, GLuint& textureHeight) const +{ + // TODO: defer the actual texture reload until LayerBase::validateTexture + // is called. + + uint32_t flags = mFlags; + glBindTexture(GL_TEXTURE_2D, textureName); + + GLuint tw = t.width; + GLuint th = t.height; + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is 4, which in essence allows a limited form of + * stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + tw += (((t.stride - tw) * bytesPerPixel(t.format)) / 4); + + /* + * round to POT if needed + */ + + GLuint texture_w = tw; + GLuint texture_h = th; + if (!(flags & DisplayHardware::NPOT_EXTENSION)) { + // find the smallest power-of-two that will accommodate our surface + texture_w = 1 << (31 - clz(t.width)); + texture_h = 1 << (31 - clz(t.height)); + if (texture_w < t.width) texture_w <<= 1; + if (texture_h < t.height) texture_h <<= 1; + if (texture_w != tw || texture_h != th) { + // we can't use DIRECT_TEXTURE since we changed the size + // of the texture + flags &= ~DisplayHardware::DIRECT_TEXTURE; + } + } + + if (flags & DisplayHardware::DIRECT_TEXTURE) { + // here we're guaranteed that texture_{w|h} == t{w|h} + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGB, tw, th, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGBA, tw, th, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, + GL_RGBA, tw, th, 0, + GL_RGBA, GL_UNSIGNED_BYTE, t.data); + } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) { + // TODO: add GL_BGRA extension + } else { + // oops, we don't handle this format, try the regular path + goto regular; + } + textureWidth = tw; + textureHeight = th; + } else { +regular: + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture_w!=textureWidth || texture_h!=textureHeight) { + // texture size changed, we need to create a new one + + if (!textureWidth || !textureHeight) { + // this is the first time, load the whole texture + if (texture_w==tw && texture_h==th) { + // we can do it one pass + data = t.data; + } else { + // we have to create the texture first because it + // doesn't match the size of the buffer + bounds.set(Rect(tw, th)); + } + } + + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture_w, texture_h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // just show the Y plane of YUV buffers + data = t.data; + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture_w, texture_h, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, textureName, t.format); + textureName = -1; + } + textureWidth = texture_w; + textureHeight = texture_h; + } + if (!data && textureName>=0) { + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.width*2); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.width*2); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.width*4); + } + } + } +} + +bool LayerBase::canUseCopybit() const +{ + return mCanUseCopyBit; +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + Client* c, int32_t i) + : LayerBase(flinger, display), client(c), + lcblk( c ? &(c->ctrlblk->layers[i]) : 0 ), + mIndex(i) +{ + if (client) { + client->bindLayer(this, i); + + // Initialize this layer's control block + memset(this->lcblk, 0, sizeof(layer_cblk_t)); + this->lcblk->identity = mIdentity; + Region::writeEmpty(&(this->lcblk->region[0]), sizeof(flat_region_t)); + Region::writeEmpty(&(this->lcblk->region[1]), sizeof(flat_region_t)); + } +} + +LayerBaseClient::~LayerBaseClient() +{ + if (client) { + client->free(mIndex); + } +} + +int32_t LayerBaseClient::serverIndex() const { + if (client) { + return (client->cid<<16)|mIndex; + } + return 0xFFFF0000 | mIndex; +} + +sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() const +{ + return new Surface(clientIndex(), mIdentity); +} + + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h new file mode 100644 index 0000000..a020f44 --- /dev/null +++ b/libs/surfaceflinger/LayerBase.h @@ -0,0 +1,356 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_BASE_H +#define ANDROID_LAYER_BASE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <private/ui/LayerState.h> + +#include <ui/Region.h> +#include <ui/Overlay.h> + +#include <pixelflinger/pixelflinger.h> + +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class DisplayHardware; +class GraphicPlane; +class Client; + +// --------------------------------------------------------------------------- + +class LayerBase +{ + // poor man's dynamic_cast below + template<typename T> + struct getTypeInfoOfAnyType { + static uint32_t get() { return T::typeInfo; } + }; + + template<typename T> + struct getTypeInfoOfAnyType<T*> { + static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); } + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + template<typename T> + static T dynamicCast(LayerBase* base) { + uint32_t mostDerivedInfo = base->getTypeInfo(); + uint32_t castToInfo = getTypeInfoOfAnyType<T>::get(); + if ((mostDerivedInfo & castToInfo) == castToInfo) + return static_cast<T>(base); + return 0; + } + + + static Vector<GLuint> deletedTextures; + + LayerBase(SurfaceFlinger* flinger, DisplayID display); + virtual ~LayerBase(); + + DisplayID dpy; + mutable bool contentDirty; + Region visibleRegionScreen; + Region transparentRegionScreen; + Region coveredRegionScreen; + + struct State { + uint32_t w; + uint32_t h; + uint32_t z; + uint8_t alpha; + uint8_t flags; + uint8_t reserved[2]; + int32_t sequence; // changes when visible regions can change + uint32_t tint; + Transform transform; + Region transparentRegion; + }; + + // modify current state + bool setPosition(int32_t x, int32_t y); + bool setLayer(uint32_t z); + bool setSize(uint32_t w, uint32_t h); + bool setAlpha(uint8_t alpha); + bool setMatrix(const layer_state_t::matrix22_t& matrix); + bool setTransparentRegionHint(const Region& opaque); + bool setFlags(uint8_t flags, uint8_t mask); + + void commitTransaction(bool skipSize); + bool requestTransaction(); + void forceVisibilityTransaction(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags); + + Rect visibleBounds() const; + void drawRegion(const Region& reg) const; + + void invalidate(); + + /** + * draw - performs some global clipping optimizations + * and calls onDraw(). + * Typically this method is not overridden, instead implement onDraw() + * to perform the actual drawing. + */ + virtual void draw(const Region& clip) const; + + /** + * onDraw - draws the surface. + */ + virtual void onDraw(const Region& clip) const = 0; + + /** + * initStates - called just after construction + */ + virtual void initStates(uint32_t w, uint32_t h, uint32_t flags); + + /** + * setSizeChanged - called when the *current* state's size is changed. + */ + virtual void setSizeChanged(uint32_t w, uint32_t h); + + /** + * doTransaction - process the transaction. This is a good place to figure + * out which attributes of the surface have changed. + */ + virtual uint32_t doTransaction(uint32_t transactionFlags); + + /** + * setVisibleRegion - called to set the new visible region. This gives + * a chance to update the new visible region or record the fact it changed. + */ + virtual void setVisibleRegion(const Region& visibleRegion); + + /** + * setCoveredRegion - called when the covered region changes. The covered + * region correspond to any area of the surface that is covered + * (transparently or not) by another surface. + */ + virtual void setCoveredRegion(const Region& coveredRegion); + + /** + * getPhysicalSize - returns the physical size of the drawing state of + * the surface. If the surface is backed by a bitmap, this is the size of + * the bitmap (as opposed to the size of the drawing state). + */ + virtual Point getPhysicalSize() const; + + /** + * validateVisibility - cache a bunch of things + */ + virtual void validateVisibility(const Transform& globalTransform); + + /** + * lockPageFlip - called each time the screen is redrawn and returns whether + * the visible regions need to be recomputed (this is a fairly heavy + * operation, so this should be set only if needed). Typically this is used + * to figure out if the content or size of a surface has changed. + */ + virtual void lockPageFlip(bool& recomputeVisibleRegions); + + /** + * unlockPageFlip - called each time the screen is redrawn. updates the + * final dirty region wrt the planeTransform. + * At this point, all visible regions, surface position and size, etc... are + * correct. + */ + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + + /** + * finishPageFlip - called after all surfaces have drawn. + */ + virtual void finishPageFlip(); + + /** + * needsBlending - true if this surface needs blending + */ + virtual bool needsBlending() const { return false; } + + /** + * transformed -- true is this surface needs a to be transformed + */ + virtual bool transformed() const { return mTransformed; } + + /** + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or vns servers. + */ + virtual bool isSecure() const { return false; } + + enum { // flags for doTransaction() + eVisibleRegion = 0x00000002, + eRestartTransaction = 0x00000008 + }; + + + inline const State& drawingState() const { return mDrawingState; } + inline const State& currentState() const { return mCurrentState; } + inline State& currentState() { return mCurrentState; } + + static int compareCurrentStateZ(LayerBase*const* layerA, LayerBase*const* layerB) { + return layerA[0]->currentState().z - layerB[0]->currentState().z; + } + + int32_t getOrientation() const { return mOrientation; } + int tx() const { return mLeft; } + int ty() const { return mTop; } + +protected: + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); + + GLuint createTexture() const; + + void drawWithOpenGL(const Region& clip, + GLint textureName, + const GGLSurface& surface, + int transform = 0) const; + + void clearWithOpenGL(const Region& clip) const; + + void loadTexture(const Region& dirty, + GLint textureName, const GGLSurface& t, + GLuint& textureWidth, GLuint& textureHeight) const; + + bool canUseCopybit() const; + + SurfaceFlinger* mFlinger; + uint32_t mFlags; + + // cached during validateVisibility() + bool mTransformed; + int32_t mOrientation; + GLfixed mVertices[4][2]; + Rect mTransformedBounds; + bool mCanUseCopyBit; + int mLeft; + int mTop; + + // these are protected by an external lock + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + + // don't change, don't need a lock + bool mPremultipliedAlpha; + + // only read + const uint32_t mIdentity; + + // atomic + volatile int32_t mInvalidate; + + +private: + void validateTexture(GLint textureName) const; + static int32_t sIdentity; +}; + + +// --------------------------------------------------------------------------- + +class LayerBaseClient : public LayerBase +{ +public: + class Surface; + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBaseClient(); + + + Client* const client; + layer_cblk_t* const lcblk; + + inline int32_t clientIndex() const { return mIndex; } + int32_t serverIndex() const; + + virtual sp<Surface> getSurface() const; + + uint32_t getIdentity() const { return mIdentity; } + + class Surface : public BnSurface + { + public: + Surface(SurfaceID id, int identity) { + mParams.token = id; + mParams.identity = identity; + } + Surface(SurfaceID id, + const sp<IMemoryHeap>& heap0, + const sp<IMemoryHeap>& heap1, + int identity) + { + mParams.token = id; + mParams.identity = identity; + mParams.heap[0] = heap0; + mParams.heap[1] = heap1; + } + virtual ~Surface() { + // TODO: We now have a point here were we can clean-up the + // client's mess. + // This is also where surface id should be recycled. + //LOGD("Surface %d, heaps={%p, %p} destroyed", + // mId, mHeap[0].get(), mHeap[1].get()); + } + + virtual void getSurfaceData( + ISurfaceFlingerClient::surface_data_t* params) const { + *params = mParams; + } + + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers) + { return INVALID_OPERATION; } + virtual void postBuffer(ssize_t offset) { } + virtual void unregisterBuffers() { }; + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format) { + return NULL; + }; + + private: + ISurfaceFlingerClient::surface_data_t mParams; + }; + +private: + int32_t mIndex; + +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BASE_H diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp new file mode 100644 index 0000000..e844350 --- /dev/null +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -0,0 +1,185 @@ +/* + * 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/memory.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/MemoryDealer.h> +#include <utils/IMemory.h> +#include <ui/PixelFormat.h> +#include <pixelflinger/pixelflinger.h> + +#include "LayerBitmap.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +LayerBitmap::LayerBitmap() + : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2) +{ + memset(&mSurface, 0, sizeof(mSurface)); +} + +LayerBitmap::~LayerBitmap() +{ + mSurface.data = 0; +} + +status_t LayerBitmap::init(const sp<MemoryDealer>& allocator) +{ + if (mAllocator != NULL) + return BAD_VALUE; + mAllocator = allocator; + return NO_ERROR; +} + +status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, + PixelFormat format, uint32_t flags) +{ + const sp<MemoryDealer>& allocator(mAllocator); + if (allocator == NULL) + return NO_INIT; + + if (UNLIKELY(w == mSurface.width && h == mSurface.height && + format == mSurface.format)) + { // same format and size, do nothing. + return NO_ERROR; + } + + PixelFormatInfo info; + getPixelFormatInfo(format, &info); + + uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED; + const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT + const uint32_t Bpp = info.bytesPerPixel; + uint32_t stride = (w + (alignment-1)) & ~(alignment-1); + stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp; + size_t size = info.getScanlineSize(stride) * h; + if (allocFlags & MemoryDealer::PAGE_ALIGNED) { + size_t pagesize = getpagesize(); + size = (size + (pagesize-1)) & ~(pagesize-1); + } + + /* FIXME: we should be able to have a h/v stride because the user of the + * surface might have stride limitation (for instance h/w codecs often do) + */ + int32_t vstride = 0; + + mAlignment = alignment; + mAllocFlags = allocFlags; + mOffset = 0; + if (mSize != size) { + // would be nice to have a reallocate() api + mBitsMemory.clear(); // free-memory + mBitsMemory = allocator->allocate(size, allocFlags); + mSize = size; + } else { + // don't erase memory if we didn't have to reallocate + flags &= ~SECURE_BITS; + } + if (mBitsMemory != 0) { + mOffset = mBitsMemory->offset(); + mSurface.data = static_cast<GGLubyte*>(mBitsMemory->pointer()); + mSurface.version = sizeof(GGLSurface); + mSurface.width = w; + mSurface.height = h; + mSurface.stride = stride; + mSurface.vstride = vstride; + mSurface.format = format; + if (flags & SECURE_BITS) + clear(); + } + + if (mBitsMemory==0 || mSurface.data==0) { + LOGE("not enough memory for layer bitmap size=%u", size); + allocator->dump("LayerBitmap"); + mSurface.data = 0; + mSize = -1U; + return NO_MEMORY; + } + return NO_ERROR; +} + +void LayerBitmap::clear() +{ + // NOTE: this memset should not be necessary, at least for + // opaque surface. However, for security reasons it's better to keep it + // (in the case of pmem, it's possible that the memory contains old + // data) + if (mSurface.data) { + memset(mSurface.data, 0, mSize); + //if (bytesPerPixel(mSurface.format) == 4) { + // android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize); + //} else { + // android_memset16((uint16_t*)mSurface.data, 0xF800, mSize); + //} + } +} + +status_t LayerBitmap::getInfo(surface_info_t* info) const +{ + if (mSurface.data == 0) { + memset(info, 0, sizeof(surface_info_t)); + info->bits_offset = NO_MEMORY; + return NO_MEMORY; + } + info->w = uint16_t(width()); + info->h = uint16_t(height()); + info->stride= uint16_t(stride()); + info->bpr = uint16_t(stride() * bytesPerPixel(pixelFormat())); + info->format= uint8_t(pixelFormat()); + info->flags = surface_info_t::eBufferDirty; + info->bits_offset = ssize_t(mOffset); + return NO_ERROR; +} + +status_t LayerBitmap::resize(uint32_t w, uint32_t h) +{ + int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS); + return err; +} + +size_t LayerBitmap::size() const +{ + return mSize; +} + +void LayerBitmap::getBitmapSurface(copybit_image_t* img) const +{ + const sp<IMemoryHeap>& mh(getAllocator()->getMemoryHeap()); + void* sbase = mh->base(); + const GGLSurface& t(surface()); + img->w = t.stride ?: t.width; + img->h = t.vstride ?: t.height; + img->format = t.format; + img->offset = intptr_t(t.data) - intptr_t(sbase); + img->base = sbase; + img->fd = mh->heapID(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h new file mode 100644 index 0000000..9ad64c4 --- /dev/null +++ b/libs/surfaceflinger/LayerBitmap.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_BITMAP_H +#define ANDROID_LAYER_BITMAP_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Atomic.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <private/ui/SharedState.h> +#include <pixelflinger/pixelflinger.h> + +class copybit_image_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class IMemory; +class MemoryDealer; +class LayerBitmap; + +// --------------------------------------------------------------------------- + +class LayerBitmap +{ +public: + + enum { + // erase memory to ensure security when necessary + SECURE_BITS = 0x00000001 + }; + + LayerBitmap(); + ~LayerBitmap(); + status_t init(const sp<MemoryDealer>& allocator); + + status_t setBits(uint32_t w, uint32_t h, uint32_t alignment, + PixelFormat format, uint32_t flags = 0); + void clear(); + + status_t getInfo(surface_info_t* info) const; + status_t resize(uint32_t w, uint32_t h); + + const GGLSurface& surface() const { return mSurface; } + Rect bounds() const { return Rect(width(), height()); } + uint32_t width() const { return surface().width; } + uint32_t height() const { return surface().height; } + uint32_t stride() const { return surface().stride; } + PixelFormat pixelFormat() const { return surface().format; } + void* serverBits() const { return surface().data; } + size_t size() const; + const sp<MemoryDealer>& getAllocator() const { return mAllocator; } + void getBitmapSurface(copybit_image_t* img) const; + +private: + sp<MemoryDealer> mAllocator; + sp<IMemory> mBitsMemory; + uint32_t mAllocFlags; + ssize_t mOffset; + GGLSurface mSurface; + size_t mSize; + uint32_t mAlignment; +}; + +}; // namespace android + +#endif // ANDROID_LAYER_BITMAP_H diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp new file mode 100644 index 0000000..d3e456f --- /dev/null +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,234 @@ +/* + * 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 <utils/Errors.h> +#include <utils/Log.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "BlurFilter.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; +const char* const LayerBlur::typeID = "LayerBlur"; + +// --------------------------------------------------------------------------- + +LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), + mRefreshCache(true), mCacheAge(0), mTextureName(-1U) +{ +} + +LayerBlur::~LayerBlur() +{ + if (mTextureName != -1U) { + //glDeleteTextures(1, &mTextureName); + deletedTextures.add(mTextureName); + } +} + +void LayerBlur::setVisibleRegion(const Region& visibleRegion) +{ + LayerBaseClient::setVisibleRegion(visibleRegion); + if (visibleRegionScreen.isEmpty()) { + if (mTextureName != -1U) { + // We're not visible, free the texture up. + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &mTextureName); + mTextureName = -1U; + } + } +} + +uint32_t LayerBlur::doTransaction(uint32_t flags) +{ + // we're doing a transaction, refresh the cache! + if (!mFlinger->isFrozen()) { + mRefreshCache = true; + mCacheDirty = true; + flags |= eVisibleRegion; + this->contentDirty = true; + } + return LayerBase::doTransaction(flags); +} + +void LayerBlur::unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(!visibleRegionScreen.isEmpty())) { + // if anything visible below us is invalidated, the cache becomes dirty + if (!mCacheDirty && + !visibleRegionScreen.intersect(outDirtyRegion).isEmpty()) { + mCacheDirty = true; + } + if (mCacheDirty) { + if (!mFlinger->isFrozen()) { + // update everything below us that is visible + outDirtyRegion.orSelf(visibleRegionScreen); + nsecs_t now = systemTime(); + if ((now - mCacheAge) >= ms2ns(500)) { + mCacheAge = now; + mRefreshCache = true; + mCacheDirty = false; + } else { + if (!mAutoRefreshPending) { + mFlinger->signalDelayedEvent(ms2ns(500)); + mAutoRefreshPending = true; + } + } + } + } + } + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBlur::onDraw(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + int x = mTransformedBounds.left; + int y = mTransformedBounds.top; + int w = mTransformedBounds.width(); + int h = mTransformedBounds.height(); + GLint X = x; + GLint Y = fbHeight - (y + h); + if (X < 0) { + w += X; + X = 0; + } + if (Y < 0) { + h += Y; + Y = 0; + } + if (w<0 || h<0) { + // we're outside of the framebuffer + return; + } + + if (mTextureName == -1U) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + glGenTextures(1, &mTextureName); + } + + Region::iterator iterator(clip); + if (iterator) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mTextureName); + + if (mRefreshCache) { + mRefreshCache = false; + mAutoRefreshPending = false; + + // allocate enough memory for 4-bytes (2 pixels) aligned data + const int32_t s = (w + 1) & ~1; + uint16_t* const pixels = (uint16_t*)malloc(s*h*2); + + // This reads the frame-buffer, so a h/w GL would have to + // finish() its rendering first. we don't want to do that + // too often. Read data is 4-bytes aligned. + glReadPixels(X, Y, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + + // blur that texture. + GGLSurface bl; + bl.version = sizeof(GGLSurface); + bl.width = w; + bl.height = h; + bl.stride = s; + bl.format = GGL_PIXEL_FORMAT_RGB_565; + bl.data = (GGLubyte*)pixels; + blurFilter(&bl, 8, 2); + + // NOTE: this works only because we have POT. we'd have to round the + // texture size up, otherwise. + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + + free((void*)pixels); + } + + const State& s = drawingState(); + if (UNLIKELY(s.alpha < 0xFF)) { + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(0, 0, 0, alpha); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else { + glDisable(GL_BLEND); + } + + glDisable(GL_DITHER); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { + // This is a very rare scenario. + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(1.0f/w, -1.0f/h, 1); + glTranslatef(-x, -y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 0, mVertices); + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } else { + Region::iterator iterator(clip); + if (iterator) { + // NOTE: this is marginally faster with the software gl, because + // glReadPixels() reads the fb bottom-to-top, however we'll + // skip all the jaccobian computations. + Rect r; + GLint crop[4] = { 0, 0, w, h }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + y = fbHeight - (y + h); + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, w, h); + } + } + } + } + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h new file mode 100644 index 0000000..24b1156 --- /dev/null +++ b/libs/surfaceflinger/LayerBlur.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_BLUR_H +#define ANDROID_LAYER_BLUR_H + +#include <stdint.h> +#include <sys/types.h> + +#include <private/ui/LayerState.h> + +#include <ui/Region.h> + +#include "LayerBase.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerBlur : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBlur(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBlur(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + virtual uint32_t doTransaction(uint32_t flags); + virtual void setVisibleRegion(const Region& visibleRegion); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + +private: + bool mCacheDirty; + mutable bool mRefreshCache; + mutable bool mAutoRefreshPending; + nsecs_t mCacheAge; + mutable GLuint mTextureName; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BLUR_H diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp new file mode 100644 index 0000000..00fab70 --- /dev/null +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -0,0 +1,655 @@ +/* + * 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 <math.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> + +#include <ui/PixelFormat.h> +#include <ui/EGLDisplaySurface.h> + +#include "LayerBuffer.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; +const char* const LayerBuffer::typeID = "LayerBuffer"; + +// --------------------------------------------------------------------------- + +LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + mNeedsBlending(false) +{ +} + +LayerBuffer::~LayerBuffer() +{ + sp<SurfaceBuffer> s(getClientSurface()); + if (s != 0) { + s->disown(); + mClientSurface.clear(); + } +} + +sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const +{ + Mutex::Autolock _l(mLock); + return mClientSurface.promote(); +} + +sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const +{ + sp<SurfaceBuffer> s; + Mutex::Autolock _l(mLock); + s = mClientSurface.promote(); + if (s == 0) { + s = new SurfaceBuffer(clientIndex(), + const_cast<LayerBuffer *>(this)); + mClientSurface = s; + } + return s; +} + +bool LayerBuffer::needsBlending() const { + return mNeedsBlending; +} + +void LayerBuffer::setNeedsBlending(bool blending) { + mNeedsBlending = blending; +} + +void LayerBuffer::postBuffer(ssize_t offset) +{ + sp<Source> source(getSource()); + if (source != 0) + source->postBuffer(offset); +} + +void LayerBuffer::unregisterBuffers() +{ + sp<Source> source(clearSource()); + if (source != 0) + source->unregisterBuffers(); +} + +uint32_t LayerBuffer::doTransaction(uint32_t flags) +{ + sp<Source> source(getSource()); + if (source != 0) + source->onTransaction(flags); + return LayerBase::doTransaction(flags); +} + +void LayerBuffer::unlockPageFlip(const Transform& planeTransform, + Region& outDirtyRegion) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + sp<Source> source(getSource()); + if (source != 0) + source->onVisibilityResolved(planeTransform); + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBuffer::onDraw(const Region& clip) const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) { + source->onDraw(clip); + } else { + clearWithOpenGL(clip); + } +} + +bool LayerBuffer::transformed() const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) + return source->transformed(); + return false; +} + +/** + * This creates a "buffer" source for this surface + */ +status_t LayerBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +{ + Mutex::Autolock _l(mLock); + if (mSource != 0) + return INVALID_OPERATION; + + sp<BufferSource> source = new BufferSource(*this, buffers); + + status_t result = source->getStatus(); + if (result == NO_ERROR) { + mSource = source; + } + return result; +} + +/** + * This creates an "overlay" source for this surface + */ +sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f) +{ + sp<OverlayRef> result; + Mutex::Autolock _l(mLock); + if (mSource != 0) + return result; + + sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f); + if (result != 0) { + mSource = source; + } + return result; +} + +sp<LayerBuffer::Source> LayerBuffer::getSource() const { + Mutex::Autolock _l(mLock); + return mSource; +} + +sp<LayerBuffer::Source> LayerBuffer::clearSource() { + sp<Source> source; + Mutex::Autolock _l(mLock); + source = mSource; + mSource.clear(); + return source; +} + +// ============================================================================ +// LayerBuffer::SurfaceBuffer +// ============================================================================ + +LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner) +: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner) +{ +} + +LayerBuffer::SurfaceBuffer::~SurfaceBuffer() +{ + unregisterBuffers(); + mOwner = 0; +} + +status_t LayerBuffer::SurfaceBuffer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (LIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + } + return LayerBaseClient::Surface::onTransact(code, data, reply, flags); +} + +status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +{ + LayerBuffer* owner(getOwner()); + if (owner) + return owner->registerBuffers(buffers); + return NO_INIT; +} + +void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset) +{ + LayerBuffer* owner(getOwner()); + if (owner) + owner->postBuffer(offset); +} + +void LayerBuffer::SurfaceBuffer::unregisterBuffers() +{ + LayerBuffer* owner(getOwner()); + if (owner) + owner->unregisterBuffers(); +} + +sp<OverlayRef> LayerBuffer::SurfaceBuffer::createOverlay( + uint32_t w, uint32_t h, int32_t format) { + sp<OverlayRef> result; + LayerBuffer* owner(getOwner()); + if (owner) + result = owner->createOverlay(w, h, format); + return result; +} + +void LayerBuffer::SurfaceBuffer::disown() +{ + Mutex::Autolock _l(mLock); + mOwner = 0; +} + +// ============================================================================ +// LayerBuffer::Buffer +// ============================================================================ + +LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) + : mBufferHeap(buffers) +{ + NativeBuffer& src(mNativeBuffer); + src.crop.l = 0; + src.crop.t = 0; + src.crop.r = buffers.w; + src.crop.b = buffers.h; + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; + src.img.offset = offset; + src.img.base = buffers.heap->base(); + src.img.fd = buffers.heap->heapID(); +} + +LayerBuffer::Buffer::~Buffer() +{ +} + +// ============================================================================ +// LayerBuffer::Source +// LayerBuffer::BufferSource +// LayerBuffer::OverlaySource +// ============================================================================ + +LayerBuffer::Source::Source(LayerBuffer& layer) + : mLayer(layer) +{ +} +LayerBuffer::Source::~Source() { +} +void LayerBuffer::Source::onDraw(const Region& clip) const { +} +void LayerBuffer::Source::onTransaction(uint32_t flags) { +} +void LayerBuffer::Source::onVisibilityResolved( + const Transform& planeTransform) { +} +void LayerBuffer::Source::postBuffer(ssize_t offset) { +} +void LayerBuffer::Source::unregisterBuffers() { +} +bool LayerBuffer::Source::transformed() const { + return mLayer.mTransformed; +} + +// --------------------------------------------------------------------------- + +LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), + mBufferSize(0), mTextureName(-1U) +{ + if (buffers.heap == NULL) { + // this is allowed, but in this case, it is illegal to receive + // postBuffer(). The surface just erases the framebuffer with + // fully transparent pixels. + mBufferHeap = buffers; + mLayer.setNeedsBlending(false); + return; + } + + status_t err = (buffers.heap->heapID() >= 0) ? NO_ERROR : NO_INIT; + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid heap (%s)", strerror(err)); + mStatus = err; + return; + } + + PixelFormatInfo info; + err = getPixelFormatInfo(buffers.format, &info); + if (err != NO_ERROR) { + LOGE("LayerBuffer::BufferSource: invalid format %d (%s)", + buffers.format, strerror(err)); + mStatus = err; + return; + } + + if (buffers.hor_stride<0 || buffers.ver_stride<0) { + LOGE("LayerBuffer::BufferSource: invalid parameters " + "(w=%d, h=%d, xs=%d, ys=%d)", + buffers.w, buffers.h, buffers.hor_stride, buffers.ver_stride); + mStatus = BAD_VALUE; + return; + } + + mBufferHeap = buffers; + mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); + mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; + mLayer.forceVisibilityTransaction(); + +} + +LayerBuffer::BufferSource::~BufferSource() +{ + if (mTextureName != -1U) { + LayerBase::deletedTextures.add(mTextureName); + } +} + +void LayerBuffer::BufferSource::postBuffer(ssize_t offset) +{ + ISurface::BufferHeap buffers; + { // scope for the lock + Mutex::Autolock _l(mLock); + buffers = mBufferHeap; + if (buffers.heap != 0) { + const size_t memorySize = buffers.heap->getSize(); + if ((size_t(offset) + mBufferSize) > memorySize) { + LOGE("LayerBuffer::BufferSource::postBuffer() " + "invalid buffer (offset=%d, size=%d, heap-size=%d", + int(offset), int(mBufferSize), int(memorySize)); + return; + } + } + } + + sp<Buffer> buffer; + if (buffers.heap != 0) { + buffer = new LayerBuffer::Buffer(buffers, offset); + if (buffer->getStatus() != NO_ERROR) + buffer.clear(); + setBuffer(buffer); + mLayer.invalidate(); + } +} + +void LayerBuffer::BufferSource::unregisterBuffers() +{ + Mutex::Autolock _l(mLock); + mBufferHeap.heap.clear(); + mBuffer.clear(); + mLayer.invalidate(); +} + +sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const +{ + Mutex::Autolock _l(mLock); + return mBuffer; +} + +void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) +{ + Mutex::Autolock _l(mLock); + mBuffer = buffer; +} + +bool LayerBuffer::BufferSource::transformed() const +{ + return mBufferHeap.transform ? true : Source::transformed(); +} + +void LayerBuffer::BufferSource::onDraw(const Region& clip) const +{ + sp<Buffer> buffer(getBuffer()); + if (UNLIKELY(buffer == 0)) { + // nothing to do, we don't have a buffer + mLayer.clearWithOpenGL(clip); + return; + } + + status_t err = NO_ERROR; + NativeBuffer src(buffer->getBuffer()); + const Rect& transformedBounds = mLayer.getTransformedBounds(); + const int can_use_copybit = mLayer.canUseCopybit(); + + if (can_use_copybit) { + const int src_width = src.crop.r - src.crop.l; + const int src_height = src.crop.b - src.crop.t; + int W = transformedBounds.width(); + int H = transformedBounds.height(); + if (mLayer.getOrientation() & Transform::ROT_90) { + int t(W); W=H; H=t; + } + + /* With LayerBuffer, it is likely that we'll have to rescale the + * surface, because this is often used for video playback or + * camera-preview. Since we want these operation as fast as possible + * we make sure we can use the 2D H/W even if it doesn't support + * the requested scale factor, in which case we perform the scaling + * in several passes. */ + + copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine(); + const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); + const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); + + float xscale = 1.0f; + if (src_width > W*min) xscale = 1.0f / min; + else if (src_width*mag < W) xscale = mag; + + float yscale = 1.0f; + if (src_height > H*min) yscale = 1.0f / min; + else if (src_height*mag < H) yscale = mag; + + if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) { + if (UNLIKELY(mTemporaryDealer == 0)) { + // allocate a memory-dealer for this the first time + mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager() + ->createHeap(ISurfaceComposer::eHardware); + mTempBitmap.init(mTemporaryDealer); + } + + const int tmp_w = floorf(src_width * xscale); + const int tmp_h = floorf(src_height * yscale); + err = mTempBitmap.setBits(tmp_w, tmp_h, 1, src.img.format); + + if (LIKELY(err == NO_ERROR)) { + NativeBuffer tmp; + mTempBitmap.getBitmapSurface(&tmp.img); + tmp.crop.l = 0; + tmp.crop.t = 0; + tmp.crop.r = tmp.img.w; + tmp.crop.b = tmp.img.h; + + region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); + err = copybit->stretch(copybit, + &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it); + src = tmp; + } + } + + const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware()); + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast<const copybit_rect_t&>(transformedBounds); + const State& s(mLayer.drawingState()); + region_iterator it(clip); + + // pick the right orientation for this buffer + int orientation = mLayer.getOrientation(); + if (UNLIKELY(mBufferHeap.transform)) { + Transform rot90; + GraphicPlane::orientationToTransfrom( + ISurfaceComposer::eOrientation90, 0, 0, &rot90); + const Transform& planeTransform(mLayer.graphicPlane(0).transform()); + const Layer::State& s(mLayer.drawingState()); + Transform tr(planeTransform * s.transform * rot90); + orientation = tr.getOrientation(); + } + + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + err = copybit->stretch(copybit, + &dst, &src.img, &drect, &src.crop, &it); + if (err != NO_ERROR) { + LOGE("copybit failed (%s)", strerror(err)); + } + } + + if (!can_use_copybit || err) { + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = mLayer.createTexture(); + } + GLuint w = 0; + GLuint h = 0; + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.crop.r; + t.height = src.crop.b; + t.stride = src.img.w; + t.vstride= src.img.h; + t.format = src.img.format; + t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset); + const Region dirty(Rect(t.width, t.height)); + mLayer.loadTexture(dirty, mTextureName, t, w, h); + mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform); + } +} + + +// --------------------------------------------------------------------------- + +LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format) + : Source(layer), mVisibilityChanged(false), + mOverlay(0), mOverlayHandle(0), mOverlayDevice(0) +{ + overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine(); + if (overlay_dev == NULL) { + // overlays not supported + return; + } + + mOverlayDevice = overlay_dev; + overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); + if (overlay == NULL) { + // couldn't create the overlay (no memory? no more overlays?) + return; + } + + // enable dithering... + overlay_dev->setParameter(overlay_dev, overlay, + OVERLAY_DITHER, OVERLAY_ENABLE); + + mOverlay = overlay; + mWidth = overlay->w; + mHeight = overlay->h; + mFormat = overlay->format; + mWidthStride = overlay->w_stride; + mHeightStride = overlay->h_stride; + + mOverlayHandle = overlay->getHandleRef(overlay); + + // NOTE: here it's okay to acquire a reference to "this"m as long as + // the reference is not released before we leave the ctor. + sp<OverlayChannel> channel = new OverlayChannel(this); + + *overlayRef = new OverlayRef(mOverlayHandle, channel, + mWidth, mHeight, mFormat, mWidthStride, mHeightStride); +} + +LayerBuffer::OverlaySource::~OverlaySource() +{ + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + } +} + +void LayerBuffer::OverlaySource::onTransaction(uint32_t flags) +{ + const Layer::State& front(mLayer.drawingState()); + const Layer::State& temp(mLayer.currentState()); + if (temp.sequence != front.sequence) { + mVisibilityChanged = true; + } +} + +void LayerBuffer::OverlaySource::onVisibilityResolved( + const Transform& planeTransform) +{ + // this code-path must be as tight as possible, it's called each time + // the screen is composited. + if (UNLIKELY(mOverlay != 0)) { + if (mVisibilityChanged) { + mVisibilityChanged = false; + const Rect& bounds = mLayer.getTransformedBounds(); + int x = bounds.left; + int y = bounds.top; + int w = bounds.width(); + int h = bounds.height(); + + // we need a lock here to protect "destroy" + Mutex::Autolock _l(mLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); + overlay_dev->setParameter(overlay_dev, mOverlay, + OVERLAY_TRANSFORM, mLayer.getOrientation()); + } + } + } +} + +void LayerBuffer::OverlaySource::serverDestroy() +{ + mLayer.clearSource(); + destroyOverlay(); +} + +void LayerBuffer::OverlaySource::destroyOverlay() +{ + // we need a lock here to protect "onVisibilityResolved" + Mutex::Autolock _l(mLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + mOverlay = 0; + } +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h new file mode 100644 index 0000000..2dc77f1 --- /dev/null +++ b/libs/surfaceflinger/LayerBuffer.h @@ -0,0 +1,216 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_BUFFER_H +#define ANDROID_LAYER_BUFFER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/IMemory.h> +#include <private/ui/LayerState.h> +#include <EGL/eglnatives.h> + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryDealer; +class Region; +class OverlayRef; + +class LayerBuffer : public LayerBaseClient +{ + class Source : public LightRefBase<Source> { + public: + Source(LayerBuffer& layer); + virtual ~Source(); + virtual void onDraw(const Region& clip) const; + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + protected: + LayerBuffer& mLayer; + }; + + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerBuffer(); + + virtual bool needsBlending() const; + + virtual sp<LayerBaseClient::Surface> getSurface() const; + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t flags); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual bool transformed() const; + + status_t registerBuffers(const ISurface::BufferHeap& buffers); + void postBuffer(ssize_t offset); + void unregisterBuffers(); + sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format); + + sp<Source> getSource() const; + sp<Source> clearSource(); + void setNeedsBlending(bool blending); + const Rect& getTransformedBounds() const { + return mTransformedBounds; + } + +private: + struct NativeBuffer { + copybit_image_t img; + copybit_rect_t crop; + }; + + class Buffer : public LightRefBase<Buffer> { + public: + Buffer(const ISurface::BufferHeap& buffers, ssize_t offset); + inline status_t getStatus() const { + return mBufferHeap.heap!=0 ? NO_ERROR : NO_INIT; + } + inline const NativeBuffer& getBuffer() const { + return mNativeBuffer; + } + protected: + friend class LightRefBase<Buffer>; + Buffer& operator = (const Buffer& rhs); + Buffer(const Buffer& rhs); + ~Buffer(); + private: + ISurface::BufferHeap mBufferHeap; + NativeBuffer mNativeBuffer; + }; + + class BufferSource : public Source { + public: + BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers); + virtual ~BufferSource(); + + status_t getStatus() const { return mStatus; } + sp<Buffer> getBuffer() const; + void setBuffer(const sp<Buffer>& buffer); + + virtual void onDraw(const Region& clip) const; + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual bool transformed() const; + private: + mutable Mutex mLock; + sp<Buffer> mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable sp<MemoryDealer> mTemporaryDealer; + mutable LayerBitmap mTempBitmap; + mutable GLuint mTextureName; + }; + + class OverlaySource : public Source { + public: + OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format); + virtual ~OverlaySource(); + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + private: + void serverDestroy(); + void destroyOverlay(); + class OverlayChannel : public BnOverlay { + mutable Mutex mLock; + sp<OverlaySource> mSource; + virtual void destroy() { + sp<OverlaySource> source; + { // scope for the lock; + Mutex::Autolock _l(mLock); + source = mSource; + mSource.clear(); + } + if (source != 0) { + source->serverDestroy(); + } + } + public: + OverlayChannel(const sp<OverlaySource>& source) + : mSource(source) { + } + }; + friend class OverlayChannel; + bool mVisibilityChanged; + + overlay_t* mOverlay; + overlay_handle_t mOverlayHandle; + overlay_control_device_t* mOverlayDevice; + uint32_t mWidth; + uint32_t mHeight; + int32_t mFormat; + int32_t mWidthStride; + int32_t mHeightStride; + mutable Mutex mLock; + }; + + + class SurfaceBuffer : public LayerBaseClient::Surface + { + public: + SurfaceBuffer(SurfaceID id, LayerBuffer* owner); + virtual ~SurfaceBuffer(); + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format); + void disown(); + private: + LayerBuffer* getOwner() const { + Mutex::Autolock _l(mLock); + return mOwner; + } + mutable Mutex mLock; + LayerBuffer* mOwner; + }; + + friend class SurfaceFlinger; + sp<SurfaceBuffer> getClientSurface() const; + + mutable Mutex mLock; + sp<Source> mSource; + + bool mInvalidate; + bool mNeedsBlending; + mutable wp<SurfaceBuffer> mClientSurface; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BUFFER_H diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp new file mode 100644 index 0000000..0c347cc --- /dev/null +++ b/libs/surfaceflinger/LayerDim.cpp @@ -0,0 +1,113 @@ +/* + * 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 <utils/Errors.h> +#include <utils/Log.h> + +#include "LayerDim.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; +const char* const LayerDim::typeID = "LayerDim"; +sp<MemoryDealer> LayerDim::mDimmerDealer; +LayerBitmap LayerDim::mDimmerBitmap; + +// --------------------------------------------------------------------------- + +LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i) + : LayerBaseClient(flinger, display, client, i) +{ +} + +void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) +{ + // must only be called once. + mDimmerDealer = flinger->getSurfaceHeapManager() + ->createHeap(ISurfaceComposer::eHardware); + if (mDimmerDealer != 0) { + mDimmerBitmap.init(mDimmerDealer); + mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565); + mDimmerBitmap.clear(); + } +} + +LayerDim::~LayerDim() +{ +} + +void LayerDim::onDraw(const Region& clip) const +{ + const State& s(drawingState()); + + Region::iterator iterator(clip); + if (s.alpha>0 && iterator) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + + status_t err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + // StopWatch watch("copybit"); + copybit_image_t dst; + hw.getDisplaySurface(&dst); + const copybit_rect_t& drect + = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); + + copybit_image_t src; + mDimmerBitmap.getBitmapSurface(&src); + const copybit_rect_t& srect(drect); + + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + region_iterator it(clip); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + + if (!can_use_copybit || err) { + const GGLfixed alpha = (s.alpha << 16)/255; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0, 0, alpha); + glVertexPointer(2, GL_FIXED, 0, mVertices); + Rect r; + while (iterator.iterate(&r)) { + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h new file mode 100644 index 0000000..3e37a47 --- /dev/null +++ b/libs/surfaceflinger/LayerDim.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_DIM_H +#define ANDROID_LAYER_DIM_H + +#include <stdint.h> +#include <sys/types.h> + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerDim : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerDim(SurfaceFlinger* flinger, DisplayID display, + Client* client, int32_t i); + virtual ~LayerDim(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); + +private: + static sp<MemoryDealer> mDimmerDealer; + static LayerBitmap mDimmerBitmap; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_DIM_H diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp new file mode 100644 index 0000000..2b72d7c --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.cpp @@ -0,0 +1,287 @@ +/* + * 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 <utils/Errors.h> +#include <utils/Log.h> + +#include <core/SkBitmap.h> + +#include <ui/EGLDisplaySurface.h> + +#include "LayerBase.h" +#include "LayerOrientationAnim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "OrientationAnimation.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; +const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; + +// --------------------------------------------------------------------------- + +LayerOrientationAnim::LayerOrientationAnim( + SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& bitmap, + const LayerBitmap& bitmapIn) + : LayerBase(flinger, display), mAnim(anim), + mBitmap(bitmap), mBitmapIn(bitmapIn), + mTextureName(-1), mTextureNameIn(-1) +{ + mStartTime = systemTime(); + mFinishTime = 0; + mOrientationCompleted = false; + mFirstRedraw = false; + mLastNormalizedTime = 0; + mLastScale = 0; + mNeedsBlending = false; +} + +LayerOrientationAnim::~LayerOrientationAnim() +{ + if (mTextureName != -1U) { + LayerBase::deletedTextures.add(mTextureName); + } + if (mTextureNameIn != -1U) { + LayerBase::deletedTextures.add(mTextureNameIn); + } +} + +bool LayerOrientationAnim::needsBlending() const +{ + return mNeedsBlending; +} + +Point LayerOrientationAnim::getPhysicalSize() const +{ + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + return Point(hw.getWidth(), hw.getHeight()); +} + +void LayerOrientationAnim::validateVisibility(const Transform&) +{ + const Layer::State& s(drawingState()); + const Transform tr(s.transform); + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); + transparentRegionScreen.clear(); + mTransformed = true; + mCanUseCopyBit = false; + copybit_device_t* copybit = mFlinger->getBlitEngine(); + if (copybit) { + mCanUseCopyBit = true; + } +} + +void LayerOrientationAnim::onOrientationCompleted() +{ + mFinishTime = systemTime(); + mOrientationCompleted = true; + mFirstRedraw = true; + mNeedsBlending = true; + mFlinger->invalidateLayerVisibility(this); +} + +void LayerOrientationAnim::onDraw(const Region& clip) const +{ + // Animation... + const float MIN_SCALE = 0.5f; + const float DURATION = ms2ns(200); + const float BOUNCES_PER_SECOND = 1.618f; + const float BOUNCES_AMPLITUDE = 1.0f/32.0f; + + const nsecs_t now = systemTime(); + float scale, alpha; + + if (mOrientationCompleted) { + if (mFirstRedraw) { + mFirstRedraw = false; + + // make a copy of what's on screen + copybit_image_t image; + mBitmapIn.getBitmapSurface(&image); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.copyBackToImage(image); + + // and erase the screen for this round + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + // FIXME: code below is gross + mNeedsBlending = false; + LayerOrientationAnim* self(const_cast<LayerOrientationAnim*>(this)); + mFlinger->invalidateLayerVisibility(self); + } + + // make sure pick-up where we left off + const float duration = DURATION * mLastNormalizedTime; + const float normalizedTime = (float(now - mFinishTime) / duration); + if (normalizedTime <= 1.0f) { + const float squaredTime = normalizedTime*normalizedTime; + scale = (1.0f - mLastScale)*squaredTime + mLastScale; + alpha = (1.0f - normalizedTime); + alpha *= alpha; + alpha *= alpha; + } else { + mAnim->onAnimationFinished(); + scale = 1.0f; + alpha = 0.0f; + } + } else { + const float normalizedTime = float(now - mStartTime) / DURATION; + if (normalizedTime <= 1.0f) { + mLastNormalizedTime = normalizedTime; + const float squaredTime = normalizedTime*normalizedTime; + scale = (MIN_SCALE-1.0f)*squaredTime + 1.0f; + alpha = 1.0f; + } else { + mLastNormalizedTime = 1.0f; + const float to_seconds = DURATION / seconds(1); + const float phi = BOUNCES_PER_SECOND * + (((normalizedTime - 1.0f) * to_seconds)*M_PI*2); + scale = MIN_SCALE + BOUNCES_AMPLITUDE * (1.0f - cosf(phi)); + alpha = 1.0f; + } + mLastScale = scale; + } + drawScaled(scale, alpha); +} + +void LayerOrientationAnim::drawScaled(float f, float alpha) const +{ + copybit_image_t dst; + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + hw.getDisplaySurface(&dst); + + // clear screen + // TODO: with update on demand, we may be able + // to not erase the screen at all during the animation + if (!mOrientationCompleted) { + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + } + + const int w = dst.w*f; + const int h = dst.h*f; + const int xc = uint32_t(dst.w-w)/2; + const int yc = uint32_t(dst.h-h)/2; + const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; + + copybit_image_t src; + mBitmap.getBitmapSurface(&src); + const copybit_rect_t srect = { 0, 0, src.w, src.h }; + + int err = NO_ERROR; + const int can_use_copybit = canUseCopybit(); + if (can_use_copybit) { + copybit_device_t* copybit = mFlinger->getBlitEngine(); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + if (alpha < 1.0f) { + copybit_image_t srcIn; + mBitmapIn.getBitmapSurface(&srcIn); + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + err = copybit->stretch(copybit, &dst, &srcIn, &drect, &srect, &it); + } + + if (!err && alpha > 0.0f) { + region_iterator it(Region(Rect( drect.l, drect.t, drect.r, drect.b ))); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alpha*255)); + err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + } + LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err)); + } + if (!can_use_copybit || err) { + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.w; + t.height = src.h; + t.stride = src.w; + t.vstride= src.h; + t.format = src.format; + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + + Transform tr; + tr.set(f,0,0,f); + tr.set(xc, yc); + + // FIXME: we should not access mVertices and mDrawingState like that, + // but since we control the animation, we know it's going to work okay. + // eventually we'd need a more formal way of doing things like this. + LayerOrientationAnim& self(const_cast<LayerOrientationAnim&>(*this)); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // Too slow to do this in software + self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; + } + + if (alpha < 1.0f) { + copybit_image_t src; + mBitmapIn.getBitmapSurface(&src); + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureNameIn == -1LU)) { + mTextureNameIn = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureNameIn, t, w, h); + } + self.mDrawingState.alpha = 255; + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } + + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureName, t, w, h); + } + self.mDrawingState.alpha = int(alpha*255); + const Region clip(Rect( drect.l, drect.t, drect.r, drect.b )); + drawWithOpenGL(clip, mTextureName, t); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h new file mode 100644 index 0000000..73676859 --- /dev/null +++ b/libs/surfaceflinger/LayerOrientationAnim.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H +#define ANDROID_LAYER_ORIENTATION_ANIM_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> +#include <utils/Parcel.h> + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- +class OrientationAnimation; + +class LayerOrientationAnim : public LayerBase +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const LayerBitmap& zoomOut, + const LayerBitmap& zoomIn); + virtual ~LayerOrientationAnim(); + + void onOrientationCompleted(); + + virtual void onDraw(const Region& clip) const; + virtual Point getPhysicalSize() const; + virtual void validateVisibility(const Transform& globalTransform); + virtual bool needsBlending() const; + virtual bool isSecure() const { return false; } +private: + void drawScaled(float scale, float alpha) const; + + OrientationAnimation* mAnim; + LayerBitmap mBitmap; + LayerBitmap mBitmapIn; + nsecs_t mStartTime; + nsecs_t mFinishTime; + bool mOrientationCompleted; + mutable bool mFirstRedraw; + mutable float mLastNormalizedTime; + mutable float mLastScale; + mutable GLuint mTextureName; + mutable GLuint mTextureNameIn; + mutable bool mNeedsBlending; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_ORIENTATION_ANIM_H diff --git a/libs/surfaceflinger/MODULE_LICENSE_APACHE2 b/libs/surfaceflinger/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/libs/surfaceflinger/MODULE_LICENSE_APACHE2 diff --git a/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/OrientationAnimation.cpp new file mode 100644 index 0000000..f6f1326 --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.cpp @@ -0,0 +1,155 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> +#include <limits.h> + +#include "LayerOrientationAnim.h" +#include "OrientationAnimation.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#include "DisplayHardware/DisplayHardware.h" + +namespace android { + +// --------------------------------------------------------------------------- + +OrientationAnimation::OrientationAnimation(const sp<SurfaceFlinger>& flinger) + : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE) +{ + // allocate a memory-dealer for this the first time + mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap( + ISurfaceComposer::eHardware); +} + +OrientationAnimation::~OrientationAnimation() +{ +} + +void OrientationAnimation::onOrientationChanged() +{ + if (mState == DONE) + mState = PREPARE; +} + +void OrientationAnimation::onAnimationFinished() +{ + if (mState != DONE) + mState = FINISH; +} + +bool OrientationAnimation::run_impl() +{ + bool skip_frame; + switch (mState) { + default: + case DONE: + skip_frame = done(); + break; + case PREPARE: + skip_frame = prepare(); + break; + case PHASE1: + skip_frame = phase1(); + break; + case PHASE2: + skip_frame = phase2(); + break; + case FINISH: + skip_frame = finished(); + break; + } + return skip_frame; +} + +bool OrientationAnimation::done() +{ + if (mFlinger->isFrozen()) { + // we are not allowed to draw, but pause a bit to make sure + // apps don't end up using the whole CPU, if they depend on + // surfaceflinger for synchronization. + usleep(8333); // 8.3ms ~ 120fps + return true; + } + return false; +} + +bool OrientationAnimation::prepare() +{ + mState = PHASE1; + + const GraphicPlane& plane(mFlinger->graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + + LayerBitmap bitmap; + bitmap.init(mTemporaryDealer); + bitmap.setBits(w, h, 1, hw.getFormat()); + + LayerBitmap bitmapIn; + bitmapIn.init(mTemporaryDealer); + bitmapIn.setBits(w, h, 1, hw.getFormat()); + + copybit_image_t front; + bitmap.getBitmapSurface(&front); + hw.copyFrontToImage(front); + + LayerOrientationAnim* l = new LayerOrientationAnim( + mFlinger.get(), 0, this, bitmap, bitmapIn); + l->initStates(w, h, 0); + l->setLayer(INT_MAX-1); + mFlinger->addLayer(l); + mLayerOrientationAnim = l; + return true; +} + +bool OrientationAnimation::phase1() +{ + if (mFlinger->isFrozen() == false) { + // start phase 2 + mState = PHASE2; + mLayerOrientationAnim->onOrientationCompleted(); + mLayerOrientationAnim->invalidate(); + return true; + + } + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::phase2() +{ + // do the 2nd phase of the animation + mLayerOrientationAnim->invalidate(); + return false; +} + +bool OrientationAnimation::finished() +{ + mState = DONE; + mFlinger->removeLayer(mLayerOrientationAnim); + mLayerOrientationAnim = NULL; + return true; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/OrientationAnimation.h new file mode 100644 index 0000000..ba33fce --- /dev/null +++ b/libs/surfaceflinger/OrientationAnimation.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef ANDROID_ORIENTATION_ANIMATION_H +#define ANDROID_ORIENTATION_ANIMATION_H + +#include <stdint.h> +#include <sys/types.h> + +#include "SurfaceFlinger.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class SurfaceFlinger; +class MemoryDealer; +class LayerOrientationAnim; + +class OrientationAnimation +{ +public: + OrientationAnimation(const sp<SurfaceFlinger>& flinger); + virtual ~OrientationAnimation(); + + void onOrientationChanged(); + void onAnimationFinished(); + inline bool run() { + if (LIKELY(mState == DONE)) + return false; + return run_impl(); + } + +private: + enum { + DONE = 0, + PREPARE, + PHASE1, + PHASE2, + FINISH + }; + + bool run_impl(); + bool done(); + bool prepare(); + bool phase1(); + bool phase2(); + bool finished(); + + sp<SurfaceFlinger> mFlinger; + sp<MemoryDealer> mTemporaryDealer; + LayerOrientationAnim* mLayerOrientationAnim; + int mState; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ORIENTATION_ANIMATION_H diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp new file mode 100644 index 0000000..900282a --- /dev/null +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -0,0 +1,1840 @@ +/* + * 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 <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <utils/IPCThreadState.h> +#include <utils/IServiceManager.h> +#include <utils/MemoryDealer.h> +#include <utils/MemoryBase.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StopWatch.h> + +#include <ui/PixelFormat.h> +#include <ui/DisplayInfo.h> +#include <ui/EGLDisplaySurface.h> + +#include <pixelflinger/pixelflinger.h> +#include <GLES/gl.h> + +#include "clz.h" +#include "CPUGauge.h" +#include "Layer.h" +#include "LayerBlur.h" +#include "LayerBuffer.h" +#include "LayerDim.h" +#include "LayerBitmap.h" +#include "LayerOrientationAnim.h" +#include "OrientationAnimation.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#include "DisplayHardware/DisplayHardware.h" +#include "GPUHardware/GPUHardware.h" + + +#define DISPLAY_COUNT 1 + +namespace android { + +// --------------------------------------------------------------------------- + +void SurfaceFlinger::instantiate() { + defaultServiceManager()->addService( + String16("SurfaceFlinger"), new SurfaceFlinger()); +} + +void SurfaceFlinger::shutdown() { + // we should unregister here, but not really because + // when (if) the service manager goes away, all the services + // it has a reference to will leave too. +} + +// --------------------------------------------------------------------------- + +SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) + : lookup(rhs.lookup), layers(rhs.layers) +{ +} + +ssize_t SurfaceFlinger::LayerVector::indexOf( + LayerBase* key, size_t guess) const +{ + if (guess<size() && lookup.keyAt(guess) == key) + return guess; + const ssize_t i = lookup.indexOfKey(key); + if (i>=0) { + const size_t idx = lookup.valueAt(i); + LOG_ASSERT(layers[idx]==key, + "LayerVector[%p]: layers[%d]=%p, key=%p", + this, int(idx), layers[idx], key); + return idx; + } + return i; +} + +ssize_t SurfaceFlinger::LayerVector::add( + LayerBase* layer, + Vector<LayerBase*>::compar_t cmp) +{ + size_t count = layers.size(); + ssize_t l = 0; + ssize_t h = count-1; + ssize_t mid; + LayerBase* const* a = layers.array(); + while (l <= h) { + mid = l + (h - l)/2; + const int c = cmp(a+mid, &layer); + if (c == 0) { l = mid; break; } + else if (c<0) { l = mid+1; } + else { h = mid-1; } + } + size_t order = l; + while (order<count && !cmp(&layer, a+order)) { + order++; + } + count = lookup.size(); + for (size_t i=0 ; i<count ; i++) { + if (lookup.valueAt(i) >= order) { + lookup.editValueAt(i)++; + } + } + layers.insertAt(layer, order); + lookup.add(layer, order); + return order; +} + +ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) +{ + const ssize_t keyIndex = lookup.indexOfKey(layer); + if (keyIndex >= 0) { + const size_t index = lookup.valueAt(keyIndex); + LOG_ASSERT(layers[index]==layer, + "LayerVector[%p]: layers[%u]=%p, layer=%p", + this, int(index), layers[index], layer); + layers.removeItemsAt(index); + lookup.removeItemsAt(keyIndex); + const size_t count = lookup.size(); + for (size_t i=0 ; i<count ; i++) { + if (lookup.valueAt(i) >= size_t(index)) { + lookup.editValueAt(i)--; + } + } + return index; + } + return NAME_NOT_FOUND; +} + +ssize_t SurfaceFlinger::LayerVector::reorder( + LayerBase* layer, + Vector<LayerBase*>::compar_t cmp) +{ + // XXX: it's a little lame. but oh well... + ssize_t err = remove(layer); + if (err >=0) + err = add(layer, cmp); + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +SurfaceFlinger::SurfaceFlinger() + : BnSurfaceComposer(), Thread(false), + mTransactionFlags(0), + mTransactionCount(0), + mBootTime(systemTime()), + mLastScheduledBroadcast(NULL), + mVisibleRegionsDirty(false), + mDeferReleaseConsole(false), + mFreezeDisplay(false), + mFreezeCount(0), + mDebugRegion(0), + mDebugCpu(0), + mDebugFps(0), + mDebugBackground(0), + mDebugNoBootAnimation(0), + mSyncObject(), + mDeplayedTransactionPending(0), + mConsoleSignals(0), + mSecureFrameBuffer(0) +{ + init(); +} + +void SurfaceFlinger::init() +{ + LOGI("SurfaceFlinger is starting"); + + // debugging stuff... + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.showupdates", value, "0"); + mDebugRegion = atoi(value); + property_get("debug.sf.showcpu", value, "0"); + mDebugCpu = atoi(value); + property_get("debug.sf.showbackground", value, "0"); + mDebugBackground = atoi(value); + property_get("debug.sf.showfps", value, "0"); + mDebugFps = atoi(value); + property_get("debug.sf.nobootanimation", value, "0"); + mDebugNoBootAnimation = atoi(value); + + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugCpu, "showcpu enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); + LOGI_IF(mDebugFps, "showfps enabled"); + LOGI_IF(mDebugNoBootAnimation, "boot animation disabled"); +} + +SurfaceFlinger::~SurfaceFlinger() +{ + glDeleteTextures(1, &mWormholeTexName); + delete mOrientationAnimation; +} + +copybit_device_t* SurfaceFlinger::getBlitEngine() const +{ + return graphicPlane(0).displayHardware().getBlitEngine(); +} + +overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const +{ + return graphicPlane(0).displayHardware().getOverlayEngine(); +} + +sp<IMemory> SurfaceFlinger::getCblk() const +{ + return mServerCblkMemory; +} + +status_t SurfaceFlinger::requestGPU(const sp<IGPUCallback>& callback, + gpu_info_t* gpu) +{ + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + status_t err = mGPU->request(pid, callback, gpu); + return err; +} + +status_t SurfaceFlinger::revokeGPU() +{ + return mGPU->friendlyRevoke(); +} + +sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() +{ + Mutex::Autolock _l(mStateLock); + uint32_t token = mTokens.acquire(); + + Client* client = new Client(token, this); + if ((client == 0) || (client->ctrlblk == 0)) { + mTokens.release(token); + return 0; + } + status_t err = mClientsMap.add(token, client); + if (err < 0) { + delete client; + mTokens.release(token); + return 0; + } + sp<BClient> bclient = + new BClient(this, token, client->controlBlockMemory()); + return bclient; +} + +void SurfaceFlinger::destroyConnection(ClientID cid) +{ + Mutex::Autolock _l(mStateLock); + Client* const client = mClientsMap.valueFor(cid); + if (client) { + // free all the layers this client owns + const Vector<LayerBaseClient*>& layers = client->getLayers(); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + LayerBaseClient* const layer = layers[i]; + removeLayer_l(layer); + } + + // the resources associated with this client will be freed + // during the next transaction, after these surfaces have been + // properly removed from the screen + + // remove this client from our ClientID->Client mapping. + mClientsMap.removeItem(cid); + + // and add it to the list of disconnected clients + mDisconnectedClients.add(client); + + // request a transaction + setTransactionFlags(eTransactionNeeded); + } +} + +const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const +{ + LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy); + const GraphicPlane& plane(mGraphicPlanes[dpy]); + return plane; +} + +GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) +{ + return const_cast<GraphicPlane&>( + const_cast<SurfaceFlinger const *>(this)->graphicPlane(dpy)); +} + +void SurfaceFlinger::bootFinished() +{ + const nsecs_t now = systemTime(); + const nsecs_t duration = now - mBootTime; + LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + if (mBootAnimation != 0) { + mBootAnimation->requestExit(); + mBootAnimation.clear(); + } +} + +void SurfaceFlinger::onFirstRef() +{ + run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY); + + // Wait for the main thread to be done with its initialization + mReadyToRunBarrier.wait(); +} + + +static inline uint16_t pack565(int r, int g, int b) { + return (r<<11)|(g<<5)|b; +} + +// this is defined in libGLES_CM.so +extern ISurfaceComposer* GLES_localSurfaceManager; + +status_t SurfaceFlinger::readyToRun() +{ + LOGI( "SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); + + // create the shared control-block + mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblkMemory = mServerHeap->allocate(4096); + LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); + + mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + new(mServerCblk) surface_flinger_cblk_t; + + // get a reference to the GPU if we have one + mGPU = GPUFactory::getGPU(); + + // create the surface Heap manager, which manages the heaps + // (be it in RAM or VRAM) where surfaces are allocated + // We give 8 MB per client. + mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); + + + GLES_localSurfaceManager = static_cast<ISurfaceComposer*>(this); + + // we only support one display currently + int dpy = 0; + + { + // initialize the main display + GraphicPlane& plane(graphicPlane(dpy)); + DisplayHardware* const hw = new DisplayHardware(this, dpy); + plane.setDisplayHardware(hw); + } + + // initialize primary screen + // (other display should be initialized in the same manner, but + // asynchronously, as they could come and go. None of this is supported + // yet). + const GraphicPlane& plane(graphicPlane(dpy)); + const DisplayHardware& hw = plane.displayHardware(); + const uint32_t w = hw.getWidth(); + const uint32_t h = hw.getHeight(); + const uint32_t f = hw.getFormat(); + hw.makeCurrent(); + + // initialize the shared control block + mServerCblk->connected |= 1<<dpy; + display_cblk_t* dcblk = mServerCblk->displays + dpy; + memset(dcblk, 0, sizeof(display_cblk_t)); + dcblk->w = w; + dcblk->h = h; + dcblk->format = f; + dcblk->orientation = ISurfaceComposer::eOrientationDefault; + dcblk->xdpi = hw.getDpiX(); + dcblk->ydpi = hw.getDpiY(); + dcblk->fps = hw.getRefreshRate(); + dcblk->density = hw.getDensity(); + asm volatile ("":::"memory"); + + // Initialize OpenGL|ES + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glEnableClientState(GL_VERTEX_ARRAY); + glEnable(GL_SCISSOR_TEST); + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + + const uint16_t g0 = pack565(0x0F,0x1F,0x0F); + const uint16_t g1 = pack565(0x17,0x2f,0x17); + const uint16_t textureData[4] = { g0, g1, g1, g0 }; + glGenTextures(1, &mWormholeTexName); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData); + + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, w, h, 0, 0, 1); + + LayerDim::initDimmer(this, w, h); + + mReadyToRunBarrier.open(); + + /* + * We're now ready to accept clients... + */ + + mOrientationAnimation = new OrientationAnimation(this); + + // start CPU gauge display + if (mDebugCpu) + mCpuGauge = new CPUGauge(this, ms2ns(500)); + + // the boot animation! + if (mDebugNoBootAnimation == false) + mBootAnimation = new BootAnimation(this); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Events Handler +#endif + +void SurfaceFlinger::waitForEvent() +{ + // wait for something to do + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + int err = mSyncObject.wait(ms2ns(5000)); + if (err != NO_ERROR) { + if (isFrozen()) { + // we timed out and are still frozen + LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", + mFreezeDisplay, mFreezeCount); + mFreezeCount = 0; + } + } + } else { + mSyncObject.wait(); + } +} + +void SurfaceFlinger::signalEvent() { + mSyncObject.open(); +} + +void SurfaceFlinger::signal() const { + mSyncObject.open(); +} + +void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +{ + if (android_atomic_or(1, &mDeplayedTransactionPending) == 0) { + sp<DelayedTransaction> delayedEvent(new DelayedTransaction(this, delay)); + delayedEvent->run("DelayedeEvent", PRIORITY_URGENT_DISPLAY); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Main loop +#endif + +bool SurfaceFlinger::threadLoop() +{ + waitForEvent(); + + // check for transactions + if (UNLIKELY(mConsoleSignals)) { + handleConsoleEvents(); + } + + if (LIKELY(mTransactionCount == 0)) { + // if we're in a global transaction, don't do anything. + const uint32_t mask = eTransactionNeeded | eTraversalNeeded; + uint32_t transactionFlags = getTransactionFlags(mask); + if (LIKELY(transactionFlags)) { + handleTransaction(transactionFlags); + } + } + + // post surfaces (if needed) + handlePageFlip(); + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + if (LIKELY(hw.canDraw())) { + // repaint the framebuffer (if needed) + handleRepaint(); + + // release the clients before we flip ('cause flip might block) + unlockClients(); + executeScheduledBroadcasts(); + + // sample the cpu gauge + if (UNLIKELY(mDebugCpu)) { + handleDebugCpu(); + } + + postFramebuffer(); + } else { + // pretend we did the post + unlockClients(); + executeScheduledBroadcasts(); + usleep(16667); // 60 fps period + } + return true; +} + +void SurfaceFlinger::postFramebuffer() +{ + const bool skip = mOrientationAnimation->run(); + if (UNLIKELY(skip)) { + return; + } + + if (!mInvalidRegion.isEmpty()) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + + if (UNLIKELY(mDebugFps)) { + debugShowFPS(); + } + + hw.flip(mInvalidRegion); + + mInvalidRegion.clear(); + + if (Layer::deletedTextures.size()) { + glDeleteTextures( + Layer::deletedTextures.size(), + Layer::deletedTextures.array()); + Layer::deletedTextures.clear(); + } + } +} + +void SurfaceFlinger::handleConsoleEvents() +{ + // something to do with the console + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + + int what = android_atomic_and(0, &mConsoleSignals); + if (what & eConsoleAcquired) { + hw.acquireScreen(); + } + + if (mDeferReleaseConsole && hw.canDraw()) { + // We got the release signal before the aquire signal + mDeferReleaseConsole = false; + revokeGPU(); + hw.releaseScreen(); + } + + if (what & eConsoleReleased) { + if (hw.canDraw()) { + revokeGPU(); + hw.releaseScreen(); + } else { + mDeferReleaseConsole = true; + } + } + + mDirtyRegion.set(hw.bounds()); +} + +void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) +{ + Mutex::Autolock _l(mStateLock); + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + + /* + * Traversal of the children + * (perform the transaction for each of them if needed) + */ + + const bool layersNeedTransaction = transactionFlags & eTraversalNeeded; + if (layersNeedTransaction) { + for (size_t i=0 ; i<count ; i++) { + LayerBase* const layer = currentLayers[i]; + uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); + if (!trFlags) continue; + + const uint32_t flags = layer->doTransaction(0); + if (flags & Layer::eVisibleRegion) + mVisibleRegionsDirty = true; + + if (flags & Layer::eRestartTransaction) { + // restart the transaction, but back-off a little + layer->setTransactionFlags(eTransactionNeeded); + setTransactionFlags(eTraversalNeeded, ms2ns(8)); + } + } + } + + /* + * Perform our own transaction if needed + */ + + if (transactionFlags & eTransactionNeeded) { + if (mCurrentState.orientation != mDrawingState.orientation) { + // the orientation has changed, recompute all visible regions + // and invalidate everything. + + const int dpy = 0; + const int orientation = mCurrentState.orientation; + GraphicPlane& plane(graphicPlane(dpy)); + plane.setOrientation(orientation); + + // update the shared control block + const DisplayHardware& hw(plane.displayHardware()); + volatile display_cblk_t* dcblk = mServerCblk->displays + dpy; + dcblk->orientation = orientation; + if (orientation & eOrientationSwapMask) { + // 90 or 270 degrees orientation + dcblk->w = hw.getHeight(); + dcblk->h = hw.getWidth(); + } else { + dcblk->w = hw.getWidth(); + dcblk->h = hw.getHeight(); + } + + mVisibleRegionsDirty = true; + mDirtyRegion.set(hw.bounds()); + + mOrientationAnimation->onOrientationChanged(); + } + + if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { + // freezing or unfreezing the display -> trigger animation if needed + mFreezeDisplay = mCurrentState.freezeDisplay; + const nsecs_t now = systemTime(); + if (mFreezeDisplay) { + mFreezeDisplayTime = now; + } else { + //LOGD("Screen was frozen for %llu us", + // ns2us(now-mFreezeDisplayTime)); + } + } + + // some layers might have been removed, so + // we need to update the regions they're exposing. + size_t c = mRemovedLayers.size(); + if (c) { + mVisibleRegionsDirty = true; + } + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added + mVisibleRegionsDirty = true; + } + + // get rid of all resources we don't need anymore + // (layers and clients) + free_resources_l(); + } + + commitTransaction(); +} + +sp<FreezeLock> SurfaceFlinger::getFreezeLock() const +{ + return new FreezeLock(const_cast<SurfaceFlinger *>(this)); +} + +void SurfaceFlinger::computeVisibleRegions( + LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + + Region aboveOpaqueLayers; + Region aboveCoveredLayers; + Region dirty; + + bool secureFrameBuffer = false; + + size_t i = currentLayers.size(); + while (i--) { + LayerBase* const layer = currentLayers[i]; + layer->validateVisibility(planeTransform); + + // start with the whole surface at its current location + const Layer::State& s = layer->drawingState(); + const Rect bounds(layer->visibleBounds()); + + // handle hidden surfaces by setting the visible region to empty + Region opaqueRegion; + Region visibleRegion; + Region coveredRegion; + if (UNLIKELY((s.flags & ISurfaceComposer::eLayerHidden) || !s.alpha)) { + visibleRegion.clear(); + } else { + const bool translucent = layer->needsBlending(); + visibleRegion.set(bounds); + coveredRegion = visibleRegion; + + // Remove the transparent area from the visible region + if (translucent) { + visibleRegion.subtractSelf(layer->transparentRegionScreen); + } + + // compute the opaque region + if (s.alpha==255 && !translucent && layer->getOrientation()>=0) { + // the opaque region is the visible region + opaqueRegion = visibleRegion; + } + } + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(aboveOpaqueLayers); + coveredRegion.andSelf(aboveCoveredLayers); + + // compute this layer's dirty region + if (layer->contentDirty) { + // we need to invalidate the whole region + dirty = visibleRegion; + // as well, as the old visible region + dirty.orSelf(layer->visibleRegionScreen); + layer->contentDirty = false; + } else { + // compute the exposed region + // dirty = what's visible now - what's wasn't covered before + // = what's visible now & what's was covered before + dirty = visibleRegion.intersect(layer->coveredRegionScreen); + } + dirty.subtractSelf(aboveOpaqueLayers); + + // accumulate to the screen dirty region + dirtyRegion.orSelf(dirty); + + // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer + aboveOpaqueLayers.orSelf(opaqueRegion); + aboveCoveredLayers.orSelf(bounds); + + // Store the visible region is screen space + layer->setVisibleRegion(visibleRegion); + layer->setCoveredRegion(coveredRegion); + + // If a secure layer is partially visible, lockdown the screen! + if (layer->isSecure() && !visibleRegion.isEmpty()) { + secureFrameBuffer = true; + } + } + + mSecureFrameBuffer = secureFrameBuffer; + opaqueRegion = aboveOpaqueLayers; +} + + +void SurfaceFlinger::commitTransaction() +{ + mDrawingState = mCurrentState; + mTransactionCV.signal(); +} + +void SurfaceFlinger::handlePageFlip() +{ + bool visibleRegions = mVisibleRegionsDirty; + LayerVector& currentLayers = const_cast<LayerVector&>(mDrawingState.layersSortedByZ); + visibleRegions |= lockPageFlip(currentLayers); + + const DisplayHardware& hw = graphicPlane(0).displayHardware(); + const Region screenRegion(hw.bounds()); + if (visibleRegions) { + Region opaqueRegion; + computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); + mWormholeRegion = screenRegion.subtract(opaqueRegion); + mVisibleRegionsDirty = false; + } + + unlockPageFlip(currentLayers); + mDirtyRegion.andSelf(screenRegion); +} + +bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) +{ + bool recomputeVisibleRegions = false; + size_t count = currentLayers.size(); + LayerBase* const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + LayerBase* const layer = layers[i]; + layer->lockPageFlip(recomputeVisibleRegions); + } + return recomputeVisibleRegions; +} + +void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + size_t count = currentLayers.size(); + LayerBase* const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + LayerBase* const layer = layers[i]; + layer->unlockPageFlip(planeTransform, mDirtyRegion); + } +} + +void SurfaceFlinger::handleRepaint() +{ + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (UNLIKELY(mDebugRegion)) { + debugFlashRegions(); + } + + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + + uint32_t flags = hw.getFlags(); + if (flags & DisplayHardware::BUFFER_PRESERVED) { + // here we assume DisplayHardware::flip()'s implementation + // performs the copy-back optimization. + } else { + if (flags & DisplayHardware::UPDATE_ON_DEMAND) { + // we need to fully redraw the part that will be updated + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // we need to redraw everything + mDirtyRegion.set(hw.bounds()); + mInvalidRegion = mDirtyRegion; + } + } + + // compose all surfaces + composeSurfaces(mDirtyRegion); + + // clear the dirty regions + mDirtyRegion.clear(); +} + +void SurfaceFlinger::composeSurfaces(const Region& dirty) +{ + if (UNLIKELY(!mWormholeRegion.isEmpty())) { + // should never happen unless the window manager has a bug + // draw something... + drawWormhole(); + } + const SurfaceFlinger& flinger(*this); + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + LayerBase const* const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + LayerBase const * const layer = layers[i]; + const Region& visibleRegion(layer->visibleRegionScreen); + if (!visibleRegion.isEmpty()) { + const Region clip(dirty.intersect(visibleRegion)); + if (!clip.isEmpty()) { + layer->draw(clip); + } + } + } +} + +void SurfaceFlinger::unlockClients() +{ + const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + LayerBase* const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + LayerBase* const layer = layers[i]; + layer->finishPageFlip(); + } +} + +void SurfaceFlinger::scheduleBroadcast(Client* client) +{ + if (mLastScheduledBroadcast != client) { + mLastScheduledBroadcast = client; + mScheduledBroadcasts.add(client); + } +} + +void SurfaceFlinger::executeScheduledBroadcasts() +{ + SortedVector<Client*>& list = mScheduledBroadcasts; + size_t count = list.size(); + while (count--) { + per_client_cblk_t* const cblk = list[count]->ctrlblk; + if (cblk->lock.tryLock() == NO_ERROR) { + cblk->cv.broadcast(); + list.removeAt(count); + cblk->lock.unlock(); + } else { + // schedule another round + LOGW("executeScheduledBroadcasts() skipped, " + "contention on the client. We'll try again later..."); + signalDelayedEvent(ms2ns(4)); + } + } + mLastScheduledBroadcast = 0; +} + +void SurfaceFlinger::handleDebugCpu() +{ + Mutex::Autolock _l(mDebugLock); + if (mCpuGauge != 0) + mCpuGauge->sample(); +} + +void SurfaceFlinger::debugFlashRegions() +{ + if (UNLIKELY(!mDirtyRegion.isRect())) { + // TODO: do this only if we don't have preserving + // swapBuffer. If we don't have update-on-demand, + // redraw everything. + composeSurfaces(Region(mDirtyRegion.bounds())); + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + glColor4x(0x10000, 0, 0x10000, 0x10000); + + Rect r; + Region::iterator iterator(mDirtyRegion); + while (iterator.iterate(&r)) { + GLfloat vertices[][2] = { + { r.left, r.top }, + { r.left, r.bottom }, + { r.right, r.bottom }, + { r.right, r.top } + }; + glVertexPointer(2, GL_FLOAT, 0, vertices); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.flip(mDirtyRegion.merge(mInvalidRegion)); + mInvalidRegion.clear(); + + if (mDebugRegion > 1) + usleep(mDebugRegion * 1000); + + glEnable(GL_SCISSOR_TEST); + //mDirtyRegion.dump("mDirtyRegion"); +} + +void SurfaceFlinger::drawWormhole() const +{ + const Region region(mWormholeRegion.intersect(mDirtyRegion)); + if (region.isEmpty()) + return; + + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t width = hw.getWidth(); + const int32_t height = hw.getHeight(); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + + if (LIKELY(!mDebugBackground)) { + glClearColorx(0,0,0,0); + Rect r; + Region::iterator iterator(region); + while (iterator.iterate(&r)) { + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glClear(GL_COLOR_BUFFER_BIT); + } + } else { + const GLshort vertices[][2] = { { 0, 0 }, { width, 0 }, + { width, height }, { 0, height } }; + const GLshort tcoords[][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + glTexCoordPointer(2, GL_SHORT, 0, tcoords); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1); + Rect r; + Region::iterator iterator(region); + while (iterator.iterate(&r)) { + const GLint sy = height - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +void SurfaceFlinger::debugShowFPS() const +{ + static int mFrameCount; + static int mLastFrameCount = 0; + static nsecs_t mLastFpsTime = 0; + static float mFps = 0; + mFrameCount++; + nsecs_t now = systemTime(); + nsecs_t diff = now - mLastFpsTime; + if (diff > ms2ns(250)) { + mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; + mLastFpsTime = now; + mLastFrameCount = mFrameCount; + } + // XXX: mFPS has the value we want + } + +status_t SurfaceFlinger::addLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + addLayer_l(layer); + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer(LayerBase* layer) +{ + Mutex::Autolock _l(mStateLock); + removeLayer_l(layer); + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::addLayer_l(LayerBase* layer) +{ + ssize_t i = mCurrentState.layersSortedByZ.add( + layer, &LayerBase::compareCurrentStateZ); + LayerBaseClient* lbc = LayerBase::dynamicCast<LayerBaseClient*>(layer); + if (lbc) { + mLayerMap.add(lbc->serverIndex(), lbc); + } + mRemovedLayers.remove(layer); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer_l(LayerBase* layerBase) +{ + ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); + if (index >= 0) { + mRemovedLayers.add(layerBase); + LayerBaseClient* layer = LayerBase::dynamicCast<LayerBaseClient*>(layerBase); + if (layer) { + mLayerMap.removeItem(layer->serverIndex()); + } + return NO_ERROR; + } + // it's possible that we don't find a layer, because it might + // have been destroyed already -- this is not technically an error + // from the user because there is a race between destroySurface, + // destroyclient and destroySurface-from-a-transaction. + return (index == NAME_NOT_FOUND) ? status_t(NO_ERROR) : index; +} + +void SurfaceFlinger::free_resources_l() +{ + // Destroy layers that were removed + destroy_all_removed_layers_l(); + + // free resources associated with disconnected clients + SortedVector<Client*>& scheduledBroadcasts(mScheduledBroadcasts); + Vector<Client*>& disconnectedClients(mDisconnectedClients); + const size_t count = disconnectedClients.size(); + for (size_t i=0 ; i<count ; i++) { + Client* client = disconnectedClients[i]; + // if this client is the scheduled broadcast list, + // remove it from there (and we don't need to signal it + // since it is dead). + int32_t index = scheduledBroadcasts.indexOf(client); + if (index >= 0) { + scheduledBroadcasts.removeItemsAt(index); + } + mTokens.release(client->cid); + delete client; + } + disconnectedClients.clear(); +} + +void SurfaceFlinger::destroy_all_removed_layers_l() +{ + size_t c = mRemovedLayers.size(); + while (c--) { + LayerBase* const removed_layer = mRemovedLayers[c]; + + LOGE_IF(mCurrentState.layersSortedByZ.indexOf(removed_layer) >= 0, + "layer %p removed but still in the current state list", + removed_layer); + + delete removed_layer; + } + mRemovedLayers.clear(); +} + + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) +{ + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +{ + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + if (delay > 0) { + signalDelayedEvent(delay); + } else { + signalEvent(); + } + } + return old; +} + +void SurfaceFlinger::openGlobalTransaction() +{ + android_atomic_inc(&mTransactionCount); +} + +void SurfaceFlinger::closeGlobalTransaction() +{ + if (android_atomic_dec(&mTransactionCount) == 1) { + signalEvent(); + } +} + +status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 1; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fadding) + return NO_ERROR; +} + +status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + mCurrentState.freezeDisplay = 0; + setTransactionFlags(eTransactionNeeded); + + // flags is intended to communicate some sort of animation behavior + // (for instance fadding) + return NO_ERROR; +} + +int SurfaceFlinger::setOrientation(DisplayID dpy, int orientation) +{ + if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) + return BAD_VALUE; + + Mutex::Autolock _l(mStateLock); + if (mCurrentState.orientation != orientation) { + if (uint32_t(orientation)<=eOrientation270 || orientation==42) { + mCurrentState.orientation = orientation; + setTransactionFlags(eTransactionNeeded); + mTransactionCV.wait(mStateLock); + } else { + orientation = BAD_VALUE; + } + } + return orientation; +} + +sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID d, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + LayerBaseClient* layer = 0; + sp<LayerBaseClient::Surface> surfaceHandle; + Mutex::Autolock _l(mStateLock); + Client* const c = mClientsMap.valueFor(clientId); + if (UNLIKELY(!c)) { + LOGE("createSurface() failed, client not found (id=%d)", clientId); + return surfaceHandle; + } + + //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); + int32_t id = c->generateId(pid); + if (uint32_t(id) >= NUM_LAYERS_MAX) { + LOGE("createSurface() failed, generateId = %d", id); + return surfaceHandle; + } + + switch (flags & eFXSurfaceMask) { + case eFXSurfaceNormal: + if (UNLIKELY(flags & ePushBuffers)) { + layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags); + } else { + layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); + } + break; + case eFXSurfaceBlur: + layer = createBlurSurfaceLocked(c, d, id, w, h, flags); + break; + case eFXSurfaceDim: + layer = createDimSurfaceLocked(c, d, id, w, h, flags); + break; + } + + if (layer) { + setTransactionFlags(eTransactionNeeded); + surfaceHandle = layer->getSurface(); + if (surfaceHandle != 0) + surfaceHandle->getSurfaceData(params); + } + + return surfaceHandle; +} + +LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) +{ + // initialize the surfaces + switch (format) { // TODO: take h/w into account + case PIXEL_FORMAT_TRANSPARENT: + case PIXEL_FORMAT_TRANSLUCENT: + format = PIXEL_FORMAT_RGBA_8888; + break; + case PIXEL_FORMAT_OPAQUE: + format = PIXEL_FORMAT_RGB_565; + break; + } + + Layer* layer = new Layer(this, display, client, id); + status_t err = layer->setBuffers(client, w, h, format, flags); + if (LIKELY(err == NO_ERROR)) { + layer->initStates(w, h, flags); + addLayer_l(layer); + } else { + LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); + delete layer; + return 0; + } + return layer; +} + +LayerBaseClient* SurfaceFlinger::createBlurSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBlur* layer = new LayerBlur(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +LayerBaseClient* SurfaceFlinger::createDimSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerDim* layer = new LayerDim(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +LayerBaseClient* SurfaceFlinger::createPushBuffersSurfaceLocked( + Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + LayerBuffer* layer = new LayerBuffer(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +status_t SurfaceFlinger::destroySurface(SurfaceID index) +{ + Mutex::Autolock _l(mStateLock); + LayerBaseClient* const layer = getLayerUser_l(index); + status_t err = removeLayer_l(layer); + if (err < 0) + return err; + setTransactionFlags(eTransactionNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::setClientState( + ClientID cid, + int32_t count, + const layer_state_t* states) +{ + Mutex::Autolock _l(mStateLock); + uint32_t flags = 0; + cid <<= 16; + for (int i=0 ; i<count ; i++) { + const layer_state_t& s = states[i]; + LayerBaseClient* layer = getLayerUser_l(s.surface | cid); + if (layer) { + const uint32_t what = s.what; + // check if it has been destroyed first + if (what & eDestroyed) { + if (removeLayer_l(layer) == NO_ERROR) { + flags |= eTransactionNeeded; + // we skip everything else... well, no, not really + // we skip ONLY that transaction. + continue; + } + } + if (what & ePositionChanged) { + if (layer->setPosition(s.x, s.y)) + flags |= eTraversalNeeded; + } + if (what & eLayerChanged) { + if (layer->setLayer(s.z)) { + mCurrentState.layersSortedByZ.reorder( + layer, &Layer::compareCurrentStateZ); + // we need traversal (state changed) + // AND transaction (list changed) + flags |= eTransactionNeeded|eTraversalNeeded; + } + } + if (what & eSizeChanged) { + if (layer->setSize(s.w, s.h)) + flags |= eTraversalNeeded; + } + if (what & eAlphaChanged) { + if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f))) + flags |= eTraversalNeeded; + } + if (what & eMatrixChanged) { + if (layer->setMatrix(s.matrix)) + flags |= eTraversalNeeded; + } + if (what & eTransparentRegionChanged) { + if (layer->setTransparentRegionHint(s.transparentRegion)) + flags |= eTraversalNeeded; + } + if (what & eVisibilityChanged) { + if (layer->setFlags(s.flags, s.mask)) + flags |= eTraversalNeeded; + } + } + } + if (flags) { + setTransactionFlags(flags); + } + return NO_ERROR; +} + +LayerBaseClient* SurfaceFlinger::getLayerUser_l(SurfaceID s) const +{ + return mLayerMap.valueFor(s); +} + +void SurfaceFlinger::screenReleased(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleReleased, &mConsoleSignals); + signalEvent(); +} + +void SurfaceFlinger::screenAcquired(int dpy) +{ + // this may be called by a signal handler, we can't do too much in here + android_atomic_or(eConsoleAcquired, &mConsoleSignals); + signalEvent(); +} + +status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 1024; + char buffer[SIZE]; + String8 result; + if (checkCallingPermission( + String16("android.permission.DUMP")) == false) + { // not allowed + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump SurfaceFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + } else { + Mutex::Autolock _l(mStateLock); + size_t s = mClientsMap.size(); + char name[64]; + for (size_t i=0 ; i<s ; i++) { + Client* client = mClientsMap.valueAt(i); + sprintf(name, " Client (id=0x%08x)", client->cid); + client->dump(name); + } + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + /*** LayerBase ***/ + LayerBase const * const layer = currentLayers[i]; + const Layer::State& s = layer->drawingState(); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + layer->getTypeID(), layer, + s.z, layer->tx(), layer->ty(), s.w, s.h, + layer->needsBlending(), layer->contentDirty, + s.alpha, s.flags, + s.transform[0], s.transform[1], + s.transform[2], s.transform[3]); + result.append(buffer); + buffer[0] = 0; + /*** LayerBaseClient ***/ + LayerBaseClient* const lbc = + LayerBase::dynamicCast<LayerBaseClient*>((LayerBase*)layer); + if (lbc) { + snprintf(buffer, SIZE, + " " + "id=0x%08x, client=0x%08x, identity=%u\n", + lbc->clientIndex(), lbc->client ? lbc->client->cid : 0, + lbc->getIdentity()); + } + result.append(buffer); + buffer[0] = 0; + /*** Layer ***/ + Layer* const l = LayerBase::dynamicCast<Layer*>((LayerBase*)layer); + if (l) { + const LayerBitmap& buf0(l->getBuffer(0)); + const LayerBitmap& buf1(l->getBuffer(1)); + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u], mTextureName=%d," + " freezeLock=%p, swapState=0x%08x\n", + l->pixelFormat(), + buf0.width(), buf0.height(), buf0.stride(), + buf1.width(), buf1.height(), buf1.stride(), + l->getTextureName(), l->getFreezeLock().get(), + l->lcblk->swapState); + } + result.append(buffer); + buffer[0] = 0; + s.transparentRegion.dump(result, "transparentRegion"); + layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); + layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); + } + mWormholeRegion.dump(result, "WormholeRegion"); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + snprintf(buffer, SIZE, + " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n", + mFreezeDisplay?"yes":"no", mFreezeCount, + mCurrentState.orientation, hw.canDraw()); + result.append(buffer); + + sp<AllocatorInterface> allocator; + if (mGPU != 0) { + snprintf(buffer, SIZE, " GPU owner: %d\n", mGPU->getOwner()); + result.append(buffer); + allocator = mGPU->getAllocator(); + if (allocator != 0) { + allocator->dump(result, "GPU Allocator"); + } + } + allocator = mSurfaceHeapManager->getAllocator(NATIVE_MEMORY_TYPE_PMEM); + if (allocator != 0) { + allocator->dump(result, "PMEM Allocator"); + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t SurfaceFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case CREATE_CONNECTION: + case OPEN_GLOBAL_TRANSACTION: + case CLOSE_GLOBAL_TRANSACTION: + case SET_ORIENTATION: + case FREEZE_DISPLAY: + case UNFREEZE_DISPLAY: + case BOOT_FINISHED: + case REVOKE_GPU: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int self_pid = getpid(); + if (UNLIKELY(pid != self_pid)) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.ACCESS_SURFACE_FLINGER"))) + { + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + } + + status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); + if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { + // HARDWARE_TEST stuff... + if (UNLIKELY(checkCallingPermission( + String16("android.permission.HARDWARE_TEST")) == false)) + { // not allowed + LOGE("Permission Denial: pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + return PERMISSION_DENIED; + } + int n; + switch (code) { + case 1000: // SHOW_CPU + n = data.readInt32(); + mDebugCpu = n ? 1 : 0; + if (mDebugCpu) { + if (mCpuGauge == 0) { + mCpuGauge = new CPUGauge(this, ms2ns(500)); + } + } else { + if (mCpuGauge != 0) { + mCpuGauge->requestExitAndWait(); + Mutex::Autolock _l(mDebugLock); + mCpuGauge.clear(); + } + } + return NO_ERROR; + case 1001: // SHOW_FPS + n = data.readInt32(); + mDebugFps = n ? 1 : 0; + return NO_ERROR; + case 1002: // SHOW_UPDATES + n = data.readInt32(); + mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); + return NO_ERROR; + case 1003: // SHOW_BACKGROUND + n = data.readInt32(); + mDebugBackground = n ? 1 : 0; + return NO_ERROR; + case 1004:{ // repaint everything + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe + signalEvent(); + } + return NO_ERROR; + case 1005: // ask GPU revoke + mGPU->friendlyRevoke(); + return NO_ERROR; + case 1006: // revoke GPU + mGPU->unconditionalRevoke(); + return NO_ERROR; + case 1007: // set mFreezeCount + mFreezeCount = data.readInt32(); + return NO_ERROR; + case 1010: // interrogate. + reply->writeInt32(mDebugCpu); + reply->writeInt32(0); + reply->writeInt32(mDebugRegion); + reply->writeInt32(mDebugBackground); + return NO_ERROR; + case 1013: { + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + reply->writeInt32(hw.getPageFlipCount()); + } + return NO_ERROR; + } + } + return err; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) + : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) +{ + mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(); + const int pgsize = getpagesize(); + const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); + mCblkHeap = new MemoryDealer(cblksize); + mCblkMemory = mCblkHeap->allocate(cblksize); + if (mCblkMemory != 0) { + ctrlblk = static_cast<per_client_cblk_t *>(mCblkMemory->pointer()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) per_client_cblk_t; + } + } +} + +Client::~Client() { + if (ctrlblk) { + const int pgsize = getpagesize(); + ctrlblk->~per_client_cblk_t(); // destroy our shared-structure. + } +} + +const sp<SurfaceHeapManager>& Client::getSurfaceHeapManager() const { + return mFlinger->getSurfaceHeapManager(); +} + +int32_t Client::generateId(int pid) +{ + const uint32_t i = clz( ~mBitmap ); + if (i >= NUM_LAYERS_MAX) { + return NO_MEMORY; + } + mPid = pid; + mInUse.add(uint8_t(i)); + mBitmap |= 1<<(31-i); + return i; +} +status_t Client::bindLayer(LayerBaseClient* layer, int32_t id) +{ + ssize_t idx = mInUse.indexOf(id); + if (idx < 0) + return NAME_NOT_FOUND; + return mLayers.insertAt(layer, idx); +} +void Client::free(int32_t id) +{ + ssize_t idx = mInUse.remove(uint8_t(id)); + if (idx >= 0) { + mBitmap &= ~(1<<(31-id)); + mLayers.removeItemsAt(idx); + } +} + +sp<MemoryDealer> Client::createAllocator(uint32_t flags) +{ + sp<MemoryDealer> allocator; + allocator = getSurfaceHeapManager()->createHeap( + flags, getClientPid(), mSharedHeapAllocator); + return allocator; +} + +bool Client::isValid(int32_t i) const { + return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); +} +const uint8_t* Client::inUseArray() const { + return mInUse.array(); +} +size_t Client::numActiveLayers() const { + return mInUse.size(); +} +LayerBaseClient* Client::getLayerUser(int32_t i) const { + ssize_t idx = mInUse.indexOf(uint8_t(i)); + if (idx<0) return 0; + return mLayers[idx]; +} + +void Client::dump(const char* what) +{ +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemory>& cblk) + : mId(cid), mFlinger(flinger), mCblk(cblk) +{ +} + +BClient::~BClient() { + // destroy all resources attached to this client + mFlinger->destroyConnection(mId); +} + +void BClient::getControlBlocks(sp<IMemory>* ctrl) const { + *ctrl = mCblk; +} + +sp<ISurface> BClient::createSurface( + ISurfaceFlingerClient::surface_data_t* params, int pid, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + return mFlinger->createSurface(mId, pid, params, display, w, h, format, flags); +} + +status_t BClient::destroySurface(SurfaceID sid) +{ + sid |= (mId << 16); // add the client-part to id + return mFlinger->destroySurface(sid); +} + +status_t BClient::setState(int32_t count, const layer_state_t* states) +{ + return mFlinger->setClientState(mId, count, states); +} + +// --------------------------------------------------------------------------- + +GraphicPlane::GraphicPlane() + : mHw(0) +{ +} + +GraphicPlane::~GraphicPlane() { + delete mHw; +} + +bool GraphicPlane::initialized() const { + return mHw ? true : false; +} + +void GraphicPlane::setDisplayHardware(DisplayHardware *hw) { + mHw = hw; +} + +void GraphicPlane::setTransform(const Transform& tr) { + mTransform = tr; + mGlobalTransform = mOrientationTransform * mTransform; +} + +status_t GraphicPlane::orientationToTransfrom( + int orientation, int w, int h, Transform* tr) +{ + float a, b, c, d, x, y; + switch (orientation) { + case ISurfaceComposer::eOrientationDefault: + a=1; b=0; c=0; d=1; x=0; y=0; + break; + case ISurfaceComposer::eOrientation90: + a=0; b=-1; c=1; d=0; x=w; y=0; + break; + case ISurfaceComposer::eOrientation180: + a=-1; b=0; c=0; d=-1; x=w; y=h; + break; + case ISurfaceComposer::eOrientation270: + a=0; b=1; c=-1; d=0; x=0; y=h; + break; + default: + return BAD_VALUE; + } + tr->set(a, b, c, d); + tr->set(x, y); + return NO_ERROR; +} + +status_t GraphicPlane::setOrientation(int orientation) +{ + const DisplayHardware& hw(displayHardware()); + const float w = hw.getWidth(); + const float h = hw.getHeight(); + + if (orientation == ISurfaceComposer::eOrientationDefault) { + // make sure the default orientation is optimal + mOrientationTransform.reset(); + mGlobalTransform = mTransform; + return NO_ERROR; + } + + // If the rotation can be handled in hardware, this is where + // the magic should happen. + if (UNLIKELY(orientation == 42)) { + float a, b, c, d, x, y; + const float r = (3.14159265f / 180.0f) * 42.0f; + const float si = sinf(r); + const float co = cosf(r); + a=co; b=-si; c=si; d=co; + x = si*(h*0.5f) + (1-co)*(w*0.5f); + y =-si*(w*0.5f) + (1-co)*(h*0.5f); + mOrientationTransform.set(a, b, c, d); + mOrientationTransform.set(x, y); + } else { + GraphicPlane::orientationToTransfrom(orientation, w, h, + &mOrientationTransform); + } + + mGlobalTransform = mOrientationTransform * mTransform; + return NO_ERROR; +} + +const DisplayHardware& GraphicPlane::displayHardware() const { + return *mHw; +} + +const Transform& GraphicPlane::transform() const { + return mGlobalTransform; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h new file mode 100644 index 0000000..f7d7764 --- /dev/null +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -0,0 +1,435 @@ +/* + * 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. + */ + +#ifndef ANDROID_SURFACE_FLINGER_H +#define ANDROID_SURFACE_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/SortedVector.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/MemoryDealer.h> + +#include <ui/PixelFormat.h> +#include <ui/ISurfaceComposer.h> +#include <ui/ISurfaceFlingerClient.h> + +#include <private/ui/SharedState.h> +#include <private/ui/LayerState.h> +#include <private/ui/SurfaceFlingerSynchro.h> + +#include "Barrier.h" +#include "BootAnimation.h" +#include "CPUGauge.h" +#include "Layer.h" +#include "Tokenizer.h" + +struct copybit_device_t; +struct overlay_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class BClient; +class DisplayHardware; +class FreezeLock; +class GPUHardwareInterface; +class IGPUCallback; +class Layer; +class LayerBuffer; +class LayerOrientationAnim; +class OrientationAnimation; +class SurfaceHeapManager; + +typedef int32_t ClientID; + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +class Client +{ +public: + Client(ClientID cid, const sp<SurfaceFlinger>& flinger); + ~Client(); + + int32_t generateId(int pid); + void free(int32_t id); + status_t bindLayer(LayerBaseClient* layer, int32_t id); + sp<MemoryDealer> createAllocator(uint32_t memory_type); + + inline bool isValid(int32_t i) const; + inline const uint8_t* inUseArray() const; + inline size_t numActiveLayers() const; + LayerBaseClient* getLayerUser(int32_t i) const; + const Vector<LayerBaseClient*>& getLayers() const { return mLayers; } + const sp<IMemory>& controlBlockMemory() const { return mCblkMemory; } + void dump(const char* what); + const sp<SurfaceHeapManager>& getSurfaceHeapManager() const; + + // pointer to this client's control block + per_client_cblk_t* ctrlblk; + ClientID cid; + + +private: + int getClientPid() const { return mPid; } + + int mPid; + uint32_t mBitmap; + SortedVector<uint8_t> mInUse; + Vector<LayerBaseClient*> mLayers; + sp<MemoryDealer> mCblkHeap; + sp<SurfaceFlinger> mFlinger; + sp<MemoryDealer> mSharedHeapAllocator; + sp<MemoryDealer> mPMemAllocator; + sp<IMemory> mCblkMemory; +}; + +// --------------------------------------------------------------------------- + +class GraphicPlane +{ +public: + static status_t orientationToTransfrom(int orientation, int w, int h, + Transform* tr); + + GraphicPlane(); + ~GraphicPlane(); + + bool initialized() const; + + void setDisplayHardware(DisplayHardware *); + void setTransform(const Transform& tr); + status_t setOrientation(int orientation); + + const DisplayHardware& displayHardware() const; + const Transform& transform() const; +private: + GraphicPlane(const GraphicPlane&); + GraphicPlane operator = (const GraphicPlane&); + + DisplayHardware* mHw; + Transform mTransform; + Transform mOrientationTransform; + Transform mGlobalTransform; +}; + +// --------------------------------------------------------------------------- + +enum { + eTransactionNeeded = 0x01, + eTraversalNeeded = 0x02 +}; + +class SurfaceFlinger : public BnSurfaceComposer, protected Thread +{ +public: + static void instantiate(); + static void shutdown(); + + SurfaceFlinger(); + virtual ~SurfaceFlinger(); + void init(); + + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // ISurfaceComposer interface + virtual sp<ISurfaceFlingerClient> createConnection(); + virtual sp<IMemory> getCblk() const; + virtual void bootFinished(); + virtual void openGlobalTransaction(); + virtual void closeGlobalTransaction(); + virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags); + virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); + virtual int setOrientation(DisplayID dpy, int orientation); + virtual void signal() const; + virtual status_t requestGPU(const sp<IGPUCallback>& callback, + gpu_info_t* gpu); + virtual status_t revokeGPU(); + + void screenReleased(DisplayID dpy); + void screenAcquired(DisplayID dpy); + + const sp<SurfaceHeapManager>& getSurfaceHeapManager() const { + return mSurfaceHeapManager; + } + + const sp<GPUHardwareInterface>& getGPU() const { + return mGPU; + } + + copybit_device_t* getBlitEngine() const; + overlay_control_device_t* getOverlayEngine() const; + + + status_t removeLayer(LayerBase* layer); + status_t addLayer(LayerBase* layer); + status_t invalidateLayerVisibility(LayerBase* layer); + +private: + friend class BClient; + friend class LayerBase; + friend class LayerBuffer; + friend class LayerBaseClient; + friend class Layer; + friend class LayerBlur; + + sp<ISurface> createSurface(ClientID client, int pid, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags); + + LayerBaseClient* createNormalSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); + + LayerBaseClient* createBlurSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + LayerBaseClient* createDimSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + LayerBaseClient* createPushBuffersSurfaceLocked(Client* client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + status_t destroySurface(SurfaceID surface_id); + status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + + + class LayerVector { + public: + inline LayerVector() { } + LayerVector(const LayerVector&); + inline size_t size() const { return layers.size(); } + inline LayerBase*const* array() const { return layers.array(); } + ssize_t add(LayerBase*, Vector<LayerBase*>::compar_t); + ssize_t remove(LayerBase*); + ssize_t reorder(LayerBase*, Vector<LayerBase*>::compar_t); + ssize_t indexOf(LayerBase* key, size_t guess=0) const; + inline LayerBase* operator [] (size_t i) const { return layers[i]; } + private: + KeyedVector<LayerBase*, size_t> lookup; + Vector<LayerBase*> layers; + }; + + struct State { + State() { + orientation = ISurfaceComposer::eOrientationDefault; + freezeDisplay = 0; + } + LayerVector layersSortedByZ; + uint8_t orientation; + uint8_t freezeDisplay; + }; + + class DelayedTransaction : public Thread + { + friend class SurfaceFlinger; + sp<SurfaceFlinger> mFlinger; + nsecs_t mDelay; + public: + DelayedTransaction(const sp<SurfaceFlinger>& flinger, nsecs_t delay) + : Thread(false), mFlinger(flinger), mDelay(delay) { + } + virtual bool threadLoop() { + usleep(mDelay / 1000); + if (android_atomic_and(~1, + &mFlinger->mDeplayedTransactionPending) == 1) { + mFlinger->signalEvent(); + } + return false; + } + }; + + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); + + void waitForEvent(); + void signalEvent(); + void signalDelayedEvent(nsecs_t delay); + + void handleConsoleEvents(); + void handleTransaction(uint32_t transactionFlags); + + void computeVisibleRegions( + LayerVector& currentLayers, + Region& dirtyRegion, + Region& wormholeRegion); + + void handlePageFlip(); + bool lockPageFlip(const LayerVector& currentLayers); + void unlockPageFlip(const LayerVector& currentLayers); + void handleRepaint(); + void handleDebugCpu(); + void scheduleBroadcast(Client* client); + void executeScheduledBroadcasts(); + void postFramebuffer(); + void composeSurfaces(const Region& dirty); + void unlockClients(); + + + void destroyConnection(ClientID cid); + LayerBaseClient* getLayerUser_l(SurfaceID index) const; + status_t addLayer_l(LayerBase* layer); + status_t removeLayer_l(LayerBase* layer); + void destroy_all_removed_layers_l(); + void free_resources_l(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + void commitTransaction(); + + + friend class FreezeLock; + sp<FreezeLock> getFreezeLock() const; + inline void incFreezeCount() { mFreezeCount++; } + inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } + inline bool hasFreezeRequest() const { return mFreezeDisplay; } + inline bool isFrozen() const { + return mFreezeDisplay || mFreezeCount>0; + } + + + void debugFlashRegions(); + void debugShowFPS() const; + void drawWormhole() const; + + // access must be protected by mStateLock + mutable Mutex mStateLock; + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + volatile int32_t mTransactionCount; + Condition mTransactionCV; + + // protected by mStateLock (but we could use another lock) + Tokenizer mTokens; + DefaultKeyedVector<ClientID, Client*> mClientsMap; + DefaultKeyedVector<SurfaceID, LayerBaseClient*> mLayerMap; + GraphicPlane mGraphicPlanes[1]; + SortedVector<LayerBase*> mRemovedLayers; + Vector<Client*> mDisconnectedClients; + + // constant members (no synchronization needed for access) + sp<MemoryDealer> mServerHeap; + sp<IMemory> mServerCblkMemory; + surface_flinger_cblk_t* mServerCblk; + sp<SurfaceHeapManager> mSurfaceHeapManager; + sp<GPUHardwareInterface> mGPU; + GLuint mWormholeTexName; + sp<BootAnimation> mBootAnimation; + nsecs_t mBootTime; + + // Can only accessed from the main thread, these members + // don't need synchronization + Region mDirtyRegion; + Region mInvalidRegion; + Region mWormholeRegion; + Client* mLastScheduledBroadcast; + SortedVector<Client*> mScheduledBroadcasts; + bool mVisibleRegionsDirty; + bool mDeferReleaseConsole; + bool mFreezeDisplay; + int32_t mFreezeCount; + nsecs_t mFreezeDisplayTime; + friend class OrientationAnimation; + OrientationAnimation* mOrientationAnimation; + + // access protected by mDebugLock + mutable Mutex mDebugLock; + sp<CPUGauge> mCpuGauge; + + // don't use a lock for these, we don't care + int mDebugRegion; + int mDebugCpu; + int mDebugFps; + int mDebugBackground; + int mDebugNoBootAnimation; + + // these are thread safe + mutable Barrier mReadyToRunBarrier; + mutable SurfaceFlingerSynchro mSyncObject; + volatile int32_t mDeplayedTransactionPending; + + // atomic variables + enum { + eConsoleReleased = 1, + eConsoleAcquired = 2 + }; + volatile int32_t mConsoleSignals; + + // only written in the main thread, only read in other threads + volatile int32_t mSecureFrameBuffer; +}; + +// --------------------------------------------------------------------------- + +class FreezeLock : public LightRefBase<FreezeLock> { + SurfaceFlinger* mFlinger; +public: + FreezeLock(SurfaceFlinger* flinger) + : mFlinger(flinger) { + mFlinger->incFreezeCount(); + } + ~FreezeLock() { + mFlinger->decFreezeCount(); + } +}; + +// --------------------------------------------------------------------------- + +class BClient : public BnSurfaceFlingerClient +{ +public: + BClient(SurfaceFlinger *flinger, ClientID cid, + const sp<IMemory>& cblk); + ~BClient(); + + // ISurfaceFlingerClient interface + virtual void getControlBlocks(sp<IMemory>* ctrl) const; + + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, + DisplayID display, uint32_t w, uint32_t h,PixelFormat format, + uint32_t flags); + + virtual status_t destroySurface(SurfaceID surfaceId); + virtual status_t setState(int32_t count, const layer_state_t* states); + +private: + ClientID mId; + SurfaceFlinger* mFlinger; + sp<IMemory> mCblk; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_H diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp new file mode 100644 index 0000000..ef51d6a --- /dev/null +++ b/libs/surfaceflinger/Tokenizer.cpp @@ -0,0 +1,172 @@ +/* + * 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 <stdio.h> + +#include "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + printf("Tokenizer (%p, size = %lu)\n", this, c); + for (size_t i=0 ; i<c ; i++) { + printf("%lu: (%u, %u)\n", i, ranges[i].first, ranges[i].length); + } +} + +}; // namespace android + diff --git a/libs/surfaceflinger/Tokenizer.h b/libs/surfaceflinger/Tokenizer.h new file mode 100644 index 0000000..6b3057d --- /dev/null +++ b/libs/surfaceflinger/Tokenizer.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef ANDROID_TOKENIZER_H +#define ANDROID_TOKENIZER_H + +#include <utils/Vector.h> +#include <utils/Errors.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector<run_t> mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_TOKENIZER_H diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp new file mode 100644 index 0000000..bec7a64 --- /dev/null +++ b/libs/surfaceflinger/Transform.cpp @@ -0,0 +1,204 @@ +/* + * 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 <ui/Region.h> + +#include <private/pixelflinger/ggl_fixed.h> + +#include "Transform.h" + +// --------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +Transform::Transform() + : mType(0) +{ + mTransform.reset(); +} + +Transform::Transform(const Transform& other) + : mTransform(other.mTransform), mType(other.mType) +{ +} + +Transform::~Transform() { +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (LIKELY(mType == 0)) + return rhs; + + Transform r(*this); + r.mTransform.preConcat(rhs.mTransform); + r.mType |= rhs.mType; + return r; +} + +float Transform::operator [] (int i) const +{ + float r = 0; + switch(i) { + case 0: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleX] ); break; + case 1: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewX] ); break; + case 2: r = SkScalarToFloat( mTransform[SkMatrix::kMSkewY] ); break; + case 3: r = SkScalarToFloat( mTransform[SkMatrix::kMScaleY] ); break; + } + return r; +} + +uint8_t Transform::type() const +{ + if (UNLIKELY(mType & 0x80000000)) { + mType = mTransform.getType(); + } + return uint8_t(mType & 0xFF); +} + +bool Transform::transformed() const { + return type() > SkMatrix::kTranslate_Mask; +} + +int Transform::tx() const { + return SkScalarRound( mTransform[SkMatrix::kMTransX] ); +} + +int Transform::ty() const { + return SkScalarRound( mTransform[SkMatrix::kMTransY] ); +} + +void Transform::reset() { + mTransform.reset(); + mType = 0; +} + +void Transform::set( float xx, float xy, + float yx, float yy) +{ + mTransform.set(SkMatrix::kMScaleX, SkFloatToScalar(xx)); + mTransform.set(SkMatrix::kMSkewX, SkFloatToScalar(xy)); + mTransform.set(SkMatrix::kMSkewY, SkFloatToScalar(yx)); + mTransform.set(SkMatrix::kMScaleY, SkFloatToScalar(yy)); + mType |= 0x80000000; +} + +void Transform::set(int tx, int ty) +{ + if (tx | ty) { + mTransform.set(SkMatrix::kMTransX, SkIntToScalar(tx)); + mTransform.set(SkMatrix::kMTransY, SkIntToScalar(ty)); + mType |= SkMatrix::kTranslate_Mask; + } else { + mTransform.set(SkMatrix::kMTransX, 0); + mTransform.set(SkMatrix::kMTransY, 0); + mType &= ~SkMatrix::kTranslate_Mask; + } +} + +void Transform::transform(GLfixed* point, int x, int y) const +{ + SkPoint s; + mTransform.mapXY(SkIntToScalar(x), SkIntToScalar(y), &s); + point[0] = SkScalarToFixed(s.fX); + point[1] = SkScalarToFixed(s.fY); +} + +Rect Transform::makeBounds(int w, int h) const +{ + Rect r; + SkRect d, s; + s.set(0, 0, SkIntToScalar(w), SkIntToScalar(h)); + mTransform.mapRect(&d, s); + r.left = SkScalarRound( d.fLeft ); + r.top = SkScalarRound( d.fTop ); + r.right = SkScalarRound( d.fRight ); + r.bottom = SkScalarRound( d.fBottom ); + return r; +} + +Rect Transform::transform(const Rect& bounds) const +{ + Rect r; + SkRect d, s; + s.set( SkIntToScalar( bounds.left ), + SkIntToScalar( bounds.top ), + SkIntToScalar( bounds.right ), + SkIntToScalar( bounds.bottom )); + mTransform.mapRect(&d, s); + r.left = SkScalarRound( d.fLeft ); + r.top = SkScalarRound( d.fTop ); + r.right = SkScalarRound( d.fRight ); + r.bottom = SkScalarRound( d.fBottom ); + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (UNLIKELY(transformed())) { + if (LIKELY(preserveRects())) { + Rect r; + Region::iterator iterator(reg); + while (iterator.iterate(&r)) { + out.orSelf(transform(r)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + out = reg.translate(tx(), ty()); + } + return out; +} + +int32_t Transform::getOrientation() const +{ + uint32_t flags = 0; + if (UNLIKELY(transformed())) { + SkScalar a = mTransform[SkMatrix::kMScaleX]; + SkScalar b = mTransform[SkMatrix::kMSkewX]; + SkScalar c = mTransform[SkMatrix::kMSkewY]; + SkScalar d = mTransform[SkMatrix::kMScaleY]; + if (b==0 && c==0 && a && d) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + } else if (b && c && a==0 && d==0) { + flags |= ROT_90; + if (b>0) flags |= FLIP_H; + if (c<0) flags |= FLIP_V; + } else { + flags = 0x80000000; + } + } + return flags; +} + +bool Transform::preserveRects() const +{ + return mTransform.rectStaysRect(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h new file mode 100644 index 0000000..0b4835e --- /dev/null +++ b/libs/surfaceflinger/Transform.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ANDROID_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include <stdint.h> +#include <sys/types.h> + +#include <ui/Point.h> +#include <ui/Rect.h> + +#include <GLES/gl.h> + +#include <core/SkMatrix.h> + +namespace android { + +class Region; + +// --------------------------------------------------------------------------- + +class Transform +{ +public: + Transform(); + Transform(const Transform& other); + ~Transform(); + + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = 0x00000001, + FLIP_V = 0x00000002, + ROT_90 = 0x00000004, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80000000 + }; + + bool transformed() const; + int32_t getOrientation() const; + bool preserveRects() const; + + int tx() const; + int ty() const; + + void reset(); + void set(float xx, float xy, float yx, float yy); + void set(int tx, int ty); + + Rect makeBounds(int w, int h) const; + void transform(GLfixed* point, int x, int y) const; + Region transform(const Region& reg) const; + Rect transform(const Rect& bounds) const; + + Transform operator * (const Transform& rhs) const; + float operator [] (int i) const; + + inline uint32_t getType() const { return type(); } + + inline Transform(bool) : mType(0xFF) { }; + +private: + uint8_t type() const; + +private: + SkMatrix mTransform; + mutable uint32_t mType; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp new file mode 100644 index 0000000..0ccd71f --- /dev/null +++ b/libs/surfaceflinger/VRamHeap.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2008 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 <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <utils/MemoryDealer.h> +#include <utils/MemoryBase.h> +#include <utils/MemoryHeapPmem.h> +#include <utils/MemoryHeapBase.h> + +#include "GPUHardware/GPUHardware.h" +#include "SurfaceFlinger.h" +#include "VRamHeap.h" + +#if HAVE_ANDROID_OS +#include <linux/android_pmem.h> +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +/* + * Amount of memory we reserve for surface, per client in PMEM + * (PMEM is used for 2D acceleration) + * 8 MB of address space per client should be enough. + */ +static const int PMEM_SIZE = int(8 * 1024 * 1024); + +int SurfaceHeapManager::global_pmem_heap = 0; + +// --------------------------------------------------------------------------- + +SurfaceHeapManager::SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, + size_t clientHeapSize) + : mFlinger(flinger), mClientHeapSize(clientHeapSize) +{ + SurfaceHeapManager::global_pmem_heap = 1; +} + +SurfaceHeapManager::~SurfaceHeapManager() +{ +} + +void SurfaceHeapManager::onFirstRef() +{ + if (global_pmem_heap) { + const char* device = "/dev/pmem"; + mPMemHeap = new PMemHeap(device, PMEM_SIZE); + if (mPMemHeap->base() == MAP_FAILED) { + mPMemHeap.clear(); + global_pmem_heap = 0; + } + } +} + +sp<MemoryDealer> SurfaceHeapManager::createHeap( + uint32_t flags, + pid_t client_pid, + const sp<MemoryDealer>& defaultAllocator) +{ + sp<MemoryDealer> dealer; + + if (flags & ISurfaceComposer::eGPU) { + // don't grant GPU memory if GPU is disabled + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.hw", value, "1"); + if (atoi(value) == 0) { + flags &= ~ISurfaceComposer::eGPU; + } + } + + if (flags & ISurfaceComposer::eGPU) { + // FIXME: this is msm7201A specific, where gpu surfaces may not be secure + if (!(flags & ISurfaceComposer::eSecure)) { + // if GPU doesn't work, we try eHardware + flags |= ISurfaceComposer::eHardware; + // asked for GPU memory, try that first + dealer = mFlinger->getGPU()->request(client_pid); + } + } + + if (dealer == NULL) { + if (defaultAllocator != NULL) + // if a default allocator is given, use that + dealer = defaultAllocator; + } + + if (dealer == NULL) { + // always try h/w accelerated memory first + if (global_pmem_heap) { + const sp<PMemHeap>& heap(mPMemHeap); + if (dealer == NULL && heap != NULL) { + dealer = new MemoryDealer( + heap->createClientHeap(), + heap->getAllocator()); + } + } + } + + if (dealer == NULL) { + // return the ashmem allocator (software rendering) + dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap"); + } + return dealer; +} + +sp<SimpleBestFitAllocator> SurfaceHeapManager::getAllocator(int type) const +{ + Mutex::Autolock _l(mLock); + sp<SimpleBestFitAllocator> allocator; + + // this is only used for debugging + switch (type) { + case NATIVE_MEMORY_TYPE_PMEM: + if (mPMemHeap != 0) { + allocator = mPMemHeap->getAllocator(); + } + break; + } + return allocator; +} + +// --------------------------------------------------------------------------- + +PMemHeap::PMemHeap(const char* const device, size_t size, size_t reserved) + : MemoryHeapBase(device, size) +{ + //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); + if (base() != MAP_FAILED) { + //LOGD("%s, %u bytes", device, virtualSize()); + if (reserved == 0) + reserved = virtualSize(); + mAllocator = new SimpleBestFitAllocator(reserved); + } +} + +PMemHeap::~PMemHeap() { + //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID()); +} + +sp<MemoryHeapPmem> PMemHeap::createClientHeap() { + sp<MemoryHeapBase> parentHeap(this); + return new MemoryHeapPmem(parentHeap); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h new file mode 100644 index 0000000..9140167 --- /dev/null +++ b/libs/surfaceflinger/VRamHeap.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef ANDROID_VRAM_HEAP_H +#define ANDROID_VRAM_HEAP_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/MemoryDealer.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class PMemHeap; +class MemoryHeapPmem; +class SurfaceFlinger; + +// --------------------------------------------------------------------------- + +class SurfaceHeapManager : public RefBase +{ +public: + SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, size_t clientHeapSize); + virtual ~SurfaceHeapManager(); + virtual void onFirstRef(); + /* use ISurfaceComposer flags eGPU|eHArdware|eSecure */ + sp<MemoryDealer> createHeap(uint32_t flags=0, pid_t client_pid = 0, + const sp<MemoryDealer>& defaultAllocator = 0); + + // used for debugging only... + sp<SimpleBestFitAllocator> getAllocator(int type) const; + +private: + sp<PMemHeap> getHeap(int type) const; + + sp<SurfaceFlinger> mFlinger; + mutable Mutex mLock; + size_t mClientHeapSize; + sp<PMemHeap> mPMemHeap; + static int global_pmem_heap; +}; + +// --------------------------------------------------------------------------- + +class PMemHeap : public MemoryHeapBase +{ +public: + PMemHeap(const char* const vram, + size_t size=0, size_t reserved=0); + virtual ~PMemHeap(); + + virtual const sp<SimpleBestFitAllocator>& getAllocator() const { + return mAllocator; + } + virtual sp<MemoryHeapPmem> createClientHeap(); + +private: + sp<SimpleBestFitAllocator> mAllocator; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_VRAM_HEAP_H diff --git a/libs/surfaceflinger/clz.cpp b/libs/surfaceflinger/clz.cpp new file mode 100644 index 0000000..2456b86 --- /dev/null +++ b/libs/surfaceflinger/clz.cpp @@ -0,0 +1,37 @@ +/* + * 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 "clz.h" + +namespace android { + +int clz_impl(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + if (!x) return 32; + int e = 31; + if (x&0xFFFF0000) { e -=16; x >>=16; } + if (x&0x0000FF00) { e -= 8; x >>= 8; } + if (x&0x000000F0) { e -= 4; x >>= 4; } + if (x&0x0000000C) { e -= 2; x >>= 2; } + if (x&0x00000002) { e -= 1; } + return e; +#endif +} + +}; // namespace android diff --git a/libs/surfaceflinger/clz.h b/libs/surfaceflinger/clz.h new file mode 100644 index 0000000..0ddf986 --- /dev/null +++ b/libs/surfaceflinger/clz.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef ANDROID_SURFACE_FLINGER_CLZ_H + +#include <stdint.h> + +namespace android { + +int clz_impl(int32_t x); + +int inline clz(int32_t x) +{ +#if defined(__arm__) && !defined(__thumb__) + return __builtin_clz(x); +#else + return clz_impl(x); +#endif +} + + +}; // namespace android + +#endif /* ANDROID_SURFACE_FLINGER_CLZ_H */ diff --git a/libs/surfaceflinger/tests/Android.mk b/libs/surfaceflinger/tests/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/libs/surfaceflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/libs/surfaceflinger/tests/overlays/Android.mk b/libs/surfaceflinger/tests/overlays/Android.mk new file mode 100644 index 0000000..dc47e45 --- /dev/null +++ b/libs/surfaceflinger/tests/overlays/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + overlays.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-overlays + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp new file mode 100644 index 0000000..f3c046f --- /dev/null +++ b/libs/surfaceflinger/tests/overlays/overlays.cpp @@ -0,0 +1,58 @@ +#include <utils/IPCThreadState.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> + +#include <ui/Surface.h> +#include <ui/ISurface.h> +#include <ui/Overlay.h> +#include <ui/SurfaceComposerClient.h> + +using namespace android; + +namespace android { +class Test { +public: + static const sp<ISurface>& getISurface(const sp<Surface>& s) { + return s->getISurface(); + } +}; +}; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp<Surface> surface = client->createSurface(getpid(), 0, 320, 240, + PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers); + + // get to the isurface + sp<ISurface> isurface = Test::getISurface(surface); + printf("isurface = %p\n", isurface.get()); + + // now request an overlay + sp<OverlayRef> ref = isurface->createOverlay(320, 240, PIXEL_FORMAT_RGB_565); + sp<Overlay> overlay = new Overlay(ref); + + + /* + * here we can use the overlay API + */ + + overlay_buffer_t buffer; + overlay->dequeueBuffer(&buffer); + printf("buffer = %p\n", buffer); + + void* address = overlay->getBufferAddress(buffer); + printf("address = %p\n", address); + + overlay->queueBuffer(buffer); + + return 0; +} |