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