Hi Will,
I forgot to respond to this. If its still helpful, here is some pseudo code which works for me on Rpi4 with Bullseye.
Basically, you replace the DMA file descriptors libcamera allocates with one allocated manually from the CMA heap. The UniqueFd/SharedFD classes (part of libcamera) are memory managed wrappers of filedescriptors so setting new ones should in theory delete the previous ones automatically for you (when they go out scope - same as std::shared_ptr and std::unique_ptr)
After calling libcamera::FrameBufferAllocator::allocate(pStream) in your libcamera startup code, you'll want to find the DMA bufs for planes in the framebuffers, and replace them:
Here is a class for allocating DMA bufs using the CMA heap:
And then make sure you wrap any memory mapped CPU access with DMA_BUF_IOCTL_SYNC something like this:
I forgot to respond to this. If its still helpful, here is some pseudo code which works for me on Rpi4 with Bullseye.
Basically, you replace the DMA file descriptors libcamera allocates with one allocated manually from the CMA heap. The UniqueFd/SharedFD classes (part of libcamera) are memory managed wrappers of filedescriptors so setting new ones should in theory delete the previous ones automatically for you (when they go out scope - same as std::shared_ptr and std::unique_ptr)
After calling libcamera::FrameBufferAllocator::allocate(pStream) in your libcamera startup code, you'll want to find the DMA bufs for planes in the framebuffers, and replace them:
Code:
void ReplaceDMABufs(libcamera::FrameBuffer framebuffer){auto planes = framebuffer.planes();foreach(auto plane : planes){const auto& DMABufferName = std::string("dma") + std::to_string(BufferIndex++); // <-- Not really importantconst auto AlignedPlaneSize = J3Maths::RoundUp(Plane.length, 4096); // TODO: obtain alignment size programmaticallyauto DMAfd = libcamera::SharedFD(DMAHeap->Allocate(DMABufferName, AlignedPlaneSize));if (!DMAfd.isValid())error...Plane.fd = DMAfd;}}Code:
class DMAHeap{public:DMAHeap(){static const std::vector<std::string> HeapNames = { "/dev/dma_heap/linux,cma", "/dev/dma_heap/reserved" };for (const auto& HeapName : HeapNames){auto Ret = ::open(HeapName.c_str(), O_RDWR, 0);if (Ret < 0) {Ret = errno;J3Log("Failed to open %s, error: %s", HeapName.c_str(), strerror(Ret));continue;}m_Handle = libcamera::UniqueFD(Ret);break;}if (!m_Handle.isValid())J3Log("Could not open any dmaHeap device");}~DMAHeap() = default;libcamera::UniqueFD Allocate(const std::string& Name, std::size_t Size){if (Name.empty())return {};struct dma_heap_allocation_data alloc = {};alloc.len = Size;alloc.fd_flags = O_CLOEXEC | O_RDWR;auto ret = ::ioctl(m_Handle.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);if (ret < 0) {J3Log("dmaHeap allocation failure for %s", Name.c_str());return {};}libcamera::UniqueFD allocFd(alloc.fd);ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, Name.c_str());if (ret < 0) {J3Log("dmaHeap naming failure for %s", Name.c_str());return {};}return allocFd;}private:libcamera::UniqueFD m_Handle;};Code:
{...LockMappedBuffer(...);DoSomething(pMappedMemory);UnlockMappedBuffer(...);...}Code:
static inline void LockMappedBuffer(const libcamera::FrameBuffer::Plane& BufferPlane, PC::IComputeBuffer::eMemoryHostAccess MemoryAccess = PC::IComputeBuffer::eMemoryHostAccess::READ_ONLY){dma_buf_sync syncStartFlags = { 0 };switch (MemoryAccess){case PC::IComputeBuffer::eMemoryHostAccess::READ_WRITE:syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;break;case PC::IComputeBuffer::eMemoryHostAccess::READ_ONLY:syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ;break;case PC::IComputeBuffer::eMemoryHostAccess::WRITE_ONLY:syncStartFlags.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE;break;default:J3AssertMsg(false, "Invalid memory access mode for Libcamera::LockMappedBuffer");break;}errno = 0;auto syncStartResult = ioctl(BufferPlane.fd.get(), DMA_BUF_IOCTL_SYNC, &syncStartFlags);while (syncStartResult != 0 && (errno == EINTR || errno == EAGAIN)) {errno = 0;syncStartResult = ioctl(BufferPlane.fd.get(), DMA_BUF_IOCTL_SYNC, &syncStartFlags);}J3Assert(syncStartResult == 0);}static inline void UnlockMappedBuffer(const libcamera::FrameBuffer::Plane& BufferPlane, PC::IComputeBuffer::eMemoryHostAccess MemoryAccess = PC::IComputeBuffer::eMemoryHostAccess::READ_ONLY){dma_buf_sync syncEndFlags = { 0 };switch (MemoryAccess){case PC::IComputeBuffer::eMemoryHostAccess::READ_WRITE:syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;break;case PC::IComputeBuffer::eMemoryHostAccess::READ_ONLY:syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ;break;case PC::IComputeBuffer::eMemoryHostAccess::WRITE_ONLY:syncEndFlags.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE;break;default:J3AssertMsg(false, "Invalid memory access mode for Libcamera::UnlockMappedBuffer");break;}errno = 0;auto syncEndResult = ioctl(BufferPlane.fd.get(), DMA_BUF_IOCTL_SYNC, &syncEndFlags);while (syncEndResult != 0 && (errno == EINTR || errno == EAGAIN)) {errno = 0;syncEndResult = ioctl(BufferPlane.fd.get(), DMA_BUF_IOCTL_SYNC, &syncEndFlags);}J3Assert(syncEndResult == 0);}Statistics: Posted by PeartreeStudios — Thu Jan 25, 2024 12:44 am