diff options
Diffstat (limited to 'services/surfaceflinger')
34 files changed, 8655 insertions, 0 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk new file mode 100644 index 0000000..86eb78d --- /dev/null +++ b/services/surfaceflinger/Android.mk @@ -0,0 +1,51 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + clz.cpp.arm \ + DisplayHardware/DisplayHardware.cpp \ + DisplayHardware/DisplayHardwareBase.cpp \ + BlurFilter.cpp.arm \ + Layer.cpp \ + LayerBase.cpp \ + LayerBuffer.cpp \ + LayerBlur.cpp \ + LayerDim.cpp \ + MessageQueue.cpp \ + SurfaceFlinger.cpp \ + Tokenizer.cpp \ + Transform.cpp + +LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +ifeq ($(TARGET_BOARD_PLATFORM), msm7k) + LOCAL_CFLAGS += -DDIM_WITH_TEXTURE +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..e2bcf6a --- /dev/null +++ b/services/surfaceflinger/Barrier.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BARRIER_H +#define ANDROID_BARRIER_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> + +namespace android { + +class Barrier +{ +public: + inline Barrier() : state(CLOSED) { } + inline ~Barrier() { } + void open() { + // gcc memory barrier, this makes sure all memory writes + // have been issued by gcc. On an SMP system we'd need a real + // h/w barrier. + asm volatile ("":::"memory"); + Mutex::Autolock _l(lock); + state = OPENED; + cv.broadcast(); + } + void close() { + Mutex::Autolock _l(lock); + state = CLOSED; + } + void wait() const { + Mutex::Autolock _l(lock); + while (state == CLOSED) { + cv.wait(lock); + } + } +private: + enum { OPENED, CLOSED }; + mutable Mutex lock; + mutable Condition cv; + volatile int state; +}; + +}; // namespace android + +#endif // ANDROID_BARRIER_H diff --git a/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..ea68352 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -0,0 +1,364 @@ +/* + * 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> + +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) +{ + 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(); + + mOverlayEngine = NULL; + hw_module_t const* module; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } + + // 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; + } + } + + EGLint w, h, dummy; + EGLint numConfigs=0; + EGLSurface surface; + EGLContext context; + mFlags = CACHED_BUFFERS; + + // 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); + + /* + * Gather EGL extensions + */ + + const char* const egl_extensions = eglQueryString( + display, EGL_EXTENSIONS); + + LOGI("EGL informations:"); + LOGI("# of configs : %d", numConfigs); + LOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + LOGI("version : %s", eglQueryString(display, EGL_VERSION)); + LOGI("extensions: %s", egl_extensions); + LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); + LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + + + 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); + + 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; + } + } + + eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); + +#ifdef EGL_ANDROID_swap_rectangle + if (strstr(egl_extensions, "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("flags : %08x", mFlags); + + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; + + /* 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); + + /* + * Gather OpenGL ES extensions + */ + + eglMakeCurrent(display, surface, surface, context); + const char* const gl_extensions = (const char*)glGetString(GL_EXTENSIONS); + const char* const gl_renderer = (const char*)glGetString(GL_RENDERER); + LOGI("OpenGL informations:"); + LOGI("vendor : %s", glGetString(GL_VENDOR)); + LOGI("renderer : %s", gl_renderer); + LOGI("version : %s", glGetString(GL_VERSION)); + LOGI("extensions: %s", gl_extensions); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); + LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); + LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); + +#if 0 + // for drivers that don't have proper support for flushing cached buffers + // on gralloc unlock, uncomment this block and test for the specific + // renderer substring + if (strstr(gl_renderer, "<some vendor string>")) { + LOGD("Assuming uncached graphics buffers."); + mFlags &= ~CACHED_BUFFERS; + } +#endif + + if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { + mFlags |= NPOT_EXTENSION; + } + if (strstr(gl_extensions, "GL_OES_draw_texture")) { + mFlags |= DRAW_TEXTURE_EXTENSION; + } +#ifdef EGL_ANDROID_image_native_buffer + if (strstr( gl_extensions, "GL_OES_EGL_image") && + (strstr(egl_extensions, "EGL_KHR_image_base") || + strstr(egl_extensions, "EGL_KHR_image")) && + strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) { + mFlags |= DIRECT_TEXTURE; + } +#else +#warning "EGL_ANDROID_image_native_buffer not supported" +#endif + + + // Unbind the context from this thread + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + mDisplay = display; + mConfig = config; + mSurface = surface; + mContext = context; + mFormat = fbDev->format; + mPageFlipCount = 0; +} + +/* + * 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..df046af --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -0,0 +1,118 @@ +/* + * 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 "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 { + DIRECT_TEXTURE = 0x00000002, + COPY_BITS_EXTENSION = 0x00000008, + NPOT_EXTENSION = 0x00000100, + DRAW_TEXTURE_EXTENSION = 0x00000200, + BUFFER_PRESERVED = 0x00010000, + PARTIAL_UPDATES = 0x00020000, // video driver feature + SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, + CACHED_BUFFERS = 0x00100000 + }; + + 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/Layer.cpp b/services/surfaceflinger/Layer.cpp new file mode 100644 index 0000000..ce7e9aa --- /dev/null +++ b/services/surfaceflinger/Layer.cpp @@ -0,0 +1,630 @@ +/* + * 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 "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; +} + +// --------------------------------------------------------------------------- + +const uint32_t Layer::typeInfo = LayerBaseClient::typeInfo | 4; +const char* const Layer::typeID = "Layer"; + +// --------------------------------------------------------------------------- + +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& c, int32_t i) + : LayerBaseClient(flinger, display, c, i), + mSecure(false), + mNoEGLImageForSwBuffers(false), + mNeedsBlending(true), + mNeedsDithering(false) +{ + // no OpenGL operation is possible here, since we might not be + // in the OpenGL thread. + mFrontBufferIndex = lcblk->getFrontBuffer(); +} + +Layer::~Layer() +{ + destroy(); + // the actual buffers will be destroyed here +} + +void Layer::destroy() +{ + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + if (mTextures[i].name != -1U) { + glDeleteTextures(1, &mTextures[i].name); + mTextures[i].name = -1U; + } + if (mTextures[i].image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTextures[i].image); + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + Mutex::Autolock _l(mLock); + mBuffers[i].clear(); + mWidth = mHeight = 0; + } + mSurface.clear(); +} + +sp<LayerBaseClient::Surface> Layer::createSurface() const +{ + return mSurface; +} + +status_t Layer::ditch() +{ + // the layer is not on screen anymore. free as much resources as possible + mFreezeLock.clear(); + destroy(); + return NO_ERROR; +} + +status_t Layer::setBuffers( uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags) +{ + // this surfaces pixel format + PixelFormatInfo info; + status_t err = getPixelFormatInfo(format, &info); + if (err) return err; + + // 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; + mWidth = w; + mHeight = h; + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; + mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); + + // we use the red index + int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); + int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); + mNeedsDithering = layerRedsize > displayRedSize; + + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mBuffers[i] = new GraphicBuffer(); + } + mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); + return NO_ERROR; +} + +void Layer::reloadTexture(const Region& dirty) +{ + Mutex::Autolock _l(mLock); + sp<GraphicBuffer> buffer(getFrontBufferLocked()); + 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; + } + + const int index = mFrontBufferIndex; + + // create the new texture name if needed + if (UNLIKELY(mTextures[index].name == -1U)) { + mTextures[index].name = createTexture(); + mTextures[index].width = 0; + mTextures[index].height = 0; + } + +#ifdef EGL_ANDROID_image_native_buffer + if (mFlags & DisplayHardware::DIRECT_TEXTURE) { + if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { + if (mTextures[index].dirty) { + if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + goto slowpath; + } + } + } else { + if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || + mHybridBuffer->height != buffer->height)) { + mHybridBuffer.clear(); + mHybridBuffer = new GraphicBuffer( + buffer->width, buffer->height, buffer->format, + GraphicBuffer::USAGE_SW_WRITE_OFTEN | + GraphicBuffer::USAGE_HW_TEXTURE); + if (initializeEglImage( + mHybridBuffer, &mTextures[0]) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + mHybridBuffer.clear(); + goto 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) { + Texture* const texture(&mTextures[0]); + + glBindTexture(GL_TEXTURE_2D, texture->name); + + sp<GraphicBuffer> buf(mHybridBuffer); + void* vaddr; + res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); + if (res == NO_ERROR) { + int bpp = 0; + switch (t.format) { + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_4444: + bpp = 2; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + bpp = 4; + break; + default: + if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + bpp = 1; + break; + } + // oops, we don't handle this format! + LOGE("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, texture->name, t.format); + } + if (bpp) { + const Rect bounds(dirty.getBounds()); + size_t src_stride = t.stride; + size_t dst_stride = buf->stride; + if (src_stride == dst_stride && + bounds.width() == t.width && + bounds.height() == t.height) + { + memcpy(vaddr, t.data, t.height * t.stride * bpp); + } else { + GLubyte const * src = t.data + + (bounds.left + bounds.top * src_stride) * bpp; + GLubyte * dst = (GLubyte *)vaddr + + (bounds.left + bounds.top * dst_stride) * bpp; + const size_t length = bounds.width() * bpp; + size_t h = bounds.height(); + src_stride *= bpp; + dst_stride *= bpp; + while (h--) { + memcpy(dst, src, length); + dst += dst_stride; + src += src_stride; + } + } + } + buf->unlock(); + } + buffer->unlock(); + } + } + } else +#endif + { +slowpath: + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + mTextures[i].image = EGL_NO_IMAGE_KHR; + } + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + loadTexture(&mTextures[0], dirty, t); + buffer->unlock(); + } + } +} + +void Layer::onDraw(const Region& clip) const +{ + int index = mFrontBufferIndex; + if (mTextures[index].image == EGL_NO_IMAGE_KHR) + index = 0; + GLuint textureName = mTextures[index].name; + if (UNLIKELY(textureName == -1LU)) { + // the texture has not been created yet, this Layer has + // in fact never been drawn into. This happens frequently with + // SurfaceView 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); + } + return; + } + drawWithOpenGL(clip, mTextures[index]); +} + +sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + + // this ensures our client doesn't go away while we're accessing + // the shared area. + sp<Client> ourClient(client.promote()); + if (ourClient == 0) { + // oops, the client is already gone + return buffer; + } + + /* + * This is called from the client's Surface::dequeue(). This can happen + * at any time, especially while we're in the middle of using the + * buffer 'index' as our front buffer. + * + * Make sure the buffer we're resizing is not the front buffer and has been + * dequeued. Once this condition is asserted, we are guaranteed that this + * buffer cannot become the front buffer under our feet, since we're called + * from Surface::dequeue() + */ + status_t err = lcblk->assertReallocate(index); + LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err)); + if (err != NO_ERROR) { + // the surface may have died + return buffer; + } + + uint32_t w, h; + { // scope for the lock + Mutex::Autolock _l(mLock); + w = mWidth; + h = mHeight; + buffer = mBuffers[index]; + + // destroy() could have been called before we get here, we log it + // because it's uncommon, and the code below should handle it + LOGW_IF(buffer==0, + "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", + index, w, h); + + mBuffers[index].clear(); + } + + const uint32_t effectiveUsage = getEffectiveUsage(usage); + if (buffer!=0 && buffer->getStrongCount() == 1) { + err = buffer->reallocate(w, h, mFormat, 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, mFormat, 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); + if (mWidth && mHeight) { + // and we have new buffer + mBuffers[index] = buffer; + // texture is now dirty... + mTextures[index].dirty = true; + } else { + // oops we got killed while we were allocating the buffer + buffer.clear(); + } + } + return buffer; +} + +uint32_t Layer::getEffectiveUsage(uint32_t usage) const +{ + /* + * 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. + if (mNoEGLImageForSwBuffers) { + if (usage & GraphicBuffer::USAGE_HW_MASK) { + // request EGLImage for h/w buffers only + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + } else { + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + } + } + return usage; +} + +uint32_t Layer::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if ((front.requested_w != temp.requested_w) || + (front.requested_h != temp.requested_h)) { + // the size changed, we need to ask our client to request a new buffer + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), requested (%dx%d), " + "drawing (%d,%d), (%dx%d), (%dx%d)", + this, + int(temp.requested_w), int(temp.requested_h), + int(front.requested_w), int(front.requested_h), + int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), + int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); + + // we're being resized and there is a freeze display request, + // acquire a freeze lock, so that the screen stays put + // until we've redrawn at the new size; this is to avoid + // glitches upon orientation changes. + if (mFlinger->hasFreezeRequest()) { + // if the surface is hidden, don't try to acquire the + // freeze lock, since hidden surfaces may never redraw + if (!(front.flags & ISurfaceComposer::eLayerHidden)) { + mFreezeLock = mFlinger->getFreezeLock(); + } + } + + // this will make sure LayerBase::doTransaction doesn't update + // the drawing state's size + Layer::State& editDraw(mDrawingState); + editDraw.requested_w = temp.requested_w; + editDraw.requested_h = temp.requested_h; + + // record the new size, form this point on, when the client request a + // buffer, it'll get the new size. + setDrawingSize(temp.requested_w, temp.requested_h); + + // all buffers need reallocation + lcblk->reallocate(); + } + + if (temp.sequence != front.sequence) { + if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) { + // this surface is now hidden, so it shouldn't hold a freeze lock + // (it may never redraw, which is fine if it is hidden) + mFreezeLock.clear(); + } + } + + return LayerBase::doTransaction(flags); +} + +void Layer::setDrawingSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mLock); + mWidth = w; + mHeight = h; +} + +// ---------------------------------------------------------------------------- +// pageflip handling... +// ---------------------------------------------------------------------------- + +void Layer::lockPageFlip(bool& recomputeVisibleRegions) +{ + ssize_t buf = lcblk->retireAndLock(); + if (buf < NO_ERROR) { + //LOGW("nothing to retire (%s)", strerror(-buf)); + // NOTE: here the buffer is locked because we will used + // for composition later in the loop + return; + } + + // ouch, this really should never happen + if (uint32_t(buf)>=NUM_BUFFERS) { + LOGE("retireAndLock() buffer index (%d) out of range", buf); + mPostedDirtyRegion.clear(); + return; + } + + // we retired a buffer, which becomes the new front buffer + mFrontBufferIndex = buf; + + // 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(); + } + + if (!mPostedDirtyRegion.isEmpty()) { + 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 thereore never release it) + mFreezeLock.clear(); + } +} + +void Layer::finishPageFlip() +{ + status_t err = lcblk->unlock( mFrontBufferIndex ); + LOGE_IF(err!=NO_ERROR, + "layer %p, buffer=%d wasn't locked!", + this, mFrontBufferIndex); +} + +// --------------------------------------------------------------------------- + +Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner) + : Surface(flinger, id, owner->getIdentity(), owner) +{ +} + +Layer::SurfaceLayer::~SurfaceLayer() +{ +} + +sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) +{ + sp<GraphicBuffer> buffer; + sp<Layer> owner(getOwner()); + if (owner != 0) { + LOGE_IF(uint32_t(index)>=NUM_BUFFERS, + "getBuffer() index (%d) out of range", index); + if (uint32_t(index) < NUM_BUFFERS) { + buffer = owner->requestBuffer(index, usage); + } + } + return buffer; +} + +// --------------------------------------------------------------------------- + + +}; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h new file mode 100644 index 0000000..743afb4 --- /dev/null +++ b/services/surfaceflinger/Layer.h @@ -0,0 +1,134 @@ +/* + * 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" + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class FreezeLock; + +// --------------------------------------------------------------------------- + +const size_t NUM_BUFFERS = 2; + +class Layer : public LayerBaseClient +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + Layer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + + virtual ~Layer(); + + status_t setBuffers(uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags=0); + + void setDrawingSize(uint32_t w, uint32_t h); + + 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 isSecure() const { return mSecure; } + virtual sp<Surface> createSurface() const; + virtual status_t ditch(); + + // only for debugging + inline sp<GraphicBuffer> getBuffer(int i) { return mBuffers[i]; } + // only for debugging + inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } + // only for debugging + inline PixelFormat pixelFormat() const { return mFormat; } + // only for debugging + inline int getFrontBufferIndex() const { return mFrontBufferIndex; } + +private: + inline sp<GraphicBuffer> getFrontBufferLocked() { + return mBuffers[mFrontBufferIndex]; + } + + void reloadTexture(const Region& dirty); + + uint32_t getEffectiveUsage(uint32_t usage) const; + + sp<GraphicBuffer> requestBuffer(int index, int usage); + void destroy(); + + class SurfaceLayer : public LayerBaseClient::Surface { + public: + SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner); + ~SurfaceLayer(); + private: + virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + sp<Layer> getOwner() const { + return static_cast<Layer*>(Surface::getOwner().get()); + } + }; + friend class SurfaceLayer; + + sp<Surface> mSurface; + + bool mSecure; + bool mNoEGLImageForSwBuffers; + int32_t mFrontBufferIndex; + bool mNeedsBlending; + bool mNeedsDithering; + Region mPostedDirtyRegion; + sp<FreezeLock> mFreezeLock; + PixelFormat mFormat; + + // protected by mLock + sp<GraphicBuffer> mBuffers[NUM_BUFFERS]; + Texture mTextures[NUM_BUFFERS]; + sp<GraphicBuffer> mHybridBuffer; + uint32_t mWidth; + uint32_t mHeight; + + mutable Mutex mLock; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_H diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp new file mode 100644 index 0000000..a8b735e --- /dev/null +++ b/services/surfaceflinger/LayerBase.cpp @@ -0,0 +1,829 @@ +/* + * 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" + + +namespace android { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBase::typeInfo = 1; +const char* const LayerBase::typeID = "LayerBase"; + +const uint32_t LayerBaseClient::typeInfo = LayerBase::typeInfo | 2; +const char* const LayerBaseClient::typeID = "LayerBaseClient"; + +// --------------------------------------------------------------------------- + +LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) + : dpy(display), contentDirty(false), + mFlinger(flinger), + mTransformed(false), + mUseLinearFiltering(false), + mOrientation(0), + mLeft(0), mTop(0), + mTransactionFlags(0), + mPremultipliedAlpha(true), 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) { + // TODO: check the matrix has changed + mCurrentState.sequence++; + mCurrentState.transform.set( + matrix.dsdx, matrix.dsdy, matrix.dtdx, matrix.dtdy); + requestTransaction(); + return true; +} +bool LayerBase::setTransparentRegionHint(const Region& transparent) { + // TODO: check the region has changed + mCurrentState.sequence++; + mCurrentState.transparentRegion = transparent; + requestTransaction(); + return true; +} +bool LayerBase::setFlags(uint8_t flags, uint8_t mask) { + const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); + if (mCurrentState.flags == newFlags) + return false; + mCurrentState.sequence++; + mCurrentState.flags = newFlags; + requestTransaction(); + return true; +} + +Rect LayerBase::visibleBounds() const +{ + return mTransformedBounds; +} + +void LayerBase::setVisibleRegion(const Region& visibleRegion) { + // always called from main thread + visibleRegionScreen = visibleRegion; +} + +void LayerBase::setCoveredRegion(const Region& coveredRegion) { + // always called from main thread + coveredRegionScreen = coveredRegion; +} + +uint32_t LayerBase::doTransaction(uint32_t flags) +{ + const Layer::State& front(drawingState()); + const Layer::State& temp(currentState()); + + if ((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; + + const bool linearFiltering = mUseLinearFiltering; + mUseLinearFiltering = 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)) { + mUseLinearFiltering = 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); + mTransformed = transformed; + 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); + + /* + glDisable(GL_TEXTURE_2D); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0x8000, 0, 0x10000); + drawRegion(transparentRegionScreen); + glDisable(GL_BLEND); + */ +} + +GLuint LayerBase::createTexture() const +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + return textureName; +} + +void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, + GLclampx green, GLclampx blue, + GLclampx alpha) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + glColor4x(red,green,blue,alpha); + glDisable(GL_TEXTURE_2D); + 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_FIXED, 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 + validateTexture(texture.name); + uint32_t width = texture.width; + uint32_t height = texture.height; + + glEnable(GL_TEXTURE_2D); + + if (UNLIKELY(s.alpha < 0xFF)) { + // We have an alpha-modulation. We need to modulate all + // texture components by alpha because we're always using + // premultiplied alpha. + + // If the texture doesn't have an alpha channel we can + // use REPLACE and switch to non premultiplied alpha + // blending (SRCA/ONE_MINUS_SRCA). + + GLenum env, src; + if (needsBlending()) { + env = GL_MODULATE; + src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + } else { + env = GL_REPLACE; + src = GL_SRC_ALPHA; + } + const GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(alpha, alpha, alpha, alpha); + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); + } else { + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4x(0x10000, 0x10000, 0x10000, 0x10000); + if (needsBlending()) { + GLenum src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + } + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + + //StopWatch watch("GL transformed"); + const GLfixed texCoords[4][2] = { + { 0, 0 }, + { 0, 0x10000 }, + { 0x10000, 0x10000 }, + { 0x10000, 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); + } + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 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::validateTexture(GLint textureName) const +{ + glBindTexture(GL_TEXTURE_2D, textureName); + // TODO: reload the texture if needed + // this is currently done in loadTexture() below + if (mUseLinearFiltering) { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + + if (needsDithering()) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } +} + +bool LayerBase::isSupportedYuvFormat(int format) const +{ + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_P: + case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_I: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return true; + } + return false; +} + +void LayerBase::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) const +{ + if (texture->name == -1U) { + // uh? + return; + } + + 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 (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + 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("layer %p, texture=%d, using format %d, which is not " + "supported by the GL", this, 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); + } + } +} + +status_t LayerBase::initializeEglImage( + const sp<GraphicBuffer>& buffer, Texture* texture) +{ + status_t err = NO_ERROR; + + // we need to recreate the texture + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + + // free the previous image + if (texture->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, texture->image); + texture->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 + }; + texture->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (texture->image != EGL_NO_IMAGE_KHR) { + glBindTexture(GL_TEXTURE_2D, texture->name); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, + (GLeglImageOES)texture->image); + GLint error = glGetError(); + if (UNLIKELY(error != GL_NO_ERROR)) { + LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " + "failed err=0x%04x", + this, texture->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + texture->NPOTAdjust = false; + texture->dirty = false; + texture->width = clientBuf->width; + texture->height = clientBuf->height; + } + } else { + LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x", + this, eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + + +// --------------------------------------------------------------------------- + +int32_t LayerBaseClient::sIdentity = 0; + +LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))) +{ + lcblk = new SharedBufferServer( + client->ctrlblk, i, NUM_BUFFERS, + mIdentity); +} + +void LayerBaseClient::onFirstRef() +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + client->bindLayer(this, mIndex); + } +} + +LayerBaseClient::~LayerBaseClient() +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + client->free(mIndex); + } + delete lcblk; +} + +int32_t LayerBaseClient::serverIndex() const +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + return (client->cid<<16)|mIndex; + } + return 0xFFFF0000 | mIndex; +} + +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, clientIndex(), mIdentity, + const_cast<LayerBaseClient *>(this)); +} + +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void LayerBaseClient::onRemoved() +{ + // wake up the condition + lcblk->setStatus(NO_INIT); +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::Surface::Surface( + const sp<SurfaceFlinger>& flinger, + SurfaceID id, int identity, + const sp<LayerBaseClient>& owner) + : mFlinger(flinger), mToken(id), 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 index, int usage) +{ + return NULL; +} + +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..62ec839 --- /dev/null +++ b/services/surfaceflinger/LayerBase.h @@ -0,0 +1,389 @@ +/* + * 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/ISurfaceFlingerClient.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 SurfaceFlinger; + +// --------------------------------------------------------------------------- + +class LayerBase : public RefBase +{ + // poor man's dynamic_cast below + template<typename T> + struct getTypeInfoOfAnyType { + static uint32_t get() { return T::typeInfo; } + }; + + template<typename T> + struct getTypeInfoOfAnyType<T*> { + static uint32_t get() { return getTypeInfoOfAnyType<T>::get(); } + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + template<typename T> + static T dynamicCast(LayerBase* base) { + uint32_t mostDerivedInfo = base->getTypeInfo(); + uint32_t castToInfo = getTypeInfoOfAnyType<T>::get(); + if ((mostDerivedInfo & castToInfo) == castToInfo) + return static_cast<T>(base); + return 0; + } + + + 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(); + + /** + * 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; } + + /** + * transformed -- true is this surface needs a to be transformed + */ + virtual bool transformed() const { return mTransformed; } + + /** + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or 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() { }; + + + 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); + + GLuint createTexture() const; + + struct Texture { + Texture() : name(-1U), width(0), height(0), + image(EGL_NO_IMAGE_KHR), transform(0), + NPOTAdjust(false), dirty(true) { } + GLuint name; + GLuint width; + GLuint height; + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + EGLImageKHR image; + uint32_t transform; + bool NPOTAdjust; + bool dirty; + }; + + void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g, + GLclampx b, GLclampx alpha) const; + void clearWithOpenGL(const Region& clip) const; + void drawWithOpenGL(const Region& clip, const Texture& texture) const; + void loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) const; + status_t initializeEglImage( + const sp<GraphicBuffer>& buffer, Texture* texture); + + bool isSupportedYuvFormat(int format) const; + + sp<SurfaceFlinger> mFlinger; + uint32_t mFlags; + + // cached during validateVisibility() + bool mTransformed; + bool mUseLinearFiltering; + int32_t mOrientation; + GLfixed 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); + void validateTexture(GLint textureName) const; +}; + + +// --------------------------------------------------------------------------- + +class LayerBaseClient : public LayerBase +{ +public: + class Surface; + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + // lcblk is (almost) only accessed from the main SF thread, in the places + // where it's not, a reference to Client must be held + SharedBufferServer* lcblk; + + LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBaseClient(); + virtual void onFirstRef(); + + const wp<Client> client; + + inline uint32_t getIdentity() const { return mIdentity; } + inline int32_t clientIndex() const { return mIndex; } + int32_t serverIndex() const; + + + sp<Surface> getSurface(); + virtual sp<Surface> createSurface() const; + + virtual void onRemoved(); + + + class Surface : public BnSurface + { + public: + int32_t getToken() const { return mToken; } + int32_t getIdentity() const { return mIdentity; } + + protected: + Surface(const sp<SurfaceFlinger>& flinger, + SurfaceID id, 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 index, int usage); + 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 mToken; + int32_t mIdentity; + wp<LayerBaseClient> mOwner; + }; + + friend class Surface; + +private: + int32_t mIndex; + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + // 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..5fd7904 --- /dev/null +++ b/services/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,265 @@ +/* + * 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 { +// --------------------------------------------------------------------------- + +const uint32_t LayerBlur::typeInfo = LayerBaseClient::typeInfo | 8; +const char* const LayerBlur::typeID = "LayerBlur"; + +// --------------------------------------------------------------------------- + +LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), 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->signalDelayedEvent(ms2ns(500)); + mAutoRefreshPending = true; + } + } + } + } + } + LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); +} + +void LayerBlur::onDraw(const Region& clip) const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t fbHeight = hw.getHeight(); + int x = mTransformedBounds.left; + int y = mTransformedBounds.top; + int w = mTransformedBounds.width(); + int h = mTransformedBounds.height(); + GLint X = x; + GLint Y = fbHeight - (y + h); + if (X < 0) { + w += X; + X = 0; + } + if (Y < 0) { + h += Y; + Y = 0; + } + if (w<0 || h<0) { + // we're outside of the framebuffer + return; + } + + if (mTextureName == -1U) { + // create the texture name the first time + // can't do that in the ctor, because it runs in another thread. + glGenTextures(1, &mTextureName); + 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) { + 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 (mFlags & (DisplayHardware::NPOT_EXTENSION)) { + 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 GGLfixed alpha = (s.alpha << 16)/255; + glColor4x(0, 0, 0, alpha); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } else { + glDisable(GL_BLEND); + } + + 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); + + if (UNLIKELY(transformed() + || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { + // This is a very rare scenario. + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(mWidthScale, mHeightScale, 1); + glTranslatef(-x, mYOffset - y, 0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glVertexPointer(2, GL_FIXED, 0, mVertices); + glTexCoordPointer(2, GL_FIXED, 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); + } else { + // NOTE: this is marginally faster with the software gl, because + // glReadPixels() reads the fb bottom-to-top, however we'll + // skip all the jaccobian computations. + Rect r; + GLint crop[4] = { 0, 0, w, h }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + y = fbHeight - (y + h); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, w, h); + } + } + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerBlur.h b/services/surfaceflinger/LayerBlur.h new file mode 100644 index 0000000..5b63dec --- /dev/null +++ b/services/surfaceflinger/LayerBlur.h @@ -0,0 +1,69 @@ +/* + * 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: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBlur(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBlur(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + virtual uint32_t doTransaction(uint32_t flags); + virtual void setVisibleRegion(const Region& visibleRegion); + virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); + +private: + bool mCacheDirty; + mutable bool mRefreshCache; + mutable bool mAutoRefreshPending; + nsecs_t mCacheAge; + mutable GLuint mTextureName; + 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..5c21593 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -0,0 +1,727 @@ +/* + * 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 { + +// --------------------------------------------------------------------------- + +const uint32_t LayerBuffer::typeInfo = LayerBaseClient::typeInfo | 0x20; +const char* const LayerBuffer::typeID = "LayerBuffer"; +gralloc_module_t const* LayerBuffer::sGrallocModule = 0; + +// --------------------------------------------------------------------------- + +LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + mNeedsBlending(false), mBlitEngine(0) +{ +} + +LayerBuffer::~LayerBuffer() +{ + if (mBlitEngine) { + copybit_close(mBlitEngine); + } +} + +void LayerBuffer::onFirstRef() +{ + LayerBaseClient::onFirstRef(); + mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(), + const_cast<LayerBuffer *>(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 + mUseLinearFiltering = !(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); + } +} + +bool LayerBuffer::transformed() const +{ + sp<Source> source(getSource()); + if (LIKELY(source != 0)) + return source->transformed(); + return false; +} + +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, + SurfaceID id, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, id, 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() { +} +bool LayerBuffer::Source::transformed() const { + return mLayer.mTransformed; +} + +// --------------------------------------------------------------------------- + +LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, + const ISurface::BufferHeap& buffers) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0), + mUseEGLImageDirectly(true) +{ + 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 + mLayer.mFlinger->mEventQueue.postMessage( + new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) ); + } + if (mTexture.image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mLayer.mFlinger->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; +} + +bool LayerBuffer::BufferSource::transformed() const +{ + return mBufferHeap.transform ? true : Source::transformed(); +} + +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 (UNLIKELY(mTexture.name == -1LU)) { + mTexture.name = mLayer.createTexture(); + } + +#if defined(EGL_ANDROID_image_native_buffer) + if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { + err = INVALID_OPERATION; + if (ourBuffer->supportsCopybit()) { + + // there are constraints on buffers used by the GPU and these may not + // be honored here. We need to change the API so the buffers + // are allocated with gralloc. For now disable this code-path +#if 0 + // First, try to use the buffer as an EGLImage directly + if (mUseEGLImageDirectly) { + // NOTE: Assume the buffer is allocated with the proper USAGE flags + + sp<GraphicBuffer> buffer = new GraphicBuffer( + src.img.w, src.img.h, src.img.format, + GraphicBuffer::USAGE_HW_TEXTURE, + src.img.w, src.img.handle, false); + + err = mLayer.initializeEglImage(buffer, &mTexture); + if (err != NO_ERROR) { + mUseEGLImageDirectly = false; + } + } +#endif + + 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)); + mLayer.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.mUseLinearFiltering = 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; + + err = mLayer.initializeEglImage(buffer, &mTexture); + } + + return err; +} + +void LayerBuffer::BufferSource::clearTempBufferImage() const +{ + // delete the image + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTexture.image); + + // and the associated texture (recreate a name) + glDeleteTextures(1, &mTexture.name); + Texture defaultTexture; + mTexture = defaultTexture; + mTexture.name = mLayer.createTexture(); +} + +// --------------------------------------------------------------------------- + +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 = mLayer.mFlinger->getOverlayEngine(); + if (overlay_dev == NULL) { + // overlays not supported + return; + } + + mOverlayDevice = overlay_dev; + overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format); + if (overlay == NULL) { + // couldn't create the overlay (no memory? no more overlays?) + return; + } + + // enable dithering... + overlay_dev->setParameter(overlay_dev, overlay, + OVERLAY_DITHER, OVERLAY_ENABLE); + + mOverlay = overlay; + mWidth = overlay->w; + mHeight = overlay->h; + mFormat = overlay->format; + mWidthStride = overlay->w_stride; + mHeightStride = overlay->h_stride; + mInitialized = false; + + mOverlayHandle = overlay->getHandleRef(overlay); + + sp<OverlayChannel> channel = new OverlayChannel( &layer ); + + *overlayRef = new OverlayRef(mOverlayHandle, channel, + mWidth, mHeight, mFormat, mWidthStride, mHeightStride); + mLayer.mFlinger->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. + GLclampx red = 0; + GLclampx green = 0; + GLclampx 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..b176623 --- /dev/null +++ b/services/surfaceflinger/LayerBuffer.h @@ -0,0 +1,225 @@ +/* + * 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" + +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 bool transformed() const; + virtual void destroy() { } + protected: + LayerBuffer& mLayer; + }; + +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerBuffer(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerBuffer(); + + virtual void onFirstRef(); + virtual bool needsBlending() const; + + 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); + virtual bool transformed() const; + + status_t registerBuffers(const ISurface::BufferHeap& buffers); + void postBuffer(ssize_t offset); + void unregisterBuffers(); + sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format, + 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 bool transformed() const; + 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 LayerBase::Texture mTexture; + mutable NativeBuffer mTempBuffer; + mutable bool mUseEGLImageDirectly; + }; + + 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, + SurfaceID id, 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..fd61e30 --- /dev/null +++ b/services/surfaceflinger/LayerDim.cpp @@ -0,0 +1,168 @@ +/* + * 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 { +// --------------------------------------------------------------------------- + +const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; +const char* const LayerDim::typeID = "LayerDim"; + +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, int32_t i) + : LayerBaseClient(flinger, display, client, i) +{ +} + +void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) +{ + sTexId = -1; + sImage = EGL_NO_IMAGE_KHR; + sWidth = w; + sHeight = h; + sUseTexture = false; + +#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) + +#warning "using a texture to implement LayerDim" + + /* On some h/w like msm7K, it is faster to use a texture because the + * software renderer will defer to copybit, for this to work we need to + * use an EGLImage texture so copybit can actually make use of it. + * This burns a full-screen worth of graphic memory. + */ + + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + uint32_t flags = hw.getFlags(); + + if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) { + sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGB_565, + GraphicBuffer::USAGE_SW_WRITE_OFTEN | + GraphicBuffer::USAGE_HW_TEXTURE); + + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + glGenTextures(1, &sTexId); + glBindTexture(GL_TEXTURE_2D, sTexId); + + EGLDisplay dpy = eglGetCurrentDisplay(); + sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0); + if (sImage == EGL_NO_IMAGE_KHR) { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + return; + } + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + eglDestroyImageKHR(dpy, sImage); + LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error); + return; + } + + // initialize the texture with zeros + GGLSurface t; + buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN); + memset(t.data, 0, t.stride * t.height * 2); + buffer->unlock(); + sUseTexture = true; + } +#endif +} + +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 GGLfixed alpha = (s.alpha << 16)/255; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0, 0, alpha); + +#if defined(DIM_WITH_TEXTURE) && defined(EGL_ANDROID_image_native_buffer) + if (sUseTexture) { + glBindTexture(GL_TEXTURE_2D, sTexId); + glEnable(GL_TEXTURE_2D); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + const GLshort texCoords[4][2] = { + { 0, 0 }, + { 0, 1 }, + { 1, 1 }, + { 1, 0 } + }; + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_SHORT, 0, texCoords); + } else +#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..d4672a1 --- /dev/null +++ b/services/surfaceflinger/LayerDim.h @@ -0,0 +1,60 @@ +/* + * 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: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerDim(SurfaceFlinger* flinger, DisplayID display, + const sp<Client>& client, int32_t i); + virtual ~LayerDim(); + + virtual void onDraw(const Region& clip) const; + virtual bool needsBlending() const { return true; } + virtual bool isSecure() const { return false; } + + static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); +}; + +// --------------------------------------------------------------------------- + +}; // 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..b43d801 --- /dev/null +++ b/services/surfaceflinger/MessageQueue.cpp @@ -0,0 +1,192 @@ +/* + * 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() +{ +} + +MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) +{ + MessageList::value_type 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 = 0; + } + + } while (again); + + return result; +} + +status_t MessageQueue::postMessage( + const MessageList::value_type& 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 MessageList::value_type& 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 MessageList::value_type& message) +{ + Mutex::Autolock _l(mLock); + dumpLocked(message); +} + +void MessageQueue::dumpLocked(const MessageList::value_type& 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..dc8138d --- /dev/null +++ b/services/surfaceflinger/MessageQueue.h @@ -0,0 +1,127 @@ +/* + * 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> + + +namespace android { + +// --------------------------------------------------------------------------- + +class MessageBase; + +class MessageList +{ + List< sp<MessageBase> > mList; + typedef List< sp<MessageBase> > LIST; +public: + typedef sp<MessageBase> value_type; + 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; } + +protected: + virtual ~MessageBase() { } + +private: + 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: + + // this is a work-around the multichar constant warning. A macro would + // work too, but would pollute the namespace. + template <int a, int b, int c, int d> + struct WHAT { + static const uint32_t Value = + (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)| + (uint32_t(c&0xff)<<8)|uint32_t(d&0xff); + }; + + MessageQueue(); + ~MessageQueue(); + + // pre-defined messages + enum { + INVALIDATE = WHAT<'_','p','d','t'>::Value + }; + + MessageList::value_type waitMessage(nsecs_t timeout = -1); + + status_t postMessage(const MessageList::value_type& message, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t invalidate(); + + void dump(const MessageList::value_type& message); + +private: + status_t queueMessage(const MessageList::value_type& message, + nsecs_t reltime, uint32_t flags); + void dumpLocked(const MessageList::value_type& message); + + Mutex mLock; + Condition mCondition; + MessageList mMessages; + bool mInvalidate; + MessageList::value_type 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..0722fda --- /dev/null +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -0,0 +1,1940 @@ +/* + * 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 "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<ISurfaceFlingerClient> SurfaceFlinger::createConnection() +{ + Mutex::Autolock _l(mStateLock); + uint32_t token = mTokens.acquire(); + + sp<Client> client = new Client(token, this); + if (client->ctrlblk == 0) { + mTokens.release(token); + return 0; + } + status_t err = mClientsMap.add(token, client); + if (err < 0) { + mTokens.release(token); + return 0; + } + sp<BClient> bclient = + new BClient(this, token, client->getControlBlockMemory()); + return bclient; +} + +void SurfaceFlinger::destroyConnection(ClientID cid) +{ + Mutex::Autolock _l(mStateLock); + sp<Client> client = mClientsMap.valueFor(cid); + if (client != 0) { + // free all the layers this client owns + Vector< wp<LayerBaseClient> > layers(client->getLayers()); + const size_t count = layers.size(); + for (size_t i=0 ; i<count ; i++) { + sp<LayerBaseClient> layer(layers[i].promote()); + if (layer != 0) { + purgatorizeLayer_l(layer); + } + } + + // the resources associated with this client will be freed + // during the next transaction, after these surfaces have been + // properly removed from the screen + + // remove this client from our ClientID->Client mapping. + mClientsMap.removeItem(cid); + + // and add it to the list of disconnected clients + mDisconnectedClients.add(client); + + // request a transaction + setTransactionFlags(eTransactionNeeded); + } +} + +const GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) const +{ + LOGE_IF(uint32_t(dpy) >= DISPLAY_COUNT, "Invalid DisplayID %d", dpy); + const GraphicPlane& plane(mGraphicPlanes[dpy]); + return plane; +} + +GraphicPlane& SurfaceFlinger::graphicPlane(int dpy) +{ + return const_cast<GraphicPlane&>( + const_cast<SurfaceFlinger const *>(this)->graphicPlane(dpy)); +} + +void SurfaceFlinger::bootFinished() +{ + const nsecs_t now = systemTime(); + const nsecs_t duration = now - mBootTime; + LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + 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(); + asm volatile ("":::"memory"); + + // Initialize OpenGL|ES + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glEnableClientState(GL_VERTEX_ARRAY); + glEnable(GL_SCISSOR_TEST); + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + + const uint16_t g0 = pack565(0x0F,0x1F,0x0F); + const uint16_t g1 = pack565(0x17,0x2f,0x17); + const uint16_t textureData[4] = { g0, g1, g1, g0 }; + glGenTextures(1, &mWormholeTexName); + glBindTexture(GL_TEXTURE_2D, mWormholeTexName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData); + + glViewport(0, 0, w, h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrthof(0, w, h, 0, 0, 1); + + LayerDim::initDimmer(this, w, h); + + mReadyToRunBarrier.open(); + + /* + * We're now ready to accept clients... + */ + + // 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; + } + + MessageList::value_type 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(); +} + +void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) +{ + mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); +} + +// ---------------------------------------------------------------------------- +#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); + } + } + } + + // get rid of all resources we don't need anymore + // (layers and clients) + free_resources_l(); + } + + commitTransaction(); +} + +sp<FreezeLock> SurfaceFlinger::getFreezeLock() const +{ + return new FreezeLock(const_cast<SurfaceFlinger *>(this)); +} + +void SurfaceFlinger::computeVisibleRegions( + LayerVector& currentLayers, Region& dirtyRegion, Region& opaqueRegion) +{ + const GraphicPlane& plane(graphicPlane(0)); + const Transform& planeTransform(plane.transform()); + 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) { + // FIXME: 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); + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + + static int toggle = 0; + toggle = 1 - toggle; + if (toggle) { + glColor4x(0x10000, 0, 0x10000, 0x10000); + } else { + glColor4x(0x10000, 0x10000, 0, 0x10000); + } + + 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)) { + glClearColorx(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); + 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::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::invalidateLayerVisibility(const sp<LayerBase>& layer) +{ + layer->forceVisibilityTransaction(); + setTransactionFlags(eTraversalNeeded); + return NO_ERROR; +} + +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) +{ + if (layer == 0) + return BAD_VALUE; + ssize_t i = mCurrentState.layersSortedByZ.add( + layer, &LayerBase::compareCurrentStateZ); + sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + mLayerMap.add(lbc->serverIndex(), lbc); + } + return NO_ERROR; +} + +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) +{ + ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); + if (index >= 0) { + mLayersRemoved = true; + sp<LayerBaseClient> layer = + LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get()); + if (layer != 0) { + mLayerMap.removeItem(layer->serverIndex()); + } + 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 BClient::destroySurface(), + // ~BClient() and ~ISurface(). + return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err; +} + + +void SurfaceFlinger::free_resources_l() +{ + // free resources associated with disconnected clients + Vector< sp<Client> >& disconnectedClients(mDisconnectedClients); + const size_t count = disconnectedClients.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Client> client = disconnectedClients[i]; + mTokens.release(client->cid); + } + disconnectedClients.clear(); +} + +uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) +{ + return android_atomic_and(~flags, &mTransactionFlags) & flags; +} + +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay) +{ + uint32_t old = android_atomic_or(flags, &mTransactionFlags); + if ((old & flags)==0) { // wake the server up + if (delay > 0) { + signalDelayedEvent(delay); + } else { + signalEvent(); + } + } + return old; +} + +void SurfaceFlinger::openGlobalTransaction() +{ + android_atomic_inc(&mTransactionCount); +} + +void SurfaceFlinger::closeGlobalTransaction() +{ + if (android_atomic_dec(&mTransactionCount) == 1) { + signalEvent(); + + // 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(ClientID clientId, int pid, + const String8& name, ISurfaceFlingerClient::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; + } + + Mutex::Autolock _l(mStateLock); + sp<Client> client = mClientsMap.valueFor(clientId); + if (UNLIKELY(client == 0)) { + LOGE("createSurface() failed, client not found (id=%d)", clientId); + return surfaceHandle; + } + + //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); + int32_t id = client->generateId(pid); + if (uint32_t(id) >= NUM_LAYERS_MAX) { + LOGE("createSurface() failed, generateId = %d", id); + return surfaceHandle; + } + + switch (flags & eFXSurfaceMask) { + case eFXSurfaceNormal: + if (UNLIKELY(flags & ePushBuffers)) { + layer = createPushBuffersSurfaceLocked(client, d, id, + w, h, flags); + } else { + layer = createNormalSurfaceLocked(client, d, id, + w, h, flags, format); + } + break; + case eFXSurfaceBlur: + layer = createBlurSurfaceLocked(client, d, id, w, h, flags); + break; + case eFXSurfaceDim: + layer = createDimSurfaceLocked(client, d, id, w, h, flags); + break; + } + + if (layer != 0) { + layer->setName(name); + setTransactionFlags(eTransactionNeeded); + surfaceHandle = layer->getSurface(); + if (surfaceHandle != 0) { + params->token = surfaceHandle->getToken(); + params->identity = surfaceHandle->getIdentity(); + params->width = w; + params->height = h; + params->format = format; + } + } + + return surfaceHandle; +} + +sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, 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: + format = PIXEL_FORMAT_RGB_565; + break; + } + + sp<Layer> layer = new Layer(this, display, client, id); + status_t err = layer->setBuffers(w, h, format, flags); + if (LIKELY(err == NO_ERROR)) { + layer->initStates(w, h, flags); + addLayer_l(layer); + } else { + LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); + layer.clear(); + } + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBlur> layer = new LayerBlur(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerDim> layer = new LayerDim(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags) +{ + sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); + layer->initStates(w, h, flags); + addLayer_l(layer); + return layer; +} + +status_t SurfaceFlinger::removeSurface(SurfaceID index) +{ + /* + * 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 = getLayerUser_l(index); + 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; + } + }; + + mEventQueue.postMessage( new MessageDestroySurface(this, layer) ); + return NO_ERROR; +} + +status_t SurfaceFlinger::setClientState( + ClientID cid, + int32_t count, + const layer_state_t* states) +{ + Mutex::Autolock _l(mStateLock); + uint32_t flags = 0; + cid <<= 16; + for (int i=0 ; i<count ; i++) { + const layer_state_t& s = states[i]; + sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid)); + 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; +} + +sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const +{ + sp<LayerBaseClient> layer = mLayerMap.valueFor(s); + return layer; +} + +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); + } + + size_t s = mClientsMap.size(); + char name[64]; + for (size_t i=0 ; i<s ; i++) { + sp<Client> client = mClientsMap.valueAt(i); + sprintf(name, " Client (id=0x%08x)", client->cid); + client->dump(name); + } + const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + const size_t count = currentLayers.size(); + for (size_t i=0 ; i<count ; i++) { + /*** LayerBase ***/ + const sp<LayerBase>& layer = currentLayers[i]; + const Layer::State& s = layer->drawingState(); + snprintf(buffer, SIZE, + "+ %s %p\n" + " " + "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "needsBlending=%1d, needsDithering=%1d, invalidate=%1d, " + "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", + layer->getTypeID(), layer.get(), + s.z, layer->tx(), layer->ty(), s.w, s.h, + layer->needsBlending(), layer->needsDithering(), + layer->contentDirty, + s.alpha, s.flags, + s.transform[0][0], s.transform[0][1], + s.transform[1][0], s.transform[1][1]); + result.append(buffer); + buffer[0] = 0; + /*** LayerBaseClient ***/ + sp<LayerBaseClient> lbc = + LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + sp<Client> client(lbc->client.promote()); + snprintf(buffer, SIZE, + " name=%s\n", lbc->getName().string()); + result.append(buffer); + snprintf(buffer, SIZE, + " id=0x%08x, client=0x%08x, identity=%u\n", + lbc->clientIndex(), client.get() ? client->cid : 0, + lbc->getIdentity()); + + result.append(buffer); + buffer[0] = 0; + } + /*** Layer ***/ + sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); + if (l != 0) { + SharedBufferStack::Statistics stats = l->lcblk->getStats(); + result.append( l->lcblk->dump(" ") ); + sp<const GraphicBuffer> buf0(l->getBuffer(0)); + sp<const GraphicBuffer> buf1(l->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", + l->pixelFormat(), + w0, h0, s0, w1, h1, s1, + l->getFreezeLock().get(), stats.totalTime); + result.append(buffer); + buffer[0] = 0; + } + s.transparentRegion.dump(result, "transparentRegion"); + layer->transparentRegionScreen.dump(result, "transparentRegionScreen"); + layer->visibleRegionScreen.dump(result, "visibleRegionScreen"); + } + mWormholeRegion.dump(result, "WormholeRegion"); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + snprintf(buffer, SIZE, + " display frozen: %s, freezeCount=%d, orientation=%d, canDraw=%d\n", + mFreezeDisplay?"yes":"no", mFreezeCount, + mCurrentState.orientation, hw.canDraw()); + result.append(buffer); + 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); + } + snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size()); + 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; +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) + : ctrlblk(0), cid(clientID), mPid(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; + } +} + +Client::~Client() { + if (ctrlblk) { + ctrlblk->~SharedClient(); // destroy our shared-structure. + } +} + +int32_t Client::generateId(int pid) +{ + const uint32_t i = clz( ~mBitmap ); + if (i >= NUM_LAYERS_MAX) { + return NO_MEMORY; + } + mPid = pid; + mInUse.add(uint8_t(i)); + mBitmap |= 1<<(31-i); + return i; +} + +status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id) +{ + ssize_t idx = mInUse.indexOf(id); + if (idx < 0) + return NAME_NOT_FOUND; + return mLayers.insertAt(layer, idx); +} + +void Client::free(int32_t id) +{ + ssize_t idx = mInUse.remove(uint8_t(id)); + if (idx >= 0) { + mBitmap &= ~(1<<(31-id)); + mLayers.removeItemsAt(idx); + } +} + +bool Client::isValid(int32_t i) const { + return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); +} + +sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { + sp<LayerBaseClient> lbc; + ssize_t idx = mInUse.indexOf(uint8_t(i)); + if (idx >= 0) { + lbc = mLayers[idx].promote(); + LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx)); + } + return lbc; +} + +void Client::dump(const char* what) +{ +} + +// --------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk) + : mId(cid), mFlinger(flinger), mCblk(cblk) +{ +} + +BClient::~BClient() { + // destroy all resources attached to this client + mFlinger->destroyConnection(mId); +} + +sp<IMemoryHeap> BClient::getControlBlock() const { + return mCblk; +} + +sp<ISurface> BClient::createSurface( + ISurfaceFlingerClient::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(mId, pid, name, params, display, w, h, + format, flags); +} + +status_t BClient::destroySurface(SurfaceID sid) +{ + sid |= (mId << 16); // add the client-part to id + return mFlinger->removeSurface(sid); +} + +status_t BClient::setState(int32_t count, const layer_state_t* states) +{ + return mFlinger->setClientState(mId, count, states); +} + +// --------------------------------------------------------------------------- + +GraphicPlane::GraphicPlane() + : mHw(0) +{ +} + +GraphicPlane::~GraphicPlane() { + delete mHw; +} + +bool GraphicPlane::initialized() const { + return mHw ? true : false; +} + +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..d75dc15 --- /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/ISurfaceFlingerClient.h> + +#include "Barrier.h" +#include "Layer.h" +#include "Tokenizer.h" + +#include "MessageQueue.h" + +struct copybit_device_t; +struct overlay_device_t; + +namespace android { + +// --------------------------------------------------------------------------- + +class Client; +class BClient; +class DisplayHardware; +class FreezeLock; +class Layer; +class LayerBuffer; + +typedef int32_t ClientID; + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// --------------------------------------------------------------------------- + +class Client : public RefBase +{ +public: + Client(ClientID cid, const sp<SurfaceFlinger>& flinger); + ~Client(); + + int32_t generateId(int pid); + void free(int32_t id); + status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id); + + inline bool isValid(int32_t i) const; + sp<LayerBaseClient> getLayerUser(int32_t i) const; + void dump(const char* what); + + const Vector< wp<LayerBaseClient> >& getLayers() const { + return mLayers; + } + + const sp<IMemoryHeap>& getControlBlockMemory() const { + return mCblkHeap; + } + + // pointer to this client's control block + SharedClient* ctrlblk; + ClientID cid; + + +private: + int getClientPid() const { return mPid; } + + int mPid; + uint32_t mBitmap; + SortedVector<uint8_t> mInUse; + Vector< wp<LayerBaseClient> > mLayers; + 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<ISurfaceFlingerClient> createConnection(); + 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); + +private: + friend class BClient; + 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(ClientID client, int pid, const String8& name, + ISurfaceFlingerClient::surface_data_t* params, + DisplayID display, uint32_t w, uint32_t h, PixelFormat format, + uint32_t flags); + + sp<LayerBaseClient> createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags, + PixelFormat& format); + + sp<LayerBaseClient> createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerBaseClient> createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + sp<LayerBaseClient> createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, uint32_t flags); + + status_t removeSurface(SurfaceID surface_id); + status_t destroySurface(const sp<LayerBaseClient>& layer); + status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + + + class LayerVector { + public: + inline LayerVector() { } + LayerVector(const LayerVector&); + inline size_t size() const { return layers.size(); } + inline 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 signalDelayedEvent(nsecs_t delay); + + 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(); + + + void destroyConnection(ClientID cid); + sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const; + status_t addLayer_l(const sp<LayerBase>& layer); + status_t removeLayer_l(const sp<LayerBase>& layer); + status_t purgatorizeLayer_l(const sp<LayerBase>& layer); + void free_resources_l(); + + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags, nsecs_t delay = 0); + void commitTransaction(); + + + friend class FreezeLock; + sp<FreezeLock> getFreezeLock() const; + inline void incFreezeCount() { + 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; + + + + // 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) + Tokenizer mTokens; + DefaultKeyedVector<ClientID, sp<Client> > mClientsMap; + DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap; + GraphicPlane mGraphicPlanes[1]; + bool mLayersRemoved; + Vector< sp<Client> > mDisconnectedClients; + + // 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(); + } +}; + +// --------------------------------------------------------------------------- + +class BClient : public BnSurfaceFlingerClient +{ +public: + BClient(SurfaceFlinger *flinger, ClientID cid, + const sp<IMemoryHeap>& cblk); + ~BClient(); + + // ISurfaceFlingerClient interface + virtual sp<IMemoryHeap> getControlBlock() 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); + +private: + ClientID mId; + SurfaceFlinger* mFlinger; + sp<IMemoryHeap> mCblk; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_SURFACE_FLINGER_H diff --git a/services/surfaceflinger/Tokenizer.cpp b/services/surfaceflinger/Tokenizer.cpp new file mode 100644 index 0000000..be3a239 --- /dev/null +++ b/services/surfaceflinger/Tokenizer.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + printf("Tokenizer (%p, size = %d)\n", this, int(c)); + for (size_t i=0 ; i<c ; i++) { + printf("%u: (%u, %u)\n", i, + uint32_t(ranges[i].first), uint32_t(ranges[i].length)); + } +} + +}; // namespace android + diff --git a/services/surfaceflinger/Tokenizer.h b/services/surfaceflinger/Tokenizer.h new file mode 100644 index 0000000..6b3057d --- /dev/null +++ b/services/surfaceflinger/Tokenizer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TOKENIZER_H +#define ANDROID_TOKENIZER_H + +#include <utils/Vector.h> +#include <utils/Errors.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector<run_t> mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_TOKENIZER_H diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp new file mode 100644 index 0000000..175f989 --- /dev/null +++ b/services/surfaceflinger/Transform.cpp @@ -0,0 +1,392 @@ +/* + * 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(fixed1616* point, int x, int y) const +{ + const float toFixed = 65536.0f; + const mat33& M(mMatrix); + vec2 v(x, y); + v = transform(v); + point[0] = v[0] * toFixed; + point[1] = v[1] * toFixed; +} + +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..2e5b893 --- /dev/null +++ b/services/surfaceflinger/Transform.h @@ -0,0 +1,128 @@ +/* + * 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(); + + typedef int32_t fixed1616; + + // 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(fixed1616* 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..c248a61 --- /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; +} |
