diff options
author | John Reck <jreck@google.com> | 2014-05-22 15:43:54 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2014-05-23 12:56:38 -0700 |
commit | a5dda645da738da7b4ae15e28fa7d93d3b04b94f (patch) | |
tree | 5ce51af907c91030662b69c37e8ece7a63e041ed /libs | |
parent | d30241541c3adcb126bb263ad8596e7902a6b5ae (diff) | |
download | frameworks_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.h | 8 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 29 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 13 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 15 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 17 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 63 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 13 |
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; |