1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "RenderThread.h" 18 19 #if defined(HAVE_PTHREADS) 20 #include <sys/resource.h> 21 #endif 22 #include <gui/DisplayEventReceiver.h> 23 #include <utils/Log.h> 24 25 #include "../RenderState.h" 26 #include "CanvasContext.h" 27 #include "EglManager.h" 28 #include "RenderProxy.h" 29 30 namespace android { 31 using namespace uirenderer::renderthread; 32 ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); 33 34 namespace uirenderer { 35 namespace renderthread { 36 37 // Number of events to read at a time from the DisplayEventReceiver pipe. 38 // The value should be large enough that we can quickly drain the pipe 39 // using just a few large reads. 40 static const size_t EVENT_BUFFER_SIZE = 100; 41 42 // Slight delay to give the UI time to push us a new frame before we replay 43 static const nsecs_t DISPATCH_FRAME_CALLBACKS_DELAY = milliseconds_to_nanoseconds(4); 44 45 TaskQueue::TaskQueue() : mHead(0), mTail(0) {} 46 47 RenderTask* TaskQueue::next() { 48 RenderTask* ret = mHead; 49 if (ret) { 50 mHead = ret->mNext; 51 if (!mHead) { 52 mTail = 0; 53 } 54 ret->mNext = 0; 55 } 56 return ret; 57 } 58 59 RenderTask* TaskQueue::peek() { 60 return mHead; 61 } 62 63 void TaskQueue::queue(RenderTask* task) { 64 // Since the RenderTask itself forms the linked list it is not allowed 65 // to have the same task queued twice 66 LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!"); 67 if (mTail) { 68 // Fast path if we can just append 69 if (mTail->mRunAt <= task->mRunAt) { 70 mTail->mNext = task; 71 mTail = task; 72 } else { 73 // Need to find the proper insertion point 74 RenderTask* previous = 0; 75 RenderTask* next = mHead; 76 while (next && next->mRunAt <= task->mRunAt) { 77 previous = next; 78 next = next->mNext; 79 } 80 if (!previous) { 81 task->mNext = mHead; 82 mHead = task; 83 } else { 84 previous->mNext = task; 85 if (next) { 86 task->mNext = next; 87 } else { 88 mTail = task; 89 } 90 } 91 } 92 } else { 93 mTail = mHead = task; 94 } 95 } 96 97 void TaskQueue::queueAtFront(RenderTask* task) { 98 if (mTail) { 99 task->mNext = mHead; 100 mHead = task; 101 } else { 102 mTail = mHead = task; 103 } 104 } 105 106 void TaskQueue::remove(RenderTask* task) { 107 // TaskQueue is strict here to enforce that users are keeping track of 108 // their RenderTasks due to how their memory is managed 109 LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task, 110 "Cannot remove a task that isn't in the queue!"); 111 112 // If task is the head we can just call next() to pop it off 113 // Otherwise we need to scan through to find the task before it 114 if (peek() == task) { 115 next(); 116 } else { 117 RenderTask* previous = mHead; 118 while (previous->mNext != task) { 119 previous = previous->mNext; 120 } 121 previous->mNext = task->mNext; 122 if (mTail == task) { 123 mTail = previous; 124 } 125 } 126 } 127 128 class DispatchFrameCallbacks : public RenderTask { 129 private: 130 RenderThread* mRenderThread; 131 public: 132 DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {} 133 134 virtual void run() { 135 mRenderThread->dispatchFrameCallbacks(); 136 } 137 }; 138 139 RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() 140 , mNextWakeup(LLONG_MAX) 141 , mDisplayEventReceiver(0) 142 , mVsyncRequested(false) 143 , mFrameCallbackTaskPending(false) 144 , mFrameCallbackTask(0) 145 , mRenderState(NULL) 146 , mEglManager(NULL) { 147 mFrameCallbackTask = new DispatchFrameCallbacks(this); 148 mLooper = new Looper(false); 149 run("RenderThread"); 150 } 151 152 RenderThread::~RenderThread() { 153 LOG_ALWAYS_FATAL("Can't destroy the render thread"); 154 } 155 156 void RenderThread::initializeDisplayEventReceiver() { 157 LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?"); 158 mDisplayEventReceiver = new DisplayEventReceiver(); 159 status_t status = mDisplayEventReceiver->initCheck(); 160 LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver " 161 "failed with status: %d", status); 162 163 // Register the FD 164 mLooper->addFd(mDisplayEventReceiver->getFd(), 0, 165 Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); 166 } 167 168 void RenderThread::initThreadLocals() { 169 initializeDisplayEventReceiver(); 170 mEglManager = new EglManager(*this); 171 mRenderState = new RenderState(*this); 172 } 173 174 int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { 175 if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { 176 ALOGE("Display event receiver pipe was closed or an error occurred. " 177 "events=0x%x", events); 178 return 0; // remove the callback 179 } 180 181 if (!(events & Looper::EVENT_INPUT)) { 182 ALOGW("Received spurious callback for unhandled poll event. " 183 "events=0x%x", events); 184 return 1; // keep the callback 185 } 186 187 reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue(); 188 189 return 1; // keep the callback 190 } 191 192 static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) { 193 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 194 nsecs_t latest = 0; 195 ssize_t n; 196 while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 197 for (ssize_t i = 0; i < n; i++) { 198 const DisplayEventReceiver::Event& ev = buf[i]; 199 switch (ev.header.type) { 200 case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: 201 latest = ev.header.timestamp; 202 break; 203 } 204 } 205 } 206 if (n < 0) { 207 ALOGW("Failed to get events from display event receiver, status=%d", status_t(n)); 208 } 209 return latest; 210 } 211 212 void RenderThread::drainDisplayEventQueue() { 213 ATRACE_CALL(); 214 nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver); 215 if (vsyncEvent > 0) { 216 mVsyncRequested = false; 217 if (mTimeLord.vsyncReceived(vsyncEvent) && !mFrameCallbackTaskPending) { 218 ATRACE_NAME("queue mFrameCallbackTask"); 219 mFrameCallbackTaskPending = true; 220 nsecs_t runAt = (vsyncEvent + DISPATCH_FRAME_CALLBACKS_DELAY); 221 queueAt(mFrameCallbackTask, runAt); 222 } 223 } 224 } 225 226 void RenderThread::dispatchFrameCallbacks() { 227 ATRACE_CALL(); 228 mFrameCallbackTaskPending = false; 229 230 std::set<IFrameCallback*> callbacks; 231 mFrameCallbacks.swap(callbacks); 232 233 if (callbacks.size()) { 234 // Assume one of them will probably animate again so preemptively 235 // request the next vsync in case it occurs mid-frame 236 requestVsync(); 237 for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) { 238 (*it)->doFrame(); 239 } 240 } 241 } 242 243 void RenderThread::requestVsync() { 244 if (!mVsyncRequested) { 245 mVsyncRequested = true; 246 status_t status = mDisplayEventReceiver->requestNextVsync(); 247 LOG_ALWAYS_FATAL_IF(status != NO_ERROR, 248 "requestNextVsync failed with status: %d", status); 249 } 250 } 251 252 bool RenderThread::threadLoop() { 253 #if defined(HAVE_PTHREADS) 254 setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY); 255 #endif 256 initThreadLocals(); 257 258 int timeoutMillis = -1; 259 for (;;) { 260 int result = mLooper->pollOnce(timeoutMillis); 261 LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR, 262 "RenderThread Looper POLL_ERROR!"); 263 264 nsecs_t nextWakeup; 265 // Process our queue, if we have anything 266 while (RenderTask* task = nextTask(&nextWakeup)) { 267 task->run(); 268 // task may have deleted itself, do not reference it again 269 } 270 if (nextWakeup == LLONG_MAX) { 271 timeoutMillis = -1; 272 } else { 273 nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC); 274 timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos); 275 if (timeoutMillis < 0) { 276 timeoutMillis = 0; 277 } 278 } 279 280 if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) { 281 drainDisplayEventQueue(); 282 mFrameCallbacks.insert( 283 mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end()); 284 mPendingRegistrationFrameCallbacks.clear(); 285 requestVsync(); 286 } 287 288 if (!mFrameCallbackTaskPending && !mVsyncRequested && mFrameCallbacks.size()) { 289 // TODO: Clean this up. This is working around an issue where a combination 290 // of bad timing and slow drawing can result in dropping a stale vsync 291 // on the floor (correct!) but fails to schedule to listen for the 292 // next vsync (oops), so none of the callbacks are run. 293 requestVsync(); 294 } 295 } 296 297 return false; 298 } 299 300 void RenderThread::queue(RenderTask* task) { 301 AutoMutex _lock(mLock); 302 mQueue.queue(task); 303 if (mNextWakeup && task->mRunAt < mNextWakeup) { 304 mNextWakeup = 0; 305 mLooper->wake(); 306 } 307 } 308 309 void RenderThread::queueAtFront(RenderTask* task) { 310 AutoMutex _lock(mLock); 311 mQueue.queueAtFront(task); 312 mLooper->wake(); 313 } 314 315 void RenderThread::queueAt(RenderTask* task, nsecs_t runAtNs) { 316 task->mRunAt = runAtNs; 317 queue(task); 318 } 319 320 void RenderThread::remove(RenderTask* task) { 321 AutoMutex _lock(mLock); 322 mQueue.remove(task); 323 } 324 325 void RenderThread::postFrameCallback(IFrameCallback* callback) { 326 mPendingRegistrationFrameCallbacks.insert(callback); 327 } 328 329 bool RenderThread::removeFrameCallback(IFrameCallback* callback) { 330 size_t erased; 331 erased = mFrameCallbacks.erase(callback); 332 erased |= mPendingRegistrationFrameCallbacks.erase(callback); 333 return erased; 334 } 335 336 void RenderThread::pushBackFrameCallback(IFrameCallback* callback) { 337 if (mFrameCallbacks.erase(callback)) { 338 mPendingRegistrationFrameCallbacks.insert(callback); 339 } 340 } 341 342 RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) { 343 AutoMutex _lock(mLock); 344 RenderTask* next = mQueue.peek(); 345 if (!next) { 346 mNextWakeup = LLONG_MAX; 347 } else { 348 mNextWakeup = next->mRunAt; 349 // Most tasks won't be delayed, so avoid unnecessary systemTime() calls 350 if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) { 351 next = mQueue.next(); 352 } else { 353 next = 0; 354 } 355 } 356 if (nextWakeup) { 357 *nextWakeup = mNextWakeup; 358 } 359 return next; 360 } 361 362 } /* namespace renderthread */ 363 } /* namespace uirenderer */ 364 } /* namespace android */ 365