TIME LORD!
Bug: 14444180
Change-Id: I68bec3807c4d1c88d5af1aec2fe6907d60b5f2f3
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index d324439..eb0cac8 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -59,7 +59,8 @@
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,)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 63f4b95..f199236 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -406,6 +406,8 @@
}
void CanvasContext::prepareTree(TreeInfo& info) {
+ info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+
mRootRenderNode->prepareTree(info);
if (info.hasAnimations && !info.hasFunctors) {
@@ -449,12 +451,11 @@
}
// 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;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0873ad4..8022c9e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -59,7 +59,7 @@
void destroyCanvasAndSurface();
// IFrameCallback, Chroreographer-driven frame callback entry point
- virtual void doFrame(nsecs_t frameTimeNanos);
+ virtual void doFrame();
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 45f5cb0..a7b781a 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -30,13 +30,17 @@
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 @@
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 @@
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;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index c280868..ea00900 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -48,30 +48,32 @@
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
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 87886e6..7b7c019 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -62,7 +62,7 @@
args->translucent = translucent;
args->rootRenderNode = rootRenderNode;
mContext = (CanvasContext*) postAndWait(task);
- mDrawFrameTask.setContext(mContext);
+ mDrawFrameTask.setContext(&mRenderThread, mContext);
}
RenderProxy::~RenderProxy() {
@@ -79,13 +79,25 @@
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 @@
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) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index eab1395..bfa2b8d 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -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 @@
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();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index e95707a..35a3eab 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -129,8 +129,7 @@
, 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 @@
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 @@
mFrameCallbacks.swap(callbacks);
for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
- (*it)->doFrame(mFrameTime);
+ (*it)->doFrame();
}
}
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index b93dfd6..215d294 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -28,6 +28,8 @@
#include <utils/Singleton.h>
#include <utils/Thread.h>
+#include "TimeLord.h"
+
namespace android {
class DisplayEventReceiver;
@@ -53,7 +55,7 @@
// 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 @@
void postFrameCallback(IFrameCallback* callback);
void removeFrameCallback(IFrameCallback* callback);
+ TimeLord& timeLord() { return mTimeLord; }
+
protected:
virtual bool threadLoop();
@@ -102,7 +106,8 @@
std::set<IFrameCallback*> mFrameCallbacks;
bool mFrameCallbackTaskPending;
DispatchFrameCallbacks* mFrameCallbackTask;
- nsecs_t mFrameTime;
+
+ TimeLord mTimeLord;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
new file mode 100644
index 0000000..758d96e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -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 */
diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h
new file mode 100644
index 0000000..52c6d9e
--- /dev/null
+++ b/libs/hwui/renderthread/TimeLord.h
@@ -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 */