Files
2026-07-13 13:33:03 +08:00

267 lines
9.0 KiB
C++
Executable File

#include <MNN/MNNDefine.h>
#include "executor/allocator.h"
#include "backend/api/neuron/NeuronAdapterShim.h"
#include "common/cpp11_compat.h"
#include "common/logging.h"
#include <android/hardware_buffer.h>
#include <fcntl.h>
#include <linux/dma-heap.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#ifdef USE_USDK_BACKEND
static constexpr bool kUseUsdkBackend = true;
#else
static constexpr bool kUseUsdkBackend = false;
#endif
namespace mtk {
/*
Ref from
https://android.googlesource.com/platform/external/libchrome/+/refs/tags/aml_res_331314010/base/android/android_hardware_buffer_compat.h
*/
using PFAHardwareBuffer_allocate = int (*)(const AHardwareBuffer_Desc* desc,
AHardwareBuffer** outBuffer);
using PFAHardwareBuffer_acquire = void (*)(AHardwareBuffer* buffer);
using PFAHardwareBuffer_describe = void (*)(const AHardwareBuffer* buffer,
AHardwareBuffer_Desc* outDesc);
using PFAHardwareBuffer_lock = int (*)(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** outVirtualAddress);
using PFAHardwareBuffer_recvHandleFromUnixSocket =
int (*)(int socketFd, AHardwareBuffer** outBuffer);
using PFAHardwareBuffer_release = void (*)(AHardwareBuffer* buffer);
using PFAHardwareBuffer_sendHandleToUnixSocket =
int (*)(const AHardwareBuffer* buffer, int socketFd);
using PFAHardwareBuffer_unlock = int (*)(AHardwareBuffer* buffer,
int32_t* fence);
class AndroidHardwareBufferCompat {
public:
bool IsSupportAvailable() const {
return mIsSupportAvailable;
}
AndroidHardwareBufferCompat();
int Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer);
void Acquire(AHardwareBuffer* buffer);
void Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc);
int Lock(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** out_virtual_address);
int RecvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer);
void Release(AHardwareBuffer* buffer);
int SendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd);
int Unlock(AHardwareBuffer* buffer, int32_t* fence);
private:
bool mIsSupportAvailable = true;
PFAHardwareBuffer_allocate allocate_;
PFAHardwareBuffer_acquire acquire_;
PFAHardwareBuffer_describe describe_;
PFAHardwareBuffer_lock lock_;
PFAHardwareBuffer_recvHandleFromUnixSocket recv_handle_;
PFAHardwareBuffer_release release_;
PFAHardwareBuffer_sendHandleToUnixSocket send_handle_;
PFAHardwareBuffer_unlock unlock_;
};
AndroidHardwareBufferCompat::AndroidHardwareBufferCompat() {
// TODO(klausw): If the Chromium build requires __ANDROID_API__ >= 26 at some
// point in the future, we could directly use the global functions instead of
// dynamic loading. However, since this would be incompatible with pre-Oreo
// devices, this is unlikely to happen in the foreseeable future, so just
// unconditionally use dynamic loading.
// cf. base/android/linker/modern_linker_jni.cc
void* main_dl_handle = dlopen(nullptr, RTLD_NOW);
*reinterpret_cast<void**>(&allocate_) =
dlsym(main_dl_handle, "AHardwareBuffer_allocate");
if(nullptr == allocate_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&acquire_) =
dlsym(main_dl_handle, "AHardwareBuffer_acquire");
if(nullptr == acquire_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&describe_) =
dlsym(main_dl_handle, "AHardwareBuffer_describe");
if(nullptr == describe_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&lock_) =
dlsym(main_dl_handle, "AHardwareBuffer_lock");
if(nullptr == lock_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&recv_handle_) =
dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket");
if(nullptr == recv_handle_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&release_) =
dlsym(main_dl_handle, "AHardwareBuffer_release");
if(nullptr == release_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&send_handle_) =
dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket");
if(nullptr == send_handle_){
mIsSupportAvailable = false;
}
*reinterpret_cast<void**>(&unlock_) =
dlsym(main_dl_handle, "AHardwareBuffer_unlock");
if(nullptr == unlock_){
mIsSupportAvailable = false;
}
}
int AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc,
AHardwareBuffer** out_buffer) {
return allocate_(desc, out_buffer);
}
void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) {
acquire_(buffer);
}
void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer,
AHardwareBuffer_Desc* out_desc) {
describe_(buffer, out_desc);
}
int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer,
uint64_t usage,
int32_t fence,
const ARect* rect,
void** out_virtual_address) {
return lock_(buffer, usage, fence, rect, out_virtual_address);
}
int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket(
int socket_fd,
AHardwareBuffer** out_buffer) {
return recv_handle_(socket_fd, out_buffer);
}
void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) {
release_(buffer);
}
int AndroidHardwareBufferCompat::SendHandleToUnixSocket(
const AHardwareBuffer* buffer,
int socket_fd) {
return send_handle_(buffer, socket_fd);
}
int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer,
int32_t* fence) {
return unlock_(buffer, fence);
}
Allocator::~Allocator() {
// NOTE: Allocator::releaseAll() needs to be called before reaching this dtor.
}
IOBuffer Allocator::allocate(const size_t size, const bool disableTracking) {
IOBuffer ioBuffer;
ioBuffer.sizeBytes = size;
ioBuffer.usedSizeBytes = size;
if (!allocateMemory(ioBuffer)) {
return IOBuffer(); // Empty buffer
}
if (disableTracking) {
// Remove from allocation tracking so that releaseAll() won't release this buffer.
mAllocatedBuffers.erase(ioBuffer.buffer);
}
return ioBuffer;
}
bool Allocator::release(void* addr) {
if (mAllocatedBuffers.find(addr) == mAllocatedBuffers.end()) {
LOG(ERROR) << "Unable to release the memory at " << addr << " by the allocator instance "
<< this;
return false;
}
auto& ioBuffer = mAllocatedBuffers.at(addr);
return releaseMemory(ioBuffer);
}
void Allocator::releaseAll() {
while (!mAllocatedBuffers.empty()) {
auto& bufferPair = *mAllocatedBuffers.begin();
auto& addr = bufferPair.first;
auto& ioBuffer = bufferPair.second;
releaseMemory(ioBuffer);
}
}
AhwBufferAllocator::AhwBufferAllocator() {
mFuncPtr.reset(new AndroidHardwareBufferCompat);
if (!mFuncPtr->IsSupportAvailable()) {
MNN_ERROR("[MNN:AhwBufferAllocator] Don't has hardware funciton ptr\n");
}
}
AhwBufferAllocator::~AhwBufferAllocator() {
releaseAll();
}
bool AhwBufferAllocator::allocateMemory(IOBuffer& ioBuffer) {
const auto& size = ioBuffer.sizeBytes;
auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY;
AHardwareBuffer_Desc ahwbDesc = {
.width = static_cast<uint32_t>(size),
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = usage,
.stride = static_cast<uint32_t>(size),
};
AHardwareBuffer* ahwbHandle = nullptr;
if (mFuncPtr->Allocate(&ahwbDesc, &ahwbHandle) != 0) {
LOG(ERROR) << "Allocate AHardwareBuffer fail";
return false;
}
void* bufferAddr = nullptr;
mFuncPtr->Lock(ahwbHandle, usage, -1, NULL, &bufferAddr);
ioBuffer.ahwbHandle = ahwbHandle;
ioBuffer.buffer = bufferAddr;
// USDK requires NeuronMemory
if (kUseUsdkBackend) {
NeuronMemory_createFromAHardwareBuffer(ioBuffer.ahwbHandle, &ioBuffer.neuronMemory);
}
// Add to allocated buffer record
mAllocatedBuffers.emplace(bufferAddr, ioBuffer);
return true;
}
bool AhwBufferAllocator::releaseMemory(IOBuffer& ioBuffer) {
if (!ioBuffer.isAllocated()) {
return true; // Do nothing
}
if (ioBuffer.neuronMemory != nullptr) {
NeuronMemory_free(ioBuffer.neuronMemory);
}
mFuncPtr->Unlock(ioBuffer.ahwbHandle, nullptr);
mFuncPtr->Release(ioBuffer.ahwbHandle);
const auto bufferAddr = ioBuffer.buffer;
ioBuffer.buffer = nullptr;
ioBuffer.sizeBytes = 0;
ioBuffer.fd = -1;
// Remove from allocated buffer record if exist
mAllocatedBuffers.erase(bufferAddr);
return true;
}
} // namespace mtk