559 lines
23 KiB
C++
559 lines
23 KiB
C++
#include <MNN/expr/Module.hpp>
|
|
#include <MNN/expr/ExprCreator.hpp>
|
|
#include <MNN/expr/ExecutorScope.hpp>
|
|
#include "core/OpCommonUtils.hpp"
|
|
#include "MNN_generated.h"
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <fstream>
|
|
#include <rapidjson/document.h>
|
|
#include "rapidjson/prettywriter.h"
|
|
#include "rapidjson/stringbuffer.h"
|
|
#include "core/MNNFileUtils.h"
|
|
#ifndef _WIN32
|
|
#include <sys/utsname.h>
|
|
#endif
|
|
|
|
using namespace rapidjson;
|
|
|
|
using namespace MNN::Express;
|
|
using namespace MNN;
|
|
|
|
static bool generateConfigFile(const std::string & qnnSDKPath, int socID, int dspArch, const std::vector<std::string> & graphNameVec, const std::string & outputDir, std::string & configPath, std::string & subConfigPath) {
|
|
configPath = MNNFilePathConcat(outputDir, "context_config.json");
|
|
subConfigPath = MNNFilePathConcat(outputDir, "htp_backend_extensions.json");
|
|
|
|
// Write context_config.json
|
|
rapidjson::Document contextConfigDoc;
|
|
contextConfigDoc.SetObject();
|
|
rapidjson::Document::AllocatorType& contextAllocator = contextConfigDoc.GetAllocator();
|
|
rapidjson::Value backendExtensions(rapidjson::kObjectType);
|
|
std::string htpBackendExtPath = MNNFilePathConcat(qnnSDKPath, "lib/x86_64-linux-clang/libQnnHtpNetRunExtensions.so");
|
|
backendExtensions.AddMember("shared_library_path", rapidjson::Value(htpBackendExtPath.c_str(), contextAllocator).Move(), contextAllocator);
|
|
backendExtensions.AddMember("config_file_path", rapidjson::Value(subConfigPath.c_str(), contextAllocator).Move(), contextAllocator);
|
|
contextConfigDoc.AddMember("backend_extensions", backendExtensions, contextAllocator);
|
|
rapidjson::StringBuffer contextBuffer;
|
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> contextWriter(contextBuffer);
|
|
contextConfigDoc.Accept(contextWriter);
|
|
std::ofstream contextConfigOut(configPath);
|
|
contextConfigOut << contextBuffer.GetString();
|
|
contextConfigOut.close();
|
|
|
|
// Write htp_backend_extensions.json
|
|
rapidjson::Document htpConfigDoc;
|
|
htpConfigDoc.SetObject();
|
|
rapidjson::Document::AllocatorType& htpConfigAllocator = htpConfigDoc.GetAllocator();
|
|
|
|
// "graphs" section
|
|
rapidjson::Value graphs(rapidjson::kArrayType);
|
|
rapidjson::Value graphObj(rapidjson::kObjectType);
|
|
graphObj.AddMember("vtcm_mb", 8, htpConfigAllocator);
|
|
rapidjson::Value names(rapidjson::kArrayType);
|
|
for (const auto& name : graphNameVec) {
|
|
names.PushBack(rapidjson::Value(name.c_str(), contextAllocator).Move(), htpConfigAllocator);
|
|
}
|
|
graphObj.AddMember("graph_names", names, htpConfigAllocator);
|
|
graphObj.AddMember("O", 3.0, htpConfigAllocator);
|
|
graphObj.AddMember("fp16_relaxed_precision", 1, htpConfigAllocator);
|
|
graphObj.AddMember("weights_packing", true, htpConfigAllocator);
|
|
graphObj.AddMember("hvx_threads", 4, htpConfigAllocator);
|
|
graphs.PushBack(graphObj, htpConfigAllocator);
|
|
htpConfigDoc.AddMember("graphs", graphs, htpConfigAllocator);
|
|
|
|
// "devices" section
|
|
rapidjson::Value devices(rapidjson::kArrayType);
|
|
rapidjson::Value deviceObj(rapidjson::kObjectType);
|
|
deviceObj.AddMember("soc_id", socID, htpConfigAllocator);
|
|
std::string hexagonArchStr = "v" + std::to_string(dspArch);
|
|
deviceObj.AddMember("dsp_arch", rapidjson::Value(hexagonArchStr.c_str(), contextAllocator).Move(), htpConfigAllocator);
|
|
rapidjson::Value cores(rapidjson::kArrayType);
|
|
rapidjson::Value coreObj(rapidjson::kObjectType);
|
|
coreObj.AddMember("core_id", 0, htpConfigAllocator);
|
|
coreObj.AddMember("perf_profile", "burst", htpConfigAllocator);
|
|
coreObj.AddMember("rpc_control_latency", 100, htpConfigAllocator);
|
|
cores.PushBack(coreObj, htpConfigAllocator);
|
|
deviceObj.AddMember("cores", cores, htpConfigAllocator);
|
|
devices.PushBack(deviceObj, htpConfigAllocator);
|
|
htpConfigDoc.AddMember("devices", devices, htpConfigAllocator);
|
|
|
|
// "context" section
|
|
rapidjson::Value contextObj(rapidjson::kObjectType);
|
|
contextObj.AddMember("weight_sharing_enabled", true, htpConfigAllocator);
|
|
htpConfigDoc.AddMember("context", contextObj, htpConfigAllocator);
|
|
|
|
rapidjson::StringBuffer htpConfigBuffer;
|
|
rapidjson::PrettyWriter<rapidjson::StringBuffer> htpConfigWriter(htpConfigBuffer);
|
|
htpConfigDoc.Accept(htpConfigWriter);
|
|
std::ofstream htpConfigOut(subConfigPath);
|
|
htpConfigOut << htpConfigBuffer.GetString();
|
|
htpConfigOut.close();
|
|
return true;
|
|
}
|
|
|
|
static bool parseDims(const std::string& s, std::vector<std::vector<int>>& out) {
|
|
auto isLegal = [](char c) {
|
|
return c == 'x' || c == '_' || (c >= '0' && c <= '9');
|
|
};
|
|
bool allLegal = std::all_of(s.begin(), s.end(), isLegal);
|
|
if(!allLegal) {
|
|
return false;
|
|
}
|
|
|
|
out.clear();
|
|
std::stringstream ss(s);
|
|
std::string segment;
|
|
MNN_PRINT("param dims: %s\n", s.c_str());
|
|
while (std::getline(ss, segment, '_')) {
|
|
if (segment.empty()) {
|
|
MNN_ERROR("%s parse error, format should be like 1x3x512x512_1x256\n", s.c_str());
|
|
return false;
|
|
}
|
|
std::vector<int> dims;
|
|
std::stringstream inner(segment);
|
|
std::string token;
|
|
|
|
while (std::getline(inner, token, 'x')) {
|
|
if (token.empty()) {
|
|
MNN_ERROR("%s parse error, format should be like 1x3x512x512_1x256\n", s.c_str());
|
|
return false;
|
|
}
|
|
int val = std::stoi(token);
|
|
dims.push_back(val);
|
|
}
|
|
if (dims.empty()) {
|
|
MNN_ERROR("%s parse error, format should be like 1x3x512x512_1x256\n", s.c_str());
|
|
return false;
|
|
}
|
|
out.push_back(dims);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool checkSystem() {
|
|
#ifdef _WIN32
|
|
// On Windows, skip the system check as this tool is primarily for Linux
|
|
// but we allow it to compile on Windows for development purposes
|
|
MNN_PRINT("Warning: This tool is designed for x86_64 Linux systems. Running on Windows may have limitations.\n");
|
|
return true;
|
|
#else
|
|
struct utsname buf;
|
|
if (uname(&buf) != 0) {
|
|
MNN_ERROR("uname error\n");
|
|
return false;
|
|
}
|
|
if (std::string(buf.sysname) == "Linux" && std::string(buf.machine) == "x86_64") {
|
|
return true;
|
|
}
|
|
MNN_ERROR("This program must be run on a x86_64 Linux system. Current system: %s %s\n", buf.sysname, buf.machine);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
int main(int argc, const char* argv[]) {
|
|
if (argc < 6) {
|
|
MNN_PRINT("This tool generates offline caches for the QNN backend.");
|
|
MNN_PRINT("Usage: %s <qnnSDKPath> <socId> <hexagonArch> <srcMNNPath> <outputDir> [totalShapeNum] [inputShape1] [inputShape2] ...\n", argv[0]);
|
|
MNN_PRINT(" <qnnSDKPath> : Path to the QNN SDK directory.\n");
|
|
MNN_PRINT(" <socId> : Target SoC ID.\n");
|
|
MNN_PRINT(" Common SoCs: 8Gen2 -> 43, 8Gen3 -> 57, 8 Elite -> 69. For others, please refer to Qualcomm's documentation.\n");
|
|
MNN_PRINT(" <hexagonArch> : Hexagon architecture version. This tool requires v73 or higher for weight sharing.\n");
|
|
MNN_PRINT(" Common Archs: 8Gen2 -> 73, 8Gen3 -> 75, 8 Elite -> 79. For others, please refer to Qualcomm's documentation.\n");
|
|
MNN_PRINT(" <srcMNNPath> : Path to the source MNN model file.\n");
|
|
MNN_PRINT(" <outputDir> : Directory to save the generated files, including a MNN model file with the suffix '.mnn' and a QNN serialized artifact with the suffix '.bin'.\n");
|
|
MNN_PRINT(" [<totalShapeNum>] : Optional. Number of dynamic input shape configurations.\n");
|
|
MNN_PRINT(" [<inputShapeN>] : Optional. Input shape configuration. Can be a shape string or a path to a .mnn file.\n");
|
|
MNN_PRINT(" Shape string format for multiple inputs: dim1xdim2_dim3xdim4. Example: 1x3x512x512_1x256\n");
|
|
MNN_PRINT("Examples:\n");
|
|
MNN_PRINT(" 1. Use default shape from the MNN model:\n");
|
|
MNN_PRINT(" %s /path/to/qnn/sdk 57 75 /path/to/model.mnn /path/to/output\n", argv[0]);
|
|
MNN_PRINT(" 2. Specify two dynamic input shapes:\n");
|
|
MNN_PRINT(" %s /path/to/qnn/sdk 57 75 /path/to/model.mnn /path/to/output 2 1x3x512x512_1x256 1x3x256x256_1x128\n", argv[0]);
|
|
MNN_PRINT(" %s /path/to/qnn/sdk 57 75 /path/to/model.mnn /path/to/output 2 input_0.mnn input_1.mnn\n", argv[0]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (!checkSystem()) {
|
|
return -1;
|
|
}
|
|
|
|
std::string qnnSdkPath = argv[1];
|
|
int socId = std::stoi(std::string(argv[2]));
|
|
int hexagonArch = std::stoi(std::string(argv[3]));
|
|
const char* srcMNNPath = argv[4];
|
|
std::string modelBaseName = [](const std::string& path) {
|
|
std::string filename = path;
|
|
auto pos = path.find_last_of("/\\");
|
|
if (pos != std::string::npos) {
|
|
filename = path.substr(pos + 1);
|
|
}
|
|
pos = filename.find_last_of('.');
|
|
if (pos != std::string::npos) {
|
|
return filename.substr(0, pos);
|
|
}
|
|
return filename;
|
|
}(srcMNNPath);
|
|
std::string modelSignature = "_" + std::to_string(socId) + "_" + std::to_string(hexagonArch);
|
|
std::string outputDir = argv[5];
|
|
std::string dstMNNPath = MNNFilePathConcat(outputDir, modelBaseName + modelSignature + ".mnn");
|
|
|
|
std::vector<std::string> inputNames;
|
|
std::vector<std::string> outputNames;
|
|
std::vector<MNN::Express::VARP> inputs;
|
|
std::vector<MNN::Express::VARP> outputs;
|
|
std::vector<std::vector<std::vector<int>>> inputShapeLists;
|
|
bool hasInputsVarp = false;
|
|
std::vector<std::vector<MNN::Express::VARP>> inputsVarpList;
|
|
|
|
int totalShapeType = 1;
|
|
if(argc > 6) {
|
|
totalShapeType = std::stoi(argv[6]);
|
|
std::vector<std::vector<int>> temp;
|
|
if(parseDims(argv[7], temp)) {
|
|
inputShapeLists.resize(totalShapeType);
|
|
for(int i = 0; i < totalShapeType; i++) {
|
|
// Each inputs shape in model: 128x1x897_1x1x128x128_1x128
|
|
if(!parseDims(argv[7+i], inputShapeLists[i])) {
|
|
return -1;
|
|
}
|
|
}
|
|
} else {
|
|
inputsVarpList.resize(totalShapeType);
|
|
for(int i = 0; i < totalShapeType; i++) {
|
|
inputsVarpList[i] = MNN::Express::Variable::load(argv[7+i]);
|
|
}
|
|
inputs = MNN::Express::Variable::load(argv[7]);
|
|
for (int i=0; i<inputs.size(); ++i) {
|
|
inputNames.emplace_back(inputs[i]->name());
|
|
}
|
|
if(argc > 7+totalShapeType) {
|
|
outputs = MNN::Express::Variable::load(argv[7+totalShapeType]);
|
|
for (int i=0; i<outputs.size(); ++i) {
|
|
outputNames.emplace_back(outputs[i]->name());
|
|
}
|
|
}
|
|
hasInputsVarp = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
generate qnn .cpp and .bin
|
|
*/
|
|
std::string totalQnnSo;
|
|
std::vector<std::string> qnnGraphNames;
|
|
std::vector<std::vector<MNN::Express::Variable::Info>> outputInfos;
|
|
std::vector<std::string> qnnModelDirs;
|
|
std::vector<int> allInputShape;
|
|
|
|
MNN_PRINT("Total input shape type size:%d\n", totalShapeType);
|
|
for(int index = 0; index < totalShapeType; index++)
|
|
{
|
|
std::string curQnnModelName = modelBaseName + std::string("_") + std::to_string(index);
|
|
qnnGraphNames.emplace_back(curQnnModelName);
|
|
std::string curQnnModelDir = MNNFilePathConcat(outputDir, curQnnModelName);
|
|
MNN_PRINT("[Temp Product]: Qnn temp product generate at %s\n", curQnnModelDir.c_str());
|
|
MNNCreateDir(curQnnModelDir.c_str());
|
|
qnnModelDirs.push_back(curQnnModelDir);
|
|
if(index < totalShapeType-1) {
|
|
totalQnnSo += (curQnnModelDir + std::string("/lib/x86_64-linux-clang/lib") + \
|
|
curQnnModelName + std::string(".so,"));
|
|
} else {
|
|
totalQnnSo += (curQnnModelDir + std::string("/lib/x86_64-linux-clang/lib") + \
|
|
curQnnModelName + std::string(".so "));
|
|
}
|
|
|
|
MNN::ScheduleConfig config;
|
|
config.type = MNN_CONVERT_QNN;
|
|
std::shared_ptr<Executor::RuntimeManager> rtmgr(Executor::RuntimeManager::createRuntimeManager(config));
|
|
rtmgr->setCache(curQnnModelDir.c_str());
|
|
MNN::Express::Module::Config mConfig;
|
|
mConfig.shapeMutable = false;
|
|
std::shared_ptr<MNN::Express::Module> m(MNN::Express::Module::load(inputNames, outputNames, srcMNNPath, rtmgr, &mConfig), MNN::Express::Module::destroy);
|
|
auto minfo = m->getInfo();
|
|
if(outputNames.empty()) {
|
|
outputNames = minfo->outputNames;
|
|
}
|
|
if(inputNames.empty()) {
|
|
inputNames = minfo->inputNames;
|
|
}
|
|
|
|
if(!hasInputsVarp) {
|
|
inputs.resize(minfo->inputs.size());
|
|
for (int i=0; i<minfo->inputs.size(); ++i) {
|
|
auto info = minfo->inputs[i];
|
|
std::vector<int> inputDims = info.dim;
|
|
if(!inputShapeLists.empty()) {
|
|
inputDims = inputShapeLists[index][i];
|
|
}
|
|
MNN_PRINT("input %d shape:", i);
|
|
for(int d = 0; d < inputDims.size(); d++) {
|
|
MNN_PRINT("%d ", inputDims[d]);
|
|
}
|
|
MNN_PRINT("\n");
|
|
auto varp = MNN::Express::_Input(inputDims, info.order, info.type);
|
|
varp->writeMap<void>();
|
|
inputs[i] = varp;
|
|
inputs[i]->setName(inputNames[i]);
|
|
}
|
|
} else {
|
|
inputs = inputsVarpList[index];
|
|
}
|
|
outputs = m->onForward(inputs);
|
|
// sync
|
|
for(int i = 0; i < outputs.size(); i++) {
|
|
outputs[i]->readMap<void>();
|
|
}
|
|
|
|
// tar weight
|
|
std::string binPath = MNNFilePathConcat(curQnnModelDir, curQnnModelName + ".bin");
|
|
std::string command = "tar -cf " + binPath + " -C " + curQnnModelDir + " $(find " + curQnnModelDir + " -maxdepth 1 -name '*.raw' -printf '%f ') && rm " + curQnnModelDir + "/*.raw";
|
|
int ret = std::system(command.c_str());
|
|
if (ret != 0) {
|
|
MNN_ERROR("Failed to execute command: %s\n", command.c_str());
|
|
}
|
|
|
|
std::string modelLibCmd = qnnSdkPath + "/bin/x86_64-linux-clang/qnn-model-lib-generator" + \
|
|
" -c " + MNNFilePathConcat(curQnnModelDir, curQnnModelName + ".cpp") + \
|
|
" -b " + binPath + \
|
|
" -t x86_64-linux-clang " + \
|
|
" -o " + curQnnModelDir + "/lib";
|
|
ret = system(modelLibCmd.c_str());
|
|
if(ret) {
|
|
MNN_ERROR("[Error]: qnn-model-lib-generator error!\n");
|
|
return -1;
|
|
} else {
|
|
MNN_PRINT("[Pass]: qnn-model-lib-generator success!\n");
|
|
}
|
|
|
|
|
|
std::vector<MNN::Express::Variable::Info> inputInfos(inputs.size());
|
|
for (int i=0; i<inputInfos.size(); ++i) {
|
|
inputInfos[i] = *inputs[i]->getInfo();
|
|
}
|
|
std::vector<int> currInputShape;
|
|
for (int i = 0; i < inputInfos.size(); i++) {
|
|
for (int j = 0; j < inputInfos[i].dim.size(); j++) {
|
|
currInputShape.emplace_back(inputInfos[i].dim[j]);
|
|
}
|
|
}
|
|
allInputShape.insert(allInputShape.end(), currInputShape.begin(), currInputShape.end());
|
|
|
|
std::vector<MNN::Express::Variable::Info> outputInfo(outputs.size());
|
|
for (int i=0; i<outputInfo.size(); ++i) {
|
|
outputInfo[i] = *outputs[i]->getInfo();
|
|
}
|
|
outputInfos.emplace_back(outputInfo);
|
|
|
|
}
|
|
|
|
std::string npuArtifactName = modelBaseName + modelSignature + ".bin";
|
|
std::string npuArtifactPath = MNNFilePathConcat(outputDir, npuArtifactName);
|
|
{
|
|
std::string configPath, subConfigPath;
|
|
if (!generateConfigFile(qnnSdkPath, socId, hexagonArch, qnnGraphNames, outputDir, configPath, subConfigPath)) {
|
|
MNN_ERROR("[Error]: Failed to generate the config file!\n");
|
|
return -1;
|
|
}
|
|
|
|
std::string binaryGenCmd = qnnSdkPath + "/bin/x86_64-linux-clang/qnn-context-binary-generator" + \
|
|
" --model " + totalQnnSo + \
|
|
" --backend " + qnnSdkPath + "/lib/x86_64-linux-clang/libQnnHtp.so" + \
|
|
" --binary_file " + modelBaseName + modelSignature + \
|
|
" --config_file " + configPath + " " + \
|
|
" --output_dir " + outputDir;
|
|
auto res = system(binaryGenCmd.c_str());
|
|
if(res) {
|
|
MNN_ERROR("[Error]: qnn-context-binary-generator error!\n");
|
|
return -1;
|
|
} else {
|
|
MNN_PRINT("[Pass]: qnn-context-binary-generator success!\n");
|
|
}
|
|
|
|
// Remove intermediate files
|
|
MNNRemoveFile(configPath.c_str());
|
|
MNNRemoveFile(subConfigPath.c_str());
|
|
for (const auto& dir : qnnModelDirs) {
|
|
std::string cmd = "rm -rf " + dir;
|
|
int ret = system(cmd.c_str());
|
|
if (ret != 0) {
|
|
MNN_PRINT("[Warning]: failed to remove temp dir: %s\n", dir.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<MNN::Express::Variable::Info> inputInfos(inputs.size());
|
|
for (int i=0; i<inputInfos.size(); ++i) {
|
|
inputInfos[i] = *inputs[i]->getInfo();
|
|
}
|
|
|
|
|
|
// Get inputs/outputs index in mnn model
|
|
std::vector<int> inputIndexes(inputNames.size());
|
|
std::vector<int> outputIndexes(outputNames.size());
|
|
{
|
|
std::shared_ptr<MNN::Interpreter> netC(MNN::Interpreter::createFromFile(srcMNNPath), MNN::Interpreter::destroy);
|
|
auto bufferPair = netC->getModelBuffer();
|
|
auto buffer = bufferPair.first;
|
|
auto length = bufferPair.second;
|
|
auto net = GetNet(buffer);
|
|
|
|
for (int i=0; i<net->tensorName()->size(); ++i) {
|
|
auto tname = net->tensorName()->GetAsString(i)->str();
|
|
for (int j=0; j<inputNames.size(); ++j) {
|
|
if (tname == inputNames[j]) {
|
|
inputIndexes[j] = i;
|
|
break;
|
|
}
|
|
}
|
|
for (int j=0; j<outputNames.size(); ++j) {
|
|
if (tname == outputNames[j]) {
|
|
outputIndexes[j] = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<MNN::NetT> dstNet(new NetT);
|
|
|
|
for (int i=0; i<inputInfos.size(); ++i) {
|
|
std::unique_ptr<OpT> input(new OpT);
|
|
input->type = OpType_Input;
|
|
auto param(new InputT);
|
|
param->dims = inputInfos[i].dim;
|
|
|
|
input->main.type = OpParameter_Input;
|
|
input->main.value = param;
|
|
input->name = inputNames[i];
|
|
input->outputIndexes.push_back(i);
|
|
dstNet->oplists.emplace_back(std::move(input));
|
|
}
|
|
|
|
/** Fuse to Op*/
|
|
std::unique_ptr<MNN::OpT> op(new OpT);
|
|
for(int i = 0; i < inputs.size(); i++) {
|
|
op->inputIndexes.push_back(i);
|
|
}
|
|
for(int i = 0; i < outputs.size(); i++) {
|
|
op->outputIndexes.push_back(inputs.size() + i);
|
|
}
|
|
op->name = "qnn/plugin/op";
|
|
op->main.Reset();
|
|
op->type = MNN::OpType_Plugin;
|
|
op->main.type = MNN::OpParameter_Plugin;
|
|
op->main.value = new MNN::PluginT;
|
|
auto extra = op->main.AsPlugin();
|
|
extra->type = "QNN";
|
|
std::unique_ptr<MNN::AttributeT> attr(new MNN::AttributeT);
|
|
|
|
|
|
dstNet->tensorName = inputNames;
|
|
dstNet->tensorName.insert(dstNet->tensorName.end(), outputNames.begin(), outputNames.end());
|
|
dstNet->tensorName.push_back(op->name);
|
|
dstNet->outputName = outputNames;
|
|
|
|
attr->key = "allInputShape";
|
|
attr->list.reset(new ListValueT);
|
|
attr->list->i.insert(attr->list->i.end(), allInputShape.begin(), allInputShape.end());
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
|
|
attr->key = "allGraphName";
|
|
attr->list.reset(new ListValueT);
|
|
attr->list->s.resize(qnnGraphNames.size());
|
|
for(int i = 0; i < qnnGraphNames.size(); i++) {
|
|
attr->list->s[i] = qnnGraphNames[i];
|
|
}
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
|
|
attr->key = "path";
|
|
attr->s = npuArtifactName;
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
|
|
attr->key = "offset";
|
|
attr->list.reset(new MNN::ListValueT);
|
|
attr->list->i.push_back(0);
|
|
attr->list->i.push_back(0);
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
|
|
file_t binaryFile = MNNOpenFile(npuArtifactPath.c_str(), MNN_FILE_READ);
|
|
size_t binarySize = MNNGetFileSize(binaryFile);
|
|
MNNCloseFile(binaryFile);
|
|
attr->key = "size";
|
|
attr->list.reset(new MNN::ListValueT);
|
|
uint32_t lowSrc = binarySize & 0xFFFFFFFF;
|
|
uint32_t highSrc = binarySize >> 32;
|
|
int32_t lowDst, highDst;
|
|
::memcpy(&lowDst, &lowSrc, sizeof(int32_t));
|
|
::memcpy(&highDst, &highSrc, sizeof(int32_t));
|
|
attr->list->i.push_back(lowDst);
|
|
attr->list->i.push_back(highDst);
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
|
|
attr->key = "inputs";
|
|
attr->list.reset(new ListValueT);
|
|
attr->list->s.resize(inputNames.size());
|
|
for (int i=0; i<inputNames.size(); ++i) {
|
|
// ::TODO
|
|
attr->list->s[i] = std::string("t") + std::to_string(inputIndexes[i]);
|
|
}
|
|
extra->attr.emplace_back(std::move(attr));
|
|
attr.reset(new MNN::AttributeT);
|
|
attr->key = "outputs";
|
|
attr->list.reset(new ListValueT);
|
|
attr->list->s.resize(outputNames.size());
|
|
for (int i=0; i<outputNames.size(); ++i) {
|
|
// ::TODO
|
|
attr->list->s[i] = std::string("t") + std::to_string(outputIndexes[i]);
|
|
}
|
|
extra->attr.emplace_back(std::move(attr));
|
|
|
|
|
|
for (int i=0; i<outputInfos.size(); ++i) {
|
|
attr.reset(new MNN::AttributeT);
|
|
for(int j = 0; j < outputInfos[i].size(); j++) {
|
|
attr->key = "o_" + std::to_string(i) + std::string("_") + std::to_string(j);
|
|
attr->tensor.reset(new BlobT);
|
|
attr->tensor->dataType = OpCommonUtils::convertDataType(outputInfos[i][j].type);
|
|
attr->tensor->dims = outputInfos[i][j].dim;
|
|
switch(outputInfos[i][j].order) {
|
|
case MNN::Express::NHWC:
|
|
attr->tensor->dataFormat = MNN_DATA_FORMAT_NHWC;
|
|
break;
|
|
case MNN::Express::NCHW:
|
|
attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW;
|
|
break;
|
|
case MNN::Express::NC4HW4:
|
|
attr->tensor->dataFormat = MNN_DATA_FORMAT_NC4HW4;
|
|
break;
|
|
default:
|
|
attr->tensor->dataFormat = MNN_DATA_FORMAT_NCHW;
|
|
break;
|
|
}
|
|
}
|
|
extra->attr.emplace_back(std::move(attr));
|
|
}
|
|
|
|
// Compile NPU Module
|
|
std::unique_ptr<OpT> npuOp;
|
|
npuOp = std::move(op);
|
|
|
|
// Merge to dst
|
|
dstNet->oplists.emplace_back(std::move(npuOp));
|
|
|
|
// Store
|
|
flatbuffers::FlatBufferBuilder builder;
|
|
builder.Finish(Net::Pack(builder, dstNet.get()));
|
|
std::ofstream outputOs(dstMNNPath.c_str(), std::ios::binary);
|
|
outputOs.write((const char*)builder.GetBufferPointer(), builder.GetSize());
|
|
outputOs.close();
|
|
|
|
MNN_PRINT("[All passed]\n");
|
|
return 0;
|
|
}
|