1 /*
2 * Copyright (C) 2017 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 "ReadbackWorker.h"
17
18 #include <string.h> // for memcpy
19
20 #include "ColorBuffer.h" // for ColorBuffer
21 #include "DispatchTables.h" // for s_gles2
22 #include "FbConfig.h" // for FbConfig, FbConfigList
23 #include "FrameBuffer.h" // for FrameBuffer
24 #include "OpenGLESDispatch/EGLDispatch.h" // for EGLDispatch, s_egl
25 #include "OpenGLESDispatch/GLESv2Dispatch.h" // for GLESv2Dispatch
26 #include "host-common/misc.h" // for getGlesVersion
27
recordDisplay(uint32_t displayId,uint32_t w,uint32_t h)28 ReadbackWorker::recordDisplay::recordDisplay(uint32_t displayId, uint32_t w, uint32_t h)
29 : mBufferSize(4 * w * h /* RGBA8 (4 bpp) */),
30 mBuffers(4 /* mailbox */,
31 0), // Note, last index is used for duplicating buffer on flush
32 mDisplayId(displayId) {}
33
initGL()34 void ReadbackWorker::initGL() {
35 mFb = FrameBuffer::getFB();
36 mFb->createSharedTrivialContext(&mContext, &mSurf);
37 mFb->createSharedTrivialContext(&mFlushContext, &mFlushSurf);
38 s_egl.eglMakeCurrent(mFb->getDisplay(), mFlushSurf, mFlushSurf, mFlushContext);
39 }
40
~ReadbackWorker()41 ReadbackWorker::~ReadbackWorker() {
42 s_gles2.glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
43 s_gles2.glBindBuffer(GL_COPY_READ_BUFFER, 0);
44 for (auto& r : mRecordDisplays) {
45 s_gles2.glDeleteBuffers(r.second.mBuffers.size(), &r.second.mBuffers[0]);
46 }
47 s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
48 mFb->destroySharedTrivialContext(mContext, mSurf);
49 mFb->destroySharedTrivialContext(mFlushContext, mFlushSurf);
50 }
51
setRecordDisplay(uint32_t displayId,uint32_t w,uint32_t h,bool add)52 void ReadbackWorker::setRecordDisplay(uint32_t displayId, uint32_t w, uint32_t h, bool add) {
53 android::base::AutoLock lock(mLock);
54 if (add) {
55 mRecordDisplays.emplace(displayId, recordDisplay(displayId, w, h));
56 recordDisplay& r = mRecordDisplays[displayId];
57 s_gles2.glGenBuffers(r.mBuffers.size(), &r.mBuffers[0]);
58 for (auto buffer : r.mBuffers) {
59 s_gles2.glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer);
60 s_gles2.glBufferData(GL_PIXEL_PACK_BUFFER, r.mBufferSize,
61 0 /* init, with no data */, GL_STREAM_READ);
62 }
63 s_gles2.glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
64 } else {
65 recordDisplay& r = mRecordDisplays[displayId];
66 s_gles2.glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
67 s_gles2.glBindBuffer(GL_COPY_READ_BUFFER, 0);
68 s_gles2.glDeleteBuffers(r.mBuffers.size(), &r.mBuffers[0]);
69 mRecordDisplays.erase(displayId);
70 }
71 }
72
doNextReadback(uint32_t displayId,ColorBuffer * cb,void * fbImage,bool repaint,bool readbackBgra)73 void ReadbackWorker::doNextReadback(uint32_t displayId,
74 ColorBuffer* cb,
75 void* fbImage,
76 bool repaint,
77 bool readbackBgra) {
78 // if |repaint|, make sure that the current frame is immediately sent down
79 // the pipeline and made available to the consumer by priming async
80 // readback; doing 4 consecutive reads in a row, which should be enough to
81 // fill the 3 buffers in the triple buffering setup and on the 4th, trigger
82 // a post callback.
83 int numIter = repaint ? 4 : 1;
84
85 // Mailbox-style triple buffering setup:
86 // We want to avoid glReadPixels while in the middle of doing
87 // memcpy to the consumer, but also want to avoid latency while
88 // that is going on.
89 //
90 // There are 3 buffer ids, A, B, and C.
91 // If we are not in the middle of copying out a frame,
92 // set glReadPixels to write to buffer A and copy from buffer B in
93 // alternation, so the consumer always gets the latest frame
94 // +1 frame of lag in order to not cause blocking on
95 // glReadPixels / glMapBufferRange.
96 // If we are in the middle of copying out a frame, reset A and B to
97 // not be the buffer being copied out, and continue glReadPixels to
98 // buffer A and B as before.
99 //
100 // The resulting invariants are:
101 // - glReadPixels is called on a different buffer every time
102 // so we avoid introducing sync points there.
103 // - At no time are we mapping/copying a buffer and also doing
104 // glReadPixels on it (avoid simultaneous map + glReadPixels)
105 // - glReadPixels and then immediately map/copy the same buffer
106 // doesn't happen either (avoid sync point in glMapBufferRange)
107 for (int i = 0; i < numIter; i++) {
108 android::base::AutoLock lock(mLock);
109 recordDisplay& r = mRecordDisplays[displayId];
110 if (r.mIsCopying) {
111 switch (r.mMapCopyIndex) {
112 // To keep double buffering effect on
113 // glReadPixels, need to keep even/oddness of
114 // mReadPixelsIndexEven and mReadPixelsIndexOdd.
115 case 0:
116 r.mReadPixelsIndexEven = 2;
117 r.mReadPixelsIndexOdd = 1;
118 break;
119 case 1:
120 r.mReadPixelsIndexEven = 0;
121 r.mReadPixelsIndexOdd = 2;
122 break;
123 case 2:
124 r.mReadPixelsIndexEven = 0;
125 r.mReadPixelsIndexOdd = 1;
126 break;
127 }
128 } else {
129 r.mReadPixelsIndexEven = 0;
130 r.mReadPixelsIndexOdd = 1;
131 r.mMapCopyIndex = r.mPrevReadPixelsIndex;
132 }
133
134 // Double buffering on buffer A / B part
135 uint32_t readAt;
136 if (r.m_readbackCount % 2 == 0) {
137 readAt = r.mReadPixelsIndexEven;
138 } else {
139 readAt = r.mReadPixelsIndexOdd;
140 }
141 r.m_readbackCount++;
142 r.mPrevReadPixelsIndex = readAt;
143
144 cb->readbackAsync(r.mBuffers[readAt], readbackBgra);
145
146 // It's possible to post callback before any of the async readbacks
147 // have written any data yet, which results in a black frame. Safer
148 // option to avoid this glitch is to wait until all 3 potential
149 // buffers in our triple buffering setup have had chances to readback.
150 lock.unlock();
151 if (r.m_readbackCount > 3) {
152 mFb->doPostCallback(fbImage, r.mDisplayId);
153 }
154 }
155 }
156
flushPipeline(uint32_t displayId)157 void ReadbackWorker::flushPipeline(uint32_t displayId) {
158 android::base::AutoLock lock(mLock);
159 recordDisplay& r = mRecordDisplays[displayId];
160 if (r.mIsCopying) {
161 // No need to make the last frame available,
162 // we are currently being read.
163 return;
164 }
165
166 auto src = r.mBuffers[r.mPrevReadPixelsIndex];
167 auto dst = r.mBuffers.back();
168
169 // This is not called from a renderthread, so let's activate
170 // the context.
171 s_egl.eglMakeCurrent(mFb->getDisplay(), mFlushSurf, mFlushSurf, mFlushContext);
172
173 // We now copy the last frame into slot 4, where no other thread
174 // ever writes.
175 s_gles2.glBindBuffer(GL_COPY_READ_BUFFER, src);
176 s_gles2.glBindBuffer(GL_COPY_WRITE_BUFFER, dst);
177 s_gles2.glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0,
178 r.mBufferSize);
179 s_egl.eglMakeCurrent(mFb->getDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE,
180 EGL_NO_CONTEXT);
181
182 r.mMapCopyIndex = r.mBuffers.size() - 1;
183 lock.unlock();
184 mFb->doPostCallback(nullptr, r.mDisplayId);
185 }
186
getPixels(uint32_t displayId,void * buf,uint32_t bytes)187 void ReadbackWorker::getPixels(uint32_t displayId, void* buf, uint32_t bytes) {
188 android::base::AutoLock lock(mLock);
189 recordDisplay& r = mRecordDisplays[displayId];
190 r.mIsCopying = true;
191 lock.unlock();
192
193 GLuint buffer = r.mBuffers[r.mMapCopyIndex];
194 s_gles2.glBindBuffer(GL_COPY_READ_BUFFER, buffer);
195 void* pixels = s_gles2.glMapBufferRange(GL_COPY_READ_BUFFER, 0, bytes,
196 GL_MAP_READ_BIT);
197 memcpy(buf, pixels, bytes);
198 s_gles2.glUnmapBuffer(GL_COPY_READ_BUFFER);
199
200 lock.lock();
201 r.mIsCopying = false;
202 lock.unlock();
203 }
204