Native-side proxy

Remove RemoteGLRenderer
 Remove reflection-based control

Change-Id: If17c2bbb61c7141986d88c4763def77ed1074985
This commit is contained in:
John Reck
2014-01-03 18:09:17 -08:00
parent 8435cf0d8b
commit 4f02bf4eef
20 changed files with 892 additions and 1412 deletions

View File

@@ -1413,15 +1413,13 @@ public class GLRenderer extends HardwareRenderer {
}
@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;
}
/**

View File

@@ -466,9 +466,8 @@ public abstract class HardwareRenderer {
* @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

File diff suppressed because it is too large Load Diff

View File

@@ -18,25 +18,26 @@ package android.view;
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 @@ import java.util.HashMap;
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 @@ public class ThreadedRenderer extends HardwareRenderer {
@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 @@ public class ThreadedRenderer extends HardwareRenderer {
@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 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
boolean loadSystemProperties() {
return (Boolean) run("loadSystemProperties");
return false;
}
@Override
@@ -174,20 +171,10 @@ public class ThreadedRenderer extends HardwareRenderer {
*
* @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 @@ public class ThreadedRenderer extends HardwareRenderer {
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 @@ public class ThreadedRenderer extends HardwareRenderer {
@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);
}

View File

@@ -660,12 +660,11 @@ public final class ViewRootImpl implements ViewParent,
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) {

View File

@@ -58,7 +58,6 @@ LOCAL_SRC_FILES:= \
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 \

View File

@@ -124,7 +124,6 @@ extern int register_android_view_GraphicBuffer(JNIEnv* env);
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 @@ static const RegJNIRec gRegJNI[] = {
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),

View File

@@ -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

View File

@@ -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 @@ namespace android {
#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 @@ private:
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 @@ const char* const kClassPathName = "android/view/ThreadedRenderer";
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
};

View File

@@ -52,6 +52,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
# RenderThread stuff
LOCAL_SRC_FILES += \
renderthread/CanvasContext.cpp \
renderthread/RenderProxy.cpp \
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp

View File

@@ -170,7 +170,7 @@ public:
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);

View File

@@ -247,8 +247,6 @@ public:
// TODO: rename for consistency
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
private:
}; // class Renderer
}; // namespace uirenderer

View File

@@ -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 @@ class GlobalContext {
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 @@ private:
// 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 @@ GlobalContext::GlobalContext()
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 @@ bool GlobalContext::loadConfig() {
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 @@ void GlobalContext::destroy() {
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 @@ bool GlobalContext::makeCurrent(EGLSurface surface) {
// 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 @@ bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
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 @@ bool CanvasContext::setSurface(EGLNativeWindowType window) {
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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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_ */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -18,6 +18,8 @@
#include "RenderThread.h"
#include "CanvasContext.h"
#include "RenderProxy.h"
#include <utils/Log.h>
namespace android {
@@ -27,8 +29,82 @@ ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
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 @@ RenderThread::~RenderThread() {
}
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 @@ bool RenderThread::threadLoop() {
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 */

View File

@@ -28,11 +28,27 @@ namespace android {
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 @@ private:
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 */