Native-side proxy
Remove RemoteGLRenderer Remove reflection-based control Change-Id: If17c2bbb61c7141986d88c4763def77ed1074985
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -247,8 +247,6 @@ public:
|
||||
|
||||
// TODO: rename for consistency
|
||||
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
|
||||
|
||||
private:
|
||||
}; // class Renderer
|
||||
|
||||
}; // namespace uirenderer
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
194
libs/hwui/renderthread/RenderProxy.cpp
Normal file
194
libs/hwui/renderthread/RenderProxy.cpp
Normal 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 */
|
||||
85
libs/hwui/renderthread/RenderProxy.h
Normal file
85
libs/hwui/renderthread/RenderProxy.h
Normal 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_ */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user