1 /*
2 * Copyright (C) 2011 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 #include "RenderThread.h"
17
18 #include "ChannelStream.h"
19 #include "RingStream.h"
20 #include "ErrorLog.h"
21 #include "FrameBuffer.h"
22 #include "ReadBuffer.h"
23 #include "RenderControl.h"
24 #include "RendererImpl.h"
25 #include "RenderChannelImpl.h"
26 #include "RenderThreadInfo.h"
27
28 #include "OpenGLESDispatch/EGLDispatch.h"
29 #include "OpenGLESDispatch/GLESv2Dispatch.h"
30 #include "OpenGLESDispatch/GLESv1Dispatch.h"
31 #include "apigen-codec-common/ChecksumCalculatorThreadInfo.h"
32
33 #include "base/System.h"
34 #include "base/Tracing.h"
35 #include "base/StreamSerializing.h"
36 #include "base/Lock.h"
37 #include "base/MessageChannel.h"
38
39 #define EMUGL_DEBUG_LEVEL 0
40 #include "host-common/crash_reporter.h"
41 #include "host-common/debug.h"
42
43 #ifndef _WIN32
44 #include <unistd.h>
45 #endif
46
47 #include <assert.h>
48 #include <string.h>
49
50 using android::base::AutoLock;
51 using android::base::MessageChannel;
52
53 namespace emugl {
54
55 struct RenderThread::SnapshotObjects {
56 RenderThreadInfo* threadInfo;
57 ChecksumCalculator* checksumCalc;
58 ChannelStream* channelStream;
59 RingStream* ringStream;
60 ReadBuffer* readBuffer;
61 };
62
getBenchmarkEnabledFromEnv()63 static bool getBenchmarkEnabledFromEnv() {
64 auto threadEnabled = android::base::getEnvironmentVariable("ANDROID_EMUGL_RENDERTHREAD_STATS");
65 if (threadEnabled == "1") return true;
66 return false;
67 }
68
69 // Start with a smaller buffer to not waste memory on a low-used render threads.
70 static constexpr int kStreamBufferSize = 128 * 1024;
71
72 // Requires this many threads on the system available to run unlimited.
73 static constexpr int kMinThreadsToRunUnlimited = 5;
74
75 // A thread run limiter that limits render threads to run one slice at a time.
76 static android::base::Lock sThreadRunLimiter;
77
RenderThread(RenderChannelImpl * channel,android::base::Stream * loadStream)78 RenderThread::RenderThread(RenderChannelImpl* channel,
79 android::base::Stream* loadStream)
80 : android::base::Thread(android::base::ThreadFlags::MaskSignals, 2 * 1024 * 1024),
81 mChannel(channel),
82 mRunInLimitedMode(android::base::getCpuCoreCount() < kMinThreadsToRunUnlimited)
83 {
84 if (loadStream) {
85 const bool success = loadStream->getByte();
86 if (success) {
87 mStream.emplace(0);
88 android::base::loadStream(loadStream, &*mStream);
89 mState = SnapshotState::StartLoading;
90 } else {
91 mFinished.store(true, std::memory_order_relaxed);
92 }
93 }
94 }
95
RenderThread(struct asg_context context,android::base::Stream * loadStream,android::emulation::asg::ConsumerCallbacks callbacks)96 RenderThread::RenderThread(
97 struct asg_context context,
98 android::base::Stream* loadStream,
99 android::emulation::asg::ConsumerCallbacks callbacks)
100 : android::base::Thread(android::base::ThreadFlags::MaskSignals, 2 * 1024 * 1024),
101 mRingStream(
102 new RingStream(context, callbacks, kStreamBufferSize)) {
103 if (loadStream) {
104 const bool success = loadStream->getByte();
105 if (success) {
106 mStream.emplace(0);
107 android::base::loadStream(loadStream, &*mStream);
108 mState = SnapshotState::StartLoading;
109 } else {
110 mFinished.store(true, std::memory_order_relaxed);
111 }
112 }
113 }
114
115
116 RenderThread::~RenderThread() = default;
117
pausePreSnapshot()118 void RenderThread::pausePreSnapshot() {
119 AutoLock lock(mLock);
120 assert(mState == SnapshotState::Empty);
121 mStream.emplace();
122 mState = SnapshotState::StartSaving;
123 if (mRingStream) {
124 mRingStream->pausePreSnapshot();
125 // mCondVar.broadcastAndUnlock(&lock);
126 }
127 if (mChannel) {
128 mChannel->pausePreSnapshot();
129 mCondVar.broadcastAndUnlock(&lock);
130 }
131 }
132
resume()133 void RenderThread::resume() {
134 AutoLock lock(mLock);
135 // This function can be called for a thread from pre-snapshot loading
136 // state; it doesn't need to do anything.
137 if (mState == SnapshotState::Empty) {
138 return;
139 }
140 if (mRingStream) mRingStream->resume();
141 waitForSnapshotCompletion(&lock);
142 mStream.clear();
143 mState = SnapshotState::Empty;
144 if (mChannel) mChannel->resume();
145 if (mRingStream) mRingStream->resume();
146 mCondVar.broadcastAndUnlock(&lock);
147 }
148
save(android::base::Stream * stream)149 void RenderThread::save(android::base::Stream* stream) {
150 bool success;
151 {
152 AutoLock lock(mLock);
153 assert(mState == SnapshotState::StartSaving ||
154 mState == SnapshotState::InProgress ||
155 mState == SnapshotState::Finished);
156 waitForSnapshotCompletion(&lock);
157 success = mState == SnapshotState::Finished;
158 }
159
160 if (success) {
161 assert(mStream);
162 stream->putByte(1);
163 android::base::saveStream(stream, *mStream);
164 } else {
165 stream->putByte(0);
166 }
167 }
168
waitForSnapshotCompletion(AutoLock * lock)169 void RenderThread::waitForSnapshotCompletion(AutoLock* lock) {
170 while (mState != SnapshotState::Finished &&
171 !mFinished.load(std::memory_order_relaxed)) {
172 mCondVar.wait(lock);
173 }
174 }
175
176 template <class OpImpl>
snapshotOperation(AutoLock * lock,OpImpl && implFunc)177 void RenderThread::snapshotOperation(AutoLock* lock, OpImpl&& implFunc) {
178 assert(isPausedForSnapshotLocked());
179 mState = SnapshotState::InProgress;
180 mCondVar.broadcastAndUnlock(lock);
181
182 implFunc();
183
184 lock->lock();
185
186 mState = SnapshotState::Finished;
187 mCondVar.broadcast();
188
189 // Only return after we're allowed to proceed.
190 while (isPausedForSnapshotLocked()) {
191 mCondVar.wait(lock);
192 }
193 }
194
loadImpl(AutoLock * lock,const SnapshotObjects & objects)195 void RenderThread::loadImpl(AutoLock* lock, const SnapshotObjects& objects) {
196 snapshotOperation(lock, [this, &objects] {
197 objects.readBuffer->onLoad(&*mStream);
198 if (objects.channelStream) objects.channelStream->load(&*mStream);
199 if (objects.ringStream) objects.ringStream->load(&*mStream);
200 objects.checksumCalc->load(&*mStream);
201 objects.threadInfo->onLoad(&*mStream);
202 });
203 }
204
saveImpl(AutoLock * lock,const SnapshotObjects & objects)205 void RenderThread::saveImpl(AutoLock* lock, const SnapshotObjects& objects) {
206 snapshotOperation(lock, [this, &objects] {
207 objects.readBuffer->onSave(&*mStream);
208 if (objects.channelStream) objects.channelStream->save(&*mStream);
209 if (objects.ringStream) objects.ringStream->save(&*mStream);
210 objects.checksumCalc->save(&*mStream);
211 objects.threadInfo->onSave(&*mStream);
212 });
213 }
214
isPausedForSnapshotLocked() const215 bool RenderThread::isPausedForSnapshotLocked() const {
216 return mState != SnapshotState::Empty;
217 }
218
doSnapshotOperation(const SnapshotObjects & objects,SnapshotState state)219 bool RenderThread::doSnapshotOperation(const SnapshotObjects& objects,
220 SnapshotState state) {
221 AutoLock lock(mLock);
222 if (mState == state) {
223 switch (state) {
224 case SnapshotState::StartLoading:
225 loadImpl(&lock, objects);
226 return true;
227 case SnapshotState::StartSaving:
228 saveImpl(&lock, objects);
229 return true;
230 default:
231 return false;
232 }
233 }
234 return false;
235 }
236
setFinished()237 void RenderThread::setFinished() {
238 // Make sure it never happens that we wait forever for the thread to
239 // save to snapshot while it was not even going to.
240 AutoLock lock(mLock);
241 mFinished.store(true, std::memory_order_relaxed);
242 if (mState != SnapshotState::Empty) {
243 mCondVar.broadcastAndUnlock(&lock);
244 }
245 }
246
main()247 intptr_t RenderThread::main() {
248 if (mFinished.load(std::memory_order_relaxed)) {
249 DBG("Error: fail loading a RenderThread @%p\n", this);
250 return 0;
251 }
252
253 RenderThreadInfo tInfo;
254 ChecksumCalculatorThreadInfo tChecksumInfo;
255 ChecksumCalculator& checksumCalc = tChecksumInfo.get();
256 bool needRestoreFromSnapshot = false;
257
258 //
259 // initialize decoders
260 //
261 tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, nullptr);
262 tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, nullptr);
263 initRenderControlContext(&tInfo.m_rcDec);
264
265 if (!mChannel && !mRingStream) {
266 DBG("Exited a loader RenderThread @%p\n", this);
267 mFinished.store(true, std::memory_order_relaxed);
268 return 0;
269 }
270
271 ChannelStream stream(mChannel, RenderChannel::Buffer::kSmallSize);
272 IOStream* ioStream =
273 mChannel ? (IOStream*)&stream : (IOStream*)mRingStream.get();
274
275 ReadBuffer readBuf(kStreamBufferSize);
276 if (mRingStream) {
277 readBuf.setNeededFreeTailSize(0);
278 }
279
280 const SnapshotObjects snapshotObjects = {
281 &tInfo, &checksumCalc, &stream, mRingStream.get(), &readBuf,
282 };
283
284 // Framebuffer initialization is asynchronous, so we need to make sure
285 // it's completely initialized before running any GL commands.
286 FrameBuffer::waitUntilInitialized();
287
288 // This is the only place where we try loading from snapshot.
289 // But the context bind / restoration will be delayed after receiving
290 // the first GL command.
291 if (doSnapshotOperation(snapshotObjects, SnapshotState::StartLoading)) {
292 DBG("Loaded RenderThread @%p from snapshot\n", this);
293 needRestoreFromSnapshot = true;
294 } else {
295 // Not loading from a snapshot: continue regular startup, read
296 // the |flags|.
297 uint32_t flags = 0;
298 while (ioStream->read(&flags, sizeof(flags)) != sizeof(flags)) {
299 // Stream read may fail because of a pending snapshot.
300 if (!doSnapshotOperation(snapshotObjects, SnapshotState::StartSaving)) {
301 setFinished();
302 DBG("Exited a RenderThread @%p early\n", this);
303 return 0;
304 }
305 }
306
307 // |flags| used to mean something, now they're not used.
308 (void)flags;
309 }
310
311 int stats_totalBytes = 0;
312 uint64_t stats_progressTimeUs = 0;
313 auto stats_t0 = android::base::getHighResTimeUs() / 1000;
314 bool benchmarkEnabled = getBenchmarkEnabledFromEnv();
315
316 //
317 // open dump file if RENDER_DUMP_DIR is defined
318 //
319 const char* dump_dir = getenv("RENDERER_DUMP_DIR");
320 FILE* dumpFP = nullptr;
321 if (dump_dir) {
322 // size_t bsize = strlen(dump_dir) + 32;
323 // char* fname = new char[bsize];
324 // snprintf(fname, bsize, "%s" PATH_SEP "stream_%p", dump_dir, this);
325 // dumpFP = android_fopen(fname, "wb");
326 // if (!dumpFP) {
327 // fprintf(stderr, "Warning: stream dump failed to open file %s\n",
328 // fname);
329 // }
330 // delete[] fname;
331 }
332
333 uint32_t* seqnoPtr = nullptr;
334
335 while (1) {
336 // Let's make sure we read enough data for at least some processing.
337 int packetSize;
338 if (readBuf.validData() >= 8) {
339 // We know that packet size is the second int32_t from the start.
340 packetSize = *(const int32_t*)(readBuf.buf() + 4);
341 if (!packetSize) {
342 // Emulator will get live-stuck here if packet size is read to be zero;
343 // crash right away so we can see these events.
344 // emugl::emugl_crash_reporter(
345 // "Guest should never send a size-0 GL packet\n");
346 }
347 } else {
348 // Read enough data to at least be able to get the packet size next
349 // time.
350 packetSize = 8;
351 }
352
353 int stat = 0;
354 if (packetSize > (int)readBuf.validData()) {
355 stat = readBuf.getData(ioStream, packetSize);
356 if (stat <= 0) {
357 if (doSnapshotOperation(snapshotObjects, SnapshotState::StartSaving)) {
358 continue;
359 } else {
360 D("Warning: render thread could not read data from stream");
361 break;
362 }
363 } else if (needRestoreFromSnapshot) {
364 // If we're using RingStream that might load before FrameBuffer
365 // restores the contexts from the handles, so check again here.
366
367 tInfo.postLoadRefreshCurrentContextSurfacePtrs();
368 // We just loaded from a snapshot, need to initialize / bind
369 // the contexts.
370 needRestoreFromSnapshot = false;
371 HandleType ctx = tInfo.currContext ? tInfo.currContext->getHndl()
372 : 0;
373 HandleType draw = tInfo.currDrawSurf ? tInfo.currDrawSurf->getHndl()
374 : 0;
375 HandleType read = tInfo.currReadSurf ? tInfo.currReadSurf->getHndl()
376 : 0;
377 FrameBuffer::getFB()->bindContext(ctx, draw, read);
378 }
379 }
380
381 DD("render thread read %d bytes, op %d, packet size %d",
382 (int)readBuf.validData(), *(int32_t*)readBuf.buf(),
383 *(int32_t*)(readBuf.buf() + 4));
384
385 //
386 // log received bandwidth statistics
387 //
388 if (benchmarkEnabled) {
389 stats_totalBytes += readBuf.validData();
390 auto dt = android::base::getHighResTimeUs() / 1000 - stats_t0;
391 if (dt > 1000) {
392 float dts = (float)dt / 1000.0f;
393 printf("Used Bandwidth %5.3f MB/s, time in progress %f ms total %f ms\n", ((float)stats_totalBytes / dts) / (1024.0f*1024.0f),
394 stats_progressTimeUs / 1000.0f,
395 (float)dt);
396 readBuf.printStats();
397 stats_t0 = android::base::getHighResTimeUs() / 1000;
398 stats_progressTimeUs = 0;
399 stats_totalBytes = 0;
400 }
401 }
402
403 //
404 // dump stream to file if needed
405 //
406 if (dumpFP) {
407 int skip = readBuf.validData() - stat;
408 fwrite(readBuf.buf() + skip, 1, readBuf.validData() - skip, dumpFP);
409 fflush(dumpFP);
410 }
411
412 bool progress;
413
414 do {
415
416 if (!seqnoPtr && tInfo.m_puid) {
417 seqnoPtr = FrameBuffer::getFB()->getProcessSequenceNumberPtr(tInfo.m_puid);
418 }
419
420 progress = false;
421 size_t last;
422
423 //
424 // try to process some of the command buffer using the
425 // Vulkan decoder
426 //
427 // Note: It's risky to limit Vulkan decoding to one thread,
428 // so we do it outside the limiter
429 {
430 last = tInfo.m_vkDec.decode(readBuf.buf(), readBuf.validData(),
431 ioStream, seqnoPtr);
432 if (last > 0) {
433 readBuf.consume(last);
434 progress = true;
435 }
436 }
437
438 if (mRunInLimitedMode) {
439 sThreadRunLimiter.lock();
440 }
441
442 // try to process some of the command buffer using the GLESv1
443 // decoder
444 //
445 // DRIVER WORKAROUND:
446 // On Linux with NVIDIA GPU's at least, we need to avoid performing
447 // GLES ops while someone else holds the FrameBuffer write lock.
448 //
449 // To be more specific, on Linux with NVIDIA Quadro K2200 v361.xx,
450 // we get a segfault in the NVIDIA driver when glTexSubImage2D
451 // is called at the same time as glXMake(Context)Current.
452 //
453 // To fix, this driver workaround avoids calling
454 // any sort of GLES call when we are creating/destroying EGL
455 // contexts.
456 {
457 FrameBuffer::getFB()->lockContextStructureRead();
458 }
459
460 {
461 last = tInfo.m_glDec.decode(
462 readBuf.buf(), readBuf.validData(), ioStream, &checksumCalc);
463 if (last > 0) {
464 progress = true;
465 readBuf.consume(last);
466 }
467 }
468
469 //
470 // try to process some of the command buffer using the GLESv2
471 // decoder
472 //
473 {
474 last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(),
475 ioStream, &checksumCalc);
476
477 if (last > 0) {
478 progress = true;
479 readBuf.consume(last);
480 }
481 }
482
483 FrameBuffer::getFB()->unlockContextStructureRead();
484 //
485 // try to process some of the command buffer using the
486 // renderControl decoder
487 //
488 {
489 last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(),
490 ioStream, &checksumCalc);
491 if (last > 0) {
492 readBuf.consume(last);
493 progress = true;
494 }
495 }
496
497 if (mRunInLimitedMode) {
498 sThreadRunLimiter.unlock();
499 }
500
501 } while (progress);
502 }
503
504 if (dumpFP) {
505 fclose(dumpFP);
506 }
507
508 // Don't check for snapshots here: if we're already exiting then snapshot
509 // should not contain this thread information at all.
510 if (!FrameBuffer::getFB()->isShuttingDown()) {
511 // Release references to the current thread's context/surfaces if any
512 FrameBuffer::getFB()->bindContext(0, 0, 0);
513 if (tInfo.currContext || tInfo.currDrawSurf || tInfo.currReadSurf) {
514 fprintf(stderr,
515 "ERROR: RenderThread exiting with current context/surfaces\n");
516 }
517
518 FrameBuffer::getFB()->drainWindowSurface();
519 FrameBuffer::getFB()->drainRenderContext();
520 }
521
522 setFinished();
523
524 DBG("Exited a RenderThread @%p\n", this);
525 return 0;
526 }
527
528 } // namespace emugl
529