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