diff options
author | Mathias Agopian <mathias@google.com> | 2010-07-14 17:59:35 -0700 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2010-07-14 17:59:35 -0700 |
commit | 81bac09fa6b01dd1495644d9c825c3666762fced (patch) | |
tree | 346e826932eef1b4402dfa98796075e63d3c90e8 /services | |
parent | e1ea0811de760256cee3b1ffca227251e1cf52c5 (diff) | |
download | frameworks_native-81bac09fa6b01dd1495644d9c825c3666762fced.zip frameworks_native-81bac09fa6b01dd1495644d9c825c3666762fced.tar.gz frameworks_native-81bac09fa6b01dd1495644d9c825c3666762fced.tar.bz2 |
move native services under services/
moved surfaceflinger, audioflinger, cameraservice
all native services should now reside in this location.
Change-Id: Iee42b83dd2a94c3bf5107ab0895fe2dfcd5337a8
Diffstat (limited to 'services')
36 files changed, 8940 insertions, 0 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk new file mode 100644 index 0000000..a14bfb5 --- /dev/null +++ b/services/surfaceflinger/Android.mk @@ -0,0 +1,52 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + clz.cpp.arm \ + DisplayHardware/DisplayHardware.cpp \ + DisplayHardware/DisplayHardwareBase.cpp \ + BlurFilter.cpp.arm \ + GLExtensions.cpp \ + Layer.cpp \ + LayerBase.cpp \ + LayerBuffer.cpp \ + LayerBlur.cpp \ + LayerDim.cpp \ + MessageQueue.cpp \ + SurfaceFlinger.cpp \ + TextureManager.cpp \ + Transform.cpp + +LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +ifeq ($(TARGET_BOARD_PLATFORM), omap3) + LOCAL_CFLAGS += -DNO_RGBX_8888 +endif + +# need "-lrt" on Linux simulator to pick up clock_gettime +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif +endif + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libpixelflinger \ + libhardware \ + libutils \ + libEGL \ + libGLESv1_CM \ + libbinder \ + libui \ + libsurfaceflinger_client + +LOCAL_C_INCLUDES := \ + $(call include-path-for, corecg graphics) + +LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc + +LOCAL_MODULE:= libsurfaceflinger + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h new file mode 100644 index 0000000..6f8507e --- /dev/null +++ b/services/surfaceflinger/Barrier.h @@ -0,0 +1,55 @@ +/* + * 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() { + 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/services/surfaceflinger/BlurFilter.cpp b/services/surfaceflinger/BlurFilter.cpp new file mode 100644 index 0000000..1ffbd5b --- /dev/null +++ b/services/surfaceflinger/BlurFilter.cpp @@ -0,0 +1,376 @@ +/* +** +** 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; + } +}; + +template <int FACTOR = 0> +struct BlurColor888X +{ + typedef uint32_t type; + int r, g, b; + inline BlurColor888X() { } + inline BlurColor888X(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + r = v & 0xFF; + g = (v >> 8) & 0xFF; + b = (v >> 16) & 0xFF; + } + inline void clear() { r=g=b=0; } + inline uint32_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+G+B)>>2; + R += ((L - R) * FACTOR) >> 8; + G += ((L - G) * FACTOR) >> 8; + B += ((L - B) * FACTOR) >> 8; + } + } + R >>= shift; + G >>= shift; + B >>= shift; + return BLUR_HOST_TO_RGBA((0xFF<<24) | (B<<16) | (G<<8) | R); + } + inline BlurColor888X& operator += (const BlurColor888X& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor888X& operator -= (const BlurColor888X& 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) +{ + status_t err = BAD_VALUE; + if (image->format == GGL_PIXEL_FORMAT_RGB_565) { + err = blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); + } else if (image->format == GGL_PIXEL_FORMAT_RGBX_8888) { + err = blurFilter< BlurColor888X<0x80> >(image, image, kernelSizeUser, repeat); + } + return err; +} + +} // 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/services/surfaceflinger/BlurFilter.h b/services/surfaceflinger/BlurFilter.h new file mode 100644 index 0000000..294db43 --- /dev/null +++ b/services/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/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp new file mode 100644 index 0000000..2eac0a8 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include <cutils/properties.h> + +#include <utils/RefBase.h> +#include <utils/Log.h> + +#include <ui/PixelFormat.h> +#include <ui/FramebufferNativeWindow.h> +#include <ui/EGLUtils.h> + +#include <GLES/gl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pixelflinger/pixelflinger.h> + +#include "DisplayHardware/DisplayHardware.h" + +#include <hardware/copybit.h> +#include <hardware/overlay.h> +#include <hardware/gralloc.h> + +#include "GLExtensions.h" + +using namespace android; + + +static __attribute__((noinline)) +void checkGLErrors() +{ + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) + break; + LOGE("GL error 0x%04x", int(error)); + } while(true); +} + +static __attribute__((noinline)) +void checkEGLErrors(const char* token) +{ + EGLint error = eglGetError(); + if (error && error != EGL_SUCCESS) { + LOGE("%s: EGL error 0x%04x (%s)", + token, int(error), EGLUtils::strerror(error)); + } +} + +/* + * Initialize the display to the specified values. + * + */ + +DisplayHardware::DisplayHardware( + const sp<SurfaceFlinger>& flinger, + uint32_t dpy) + : DisplayHardwareBase(flinger, dpy), + mFlags(0) +{ + 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; } +uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; } +uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; } + +void DisplayHardware::init(uint32_t dpy) +{ + mNativeWindow = new FramebufferNativeWindow(); + framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; + + mOverlayEngine = NULL; + hw_module_t const* module; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } + + EGLint w, h, dummy; + EGLint numConfigs=0; + EGLSurface surface; + EGLContext context; + + // initialize EGL + EGLint attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE, 0, + EGL_NONE + }; + + // debug: disable h/w rendering + char property[PROPERTY_VALUE_MAX]; + if (property_get("debug.sf.hw", property, NULL) > 0) { + if (atoi(property) == 0) { + LOGW("H/W composition disabled"); + attribs[2] = EGL_CONFIG_CAVEAT; + attribs[3] = EGL_SLOW_CONFIG; + } + } + + // 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); + + EGLConfig config; + status_t err = EGLUtils::selectConfigForNativeWindow( + display, attribs, mNativeWindow.get(), &config); + LOGE_IF(err, "couldn't find an EGLConfig matching the screen format"); + + EGLint r,g,b,a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + + if (mNativeWindow->isUpdateOnDemand()) { + mFlags |= PARTIAL_UPDATES; + } + + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { + if (dummy == EGL_SLOW_CONFIG) + mFlags |= SLOW_CONFIG; + } + + /* + * Create our main surface + */ + + surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); + + if (mFlags & PARTIAL_UPDATES) { + // if we have partial updates, we definitely don't need to + // preserve the backbuffer, which may be costly. + eglSurfaceAttrib(display, surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + } + + if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { + if (dummy == EGL_BUFFER_PRESERVED) { + mFlags |= BUFFER_PRESERVED; + } + } + + /* Read density from build-specific ro.sf.lcd_density property + * except if it is overridden by qemu.sf.lcd_density. + */ + if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) { + 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"); + } + } else { + /* for the emulator case, reset the dpi values too */ + mDpiX = mDpiY = atoi(property); + } + mDensity = atoi(property) * (1.0f/160.0f); + + + /* + * Create our OpenGL ES context + */ + + context = eglCreateContext(display, config, NULL, NULL); + + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = fbDev->format; + mPageFlipCount = 0; + + /* + * Gather OpenGL ES extensions + */ + + eglMakeCurrent(display, surface, surface, context); + + GLExtensions& extensions(GLExtensions::getInstance()); + extensions.initWithGLStrings( + glGetString(GL_VENDOR), + glGetString(GL_RENDERER), + glGetString(GL_VERSION), + glGetString(GL_EXTENSIONS), + eglQueryString(display, EGL_VENDOR), + eglQueryString(display, EGL_VERSION), + eglQueryString(display, EGL_EXTENSIONS)); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); + + +#ifdef EGL_ANDROID_swap_rectangle + if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { + if (eglSetSwapRectangleANDROID(display, surface, + 0, 0, mWidth, mHeight) == EGL_TRUE) { + // This could fail if this extension is not supported by this + // specific surface (of config) + mFlags |= SWAP_RECTANGLE; + } + } + // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE + // choose PARTIAL_UPDATES, which should be more efficient + if (mFlags & PARTIAL_UPDATES) + mFlags &= ~SWAP_RECTANGLE; +#endif + + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", extensions.getEglVendor()); + LOGI("version : %s", extensions.getEglVersion()); + LOGI("extensions: %s", extensions.getEglExtension()); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + + LOGI("OpenGL informations:"); + LOGI("vendor : %s", extensions.getVendor()); + LOGI("renderer : %s", extensions.getRenderer()); + LOGI("version : %s", extensions.getVersion()); + LOGI("extensions: %s", extensions.getExtension()); + LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); + LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + LOGI("flags = %08x", mFlags); + + // Unbind the context from this thread + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +/* + * 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); + overlay_control_close(mOverlayEngine); +} + +void DisplayHardware::releaseScreen() const +{ + DisplayHardwareBase::releaseScreen(); +} + +void DisplayHardware::acquireScreen() const +{ + DisplayHardwareBase::acquireScreen(); +} + +uint32_t DisplayHardware::getPageFlipCount() const { + return mPageFlipCount; +} + +status_t DisplayHardware::compositionComplete() const { + return mNativeWindow->compositionComplete(); +} + +void DisplayHardware::flip(const Region& dirty) const +{ + checkGLErrors(); + + EGLDisplay dpy = mDisplay; + EGLSurface surface = mSurface; + +#ifdef EGL_ANDROID_swap_rectangle + if (mFlags & SWAP_RECTANGLE) { + const Region newDirty(dirty.intersect(bounds())); + const Rect b(newDirty.getBounds()); + eglSetSwapRectangleANDROID(dpy, surface, + b.left, b.top, b.width(), b.height()); + } +#endif + + if (mFlags & PARTIAL_UPDATES) { + mNativeWindow->setUpdateRectangle(dirty.getBounds()); + } + + mPageFlipCount++; + 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); +} diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h new file mode 100644 index 0000000..66bf521 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -0,0 +1,116 @@ +/* + * 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 <GLES/gl.h> +#include <GLES/glext.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pixelflinger/pixelflinger.h> + +#include "GLExtensions.h" + +#include "DisplayHardware/DisplayHardwareBase.h" + +struct overlay_control_device_t; +struct framebuffer_device_t; +struct copybit_image_t; + +namespace android { + +class FramebufferNativeWindow; + +class DisplayHardware : public DisplayHardwareBase +{ +public: + enum { + COPY_BITS_EXTENSION = 0x00000008, + BUFFER_PRESERVED = 0x00010000, + PARTIAL_UPDATES = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, + }; + + 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 getMaxTextureSize() const; + uint32_t getMaxViewportDims() const; + + uint32_t getPageFlipCount() const; + EGLDisplay getEGLDisplay() const { return mDisplay; } + overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } + + status_t compositionComplete() 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 uint32_t mPageFlipCount; + GLint mMaxViewportDims; + GLint mMaxTextureSize; + + sp<FramebufferNativeWindow> mNativeWindow; + overlay_control_device_t* mOverlayEngine; +}; + +}; // namespace android + +#endif // ANDROID_DISPLAY_HARDWARE_H diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp new file mode 100644 index 0000000..1d09f84 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -0,0 +1,401 @@ +/* + * 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 <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/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h b/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h new file mode 100644 index 0000000..8369bb8 --- /dev/null +++ b/services/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/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp new file mode 100644 index 0000000..7f4f9fc --- /dev/null +++ b/services/surfaceflinger/GLExtensions.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 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 <stdint.h> + +#include "GLExtensions.h" + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( GLExtensions ) + +GLExtensions::GLExtensions() + : mHaveTextureExternal(false), + mHaveNpot(false), + mHaveDirectTexture(false) +{ +} + +void GLExtensions::initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions) +{ + mVendor = (char const*)vendor; + mRenderer = (char const*)renderer; + mVersion = (char const*)version; + mExtensions = (char const*)extensions; + mEglVendor = egl_vendor; + mEglVersion = egl_version; + mEglExtensions = egl_extensions; + + char const* curr = (char const*)extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + + curr = egl_extensions; + head = curr; + do { + head = strchr(curr, ' '); + String8 s(curr, head ? head-curr : strlen(curr)); + if (s.length()) { + mExtensionList.add(s); + } + curr = head+1; + } while (head); + +#ifdef EGL_ANDROID_image_native_buffer + if (hasExtension("GL_OES_EGL_image") && + (hasExtension("EGL_KHR_image_base") || hasExtension("EGL_KHR_image")) && + hasExtension("EGL_ANDROID_image_native_buffer")) + { + mHaveDirectTexture = true; + } +#else +#warning "EGL_ANDROID_image_native_buffer not supported" +#endif + + if (hasExtension("GL_ARB_texture_non_power_of_two")) { + mHaveNpot = true; + } + + if (hasExtension("GL_OES_texture_external")) { + mHaveTextureExternal = true; + } else if (strstr(mRenderer.string(), "Adreno")) { + // hack for Adreno 200 + mHaveTextureExternal = true; + } +} + +bool GLExtensions::hasExtension(char const* extension) const +{ + const String8 s(extension); + return mExtensionList.indexOf(s) >= 0; +} + +char const* GLExtensions::getVendor() const { + return mVendor.string(); +} + +char const* GLExtensions::getRenderer() const { + return mRenderer.string(); +} + +char const* GLExtensions::getVersion() const { + return mVersion.string(); +} + +char const* GLExtensions::getExtension() const { + return mExtensions.string(); +} + +char const* GLExtensions::getEglVendor() const { + return mEglVendor.string(); +} + +char const* GLExtensions::getEglVersion() const { + return mEglVersion.string(); +} + +char const* GLExtensions::getEglExtension() const { + return mEglExtensions.string(); +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h new file mode 100644 index 0000000..bbb284e --- /dev/null +++ b/services/surfaceflinger/GLExtensions.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 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_SF_GLEXTENSION_H +#define ANDROID_SF_GLEXTENSION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/String8.h> +#include <utils/SortedVector.h> +#include <utils/Singleton.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +namespace android { +// --------------------------------------------------------------------------- + +class GLExtensions : public Singleton<GLExtensions> +{ + friend class Singleton<GLExtensions>; + + bool mHaveTextureExternal : 1; + bool mHaveNpot : 1; + bool mHaveDirectTexture : 1; + + String8 mVendor; + String8 mRenderer; + String8 mVersion; + String8 mExtensions; + String8 mEglVendor; + String8 mEglVersion; + String8 mEglExtensions; + SortedVector<String8> mExtensionList; + + GLExtensions(const GLExtensions&); + GLExtensions& operator = (const GLExtensions&); + +protected: + GLExtensions(); + +public: + inline bool haveTextureExternal() const { + return mHaveTextureExternal; + } + inline bool haveNpot() const { + return mHaveNpot; + } + inline bool haveDirectTexture() const { + return mHaveDirectTexture; + } + + void initWithGLStrings( + GLubyte const* vendor, + GLubyte const* renderer, + GLubyte const* version, + GLubyte const* extensions, + char const* egl_vendor, + char const* egl_version, + char const* egl_extensions); + + char const* getVendor() const; + char const* getRenderer() const; + char const* getVersion() const; + char const* getExtension() const; + + char const* getEglVendor() const; + char const* getEglVersion() const; + char const* getEglExtension() const; + + bool hasExtension(char const* extension) const; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SF_GLEXTENSION_H diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp new file mode 100644 index 0000000..758da4e --- /dev/null +++ b/services/surfaceflinger/Layer.cpp @@ -0,0 +1,875 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/properties.h> +#include <cutils/native_handle.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> + +#include <surfaceflinger/Surface.h> + +#include "clz.h" +#include "GLExtensions.h" +#include "Layer.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + + +#define DEBUG_RESIZE 0 + + +namespace android { + +template <typename T> inline T min(T a, T b) { + return a<b ? a : b; +} + +// --------------------------------------------------------------------------- + +Layer::Layer(SurfaceFlinger* flinger, + DisplayID display, const sp<Client>& client) + : LayerBaseClient(flinger, display, client), + mGLExtensions(GLExtensions::getInstance()), + mNeedsBlending(true), + mNeedsDithering(false), + mSecure(false), + mTextureManager(), + mBufferManager(mTextureManager), + mWidth(0), mHeight(0), mFixedSize(false) +{ +} + +Layer::~Layer() +{ + // FIXME: must be called from the main UI thread + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + + // we can use getUserClientUnsafe here because we know we're + // single-threaded at that point. + sp<UserClient> ourClient(mUserClientRef.getUserClientUnsafe()); + if (ourClient != 0) { + ourClient->detachLayer(this); + } +} + +status_t Layer::setToken(const sp<UserClient>& userClient, + SharedClient* sharedClient, int32_t token) +{ + sp<SharedBufferServer> lcblk = new SharedBufferServer( + sharedClient, token, mBufferManager.getDefaultBufferCount(), + getIdentity()); + + status_t err = mUserClientRef.setToken(userClient, lcblk, token); + + LOGE_IF(err != NO_ERROR, + "ClientRef::setToken(%p, %p, %u) failed", + userClient.get(), lcblk.get(), token); + + if (err == NO_ERROR) { + // we need to free the buffers associated with this surface + } + + return err; +} + +int32_t Layer::getToken() const +{ + return mUserClientRef.getToken(); +} + +sp<UserClient> Layer::getClient() const +{ + return mUserClientRef.getClient(); +} + +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void Layer::onRemoved() +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + // wake up the condition + lcblk->setStatus(NO_INIT); + } +} + +sp<LayerBaseClient::Surface> Layer::createSurface() const +{ + return mSurface; +} + +status_t Layer::ditch() +{ + // NOTE: Called from the main UI thread + + // the layer is not on screen anymore. free as much resources as possible + mFreezeLock.clear(); + + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + mSurface.clear(); + + Mutex::Autolock _l(mLock); + mWidth = mHeight = 0; + return NO_ERROR; +} + +status_t Layer::setBuffers( uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags) +{ + // this surfaces pixel format + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err) return err; + + // the display's pixel format + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + uint32_t const maxSurfaceDims = min( + hw.getMaxTextureSize(), hw.getMaxViewportDims()); + + // never allow a surface larger than what our underlying GL implementation + // can handle. + if ((uint32_t(w)>maxSurfaceDims) || (uint32_t(h)>maxSurfaceDims)) { + return BAD_VALUE; + } + + PixelFormatInfo displayInfo; + getPixelFormatInfo(hw.getFormat(), &displayInfo); + const uint32_t hwFlags = hw.getFlags(); + + mFormat = format; + mReqFormat = format; + mWidth = w; + mHeight = h; + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; + + // we use the red index + int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); + int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); + mNeedsDithering = layerRedsize > displayRedSize; + + mSurface = new SurfaceLayer(mFlinger, this); + return NO_ERROR; +} + +void Layer::reloadTexture(const Region& dirty) +{ + sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); + if (buffer == NULL) { + // this situation can happen if we ran out of memory for instance. + // not much we can do. continue to use whatever texture was bound + // to this context. + return; + } + + if (mGLExtensions.haveDirectTexture()) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { + // not sure what we can do here... + goto slowpath; + } + } else { +slowpath: + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + mBufferManager.loadTexture(dirty, t); + buffer->unlock(); + } + } +} + +void Layer::onDraw(const Region& clip) const +{ + Texture tex(mBufferManager.getActiveTexture()); + if (tex.name == -1LU) { + // the texture has not been created yet, this Layer has + // in fact never been drawn into. This happens frequently with + // SurfaceView because the WindowManager can't know when the client + // has drawn the first time. + + // If there is nothing under us, we paint the screen in black, otherwise + // we just skip this update. + + // figure out if there is something below us + Region under; + const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ); + const size_t count = drawingLayers.size(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer(drawingLayers[i]); + if (layer.get() == static_cast<LayerBase const*>(this)) + break; + under.orSelf(layer->visibleRegionScreen); + } + // if not everything below us is covered, we plug the holes! + Region holes(clip.subtract(under)); + if (!holes.isEmpty()) { + clearWithOpenGL(holes, 0, 0, 0, 1); + } + return; + } + drawWithOpenGL(clip, tex); +} + +bool Layer::needsFiltering() const +{ + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // NOTE: there is a race here, because mFixedSize is updated in a + // binder transaction. however, it doesn't really matter since it is + // evaluated each time we draw. To be perfectly correct, this flag + // would have to be associated with a buffer. + if (mFixedSize) + return true; + } + return LayerBase::needsFiltering(); +} + + +status_t Layer::setBufferCount(int bufferCount) +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { + // oops, the client is already gone + return DEAD_OBJECT; + } + + // NOTE: lcblk->resize() is protected by an internal lock + status_t err = lcblk->resize(bufferCount); + if (err == NO_ERROR) + mBufferManager.resize(bufferCount); + + return err; +} + +sp<GraphicBuffer> Layer::requestBuffer(int index, + uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat, + uint32_t usage) +{ + sp<GraphicBuffer> buffer; + + if (int32_t(reqWidth | reqHeight | reqFormat) < 0) + return buffer; + + if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight)) + return buffer; + + // this ensures our client doesn't go away while we're accessing + // the shared area. + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { + // oops, the client is already gone + return buffer; + } + + /* + * This is called from the client's Surface::dequeue(). This can happen + * at any time, especially while we're in the middle of using the + * buffer 'index' as our front buffer. + * + * Make sure the buffer we're resizing is not the front buffer and has been + * dequeued. Once this condition is asserted, we are guaranteed that this + * buffer cannot become the front buffer under our feet, since we're called + * from Surface::dequeue() + */ + status_t err = lcblk->assertReallocate(index); + LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err)); + if (err != NO_ERROR) { + // the surface may have died + return buffer; + } + + uint32_t w, h, f; + { // scope for the lock + Mutex::Autolock _l(mLock); + const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight); + const bool formatChanged = mReqFormat != reqFormat; + mReqWidth = reqWidth; + mReqHeight = reqHeight; + mReqFormat = reqFormat; + mFixedSize = reqWidth && reqHeight; + w = reqWidth ? reqWidth : mWidth; + h = reqHeight ? reqHeight : mHeight; + f = reqFormat ? reqFormat : mFormat; + buffer = mBufferManager.detachBuffer(index); + if (fixedSizeChanged || formatChanged) { + lcblk->reallocateAllExcept(index); + } + } + + const uint32_t effectiveUsage = getEffectiveUsage(usage); + if (buffer!=0 && buffer->getStrongCount() == 1) { + err = buffer->reallocate(w, h, f, effectiveUsage); + } else { + // here we have to reallocate a new buffer because we could have a + // client in our process with a reference to it (eg: status bar), + // and we can't release the handle under its feet. + buffer.clear(); + buffer = new GraphicBuffer(w, h, f, effectiveUsage); + err = buffer->initCheck(); + } + + if (err || buffer->handle == 0) { + LOGE_IF(err || buffer->handle == 0, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)", + this, index, w, h, strerror(-err)); + } else { + LOGD_IF(DEBUG_RESIZE, + "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d, handle=%p", + this, index, w, h, buffer->handle); + } + + if (err == NO_ERROR && buffer->handle != 0) { + Mutex::Autolock _l(mLock); + mBufferManager.attachBuffer(index, buffer); + } + return buffer; +} + +uint32_t Layer::getEffectiveUsage(uint32_t usage) const +{ + /* + * buffers used for software rendering, but h/w composition + * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE + * + * buffers used for h/w rendering and h/w composition + * are allocated with HW_RENDER | HW_TEXTURE + * + * buffers used with h/w rendering and either NPOT or no egl_image_ext + * are allocated with SW_READ_RARELY | HW_RENDER + * + */ + + if (mSecure) { + // secure buffer, don't store it into the GPU + usage = GraphicBuffer::USAGE_SW_READ_OFTEN | + GraphicBuffer::USAGE_SW_WRITE_OFTEN; + } else { + // it's allowed to modify the usage flags here, but generally + // the requested flags should be honored. + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + return usage; +} + +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + const bool sizeChanged = (front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h); + + if (sizeChanged) { + // the size changed, we need to ask our client to request a new buffer + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), requested (%dx%d), drawing (%d,%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h)); + + if (!isFixedSize()) { + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } + } + + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; + + // record the new size, form this point on, when the client request + // a buffer, it'll get the new size. + setBufferSize(temp.requested_w, temp.requested_h); + + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + // all buffers need reallocation + lcblk->reallocateAll(); + } + } else { + // record the new size + setBufferSize(temp.requested_w, temp.requested_h); + } + } + + 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); +} + +void Layer::setBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + mWidth = w; + mHeight = h; +} + +bool Layer::isFixedSize() const { + Mutex::Autolock _l(mLock); + return mFixedSize; +} + +// ---------------------------------------------------------------------------- +// pageflip handling... +// ---------------------------------------------------------------------------- + +void Layer::lockPageFlip(bool& recomputeVisibleRegions) +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (!lcblk) { + // client died + recomputeVisibleRegions = true; + return; + } + + ssize_t buf = lcblk->retireAndLock(); + if (buf == NOT_ENOUGH_DATA) { + // NOTE: This is not an error, it simply means there is nothing to + // retire. The buffer is locked because we will use it + // for composition later in the loop + return; + } + + if (buf < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); + mPostedDirtyRegion.clear(); + return; + } + + // we retired a buffer, which becomes the new front buffer + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", int(buf)); + mPostedDirtyRegion.clear(); + return; + } + + // get the dirty region + sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); + if (newFrontBuffer != NULL) { + // compute the posted region + const Region dirty(lcblk->getDirtyRegion(buf)); + mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); + + // update the layer size and release freeze-lock + const Layer::State& front(drawingState()); + if (newFrontBuffer->getWidth() == front.requested_w && + newFrontBuffer->getHeight() == front.requested_h) + { + if ((front.w != front.requested_w) || + (front.h != front.requested_h)) + { + // Here we pretend the transaction happened by updating the + // current and drawing states. Drawing state is only accessed + // in this thread, no need to have it locked + Layer::State& editDraw(mDrawingState); + editDraw.w = editDraw.requested_w; + editDraw.h = editDraw.requested_h; + + // We also need to update the current state so that we don't + // end-up doing too much work during the next transaction. + // NOTE: We actually don't need hold the transaction lock here + // because State::w and State::h are only accessed from + // this thread + Layer::State& editTemp(currentState()); + editTemp.w = editDraw.w; + editTemp.h = editDraw.h; + + // recompute visible region + recomputeVisibleRegions = true; + } + + // we now have the correct size, unfreeze the screen + mFreezeLock.clear(); + } + } else { + // this should not happen unless we ran out of memory while + // allocating the buffer. we're hoping that things will get back + // to normal the next time the app tries to draw into this buffer. + // meanwhile, pretend the screen didn't update. + mPostedDirtyRegion.clear(); + } + + if (lcblk->getQueuedCount()) { + // signal an event if we have more buffers waiting + mFlinger->signalEvent(); + } + + /* a buffer was posted, so we need to call reloadTexture(), which + * will update our internal data structures (eg: EGLImageKHR or + * texture names). we need to do this even if mPostedDirtyRegion is + * empty -- it's orthogonal to the fact that a new buffer was posted, + * for instance, a degenerate case could be that the user did an empty + * update but repainted the buffer with appropriate content (after a + * resize for instance). + */ + reloadTexture( mPostedDirtyRegion ); +} + +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); + } + if (visibleRegionScreen.isEmpty()) { + // an invisible layer should not hold a freeze-lock + // (because it may never be updated and therefore never release it) + mFreezeLock.clear(); + } +} + +void Layer::finishPageFlip() +{ + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + if (lcblk) { + int buf = mBufferManager.getActiveBufferIndex(); + if (buf >= 0) { + status_t err = lcblk->unlock( buf ); + LOGE_IF(err!=NO_ERROR, + "layer %p, buffer=%d wasn't locked!", + this, buf); + } + } +} + + +void Layer::dump(String8& result, char* buffer, size_t SIZE) const +{ + LayerBaseClient::dump(result, buffer, SIZE); + + ClientRef::Access sharedClient(mUserClientRef); + SharedBufferServer* lcblk(sharedClient.get()); + uint32_t totalTime = 0; + if (lcblk) { + SharedBufferStack::Statistics stats = lcblk->getStats(); + totalTime= stats.totalTime; + result.append( lcblk->dump(" ") ); + } + + sp<const GraphicBuffer> buf0(getBuffer(0)); + sp<const GraphicBuffer> buf1(getBuffer(1)); + uint32_t w0=0, h0=0, s0=0; + uint32_t w1=0, h1=0, s1=0; + if (buf0 != 0) { + w0 = buf0->getWidth(); + h0 = buf0->getHeight(); + s0 = buf0->getStride(); + } + if (buf1 != 0) { + w1 = buf1->getWidth(); + h1 = buf1->getHeight(); + s1 = buf1->getStride(); + } + snprintf(buffer, SIZE, + " " + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," + " freezeLock=%p, dq-q-time=%u us\n", + mFormat, w0, h0, s0, w1, h1, s1, + getFreezeLock().get(), totalTime); + + result.append(buffer); +} + +// --------------------------------------------------------------------------- + +Layer::ClientRef::ClientRef() + : mControlBlock(0), mToken(-1) { +} + +Layer::ClientRef::~ClientRef() { +} + +int32_t Layer::ClientRef::getToken() const { + Mutex::Autolock _l(mLock); + return mToken; +} + +sp<UserClient> Layer::ClientRef::getClient() const { + Mutex::Autolock _l(mLock); + return mUserClient.promote(); +} + +status_t Layer::ClientRef::setToken(const sp<UserClient>& uc, + const sp<SharedBufferServer>& sharedClient, int32_t token) { + Mutex::Autolock _l(mLock); + + { // scope for strong mUserClient reference + sp<UserClient> userClient(mUserClient.promote()); + if (mUserClient != 0 && mControlBlock != 0) { + mControlBlock->setStatus(NO_INIT); + } + } + + mUserClient = uc; + mToken = token; + mControlBlock = sharedClient; + return NO_ERROR; +} + +sp<UserClient> Layer::ClientRef::getUserClientUnsafe() const { + return mUserClient.promote(); +} + +// this class gives us access to SharedBufferServer safely +// it makes sure the UserClient (and its associated shared memory) +// won't go away while we're accessing it. +Layer::ClientRef::Access::Access(const ClientRef& ref) + : mControlBlock(0) +{ + Mutex::Autolock _l(ref.mLock); + mUserClientStrongRef = ref.mUserClient.promote(); + if (mUserClientStrongRef != 0) + mControlBlock = ref.mControlBlock; +} + +Layer::ClientRef::Access::~Access() +{ +} + +// --------------------------------------------------------------------------- + +Layer::BufferManager::BufferManager(TextureManager& tm) + : mNumBuffers(NUM_BUFFERS), mTextureManager(tm), + mActiveBuffer(-1), mFailover(false) +{ +} + +Layer::BufferManager::~BufferManager() +{ +} + +status_t Layer::BufferManager::resize(size_t size) +{ + Mutex::Autolock _l(mLock); + mNumBuffers = size; + return NO_ERROR; +} + +// only for debugging +sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const { + return mBufferData[index].buffer; +} + +status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { + mActiveBuffer = index; + return NO_ERROR; +} + +size_t Layer::BufferManager::getActiveBufferIndex() const { + return mActiveBuffer; +} + +Texture Layer::BufferManager::getActiveTexture() const { + Texture res; + if (mFailover || mActiveBuffer<0) { + res = mFailoverTexture; + } else { + static_cast<Image&>(res) = mBufferData[mActiveBuffer].texture; + } + return res; +} + +sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const { + sp<GraphicBuffer> result; + const ssize_t activeBuffer = mActiveBuffer; + if (activeBuffer >= 0) { + BufferData const * const buffers = mBufferData; + Mutex::Autolock _l(mLock); + result = buffers[activeBuffer].buffer; + } + return result; +} + +sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index) +{ + BufferData* const buffers = mBufferData; + sp<GraphicBuffer> buffer; + Mutex::Autolock _l(mLock); + buffer = buffers[index].buffer; + buffers[index].buffer = 0; + return buffer; +} + +status_t Layer::BufferManager::attachBuffer(size_t index, + const sp<GraphicBuffer>& buffer) +{ + BufferData* const buffers = mBufferData; + Mutex::Autolock _l(mLock); + buffers[index].buffer = buffer; + buffers[index].texture.dirty = true; + return NO_ERROR; +} + +status_t Layer::BufferManager::destroy(EGLDisplay dpy) +{ + BufferData* const buffers = mBufferData; + size_t num; + { // scope for the lock + Mutex::Autolock _l(mLock); + num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + buffers[i].buffer = 0; + } + } + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&buffers[i].texture, dpy); + } + destroyTexture(&mFailoverTexture, dpy); + return NO_ERROR; +} + +status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_INIT; + ssize_t index = mActiveBuffer; + if (index >= 0) { + if (!mFailover) { + Image& texture(mBufferData[index].texture); + err = mTextureManager.initEglImage(&texture, dpy, buffer); + // if EGLImage fails, we switch to regular texture mode, and we + // free all resources associated with using EGLImages. + if (err == NO_ERROR) { + mFailover = false; + destroyTexture(&mFailoverTexture, dpy); + } else { + mFailover = true; + const size_t num = mNumBuffers; + for (size_t i=0 ; i<num ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + } + } + } else { + // we failed once, don't try again + err = BAD_VALUE; + } + } + return err; +} + +status_t Layer::BufferManager::loadTexture( + const Region& dirty, const GGLSurface& t) +{ + return mTextureManager.loadTexture(&mFailoverTexture, dirty, t); +} + +status_t Layer::BufferManager::destroyTexture(Image* tex, EGLDisplay dpy) +{ + if (tex->name != -1U) { + glDeleteTextures(1, &tex->name); + tex->name = -1U; + } + if (tex->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, tex->image); + tex->image = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, + const sp<Layer>& owner) + : Surface(flinger, owner->getIdentity(), owner) +{ +} + +Layer::SurfaceLayer::~SurfaceLayer() +{ +} + +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) +{ + sp<GraphicBuffer> buffer; + sp<Layer> owner(getOwner()); + if (owner != 0) { + /* + * requestBuffer() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + buffer = owner->requestBuffer(index, w, h, format, usage); + } + return buffer; +} + +status_t Layer::SurfaceLayer::setBufferCount(int bufferCount) +{ + status_t err = DEAD_OBJECT; + sp<Layer> owner(getOwner()); + if (owner != 0) { + /* + * setBufferCount() cannot be called from the main thread + * as it could cause a dead-lock, since it may have to wait + * on conditions updated my the main thread. + */ + err = owner->setBufferCount(bufferCount); + } + return err; +} + +// --------------------------------------------------------------------------- + + +}; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h new file mode 100644 index 0000000..e1d283b --- /dev/null +++ b/services/surfaceflinger/Layer.h @@ -0,0 +1,239 @@ +/* + * 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/GraphicBuffer.h> +#include <ui/PixelFormat.h> +#include <pixelflinger/pixelflinger.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "LayerBase.h" +#include "Transform.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class FreezeLock; +class Client; +class GLExtensions; +class UserClient; + +// --------------------------------------------------------------------------- + +class Layer : public LayerBaseClient +{ +public: + Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); + + virtual ~Layer(); + + virtual const char* getTypeId() const { return "Layer"; } + + // the this layer's size and format + status_t setBuffers(uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); + + // associate a UserClient to this Layer + status_t setToken(const sp<UserClient>& uc, SharedClient* sc, int32_t idx); + int32_t getToken() const; + sp<UserClient> getClient() const; + + // Set this Layer's buffers size + void setBufferSize(uint32_t w, uint32_t h); + bool isFixedSize() const; + + // LayerBase interface + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t transactionFlags); + virtual void lockPageFlip(bool& recomputeVisibleRegions); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + virtual void finishPageFlip(); + virtual bool needsBlending() const { return mNeedsBlending; } + virtual bool needsDithering() const { return mNeedsDithering; } + virtual bool needsFiltering() const; + virtual bool isSecure() const { return mSecure; } + virtual sp<Surface> createSurface() const; + virtual status_t ditch(); + virtual void onRemoved(); + + // only for debugging + inline sp<GraphicBuffer> getBuffer(int i) const { + return mBufferManager.getBuffer(i); } + // only for debugging + inline const sp<FreezeLock>& getFreezeLock() const { + return mFreezeLock; } + +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; + +private: + void reloadTexture(const Region& dirty); + uint32_t getEffectiveUsage(uint32_t usage) const; + sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + status_t setBufferCount(int bufferCount); + + // ----------------------------------------------------------------------- + + class SurfaceLayer : public LayerBaseClient::Surface { + public: + SurfaceLayer(const sp<SurfaceFlinger>& flinger, const sp<Layer>& owner); + ~SurfaceLayer(); + private: + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); + sp<Layer> getOwner() const { + return static_cast<Layer*>(Surface::getOwner().get()); + } + }; + friend class SurfaceLayer; + + // ----------------------------------------------------------------------- + + class ClientRef { + ClientRef(const ClientRef& rhs); + ClientRef& operator = (const ClientRef& rhs); + mutable Mutex mLock; + // binder thread, page-flip thread + sp<SharedBufferServer> mControlBlock; + wp<UserClient> mUserClient; + int32_t mToken; + public: + ClientRef(); + ~ClientRef(); + int32_t getToken() const; + sp<UserClient> getClient() const; + status_t setToken(const sp<UserClient>& uc, + const sp<SharedBufferServer>& sharedClient, int32_t token); + sp<UserClient> getUserClientUnsafe() const; + class Access { + Access(const Access& rhs); + Access& operator = (const Access& rhs); + sp<UserClient> mUserClientStrongRef; + sp<SharedBufferServer> mControlBlock; + public: + Access(const ClientRef& ref); + ~Access(); + inline SharedBufferServer* get() const { return mControlBlock.get(); } + }; + friend class Access; + }; + + // ----------------------------------------------------------------------- + + class BufferManager { + static const size_t NUM_BUFFERS = 2; + struct BufferData { + sp<GraphicBuffer> buffer; + Image texture; + }; + // this lock protect mBufferData[].buffer but since there + // is very little contention, we have only one like for + // the whole array, we also use it to protect mNumBuffers. + mutable Mutex mLock; + BufferData mBufferData[SharedBufferStack::NUM_BUFFER_MAX]; + size_t mNumBuffers; + Texture mFailoverTexture; + TextureManager& mTextureManager; + ssize_t mActiveBuffer; + bool mFailover; + static status_t destroyTexture(Image* tex, EGLDisplay dpy); + + public: + static size_t getDefaultBufferCount() { return NUM_BUFFERS; } + BufferManager(TextureManager& tm); + ~BufferManager(); + + // detach/attach buffer from/to given index + sp<GraphicBuffer> detachBuffer(size_t index); + status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer); + // resize the number of active buffers + status_t resize(size_t size); + + // ---------------------------------------------- + // must be called from GL thread + + // set/get active buffer index + status_t setActiveBufferIndex(size_t index); + size_t getActiveBufferIndex() const; + // return the active buffer + sp<GraphicBuffer> getActiveBuffer() const; + // return the active texture (or fail-over) + Texture getActiveTexture() const; + // frees resources associated with all buffers + status_t destroy(EGLDisplay dpy); + // load bitmap data into the active buffer + status_t loadTexture(const Region& dirty, const GGLSurface& t); + // make active buffer an EGLImage if needed + status_t initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer); + + // ---------------------------------------------- + // only for debugging + sp<GraphicBuffer> getBuffer(size_t index) const; + }; + + // ----------------------------------------------------------------------- + + // thread-safe + ClientRef mUserClientRef; + + // constants + sp<Surface> mSurface; + PixelFormat mFormat; + const GLExtensions& mGLExtensions; + bool mNeedsBlending; + bool mNeedsDithering; + + // page-flip thread (currently main thread) + bool mSecure; + Region mPostedDirtyRegion; + + // page-flip thread and transaction thread (currently main thread) + sp<FreezeLock> mFreezeLock; + + // see threading usage in declaration + TextureManager mTextureManager; + BufferManager mBufferManager; + + // binder thread, transaction thread + mutable Mutex mLock; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mReqWidth; + uint32_t mReqHeight; + uint32_t mReqFormat; + bool mFixedSize; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_H diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp new file mode 100644 index 0000000..d5aa53f --- /dev/null +++ b/services/surfaceflinger/LayerBase.cpp @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "LayerBase.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" + + +namespace android { + +// --------------------------------------------------------------------------- + +LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) + : dpy(display), contentDirty(false), + mFlinger(flinger), + mNeedsFiltering(false), + mOrientation(0), + mLeft(0), mTop(0), + mTransactionFlags(0), + mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), + mInvalidate(0) +{ + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + mFlags = hw.getFlags(); +} + +LayerBase::~LayerBase() +{ +} + +void LayerBase::setName(const String8& name) { + mName = name; +} + +String8 LayerBase::getName() const { + return mName; +} + +const GraphicPlane& LayerBase::graphicPlane(int dpy) const +{ + return mFlinger->graphicPlane(dpy); +} + +GraphicPlane& LayerBase::graphicPlane(int dpy) +{ + return mFlinger->graphicPlane(dpy); +} + +void LayerBase::initStates(uint32_t w, uint32_t h, uint32_t flags) +{ + uint32_t layerFlags = 0; + if (flags & ISurfaceComposer::eHidden) + layerFlags = ISurfaceComposer::eLayerHidden; + + if (flags & ISurfaceComposer::eNonPremultiplied) + mPremultipliedAlpha = false; + + mCurrentState.z = 0; + mCurrentState.w = w; + mCurrentState.h = h; + mCurrentState.requested_w = w; + mCurrentState.requested_h = h; + mCurrentState.alpha = 0xFF; + mCurrentState.flags = layerFlags; + mCurrentState.sequence = 0; + mCurrentState.transform.set(0, 0); + + // drawing state & current state are identical + mDrawingState = mCurrentState; +} + +void LayerBase::commitTransaction() { + mDrawingState = mCurrentState; +} +void LayerBase::forceVisibilityTransaction() { + // this can be called without SurfaceFlinger.mStateLock, but if we + // can atomically increment the sequence number, it doesn't matter. + android_atomic_inc(&mCurrentState.sequence); + requestTransaction(); +} +bool LayerBase::requestTransaction() { + int32_t old = setTransactionFlags(eTransactionNeeded); + return ((old & eTransactionNeeded) == 0); +} +uint32_t LayerBase::getTransactionFlags(uint32_t flags) { + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} +uint32_t LayerBase::setTransactionFlags(uint32_t flags) { + return android_atomic_or(flags, &mTransactionFlags); +} + +bool LayerBase::setPosition(int32_t x, int32_t y) { + if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) + return false; + mCurrentState.sequence++; + mCurrentState.transform.set(x, y); + requestTransaction(); + return true; +} +bool LayerBase::setLayer(uint32_t z) { + if (mCurrentState.z == z) + return false; + mCurrentState.sequence++; + mCurrentState.z = z; + requestTransaction(); + return true; +} +bool LayerBase::setSize(uint32_t w, uint32_t h) { + if (mCurrentState.requested_w == w && mCurrentState.requested_h == h) + return false; + mCurrentState.requested_w = w; + mCurrentState.requested_h = h; + requestTransaction(); + return true; +} +bool LayerBase::setAlpha(uint8_t alpha) { + if (mCurrentState.alpha == alpha) + return false; + mCurrentState.sequence++; + mCurrentState.alpha = alpha; + requestTransaction(); + return true; +} +bool LayerBase::setMatrix(const layer_state_t::matrix22_t& matrix) { + mCurrentState.sequence++; + mCurrentState.transform.set( + matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); + requestTransaction(); + return true; +} +bool LayerBase::setTransparentRegionHint(const Region& transparent) { + mCurrentState.sequence++; + mCurrentState.transparentRegion = transparent; + requestTransaction(); + return true; +} +bool LayerBase::setFlags(uint8_t flags, uint8_t mask) { + const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); + if (mCurrentState.flags == newFlags) + return false; + mCurrentState.sequence++; + mCurrentState.flags = newFlags; + requestTransaction(); + return true; +} + +Rect LayerBase::visibleBounds() const +{ + return mTransformedBounds; +} + +void LayerBase::setVisibleRegion(const Region& visibleRegion) { + // always called from main thread + visibleRegionScreen = visibleRegion; +} + +void LayerBase::setCoveredRegion(const Region& coveredRegion) { + // always called from main thread + coveredRegionScreen = coveredRegion; +} + +uint32_t LayerBase::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if ((front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h)) { + // resize the layer, set the physical size to the requested size + Layer::State& editTemp(currentState()); + editTemp.w = temp.requested_w; + editTemp.h = temp.requested_h; + } + + if ((front.w != temp.w) || (front.h != temp.h)) { + // invalidate and recompute the visible regions if needed + flags |= Layer::eVisibleRegion; + } + + if (temp.sequence != front.sequence) { + // invalidate and recompute the visible regions if needed + flags |= eVisibleRegion; + this->contentDirty = true; + + mNeedsFiltering = false; + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // we may use linear filtering, if the matrix scales us + const uint8_t type = temp.transform.getType(); + if (!temp.transform.preserveRects() || (type >= Transform::SCALE)) { + mNeedsFiltering = true; + } + } + } + + // Commit the transaction + commitTransaction(); + return flags; +} + +void LayerBase::validateVisibility(const Transform& planeTransform) +{ + const Layer::State& s(drawingState()); + const Transform tr(planeTransform * s.transform); + const bool transformed = tr.transformed(); + + uint32_t w = s.w; + uint32_t h = s.h; + tr.transform(mVertices[0], 0, 0); + tr.transform(mVertices[1], 0, h); + tr.transform(mVertices[2], w, h); + tr.transform(mVertices[3], w, 0); + if (UNLIKELY(transformed)) { + // NOTE: here we could also punt if we have too many rectangles + // in the transparent region + if (tr.preserveRects()) { + // transform the transparent region + transparentRegionScreen = tr.transform(s.transparentRegion); + } else { + // transformation too complex, can't do the transparent region + // optimization. + transparentRegionScreen.clear(); + } + } else { + transparentRegionScreen = s.transparentRegion; + } + + // cache a few things... + mOrientation = tr.getOrientation(); + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); +} + +void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) +{ +} + +void LayerBase::unlockPageFlip( + const Transform& planeTransform, Region& outDirtyRegion) +{ + if ((android_atomic_and(~1, &mInvalidate)&1) == 1) { + outDirtyRegion.orSelf(visibleRegionScreen); + } +} + +void LayerBase::finishPageFlip() +{ +} + +void LayerBase::invalidate() +{ + if ((android_atomic_or(1, &mInvalidate)&1) == 0) { + mFlinger->signalEvent(); + } +} + +void LayerBase::drawRegion(const Region& reg) const +{ + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + if (it != end) { + Rect r; + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const int32_t fbWidth = hw.getWidth(); + const int32_t fbHeight = hw.getHeight(); + const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, + { fbWidth, fbHeight }, { 0, fbHeight } }; + glVertexPointer(2, GL_SHORT, 0, vertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } +} + +void LayerBase::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); +} + +void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, + GLclampf green, GLclampf blue, + GLclampf alpha) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + glColor4f(red,green,blue,alpha); + + TextureManager::deactivateTextures(); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + glEnable(GL_SCISSOR_TEST); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } +} + +void LayerBase::clearWithOpenGL(const Region& clip) const +{ + clearWithOpenGL(clip,0,0,0,0); +} + +void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + const State& s(drawingState()); + + // bind our texture + TextureManager::activateTexture(texture, needsFiltering()); + uint32_t width = texture.width; + uint32_t height = texture.height; + + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + if (UNLIKELY(s.alpha < 0xFF)) { + const GLfloat alpha = s.alpha * (1.0f/255.0f); + if (mPremultipliedAlpha) { + glColor4f(alpha, alpha, alpha, alpha); + } else { + glColor4f(1, 1, 1, alpha); + } + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } else { + glColor4f(1, 1, 1, 1); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + if (needsBlending()) { + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + } + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + const GLfloat texCoords[4][2] = { + { 0, 0 }, + { 0, 1 }, + { 1, 1 }, + { 1, 0 } + }; + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + // the texture's source is rotated + switch (texture.transform) { + case HAL_TRANSFORM_ROT_90: + glTranslatef(0, 1, 0); + glRotatef(-90, 0, 0, 1); + break; + case HAL_TRANSFORM_ROT_180: + glTranslatef(1, 1, 0); + glRotatef(-180, 0, 0, 1); + break; + case HAL_TRANSFORM_ROT_270: + glTranslatef(1, 0, 0); + glRotatef(-270, 0, 0, 1); + break; + } + + if (texture.NPOTAdjust) { + glScalef(texture.wScale, texture.hScale, 1.0f); + } + + if (needsDithering()) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const +{ + const Layer::State& s(drawingState()); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + getTypeId(), this, s.z, tx(), ty(), s.w, s.h, + needsBlending(), needsDithering(), contentDirty, + s.alpha, s.flags, + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); + result.append(buffer); +} + +// --------------------------------------------------------------------------- + +int32_t LayerBaseClient::sIdentity = 1; + +LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client) + : LayerBase(flinger, display), mClientRef(client), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))) +{ +} + +LayerBaseClient::~LayerBaseClient() +{ + sp<Client> c(mClientRef.promote()); + if (c != 0) { + c->detachLayer(this); + } +} + +sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() +{ + sp<Surface> s; + Mutex::Autolock _l(mLock); + s = mClientSurface.promote(); + if (s == 0) { + s = createSurface(); + mClientSurface = s; + } + return s; +} + +sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const +{ + return new Surface(mFlinger, mIdentity, + const_cast<LayerBaseClient *>(this)); +} + +void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const +{ + LayerBase::dump(result, buffer, SIZE); + + sp<Client> client(mClientRef.promote()); + snprintf(buffer, SIZE, + " name=%s\n" + " client=%p, identity=%u\n", + getName().string(), + client.get(), getIdentity()); + + result.append(buffer); +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::Surface::Surface( + const sp<SurfaceFlinger>& flinger, + int identity, + const sp<LayerBaseClient>& owner) + : mFlinger(flinger), mIdentity(identity), mOwner(owner) +{ +} + +LayerBaseClient::Surface::~Surface() +{ + /* + * This is a good place to clean-up all client resources + */ + + // destroy client resources + sp<LayerBaseClient> layer = getOwner(); + if (layer != 0) { + mFlinger->destroySurface(layer); + } +} + +sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const { + sp<LayerBaseClient> owner(mOwner.promote()); + return owner; +} + +status_t LayerBaseClient::Surface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + return BnSurface::onTransact(code, data, reply, flags); +} + +sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) +{ + return NULL; +} + +status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) +{ + return INVALID_OPERATION; +} + +status_t LayerBaseClient::Surface::registerBuffers( + const ISurface::BufferHeap& buffers) +{ + return INVALID_OPERATION; +} + +void LayerBaseClient::Surface::postBuffer(ssize_t offset) +{ +} + +void LayerBaseClient::Surface::unregisterBuffers() +{ +} + +sp<OverlayRef> LayerBaseClient::Surface::createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation) +{ + return NULL; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h new file mode 100644 index 0000000..4288cf7 --- /dev/null +++ b/services/surfaceflinger/LayerBase.h @@ -0,0 +1,333 @@ +/* + * 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 <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <utils/RefBase.h> + +#include <ui/Region.h> +#include <ui/Overlay.h> + +#include <surfaceflinger/ISurfaceComposerClient.h> +#include <private/surfaceflinger/SharedBufferStack.h> +#include <private/surfaceflinger/LayerState.h> + +#include <pixelflinger/pixelflinger.h> + +#include "Transform.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class DisplayHardware; +class Client; +class GraphicBuffer; +class GraphicPlane; +class LayerBaseClient; +class SurfaceFlinger; +class Texture; + +// --------------------------------------------------------------------------- + +class LayerBase : public RefBase +{ +public: + LayerBase(SurfaceFlinger* flinger, DisplayID display); + + DisplayID dpy; + mutable bool contentDirty; + Region visibleRegionScreen; + Region transparentRegionScreen; + Region coveredRegionScreen; + + struct State { + uint32_t w; + uint32_t h; + uint32_t requested_w; + uint32_t requested_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; + }; + + void setName(const String8& name); + String8 getName() const; + + // 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 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(); + + virtual sp<LayerBaseClient> getLayerBaseClient() const { return 0; } + + virtual const char* getTypeId() const { return "LayerBase"; } + + /** + * 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); + + /** + * 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 corresponds to any area of the surface that is covered + * (transparently or not) by another surface. + */ + virtual void setCoveredRegion(const Region& coveredRegion); + + /** + * 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; } + + /** + * needsDithering - true if this surface needs dithering + */ + virtual bool needsDithering() const { return false; } + + /** + * needsLinearFiltering - true if this surface needs filtering + */ + virtual bool needsFiltering() const { return mNeedsFiltering; } + + /** + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or VNC servers. + */ + virtual bool isSecure() const { return false; } + + /** Called from the main thread, when the surface is removed from the + * draw list */ + virtual status_t ditch() { return NO_ERROR; } + + /** called with the state lock when the surface is removed from the + * current list */ + virtual void onRemoved() { }; + + /** always call base class first */ + virtual void dump(String8& result, char* scratch, size_t size) const; + + + enum { // flags for doTransaction() + eVisibleRegion = 0x00000002, + }; + + + inline const State& drawingState() const { return mDrawingState; } + inline const State& currentState() const { return mCurrentState; } + inline State& currentState() { return mCurrentState; } + + static int compareCurrentStateZ( + sp<LayerBase> const * layerA, + sp<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); + + void clearWithOpenGL(const Region& clip, GLclampf r, GLclampf g, + GLclampf b, GLclampf alpha) const; + void clearWithOpenGL(const Region& clip) const; + void drawWithOpenGL(const Region& clip, const Texture& texture) const; + + sp<SurfaceFlinger> mFlinger; + uint32_t mFlags; + + // cached during validateVisibility() + bool mNeedsFiltering; + int32_t mOrientation; + GLfloat mVertices[4][2]; + Rect mTransformedBounds; + 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; + String8 mName; + mutable bool mDebug; + + + // atomic + volatile int32_t mInvalidate; + + +protected: + virtual ~LayerBase(); + +private: + LayerBase(const LayerBase& rhs); +}; + + +// --------------------------------------------------------------------------- + +class LayerBaseClient : public LayerBase +{ +public: + class Surface; + + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); + virtual ~LayerBaseClient(); + + sp<Surface> getSurface(); + virtual sp<Surface> createSurface() const; + virtual sp<LayerBaseClient> getLayerBaseClient() const { + return const_cast<LayerBaseClient*>(this); } + virtual const char* getTypeId() const { return "LayerBaseClient"; } + + uint32_t getIdentity() const { return mIdentity; } + + class Surface : public BnSurface { + public: + int32_t getIdentity() const { return mIdentity; } + + protected: + Surface(const sp<SurfaceFlinger>& flinger, int identity, + const sp<LayerBaseClient>& owner); + virtual ~Surface(); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + sp<LayerBaseClient> getOwner() const; + + private: + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage); + virtual status_t setBufferCount(int bufferCount); + + 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, int32_t orientation); + + protected: + friend class LayerBaseClient; + sp<SurfaceFlinger> mFlinger; + int32_t mIdentity; + wp<LayerBaseClient> mOwner; + }; + + friend class Surface; + +protected: + virtual void dump(String8& result, char* scratch, size_t size) const; + +private: + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + const wp<Client> mClientRef; + // only read + const uint32_t mIdentity; + static int32_t sIdentity; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BASE_H diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp new file mode 100644 index 0000000..64a43c7 --- /dev/null +++ b/services/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "clz.h" +#include "BlurFilter.h" +#include "LayerBlur.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client) + : LayerBaseClient(flinger, display, client), mCacheDirty(true), + mRefreshCache(true), mCacheAge(0), mTextureName(-1U), + mWidthScale(1.0f), mHeightScale(1.0f), + mBlurFormat(GGL_PIXEL_FORMAT_RGB_565) +{ +} + +LayerBlur::~LayerBlur() +{ + if (mTextureName != -1U) { + glDeleteTextures(1, &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->postMessageAsync( + new MessageBase(MessageQueue::INVALIDATE), + 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); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &mReadFormat); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &mReadType); + if (mReadFormat != GL_RGB || mReadType != GL_UNSIGNED_SHORT_5_6_5) { + mReadFormat = GL_RGBA; + mReadType = GL_UNSIGNED_BYTE; + mBlurFormat = GGL_PIXEL_FORMAT_RGBX_8888; + } + } + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mTextureName); + + if (mRefreshCache) { + mRefreshCache = false; + mAutoRefreshPending = false; + + int32_t pixelSize = 4; + int32_t s = w; + if (mReadType == GL_UNSIGNED_SHORT_5_6_5) { + // allocate enough memory for 4-bytes (2 pixels) aligned data + s = (w + 1) & ~1; + pixelSize = 2; + } + + uint16_t* const pixels = (uint16_t*)malloc(s*h*pixelSize); + + // 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, mReadFormat, mReadType, pixels); + + // blur that texture. + GGLSurface bl; + bl.version = sizeof(GGLSurface); + bl.width = w; + bl.height = h; + bl.stride = s; + bl.format = mBlurFormat; + bl.data = (GGLubyte*)pixels; + blurFilter(&bl, 8, 2); + + if (GLExtensions::getInstance().haveNpot()) { + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0, + mReadFormat, mReadType, pixels); + mWidthScale = 1.0f / w; + mHeightScale =-1.0f / h; + mYOffset = 0; + } else { + GLuint tw = 1 << (31 - clz(w)); + GLuint th = 1 << (31 - clz(h)); + if (tw < GLuint(w)) tw <<= 1; + if (th < GLuint(h)) th <<= 1; + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, tw, th, 0, + mReadFormat, mReadType, NULL); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, + mReadFormat, mReadType, pixels); + mWidthScale = 1.0f / tw; + mHeightScale =-1.0f / th; + mYOffset = th-h; + } + + free((void*)pixels); + } + + const State& s = drawingState(); + if (UNLIKELY(s.alpha < 0xFF)) { + const GLfloat alpha = s.alpha * (1.0f/255.0f); + glColor4f(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); + } + + if (mFlags & DisplayHardware::SLOW_CONFIG) { + glDisable(GL_DITHER); + } else { + glEnable(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); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(mWidthScale, mHeightScale, 1); + glTranslatef(-x, mYOffset - y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + glTexCoordPointer(2, GL_FLOAT, 0, mVertices); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h new file mode 100644 index 0000000..4c9ec64 --- /dev/null +++ b/services/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 <ui/Region.h> + +#include "LayerBase.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class LayerBlur : public LayerBaseClient +{ +public: + LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); + virtual ~LayerBlur(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerBlur"; } + + 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; + mutable GLfloat mWidthScale; + mutable GLfloat mHeightScale; + mutable GLfloat mYOffset; + mutable GLint mReadFormat; + mutable GLint mReadType; + mutable uint32_t mBlurFormat; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BLUR_H diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp new file mode 100644 index 0000000..5f83636 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <math.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBuffer.h> +#include <ui/PixelFormat.h> +#include <ui/FramebufferNativeWindow.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +#include <hardware/copybit.h> + +#include "LayerBuffer.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { + +// --------------------------------------------------------------------------- + +gralloc_module_t const* LayerBuffer::sGrallocModule = 0; + +// --------------------------------------------------------------------------- + +LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client) + : LayerBaseClient(flinger, display, client), + mNeedsBlending(false), mBlitEngine(0) +{ +} + +LayerBuffer::~LayerBuffer() +{ + if (mBlitEngine) { + copybit_close(mBlitEngine); + } +} + +void LayerBuffer::onFirstRef() +{ + LayerBaseClient::onFirstRef(); + mSurface = new SurfaceLayerBuffer(mFlinger, this); + + hw_module_t const* module = (hw_module_t const*)sGrallocModule; + if (!module) { + // NOTE: technically there is a race here, but it shouldn't + // cause any problem since hw_get_module() always returns + // the same value. + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { + sGrallocModule = (gralloc_module_t const *)module; + } + } + + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } +} + +sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const +{ + return mSurface; +} + +status_t LayerBuffer::ditch() +{ + mSurface.clear(); + return NO_ERROR; +} + +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); + uint32_t res = LayerBase::doTransaction(flags); + // we always want filtering for these surfaces + mNeedsFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG); + return res; +} + +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); + } +} + +void LayerBuffer::serverDestroy() +{ + sp<Source> source(clearSource()); + if (source != 0) { + source->destroy(); + } +} + +/** + * 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, + int32_t orientation) +{ + sp<OverlayRef> result; + Mutex::Autolock _l(mLock); + if (mSource != 0) + return result; + + sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f, orientation); + 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::SurfaceLayerBuffer +// ============================================================================ + +LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer( + const sp<SurfaceFlinger>& flinger, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, owner->getIdentity(), owner) +{ +} + +LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer() +{ + unregisterBuffers(); +} + +status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers( + const ISurface::BufferHeap& buffers) +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + return owner->registerBuffers(buffers); + return NO_INIT; +} + +void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset) +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + owner->postBuffer(offset); +} + +void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers() +{ + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + owner->unregisterBuffers(); +} + +sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation) { + sp<OverlayRef> result; + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) + result = owner->createOverlay(w, h, format, orientation); + return result; +} + +// ============================================================================ +// LayerBuffer::Buffer +// ============================================================================ + +LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, + ssize_t offset, size_t bufferSize) + : mBufferHeap(buffers), mSupportsCopybit(false) +{ + 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.base = (void*)(intptr_t(buffers.heap->base()) + offset); + src.img.handle = 0; + + gralloc_module_t const * module = LayerBuffer::getGrallocModule(); + if (module && module->perform) { + int err = module->perform(module, + GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER, + buffers.heap->heapID(), bufferSize, + offset, buffers.heap->base(), + &src.img.handle); + + // we can fail here is the passed buffer is purely software + mSupportsCopybit = (err == NO_ERROR); + } + } + +LayerBuffer::Buffer::~Buffer() +{ + NativeBuffer& src(mNativeBuffer); + if (src.img.handle) { + native_handle_delete(src.img.handle); + } +} + +// ============================================================================ +// 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() { +} + +// --------------------------------------------------------------------------- + +LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0) +{ + 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() +{ + class MessageDestroyTexture : public MessageBase { + SurfaceFlinger* flinger; + GLuint name; + public: + MessageDestroyTexture( + SurfaceFlinger* flinger, GLuint name) + : flinger(flinger), name(name) { } + virtual bool handler() { + glDeleteTextures(1, &name); + return true; + } + }; + + if (mTexture.name != -1U) { + // GL textures can only be destroyed from the GL thread + getFlinger()->mEventQueue.postMessage( + new MessageDestroyTexture(getFlinger(), mTexture.name) ); + } + if (mTexture.image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); + } +} + +void LayerBuffer::BufferSource::postBuffer(ssize_t offset) +{ + ISurface::BufferHeap buffers; + { // scope for the lock + Mutex::Autolock _l(mBufferSourceLock); + 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, mBufferSize); + if (buffer->getStatus() != NO_ERROR) + buffer.clear(); + setBuffer(buffer); + mLayer.invalidate(); + } +} + +void LayerBuffer::BufferSource::unregisterBuffers() +{ + Mutex::Autolock _l(mBufferSourceLock); + mBufferHeap.heap.clear(); + mBuffer.clear(); + mLayer.invalidate(); +} + +sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const +{ + Mutex::Autolock _l(mBufferSourceLock); + return mBuffer; +} + +void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer) +{ + Mutex::Autolock _l(mBufferSourceLock); + mBuffer = buffer; +} + +void LayerBuffer::BufferSource::onDraw(const Region& clip) const +{ + sp<Buffer> ourBuffer(getBuffer()); + if (UNLIKELY(ourBuffer == 0)) { + // nothing to do, we don't have a buffer + mLayer.clearWithOpenGL(clip); + return; + } + + status_t err = NO_ERROR; + NativeBuffer src(ourBuffer->getBuffer()); + const Rect transformedBounds(mLayer.getTransformedBounds()); + +#if defined(EGL_ANDROID_image_native_buffer) + if (GLExtensions::getInstance().haveDirectTexture()) { + err = INVALID_OPERATION; + if (ourBuffer->supportsCopybit()) { + copybit_device_t* copybit = mLayer.mBlitEngine; + if (copybit && err != NO_ERROR) { + // create our EGLImageKHR the first time + err = initTempBuffer(); + if (err == NO_ERROR) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + const NativeBuffer& dst(mTempBuffer); + region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + err = copybit->stretch(copybit, &dst.img, &src.img, + &dst.crop, &src.crop, &clip); + if (err != NO_ERROR) { + clearTempBufferImage(); + } + } + } + } + } +#endif + else { + err = INVALID_OPERATION; + } + + if (err != NO_ERROR) { + // slower fallback + 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*)src.img.base; + const Region dirty(Rect(t.width, t.height)); + mTextureManager.loadTexture(&mTexture, dirty, t); + } + + mTexture.transform = mBufferHeap.transform; + mLayer.drawWithOpenGL(clip, mTexture); +} + +status_t LayerBuffer::BufferSource::initTempBuffer() const +{ + // figure out the size we need now + const ISurface::BufferHeap& buffers(mBufferHeap); + uint32_t w = mLayer.mTransformedBounds.width(); + uint32_t h = mLayer.mTransformedBounds.height(); + if (buffers.w * h != buffers.h * w) { + int t = w; w = h; h = t; + } + + // we're in the copybit case, so make sure we can handle this blit + // we don't have to keep the aspect ratio here + copybit_device_t* copybit = mLayer.mBlitEngine; + const int down = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); + const int up = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); + if (buffers.w > w*down) w = buffers.w / down; + else if (w > buffers.w*up) w = buffers.w*up; + if (buffers.h > h*down) h = buffers.h / down; + else if (h > buffers.h*up) h = buffers.h*up; + + if (mTexture.image != EGL_NO_IMAGE_KHR) { + // we have an EGLImage, make sure the needed size didn't change + if (w!=mTexture.width || h!= mTexture.height) { + // delete the EGLImage and texture + clearTempBufferImage(); + } else { + // we're good, we have an EGLImageKHR and it's (still) the + // right size + return NO_ERROR; + } + } + + // figure out if we need linear filtering + if (buffers.w * h == buffers.h * w) { + // same pixel area, don't use filtering + mLayer.mNeedsFiltering = false; + } + + // Allocate a temporary buffer and create the corresponding EGLImageKHR + // once the EGLImage has been created we don't need the + // graphic buffer reference anymore. + sp<GraphicBuffer> buffer = new GraphicBuffer( + w, h, HAL_PIXEL_FORMAT_RGB_565, + GraphicBuffer::USAGE_HW_TEXTURE | + GraphicBuffer::USAGE_HW_2D); + + status_t err = buffer->initCheck(); + if (err == NO_ERROR) { + NativeBuffer& dst(mTempBuffer); + dst.img.w = buffer->getStride(); + dst.img.h = h; + dst.img.format = buffer->getPixelFormat(); + dst.img.handle = (native_handle_t *)buffer->handle; + dst.img.base = 0; + dst.crop.l = 0; + dst.crop.t = 0; + dst.crop.r = w; + dst.crop.b = h; + + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); + err = mTextureManager.initEglImage(&mTexture, dpy, buffer); + } + + return err; +} + +void LayerBuffer::BufferSource::clearTempBufferImage() const +{ + // delete the image + EGLDisplay dpy(getFlinger()->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); + + // and the associated texture (recreate a name) + glDeleteTextures(1, &mTexture.name); + Texture defaultTexture; + mTexture = defaultTexture; +} + +// --------------------------------------------------------------------------- + +LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format, int32_t orientation) + : Source(layer), mVisibilityChanged(false), + mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation) +{ + overlay_control_device_t* overlay_dev = getFlinger()->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; + mInitialized = false; + + mOverlayHandle = overlay->getHandleRef(overlay); + + sp<OverlayChannel> channel = new OverlayChannel( &layer ); + + *overlayRef = new OverlayRef(mOverlayHandle, channel, + mWidth, mHeight, mFormat, mWidthStride, mHeightStride); + getFlinger()->signalEvent(); +} + +LayerBuffer::OverlaySource::~OverlaySource() +{ + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + } +} + +void LayerBuffer::OverlaySource::onDraw(const Region& clip) const +{ + // this would be where the color-key would be set, should we need it. + GLclampf red = 0; + GLclampf green = 0; + GLclampf blue = 0; + mLayer.clearWithOpenGL(clip, red, green, blue, 0); +} + +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 || !mInitialized) { + mVisibilityChanged = false; + mInitialized = true; + 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(mOverlaySourceLock); + if (mOverlay) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); + // we need to combine the layer orientation and the + // user-requested orientation. + Transform finalTransform = Transform(mOrientation) * + Transform(mLayer.getOrientation()); + overlay_dev->setParameter(overlay_dev, mOverlay, + OVERLAY_TRANSFORM, finalTransform.getOrientation()); + overlay_dev->commit(overlay_dev, mOverlay); + } + } + } +} + +void LayerBuffer::OverlaySource::destroy() +{ + // we need a lock here to protect "onVisibilityResolved" + Mutex::Autolock _l(mOverlaySourceLock); + if (mOverlay && mOverlayDevice) { + overlay_control_device_t* overlay_dev = mOverlayDevice; + overlay_dev->destroyOverlay(overlay_dev, mOverlay); + mOverlay = 0; + } +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h new file mode 100644 index 0000000..1c0bf83 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.h @@ -0,0 +1,220 @@ +/* + * 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 "LayerBase.h" +#include "TextureManager.h" + +struct copybit_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Buffer; +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 void destroy() { } + SurfaceFlinger* getFlinger() const { return mLayer.mFlinger.get(); } + protected: + LayerBuffer& mLayer; + }; + +public: + LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); + virtual ~LayerBuffer(); + + virtual void onFirstRef(); + virtual bool needsBlending() const; + virtual const char* getTypeId() const { return "LayerBuffer"; } + + virtual sp<LayerBaseClient::Surface> createSurface() const; + virtual status_t ditch(); + virtual void onDraw(const Region& clip) const; + virtual uint32_t doTransaction(uint32_t flags); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + + 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, + int32_t orientation); + + sp<Source> getSource() const; + sp<Source> clearSource(); + void setNeedsBlending(bool blending); + Rect getTransformedBounds() const { + return mTransformedBounds; + } + + void serverDestroy(); + +private: + struct NativeBuffer { + copybit_image_t img; + copybit_rect_t crop; + }; + + static gralloc_module_t const* sGrallocModule; + static gralloc_module_t const* getGrallocModule() { + return sGrallocModule; + } + + class Buffer : public LightRefBase<Buffer> { + public: + Buffer(const ISurface::BufferHeap& buffers, + ssize_t offset, size_t bufferSize); + inline bool supportsCopybit() const { + return mSupportsCopybit; + } + 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; + bool mSupportsCopybit; + }; + + 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 void destroy() { } + private: + status_t initTempBuffer() const; + void clearTempBufferImage() const; + mutable Mutex mBufferSourceLock; + sp<Buffer> mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable Texture mTexture; + mutable NativeBuffer mTempBuffer; + mutable TextureManager mTextureManager; + }; + + class OverlaySource : public Source { + public: + OverlaySource(LayerBuffer& layer, + sp<OverlayRef>* overlayRef, + uint32_t w, uint32_t h, int32_t format, int32_t orientation); + virtual ~OverlaySource(); + virtual void onDraw(const Region& clip) const; + virtual void onTransaction(uint32_t flags); + virtual void onVisibilityResolved(const Transform& planeTransform); + virtual void destroy(); + private: + + class OverlayChannel : public BnOverlay { + wp<LayerBuffer> mLayer; + virtual void destroy() { + sp<LayerBuffer> layer(mLayer.promote()); + if (layer != 0) { + layer->serverDestroy(); + } + } + public: + OverlayChannel(const sp<LayerBuffer>& layer) + : mLayer(layer) { + } + }; + + 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; + int32_t mOrientation; + mutable Mutex mOverlaySourceLock; + bool mInitialized; + }; + + + class SurfaceLayerBuffer : public LayerBaseClient::Surface + { + public: + SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger, + const sp<LayerBuffer>& owner); + virtual ~SurfaceLayerBuffer(); + + 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, int32_t orientation); + private: + sp<LayerBuffer> getOwner() const { + return static_cast<LayerBuffer*>(Surface::getOwner().get()); + } + }; + + mutable Mutex mLock; + sp<Source> mSource; + sp<Surface> mSurface; + bool mInvalidate; + bool mNeedsBlending; + copybit_device_t* mBlitEngine; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_BUFFER_H diff --git a/services/surfaceflinger/LayerDim.cpp b/services/surfaceflinger/LayerDim.cpp new file mode 100644 index 0000000..a1f339e --- /dev/null +++ b/services/surfaceflinger/LayerDim.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include "LayerDim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +bool LayerDim::sUseTexture; +GLuint LayerDim::sTexId; +EGLImageKHR LayerDim::sImage; +int32_t LayerDim::sWidth; +int32_t LayerDim::sHeight; + +// --------------------------------------------------------------------------- + +LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client) + : LayerBaseClient(flinger, display, client) +{ +} + +void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) +{ + sTexId = -1; + sImage = EGL_NO_IMAGE_KHR; + sWidth = w; + sHeight = h; + sUseTexture = false; +} + +LayerDim::~LayerDim() +{ +} + +void LayerDim::onDraw(const Region& clip) const +{ + const State& s(drawingState()); + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (s.alpha>0 && (it != end)) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const GLfloat alpha = s.alpha/255.0f; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0, 0, 0, alpha); + +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif + glDisable(GL_TEXTURE_2D); + + GLshort w = sWidth; + GLshort h = sHeight; + const GLshort vertices[4][2] = { + { 0, 0 }, + { 0, h }, + { w, h }, + { w, 0 } + }; + glVertexPointer(2, GL_SHORT, 0, vertices); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerDim.h b/services/surfaceflinger/LayerDim.h new file mode 100644 index 0000000..f032314 --- /dev/null +++ b/services/surfaceflinger/LayerDim.h @@ -0,0 +1,56 @@ +/* + * 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 <EGL/egl.h> +#include <EGL/eglext.h> + +#include "LayerBase.h" + +// --------------------------------------------------------------------------- + +namespace android { + +class LayerDim : public LayerBaseClient +{ + static bool sUseTexture; + static GLuint sTexId; + static EGLImageKHR sImage; + static int32_t sWidth; + static int32_t sHeight; +public: + LayerDim(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client); + virtual ~LayerDim(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + virtual const char* getTypeId() const { return "LayerDim"; } + + static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_DIM_H diff --git a/services/surfaceflinger/MODULE_LICENSE_APACHE2 b/services/surfaceflinger/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/surfaceflinger/MODULE_LICENSE_APACHE2 diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp new file mode 100644 index 0000000..d668e88 --- /dev/null +++ b/services/surfaceflinger/MessageQueue.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2009 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 <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> + +#include "MessageQueue.h" + +namespace android { + +// --------------------------------------------------------------------------- + +void MessageList::insert(const sp<MessageBase>& node) +{ + LIST::iterator cur(mList.begin()); + LIST::iterator end(mList.end()); + while (cur != end) { + if (*node < **cur) { + mList.insert(cur, node); + return; + } + ++cur; + } + mList.insert(++end, node); +} + +void MessageList::remove(MessageList::LIST::iterator pos) +{ + mList.erase(pos); +} + +// --------------------------------------------------------------------------- + +MessageQueue::MessageQueue() + : mInvalidate(false) +{ + mInvalidateMessage = new MessageBase(INVALIDATE); +} + +MessageQueue::~MessageQueue() +{ +} + +sp<MessageBase> MessageQueue::waitMessage(nsecs_t timeout) +{ + sp<MessageBase> result; + + bool again; + do { + const nsecs_t timeoutTime = systemTime() + timeout; + while (true) { + Mutex::Autolock _l(mLock); + nsecs_t now = systemTime(); + nsecs_t nextEventTime = -1; + + // invalidate messages are always handled first + if (mInvalidate) { + mInvalidate = false; + mInvalidateMessage->when = now; + result = mInvalidateMessage; + break; + } + + LIST::iterator cur(mMessages.begin()); + if (cur != mMessages.end()) { + result = *cur; + } + + if (result != 0) { + if (result->when <= now) { + // there is a message to deliver + mMessages.remove(cur); + break; + } + if (timeout>=0 && timeoutTime < now) { + // we timed-out, return a NULL message + result = 0; + break; + } + nextEventTime = result->when; + result = 0; + } + + if (timeout >= 0 && nextEventTime > 0) { + if (nextEventTime > timeoutTime) { + nextEventTime = timeoutTime; + } + } + + if (nextEventTime >= 0) { + //LOGD("nextEventTime = %lld ms", nextEventTime); + if (nextEventTime > 0) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + const nsecs_t reltime = nextEventTime - systemTime(); + if (reltime > 0) { + mCondition.waitRelative(mLock, reltime); + } + } + } else { + //LOGD("going to wait"); + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mCondition.wait(mLock); + } + } + // here we're not holding the lock anymore + + if (result == 0) + break; + + again = result->handler(); + if (again) { + // the message has been processed. release our reference to it + // without holding the lock. + result->notify(); + result = 0; + } + + } while (again); + + return result; +} + +status_t MessageQueue::postMessage( + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) +{ + return queueMessage(message, relTime, flags); +} + +status_t MessageQueue::invalidate() { + Mutex::Autolock _l(mLock); + mInvalidate = true; + mCondition.signal(); + return NO_ERROR; +} + +status_t MessageQueue::queueMessage( + const sp<MessageBase>& message, nsecs_t relTime, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + message->when = systemTime() + relTime; + mMessages.insert(message); + + //LOGD("MessageQueue::queueMessage time = %lld ms", message->when); + //dumpLocked(message); + + mCondition.signal(); + return NO_ERROR; +} + +void MessageQueue::dump(const sp<MessageBase>& message) +{ + Mutex::Autolock _l(mLock); + dumpLocked(message); +} + +void MessageQueue::dumpLocked(const sp<MessageBase>& message) +{ + LIST::const_iterator cur(mMessages.begin()); + LIST::const_iterator end(mMessages.end()); + int c = 0; + while (cur != end) { + const char tick = (*cur == message) ? '>' : ' '; + LOGD("%c %d: msg{.what=%08x, when=%lld}", + tick, c, (*cur)->what, (*cur)->when); + ++cur; + c++; + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h new file mode 100644 index 0000000..890f809 --- /dev/null +++ b/services/surfaceflinger/MessageQueue.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 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_MESSAGE_QUEUE_H +#define ANDROID_MESSAGE_QUEUE_H + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/List.h> + +#include "Barrier.h" + +namespace android { + +// --------------------------------------------------------------------------- + +class MessageBase; + +class MessageList +{ + List< sp<MessageBase> > mList; + typedef List< sp<MessageBase> > LIST; +public: + inline LIST::iterator begin() { return mList.begin(); } + inline LIST::const_iterator begin() const { return mList.begin(); } + inline LIST::iterator end() { return mList.end(); } + inline LIST::const_iterator end() const { return mList.end(); } + inline bool isEmpty() const { return mList.empty(); } + void insert(const sp<MessageBase>& node); + void remove(LIST::iterator pos); +}; + +// ============================================================================ + +class MessageBase : + public LightRefBase<MessageBase> +{ +public: + nsecs_t when; + uint32_t what; + int32_t arg0; + + MessageBase() : when(0), what(0), arg0(0) { } + MessageBase(uint32_t what, int32_t arg0=0) + : when(0), what(what), arg0(arg0) { } + + // return true if message has a handler + virtual bool handler() { return false; } + + // waits for the handler to be processed + void wait() const { barrier.wait(); } + + // releases all waiters. this is done automatically if + // handler returns true + void notify() const { barrier.open(); } + +protected: + virtual ~MessageBase() { } + +private: + mutable Barrier barrier; + friend class LightRefBase<MessageBase>; +}; + +inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) { + return lhs.when < rhs.when; +} + +// --------------------------------------------------------------------------- + +class MessageQueue +{ + typedef List< sp<MessageBase> > LIST; +public: + + MessageQueue(); + ~MessageQueue(); + + // pre-defined messages + enum { + INVALIDATE = '_upd' + }; + + sp<MessageBase> waitMessage(nsecs_t timeout = -1); + + status_t postMessage(const sp<MessageBase>& message, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t invalidate(); + + void dump(const sp<MessageBase>& message); + +private: + status_t queueMessage(const sp<MessageBase>& message, + nsecs_t reltime, uint32_t flags); + void dumpLocked(const sp<MessageBase>& message); + + Mutex mLock; + Condition mCondition; + MessageList mMessages; + bool mInvalidate; + sp<MessageBase> mInvalidateMessage; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp new file mode 100644 index 0000000..68e8f19 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -0,0 +1,1909 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <math.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryHeapBase.h> + +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/StopWatch.h> + +#include <ui/GraphicBufferAllocator.h> +#include <ui/PixelFormat.h> + +#include <pixelflinger/pixelflinger.h> +#include <GLES/gl.h> + +#include "clz.h" +#include "GLExtensions.h" +#include "Layer.h" +#include "LayerBlur.h" +#include "LayerBuffer.h" +#include "LayerDim.h" +#include "SurfaceFlinger.h" + +#include "DisplayHardware/DisplayHardware.h" + +/* ideally AID_GRAPHICS would be in a semi-public header + * or there would be a way to map a user/group name to its id + */ +#ifndef AID_GRAPHICS +#define AID_GRAPHICS 1003 +#endif + +#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( + const sp<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); + LOGE_IF(layers[idx]!=key, + "LayerVector[%p]: layers[%d]=%p, key=%p", + this, int(idx), layers[idx].get(), key.get()); + return idx; + } + return i; +} + +ssize_t SurfaceFlinger::LayerVector::add( + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) +{ + size_t count = layers.size(); + ssize_t l = 0; + ssize_t h = count-1; + ssize_t mid; + sp<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(const sp<LayerBase>& layer) +{ + const ssize_t keyIndex = lookup.indexOfKey(layer); + if (keyIndex >= 0) { + const size_t index = lookup.valueAt(keyIndex); + LOGE_IF(layers[index]!=layer, + "LayerVector[%p]: layers[%u]=%p, layer=%p", + this, int(index), layers[index].get(), layer.get()); + 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( + const sp<LayerBase>& layer, + Vector< sp<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), + mResizeTransationPending(false), + mLayersRemoved(false), + mBootTime(systemTime()), + mHardwareTest("android.permission.HARDWARE_TEST"), + mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"), + mDump("android.permission.DUMP"), + mVisibleRegionsDirty(false), + mDeferReleaseConsole(false), + mFreezeDisplay(false), + mFreezeCount(0), + mFreezeDisplayTime(0), + mDebugRegion(0), + mDebugBackground(0), + mDebugInSwapBuffers(0), + mLastSwapBufferTime(0), + mDebugInTransaction(0), + mLastTransactionTime(0), + mBootFinished(false), + 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.showbackground", value, "0"); + mDebugBackground = atoi(value); + + LOGI_IF(mDebugRegion, "showupdates enabled"); + LOGI_IF(mDebugBackground, "showbackground enabled"); +} + +SurfaceFlinger::~SurfaceFlinger() +{ + glDeleteTextures(1, &mWormholeTexName); +} + +overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const +{ + return graphicPlane(0).displayHardware().getOverlayEngine(); +} + +sp<IMemoryHeap> SurfaceFlinger::getCblk() const +{ + return mServerHeap; +} + +sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() +{ + sp<ISurfaceComposerClient> bclient; + sp<Client> client(new Client(this)); + status_t err = client->initCheck(); + if (err == NO_ERROR) { + bclient = client; + } + return bclient; +} + +sp<ISurfaceComposerClient> SurfaceFlinger::createClientConnection() +{ + sp<ISurfaceComposerClient> bclient; + sp<UserClient> client(new UserClient(this)); + status_t err = client->initCheck(); + if (err == NO_ERROR) { + bclient = client; + } + return bclient; +} + + +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)) ); + mBootFinished = true; + property_set("ctl.stop", "bootanim"); +} + +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; +} + +status_t SurfaceFlinger::readyToRun() +{ + LOGI( "SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); + + // 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); + } + + // create the shared control-block + mServerHeap = new MemoryHeapBase(4096, + MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap"); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + + new(mServerCblk) surface_flinger_cblk_t; + + // 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 = plane.getWidth(); + dcblk->h = plane.getHeight(); + dcblk->format = f; + dcblk->orientation = ISurfaceComposer::eOrientationDefault; + dcblk->xdpi = hw.getDpiX(); + dcblk->ydpi = hw.getDpiY(); + dcblk->fps = hw.getRefreshRate(); + dcblk->density = hw.getDensity(); + + // Initialize OpenGL|ES + 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... + */ + + // start boot animation + property_set("ctl.start", "bootanim"); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Events Handler +#endif + +void SurfaceFlinger::waitForEvent() +{ + while (true) { + nsecs_t timeout = -1; + const nsecs_t freezeDisplayTimeout = ms2ns(5000); + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + const nsecs_t now = systemTime(); + if (mFreezeDisplayTime == 0) { + mFreezeDisplayTime = now; + } + nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); + timeout = waitTime>0 ? waitTime : 0; + } + + sp<MessageBase> msg = mEventQueue.waitMessage(timeout); + + // see if we timed out + if (isFrozen()) { + const nsecs_t now = systemTime(); + nsecs_t frozenTime = (now - mFreezeDisplayTime); + if (frozenTime >= freezeDisplayTimeout) { + // we timed out and are still frozen + LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", + mFreezeDisplay, mFreezeCount); + mFreezeDisplayTime = 0; + mFreezeCount = 0; + mFreezeDisplay = false; + } + } + + if (msg != 0) { + switch (msg->what) { + case MessageQueue::INVALIDATE: + // invalidate message, just return to the main loop + return; + } + } + } +} + +void SurfaceFlinger::signalEvent() { + mEventQueue.invalidate(); +} + +void SurfaceFlinger::signal() const { + // this is the IPC call + const_cast<SurfaceFlinger*>(this)->signalEvent(); +} + +status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) +{ + return mEventQueue.postMessage(msg, reltime, flags); +} + +status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime, uint32_t flags) +{ + status_t res = mEventQueue.postMessage(msg, reltime, flags); + if (res == NO_ERROR) { + msg->wait(); + } + return res; +} + +// ---------------------------------------------------------------------------- +#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() && !isFrozen())) { + // repaint the framebuffer (if needed) + handleRepaint(); + + // inform the h/w that we're done compositing + hw.compositionComplete(); + + // release the clients before we flip ('cause flip might block) + unlockClients(); + + postFramebuffer(); + } else { + // pretend we did the post + unlockClients(); + usleep(16667); // 60 fps period + } + return true; +} + +void SurfaceFlinger::postFramebuffer() +{ + if (!mInvalidRegion.isEmpty()) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const nsecs_t now = systemTime(); + mDebugInSwapBuffers = now; + hw.flip(mInvalidRegion); + mLastSwapBufferTime = systemTime() - now; + mDebugInSwapBuffers = 0; + mInvalidRegion.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 acquire signal + mDeferReleaseConsole = false; + hw.releaseScreen(); + } + + if (what & eConsoleReleased) { + if (hw.canDraw()) { + hw.releaseScreen(); + } else { + mDeferReleaseConsole = true; + } + } + + mDirtyRegion.set(hw.bounds()); +} + +void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) +{ + Vector< sp<LayerBase> > ditchedLayers; + + { // scope for the lock + Mutex::Autolock _l(mStateLock); + const nsecs_t now = systemTime(); + mDebugInTransaction = now; + handleTransactionLocked(transactionFlags, ditchedLayers); + mLastTransactionTime = systemTime() - now; + mDebugInTransaction = 0; + } + + // do this without lock held + const size_t count = ditchedLayers.size(); + for (size_t i=0 ; i<count ; i++) { + if (ditchedLayers[i] != 0) { + //LOGD("ditching layer %p", ditchedLayers[i].get()); + ditchedLayers[i]->ditch(); + } + } +} + +void SurfaceFlinger::handleTransactionLocked( + uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers) +{ + 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++) { + const sp<LayerBase>& 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; + } + } + + /* + * 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; + const uint32_t type = mCurrentState.orientationType; + 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; + dcblk->w = plane.getWidth(); + dcblk->h = plane.getHeight(); + + mVisibleRegionsDirty = true; + mDirtyRegion.set(hw.bounds()); + } + + if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { + // freezing or unfreezing the display -> trigger animation if needed + mFreezeDisplay = mCurrentState.freezeDisplay; + if (mFreezeDisplay) + mFreezeDisplayTime = 0; + } + + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added + mVisibleRegionsDirty = true; + } + + // some layers might have been removed, so + // we need to update the regions they're exposing. + if (mLayersRemoved) { + mLayersRemoved = false; + mVisibleRegionsDirty = true; + const LayerVector& previousLayers(mDrawingState.layersSortedByZ); + const size_t count = previousLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(previousLayers[i]); + if (currentLayers.indexOf( layer ) < 0) { + // this layer is not visible anymore + ditchedLayers.add(layer); + mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen); + } + } + } + } + + 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()); + const DisplayHardware& hw(plane.displayHardware()); + const Region screenRegion(hw.bounds()); + + Region aboveOpaqueLayers; + Region aboveCoveredLayers; + Region dirty; + + bool secureFrameBuffer = false; + + size_t i = currentLayers.size(); + while (i--) { + const sp<LayerBase>& layer = currentLayers[i]; + layer->validateVisibility(planeTransform); + + // start with the whole surface at its current location + const Layer::State& s(layer->drawingState()); + + /* + * opaqueRegion: area of a surface that is fully opaque. + */ + Region opaqueRegion; + + /* + * visibleRegion: area of a surface that is visible on screen + * and not fully transparent. This is essentially the layer's + * footprint minus the opaque regions above it. + * Areas covered by a translucent surface are considered visible. + */ + Region visibleRegion; + + /* + * coveredRegion: area of a surface that is covered by all + * visible regions above it (which includes the translucent areas). + */ + Region coveredRegion; + + + // handle hidden surfaces by setting the visible region to empty + if (LIKELY(!(s.flags & ISurfaceComposer::eLayerHidden) && s.alpha)) { + const bool translucent = layer->needsBlending(); + const Rect bounds(layer->visibleBounds()); + visibleRegion.set(bounds); + visibleRegion.andSelf(screenRegion); + if (!visibleRegion.isEmpty()) { + // Remove the transparent area from the visible region + if (translucent) { + visibleRegion.subtractSelf(layer->transparentRegionScreen); + } + + // compute the opaque region + const int32_t layerOrientation = layer->getOrientation(); + if (s.alpha==255 && !translucent && + ((layerOrientation & Transform::ROT_INVALID) == false)) { + // the opaque region is the layer's footprint + opaqueRegion = visibleRegion; + } + } + } + + // Clip the covered region to the visible region + coveredRegion = aboveCoveredLayers.intersect(visibleRegion); + + // Update aboveCoveredLayers for next (lower) layer + aboveCoveredLayers.orSelf(visibleRegion); + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(aboveOpaqueLayers); + + // 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: + * the exposed region consists of two components: + * 1) what's VISIBLE now and was COVERED before + * 2) what's EXPOSED now less what was EXPOSED before + * + * note that (1) is conservative, we start with the whole + * visible region but only keep what used to be covered by + * something -- which mean it may have been exposed. + * + * (2) handles areas that were not covered by anything but got + * exposed because of a resize. + */ + const Region newExposed = visibleRegion - coveredRegion; + const Region oldVisibleRegion = layer->visibleRegionScreen; + const Region oldCoveredRegion = layer->coveredRegionScreen; + const Region oldExposed = oldVisibleRegion - oldCoveredRegion; + dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed); + } + dirty.subtractSelf(aboveOpaqueLayers); + + // accumulate to the screen dirty region + dirtyRegion.orSelf(dirty); + + // Update aboveOpaqueLayers for next (lower) layer + aboveOpaqueLayers.orSelf(opaqueRegion); + + // Store the visible region is screen space + layer->setVisibleRegion(visibleRegion); + layer->setCoveredRegion(coveredRegion); + + // If a secure layer is partially visible, lock-down the screen! + if (layer->isSecure() && !visibleRegion.isEmpty()) { + secureFrameBuffer = true; + } + } + + // invalidate the areas where a layer was removed + dirtyRegion.orSelf(mDirtyRegionRemovedLayer); + mDirtyRegionRemovedLayer.clear(); + + mSecureFrameBuffer = secureFrameBuffer; + opaqueRegion = aboveOpaqueLayers; +} + + +void SurfaceFlinger::commitTransaction() +{ + mDrawingState = mCurrentState; + mResizeTransationPending = false; + mTransactionCV.broadcast(); +} + +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(); + sp<LayerBase> const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& 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(); + sp<LayerBase> const* layers = currentLayers.array(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(layers[i]); + layer->unlockPageFlip(planeTransform, mDirtyRegion); + } +} + + +void SurfaceFlinger::handleRepaint() +{ + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + if (mInvalidRegion.isEmpty()) { + // nothing to do + return; + } + + if (UNLIKELY(mDebugRegion)) { + debugFlashRegions(); + } + + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + uint32_t flags = hw.getFlags(); + if ((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED)) + { + // we can redraw only what's dirty, but since SWAP_RECTANGLE only + // takes a rectangle, we must make sure to update that whole + // rectangle in that case + if (flags & DisplayHardware::SWAP_RECTANGLE) { + // TODO: we really should be able to pass a region to + // SWAP_RECTANGLE so that we don't have to redraw all this. + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // in the BUFFER_PRESERVED case, obviously, we can update only + // what's needed and nothing more. + // NOTE: this is NOT a common case, as preserving the backbuffer + // is costly and usually involves copying the whole update back. + } + } else { + if (flags & DisplayHardware::PARTIAL_UPDATES) { + // We need to redraw the rectangle that will be updated + // (pushed to the framebuffer). + // This is needed because PARTIAL_UPDATES only takes one + // rectangle instead of a region (see DisplayHardware::flip()) + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // we need to redraw everything (the whole screen) + 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(); + sp<LayerBase> const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& 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(); + sp<LayerBase> const* const layers = drawingLayers.array(); + for (size_t i=0 ; i<count ; ++i) { + const sp<LayerBase>& layer = layers[i]; + layer->finishPageFlip(); + } +} + +void SurfaceFlinger::debugFlashRegions() +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t flags = hw.getFlags(); + + if (!((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED))) { + const Region repaint((flags & DisplayHardware::PARTIAL_UPDATES) ? + mDirtyRegion.bounds() : hw.bounds()); + composeSurfaces(repaint); + } + + TextureManager::deactivateTextures(); + + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + static int toggle = 0; + toggle = 1 - toggle; + if (toggle) { + glColor4f(1, 0, 1, 1); + } else { + glColor4f(1, 1, 0, 1); + } + + Region::const_iterator it = mDirtyRegion.begin(); + Region::const_iterator const end = mDirtyRegion.end(); + while (it != end) { + const Rect& r = *it++; + 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); + } + + if (mInvalidRegion.isEmpty()) { + mDirtyRegion.dump("mDirtyRegion"); + mInvalidRegion.dump("mInvalidRegion"); + } + hw.flip(mInvalidRegion); + + 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)) { + glClearColor(0,0,0,0); + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; + 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); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif + 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); + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; + 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(const sp<LayerBase>& layer) +{ + Mutex::Autolock _l(mStateLock); + addLayer_l(layer); + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) +{ + ssize_t i = mCurrentState.layersSortedByZ.add( + layer, &LayerBase::compareCurrentStateZ); + return (i < 0) ? status_t(i) : status_t(NO_ERROR); +} + +ssize_t SurfaceFlinger::addClientLayer(const sp<Client>& client, + const sp<LayerBaseClient>& lbc) +{ + Mutex::Autolock _l(mStateLock); + + // attach this layer to the client + ssize_t name = client->attachLayer(lbc); + + // add this layer to the current state list + addLayer_l(lbc); + + return name; +} + +status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) +{ + Mutex::Autolock _l(mStateLock); + status_t err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) + setTransactionFlags(eTransactionNeeded); + return err; +} + +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) +{ + sp<LayerBaseClient> lbc(layerBase->getLayerBaseClient()); + if (lbc != 0) { + mLayerMap.removeItem( lbc->getSurface()->asBinder() ); + } + ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); + if (index >= 0) { + mLayersRemoved = true; + return NO_ERROR; + } + return status_t(index); +} + +status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) +{ + // remove the layer from the main list (through a transaction). + ssize_t err = removeLayer_l(layerBase); + + layerBase->onRemoved(); + + // 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 Client::destroySurface(), + // ~Client() and ~ISurface(). + return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err; +} + +status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) +{ + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) +{ + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + signalEvent(); + } + return old; +} + +void SurfaceFlinger::openGlobalTransaction() +{ + android_atomic_inc(&mTransactionCount); +} + +void SurfaceFlinger::closeGlobalTransaction() +{ + if (android_atomic_dec(&mTransactionCount) == 1) { + signalEvent(); + + // if there is a transaction with a resize, wait for it to + // take effect before returning. + Mutex::Autolock _l(mStateLock); + while (mResizeTransationPending) { + status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + // just in case something goes wrong in SF, return to the + // called after a few seconds. + LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!"); + mResizeTransationPending = false; + break; + } + } + } +} + +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 fading) + 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 fading) + return NO_ERROR; +} + +int SurfaceFlinger::setOrientation(DisplayID dpy, + int orientation, uint32_t flags) +{ + 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.orientationType = flags; + mCurrentState.orientation = orientation; + setTransactionFlags(eTransactionNeeded); + mTransactionCV.wait(mStateLock); + } else { + orientation = BAD_VALUE; + } + } + return orientation; +} + +sp<ISurface> SurfaceFlinger::createSurface(const sp<Client>& client, int pid, + const String8& name, ISurfaceComposerClient::surface_data_t* params, + DisplayID d, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + sp<LayerBaseClient> layer; + sp<LayerBaseClient::Surface> surfaceHandle; + + if (int32_t(w|h) < 0) { + LOGE("createSurface() failed, w or h is negative (w=%d, h=%d)", + int(w), int(h)); + return surfaceHandle; + } + + //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); + sp<Layer> normalLayer; + switch (flags & eFXSurfaceMask) { + case eFXSurfaceNormal: + if (UNLIKELY(flags & ePushBuffers)) { + layer = createPushBuffersSurface(client, d, w, h, flags); + } else { + normalLayer = createNormalSurface(client, d, w, h, flags, format); + layer = normalLayer; + } + break; + case eFXSurfaceBlur: + layer = createBlurSurface(client, d, w, h, flags); + break; + case eFXSurfaceDim: + layer = createDimSurface(client, d, w, h, flags); + break; + } + + if (layer != 0) { + layer->initStates(w, h, flags); + layer->setName(name); + ssize_t token = addClientLayer(client, layer); + + surfaceHandle = layer->getSurface(); + if (surfaceHandle != 0) { + params->token = token; + params->identity = surfaceHandle->getIdentity(); + params->width = w; + params->height = h; + params->format = format; + if (normalLayer != 0) { + Mutex::Autolock _l(mStateLock); + mLayerMap.add(surfaceHandle->asBinder(), normalLayer); + } + } + + setTransactionFlags(eTransactionNeeded); + } + + return surfaceHandle; +} + +sp<Layer> SurfaceFlinger::createNormalSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format) +{ + // 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: +#ifdef NO_RGBX_8888 + format = PIXEL_FORMAT_RGB_565; +#else + format = PIXEL_FORMAT_RGBX_8888; +#endif + break; + } + +#ifdef NO_RGBX_8888 + if (format == PIXEL_FORMAT_RGBX_8888) + format = PIXEL_FORMAT_RGBA_8888; +#endif + + sp<Layer> layer = new Layer(this, display, client); + status_t err = layer->setBuffers(w, h, format, flags); + if (LIKELY(err != NO_ERROR)) { + LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); + layer.clear(); + } + return layer; +} + +sp<LayerBlur> SurfaceFlinger::createBlurSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBlur> layer = new LayerBlur(this, display, client); + layer->initStates(w, h, flags); + return layer; +} + +sp<LayerDim> SurfaceFlinger::createDimSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerDim> layer = new LayerDim(this, display, client); + layer->initStates(w, h, flags); + return layer; +} + +sp<LayerBuffer> SurfaceFlinger::createPushBuffersSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBuffer> layer = new LayerBuffer(this, display, client); + layer->initStates(w, h, flags); + return layer; +} + +status_t SurfaceFlinger::removeSurface(const sp<Client>& client, SurfaceID sid) +{ + /* + * called by the window manager, when a surface should be marked for + * destruction. + * + * The surface is removed from the current and drawing lists, but placed + * in the purgatory queue, so it's not destroyed right-away (we need + * to wait for all client's references to go away first). + */ + + status_t err = NAME_NOT_FOUND; + Mutex::Autolock _l(mStateLock); + sp<LayerBaseClient> layer = client->getLayerUser(sid); + if (layer != 0) { + err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) { + setTransactionFlags(eTransactionNeeded); + } + } + return err; +} + +status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) +{ + // called by ~ISurface() when all references are gone + + class MessageDestroySurface : public MessageBase { + SurfaceFlinger* flinger; + sp<LayerBaseClient> layer; + public: + MessageDestroySurface( + SurfaceFlinger* flinger, const sp<LayerBaseClient>& layer) + : flinger(flinger), layer(layer) { } + virtual bool handler() { + sp<LayerBaseClient> l(layer); + layer.clear(); // clear it outside of the lock; + Mutex::Autolock _l(flinger->mStateLock); + /* + * remove the layer from the current list -- chances are that it's + * not in the list anyway, because it should have been removed + * already upon request of the client (eg: window manager). + * However, a buggy client could have not done that. + * Since we know we don't have any more clients, we don't need + * to use the purgatory. + */ + status_t err = flinger->removeLayer_l(l); + LOGE_IF(err<0 && err != NAME_NOT_FOUND, + "error removing layer=%p (%s)", l.get(), strerror(-err)); + return true; + } + }; + + postMessageAsync( new MessageDestroySurface(this, layer) ); + return NO_ERROR; +} + +status_t SurfaceFlinger::setClientState( + const sp<Client>& client, + int32_t count, + const layer_state_t* states) +{ + Mutex::Autolock _l(mStateLock); + uint32_t flags = 0; + for (int i=0 ; i<count ; i++) { + const layer_state_t& s(states[i]); + sp<LayerBaseClient> layer(client->getLayerUser(s.surface)); + if (layer != 0) { + const uint32_t what = s.what; + 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; + mResizeTransationPending = true; + } + } + 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; +} + +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 (!mDump.checkCalling()) { + 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 { + + // figure out if we're stuck somewhere + const nsecs_t now = systemTime(); + const nsecs_t inSwapBuffers(mDebugInSwapBuffers); + const nsecs_t inTransaction(mDebugInTransaction); + nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; + + // Try to get the main lock, but don't insist if we can't + // (this would indicate SF is stuck, but we want to be able to + // print something in dumpsys). + int retry = 3; + while (mStateLock.tryLock()<0 && --retry>=0) { + usleep(1000000); + } + const bool locked(retry >= 0); + if (!locked) { + snprintf(buffer, SIZE, + "SurfaceFlinger appears to be unresponsive, " + "dumping anyways (no locks held)\n"); + result.append(buffer); + } + + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(currentLayers[i]); + layer->dump(result, buffer, SIZE); + const Layer::State& s(layer->drawingState()); + 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); + snprintf(buffer, SIZE, + " last eglSwapBuffers() time: %f us\n" + " last transaction time : %f us\n", + mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0); + result.append(buffer); + + if (inSwapBuffersDuration || !locked) { + snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n", + inSwapBuffersDuration/1000.0); + result.append(buffer); + } + + if (inTransactionDuration || !locked) { + snprintf(buffer, SIZE, " transaction time: %f us\n", + inTransactionDuration/1000.0); + result.append(buffer); + } + + const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); + alloc.dump(result); + + if (locked) { + mStateLock.unlock(); + } + } + 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: + { + // codes that require permission check + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) { + 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) { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + if (UNLIKELY(!mHardwareTest.checkCalling())) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + int n; + switch (code) { + case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE + return NO_ERROR; + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE + 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:{ // force transaction + setTransactionFlags(eTransactionNeeded|eTraversalNeeded); + return NO_ERROR; + } + case 1007: // set mFreezeCount + mFreezeCount = data.readInt32(); + mFreezeDisplayTime = 0; + return NO_ERROR; + case 1010: // interrogate. + reply->writeInt32(0); + 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; +} + +// --------------------------------------------------------------------------- + +sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const +{ + sp<Layer> result; + Mutex::Autolock _l(mStateLock); + result = mLayerMap.valueFor( sur->asBinder() ).promote(); + return result; +} + +// --------------------------------------------------------------------------- + +Client::Client(const sp<SurfaceFlinger>& flinger) + : mFlinger(flinger), mNameGenerator(1) +{ +} + +Client::~Client() +{ + const size_t count = mLayers.size(); + for (size_t i=0 ; i<count ; i++) { + sp<LayerBaseClient> layer(mLayers.valueAt(i).promote()); + if (layer != 0) { + mFlinger->removeLayer(layer); + } + } +} + +status_t Client::initCheck() const { + return NO_ERROR; +} + +ssize_t Client::attachLayer(const sp<LayerBaseClient>& layer) +{ + int32_t name = android_atomic_inc(&mNameGenerator); + mLayers.add(name, layer); + return name; +} + +void Client::detachLayer(const LayerBaseClient* layer) +{ + // we do a linear search here, because this doesn't happen often + const size_t count = mLayers.size(); + for (size_t i=0 ; i<count ; i++) { + if (mLayers.valueAt(i) == layer) { + mLayers.removeItemsAt(i, 1); + break; + } + } +} +sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { + sp<LayerBaseClient> lbc; + const wp<LayerBaseClient>& layer(mLayers.valueFor(i)); + if (layer != 0) { + lbc = layer.promote(); + LOGE_IF(lbc==0, "getLayerUser(name=%d) is dead", int(i)); + } + return lbc; +} + +sp<IMemoryHeap> Client::getControlBlock() const { + return 0; +} +ssize_t Client::getTokenForSurface(const sp<ISurface>& sur) const { + return -1; +} +sp<ISurface> Client::createSurface( + ISurfaceComposerClient::surface_data_t* params, int pid, + const String8& name, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) +{ + return mFlinger->createSurface(this, pid, name, params, + display, w, h, format, flags); +} +status_t Client::destroySurface(SurfaceID sid) { + return mFlinger->removeSurface(this, sid); +} +status_t Client::setState(int32_t count, const layer_state_t* states) { + return mFlinger->setClientState(this, count, states); +} + +// --------------------------------------------------------------------------- + +UserClient::UserClient(const sp<SurfaceFlinger>& flinger) + : ctrlblk(0), mBitmap(0), mFlinger(flinger) +{ + const int pgsize = getpagesize(); + const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1)); + + mCblkHeap = new MemoryHeapBase(cblksize, 0, + "SurfaceFlinger Client control-block"); + + ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) SharedClient; + } +} + +UserClient::~UserClient() +{ + if (ctrlblk) { + ctrlblk->~SharedClient(); // destroy our shared-structure. + } + + /* + * When a UserClient dies, it's unclear what to do exactly. + * We could go ahead and destroy all surfaces linked to that client + * however, it wouldn't be fair to the main Client + * (usually the the window-manager), which might want to re-target + * the layer to another UserClient. + * I think the best is to do nothing, or not much; in most cases the + * WM itself will go ahead and clean things up when it detects a client of + * his has died. + * The remaining question is what to display? currently we keep + * just keep the current buffer. + */ +} + +status_t UserClient::initCheck() const { + return ctrlblk == 0 ? NO_INIT : NO_ERROR; +} + +void UserClient::detachLayer(const Layer* layer) +{ + int32_t name = layer->getToken(); + if (name >= 0) { + int32_t mask = 1LU<<name; + if ((android_atomic_and(~mask, &mBitmap) & mask) == 0) { + LOGW("token %d wasn't marked as used %08x", name, int(mBitmap)); + } + } +} + +sp<IMemoryHeap> UserClient::getControlBlock() const { + return mCblkHeap; +} + +ssize_t UserClient::getTokenForSurface(const sp<ISurface>& sur) const +{ + int32_t name = NAME_NOT_FOUND; + sp<Layer> layer(mFlinger->getLayer(sur)); + if (layer == 0) return name; + + // if this layer already has a token, just return it + name = layer->getToken(); + if ((name >= 0) && (layer->getClient() == this)) + return name; + + name = 0; + do { + int32_t mask = 1LU<<name; + if ((android_atomic_or(mask, &mBitmap) & mask) == 0) { + // we found and locked that name + status_t err = layer->setToken( + const_cast<UserClient*>(this), ctrlblk, name); + if (err != NO_ERROR) { + // free the name + android_atomic_and(~mask, &mBitmap); + name = err; + } + break; + } + if (++name > 31) + name = NO_MEMORY; + } while(name >= 0); + + //LOGD("getTokenForSurface(%p) => %d (client=%p, bitmap=%08lx)", + // sur->asBinder().get(), name, this, mBitmap); + return name; +} + +sp<ISurface> UserClient::createSurface( + ISurfaceComposerClient::surface_data_t* params, int pid, + const String8& name, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags) { + return 0; +} +status_t UserClient::destroySurface(SurfaceID sid) { + return INVALID_OPERATION; +} +status_t UserClient::setState(int32_t count, const layer_state_t* states) { + return INVALID_OPERATION; +} + +// --------------------------------------------------------------------------- + +GraphicPlane::GraphicPlane() + : mHw(0) +{ +} + +GraphicPlane::~GraphicPlane() { + delete mHw; +} + +bool GraphicPlane::initialized() const { + return mHw ? true : false; +} + +int GraphicPlane::getWidth() const { + return mWidth; +} + +int GraphicPlane::getHeight() const { + return mHeight; +} + +void GraphicPlane::setDisplayHardware(DisplayHardware *hw) +{ + mHw = hw; + + // initialize the display orientation transform. + // it's a constant that should come from the display driver. + int displayOrientation = ISurfaceComposer::eOrientationDefault; + char property[PROPERTY_VALUE_MAX]; + if (property_get("ro.sf.hwrotation", property, NULL) > 0) { + //displayOrientation + switch (atoi(property)) { + case 90: + displayOrientation = ISurfaceComposer::eOrientation90; + break; + case 270: + displayOrientation = ISurfaceComposer::eOrientation270; + break; + } + } + + const float w = hw->getWidth(); + const float h = hw->getHeight(); + GraphicPlane::orientationToTransfrom(displayOrientation, w, h, + &mDisplayTransform); + if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) { + mDisplayWidth = h; + mDisplayHeight = w; + } else { + mDisplayWidth = w; + mDisplayHeight = h; + } + + setOrientation(ISurfaceComposer::eOrientationDefault); +} + +status_t GraphicPlane::orientationToTransfrom( + int orientation, int w, int h, Transform* tr) +{ + uint32_t flags = 0; + switch (orientation) { + case ISurfaceComposer::eOrientationDefault: + flags = Transform::ROT_0; + break; + case ISurfaceComposer::eOrientation90: + flags = Transform::ROT_90; + break; + case ISurfaceComposer::eOrientation180: + flags = Transform::ROT_180; + break; + case ISurfaceComposer::eOrientation270: + flags = Transform::ROT_270; + break; + default: + return BAD_VALUE; + } + tr->set(flags, w, h); + return NO_ERROR; +} + +status_t GraphicPlane::setOrientation(int orientation) +{ + // If the rotation can be handled in hardware, this is where + // the magic should happen. + + const DisplayHardware& hw(displayHardware()); + const float w = mDisplayWidth; + const float h = mDisplayHeight; + mWidth = int(w); + mHeight = int(h); + + Transform orientationTransform; + GraphicPlane::orientationToTransfrom(orientation, w, h, + &orientationTransform); + if (orientation & ISurfaceComposer::eOrientationSwapMask) { + mWidth = int(h); + mHeight = int(w); + } + + mOrientation = orientation; + mGlobalTransform = mDisplayTransform * orientationTransform; + return NO_ERROR; +} + +const DisplayHardware& GraphicPlane::displayHardware() const { + return *mHw; +} + +const Transform& GraphicPlane::transform() const { + return mGlobalTransform; +} + +EGLDisplay GraphicPlane::getEGLDisplay() const { + return mHw->getEGLDisplay(); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h new file mode 100644 index 0000000..0bfc170 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -0,0 +1,420 @@ +/* + * 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/RefBase.h> + +#include <binder/IMemory.h> +#include <binder/Permission.h> + +#include <ui/PixelFormat.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/ISurfaceComposerClient.h> + +#include "Barrier.h" +#include "Layer.h" + +#include "MessageQueue.h" + +struct copybit_device_t; +struct overlay_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class DisplayHardware; +class FreezeLock; +class Layer; +class LayerBlur; +class LayerDim; +class LayerBuffer; + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +class Client : public BnSurfaceComposerClient +{ +public: + Client(const sp<SurfaceFlinger>& flinger); + ~Client(); + + status_t initCheck() const; + + // protected by SurfaceFlinger::mStateLock + ssize_t attachLayer(const sp<LayerBaseClient>& layer); + void detachLayer(const LayerBaseClient* layer); + sp<LayerBaseClient> getLayerUser(int32_t i) const; + +private: + + // ISurfaceComposerClient interface + virtual sp<IMemoryHeap> getControlBlock() const; + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, const String8& name, + 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); + + DefaultKeyedVector< size_t, wp<LayerBaseClient> > mLayers; + sp<SurfaceFlinger> mFlinger; + int32_t mNameGenerator; +}; + +class UserClient : public BnSurfaceComposerClient +{ +public: + // pointer to this client's control block + SharedClient* ctrlblk; + +public: + UserClient(const sp<SurfaceFlinger>& flinger); + ~UserClient(); + + status_t initCheck() const; + + // protected by SurfaceFlinger::mStateLock + void detachLayer(const Layer* layer); + +private: + + // ISurfaceComposerClient interface + virtual sp<IMemoryHeap> getControlBlock() const; + virtual ssize_t getTokenForSurface(const sp<ISurface>& sur) const; + virtual sp<ISurface> createSurface( + surface_data_t* params, int pid, const String8& name, + 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); + + // atomic-ops + mutable volatile int32_t mBitmap; + + sp<IMemoryHeap> mCblkHeap; + sp<SurfaceFlinger> mFlinger; +}; + +// --------------------------------------------------------------------------- + +class GraphicPlane +{ +public: + static status_t orientationToTransfrom(int orientation, int w, int h, + Transform* tr); + + GraphicPlane(); + ~GraphicPlane(); + + bool initialized() const; + + void setDisplayHardware(DisplayHardware *); + status_t setOrientation(int orientation); + int getOrientation() const { return mOrientation; } + int getWidth() const; + int getHeight() const; + + const DisplayHardware& displayHardware() const; + const Transform& transform() const; + EGLDisplay getEGLDisplay() const; + +private: + GraphicPlane(const GraphicPlane&); + GraphicPlane operator = (const GraphicPlane&); + + DisplayHardware* mHw; + Transform mGlobalTransform; + Transform mDisplayTransform; + int mOrientation; + float mDisplayWidth; + float mDisplayHeight; + int mWidth; + int mHeight; +}; + +// --------------------------------------------------------------------------- + +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<ISurfaceComposerClient> createConnection(); + virtual sp<ISurfaceComposerClient> createClientConnection(); + virtual sp<IMemoryHeap> 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, uint32_t flags); + virtual void signal() const; + + void screenReleased(DisplayID dpy); + void screenAcquired(DisplayID dpy); + + overlay_control_device_t* getOverlayEngine() const; + + status_t removeLayer(const sp<LayerBase>& layer); + status_t addLayer(const sp<LayerBase>& layer); + status_t invalidateLayerVisibility(const sp<LayerBase>& layer); + + sp<Layer> getLayer(const sp<ISurface>& sur) const; + +private: + friend class Client; + friend class LayerBase; + friend class LayerBuffer; + friend class LayerBaseClient; + friend class LayerBaseClient::Surface; + friend class Layer; + friend class LayerBlur; + friend class LayerDim; + + sp<ISurface> createSurface(const sp<Client>& client, + int pid, const String8& name, + ISurfaceComposerClient::surface_data_t* params, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags); + + sp<Layer> createNormalSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format); + + sp<LayerBlur> createBlurSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerDim> createDimSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerBuffer> createPushBuffersSurface( + const sp<Client>& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags); + + status_t removeSurface(const sp<Client>& client, SurfaceID sid); + status_t destroySurface(const sp<LayerBaseClient>& layer); + status_t setClientState(const sp<Client>& client, + 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 sp<LayerBase> const* array() const { return layers.array(); } + ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t remove(const sp<LayerBase>&); + ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const; + inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; } + private: + KeyedVector< sp<LayerBase> , size_t> lookup; + Vector< sp<LayerBase> > layers; + }; + + struct State { + State() { + orientation = ISurfaceComposer::eOrientationDefault; + freezeDisplay = 0; + } + LayerVector layersSortedByZ; + uint8_t orientation; + uint8_t orientationType; + uint8_t freezeDisplay; + }; + + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + +public: // hack to work around gcc 4.0.3 bug + const GraphicPlane& graphicPlane(int dpy) const; + GraphicPlane& graphicPlane(int dpy); +private: + + void waitForEvent(); +public: // hack to work around gcc 4.0.3 bug + void signalEvent(); +private: + void handleConsoleEvents(); + void handleTransaction(uint32_t transactionFlags); + void handleTransactionLocked( + uint32_t transactionFlags, + Vector< sp<LayerBase> >& ditchedLayers); + + void computeVisibleRegions( + LayerVector& currentLayers, + Region& dirtyRegion, + Region& wormholeRegion); + + void handlePageFlip(); + bool lockPageFlip(const LayerVector& currentLayers); + void unlockPageFlip(const LayerVector& currentLayers); + void handleRepaint(); + void postFramebuffer(); + void composeSurfaces(const Region& dirty); + void unlockClients(); + + + ssize_t addClientLayer(const sp<Client>& client, + const sp<LayerBaseClient>& lbc); + status_t addLayer_l(const sp<LayerBase>& layer); + status_t removeLayer_l(const sp<LayerBase>& layer); + status_t purgatorizeLayer_l(const sp<LayerBase>& layer); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags); + void commitTransaction(); + + + friend class FreezeLock; + sp<FreezeLock> getFreezeLock() const; + inline void incFreezeCount() { + if (mFreezeCount == 0) + mFreezeDisplayTime = 0; + mFreezeCount++; + } + inline void decFreezeCount() { if (mFreezeCount > 0) mFreezeCount--; } + inline bool hasFreezeRequest() const { return mFreezeDisplay; } + inline bool isFrozen() const { + return (mFreezeDisplay || mFreezeCount>0) && mBootFinished; + } + + + void debugFlashRegions(); + void debugShowFPS() const; + void drawWormhole() const; + + + mutable MessageQueue mEventQueue; + + status_t postMessageAsync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t postMessageSync(const sp<MessageBase>& msg, + nsecs_t reltime=0, uint32_t flags = 0); + + // access must be protected by mStateLock + mutable Mutex mStateLock; + State mCurrentState; + State mDrawingState; + volatile int32_t mTransactionFlags; + volatile int32_t mTransactionCount; + Condition mTransactionCV; + bool mResizeTransationPending; + + // protected by mStateLock (but we could use another lock) + GraphicPlane mGraphicPlanes[1]; + bool mLayersRemoved; + DefaultKeyedVector< wp<IBinder>, wp<Layer> > mLayerMap; + + // constant members (no synchronization needed for access) + sp<IMemoryHeap> mServerHeap; + surface_flinger_cblk_t* mServerCblk; + GLuint mWormholeTexName; + nsecs_t mBootTime; + Permission mHardwareTest; + Permission mAccessSurfaceFlinger; + Permission mDump; + + // Can only accessed from the main thread, these members + // don't need synchronization + Region mDirtyRegion; + Region mDirtyRegionRemovedLayer; + Region mInvalidRegion; + Region mWormholeRegion; + bool mVisibleRegionsDirty; + bool mDeferReleaseConsole; + bool mFreezeDisplay; + int32_t mFreezeCount; + nsecs_t mFreezeDisplayTime; + + // don't use a lock for these, we don't care + int mDebugRegion; + int mDebugBackground; + volatile nsecs_t mDebugInSwapBuffers; + nsecs_t mLastSwapBufferTime; + volatile nsecs_t mDebugInTransaction; + nsecs_t mLastTransactionTime; + bool mBootFinished; + + // these are thread safe + mutable Barrier mReadyToRunBarrier; + + // 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(); + } +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp new file mode 100644 index 0000000..3b326df --- /dev/null +++ b/services/surfaceflinger/TextureManager.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "GLExtensions.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager() + : mGLExtensions(GLExtensions::getInstance()) +{ +} + +GLenum TextureManager::getTextureTarget(const Image* image) { +#if defined(GL_OES_texture_external) + switch (image->target) { + case Texture::TEXTURE_EXTERNAL: + return GL_TEXTURE_EXTERNAL_OES; + } +#endif + return GL_TEXTURE_2D; +} + +status_t TextureManager::initTexture(Texture* texture) +{ + if (texture->name != -1UL) + return INVALID_OPERATION; + + GLuint textureName = -1; + glGenTextures(1, &textureName); + texture->name = textureName; + texture->width = 0; + texture->height = 0; + + const GLenum target = GL_TEXTURE_2D; + glBindTexture(target, textureName); + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return NO_ERROR; +} + +status_t TextureManager::initTexture(Image* pImage, int32_t format) +{ + if (pImage->name != -1UL) + return INVALID_OPERATION; + + GLuint textureName = -1; + glGenTextures(1, &textureName); + pImage->name = textureName; + pImage->width = 0; + pImage->height = 0; + + GLenum target = GL_TEXTURE_2D; +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + if (format && isYuvFormat(format)) { + target = GL_TEXTURE_EXTERNAL_OES; + pImage->target = Texture::TEXTURE_EXTERNAL; + } + } +#endif + + glBindTexture(target, textureName); + glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + return NO_ERROR; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_YV12: + return true; + } + return false; +} + +bool TextureManager::isYuvFormat(int format) +{ + switch (format) { + // supported YUV formats + case HAL_PIXEL_FORMAT_YV12: + // Legacy/deprecated YUV formats + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: + return true; + } + + // Any OEM format needs to be considered + if (format>=0x100 && format<=0x1FF) + return true; + + return false; +} + +status_t TextureManager::initEglImage(Image* pImage, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_ERROR; + if (!pImage->dirty) return err; + + // free the previous image + if (pImage->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, pImage->image); + pImage->image = EGL_NO_IMAGE_KHR; + } + + // construct an EGL_NATIVE_BUFFER_ANDROID + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + // create the new EGLImageKHR + const EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, EGL_NONE + }; + pImage->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (pImage->image != EGL_NO_IMAGE_KHR) { + if (pImage->name == -1UL) { + initTexture(pImage, buffer->format); + } + const GLenum target = getTextureTarget(pImage); + glBindTexture(target, pImage->name); + glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", + pImage->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + pImage->dirty = false; + pImage->width = clientBuf->width; + pImage->height = clientBuf->height; + } + } else { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + +status_t TextureManager::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) +{ + if (texture->name == -1UL) { + status_t err = initTexture(texture); + LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err)); + return err; + } + + if (texture->target != GL_TEXTURE_2D) + return INVALID_OPERATION; + + glBindTexture(GL_TEXTURE_2D, texture->name); + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is 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 + */ + + int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); + unpack = 1 << ((unpack > 3) ? 3 : unpack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + + /* + * round to POT if needed + */ + if (!mGLExtensions.haveNpot()) { + texture->NPOTAdjust = true; + } + + if (texture->NPOTAdjust) { + // find the smallest power-of-two that will accommodate our surface + texture->potWidth = 1 << (31 - clz(t.width)); + texture->potHeight = 1 << (31 - clz(t.height)); + if (texture->potWidth < t.width) texture->potWidth <<= 1; + if (texture->potHeight < t.height) texture->potHeight <<= 1; + texture->wScale = float(t.width) / texture->potWidth; + texture->hScale = float(t.height) / texture->potHeight; + } else { + texture->potWidth = t.width; + texture->potHeight = t.height; + } + + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture->width != t.width || texture->height != t.height) { + texture->width = t.width; + texture->height = t.height; + + // texture size changed, we need to create a new one + bounds.set(Rect(t.width, t.height)); + if (t.width == texture->potWidth && + t.height == texture->potHeight) { + // we can do it one pass + data = t.data; + } + + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture->potWidth, texture->potHeight, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("texture=%d, using format %d, which is not " + "supported by the GL", texture->name, t.format); + } + } + if (!data) { + if (t.format == HAL_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.stride*2); + } else if (t.format == HAL_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.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride*4); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_LUMINANCE, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride); + } + } + return NO_ERROR; +} + +void TextureManager::activateTexture(const Texture& texture, bool filter) +{ + const GLenum target = getTextureTarget(&texture); + if (target == GL_TEXTURE_2D) { + glBindTexture(GL_TEXTURE_2D, texture.name); + glEnable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } + } else { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name); + glEnable(GL_TEXTURE_EXTERNAL_OES); + glDisable(GL_TEXTURE_2D); +#endif + } + + if (filter) { + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } +} + +void TextureManager::deactivateTextures() +{ + glDisable(GL_TEXTURE_2D); +#if defined(GL_OES_texture_external) + if (GLExtensions::getInstance().haveTextureExternal()) { + glDisable(GL_TEXTURE_EXTERNAL_OES); + } +#endif +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h new file mode 100644 index 0000000..c7c14e7 --- /dev/null +++ b/services/surfaceflinger/TextureManager.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 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_TEXTURE_MANAGER_H +#define ANDROID_TEXTURE_MANAGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <ui/Region.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class GLExtensions; +class GraphicBuffer; + +// --------------------------------------------------------------------------- + +struct Image { + enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 }; + Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0), + transform(0), dirty(1), target(TEXTURE_2D) { } + GLuint name; + EGLImageKHR image; + GLuint width; + GLuint height; + uint32_t transform; + unsigned dirty : 1; + unsigned target : 1; +}; + +struct Texture : public Image { + Texture() : Image(), NPOTAdjust(0) { } + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + unsigned NPOTAdjust : 1; +}; + +// --------------------------------------------------------------------------- + +class TextureManager { + const GLExtensions& mGLExtensions; + static status_t initTexture(Image* texture, int32_t format); + static status_t initTexture(Texture* texture); + static bool isSupportedYuvFormat(int format); + static bool isYuvFormat(int format); + static GLenum getTextureTarget(const Image* pImage); +public: + + TextureManager(); + + // load bitmap data into the active buffer + status_t loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(Image* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer); + + // activate a texture + static void activateTexture(const Texture& texture, bool filter); + + // deactivate a texture + static void deactivateTextures(); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_TEXTURE_MANAGER_H diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp new file mode 100644 index 0000000..5e27cc9 --- /dev/null +++ b/services/surfaceflinger/Transform.cpp @@ -0,0 +1,391 @@ +/* + * 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 <math.h> + +#include <cutils/compiler.h> +#include <utils/String8.h> +#include <ui/Region.h> + +#include "Transform.h" + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +template <typename T> inline T min(T a, T b) { + return a<b ? a : b; +} +template <typename T> inline T min(T a, T b, T c) { + return min(a, min(b, c)); +} +template <typename T> inline T min(T a, T b, T c, T d) { + return min(a, b, min(c, d)); +} + +template <typename T> inline T max(T a, T b) { + return a>b ? a : b; +} +template <typename T> inline T max(T a, T b, T c) { + return max(a, max(b, c)); +} +template <typename T> inline T max(T a, T b, T c, T d) { + return max(a, b, max(c, d)); +} + +// --------------------------------------------------------------------------- + +Transform::Transform() { + reset(); +} + +Transform::Transform(const Transform& other) + : mMatrix(other.mMatrix), mType(other.mType) { +} + +Transform::Transform(uint32_t orientation) { + set(orientation, 0, 0); +} + +Transform::~Transform() { +} + +static const float EPSILON = 0.0f; + +bool Transform::isZero(float f) { + return fabs(f) <= EPSILON; +} + +bool Transform::absIsOne(float f) { + return isZero(fabs(f) - 1.0f); +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (CC_LIKELY(mType == IDENTITY)) + return rhs; + + Transform r(*this); + if (rhs.mType == IDENTITY) + return r; + + // TODO: we could use mType to optimize the matrix multiply + const mat33& A(mMatrix); + const mat33& B(rhs.mMatrix); + mat33& D(r.mMatrix); + for (int i=0 ; i<3 ; i++) { + const float v0 = A[0][i]; + const float v1 = A[1][i]; + const float v2 = A[2][i]; + D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; + D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; + D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; + } + r.mType |= rhs.mType; + + // TODO: we could recompute this value from r and rhs + r.mType &= 0xFF; + r.mType |= UNKNOWN_TYPE; + return r; +} + +float const* Transform::operator [] (int i) const { + return mMatrix[i].v; +} + +bool Transform::transformed() const { + return type() > TRANSLATE; +} + +int Transform::tx() const { + return floorf(mMatrix[2][0] + 0.5f); +} + +int Transform::ty() const { + return floorf(mMatrix[2][1] + 0.5f); +} + +void Transform::reset() { + mType = IDENTITY; + for(int i=0 ; i<3 ; i++) { + vec3& v(mMatrix[i]); + for (int j=0 ; j<3 ; j++) + v[j] = ((i==j) ? 1.0f : 0.0f); + } +} + +void Transform::set(float tx, float ty) +{ + mMatrix[2][0] = tx; + mMatrix[2][1] = ty; + mMatrix[2][2] = 1.0f; + + if (isZero(tx) && isZero(ty)) { + mType &= ~TRANSLATE; + } else { + mType |= TRANSLATE; + } +} + +void Transform::set(float a, float b, float c, float d) +{ + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; + M[0][1] = c; M[1][1] = d; + M[0][2] = 0; M[1][2] = 0; + mType = UNKNOWN_TYPE; +} + +status_t Transform::set(uint32_t flags, float w, float h) +{ + if (flags & ROT_INVALID) { + // that's not allowed! + reset(); + return BAD_VALUE; + } + + mType = flags << 8; + float sx = (flags & FLIP_H) ? -1 : 1; + float sy = (flags & FLIP_V) ? -1 : 1; + float a=0, b=0, c=0, d=0, x=0, y=0; + int xmask = 0; + + // computation of x,y + // x y + // 0 0 0 + // w 0 ROT90 + // w h FLIPH|FLIPV + // 0 h FLIPH|FLIPV|ROT90 + + if (flags & ROT_90) { + mType |= ROTATE; + b = -sy; + c = sx; + xmask = 1; + } else { + a = sx; + d = sy; + } + + if (flags & FLIP_H) { + mType ^= SCALE; + xmask ^= 1; + } + + if (flags & FLIP_V) { + mType ^= SCALE; + y = h; + } + + if ((flags & ROT_180) == ROT_180) { + mType |= ROTATE; + } + + if (xmask) { + x = w; + } + + if (!isZero(x) || !isZero(y)) { + mType |= TRANSLATE; + } + + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; M[2][0] = x; + M[0][1] = c; M[1][1] = d; M[2][1] = y; + M[0][2] = 0; M[1][2] = 0; M[2][2] = 1; + + return NO_ERROR; +} + +Transform::vec2 Transform::transform(const vec2& v) const { + vec2 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; + return r; +} + +Transform::vec3 Transform::transform(const vec3& v) const { + vec3 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; + r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; + return r; +} + +void Transform::transform(float* point, int x, int y) const +{ + const mat33& M(mMatrix); + vec2 v(x, y); + v = transform(v); + point[0] = v[0]; + point[1] = v[1]; +} + +Rect Transform::makeBounds(int w, int h) const +{ + return transform( Rect(w, h) ); +} + +Rect Transform::transform(const Rect& bounds) const +{ + Rect r; + vec2 lt( bounds.left, bounds.top ); + vec2 rt( bounds.right, bounds.top ); + vec2 lb( bounds.left, bounds.bottom ); + vec2 rb( bounds.right, bounds.bottom ); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + r.left = floorf(min(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.top = floorf(min(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + r.right = floorf(max(lt[0], rt[0], lb[0], rb[0]) + 0.5f); + r.bottom = floorf(max(lt[1], rt[1], lb[1], rb[1]) + 0.5f); + + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (CC_UNLIKELY(transformed())) { + if (CC_LIKELY(preserveRects())) { + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + while (it != end) { + out.orSelf(transform(*it++)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + out = reg.translate(tx(), ty()); + } + return out; +} + +uint32_t Transform::type() const +{ + if (mType & UNKNOWN_TYPE) { + // recompute what this transform is + + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + bool scale = false; + uint32_t flags = ROT_0; + if (isZero(b) && isZero(c)) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + if (!absIsOne(a) || !absIsOne(d)) { + scale = true; + } + } else if (isZero(a) && isZero(d)) { + flags |= ROT_90; + if (b>0) flags |= FLIP_H; + if (c<0) flags |= FLIP_V; + if (!absIsOne(b) || !absIsOne(c)) { + scale = true; + } + } else { + flags = ROT_INVALID; + } + + mType = flags << 8; + if (flags & ROT_INVALID) { + mType |= UNKNOWN; + } else { + if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) + mType |= ROTATE; + if (flags & FLIP_H) + mType ^= SCALE; + if (flags & FLIP_V) + mType ^= SCALE; + if (scale) + mType |= SCALE; + } + + if (!isZero(x) || !isZero(y)) + mType |= TRANSLATE; + } + return mType; +} + +uint32_t Transform::getType() const { + return type() & 0xFF; +} + +uint32_t Transform::getOrientation() const +{ + return (type() >> 8) & 0xFF; +} + +bool Transform::preserveRects() const +{ + return (type() & ROT_INVALID) ? false : true; +} + +void Transform::dump(const char* name) const +{ + type(); // updates the type + + String8 flags, type; + const mat33& m(mMatrix); + uint32_t orient = mType >> 8; + + if (orient&ROT_INVALID) { + flags.append("ROT_INVALID "); + } else { + if (orient&ROT_90) { + flags.append("ROT_90 "); + } else { + flags.append("ROT_0 "); + } + if (orient&FLIP_V) + flags.append("FLIP_V "); + if (orient&FLIP_H) + flags.append("FLIP_H "); + } + + if (!(mType&(SCALE|ROTATE|TRANSLATE))) + type.append("IDENTITY "); + if (mType&SCALE) + type.append("SCALE "); + if (mType&ROTATE) + type.append("ROTATE "); + if (mType&TRANSLATE) + type.append("TRANSLATE "); + + LOGD("%s 0x%08x (%s, %s)", name, mType, flags.string(), type.string()); + LOGD("%.4f %.4f %.4f", m[0][0], m[1][0], m[2][0]); + LOGD("%.4f %.4f %.4f", m[0][1], m[1][1], m[2][1]); + LOGD("%.4f %.4f %.4f", m[0][2], m[1][2], m[2][2]); +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h new file mode 100644 index 0000000..20fa11a --- /dev/null +++ b/services/surfaceflinger/Transform.h @@ -0,0 +1,126 @@ +/* + * 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> + +namespace android { + +class Region; + +// --------------------------------------------------------------------------- + +class Transform +{ +public: + Transform(); + Transform(const Transform& other); + explicit Transform(uint32_t orientation); + ~Transform(); + + // FIXME: must match OVERLAY_TRANSFORM_*, pull from hardware.h + 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 = 0x80 + }; + + enum type_mask { + IDENTITY = 0, + TRANSLATE = 0x1, + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 + }; + + // query the transform + bool transformed() const; + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + float const* operator [] (int i) const; // returns column i + int tx() const; + int ty() const; + + // modify the transform + void reset(); + void set(float tx, float ty); + void set(float a, float b, float c, float d); + status_t set(uint32_t flags, float w, float h); + + // transform data + Rect makeBounds(int w, int h) const; + void transform(float* point, int x, int y) const; + Region transform(const Region& reg) const; + Transform operator * (const Transform& rhs) const; + + // for debugging + void dump(const char* name) const; + +private: + struct vec3 { + float v[3]; + inline vec3() { } + inline vec3(float a, float b, float c) { + v[0] = a; v[1] = b; v[2] = c; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct vec2 { + float v[2]; + inline vec2() { } + inline vec2(float a, float b) { + v[0] = a; v[1] = b; + } + inline float operator [] (int i) const { return v[i]; } + inline float& operator [] (int i) { return v[i]; } + }; + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (int i) const { return v[i]; } + inline vec3& operator [] (int i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + Rect transform(const Rect& bounds) const; + uint32_t type() const; + static bool absIsOne(float f); + static bool isZero(float f); + + mat33 mMatrix; + mutable uint32_t mType; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif /* ANDROID_TRANSFORM_H */ diff --git a/services/surfaceflinger/clz.cpp b/services/surfaceflinger/clz.cpp new file mode 100644 index 0000000..2456b86 --- /dev/null +++ b/services/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/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h new file mode 100644 index 0000000..0ddf986 --- /dev/null +++ b/services/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/services/surfaceflinger/tests/Android.mk b/services/surfaceflinger/tests/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/services/surfaceflinger/tests/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/services/surfaceflinger/tests/overlays/Android.mk b/services/surfaceflinger/tests/overlays/Android.mk new file mode 100644 index 0000000..592b601 --- /dev/null +++ b/services/surfaceflinger/tests/overlays/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + overlays.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-overlays + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/surfaceflinger/tests/overlays/overlays.cpp b/services/surfaceflinger/tests/overlays/overlays.cpp new file mode 100644 index 0000000..c248a615 --- /dev/null +++ b/services/surfaceflinger/tests/overlays/overlays.cpp @@ -0,0 +1,59 @@ +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> + +#include <ui/Overlay.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/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; +} diff --git a/services/surfaceflinger/tests/resize/Android.mk b/services/surfaceflinger/tests/resize/Android.mk new file mode 100644 index 0000000..24c2d01 --- /dev/null +++ b/services/surfaceflinger/tests/resize/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + resize.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui \ + libsurfaceflinger_client + +LOCAL_MODULE:= test-resize + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp new file mode 100644 index 0000000..127cca3 --- /dev/null +++ b/services/surfaceflinger/tests/resize/resize.cpp @@ -0,0 +1,62 @@ +#include <cutils/memory.h> + +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/ISurface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <ui/Overlay.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, 160, 240, + PIXEL_FORMAT_RGB_565); + + + client->openTransaction(); + surface->setLayer(100000); + client->closeTransaction(); + + Surface::SurfaceInfo info; + surface->lock(&info); + ssize_t bpr = info.s * bytesPerPixel(info.format); + android_memset16((uint16_t*)info.bits, 0xF800, bpr*info.h); + surface->unlockAndPost(); + + surface->lock(&info); + android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h); + surface->unlockAndPost(); + + client->openTransaction(); + surface->setSize(320, 240); + client->closeTransaction(); + + + IPCThreadState::self()->joinThreadPool(); + + return 0; +} |