summaryrefslogtreecommitdiffstats
path: root/libs/hwui/renderthread
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread')
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp30
-rw-r--r--libs/hwui/renderthread/CanvasContext.h15
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp6
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h1
-rw-r--r--libs/hwui/renderthread/EglManager.cpp64
-rw-r--r--libs/hwui/renderthread/EglManager.h14
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp69
-rw-r--r--libs/hwui/renderthread/RenderProxy.h10
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp36
-rw-r--r--libs/hwui/renderthread/RenderThread.h6
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp6
-rw-r--r--libs/hwui/renderthread/TimeLord.h3
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: