blob: 8304f74e80d0354fb64e3b42e591c34bcdbaa9c2 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "C2BqBuffer"
#include <utils/Log.h>
#include <ui/BufferQueueDefs.h>
#include <ui/GraphicBuffer.h>
#include <ui/Fence.h>
#include <types.h>
#include <hidl/HidlSupport.h>
#include <C2AllocatorGralloc.h>
#include <C2BqBufferPriv.h>
#include <C2BlockInternal.h>
#include <list>
#include <map>
#include <mutex>
using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
using ::android::C2AllocatorGralloc;
using ::android::C2AndroidMemoryUsage;
using ::android::Fence;
using ::android::GraphicBuffer;
using ::android::sp;
using ::android::status_t;
using ::android::wp;
using ::android::hardware::hidl_handle;
using ::android::hardware::Return;
using HBuffer = ::android::hardware::graphics::common::V1_2::HardwareBuffer;
using HStatus = ::android::hardware::graphics::bufferqueue::V2_0::Status;
using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h;
using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b;
using ::android::hardware::graphics::bufferqueue::V2_0::utils::HFenceWrapper;
using HGraphicBufferProducer = ::android::hardware::graphics::bufferqueue::V2_0
::IGraphicBufferProducer;
struct C2BufferQueueBlockPoolData : public _C2BlockPoolData {
bool held;
bool local;
uint32_t generation;
uint64_t bqId;
int32_t bqSlot;
bool transfer; // local transfer to remote
bool attach; // attach on remote
bool display; // display on remote;
std::weak_ptr<int> owner;
sp<HGraphicBufferProducer> igbp;
std::shared_ptr<C2BufferQueueBlockPool::Impl> localPool;
mutable std::mutex lock;
virtual type_t getType() const override {
return TYPE_BUFFERQUEUE;
}
// Create a remote BlockPoolData.
C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
const std::shared_ptr<int> &owner,
const sp<HGraphicBufferProducer>& producer);
// Create a local BlockPoolData.
C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool);
virtual ~C2BufferQueueBlockPoolData() override;
int migrate(const sp<HGraphicBufferProducer>& producer,
uint32_t toGeneration, uint64_t toBqId,
sp<GraphicBuffer> *buffers, uint32_t oldGeneration);
};
bool _C2BlockFactory::GetBufferQueueData(
const std::shared_ptr<const _C2BlockPoolData>& data,
uint32_t* generation, uint64_t* bqId, int32_t* bqSlot) {
if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERQUEUE) {
if (generation) {
const std::shared_ptr<const C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<const C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
*generation = poolData->generation;
if (bqId) {
*bqId = poolData->bqId;
}
if (bqSlot) {
*bqSlot = poolData->bqSlot;
}
}
return true;
}
return false;
}
bool _C2BlockFactory::HoldBlockFromBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data,
const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& igbp) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
if (!poolData->local) {
poolData->owner = owner;
poolData->igbp = igbp;
}
if (poolData->held) {
poolData->held = true;
return false;
}
poolData->held = true;
return true;
}
bool _C2BlockFactory::BeginTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
poolData->transfer = true;
return true;
}
bool _C2BlockFactory::EndTransferBlockToClient(
const std::shared_ptr<_C2BlockPoolData>& data,
bool transfer) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
poolData->transfer = false;
if (transfer) {
poolData->held = false;
}
return true;
}
bool _C2BlockFactory::BeginAttachBlockToBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
if (poolData->local || poolData->display ||
poolData->attach || !poolData->held) {
return false;
}
if (poolData->bqId == 0) {
return false;
}
poolData->attach = true;
return true;
}
// if display was tried during attach, buffer should be retired ASAP.
bool _C2BlockFactory::EndAttachBlockToBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data,
const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& igbp,
uint32_t generation,
uint64_t bqId,
int32_t bqSlot) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
if (poolData->local || !poolData->attach ) {
return false;
}
if (poolData->display) {
poolData->attach = false;
poolData->held = false;
return false;
}
poolData->attach = false;
poolData->held = true;
poolData->owner = owner;
poolData->igbp = igbp;
poolData->generation = generation;
poolData->bqId = bqId;
poolData->bqSlot = bqSlot;
return true;
}
bool _C2BlockFactory::DisplayBlockToBufferQueue(
const std::shared_ptr<_C2BlockPoolData>& data) {
const std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::static_pointer_cast<C2BufferQueueBlockPoolData>(data);
std::scoped_lock<std::mutex> lock(poolData->lock);
if (poolData->local || poolData->display || !poolData->held) {
return false;
}
if (poolData->bqId == 0) {
return false;
}
poolData->display = true;
if (poolData->attach) {
return false;
}
poolData->held = false;
return true;
}
std::shared_ptr<C2GraphicBlock> _C2BlockFactory::CreateGraphicBlock(
const C2Handle *handle) {
// TODO: get proper allocator? and mutex?
static std::unique_ptr<C2AllocatorGralloc> sAllocator = std::make_unique<C2AllocatorGralloc>(0);
std::shared_ptr<C2GraphicAllocation> alloc;
if (C2AllocatorGralloc::isValid(handle)) {
uint32_t width;
uint32_t height;
uint32_t format;
uint64_t usage;
uint32_t stride;
uint32_t generation;
uint64_t bqId;
uint32_t bqSlot;
android::_UnwrapNativeCodec2GrallocMetadata(
handle, &width, &height, &format, &usage, &stride, &generation, &bqId, &bqSlot);
c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc);
if (err == C2_OK) {
std::shared_ptr<C2GraphicBlock> block;
if (bqId || bqSlot) {
// BQBBP
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(generation,
bqId,
(int32_t)bqSlot,
nullptr,
nullptr);
block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
} else {
block = _C2BlockFactory::CreateGraphicBlock(alloc);
}
return block;
}
}
return nullptr;
}
namespace {
int64_t getTimestampNow() {
int64_t stamp;
struct timespec ts;
// TODO: CLOCK_MONOTONIC_COARSE?
clock_gettime(CLOCK_MONOTONIC, &ts);
stamp = ts.tv_nsec / 1000;
stamp += (ts.tv_sec * 1000000LL);
return stamp;
}
bool getGenerationNumber(const sp<HGraphicBufferProducer> &producer,
uint32_t *generation) {
status_t status{};
int slot{};
bool bufferNeedsReallocation{};
sp<Fence> fence = new Fence();
using Input = HGraphicBufferProducer::DequeueBufferInput;
using Output = HGraphicBufferProducer::DequeueBufferOutput;
Return<void> transResult = producer->dequeueBuffer(
Input{640, 480, HAL_PIXEL_FORMAT_YCBCR_420_888, 0},
[&status, &slot, &bufferNeedsReallocation, &fence]
(HStatus hStatus, int32_t hSlot, Output const& hOutput) {
slot = static_cast<int>(hSlot);
if (!h2b(hStatus, &status) || !h2b(hOutput.fence, &fence)) {
status = ::android::BAD_VALUE;
} else {
bufferNeedsReallocation =
hOutput.bufferNeedsReallocation;
}
});
if (!transResult.isOk() || status != android::OK) {
return false;
}
HFenceWrapper hFenceWrapper{};
if (!b2h(fence, &hFenceWrapper)) {
(void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
ALOGE("Invalid fence received from dequeueBuffer.");
return false;
}
sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
// N.B. This assumes requestBuffer# returns an existing allocation
// instead of a new allocation.
transResult = producer->requestBuffer(
slot,
[&status, &slotBuffer, &generation](
HStatus hStatus,
HBuffer const& hBuffer,
uint32_t generationNumber){
if (h2b(hStatus, &status) &&
h2b(hBuffer, &slotBuffer) &&
slotBuffer) {
*generation = generationNumber;
slotBuffer->setGenerationNumber(generationNumber);
} else {
status = android::BAD_VALUE;
}
});
if (!transResult.isOk()) {
return false;
} else if (status != android::NO_ERROR) {
(void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
return false;
}
(void)producer->detachBuffer(static_cast<int32_t>(slot)).isOk();
return true;
}
};
class C2BufferQueueBlockPool::Impl
: public std::enable_shared_from_this<C2BufferQueueBlockPool::Impl> {
private:
c2_status_t fetchFromIgbp_l(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
// We have an IGBP now.
C2AndroidMemoryUsage androidUsage = usage;
status_t status{};
int slot{};
bool bufferNeedsReallocation{};
sp<Fence> fence = new Fence();
ALOGV("tries to dequeue buffer");
{ // Call dequeueBuffer().
using Input = HGraphicBufferProducer::DequeueBufferInput;
using Output = HGraphicBufferProducer::DequeueBufferOutput;
Return<void> transResult = mProducer->dequeueBuffer(
Input{
width,
height,
format,
androidUsage.asGrallocUsage()},
[&status, &slot, &bufferNeedsReallocation,
&fence](HStatus hStatus,
int32_t hSlot,
Output const& hOutput) {
slot = static_cast<int>(hSlot);
if (!h2b(hStatus, &status) ||
!h2b(hOutput.fence, &fence)) {
status = ::android::BAD_VALUE;
} else {
bufferNeedsReallocation =
hOutput.bufferNeedsReallocation;
}
});
if (!transResult.isOk() || status != android::OK) {
if (transResult.isOk()) {
++mDqFailure;
if (status == android::INVALID_OPERATION ||
status == android::TIMED_OUT ||
status == android::WOULD_BLOCK) {
// Dequeue buffer is blocked temporarily. Retrying is
// required.
return C2_BLOCKING;
}
}
ALOGD("cannot dequeue buffer %d", status);
return C2_BAD_VALUE;
}
mDqFailure = 0;
mLastDqTs = getTimestampNow();
}
HFenceWrapper hFenceWrapper{};
if (!b2h(fence, &hFenceWrapper)) {
ALOGE("Invalid fence received from dequeueBuffer.");
return C2_BAD_VALUE;
}
ALOGV("dequeued a buffer successfully");
if (fence) {
static constexpr int kFenceWaitTimeMs = 10;
status_t status = fence->wait(kFenceWaitTimeMs);
if (status == -ETIME) {
// fence is not signalled yet.
(void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
return C2_BLOCKING;
}
if (status != android::NO_ERROR) {
ALOGD("buffer fence wait error %d", status);
(void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
return C2_BAD_VALUE;
} else if (mRenderCallback) {
nsecs_t signalTime = fence->getSignalTime();
if (signalTime >= 0 && signalTime < INT64_MAX) {
mRenderCallback(mProducerId, slot, signalTime);
} else {
ALOGV("got fence signal time of %lld", (long long)signalTime);
}
}
}
sp<GraphicBuffer> &slotBuffer = mBuffers[slot];
uint32_t outGeneration;
if (bufferNeedsReallocation || !slotBuffer) {
if (!slotBuffer) {
slotBuffer = new GraphicBuffer();
}
// N.B. This assumes requestBuffer# returns an existing allocation
// instead of a new allocation.
Return<void> transResult = mProducer->requestBuffer(
slot,
[&status, &slotBuffer, &outGeneration](
HStatus hStatus,
HBuffer const& hBuffer,
uint32_t generationNumber){
if (h2b(hStatus, &status) &&
h2b(hBuffer, &slotBuffer) &&
slotBuffer) {
slotBuffer->setGenerationNumber(generationNumber);
outGeneration = generationNumber;
} else {
status = android::BAD_VALUE;
}
});
if (!transResult.isOk()) {
slotBuffer.clear();
return C2_BAD_VALUE;
} else if (status != android::NO_ERROR) {
slotBuffer.clear();
(void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
return C2_BAD_VALUE;
}
if (mGeneration == 0) {
// getting generation # lazily due to dequeue failure.
mGeneration = outGeneration;
}
}
if (slotBuffer) {
ALOGV("buffer wraps %llu %d", (unsigned long long)mProducerId, slot);
C2Handle *c2Handle = android::WrapNativeCodec2GrallocHandle(
slotBuffer->handle,
slotBuffer->width,
slotBuffer->height,
slotBuffer->format,
slotBuffer->usage,
slotBuffer->stride,
slotBuffer->getGenerationNumber(),
mProducerId, slot);
if (c2Handle) {
std::shared_ptr<C2GraphicAllocation> alloc;
c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
if (err != C2_OK) {
return err;
}
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(
slotBuffer->getGenerationNumber(),
mProducerId, slot,
shared_from_this());
mPoolDatas[slot] = poolData;
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
return C2_OK;
}
// Block was not created. call requestBuffer# again next time.
slotBuffer.clear();
(void)mProducer->cancelBuffer(slot, hFenceWrapper.getHandle()).isOk();
}
return C2_BAD_VALUE;
}
public:
Impl(const std::shared_ptr<C2Allocator> &allocator)
: mInit(C2_OK), mProducerId(0), mGeneration(0),
mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0),
mAllocator(allocator) {
}
~Impl() {
bool noInit = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
if (!noInit && mProducer) {
Return<HStatus> transResult =
mProducer->detachBuffer(static_cast<int32_t>(i));
noInit = !transResult.isOk() ||
static_cast<HStatus>(transResult) == HStatus::NO_INIT;
}
mBuffers[i].clear();
}
}
c2_status_t fetchGraphicBlock(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
block->reset();
if (mInit != C2_OK) {
return mInit;
}
static int kMaxIgbpRetryDelayUs = 10000;
std::unique_lock<std::mutex> lock(mMutex);
if (mLastDqLogTs == 0) {
mLastDqLogTs = getTimestampNow();
} else {
int64_t now = getTimestampNow();
if (now >= mLastDqLogTs + 5000000) {
if (now >= mLastDqTs + 1000000 || mDqFailure > 5) {
ALOGW("last successful dequeue was %lld us ago, "
"%zu consecutive failures",
(long long)(now - mLastDqTs), mDqFailure);
}
mLastDqLogTs = now;
}
}
if (mProducerId == 0) {
std::shared_ptr<C2GraphicAllocation> alloc;
c2_status_t err = mAllocator->newGraphicAllocation(
width, height, format, usage, &alloc);
if (err != C2_OK) {
return err;
}
std::shared_ptr<C2BufferQueueBlockPoolData> poolData =
std::make_shared<C2BufferQueueBlockPoolData>(
0, (uint64_t)0, ~0, shared_from_this());
*block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData);
ALOGV("allocated a buffer successfully");
return C2_OK;
}
c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block);
if (status == C2_BLOCKING) {
lock.unlock();
// in order not to drain cpu from component's spinning
::usleep(kMaxIgbpRetryDelayUs);
}
return status;
}
void setRenderCallback(const OnRenderCallback &renderCallback) {
std::scoped_lock<std::mutex> lock(mMutex);
mRenderCallback = renderCallback;
}
void configureProducer(const sp<HGraphicBufferProducer> &producer) {
uint64_t producerId = 0;
uint32_t generation = 0;
bool haveGeneration = false;
if (producer) {
Return<uint64_t> transResult = producer->getUniqueId();
if (!transResult.isOk()) {
ALOGD("configureProducer -- failed to connect to the producer");
return;
}
producerId = static_cast<uint64_t>(transResult);
// TODO: provide gneration number from parameter.
haveGeneration = getGenerationNumber(producer, &generation);
if (!haveGeneration) {
ALOGW("get generationNumber failed %llu",
(unsigned long long)producerId);
}
}
int migrated = 0;
{
sp<GraphicBuffer> buffers[NUM_BUFFER_SLOTS];
std::weak_ptr<C2BufferQueueBlockPoolData>
poolDatas[NUM_BUFFER_SLOTS];
std::scoped_lock<std::mutex> lock(mMutex);
bool noInit = false;
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
if (!noInit && mProducer) {
Return<HStatus> transResult =
mProducer->detachBuffer(static_cast<int32_t>(i));
noInit = !transResult.isOk() ||
static_cast<HStatus>(transResult) == HStatus::NO_INIT;
}
}
int32_t oldGeneration = mGeneration;
if (producer) {
mProducer = producer;
mProducerId = producerId;
mGeneration = haveGeneration ? generation : 0;
} else {
mProducer = nullptr;
mProducerId = 0;
mGeneration = 0;
ALOGW("invalid producer producer(%d), generation(%d)",
(bool)producer, haveGeneration);
}
if (mProducer && haveGeneration) { // migrate buffers
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
std::shared_ptr<C2BufferQueueBlockPoolData> data =
mPoolDatas[i].lock();
if (data) {
int slot = data->migrate(
mProducer, generation,
producerId, mBuffers, oldGeneration);
if (slot >= 0) {
buffers[slot] = mBuffers[i];
poolDatas[slot] = data;
++migrated;
}
}
}
}
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
mBuffers[i] = buffers[i];
mPoolDatas[i] = poolDatas[i];
}
}
if (producer && haveGeneration) {
ALOGD("local generation change %u , "
"bqId: %llu migrated buffers # %d",
generation, (unsigned long long)producerId, migrated);
}
}
private:
friend struct C2BufferQueueBlockPoolData;
void cancel(uint32_t generation, uint64_t igbp_id, int32_t igbp_slot) {
bool cancelled = false;
{
std::scoped_lock<std::mutex> lock(mMutex);
if (generation == mGeneration && igbp_id == mProducerId && mProducer) {
(void)mProducer->cancelBuffer(igbp_slot, hidl_handle{}).isOk();
cancelled = true;
}
}
}
c2_status_t mInit;
uint64_t mProducerId;
uint32_t mGeneration;
OnRenderCallback mRenderCallback;
size_t mDqFailure;
int64_t mLastDqTs;
int64_t mLastDqLogTs;
const std::shared_ptr<C2Allocator> mAllocator;
std::mutex mMutex;
sp<HGraphicBufferProducer> mProducer;
sp<HGraphicBufferProducer> mSavedProducer;
sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
};
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
const std::shared_ptr<int>& owner,
const sp<HGraphicBufferProducer>& producer) :
held(producer && bqId != 0), local(false),
generation(generation), bqId(bqId), bqSlot(bqSlot),
transfer(false), attach(false), display(false),
owner(owner), igbp(producer),
localPool() {
}
C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData(
uint32_t generation, uint64_t bqId, int32_t bqSlot,
const std::shared_ptr<C2BufferQueueBlockPool::Impl>& pool) :
held(true), local(true),
generation(generation), bqId(bqId), bqSlot(bqSlot),
transfer(false), attach(false), display(false),
igbp(pool ? pool->mProducer : nullptr),
localPool(pool) {
}
C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() {
if (!held || bqId == 0) {
return;
}
if (local) {
if (localPool) {
localPool->cancel(generation, bqId, bqSlot);
}
} else if (igbp && !owner.expired()) {
igbp->cancelBuffer(bqSlot, hidl_handle{}).isOk();
}
}
int C2BufferQueueBlockPoolData::migrate(
const sp<HGraphicBufferProducer>& producer,
uint32_t toGeneration, uint64_t toBqId,
sp<GraphicBuffer> *buffers, uint32_t oldGeneration) {
std::scoped_lock<std::mutex> l(lock);
if (!held || bqId == 0) {
ALOGV("buffer is not owned");
return -1;
}
if (!local || !localPool) {
ALOGV("pool is not local");
return -1;
}
if (bqSlot < 0 || bqSlot >= NUM_BUFFER_SLOTS || !buffers[bqSlot]) {
ALOGV("slot is not in effect");
return -1;
}
if (toGeneration == generation && bqId == toBqId) {
ALOGV("cannot migrate to same bufferqueue");
return -1;
}
if (oldGeneration != generation) {
ALOGV("cannot migrate stale buffer");
}
if (transfer) {
// either transferred or detached.
ALOGV("buffer is in transfer");
return -1;
}
sp<GraphicBuffer> const& graphicBuffer = buffers[bqSlot];
graphicBuffer->setGenerationNumber(toGeneration);
HBuffer hBuffer{};
uint32_t hGenerationNumber{};
if (!b2h(graphicBuffer, &hBuffer, &hGenerationNumber)) {
ALOGD("I to O conversion failed");
return -1;
}
bool converted{};
status_t bStatus{};
int slot;
int *outSlot = &slot;
Return<void> transResult =
producer->attachBuffer(hBuffer, hGenerationNumber,
[&converted, &bStatus, outSlot](
HStatus hStatus, int32_t hSlot, bool releaseAll) {
converted = h2b(hStatus, &bStatus);
*outSlot = static_cast<int>(hSlot);
if (converted && releaseAll && bStatus == android::OK) {
bStatus = android::INVALID_OPERATION;
}
});
if (!transResult.isOk() || !converted || bStatus != android::OK) {
ALOGD("attach failed %d", static_cast<int>(bStatus));
return -1;
}
ALOGV("local migration from gen %u : %u slot %d : %d",
generation, toGeneration, bqSlot, slot);
generation = toGeneration;
bqId = toBqId;
bqSlot = slot;
return slot;
}
C2BufferQueueBlockPool::C2BufferQueueBlockPool(
const std::shared_ptr<C2Allocator> &allocator, const local_id_t localId)
: mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {}
C2BufferQueueBlockPool::~C2BufferQueueBlockPool() {}
c2_status_t C2BufferQueueBlockPool::fetchGraphicBlock(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block /* nonnull */) {
if (mImpl) {
return mImpl->fetchGraphicBlock(width, height, format, usage, block);
}
return C2_CORRUPTED;
}
void C2BufferQueueBlockPool::configureProducer(const sp<HGraphicBufferProducer> &producer) {
if (mImpl) {
mImpl->configureProducer(producer);
}
}
void C2BufferQueueBlockPool::setRenderCallback(const OnRenderCallback &renderCallback) {
if (mImpl) {
mImpl->setRenderCallback(renderCallback);
}
}