1176 lines
47 KiB
C++
1176 lines
47 KiB
C++
// Copyright 2020 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "C2VdaBqBlockPool"
|
|
|
|
#include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <chrono>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#include <C2AllocatorGralloc.h>
|
|
#include <C2BlockInternal.h>
|
|
#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h>
|
|
#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h>
|
|
#include <base/callback.h>
|
|
#include <log/log.h>
|
|
#include <system/window.h>
|
|
#include <types.h>
|
|
#include <ui/BufferQueueDefs.h>
|
|
|
|
#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
|
|
|
|
namespace android {
|
|
namespace {
|
|
|
|
// The wait time for acquire fence in milliseconds.
|
|
constexpr int kFenceWaitTimeMs = 10;
|
|
// The timeout limit of acquiring lock of timed_mutex in milliseconds.
|
|
constexpr std::chrono::milliseconds kTimedMutexTimeoutMs = std::chrono::milliseconds(500);
|
|
|
|
} // namespace
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
using ::android::C2AndroidMemoryUsage;
|
|
using ::android::Fence;
|
|
using ::android::GraphicBuffer;
|
|
using ::android::sp;
|
|
using ::android::status_t;
|
|
using ::android::BufferQueueDefs::BUFFER_NEEDS_REALLOCATION;
|
|
using ::android::BufferQueueDefs::NUM_BUFFER_SLOTS;
|
|
using ::android::BufferQueueDefs::RELEASE_ALL_BUFFERS;
|
|
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 HGraphicBufferProducer =
|
|
::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
|
|
using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0::IProducerListener;
|
|
using HConnectionType = hardware::graphics::bufferqueue::V2_0::ConnectionType;
|
|
using HQueueBufferOutput =
|
|
::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer::QueueBufferOutput;
|
|
|
|
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;
|
|
|
|
static c2_status_t asC2Error(int32_t err) {
|
|
switch (err) {
|
|
case android::NO_ERROR:
|
|
return C2_OK;
|
|
case android::NO_INIT:
|
|
return C2_NO_INIT;
|
|
case android::BAD_VALUE:
|
|
return C2_BAD_VALUE;
|
|
case android::TIMED_OUT:
|
|
return C2_TIMED_OUT;
|
|
case android::WOULD_BLOCK:
|
|
return C2_BLOCKING;
|
|
case android::NO_MEMORY:
|
|
return C2_NO_MEMORY;
|
|
}
|
|
return C2_CORRUPTED;
|
|
}
|
|
|
|
class H2BGraphicBufferProducer {
|
|
public:
|
|
explicit H2BGraphicBufferProducer(sp<HGraphicBufferProducer> base) : mBase(base) {}
|
|
~H2BGraphicBufferProducer() = default;
|
|
|
|
status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) {
|
|
bool converted = false;
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<void> transResult = mBase->requestBuffer(
|
|
slot, [&converted, &status, buf](HStatus hStatus, HBuffer const& hBuffer,
|
|
uint32_t generationNumber) {
|
|
converted = h2b(hStatus, &status) && h2b(hBuffer, buf);
|
|
if (*buf) {
|
|
(*buf)->setGenerationNumber(generationNumber);
|
|
}
|
|
});
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!converted) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) {
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<HStatus> transResult =
|
|
mBase->setMaxDequeuedBufferCount(static_cast<int32_t>(maxDequeuedBuffers));
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!h2b(static_cast<HStatus>(transResult), &status)) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t dequeueBuffer(uint32_t width, uint32_t height, uint32_t pixelFormat,
|
|
C2AndroidMemoryUsage androidUsage, int* slot, sp<Fence>* fence) {
|
|
using Input = HGraphicBufferProducer::DequeueBufferInput;
|
|
using Output = HGraphicBufferProducer::DequeueBufferOutput;
|
|
Input input{width, height, pixelFormat, androidUsage.asGrallocUsage()};
|
|
|
|
bool converted = false;
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<void> transResult = mBase->dequeueBuffer(
|
|
input, [&converted, &status, &slot, &fence](HStatus hStatus, int32_t hSlot,
|
|
Output const& hOutput) {
|
|
converted = h2b(hStatus, &status);
|
|
if (!converted || status != android::NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
*slot = hSlot;
|
|
if (hOutput.bufferNeedsReallocation) {
|
|
status = BUFFER_NEEDS_REALLOCATION;
|
|
}
|
|
converted = h2b(hOutput.fence, fence);
|
|
});
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!converted) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION &&
|
|
status != android::TIMED_OUT) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t detachBuffer(int slot) {
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<HStatus> transResult = mBase->detachBuffer(static_cast<int32_t>(slot));
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!h2b(static_cast<HStatus>(transResult), &status)) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t attachBuffer(const sp<GraphicBuffer>& buffer, int* outSlot) {
|
|
HBuffer hBuffer;
|
|
uint32_t hGenerationNumber;
|
|
if (!b2h(buffer, &hBuffer, &hGenerationNumber)) {
|
|
ALOGE("%s: invalid input buffer.", __func__);
|
|
return BAD_VALUE;
|
|
}
|
|
|
|
bool converted = false;
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<void> transResult = mBase->attachBuffer(
|
|
hBuffer, hGenerationNumber,
|
|
[&converted, &status, outSlot](HStatus hStatus, int32_t hSlot,
|
|
bool releaseAllBuffers) {
|
|
converted = h2b(hStatus, &status);
|
|
*outSlot = static_cast<int>(hSlot);
|
|
if (converted && releaseAllBuffers && status == android::NO_ERROR) {
|
|
status = RELEASE_ALL_BUFFERS;
|
|
}
|
|
});
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!converted) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t cancelBuffer(int slot, const sp<Fence>& fence) {
|
|
HFenceWrapper hFenceWrapper;
|
|
if (!b2h(fence, &hFenceWrapper)) {
|
|
ALOGE("%s(): corrupted input fence.", __func__);
|
|
return UNKNOWN_ERROR;
|
|
}
|
|
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<HStatus> transResult =
|
|
mBase->cancelBuffer(static_cast<int32_t>(slot), hFenceWrapper.getHandle());
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!h2b(static_cast<HStatus>(transResult), &status)) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int query(int what, int* value) {
|
|
int result = 0;
|
|
Return<void> transResult =
|
|
mBase->query(static_cast<int32_t>(what), [&result, value](int32_t r, int32_t v) {
|
|
result = static_cast<int>(r);
|
|
*value = static_cast<int>(v);
|
|
});
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
status_t allowAllocation(bool allow) {
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<HStatus> transResult = mBase->allowAllocation(allow);
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!h2b(static_cast<HStatus>(transResult), &status)) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (status != android::NO_ERROR) {
|
|
ALOGW("%s() failed: %d", __func__, status);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t getUniqueId(uint64_t* outId) const {
|
|
Return<uint64_t> transResult = mBase->getUniqueId();
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
|
|
*outId = static_cast<uint64_t>(transResult);
|
|
return android::NO_ERROR;
|
|
}
|
|
|
|
// android::IProducerListener cannot be depended by vendor library, so we use HProducerListener
|
|
// directly.
|
|
status_t connect(sp<HProducerListener> const& hListener, int32_t api,
|
|
bool producerControlledByApp) {
|
|
bool converted = false;
|
|
status_t status = UNKNOWN_ERROR;
|
|
// hack(b/146409777): We pass self-defined api, so we don't use b2h() here.
|
|
Return<void> transResult = mBase->connect(
|
|
hListener, static_cast<HConnectionType>(api), producerControlledByApp,
|
|
[&converted, &status](HStatus hStatus, HQueueBufferOutput const& /* hOutput */) {
|
|
converted = h2b(hStatus, &status);
|
|
});
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!converted) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
status_t setDequeueTimeout(nsecs_t timeout) {
|
|
status_t status = UNKNOWN_ERROR;
|
|
Return<HStatus> transResult = mBase->setDequeueTimeout(static_cast<int64_t>(timeout));
|
|
|
|
if (!transResult.isOk()) {
|
|
ALOGE("%s(): transaction failed: %s", __func__, transResult.description().c_str());
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
if (!h2b(static_cast<HStatus>(transResult), &status)) {
|
|
ALOGE("%s(): corrupted transaction.", __func__);
|
|
return FAILED_TRANSACTION;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
private:
|
|
const sp<HGraphicBufferProducer> mBase;
|
|
};
|
|
|
|
// This class is used to notify the listener when a certain event happens.
|
|
class EventNotifier : public virtual android::RefBase {
|
|
public:
|
|
class Listener {
|
|
public:
|
|
virtual ~Listener() = default;
|
|
|
|
// Called by EventNotifier when a certain event happens.
|
|
virtual void onEventNotified() = 0;
|
|
};
|
|
|
|
explicit EventNotifier(const std::shared_ptr<Listener>& listener) : mListener(listener) {}
|
|
virtual ~EventNotifier() = default;
|
|
|
|
protected:
|
|
void notify() {
|
|
ALOGV("%s()", __func__);
|
|
std::shared_ptr<Listener> listener = mListener.lock();
|
|
if (listener) {
|
|
listener->onEventNotified();
|
|
}
|
|
}
|
|
|
|
std::weak_ptr<Listener> mListener;
|
|
};
|
|
|
|
// Notifies the listener when the connected IGBP releases buffers.
|
|
class BufferReleasedNotifier : public EventNotifier, public HProducerListener {
|
|
public:
|
|
using EventNotifier::EventNotifier;
|
|
~BufferReleasedNotifier() override = default;
|
|
|
|
// HProducerListener implementation
|
|
Return<void> onBuffersReleased(uint32_t count) override {
|
|
ALOGV("%s(%u)", __func__, count);
|
|
if (count > 0) {
|
|
notify();
|
|
}
|
|
return {};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* BlockPoolData implementation for C2VdaBqBlockPool. The life cycle of this object should be as
|
|
* long as its accompanied C2GraphicBlock.
|
|
*
|
|
* When C2VdaBqBlockPoolData is created, |mShared| is false, and the owner of the accompanied
|
|
* C2GraphicBlock is the component that called fetchGraphicBlock(). If this is released before
|
|
* sharing, the destructor will call detachBuffer() to BufferQueue to free the slot.
|
|
*
|
|
* When the accompanied C2GraphicBlock is going to share to client from component, component should
|
|
* call MarkBlockPoolDataAsShared() to set |mShared| to true, and then this will be released after
|
|
* the transition of C2GraphicBlock across HIDL interface. At this time, the destructor will not
|
|
* call detachBuffer().
|
|
*/
|
|
struct C2VdaBqBlockPoolData : public _C2BlockPoolData {
|
|
// This type should be a different value than what _C2BlockPoolData::type_t has defined.
|
|
static constexpr int kTypeVdaBufferQueue = TYPE_BUFFERQUEUE + 256;
|
|
|
|
C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId,
|
|
const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool);
|
|
C2VdaBqBlockPoolData() = delete;
|
|
|
|
// If |mShared| is false, call detach buffer to BufferQueue via |mPool|
|
|
virtual ~C2VdaBqBlockPoolData() override;
|
|
|
|
type_t getType() const override { return static_cast<type_t>(kTypeVdaBufferQueue); }
|
|
|
|
bool mShared = false; // whether is shared from component to client.
|
|
const uint64_t mProducerId;
|
|
const int32_t mSlotId;
|
|
const std::shared_ptr<C2VdaBqBlockPool::Impl> mPool;
|
|
};
|
|
|
|
c2_status_t MarkBlockPoolDataAsShared(const C2ConstGraphicBlock& sharedBlock) {
|
|
std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(sharedBlock);
|
|
if (!data || data->getType() != C2VdaBqBlockPoolData::kTypeVdaBufferQueue) {
|
|
// Skip this functtion if |sharedBlock| is not fetched from C2VdaBqBlockPool.
|
|
return C2_OMITTED;
|
|
}
|
|
const std::shared_ptr<C2VdaBqBlockPoolData> poolData =
|
|
std::static_pointer_cast<C2VdaBqBlockPoolData>(data);
|
|
if (poolData->mShared) {
|
|
ALOGE("C2VdaBqBlockPoolData(id=%" PRIu64 ", slot=%d) is already marked as shared...",
|
|
poolData->mProducerId, poolData->mSlotId);
|
|
return C2_BAD_STATE;
|
|
}
|
|
poolData->mShared = true;
|
|
return C2_OK;
|
|
}
|
|
|
|
// static
|
|
std::optional<uint32_t> C2VdaBqBlockPool::getBufferIdFromGraphicBlock(const C2Block2D& block) {
|
|
uint32_t width, height, format, stride, igbp_slot, generation;
|
|
uint64_t usage, igbp_id;
|
|
android::_UnwrapNativeCodec2GrallocMetadata(block.handle(), &width, &height, &format, &usage,
|
|
&stride, &generation, &igbp_id, &igbp_slot);
|
|
ALOGV("Unwrap Metadata: igbp[%" PRIu64 ", %u] (%u*%u, fmt %#x, usage %" PRIx64 ", stride %u)",
|
|
igbp_id, igbp_slot, width, height, format, usage, stride);
|
|
return igbp_slot;
|
|
}
|
|
|
|
class C2VdaBqBlockPool::Impl : public std::enable_shared_from_this<C2VdaBqBlockPool::Impl>,
|
|
public EventNotifier::Listener {
|
|
public:
|
|
using HGraphicBufferProducer = C2VdaBqBlockPool::HGraphicBufferProducer;
|
|
|
|
explicit Impl(const std::shared_ptr<C2Allocator>& allocator);
|
|
// TODO: should we detach buffers on producer if any on destructor?
|
|
~Impl() = default;
|
|
|
|
// EventNotifier::Listener implementation.
|
|
void onEventNotified() override;
|
|
|
|
c2_status_t fetchGraphicBlock(uint32_t width, uint32_t height, uint32_t format,
|
|
C2MemoryUsage usage,
|
|
std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
|
|
void setRenderCallback(const C2BufferQueueBlockPool::OnRenderCallback& renderCallback);
|
|
void configureProducer(const sp<HGraphicBufferProducer>& producer);
|
|
c2_status_t requestNewBufferSet(int32_t bufferCount);
|
|
c2_status_t updateGraphicBlock(bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
|
|
std::shared_ptr<C2GraphicBlock>* block /* nonnull */);
|
|
c2_status_t getMinBuffersForDisplay(size_t* bufferCount);
|
|
bool setNotifyBlockAvailableCb(::base::OnceClosure cb);
|
|
|
|
private:
|
|
friend struct C2VdaBqBlockPoolData;
|
|
|
|
// Requested buffer formats.
|
|
struct BufferFormat {
|
|
BufferFormat(uint32_t width, uint32_t height, uint32_t pixelFormat,
|
|
C2AndroidMemoryUsage androidUsage)
|
|
: mWidth(width), mHeight(height), mPixelFormat(pixelFormat), mUsage(androidUsage) {}
|
|
BufferFormat() = default;
|
|
|
|
uint32_t mWidth = 0;
|
|
uint32_t mHeight = 0;
|
|
uint32_t mPixelFormat = 0;
|
|
C2AndroidMemoryUsage mUsage = C2MemoryUsage(0);
|
|
};
|
|
|
|
// For C2VdaBqBlockPoolData to detach corresponding slot buffer from BufferQueue.
|
|
void detachBuffer(uint64_t producerId, int32_t slotId);
|
|
|
|
// Queries the generation and usage flags from the given producer by dequeuing and requesting a
|
|
// buffer (the buffer is then detached and freed).
|
|
c2_status_t queryGenerationAndUsage(H2BGraphicBufferProducer* const producer, uint32_t width,
|
|
uint32_t height, uint32_t pixelFormat,
|
|
C2AndroidMemoryUsage androidUsage, uint32_t* generation,
|
|
uint64_t* usage);
|
|
|
|
// Switches producer and transfers allocated buffers from old producer to the new one.
|
|
bool switchProducer(H2BGraphicBufferProducer* const newProducer, uint64_t newProducerId);
|
|
|
|
const std::shared_ptr<C2Allocator> mAllocator;
|
|
|
|
std::unique_ptr<H2BGraphicBufferProducer> mProducer;
|
|
uint64_t mProducerId;
|
|
C2BufferQueueBlockPool::OnRenderCallback mRenderCallback;
|
|
|
|
// Function mutex to lock at the start of each API function call for protecting the
|
|
// synchronization of all member variables.
|
|
std::mutex mMutex;
|
|
// The mutex of excluding the procedures of configuring producer and allocating buffers. They
|
|
// should be blocked mutually. Set the timeout for acquiring lock in case of any deadlock.
|
|
// Configuring producer: configureProducer() called by CCodec.
|
|
// Allocating buffers: requestNewBufferSet(), then a loop of fetchGraphicBlock() called by
|
|
// compoenent until |mSlotAllocations|.size() equals |mBuffersRequested|.
|
|
std::timed_mutex mConfigureProducerAndAllocateBuffersMutex;
|
|
// The unique lock of the procedure of allocating buffers. It should be locked in the beginning
|
|
// of requestNewBufferSet() and unlock in the end of the loop of fetchGraphicBlock(). Note that
|
|
// all calls should be in the same thread.
|
|
std::unique_lock<std::timed_mutex> mAllocateBuffersLock;
|
|
|
|
// The map restored C2GraphicAllocation from corresponding slot index.
|
|
std::map<int32_t, std::shared_ptr<C2GraphicAllocation>> mSlotAllocations;
|
|
// Number of buffers requested on requestNewBufferSet() call.
|
|
size_t mBuffersRequested;
|
|
// Currently requested buffer formats.
|
|
BufferFormat mBufferFormat;
|
|
// The map recorded the slot indices from old producer to new producer.
|
|
std::map<int32_t, int32_t> mProducerChangeSlotMap;
|
|
// The counter for representing the buffer count in client. Only used in producer switching
|
|
// case. It will be reset in switchProducer(), and accumulated in updateGraphicBlock() routine.
|
|
uint32_t mBuffersInClient = 0u;
|
|
// The indicator to record if producer has been switched. Set to true when producer is switched.
|
|
// Toggle off when requestNewBufferSet() is called. We forcedly detach all slots to make sure
|
|
// all slots are available, except the ones owned by client.
|
|
bool mProducerSwitched = false;
|
|
|
|
// Listener for buffer release events.
|
|
sp<EventNotifier> mFetchBufferNotifier;
|
|
|
|
std::mutex mBufferReleaseMutex;
|
|
// Set to true when the buffer release event is triggered after dequeueing
|
|
// buffer from IGBP times out.
|
|
bool mBufferReleasedAfterTimedOut GUARDED_BY(mBufferReleaseMutex) = false;
|
|
// The callback to notify the caller the buffer is available.
|
|
::base::OnceClosure mNotifyBlockAvailableCb GUARDED_BY(mBufferReleaseMutex);
|
|
};
|
|
|
|
C2VdaBqBlockPool::Impl::Impl(const std::shared_ptr<C2Allocator>& allocator)
|
|
: mAllocator(allocator),
|
|
mAllocateBuffersLock(mConfigureProducerAndAllocateBuffersMutex, std::defer_lock),
|
|
mBuffersRequested(0u) {}
|
|
|
|
c2_status_t C2VdaBqBlockPool::Impl::fetchGraphicBlock(
|
|
uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
|
|
std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
|
|
ALOGV("%s()", __func__);
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (!mProducer) {
|
|
// Producer will not be configured in byte-buffer mode. Allocate buffers from allocator
|
|
// directly as a basic graphic block pool.
|
|
std::shared_ptr<C2GraphicAllocation> alloc;
|
|
c2_status_t err = mAllocator->newGraphicAllocation(width, height, format, usage, &alloc);
|
|
if (err != C2_OK) {
|
|
return err;
|
|
}
|
|
*block = _C2BlockFactory::CreateGraphicBlock(alloc);
|
|
return C2_OK;
|
|
}
|
|
|
|
// The existence of |mProducerChangeSlotMap| indicates producer is just switched. Use return
|
|
// code C2_BAD_STATE to inform the component to handle the procedure of producer change.
|
|
// TODO(johnylin): consider to inform producer change to component in an active way.
|
|
if (!mProducerChangeSlotMap.empty()) {
|
|
return C2_BAD_STATE;
|
|
}
|
|
|
|
C2AndroidMemoryUsage androidUsage = usage;
|
|
uint32_t pixelFormat = format;
|
|
int32_t slot;
|
|
sp<Fence> fence = new Fence();
|
|
status_t status =
|
|
mProducer->dequeueBuffer(width, height, pixelFormat, androidUsage, &slot, &fence);
|
|
// The C2VdaBqBlockPool does not fully own the bufferqueue. After buffers are dequeued here,
|
|
// they are passed into the codec2 framework, processed, and eventually queued into the
|
|
// bufferqueue. The C2VdaBqBlockPool cannot determine exactly when a buffer gets queued.
|
|
// However, if every buffer is being processed by the codec2 framework, then dequeueBuffer()
|
|
// will return INVALID_OPERATION because of an attempt to dequeue too many buffers.
|
|
// The C2VdaBqBlockPool cannot prevent this from happening, so just map it to TIMED_OUT
|
|
// and let the C2VdaBqBlockPool's caller's timeout retry logic handle the failure.
|
|
if (status == android::INVALID_OPERATION) {
|
|
status = android::TIMED_OUT;
|
|
}
|
|
if (status == android::TIMED_OUT) {
|
|
std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
|
|
mBufferReleasedAfterTimedOut = false;
|
|
}
|
|
if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION) {
|
|
return asC2Error(status);
|
|
}
|
|
|
|
// Wait for acquire fence if we get one.
|
|
if (fence) {
|
|
status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
|
|
if (fenceStatus != android::NO_ERROR) {
|
|
if (mProducer->cancelBuffer(slot, fence) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
|
|
if (fenceStatus == -ETIME) { // fence wait timed out
|
|
ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot);
|
|
return C2_TIMED_OUT;
|
|
}
|
|
ALOGE("buffer fence wait error: %d", fenceStatus);
|
|
return asC2Error(fenceStatus);
|
|
}
|
|
|
|
if (mRenderCallback) {
|
|
nsecs_t signalTime = fence->getSignalTime();
|
|
if (signalTime >= 0 && signalTime < INT64_MAX) {
|
|
mRenderCallback(mProducerId, slot, signalTime);
|
|
} else {
|
|
ALOGV("got fence signal time of %" PRId64 " nsec", signalTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto iter = mSlotAllocations.find(slot);
|
|
if (iter == mSlotAllocations.end()) {
|
|
if (mSlotAllocations.size() >= mBuffersRequested) {
|
|
// The dequeued slot has a pre-allocated buffer whose size and format is as same as
|
|
// currently requested (but was not dequeued during allocation cycle). Just detach it to
|
|
// free this slot. And try dequeueBuffer again.
|
|
ALOGD("dequeued a new slot index but already allocated enough buffers. Detach it.");
|
|
|
|
if (mProducer->detachBuffer(slot) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
return C2_TIMED_OUT;
|
|
}
|
|
if (status != BUFFER_NEEDS_REALLOCATION) {
|
|
// The dequeued slot has a pre-allocated buffer whose size and format is as same as
|
|
// currently requested, so there is no BUFFER_NEEDS_REALLOCATION flag. However since the
|
|
// buffer reference is already dropped, still call requestBuffer to re-allocate then.
|
|
// Add a debug note here for tracking.
|
|
ALOGD("dequeued a new slot index without BUFFER_NEEDS_REALLOCATION flag.");
|
|
}
|
|
|
|
// Call requestBuffer to allocate buffer for the slot and obtain the reference.
|
|
sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
|
|
status = mProducer->requestBuffer(slot, &slotBuffer);
|
|
if (status != android::NO_ERROR) {
|
|
if (mProducer->cancelBuffer(slot, fence) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
return asC2Error(status);
|
|
}
|
|
|
|
// Convert GraphicBuffer to C2GraphicAllocation and wrap producer id and slot index
|
|
ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", mProducerId, slot);
|
|
C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
|
|
slotBuffer->handle, slotBuffer->width, slotBuffer->height, slotBuffer->format,
|
|
slotBuffer->usage, slotBuffer->stride, slotBuffer->getGenerationNumber(),
|
|
mProducerId, slot);
|
|
if (!c2Handle) {
|
|
ALOGE("WrapNativeCodec2GrallocHandle failed");
|
|
return C2_NO_MEMORY;
|
|
}
|
|
|
|
std::shared_ptr<C2GraphicAllocation> alloc;
|
|
c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
|
|
if (err != C2_OK) {
|
|
ALOGE("priorGraphicAllocation failed: %d", err);
|
|
return err;
|
|
}
|
|
|
|
mSlotAllocations[slot] = std::move(alloc);
|
|
if (mSlotAllocations.size() == mBuffersRequested) {
|
|
// Already allocated enough buffers, set allowAllocation to false to restrict the
|
|
// eligible slots to allocated ones for future dequeue.
|
|
status = mProducer->allowAllocation(false);
|
|
if (status != android::NO_ERROR) {
|
|
return asC2Error(status);
|
|
}
|
|
// Store buffer formats for future usage.
|
|
mBufferFormat = BufferFormat(width, height, pixelFormat, androidUsage);
|
|
ALOG_ASSERT(mAllocateBuffersLock.owns_lock());
|
|
mAllocateBuffersLock.unlock();
|
|
}
|
|
}
|
|
|
|
auto poolData = std::make_shared<C2VdaBqBlockPoolData>(mProducerId, slot, shared_from_this());
|
|
*block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot], std::move(poolData));
|
|
return C2_OK;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::Impl::onEventNotified() {
|
|
ALOGV("%s()", __func__);
|
|
::base::OnceClosure outputCb;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
|
|
|
|
mBufferReleasedAfterTimedOut = true;
|
|
if (mNotifyBlockAvailableCb) {
|
|
outputCb = std::move(mNotifyBlockAvailableCb);
|
|
}
|
|
}
|
|
|
|
// Calling the callback outside the lock to avoid the deadlock.
|
|
if (outputCb) {
|
|
std::move(outputCb).Run();
|
|
}
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::Impl::queryGenerationAndUsage(
|
|
H2BGraphicBufferProducer* const producer, uint32_t width, uint32_t height,
|
|
uint32_t pixelFormat, C2AndroidMemoryUsage androidUsage, uint32_t* generation,
|
|
uint64_t* usage) {
|
|
ALOGV("queryGenerationAndUsage");
|
|
sp<Fence> fence = new Fence();
|
|
int32_t status;
|
|
int32_t slot;
|
|
|
|
status = producer->dequeueBuffer(width, height, pixelFormat, androidUsage, &slot, &fence);
|
|
if (status != android::NO_ERROR && status != BUFFER_NEEDS_REALLOCATION) {
|
|
return asC2Error(status);
|
|
}
|
|
|
|
// Wait for acquire fence if we get one.
|
|
if (fence) {
|
|
status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
|
|
if (fenceStatus != android::NO_ERROR) {
|
|
if (producer->cancelBuffer(slot, fence) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
if (fenceStatus == -ETIME) { // fence wait timed out
|
|
ALOGV("%s(): buffer (slot=%d) fence wait timed out", __func__, slot);
|
|
return C2_TIMED_OUT;
|
|
}
|
|
ALOGE("buffer fence wait error: %d", fenceStatus);
|
|
return asC2Error(fenceStatus);
|
|
}
|
|
}
|
|
|
|
// Call requestBuffer to allocate buffer for the slot and obtain the reference.
|
|
// Get generation number here.
|
|
sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
|
|
status = producer->requestBuffer(slot, &slotBuffer);
|
|
|
|
// Detach and delete the temporary buffer.
|
|
if (producer->detachBuffer(slot) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
|
|
// Check requestBuffer return flag.
|
|
if (status != android::NO_ERROR) {
|
|
return asC2Error(status);
|
|
}
|
|
|
|
// Get generation number and usage from the slot buffer.
|
|
*usage = slotBuffer->getUsage();
|
|
*generation = slotBuffer->getGenerationNumber();
|
|
ALOGV("Obtained from temp buffer: generation = %u, usage = %" PRIu64 "", *generation, *usage);
|
|
return C2_OK;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::Impl::setRenderCallback(
|
|
const C2BufferQueueBlockPool::OnRenderCallback& renderCallback) {
|
|
ALOGV("setRenderCallback");
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
mRenderCallback = renderCallback;
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::Impl::requestNewBufferSet(int32_t bufferCount) {
|
|
if (bufferCount <= 0) {
|
|
ALOGE("Invalid requested buffer count = %d", bufferCount);
|
|
return C2_BAD_VALUE;
|
|
}
|
|
|
|
if (!mAllocateBuffersLock.try_lock_for(kTimedMutexTimeoutMs)) {
|
|
ALOGE("Cannot acquire allocate buffers / configure producer lock over %" PRId64 " ms...",
|
|
static_cast<int64_t>(kTimedMutexTimeoutMs.count()));
|
|
return C2_BLOCKING;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!mProducer) {
|
|
ALOGD("No HGraphicBufferProducer is configured...");
|
|
return C2_NO_INIT;
|
|
}
|
|
|
|
if (mProducerSwitched) {
|
|
// Some slots can be occupied by buffers transferred from the old producer. They will not
|
|
// used in the current producer. Free the slots of the buffers here. But we cannot find a
|
|
// slot is associated with the staled buffer. We free all slots whose associated buffers
|
|
// are not owned by client.
|
|
ALOGI("requestNewBufferSet: detachBuffer all slots forcedly");
|
|
for (int32_t slot = 0; slot < static_cast<int32_t>(NUM_BUFFER_SLOTS); ++slot) {
|
|
if (mSlotAllocations.find(slot) != mSlotAllocations.end()) {
|
|
// Skip detaching the buffer which is owned by client now.
|
|
continue;
|
|
}
|
|
status_t status = mProducer->detachBuffer(slot);
|
|
if (status == android::NO_INIT) {
|
|
// No more active buffer slot. Break the loop now.
|
|
break;
|
|
} else if (status != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
}
|
|
mProducerSwitched = false;
|
|
}
|
|
|
|
ALOGV("Requested new buffer count: %d, still dequeued buffer count: %zu", bufferCount,
|
|
mSlotAllocations.size());
|
|
|
|
// The remained slot indices in |mSlotAllocations| now are still dequeued (un-available).
|
|
// maxDequeuedBufferCount should be set to "new requested buffer count" + "still dequeued buffer
|
|
// count" to make sure it has enough available slots to request buffer from.
|
|
status_t status = mProducer->setMaxDequeuedBufferCount(bufferCount + mSlotAllocations.size());
|
|
if (status != android::NO_ERROR) {
|
|
return asC2Error(status);
|
|
}
|
|
|
|
// Release all remained slot buffer references here. CCodec should either cancel or queue its
|
|
// owned buffers from this set before the next resolution change.
|
|
mSlotAllocations.clear();
|
|
mProducerChangeSlotMap.clear();
|
|
mBuffersRequested = static_cast<size_t>(bufferCount);
|
|
|
|
status = mProducer->allowAllocation(true);
|
|
if (status != android::NO_ERROR) {
|
|
return asC2Error(status);
|
|
}
|
|
return C2_OK;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::Impl::configureProducer(const sp<HGraphicBufferProducer>& producer) {
|
|
ALOGV("configureProducer");
|
|
if (producer == nullptr) {
|
|
ALOGE("input producer is nullptr...");
|
|
return;
|
|
}
|
|
|
|
std::unique_lock<std::timed_mutex> configureProducerLock(
|
|
mConfigureProducerAndAllocateBuffersMutex, std::defer_lock);
|
|
if (!configureProducerLock.try_lock_for(kTimedMutexTimeoutMs)) {
|
|
ALOGE("Cannot acquire configure producer / allocate buffers lock over %" PRId64 " ms...",
|
|
static_cast<int64_t>(kTimedMutexTimeoutMs.count()));
|
|
return;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
auto newProducer = std::make_unique<H2BGraphicBufferProducer>(producer);
|
|
uint64_t producerId;
|
|
if (newProducer->getUniqueId(&producerId) != android::NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
if (mProducer && mProducerId != producerId) {
|
|
ALOGI("Producer (Surface) is going to switch... ( %" PRIu64 " -> %" PRIu64 " )",
|
|
mProducerId, producerId);
|
|
if (!switchProducer(newProducer.get(), producerId)) {
|
|
mProducerChangeSlotMap.clear();
|
|
return;
|
|
}
|
|
} else {
|
|
mSlotAllocations.clear();
|
|
}
|
|
|
|
if (newProducer->setDequeueTimeout(0) != android::NO_ERROR) {
|
|
ALOGE("%s(): failed to setDequeueTimeout(0)", __func__);
|
|
return;
|
|
}
|
|
|
|
// hack(b/146409777): Try to connect ARC-specific listener first.
|
|
sp<BufferReleasedNotifier> listener = new BufferReleasedNotifier(shared_from_this());
|
|
if (newProducer->connect(listener, 'ARC\0', false) == android::NO_ERROR) {
|
|
ALOGI("connected to ARC-specific IGBP listener.");
|
|
mFetchBufferNotifier = listener;
|
|
}
|
|
|
|
// HGraphicBufferProducer could (and should) be replaced if the client has set a new generation
|
|
// number to producer. The old HGraphicBufferProducer will be disconnected and deprecated then.
|
|
mProducer = std::move(newProducer);
|
|
mProducerId = producerId;
|
|
}
|
|
|
|
bool C2VdaBqBlockPool::Impl::switchProducer(H2BGraphicBufferProducer* const newProducer,
|
|
uint64_t newProducerId) {
|
|
if (mAllocator->getId() == android::V4L2AllocatorId::SECURE_GRAPHIC) {
|
|
// TODO(johnylin): support this when we meet the use case in the future.
|
|
ALOGE("Switch producer for secure buffer is not supported...");
|
|
return false;
|
|
}
|
|
|
|
// Set maxDequeuedBufferCount to new producer.
|
|
// Just like requestNewBufferSet(), maxDequeuedBufferCount should be set to "requested buffer
|
|
// count" + "buffer count in client" to make sure it has enough available slots to request
|
|
// buffers from.
|
|
// "Requested buffer count" could be obtained by the size of |mSlotAllocations|. However, it is
|
|
// not able to know "buffer count in client" in blockpool's aspect. The alternative solution is
|
|
// to set the worse case first, which is equal to the size of |mSlotAllocations|. And in the end
|
|
// of updateGraphicBlock() routine, we could get the arbitrary "buffer count in client" by
|
|
// counting the calls of updateGraphicBlock(willCancel=true). Then we set maxDequeuedBufferCount
|
|
// again to the correct value.
|
|
if (newProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() * 2) != android::NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// Reset "buffer count in client". It will be accumulated in updateGraphicBlock() routine.
|
|
mBuffersInClient = 0;
|
|
|
|
// Set allowAllocation to new producer.
|
|
if (newProducer->allowAllocation(true) != android::NO_ERROR) {
|
|
return false;
|
|
}
|
|
|
|
// Get a buffer from the new producer to get the generation number and usage of new producer.
|
|
// While attaching buffers, generation number and usage must be aligned to the producer.
|
|
uint32_t newGeneration;
|
|
uint64_t newUsage;
|
|
c2_status_t err = queryGenerationAndUsage(newProducer, mBufferFormat.mWidth,
|
|
mBufferFormat.mHeight, mBufferFormat.mPixelFormat,
|
|
mBufferFormat.mUsage, &newGeneration, &newUsage);
|
|
if (err != C2_OK) {
|
|
ALOGE("queryGenerationAndUsage failed: %d", err);
|
|
return false;
|
|
}
|
|
|
|
// Attach all buffers to new producer.
|
|
mProducerChangeSlotMap.clear();
|
|
int32_t slot;
|
|
std::map<int32_t, std::shared_ptr<C2GraphicAllocation>> newSlotAllocations;
|
|
for (auto iter = mSlotAllocations.begin(); iter != mSlotAllocations.end(); ++iter) {
|
|
// Convert C2GraphicAllocation to GraphicBuffer.
|
|
uint32_t width, height, format, stride, igbp_slot, generation;
|
|
uint64_t usage, igbp_id;
|
|
android::_UnwrapNativeCodec2GrallocMetadata(iter->second->handle(), &width, &height,
|
|
&format, &usage, &stride, &generation, &igbp_id,
|
|
&igbp_slot);
|
|
native_handle_t* grallocHandle =
|
|
android::UnwrapNativeCodec2GrallocHandle(iter->second->handle());
|
|
|
|
// Update generation number and usage.
|
|
sp<GraphicBuffer> graphicBuffer =
|
|
new GraphicBuffer(grallocHandle, GraphicBuffer::CLONE_HANDLE, width, height, format,
|
|
1, newUsage, stride);
|
|
if (graphicBuffer->initCheck() != android::NO_ERROR) {
|
|
ALOGE("Failed to create GraphicBuffer: %d", graphicBuffer->initCheck());
|
|
return false;
|
|
}
|
|
graphicBuffer->setGenerationNumber(newGeneration);
|
|
native_handle_delete(grallocHandle);
|
|
|
|
if (newProducer->attachBuffer(graphicBuffer, &slot) != android::NO_ERROR) {
|
|
return false;
|
|
}
|
|
// Convert back to C2GraphicAllocation wrapping new producer id, generation number, usage
|
|
// and slot index.
|
|
ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", newProducerId, slot);
|
|
C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
|
|
graphicBuffer->handle, width, height, format, newUsage, stride, newGeneration,
|
|
newProducerId, slot);
|
|
if (!c2Handle) {
|
|
ALOGE("WrapNativeCodec2GrallocHandle failed");
|
|
return false;
|
|
}
|
|
std::shared_ptr<C2GraphicAllocation> alloc;
|
|
c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
|
|
if (err != C2_OK) {
|
|
ALOGE("priorGraphicAllocation failed: %d", err);
|
|
return false;
|
|
}
|
|
|
|
// Store to |newSlotAllocations| and also store old-to-new producer slot map.
|
|
ALOGV("Transfered buffer from old producer to new, slot prev: %d -> new %d", iter->first,
|
|
slot);
|
|
newSlotAllocations[slot] = std::move(alloc);
|
|
mProducerChangeSlotMap[iter->first] = slot;
|
|
}
|
|
|
|
// Set allowAllocation to false so producer could not allocate new buffers.
|
|
if (newProducer->allowAllocation(false) != android::NO_ERROR) {
|
|
ALOGE("allowAllocation(false) failed");
|
|
return false;
|
|
}
|
|
|
|
// Try to detach all buffers from old producer.
|
|
for (const auto& slotAllocation : mSlotAllocations) {
|
|
status_t status = mProducer->detachBuffer(slotAllocation.first);
|
|
if (status != android::NO_ERROR) {
|
|
ALOGW("detachBuffer slot=%d from old producer failed: %d", slotAllocation.first,
|
|
status);
|
|
}
|
|
}
|
|
|
|
mSlotAllocations = std::move(newSlotAllocations);
|
|
return true;
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::Impl::updateGraphicBlock(
|
|
bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
|
|
std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
|
|
if (mProducerChangeSlotMap.empty()) {
|
|
ALOGD("A new buffer set is requested right after producer change, no more update needed.");
|
|
return C2_CANCELED;
|
|
}
|
|
|
|
auto it = mProducerChangeSlotMap.find(static_cast<int32_t>(oldSlot));
|
|
if (it == mProducerChangeSlotMap.end()) {
|
|
ALOGE("Cannot find old slot = %u in map...", oldSlot);
|
|
return C2_NOT_FOUND;
|
|
}
|
|
|
|
int32_t slot = it->second;
|
|
*newSlot = static_cast<uint32_t>(slot);
|
|
mProducerChangeSlotMap.erase(it);
|
|
|
|
if (willCancel) {
|
|
sp<Fence> fence = new Fence();
|
|
// The old C2GraphicBlock might be owned by client. Cancel this slot.
|
|
if (mProducer->cancelBuffer(slot, fence) != android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
// Client might try to attach the old buffer to the current producer on client's end,
|
|
// although it is useless for us anymore. However it will still occupy an available slot.
|
|
mBuffersInClient++;
|
|
} else {
|
|
// The old C2GraphicBlock is still owned by component, replace by the new one and keep this
|
|
// slot dequeued.
|
|
auto poolData =
|
|
std::make_shared<C2VdaBqBlockPoolData>(mProducerId, slot, shared_from_this());
|
|
*block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot], std::move(poolData));
|
|
}
|
|
|
|
if (mProducerChangeSlotMap.empty()) {
|
|
// The updateGraphicBlock() routine is about to finish.
|
|
// Set the correct maxDequeuedBufferCount to producer, which is "requested buffer count" +
|
|
// "buffer count in client".
|
|
ALOGV("Requested buffer count: %zu, buffer count in client: %u", mSlotAllocations.size(),
|
|
mBuffersInClient);
|
|
if (mProducer->setMaxDequeuedBufferCount(mSlotAllocations.size() + mBuffersInClient) !=
|
|
android::NO_ERROR) {
|
|
return C2_CORRUPTED;
|
|
}
|
|
mProducerSwitched = true;
|
|
}
|
|
|
|
return C2_OK;
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::Impl::getMinBuffersForDisplay(size_t* bufferCount) {
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (!mProducer) {
|
|
ALOGD("No HGraphicBufferProducer is configured...");
|
|
return C2_NO_INIT;
|
|
}
|
|
|
|
int32_t status, value;
|
|
status = mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value);
|
|
if (status != android::NO_ERROR) {
|
|
ALOGE("query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) failed: %d", status);
|
|
return asC2Error(status);
|
|
}
|
|
if (value <= 0) {
|
|
ALOGE("Illegal value of NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = %d", value);
|
|
return C2_BAD_VALUE;
|
|
}
|
|
*bufferCount = static_cast<size_t>(value);
|
|
return C2_OK;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::Impl::detachBuffer(uint64_t producerId, int32_t slotId) {
|
|
ALOGV("detachBuffer: producer id = %" PRIu64 ", slot = %d", producerId, slotId);
|
|
std::lock_guard<std::mutex> lock(mMutex);
|
|
if (producerId == mProducerId && mProducer) {
|
|
if (mProducer->detachBuffer(slotId) != android::NO_ERROR) {
|
|
return;
|
|
}
|
|
|
|
auto it = mSlotAllocations.find(slotId);
|
|
// It may happen that the slot is not included in |mSlotAllocations|, which means it is
|
|
// released after resolution change.
|
|
if (it != mSlotAllocations.end()) {
|
|
mSlotAllocations.erase(it);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool C2VdaBqBlockPool::Impl::setNotifyBlockAvailableCb(::base::OnceClosure cb) {
|
|
ALOGV("%s()", __func__);
|
|
if (mFetchBufferNotifier == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
::base::OnceClosure outputCb;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mBufferReleaseMutex);
|
|
|
|
// If there is any buffer released after dequeueBuffer() timed out, then we could notify the
|
|
// caller directly.
|
|
if (mBufferReleasedAfterTimedOut) {
|
|
outputCb = std::move(cb);
|
|
} else {
|
|
mNotifyBlockAvailableCb = std::move(cb);
|
|
}
|
|
}
|
|
|
|
// Calling the callback outside the lock to avoid the deadlock.
|
|
if (outputCb) {
|
|
std::move(outputCb).Run();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator,
|
|
const local_id_t localId)
|
|
: C2BufferQueueBlockPool(allocator, localId), mLocalId(localId), mImpl(new Impl(allocator)) {}
|
|
|
|
c2_status_t C2VdaBqBlockPool::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_NO_INIT;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::setRenderCallback(
|
|
const C2BufferQueueBlockPool::OnRenderCallback& renderCallback) {
|
|
if (mImpl) {
|
|
mImpl->setRenderCallback(renderCallback);
|
|
}
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::requestNewBufferSet(int32_t bufferCount) {
|
|
if (mImpl) {
|
|
return mImpl->requestNewBufferSet(bufferCount);
|
|
}
|
|
return C2_NO_INIT;
|
|
}
|
|
|
|
void C2VdaBqBlockPool::configureProducer(const sp<HGraphicBufferProducer>& producer) {
|
|
if (mImpl) {
|
|
mImpl->configureProducer(producer);
|
|
}
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::updateGraphicBlock(
|
|
bool willCancel, uint32_t oldSlot, uint32_t* newSlot,
|
|
std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
|
|
if (mImpl) {
|
|
return mImpl->updateGraphicBlock(willCancel, oldSlot, newSlot, block);
|
|
}
|
|
return C2_NO_INIT;
|
|
}
|
|
|
|
c2_status_t C2VdaBqBlockPool::getMinBuffersForDisplay(size_t* bufferCount) {
|
|
if (mImpl) {
|
|
return mImpl->getMinBuffersForDisplay(bufferCount);
|
|
}
|
|
return C2_NO_INIT;
|
|
}
|
|
|
|
bool C2VdaBqBlockPool::setNotifyBlockAvailableCb(::base::OnceClosure cb) {
|
|
if (mImpl) {
|
|
return mImpl->setNotifyBlockAvailableCb(std::move(cb));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
C2VdaBqBlockPoolData::C2VdaBqBlockPoolData(uint64_t producerId, int32_t slotId,
|
|
const std::shared_ptr<C2VdaBqBlockPool::Impl>& pool)
|
|
: mProducerId(producerId), mSlotId(slotId), mPool(pool) {}
|
|
|
|
C2VdaBqBlockPoolData::~C2VdaBqBlockPoolData() {
|
|
if (mShared || !mPool) {
|
|
return;
|
|
}
|
|
mPool->detachBuffer(mProducerId, mSlotId);
|
|
}
|
|
|
|
} // namespace android
|