RingBufferConsumer.cpp revision deeef54487a34034dc0cfaab20b20d557224c07c
1/*
2 * Copyright (C) 2013 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
17//#define LOG_NDEBUG 0
18#define LOG_TAG "RingBufferConsumer"
19#define ATRACE_TAG ATRACE_TAG_GRAPHICS
20#include <utils/Log.h>
21
22#include <gui/RingBufferConsumer.h>
23
24#define BI_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
25#define BI_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
26#define BI_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
27#define BI_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
28#define BI_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
29
30#undef assert
31#define assert(x) ALOG_ASSERT((x), #x)
32
33typedef android::RingBufferConsumer::PinnedBufferItem PinnedBufferItem;
34
35namespace android {
36
37RingBufferConsumer::RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer,
38        uint32_t consumerUsage,
39        int bufferCount) :
40    ConsumerBase(consumer),
41    mBufferCount(bufferCount)
42{
43    mConsumer->setConsumerUsageBits(consumerUsage);
44    mConsumer->setMaxAcquiredBufferCount(bufferCount);
45
46    assert(bufferCount > 0);
47}
48
49RingBufferConsumer::~RingBufferConsumer() {
50}
51
52void RingBufferConsumer::setName(const String8& name) {
53    Mutex::Autolock _l(mMutex);
54    mName = name;
55    mConsumer->setConsumerName(name);
56}
57
58sp<PinnedBufferItem> RingBufferConsumer::pinSelectedBuffer(
59        const RingBufferComparator& filter,
60        bool waitForFence) {
61
62    sp<PinnedBufferItem> pinnedBuffer;
63
64    {
65        List<RingBufferItem>::iterator it, end, accIt;
66        BufferInfo acc, cur;
67        BufferInfo* accPtr = NULL;
68
69        Mutex::Autolock _l(mMutex);
70
71        for (it = mBufferItemList.begin(), end = mBufferItemList.end();
72             it != end;
73             ++it) {
74
75            const RingBufferItem& item = *it;
76
77            cur.mCrop = item.mCrop;
78            cur.mTransform = item.mTransform;
79            cur.mScalingMode = item.mScalingMode;
80            cur.mTimestamp = item.mTimestamp;
81            cur.mFrameNumber = item.mFrameNumber;
82            cur.mPinned = item.mPinCount > 0;
83
84            int ret = filter.compare(accPtr, &cur);
85
86            if (ret == 0) {
87                accPtr = NULL;
88            } else if (ret > 0) {
89                acc = cur;
90                accPtr = &acc;
91                accIt = it;
92            } // else acc = acc
93        }
94
95        if (!accPtr) {
96            return NULL;
97        }
98
99        pinnedBuffer = new PinnedBufferItem(this, *accIt);
100        pinBufferLocked(pinnedBuffer->getBufferItem());
101
102    } // end scope of mMutex autolock
103
104    if (waitForFence) {
105        status_t err = pinnedBuffer->getBufferItem().mFence->waitForever(
106                "RingBufferConsumer::pinSelectedBuffer");
107        if (err != OK) {
108            BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)",
109                    strerror(-err), err);
110        }
111    }
112
113    return pinnedBuffer;
114}
115
116status_t RingBufferConsumer::clear() {
117
118    status_t err;
119    Mutex::Autolock _l(mMutex);
120
121    BI_LOGV("%s", __FUNCTION__);
122
123    // Avoid annoying log warnings by returning early
124    if (mBufferItemList.size() == 0) {
125        return OK;
126    }
127
128    do {
129        size_t pinnedFrames = 0;
130        err = releaseOldestBufferLocked(&pinnedFrames);
131
132        if (err == NO_BUFFER_AVAILABLE) {
133            assert(pinnedFrames == mBufferItemList.size());
134            break;
135        }
136
137        if (err == NOT_ENOUGH_DATA) {
138            // Fine. Empty buffer item list.
139            break;
140        }
141
142        if (err != OK) {
143            BI_LOGE("Clear failed, could not release buffer");
144            return err;
145        }
146
147    } while(true);
148
149    return OK;
150}
151
152void RingBufferConsumer::pinBufferLocked(const BufferItem& item) {
153    List<RingBufferItem>::iterator it, end;
154
155    for (it = mBufferItemList.begin(), end = mBufferItemList.end();
156         it != end;
157         ++it) {
158
159        RingBufferItem& find = *it;
160        if (item.mGraphicBuffer == find.mGraphicBuffer) {
161            find.mPinCount++;
162            break;
163        }
164    }
165
166    if (it == end) {
167        BI_LOGE("Failed to pin buffer (timestamp %lld, framenumber %lld)",
168                 item.mTimestamp, item.mFrameNumber);
169    } else {
170        BI_LOGV("Pinned buffer (frame %lld, timestamp %lld)",
171                item.mFrameNumber, item.mTimestamp);
172    }
173}
174
175status_t RingBufferConsumer::releaseOldestBufferLocked(size_t* pinnedFrames) {
176    status_t err = OK;
177
178    List<RingBufferItem>::iterator it, end, accIt;
179
180    it = mBufferItemList.begin();
181    end = mBufferItemList.end();
182    accIt = end;
183
184    if (it == end) {
185        /**
186         * This is fine. We really care about being able to acquire a buffer
187         * successfully after this function completes, not about it releasing
188         * some buffer.
189         */
190        BI_LOGV("%s: No buffers yet acquired, can't release anything",
191              __FUNCTION__);
192        return NOT_ENOUGH_DATA;
193    }
194
195    for (; it != end; ++it) {
196        RingBufferItem& find = *it;
197
198        if (find.mPinCount > 0) {
199            if (pinnedFrames != NULL) {
200                ++(*pinnedFrames);
201            }
202            // Filter out pinned frame when searching for buffer to release
203            continue;
204        }
205
206        if (find.mTimestamp < accIt->mTimestamp || accIt == end) {
207            accIt = it;
208        }
209    }
210
211    if (accIt != end) {
212        RingBufferItem& item = *accIt;
213
214        // In case the object was never pinned, pass the acquire fence
215        // back to the release fence. If the fence was already waited on,
216        // it'll just be a no-op to wait on it again.
217
218        // item.mGraphicBuffer was populated with the proper graphic-buffer
219        // at acquire even if it was previously acquired
220        err = addReleaseFenceLocked(item.mBuf,
221                item.mGraphicBuffer, item.mFence);
222
223        if (err != OK) {
224            BI_LOGE("Failed to add release fence to buffer "
225                    "(timestamp %lld, framenumber %lld",
226                    item.mTimestamp, item.mFrameNumber);
227            return err;
228        }
229
230        BI_LOGV("Attempting to release buffer timestamp %lld, frame %lld",
231                item.mTimestamp, item.mFrameNumber);
232
233        // item.mGraphicBuffer was populated with the proper graphic-buffer
234        // at acquire even if it was previously acquired
235        err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer,
236                                  EGL_NO_DISPLAY,
237                                  EGL_NO_SYNC_KHR);
238        if (err != OK) {
239            BI_LOGE("Failed to release buffer: %s (%d)",
240                    strerror(-err), err);
241            return err;
242        }
243
244        BI_LOGV("Buffer timestamp %lld, frame %lld evicted",
245                item.mTimestamp, item.mFrameNumber);
246
247        size_t currentSize = mBufferItemList.size();
248        mBufferItemList.erase(accIt);
249        assert(mBufferItemList.size() == currentSize - 1);
250    } else {
251        BI_LOGW("All buffers pinned, could not find any to release");
252        return NO_BUFFER_AVAILABLE;
253
254    }
255
256    return OK;
257}
258
259void RingBufferConsumer::onFrameAvailable() {
260    status_t err;
261
262    {
263        Mutex::Autolock _l(mMutex);
264
265        /**
266         * Release oldest frame
267         */
268        if (mBufferItemList.size() >= (size_t)mBufferCount) {
269            err = releaseOldestBufferLocked(/*pinnedFrames*/NULL);
270            assert(err != NOT_ENOUGH_DATA);
271
272            // TODO: implement the case for NO_BUFFER_AVAILABLE
273            assert(err != NO_BUFFER_AVAILABLE);
274            if (err != OK) {
275                return;
276            }
277            // TODO: in unpinBuffer rerun this routine if we had buffers
278            // we could've locked but didn't because there was no space
279        }
280
281        RingBufferItem& item = *mBufferItemList.insert(mBufferItemList.end(),
282                                                       RingBufferItem());
283
284        /**
285         * Acquire new frame
286         */
287        err = acquireBufferLocked(&item, 0);
288        if (err != OK) {
289            if (err != NO_BUFFER_AVAILABLE) {
290                BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
291            }
292
293            mBufferItemList.erase(--mBufferItemList.end());
294            return;
295        }
296
297        BI_LOGV("New buffer acquired (timestamp %lld), "
298                "buffer items %u out of %d",
299                item.mTimestamp,
300                mBufferItemList.size(), mBufferCount);
301
302        item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer;
303    } // end of mMutex lock
304
305    ConsumerBase::onFrameAvailable();
306}
307
308void RingBufferConsumer::unpinBuffer(const BufferItem& item) {
309    Mutex::Autolock _l(mMutex);
310
311    List<RingBufferItem>::iterator it, end, accIt;
312
313    for (it = mBufferItemList.begin(), end = mBufferItemList.end();
314         it != end;
315         ++it) {
316
317        RingBufferItem& find = *it;
318        if (item.mGraphicBuffer == find.mGraphicBuffer) {
319            status_t res = addReleaseFenceLocked(item.mBuf,
320                    item.mGraphicBuffer, item.mFence);
321
322            if (res != OK) {
323                BI_LOGE("Failed to add release fence to buffer "
324                        "(timestamp %lld, framenumber %lld",
325                        item.mTimestamp, item.mFrameNumber);
326                return;
327            }
328
329            find.mPinCount--;
330            break;
331        }
332    }
333
334    if (it == end) {
335        // This should never happen. If it happens, we have a bug.
336        BI_LOGE("Failed to unpin buffer (timestamp %lld, framenumber %lld)",
337                 item.mTimestamp, item.mFrameNumber);
338    } else {
339        BI_LOGV("Unpinned buffer (timestamp %lld, framenumber %lld)",
340                 item.mTimestamp, item.mFrameNumber);
341    }
342}
343
344status_t RingBufferConsumer::setDefaultBufferSize(uint32_t w, uint32_t h) {
345    Mutex::Autolock _l(mMutex);
346    return mConsumer->setDefaultBufferSize(w, h);
347}
348
349status_t RingBufferConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
350    Mutex::Autolock _l(mMutex);
351    return mConsumer->setDefaultBufferFormat(defaultFormat);
352}
353
354status_t RingBufferConsumer::setConsumerUsage(uint32_t usage) {
355    Mutex::Autolock _l(mMutex);
356    return mConsumer->setConsumerUsageBits(usage);
357}
358
359} // namespace android
360