summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
authorJohn Reck <jreck@google.com>2014-05-22 15:43:54 -0700
committerJohn Reck <jreck@google.com>2014-05-23 12:56:38 -0700
commita5dda645da738da7b4ae15e28fa7d93d3b04b94f (patch)
tree5ce51af907c91030662b69c37e8ece7a63e041ed /libs
parentd30241541c3adcb126bb263ad8596e7902a6b5ae (diff)
downloadframeworks_base-a5dda645da738da7b4ae15e28fa7d93d3b04b94f.zip
frameworks_base-a5dda645da738da7b4ae15e28fa7d93d3b04b94f.tar.gz
frameworks_base-a5dda645da738da7b4ae15e28fa7d93d3b04b94f.tar.bz2
Bag of scheduling tweaks
Bug: 15118640 * Prevent over-stuffing the queue by dropping frames * Prevent double-drawing in one pulse by RT by deferring vsync registration until post-draw so that it catches the next vsync pulse instead of the current one * Bias vsync race condition towards the UI thread * Fix queueDelay to actually work Change-Id: Ibf584258bd93ebcbba058bd976dc8b307f1c6155
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/TreeInfo.h8
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp29
-rw-r--r--libs/hwui/renderthread/CanvasContext.h13
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp15
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h3
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp17
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp63
-rw-r--r--libs/hwui/renderthread/RenderThread.h13
9 files changed, 130 insertions, 32 deletions
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index d4a23b8..8355f83 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -52,6 +52,7 @@ struct TreeInfo {
: hasFunctors(false)
, hasAnimations(false)
, requiresUiRedraw(false)
+ , canDrawThisFrame(true)
{}
bool hasFunctors;
// This is only updated if evaluateAnimations is true
@@ -60,6 +61,13 @@ struct TreeInfo {
// animate itself, such as if hasFunctors is true
// This is only set if hasAnimations is true
bool requiresUiRedraw;
+ // This is set to true if draw() can be called this frame
+ // false means that we must delay until the next vsync pulse as frame
+ // production is outrunning consumption
+ // NOTE that if this is false CanvasContext will set either requiresUiRedraw
+ // *OR* will post itself for the next vsync automatically, use this
+ // only to avoid calling draw()
+ bool canDrawThisFrame;
} out;
// TODO: Damage calculations
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 839ef91..b6b3428 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -360,7 +360,9 @@ void CanvasContext::destroyCanvasAndSurface() {
setSurface(NULL);
}
-void CanvasContext::setSurface(EGLNativeWindowType window) {
+void CanvasContext::setSurface(ANativeWindow* window) {
+ mNativeWindow = window;
+
if (mEglSurface != EGL_NO_SURFACE) {
mGlobalContext->destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -393,7 +395,7 @@ void CanvasContext::requireSurface() {
makeCurrent();
}
-bool CanvasContext::initialize(EGLNativeWindowType window) {
+bool CanvasContext::initialize(ANativeWindow* window) {
if (mCanvas) return false;
setSurface(window);
mCanvas = new OpenGLRenderer();
@@ -401,11 +403,11 @@ bool CanvasContext::initialize(EGLNativeWindowType window) {
return true;
}
-void CanvasContext::updateSurface(EGLNativeWindowType window) {
+void CanvasContext::updateSurface(ANativeWindow* window) {
setSurface(window);
}
-void CanvasContext::pauseSurface(EGLNativeWindowType window) {
+void CanvasContext::pauseSurface(ANativeWindow* window) {
// TODO: For now we just need a fence, in the future suspend any animations
// and such to prevent from trying to render into this surface
}
@@ -456,7 +458,15 @@ void CanvasContext::prepareTree(TreeInfo& info) {
info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
mRootRenderNode->prepareTree(info);
- if (info.out.hasAnimations) {
+ int runningBehind = 0;
+ // TODO: This query is moderately expensive, investigate adding some sort
+ // of fast-path based off when we last called eglSwapBuffers() as well as
+ // last vsync time. Or something.
+ mNativeWindow->query(mNativeWindow.get(),
+ NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+ info.out.canDrawThisFrame = !runningBehind;
+
+ if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
if (info.out.hasFunctors) {
info.out.requiresUiRedraw = true;
} else if (!info.out.requiresUiRedraw) {
@@ -467,6 +477,11 @@ void CanvasContext::prepareTree(TreeInfo& info) {
}
}
+void CanvasContext::notifyFramePending() {
+ ATRACE_CALL();
+ mRenderThread.pushBackFrameCallback(this);
+}
+
void CanvasContext::draw(Rect* dirty) {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawDisplayList called on a context with no canvas or surface!");
@@ -515,7 +530,9 @@ void CanvasContext::doFrame() {
info.prepareTextures = false;
prepareTree(info);
- draw(NULL);
+ if (info.out.canDrawThisFrame) {
+ draw(NULL);
+ }
}
void CanvasContext::invokeFunctor(Functor* functor) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4232f27..a54b33e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -48,9 +48,9 @@ public:
CanvasContext(bool translucent, RenderNode* rootRenderNode);
virtual ~CanvasContext();
- bool initialize(EGLNativeWindowType window);
- void updateSurface(EGLNativeWindowType window);
- void pauseSurface(EGLNativeWindowType window);
+ bool initialize(ANativeWindow* window);
+ void updateSurface(ANativeWindow* window);
+ void pauseSurface(ANativeWindow* window);
void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
void setOpaque(bool opaque);
void makeCurrent();
@@ -73,11 +73,15 @@ public:
ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
int64_t* map, size_t mapSize);
+ void notifyFramePending();
+
private:
+ friend class RegisterFrameCallbackTask;
+
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
void prepareTree(TreeInfo& info);
- void setSurface(EGLNativeWindowType window);
+ void setSurface(ANativeWindow* window);
void swapBuffers();
void requireSurface();
@@ -85,6 +89,7 @@ private:
GlobalContext* mGlobalContext;
RenderThread& mRenderThread;
+ sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface;
bool mDirtyRegionsEnabled;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 3b8786c..ee3e059 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -87,7 +87,13 @@ void DrawFrameTask::postAndWait() {
void DrawFrameTask::run() {
ATRACE_NAME("DrawFrame");
- bool canUnblockUiThread = syncFrameState();
+ bool canUnblockUiThread;
+ bool canDrawThisFrame;
+ {
+ TreeInfo info;
+ canUnblockUiThread = syncFrameState(info);
+ canDrawThisFrame = info.out.canDrawThisFrame;
+ }
// Grab a copy of everything we need
Rect dirty(mDirty);
@@ -98,7 +104,9 @@ void DrawFrameTask::run() {
unblockUiThread();
}
- context->draw(&dirty);
+ if (CC_LIKELY(canDrawThisFrame)) {
+ context->draw(&dirty);
+ }
if (!canUnblockUiThread) {
unblockUiThread();
@@ -111,12 +119,11 @@ static void initTreeInfo(TreeInfo& info) {
info.evaluateAnimations = true;
}
-bool DrawFrameTask::syncFrameState() {
+bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
- TreeInfo info;
initTreeInfo(info);
mContext->prepareDraw(&mLayers, info);
if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index b9307e1..acbc02a 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -24,6 +24,7 @@
#include "RenderTask.h"
#include "../Rect.h"
+#include "../TreeInfo.h"
namespace android {
namespace uirenderer {
@@ -65,7 +66,7 @@ public:
private:
void postAndWait();
- bool syncFrameState();
+ bool syncFrameState(TreeInfo& info);
void unblockUiThread();
Mutex mLock;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ef8e45b..2e103d8 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -112,7 +112,7 @@ bool RenderProxy::loadSystemProperties() {
return (bool) postAndWait(task);
}
-CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
return (void*) args->context->initialize(args->window);
}
@@ -123,7 +123,7 @@ bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
return (bool) postAndWait(task);
}
-CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, ANativeWindow* window) {
args->context->updateSurface(args->window);
return NULL;
}
@@ -135,7 +135,7 @@ void RenderProxy::updateSurface(const sp<ANativeWindow>& window) {
postAndWait(task);
}
-CREATE_BRIDGE2(pauseSurface, CanvasContext* context, EGLNativeWindowType window) {
+CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) {
args->context->pauseSurface(args->window);
return NULL;
}
@@ -292,6 +292,17 @@ void RenderProxy::fence() {
postAndWait(task);
}
+CREATE_BRIDGE1(notifyFramePending, CanvasContext* context) {
+ args->context->notifyFramePending();
+ return NULL;
+}
+
+void RenderProxy::notifyFramePending() {
+ SETUP_TASK(notifyFramePending);
+ args->context = mContext;
+ mRenderThread.queueAtFront(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 4d3499e..8aeb264 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -82,6 +82,7 @@ public:
ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
ANDROID_API void fence();
+ ANDROID_API void notifyFramePending();
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 35a3eab..4a4e254 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -37,7 +37,7 @@ namespace renderthread {
static const size_t EVENT_BUFFER_SIZE = 100;
// Slight delay to give the UI time to push us a new frame before we replay
-static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 4;
TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
@@ -91,6 +91,15 @@ void TaskQueue::queue(RenderTask* task) {
}
}
+void TaskQueue::queueAtFront(RenderTask* task) {
+ if (mTail) {
+ task->mNext = mHead;
+ mHead = 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
@@ -188,20 +197,22 @@ static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
return latest;
}
-void RenderThread::drainDisplayEventQueue() {
+void RenderThread::drainDisplayEventQueue(bool skipCallbacks) {
+ ATRACE_CALL();
nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
if (vsyncEvent > 0) {
mVsyncRequested = false;
mTimeLord.vsyncReceived(vsyncEvent);
- if (!mFrameCallbackTaskPending) {
+ if (!skipCallbacks && !mFrameCallbackTaskPending) {
+ ATRACE_NAME("queue mFrameCallbackTask");
mFrameCallbackTaskPending = true;
- //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
- queue(mFrameCallbackTask);
+ queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
}
}
}
void RenderThread::dispatchFrameCallbacks() {
+ ATRACE_CALL();
mFrameCallbackTaskPending = false;
std::set<IFrameCallback*> callbacks;
@@ -212,6 +223,15 @@ void RenderThread::dispatchFrameCallbacks() {
}
}
+void RenderThread::requestVsync() {
+ if (!mVsyncRequested) {
+ mVsyncRequested = true;
+ status_t status = mDisplayEventReceiver->requestNextVsync();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "requestNextVsync failed with status: %d", status);
+ }
+}
+
bool RenderThread::threadLoop() {
initializeDisplayEventReceiver();
@@ -236,6 +256,14 @@ bool RenderThread::threadLoop() {
timeoutMillis = 0;
}
}
+
+ if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
+ drainDisplayEventQueue(true);
+ mFrameCallbacks.insert(
+ mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
+ mPendingRegistrationFrameCallbacks.clear();
+ requestVsync();
+ }
}
return false;
@@ -250,6 +278,12 @@ void RenderThread::queue(RenderTask* task) {
}
}
+void RenderThread::queueAtFront(RenderTask* task) {
+ AutoMutex _lock(mLock);
+ mQueue.queueAtFront(task);
+ mLooper->wake();
+}
+
void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
task->mRunAt = now + milliseconds_to_nanoseconds(delayMs);
@@ -262,17 +296,18 @@ void RenderThread::remove(RenderTask* task) {
}
void RenderThread::postFrameCallback(IFrameCallback* callback) {
- mFrameCallbacks.insert(callback);
- if (!mVsyncRequested) {
- mVsyncRequested = true;
- status_t status = mDisplayEventReceiver->requestNextVsync();
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "requestNextVsync failed with status: %d", status);
- }
+ mPendingRegistrationFrameCallbacks.insert(callback);
}
void RenderThread::removeFrameCallback(IFrameCallback* callback) {
mFrameCallbacks.erase(callback);
+ mPendingRegistrationFrameCallbacks.erase(callback);
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
+ if (mFrameCallbacks.erase(callback)) {
+ mPendingRegistrationFrameCallbacks.insert(callback);
+ }
}
RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
@@ -281,11 +316,13 @@ RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
if (!next) {
mNextWakeup = LLONG_MAX;
} else {
+ mNextWakeup = next->mRunAt;
// Most tasks won't be delayed, so avoid unnecessary systemTime() calls
if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
next = mQueue.next();
+ } else {
+ next = 0;
}
- mNextWakeup = next->mRunAt;
}
if (nextWakeup) {
*nextWakeup = mNextWakeup;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 215d294..4412584 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -44,6 +44,7 @@ public:
RenderTask* next();
void queue(RenderTask* task);
+ void queueAtFront(RenderTask* task);
RenderTask* peek();
void remove(RenderTask* task);
@@ -66,12 +67,16 @@ public:
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
ANDROID_API void queue(RenderTask* task);
+ ANDROID_API void queueAtFront(RenderTask* task);
void queueDelayed(RenderTask* task, int delayMs);
void remove(RenderTask* task);
// Mimics android.view.Choreographer
void postFrameCallback(IFrameCallback* callback);
void removeFrameCallback(IFrameCallback* callback);
+ // If the callback is currently registered, it will be pushed back until
+ // the next vsync. If it is not currently registered this does nothing.
+ void pushBackFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
@@ -87,8 +92,9 @@ private:
void initializeDisplayEventReceiver();
static int displayEventReceiverCallback(int fd, int events, void* data);
- void drainDisplayEventQueue();
+ void drainDisplayEventQueue(bool skipCallbacks = false);
void dispatchFrameCallbacks();
+ void requestVsync();
// 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
@@ -104,6 +110,11 @@ private:
DisplayEventReceiver* mDisplayEventReceiver;
bool mVsyncRequested;
std::set<IFrameCallback*> mFrameCallbacks;
+ // We defer the actual registration of these callbacks until
+ // both mQueue *and* mDisplayEventReceiver have been drained off all
+ // immediate events. This makes sure that we catch the next vsync, not
+ // the previous one
+ std::set<IFrameCallback*> mPendingRegistrationFrameCallbacks;
bool mFrameCallbackTaskPending;
DispatchFrameCallbacks* mFrameCallbackTask;