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