diff options
48 files changed, 990 insertions, 365 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bf660ea94df9..425b2849c633 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4281,7 +4281,14 @@ public class Activity extends ContextThemeWrapper final void performPause() { mFragments.dispatchPause(); + mCalled = false; onPause(); + if (!mCalled && getApplicationInfo().targetSdkVersion + >= android.os.Build.VERSION_CODES.GINGERBREAD) { + throw new SuperNotCalledException( + "Activity " + mComponent.toShortString() + + " did not call through to super.onPause()"); + } } final void performUserLeaving() { diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 227d94d85229..60ccdafc0a9b 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -322,6 +322,8 @@ public final class MediaStore { private static final int MICRO_KIND = 3; private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA}; static final int DEFAULT_GROUP_ID = 0; + private static final Object sThumbBufLock = new Object(); + private static byte[] sThumbBuf; private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) { Bitmap bitmap = null; @@ -397,11 +399,15 @@ public final class MediaStore { long magic = thumbFile.getMagic(origId); if (magic != 0) { if (kind == MICRO_KIND) { - byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; - if (thumbFile.getMiniThumbFromFile(origId, data) != null) { - bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap == null) { - Log.w(TAG, "couldn't decode byte array."); + synchronized (sThumbBufLock) { + if (sThumbBuf == null) { + sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; + } + if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) { + bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length); + if (bitmap == null) { + Log.w(TAG, "couldn't decode byte array."); + } } } return bitmap; @@ -427,11 +433,15 @@ public final class MediaStore { // Assuming thumbnail has been generated, at least original image exists. if (kind == MICRO_KIND) { - byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; - if (thumbFile.getMiniThumbFromFile(origId, data) != null) { - bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap == null) { - Log.w(TAG, "couldn't decode byte array."); + synchronized (sThumbBufLock) { + if (sThumbBuf == null) { + sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; + } + if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) { + bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length); + if (bitmap == null) { + Log.w(TAG, "couldn't decode byte array."); + } } } } else if (kind == MINI_KIND) { diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 4c72e9502cef..b058685009c4 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -44,7 +44,7 @@ class GLES20Canvas extends Canvas { @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) private final GL mGl; private final boolean mOpaque; - private final int mRenderer; + private int mRenderer; private int mWidth; private int mHeight; @@ -76,16 +76,25 @@ class GLES20Canvas extends Canvas { mOpaque = !translucent; mRenderer = nCreateRenderer(); + if (mRenderer == 0) { + throw new IllegalStateException("Could not create GLES20Canvas renderer"); + } } - + private native int nCreateRenderer(); - @Override - protected void finalize() throws Throwable { - try { - super.finalize(); - } finally { + /** + * This method <strong>must</strong> be called before releasing a + * reference to a GLES20Canvas. This method is responsible for freeing + * native resources associated with the hardware. Not invoking this + * method properly can result in memory leaks. + * + * @hide + */ + public synchronized void destroy() { + if (mRenderer != 0) { nDestroyRenderer(mRenderer); + mRenderer = 0; } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index ca60a8966fe5..32d8123cd6f8 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -35,9 +35,10 @@ import javax.microedition.khronos.opengles.GL; * @hide */ public abstract class HardwareRenderer { + private static final String LOG_TAG = "HardwareRenderer"; + private boolean mEnabled; private boolean mRequested = true; - private static final String LOG_TAG = "HardwareRenderer"; /** * Indicates whether hardware acceleration is available under any form for @@ -70,9 +71,8 @@ public abstract class HardwareRenderer { * * @param width Width of the drawing surface. * @param height Height of the drawing surface. - * @param attachInfo The AttachInfo used to render the ViewRoot. */ - abstract void setup(int width, int height, View.AttachInfo attachInfo); + abstract void setup(int width, int height); /** * Draws the specified view. @@ -96,12 +96,11 @@ public abstract class HardwareRenderer { */ void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, SurfaceHolder holder) { - if (isRequested()) { // We lost the gl context, so recreate it. if (!isEnabled()) { if (initialize(holder)) { - setup(width, height, attachInfo); + setup(width, height); } } } @@ -165,18 +164,23 @@ public abstract class HardwareRenderer { static abstract class GlRenderer extends HardwareRenderer { private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - EGL10 mEgl; - EGLDisplay mEglDisplay; - EGLContext mEglContext; - EGLSurface mEglSurface; - EGLConfig mEglConfig; + static EGLContext sEglContext; + static EGL10 sEgl; + static EGLDisplay sEglDisplay; + static EGLConfig sEglConfig; + private static Thread sEglThread; + + EGLSurface mEglSurface; + GL mGl; - Canvas mCanvas; + GLES20Canvas mCanvas; final int mGlVersion; final boolean mTranslucent; + private boolean mDestroyed; + GlRenderer(int glVersion, boolean translucent) { mGlVersion = glVersion; mTranslucent = translucent; @@ -189,7 +193,7 @@ public abstract class HardwareRenderer { */ void checkErrors() { if (isEnabled()) { - int error = mEgl.eglGetError(); + int error = sEgl.eglGetError(); if (error != EGL10.EGL_SUCCESS) { // something bad has happened revert to // normal rendering. @@ -208,13 +212,17 @@ public abstract class HardwareRenderer { if (isRequested() && !isEnabled()) { initializeEgl(); mGl = createEglSurface(holder); + mDestroyed = false; if (mGl != null) { - int err = mEgl.eglGetError(); + int err = sEgl.eglGetError(); if (err != EGL10.EGL_SUCCESS) { destroy(); setRequested(false); } else { + if (mCanvas != null) { + destroyCanvas(); + } mCanvas = createCanvas(); if (mCanvas != null) { setEnabled(true); @@ -229,64 +237,75 @@ public abstract class HardwareRenderer { return false; } - abstract Canvas createCanvas(); + private void destroyCanvas() { + mCanvas.destroy(); + mCanvas = null; + } + + abstract GLES20Canvas createCanvas(); void initializeEgl() { - mEgl = (EGL10) EGLContext.getEGL(); + if (sEglContext != null) return; + + sEglThread = Thread.currentThread(); + sEgl = (EGL10) EGLContext.getEGL(); // Get to the default display. - mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { + if (sEglDisplay == EGL10.EGL_NO_DISPLAY) { throw new RuntimeException("eglGetDisplay failed"); } // We can now initialize EGL for that display int[] version = new int[2]; - if (!mEgl.eglInitialize(mEglDisplay, version)) { + if (!sEgl.eglInitialize(sEglDisplay, version)) { throw new RuntimeException("eglInitialize failed"); } - mEglConfig = getConfigChooser(mGlVersion).chooseConfig(mEgl, mEglDisplay); + sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay); /* * Create an EGL context. We want to do this as rarely as we can, because an * EGL context is a somewhat heavy object. */ - mEglContext = createContext(mEgl, mEglDisplay, mEglConfig); + sEglContext = createContext(sEgl, sEglDisplay, sEglConfig); } GL createEglSurface(SurfaceHolder holder) { // Check preconditions. - if (mEgl == null) { + if (sEgl == null) { throw new RuntimeException("egl not initialized"); } - if (mEglDisplay == null) { + if (sEglDisplay == null) { throw new RuntimeException("eglDisplay not initialized"); } - if (mEglConfig == null) { + if (sEglConfig == null) { throw new RuntimeException("mEglConfig not initialized"); } + if (Thread.currentThread() != sEglThread) { + throw new IllegalStateException("HardwareRenderer cannot be used " + + "from multiple threads"); + } /* * The window size has changed, so we need to create a new * surface. */ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { - /* * Unbind and destroy the old EGL surface, if * there is one. */ - mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, + sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); + sEgl.eglDestroySurface(sEglDisplay, mEglSurface); } // Create an EGL surface we can render into. - mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null); + mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null); if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { - int error = mEgl.eglGetError(); + int error = sEgl.eglGetError(); if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); return null; @@ -298,11 +317,11 @@ public abstract class HardwareRenderer { * Before we can issue GL commands, we need to make sure * the context is current and bound to a surface. */ - if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { throw new RuntimeException("eglMakeCurrent failed"); } - return mEglContext.getGL(); + return sEglContext.getGL(); } EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { @@ -315,7 +334,6 @@ public abstract class HardwareRenderer { @Override void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, SurfaceHolder holder) { - if (isRequested()) { checkErrors(); super.initializeIfNeeded(width, height, attachInfo, holder); @@ -324,28 +342,34 @@ public abstract class HardwareRenderer { @Override void destroy() { - if (!isEnabled()) return; + if (!isEnabled() || mDestroyed) return; + + mDestroyed = true; - mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, + checkCurrent(); + // Destroy the Canvas first in case it needs to use a GL context + // to perform its cleanup. + destroyCanvas(); + + sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEgl.eglDestroyContext(mEglDisplay, mEglContext); - mEgl.eglDestroySurface(mEglDisplay, mEglSurface); - mEgl.eglTerminate(mEglDisplay); + sEgl.eglDestroySurface(sEglDisplay, mEglSurface); - mEglContext = null; mEglSurface = null; - mEglDisplay = null; - mEgl = null; mGl = null; - mCanvas = null; + + // mEgl.eglDestroyContext(mEglDisplay, mEglContext); + // mEglContext = null; + // mEgl.eglTerminate(mEglDisplay); + // mEgl = null; + // mEglDisplay = null; setEnabled(false); - } - + } + @Override - void setup(int width, int height, View.AttachInfo attachInfo) { - final float scale = attachInfo.mApplicationScale; - mCanvas.setViewport((int) (width * scale + 0.5f), (int) (height * scale + 0.5f)); + void setup(int width, int height) { + mCanvas.setViewport(width, height); } boolean canDraw() { @@ -363,7 +387,8 @@ public abstract class HardwareRenderer { * @param glVersion */ EglConfigChooser getConfigChooser(int glVersion) { - return new ComponentSizeChooser(glVersion, 8, 8, 8, mTranslucent ? 8 : 0, 0, 0); + // TODO: Check for mTranslucent here, which means at least 2 EGL contexts + return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0); } @Override @@ -373,14 +398,7 @@ public abstract class HardwareRenderer { attachInfo.mIgnoreDirtyState = true; view.mPrivateFlags |= View.DRAWN; - // TODO: Don't check the current context when we have one per UI thread - // TODO: Use a threadlocal flag to know whether the surface has changed - if (mEgl.eglGetCurrentContext() != mEglContext || - mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) { - if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - throw new RuntimeException("eglMakeCurrent failed"); - } - } + checkCurrent(); onPreDraw(); @@ -396,11 +414,22 @@ public abstract class HardwareRenderer { attachInfo.mIgnoreDirtyState = false; - mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); + sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); checkErrors(); } } + private void checkCurrent() { + // TODO: Don't check the current context when we have one per UI thread + // TODO: Use a threadlocal flag to know whether the surface has changed + if (sEgl.eglGetCurrentContext() != sEglContext || + sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) { + if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { + throw new RuntimeException("eglMakeCurrent failed"); + } + } + } + static abstract class EglConfigChooser { final int[] mConfigSpec; private final int mGlVersion; @@ -496,7 +525,7 @@ public abstract class HardwareRenderer { int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0); int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0); int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); - if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) { + if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) { return config; } } @@ -514,7 +543,7 @@ public abstract class HardwareRenderer { } } } - + /** * Hardware renderer using OpenGL ES 2.0. */ @@ -526,8 +555,9 @@ public abstract class HardwareRenderer { } @Override - Canvas createCanvas() { - return mGlCanvas = new GLES20Canvas(mGl, mTranslucent); + GLES20Canvas createCanvas() { + // TODO: Pass mTranslucent instead of true + return mGlCanvas = new GLES20Canvas(mGl, true); } @Override diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index d32ccb1ddbbc..5999aba42e5e 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -251,7 +251,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mTempRect = new Rect(); mVisRect = new Rect(); mWinFrame = new Rect(); - mWindow = new W(this, context); + mWindow = new W(this); mInputMethodCallback = new InputMethodCallback(this); mViewVisibility = View.GONE; mTransparentRegion = new Region(); @@ -469,6 +469,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (attrs != null && (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) { final boolean translucent = attrs.format != PixelFormat.OPAQUE; + destroyHardwareRenderer(); mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent); } } @@ -677,9 +678,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn attachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { - if (mHwRenderer != null) { - mHwRenderer.destroy(); - } + destroyHardwareRenderer(); } if (viewVisibility == View.GONE) { // After making a window gone, we will count it as being @@ -963,7 +962,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } if (hwIntialized) { - mHwRenderer.setup(mWidth, mHeight, mAttachInfo); + mHwRenderer.setup(mWidth, mHeight); } boolean focusChangedDueToTouchMode = ensureTouchModeLocally( @@ -1598,9 +1597,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn mAttachInfo.mRootView = null; mAttachInfo.mSurface = null; - if (mHwRenderer != null) { - mHwRenderer.destroy(); - } + destroyHardwareRenderer(); + mHwRenderer = null; + mSurface.release(); if (mInputChannel != null) { @@ -1625,6 +1624,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } } + private void destroyHardwareRenderer() { + if (mHwRenderer != null) { + mHwRenderer.destroy(); + } + } + void updateConfiguration(Configuration config, boolean force) { if (DEBUG_CONFIGURATION) Log.v(TAG, "Applying new config to window " @@ -2734,10 +2739,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn public void childDrawableStateChanged(View child) { } - protected Rect getWindowFrame() { - return mWinFrame; - } - void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( @@ -2816,16 +2817,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn static class W extends IWindow.Stub { private final WeakReference<ViewRoot> mViewRoot; - public W(ViewRoot viewRoot, Context context) { + W(ViewRoot viewRoot) { mViewRoot = new WeakReference<ViewRoot>(viewRoot); } - public void resized(int w, int h, Rect coveredInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, + boolean reportDraw, Configuration newConfig) { final ViewRoot viewRoot = mViewRoot.get(); if (viewRoot != null) { - viewRoot.dispatchResized(w, h, coveredInsets, - visibleInsets, reportDraw, newConfig); + viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig); } } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 4c6ecedff6ae..98c03a65f6fb 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -34,7 +34,6 @@ #include <SkiaShader.h> #include <SkiaColorFilter.h> #include <Rect.h> -#include <ui/Rect.h> #include "TextLayout.h" @@ -418,7 +417,7 @@ static JNINativeMethod gMethods[] = { { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRun }, - { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", + { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds }, #endif }; diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 3ebe7cb85cee..6ed7b7145589 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -593,6 +593,7 @@ UI style also includes a full action bar by default. --> <style name="Theme.Light.Holo"> <item name="editTextBackground">@android:drawable/edit_text_holo_light</item> + <item name="android:windowActionBar">true</item> <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_holo_light</item> <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_holo_light</item> </style> diff --git a/docs/html/resources/samples/images/sample_notepadtest_junit.png b/docs/html/resources/samples/images/sample_notepadtest_junit.png Binary files differnew file mode 100644 index 000000000000..4cda54e94eae --- /dev/null +++ b/docs/html/resources/samples/images/sample_notepadtest_junit.png diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 1eb178ebc130..d016dfa969e4 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -54,11 +54,6 @@ class SharedClient; // ---------------------------------------------------------------------------- -// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX -// 4 * (11 + 7 + (1 + 2*7)*16) * 31 -// 1032 * 31 -// = ~27 KiB (31992) - class SharedBufferStack { friend class SharedClient; @@ -85,7 +80,7 @@ public: }; struct FlatRegion { // 52 bytes = 4 * (1 + 2*N) - static const unsigned int NUM_RECT_MAX = 6; + static const unsigned int NUM_RECT_MAX = 5; uint32_t count; SmallRect rects[NUM_RECT_MAX]; }; @@ -93,13 +88,18 @@ public: struct BufferData { FlatRegion dirtyRegion; SmallRect crop; + uint8_t transform; + uint8_t reserved[3]; }; SharedBufferStack(); void init(int32_t identity); status_t setDirtyRegion(int buffer, const Region& reg); status_t setCrop(int buffer, const Rect& reg); + status_t setTransform(int buffer, uint8_t transform); Region getDirtyRegion(int buffer) const; + Rect getCrop(int buffer) const; + uint32_t getTransform(int buffer) const; // these attributes are part of the conditions/updates volatile int32_t head; // server's current front buffer @@ -117,7 +117,7 @@ public: int32_t reserved32[1]; Statistics stats; int32_t reserved; - BufferData buffers[NUM_BUFFER_MAX]; // 960 bytes + BufferData buffers[NUM_BUFFER_MAX]; // 1024 bytes }; // ---------------------------------------------------------------------------- @@ -206,7 +206,7 @@ public: bool needNewBuffer(int buffer) const; status_t setDirtyRegion(int buffer, const Region& reg); status_t setCrop(int buffer, const Rect& reg); - + status_t setTransform(int buffer, uint32_t transform); class SetBufferCountCallback { friend class SharedBufferClient; @@ -275,6 +275,8 @@ public: status_t reallocateAllExcept(int buffer); int32_t getQueuedCount() const; Region getDirtyRegion(int buffer) const; + Rect getCrop(int buffer) const; + uint32_t getTransform(int buffer) const; status_t resize(int newNumBuffers); diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index 6fdd2ae7ba6f..a2108804af5e 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -217,6 +217,7 @@ private: int dispatch_crop(va_list args); int dispatch_set_buffer_count(va_list args); int dispatch_set_buffers_geometry(va_list args); + int dispatch_set_buffers_transform(va_list args); void setUsage(uint32_t reqUsage); int connect(int api); @@ -224,6 +225,7 @@ private: int crop(Rect const* rect); int setBufferCount(int bufferCount); int setBuffersGeometry(int w, int h, int format); + int setBuffersTransform(int transform); /* * private stuff... @@ -279,6 +281,7 @@ private: Rect mSwapRectangle; int mConnected; Rect mNextBufferCrop; + uint32_t mNextBufferTransform; BufferInfo mBufferInfo; // protected by mSurfaceLock. These are also used from lock/unlock diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h index ca89b06816cb..d59d72b7e520 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -85,6 +85,7 @@ enum { NATIVE_WINDOW_SET_CROP, NATIVE_WINDOW_SET_BUFFER_COUNT, NATIVE_WINDOW_SET_BUFFERS_GEOMETRY, + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, }; /* parameter for NATIVE_WINDOW_[DIS]CONNECT */ @@ -92,6 +93,20 @@ enum { NATIVE_WINDOW_API_EGL = 1 }; +/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */ +enum { + /* flip source image horizontally */ + NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , + /* flip source image vertically */ + NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, + /* rotate source image 90 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, + /* rotate source image 180 degrees */ + NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, + /* rotate source image 270 degrees clock-wise */ + NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, +}; + struct ANativeWindow { #ifdef __cplusplus @@ -196,6 +211,7 @@ struct ANativeWindow * NATIVE_WINDOW_SET_CROP * NATIVE_WINDOW_SET_BUFFER_COUNT * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY + * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM * */ @@ -298,6 +314,19 @@ static inline int native_window_set_buffers_geometry( w, h, format); } +/* + * native_window_set_buffers_transform(..., int transform) + * All buffers queued after this call will be displayed transformed according + * to the transform parameter specified. + */ +static inline int native_window_set_buffers_transform( + ANativeWindow* window, + int transform) +{ + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM, + transform); +} + // --------------------------------------------------------------------------- /* FIXME: this is legacy for pixmaps */ diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h new file mode 100644 index 000000000000..151e29f45a70 --- /dev/null +++ b/libs/hwui/Caches.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UI_CACHES_H +#define ANDROID_UI_CACHES_H + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/Singleton.h> + +#include "TextureCache.h" +#include "LayerCache.h" +#include "GradientCache.h" +#include "PatchCache.h" +#include "FontRenderer.h" +#include "ProgramCache.h" +#include "PathCache.h" +#include "TextDropShadowCache.h" + +namespace android { +namespace uirenderer { + +struct CacheLogger { + CacheLogger() { + LOGD("Creating caches"); + } +}; // struct CacheLogger + +class Caches: public Singleton<Caches> { + Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), + lastDstMode(GL_ZERO), currentProgram(NULL) { + dropShadowCache.setFontRenderer(fontRenderer); + } + + friend class Singleton<Caches>; + + CacheLogger logger; + +public: + bool blend; + GLenum lastSrcMode; + GLenum lastDstMode; + Program* currentProgram; + + TextureCache textureCache; + LayerCache layerCache; + GradientCache gradientCache; + ProgramCache programCache; + PathCache pathCache; + PatchCache patchCache; + TextDropShadowCache dropShadowCache; + FontRenderer fontRenderer; +}; // class Caches + +}; // namespace uirenderer + +#ifdef USE_OPENGL_RENDERER +using namespace uirenderer; +ANDROID_SINGLETON_STATIC_INSTANCE(Caches); +#endif + +}; // namespace android + +#endif // ANDROID_UI_CACHES_H diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h index 45b3ffaf074a..c358c8096cfd 100644 --- a/libs/hwui/GenerationCache.h +++ b/libs/hwui/GenerationCache.h @@ -104,12 +104,14 @@ void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* list template<typename K, typename V> void GenerationCache<K, V>::clear() { if (mListener) { - while (mCache.size() > 0) { - removeOldest(); + for (uint32_t i = 0; i < mCache.size(); i++) { + sp<Entry<K, V> > entry = mCache.valueAt(i); + if (mListener) { + (*mListener)(entry->key, entry->value); + } } - } else { - mCache.clear(); } + mCache.clear(); mYoungest.clear(); mOldest.clear(); } diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 59fa0a7badcd..58920bd6f96d 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -22,6 +22,7 @@ #include <SkGradientShader.h> #include "GradientCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -30,6 +31,20 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +GradientCache::GradientCache(): + mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting gradient cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); + } + + mCache.setOnEntryRemovedListener(this); +} + GradientCache::GradientCache(uint32_t maxByteSize): mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 12c8a231aabe..8795920e6944 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -32,6 +32,7 @@ namespace uirenderer { */ class GradientCache: public OnEntryRemoved<SkShader*, Texture*> { public: + GradientCache(); GradientCache(uint32_t maxByteSize); ~GradientCache(); diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 3d263a3f71df..a20477801211 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -21,6 +21,7 @@ #include <utils/Log.h> #include "LayerCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -29,6 +30,18 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +LayerCache::LayerCache(): + mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity), + mIdGenerator(1), mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting layer cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE); + } +} + LayerCache::LayerCache(uint32_t maxByteSize): mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity), mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) { diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 258055130b67..9860994d1922 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -43,6 +43,7 @@ namespace uirenderer { class LayerCache: public OnEntryRemoved<LayerSize, Layer*> { public: + LayerCache(); LayerCache(uint32_t maxByteSize); ~LayerCache(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 49d49da44f44..033d8e2efd58 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -23,11 +23,9 @@ #include <SkCanvas.h> #include <SkTypeface.h> -#include <cutils/properties.h> #include <utils/Log.h> #include "OpenGLRenderer.h" -#include "Properties.h" namespace android { namespace uirenderer { @@ -36,18 +34,8 @@ namespace uirenderer { // Defines /////////////////////////////////////////////////////////////////////////////// -#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f -#define DEFAULT_LAYER_CACHE_SIZE 6.0f -#define DEFAULT_PATH_CACHE_SIZE 6.0f -#define DEFAULT_PATCH_CACHE_SIZE 100 -#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f -#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f - #define REQUIRED_TEXTURE_UNITS_COUNT 3 -// Converts a number of mega-bytes into bytes -#define MB(s) s * 1024 * 1024 - // Generates simple and textured vertices #define FV(x, y, u, v) { { x, y }, { u, v } } @@ -102,54 +90,9 @@ static const GLenum gTextureUnits[] = { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -OpenGLRenderer::OpenGLRenderer(): - mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO), - mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)), - mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)), - mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)), - mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)), - mPatchCache(DEFAULT_PATCH_CACHE_SIZE), - mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { +OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) { LOGD("Create OpenGLRenderer"); - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { - LOGD(" Setting texture cache size to %sMB", property); - mTextureCache.setMaxSize(MB(atof(property))); - } else { - LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); - } - - if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { - LOGD(" Setting layer cache size to %sMB", property); - mLayerCache.setMaxSize(MB(atof(property))); - } else { - LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE); - } - - if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { - LOGD(" Setting gradient cache size to %sMB", property); - mGradientCache.setMaxSize(MB(atof(property))); - } else { - LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); - } - - if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { - LOGD(" Setting path cache size to %sMB", property); - mPathCache.setMaxSize(MB(atof(property))); - } else { - LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); - } - - if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { - LOGD(" Setting drop shadow cache size to %sMB", property); - mDropShadowCache.setMaxSize(MB(atof(property))); - } else { - LOGD(" Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE); - } - mDropShadowCache.setFontRenderer(mFontRenderer); - - mCurrentProgram = NULL; mShader = NULL; mColorFilter = NULL; mHasShadow = false; @@ -167,14 +110,6 @@ OpenGLRenderer::OpenGLRenderer(): OpenGLRenderer::~OpenGLRenderer() { LOGD("Destroy OpenGLRenderer"); - - mTextureCache.clear(); - mLayerCache.clear(); - mGradientCache.clear(); - mPathCache.clear(); - mPatchCache.clear(); - mProgramCache.clear(); - mDropShadowCache.clear(); } /////////////////////////////////////////////////////////////////////////////// @@ -195,6 +130,8 @@ void OpenGLRenderer::prepare() { mSnapshot = new Snapshot(mFirstSnapshot); mSaveCount = 1; + glViewport(0, 0, mWidth, mHeight); + glDisable(GL_SCISSOR_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -310,12 +247,12 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top); - LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize()); + LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0; LayerSize size(right - left, bottom - top); - Layer* layer = mLayerCache.get(size, previousFbo); + Layer* layer = mCaches.layerCache.get(size, previousFbo); if (!layer) { return false; } @@ -380,7 +317,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { LayerSize size(rect.getWidth(), rect.getHeight()); // Failing to add the layer to the cache should happen only if the // layer is too large - if (!mLayerCache.put(size, layer)) { + if (!mCaches.layerCache.put(size, layer)) { LAYER_LOGD("Deleting layer"); glDeleteFramebuffers(1, &layer->fbo); @@ -461,7 +398,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const S } glActiveTexture(GL_TEXTURE0); - const Texture* texture = mTextureCache.get(bitmap); + const Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -478,7 +415,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const } glActiveTexture(GL_TEXTURE0); - const Texture* texture = mTextureCache.get(bitmap); + const Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -494,7 +431,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, } glActiveTexture(GL_TEXTURE0); - const Texture* texture = mTextureCache.get(bitmap); + const Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -520,7 +457,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, } glActiveTexture(GL_TEXTURE0); - const Texture* texture = mTextureCache.get(bitmap); + const Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -528,7 +465,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - Patch* mesh = mPatchCache.get(patch); + Patch* mesh = mCaches.patchCache.get(patch); mesh->updateVertices(bitmap, left, top, right, bottom, &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs); @@ -607,10 +544,11 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f; const GLfloat b = a * ((color ) & 0xFF) / 255.0f; - mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); + mCaches.fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), + paint->getTextSize()); if (mHasShadow) { glActiveTexture(gTextureUnits[0]); - const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount, + const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount, count, mShadowRadius); const AutoTexture autoCleanup(shadow); @@ -618,20 +556,20 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, // Draw the mesh glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); - glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords")); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); } GLuint textureUnit = 0; glActiveTexture(gTextureUnits[textureUnit]); - setupTextureAlpha8(mFontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a, + setupTextureAlpha8(mCaches.fontRenderer.getTexture(), 0, 0, textureUnit, x, y, r, g, b, a, mode, false, true); const Rect& clip = mSnapshot->getLocalClip(); - mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); + mCaches.fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords")); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); drawTextDecorations(text, bytesCount, length, x, y, paint); @@ -646,7 +584,7 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { GLuint textureUnit = 0; glActiveTexture(gTextureUnits[textureUnit]); - const PathTexture* texture = mPathCache.get(path, paint); + const PathTexture* texture = mCaches.pathCache.get(path, paint); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -671,7 +609,7 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { // Draw the mesh glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); - glDisableVertexAttribArray(mCurrentProgram->getAttrib("texCoords")); + glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); } /////////////////////////////////////////////////////////////////////////////// @@ -685,7 +623,7 @@ void OpenGLRenderer::resetShader() { void OpenGLRenderer::setupShader(SkiaShader* shader) { mShader = shader; if (mShader) { - mShader->set(&mTextureCache, &mGradientCache); + mShader->set(&mCaches.textureCache, &mCaches.gradientCache); } } @@ -761,18 +699,18 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t } // Build and use the appropriate shader - useProgram(mProgramCache.get(description)); + useProgram(mCaches.programCache.get(description)); // Setup the blending mode chooseBlending(true, mode); bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit); - glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), textureUnit); - int texCoordsSlot = mCurrentProgram->getAttrib("texCoords"); + int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); glEnableVertexAttribArray(texCoordsSlot); // Setup attributes - glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, gMeshStride, &mMeshVertices[0].position[0]); glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, &mMeshVertices[0].texture[0]); @@ -784,17 +722,17 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t } else { mModelView.loadIdentity(); } - mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); - glUniform4f(mCurrentProgram->color, r, g, b, a); + mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); + glUniform4f(mCaches.currentProgram->color, r, g, b, a); textureUnit++; if (applyFilters) { // Setup attributes and uniforms required by the shaders if (mShader) { - mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit); + mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit); } if (mColorFilter) { - mColorFilter->setupProgram(mCurrentProgram); + mColorFilter->setupProgram(mCaches.currentProgram); } } } @@ -879,29 +817,29 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot } // Build and use the appropriate shader - useProgram(mProgramCache.get(description)); + useProgram(mCaches.programCache.get(description)); // Setup attributes - glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, gMeshStride, &mMeshVertices[0].position[0]); // Setup uniforms mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); if (!ignoreTransform) { - mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); + mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); } else { mat4 identity; - mCurrentProgram->set(mOrthoMatrix, mModelView, identity); + mCaches.currentProgram->set(mOrthoMatrix, mModelView, identity); } - glUniform4f(mCurrentProgram->color, r, g, b, a); + glUniform4f(mCaches.currentProgram->color, r, g, b, a); // Setup attributes and uniforms required by the shaders if (mShader) { - mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit); + mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &textureUnit); } if (mColorFilter) { - mColorFilter->setupProgram(mCurrentProgram); + mColorFilter->setupProgram(mCaches.currentProgram); } // Draw the mesh @@ -936,28 +874,28 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); - useProgram(mProgramCache.get(description)); - mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); + useProgram(mCaches.programCache.get(description)); + mCaches.currentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform); chooseBlending(blend || alpha < 1.0f, mode); // Texture bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0); - glUniform1i(mCurrentProgram->getUniform("sampler"), 0); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), 0); // Always premultiplied - glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha); + glUniform4f(mCaches.currentProgram->color, alpha, alpha, alpha, alpha); // Mesh - int texCoordsSlot = mCurrentProgram->getAttrib("texCoords"); + int texCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); glEnableVertexAttribArray(texCoordsSlot); - glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE, + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, gMeshStride, vertices); glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords); // Color filter if (mColorFilter) { - mColorFilter->setupProgram(mCurrentProgram); + mColorFilter->setupProgram(mCaches.currentProgram); } if (!indices) { @@ -971,7 +909,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) { blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { - if (!mBlend) { + if (!mCaches.blend) { glEnable(GL_BLEND); } @@ -981,22 +919,22 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPr sourceMode = GL_SRC_ALPHA; } - if (sourceMode != mLastSrcMode || destMode != mLastDstMode) { + if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { glBlendFunc(sourceMode, destMode); - mLastSrcMode = sourceMode; - mLastDstMode = destMode; + mCaches.lastSrcMode = sourceMode; + mCaches.lastDstMode = destMode; } - } else if (mBlend) { + } else if (mCaches.blend) { glDisable(GL_BLEND); } - mBlend = blend; + mCaches.blend = blend; } bool OpenGLRenderer::useProgram(Program* program) { if (!program->isInUse()) { - if (mCurrentProgram != NULL) mCurrentProgram->remove(); + if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove(); program->use(); - mCurrentProgram = program; + mCaches.currentProgram = program; return false; } return true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 5c0089f35214..5748d57d8787 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -35,17 +35,10 @@ #include "Program.h" #include "Rect.h" #include "Snapshot.h" -#include "TextureCache.h" -#include "LayerCache.h" -#include "GradientCache.h" -#include "PatchCache.h" #include "Vertex.h" -#include "FontRenderer.h" -#include "ProgramCache.h" #include "SkiaShader.h" #include "SkiaColorFilter.h" -#include "PathCache.h" -#include "TextDropShadowCache.h" +#include "Caches.h" namespace android { namespace uirenderer { @@ -356,7 +349,6 @@ private: sp<Snapshot> mSnapshot; // Shaders - Program* mCurrentProgram; SkiaShader* mShader; // Color filters @@ -365,17 +357,9 @@ private: // Used to draw textured quads TextureVertex mMeshVertices[4]; - // Last known blend state - bool mBlend; - GLenum mLastSrcMode; - GLenum mLastDstMode; - // GL extensions Extensions mExtensions; - // Font renderer - FontRenderer mFontRenderer; - // Drop shadow bool mHasShadow; float mShadowRadius; @@ -384,13 +368,7 @@ private: int mShadowColor; // Various caches - TextureCache mTextureCache; - LayerCache mLayerCache; - GradientCache mGradientCache; - ProgramCache mProgramCache; - PathCache mPathCache; - PatchCache mPatchCache; - TextDropShadowCache mDropShadowCache; + Caches& mCaches; }; // class OpenGLRenderer }; // namespace uirenderer diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 694e3fd6205c..a7c0ccef89f8 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -20,6 +20,7 @@ #include <utils/ResourceTypes.h> #include "PatchCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -28,6 +29,9 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +PatchCache::PatchCache(): mCache(DEFAULT_PATCH_CACHE_SIZE) { +} + PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) { } diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index de9508717df3..e874a1686e5c 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -43,6 +43,7 @@ namespace uirenderer { class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> { public: + PatchCache(); PatchCache(uint32_t maxCapacity); ~PatchCache(); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 10440ea3cebf..158c0cc70ff0 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -22,6 +22,7 @@ #include <SkRect.h> #include "PathCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -30,9 +31,30 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +PathCache::PathCache(): + mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting path cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); + } + init(); +} + PathCache::PathCache(uint32_t maxByteSize): mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { + init(); +} + +PathCache::~PathCache() { + mCache.clear(); +} + +void PathCache::init() { mCache.setOnEntryRemovedListener(this); GLint maxTextureSize; @@ -40,10 +62,6 @@ PathCache::PathCache(uint32_t maxByteSize): mMaxTextureSize = maxTextureSize; } -PathCache::~PathCache() { - mCache.clear(); -} - /////////////////////////////////////////////////////////////////////////////// // Size management /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index d09789f03763..522e5e0f65ad 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -95,6 +95,7 @@ struct PathTexture: public Texture { */ class PathCache: public OnEntryRemoved<PathCacheEntry, PathTexture*> { public: + PathCache(); PathCache(uint32_t maxByteSize); ~PathCache(); @@ -135,6 +136,8 @@ private: PathTexture* addTexture(const PathCacheEntry& entry, const SkPath *path, const SkPaint* paint); + void init(); + GenerationCache<PathCacheEntry, PathTexture*> mCache; uint32_t mSize; diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index fa4b8c4b3e47..f211ab60261f 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -183,7 +183,6 @@ private: void printLongString(const String8& shader) const; KeyedVector<programid, Program*> mCache; - }; // class ProgramCache }; // namespace uirenderer diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 7514b6f0d905..dfe022ac9377 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -17,6 +17,8 @@ #ifndef ANDROID_UI_PROPERTIES_H #define ANDROID_UI_PROPERTIES_H +#include <cutils/properties.h> + /** * This file contains the list of system properties used to configure * the OpenGLRenderer. @@ -33,4 +35,14 @@ #define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width" #define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height" +// Converts a number of mega-bytes into bytes +#define MB(s) s * 1024 * 1024 + +#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f +#define DEFAULT_LAYER_CACHE_SIZE 6.0f +#define DEFAULT_PATH_CACHE_SIZE 6.0f +#define DEFAULT_PATCH_CACHE_SIZE 100 +#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f +#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f + #endif // ANDROID_UI_PROPERTIES_H diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index aab5bd4d347d..f95d2be3acf9 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -25,6 +25,20 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +TextDropShadowCache::TextDropShadowCache(): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting drop shadow cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE); + } + + mCache.setOnEntryRemovedListener(this); +} + TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index c3be4832b93f..b65d62acc522 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -69,16 +69,16 @@ struct ShadowText { char *text; bool operator<(const ShadowText& rhs) const { - if (len < rhs.len) return true; - else if (len == rhs.len) { - if (radius < rhs.radius) return true; - else if (radius == rhs.radius) { - if (textSize < rhs.textSize) return true; - else if (textSize == rhs.textSize) { - if (typeface < rhs.typeface) return true; - else if (typeface == rhs.typeface) { - if (hash < rhs.hash) return true; - if (hash == rhs.hash) { + if (hash < rhs.hash) return true; + else if (hash == rhs.hash) { + if (len < rhs.len) return true; + else if (len == rhs.len) { + if (radius < rhs.radius) return true; + else if (radius == rhs.radius) { + if (textSize < rhs.textSize) return true; + else if (textSize == rhs.textSize) { + if (typeface < rhs.typeface) return true; + else if (typeface == rhs.typeface) { return strncmp(text, rhs.text, len) < 0; } } @@ -102,6 +102,7 @@ struct ShadowTexture: public Texture { class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> { public: + TextDropShadowCache(); TextDropShadowCache(uint32_t maxByteSize); ~TextDropShadowCache(); diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 2e8a8be3cf86..753c5446b5bc 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -19,6 +19,7 @@ #include <GLES2/gl2.h> #include "TextureCache.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -27,19 +28,37 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// +TextureCache::TextureCache(): + mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { + LOGD(" Setting texture cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); + } + + init(); +} + TextureCache::TextureCache(uint32_t maxByteSize): mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { - mCache.setOnEntryRemovedListener(this); - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); - LOGD("Maximum texture dimension is %d pixels", mMaxTextureSize); + init(); } TextureCache::~TextureCache() { mCache.clear(); } +void TextureCache::init() { + mCache.setOnEntryRemovedListener(this); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); +} + /////////////////////////////////////////////////////////////////////////////// // Size management /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 452716c947ff..b5e4c7c19038 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -32,6 +32,7 @@ namespace uirenderer { */ class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { public: + TextureCache(); TextureCache(uint32_t maxByteSize); ~TextureCache(); @@ -78,6 +79,8 @@ private: */ void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + void init(); + GenerationCache<SkBitmap*, Texture*> mCache; uint32_t mSize; diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index 47ec78a7dd13..4c01cd229225 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -75,6 +75,14 @@ status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) return NO_ERROR; } +status_t SharedBufferStack::setTransform(int buffer, uint8_t transform) +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return BAD_INDEX; + buffers[buffer].transform = transform; + return NO_ERROR; +} + status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty) { if (uint32_t(buffer) >= NUM_BUFFER_MAX) @@ -137,6 +145,26 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const return res; } +Rect SharedBufferStack::getCrop(int buffer) const +{ + Rect res(-1, -1); + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return res; + res.left = buffers[buffer].crop.l; + res.top = buffers[buffer].crop.t; + res.right = buffers[buffer].crop.r; + res.bottom = buffers[buffer].crop.b; + return res; +} + +uint32_t SharedBufferStack::getTransform(int buffer) const +{ + if (uint32_t(buffer) >= NUM_BUFFER_MAX) + return 0; + return buffers[buffer].transform; +} + + // ---------------------------------------------------------------------------- SharedBufferBase::SharedBufferBase(SharedClient* sharedClient, @@ -428,6 +456,12 @@ status_t SharedBufferClient::setCrop(int buf, const Rect& crop) return stack.setCrop(buf, crop); } +status_t SharedBufferClient::setTransform(int buf, uint32_t transform) +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.setTransform(buf, uint8_t(transform)); +} + status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) { SharedBufferStack& stack( *mSharedStack ); @@ -544,6 +578,18 @@ Region SharedBufferServer::getDirtyRegion(int buf) const return stack.getDirtyRegion(buf); } +Rect SharedBufferServer::getCrop(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getCrop(buf); +} + +uint32_t SharedBufferServer::getTransform(int buf) const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.getTransform(buf); +} + /* * NOTE: this is not thread-safe on the server-side, meaning * 'head' cannot move during this operation. The client-side diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index f52447650a39..2bc5fadecf43 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -429,8 +429,10 @@ void Surface::init() const_cast<int&>(ANativeWindow::maxSwapInterval) = 1; const_cast<uint32_t&>(ANativeWindow::flags) = 0; + mNextBufferTransform = 0; mConnected = 0; mSwapRectangle.makeInvalid(); + mNextBufferCrop = Rect(0,0); // two buffers by default mBuffers.setCapacity(2); mBuffers.insertAt(0, 2); @@ -641,6 +643,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer) } int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); + mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); err = mSharedBufferClient->queue(bufIdx); @@ -695,6 +698,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: res = dispatch_set_buffers_geometry( args ); break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatch_set_buffers_transform( args ); + break; default: res = NAME_NOT_FOUND; break; @@ -729,6 +735,11 @@ int Surface::dispatch_set_buffers_geometry(va_list args) { return setBuffersGeometry(w, h, f); } +int Surface::dispatch_set_buffers_transform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + void Surface::setUsage(uint32_t reqUsage) { Mutex::Autolock _l(mSurfaceLock); @@ -775,6 +786,10 @@ int Surface::disconnect(int api) int Surface::crop(Rect const* rect) { + // empty/invalid rects are not allowed + if (rect->isEmpty()) + return BAD_VALUE; + Mutex::Autolock _l(mSurfaceLock); // TODO: validate rect size mNextBufferCrop = *rect; @@ -814,6 +829,13 @@ int Surface::setBuffersGeometry(int w, int h, int format) return NO_ERROR; } +int Surface::setBuffersTransform(int transform) +{ + Mutex::Autolock _l(mSurfaceLock); + mNextBufferTransform = transform; + return NO_ERROR; +} + // ---------------------------------------------------------------------------- int Surface::getConnectedApi() const diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index b7beb6b06fd6..e167afa2bd37 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -199,6 +199,7 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mLastVideoBuffer(NULL), mVideoBuffer(NULL), + mRTSPTimeOffset(0), mSuspensionState(NULL) { CHECK_EQ(mClient.connect(), OK); @@ -396,7 +397,11 @@ void AwesomePlayer::reset_l() { mVideoBuffer = NULL; } - mRTSPController.clear(); + if (mRTSPController != NULL) { + mRTSPController->disconnect(); + mRTSPController.clear(); + } + mRTPPusher.clear(); mRTCPPusher.clear(); mRTPSession.clear(); @@ -758,6 +763,10 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) { *positionUs = 0; } + if (mRTSPController != NULL) { + *positionUs += mRTSPTimeOffset; + } + return OK; } @@ -773,6 +782,17 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { } status_t AwesomePlayer::seekTo_l(int64_t timeUs) { + if (mRTSPController != NULL) { + pause_l(); + mRTSPController->seek(timeUs); + play_l(); + + notifyListener_l(MEDIA_SEEK_COMPLETE); + mSeekNotificationSent = true; + mRTSPTimeOffset = timeUs; + return OK; + } + if (mFlags & CACHE_UNDERRUN) { mFlags &= ~CACHE_UNDERRUN; play_l(); diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h index 2542e4ecc209..70205648b54e 100644 --- a/media/libstagefright/include/ARTSPController.h +++ b/media/libstagefright/include/ARTSPController.h @@ -33,6 +33,8 @@ struct ARTSPController : public MediaExtractor { status_t connect(const char *url); void disconnect(); + void seek(int64_t timeUs); + virtual size_t countTracks(); virtual sp<MediaSource> getTrack(size_t index); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index f34eb45615b5..f5df1b57c7a6 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -182,6 +182,7 @@ private: sp<ALooper> mLooper; sp<ARTSPController> mRTSPController; + int64_t mRTSPTimeOffset; sp<ARTPSession> mRTPSession; sp<UDPPusher> mRTPPusher, mRTCPPusher; diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index 8c56cb785608..b930184e7438 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -406,9 +406,7 @@ APacketSource::APacketSource( const sp<ASessionDescription> &sessionDesc, size_t index) : mInitCheck(NO_INIT), mFormat(new MetaData), - mEOSResult(OK), - mFirstAccessUnit(true), - mFirstAccessUnitNTP(0) { + mEOSResult(OK) { unsigned long PT; AString desc; AString params; @@ -550,9 +548,6 @@ status_t APacketSource::initCheck() const { } status_t APacketSource::start(MetaData *params) { - mFirstAccessUnit = true; - mFirstAccessUnitNTP = 0; - return OK; } @@ -600,25 +595,6 @@ void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { return; } - uint64_t ntpTime; - CHECK(buffer->meta()->findInt64( - "ntp-time", (int64_t *)&ntpTime)); - - if (mFirstAccessUnit) { - mFirstAccessUnit = false; - mFirstAccessUnitNTP = ntpTime; - } - - if (ntpTime > mFirstAccessUnitNTP) { - ntpTime -= mFirstAccessUnitNTP; - } else { - ntpTime = 0; - } - - int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); - - buffer->meta()->setInt64("timeUs", timeUs); - Mutex::Autolock autoLock(mLock); mBuffers.push_back(buffer); mCondition.signal(); @@ -632,31 +608,9 @@ void APacketSource::signalEOS(status_t result) { mCondition.signal(); } -int64_t APacketSource::getQueuedDuration(bool *eos) { +void APacketSource::flushQueue() { Mutex::Autolock autoLock(mLock); - - *eos = (mEOSResult != OK); - - if (mBuffers.empty()) { - return 0; - } - - sp<ABuffer> buffer = *mBuffers.begin(); - - uint64_t ntpTime; - CHECK(buffer->meta()->findInt64( - "ntp-time", (int64_t *)&ntpTime)); - - int64_t firstTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); - - buffer = *--mBuffers.end(); - - CHECK(buffer->meta()->findInt64( - "ntp-time", (int64_t *)&ntpTime)); - - int64_t lastTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); - - return lastTimeUs - firstTimeUs; + mBuffers.clear(); } } // namespace android diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h index 647da6e5f66f..197af3ed54f1 100644 --- a/media/libstagefright/rtsp/APacketSource.h +++ b/media/libstagefright/rtsp/APacketSource.h @@ -43,7 +43,7 @@ struct APacketSource : public MediaSource { void queueAccessUnit(const sp<ABuffer> &buffer); void signalEOS(status_t result); - int64_t getQueuedDuration(bool *eos); + void flushQueue(); protected: virtual ~APacketSource(); @@ -58,9 +58,6 @@ private: List<sp<ABuffer> > mBuffers; status_t mEOSResult; - bool mFirstAccessUnit; - uint64_t mFirstAccessUnitNTP; - DISALLOW_EVIL_CONSTRUCTORS(APacketSource); }; diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp index ceae3a676ea3..9df17cbad45c 100644 --- a/media/libstagefright/rtsp/ARTSPController.cpp +++ b/media/libstagefright/rtsp/ARTSPController.cpp @@ -33,7 +33,7 @@ ARTSPController::ARTSPController(const sp<ALooper> &looper) } ARTSPController::~ARTSPController() { - disconnect(); + CHECK_EQ((int)mState, (int)DISCONNECTED); mLooper->unregisterHandler(mReflector->id()); } @@ -80,6 +80,16 @@ void ARTSPController::disconnect() { mHandler.clear(); } +void ARTSPController::seek(int64_t timeUs) { + Mutex::Autolock autoLock(mLock); + + if (mState != CONNECTED) { + return; + } + + mHandler->seek(timeUs); +} + size_t ARTSPController::countTracks() { if (mHandler == NULL) { return 0; diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index b2419bf14a76..caff43556e86 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -38,7 +38,10 @@ struct MyHandler : public AHandler { mConn(new ARTSPConnection), mRTPConn(new ARTPConnection), mSessionURL(url), - mSetupTracksSuccessful(false) { + mSetupTracksSuccessful(false), + mSeekPending(false), + mFirstAccessUnit(true), + mFirstAccessUnitNTP(0) { mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, @@ -62,6 +65,12 @@ struct MyHandler : public AHandler { (new AMessage('abor', id()))->post(); } + void seek(int64_t timeUs) { + sp<AMessage> msg = new AMessage('seek', id()); + msg->setInt64("time", timeUs); + msg->post(); + } + virtual void onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case 'conn': @@ -88,8 +97,6 @@ struct MyHandler : public AHandler { case 'disc': { - LOG(INFO) << "disconnect completed"; - (new AMessage('quit', id()))->post(); break; } @@ -337,7 +344,20 @@ struct MyHandler : public AHandler { CHECK(accessUnit->meta()->findInt64( "ntp-time", (int64_t *)&ntpTime)); - accessUnit->meta()->setInt64("ntp-time", ntpTime); + if (mFirstAccessUnit) { + mFirstAccessUnit = false; + mFirstAccessUnitNTP = ntpTime; + } + + if (ntpTime >= mFirstAccessUnitNTP) { + ntpTime -= mFirstAccessUnitNTP; + } else { + ntpTime = 0; + } + + int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); + + accessUnit->meta()->setInt64("timeUs", timeUs); #if 0 int32_t damaged; @@ -353,6 +373,84 @@ struct MyHandler : public AHandler { break; } + case 'seek': + { + if (mSeekPending) { + break; + } + + int64_t timeUs; + CHECK(msg->findInt64("time", &timeUs)); + + mSeekPending = true; + + AString request = "PAUSE "; + request.append(mSessionURL); + request.append(" RTSP/1.0\r\n"); + + request.append("Session: "); + request.append(mSessionID); + request.append("\r\n"); + + request.append("\r\n"); + + sp<AMessage> reply = new AMessage('see1', id()); + reply->setInt64("time", timeUs); + mConn->sendRequest(request.c_str(), reply); + break; + } + + case 'see1': + { + int64_t timeUs; + CHECK(msg->findInt64("time", &timeUs)); + + AString request = "PLAY "; + request.append(mSessionURL); + request.append(" RTSP/1.0\r\n"); + + request.append("Session: "); + request.append(mSessionID); + request.append("\r\n"); + + request.append( + StringPrintf( + "Range: npt=%lld-\r\n", timeUs / 1000000ll)); + + request.append("\r\n"); + + sp<AMessage> reply = new AMessage('see2', id()); + mConn->sendRequest(request.c_str(), reply); + break; + } + + case 'see2': + { + CHECK(mSeekPending); + + LOG(INFO) << "seek completed."; + mSeekPending = false; + + int32_t result; + CHECK(msg->findInt32("result", &result)); + if (result != OK) { + LOG(ERROR) << "seek FAILED"; + break; + } + + sp<RefBase> obj; + CHECK(msg->findObject("response", &obj)); + sp<ARTSPResponse> response = + static_cast<ARTSPResponse *>(obj.get()); + + CHECK_EQ(response->mStatusCode, 200u); + + for (size_t i = 0; i < mTracks.size(); ++i) { + mTracks.editItemAt(i).mPacketSource->flushQueue(); + } + break; + } + default: TRESPASS(); break; @@ -380,6 +478,9 @@ private: AString mBaseURL; AString mSessionID; bool mSetupTracksSuccessful; + bool mSeekPending; + bool mFirstAccessUnit; + uint64_t mFirstAccessUnitNTP; struct TrackInfo { int mRTPSocket; diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 185d72a966e3..8349fe6fa797 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -47,7 +47,8 @@ <bool name="def_networks_available_notification_on">true</bool> <bool name="def_backup_enabled">false</bool> - <string name="def_backup_transport" translatable="false"></string> + <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string> + <!-- Default value for whether or not to pulse the notification LED when there is a pending notification --> <bool name="def_notification_pulse">true</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 2e95932c2927..81d82dea6aef 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -22,6 +22,8 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import android.app.backup.BackupManager; import android.content.ContentProvider; @@ -37,6 +39,7 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; +import android.os.FileObserver; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.provider.DrmStore; @@ -56,9 +59,15 @@ public class SettingsProvider extends ContentProvider { // Cache for settings, access-ordered for acting as LRU. // Guarded by themselves. - private static final int MAX_CACHE_ENTRIES = 50; - private static final SettingsCache sSystemCache = new SettingsCache(); - private static final SettingsCache sSecureCache = new SettingsCache(); + private static final int MAX_CACHE_ENTRIES = 200; + private static final SettingsCache sSystemCache = new SettingsCache("system"); + private static final SettingsCache sSecureCache = new SettingsCache("secure"); + + // The count of how many known (handled by SettingsProvider) + // database mutations are currently being handled. Used by + // sFileObserver to not reload the database when it's ourselves + // modifying it. + private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0); // Over this size we don't reject loading or saving settings but // we do consider them broken/malicious and don't keep them in @@ -67,6 +76,10 @@ public class SettingsProvider extends ContentProvider { private static final Bundle NULL_SETTING = Bundle.forPair("value", null); + // Used as a sentinel value in an instance equality test when we + // want to cache the existence of a key, but not store its value. + private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null); + protected DatabaseHelper mOpenHelper; private BackupManager mBackupManager; @@ -201,6 +214,43 @@ public class SettingsProvider extends ContentProvider { } } + // FileObserver for external modifications to the database file. + // Note that this is for platform developers only with + // userdebug/eng builds who should be able to tinker with the + // sqlite database out from under the SettingsProvider, which is + // normally the exclusive owner of the database. But we keep this + // enabled all the time to minimize development-vs-user + // differences in testing. + private static SettingsFileObserver sObserverInstance; + private class SettingsFileObserver extends FileObserver { + private final AtomicBoolean mIsDirty = new AtomicBoolean(false); + private final String mPath; + + public SettingsFileObserver(String path) { + super(path, FileObserver.CLOSE_WRITE | + FileObserver.CREATE | FileObserver.DELETE | + FileObserver.MOVED_TO | FileObserver.MODIFY); + mPath = path; + } + + public void onEvent(int event, String path) { + int modsInFlight = sKnownMutationsInFlight.get(); + if (modsInFlight > 0) { + // our own modification. + return; + } + Log.d(TAG, "external modification to " + mPath + "; event=" + event); + if (!mIsDirty.compareAndSet(false, true)) { + // already handled. (we get a few update events + // during an sqlite write) + return; + } + Log.d(TAG, "updating our caches for " + mPath); + fullyPopulateCaches(); + mIsDirty.set(false); + } + } + @Override public boolean onCreate() { mOpenHelper = new DatabaseHelper(getContext()); @@ -210,9 +260,65 @@ public class SettingsProvider extends ContentProvider { return false; } + // Watch for external modifications to the database file, + // keeping our cache in sync. + // It's kinda lame to call mOpenHelper.getReadableDatabase() + // during onCreate(), but since ensureAndroidIdIsSet has + // already done it above and initialized/upgraded the + // database, might as well just use it... + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + sObserverInstance = new SettingsFileObserver(db.getPath()); + sObserverInstance.startWatching(); + startAsyncCachePopulation(); return true; } + private void startAsyncCachePopulation() { + new Thread("populate-settings-caches") { + public void run() { + fullyPopulateCaches(); + } + }.start(); + } + + private void fullyPopulateCaches() { + fullyPopulateCache("secure", sSecureCache); + fullyPopulateCache("system", sSystemCache); + } + + // Slurp all values (if sane in number & size) into cache. + private void fullyPopulateCache(String table, SettingsCache cache) { + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor c = db.query( + table, + new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE }, + null, null, null, null, null, + "" + (MAX_CACHE_ENTRIES + 1) /* limit */); + try { + synchronized (cache) { + cache.clear(); + cache.setFullyMatchesDisk(true); // optimistic + int rows = 0; + while (c.moveToNext()) { + rows++; + String name = c.getString(0); + String value = c.getString(1); + cache.populate(name, value); + } + if (rows > MAX_CACHE_ENTRIES) { + // Somewhat redundant, as removeEldestEntry() will + // have already done this, but to be explicit: + cache.setFullyMatchesDisk(false); + Log.d(TAG, "row count exceeds max cache entries for table " + table); + } + Log.d(TAG, "cache for settings table '" + table + "' fullycached=" + + cache.fullyMatchesDisk()); + } + } finally { + c.close(); + } + } + private boolean ensureAndroidIdIsSet() { final Cursor c = query(Settings.Secure.CONTENT_URI, new String[] { Settings.NameValueTable.VALUE }, @@ -262,7 +368,19 @@ public class SettingsProvider extends ContentProvider { private Bundle lookupValue(String table, SettingsCache cache, String key) { synchronized (cache) { if (cache.containsKey(key)) { - return cache.get(key); + Bundle value = cache.get(key); + if (value != TOO_LARGE_TO_CACHE_MARKER) { + return value; + } + // else we fall through and read the value from disk + } else if (cache.fullyMatchesDisk()) { + // Fast path (very common). Don't even try touch disk + // if we know we've slurped it all in. Trying to + // touch the disk would mean waiting for yaffs2 to + // give us access, which could takes hundreds of + // milliseconds. And we're very likely being called + // from somebody's UI thread... + return NULL_SETTING; } } @@ -338,6 +456,7 @@ public class SettingsProvider extends ContentProvider { checkWritePermissions(args); SettingsCache cache = SettingsCache.forTable(args.table); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try { @@ -350,6 +469,7 @@ public class SettingsProvider extends ContentProvider { db.setTransactionSuccessful(); } finally { db.endTransaction(); + sKnownMutationsInFlight.decrementAndGet(); } sendNotify(uri); @@ -449,8 +569,10 @@ public class SettingsProvider extends ContentProvider { return Uri.withAppendedPath(url, name); } + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final long rowId = db.insert(args.table, null, initialValues); + sKnownMutationsInFlight.decrementAndGet(); if (rowId <= 0) return null; SettingsCache.populate(cache, initialValues); // before we notify @@ -471,12 +593,15 @@ public class SettingsProvider extends ContentProvider { } checkWritePermissions(args); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.delete(args.table, args.where, args.args); + sKnownMutationsInFlight.decrementAndGet(); if (count > 0) { SettingsCache.wipe(args.table); // before we notify sendNotify(url); } + startAsyncCachePopulation(); if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted"); return count; } @@ -489,12 +614,15 @@ public class SettingsProvider extends ContentProvider { } checkWritePermissions(args); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + sKnownMutationsInFlight.decrementAndGet(); int count = db.update(args.table, initialValues, args.where, args.args); if (count > 0) { SettingsCache.wipe(args.table); // before we notify sendNotify(url); } + startAsyncCachePopulation(); if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues); return count; } @@ -506,12 +634,12 @@ public class SettingsProvider extends ContentProvider { * When a client attempts to openFile the default ringtone or * notification setting Uri, we will proxy the call to the current * default ringtone's Uri (if it is in the DRM or media provider). - */ + */ int ringtoneType = RingtoneManager.getDefaultType(uri); // Above call returns -1 if the Uri doesn't match a default type if (ringtoneType != -1) { Context context = getContext(); - + // Get the current value for the default sound Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType); @@ -531,7 +659,7 @@ public class SettingsProvider extends ContentProvider { throw new FileNotFoundException(e.getMessage()); } } - + return context.getContentResolver().openFileDescriptor(soundUri, mode); } } @@ -607,13 +735,38 @@ public class SettingsProvider extends ContentProvider { */ private static final class SettingsCache extends LinkedHashMap<String, Bundle> { - public SettingsCache() { + private final String mCacheName; + private boolean mCacheFullyMatchesDisk = false; // has the whole database slurped. + + public SettingsCache(String name) { super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */); + mCacheName = name; + } + + /** + * Is the whole database table slurped into this cache? + */ + public boolean fullyMatchesDisk() { + synchronized (this) { + return mCacheFullyMatchesDisk; + } + } + + public void setFullyMatchesDisk(boolean value) { + synchronized (this) { + mCacheFullyMatchesDisk = value; + } } @Override protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_CACHE_ENTRIES; + if (size() <= MAX_CACHE_ENTRIES) { + return false; + } + synchronized (this) { + mCacheFullyMatchesDisk = false; + } + return true; } /** @@ -658,11 +811,15 @@ public class SettingsProvider extends ContentProvider { return; } String value = contentValues.getAsString(Settings.NameValueTable.VALUE); - synchronized (cache) { + cache.populate(name, value); + } + + public void populate(String name, String value) { + synchronized (this) { if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) { - cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); + put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); } else { - cache.remove(name); + put(name, TOO_LARGE_TO_CACHE_MARKER); } } } @@ -678,6 +835,7 @@ public class SettingsProvider extends ContentProvider { } synchronized (cache) { cache.clear(); + cache.mCacheFullyMatchesDisk = true; } } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 3720e166992b..212c08bff93d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -530,9 +530,9 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) return; } - // get the dirty region sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); if (newFrontBuffer != NULL) { + // get the dirty region // compute the posted region const Region dirty(lcblk->getDirtyRegion(buf)); mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); @@ -568,6 +568,13 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // we now have the correct size, unfreeze the screen mFreezeLock.clear(); } + + // get the crop region + setBufferCrop( lcblk->getCrop(buf) ); + + // get the transformation + setBufferTransform( lcblk->getTransform(buf) ); + } else { // this should not happen unless we ran out of memory while // allocating the buffer. we're hoping that things will get back diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 043d54d96210..3d049a79616f 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -54,6 +54,8 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); + mBufferCrop.makeInvalid(); + mBufferTransform = 0; } LayerBase::~LayerBase() @@ -354,6 +356,14 @@ void LayerBase::clearWithOpenGL(const Region& clip) const clearWithOpenGL(clip,0,0,0,0); } +template <typename T> +static inline +void swap(T& a, T& b) { + T t(a); + a = b; + b = t; +} + void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); @@ -387,38 +397,73 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } } - Region::const_iterator it = clip.begin(); - Region::const_iterator const end = clip.end(); - const GLfloat texCoords[4][2] = { - { 0, 0 }, - { 0, 1 }, - { 1, 1 }, - { 1, 0 } + /* + * compute texture coordinates + * here, we handle NPOT, cropping and buffer transformations + */ + + GLfloat cl, ct, cr, cb; + if (!mBufferCrop.isEmpty()) { + // source is cropped + const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width; + const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height; + cl = mBufferCrop.left * us; + ct = mBufferCrop.top * vs; + cr = mBufferCrop.right * us; + cb = mBufferCrop.bottom * vs; + } else { + cl = 0; + ct = 0; + cr = (texture.NPOTAdjust ? texture.wScale : 1.0f); + cb = (texture.NPOTAdjust ? texture.hScale : 1.0f); + } + + struct TexCoords { + GLfloat u; + GLfloat v; + }; + + enum { + // name of the corners in the texture map + LB = 0, // left-bottom + LT = 1, // left-top + RT = 2, // right-top + RB = 3 // right-bottom }; - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); + // vertices in screen space + int vLT = LB; + int vLB = LT; + int vRB = RT; + int vRT = RB; // 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; + uint32_t transform = mBufferTransform; + if (transform & HAL_TRANSFORM_ROT_90) { + vLT = RB; + vLB = LB; + vRB = LT; + vRT = RT; } - - if (texture.NPOTAdjust) { - glScalef(texture.wScale, texture.hScale, 1.0f); + if (transform & HAL_TRANSFORM_FLIP_V) { + swap(vLT, vLB); + swap(vRB, vRT); + } + if (transform & HAL_TRANSFORM_FLIP_H) { + swap(vLT, vRB); + swap(vLB, vRT); } + TexCoords texCoords[4]; + texCoords[vLT].u = cl; + texCoords[vLT].v = ct; + texCoords[vLB].u = cl; + texCoords[vLB].v = cb; + texCoords[vRB].u = cr; + texCoords[vRB].v = cb; + texCoords[vRT].u = cr; + texCoords[vRT].v = ct; + if (needsDithering()) { glEnable(GL_DITHER); } else { @@ -429,6 +474,8 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glVertexPointer(2, GL_FLOAT, 0, mVertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); while (it != end) { const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); @@ -438,6 +485,16 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glDisableClientState(GL_TEXTURE_COORD_ARRAY); } +void LayerBase::setBufferCrop(const Rect& crop) { + if (!crop.isEmpty()) { + mBufferCrop = crop; + } +} + +void LayerBase::setBufferTransform(uint32_t transform) { + mBufferTransform = transform; +} + void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index dd1cd05f1b93..c66dc3450ba8 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -232,9 +232,17 @@ protected: void clearWithOpenGL(const Region& clip) const; void drawWithOpenGL(const Region& clip, const Texture& texture) const; + // these must be called from the post/drawing thread + void setBufferCrop(const Rect& crop); + void setBufferTransform(uint32_t transform); + sp<SurfaceFlinger> mFlinger; uint32_t mFlags; + // post/drawing thread + Rect mBufferCrop; + uint32_t mBufferTransform; + // cached during validateVisibility() bool mNeedsFiltering; int32_t mOrientation; diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp index 64a43c78f446..2ee21b92b531 100644 --- a/services/surfaceflinger/LayerBlur.cpp +++ b/services/surfaceflinger/LayerBlur.cpp @@ -241,6 +241,8 @@ void LayerBlur::onDraw(const Region& clip) const glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); } } diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp index e8b2ebf16745..fdf9abc81ada 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -485,7 +485,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const mTextureManager.loadTexture(&mTexture, dirty, t); } - mTexture.transform = mBufferHeap.transform; + mLayer.setBufferTransform(mBufferHeap.transform); mLayer.drawWithOpenGL(clip, mTexture); } diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h index c7c14e70bb44..18c4348693ff 100644 --- a/services/surfaceflinger/TextureManager.h +++ b/services/surfaceflinger/TextureManager.h @@ -40,12 +40,11 @@ class GraphicBuffer; struct Image { enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 }; Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0), - transform(0), dirty(1), target(TEXTURE_2D) { } + dirty(1), target(TEXTURE_2D) { } GLuint name; EGLImageKHR image; GLuint width; GLuint height; - uint32_t transform; unsigned dirty : 1; unsigned target : 1; }; diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java index c6389587e5d5..94b936b13844 100644 --- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ListActivity.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.os.Bundle; import android.util.DisplayMetrics; +import android.view.ContextMenu; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -83,6 +84,17 @@ public class ListActivity extends Activity { ListView list = (ListView) findViewById(R.id.list); list.setAdapter(adapter); + + registerForContextMenu(list); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.setHeaderTitle("Context menu"); + menu.add("List item 1"); + menu.add("List item 2"); + menu.add("List item 3"); } private static class SimpleListAdapter extends ArrayAdapter<String> { diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java index e71c29318392..ec8d0ed2c395 100644 --- a/voip/java/android/net/sip/SipProfile.java +++ b/voip/java/android/net/sip/SipProfile.java @@ -35,7 +35,7 @@ import javax.sip.address.URI; * Class containing a SIP account, domain and server information. * @hide */ -public class SipProfile implements Parcelable, Serializable { +public class SipProfile implements Parcelable, Serializable, Cloneable { private static final long serialVersionUID = 1L; private static final int DEFAULT_PORT = 5060; private Address mAddress; @@ -46,6 +46,7 @@ public class SipProfile implements Parcelable, Serializable { private String mProfileName; private boolean mSendKeepAlive = false; private boolean mAutoRegistration = true; + private boolean mAllowOutgoingCall = false; /** @hide */ public static final Parcelable.Creator<SipProfile> CREATOR = @@ -79,6 +80,23 @@ public class SipProfile implements Parcelable, Serializable { } /** + * Creates a builder based on the given profile. + */ + public Builder(SipProfile profile) { + if (profile == null) throw new NullPointerException(); + try { + mProfile = (SipProfile) profile.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("should not occur", e); + } + mProfile.mAddress = null; + mUri = profile.getUri(); + mUri.setUserPassword(profile.getPassword()); + mDisplayName = profile.getDisplayName(); + mProxyAddress = profile.getProxyAddress(); + } + + /** * Constructor. * * @param uriString the URI string as "sip:<user_name>@<domain>" @@ -226,6 +244,18 @@ public class SipProfile implements Parcelable, Serializable { } /** + * Sets the allow-outgoing-call flag. + * + * @param flag true if allowing to make outgoing call on the profile; + * false otherwise + * @return this builder object + */ + public Builder setOutgoingCallAllowed(boolean flag) { + mProfile.mAllowOutgoingCall = flag; + return this; + } + + /** * Builds and returns the SIP profile object. * * @return the profile object created @@ -262,6 +292,7 @@ public class SipProfile implements Parcelable, Serializable { mProfileName = in.readString(); mSendKeepAlive = (in.readInt() == 0) ? false : true; mAutoRegistration = (in.readInt() == 0) ? false : true; + mAllowOutgoingCall = (in.readInt() == 0) ? false : true; } /** @hide */ @@ -274,6 +305,7 @@ public class SipProfile implements Parcelable, Serializable { out.writeString(mProfileName); out.writeInt(mSendKeepAlive ? 1 : 0); out.writeInt(mAutoRegistration ? 1 : 0); + out.writeInt(mAllowOutgoingCall ? 1 : 0); } /** @hide */ @@ -398,4 +430,11 @@ public class SipProfile implements Parcelable, Serializable { public boolean getAutoRegistration() { return mAutoRegistration; } + + /** + * Returns true if allowing to make outgoing calls on this profile. + */ + public boolean isOutgoingCallAllowed() { + return mAllowOutgoingCall; + } } |