• 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 "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