/* * Copyright (C) 2016 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 "C2Buffer" #include #include #include #include #include #include #include #include #include namespace { using android::C2AllocatorGralloc; using android::C2AllocatorIon; using android::hardware::media::bufferpool::BufferPoolData; using android::hardware::media::bufferpool::V1_0::ResultStatus; using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocation; using android::hardware::media::bufferpool::V1_0::implementation::BufferPoolAllocator; using android::hardware::media::bufferpool::V1_0::implementation::ClientManager; using android::hardware::media::bufferpool::V1_0::implementation::ConnectionId; using android::hardware::media::bufferpool::V1_0::implementation::INVALID_CONNECTIONID; // This anonymous namespace contains the helper classes that allow our implementation to create // block/buffer objects. // // Inherit from the parent, share with the friend. class ReadViewBuddy : public C2ReadView { using C2ReadView::C2ReadView; friend class ::C2ConstLinearBlock; }; class WriteViewBuddy : public C2WriteView { using C2WriteView::C2WriteView; friend class ::C2LinearBlock; }; class ConstLinearBlockBuddy : public C2ConstLinearBlock { using C2ConstLinearBlock::C2ConstLinearBlock; friend class ::C2LinearBlock; }; class LinearBlockBuddy : public C2LinearBlock { using C2LinearBlock::C2LinearBlock; friend class ::C2BasicLinearBlockPool; }; class AcquirableReadViewBuddy : public C2Acquirable { using C2Acquirable::C2Acquirable; friend class ::C2ConstLinearBlock; }; class AcquirableWriteViewBuddy : public C2Acquirable { using C2Acquirable::C2Acquirable; friend class ::C2LinearBlock; }; class GraphicViewBuddy : public C2GraphicView { using C2GraphicView::C2GraphicView; friend class ::C2ConstGraphicBlock; friend class ::C2GraphicBlock; }; class AcquirableConstGraphicViewBuddy : public C2Acquirable { using C2Acquirable::C2Acquirable; friend class ::C2ConstGraphicBlock; }; class AcquirableGraphicViewBuddy : public C2Acquirable { using C2Acquirable::C2Acquirable; friend class ::C2GraphicBlock; }; class ConstGraphicBlockBuddy : public C2ConstGraphicBlock { using C2ConstGraphicBlock::C2ConstGraphicBlock; friend class ::C2GraphicBlock; }; class GraphicBlockBuddy : public C2GraphicBlock { using C2GraphicBlock::C2GraphicBlock; friend class ::C2BasicGraphicBlockPool; }; class BufferDataBuddy : public C2BufferData { using C2BufferData::C2BufferData; friend class ::C2Buffer; }; } // namespace /* ========================================== 1D BLOCK ========================================= */ /** * This class is the base class for all 1D block and view implementations. * * This is basically just a placeholder for the underlying 1D allocation and the range of the * alloted portion to this block. There is also a placeholder for a blockpool data. */ class C2_HIDE _C2Block1DImpl : public _C2LinearRangeAspect { public: _C2Block1DImpl(const std::shared_ptr &alloc, const std::shared_ptr<_C2BlockPoolData> &poolData = nullptr, size_t offset = 0, size_t size = ~(size_t)0) : _C2LinearRangeAspect(alloc.get(), offset, size), mAllocation(alloc), mPoolData(poolData) { } _C2Block1DImpl(const _C2Block1DImpl &other, size_t offset = 0, size_t size = ~(size_t)0) : _C2LinearRangeAspect(&other, offset, size), mAllocation(other.mAllocation), mPoolData(other.mPoolData) { } /** returns pool data */ std::shared_ptr<_C2BlockPoolData> poolData() const { return mPoolData; } /** returns native handle */ const C2Handle *handle() const { return mAllocation ? mAllocation->handle() : nullptr; } /** returns the allocator's ID */ C2Allocator::id_t getAllocatorId() const { // BAD_ID can only happen if this Impl class is initialized for a view - never for a block. return mAllocation ? mAllocation->getAllocatorId() : C2Allocator::BAD_ID; } std::shared_ptr getAllocation() const { return mAllocation; } private: std::shared_ptr mAllocation; std::shared_ptr<_C2BlockPoolData> mPoolData; }; /** * This class contains the mapped data pointer, and the potential error. * * range is the mapped range of the underlying allocation (which is part of the allotted * range). */ class C2_HIDE _C2MappedBlock1DImpl : public _C2Block1DImpl { public: _C2MappedBlock1DImpl(const _C2Block1DImpl &block, uint8_t *data, size_t offset = 0, size_t size = ~(size_t)0) : _C2Block1DImpl(block, offset, size), mData(data), mError(C2_OK) { } _C2MappedBlock1DImpl(c2_status_t error) : _C2Block1DImpl(nullptr), mData(nullptr), mError(error) { // CHECK(error != C2_OK); } const uint8_t *data() const { return mData; } uint8_t *data() { return mData; } c2_status_t error() const { return mError; } private: uint8_t *mData; c2_status_t mError; }; /** * Block implementation. */ class C2Block1D::Impl : public _C2Block1DImpl { using _C2Block1DImpl::_C2Block1DImpl; }; const C2Handle *C2Block1D::handle() const { return mImpl->handle(); }; C2Allocator::id_t C2Block1D::getAllocatorId() const { return mImpl->getAllocatorId(); }; C2Block1D::C2Block1D(std::shared_ptr impl, const _C2LinearRangeAspect &range) // always clamp subrange to parent (impl) range for safety : _C2LinearRangeAspect(impl.get(), range.offset(), range.size()), mImpl(impl) { } /** * Read view implementation. * * range of Impl is the mapped range of the underlying allocation (which is part of the allotted * range). range of View is 0 to capacity() (not represented as an actual range). This maps to a * subrange of Impl range starting at mImpl->offset() + _mOffset. */ class C2ReadView::Impl : public _C2MappedBlock1DImpl { using _C2MappedBlock1DImpl::_C2MappedBlock1DImpl; }; C2ReadView::C2ReadView(std::shared_ptr impl, uint32_t offset, uint32_t size) : _C2LinearCapacityAspect(C2LinearCapacity(impl->size()).range(offset, size).size()), mImpl(impl), mOffset(C2LinearCapacity(impl->size()).range(offset, size).offset()) { } C2ReadView::C2ReadView(c2_status_t error) : _C2LinearCapacityAspect(0u), mImpl(std::make_shared(error)), mOffset(0u) { // CHECK(error != C2_OK); } const uint8_t *C2ReadView::data() const { return mImpl->error() ? nullptr : mImpl->data() + mOffset; } c2_status_t C2ReadView::error() const { return mImpl->error(); } C2ReadView C2ReadView::subView(size_t offset, size_t size) const { C2LinearRange subRange(*this, offset, size); return C2ReadView(mImpl, mOffset + subRange.offset(), subRange.size()); } /** * Write view implementation. */ class C2WriteView::Impl : public _C2MappedBlock1DImpl { using _C2MappedBlock1DImpl::_C2MappedBlock1DImpl; }; C2WriteView::C2WriteView(std::shared_ptr impl) // UGLY: _C2LinearRangeAspect requires a bona-fide object for capacity to prevent spoofing, so // this is what we have to do. // TODO: use childRange : _C2EditableLinearRangeAspect(std::make_unique(impl->size()).get()), mImpl(impl) { } C2WriteView::C2WriteView(c2_status_t error) : _C2EditableLinearRangeAspect(nullptr), mImpl(std::make_shared(error)) {} uint8_t *C2WriteView::base() { return mImpl->data(); } uint8_t *C2WriteView::data() { return mImpl->data() + offset(); } c2_status_t C2WriteView::error() const { return mImpl->error(); } /** * Const linear block implementation. */ C2ConstLinearBlock::C2ConstLinearBlock(std::shared_ptr impl, const _C2LinearRangeAspect &range, C2Fence fence) : C2Block1D(impl, range), mFence(fence) { } C2Acquirable C2ConstLinearBlock::map() const { void *base = nullptr; uint32_t len = size(); c2_status_t error = mImpl->getAllocation()->map( offset(), len, { C2MemoryUsage::CPU_READ, 0 }, nullptr, &base); // TODO: wait on fence if (error == C2_OK) { std::shared_ptr rvi = std::shared_ptr( new ReadViewBuddy::Impl(*mImpl, (uint8_t *)base, offset(), len), [base, len](ReadViewBuddy::Impl *i) { (void)i->getAllocation()->unmap(base, len, nullptr); delete i; }); return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(rvi, 0, len)); } else { return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(error)); } } C2ConstLinearBlock C2ConstLinearBlock::subBlock(size_t offset_, size_t size_) const { C2LinearRange subRange(*mImpl, offset_, size_); return C2ConstLinearBlock(mImpl, subRange, mFence); } /** * Linear block implementation. */ C2LinearBlock::C2LinearBlock(std::shared_ptr impl, const _C2LinearRangeAspect &range) : C2Block1D(impl, range) { } C2Acquirable C2LinearBlock::map() { void *base = nullptr; uint32_t len = size(); c2_status_t error = mImpl->getAllocation()->map( offset(), len, { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, nullptr, &base); // TODO: wait on fence if (error == C2_OK) { std::shared_ptr rvi = std::shared_ptr( new WriteViewBuddy::Impl(*mImpl, (uint8_t *)base, 0, len), [base, len](WriteViewBuddy::Impl *i) { (void)i->getAllocation()->unmap(base, len, nullptr); delete i; }); return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(rvi)); } else { return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(error)); } } C2ConstLinearBlock C2LinearBlock::share(size_t offset_, size_t size_, C2Fence fence) { return ConstLinearBlockBuddy(mImpl, C2LinearRange(*this, offset_, size_), fence); } C2BasicLinearBlockPool::C2BasicLinearBlockPool( const std::shared_ptr &allocator) : mAllocator(allocator) { } c2_status_t C2BasicLinearBlockPool::fetchLinearBlock( uint32_t capacity, C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { block->reset(); std::shared_ptr alloc; c2_status_t err = mAllocator->newLinearAllocation(capacity, usage, &alloc); if (err != C2_OK) { return err; } *block = _C2BlockFactory::CreateLinearBlock(alloc); return C2_OK; } struct C2_HIDE C2PooledBlockPoolData : _C2BlockPoolData { virtual type_t getType() const override { return TYPE_BUFFERPOOL; } void getBufferPoolData(std::shared_ptr *data) const { *data = mData; } C2PooledBlockPoolData(const std::shared_ptr &data) : mData(data) {} virtual ~C2PooledBlockPoolData() override {} private: std::shared_ptr mData; }; bool _C2BlockFactory::GetBufferPoolData( const std::shared_ptr &data, std::shared_ptr *bufferPoolData) { if (data && data->getType() == _C2BlockPoolData::TYPE_BUFFERPOOL) { const std::shared_ptr poolData = std::static_pointer_cast(data); poolData->getBufferPoolData(bufferPoolData); return true; } return false; } std::shared_ptr _C2BlockFactory::CreateLinearBlock( const std::shared_ptr &alloc, const std::shared_ptr<_C2BlockPoolData> &data, size_t offset, size_t size) { std::shared_ptr impl = std::make_shared(alloc, data, offset, size); return std::shared_ptr(new C2LinearBlock(impl, *impl)); } std::shared_ptr<_C2BlockPoolData> _C2BlockFactory::GetLinearBlockPoolData( const C2Block1D &block) { if (block.mImpl) { return block.mImpl->poolData(); } return nullptr; } std::shared_ptr _C2BlockFactory::CreateLinearBlock( const C2Handle *handle) { // TODO: get proper allocator? and mutex? static std::unique_ptr sAllocator = std::make_unique(0); std::shared_ptr alloc; if (C2AllocatorIon::isValid(handle)) { c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc); if (err == C2_OK) { std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(alloc); return block; } } return nullptr; } std::shared_ptr _C2BlockFactory::CreateLinearBlock( const C2Handle *cHandle, const std::shared_ptr &data) { // TODO: get proper allocator? and mutex? static std::unique_ptr sAllocator = std::make_unique(0); std::shared_ptr alloc; if (C2AllocatorIon::isValid(cHandle)) { native_handle_t *handle = native_handle_clone(cHandle); if (handle) { c2_status_t err = sAllocator->priorLinearAllocation(handle, &alloc); const std::shared_ptr poolData = std::make_shared(data); if (err == C2_OK && poolData) { // TODO: config params? std::shared_ptr block = _C2BlockFactory::CreateLinearBlock(alloc, poolData); return block; } } } return nullptr; }; /** * Wrapped C2Allocator which is injected to buffer pool on behalf of * C2BlockPool. */ class _C2BufferPoolAllocator : public BufferPoolAllocator { public: _C2BufferPoolAllocator(const std::shared_ptr &allocator) : mAllocator(allocator) {} ~_C2BufferPoolAllocator() override {} ResultStatus allocate(const std::vector ¶ms, std::shared_ptr *alloc, size_t *allocSize) override; bool compatible(const std::vector &newParams, const std::vector &oldParams) override; // Methods for codec2 component (C2BlockPool). /** * Transforms linear allocation parameters for C2Allocator to parameters * for buffer pool. * * @param capacity size of linear allocation * @param usage memory usage pattern for linear allocation * @param params allocation parameters for buffer pool */ void getLinearParams(uint32_t capacity, C2MemoryUsage usage, std::vector *params); /** * Transforms graphic allocation parameters for C2Allocator to parameters * for buffer pool. * * @param width width of graphic allocation * @param height height of graphic allocation * @param format color format of graphic allocation * @param params allocation parameter for buffer pool */ void getGraphicParams(uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, std::vector *params); /** * Transforms an existing native handle to an C2LinearAllcation. * Wrapper to C2Allocator#priorLinearAllocation */ c2_status_t priorLinearAllocation( const C2Handle *handle, std::shared_ptr *c2Allocation); /** * Transforms an existing native handle to an C2GraphicAllcation. * Wrapper to C2Allocator#priorGraphicAllocation */ c2_status_t priorGraphicAllocation( const C2Handle *handle, std::shared_ptr *c2Allocation); private: static constexpr int kMaxIntParams = 5; // large enough number; enum AllocType : uint8_t { ALLOC_NONE = 0, ALLOC_LINEAR, ALLOC_GRAPHIC, }; union AllocParams { struct { AllocType allocType; C2MemoryUsage usage; uint32_t params[kMaxIntParams]; } data; uint8_t array[0]; AllocParams() : data{ALLOC_NONE, {0, 0}, {0}} {} AllocParams(C2MemoryUsage usage, uint32_t capacity) : data{ALLOC_LINEAR, usage, {[0] = capacity}} {} AllocParams( C2MemoryUsage usage, uint32_t width, uint32_t height, uint32_t format) : data{ALLOC_GRAPHIC, usage, {width, height, format}} {} }; const std::shared_ptr mAllocator; }; struct LinearAllocationDtor { LinearAllocationDtor(const std::shared_ptr &alloc) : mAllocation(alloc) {} void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; } const std::shared_ptr mAllocation; }; struct GraphicAllocationDtor { GraphicAllocationDtor(const std::shared_ptr &alloc) : mAllocation(alloc) {} void operator()(BufferPoolAllocation *poolAlloc) { delete poolAlloc; } const std::shared_ptr mAllocation; }; ResultStatus _C2BufferPoolAllocator::allocate( const std::vector ¶ms, std::shared_ptr *alloc, size_t *allocSize) { AllocParams c2Params; memcpy(&c2Params, params.data(), std::min(sizeof(AllocParams), params.size())); c2_status_t status = C2_BAD_VALUE; switch(c2Params.data.allocType) { case ALLOC_NONE: break; case ALLOC_LINEAR: { std::shared_ptr c2Linear; status = mAllocator->newLinearAllocation( c2Params.data.params[0], c2Params.data.usage, &c2Linear); if (status == C2_OK && c2Linear) { BufferPoolAllocation *ptr = new BufferPoolAllocation(c2Linear->handle()); if (ptr) { *alloc = std::shared_ptr( ptr, LinearAllocationDtor(c2Linear)); if (*alloc) { *allocSize = (size_t)c2Params.data.params[0]; return ResultStatus::OK; } delete ptr; } return ResultStatus::NO_MEMORY; } break; } case ALLOC_GRAPHIC: { std::shared_ptr c2Graphic; status = mAllocator->newGraphicAllocation( c2Params.data.params[0], c2Params.data.params[1], c2Params.data.params[2], c2Params.data.usage, &c2Graphic); if (status == C2_OK && c2Graphic) { BufferPoolAllocation *ptr = new BufferPoolAllocation(c2Graphic->handle()); if (ptr) { *alloc = std::shared_ptr( ptr, GraphicAllocationDtor(c2Graphic)); if (*alloc) { *allocSize = c2Params.data.params[0] * c2Params.data.params[1]; return ResultStatus::OK; } delete ptr; } return ResultStatus::NO_MEMORY; } break; } default: break; } return ResultStatus::CRITICAL_ERROR; } bool _C2BufferPoolAllocator::compatible( const std::vector &newParams, const std::vector &oldParams) { AllocParams newAlloc; AllocParams oldAlloc; memcpy(&newAlloc, newParams.data(), std::min(sizeof(AllocParams), newParams.size())); memcpy(&oldAlloc, oldParams.data(), std::min(sizeof(AllocParams), oldParams.size())); // TODO: support not exact matching. e.g) newCapacity < oldCapacity if (newAlloc.data.allocType == oldAlloc.data.allocType && newAlloc.data.usage.expected == oldAlloc.data.usage.expected) { for (int i = 0; i < kMaxIntParams; ++i) { if (newAlloc.data.params[i] != oldAlloc.data.params[i]) { return false; } } return true; } return false; } void _C2BufferPoolAllocator::getLinearParams( uint32_t capacity, C2MemoryUsage usage, std::vector *params) { AllocParams c2Params(usage, capacity); params->assign(c2Params.array, c2Params.array + sizeof(AllocParams)); } void _C2BufferPoolAllocator::getGraphicParams( uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, std::vector *params) { AllocParams c2Params(usage, width, height, format); params->assign(c2Params.array, c2Params.array + sizeof(AllocParams)); } c2_status_t _C2BufferPoolAllocator::priorLinearAllocation( const C2Handle *handle, std::shared_ptr *c2Allocation) { return mAllocator->priorLinearAllocation(handle, c2Allocation); } c2_status_t _C2BufferPoolAllocator::priorGraphicAllocation( const C2Handle *handle, std::shared_ptr *c2Allocation) { return mAllocator->priorGraphicAllocation(handle, c2Allocation); } class C2PooledBlockPool::Impl { public: Impl(const std::shared_ptr &allocator) : mInit(C2_OK), mBufferPoolManager(ClientManager::getInstance()), mAllocator(std::make_shared<_C2BufferPoolAllocator>(allocator)) { if (mAllocator && mBufferPoolManager) { if (mBufferPoolManager->create( mAllocator, &mConnectionId) == ResultStatus::OK) { return; } } mInit = C2_NO_INIT; } ~Impl() { if (mInit == C2_OK) { mBufferPoolManager->close(mConnectionId); } } c2_status_t fetchLinearBlock( uint32_t capacity, C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { block->reset(); if (mInit != C2_OK) { return mInit; } std::vector params; mAllocator->getLinearParams(capacity, usage, ¶ms); std::shared_ptr bufferPoolData; native_handle_t *cHandle = nullptr; ResultStatus status = mBufferPoolManager->allocate( mConnectionId, params, &cHandle, &bufferPoolData); if (status == ResultStatus::OK) { native_handle_t *handle = native_handle_clone(cHandle); if (handle) { std::shared_ptr alloc; std::shared_ptr poolData = std::make_shared(bufferPoolData); c2_status_t err = mAllocator->priorLinearAllocation(handle, &alloc); if (err == C2_OK && poolData && alloc) { *block = _C2BlockFactory::CreateLinearBlock(alloc, poolData, 0, capacity); if (*block) { return C2_OK; } } } return C2_NO_MEMORY; } if (status == ResultStatus::NO_MEMORY) { return C2_NO_MEMORY; } return C2_CORRUPTED; } c2_status_t fetchGraphicBlock( uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, std::shared_ptr *block) { block->reset(); if (mInit != C2_OK) { return mInit; } std::vector params; mAllocator->getGraphicParams(width, height, format, usage, ¶ms); std::shared_ptr bufferPoolData; native_handle_t *cHandle = nullptr; ResultStatus status = mBufferPoolManager->allocate( mConnectionId, params, &cHandle, &bufferPoolData); if (status == ResultStatus::OK) { native_handle_t *handle = native_handle_clone(cHandle); if (handle) { std::shared_ptr alloc; std::shared_ptr poolData = std::make_shared(bufferPoolData); c2_status_t err = mAllocator->priorGraphicAllocation( handle, &alloc); if (err == C2_OK && poolData && alloc) { *block = _C2BlockFactory::CreateGraphicBlock( alloc, poolData, C2Rect(width, height)); if (*block) { return C2_OK; } } } return C2_NO_MEMORY; } if (status == ResultStatus::NO_MEMORY) { return C2_NO_MEMORY; } return C2_CORRUPTED; } ConnectionId getConnectionId() { return mInit != C2_OK ? INVALID_CONNECTIONID : mConnectionId; } private: c2_status_t mInit; const android::sp mBufferPoolManager; ConnectionId mConnectionId; // locally const std::shared_ptr<_C2BufferPoolAllocator> mAllocator; }; C2PooledBlockPool::C2PooledBlockPool( const std::shared_ptr &allocator, const local_id_t localId) : mAllocator(allocator), mLocalId(localId), mImpl(new Impl(allocator)) {} C2PooledBlockPool::~C2PooledBlockPool() { } c2_status_t C2PooledBlockPool::fetchLinearBlock( uint32_t capacity, C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { if (mImpl) { return mImpl->fetchLinearBlock(capacity, usage, block); } return C2_CORRUPTED; } c2_status_t C2PooledBlockPool::fetchGraphicBlock( uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, std::shared_ptr *block) { if (mImpl) { return mImpl->fetchGraphicBlock(width, height, format, usage, block); } return C2_CORRUPTED; } int64_t C2PooledBlockPool::getConnectionId() { if (mImpl) { return mImpl->getConnectionId(); } return 0; } /* ========================================== 2D BLOCK ========================================= */ /** * Implementation that is shared between all 2D blocks and views. * * For blocks' Impl's crop is always the allotted crop, even if it is a sub block. * * For views' Impl's crop is the mapped portion - which for now is always the * allotted crop. */ class C2_HIDE _C2Block2DImpl : public _C2PlanarSectionAspect { public: /** * Impl's crop is always the or part of the allotted crop of the allocation. */ _C2Block2DImpl(const std::shared_ptr &alloc, const std::shared_ptr<_C2BlockPoolData> &poolData = nullptr, const C2Rect &allottedCrop = C2Rect(~0u, ~0u)) : _C2PlanarSectionAspect(alloc.get(), allottedCrop), mAllocation(alloc), mPoolData(poolData) { } virtual ~_C2Block2DImpl() = default; /** returns pool data */ std::shared_ptr<_C2BlockPoolData> poolData() const { return mPoolData; } /** returns native handle */ const C2Handle *handle() const { return mAllocation ? mAllocation->handle() : nullptr; } /** returns the allocator's ID */ C2Allocator::id_t getAllocatorId() const { // BAD_ID can only happen if this Impl class is initialized for a view - never for a block. return mAllocation ? mAllocation->getAllocatorId() : C2Allocator::BAD_ID; } std::shared_ptr getAllocation() const { return mAllocation; } private: std::shared_ptr mAllocation; std::shared_ptr<_C2BlockPoolData> mPoolData; }; class C2_HIDE _C2MappingBlock2DImpl : public _C2Block2DImpl, public std::enable_shared_from_this<_C2MappingBlock2DImpl> { public: using _C2Block2DImpl::_C2Block2DImpl; virtual ~_C2MappingBlock2DImpl() override = default; /** * This class contains the mapped data pointer, and the potential error. */ struct Mapped { private: friend class _C2MappingBlock2DImpl; Mapped(const std::shared_ptr<_C2Block2DImpl> &impl, bool writable, C2Fence *fence __unused) : mImpl(impl), mWritable(writable) { memset(mData, 0, sizeof(mData)); const C2Rect crop = mImpl->crop(); // gralloc requires mapping the whole region of interest as we cannot // map multiple regions mError = mImpl->getAllocation()->map( crop, { C2MemoryUsage::CPU_READ, writable ? C2MemoryUsage::CPU_WRITE : 0 }, nullptr, &mLayout, mData); if (mError != C2_OK) { memset(&mLayout, 0, sizeof(mLayout)); memset(mData, 0, sizeof(mData)); memset(mOffsetData, 0, sizeof(mData)); } else { // TODO: validate plane layout and // adjust data pointers to the crop region's top left corner. // fail if it is not on a subsampling boundary for (size_t planeIx = 0; planeIx < mLayout.numPlanes; ++planeIx) { const uint32_t colSampling = mLayout.planes[planeIx].colSampling; const uint32_t rowSampling = mLayout.planes[planeIx].rowSampling; if (crop.left % colSampling || crop.right() % colSampling || crop.top % rowSampling || crop.bottom() % rowSampling) { // cannot calculate data pointer mImpl->getAllocation()->unmap(mData, crop, nullptr); memset(&mLayout, 0, sizeof(mLayout)); memset(mData, 0, sizeof(mData)); memset(mOffsetData, 0, sizeof(mData)); mError = C2_BAD_VALUE; return; } mOffsetData[planeIx] = mData[planeIx] + (ssize_t)crop.left * mLayout.planes[planeIx].colInc + (ssize_t)crop.top * mLayout.planes[planeIx].rowInc; } } } explicit Mapped(c2_status_t error) : mImpl(nullptr), mWritable(false), mError(error) { // CHECK(error != C2_OK); memset(&mLayout, 0, sizeof(mLayout)); memset(mData, 0, sizeof(mData)); memset(mOffsetData, 0, sizeof(mData)); } public: ~Mapped() { if (mData[0] != nullptr) { mImpl->getAllocation()->unmap(mData, mImpl->crop(), nullptr); } } /** returns mapping status */ c2_status_t error() const { return mError; } /** returns data pointer */ uint8_t *const *data() const { return mOffsetData; } /** returns the plane layout */ C2PlanarLayout layout() const { return mLayout; } /** returns whether the mapping is writable */ bool writable() const { return mWritable; } private: const std::shared_ptr<_C2Block2DImpl> mImpl; bool mWritable; c2_status_t mError; uint8_t *mData[C2PlanarLayout::MAX_NUM_PLANES]; uint8_t *mOffsetData[C2PlanarLayout::MAX_NUM_PLANES]; C2PlanarLayout mLayout; }; /** * Maps the allotted region. * * If already mapped and it is currently in use, returns the existing mapping. * If fence is provided, an acquire fence is stored there. */ std::shared_ptr map(bool writable, C2Fence *fence) { std::lock_guard lock(mMappedLock); std::shared_ptr existing = mMapped.lock(); if (!existing) { existing = std::shared_ptr(new Mapped(shared_from_this(), writable, fence)); mMapped = existing; } else { // if we mapped the region read-only, we cannot remap it read-write if (writable && !existing->writable()) { existing = std::shared_ptr(new Mapped(C2_CANNOT_DO)); } if (fence != nullptr) { *fence = C2Fence(); } } return existing; } private: std::weak_ptr mMapped; std::mutex mMappedLock; }; class C2_HIDE _C2MappedBlock2DImpl : public _C2Block2DImpl { public: _C2MappedBlock2DImpl(const _C2Block2DImpl &impl, std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping) : _C2Block2DImpl(impl), mMapping(mapping) { } virtual ~_C2MappedBlock2DImpl() override = default; std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping() const { return mMapping; } private: std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mMapping; }; /** * Block implementation. */ class C2Block2D::Impl : public _C2MappingBlock2DImpl { public: using _C2MappingBlock2DImpl::_C2MappingBlock2DImpl; virtual ~Impl() override = default; }; const C2Handle *C2Block2D::handle() const { return mImpl->handle(); } C2Allocator::id_t C2Block2D::getAllocatorId() const { return mImpl->getAllocatorId(); } C2Block2D::C2Block2D(std::shared_ptr impl, const _C2PlanarSectionAspect §ion) // always clamp subsection to parent (impl) crop for safety : _C2PlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) { } /** * Graphic view implementation. * * range of Impl is the mapped range of the underlying allocation. range of View is the current * crop. */ class C2GraphicView::Impl : public _C2MappedBlock2DImpl { public: using _C2MappedBlock2DImpl::_C2MappedBlock2DImpl; virtual ~Impl() override = default; }; C2GraphicView::C2GraphicView(std::shared_ptr impl, const _C2PlanarSectionAspect §ion) : _C2EditablePlanarSectionAspect(impl.get(), section.crop()), mImpl(impl) { } const uint8_t *const *C2GraphicView::data() const { return mImpl->mapping()->data(); } uint8_t *const *C2GraphicView::data() { return mImpl->mapping()->data(); } const C2PlanarLayout C2GraphicView::layout() const { return mImpl->mapping()->layout(); } const C2GraphicView C2GraphicView::subView(const C2Rect &rect) const { return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect)); } C2GraphicView C2GraphicView::subView(const C2Rect &rect) { return C2GraphicView(mImpl, C2PlanarSection(*mImpl, rect)); } c2_status_t C2GraphicView::error() const { return mImpl->mapping()->error(); } /** * Const graphic block implementation. */ C2ConstGraphicBlock::C2ConstGraphicBlock( std::shared_ptr impl, const _C2PlanarSectionAspect §ion, C2Fence fence) : C2Block2D(impl, section), mFence(fence) { } C2Acquirable C2ConstGraphicBlock::map() const { C2Fence fence; std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping = mImpl->map(false /* writable */, &fence); std::shared_ptr gvi = std::shared_ptr(new GraphicViewBuddy::Impl(*mImpl, mapping)); return AcquirableConstGraphicViewBuddy( mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop()))); } C2ConstGraphicBlock C2ConstGraphicBlock::subBlock(const C2Rect &rect) const { return C2ConstGraphicBlock(mImpl, C2PlanarSection(*mImpl, crop().intersect(rect)), mFence); } /** * Graphic block implementation. */ C2GraphicBlock::C2GraphicBlock( std::shared_ptr impl, const _C2PlanarSectionAspect §ion) : C2Block2D(impl, section) { } C2Acquirable C2GraphicBlock::map() { C2Fence fence; std::shared_ptr<_C2MappingBlock2DImpl::Mapped> mapping = mImpl->map(true /* writable */, &fence); std::shared_ptr gvi = std::shared_ptr(new GraphicViewBuddy::Impl(*mImpl, mapping)); return AcquirableGraphicViewBuddy( mapping->error(), fence, GraphicViewBuddy(gvi, C2PlanarSection(*mImpl, crop()))); } C2ConstGraphicBlock C2GraphicBlock::share(const C2Rect &crop, C2Fence fence) { return ConstGraphicBlockBuddy(mImpl, C2PlanarSection(*mImpl, crop), fence); } /** * Basic block pool implementations. */ C2BasicGraphicBlockPool::C2BasicGraphicBlockPool( const std::shared_ptr &allocator) : mAllocator(allocator) {} c2_status_t C2BasicGraphicBlockPool::fetchGraphicBlock( uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage, std::shared_ptr *block /* nonnull */) { block->reset(); std::shared_ptr 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; } std::shared_ptr _C2BlockFactory::CreateGraphicBlock( const std::shared_ptr &alloc, const std::shared_ptr<_C2BlockPoolData> &data, const C2Rect &allottedCrop) { std::shared_ptr impl = std::make_shared(alloc, data, allottedCrop); return std::shared_ptr(new C2GraphicBlock(impl, *impl)); } std::shared_ptr<_C2BlockPoolData> _C2BlockFactory::GetGraphicBlockPoolData( const C2Block2D &block) { if (block.mImpl) { return block.mImpl->poolData(); } return nullptr; } std::shared_ptr _C2BlockFactory::CreateGraphicBlock( const C2Handle *cHandle, const std::shared_ptr &data) { // TODO: get proper allocator? and mutex? static std::unique_ptr sAllocator = std::make_unique(0); std::shared_ptr alloc; if (C2AllocatorGralloc::isValid(cHandle)) { native_handle_t *handle = native_handle_clone(cHandle); if (handle) { c2_status_t err = sAllocator->priorGraphicAllocation(handle, &alloc); const std::shared_ptr poolData = std::make_shared(data); if (err == C2_OK && poolData) { // TODO: config setup? std::shared_ptr block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); return block; } } } return nullptr; }; /* ========================================== BUFFER ========================================= */ class C2BufferData::Impl { public: explicit Impl(const std::vector &blocks) : mType(blocks.size() == 1 ? LINEAR : LINEAR_CHUNKS), mLinearBlocks(blocks) { } explicit Impl(const std::vector &blocks) : mType(blocks.size() == 1 ? GRAPHIC : GRAPHIC_CHUNKS), mGraphicBlocks(blocks) { } type_t type() const { return mType; } const std::vector &linearBlocks() const { return mLinearBlocks; } const std::vector &graphicBlocks() const { return mGraphicBlocks; } private: type_t mType; std::vector mLinearBlocks; std::vector mGraphicBlocks; }; C2BufferData::C2BufferData(const std::vector &blocks) : mImpl(new Impl(blocks)) {} C2BufferData::C2BufferData(const std::vector &blocks) : mImpl(new Impl(blocks)) {} C2BufferData::type_t C2BufferData::type() const { return mImpl->type(); } const std::vector C2BufferData::linearBlocks() const { return mImpl->linearBlocks(); } const std::vector C2BufferData::graphicBlocks() const { return mImpl->graphicBlocks(); } class C2Buffer::Impl { public: Impl(C2Buffer *thiz, const std::vector &blocks) : mThis(thiz), mData(blocks) {} Impl(C2Buffer *thiz, const std::vector &blocks) : mThis(thiz), mData(blocks) {} ~Impl() { for (const auto &pair : mNotify) { pair.first(mThis, pair.second); } } const C2BufferData &data() const { return mData; } c2_status_t registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) { auto it = std::find_if( mNotify.begin(), mNotify.end(), [onDestroyNotify, arg] (const auto &pair) { return pair.first == onDestroyNotify && pair.second == arg; }); if (it != mNotify.end()) { return C2_DUPLICATE; } mNotify.emplace_back(onDestroyNotify, arg); return C2_OK; } c2_status_t unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) { auto it = std::find_if( mNotify.begin(), mNotify.end(), [onDestroyNotify, arg] (const auto &pair) { return pair.first == onDestroyNotify && pair.second == arg; }); if (it == mNotify.end()) { return C2_NOT_FOUND; } mNotify.erase(it); return C2_OK; } std::vector> info() const { std::vector> result(mInfos.size()); std::transform( mInfos.begin(), mInfos.end(), result.begin(), [] (const auto &elem) { return elem.second; }); return result; } c2_status_t setInfo(const std::shared_ptr &info) { // To "update" you need to erase the existing one if any, and then insert. (void) mInfos.erase(info->coreIndex()); (void) mInfos.insert({ info->coreIndex(), info }); return C2_OK; } bool hasInfo(C2Param::Type index) const { return mInfos.count(index.coreIndex()) > 0; } std::shared_ptr getInfo(C2Param::Type index) const { auto it = mInfos.find(index.coreIndex()); if (it == mInfos.end()) { return nullptr; } return std::const_pointer_cast(it->second); } std::shared_ptr removeInfo(C2Param::Type index) { auto it = mInfos.find(index.coreIndex()); if (it == mInfos.end()) { return nullptr; } std::shared_ptr ret = it->second; (void) mInfos.erase(it); return ret; } private: C2Buffer * const mThis; BufferDataBuddy mData; std::map> mInfos; std::list> mNotify; }; C2Buffer::C2Buffer(const std::vector &blocks) : mImpl(new Impl(this, blocks)) {} C2Buffer::C2Buffer(const std::vector &blocks) : mImpl(new Impl(this, blocks)) {} const C2BufferData C2Buffer::data() const { return mImpl->data(); } c2_status_t C2Buffer::registerOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) { return mImpl->registerOnDestroyNotify(onDestroyNotify, arg); } c2_status_t C2Buffer::unregisterOnDestroyNotify(OnDestroyNotify onDestroyNotify, void *arg) { return mImpl->unregisterOnDestroyNotify(onDestroyNotify, arg); } const std::vector> C2Buffer::info() const { return mImpl->info(); } c2_status_t C2Buffer::setInfo(const std::shared_ptr &info) { return mImpl->setInfo(info); } bool C2Buffer::hasInfo(C2Param::Type index) const { return mImpl->hasInfo(index); } std::shared_ptr C2Buffer::getInfo(C2Param::Type index) const { return mImpl->getInfo(index); } std::shared_ptr C2Buffer::removeInfo(C2Param::Type index) { return mImpl->removeInfo(index); } // static std::shared_ptr C2Buffer::CreateLinearBuffer(const C2ConstLinearBlock &block) { return std::shared_ptr(new C2Buffer({ block })); } // static std::shared_ptr C2Buffer::CreateGraphicBuffer(const C2ConstGraphicBlock &block) { return std::shared_ptr(new C2Buffer({ block })); }