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