// // ModuleBasic.cpp // MNN // // Created by MNN on 2021/10/15. // Copyright © 2018, Alibaba Group Holding Limited // #include "MNN_generated.h" #include #include #include #define MNN_OPEN_TIME_TRACE #include #include "rapidjson/document.h" #include "core/MemoryFormater.h" #include #include #include #include "ExprDebug.hpp" #include "MNN/MNNSharedContext.h" using namespace MNN::Express; using namespace MNN; #ifdef __ANDROID__ #include #include /* 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_; }; #define DCHECK(x) MNN_ASSERT(x) 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(&allocate_) = dlsym(main_dl_handle, "AHardwareBuffer_allocate"); if(nullptr == allocate_){ mIsSupportAvailable = false; } *reinterpret_cast(&acquire_) = dlsym(main_dl_handle, "AHardwareBuffer_acquire"); if(nullptr == acquire_){ mIsSupportAvailable = false; } *reinterpret_cast(&describe_) = dlsym(main_dl_handle, "AHardwareBuffer_describe"); if(nullptr == describe_){ mIsSupportAvailable = false; } *reinterpret_cast(&lock_) = dlsym(main_dl_handle, "AHardwareBuffer_lock"); if(nullptr == lock_){ mIsSupportAvailable = false; } *reinterpret_cast(&recv_handle_) = dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket"); if(nullptr == recv_handle_){ mIsSupportAvailable = false; } *reinterpret_cast(&release_) = dlsym(main_dl_handle, "AHardwareBuffer_release"); if(nullptr == release_){ mIsSupportAvailable = false; } *reinterpret_cast(&send_handle_) = dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket"); if(nullptr == send_handle_){ mIsSupportAvailable = false; } *reinterpret_cast(&unlock_) = dlsym(main_dl_handle, "AHardwareBuffer_unlock"); if(nullptr == unlock_){ mIsSupportAvailable = false; } } int AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** out_buffer) { DCHECK(IsSupportAvailable()); return allocate_(desc, out_buffer); } void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) { DCHECK(IsSupportAvailable()); acquire_(buffer); } void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* out_desc) { DCHECK(IsSupportAvailable()); describe_(buffer, out_desc); } int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** out_virtual_address) { DCHECK(IsSupportAvailable()); return lock_(buffer, usage, fence, rect, out_virtual_address); } int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket( int socket_fd, AHardwareBuffer** out_buffer) { DCHECK(IsSupportAvailable()); return recv_handle_(socket_fd, out_buffer); } void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) { DCHECK(IsSupportAvailable()); release_(buffer); } int AndroidHardwareBufferCompat::SendHandleToUnixSocket( const AHardwareBuffer* buffer, int socket_fd) { DCHECK(IsSupportAvailable()); return send_handle_(buffer, socket_fd); } int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer, int32_t* fence) { DCHECK(IsSupportAvailable()); return unlock_(buffer, fence); } static std::shared_ptr gFunction; static AHardwareBuffer* creatAHardwareBuffer(int width, int height, void *data){ // 创建和初始化硬件缓冲区 AHardwareBuffer_Desc bufferDesc = {}; bufferDesc.width = width; bufferDesc.height = height; bufferDesc.layers = 1; bufferDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; bufferDesc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; AHardwareBuffer* buffer = nullptr; int result = gFunction->Allocate(&bufferDesc, &buffer); if(result != 0) { // Handle allocation error MNN_PRINT("alloc AHardwareBuffer failed %d\n", result); } if(nullptr != data){ void* map = nullptr; ARect rect = { 0, 0, width, height }; // Define the region to lock result = gFunction->Lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, &rect, &map); if (result != 0) { // Handle lock failure MNN_PRINT("Handle lock failed\n"); } if (map) { // Now write your pixel data to 'data' // For example, fill it with a solid color: memcpy(map, data, width * height * 4); // Assuming RGBA8888 format } gFunction->Unlock(buffer, nullptr); } return buffer; } static void copyDataFromAHardWareBuffer(AHardwareBuffer* buffer, int width, int height, void *data){ int result = 0; if(nullptr != data){ void* map = nullptr; ARect rect = { 0, 0, width, height }; // Define the region to lock result = gFunction->Lock(buffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, &rect, &map); if (result != 0) { MNN_PRINT("Handle lock failed\n"); } if (map) { memcpy(data, map, width * height * 4); } gFunction->Unlock(buffer, nullptr); } } static void ReleaseAHardWareBuffer(AHardwareBuffer* buffer){ if(buffer != nullptr){ gFunction->Release(buffer); } } #endif int main(int argc, char *argv[]) { if (argc < 3) { MNN_ERROR("Usage: ./GpuInterTest.out ${test.mnn} ${Dir} [testMode] [forwardType] [numberThread] [precision | memory]\n"); return 0; } std::string modelName = argv[1]; std::string directName = argv[2]; MNN_PRINT("Test %s from input info: %s\n", modelName.c_str(), directName.c_str()); std::map inputInfo; std::map> inputShape; std::vector inputNames; std::vector outputNames; int repeatNumber = 1; bool shapeMutable = true; std::vector inputs; std::vector outputs; if (inputNames.empty()) { rapidjson::Document document; std::ostringstream jsonNameOs; jsonNameOs << directName << "/input.json"; std::ifstream fileNames(jsonNameOs.str().c_str()); std::ostringstream output; output << fileNames.rdbuf(); auto outputStr = output.str(); document.Parse(outputStr.c_str()); if (document.HasParseError()) { MNN_ERROR("Invalid json\n"); return 0; } if (document.HasMember("inputs")) { auto inputsInfo = document["inputs"].GetArray(); for (auto iter = inputsInfo.begin(); iter !=inputsInfo.end(); iter++) { auto obj = iter->GetObject(); std::string name = obj["name"].GetString(); inputNames.emplace_back(name); MNN_PRINT("%s\n", name.c_str()); if (obj.HasMember("value")) { float value = obj["value"].GetFloat(); inputInfo.insert(std::make_pair(name, value)); } if (obj.HasMember("shape")) { auto dims = obj["shape"].GetArray(); std::vector shapes; for (auto iter = dims.begin(); iter != dims.end(); iter++) { shapes.emplace_back(iter->GetInt()); } inputShape.insert(std::make_pair(name, shapes)); } } } if (document.HasMember("outputs")) { auto array = document["outputs"].GetArray(); for (auto iter = array.begin(); iter !=array.end(); iter++) { std::string name = iter->GetString(); MNN_PRINT("output: %s\n", name.c_str()); outputNames.emplace_back(name); } } if (document.HasMember("shapeMutable")) { shapeMutable = document["shapeMutable"].GetBool(); } if (document.HasMember("repeat")) { repeatNumber = document["repeat"].GetInt(); } } int testMode = 0; //testMode = 0 AhardwareBuffer if(argc > 3){ testMode = atoi(argv[3]); MNN_PRINT("Use extra forward type: %d(0:AhardwareBuffer)\n", testMode); } auto type = MNN_FORWARD_CPU; if (argc > 4) { type = (MNNForwardType)atoi(argv[4]); MNN_PRINT("Use extra forward type: %d\n", type); } // Default single thread int modeNum = 1; if (argc > 5) { modeNum = ::atoi(argv[5]); } int precision = BackendConfig::Precision_Normal; int memory = BackendConfig::Memory_Normal; if (argc > 6) { int mask = atoi(argv[6]); precision = mask % 4; memory = (mask / 4) % 4; } const char* cacheFileName = ".tempcache"; FUNC_PRINT(precision); FUNC_PRINT(memory); FUNC_PRINT_ALL(cacheFileName, s); // create session MNN::ScheduleConfig config; config.type = type; /*modeNum means gpuMode for GPU usage, Or means numThread for CPU usage.*/ config.numThread = modeNum; // If type not fount, let it failed config.backupType = type; BackendConfig backendConfig; backendConfig.precision = static_cast(precision); backendConfig.memory = static_cast(memory); config.backendConfig = &backendConfig; MNN::Express::Module::Config mConfig; mConfig.shapeMutable = shapeMutable; #ifdef __ANDROID__ gFunction.reset(new AndroidHardwareBufferCompat); std::vector AHardwarePtrInputVec; std::vector AHardwarePtrOutputVec; #endif std::shared_ptr rtmgr(Executor::RuntimeManager::createRuntimeManager(config)); rtmgr->setCache(cacheFileName); std::shared_ptr net; { AUTOTIME; net.reset(Module::load(inputNames, outputNames, modelName.c_str(), rtmgr, &mConfig)); if (net == nullptr) { MNN_PRINT("Error: can't load module\n"); return 0; } } auto mInfo = net->getInfo(); #ifdef __ANDROID__ AHardwarePtrInputVec.resize(mInfo->inputs.size()); AHardwarePtrOutputVec.resize(outputNames.size()); #endif if (inputs.empty()) { inputs.resize(mInfo->inputs.size()); for (int i=0; iinputs[i].dim, mInfo->inputs[i].order, mInfo->inputs[i].type); } for (int i=0; igetInfo(); int width = info->dim[3], height = info->dim[2], channel = info->dim[1]; auto shapeIter = inputShape.find(inputName); if (shapeIter != inputShape.end()) { auto s = shapeIter->second; inputs[i] = _Input(s, mInfo->inputs[i].order, mInfo->inputs[i].type); width = s[3]; height = s[2]; channel = s[1]; } // set input device ptr #ifdef __ANDROID__ // OpenGL Texture defaultFormat NC4HW4 if(testMode == 0){ width = width * ((channel + 3) / 4); AHardwarePtrInputVec[i] = creatAHardwareBuffer(width,height,nullptr); volatile uint64_t value = (uint64_t)AHardwarePtrInputVec[i]; inputs[i]->setDevicePtr((void*)value, MNN_MEMORY_AHARDWAREBUFFER); } #endif } } bool modelError = false; for (int repeat = 0; repeat < repeatNumber; ++repeat) { AUTOTIME; auto outputs = net->onForward(inputs); if (outputs.empty()) { MNN_ERROR("Error in forward\n"); return 0; } for (int i=0; igetInfo(); int width = info->dim[3], height = info->dim[2], channel = info->dim[1]; // copy output to device ptr #ifdef __ANDROID__ if(testMode == 0){ AHardwarePtrOutputVec[i] = creatAHardwareBuffer(width,height,nullptr); volatile uint64_t value = (uint64_t)AHardwarePtrOutputVec[i]; outputs[i]->copyToDevicePtr((void*)value, MNN_MEMORY_AHARDWAREBUFFER); } #endif } // Print module's memory float memoryInMB = 0.0f; rtmgr->getInfo(Interpreter::MEMORY, &memoryInMB); FUNC_PRINT_ALL(memoryInMB, f); } #ifdef __ANDROID__ if(testMode == 1){ for(int i = 0; i < AHardwarePtrInputVec.size(); ++i){ ReleaseAHardWareBuffer(AHardwarePtrInputVec[i]); } for(int i = 0; i < AHardwarePtrOutputVec.size(); ++i){ ReleaseAHardWareBuffer(AHardwarePtrOutputVec[i]); } } #endif rtmgr->updateCache(); return 0; }