summaryrefslogtreecommitdiffstats
path: root/libs/hwui/renderthread
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread')
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp35
-rw-r--r--libs/hwui/renderthread/CanvasContext.h17
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp36
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h3
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp10
-rw-r--r--libs/hwui/renderthread/RenderProxy.h4
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp123
-rw-r--r--libs/hwui/renderthread/RenderThread.h34
8 files changed, 213 insertions, 49 deletions
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5ce7ba6..63f4b95 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -308,18 +308,20 @@ bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
return value == EGL_BUFFER_PRESERVED;
}
-CanvasContext::CanvasContext(bool translucent)
+CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
: mRenderThread(RenderThread::getInstance())
, mEglSurface(EGL_NO_SURFACE)
, mDirtyRegionsEnabled(false)
, mOpaque(!translucent)
, mCanvas(0)
- , mHaveNewSurface(false) {
+ , mHaveNewSurface(false)
+ , mRootRenderNode(rootRenderNode) {
mGlobalContext = GlobalContext::get();
}
CanvasContext::~CanvasContext() {
destroyCanvasAndSurface();
+ mRenderThread.removeFrameCallback(this);
}
void CanvasContext::destroyCanvasAndSurface() {
@@ -403,7 +405,16 @@ void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* lay
}
}
-void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
+void CanvasContext::prepareTree(TreeInfo& info) {
+ mRootRenderNode->prepareTree(info);
+
+ if (info.hasAnimations && !info.hasFunctors) {
+ // TODO: Functors
+ mRenderThread.postFrameCallback(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!");
@@ -417,7 +428,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
}
status_t status;
- if (dirty) {
+ if (dirty && !dirty->isEmpty()) {
status = mCanvas->prepareDirty(dirty->left, dirty->top,
dirty->right, dirty->bottom, mOpaque);
} else {
@@ -425,7 +436,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
}
Rect outBounds;
- status |= mCanvas->drawDisplayList(displayList, outBounds);
+ status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
// TODO: Draw debug info
// TODO: Performance tracking
@@ -437,6 +448,20 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
}
}
+// Called by choreographer to do an RT-driven animation
+void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
+ ATRACE_CALL();
+
+ TreeInfo info;
+ info.evaluateAnimations = true;
+ info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
+ info.performStagingPush = false;
+ info.prepareTextures = false;
+
+ prepareTree(info);
+ draw(NULL);
+}
+
void CanvasContext::invokeFunctor(Functor* functor) {
ATRACE_CALL();
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a3fe591..0873ad4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
#include "../RenderNode.h"
#include "RenderTask.h"
+#include "RenderThread.h"
#define FUNCTOR_PROCESS_DELAY 4
@@ -39,15 +40,13 @@ class Layer;
namespace renderthread {
class GlobalContext;
-class CanvasContext;
-class RenderThread;
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
-class CanvasContext {
+class CanvasContext : public IFrameCallback {
public:
- CanvasContext(bool translucent);
- ~CanvasContext();
+ CanvasContext(bool translucent, RenderNode* rootRenderNode);
+ virtual ~CanvasContext();
bool initialize(EGLNativeWindowType window);
void updateSurface(EGLNativeWindowType window);
@@ -55,9 +54,13 @@ public:
void setup(int width, int height);
void makeCurrent();
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
- void drawDisplayList(RenderNode* displayList, Rect* dirty);
+ void prepareTree(TreeInfo& info);
+ void draw(Rect* dirty);
void destroyCanvasAndSurface();
+ // IFrameCallback, Chroreographer-driven frame callback entry point
+ virtual void doFrame(nsecs_t frameTimeNanos);
+
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
void invokeFunctor(Functor* functor);
@@ -82,6 +85,8 @@ private:
bool mOpaque;
OpenGLRenderer* mCanvas;
bool mHaveNewSurface;
+
+ const sp<RenderNode> mRootRenderNode;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index f542d43..ff4be71 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -30,7 +30,7 @@ namespace android {
namespace uirenderer {
namespace renderthread {
-DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) {
+DrawFrameTask::DrawFrameTask() : mContext(0) {
}
DrawFrameTask::~DrawFrameTask() {
@@ -55,25 +55,17 @@ void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) {
}
}
-void DrawFrameTask::setRenderNode(RenderNode* renderNode) {
- LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!");
-
- mRenderNode = renderNode;
-}
-
void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
mDirty.set(left, top, right, bottom);
}
void DrawFrameTask::drawFrame(RenderThread* renderThread) {
- LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!");
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
postAndWait(renderThread);
// Reset the single-frame data
mDirty.setEmpty();
- mRenderNode = 0;
}
void DrawFrameTask::postAndWait(RenderThread* renderThread) {
@@ -88,8 +80,7 @@ void DrawFrameTask::run() {
bool canUnblockUiThread = syncFrameState();
// Grab a copy of everything we need
- Rect dirtyCopy(mDirty);
- sp<RenderNode> renderNode = mRenderNode;
+ Rect dirty(mDirty);
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
@@ -97,15 +88,20 @@ void DrawFrameTask::run() {
unblockUiThread();
}
- drawRenderNode(context, renderNode.get(), &dirtyCopy);
+ context->draw(&dirty);
if (!canUnblockUiThread) {
unblockUiThread();
}
}
-static void prepareTreeInfo(TreeInfo& info) {
+static void initTreeInfo(TreeInfo& info) {
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() {
@@ -113,9 +109,9 @@ bool DrawFrameTask::syncFrameState() {
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
TreeInfo info;
- prepareTreeInfo(info);
+ initTreeInfo(info);
mContext->processLayerUpdates(&mLayers, info);
- mRenderNode->prepareTree(info);
+ mContext->prepareTree(info);
// If prepareTextures is false, we ran out of texture cache space
return !info.hasFunctors && info.prepareTextures;
}
@@ -125,16 +121,6 @@ void DrawFrameTask::unblockUiThread() {
mSignal.signal();
}
-void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) {
- ATRACE_CALL();
-
- if (dirty->bottom == -1 && dirty->left == -1
- && dirty->top == -1 && dirty->right == -1) {
- dirty = 0;
- }
- context->drawDisplayList(renderNode, dirty);
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 055d4cf..c280868 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -53,7 +53,6 @@ public:
void addLayer(DeferredLayerUpdater* layer);
void removeLayer(DeferredLayerUpdater* layer);
- void setRenderNode(RenderNode* renderNode);
void setDirty(int left, int top, int right, int bottom);
void drawFrame(RenderThread* renderThread);
@@ -63,7 +62,6 @@ private:
void postAndWait(RenderThread* renderThread);
bool syncFrameState();
void unblockUiThread();
- static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty);
Mutex mLock;
Condition mSignal;
@@ -73,7 +71,6 @@ private:
/*********************************************
* Single frame data
*********************************************/
- sp<RenderNode> mRenderNode;
Rect mDirty;
/*********************************************
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ce490f1..87886e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -51,15 +51,16 @@ namespace renderthread {
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload()
-CREATE_BRIDGE1(createContext, bool translucent) {
- return new CanvasContext(args->translucent);
+CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
+ return new CanvasContext(args->translucent, args->rootRenderNode);
}
-RenderProxy::RenderProxy(bool translucent)
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
: mRenderThread(RenderThread::getInstance())
, mContext(0) {
SETUP_TASK(createContext);
args->translucent = translucent;
+ args->rootRenderNode = rootRenderNode;
mContext = (CanvasContext*) postAndWait(task);
mDrawFrameTask.setContext(mContext);
}
@@ -133,9 +134,8 @@ void RenderProxy::setup(int width, int height) {
post(task);
}
-void RenderProxy::drawDisplayList(RenderNode* displayList,
+void RenderProxy::syncAndDrawFrame(
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
- mDrawFrameTask.setRenderNode(displayList);
mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
mDrawFrameTask.drawFrame(&mRenderThread);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a112493..eab1395 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -56,14 +56,14 @@ class RenderProxyBridge;
*/
class ANDROID_API RenderProxy {
public:
- ANDROID_API RenderProxy(bool translucent);
+ ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
ANDROID_API virtual ~RenderProxy();
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 drawDisplayList(RenderNode* displayList,
+ ANDROID_API void syncAndDrawFrame(
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 212f475..e95707a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -18,9 +18,11 @@
#include "RenderThread.h"
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+
#include "CanvasContext.h"
#include "RenderProxy.h"
-#include <utils/Log.h>
namespace android {
using namespace uirenderer::renderthread;
@@ -29,6 +31,14 @@ ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
namespace uirenderer {
namespace renderthread {
+// Number of events to read at a time from the DisplayEventReceiver pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+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;
+
TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
RenderTask* TaskQueue::next() {
@@ -103,8 +113,25 @@ void TaskQueue::remove(RenderTask* task) {
}
}
+class DispatchFrameCallbacks : public RenderTask {
+private:
+ RenderThread* mRenderThread;
+public:
+ DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
+
+ virtual void run() {
+ mRenderThread->dispatchFrameCallbacks();
+ }
+};
+
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
- , mNextWakeup(LLONG_MAX) {
+ , mNextWakeup(LLONG_MAX)
+ , mDisplayEventReceiver(0)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mFrameCallbackTask(0)
+ , mFrameTime(0) {
+ mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
}
@@ -112,10 +139,86 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
RenderThread::~RenderThread() {
}
+void RenderThread::initializeDisplayEventReceiver() {
+ LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
+ mDisplayEventReceiver = new DisplayEventReceiver();
+ status_t status = mDisplayEventReceiver->initCheck();
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
+ "failed with status: %d", status);
+
+ // Register the FD
+ mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
+ Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
+}
+
+int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x", events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x", events);
+ return 1; // keep the callback
+ }
+
+ reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
+
+ return 1; // keep the callback
+}
+
+static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ nsecs_t latest = 0;
+ ssize_t n;
+ while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ latest = ev.header.timestamp;
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+ }
+ return latest;
+}
+
+void RenderThread::drainDisplayEventQueue() {
+ nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+ if (vsyncEvent > 0) {
+ mVsyncRequested = false;
+ mFrameTime = vsyncEvent;
+ if (!mFrameCallbackTaskPending) {
+ mFrameCallbackTaskPending = true;
+ //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
+ queue(mFrameCallbackTask);
+ }
+ }
+}
+
+void RenderThread::dispatchFrameCallbacks() {
+ mFrameCallbackTaskPending = false;
+
+ std::set<IFrameCallback*> callbacks;
+ mFrameCallbacks.swap(callbacks);
+
+ for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
+ (*it)->doFrame(mFrameTime);
+ }
+}
+
bool RenderThread::threadLoop() {
+ initializeDisplayEventReceiver();
+
int timeoutMillis = -1;
for (;;) {
- int result = mLooper->pollAll(timeoutMillis);
+ int result = mLooper->pollOnce(timeoutMillis);
LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
"RenderThread Looper POLL_ERROR!");
@@ -159,6 +262,20 @@ void RenderThread::remove(RenderTask* task) {
mQueue.remove(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);
+ }
+}
+
+void RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ mFrameCallbacks.erase(callback);
+}
+
RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
AutoMutex _lock(mLock);
RenderTask* next = mQueue.peek();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index e444aa0..b93dfd6 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -18,6 +18,10 @@
#define RENDERTHREAD_H_
#include "RenderTask.h"
+
+#include <memory>
+#include <set>
+
#include <cutils/compiler.h>
#include <utils/Looper.h>
#include <utils/Mutex.h>
@@ -25,9 +29,13 @@
#include <utils/Thread.h>
namespace android {
+class DisplayEventReceiver;
+
namespace uirenderer {
namespace renderthread {
+class DispatchFrameCallbacks;
+
class TaskQueue {
public:
TaskQueue();
@@ -42,6 +50,15 @@ private:
RenderTask* mTail;
};
+// Mimics android.view.Choreographer.FrameCallback
+class IFrameCallback {
+public:
+ virtual void doFrame(nsecs_t frameTimeNanos) = 0;
+
+protected:
+ ~IFrameCallback() {}
+};
+
class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
public:
// RenderThread takes complete ownership of tasks that are queued
@@ -50,15 +67,25 @@ public:
void queueDelayed(RenderTask* task, int delayMs);
void remove(RenderTask* task);
+ // Mimics android.view.Choreographer
+ void postFrameCallback(IFrameCallback* callback);
+ void removeFrameCallback(IFrameCallback* callback);
+
protected:
virtual bool threadLoop();
private:
friend class Singleton<RenderThread>;
+ friend class DispatchFrameCallbacks;
RenderThread();
virtual ~RenderThread();
+ void initializeDisplayEventReceiver();
+ static int displayEventReceiverCallback(int fd, int events, void* data);
+ void drainDisplayEventQueue();
+ void dispatchFrameCallbacks();
+
// 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
// set to this time
@@ -69,6 +96,13 @@ private:
nsecs_t mNextWakeup;
TaskQueue mQueue;
+
+ DisplayEventReceiver* mDisplayEventReceiver;
+ bool mVsyncRequested;
+ std::set<IFrameCallback*> mFrameCallbacks;
+ bool mFrameCallbackTaskPending;
+ DispatchFrameCallbacks* mFrameCallbackTask;
+ nsecs_t mFrameTime;
};
} /* namespace renderthread */