Native-side proxy
Remove RemoteGLRenderer
Remove reflection-based control
Change-Id: If17c2bbb61c7141986d88c4763def77ed1074985
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 1811111..986c296 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -1413,15 +1413,13 @@
}
@Override
- boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
+ void attachFunctor(View.AttachInfo attachInfo, int functor) {
if (mCanvas != null) {
mCanvas.attachFunctor(functor);
mFunctorsRunnable.attachInfo = attachInfo;
attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0);
- return true;
}
- return false;
}
/**
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ac728f1..676ac10 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -466,9 +466,8 @@
* @see HardwareCanvas#callDrawGLFunction(int)
* @see #detachFunctor(int)
*
- * @return true if the functor was attached successfully
*/
- abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor);
+ abstract void attachFunctor(View.AttachInfo attachInfo, int functor);
/**
* Initializes the hardware renderer for the specified surface and setup the
diff --git a/core/java/android/view/RemoteGLRenderer.java b/core/java/android/view/RemoteGLRenderer.java
deleted file mode 100644
index 2919775..0000000
--- a/core/java/android/view/RemoteGLRenderer.java
+++ /dev/null
@@ -1,1074 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-package android.view;
-
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Surface.OutOfResourcesException;
-
-import java.io.PrintWriter;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Hardware renderer using OpenGL that's used as the remote endpoint
- * of ThreadedRenderer
- *
- * Currently this is mostly a copy of GLRenderer, but with a few modifications
- * to deal with the threading issues. Ideally native-side functionality
- * will replace this, but we need this to bootstrap without risking breaking
- * changes in GLRenderer
- *
- * @hide
- */
-public class RemoteGLRenderer extends HardwareRenderer {
- static final int SURFACE_STATE_ERROR = 0;
- static final int SURFACE_STATE_SUCCESS = 1;
- static final int SURFACE_STATE_UPDATED = 2;
-
- static final int FUNCTOR_PROCESS_DELAY = 4;
-
- /**
- * Number of frames to profile.
- */
- private static final int PROFILE_MAX_FRAMES = 128;
-
- /**
- * Number of floats per profiled frame.
- */
- private static final int PROFILE_FRAME_DATA_COUNT = 3;
-
- private static final int PROFILE_DRAW_MARGIN = 0;
- private static final int PROFILE_DRAW_WIDTH = 3;
- private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
- private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
- private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
- private static final String[] VISUALIZERS = {
- PROFILE_PROPERTY_VISUALIZE_BARS,
- PROFILE_PROPERTY_VISUALIZE_LINES
- };
-
- private static final String[] OVERDRAW = {
- OVERDRAW_PROPERTY_SHOW,
- OVERDRAW_PROPERTY_COUNT
- };
- private static final int OVERDRAW_TYPE_COUNT = 1;
- private static final int GL_VERSION = 2;
-
- int mWidth = -1, mHeight = -1;
-
- HardwareCanvas mCanvas;
-
- String mName;
-
- long mFrameCount;
- Paint mDebugPaint;
-
- boolean mDirtyRegionsEnabled;
- boolean mSurfaceUpdated;
-
- boolean mProfileEnabled;
- int mProfileVisualizerType = -1;
- float[] mProfileData;
- ReentrantLock mProfileLock;
- int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-
- GraphDataProvider mDebugDataProvider;
- float[][] mProfileShapes;
- Paint mProfilePaint;
-
- boolean mDebugDirtyRegions;
- int mDebugOverdraw = -1;
- HardwareLayer mDebugOverdrawLayer;
- Paint mDebugOverdrawPaint;
-
- final boolean mTranslucent;
-
- private final Rect mRedrawClip = new Rect();
-
- private final int[] mSurfaceSize = new int[2];
- private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
-
- private long mDrawDelta = Long.MAX_VALUE;
-
- private GLES20Canvas mGlCanvas;
-
- private DisplayMetrics mDisplayMetrics;
- private ThreadedRenderer mOwningRenderer;
- private long mNativeCanvasContext;
-
- HardwareCanvas createCanvas() {
- return mGlCanvas = new GLES20Canvas(mTranslucent);
- }
-
- void initCaches() {
- if (GLES20Canvas.initCaches()) {
- // Caches were (re)initialized, rebind atlas
- initAtlas();
- }
- }
-
- void initAtlas() {
- IBinder binder = ServiceManager.getService("assetatlas");
- if (binder == null) return;
-
- IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
- try {
- if (atlas.isCompatible(android.os.Process.myPpid())) {
- GraphicBuffer buffer = atlas.getBuffer();
- if (buffer != null) {
- int[] map = atlas.getMap();
- if (map != null) {
- GLES20Canvas.initAtlas(buffer, map);
- }
- // If IAssetAtlas is not the same class as the IBinder
- // we are using a remote service and we can safely
- // destroy the graphic buffer
- if (atlas.getClass() != binder.getClass()) {
- buffer.destroy();
- }
- }
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Could not acquire atlas", e);
- }
- }
-
- boolean canDraw() {
- return mCanvas != null && mGlCanvas != null;
- }
-
- int onPreDraw(Rect dirty) {
- return mGlCanvas.onPreDraw(dirty);
- }
-
- void onPostDraw() {
- mGlCanvas.onPostDraw();
- }
-
- void drawProfileData(View.AttachInfo attachInfo) {
- if (mDebugDataProvider != null) {
- final GraphDataProvider provider = mDebugDataProvider;
- initProfileDrawData(attachInfo, provider);
-
- final int height = provider.getVerticalUnitSize();
- final int margin = provider.getHorizontaUnitMargin();
- final int width = provider.getHorizontalUnitSize();
-
- int x = 0;
- int count = 0;
- int current = 0;
-
- final float[] data = provider.getData();
- final int elementCount = provider.getElementCount();
- final int graphType = provider.getGraphType();
-
- int totalCount = provider.getFrameCount() * elementCount;
- if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
- totalCount -= elementCount;
- }
-
- for (int i = 0; i < totalCount; i += elementCount) {
- if (data[i] < 0.0f) break;
-
- int index = count * 4;
- if (i == provider.getCurrentFrame() * elementCount) current = index;
-
- x += margin;
- int x2 = x + width;
-
- int y2 = mHeight;
- int y1 = (int) (y2 - data[i] * height);
-
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- case GraphDataProvider.GRAPH_TYPE_LINES: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = (x + x2) * 0.5f;
- r[index + 1] = index == 0 ? y1 : r[index - 1];
- r[index + 2] = r[index] + width;
- r[index + 3] = y1;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- }
-
-
- x += width;
- count++;
- }
-
- x += margin;
-
- drawGraph(graphType, count);
- drawCurrentFrame(graphType, current);
- drawThreshold(x, height);
- }
- }
-
- private void drawGraph(int graphType, int count) {
- for (int i = 0; i < mProfileShapes.length; i++) {
- mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawCurrentFrame(int graphType, int index) {
- if (index >= 0) {
- mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
- mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index], mHeight, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawThreshold(int x, int height) {
- float threshold = mDebugDataProvider.getThreshold();
- if (threshold > 0.0f) {
- mDebugDataProvider.setupThresholdPaint(mProfilePaint);
- int y = (int) (mHeight - threshold * height);
- mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
- }
- }
-
- private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
- if (mProfileShapes == null) {
- final int elementCount = provider.getElementCount();
- final int frameCount = provider.getFrameCount();
-
- mProfileShapes = new float[elementCount][];
- for (int i = 0; i < elementCount; i++) {
- mProfileShapes[i] = new float[frameCount * 4];
- }
-
- mProfilePaint = new Paint();
- }
-
- mProfilePaint.reset();
- if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
- mProfilePaint.setAntiAlias(true);
- }
-
- if (mDisplayMetrics == null) {
- mDisplayMetrics = new DisplayMetrics();
- }
-
- attachInfo.mDisplay.getMetrics(mDisplayMetrics);
- provider.prepare(mDisplayMetrics);
- }
-
- @Override
- void destroy(boolean full) {
- try {
- if (full && mCanvas != null) {
- mCanvas = null;
- }
- if (mNativeCanvasContext != 0) {
- destroyContext(mNativeCanvasContext);
- mNativeCanvasContext = 0;
- }
- setEnabled(false);
- } finally {
- if (full && mGlCanvas != null) {
- mGlCanvas = null;
- }
- }
- }
-
- @Override
- void pushLayerUpdate(HardwareLayer layer) {
- mGlCanvas.pushLayerUpdate(layer);
- }
-
- @Override
- void cancelLayerUpdate(HardwareLayer layer) {
- mGlCanvas.cancelLayerUpdate(layer);
- }
-
- @Override
- void flushLayerUpdates() {
- mGlCanvas.flushLayerUpdates();
- }
-
- @Override
- HardwareLayer createHardwareLayer(boolean isOpaque) {
- return new GLES20TextureLayer(isOpaque);
- }
-
- @Override
- public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
- return new GLES20RenderLayer(width, height, isOpaque);
- }
-
- void countOverdraw(HardwareCanvas canvas) {
- ((GLES20Canvas) canvas).setCountOverdrawEnabled(true);
- }
-
- float getOverdraw(HardwareCanvas canvas) {
- return ((GLES20Canvas) canvas).getOverdraw();
- }
-
- @Override
- public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
- return ((GLES20TextureLayer) layer).getSurfaceTexture();
- }
-
- @Override
- void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
- ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
- }
-
- @Override
- boolean safelyRun(Runnable action) {
- boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-
- if (needsContext) {
- if (!usePBufferSurface()) {
- return false;
- }
- }
-
- action.run();
-
- return true;
- }
-
- @Override
- void destroyLayers(final View view) {
- if (view != null) {
- safelyRun(new Runnable() {
- @Override
- public void run() {
- if (mCanvas != null) {
- mCanvas.clearLayerUpdates();
- }
- destroyHardwareLayer(view);
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
- }
- });
- }
- }
-
- private static void destroyHardwareLayer(View view) {
- view.destroyLayer(true);
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyHardwareLayer(group.getChildAt(i));
- }
- }
- }
-
- @Override
- void destroyHardwareResources(final View view) {
- if (view != null) {
- safelyRun(new Runnable() {
- @Override
- public void run() {
- if (mCanvas != null) {
- mCanvas.clearLayerUpdates();
- }
- destroyResources(view);
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
- }
- });
- }
- }
-
- private static void destroyResources(View view) {
- view.destroyHardwareResources();
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyResources(group.getChildAt(i));
- }
- }
- }
-
- RemoteGLRenderer(ThreadedRenderer owningRenderer, boolean translucent) {
- mOwningRenderer = owningRenderer;
- mTranslucent = translucent;
-
- loadSystemProperties();
- }
-
- @Override
- boolean loadSystemProperties() {
- boolean value;
- boolean changed = false;
-
- String profiling = SystemProperties.get(PROFILE_PROPERTY);
- int graphType = search(VISUALIZERS, profiling);
- value = graphType >= 0;
-
- if (graphType != mProfileVisualizerType) {
- changed = true;
- mProfileVisualizerType = graphType;
-
- mProfileShapes = null;
- mProfilePaint = null;
-
- if (value) {
- mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
- } else {
- mDebugDataProvider = null;
- }
- }
-
- // If on-screen profiling is not enabled, we need to check whether
- // console profiling only is enabled
- if (!value) {
- value = Boolean.parseBoolean(profiling);
- }
-
- if (value != mProfileEnabled) {
- changed = true;
- mProfileEnabled = value;
-
- if (mProfileEnabled) {
- Log.d(LOG_TAG, "Profiling hardware renderer");
-
- int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
- PROFILE_MAX_FRAMES);
- mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
-
- mProfileLock = new ReentrantLock();
- } else {
- mProfileData = null;
- mProfileLock = null;
- mProfileVisualizerType = -1;
- }
-
- mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- }
-
- value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
- if (value != mDebugDirtyRegions) {
- changed = true;
- mDebugDirtyRegions = value;
-
- if (mDebugDirtyRegions) {
- Log.d(LOG_TAG, "Debugging dirty regions");
- }
- }
-
- String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
- int debugOverdraw = search(OVERDRAW, overdraw);
- if (debugOverdraw != mDebugOverdraw) {
- changed = true;
- mDebugOverdraw = debugOverdraw;
-
- if (mDebugOverdraw != OVERDRAW_TYPE_COUNT) {
- if (mDebugOverdrawLayer != null) {
- mDebugOverdrawLayer.destroy();
- mDebugOverdrawLayer = null;
- mDebugOverdrawPaint = null;
- }
- }
- }
-
- if (GLRenderer.loadProperties()) {
- changed = true;
- }
-
- return changed;
- }
-
- private static int search(String[] values, String value) {
- for (int i = 0; i < values.length; i++) {
- if (values[i].equals(value)) return i;
- }
- return -1;
- }
-
- @Override
- void dumpGfxInfo(PrintWriter pw) {
- if (mProfileEnabled) {
- pw.printf("\n\tDraw\tProcess\tExecute\n");
-
- mProfileLock.lock();
- try {
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- if (mProfileData[i] < 0) {
- break;
- }
- pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
- mProfileData[i + 2]);
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
- mProfileCurrentFrame = mProfileData.length;
- } finally {
- mProfileLock.unlock();
- }
- }
- }
-
- @Override
- long getFrameCount() {
- return mFrameCount;
- }
-
- /**
- * Indicates whether this renderer instance can track and update dirty regions.
- */
- boolean hasDirtyRegions() {
- return mDirtyRegionsEnabled;
- }
-
- private void triggerSoftwareFallback() {
- destroy(true);
- // we'll try again if it was context lost
- setRequested(false);
- Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
- + "Switching back to software rendering.");
- }
-
- @Override
- boolean initialize(Surface surface) throws OutOfResourcesException {
- if (isRequested() && !isEnabled()) {
- mNativeCanvasContext = createContext();
- boolean surfaceCreated = createEglSurface(surface);
-
- if (surfaceCreated) {
- if (mCanvas == null) {
- mCanvas = createCanvas();
- }
- setEnabled(true);
- initAtlas();
- return true;
- } else {
- destroy(true);
- setRequested(false);
- }
- }
- return false;
- }
-
- @Override
- void updateSurface(Surface surface) throws OutOfResourcesException {
- if (isRequested() && isEnabled()) {
- createEglSurface(surface);
- }
- }
-
- boolean createEglSurface(Surface surface) throws OutOfResourcesException {
- // Create an EGL surface we can render into.
- if (!setSurface(mNativeCanvasContext, surface)) {
- return false;
- }
- makeCurrent(mNativeCanvasContext);
- mSurfaceUpdated = true;
-
- initCaches();
- return true;
- }
-
- @Override
- void invalidate(Surface surface) {
- setSurface(mNativeCanvasContext, null);
- setEnabled(false);
-
- if (surface.isValid()) {
- if (createEglSurface(surface) && mCanvas != null) {
- setEnabled(true);
- }
- }
- }
-
- @Override
- boolean validate() {
- return checkRenderContext() != SURFACE_STATE_ERROR;
- }
-
- @Override
- void setup(int width, int height) {
- if (validate()) {
- mCanvas.setViewport(width, height);
- mWidth = width;
- mHeight = height;
- }
- }
-
- @Override
- int getWidth() {
- return mWidth;
- }
-
- @Override
- int getHeight() {
- return mHeight;
- }
-
- @Override
- void setName(String name) {
- mName = name;
- }
-
- // TODO: Ping pong is fun and all, but this isn't the time or place
- // However we don't yet have the ability for the RenderThread to run
- // independently nor have a way to postDelayed, so this will work for now
- private Runnable mDispatchFunctorsRunnable = new Runnable() {
- @Override
- public void run() {
- ThreadedRenderer.postToRenderThread(mFunctorsRunnable);
- }
- };
-
- class FunctorsRunnable implements Runnable {
- View.AttachInfo attachInfo;
-
- @Override
- public void run() {
- final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
- if (renderer == null || !renderer.isEnabled() || renderer != mOwningRenderer) {
- return;
- }
-
- if (checkRenderContext() != SURFACE_STATE_ERROR) {
- int status = mCanvas.invokeFunctors(mRedrawClip);
- handleFunctorStatus(attachInfo, status);
- }
- }
- }
-
- /**
- * @param displayList The display list to draw
- * @param attachInfo AttachInfo tied to the specified view.
- * @param callbacks Callbacks invoked when drawing happens.
- * @param dirty The dirty rectangle to update, can be null.
- */
- void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
- HardwareDrawCallbacks callbacks, Rect dirty) {
- if (canDraw()) {
- if (!hasDirtyRegions()) {
- dirty = null;
- }
-
- final int surfaceState = checkRenderContext();
- if (surfaceState != SURFACE_STATE_ERROR) {
- HardwareCanvas canvas = mCanvas;
-
- if (mProfileEnabled) {
- mProfileLock.lock();
- }
-
- dirty = beginFrame(canvas, dirty, surfaceState);
-
- int saveCount = 0;
- int status = DisplayList.STATUS_DONE;
-
- long start = GLRenderer.getSystemTime();
- try {
- status = prepareFrame(dirty);
-
- saveCount = canvas.save();
- callbacks.onHardwarePreDraw(canvas);
-
- status |= doDrawDisplayList(attachInfo, canvas, displayList, status);
- } catch (Exception e) {
- Log.e(LOG_TAG, "An error has occurred while drawing:", e);
- } finally {
- callbacks.onHardwarePostDraw(canvas);
- canvas.restoreToCount(saveCount);
-
- mDrawDelta = GLRenderer.getSystemTime() - start;
-
- if (mDrawDelta > 0) {
- mFrameCount++;
-
- debugOverdraw(attachInfo, dirty, canvas, displayList);
- debugDirtyRegions(dirty, canvas);
- drawProfileData(attachInfo);
- }
- }
-
- onPostDraw();
-
- swapBuffers(status);
-
- if (mProfileEnabled) {
- mProfileLock.unlock();
- }
-
- attachInfo.mIgnoreDirtyState = false;
- }
- }
- }
-
- @Override
- void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
- Rect dirty) {
- throw new IllegalAccessError();
- }
-
- private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
- HardwareCanvas canvas, DisplayList displayList) {
-
- if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
- if (mDebugOverdrawLayer == null) {
- mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
- } else if (mDebugOverdrawLayer.getWidth() != mWidth ||
- mDebugOverdrawLayer.getHeight() != mHeight) {
- mDebugOverdrawLayer.resize(mWidth, mHeight);
- }
-
- if (!mDebugOverdrawLayer.isValid()) {
- mDebugOverdraw = -1;
- return;
- }
-
- HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
- countOverdraw(layerCanvas);
- final int restoreCount = layerCanvas.save();
- layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
- layerCanvas.restoreToCount(restoreCount);
- mDebugOverdrawLayer.end(canvas);
-
- float overdraw = getOverdraw(layerCanvas);
- DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();
-
- drawOverdrawCounter(canvas, overdraw, metrics.density);
- }
- }
-
- private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
- final String text = String.format("%.2fx", overdraw);
- final Paint paint = setupPaint(density);
- // HSBtoColor will clamp the values in the 0..1 range
- paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));
-
- canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
- }
-
- private Paint setupPaint(float density) {
- if (mDebugOverdrawPaint == null) {
- mDebugOverdrawPaint = new Paint();
- mDebugOverdrawPaint.setAntiAlias(true);
- mDebugOverdrawPaint.setShadowLayer(density * 3.0f, 0.0f, 0.0f, 0xff000000);
- mDebugOverdrawPaint.setTextSize(density * 20.0f);
- }
- return mDebugOverdrawPaint;
- }
-
- private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
- // We had to change the current surface and/or context, redraw everything
- if (surfaceState == SURFACE_STATE_UPDATED) {
- dirty = null;
- GLRenderer.beginFrame(null);
- } else {
- int[] size = mSurfaceSize;
- GLRenderer.beginFrame(size);
-
- if (size[1] != mHeight || size[0] != mWidth) {
- mWidth = size[0];
- mHeight = size[1];
-
- canvas.setViewport(mWidth, mHeight);
-
- dirty = null;
- }
- }
-
- if (mDebugDataProvider != null) dirty = null;
-
- return dirty;
- }
-
- private int prepareFrame(Rect dirty) {
- int status;
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
- try {
- status = onPreDraw(dirty);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- return status;
- }
-
- private int doDrawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
- DisplayList displayList, int status) {
-
- long drawDisplayListStartTime = 0;
- if (mProfileEnabled) {
- drawDisplayListStartTime = System.nanoTime();
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
- try {
- status |= canvas.drawDisplayList(displayList, mRedrawClip,
- DisplayList.FLAG_CLIP_CHILDREN);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - drawDisplayListStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 1] = total;
- }
-
- handleFunctorStatus(attachInfo, status);
- return status;
- }
-
- private void swapBuffers(int status) {
- if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
- long eglSwapBuffersStartTime = 0;
- if (mProfileEnabled) {
- eglSwapBuffersStartTime = System.nanoTime();
- }
-
- if (!swapBuffers(mNativeCanvasContext)) {
- triggerSoftwareFallback();
- }
- mSurfaceUpdated = false;
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - eglSwapBuffersStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 2] = total;
- }
- }
- }
-
- private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
- if (mDebugDirtyRegions) {
- if (mDebugPaint == null) {
- mDebugPaint = new Paint();
- mDebugPaint.setColor(0x7fff0000);
- }
-
- if (dirty != null && (mFrameCount & 1) == 0) {
- canvas.drawRect(dirty, mDebugPaint);
- }
- }
- }
-
- private void handleFunctorStatus(final View.AttachInfo attachInfo, int status) {
- // If the draw flag is set, functors will be invoked while executing
- // the tree of display lists
- if ((status & DisplayList.STATUS_DRAW) != 0) {
- // TODO: Can we just re-queue ourselves up to draw next frame instead
- // of bouncing back to the UI thread?
- // TODO: Respect mRedrawClip - for now just full inval
- attachInfo.mHandler.post(new Runnable() {
- @Override
- public void run() {
- attachInfo.mViewRootImpl.invalidate();
- }
- });
- mRedrawClip.setEmpty();
- }
-
- if ((status & DisplayList.STATUS_INVOKE) != 0 ||
- attachInfo.mHandler.hasCallbacks(mDispatchFunctorsRunnable)) {
- attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
- mFunctorsRunnable.attachInfo = attachInfo;
- attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
- }
- }
-
- @Override
- void detachFunctor(int functor) {
- if (mCanvas != null) {
- mCanvas.detachFunctor(functor);
- }
- }
-
- @Override
- boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
- if (mCanvas != null) {
- mCanvas.attachFunctor(functor);
- mFunctorsRunnable.attachInfo = attachInfo;
- attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
- attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable, 0);
- return true;
- }
- return false;
- }
-
- /**
- * Ensures the current EGL context and surface are the ones we expect.
- * This method throws an IllegalStateException if invoked from a thread
- * that did not initialize EGL.
- *
- * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
- * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
- * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
- *
- * @see #checkRenderContextUnsafe()
- */
- int checkRenderContext() {
- if (!makeCurrent(mNativeCanvasContext)) {
- triggerSoftwareFallback();
- return SURFACE_STATE_ERROR;
- }
- return mSurfaceUpdated ? SURFACE_STATE_UPDATED : SURFACE_STATE_SUCCESS;
- }
-
- private static int dpToPx(int dp, float density) {
- return (int) (dp * density + 0.5f);
- }
-
- class DrawPerformanceDataProvider extends GraphDataProvider {
- private final int mGraphType;
-
- private int mVerticalUnit;
- private int mHorizontalUnit;
- private int mHorizontalMargin;
- private int mThresholdStroke;
-
- DrawPerformanceDataProvider(int graphType) {
- mGraphType = graphType;
- }
-
- @Override
- void prepare(DisplayMetrics metrics) {
- final float density = metrics.density;
-
- mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
- mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
- mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
- mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
- }
-
- @Override
- int getGraphType() {
- return mGraphType;
- }
-
- @Override
- int getVerticalUnitSize() {
- return mVerticalUnit;
- }
-
- @Override
- int getHorizontalUnitSize() {
- return mHorizontalUnit;
- }
-
- @Override
- int getHorizontaUnitMargin() {
- return mHorizontalMargin;
- }
-
- @Override
- float[] getData() {
- return mProfileData;
- }
-
- @Override
- float getThreshold() {
- return 16;
- }
-
- @Override
- int getFrameCount() {
- return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
- }
-
- @Override
- int getElementCount() {
- return PROFILE_FRAME_DATA_COUNT;
- }
-
- @Override
- int getCurrentFrame() {
- return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
- }
-
- @Override
- void setupGraphPaint(Paint paint, int elementIndex) {
- paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
-
- @Override
- void setupThresholdPaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
- paint.setStrokeWidth(mThresholdStroke);
- }
-
- @Override
- void setupCurrentFramePaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
- }
-
- static native long createContext();
- static native boolean usePBufferSurface();
- static native boolean setSurface(long nativeCanvasContext, Surface surface);
- static native boolean swapBuffers(long nativeCanvasContext);
- static native boolean makeCurrent(long nativeCanvasContext);
- static native void destroyContext(long nativeCanvasContext);
-}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2cd1b6e..567f862 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -18,25 +18,26 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
-import android.os.Looper;
import android.os.SystemClock;
import android.os.Trace;
-import android.util.Log;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
import java.io.PrintWriter;
-import java.lang.reflect.Method;
-import java.util.HashMap;
/**
* Hardware renderer that proxies the rendering to a render thread. Most calls
- * are synchronous, however a few such as draw() are posted async. The display list
- * is shared between the two threads and is guarded by a top level lock.
+ * are currently synchronous.
+ * TODO: Make draw() async.
+ * TODO: Figure out how to share the DisplayList between two threads (global lock?)
*
* The UI thread can block on the RenderThread, but RenderThread must never
* block on the UI thread.
*
+ * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
+ * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
+ * by the lifecycle of the RenderProxy.
+ *
* Note that although currently the EGL context & surfaces are created & managed
* by the render thread, the goal is to move that into a shared structure that can
* be managed by both threads. EGLSurface creation & deletion should ideally be
@@ -48,49 +49,29 @@
public class ThreadedRenderer extends HardwareRenderer {
private static final String LOGTAG = "ThreadedRenderer";
- @SuppressWarnings("serial")
- static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{
- Method[] methods = RemoteGLRenderer.class.getDeclaredMethods();
- for (Method m : methods) {
- m.setAccessible(true);
- put(m.getName() + ":" + m.getParameterTypes().length, m);
- }
- }};
- static boolean sNeedsInit = true;
+ private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1);
- private RemoteGLRenderer mRemoteRenderer;
private int mWidth, mHeight;
- private RTJob mPreviousDraw;
+ private long mNativeProxy;
ThreadedRenderer(boolean translucent) {
- mRemoteRenderer = new RemoteGLRenderer(this, translucent);
- setEnabled(true);
- if (sNeedsInit) {
- sNeedsInit = false;
- postToRenderThread(new Runnable() {
- @Override
- public void run() {
- // Hack to allow GLRenderer to create a handler to post the EGL
- // destruction to, although it'll never run
- Looper.prepare();
- }
- });
- }
+ mNativeProxy = nCreateProxy(translucent);
+ setEnabled(mNativeProxy != 0);
}
@Override
void destroy(boolean full) {
- run("destroy", full);
+ nDestroyCanvas(mNativeProxy);
}
@Override
boolean initialize(Surface surface) throws OutOfResourcesException {
- return (Boolean) run("initialize", surface);
+ return nInitialize(mNativeProxy, surface);
}
@Override
void updateSurface(Surface surface) throws OutOfResourcesException {
- post("updateSurface", surface);
+ nUpdateSurface(mNativeProxy, surface);
}
@Override
@@ -100,12 +81,27 @@
@Override
void destroyHardwareResources(View view) {
- run("destroyHardwareResources", view);
+ // TODO: canvas.clearLayerUpdates()
+ destroyResources(view);
+ // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ }
+
+ private static void destroyResources(View view) {
+ view.destroyHardwareResources();
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+
+ int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ destroyResources(group.getChildAt(i));
+ }
+ }
}
@Override
void invalidate(Surface surface) {
- post("invalidate", surface);
+ updateSurface(surface);
}
@Override
@@ -116,14 +112,15 @@
@Override
boolean safelyRun(Runnable action) {
- return (Boolean) run("safelyRun", action);
+ // TODO:
+ return false;
}
@Override
void setup(int width, int height) {
mWidth = width;
mHeight = height;
- post("setup", width, height);
+ nSetup(mNativeProxy, width, height);
}
@Override
@@ -149,7 +146,7 @@
@Override
boolean loadSystemProperties() {
- return (Boolean) run("loadSystemProperties");
+ return false;
}
@Override
@@ -174,20 +171,10 @@
*
* @hide */
public void repeatLastDraw() {
- if (mPreviousDraw == null) {
- throw new IllegalStateException("There isn't a previous draw");
- }
- synchronized (mPreviousDraw) {
- mPreviousDraw.completed = false;
- }
- mPreviousDraw.args[3] = null;
- postToRenderThread(mPreviousDraw);
}
@Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
- requireCompletion(mPreviousDraw);
-
attachInfo.mIgnoreDirtyState = true;
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
view.mPrivateFlags |= View.PFLAG_DRAWN;
@@ -202,8 +189,11 @@
view.mRecreateDisplayList = false;
- mPreviousDraw = post("drawDisplayList", displayList, attachInfo,
- callbacks, dirty);
+ if (dirty == null) {
+ dirty = NULL_RECT;
+ }
+ nDrawDisplayList(mNativeProxy, displayList.getNativeDisplayList(),
+ dirty.left, dirty.top, dirty.right, dirty.bottom);
}
@Override
@@ -228,85 +218,40 @@
@Override
void detachFunctor(int functor) {
- run("detachFunctor", functor);
+ nDetachFunctor(mNativeProxy, functor);
}
@Override
- boolean attachFunctor(AttachInfo attachInfo, int functor) {
- return (Boolean) run("attachFunctor", attachInfo, functor);
+ void attachFunctor(AttachInfo attachInfo, int functor) {
+ nAttachFunctor(mNativeProxy, functor);
}
@Override
void setName(String name) {
- post("setName", name);
}
- private static void requireCompletion(RTJob job) {
- if (job != null) {
- synchronized (job) {
- if (!job.completed) {
- try {
- job.wait();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
- }
-
- private RTJob post(String method, Object... args) {
- RTJob job = new RTJob();
- job.method = sMethodLut.get(method + ":" + args.length);
- job.args = args;
- job.target = mRemoteRenderer;
- if (job.method == null) {
- throw new NullPointerException("Couldn't find method: " + method);
- }
- postToRenderThread(job);
- return job;
- }
-
- private Object run(String method, Object... args) {
- RTJob job = new RTJob();
- job.method = sMethodLut.get(method + ":" + args.length);
- job.args = args;
- job.target = mRemoteRenderer;
- if (job.method == null) {
- throw new NullPointerException("Couldn't find method: " + method);
- }
- synchronized (job) {
- postToRenderThread(job);
- try {
- job.wait();
- return job.ret;
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- static class RTJob implements Runnable {
- Method method;
- Object[] args;
- Object target;
- Object ret;
- boolean completed = false;
-
- @Override
- public void run() {
- try {
- ret = method.invoke(target, args);
- synchronized (this) {
- completed = true;
- notify();
- }
- } catch (Exception e) {
- Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e);
- }
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nDeleteProxy(mNativeProxy);
+ } finally {
+ super.finalize();
}
}
/** @hide */
public static native void postToRenderThread(Runnable runnable);
+
+ private static native long nCreateProxy(boolean translucent);
+ private static native void nDeleteProxy(long nativeProxy);
+
+ private static native boolean nInitialize(long nativeProxy, Surface window);
+ private static native void nUpdateSurface(long nativeProxy, Surface window);
+ private static native void nSetup(long nativeProxy, int width, int height);
+ private static native void nDrawDisplayList(long nativeProxy, long displayList,
+ int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ private static native void nDestroyCanvas(long nativeProxy);
+
+ private static native void nAttachFunctor(long nativeProxy, long functor);
+ private static native void nDetachFunctor(long nativeProxy, long functor);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 41b0c67..5aa46f3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -660,12 +660,11 @@
mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
}
- public boolean attachFunctor(int functor) {
+ public void attachFunctor(int functor) {
//noinspection SimplifiableIfStatement
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
- return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
+ mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
}
- return false;
}
public void detachFunctor(int functor) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d72b054..f8d96e3 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -58,7 +58,6 @@
android_view_GLRenderer.cpp \
android_view_GLES20Canvas.cpp \
android_view_ThreadedRenderer.cpp \
- android_view_RemoteGLRenderer.cpp \
android_view_MotionEvent.cpp \
android_view_PointerIcon.cpp \
android_view_VelocityTracker.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 074d7ac..7ed6641 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -124,7 +124,6 @@
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_GLRenderer(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
-extern int register_android_view_RemoteGLRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
extern int register_android_view_SurfaceSession(JNIEnv* env);
@@ -1129,7 +1128,6 @@
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_GLRenderer),
REG_JNI(register_android_view_ThreadedRenderer),
- REG_JNI(register_android_view_RemoteGLRenderer),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),
REG_JNI(register_android_view_SurfaceSession),
diff --git a/core/jni/android_view_RemoteGLRenderer.cpp b/core/jni/android_view_RemoteGLRenderer.cpp
deleted file mode 100644
index 96a203b..0000000
--- a/core/jni/android_view_RemoteGLRenderer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "RemoteGLRenderer"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <utils/StrongPointer.h>
-#include <android_runtime/android_view_Surface.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <renderthread/CanvasContext.h>
-#include <system/window.h>
-
-namespace android {
-
-#ifdef USE_OPENGL_RENDERER
-
-#define CHECK_CONTEXT(c) if (!c) ALOGE("Null context passed to %s!", __func__ )
-
-namespace RT = android::uirenderer::renderthread;
-
-static jlong android_view_RemoteGLRenderer_createContext(JNIEnv* env, jobject clazz) {
- RT::CanvasContext* context = new RT::CanvasContext();
- return reinterpret_cast<jlong>(context);
-}
-
-static jboolean android_view_RemoteGLRenderer_usePBufferSurface(JNIEnv* env, jobject clazz) {
- return RT::CanvasContext::useGlobalPBufferSurface();
-}
-
-static jboolean android_view_RemoteGLRenderer_setSurface(JNIEnv* env, jobject clazz,
- jlong jcontextptr, jobject jsurface) {
- RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
- CHECK_CONTEXT(context);
- sp<ANativeWindow> window;
- if (jsurface) {
- window = android_view_Surface_getNativeWindow(env, jsurface);
- }
- return context->setSurface(window.get());
-}
-
-static jboolean android_view_RemoteGLRenderer_swapBuffers(JNIEnv* env, jobject clazz,
- jlong jcontextptr) {
- RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
- CHECK_CONTEXT(context);
- return context->swapBuffers();
-}
-
-static jboolean android_view_RemoteGLRenderer_makeCurrent(JNIEnv* env, jobject clazz,
- jlong jcontextptr) {
- RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
- CHECK_CONTEXT(context);
- return context->makeCurrent();
-}
-
-static void android_view_RemoteGLRenderer_destroyContext(JNIEnv* env, jobject clazz,
- jlong jcontextptr) {
- RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
- CHECK_CONTEXT(context);
- delete context;
-}
-#endif
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/RemoteGLRenderer";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
- { "createContext", "()J", (void*) android_view_RemoteGLRenderer_createContext },
- { "usePBufferSurface", "()Z", (void*) android_view_RemoteGLRenderer_usePBufferSurface },
- { "setSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_RemoteGLRenderer_setSurface },
- { "swapBuffers", "(J)Z", (void*) android_view_RemoteGLRenderer_swapBuffers },
- { "makeCurrent", "(J)Z", (void*) android_view_RemoteGLRenderer_makeCurrent },
- { "destroyContext", "(J)V", (void*) android_view_RemoteGLRenderer_destroyContext },
-#endif
-};
-
-int register_android_view_RemoteGLRenderer(JNIEnv* env) {
- return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8d77e36..8e121de 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -20,6 +20,11 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/StrongPointer.h>
+#include <android_runtime/android_view_Surface.h>
+#include <system/window.h>
+
+#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
@@ -27,23 +32,22 @@
#ifdef USE_OPENGL_RENDERER
-namespace RT = android::uirenderer::renderthread;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
static jmethodID gRunnableMethod;
-class JavaTask : public RT::RenderTask {
+class JavaTask : public RenderTask {
public:
JavaTask(JNIEnv* env, jobject jrunnable) {
env->GetJavaVM(&mVm);
mRunnable = env->NewGlobalRef(jrunnable);
}
- virtual ~JavaTask() {
- env()->DeleteGlobalRef(mRunnable);
- }
-
virtual void run() {
env()->CallVoidMethod(mRunnable, gRunnableMethod);
+ env()->DeleteGlobalRef(mRunnable);
+ delete this;
};
private:
@@ -61,8 +65,70 @@
static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
jobject jrunnable) {
- RT::RenderTask* task = new JavaTask(env, jrunnable);
- RT::RenderThread::getInstance().queue(task);
+ RenderTask* task = new JavaTask(env, jrunnable);
+ RenderThread::getInstance().queue(task);
+}
+
+static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
+ jboolean translucent) {
+ return (jlong) new RenderProxy(translucent);
+}
+
+static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
+ jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ delete proxy;
+}
+
+static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jobject jsurface) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
+ return proxy->initialize(window.get());
+}
+
+static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jobject jsurface) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ sp<ANativeWindow> window;
+ if (jsurface) {
+ window = android_view_Surface_getNativeWindow(env, jsurface);
+ }
+ proxy->updateSurface(window.get());
+}
+
+static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jint width, jint height) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ proxy->setup(width, height);
+}
+
+static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop,
+ jint dirtyRight, jint dirtyBottom) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ DisplayList* displayList = reinterpret_cast<DisplayList*>( displayListPtr);
+ proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+}
+
+static void android_view_ThreadedRenderer_destroyCanvas(JNIEnv* env, jobject clazz,
+ jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ proxy->destroyCanvas();
+}
+
+static void android_view_ThreadedRenderer_attachFunctor(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong functorPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+ proxy->attachFunctor(functor);
+}
+
+static void android_view_ThreadedRenderer_detachFunctor(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jlong functorPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+ Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+ proxy->detachFunctor(functor);
}
#endif
@@ -76,6 +142,15 @@
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
{ "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread },
+ { "nCreateProxy", "(Z)J", (void*) android_view_ThreadedRenderer_createProxy },
+ { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
+ { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
+ { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
+ { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
+ { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList},
+ { "nDestroyCanvas", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvas},
+ { "nAttachFunctor", "(JJ)V", (void*) android_view_ThreadedRenderer_attachFunctor},
+ { "nDetachFunctor", "(JJ)V", (void*) android_view_ThreadedRenderer_detachFunctor},
#endif
};
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 88f11db..e4648f6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -52,6 +52,7 @@
# RenderThread stuff
LOCAL_SRC_FILES += \
renderthread/CanvasContext.cpp \
+ renderthread/RenderProxy.cpp \
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fb780ce..6371561 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -170,7 +170,7 @@
int saveLayerDeferred(float left, float top, float right, float bottom,
int alpha, SkXfermode::Mode mode, int flags);
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags);
+ virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags = 1);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 9d4e83e..4799b32 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -247,8 +247,6 @@
// TODO: rename for consistency
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
-
-private:
}; // class Renderer
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ffb8a32..a848c8f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,14 +19,22 @@
#include "CanvasContext.h"
#include <cutils/properties.h>
+#include <private/hwui/DrawGlInfo.h>
#include <strings.h>
+#include "RenderThread.h"
#include "../Caches.h"
+#include "../OpenGLRenderer.h"
#include "../Stencil.h"
#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
#define GLES_VERSION 2
+#ifdef USE_OPENGL_RENDERER
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+#endif
+
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -69,19 +77,19 @@
public:
static GlobalContext* get();
- // Returns true if EGL was initialized,
- // false if it was already initialized
- bool initialize();
+ // Returns true on success, false on failure
+ void initialize();
- bool usePBufferSurface();
+ void usePBufferSurface();
EGLSurface createSurface(EGLNativeWindowType window);
void destroySurface(EGLSurface surface);
void destroy();
bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
- bool makeCurrent(EGLSurface surface);
- bool swapBuffers(EGLSurface surface);
+ void makeCurrent(EGLSurface surface);
+ void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+ void swapBuffers(EGLSurface surface);
bool enableDirtyRegions(EGLSurface surface);
@@ -90,8 +98,9 @@
// GlobalContext is never destroyed, method is purposely not implemented
~GlobalContext();
- bool loadConfig();
- bool createContext();
+ void loadConfig();
+ void createContext();
+ void initAtlas();
static GlobalContext* sContext;
@@ -126,33 +135,27 @@
ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
}
-bool GlobalContext::initialize() {
- if (mEglDisplay != EGL_NO_DISPLAY) return false;
+void GlobalContext::initialize() {
+ if (mEglDisplay != EGL_NO_DISPLAY) return;
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (mEglDisplay == EGL_NO_DISPLAY) {
- ALOGE("Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
EGLint major, minor;
- if (eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE) {
- ALOGE("Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
- return false;
- }
+ LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+ "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
- if (!loadConfig()) {
- return false;
- }
- if (!createContext()) {
- return false;
- }
-
- return true;
+ loadConfig();
+ createContext();
+ usePBufferSurface();
+ Caches::getInstance().init();
+ initAtlas();
}
-bool GlobalContext::loadConfig() {
+void GlobalContext::loadConfig() {
EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
@@ -177,31 +180,32 @@
mCanSetDirtyRegions = false;
loadConfig();
} else {
- ALOGE("Failed to choose config, error = %s", egl_error_str());
- return false;
+ LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
}
}
- return true;
}
-bool GlobalContext::createContext() {
+void GlobalContext::createContext() {
EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
- if (mEglContext == EGL_NO_CONTEXT) {
- ALOGE("Failed to create context, error = %s", egl_error_str());
- return false;
- }
- return true;
+ LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+ "Failed to create context, error = %s", egl_error_str());
}
-bool GlobalContext::usePBufferSurface() {
- if (mEglDisplay == EGL_NO_DISPLAY) return false;
+void GlobalContext::initAtlas() {
+ // TODO implement
+ // For now just run without an atlas
+}
+
+void GlobalContext::usePBufferSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "usePBufferSurface() called on uninitialized GlobalContext!");
if (mPBufferSurface == EGL_NO_SURFACE) {
EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
}
- return makeCurrent(mPBufferSurface);
+ makeCurrent(mPBufferSurface);
}
EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
@@ -238,8 +242,8 @@
mCurrentSurface = EGL_NO_SURFACE;
}
-bool GlobalContext::makeCurrent(EGLSurface surface) {
- if (isCurrent(surface)) return true;
+void GlobalContext::makeCurrent(EGLSurface surface) {
+ if (isCurrent(surface)) return;
if (surface == EGL_NO_SURFACE) {
// If we are setting EGL_NO_SURFACE we don't care about any of the potential
@@ -247,19 +251,31 @@
// destroyed in which case the current context is already NO_CONTEXT
eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
} else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
- ALOGE("Failed to make current on surface %p, error=%s", (void*)surface, egl_error_str());
- return false;
+ LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+ (void*)surface, egl_error_str());
}
mCurrentSurface = surface;
- return true;
}
-bool GlobalContext::swapBuffers(EGLSurface surface) {
- if (!eglSwapBuffers(mEglDisplay, surface)) {
- ALOGW("eglSwapBuffers failed on surface %p, error=%s", (void*)surface, egl_error_str());
- return false;
+void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Tried to beginFrame on EGL_NO_SURFACE!");
+ makeCurrent(surface);
+ if (width) {
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
}
- return true;
+ if (height) {
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+ }
+ eglBeginFrame(mEglDisplay, surface);
+}
+
+void GlobalContext::swapBuffers(EGLSurface surface) {
+ eglSwapBuffers(mEglDisplay, surface);
+ EGLint err = eglGetError();
+ // TODO: Check whether we need to special case EGL_CONTEXT_LOST
+ LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+ "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
}
bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
@@ -283,17 +299,32 @@
return value == EGL_BUFFER_PRESERVED;
}
-CanvasContext::CanvasContext()
- : mEglSurface(EGL_NO_SURFACE)
- , mDirtyRegionsEnabled(false) {
+CanvasContext::CanvasContext(bool translucent)
+ : mRenderThread(RenderThread::getInstance())
+ , mEglSurface(EGL_NO_SURFACE)
+ , mDirtyRegionsEnabled(false)
+ , mOpaque(!translucent)
+ , mCanvas(0)
+ , mHaveNewSurface(false)
+ , mInvokeFunctorsPending(false)
+ , mInvokeFunctorsTask(this) {
mGlobalContext = GlobalContext::get();
}
CanvasContext::~CanvasContext() {
+ removeFunctorsTask();
+ destroyCanvas();
+}
+
+void CanvasContext::destroyCanvas() {
+ if (mCanvas) {
+ delete mCanvas;
+ mCanvas = 0;
+ }
setSurface(NULL);
}
-bool CanvasContext::setSurface(EGLNativeWindowType window) {
+void CanvasContext::setSurface(EGLNativeWindowType window) {
if (mEglSurface != EGL_NO_SURFACE) {
mGlobalContext->destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -301,24 +332,134 @@
if (window) {
mEglSurface = mGlobalContext->createSurface(window);
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "Failed to create EGLSurface for window %p, eglErr = %s",
+ (void*) window, egl_error_str());
}
if (mEglSurface != EGL_NO_SURFACE) {
mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+ mHaveNewSurface = true;
}
- return !window || mEglSurface != EGL_NO_SURFACE;
}
-bool CanvasContext::swapBuffers() {
- return mGlobalContext->swapBuffers(mEglSurface);
+void CanvasContext::swapBuffers() {
+ mGlobalContext->swapBuffers(mEglSurface);
+ mHaveNewSurface = false;
}
-bool CanvasContext::makeCurrent() {
- return mGlobalContext->makeCurrent(mEglSurface);
+void CanvasContext::makeCurrent() {
+ mGlobalContext->makeCurrent(mEglSurface);
}
-bool CanvasContext::useGlobalPBufferSurface() {
- return GlobalContext::get()->usePBufferSurface();
+bool CanvasContext::initialize(EGLNativeWindowType window) {
+ if (mCanvas) return false;
+ setSurface(window);
+ makeCurrent();
+ mCanvas = new OpenGLRenderer();
+ mCanvas->initProperties();
+ return true;
+}
+
+void CanvasContext::updateSurface(EGLNativeWindowType window) {
+ setSurface(window);
+ makeCurrent();
+}
+
+void CanvasContext::setup(int width, int height) {
+ if (!mCanvas) return;
+ mCanvas->setViewport(width, height);
+}
+
+void CanvasContext::drawDisplayList(DisplayList* displayList, Rect* dirty) {
+ LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
+ "drawDisplayList called on a context with no canvas or surface!");
+
+ EGLint width, height;
+ mGlobalContext->beginFrame(mEglSurface, &width, &height);
+ if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
+ mCanvas->setViewport(width, height);
+ dirty = NULL;
+ } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
+ dirty = NULL;
+ }
+
+ status_t status;
+ if (dirty) {
+ status = mCanvas->prepareDirty(dirty->left, dirty->top,
+ dirty->right, dirty->bottom, mOpaque);
+ } else {
+ status = mCanvas->prepare(mOpaque);
+ }
+
+ Rect outBounds;
+ status |= mCanvas->drawDisplayList(displayList, outBounds);
+ handleFunctorStatus(status, outBounds);
+
+ // TODO: Draw debug info
+ // TODO: Performance tracking
+
+ mCanvas->finish();
+
+ if (status & DrawGlInfo::kStatusDrew) {
+ swapBuffers();
+ }
+}
+
+void InvokeFunctorsTask::run() {
+ mContext->invokeFunctors();
+}
+
+void CanvasContext::attachFunctor(Functor* functor) {
+ if (!mCanvas) return;
+
+ mCanvas->attachFunctor(functor);
+ removeFunctorsTask();
+ queueFunctorsTask(0);
+}
+
+void CanvasContext::detachFunctor(Functor* functor) {
+ if (!mCanvas) return;
+
+ mCanvas->detachFunctor(functor);
+}
+
+void CanvasContext::invokeFunctors() {
+ mInvokeFunctorsPending = false;
+
+ if (!mCanvas) return;
+
+ makeCurrent();
+ Rect dirty;
+ int status = mCanvas->invokeFunctors(dirty);
+ handleFunctorStatus(status, dirty);
+}
+
+void CanvasContext::handleFunctorStatus(int status, const Rect& redrawClip) {
+ if (status & DrawGlInfo::kStatusDraw) {
+ // TODO: Invalidate the redrawClip
+ // Do we need to post to ViewRootImpl like the current renderer?
+ // Can we just enqueue ourselves to re-invoke the same display list?
+ // Something else entirely? Does ChromiumView still want this in a
+ // RenderThread world?
+ }
+
+ if (status & DrawGlInfo::kStatusInvoke) {
+ queueFunctorsTask();
+ }
+}
+
+void CanvasContext::removeFunctorsTask() {
+ if (!mInvokeFunctorsPending) return;
+
+ mRenderThread.remove(&mInvokeFunctorsTask);
+}
+
+void CanvasContext::queueFunctorsTask(int delayMs) {
+ if (mInvokeFunctorsPending) return;
+
+ mInvokeFunctorsPending = true;
+ mRenderThread.queueDelayed(&mInvokeFunctorsTask, delayMs);
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 77ae737..2daa905 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -19,31 +19,75 @@
#include <cutils/compiler.h>
#include <EGL/egl.h>
+#include <utils/Functor.h>
+
+#include "RenderTask.h"
+
+#define FUNCTOR_PROCESS_DELAY 4
namespace android {
namespace uirenderer {
+
+class DisplayList;
+class OpenGLRenderer;
+class Rect;
+
namespace renderthread {
class GlobalContext;
+class CanvasContext;
+class RenderThread;
+
+class InvokeFunctorsTask : public RenderTask {
+public:
+ InvokeFunctorsTask(CanvasContext* context)
+ : mContext(context) {}
+
+ virtual void run();
+
+private:
+ CanvasContext* mContext;
+};
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
class CanvasContext {
public:
- ANDROID_API CanvasContext();
- ANDROID_API ~CanvasContext();
+ CanvasContext(bool translucent);
+ ~CanvasContext();
- ANDROID_API bool setSurface(EGLNativeWindowType window);
- ANDROID_API bool swapBuffers();
- ANDROID_API bool makeCurrent();
+ bool initialize(EGLNativeWindowType window);
+ void updateSurface(EGLNativeWindowType window);
+ void setup(int width, int height);
+ void drawDisplayList(DisplayList* displayList, Rect* dirty);
+ void destroyCanvas();
- ANDROID_API static bool useGlobalPBufferSurface();
+ void attachFunctor(Functor* functor);
+ void detachFunctor(Functor* functor);
private:
+ void setSurface(EGLNativeWindowType window);
+ void swapBuffers();
+ void makeCurrent();
+
+ friend class InvokeFunctorsTask;
+ void invokeFunctors();
+ void handleFunctorStatus(int status, const Rect& redrawClip);
+ void removeFunctorsTask();
+ void queueFunctorsTask(int delayMs = FUNCTOR_PROCESS_DELAY);
GlobalContext* mGlobalContext;
+ RenderThread& mRenderThread;
EGLSurface mEglSurface;
bool mDirtyRegionsEnabled;
+
+ bool mOpaque;
+ OpenGLRenderer* mCanvas;
+ bool mHaveNewSurface;
+
+ bool mInvokeFunctorsPending;
+ InvokeFunctorsTask mInvokeFunctorsTask;
+
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
new file mode 100644
index 0000000..25badac
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "RenderProxy"
+
+#include "RenderProxy.h"
+
+#include "CanvasContext.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#include "../DisplayList.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ARGS(method) method ## Args
+
+#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
+#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
+#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
+#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
+#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
+ typedef struct { \
+ a1; a2; a3; a4; a5; a6; a7; a8; \
+ } ARGS(name); \
+ static void* Bridge_ ## name(ARGS(name)* args)
+
+#define SETUP_TASK(method) \
+ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
+ "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \
+ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
+ MethodInvokeRenderTask* task = createTask((RunnableMethod) Bridge_ ## method); \
+ ARGS(method) *args = (ARGS(method) *) task->payload()
+
+CREATE_BRIDGE1(createContext, bool translucent) {
+ return new CanvasContext(args->translucent);
+}
+
+RenderProxy::RenderProxy(bool translucent)
+ : mRenderThread(RenderThread::getInstance())
+ , mContext(0) {
+ SETUP_TASK(createContext);
+ args->translucent = translucent;
+ mContext = (CanvasContext*) postAndWait(task);
+}
+
+RenderProxy::~RenderProxy() {
+ destroyContext();
+}
+
+CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
+ delete args->context;
+ return NULL;
+}
+
+void RenderProxy::destroyContext() {
+ if (mContext) {
+ SETUP_TASK(destroyContext);
+ args->context = mContext;
+ mContext = 0;
+ post(task);
+ }
+}
+
+CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+ return (void*) args->context->initialize(args->window);
+}
+
+bool RenderProxy::initialize(EGLNativeWindowType window) {
+ SETUP_TASK(initialize);
+ args->context = mContext;
+ args->window = window;
+ return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+ args->context->updateSurface(args->window);
+ return NULL;
+}
+
+void RenderProxy::updateSurface(EGLNativeWindowType window) {
+ SETUP_TASK(updateSurface);
+ args->context = mContext;
+ args->window = window;
+ post(task);
+}
+
+CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) {
+ args->context->setup(args->width, args->height);
+ return NULL;
+}
+
+void RenderProxy::setup(int width, int height) {
+ SETUP_TASK(setup);
+ args->context = mContext;
+ args->width = width;
+ args->height = height;
+ post(task);
+}
+
+CREATE_BRIDGE3(drawDisplayList, CanvasContext* context, DisplayList* displayList,
+ Rect dirty) {
+ Rect* dirty = &args->dirty;
+ if (dirty->bottom == -1 && dirty->left == -1 &&
+ dirty->top == -1 && dirty->right == -1) {
+ dirty = 0;
+ }
+ args->context->drawDisplayList(args->displayList, dirty);
+ return NULL;
+}
+
+void RenderProxy::drawDisplayList(DisplayList* displayList,
+ int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+ SETUP_TASK(drawDisplayList);
+ args->context = mContext;
+ args->displayList = displayList;
+ args->dirty.set(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ // TODO: Switch to post() once some form of thread safety strategy is in place
+ postAndWait(task);
+}
+
+CREATE_BRIDGE1(destroyCanvas, CanvasContext* context) {
+ args->context->destroyCanvas();
+ return NULL;
+}
+
+void RenderProxy::destroyCanvas() {
+ SETUP_TASK(destroyCanvas);
+ args->context = mContext;
+ post(task);
+}
+
+CREATE_BRIDGE2(attachFunctor, CanvasContext* context, Functor* functor) {
+ args->context->attachFunctor(args->functor);
+ return NULL;
+}
+
+void RenderProxy::attachFunctor(Functor* functor) {
+ SETUP_TASK(attachFunctor);
+ args->context = mContext;
+ args->functor = functor;
+ post(task);
+}
+
+CREATE_BRIDGE2(detachFunctor, CanvasContext* context, Functor* functor) {
+ args->context->detachFunctor(args->functor);
+ return NULL;
+}
+
+void RenderProxy::detachFunctor(Functor* functor) {
+ SETUP_TASK(detachFunctor);
+ args->context = mContext;
+ args->functor = functor;
+ post(task);
+}
+
+MethodInvokeRenderTask* RenderProxy::createTask(RunnableMethod method) {
+ // TODO: Consider having a small pool of these to avoid alloc churn
+ return new MethodInvokeRenderTask(method);
+}
+
+void RenderProxy::post(RenderTask* task) {
+ mRenderThread.queue(task);
+}
+
+void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
+ void* retval;
+ task->setReturnPtr(&retval);
+ SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
+ AutoMutex _lock(mSyncMutex);
+ mRenderThread.queue(&syncTask);
+ mSyncCondition.wait(mSyncMutex);
+ return retval;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
new file mode 100644
index 0000000..113c5a8
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 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 RENDERPROXY_H_
+#define RENDERPROXY_H_
+
+#include "RenderTask.h"
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <utils/Condition.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayList;
+class Rect;
+
+namespace renderthread {
+
+class CanvasContext;
+class ErrorChannel;
+class RenderThread;
+class RenderProxyBridge;
+
+/*
+ * RenderProxy is strictly single threaded. All methods must be invoked on the owning
+ * thread. It is important to note that RenderProxy may be deleted while it has
+ * tasks post()'d as a result. Therefore any RenderTask that is post()'d must not
+ * reference RenderProxy or any of its fields. The exception here is that postAndWait()
+ * references RenderProxy fields. This is safe as RenderProxy cannot
+ * be deleted if it is blocked inside a call.
+ */
+class ANDROID_API RenderProxy {
+public:
+ ANDROID_API RenderProxy(bool translucent);
+ ANDROID_API virtual ~RenderProxy();
+
+ ANDROID_API bool initialize(EGLNativeWindowType window);
+ ANDROID_API void updateSurface(EGLNativeWindowType window);
+ ANDROID_API void setup(int width, int height);
+ ANDROID_API void drawDisplayList(DisplayList* displayList,
+ int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ ANDROID_API void destroyCanvas();
+
+ ANDROID_API void attachFunctor(Functor* functor);
+ ANDROID_API void detachFunctor(Functor* functor);
+
+private:
+ RenderThread& mRenderThread;
+ CanvasContext* mContext;
+
+ Mutex mSyncMutex;
+ Condition mSyncCondition;
+
+ void destroyContext();
+
+ MethodInvokeRenderTask* createTask(RunnableMethod method);
+ void post(RenderTask* task);
+ void* postAndWait(MethodInvokeRenderTask* task);
+
+ // Friend class to help with bridging
+ friend class RenderProxyBridge;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERPROXY_H_ */
diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp
index 2da91c5..7ca61e4 100644
--- a/libs/hwui/renderthread/RenderTask.cpp
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -19,15 +19,18 @@
#include "RenderTask.h"
#include <utils/Log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
namespace android {
namespace uirenderer {
namespace renderthread {
-RenderTask::RenderTask() : mNext(0) {
-}
-
-RenderTask::~RenderTask() {
+void SignalingRenderTask::run() {
+ mTask->run();
+ mLock->lock();
+ mSignal->signal();
+ mLock->unlock();
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
index 865b1e6..9fe7573 100644
--- a/libs/hwui/renderthread/RenderTask.h
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -18,19 +18,79 @@
#define RENDERTASK_H_
#include <cutils/compiler.h>
+#include <utils/Timers.h>
namespace android {
+class Mutex;
+class Condition;
namespace uirenderer {
namespace renderthread {
+#define METHOD_INVOKE_PAYLOAD_SIZE (8 * sizeof(void*))
+
+/*
+ * Notes about memory management
+ *
+ * RenderThread will only invoke RenderTask::run(). It is the responsibility
+ * of the RenderTask to know if it needs to suicide at the end of run() or
+ * if some other lifecycle is being used. As such, it is not valid to reference
+ * anything on RenderTask after the first call to run().
+ *
+ * For example SignalingRenderTask
+ * is expected to be stack allocated by the calling thread, so it does not
+ * suicide in run() but instead relies on the caller to destroy it.
+ *
+ * MethodInvokeRenderTask however is currently allocated with new, so it will
+ * suicide at the end of run(). TODO: Replace this with a small pool to avoid
+ * malloc/free churn of small objects?
+ */
+
class ANDROID_API RenderTask {
public:
- ANDROID_API RenderTask();
- ANDROID_API virtual ~RenderTask();
+ ANDROID_API RenderTask() : mNext(0), mRunAt(0) {}
+ ANDROID_API virtual ~RenderTask() {}
ANDROID_API virtual void run() = 0;
RenderTask* mNext;
+ nsecs_t mRunAt;
+};
+
+class SignalingRenderTask : public RenderTask {
+public:
+ // Takes ownership of task, caller owns lock and signal
+ SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
+ : mTask(task), mLock(lock), mSignal(signal) {}
+ virtual void run();
+
+private:
+ RenderTask* mTask;
+ Mutex* mLock;
+ Condition* mSignal;
+};
+
+typedef void* (*RunnableMethod)(void* data);
+
+class MethodInvokeRenderTask : public RenderTask {
+public:
+ MethodInvokeRenderTask(RunnableMethod method)
+ : mMethod(method), mReturnPtr(0) {}
+
+ void* payload() { return mData; }
+ void setReturnPtr(void** retptr) { mReturnPtr = retptr; }
+
+ virtual void run() {
+ void* retval = mMethod(mData);
+ if (mReturnPtr) {
+ *mReturnPtr = retval;
+ }
+ // Commit suicide
+ delete this;
+ }
+private:
+ RunnableMethod mMethod;
+ char mData[METHOD_INVOKE_PAYLOAD_SIZE];
+ void** mReturnPtr;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index bccd6e6..e4ec164 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -18,6 +18,8 @@
#include "RenderThread.h"
+#include "CanvasContext.h"
+#include "RenderProxy.h"
#include <utils/Log.h>
namespace android {
@@ -27,8 +29,82 @@
namespace uirenderer {
namespace renderthread {
+TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
+
+RenderTask* TaskQueue::next() {
+ RenderTask* ret = mHead;
+ if (ret) {
+ mHead = ret->mNext;
+ if (!mHead) {
+ mTail = 0;
+ }
+ ret->mNext = 0;
+ }
+ return ret;
+}
+
+RenderTask* TaskQueue::peek() {
+ return mHead;
+}
+
+void TaskQueue::queue(RenderTask* task) {
+ // Since the RenderTask itself forms the linked list it is not allowed
+ // to have the same task queued twice
+ LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
+ if (mTail) {
+ // Fast path if we can just append
+ if (mTail->mRunAt <= task->mRunAt) {
+ mTail->mNext = task;
+ mTail = task;
+ } else {
+ // Need to find the proper insertion point
+ RenderTask* previous = 0;
+ RenderTask* next = mHead;
+ while (next && next->mRunAt <= task->mRunAt) {
+ previous = next;
+ next = next->mNext;
+ }
+ if (!previous) {
+ task->mNext = mHead;
+ mHead = task;
+ } else {
+ previous->mNext = task;
+ if (next) {
+ task->mNext = next;
+ } else {
+ mTail = task;
+ }
+ }
+ }
+ } else {
+ mTail = mHead = task;
+ }
+}
+
+void TaskQueue::remove(RenderTask* task) {
+ // TaskQueue is strict here to enforce that users are keeping track of
+ // their RenderTasks due to how their memory is managed
+ LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task,
+ "Cannot remove a task that isn't in the queue!");
+
+ // If task is the head we can just call next() to pop it off
+ // Otherwise we need to scan through to find the task before it
+ if (peek() == task) {
+ next();
+ } else {
+ RenderTask* previous = mHead;
+ while (previous->mNext != task) {
+ previous = previous->mNext;
+ }
+ previous->mNext = task->mNext;
+ if (mTail == task) {
+ mTail = previous;
+ }
+ }
+}
+
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
- , mQueueHead(0), mQueueTail(0) {
+ , mNextWakeup(LLONG_MAX) {
mLooper = new Looper(false);
run("RenderThread");
}
@@ -37,16 +113,25 @@
}
bool RenderThread::threadLoop() {
+ int timeoutMillis = -1;
for (;;) {
- int result = mLooper->pollAll(-1);
- if (result == Looper::POLL_ERROR) {
- // TODO Something?
- break;
- }
+ int result = mLooper->pollAll(timeoutMillis);
+ LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
+ "RenderThread Looper POLL_ERROR!");
+
+ nsecs_t nextWakeup;
// Process our queue, if we have anything
- while (RenderTask* task = nextTask()) {
+ while (RenderTask* task = nextTask(&nextWakeup)) {
task->run();
- delete task;
+ // task may have deleted itself, do not reference it again
+ }
+ if (nextWakeup == LLONG_MAX) {
+ timeoutMillis = -1;
+ } else {
+ timeoutMillis = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
+ if (timeoutMillis < 0) {
+ timeoutMillis = 0;
+ }
}
}
@@ -55,30 +140,40 @@
void RenderThread::queue(RenderTask* task) {
AutoMutex _lock(mLock);
- if (mQueueTail) {
- mQueueTail->mNext = task;
- } else {
- mQueueHead = task;
- }
- mQueueTail = task;
- if (mQueueHead == task) {
- // Only wake if this is the first task
+ mQueue.queue(task);
+ if (mNextWakeup && task->mRunAt < mNextWakeup) {
+ mNextWakeup = 0;
mLooper->wake();
}
}
-RenderTask* RenderThread::nextTask() {
+void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ task->mRunAt = now + delayMs;
+ queue(task);
+}
+
+void RenderThread::remove(RenderTask* task) {
AutoMutex _lock(mLock);
- RenderTask* ret = mQueueHead;
- if (ret) {
- if (mQueueTail == mQueueHead) {
- mQueueTail = mQueueHead = 0;
- } else {
- mQueueHead = ret->mNext;
+ mQueue.remove(task);
+}
+
+RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
+ AutoMutex _lock(mLock);
+ RenderTask* next = mQueue.peek();
+ if (!next) {
+ mNextWakeup = LLONG_MAX;
+ } else {
+ // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
+ if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
+ next = mQueue.next();
}
- ret->mNext = 0;
+ mNextWakeup = next->mRunAt;
}
- return ret;
+ if (nextWakeup) {
+ *nextWakeup = mNextWakeup;
+ }
+ return next;
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 4edd575..e444aa0 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -28,11 +28,27 @@
namespace uirenderer {
namespace renderthread {
+class TaskQueue {
+public:
+ TaskQueue();
+
+ RenderTask* next();
+ void queue(RenderTask* task);
+ RenderTask* peek();
+ void remove(RenderTask* task);
+
+private:
+ RenderTask* mHead;
+ RenderTask* mTail;
+};
+
class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
public:
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ void queueDelayed(RenderTask* task, int delayMs);
+ void remove(RenderTask* task);
protected:
virtual bool threadLoop();
@@ -43,13 +59,16 @@
RenderThread();
virtual ~RenderThread();
- RenderTask* nextTask();
+ // Returns the next task to be run. If this returns NULL nextWakeup is set
+ // to the time to requery for the nextTask to run. mNextWakeup is also
+ // set to this time
+ RenderTask* nextTask(nsecs_t* nextWakeup);
sp<Looper> mLooper;
Mutex mLock;
- RenderTask* mQueueHead;
- RenderTask* mQueueTail;
+ nsecs_t mNextWakeup;
+ TaskQueue mQueue;
};
} /* namespace renderthread */