1/*
2 * Copyright (C) 2018 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 "C2BqBuffer"
19#include <utils/Log.h>
20
21#include <gui/BufferQueueDefs.h>
22#include <list>
23#include <map>
24#include <mutex>
25
26#include <C2AllocatorGralloc.h>
27#include <C2BqBufferPriv.h>
28#include <C2BlockInternal.h>
29
30using ::android::AnwBuffer;
31using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
32using ::android::C2AllocatorGralloc;
33using ::android::C2AndroidMemoryUsage;
34using ::android::Fence;
35using ::android::GraphicBuffer;
36using ::android::HGraphicBufferProducer;
37using ::android::IGraphicBufferProducer;
38using ::android::hardware::graphics::common::V1_0::PixelFormat;
39using ::android::hidl_handle;
40using ::android::sp;
41using ::android::status_t;
42using ::android::wp;
43
44struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
45
46    bool held;
47    bool local;
48    uint64_t bqId;
49    int32_t bqSlot;
50    sp<HGraphicBufferProducer> igbp;
51    std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
52
53    virtual type_t getType() const override {
54        return TYPE_BUFFERQUEUE;
55    }
56
57    // Create a remote BlockPoolData.
58    C2BufferQueueBlockPoolData(
59            uint64_t bqId, int32_t bqSlot,
60            const sp<HGraphicBufferProducer>& producer = nullptr);
61
62    // Create a local BlockPoolData.
63    C2BufferQueueBlockPoolData(
64            uint64_t bqId, int32_t bqSlot,
65            const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool);
66
67    virtual ~C2BufferQueueBlockPoolData() override;
68
69};
70
71bool _C2BlockFactory::GetBufferQueueData(
72        const std::shared_ptr<_C2BlockPoolData>& data,
73        uint64_t* bqId, int32_t* bqSlot) {
74    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
75        if (bqId) {
76            const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
77                    std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
78            *bqId = poolData->bqId;
79            if (bqSlot) {
80                *bqSlot = poolData->bqSlot;
81            }
82        }
83        return true;
84    }
85    return false;
86}
87
88bool _C2BlockFactory::AssignBlockToBufferQueue(
89        const std::shared_ptr<_C2BlockPoolData>& data,
90        const sp<HGraphicBufferProducer>& igbp,
91        uint64_t bqId,
92        int32_t bqSlot,
93        bool held) {
94    if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
95        const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
96                std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
97        poolData->igbp = igbp;
98        poolData->bqId = bqId;
99        poolData->bqSlot = bqSlot;
100        poolData->held = held;
101        return true;
102    }
103    return false;
104}
105
106bool _C2BlockFactory::HoldBlockFromBufferQueue(
107        const std::shared_ptr<_C2BlockPoolData>& data,
108        const sp<HGraphicBufferProducer>& igbp) {
109    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
110            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
111    if (!poolData->local) {
112        poolData->igbp = igbp;
113    }
114    if (poolData->held) {
115        poolData->held = true;
116        return false;
117    }
118    poolData->held = true;
119    return true;
120}
121
122bool _C2BlockFactory::YieldBlockToBufferQueue(
123        const std::shared_ptr<_C2BlockPoolData>& data) {
124    const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
125            std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
126    if (!poolData->held) {
127        poolData->held = false;
128        return false;
129    }
130    poolData->held = false;
131    return true;
132}
133
134std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
135        const C2Handle *handle) {
136    // TODO: get proper allocator? and mutex?
137    static std::unique_ptr<C2AllocatorGralloc> sAllocator = std::make_unique<C2AllocatorGralloc>(0);
138
139    std::shared_ptr<C2GraphicAllocation> alloc;
140    if (C2AllocatorGralloc::isValid(handle)) {
141        uint32_t width;
142        uint32_t height;
143        uint32_t format;
144        uint64_t usage;
145        uint32_t stride;
146        uint64_t igbp_id;
147        uint32_t igbp_slot;
148        android::_UnwrapNativeCodec2GrallocMetadata(
149                handle, &width, &height, &format, &usage, &stride, &igbp_id, &igbp_slot);
150        c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc);
151        if (err == C2_OK) {
152            std::shared_ptr<C2GraphicBlock> block;
153            if (igbp_id || igbp_slot) {
154                // BQBBP
155                std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
156                        std::make_shared<C2BufferQueueBlockPoolData>(igbp_id, (int32_t)igbp_slot);
157                block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
158            } else {
159                block = _C2BlockFactory::CreateGraphicBlock(alloc);
160            }
161            return block;
162        }
163    }
164    return nullptr;
165}
166
167class C2BufferQueueBlockPool::Impl
168        : public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
169private:
170    c2_status_t fetchFromIgbp_l(
171            uint32_t width,
172            uint32_t height,
173            uint32_t format,
174            C2MemoryUsage usage,
175            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
176        // We have an IGBP now.
177        sp<Fence> fence = new Fence();
178        C2AndroidMemoryUsage androidUsage = usage;
179        status_t status;
180        PixelFormat pixelFormat = static_cast<PixelFormat>(format);
181        int slot;
182        ALOGV("tries to dequeue buffer");
183        mProducer->dequeueBuffer(
184                width, height, pixelFormat, androidUsage.asGrallocUsage(), true,
185                [&status, &slot, &fence](
186                        int32_t tStatus, int32_t tSlot, hidl_handle const& tFence,
187                        HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
188                    status = tStatus;
189                    slot = tSlot;
190                    if (!android::conversion::convertTo(fence.get(), tFence) &&
191                            status == android::NO_ERROR) {
192                        status = android::BAD_VALUE;
193                    }
194                    (void) tTs;
195                });
196        // dequeueBuffer returns flag.
197        if (status < android::OK) {
198            ALOGD("cannot dequeue buffer %d", status);
199            if (status == android::INVALID_OPERATION) {
200              // Too many buffer dequeued. retrying after some time is required.
201              return C2_TIMED_OUT;
202            } else {
203              return C2_BAD_VALUE;
204            }
205        }
206        ALOGV("dequeued a buffer successfully");
207        native_handle_t* nh = nullptr;
208        hidl_handle fenceHandle;
209        if (fence) {
210            android::conversion::wrapAs(&fenceHandle, &nh, *fence);
211        }
212        if (fence) {
213            static constexpr int kFenceWaitTimeMs = 10;
214
215            status_t status = fence->wait(kFenceWaitTimeMs);
216            if (status == -ETIME) {
217                // fence is not signalled yet.
218                mProducer->cancelBuffer(slot, fenceHandle);
219                return C2_TIMED_OUT;
220            }
221            if (status != android::NO_ERROR) {
222                ALOGD("buffer fence wait error %d", status);
223                mProducer->cancelBuffer(slot, fenceHandle);
224                return C2_BAD_VALUE;
225            } else if (mRenderCallback) {
226                nsecs_t signalTime = fence->getSignalTime();
227                if (signalTime >= 0 && signalTime < INT64_MAX) {
228                    mRenderCallback(mProducerId, slot, signalTime);
229                } else {
230                    ALOGV("got fence signal time of %lld", (long long)signalTime);
231                }
232            }
233        }
234
235        sp<GraphicBuffer> &slotBuffer = mBuffers[slot];
236        if (status & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || !slotBuffer) {
237            if (!slotBuffer) {
238                slotBuffer = new GraphicBuffer();
239            }
240            // N.B. This assumes requestBuffer# returns an existing allocation
241            // instead of a new allocation.
242            mProducer->requestBuffer(
243                    slot,
244                    [&status, &slotBuffer](int32_t tStatus, AnwBuffer const& tBuffer){
245                        status = tStatus;
246                        if (!android::conversion::convertTo(slotBuffer.get(), tBuffer) &&
247                                status == android::NO_ERROR) {
248                            status = android::BAD_VALUE;
249                        }
250                    });
251
252            if (status != android::NO_ERROR) {
253                mBuffers[slot].clear();
254                mProducer->cancelBuffer(slot, fenceHandle);
255                return C2_BAD_VALUE;
256            }
257        }
258        if (mBuffers[slot]) {
259            native_handle_t *grallocHandle = native_handle_clone(mBuffers[slot]->handle);
260
261            if (grallocHandle) {
262                ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
263                C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
264                        grallocHandle,
265                        mBuffers[slot]->width,
266                        mBuffers[slot]->height,
267                        mBuffers[slot]->format,
268                        mBuffers[slot]->usage,
269                        mBuffers[slot]->stride,
270                        mProducerId, slot);
271                if (c2Handle) {
272                    // Moved everything to c2Handle.
273                    native_handle_delete(grallocHandle);
274                    std::shared_ptr<C2GraphicAllocation> alloc;
275                    c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
276                    if (err != C2_OK) {
277                        return err;
278                    }
279                    std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
280                            std::make_shared<C2BufferQueueBlockPoolData>(
281                                    mProducerId, slot, shared_from_this());
282                    *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
283                    return C2_OK;
284                }
285                native_handle_close(grallocHandle);
286                native_handle_delete(grallocHandle);
287            }
288            // Block was not created. call requestBuffer# again next time.
289            mBuffers[slot].clear();
290            mProducer->cancelBuffer(slot, fenceHandle);
291        }
292        return C2_BAD_VALUE;
293    }
294
295public:
296    Impl(const std::shared_ptr<C2Allocator> &allocator)
297        : mInit(C2_OK), mProducerId(0), mAllocator(allocator) {
298    }
299
300    ~Impl() {
301        std::lock_guard<std::mutex> lock(mMutex);
302        bool noInit = false;
303        for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
304            if (!noInit && mProducer) {
305                noInit = mProducer->detachBuffer(i) == android::NO_INIT;
306            }
307            mBuffers[i].clear();
308        }
309    }
310
311    c2_status_t fetchGraphicBlock(
312            uint32_t width,
313            uint32_t height,
314            uint32_t format,
315            C2MemoryUsage usage,
316            std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
317        block->reset();
318        if (mInit != C2_OK) {
319            return mInit;
320        }
321
322        static int kMaxIgbpRetry = 20; // TODO: small number can cause crash in releasing.
323        static int kMaxIgbpRetryDelayUs = 10000;
324
325        int curTry = 0;
326
327        while (curTry++ < kMaxIgbpRetry) {
328            std::unique_lock<std::mutex> lock(mMutex);
329            // TODO: return C2_NO_INIT
330            if (mProducerId == 0) {
331                std::shared_ptr<C2GraphicAllocation> alloc;
332                c2_status_t err = mAllocator->newGraphicAllocation(
333                        width, height, format, usage, &alloc);
334                if (err != C2_OK) {
335                    return err;
336                }
337                std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
338                        std::make_shared<C2BufferQueueBlockPoolData>(
339                                (uint64_t)0, ~0, shared_from_this());
340                // TODO: config?
341                *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
342                ALOGV("allocated a buffer successfully");
343
344                return C2_OK;
345            }
346            c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block);
347            if (status == C2_TIMED_OUT) {
348                lock.unlock();
349                ::usleep(kMaxIgbpRetryDelayUs);
350                continue;
351            }
352            return status;
353        }
354        return C2_TIMED_OUT;
355    }
356
357    void setRenderCallback(const OnRenderCallback &renderCallback) {
358        std::lock_guard<std::mutex> lock(mMutex);
359        mRenderCallback = renderCallback;
360    }
361
362    void configureProducer(const sp<HGraphicBufferProducer> &producer) {
363        int32_t status = android::OK;
364        uint64_t producerId = 0;
365        if (producer) {
366            producer->getUniqueId(
367                    [&status, &producerId](int32_t tStatus, int64_t tProducerId) {
368                status = tStatus;
369                producerId = tProducerId;
370                    });
371        }
372        {
373            std::lock_guard<std::mutex> lock(mMutex);
374            if (status == android::OK && producerId == mProducerId) {
375                // producer is not changed.
376                return;
377            }
378            bool noInit = false;
379            for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
380                if (!noInit && mProducer) {
381                    noInit = mProducer->detachBuffer(i) == android::NO_INIT;
382                }
383                mBuffers[i].clear();
384            }
385            if (producer && status == android::OK) {
386                mProducer = producer;
387                mProducerId = producerId;
388            } else {
389                mProducer = nullptr;
390                mProducerId = 0;
391            }
392        }
393    }
394
395private:
396    friend struct C2BufferQueueBlockPoolData;
397
398    void cancel(uint64_t igbp_id, int32_t igbp_slot) {
399        std::lock_guard<std::mutex> lock(mMutex);
400        if (igbp_id == mProducerId && mProducer) {
401            mProducer->cancelBuffer(igbp_slot, nullptr);
402        }
403    }
404
405    c2_status_t mInit;
406    uint64_t mProducerId;
407    OnRenderCallback mRenderCallback;
408
409    const std::shared_ptr<C2Allocator> mAllocator;
410
411    std::mutex mMutex;
412    sp<HGraphicBufferProducer> mProducer;
413
414    sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
415};
416
417C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
418        uint64_t bqId, int32_t bqSlot,
419        const sp<HGraphicBufferProducer>& producer) :
420        held(producer && bqId != 0), local(false),
421        bqId(bqId), bqSlot(bqSlot),
422        igbp(producer),
423        localPool() {
424}
425
426C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
427        uint64_t bqId, int32_t bqSlot,
428        const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
429        held(true), local(true),
430        bqId(bqId), bqSlot(bqSlot),
431        igbp(pool ? pool->mProducer : nullptr),
432        localPool(pool) {
433}
434
435C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() {
436    if (!held || bqId == 0) {
437        return;
438    }
439    if (local && localPool) {
440        localPool->cancel(bqId, bqSlot);
441    } else if (igbp) {
442        igbp->cancelBuffer(bqSlot, nullptr);
443    }
444}
445
446C2BufferQueueBlockPool::C2BufferQueueBlockPool(
447        const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
448        : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
449
450C2BufferQueueBlockPool::~C2BufferQueueBlockPool() {}
451
452c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock(
453        uint32_t width,
454        uint32_t height,
455        uint32_t format,
456        C2MemoryUsage usage,
457        std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
458    if (mImpl) {
459        return mImpl->fetchGraphicBlock(width, height, format, usage, block);
460    }
461    return C2_CORRUPTED;
462}
463
464void C2BufferQueueBlockPool::configureProducer(const sp<HGraphicBufferProducer> &producer) {
465    if (mImpl) {
466        mImpl->configureProducer(producer);
467    }
468}
469
470void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) {
471    if (mImpl) {
472        mImpl->setRenderCallback(renderCallback);
473    }
474}
475