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