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