TIME LORD!

Bug: 14444180

Change-Id: I68bec3807c4d1c88d5af1aec2fe6907d60b5f2f3
This commit is contained in:
John Reck
2014-05-02 16:46:41 -07:00
parent 29e594c520
commit 18f16e6fba
14 changed files with 181 additions and 43 deletions

View File

@@ -259,6 +259,14 @@ public final class Choreographer {
return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
}
/**
* @return The refresh rate as the nanoseconds between frames
* @hide
*/
long getFrameIntervalNanos() {
return mFrameIntervalNanos;
}
void dump(String prefix, PrintWriter writer) {
String innerPrefix = prefix + " ";
writer.print(prefix); writer.println("Choreographer:");

View File

@@ -19,7 +19,6 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.SystemClock;
import android.os.Trace;
import android.view.Surface.OutOfResourcesException;
import android.view.View.AttachInfo;
@@ -52,16 +51,23 @@ public class ThreadedRenderer extends HardwareRenderer {
private static final Rect NULL_RECT = new Rect();
private static final long NANOS_PER_MS = 1000000;
private int mWidth, mHeight;
private long mNativeProxy;
private boolean mInitialized = false;
private RenderNode mRootNode;
private Choreographer mChoreographer;
ThreadedRenderer(boolean translucent) {
long rootNodePtr = nCreateRootRenderNode();
mRootNode = RenderNode.adopt(rootNodePtr);
mRootNode.setClipToBounds(false);
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
// Setup timing
mChoreographer = Choreographer.getInstance();
nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
}
@Override
@@ -161,15 +167,6 @@ public class ThreadedRenderer extends HardwareRenderer {
return false;
}
/**
* TODO: Remove
* Temporary hack to allow RenderThreadTest prototype app to trigger
* replaying a DisplayList after modifying the displaylist properties
*
* @hide */
public void repeatLastDraw() {
}
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
view.mPrivateFlags |= View.PFLAG_DRAWN;
@@ -194,7 +191,8 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
attachInfo.mIgnoreDirtyState = true;
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
long frameTimeNanos = mChoreographer.getFrameTimeNanos();
attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS;
updateRootDisplayList(view, callbacks);
@@ -203,7 +201,8 @@ public class ThreadedRenderer extends HardwareRenderer {
if (dirty == null) {
dirty = NULL_RECT;
}
nSyncAndDrawFrame(mNativeProxy, dirty.left, dirty.top, dirty.right, dirty.bottom);
nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
dirty.left, dirty.top, dirty.right, dirty.bottom);
}
@Override
@@ -297,13 +296,15 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native long nCreateProxy(boolean translucent, long rootRenderNode);
private static native void nDeleteProxy(long nativeProxy);
private static native void nSetFrameInterval(long nativeProxy, long frameIntervalNanos);
private static native boolean nInitialize(long nativeProxy, Surface window);
private static native void nUpdateSurface(long nativeProxy, Surface window);
private static native void nPauseSurface(long nativeProxy, Surface window);
private static native void nSetup(long nativeProxy, int width, int height);
private static native void nSetDisplayListData(long nativeProxy, long displayList,
long newData);
private static native void nSyncAndDrawFrame(long nativeProxy,
private static native void nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
private static native void nDestroyCanvasAndSurface(long nativeProxy);

View File

@@ -152,6 +152,12 @@ static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz
delete proxy;
}
static void android_view_ThreadedRenderer_setFrameInterval(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong frameIntervalNanos) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
proxy->setFrameInterval(frameIntervalNanos);
}
static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
jlong proxyPtr, jobject jsurface) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -186,10 +192,10 @@ static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz,
}
static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jint dirtyLeft, jint dirtyTop,
jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
jint dirtyRight, jint dirtyBottom) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
proxy->syncAndDrawFrame(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
}
static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -261,11 +267,12 @@ static JNINativeMethod gMethods[] = {
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
{ "nSetFrameInterval", "(JJ)V", (void*) android_view_ThreadedRenderer_setFrameInterval },
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
{ "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSyncAndDrawFrame", "(JIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nSyncAndDrawFrame", "(JJIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
{ "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
{ "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },

View File

@@ -59,7 +59,8 @@ ifeq ($(USE_OPENGL_RENDERER),true)
renderthread/DrawFrameTask.cpp \
renderthread/RenderProxy.cpp \
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp
renderthread/RenderThread.cpp \
renderthread/TimeLord.cpp
intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)

View File

@@ -406,6 +406,8 @@ void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* lay
}
void CanvasContext::prepareTree(TreeInfo& info) {
info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
mRootRenderNode->prepareTree(info);
if (info.hasAnimations && !info.hasFunctors) {
@@ -449,12 +451,11 @@ void CanvasContext::draw(Rect* dirty) {
}
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
void CanvasContext::doFrame() {
ATRACE_CALL();
TreeInfo info;
info.evaluateAnimations = true;
info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
info.performStagingPush = false;
info.prepareTextures = false;

View File

@@ -59,7 +59,7 @@ public:
void destroyCanvasAndSurface();
// IFrameCallback, Chroreographer-driven frame callback entry point
virtual void doFrame(nsecs_t frameTimeNanos);
virtual void doFrame();
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);

View File

@@ -30,13 +30,17 @@ namespace android {
namespace uirenderer {
namespace renderthread {
DrawFrameTask::DrawFrameTask() : mContext(0) {
DrawFrameTask::DrawFrameTask()
: mRenderThread(NULL)
, mContext(NULL)
, mFrameTimeNanos(NULL) {
}
DrawFrameTask::~DrawFrameTask() {
}
void DrawFrameTask::setContext(CanvasContext* context) {
void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
mRenderThread = thread;
mContext = context;
}
@@ -59,18 +63,20 @@ void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
mDirty.set(left, top, right, bottom);
}
void DrawFrameTask::drawFrame(RenderThread* renderThread) {
void DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
postAndWait(renderThread);
mFrameTimeNanos = frameTimeNanos;
postAndWait();
// Reset the single-frame data
mFrameTimeNanos = 0;
mDirty.setEmpty();
}
void DrawFrameTask::postAndWait(RenderThread* renderThread) {
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
renderThread->queue(this);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
@@ -99,13 +105,11 @@ static void initTreeInfo(TreeInfo& info) {
info.prepareTextures = true;
info.performStagingPush = true;
info.evaluateAnimations = true;
// TODO: Get this from Choreographer
nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs);
}
bool DrawFrameTask::syncFrameState() {
ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
TreeInfo info;

View File

@@ -48,30 +48,32 @@ public:
DrawFrameTask();
virtual ~DrawFrameTask();
void setContext(CanvasContext* context);
void setContext(RenderThread* thread, CanvasContext* context);
void addLayer(DeferredLayerUpdater* layer);
void removeLayer(DeferredLayerUpdater* layer);
void setDirty(int left, int top, int right, int bottom);
void drawFrame(RenderThread* renderThread);
void drawFrame(nsecs_t frameTimeNanos);
virtual void run();
private:
void postAndWait(RenderThread* renderThread);
void postAndWait();
bool syncFrameState();
void unblockUiThread();
Mutex mLock;
Condition mSignal;
RenderThread* mRenderThread;
CanvasContext* mContext;
/*********************************************
* Single frame data
*********************************************/
Rect mDirty;
nsecs_t mFrameTimeNanos;
/*********************************************
* Multi frame data

View File

@@ -62,7 +62,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
args->translucent = translucent;
args->rootRenderNode = rootRenderNode;
mContext = (CanvasContext*) postAndWait(task);
mDrawFrameTask.setContext(mContext);
mDrawFrameTask.setContext(&mRenderThread, mContext);
}
RenderProxy::~RenderProxy() {
@@ -79,13 +79,25 @@ void RenderProxy::destroyContext() {
SETUP_TASK(destroyContext);
args->context = mContext;
mContext = 0;
mDrawFrameTask.setContext(0);
mDrawFrameTask.setContext(NULL, NULL);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
postAndWait(task);
}
}
CREATE_BRIDGE2(setFrameInterval, RenderThread* thread, nsecs_t frameIntervalNanos) {
args->thread->timeLord().setFrameInterval(args->frameIntervalNanos);
return NULL;
}
void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
SETUP_TASK(setFrameInterval);
args->thread = &mRenderThread;
args->frameIntervalNanos = frameIntervalNanos;
post(task);
}
CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
return (void*) args->context->initialize(args->window);
}
@@ -134,10 +146,10 @@ void RenderProxy::setup(int width, int height) {
post(task);
}
void RenderProxy::syncAndDrawFrame(
void RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
mDrawFrameTask.drawFrame(&mRenderThread);
mDrawFrameTask.drawFrame(frameTimeNanos);
}
CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {

View File

@@ -25,6 +25,7 @@
#include <utils/Condition.h>
#include <utils/Functor.h>
#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
@@ -59,11 +60,13 @@ public:
ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
ANDROID_API virtual ~RenderProxy();
ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
ANDROID_API void setup(int width, int height);
ANDROID_API void syncAndDrawFrame(
ANDROID_API void syncAndDrawFrame(nsecs_t frameTimeNanos,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
ANDROID_API void destroyCanvasAndSurface();

View File

@@ -129,8 +129,7 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
, mDisplayEventReceiver(0)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
, mFrameCallbackTask(0)
, mFrameTime(0) {
, mFrameCallbackTask(0) {
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
@@ -193,7 +192,7 @@ void RenderThread::drainDisplayEventQueue() {
nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
if (vsyncEvent > 0) {
mVsyncRequested = false;
mFrameTime = vsyncEvent;
mTimeLord.vsyncReceived(vsyncEvent);
if (!mFrameCallbackTaskPending) {
mFrameCallbackTaskPending = true;
//queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
@@ -209,7 +208,7 @@ void RenderThread::dispatchFrameCallbacks() {
mFrameCallbacks.swap(callbacks);
for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
(*it)->doFrame(mFrameTime);
(*it)->doFrame();
}
}

View File

@@ -28,6 +28,8 @@
#include <utils/Singleton.h>
#include <utils/Thread.h>
#include "TimeLord.h"
namespace android {
class DisplayEventReceiver;
@@ -53,7 +55,7 @@ private:
// Mimics android.view.Choreographer.FrameCallback
class IFrameCallback {
public:
virtual void doFrame(nsecs_t frameTimeNanos) = 0;
virtual void doFrame() = 0;
protected:
~IFrameCallback() {}
@@ -71,6 +73,8 @@ public:
void postFrameCallback(IFrameCallback* callback);
void removeFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
protected:
virtual bool threadLoop();
@@ -102,7 +106,8 @@ private:
std::set<IFrameCallback*> mFrameCallbacks;
bool mFrameCallbackTaskPending;
DispatchFrameCallbacks* mFrameCallbackTask;
nsecs_t mFrameTime;
TimeLord mTimeLord;
};
} /* namespace renderthread */

View File

@@ -0,0 +1,46 @@
/*
* 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.
*/
#include "TimeLord.h"
namespace android {
namespace uirenderer {
namespace renderthread {
TimeLord::TimeLord()
: mFrameIntervalNanos(0)
, mFrameTimeNanos(0) {
}
void TimeLord::vsyncReceived(nsecs_t vsync) {
if (vsync > mFrameTimeNanos) {
mFrameTimeNanos = vsync;
}
}
nsecs_t TimeLord::frameTimeMs() {
// Logic copied from Choreographer.java
nsecs_t now = systemTime(CLOCK_MONOTONIC);
nsecs_t jitterNanos = now - mFrameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
nsecs_t lastFrameOffset = jitterNanos % mFrameIntervalNanos;
mFrameTimeNanos = now - lastFrameOffset;
}
return nanoseconds_to_milliseconds(mFrameTimeNanos);
}
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -0,0 +1,49 @@
/*
* 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.
*/
#ifndef TIMELORD_H
#define TIMELORD_H
#include <utils/Timers.h>
namespace android {
namespace uirenderer {
namespace renderthread {
class RenderThread;
// This class serves as a helper to filter & manage frame times from multiple sources
// ensuring that time flows linearly and smoothly
class TimeLord {
public:
void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; }
void vsyncReceived(nsecs_t vsync);
nsecs_t frameTimeMs();
private:
friend class RenderThread;
TimeLord();
~TimeLord() {}
nsecs_t mFrameIntervalNanos;
nsecs_t mFrameTimeNanos;
};
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
#endif /* TIMELORD_H */