diff options
Diffstat (limited to 'libs/hwui/renderthread')
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 30 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 15 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 1 | ||||
| -rw-r--r-- | libs/hwui/renderthread/EglManager.cpp | 64 | ||||
| -rw-r--r-- | libs/hwui/renderthread/EglManager.h | 14 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 69 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 10 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 36 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/TimeLord.cpp | 6 | ||||
| -rw-r--r-- | libs/hwui/renderthread/TimeLord.h | 3 |
12 files changed, 176 insertions, 84 deletions
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 92a30e5..b7e1752 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -42,7 +42,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, : mRenderThread(thread) , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) - , mDirtyRegionsEnabled(false) + , mBufferPreserved(false) + , mSwapBehavior(kSwap_default) , mOpaque(!translucent) , mCanvas(NULL) , mHaveNewSurface(false) @@ -70,6 +71,8 @@ void CanvasContext::destroy() { } void CanvasContext::setSurface(ANativeWindow* window) { + ATRACE_CALL(); + mNativeWindow = window; if (mEglSurface != EGL_NO_SURFACE) { @@ -82,7 +85,8 @@ void CanvasContext::setSurface(ANativeWindow* window) { } if (mEglSurface != EGL_NO_SURFACE) { - mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); + const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); + mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); mHaveNewSurface = true; makeCurrent(); } else { @@ -103,6 +107,10 @@ void CanvasContext::requireSurface() { makeCurrent(); } +void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { + mSwapBehavior = swapBehavior; +} + bool CanvasContext::initialize(ANativeWindow* window) { setSurface(window); if (mCanvas) return false; @@ -115,13 +123,13 @@ void CanvasContext::updateSurface(ANativeWindow* window) { setSurface(window); } -void CanvasContext::pauseSurface(ANativeWindow* window) { - stopDrawing(); +bool CanvasContext::pauseSurface(ANativeWindow* window) { + return mRenderThread.removeFrameCallback(this); } // TODO: don't pass viewport size, it's automatic via EGL -void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { +void CanvasContext::setup(int width, int height, const Vector3& lightCenter, + float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { if (!mCanvas) return; mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha); } @@ -160,6 +168,11 @@ void CanvasContext::prepareTree(TreeInfo& info) { freePrefetechedLayers(); } + if (CC_UNLIKELY(!mNativeWindow.get())) { + info.out.canDrawThisFrame = false; + return; + } + 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 @@ -200,7 +213,7 @@ void CanvasContext::draw() { if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); - } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { + } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { @@ -230,6 +243,8 @@ void CanvasContext::draw() { if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); + } else { + mEglManager.cancelFrame(); } profiler().finishFrame(); @@ -330,6 +345,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { // No context means nothing to free if (!thread.eglManager().hasEglContext()) return; + ATRACE_CALL(); thread.eglManager().requireGlContext(); if (level >= TRIM_MEMORY_COMPLETE) { Caches::getInstance().flush(Caches::kFlushMode_Full); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index d4282fa..0cc2c7c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -48,6 +48,11 @@ namespace renderthread { class EglManager; +enum SwapBehavior { + kSwap_default, + kSwap_discardBuffer, +}; + // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager @@ -57,9 +62,14 @@ public: IContextFactory* contextFactory); virtual ~CanvasContext(); + // Won't take effect until next EGLSurface creation + void setSwapBehavior(SwapBehavior swapBehavior); + bool initialize(ANativeWindow* window); void updateSurface(ANativeWindow* window); - void pauseSurface(ANativeWindow* window); + bool pauseSurface(ANativeWindow* window); + bool hasSurface() { return mNativeWindow.get(); } + void setup(int width, int height, const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setOpaque(bool opaque); @@ -111,7 +121,8 @@ private: EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; EGLSurface mEglSurface; - bool mDirtyRegionsEnabled; + bool mBufferPreserved; + SwapBehavior mSwapBehavior; bool mOpaque; OpenGLRenderer* mCanvas; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index dd34e09..97b31a9 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -132,6 +132,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mLayers.clear(); mContext->prepareTree(info); + // This is after the prepareTree so that any pending operations + // (RenderNode tree state, prefetched layers, etc...) will be flushed. + if (CC_UNLIKELY(!mContext->hasSurface())) { + mSyncResult |= kSync_LostSurfaceRewardIfFound; + } + if (info.out.hasAnimations) { if (info.out.requiresUiRedraw) { mSyncResult |= kSync_UIRedrawRequired; diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 243cc5d..28f6cb2 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -42,6 +42,7 @@ class RenderThread; enum SyncResult { kSync_OK = 0, kSync_UIRedrawRequired = 1 << 0, + kSync_LostSurfaceRewardIfFound = 1 << 1, }; /* diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index a87834e..8fb1b10 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -70,17 +70,20 @@ EglManager::EglManager(RenderThread& thread) , mEglConfig(0) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mRequestDirtyRegions(load_dirty_regions_property()) + , mAllowPreserveBuffer(load_dirty_regions_property()) , mCurrentSurface(EGL_NO_SURFACE) , mAtlasMap(NULL) - , mAtlasMapSize(0) { - mCanSetDirtyRegions = mRequestDirtyRegions; - ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); + , mAtlasMapSize(0) + , mInFrame(false) { + mCanSetPreserveBuffer = mAllowPreserveBuffer; + ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false"); } void EglManager::initialize() { if (hasEglContext()) return; + ATRACE_NAME("Creating EGLContext"); + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); @@ -105,15 +108,16 @@ bool EglManager::hasEglContext() { void EglManager::requireGlContext() { LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context"); - // We don't care *WHAT* surface is active, just that one is active to give - // us access to the GL context - if (mCurrentSurface == EGL_NO_SURFACE) { + if (!mInFrame) { + // We can't be certain about the state of the current surface (whether + // or not it is destroyed, for example), so err on the side of using + // the pbuffer surface which we fully control usePBufferSurface(); } } void EglManager::loadConfig() { - EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint swapBehavior = mCanSetPreserveBuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; EGLint attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -131,10 +135,10 @@ void EglManager::loadConfig() { if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) || num_configs != 1) { // Failed to get a valid config - if (mCanSetDirtyRegions) { + if (mCanSetPreserveBuffer) { ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); // Try again without dirty regions enabled - mCanSetDirtyRegions = false; + mCanSetPreserveBuffer = false; loadConfig(); } else { LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); @@ -171,7 +175,8 @@ void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, void EglManager::initAtlas() { if (mAtlasBuffer.get()) { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + mRenderThread.renderState().assetAtlas().init(mAtlasBuffer, + mAtlasMap, mAtlasMapSize); } } @@ -252,9 +257,11 @@ void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); } eglBeginFrame(mEglDisplay, surface); + mInFrame = true; } bool EglManager::swapBuffers(EGLSurface surface) { + mInFrame = false; eglSwapBuffers(mEglDisplay, surface); EGLint err = eglGetError(); if (CC_LIKELY(err == EGL_SUCCESS)) { @@ -273,25 +280,34 @@ bool EglManager::swapBuffers(EGLSurface surface) { return false; } -bool EglManager::enableDirtyRegions(EGLSurface surface) { - if (!mRequestDirtyRegions) return false; +void EglManager::cancelFrame() { + mInFrame = false; +} + +bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { + if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false; - if (mCanSetDirtyRegions) { - if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + bool preserved = false; + if (mCanSetPreserveBuffer) { + preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, + preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED); + if (CC_UNLIKELY(!preserved)) { ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", (void*) surface, egl_error_str()); - return false; } - return true; } - // Perhaps it is already enabled? - EGLint value; - if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { - ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", - (void*) surface, egl_error_str()); - return false; + if (CC_UNLIKELY(!preserved)) { + // Maybe it's already set? + EGLint swapBehavior; + if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) { + preserved = (swapBehavior == EGL_BUFFER_PRESERVED); + } else { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + } } - return value == EGL_BUFFER_PRESERVED; + + return preserved; } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 71213fb..e12db3a 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -48,8 +48,10 @@ public: bool makeCurrent(EGLSurface surface); void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); bool swapBuffers(EGLSurface surface); + void cancelFrame(); - bool enableDirtyRegions(EGLSurface surface); + // Returns true iff the surface is now preserving buffers. + bool setPreserveBuffer(EGLSurface surface, bool preserve); void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); @@ -71,14 +73,20 @@ private: EGLContext mEglContext; EGLSurface mPBufferSurface; - const bool mRequestDirtyRegions; - bool mCanSetDirtyRegions; + const bool mAllowPreserveBuffer; + bool mCanSetPreserveBuffer; EGLSurface mCurrentSurface; sp<GraphicBuffer> mAtlasBuffer; int64_t* mAtlasMap; size_t mAtlasMapSize; + + // Whether or not we are in the middle of drawing a frame. This is used + // to avoid switching surfaces mid-frame if requireGlContext() is called + // TODO: Need to be better about surface/context management so that this isn't + // necessary + bool mInFrame; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index c6ea82b..36ba3a9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -103,6 +103,18 @@ void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { post(task); } +CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) { + args->context->setSwapBehavior(args->swapBehavior); + return NULL; +} + +void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { + SETUP_TASK(setSwapBehavior); + args->context = mContext; + args->swapBehavior = swapBehavior; + post(task); +} + CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { bool needsRedraw = false; if (Caches::hasInstance()) { @@ -144,15 +156,14 @@ void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { } CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) { - args->context->pauseSurface(args->window); - return NULL; + return (void*) args->context->pauseSurface(args->window); } -void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { +bool RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { SETUP_TASK(pauseSurface); args->context = mContext; args->window = window.get(); - postAndWait(task); + return (bool) postAndWait(task); } CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height, @@ -223,12 +234,7 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { // waitForCompletion = true is expected to be fairly rare and only // happen in destruction. Thus it should be fine to temporarily // create a Mutex - Mutex mutex; - Condition condition; - SignalingRenderTask syncTask(task, &mutex, &condition); - AutoMutex _lock(mutex); - thread.queue(&syncTask); - condition.wait(mutex); + staticPostAndWait(task); } else { thread.queue(task); } @@ -246,17 +252,6 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) { postAndWait(task); } -CREATE_BRIDGE1(destroyLayer, Layer* layer) { - LayerRenderer::destroyLayer(args->layer); - return NULL; -} - -void RenderProxy::enqueueDestroyLayer(Layer* layer) { - SETUP_TASK(destroyLayer); - args->layer = layer; - RenderThread::getInstance().queue(task); -} - CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; @@ -344,14 +339,16 @@ void RenderProxy::trimMemory(int level) { } } +template <typename T> +void UNUSED(T) {} + + CREATE_BRIDGE0(fence) { // Intentionally empty + UNUSED(args); return NULL; } -template <typename T> -void UNUSED(T t) {} - void RenderProxy::fence() { SETUP_TASK(fence); UNUSED(args); @@ -392,6 +389,17 @@ void RenderProxy::dumpProfileInfo(int fd) { postAndWait(task); } +CREATE_BRIDGE1(outputLogBuffer, int fd) { + RenderNode::outputLogBuffer(args->fd); + return NULL; +} + +void RenderProxy::outputLogBuffer(int fd) { + SETUP_TASK(outputLogBuffer); + args->fd = fd; + staticPostAndWait(task); +} + CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) { CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); args->buffer->decStrong(0); @@ -422,6 +430,19 @@ void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) { return retval; } +void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) { + RenderThread& thread = RenderThread::getInstance(); + void* retval; + task->setReturnPtr(&retval); + Mutex mutex; + Condition condition; + SignalingRenderTask syncTask(task, &mutex, &condition); + AutoMutex _lock(mutex); + thread.queue(&syncTask); + condition.wait(mutex); + return retval; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 678e7e2..fd1fe05 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -31,6 +31,7 @@ #include "../Caches.h" #include "../IContextFactory.h" +#include "CanvasContext.h" #include "DrawFrameTask.h" namespace android { @@ -44,7 +45,6 @@ class Rect; namespace renderthread { -class CanvasContext; class ErrorChannel; class RenderThread; class RenderProxyBridge; @@ -63,11 +63,13 @@ public: ANDROID_API virtual ~RenderProxy(); ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos); + // Won't take effect until next EGLSurface creation + ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior); ANDROID_API bool loadSystemProperties(); 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 bool pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setOpaque(bool opaque); @@ -79,7 +81,6 @@ public: ANDROID_API void runWithGlContext(RenderTask* task); - static void enqueueDestroyLayer(Layer* layer); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API void buildLayer(RenderNode* node); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -95,6 +96,7 @@ public: ANDROID_API void notifyFramePending(); ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API static void outputLogBuffer(int fd); ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); @@ -112,6 +114,8 @@ private: void post(RenderTask* task); void* postAndWait(MethodInvokeRenderTask* task); + static void* staticPostAndWait(MethodInvokeRenderTask* task); + // Friend class to help with bridging friend class RenderProxyBridge; }; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 403e164..3e4e965 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -40,7 +40,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 = 4; +static const nsecs_t DISPATCH_FRAME_CALLBACKS_DELAY = milliseconds_to_nanoseconds(4); TaskQueue::TaskQueue() : mHead(0), mTail(0) {} @@ -168,7 +168,7 @@ void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); - mRenderState = new RenderState(); + mRenderState = new RenderState(*this); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { @@ -209,16 +209,16 @@ static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { return latest; } -void RenderThread::drainDisplayEventQueue(bool skipCallbacks) { +void RenderThread::drainDisplayEventQueue() { ATRACE_CALL(); nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver); if (vsyncEvent > 0) { mVsyncRequested = false; - mTimeLord.vsyncReceived(vsyncEvent); - if (!skipCallbacks && !mFrameCallbackTaskPending) { + if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) { ATRACE_NAME("queue mFrameCallbackTask"); mFrameCallbackTaskPending = true; - queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY); + nsecs_t runAt = (vsyncEvent + DISPATCH_FRAME_CALLBACKS_DELAY); + queueAt(mFrameCallbackTask, runAt); } } } @@ -230,8 +230,13 @@ void RenderThread::dispatchFrameCallbacks() { std::set<IFrameCallback*> callbacks; mFrameCallbacks.swap(callbacks); - for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { - (*it)->doFrame(); + if (callbacks.size()) { + // Assume one of them will probably animate again so preemptively + // request the next vsync in case it occurs mid-frame + requestVsync(); + for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { + (*it)->doFrame(); + } } } @@ -273,7 +278,7 @@ bool RenderThread::threadLoop() { } if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { - drainDisplayEventQueue(true); + drainDisplayEventQueue(); mFrameCallbacks.insert( mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); mPendingRegistrationFrameCallbacks.clear(); @@ -299,9 +304,8 @@ void RenderThread::queueAtFront(RenderTask* task) { mLooper->wake(); } -void RenderThread::queueDelayed(RenderTask* task, int delayMs) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - task->mRunAt = now + milliseconds_to_nanoseconds(delayMs); +void RenderThread::queueAt(RenderTask* task, nsecs_t runAtNs) { + task->mRunAt = runAtNs; queue(task); } @@ -314,9 +318,11 @@ void RenderThread::postFrameCallback(IFrameCallback* callback) { mPendingRegistrationFrameCallbacks.insert(callback); } -void RenderThread::removeFrameCallback(IFrameCallback* callback) { - mFrameCallbacks.erase(callback); - mPendingRegistrationFrameCallbacks.erase(callback); +bool RenderThread::removeFrameCallback(IFrameCallback* callback) { + size_t erased; + erased = mFrameCallbacks.erase(callback); + erased |= mPendingRegistrationFrameCallbacks.erase(callback); + return erased; } void RenderThread::pushBackFrameCallback(IFrameCallback* callback) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c461f3a..99c2e15 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -75,12 +75,12 @@ public: // 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 queueAt(RenderTask* task, nsecs_t runAtNs); void remove(RenderTask* task); // Mimics android.view.Choreographer void postFrameCallback(IFrameCallback* callback); - void removeFrameCallback(IFrameCallback* callback); + bool 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); @@ -103,7 +103,7 @@ private: void initThreadLocals(); void initializeDisplayEventReceiver(); static int displayEventReceiverCallback(int fd, int events, void* data); - void drainDisplayEventQueue(bool skipCallbacks = false); + void drainDisplayEventQueue(); void dispatchFrameCallbacks(); void requestVsync(); diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index cf3d039..f187493 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -20,14 +20,16 @@ namespace uirenderer { namespace renderthread { TimeLord::TimeLord() - : mFrameIntervalNanos(0) + : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)) , mFrameTimeNanos(0) { } -void TimeLord::vsyncReceived(nsecs_t vsync) { +bool TimeLord::vsyncReceived(nsecs_t vsync) { if (vsync > mFrameTimeNanos) { mFrameTimeNanos = vsync; + return true; } + return false; } nsecs_t TimeLord::computeFrameTimeMs() { diff --git a/libs/hwui/renderthread/TimeLord.h b/libs/hwui/renderthread/TimeLord.h index 8b0372c..7c155d2 100644 --- a/libs/hwui/renderthread/TimeLord.h +++ b/libs/hwui/renderthread/TimeLord.h @@ -29,7 +29,8 @@ class RenderThread; class TimeLord { public: void setFrameInterval(nsecs_t intervalNanos) { mFrameIntervalNanos = intervalNanos; } - void vsyncReceived(nsecs_t vsync); + // returns true if the vsync is newer, false if it was rejected for staleness + bool vsyncReceived(nsecs_t vsync); nsecs_t computeFrameTimeMs(); private: |
