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