diff options
Diffstat (limited to 'libs/hwui/renderthread')
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 35 | ||||
-rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 17 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.cpp | 36 | ||||
-rw-r--r-- | libs/hwui/renderthread/DrawFrameTask.h | 3 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 4 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.cpp | 123 | ||||
-rw-r--r-- | libs/hwui/renderthread/RenderThread.h | 34 |
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 */ |