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