blob: 75d7f60fdadc034ffa2017479c8aacf651232844 [file] [edit]
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2023, Google Inc.
*
* info_frame.cpp - InfoFrame and InfoFramePool
*/
#include <sys/mman.h>
#include <unistd.h>
#include <unordered_set>
#include "libcamera/internal/dma_heaps.h"
#include "libcamera/internal/formats.h"
#include <libcamera/internal/info_frame.h>
namespace libcamera {
LOG_DEFINE_CATEGORY(InfoFrame)
InfoFrame::InfoFrame() = default;
InfoFrame::InfoFrame(const PixelFormat &format, const Size &size, FrameBuffer *buffer,
unsigned int strideAlign, unsigned int scanAlign)
: size_(size), format_(format), buffer_(buffer), strideAlign_(strideAlign), scanAlign_(scanAlign)
{
numPlanes_ = buffer->planes().size();
for (size_t i = 0; i < 3; i++)
planes_[i].address = nullptr;
}
void InfoFrame::setAddress(unsigned int plane, uint8_t *address)
{
if (plane < numPlanes_)
planes_[plane].address = address;
}
uint8_t *InfoFrame::address(unsigned int plane) const
{
if (plane < numPlanes_)
return planes_[plane].address;
return nullptr;
}
InfoFramePool::InfoFramePool() = default;
InfoFramePool::~InfoFramePool()
{
release();
}
void InfoFramePool::release()
{
if (unmap())
LOG(InfoFrame, Error) << "Failed to unmap mapped buffers";
pool_.release();
}
int InfoFramePool::setBuffers(const PixelFormat &format, const Size &size,
std::vector<std::unique_ptr<FrameBuffer>> &buffers,
unsigned int strideAlign, unsigned int scanAlign)
{
if (unmap())
LOG(InfoFrame, Error) << "Failed to unmap buffers";
size_ = size;
format_ = format;
pool_.setData(buffers);
strideAlign_ = strideAlign;
scanAlign_ = scanAlign;
return 0;
}
std::vector<int> InfoFramePool::collectFds()
{
std::unordered_set<int> fds;
for (auto &buffer : pool_.content()) {
for (auto &plane : buffer->planes()) {
const int fd = plane.fd.get();
if (fd < 0) {
LOG(InfoFrame, Error) << "Invalid fd: " << fd;
continue;
}
fds.insert(fd);
}
}
return std::vector<int>(fds.begin(), fds.end());
}
int InfoFramePool::mmap()
{
if (!mappedBuffers_.empty())
return 0;
for (auto &buffer : pool_.content()) {
for (const FrameBuffer::Plane &plane : buffer->planes()) {
const int fd = plane.fd.get();
if (mappedBuffers_.find(fd) == mappedBuffers_.end()) {
const size_t length = lseek(fd, 0, SEEK_END);
mappedBuffers_[fd] = MappedBufferInfo{ nullptr, length };
}
}
}
for (auto &[fd, info] : mappedBuffers_) {
void *address = ::mmap(nullptr, info.dmabufLength,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
info.address = static_cast<uint8_t *>(address);
}
return 0;
}
int InfoFramePool::unmap()
{
if (mappedBuffers_.empty())
return 0;
for (auto &[_, info] : mappedBuffers_)
munmap(info.address, info.dmabufLength);
mappedBuffers_.clear();
return 0;
}
InfoFrame InfoFramePool::get()
{
FrameBuffer *buffer = pool_.get();
InfoFrame info(format_, size_, buffer, strideAlign_, scanAlign_);
for (size_t i = 0; i < buffer->planes().size(); i++) {
const int fd = buffer->planes()[i].fd.get();
const unsigned int offset = buffer->planes()[i].offset;
if (mappedBuffers_.count(fd))
info.setAddress(i, mappedBuffers_[fd].address + offset);
}
return info;
}
void InfoFramePool::put(InfoFrame &info)
{
pool_.put(info.buffer());
}
void InfoFramePool::fetch(SharedMailBox<InfoFrame> &mailBox)
{
auto recycler = [this](InfoFrame &info) {
this->put(info);
};
mailBox->put(get(), recycler);
}
int InfoFramePool::createBuffers(DmaHeap *dmaHeap,
const PixelFormat &format, const Size &size,
unsigned int count, DmaHeap::Type type,
unsigned int strideAlign, unsigned scanAlign)
{
release();
const PixelFormatInfo &info = PixelFormatInfo::info(format);
uint32_t bufferSize = 0;
for (unsigned int i = 0; i < info.numPlanes(); i++)
bufferSize += info.planeSize(size, i, strideAlign, scanAlign);
std::vector<std::unique_ptr<FrameBuffer>> buffers;
buffers.reserve(count);
for (unsigned int i = 0; i < count; i++) {
SharedFD fd(dmaHeap->alloc(bufferSize, type));
if (!fd.isValid()) {
buffers.clear();
return -EBUSY;
}
uint32_t offset = 0;
std::vector<FrameBuffer::Plane> planes;
for (unsigned int j = 0; j < info.numPlanes(); j++) {
FrameBuffer::Plane plane;
plane.fd = fd;
plane.offset = offset;
plane.length = info.planeSize(size, j, strideAlign, scanAlign);
plane.stride = info.stride(size.width, j, strideAlign);
planes.emplace_back(plane);
offset += plane.length;
}
buffers.emplace_back(std::make_unique<FrameBuffer>(planes));
}
return setBuffers(format, size, buffers, strideAlign, scanAlign);
}
int LazyInfoFramePool::setFormat(DmaHeap *dmaHeap, const PixelFormat &format,
const Size &size,
DmaHeap::Type type,
unsigned int strideAlign, unsigned scanAlign)
{
dmaHeap_ = dmaHeap;
type_ = type;
size_ = size;
format_ = format;
strideAlign_ = strideAlign;
scanAlign_ = scanAlign;
return 0;
}
void LazyInfoFramePool::release()
{
allocatedBuffers_.clear();
}
void LazyInfoFramePool::fetch(SharedMailBox<InfoFrame> &mailBox)
{
auto recycler = [this](InfoFrame &info) {
this->put(info);
};
mailBox->put(get(), recycler);
}
InfoFrame LazyInfoFramePool::get()
{
const PixelFormatInfo &info = PixelFormatInfo::info(format_);
uint32_t bufferSize = 0;
for (unsigned int i = 0; i < info.numPlanes(); i++)
bufferSize += info.planeSize(size_, i, strideAlign_, scanAlign_);
SharedFD fd(dmaHeap_->alloc(bufferSize, type_));
if (!fd.isValid()) {
LOG(InfoFrame, Fatal) << "fail to allocate dma buf, size " << bufferSize;
}
uint32_t offset = 0;
std::vector<FrameBuffer::Plane> planes;
for (unsigned int j = 0; j < info.numPlanes(); j++) {
FrameBuffer::Plane plane;
plane.fd = fd;
plane.offset = offset;
plane.length = info.planeSize(size_, j, strideAlign_, scanAlign_);
plane.stride = info.stride(size_.width, j, strideAlign_);
planes.emplace_back(plane);
offset += plane.length;
}
std::scoped_lock lock(mutex_);
allocatedBuffers_.emplace_back(std::make_unique<FrameBuffer>(planes));
FrameBuffer *buffer = allocatedBuffers_.back().get();
InfoFrame infoFrame(format_, size_, buffer, strideAlign_, scanAlign_);
return infoFrame;
}
void LazyInfoFramePool::put(InfoFrame &frameInfo)
{
std::scoped_lock lock(mutex_);
for (auto iter = allocatedBuffers_.begin();
iter != allocatedBuffers_.end(); iter++) {
if (iter->get() == frameInfo.buffer()) {
allocatedBuffers_.erase(iter);
return;
}
}
LOG(InfoFrame, Fatal) << "Unknown buffer returned to LazyInfoFramePool";
}
int ElasticInfoFramePool::setFormat(DmaHeap *dmaHeap, const PixelFormat &format,
const Size &size, uint32_t minCount,
DmaHeap::Type type,
unsigned int strideAlign, unsigned scanAlign)
{
release();
if (minCount > 0) {
const PixelFormatInfo &info = PixelFormatInfo::info(format);
uint32_t bufferSize = 0;
for (unsigned int i = 0; i < info.numPlanes(); i++)
bufferSize += info.planeSize(size, i, strideAlign, scanAlign);
std::vector<std::unique_ptr<FrameBuffer>> buffers;
buffers.reserve(minCount);
for (unsigned int i = 0; i < minCount; i++) {
SharedFD fd(dmaHeap->alloc(bufferSize, type));
if (!fd.isValid()) {
buffers.clear();
return -EBUSY;
}
uint32_t offset = 0;
std::vector<FrameBuffer::Plane> planes;
for (unsigned int j = 0; j < info.numPlanes(); j++) {
FrameBuffer::Plane plane;
plane.fd = fd;
plane.offset = offset;
plane.length = info.planeSize(size, j, strideAlign, scanAlign);
plane.stride = info.stride(size.width, j, strideAlign);
planes.emplace_back(plane);
offset += plane.length;
}
buffers.emplace_back(std::make_unique<FrameBuffer>(planes));
}
pool_.setData(buffers);
MutexLocker lock(mutex_);
idleCnt_ = minCount;
}
dmaHeap_ = dmaHeap;
type_ = type;
size_ = size;
minCount_ = minCount;
format_ = format;
strideAlign_ = strideAlign;
scanAlign_ = scanAlign;
return 0;
}
void ElasticInfoFramePool::release()
{
pool_.release();
minCount_ = 0;
MutexLocker lock(mutex_);
idleCnt_ = 0;
}
void ElasticInfoFramePool::releaseElastic()
{
size_t num = pool_.size() - minCount_;
pool_.drop(num);
MutexLocker lock(mutex_);
if (idleCnt_ < num)
LOG(InfoFrame, Fatal) << "Not enough idle buffers";
idleCnt_ -= num;
}
void ElasticInfoFramePool::fetch(SharedMailBox<InfoFrame> &mailBox)
{
auto recycler = [this](InfoFrame &info) {
this->put(info);
};
mailBox->put(get(), recycler);
}
InfoFrame ElasticInfoFramePool::get()
{
{
MutexLocker lock(mutex_);
if (idleCnt_ > 0) {
--idleCnt_;
return InfoFrame(format_, size_, pool_.get(),
strideAlign_, scanAlign_);
}
}
const PixelFormatInfo &info = PixelFormatInfo::info(format_);
uint32_t bufferSize = 0;
for (unsigned int i = 0; i < info.numPlanes(); i++)
bufferSize += info.planeSize(size_, i, strideAlign_, scanAlign_);
SharedFD fd(dmaHeap_->alloc(bufferSize, type_));
if (!fd.isValid()) {
LOG(InfoFrame, Fatal) << "fail to allocate dma buf, size " << bufferSize;
}
uint32_t offset = 0;
std::vector<FrameBuffer::Plane> planes;
for (unsigned int j = 0; j < info.numPlanes(); j++) {
FrameBuffer::Plane plane;
plane.fd = fd;
plane.offset = offset;
plane.length = info.planeSize(size_, j, strideAlign_, scanAlign_);
plane.stride = info.stride(size_.width, j, strideAlign_);
planes.emplace_back(plane);
offset += plane.length;
}
FrameBuffer *buffer = pool_.addAndGet(std::make_unique<FrameBuffer>(planes));
InfoFrame infoFrame(format_, size_, buffer, strideAlign_, scanAlign_);
return infoFrame;
}
void ElasticInfoFramePool::put(InfoFrame &frameInfo)
{
pool_.put(frameInfo.buffer());
MutexLocker lock(mutex_);
++idleCnt_;
}
} /* namespace libcamera */