mirror of
https://github.com/openharmony/neural_network_runtime.git
synced 2026-07-01 08:12:02 -04:00
+20
@@ -0,0 +1,20 @@
|
||||
# ignore .a .o
|
||||
*.[ao]
|
||||
*.so
|
||||
*.ms
|
||||
|
||||
# ignore build
|
||||
build/
|
||||
libs/
|
||||
obj/
|
||||
_ignore/
|
||||
|
||||
# ignore schema
|
||||
jni/include/schema/
|
||||
|
||||
# ignore vscode
|
||||
.vscode
|
||||
|
||||
# ignore runtime file
|
||||
deploy.bat
|
||||
log.txt
|
||||
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import("//build/ohos.gni")
|
||||
|
||||
group("nnrt_target") {
|
||||
deps = [
|
||||
"frameworks:libneural_network_runtime"
|
||||
]
|
||||
}
|
||||
|
||||
group("nnrt_test_target") {
|
||||
testonly = true
|
||||
deps = [
|
||||
"test/unittest:unittest",
|
||||
"test/system_test:system_test"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
@@ -174,28 +175,3 @@
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
This is the configuration file template for OpenHarmony OSS Audit Tool, please copy it to your project root dir and modify it refer to OpenHarmony/tools_oat/README.
|
||||
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<oatconfig>
|
||||
<filefilterlist>
|
||||
<filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">
|
||||
<filteritem type="filename" name="neural_network_runtime_intro.png" desc="自研图片"/>
|
||||
<filteritem type="filename" name="neural_network_runtime_add_op_model.png" desc="自研图片"/>
|
||||
<filteritem type="filename" name="arch_diagram.png" desc="自研图片"/>
|
||||
<filteritem type="filename" name="dev_flow.png" desc="自研图片"/>
|
||||
<filteritem type="filename" name="Flowchart.png" desc="自研图片"/>
|
||||
<filteritem type="filename" name="Principle.png" desc="自研图片"/>
|
||||
</filefilter>
|
||||
</filefilterlist>
|
||||
</oatconfig>
|
||||
</configuration>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# neural_network_runtime
|
||||
|
||||
#### Description
|
||||
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
@@ -1,39 +0,0 @@
|
||||
# neural_network_runtime
|
||||
|
||||
#### 介绍
|
||||
{**以下是 Gitee 平台说明,您可以替换此简介**
|
||||
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
|
||||
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
|
||||
|
||||
#### 软件架构
|
||||
软件架构说明
|
||||
|
||||
|
||||
#### 安装教程
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 使用说明
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### 参与贡献
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 新建 Feat_xxx 分支
|
||||
3. 提交代码
|
||||
4. 新建 Pull Request
|
||||
|
||||
|
||||
#### 特技
|
||||
|
||||
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
|
||||
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
|
||||
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
|
||||
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
|
||||
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Neural Network Runtime
|
||||
|
||||
Neural Network Runtime(神经网络运行时)是一套面向AI领域的运行时部件,适配上层AI推理引擎和底层加速芯片,为端侧AI推理引擎提供硬件加速的计算能力。
|
||||
|
||||
## 基本概念
|
||||
|
||||
在开发前,需要先了解以下概念,以便更好地理解全文内容:
|
||||
|
||||
- Native API:Openharmony 面向应用开发者的C语言接口。
|
||||
- HDI:Hardware Device Interface,硬件设备接口,是OpenHarmony中系统组件与芯片组件通信的接口。关于更多HDI的细节,请浏览[驱动子系统](https://gitee.com/openharmony/docs/blob/master/zh-cn/readme/%E9%A9%B1%E5%8A%A8%E5%AD%90%E7%B3%BB%E7%BB%9F.md)。
|
||||
|
||||
## 运作机制
|
||||
|
||||
**图1** Neural Network Runtime架构图
|
||||

|
||||
|
||||
如图1所示,在OpenHarmony系统上,AI应用通常要经过AI推理引擎和Neural Network Runtime才能对接底层芯片驱动,进而加速推理计算。Neural Network Runtime和芯片驱动直接通过HDI接口交互,Neural Network Runtime将模型和数据传递给芯片驱动,通过HDI接口在加速芯片上执行推理计算,计算结果通过Neural Network Runtime、AI推理引擎逐层返回至AI应用。
|
||||
|
||||
通常,AI应用、AI推理引擎、Neural Network Runtime处在同一个进程下,芯片驱动运行在另一个进程下,两者之间需要借助进程间通信(IPC)传递模型和计算数据。Neural Network Runtime根据HDI接口实现了HDI客户端,相应的,芯片厂商需要根据HDI接口实现并开放HDI服务。
|
||||
|
||||
架构图中每层功能简单阐述如下:
|
||||
- AI应用:借助AI模型,提供丰富的应用能力,如:图像分类、人脸识别、文字识别等。
|
||||
- AI推理引擎:为AI应用提供模型搭建、模型优化、推理计算的能力。
|
||||
- Neural Network Runtime:作为AI推理引擎和底层加速芯片的桥梁,它开放了标准统一的HDI接口,不同的芯片都可以通过HDI接口接入Neural Network Runtime。
|
||||
- HDI服务端:HDI服务端接收Neural Network Runtime传入的模型,将模型转换为加速芯片驱动所使用模型格式,并调用芯片驱动的接口执行计算。
|
||||
- 加速芯片:加速芯片通常能够加速AI模型或者模型中部分算子的计算,提供优于CPU的性能。
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@ohos/neural_network_runtime",
|
||||
"description": "The Neural Network Runtime that bridges the inference framework and the device accelerator.",
|
||||
"version": "3.2",
|
||||
"license": "MIT",
|
||||
"publishAs": "code-segment",
|
||||
"segment": {
|
||||
"destPath": "foundation/ai/neural_network_runtime"
|
||||
},
|
||||
"dirs": {},
|
||||
"scripts": {},
|
||||
"licensePath": "COPYING",
|
||||
"readmePath": {
|
||||
"en": "README.rst"
|
||||
},
|
||||
"component": {
|
||||
"name": "neural_network_runtime",
|
||||
"subsystem": "ai",
|
||||
"syscap": [],
|
||||
"features": [],
|
||||
"adapted_system_type": ["standard"],
|
||||
"rom": "1024KB",
|
||||
"ram": "2048KB",
|
||||
"deps": {
|
||||
"components": [
|
||||
"hilog"
|
||||
],
|
||||
"third_party": []
|
||||
},
|
||||
"build": {
|
||||
"sub_component": [
|
||||
"//foundation/ai/neural_network_runtime:nnrt_target"
|
||||
],
|
||||
"inner_kits": [
|
||||
{"type": "so",
|
||||
"name": "//foundation/ai/neural_network_runtime:nnrt_target",
|
||||
"header": {
|
||||
"header_files": [
|
||||
"neural_network_runtime_inner.h"
|
||||
],
|
||||
"header_base":"//foundation/ai/neural_network_runtime/interfaces/innerkits/c"
|
||||
}
|
||||
}
|
||||
],
|
||||
"test": [
|
||||
"//foundation/ai/neural_network_runtime:nnrt_test_target"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_LOG_H
|
||||
#define NEURAL_NETWORK_RUNTIME_LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include "hilog/log_c.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef NNR_LOG_DOMAIN
|
||||
#define NNR_LOG_DOMAIN 0xD002101
|
||||
#endif
|
||||
|
||||
#define LOGD(...) HiLogPrint(LOG_CORE, LOG_DEBUG, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
|
||||
#define LOGI(...) HiLogPrint(LOG_CORE, LOG_INFO, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
|
||||
#define LOGW(...) HiLogPrint(LOG_CORE, LOG_WARN, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
|
||||
#define LOGE(...) HiLogPrint(LOG_CORE, LOG_ERROR, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
|
||||
#define LOGF(...) HiLogPrint(LOG_CORE, LOG_FATAL, NNR_LOG_DOMAIN, "NNRt", __VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_LOG_H
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
|
||||
#define NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
|
||||
|
||||
#include <string>
|
||||
#include "hitrace/trace.h"
|
||||
|
||||
#define NNRT_TRACE_NAME(name) ScopedTrace ___tracer(name)
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class ScopedTrace {
|
||||
public:
|
||||
inline ScopedTrace(const std::string& name)
|
||||
{
|
||||
m_name = name;
|
||||
HiviewDFX::HiTraceId traceId = HiviewDFX::HiTraceChain::GetId();
|
||||
if (traceId.IsValid()) {
|
||||
HiviewDFX::HiTraceChain::Tracepoint(HITRACE_TP_GENERAL, traceId, "NNRt Trace start: %s", name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline ~ScopedTrace()
|
||||
{
|
||||
HiviewDFX::HiTraceId traceId = HiviewDFX::HiTraceChain::GetId();
|
||||
if (traceId.IsValid()) {
|
||||
HiviewDFX::HiTraceChain::Tracepoint(HITRACE_TP_GENERAL, traceId, "NNRt Trace end: %s", m_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name {};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_SCOPED_TRACE_H
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_UTILS_H
|
||||
#define NEURAL_NETWORK_RUNTIME_UTILS_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
template<typename T, typename... Args>
|
||||
std::shared_ptr<T> CreateSharedPtr(Args&&... args)
|
||||
{
|
||||
std::shared_ptr<T> tPtr = nullptr;
|
||||
try {
|
||||
tPtr = std::make_shared<T>(args...);
|
||||
} catch (const std::bad_alloc& except) {
|
||||
LOGW("Create a new shared pointer failed. Error: %s", except.what());
|
||||
return nullptr;
|
||||
}
|
||||
return tPtr;
|
||||
}
|
||||
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_UTILS_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# CMake lowest version requirement
|
||||
cmake_minimum_required(VERSION 3.16.5)
|
||||
|
||||
# project information
|
||||
project(label_classification)
|
||||
|
||||
set(CMAKE_C_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2")
|
||||
|
||||
# If the CPU architecture is 32-bit, -march=armv7-a
|
||||
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
|
||||
set(LOCAL_DIRECTORY_PATH ${PROJECT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(${LOCAL_DIRECTORY_PATH}/tflite)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,218 @@
|
||||
# Tensorflow Lite 接入NNRt Delegate Demo开发指南
|
||||
|
||||
## 概述
|
||||
|
||||
### 功能简介
|
||||
- 神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口;
|
||||
- 本demo旨在介绍上层AI业务如何利用NNRt在专有芯片上加速推理,使能OpenHarmony社区生态;
|
||||
- 本demo根据用户输入参数(模型、标签、模型输入shape、循环浮点推理次数、是否允许动态尺寸推理、以及是否打印结果等)完成标签分类模型推理,用户可通过打印信息观察在不同条件下的模型推理性能、精度等KIP。
|
||||
|
||||
### 基本概念
|
||||
在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
|
||||
- NNRt: Neural Network Runtime,神经网络运行时,是本指导主要介绍的部件。
|
||||
- OHOS:OpenHarmony Operating System,开源鸿蒙操作系统。
|
||||
|
||||
### 约束与限制
|
||||
- 系统版本:OpenHarmonyOS 3.2及以上
|
||||
- 开发环境:Ubuntu 18.04及以上
|
||||
- 接入设备:OpenHarmony定义的标准设备
|
||||
- 其他开发依赖:
|
||||
- tensorflow-lite.so及其依赖库,目前完成在tensorflow lite 2.6版本上的测试;
|
||||
- NNRt库libneural_network_runtime.z.so;
|
||||
- TensorFlow Lite头文件:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite;
|
||||
- mobilenetv2.tflite模型(https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz);
|
||||
- 标签文件labels.txt;
|
||||
- 测试图片grace_hopper.bmp;
|
||||
|
||||
### 运作机制
|
||||
<div align=center><img src="./Flowchart.png"></div>
|
||||
|
||||
- 用户调用TFLite的BuildFromFile接口完成初始构图;
|
||||
- 用户设置自定义的参数options,应用于创建NnrtDelegate;
|
||||
- 用户创建DelegateProviders,并调用DelegateProviders的CreateAllRankedDelegates接口创建NnrtDelegate,创建NnrtDelegate过程中dlopen打开NNRt的动态库,并加载API,返回delegate;
|
||||
- 用户调用ModifyGraphWithDelegate接口完成Node替换,其中该步分四个步骤;
|
||||
- Initalize初始化NnrtDelegate;
|
||||
- 判断图中各node是否支持NnrtDelegate,返回支持的node集合;
|
||||
- 调用TFLiteRegistration注册NnrtDelegate,并初始化init, prepare, invoke成员函数指针,指向delegateKernel的Init, Prepare和run函数方法;
|
||||
- 替换TensorFlow Delegate的node为已注册的NNrt delegate kernel, 并调用Init完成构图步骤;
|
||||
- 用户调用AllocateTensors,完成内存分配和图编译,其中支持delegate的node会转到delegateKernel的prepare完成编译,不支持delegate的会调用原有tflite node的prepare编译;
|
||||
- 用户调用Invoke完成图执行;
|
||||
|
||||
### 开发流程
|
||||
<div align=center><img src="./Principle.png"></div>
|
||||
|
||||
### 开发步骤
|
||||
本节主要描述NNRt接入TFLite的TFLite-delegate代理机制,重点对TFLite调用delegate的流程和delegate对接NNRt的方式进行了介绍。
|
||||
TensorFlow Lite Delegate有两个基类DelegateProvider、TfLiteDelegate, 本节主要描述继承这两个基类得到子类NnrtDelegate和NnrtDelegateProvider。
|
||||
|
||||
本demo主要文件目录结构如下图:
|
||||
```text
|
||||
.
|
||||
├── CMakeLists.txt
|
||||
├── delegates
|
||||
│ └── nnrt_delegate
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── nnrt_delegate.cpp
|
||||
│ ├── nnrt_delegate.h
|
||||
│ ├── nnrt_delegate_kernel.cpp
|
||||
│ ├── nnrt_delegate_kernel.h
|
||||
│ ├── nnrt_delegate_provider.cpp
|
||||
│ ├── nnrt_op_builder.cpp
|
||||
│ ├── nnrt_op_builder.h
|
||||
│ ├── nnrt_utils.cpp
|
||||
│ ├── nnrt_utils.h
|
||||
│ └── tensor_mapping.h
|
||||
├── label_classify
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── label_classify.cpp
|
||||
│ └── label_classify.h
|
||||
├── nnrt
|
||||
│ ├── CMakeLists.txt
|
||||
│ ├── nnrt_implementation.cpp
|
||||
│ └── nnrt_implementation.h
|
||||
└── tools
|
||||
├── bitmap_helpers.cpp
|
||||
├── bitmap_helpers.h
|
||||
├── get_topn.h
|
||||
├── log.h
|
||||
├── utils.cpp
|
||||
└── utils.h
|
||||
```
|
||||
1. 创建Tensorflow Lite NnrtDelegate类
|
||||
- Tensorflow Lite NNRt Delegate 使TensorFlow Lite模型能够运行在NNRt框架(https://gitee.com/openharmony/neural_network_runtime)上,这导致了在OHOS设备上更快的模型推理
|
||||
- nnrt_delegate依赖nnrt_delegate_kernel, nnrt_delegate_kernel(将支持替换的TensorFlow Lite模型中的operators替换成Nnrt中的operators)依赖nnrt_op_builder(给每个operators设置输入输出tensor和operation属性),完成nnrt_delegate的自定义。
|
||||
|
||||
|
||||
2. 创建NnrtDelegateProvider
|
||||
- NnrtDelegateProvider依赖nnrt_implementation(用于加载libneural_network_runtime.z.so中的Api)和nnrt_delegate(用于创建子类NnrtDelegate对象),完成与TFLite的对接;
|
||||
|
||||
- 注册NnrtDelegateProvider
|
||||
```cpp
|
||||
REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
|
||||
```
|
||||
|
||||
- 创建CreateTfLiteDelegate主要有以下几步
|
||||
```cpp
|
||||
NnrtDelegate::Options options;
|
||||
|
||||
const auto* nnrtImpl = NnrtImplementation();
|
||||
if (!nnrtImpl->nnrtExists) {
|
||||
TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
|
||||
return delegate;
|
||||
}
|
||||
|
||||
Interpreter::TfLiteDelegatePtr TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
|
||||
[](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
|
||||
```
|
||||
|
||||
3. label_classify.cpp中加载Nnrt_Delegate
|
||||
```cpp
|
||||
interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate))
|
||||
```
|
||||
|
||||
### 调测命令
|
||||
1. 编译生成Tensorflow Lite库及其依赖库
|
||||
请参考Tensorflow Lite交叉编译指南(https://www.tensorflow.org/lite/guide/build_cmake_arm), 同时在```tensorflow/lite/CMakeLists.txt```中增加以下内容:
|
||||
```text
|
||||
# TODO: TFLite External Delegate
|
||||
list(APPEND TFLITE_EXTERNAL_DELEGATE_SRC
|
||||
${TFLITE_SOURCE_DIR}/tools/delegates/delegate_provider.cc
|
||||
# ${TFLITE_SOURCE_DIR}/tools/delegates/external_delegate_provider.cc
|
||||
${TFLITE_SOURCE_DIR}/tools/tool_params.cc
|
||||
${TFLITE_SOURCE_DIR}/tools/command_line_flags.cc
|
||||
)
|
||||
```
|
||||
```text
|
||||
target_link_libraries(tensorflow-lite
|
||||
PUBLIC
|
||||
Eigen3::Eigen
|
||||
NEON_2_SSE
|
||||
absl::flags
|
||||
absl::hash
|
||||
absl::status
|
||||
absl::strings
|
||||
absl::synchronization
|
||||
absl::variant
|
||||
farmhash
|
||||
fft2d_fftsg2d
|
||||
flatbuffers
|
||||
gemmlowp
|
||||
ruy
|
||||
${CMAKE_DL_LIBS}
|
||||
${TFLITE_TARGET_DEPENDENCIES}
|
||||
)
|
||||
```
|
||||
2. 编译生成NNRt库libneural_network_runtime.z.so
|
||||
请参考编译指导(https://gitee.com/openharmony/build),编译命令如下
|
||||
```shell
|
||||
./build.sh --product-name rk3568 –ccache --jobs=16 --build-target=neural_network_runtime
|
||||
```
|
||||
3. 用cmake编译北向demo
|
||||
- 将TensorFlow Lite头文件(https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite)和编译生成的TensorFlow Lite库,分别放在```deep_learning_framework/lib_3rd_nnrt_tflite/include/tensorflow/lite/```和```deep_learning_framework/lib_3rd_nnrt_tflite/com/arm64-v8a/lib/```下;
|
||||
- 指定ohos的cmake, ohos.toolchain.cmake路径,在```foundation/ai/neural_network_runtime/example/cmake_build/build_ohos_tflite.sh```中替换以下两行;
|
||||
```shell
|
||||
./tool_chain/native/build-tools/cmake/bin/cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/cmake_build/cmake/ohos.toolchain.cmake \
|
||||
```
|
||||
- 进入```foundation/ai/neural_network_runtime/example/cmake_build```:
|
||||
- 如果需要在arm32架构的CPU上运行:
|
||||
- 修改```tflite/CMakeLists.txt```
|
||||
```text
|
||||
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv7-a")
|
||||
```
|
||||
- 执行编译命令
|
||||
```shell
|
||||
bash build_ohos_tflite.sh armeabi-v7a
|
||||
```
|
||||
- 如果需要在arm64架构的CPU上运行:
|
||||
- 修改```tflite/CMakeLists.txt```
|
||||
```text
|
||||
set(CMAKE_CXX_FLAGS "-pthread -fstack-protector-all -fPIC -D_FORTIFY_SOURCE=2 -march=armv8-a")
|
||||
```
|
||||
- 执行编译命令
|
||||
```shell
|
||||
bash build_ohos_tflite.sh arm64-v8a
|
||||
```
|
||||
- 在```example/deep_learning_framework/```目录下创建lib和output两个文件夹:
|
||||
```shell
|
||||
mkdir lib output
|
||||
```
|
||||
- 进入```foundation/ai/neural_network_runtime/example/cmake_build```, 执行链接命令:
|
||||
```shell
|
||||
make
|
||||
```
|
||||
- 北向demo成功编译完成后会在```deep_learning_framework/lib```生成libnnrt_delegate.so和libnnrt_implementation.so, 在```deep_learning_framework/output```下生成label_classify可执行文件,目录结构体如下所示。
|
||||
|
||||
```text
|
||||
deep_learning_framework
|
||||
├── lib
|
||||
│ ├── libnnrt_delegate.so # 生成的TensorFlow Lite nnrt delegate库
|
||||
│ └── libnnrt_implementation.so # 生成的nnrt在TensorFlow Lite中接口实现库
|
||||
└── output
|
||||
└── label_classify # 生成的可执行文件
|
||||
```
|
||||
|
||||
4. 在开发板上运行北向demo
|
||||
- 将步骤1生成的libnnrt_implementation.so, libnnrt_delegate.so和可执行文件label_classify, libneural_network_runtime.z.so, tensorflow-lite.so及其依赖的库, mobilenetv2.tflite模型, 标签labels.txt, 测试图片grace_hopper.bmp推送到开发板上:
|
||||
```shell
|
||||
# 假设上述待推送文件均放在push_files/文件夹下
|
||||
hdc_std file send push_files/ /data/demo/
|
||||
```
|
||||
- 进入开发板,执行demo前需要添加环境变量,文件执行权限等:
|
||||
```shell
|
||||
# 进入开发板
|
||||
hdc_std shell
|
||||
|
||||
# 进入推送文件目录,并增加可执行文件权限
|
||||
cd /data/demo
|
||||
chmod +x ./label_classify
|
||||
|
||||
# 添加环境变量
|
||||
export LD_LIBRARY_PATH=/data/demo:$LD_LIBRARY_PATH
|
||||
|
||||
# 执行demo,-m tflite模型, -i 测试图片, -l 数据标签, -a 1表示使用nnrt, 0表示不使用nnrt推理,-z 1 表示打印输出张量大小的结果
|
||||
./label_classify -m mobilenetv2.tflite -i grace_hopper.bmp -l labels.txt -a 1 -z 1
|
||||
```
|
||||
|
||||
### 开发实例
|
||||
完整[Demo实例](xxx, Demo暂时还在黄区代码仓,超链接需等Demo开源后补充)可以参考社区实现。
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
function help_info() {
|
||||
echo "arm64-v8a(armeabi-v7a) means the CPU architecture is 64-bit(32-bit), the compile command like the following:"
|
||||
echo "bash build_ohos_tflite.sh arm64-v8a"
|
||||
}
|
||||
|
||||
function build() {
|
||||
echo "$1"
|
||||
./tool_chain/native/build-tools/cmake/bin/cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=./tool_chain/native/build/cmake/ohos.toolchain.cmake \
|
||||
-DOHOS_ARCH=$1 \
|
||||
-DOHOS_PLATFORM=OHOS \
|
||||
-DCMAKE_BUILD_TYPE=RELEASE \
|
||||
-DBUILD_SHARED_LIBS=true \
|
||||
-DOHOS_STL=c++_static \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
..
|
||||
}
|
||||
|
||||
if [ "$#" != 1 ]; then
|
||||
echo "Incorrect command, please pass the correct number of parameters to the compile command."
|
||||
help_info
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [ "$1" == "arm64-v8a" ]; then
|
||||
build arm64-v8a
|
||||
elif [ "$1" == "armeabi-v7a" ]; then
|
||||
build armeabi-v7a
|
||||
else
|
||||
echo "Incorrect CPU architecture parameter or missing setting it, please pass the correct compile command."
|
||||
help_info
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set(NNRT_INTERFACE_HOME ${LOCAL_DIRECTORY_PATH}/tflite/nnrt)
|
||||
set(NNRT_DELEGATE_HOME ${LOCAL_DIRECTORY_PATH}/tflite/delegates/nnrt_delegate)
|
||||
set(NNRT_DEMO_HOME ${LOCAL_DIRECTORY_PATH}/tflite/label_classify)
|
||||
set(TFLITE_LIB_PATH ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite)
|
||||
|
||||
add_subdirectory(${NNRT_INTERFACE_HOME})
|
||||
add_subdirectory(${NNRT_DELEGATE_HOME})
|
||||
add_subdirectory(${NNRT_DEMO_HOME})
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
set(TFLITE_PATH ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite)
|
||||
|
||||
LINK_DIRECTORIES(${TFLITE_PATH}/com/arm64-v8a/lib/)
|
||||
|
||||
# Header path
|
||||
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
|
||||
set(TOOLS_INC ${LOCAL_DIRECTORY_PATH}/tflite/tools)
|
||||
set(TFLITE_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include)
|
||||
include_directories(${LOCAL_DIRECTORY_PATH} ${NNRT_DELEGATE_HOME} ${TFLITE_INC} ${OHOS_INC} ${TOOLS_INC})
|
||||
|
||||
# Scr path
|
||||
file(GLOB NNRT_DELEGATE_SRCS "${NNRT_DELEGATE_HOME}/*.cpp")
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LOCAL_DIRECTORY_PATH}/lib)
|
||||
|
||||
add_library(nnrt_delegate SHARED ${NNRT_DELEGATE_SRCS})
|
||||
target_link_libraries(nnrt_delegate -ltensorflow-lite nnrt_implementation)
|
||||
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nnrt_delegate.h"
|
||||
|
||||
#include "tensorflow/lite/util.h"
|
||||
#include "tensorflow/lite/context_util.h"
|
||||
#include "tensorflow/lite/minimal_logging.h"
|
||||
|
||||
#include "nnrt_utils.h"
|
||||
#include "nnrt_delegate_kernel.h"
|
||||
|
||||
namespace tflite {
|
||||
const char* g_tfliteNnrtDelegateName = "TfLiteNnrtDelegate";
|
||||
constexpr int32_t TFLITE_NNRT_DELEGATE_VERSION = 1;
|
||||
|
||||
NnrtDelegate::Data::Data(const NnrtApi* nnrt) : nnrt(nnrt) {}
|
||||
|
||||
NnrtDelegate::Data::~Data() {}
|
||||
|
||||
void NnrtDelegate::NnrtDelegateConstructorImpl(const Options& options)
|
||||
{
|
||||
m_delegateData.acceleratorName = options.acceleratorName;
|
||||
m_delegateData.cacheDir = options.cacheDir;
|
||||
m_delegateData.modelToken = options.modelToken;
|
||||
m_delegateData.enableFp16 = options.enableFp16;
|
||||
m_delegateData.executionPriority = options.executionPriority;
|
||||
m_delegateData.executionPerformance = options.executionPerformance;
|
||||
m_delegateData.allowDynamicDimensions = options.allowDynamicDimensions;
|
||||
m_delegateData.maxNumberDelegatedPartitions = options.maxNumberDelegatedPartitions;
|
||||
m_delegateData.maxCompilationTimeoutDurationNs = options.maxCompilationTimeoutDurationNs;
|
||||
m_delegateData.maxExecutionTimeoutDurationNs = options.maxExecutionTimeoutDurationNs;
|
||||
m_delegateData.maxExecutionLoopTimeoutDurationNs = options.maxExecutionLoopTimeoutDurationNs;
|
||||
|
||||
Prepare = DoPrepare;
|
||||
CopyFromBufferHandle = DoCopyFromBufferHandle;
|
||||
CopyToBufferHandle = DoCopyToBufferHandle;
|
||||
FreeBufferHandle = DoFreeBufferHandle;
|
||||
data_ = &m_delegateData;
|
||||
|
||||
// NNRT support dynamic shape feature.
|
||||
flags |= kTfLiteDelegateFlagsAllowDynamicTensors;
|
||||
flags |= kTfLiteDelegateFlagsRequirePropagatedShapes;
|
||||
}
|
||||
|
||||
NnrtDelegate::NnrtDelegate(const NnrtApi* nnrt) : NnrtDelegate(nnrt, Options()) {}
|
||||
|
||||
NnrtDelegate::NnrtDelegate(const Options& options) : NnrtDelegate(NnrtImplementation(), options) {}
|
||||
|
||||
NnrtDelegate::NnrtDelegate(const NnrtApi* nnrt, const Options& options)
|
||||
: TfLiteDelegate(TfLiteDelegateCreate()), m_delegateData(nnrt)
|
||||
{
|
||||
NnrtDelegateConstructorImpl(options);
|
||||
}
|
||||
|
||||
NnrtDelegate::NnrtDelegate() : NnrtDelegate(Options()) {}
|
||||
|
||||
TfLiteStatus NnrtDelegate::GetOptions(const TfLiteDelegate* pDelegate, Options& options)
|
||||
{
|
||||
// Caller guarantees that parameters are legal
|
||||
auto pDelegateData = static_cast<Data*>(pDelegate->data_);
|
||||
options.acceleratorName = pDelegateData->acceleratorName;
|
||||
options.cacheDir = pDelegateData->cacheDir;
|
||||
options.modelToken = pDelegateData->modelToken;
|
||||
options.enableFp16 = pDelegateData->enableFp16;
|
||||
options.executionPriority = pDelegateData->executionPriority;
|
||||
options.executionPerformance = pDelegateData->executionPerformance;
|
||||
options.allowDynamicDimensions = pDelegateData->allowDynamicDimensions;
|
||||
options.maxNumberDelegatedPartitions = pDelegateData->maxNumberDelegatedPartitions;
|
||||
options.maxCompilationTimeoutDurationNs = pDelegateData->maxCompilationTimeoutDurationNs;
|
||||
options.maxExecutionTimeoutDurationNs = pDelegateData->maxExecutionTimeoutDurationNs;
|
||||
options.maxExecutionLoopTimeoutDurationNs = pDelegateData->maxExecutionLoopTimeoutDurationNs;
|
||||
options.version = pDelegateData->version;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::DoCopyFromBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor)
|
||||
{
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::DoCopyToBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor)
|
||||
{
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
void NnrtDelegate::DoFreeBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle* handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::LimitDelegatedPartitions(int32_t maxPartitions,
|
||||
std::vector<TfLiteDelegateParams> partitionParamsArray, std::vector<int32_t>& nodesToDelegate)
|
||||
{
|
||||
int32_t numPartitions = partitionParamsArray.size();
|
||||
if ((maxPartitions <= 0) || (numPartitions <= maxPartitions)) { // no limit or not exceed limit
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
int32_t numberDelegatedPartitions = std::count_if(
|
||||
partitionParamsArray.begin(), partitionParamsArray.end(),
|
||||
[nodesToDelegate](const TfLiteDelegateParams& partitionParams) {
|
||||
return std::find(nodesToDelegate.begin(), nodesToDelegate.end(),
|
||||
partitionParams.nodes_to_replace->data[0]) != nodesToDelegate.end();
|
||||
});
|
||||
// Adapt maxPartitions to limit delegate paritions, sort and abandon the low-ranking nodes.
|
||||
if (numberDelegatedPartitions > maxPartitions) {
|
||||
std::sort(partitionParamsArray.begin(), partitionParamsArray.end(),
|
||||
[](const TfLiteDelegateParams& left, const TfLiteDelegateParams& right) -> bool {
|
||||
return left.nodes_to_replace->size > right.nodes_to_replace->size;
|
||||
});
|
||||
|
||||
nodesToDelegate.clear();
|
||||
|
||||
for (int32_t i = 0; i < maxPartitions; ++i) {
|
||||
const TfLiteDelegateParams& partitionParams = partitionParamsArray[i];
|
||||
nodesToDelegate.insert(nodesToDelegate.end(),
|
||||
partitionParams.nodes_to_replace->data,
|
||||
partitionParams.nodes_to_replace->data +
|
||||
partitionParams.nodes_to_replace->size);
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::GetSupportedNodes(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, std::vector<int32_t>& supportedNodes)
|
||||
{
|
||||
// Caller guarantees that parameters are legal
|
||||
TfLiteIntArray* executionPlan = nullptr;
|
||||
TF_LITE_ENSURE_STATUS(context->GetExecutionPlan(context, &executionPlan));
|
||||
TF_LITE_ENSURE_EQ(context, executionPlan != nullptr, true);
|
||||
|
||||
// Check for every node if it is supported
|
||||
TfLiteNode* node = nullptr;
|
||||
TfLiteRegistration* registration = nullptr;
|
||||
for (auto nodeIndex : TfLiteIntArrayView(executionPlan)) {
|
||||
node = nullptr;
|
||||
registration = nullptr;
|
||||
TF_LITE_ENSURE_STATUS(context->GetNodeAndRegistration(context, nodeIndex, &node, ®istration));
|
||||
if (NnrtDelegateKernel::Validate(registration->builtin_code)) {
|
||||
supportedNodes.emplace_back(nodeIndex);
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_WARNING,
|
||||
"[NNRT-DELEGATE] Get unsupportted node: %d.", registration->builtin_code);
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void NnrtDelegate::GetDelegateKernelRegistration(TfLiteDelegate* delegate, TfLiteRegistration& nnrtDelegateKernel)
|
||||
{
|
||||
// Caller guarantees that parameters are legal
|
||||
nnrtDelegateKernel.profiling_string = nullptr;
|
||||
nnrtDelegateKernel.builtin_code = kTfLiteBuiltinDelegate;
|
||||
nnrtDelegateKernel.custom_name = g_tfliteNnrtDelegateName;
|
||||
nnrtDelegateKernel.version = TFLITE_NNRT_DELEGATE_VERSION;
|
||||
|
||||
nnrtDelegateKernel.init = [](TfLiteContext* context, const char* buffer, size_t length) -> void* {
|
||||
if (buffer == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const TfLiteDelegateParams* params = reinterpret_cast<const TfLiteDelegateParams*>(buffer);
|
||||
auto* delegateData = static_cast<Data*>(params->delegate->data_);
|
||||
NnrtDelegateKernel* state = new (std::nothrow) NnrtDelegateKernel(delegateData->nnrt);
|
||||
if (state == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to create NnrtDelegateKernel instance.");
|
||||
return state;
|
||||
}
|
||||
|
||||
TfLiteStatus status = state->Init(context, params);
|
||||
if (status != kTfLiteOk) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to init NnrtDelegateKernel.");
|
||||
delete state;
|
||||
state = nullptr;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
nnrtDelegateKernel.free = [](TfLiteContext* context, void* buffer) -> void {
|
||||
if (buffer != nullptr) {
|
||||
delete static_cast<NnrtDelegateKernel*>(buffer);
|
||||
buffer = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
nnrtDelegateKernel.prepare = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
|
||||
if (node == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to prepare delegate kernels, the node is nullptr.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
|
||||
return state->Prepare(context, node);
|
||||
};
|
||||
|
||||
nnrtDelegateKernel.invoke = [](TfLiteContext* context, TfLiteNode* node) -> TfLiteStatus {
|
||||
if (node == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to invoke delegate kernels, the node is nullptr.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
NnrtDelegateKernel* state = reinterpret_cast<NnrtDelegateKernel*>(node->user_data);
|
||||
return state->Invoke(context, node);
|
||||
};
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::CheckDeviceValid(TfLiteContext* context, TfLiteDelegate* delegate)
|
||||
{
|
||||
// Caller guarantees that parameters are legal
|
||||
auto* delegateData = static_cast<Data*>(delegate->data_);
|
||||
if (delegateData == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Delegate data not be found.");
|
||||
return kTfLiteDelegateDataNotFound;
|
||||
}
|
||||
|
||||
const NnrtApi* nnrt = delegateData->nnrt;
|
||||
if (nnrt == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "Failed to get nnrt instance.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
|
||||
|
||||
if (tflite::IsUseTargetDevice(delegateOptions)) {
|
||||
size_t nnrtDevice;
|
||||
TF_LITE_ENSURE_STATUS(GetTargetDevice(context, delegate, nnrt, nnrtDevice));
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegate::DoPrepare(TfLiteContext* context, TfLiteDelegate* delegate)
|
||||
{
|
||||
if ((context == nullptr) || (delegate == nullptr)) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE] Input TFLite-Context or TFLite-Delegate is nullptr.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
auto* delegateData = static_cast<Data*>(delegate->data_);
|
||||
const NnrtApi* nnrt = delegateData->nnrt;
|
||||
|
||||
// Do not delegate nodes_ if NN API is unavailable.
|
||||
if (!nnrt->nnrtExists) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Check devices validity
|
||||
TF_LITE_ENSURE_STATUS(CheckDeviceValid(context, delegate));
|
||||
|
||||
// Get supportted nodes by tflite.
|
||||
// We don't care about all nodes_, we only care about ones in the current plan.
|
||||
std::vector<int32_t> supportedNodes;
|
||||
GetSupportedNodes(context, delegate, supportedNodes);
|
||||
|
||||
// If there are no delegated nodes, short-circuit node replacement.
|
||||
if (supportedNodes.empty()) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "[NNRT-DELEGATE] supportted node list is empty.");
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
static TfLiteRegistration nnrtDelegateKernel;
|
||||
GetDelegateKernelRegistration(delegate, nnrtDelegateKernel);
|
||||
|
||||
std::vector<int32_t> nodesToDelegate(supportedNodes);
|
||||
int32_t numPartitions;
|
||||
TfLiteDelegateParams* paramsArray = nullptr;
|
||||
auto supportedNodesArray = BuildTfLiteIntArray(supportedNodes);
|
||||
TF_LITE_ENSURE_STATUS(context->PreviewDelegatePartitioning(
|
||||
context, supportedNodesArray.get(), ¶msArray, &numPartitions));
|
||||
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
|
||||
const auto partitionParamsArray = std::vector<TfLiteDelegateParams>(paramsArray, paramsArray + numPartitions);
|
||||
TF_LITE_ENSURE_STATUS(LimitDelegatedPartitions(
|
||||
delegateOptions.maxNumberDelegatedPartitions, partitionParamsArray, nodesToDelegate));
|
||||
|
||||
auto nodesToDelegateArray = BuildTfLiteIntArray(nodesToDelegate);
|
||||
if (nodesToDelegateArray->size == 0) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_INFO, "[NNRT-DELEGATE] No node to delegate.");
|
||||
return kTfLiteOk;
|
||||
} else {
|
||||
// Request TFLite to partition the graph and make kernels
|
||||
// for each independent node sub set a new nnrtDelegateKernel.
|
||||
return context->ReplaceNodeSubsetsWithDelegateKernels(context,
|
||||
nnrtDelegateKernel, nodesToDelegateArray.get(), delegate);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a singleton NNRT Delegate that can check ops supported.
|
||||
TfLiteDelegate* NnrtDelegateSingleton()
|
||||
{
|
||||
static NnrtDelegate delegate;
|
||||
return &delegate;
|
||||
}
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
|
||||
#define TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "neural_network_runtime.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/delegates/serialization.h"
|
||||
|
||||
#include "../nnrt/nnrt_implementation.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
class NnrtDelegateKernel;
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
|
||||
using tflite::delegate::nnrt::NnrtDelegateKernel;
|
||||
|
||||
// TFliteDelegate to interface with NNRT.
|
||||
class NnrtDelegate : public TfLiteDelegate {
|
||||
public:
|
||||
struct Options {
|
||||
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
|
||||
std::string acceleratorName;
|
||||
std::string cacheDir;
|
||||
std::string modelToken;
|
||||
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
|
||||
int32_t maxNumberDelegatedPartitions = -1;
|
||||
uint64_t maxCompilationTimeoutDurationNs = 0;
|
||||
uint64_t maxExecutionTimeoutDurationNs = 0;
|
||||
uint64_t maxExecutionLoopTimeoutDurationNs = 0;
|
||||
// allow fp32 compuation to be run in fp16.
|
||||
bool enableFp16 = false;
|
||||
bool allowDynamicDimensions = false;
|
||||
|
||||
uint32_t version {0};
|
||||
};
|
||||
|
||||
// Uses default options.
|
||||
NnrtDelegate();
|
||||
|
||||
// The ownership of the NNRT instance is left to the caller of the
|
||||
// NnrtDelegate constructor; the caller must ensure that the lifetime
|
||||
// of the NNRT instance exceeds the lifetime of the NnrtDelegate.
|
||||
explicit NnrtDelegate(const NnrtApi* nnrt);
|
||||
|
||||
// The constructor that accepts options from user.
|
||||
// This makes a copy of any data that it needs from Options, so
|
||||
// the caller can safely deallocate any storage pointed to by
|
||||
// the 'const char *' members of Options immediately after calling this.
|
||||
explicit NnrtDelegate(const Options& options);
|
||||
|
||||
// Constructor that accepts both an NNRT instance and options.
|
||||
// The ownership of the NNRT instance is left to the caller of the
|
||||
// NnrtDelegate constructor; the caller must ensure that the lifetime
|
||||
// of the NNRT instance exceeds the lifetime of the NnrtDelegate.
|
||||
// This constructor makes a copy of any data that it needs from Options, so
|
||||
// the caller can safely deallocate any storage pointed to by
|
||||
// the 'const char *' members of Options immediately after calling this.
|
||||
NnrtDelegate(const NnrtApi* nnrt, const Options& options);
|
||||
|
||||
~NnrtDelegate() = default;
|
||||
|
||||
// Returns the delegate options.
|
||||
// The lifetime of the storage pointed to by the 'const char *' members of the
|
||||
// returned Options object is the same as the lifetime of the supplied
|
||||
// TfLiteDelegate instance.
|
||||
static TfLiteStatus GetOptions(const TfLiteDelegate* pDelegate, Options& options);
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
const NnrtApi* nnrt = nullptr;
|
||||
|
||||
// Preferred Power/perf trade-off.
|
||||
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
|
||||
|
||||
// Selected NNRT accelerator name.
|
||||
std::string acceleratorName;
|
||||
|
||||
// The cache dir for NNRT model.
|
||||
std::string cacheDir;
|
||||
|
||||
// The unique token string for NNRT model.
|
||||
std::string modelToken;
|
||||
|
||||
// Maximum number of NNRT partition to delegate. Zero or negative means
|
||||
// no limit.
|
||||
int32_t maxNumberDelegatedPartitions = -1;
|
||||
|
||||
// Specifies the relative priority for executions of the model.
|
||||
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
|
||||
|
||||
// Specifies the maximum expected duration in nanosecond for compiling the
|
||||
// model.
|
||||
uint64_t maxCompilationTimeoutDurationNs = 0;
|
||||
|
||||
// Specifies the maximum expected duration in nanosecond for executing the
|
||||
// model.
|
||||
uint64_t maxExecutionTimeoutDurationNs = 0;
|
||||
|
||||
// Specifies the maximum expected duration in nanosecond for WHILE loops in
|
||||
// the execution
|
||||
uint64_t maxExecutionLoopTimeoutDurationNs = 0;
|
||||
|
||||
// allow fp32 compuation to be run in fp16.
|
||||
bool enableFp16 = false;
|
||||
|
||||
// Whether to allow dynamic dimension sizes without re-compilation.
|
||||
bool allowDynamicDimensions = false;
|
||||
|
||||
uint32_t version {0};
|
||||
|
||||
explicit Data(const NnrtApi* nnrt);
|
||||
~Data();
|
||||
};
|
||||
|
||||
static TfLiteStatus DoPrepare(TfLiteContext* context, TfLiteDelegate* delegate);
|
||||
|
||||
static TfLiteStatus DoCopyFromBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor);
|
||||
|
||||
static TfLiteStatus DoCopyToBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle bufferHandle, TfLiteTensor* tensor);
|
||||
|
||||
static void DoFreeBufferHandle(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, TfLiteBufferHandle* handle);
|
||||
|
||||
static TfLiteStatus LimitDelegatedPartitions(int32_t maxPartitions,
|
||||
std::vector<TfLiteDelegateParams> partitionParamsArray, std::vector<int32_t>& nodesToDelegate);
|
||||
|
||||
static TfLiteStatus GetSupportedNodes(TfLiteContext* context,
|
||||
TfLiteDelegate* delegate, std::vector<int32_t>& supportedNodes);
|
||||
|
||||
static void GetDelegateKernelRegistration(TfLiteDelegate* delegate, TfLiteRegistration& nnrtDelegateKernel);
|
||||
|
||||
static TfLiteStatus CheckDeviceValid(TfLiteContext* context, TfLiteDelegate* delegate);
|
||||
|
||||
void NnrtDelegateConstructorImpl(const Options& options);
|
||||
|
||||
private:
|
||||
// Delegate data presented through TfLiteDelegate::data_.
|
||||
Data m_delegateData;
|
||||
};
|
||||
|
||||
TfLiteDelegate* NnrtDelegateSingleton();
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_H
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nnrt_delegate_kernel.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/lite/context_util.h"
|
||||
#include "neural_network_runtime.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
constexpr int32_t SCALAR_RANK = 1;
|
||||
|
||||
#define RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(code, callDesc) \
|
||||
do { \
|
||||
if ( (code) != OH_NN_SUCCESS) { \
|
||||
const auto errorDesc = NnrtErrorDescription((code)); \
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "NN API returned error %s at line %d while %s.\n", errorDesc.c_str(), \
|
||||
__LINE__, (callDesc)); \
|
||||
m_nnrt->OH_NNCompilation_Destroy(&m_pNnCompilation); \
|
||||
return kTfLiteError; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
bool NnrtDelegateKernel::Validate(const int32_t builtinCode)
|
||||
{
|
||||
if (TFLITE_TYPE_TO_NNRT_TYPE.count(builtinCode) &&
|
||||
TFLITE_TYPE_TO_NNRT_TYPE.at(builtinCode) != OH_NN_UNSUPPORT_OPS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::Init(TfLiteContext* context, const TfLiteDelegateParams* params)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, params != nullptr, true);
|
||||
|
||||
if (m_initialised) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_INFO,
|
||||
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel has completed initialization, no need init again.");
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
for (auto nodeIndex : TfLiteIntArrayView(params->nodes_to_replace)) {
|
||||
m_delegateNodes.emplace_back(nodeIndex);
|
||||
}
|
||||
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(params->delegate, delegateOptions));
|
||||
TF_LITE_ENSURE_STATUS(tflite::GetTargetDevice(context, params->delegate, m_nnrt, m_nnrtDevice));
|
||||
if (m_nnModel == nullptr) {
|
||||
m_nnModel = m_nnrt->OH_NNModel_Construct();
|
||||
if (m_nnModel == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create ONNRT model.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_STATUS(BuildGraph(context, delegateOptions, params->input_tensors, params->output_tensors));
|
||||
}
|
||||
|
||||
m_initialised = true;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::Prepare(TfLiteContext* context, TfLiteNode* node)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
|
||||
|
||||
if (!m_initialised) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel Prepare failed, not Init yet.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (m_compiled) {
|
||||
return kTfLiteOk; // If model has completed compilation, no need compile again.
|
||||
}
|
||||
|
||||
// Create OH_NNCompilation
|
||||
m_pNnCompilation = m_nnrt->OH_NNCompilation_Construct(m_nnModel);
|
||||
if (m_pNnCompilation == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create OH_NNCompilation instance.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(node->delegate, delegateOptions));
|
||||
|
||||
TF_LITE_ENSURE_STATUS(SetNnOptions(context, delegateOptions));
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(m_nnrt->OH_NNCompilation_Build(m_pNnCompilation),
|
||||
"completing NNRT compilation");
|
||||
|
||||
m_compiled = true;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::Invoke(TfLiteContext* context, TfLiteNode* node)
|
||||
{
|
||||
if (!m_compiled) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-DELEGATE_KERNEL] NnrtDelegateKernel Invoke failed, not compile yet.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Create OH_NNExecutor_Construct
|
||||
OH_NNExecutor* pNnExecution {nullptr};
|
||||
pNnExecution = m_nnrt->OH_NNExecutor_Construct(m_pNnCompilation);
|
||||
if (pNnExecution == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Fail to create OH_NNExecutor instance.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Set the input tensor buffers.
|
||||
OH_NN_Tensor inputNnTensor;
|
||||
TF_LITE_ENSURE_STATUS(SetInputTensors(context, node, pNnExecution, inputNnTensor));
|
||||
|
||||
// Get the output tensor buffers.
|
||||
TF_LITE_ENSURE_STATUS(SetOutputTensors(context, node, pNnExecution));
|
||||
|
||||
// Invoke delegated subgraph.
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNExecutor_Run(pNnExecution), "running computation");
|
||||
|
||||
m_nnrt->OH_NNExecutor_Destroy(&pNnExecution);
|
||||
pNnExecution = nullptr;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::Map(const int32_t builtinCode, const NnrtOpMappingArgs& mappingArgs,
|
||||
int32_t& nnOpType) const
|
||||
{
|
||||
if (TFLITE_TYPE_TO_NNRT_TYPE.count(builtinCode) == 0) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-DELEGATE_KERNEL] Not support current TF-Lite Operator, builtCode: %d.", builtinCode);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TfLiteStatus retValue = mappingArgs.builder->AddOpFuncParams(mappingArgs, builtinCode);
|
||||
if (retValue != kTfLiteOk) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Failed to add params to these operations.");
|
||||
return retValue;
|
||||
}
|
||||
nnOpType = TFLITE_TYPE_TO_NNRT_TYPE.at(builtinCode);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::BuildGraph(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions,
|
||||
const TfLiteIntArray* inputTensors, const TfLiteIntArray* outputTensors)
|
||||
{
|
||||
if (context == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] The context is nullptr when building the graph.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, inputTensors != nullptr, true);
|
||||
TF_LITE_ENSURE_EQ(context, outputTensors != nullptr, true);
|
||||
|
||||
// Build the ops and tensors.
|
||||
TF_LITE_ENSURE_STATUS(AddOpsAndTensors(context, inputTensors, delegateOptions));
|
||||
// Map input and output tensor indices to NN
|
||||
// Make the TensorFlow Lite inputs and outputs to nn_indices.
|
||||
OH_NN_UInt32Array inputIndices;
|
||||
OH_NN_UInt32Array outputIndices;
|
||||
std::vector<uint32_t> inputsData;
|
||||
for (auto i : TfLiteIntArrayView(inputTensors)) {
|
||||
// Constant tensors are not NNRT inputs.
|
||||
if ((i != kTfLiteOptionalTensor) && (context->tensors[i].allocation_type != kTfLiteMmapRo) &&
|
||||
// The delegate might not have mapped this input (this can
|
||||
// happen if one tensor is split in several ones)
|
||||
(m_tensorMapping.LiteIndexToNn(i) != INVALID_INDEX)) {
|
||||
const int32_t inputTensorNnIndex = m_tensorMapping.LiteIndexToNn(i);
|
||||
inputsData.emplace_back(inputTensorNnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> outputsData;
|
||||
for (auto i : TfLiteIntArrayView(outputTensors)) {
|
||||
const int32_t outputTensorNnIndex = m_tensorMapping.LiteIndexToNn(i);
|
||||
// Unmapped outputs are not added
|
||||
if (outputTensorNnIndex != INVALID_INDEX) {
|
||||
outputsData.emplace_back(outputTensorNnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
inputIndices.data = inputsData.data();
|
||||
outputIndices.data = outputsData.data();
|
||||
inputIndices.size = inputsData.size();
|
||||
outputIndices.size = outputsData.size();
|
||||
// Tell NN to declare inputs/outputs
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_SpecifyInputsAndOutputs(m_nnModel, &inputIndices,
|
||||
&outputIndices), "identifying model inputs and outputs");
|
||||
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_Finish(m_nnModel), "finalizing the model");
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::AddOpsAndTensors(TfLiteContext* context, const TfLiteIntArray* inputTensors,
|
||||
const NnrtDelegate::Options& delegateOptions)
|
||||
{
|
||||
// The tensor builder allows creating a single op. It is created outside
|
||||
// the for loop to avoid reallocating the vectors.
|
||||
NnrtOpBuilderArgs opBuilderArgs = {
|
||||
.context = context,
|
||||
.nnModel = m_nnModel,
|
||||
.inputTensors = const_cast<TfLiteIntArray*>(inputTensors),
|
||||
.pTensorMapping = &m_tensorMapping,
|
||||
.delegateOptions = delegateOptions
|
||||
};
|
||||
NnrtOpBuilder builder(m_nnrt, opBuilderArgs);
|
||||
|
||||
// Clear the input and output lists.
|
||||
builder.ClearInputOuputLists();
|
||||
|
||||
// Add other tensors.
|
||||
TfLiteNode* node = nullptr;
|
||||
TfLiteRegistration* reg = nullptr;
|
||||
for (int32_t nodeIndex : m_delegateNodes) {
|
||||
node = nullptr;
|
||||
reg = nullptr;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
context->GetNodeAndRegistration(context, nodeIndex, &node, ®)); // Obtain the op and registration.
|
||||
if ((node == nullptr) || (reg == nullptr)) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-DELEGATE_KERNEL] Get node and registration failed.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
const bool scalarAsTensor = IsScalarInputSupported(reg->builtin_code);
|
||||
int32_t inputTensorFlags = 0;
|
||||
if (scalarAsTensor) {
|
||||
inputTensorFlags |= NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
|
||||
}
|
||||
|
||||
// Get op type and tensors, fails if the Validate function failed.
|
||||
int32_t nnOpType;
|
||||
NnrtOpMappingArgs opMappingArgs = { context, &builder, node, nodeIndex };
|
||||
TF_LITE_ENSURE_STATUS(Map(reg->builtin_code, opMappingArgs, nnOpType));
|
||||
|
||||
for (int32_t inputPos = 0; inputPos < node->inputs->size; ++inputPos) {
|
||||
if ((reg->builtin_code == kTfLiteBuiltinFullyConnected) &&
|
||||
(node->inputs->data[inputPos] == kTfLiteOptionalTensor)) {
|
||||
continue; // skip optional bias and handle it during mapping.
|
||||
}
|
||||
const auto inputIndex = node->inputs->data[inputPos];
|
||||
TF_LITE_ENSURE_STATUS(builder.AddTensorInput(inputIndex, reg->builtin_code, inputTensorFlags));
|
||||
}
|
||||
// Map outputs to NN API tensor indices.
|
||||
int32_t outputTensorFlags = 0;
|
||||
for (int32_t outputPos = 0; outputPos < node->outputs->size; ++outputPos) {
|
||||
auto outputIndex = node->outputs->data[outputPos];
|
||||
TF_LITE_ENSURE_STATUS(builder.AddTensorOutput(outputIndex, reg->builtin_code, outputTensorFlags));
|
||||
}
|
||||
TF_LITE_ENSURE_STATUS(builder.FinalizeAddOperation(static_cast<OH_NN_OperationType>(nnOpType), nodeIndex));
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::ConvertTensorTypeToNn(TfLiteContext* context,
|
||||
const std::pair<int32_t, int32_t>& indexPair, OH_NN_QuantParam* nnQuantParam, OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, context->tensors_size > indexPair.first, true);
|
||||
TfLiteTensor* tensor = &(context->tensors[indexPair.first]);
|
||||
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
|
||||
|
||||
OH_NN_DataType nnType {OH_NN_UNKNOWN};
|
||||
TF_LITE_ENSURE_STATUS(m_tensorMapping.ConvertType(context, indexPair.first, 0, nnType));
|
||||
|
||||
uint32_t tensorRank = static_cast<uint32_t>(tensor->dims->size);
|
||||
int32_t* tensorDims = reinterpret_cast<int32_t*>(tensor->dims->data);
|
||||
if (tensorDims == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-DELEGATE_KERNEL] The tensorDims is nullptr when converting the type of tensors to nnrt.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// treat scalar input as single cell tensor in NNRT.
|
||||
if (tensorRank == 0) {
|
||||
tensorRank = SCALAR_RANK;
|
||||
*tensorDims = SCALAR_RANK;
|
||||
}
|
||||
|
||||
nnTensor.dataType = nnType;
|
||||
nnTensor.dimensionCount = tensorRank;
|
||||
nnTensor.dimensions = tensorDims;
|
||||
nnTensor.quantParam = nnQuantParam;
|
||||
nnTensor.type = OH_NN_TENSOR;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::SetInputTensors(TfLiteContext* context, TfLiteNode* node,
|
||||
OH_NNExecutor* pNnExecution, OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
|
||||
TF_LITE_ENSURE_EQ(context, pNnExecution != nullptr, true);
|
||||
|
||||
// Note: we access tflite tensors using
|
||||
// absolute indices but NN api indices inputs by relative indices.
|
||||
int32_t relativeIndex = 0;
|
||||
OH_NN_QuantParam* nnQuantParam = nullptr;
|
||||
TfLiteIntArray* tensors = node->inputs;
|
||||
TF_LITE_ENSURE_EQ(context, tensors != nullptr, true);
|
||||
|
||||
for (auto absoluteIndex : TfLiteIntArrayView(tensors)) {
|
||||
if (absoluteIndex == kTfLiteOptionalTensor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::pair<int32_t, int32_t> indexPair = std::make_pair(absoluteIndex, relativeIndex);
|
||||
ConvertTensorTypeToNn(context, indexPair, nnQuantParam, nnTensor);
|
||||
|
||||
TfLiteTensor* tensor = &context->tensors[absoluteIndex];
|
||||
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
|
||||
|
||||
if (tensor->allocation_type != kTfLiteMmapRo) {
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(m_nnrt->OH_NNExecutor_SetInput(pNnExecution, relativeIndex,
|
||||
&nnTensor, tensor->data.raw, tensor->bytes),
|
||||
"associating NNRT execution output with a memory object", tensor);
|
||||
++relativeIndex;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::SetOutputTensors(TfLiteContext* context, TfLiteNode* node,
|
||||
OH_NNExecutor* pNnExecution)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, node != nullptr, true);
|
||||
TF_LITE_ENSURE_EQ(context, pNnExecution != nullptr, true);
|
||||
|
||||
// Note: we access tflite tensors using
|
||||
// absolute indices but NN api indices inputs by relative indices.
|
||||
int32_t relativeIndex = 0;
|
||||
TfLiteIntArray* tensors = node->outputs;
|
||||
TF_LITE_ENSURE_EQ(context, tensors != nullptr, true);
|
||||
for (auto absoluteIndex : TfLiteIntArrayView(tensors)) {
|
||||
if (m_tensorMapping.LiteIndexToNn(absoluteIndex) == INVALID_INDEX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TfLiteTensor* tensor = &context->tensors[absoluteIndex];
|
||||
TF_LITE_ENSURE_EQ(context, tensor != nullptr, true);
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(
|
||||
m_nnrt->OH_NNExecutor_SetOutput(pNnExecution, relativeIndex, tensor->data.raw, tensor->bytes),
|
||||
"associating NNRT execution output to a memory object", tensor);
|
||||
++relativeIndex;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtDelegateKernel::SetNnOptions(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions)
|
||||
{
|
||||
if (context == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-DELEGATE_KERNEL] The context is nullptr when setting nnrt options.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNCompilation_SetDevice(m_pNnCompilation, m_nnrtDevice),
|
||||
"creating NNRT compilation");
|
||||
|
||||
auto performance = delegateOptions.executionPerformance;
|
||||
if (performance != OH_NN_PERFORMANCE_NONE) {
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(
|
||||
m_nnrt->OH_NNCompilation_SetPerformanceMode(m_pNnCompilation, performance),
|
||||
"setting compilation performance");
|
||||
}
|
||||
|
||||
// Set cacahe, if cacheDir & modelToken & device is valid.
|
||||
std::string cacheDir = delegateOptions.cacheDir;
|
||||
std::string modelToken = delegateOptions.modelToken;
|
||||
uint32_t version = delegateOptions.version;
|
||||
if (!cacheDir.empty() && (!IsUseTargetDevice(delegateOptions) ||
|
||||
(delegateOptions.acceleratorName == NNRT_REFERENCE_DEVICE))) {
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_COMPILE(
|
||||
m_nnrt->OH_NNCompilation_SetCache(m_pNnCompilation, cacheDir.c_str(), version),
|
||||
"setting compilation cache");
|
||||
} else if (cacheDir.empty()) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_WARNING, "The cacheDir is empty, will not load or save cache.");
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // tflite
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
|
||||
#define TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
|
||||
|
||||
|
||||
#include "neural_network_runtime.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
#include "tensor_mapping.h"
|
||||
#include "nnrt_op_builder.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
|
||||
// Represents a subgraph in TFLite that will be delegated to NNRt.
|
||||
// It is abstracted as a single kernel node in the main TFLite graph and
|
||||
// implements Init/Prepare/Invoke as TFLite kernel nodes.
|
||||
class NnrtDelegateKernel {
|
||||
public:
|
||||
explicit NnrtDelegateKernel(const NnrtApi* nnrt)
|
||||
: m_initialised(false),
|
||||
m_compiled(false),
|
||||
m_nnrt(nnrt),
|
||||
m_nnModel(nullptr),
|
||||
m_pNnCompilation(nullptr) {}
|
||||
|
||||
NnrtDelegateKernel() : NnrtDelegateKernel(NnrtImplementation()) {}
|
||||
virtual ~NnrtDelegateKernel()
|
||||
{
|
||||
m_nnrt->OH_NNModel_Destroy(&m_nnModel);
|
||||
m_nnrt->OH_NNCompilation_Destroy(&m_pNnCompilation);
|
||||
m_nnrt = nullptr;
|
||||
}
|
||||
|
||||
// Returns true if the node can be accelerated with NNRT.
|
||||
static bool Validate(const int32_t builtinCode);
|
||||
|
||||
// Initialize the kernel (a NN model) and builds the NN Model.
|
||||
TfLiteStatus Init(TfLiteContext* context, const TfLiteDelegateParams* params);
|
||||
|
||||
// Creates the NNRT Compilation for the NN model. It assumes that Init has
|
||||
// been called and completed successfully.
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// Invoke the NN Model. Expects Init and Prepare to have been completed successfully.
|
||||
TfLiteStatus Invoke(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
private:
|
||||
TfLiteStatus Map(int32_t builtinCode, const NnrtOpMappingArgs& mappingArgs, int32_t& nnOpType) const;
|
||||
TfLiteStatus AddOpsAndTensors(TfLiteContext* context, const TfLiteIntArray* inputTensors,
|
||||
const NnrtDelegate::Options& delegateOptions);
|
||||
TfLiteStatus BuildGraph(TfLiteContext* context, const NnrtDelegate::Options& options,
|
||||
const TfLiteIntArray* inputTensors, const TfLiteIntArray* outputTensors);
|
||||
TfLiteStatus ConvertTensorTypeToNn(TfLiteContext* context, const std::pair<int32_t, int32_t>& indexPair,
|
||||
OH_NN_QuantParam* nnQuantParam, OH_NN_Tensor& nnTensor);
|
||||
TfLiteStatus SetInputTensors(TfLiteContext* context, TfLiteNode* node, OH_NNExecutor* pNnExecution,
|
||||
OH_NN_Tensor& nnTensor);
|
||||
TfLiteStatus SetOutputTensors(TfLiteContext* context, TfLiteNode* node, OH_NNExecutor* pNnExecution);
|
||||
TfLiteStatus SetNnOptions(TfLiteContext* context, const NnrtDelegate::Options& delegateOptions);
|
||||
|
||||
private:
|
||||
// True if initialization has been completed successfully
|
||||
bool m_initialised;
|
||||
|
||||
// True if compilation has been completed successfully
|
||||
bool m_compiled;
|
||||
|
||||
// NN device handle.
|
||||
size_t m_nnrtDevice;
|
||||
|
||||
// Access to NNRT.
|
||||
const NnrtApi* m_nnrt;
|
||||
|
||||
// NN API state.
|
||||
OH_NNModel* m_nnModel;
|
||||
OH_NNCompilation* m_pNnCompilation;
|
||||
|
||||
// Node indices that this delegate is responsible for. Indices here
|
||||
// indexes into the nodes array in the TfLiteContext.
|
||||
std::vector<int32_t> m_delegateNodes;
|
||||
|
||||
// Track indices we use
|
||||
TensorMapping m_tensorMapping;
|
||||
};
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_DELEGATE_KERNEL_H
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
|
||||
|
||||
#include "nnrt_delegate.h"
|
||||
#include "../nnrt/nnrt_implementation.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace tools {
|
||||
constexpr int32_t DEFAULT_THREADS = 1;
|
||||
constexpr int32_t DEFAULT_DELEGATE_NUM = -1;
|
||||
class NnrtDelegateProvider : public DelegateProvider {
|
||||
public:
|
||||
NnrtDelegateProvider()
|
||||
{
|
||||
default_params_.AddParam("use_nnrt", ToolParam::Create<bool>(false));
|
||||
default_params_.AddParam("performance", ToolParam::Create<std::string>(""));
|
||||
default_params_.AddParam("priority", ToolParam::Create<std::string>(""));
|
||||
default_params_.AddParam("device", ToolParam::Create<std::string>(""));
|
||||
default_params_.AddParam("cache_dir", ToolParam::Create<std::string>(""));
|
||||
default_params_.AddParam("model_token", ToolParam::Create<std::string>(""));
|
||||
default_params_.AddParam("max_delegate_num", ToolParam::Create<int32_t>(DEFAULT_DELEGATE_NUM));
|
||||
default_params_.AddParam("enable_fp16", ToolParam::Create<bool>(false));
|
||||
default_params_.AddParam("allow_dynamic_dimensions", ToolParam::Create<bool>(false));
|
||||
}
|
||||
|
||||
~NnrtDelegateProvider() {};
|
||||
|
||||
std::vector<Flag> CreateFlags(ToolParams* param) const final;
|
||||
|
||||
void LogParams(const ToolParams& params, bool verbose) const final;
|
||||
|
||||
TfLiteDelegatePtr CreateTfLiteDelegate(const ToolParams& params) const final;
|
||||
|
||||
std::pair<TfLiteDelegatePtr, int32_t> CreateRankedTfLiteDelegate(const ToolParams& params) const final;
|
||||
|
||||
std::string GetName() const final
|
||||
{
|
||||
return "NNRT";
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_DELEGATE_PROVIDER(NnrtDelegateProvider);
|
||||
|
||||
std::vector<Flag> NnrtDelegateProvider::CreateFlags(ToolParams* params) const
|
||||
{
|
||||
std::vector<Flag> flags = {
|
||||
CreateFlag<int32_t>("max_delegate_num", params, "Delegate max num limit, max_delegate_num <= 0 means no limit"),
|
||||
CreateFlag<bool>("enable_fp16", params, "Whether to Infer model with FP16."),
|
||||
CreateFlag<bool>("allow_dynamic_dimensions", params,
|
||||
"Whether to allow dynamic dimension sizes without re-compilation."),
|
||||
CreateFlag<std::string>("performance", params,
|
||||
"Execution performance for nnrt delegate. "
|
||||
"choose within [low, medium, high, extreme, default]."),
|
||||
CreateFlag<std::string>("priority", params,
|
||||
"The model execution priority in nnrt, and it "
|
||||
"choose within [default, low, medium, high]."),
|
||||
CreateFlag<std::string>("device", params,
|
||||
"The name of the nnrt accelerator to use, "
|
||||
"choose within [cpu, gpu, apu, nnrt-reference], "
|
||||
"nnrt-reference means chosen automatically by nnrt."),
|
||||
CreateFlag<std::string>("cache_dir", params, "The directory of load and save cache for delegate"),
|
||||
CreateFlag<std::string>("model_token", params, "The file_name of load and save cache for delegate"),
|
||||
};
|
||||
return flags;
|
||||
}
|
||||
|
||||
void NnrtDelegateProvider::LogParams(const ToolParams& params, bool verbose) const
|
||||
{
|
||||
LOG_TOOL_PARAM(params, bool, "use_nnrt", "Use NNRT", verbose);
|
||||
if (!params.Get<bool>("use_nnrt")) {
|
||||
return; // no use nnrt, return.
|
||||
}
|
||||
|
||||
LOG_TOOL_PARAM(params, std::string, "performance", "NNRT execution performance", verbose);
|
||||
LOG_TOOL_PARAM(params, std::string, "priority", "NNRT execution priority", verbose);
|
||||
LOG_TOOL_PARAM(params, std::string, "device", "NNRT accelerator name", verbose);
|
||||
LOG_TOOL_PARAM(params, std::string, "cache_dir", "NNRT model cache directory", verbose);
|
||||
LOG_TOOL_PARAM(params, std::string, "model_token", "NNRT model cache filename", verbose);
|
||||
LOG_TOOL_PARAM(params, int32_t, "max_delegate_num", "NNRT delegate max partition", verbose);
|
||||
LOG_TOOL_PARAM(params, bool, "enable_fp16", "NNRT allow fp16 inference", verbose);
|
||||
LOG_TOOL_PARAM(params, bool, "allow_dynamic_dimensions", "NNRT allow dynamic dimensions", verbose);
|
||||
}
|
||||
|
||||
TfLiteStatus GetExecutionPerformance(const ToolParams& params, NnrtDelegate::Options& options)
|
||||
{
|
||||
std::string stringExecutionPerformance = params.Get<std::string>("performance");
|
||||
if (stringExecutionPerformance.empty()) {
|
||||
return kTfLiteOk; // no set performance
|
||||
}
|
||||
|
||||
OH_NN_PerformanceMode executionPerformance = OH_NN_PERFORMANCE_NONE;
|
||||
if (stringExecutionPerformance == "low") {
|
||||
executionPerformance = OH_NN_PERFORMANCE_LOW;
|
||||
} else if (stringExecutionPerformance == "medium") {
|
||||
executionPerformance = OH_NN_PERFORMANCE_MEDIUM;
|
||||
} else if (stringExecutionPerformance == "high") {
|
||||
executionPerformance = OH_NN_PERFORMANCE_HIGH;
|
||||
} else if (stringExecutionPerformance == "extreme") {
|
||||
executionPerformance = OH_NN_PERFORMANCE_EXTREME;
|
||||
} else if (stringExecutionPerformance == "default") {
|
||||
executionPerformance = OH_NN_PERFORMANCE_NONE;
|
||||
} else {
|
||||
TFLITE_LOG(ERROR) << "The provided value is not a valid nnrt execution performance.";
|
||||
return kTfLiteError;
|
||||
}
|
||||
options.executionPerformance = executionPerformance;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GetExecutionPriority(const ToolParams& params, NnrtDelegate::Options& options)
|
||||
{
|
||||
std::string stringExecutionPriority = params.Get<std::string>("priority");
|
||||
if (stringExecutionPriority.empty()) {
|
||||
return kTfLiteOk; // no set priority
|
||||
}
|
||||
|
||||
OH_NN_Priority executionPriority = OH_NN_PRIORITY_MEDIUM;
|
||||
if (stringExecutionPriority == "low") {
|
||||
executionPriority = OH_NN_PRIORITY_LOW;
|
||||
} else if (stringExecutionPriority == "medium") {
|
||||
executionPriority = OH_NN_PRIORITY_MEDIUM;
|
||||
} else if (stringExecutionPriority == "high") {
|
||||
executionPriority = OH_NN_PRIORITY_HIGH;
|
||||
} else if (stringExecutionPriority == "default") {
|
||||
executionPriority = OH_NN_PRIORITY_MEDIUM;
|
||||
} else {
|
||||
TFLITE_LOG(ERROR) << "The provided value is not a valid nnrt execution priority.";
|
||||
return kTfLiteError;
|
||||
}
|
||||
options.executionPriority = executionPriority;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MapParams(const ToolParams& params, NnrtDelegate::Options& options)
|
||||
{
|
||||
std::string acceleratorName = params.Get<std::string>("device");
|
||||
if (!acceleratorName.empty()) {
|
||||
options.acceleratorName = acceleratorName;
|
||||
}
|
||||
|
||||
if (params.GetParam("max_delegate_num") != nullptr) {
|
||||
options.maxNumberDelegatedPartitions = params.Get<int32_t>("max_delegate_num");
|
||||
}
|
||||
|
||||
std::string cacheDir = params.Get<std::string>("cache_dir");
|
||||
if (!cacheDir.empty()) {
|
||||
options.cacheDir = cacheDir;
|
||||
}
|
||||
|
||||
std::string modelToken = params.Get<std::string>("model_token");
|
||||
if (!modelToken.empty()) {
|
||||
options.modelToken = modelToken;
|
||||
}
|
||||
|
||||
if (params.Get<bool>("enable_fp16")) {
|
||||
options.enableFp16 = true;
|
||||
}
|
||||
|
||||
if (params.Get<bool>("allow_dynamic_dimensions")) {
|
||||
options.allowDynamicDimensions = true;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteDelegatePtr NnrtDelegateProvider::CreateTfLiteDelegate(const ToolParams& params) const
|
||||
{
|
||||
TfLiteDelegatePtr delegate(nullptr, [](TfLiteDelegate*) {});
|
||||
if (!params.Get<bool>("use_nnrt")) {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
NnrtDelegate::Options options;
|
||||
TFLITE_TOOLS_CHECK(MapParams(params, options) == kTfLiteOk) << "Map params to NNRT Delegate options failed.";
|
||||
TFLITE_TOOLS_CHECK(GetExecutionPerformance(params, options) == kTfLiteOk) <<
|
||||
"Create TfLite NNRT Delegate failed.";
|
||||
TFLITE_TOOLS_CHECK(GetExecutionPriority(params, options) == kTfLiteOk) << "Create TfLite NNRT Delegate failed.";
|
||||
|
||||
const auto* nnrtImpl = NnrtImplementation();
|
||||
if (!nnrtImpl->nnrtExists) {
|
||||
TFLITE_LOG(WARN) << "NNRT acceleration is unsupported on this platform.";
|
||||
return delegate;
|
||||
}
|
||||
|
||||
return TfLiteDelegatePtr(new (std::nothrow) NnrtDelegate(nnrtImpl, options),
|
||||
[](TfLiteDelegate* delegate) { delete reinterpret_cast<NnrtDelegate*>(delegate); });
|
||||
}
|
||||
|
||||
std::pair<TfLiteDelegatePtr, int32_t> NnrtDelegateProvider::CreateRankedTfLiteDelegate(const ToolParams& params) const
|
||||
{
|
||||
auto ptr = CreateTfLiteDelegate(params);
|
||||
LogParams(params, false);
|
||||
return std::make_pair(std::move(ptr), params.GetPosition<bool>("use_nnrt"));
|
||||
}
|
||||
} // namespace tools
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
|
||||
#include "nnrt_op_builder.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "neural_network_runtime.h"
|
||||
#include "tensorflow/lite/util.h"
|
||||
#include "tensorflow/lite/context_util.h"
|
||||
|
||||
#include "nnrt_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
constexpr int32_t SCALAR_TENSOR_RANK = 1;
|
||||
constexpr int32_t ADDZEROS_BIAS_INDEX = -1;
|
||||
constexpr int32_t UNSPECIFIED_DIMENSION_VALUE = -1;
|
||||
const std::vector<int32_t> DEPTHWISE_TRANSPOSE_AXISS = { 3, 1, 2, 0 };
|
||||
|
||||
NnrtOpBuilder::NnrtOpBuilder(const NnrtApi* nnrt, NnrtOpBuilderArgs& opBuilderArgs)
|
||||
: m_nnrt(nnrt),
|
||||
m_context(opBuilderArgs.context),
|
||||
m_pTensorMapping(opBuilderArgs.pTensorMapping),
|
||||
m_nnModel(opBuilderArgs.nnModel),
|
||||
m_allowDynamicDimensions(opBuilderArgs.delegateOptions.allowDynamicDimensions)
|
||||
{
|
||||
// Map Op func pointer
|
||||
MapBuiltinCodeToFunc();
|
||||
|
||||
// Get model inputs
|
||||
for (int32_t i : TfLiteIntArrayView(opBuilderArgs.inputTensors)) {
|
||||
// Constant tensors are not NNRT inputs.
|
||||
if (i != kTfLiteOptionalTensor && opBuilderArgs.context->tensors[i].allocation_type != kTfLiteMmapRo) {
|
||||
m_inputs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddZerosBias(const NnrtOpMappingArgs& mappingArgs, int32_t inputId, int32_t filterId,
|
||||
int32_t channelNum)
|
||||
{
|
||||
int32_t biasIndex = ADDZEROS_BIAS_INDEX;
|
||||
mappingArgs.context->AddTensors(mappingArgs.context, 1, &biasIndex);
|
||||
TfLiteTensor* biasTensor = &mappingArgs.context->tensors[biasIndex];
|
||||
const auto inputType = mappingArgs.context->tensors[inputId].type;
|
||||
|
||||
if (inputType == kTfLiteFloat32) {
|
||||
biasTensor->type = kTfLiteFloat32;
|
||||
} else {
|
||||
biasTensor->type = kTfLiteInt32;
|
||||
}
|
||||
|
||||
// Create an array with a required bias shape and resize the bias tensor.
|
||||
TfLiteIntArray* biasShape = TfLiteIntArrayCreate(1); // 1-dimension
|
||||
biasShape->data[0] = channelNum;
|
||||
biasTensor->allocation_type = kTfLiteDynamic;
|
||||
mappingArgs.context->ResizeTensor(mappingArgs.context, biasTensor, biasShape);
|
||||
|
||||
// Set tensor's values to zeroes and add it using AddVector*, so that the values are copied to NNRT.
|
||||
#ifdef __STDC_LIB_EXT1__
|
||||
if (inputType == kTfLiteFloat32) {
|
||||
memset_s(biasTensor->data.f, biasTensor->bytes, 0, channelNum * sizeof(float));
|
||||
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorFloat32Tensor(biasTensor->data.f, channelNum,
|
||||
OH_NN_TENSOR));
|
||||
} else {
|
||||
memset_s(biasTensor->data.i32, biasTensor->bytes, 0, channelNum * sizeof(int32_t));
|
||||
const TfLiteTensor& inputTensor = mappingArgs.context->tensors[inputId];
|
||||
const TfLiteTensor& filterTensor = mappingArgs.context->tensors[filterId];
|
||||
|
||||
// NNRT requires bias scale to be a product of an input scale and a filter scale.
|
||||
biasTensor->params.scale = inputTensor.params.scale * filterTensor.params.scale;
|
||||
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt32Tensor(biasTensor->data.i32, channelNum,
|
||||
OH_NN_TENSOR));
|
||||
}
|
||||
#endif
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddBasicComputeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
if (builtinCode == kTfLiteBuiltinAdd) {
|
||||
auto builtin = reinterpret_cast<TfLiteAddParams*>(mappingArgs.node->builtin_data);
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteAddParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_ADD_ACTIVATIONTYPE));
|
||||
} else if (builtinCode == kTfLiteBuiltinMul) {
|
||||
auto builtin = reinterpret_cast<TfLiteMulParams*>(mappingArgs.node->builtin_data);
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteMulParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_MUL_ACTIVATION_TYPE));
|
||||
} else if (builtinCode == kTfLiteBuiltinSub) {
|
||||
auto builtin = reinterpret_cast<TfLiteSubParams*>(mappingArgs.node->builtin_data);
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteSubParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_SUB_ACTIVATIONTYPE));
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted basic compute type %d.", builtinCode);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddAvgPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLitePoolParams*>(mappingArgs.node->builtin_data);
|
||||
std::vector<int64_t> kernel = { static_cast<int64_t>(builtin->filter_height),
|
||||
static_cast<int64_t>(builtin->filter_width) };
|
||||
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
|
||||
static_cast<int64_t>(builtin->stride_width) };
|
||||
|
||||
mappingArgs.builder->AddVectorInt64Tensor(kernel.data(), kernel.size(), OH_NN_AVG_POOL_KERNEL_SIZE);
|
||||
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_AVG_POOL_STRIDE);
|
||||
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_AVG_POOL_PAD_MODE));
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_AVG_POOL_ACTIVATION_TYPE));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddMaxPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLitePoolParams*>(mappingArgs.node->builtin_data);
|
||||
std::vector<int64_t> kernel = { static_cast<int64_t>(builtin->filter_height),
|
||||
static_cast<int64_t>(builtin->filter_width) };
|
||||
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
|
||||
static_cast<int64_t>(builtin->stride_width) };
|
||||
|
||||
mappingArgs.builder->AddVectorInt64Tensor(kernel.data(), kernel.size(), OH_NN_MAX_POOL_KERNEL_SIZE);
|
||||
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_MAX_POOL_STRIDE);
|
||||
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_MAX_POOL_PAD_MODE));
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLitePoolParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_MAX_POOL_ACTIVATION_TYPE));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddFullConnectedParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
// IF bias is not presented, bias input index will be -1.
|
||||
const bool isBiasPresent =
|
||||
(mappingArgs.node->inputs->size == 3) && (mappingArgs.node->inputs->data[2] != kTfLiteOptionalTensor);
|
||||
|
||||
if (!isBiasPresent) {
|
||||
const int32_t inputTensorId = mappingArgs.node->inputs->data[0]; // kInputTensor
|
||||
const int32_t filterTensorId = mappingArgs.node->inputs->data[1]; // kWeightsTensor
|
||||
const int32_t numUnits = mappingArgs.context->tensors[filterTensorId].dims->data[0]; // bias channel num
|
||||
TF_LITE_ENSURE_STATUS(AddZerosBias(mappingArgs, inputTensorId, filterTensorId, numUnits));
|
||||
}
|
||||
|
||||
auto builtin = reinterpret_cast<TfLiteFullyConnectedParams*>(mappingArgs.node->builtin_data);
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteFullyConnectedParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_FULL_CONNECTION_ACTIVATIONTYPE));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddConcatenationParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteConcatenationParams*>(mappingArgs.node->builtin_data);
|
||||
const int64_t axis = static_cast<int64_t>(builtin->axis);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_CONCAT_AXIS);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddSoftmaxParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteSoftmaxParams*>(mappingArgs.node->builtin_data);
|
||||
const int64_t axis = static_cast<int64_t>(builtin->beta);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_SOFTMAX_AXIS);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddQuantizeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
OH_NN_DataType nnType {OH_NN_FLOAT32};
|
||||
|
||||
int32_t inputIndex = mappingArgs.node->inputs->data[0];
|
||||
m_pTensorMapping->ConvertType(m_context, inputIndex, 0, nnType);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(nnType), OH_NN_QUANT_DTYPE_CAST_SRC_T);
|
||||
|
||||
int32_t outputIndex = mappingArgs.node->outputs->data[0];
|
||||
m_pTensorMapping->ConvertType(m_context, outputIndex, 0, nnType);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(nnType), OH_NN_QUANT_DTYPE_CAST_DST_T);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddPackParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLitePackParams*>(mappingArgs.node->builtin_data);
|
||||
const int64_t axis = static_cast<int64_t>(builtin->axis);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(axis, OH_NN_STACK_AXIS);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddPadParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
float padValue = 0.0;
|
||||
mappingArgs.builder->AddScalarFloat32Tensor(padValue, OH_NN_PAD_CONSTANT_VALUE);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddReduceMeanParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteReducerParams*>(mappingArgs.node->builtin_data);
|
||||
const int32_t keepDims = (builtin->keep_dims);
|
||||
mappingArgs.builder->AddScalarBoolTensor(keepDims, OH_NN_REDUCE_MEAN_KEEP_DIMS);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddStridedSliceParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteStridedSliceParams*>(mappingArgs.node->builtin_data);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->begin_mask),
|
||||
OH_NN_STRIDED_SLICE_BEGIN_MASK);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->end_mask),
|
||||
OH_NN_STRIDED_SLICE_END_MASK);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->ellipsis_mask),
|
||||
OH_NN_STRIDED_SLICE_ELLIPSIS_MASK);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->new_axis_mask),
|
||||
OH_NN_STRIDED_SLICE_NEW_AXIS_MASK);
|
||||
mappingArgs.builder->AddScalarInt64Tensor(static_cast<int64_t>(builtin->shrink_axis_mask),
|
||||
OH_NN_STRIDED_SLICE_SHRINK_AXIS_MASK);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddReshapeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
if (mappingArgs.node->inputs->size == 1) {
|
||||
auto builtin = reinterpret_cast<TfLiteReshapeParams*>(mappingArgs.node->builtin_data);
|
||||
int32_t numDimensions = builtin->num_dimensions;
|
||||
std::vector<int32_t> outputShape(numDimensions);
|
||||
for (int32_t i = 0; i < numDimensions; ++i) {
|
||||
outputShape[i] = builtin->shape[i];
|
||||
}
|
||||
mappingArgs.builder->AddVectorInt32Tensor(outputShape.data(), outputShape.size(), OH_NN_TENSOR);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteConvParams*>(mappingArgs.node->builtin_data);
|
||||
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
|
||||
static_cast<int64_t>(builtin->stride_width) };
|
||||
std::vector<int64_t> dilation = { static_cast<int64_t>(builtin->dilation_height_factor),
|
||||
static_cast<int64_t>(builtin->dilation_width_factor) };
|
||||
int64_t groupNum = 1;
|
||||
|
||||
mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(), OH_NN_CONV2D_STRIDES);
|
||||
mappingArgs.builder->AddVectorInt64Tensor(dilation.data(), dilation.size(), OH_NN_CONV2D_DILATION);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLiteConvParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_CONV2D_PAD_MODE));
|
||||
mappingArgs.builder->AddScalarInt64Tensor(groupNum, OH_NN_CONV2D_GROUP);
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteConvParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_CONV2D_ACTIVATION_TYPE));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddDepthwiseConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
auto builtin = reinterpret_cast<TfLiteDepthwiseConvParams*>(mappingArgs.node->builtin_data);
|
||||
std::vector<int64_t> stride = { static_cast<int64_t>(builtin->stride_height),
|
||||
static_cast<int64_t>(builtin->stride_width) };
|
||||
std::vector<int64_t> dilation = { static_cast<int64_t>(builtin->dilation_height_factor),
|
||||
static_cast<int64_t>(builtin->dilation_width_factor) };
|
||||
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt64Tensor(stride.data(), stride.size(),
|
||||
OH_NN_DEPTHWISE_CONV2D_NATIVE_STRIDES));
|
||||
TF_LITE_ENSURE_STATUS(mappingArgs.builder->AddVectorInt64Tensor(dilation.data(), dilation.size(),
|
||||
OH_NN_DEPTHWISE_CONV2D_NATIVE_DILATION));
|
||||
TF_LITE_ENSURE_STATUS(AddPadParamsInOperator<TfLiteDepthwiseConvParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_DEPTHWISE_CONV2D_NATIVE_PAD));
|
||||
TF_LITE_ENSURE_STATUS(AddActivateParamsInOperator<TfLiteDepthwiseConvParams>(mappingArgs, builtin, builtinCode,
|
||||
OH_NN_DEPTHWISE_CONV2D_NATIVE_ACTIVATION_TYPE));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::FinalizeAddOperation(OH_NN_OperationType type, int32_t liteNodeIndex)
|
||||
{
|
||||
// Actually add a NN API Operation
|
||||
OH_NN_UInt32Array inputIndices;
|
||||
OH_NN_UInt32Array outputIndices;
|
||||
OH_NN_UInt32Array paramIndices;
|
||||
inputIndices.data = m_augmentedInputs.data();
|
||||
inputIndices.size = static_cast<uint32_t>(m_augmentedInputs.size());
|
||||
outputIndices.data = m_augmentedOutputs.data();
|
||||
outputIndices.size = static_cast<uint32_t>(m_augmentedOutputs.size());
|
||||
paramIndices.size = static_cast<uint32_t>(m_augmentedParams.size());
|
||||
|
||||
paramIndices.data = (m_augmentedParams.size() == 0) ? nullptr : m_augmentedParams.data();
|
||||
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(m_nnrt->OH_NNModel_AddOperation(m_nnModel,
|
||||
type, ¶mIndices, &inputIndices, &outputIndices), "adding operation");
|
||||
|
||||
m_augmentedInputs.clear();
|
||||
m_augmentedOutputs.clear();
|
||||
m_augmentedParams.clear();
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddTensor(int32_t tensorIndex, int32_t builtinCode, std::vector<uint32_t>& indices,
|
||||
int32_t tensorFlags)
|
||||
{
|
||||
int32_t nnTensorIndex = m_pTensorMapping->LiteIndexToNn(tensorIndex);
|
||||
if (nnTensorIndex != INVALID_INDEX) {
|
||||
indices.emplace_back(nnTensorIndex);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Parameters needed for new type.
|
||||
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
|
||||
if (kTfLiteNoType == tensor->type) {
|
||||
indices.emplace_back(INVALID_INDEX);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(AddTensor(tensorIndex, builtinCode, tensorFlags, nnTensorIndex));
|
||||
|
||||
indices.emplace_back(nnTensorIndex);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
|
||||
int32_t& nnTensorIndex)
|
||||
{
|
||||
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
|
||||
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
|
||||
OH_NN_Tensor nnTensor;
|
||||
OH_NN_QuantParam nnQuantParam;
|
||||
std::vector<int32_t> weightDims;
|
||||
void* tensorData = tensor->data.data;
|
||||
std::vector<int8_t> depthwiseTensorData;
|
||||
TF_LITE_ENSURE_STATUS(ConstructNNTensor(tensorIndex, builtinCode, scalarAsTensor, nnQuantParam, nnTensor));
|
||||
|
||||
// For depth-wise conv operator, we should transpose weight tensor to adapt NN tensor format.
|
||||
if ((builtinCode == kTfLiteBuiltinDepthwiseConv2d) && (tensor->allocation_type == kTfLiteMmapRo) &&
|
||||
(nnTensor.dimensionCount == DEPTHWISE_WEIGHT_DIMENSION_COUNT)) {
|
||||
size_t typeBytes = 0;
|
||||
int64_t tensorSize = 0;
|
||||
TF_LITE_ENSURE_STATUS(GetSizeOfType(m_context, tensor->type, &typeBytes));
|
||||
TF_LITE_ENSURE_STATUS(GetTensorSize(m_context, nnTensor.dimensions, nnTensor.dimensionCount, tensorSize));
|
||||
|
||||
depthwiseTensorData.assign(tensorSize * typeBytes, 0);
|
||||
TfLiteStatus retCode = TransposeDepthwiseTensor(tensorIndex, nnTensor, weightDims, depthwiseTensorData);
|
||||
if (retCode != kTfLiteOk) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to transpose depthwise tensor.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
tensorData = static_cast<void*>(depthwiseTensorData.data());
|
||||
}
|
||||
|
||||
int32_t nnRet = m_nnrt->OH_NNModel_AddTensor(m_nnModel, &nnTensor);
|
||||
if (nnRet != OH_NN_SUCCESS) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to add nnTensor to NN model.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Allocate a new tensor index
|
||||
nnTensorIndex = m_pTensorMapping->AddNewNnTensorIndex(tensorIndex);
|
||||
if (tensor->allocation_type == kTfLiteMmapRo) {
|
||||
nnRet = m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnTensorIndex,
|
||||
tensorData, tensor->bytes);
|
||||
if (nnRet != OH_NN_SUCCESS) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] Fail to setting new nnTensor value.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::TransposeDepthwiseTensor(int32_t tensorIndex, OH_NN_Tensor& nnTensor,
|
||||
std::vector<int32_t>& weightDims, std::vector<int8_t>& tensorData)
|
||||
{
|
||||
const int32_t* tensorDims = nnTensor.dimensions;
|
||||
uint32_t tensorRank = nnTensor.dimensionCount;
|
||||
|
||||
// For Depth-wise Convolution, NNRT choose to Transpose dimension with [3, 1, 2, 0]
|
||||
TF_LITE_ENSURE_STATUS(TransposeDims(m_context, tensorDims, tensorRank, DEPTHWISE_TRANSPOSE_AXISS, weightDims));
|
||||
nnTensor.dimensions = weightDims.data();
|
||||
|
||||
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
|
||||
if (tensor->type == kTfLiteFloat32) {
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<float*>(tensorData.data())));
|
||||
} else if (tensor->type == kTfLiteInt32) {
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<int32_t*>(tensorData.data())));
|
||||
} else if (tensor->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<int8_t*>(tensorData.data())));
|
||||
} else if (tensor->type == kTfLiteUInt8) {
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TransposeTensor(m_context, tensorIndex, tensorDims, reinterpret_cast<uint8_t*>(tensorData.data())));
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted weight tensor type %d.", tensor->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::ConstructNNTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
|
||||
OH_NN_QuantParam& nnQuantParam, OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
OH_NN_DataType nnType {OH_NN_UNKNOWN};
|
||||
TF_LITE_ENSURE_STATUS(m_pTensorMapping->ConvertType(m_context, tensorIndex, tensorFlags, nnType));
|
||||
TF_LITE_ENSURE_STATUS(m_pTensorMapping->ConvertQuantParams(m_context, tensorIndex, nnQuantParam));
|
||||
|
||||
TfLiteTensor* tensor = &(m_context->tensors[tensorIndex]);
|
||||
uint32_t tensorRank = static_cast<uint32_t>(tensor->dims->size);
|
||||
m_dimsUnspecified.assign(tensorRank, UNSPECIFIED_DIMENSION_VALUE);
|
||||
|
||||
int32_t* tensorDims = (m_allowDynamicDimensions && (tensor->allocation_type != kTfLiteMmapRo) &&
|
||||
std::find(m_inputs.begin(), m_inputs.end(), tensorIndex) != m_inputs.end()) ?
|
||||
reinterpret_cast<int32_t*>(m_dimsUnspecified.data()) :
|
||||
tensor->dims->data;
|
||||
|
||||
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
|
||||
if (scalarAsTensor && tensorRank == 0) {
|
||||
tensorRank = SCALAR_TENSOR_RANK; // Use rank 1, shape {1} nnTensor for TFLite scalar tensors.
|
||||
tensorDims = const_cast<int32_t*>(&SCALAR_TENSOR_RANK);
|
||||
}
|
||||
|
||||
if (tensorRank == 0) {
|
||||
// if the tensorRank is 0, the dimension ptr must be nullptr.
|
||||
tensorDims = nullptr;
|
||||
}
|
||||
|
||||
nnTensor.dataType = nnType;
|
||||
nnTensor.dimensionCount = tensorRank;
|
||||
nnTensor.dimensions = tensorDims;
|
||||
nnTensor.quantParam = nnQuantParam.quantCount ? &nnQuantParam : nullptr;
|
||||
nnTensor.type = OH_NN_TENSOR;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::AddOpFuncParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
if (!m_keyToOpFunc.count(builtinCode)) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unsupportted Op builtinCode : %d.", builtinCode);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
OpFuncPtr pfunc = m_keyToOpFunc[builtinCode];
|
||||
return (this->*pfunc)(mappingArgs, builtinCode);
|
||||
}
|
||||
|
||||
TfLiteStatus NnrtOpBuilder::MapBuiltinCodeToFunc()
|
||||
{
|
||||
m_keyToOpFunc[kTfLiteBuiltinAdd] = &NnrtOpBuilder::AddBasicComputeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinAveragePool2d] = &NnrtOpBuilder::AddAvgPoolingParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinConcatenation] = &NnrtOpBuilder::AddConcatenationParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinConv2d] = &NnrtOpBuilder::AddConv2DParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinDepthwiseConv2d] = &NnrtOpBuilder::AddDepthwiseConv2DParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinDequantize] = &NnrtOpBuilder::AddQuantizeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinFullyConnected] = &NnrtOpBuilder::AddFullConnectedParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinMaxPool2d] = &NnrtOpBuilder::AddMaxPoolingParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinMul] = &NnrtOpBuilder::AddBasicComputeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinSub] = &NnrtOpBuilder::AddBasicComputeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinReshape] = &NnrtOpBuilder::AddReshapeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinSoftmax] = &NnrtOpBuilder::AddSoftmaxParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinStridedSlice] = &NnrtOpBuilder::AddStridedSliceParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinPack] = &NnrtOpBuilder::AddPackParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinPad] = &NnrtOpBuilder::AddPadParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinMean] = &NnrtOpBuilder::AddReduceMeanParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinQuantize] = &NnrtOpBuilder::AddQuantizeParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinHardSwish] = &NnrtOpBuilder::AddDefaultOpParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinShape] = &NnrtOpBuilder::AddDefaultOpParams;
|
||||
m_keyToOpFunc[kTfLiteBuiltinLogistic] = &NnrtOpBuilder::AddDefaultOpParams;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
|
||||
#define TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/lite/builtin_ops.h"
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/minimal_logging.h"
|
||||
|
||||
#include "../nnrt/nnrt_implementation.h"
|
||||
#include "tensor_mapping.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
constexpr int32_t PADDING_SAME = 0;
|
||||
constexpr int32_t PADDING_VALID = 1;
|
||||
|
||||
// NN API Operator Builder
|
||||
class NnrtOpBuilder;
|
||||
|
||||
// The kernel that represents the node sub set of TF Lite being run on NN API.
|
||||
struct NnrtOpMappingArgs {
|
||||
TfLiteContext* context {nullptr};
|
||||
NnrtOpBuilder* builder {nullptr};
|
||||
TfLiteNode* node {nullptr};
|
||||
int32_t nodeIndex {-1};
|
||||
};
|
||||
|
||||
struct NnrtOpBuilderArgs {
|
||||
TfLiteContext* context {nullptr};
|
||||
OH_NNModel* nnModel {nullptr};
|
||||
TfLiteIntArray* inputTensors {nullptr};
|
||||
TensorMapping* pTensorMapping {nullptr};
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
};
|
||||
|
||||
// Abstract builder for building an op in the NN API graph. This handles
|
||||
// the disparity between TFLite and NN API nnTensor types. NN API has singular
|
||||
// nnTensors for both tensors and parameters, and TFLite separates the two.
|
||||
class NnrtOpBuilder {
|
||||
public:
|
||||
NnrtOpBuilder(const NnrtApi* nnrt, NnrtOpBuilderArgs& opBuilderArgs);
|
||||
~NnrtOpBuilder() = default;
|
||||
|
||||
// Add scalar nnTensor, the datatypes involved are bool, Int32, Int8, Int64, Float32
|
||||
TfLiteStatus AddScalarBoolTensor(bool value, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddScalarTensor<bool>(value, OH_NN_BOOL, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddScalarInt32Tensor(int32_t value, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddScalarTensor<int32_t>(value, OH_NN_INT32, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddScalarInt8Tensor(int32_t value, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddScalarTensor<int8_t>(value, OH_NN_INT8, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddScalarInt64Tensor(int64_t value, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddScalarTensor<int64_t>(value, OH_NN_INT64, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddScalarFloat32Tensor(float value, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddScalarTensor<float>(value, OH_NN_FLOAT32, nnTensorType);
|
||||
}
|
||||
|
||||
// Add vector nnTensor, the datatypes involved are Int32, Int64, Int16, Int8, Float32
|
||||
TfLiteStatus AddVectorInt32Tensor(const int32_t* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddVectorTensor<int32_t>(values, numValues, OH_NN_UINT32, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddVectorInt64Tensor(const int64_t* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddVectorTensor<int64_t>(values, numValues, OH_NN_INT64, nnTensorType);
|
||||
}
|
||||
TfLiteStatus AddVectorFloat32Tensor(const float* values, uint32_t numValues, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
return AddVectorTensor<float>(values, numValues, OH_NN_FLOAT32, nnTensorType);
|
||||
}
|
||||
|
||||
// Add input tensor
|
||||
TfLiteStatus AddTensorInput(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags = 0)
|
||||
{
|
||||
return AddTensor(tensorIndex, builtinCode, m_augmentedInputs, tensorFlags);
|
||||
}
|
||||
// Add output tensor
|
||||
TfLiteStatus AddTensorOutput(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags = 0)
|
||||
{
|
||||
return AddTensor(tensorIndex, builtinCode, m_augmentedOutputs, tensorFlags);
|
||||
}
|
||||
|
||||
// Finish emitting the op (of type `type`) into the NN API.
|
||||
TfLiteStatus FinalizeAddOperation(OH_NN_OperationType type, int32_t liteNodeIndex);
|
||||
|
||||
void ClearInputOuputLists()
|
||||
{
|
||||
m_augmentedInputs.clear();
|
||||
m_augmentedOutputs.clear();
|
||||
m_augmentedParams.clear();
|
||||
}
|
||||
|
||||
TfLiteStatus AddOpFuncParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus MapBuiltinCodeToFunc();
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
TfLiteStatus AddScalarTensor(T value, OH_NN_DataType nnType, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
OH_NN_Tensor tensor {
|
||||
.dataType = nnType,
|
||||
.dimensionCount = 0,
|
||||
.dimensions = nullptr,
|
||||
.quantParam = nullptr,
|
||||
.type = nnTensorType,
|
||||
};
|
||||
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(
|
||||
m_nnrt->OH_NNModel_AddTensor(m_nnModel, &tensor), "adding nnTensor");
|
||||
|
||||
const int32_t nnIndex = m_pTensorMapping->AddNewNonTensorTensor();
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(
|
||||
m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnIndex, &value, sizeof(value)),
|
||||
"setting new nnTensor value");
|
||||
m_augmentedParams.emplace_back(nnIndex);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TfLiteStatus AddVectorTensor(const T* values, int32_t numValues, OH_NN_DataType nnType,
|
||||
OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
if (values == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-OPBUILDER] The variable of values is nullptr when adding vector to operator.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
uint32_t numBits = 8;
|
||||
double doubleScale = 0.f;
|
||||
int32_t zeroPoint = 0;
|
||||
OH_NN_QuantParam quantParam = {
|
||||
.quantCount = 1,
|
||||
.numBits = &numBits,
|
||||
.scale = &doubleScale,
|
||||
.zeroPoint = &zeroPoint
|
||||
};
|
||||
|
||||
OH_NN_Tensor tensor {
|
||||
.dataType = nnType,
|
||||
.dimensionCount = 1, // For 1-dim vector, dimensionCount is one.
|
||||
.dimensions = &numValues,
|
||||
.quantParam = &quantParam,
|
||||
.type = nnTensorType,
|
||||
};
|
||||
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(
|
||||
m_nnrt->OH_NNModel_AddTensor(m_nnModel, &tensor), "adding nnTensor");
|
||||
const int32_t nnIndex = m_pTensorMapping->AddNewNonTensorTensor();
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(
|
||||
m_nnrt->OH_NNModel_SetTensorData(m_nnModel, nnIndex, values, sizeof(*(values)) * numValues),
|
||||
"settings new nnTensor value");
|
||||
m_augmentedParams.emplace_back(nnIndex);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TfLiteStatus AddActivateParamsInOperator(const NnrtOpMappingArgs& mappingArgs, T* builtinParams,
|
||||
int32_t builtinCode, OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
if (builtinParams == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-OPBUILDER] The builtin params is nullptr when adding activate params to operator.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if ((builtinParams->activation >= 0) &&
|
||||
(builtinParams->activation < ACTIVATE_FUSE_TYPE_LIST.size()) &&
|
||||
(ACTIVATE_FUSE_TYPE_LIST[builtinParams->activation] != OH_NN_FUSE_UNSUPPORTED)) {
|
||||
mappingArgs.builder->AddScalarInt8Tensor(ACTIVATE_FUSE_TYPE_LIST[builtinParams->activation], nnTensorType);
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-OPBUILDER] unsupportted fused activation type %d for OpType %d.",
|
||||
builtinParams->activation, builtinCode);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TfLiteStatus AddPadParamsInOperator(const NnrtOpMappingArgs& mappingArgs, T* builtinParams, int32_t builtinCode,
|
||||
OH_NN_TensorType nnTensorType)
|
||||
{
|
||||
if (builtinParams == nullptr) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-OPBUILDER] The builtin params is nullptr when adding pad params to operator.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
int32_t padding = 0;
|
||||
if (builtinParams->padding == kTfLitePaddingUnknown) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-OPBUILDER] unknown padding mode for OpType %d.", builtinCode);
|
||||
return kTfLiteError;
|
||||
} else {
|
||||
padding = (builtinParams->padding == kTfLitePaddingSame) ? PADDING_SAME : PADDING_VALID;
|
||||
}
|
||||
mappingArgs.builder->AddScalarInt8Tensor(padding, nnTensorType);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// NNRT requires a bias tensor, so we allocate a new tensor to fill it with zeroes.
|
||||
// It is deleted with other tensors in the context during subgraph destructor call.
|
||||
TfLiteStatus AddZerosBias(const NnrtOpMappingArgs& mappingArgs, int32_t inputId, int32_t filterId,
|
||||
int32_t channelNum);
|
||||
|
||||
TfLiteStatus AddBasicComputeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddAvgPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddMaxPoolingParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddFullConnectedParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddConcatenationParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddSoftmaxParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddQuantizeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddPackParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddPadParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddReduceMeanParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddReshapeParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddStridedSliceParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddDepthwiseConv2DParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
|
||||
TfLiteStatus AddDefaultOpParams(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode)
|
||||
{
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Adds a new NN API tensor that shadows the TF Lite tensor `tensorIndex`.
|
||||
// This restores the NN API tensor index corresponding to the created tensor.
|
||||
// If another caller previously created a NN API tensor for `tensorIndex`
|
||||
// then the existing one is restored.
|
||||
TfLiteStatus AddTensor(int32_t tensorIndex, int32_t builtinCode, std::vector<uint32_t>& indices,
|
||||
int32_t tensorFlags = 0);
|
||||
|
||||
// Adds a new NN API nnTensor to NNModel.
|
||||
// If the builtinCode is kTfLiteBuiltinDepthwiseConv2d, the weight tensor will be transposed to CHWN format.
|
||||
TfLiteStatus AddTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags, int32_t& nnTensorIndex);
|
||||
|
||||
// Transpose dimension for Depth-wise Convolution Operator.
|
||||
TfLiteStatus TransposeDepthwiseTensor(int32_t tensorIndex, OH_NN_Tensor& nnTensor, std::vector<int32_t>& destDims,
|
||||
std::vector<int8_t>& tensorData);
|
||||
|
||||
// Get NN nnTensor from tensor
|
||||
TfLiteStatus ConstructNNTensor(int32_t tensorIndex, int32_t builtinCode, int32_t tensorFlags,
|
||||
OH_NN_QuantParam& nnQuantParam, OH_NN_Tensor& nnTensor);
|
||||
|
||||
private:
|
||||
// Access to NNRT.
|
||||
const NnrtApi* const m_nnrt;
|
||||
|
||||
// TfLiteContext for error handling.
|
||||
TfLiteContext* const m_context;
|
||||
|
||||
// Indices of all inputs of tflite subgraph.
|
||||
std::vector<int32_t> m_inputs;
|
||||
|
||||
// Tracks relationship between indices.
|
||||
TensorMapping* const m_pTensorMapping;
|
||||
|
||||
// The NNRT model.
|
||||
OH_NNModel* const m_nnModel;
|
||||
|
||||
// Inputs and outputs for the current op. These are augmented in the sense
|
||||
// that NN API uses nnTensors for all arguments, not just tensors, unlike
|
||||
// TensorFlow Lite.
|
||||
std::vector<uint32_t> m_augmentedInputs;
|
||||
std::vector<uint32_t> m_augmentedParams;
|
||||
std::vector<uint32_t> m_augmentedOutputs;
|
||||
|
||||
// Whether to allow dynamic batch size without re-compilation.
|
||||
bool m_allowDynamicDimensions;
|
||||
|
||||
// the dynamic dimension information.
|
||||
std::vector<int32_t> m_dimsUnspecified;
|
||||
|
||||
// key builtInCode to OpFunc Map
|
||||
using OpFuncPtr = TfLiteStatus(NnrtOpBuilder::*)(const NnrtOpMappingArgs& mappingArgs, int32_t builtinCode);
|
||||
std::map<int32_t, OpFuncPtr> m_keyToOpFunc;
|
||||
};
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_OP_BUILDER_H
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nnrt_utils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "tensorflow/lite/util.h"
|
||||
#include "tensorflow/lite/builtin_ops.h"
|
||||
#include "tensorflow/lite/context_util.h"
|
||||
#include "tensorflow/lite/minimal_logging.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
|
||||
#include "neural_network_runtime_type.h"
|
||||
|
||||
namespace tflite {
|
||||
std::string NnrtErrorDescription(int32_t errorCode)
|
||||
{
|
||||
switch (errorCode) {
|
||||
case OH_NN_SUCCESS:
|
||||
return "OH_NN_SUCCESS";
|
||||
case OH_NN_FAILED:
|
||||
return "OH_NN_FAILED";
|
||||
case OH_NN_INVALID_PARAMETER:
|
||||
return "OH_NN_INVALID_PARAMETER";
|
||||
case OH_NN_MEMORY_ERROR:
|
||||
return "OH_NN_MEMORY_ERROR";
|
||||
case OH_NN_OPERATION_FORBIDDEN:
|
||||
return "OH_NN_OPERATION_FORBIDDEN";
|
||||
case OH_NN_NULL_PTR:
|
||||
return "OH_NN_NULL_PTR";
|
||||
case OH_NN_INVALID_FILE:
|
||||
return "OH_NN_INVALID_FILE";
|
||||
case OH_NN_UNAVALIDABLE_DEVICE:
|
||||
return "OH_NN_UNAVALIDABLE_DEVICE";
|
||||
case OH_NN_INVALID_PATH:
|
||||
return "OH_NN_INVALID_PATH";
|
||||
default:
|
||||
return "Unknown NNRT error code: " + std::to_string(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsFloat(TfLiteType type)
|
||||
{
|
||||
return type == kTfLiteFloat32;
|
||||
}
|
||||
|
||||
bool IsQuantized(TfLiteType type)
|
||||
{
|
||||
return ((type == kTfLiteUInt8) || (type == kTfLiteInt8));
|
||||
}
|
||||
|
||||
bool IsScalarInputSupported(int32_t builtinCode)
|
||||
{
|
||||
switch (builtinCode) {
|
||||
case kTfLiteBuiltinAdd:
|
||||
case kTfLiteBuiltinMul:
|
||||
case kTfLiteBuiltinSub:
|
||||
case kTfLiteBuiltinDiv:
|
||||
case kTfLiteBuiltinEqual:
|
||||
case kTfLiteBuiltinNotEqual:
|
||||
case kTfLiteBuiltinGreater:
|
||||
case kTfLiteBuiltinGreaterEqual:
|
||||
case kTfLiteBuiltinLess:
|
||||
case kTfLiteBuiltinLessEqual:
|
||||
case kTfLiteBuiltinPow:
|
||||
case kTfLiteBuiltinMaximum:
|
||||
case kTfLiteBuiltinMinimum:
|
||||
case kTfLiteBuiltinPrelu:
|
||||
case kTfLiteBuiltinLeakyRelu:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsUseTargetDevice(NnrtDelegate::Options delegateOptions, bool excludeNnrtReference)
|
||||
{
|
||||
const std::string& deviceName = delegateOptions.acceleratorName;
|
||||
bool hasSelectedAccelerator = !deviceName.empty();
|
||||
if (!excludeNnrtReference && hasSelectedAccelerator) {
|
||||
if (!deviceName.compare(NNRT_REFERENCE_DEVICE)) {
|
||||
hasSelectedAccelerator = false;
|
||||
}
|
||||
}
|
||||
|
||||
return hasSelectedAccelerator;
|
||||
}
|
||||
|
||||
TfLiteStatus GetTargetDevice(TfLiteContext* context, TfLiteDelegate* delegate, const NnrtApi* nnrt, size_t& dev)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, nnrt != nullptr, true);
|
||||
TF_LITE_ENSURE_EQ(context, delegate != nullptr, true);
|
||||
|
||||
NnrtDelegate::Options delegateOptions;
|
||||
TF_LITE_ENSURE_STATUS(NnrtDelegate::GetOptions(delegate, delegateOptions));
|
||||
const std::string& deviceName = delegateOptions.acceleratorName;
|
||||
|
||||
uint32_t numDevices {0};
|
||||
const size_t* alldevicesID {nullptr};
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(nnrt->OH_NNDevice_GetAllDevicesID(&alldevicesID, &numDevices),
|
||||
"Get available device number and deviceID.");
|
||||
if (numDevices == 0) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Have no available device.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
std::vector<OH_NN_DeviceType> deviceTypes;
|
||||
for (uint32_t i = 0; i < numDevices; ++i) {
|
||||
OH_NN_DeviceType tempDeviceType {OH_NN_ACCELERATOR};
|
||||
RETURN_TFLITE_ERROR_IF_NN_ERROR(nnrt->OH_NNDevice_GetType(alldevicesID[i], &tempDeviceType),
|
||||
"Get available devicesType.");
|
||||
deviceTypes.emplace_back(tempDeviceType);
|
||||
}
|
||||
|
||||
OH_NN_DeviceType deviceType {OH_NN_CPU};
|
||||
std::vector<OH_NN_DeviceType>::iterator pos = std::find(deviceTypes.begin(), deviceTypes.end(), deviceType);
|
||||
if (pos != deviceTypes.end()) {
|
||||
int index = distance(deviceTypes.begin(), pos);
|
||||
dev = alldevicesID[index];
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-UTILS] Cannot find the %s device, please choose another process unit.",
|
||||
deviceName.c_str());
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus TransposeDims(TfLiteContext* context, const int32_t* dims, uint32_t dimCount,
|
||||
std::vector<int32_t> destAxis, std::vector<int32_t>& weightDims)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
|
||||
|
||||
if (dimCount != destAxis.size()) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Invalid dimension count %d.", dimCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
for (auto axis : destAxis) {
|
||||
if (axis < 0) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Invalid axis %d.", axis);
|
||||
return kTfLiteError;
|
||||
}
|
||||
weightDims.emplace_back(*(dims + axis));
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GetTensorSize(TfLiteContext* context, const int32_t* dims, int32_t dimCount, int64_t& tensorSize)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
|
||||
|
||||
if (dimCount != DEPTHWISE_WEIGHT_DIMENSION_COUNT) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[NNRT-UTILS] Dimension count is not equal to destination axis number, should be 4.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
tensorSize = 1;
|
||||
for (int32_t i = 0; i < dimCount; ++i) {
|
||||
if (*(dims + i) <= 0) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "[NNRT-UTILS] Get invalid dimenision.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
tensorSize *= *(dims + i);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
const std::vector<int32_t> ACTIVATE_FUSE_TYPE_LIST = {
|
||||
OH_NN_FUSED_NONE,
|
||||
OH_NN_FUSED_RELU,
|
||||
OH_NN_FUSE_UNSUPPORTED,
|
||||
OH_NN_FUSED_RELU6,
|
||||
OH_NN_FUSE_UNSUPPORTED,
|
||||
OH_NN_FUSE_UNSUPPORTED,
|
||||
OH_NN_FUSE_UNSUPPORTED
|
||||
};
|
||||
|
||||
const unorderedTypeMap TFLITE_TYPE_TO_NNRT_TYPE = {
|
||||
{kTfLiteBuiltinAdd, OH_NN_OPS_ADD},
|
||||
{kTfLiteBuiltinAveragePool2d, OH_NN_OPS_AVG_POOL},
|
||||
{kTfLiteBuiltinConcatenation, OH_NN_OPS_CONCAT},
|
||||
{kTfLiteBuiltinConv2d, OH_NN_OPS_CONV2D},
|
||||
{kTfLiteBuiltinDepthwiseConv2d, OH_NN_OPS_DEPTHWISE_CONV2D_NATIVE},
|
||||
{kTfLiteBuiltinDepthToSpace, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinDequantize, OH_NN_OPS_QUANT_DTYPE_CAST},
|
||||
{kTfLiteBuiltinEmbeddingLookup, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFloor, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFullyConnected, OH_NN_OPS_FULL_CONNECTION},
|
||||
{kTfLiteBuiltinHashtableLookup, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinL2Normalization, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinL2Pool2d, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLocalResponseNormalization, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLogistic, OH_NN_OPS_SIGMOID},
|
||||
{kTfLiteBuiltinLshProjection, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLstm, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMaxPool2d, OH_NN_OPS_MAX_POOL},
|
||||
{kTfLiteBuiltinMul, OH_NN_OPS_MUL},
|
||||
{kTfLiteBuiltinRelu, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReluN1To1, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRelu6, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReshape, OH_NN_OPS_RESHAPE},
|
||||
{kTfLiteBuiltinResizeBilinear, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRnn, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSoftmax, OH_NN_OPS_SOFTMAX},
|
||||
{kTfLiteBuiltinSpaceToDepth, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSvdf, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinTanh, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinConcatEmbeddings, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSkipGram, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCall, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCustom, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinEmbeddingLookupSparse, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinPad, OH_NN_OPS_PAD},
|
||||
{kTfLiteBuiltinUnidirectionalSequenceRnn, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinGather, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinBatchToSpaceNd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSpaceToBatchNd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinTranspose, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMean, OH_NN_OPS_REDUCE_MEAN},
|
||||
{kTfLiteBuiltinSub, OH_NN_OPS_SUB},
|
||||
{kTfLiteBuiltinDiv, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSqueeze, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinUnidirectionalSequenceLstm, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinStridedSlice, OH_NN_OPS_STRIDED_SLICE},
|
||||
{kTfLiteBuiltinBidirectionalSequenceRnn, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinExp, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinTopkV2, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSplit, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLogSoftmax, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinDelegate, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinBidirectionalSequenceLstm, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCast, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinPrelu, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMaximum, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinArgMax, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMinimum, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLess, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinNeg, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinPadv2, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinGreater, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinGreaterEqual, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLessEqual, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSelect, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSlice, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSin, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinTransposeConv, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSparseToDense, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinTile, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinExpandDims, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinEqual, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinNotEqual, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLog, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSum, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSqrt, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRsqrt, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinShape, OH_NN_OPS_SHAPE},
|
||||
{kTfLiteBuiltinPow, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinArgMin, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFakeQuant, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReduceProd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReduceMax, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinPack, OH_NN_OPS_STACK},
|
||||
{kTfLiteBuiltinLogicalOr, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinOneHot, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLogicalAnd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLogicalNot, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinUnpack, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReduceMin, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFloorDiv, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReduceAny, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSquare, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinZerosLike, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFill, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinFloorMod, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRange, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinResizeNearestNeighbor, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinLeakyRelu, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSquaredDifference, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMirrorPad, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinAbs, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSplitV, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinUnique, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCeil, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReverseV2, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinAddN, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinGatherNd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCos, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinWhere, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRank, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinElu, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReverseSequence, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinMatrixDiag, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinQuantize, OH_NN_OPS_QUANT_DTYPE_CAST},
|
||||
{kTfLiteBuiltinMatrixSetDiag, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRound, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinHardSwish, OH_NN_OPS_HSWISH},
|
||||
{kTfLiteBuiltinIf, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinWhile, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinNonMaxSuppressionV4, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinNonMaxSuppressionV5, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinScatterNd, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSelectV2, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinDensify, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinSegmentSum, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinBatchMatmul, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinPlaceholderForGreaterOpCodes, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCumsum, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinCallOnce, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinBroadcastTo, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinRfft2d, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinConv3d, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinImag, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReal, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinComplexAbs, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinHashtable, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinHashtableFind, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinHashtableImport, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinHashtableSize, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReduceAll, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinConv3dTranspose, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinVarHandle, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinReadVariable, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinAssignVariable, OH_NN_UNSUPPORT_OPS},
|
||||
{kTfLiteBuiltinBroadcastTo, OH_NN_UNSUPPORT_OPS},
|
||||
};
|
||||
} // nnrt
|
||||
} // namespace
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
|
||||
#define TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nnrt_delegate.h"
|
||||
|
||||
namespace tflite {
|
||||
constexpr int32_t DEPTHWISE_WEIGHT_BATCH_DIMENSION = 0;
|
||||
constexpr int32_t DEPTHWISE_WEIGHT_HEIGHT_DIMENSION = 1;
|
||||
constexpr int32_t DEPTHWISE_WEIGHT_WIDTH_DIMENSION = 2;
|
||||
constexpr int32_t DEPTHWISE_WEIGHT_CHANNEL_DIMENSION = 3;
|
||||
constexpr int32_t DEPTHWISE_WEIGHT_DIMENSION_COUNT = 4;
|
||||
const std::string NNRT_REFERENCE_DEVICE = "nnrt-reference";
|
||||
|
||||
// Bit mask for tensor flags.
|
||||
enum BIT_MASK {
|
||||
NN_TENSOR_FLAG_SCALAR_AS_TENSOR = 1U << 0,
|
||||
NN_TENSOR_FLAG_INT8_CONVERSION = 1U << 1,
|
||||
NN_TENSOR_FLAG_USE_INT8_ASYMM_SIGNED = 1U << 2,
|
||||
NN_TENSOR_FLAG_FORCE_PER_CHANNEL = 1U << 3,
|
||||
NN_TENSOR_FLAG_HALF_TO_FLOAT_CONVERSION = 1U << 4,
|
||||
};
|
||||
|
||||
// Returns the enum name corresponding to the given error code if the given
|
||||
// value corresponds to an of the error codes in the enumeration above or
|
||||
// an message with the unknown code.
|
||||
// LINT.IfChange(NnrtErrorDescription)
|
||||
extern std::string NnrtErrorDescription(int32_t errorCode);
|
||||
|
||||
#define RETURN_TFLITE_ERROR_IF_NN_ERROR(code, callDesc) \
|
||||
do { \
|
||||
if ((code) != OH_NN_SUCCESS) { \
|
||||
const auto errorDesc = NnrtErrorDescription((code)); \
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, "NN API returned error %s at line %d while %s.\n", \
|
||||
errorDesc.c_str(), __LINE__, (callDesc)); \
|
||||
return kTfLiteError; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_TFLITE_ERROR_IF_NN_ERROR_FOR_TENSOR(code, callDesc, pTensor) \
|
||||
do { \
|
||||
if ((code) != OH_NN_SUCCESS) { \
|
||||
const auto errorDesc = NnrtErrorDescription((code)); \
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR, \
|
||||
"NN API returned error %s at line %d while %s for tensor '%s'.\n", errorDesc.c_str(), \
|
||||
__LINE__, (callDesc), (pTensor)->name ? (pTensor)->name : "no-name"); \
|
||||
return kTfLiteError; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Return true if type is kTfLiteFloat32.
|
||||
extern bool IsFloat(TfLiteType type);
|
||||
|
||||
// Return true if type is kTfLiteUInt8 or kTfLiteInt8.
|
||||
extern bool IsQuantized(TfLiteType type);
|
||||
|
||||
// Return true if the operator supports scalar data as input.
|
||||
extern bool IsScalarInputSupported(int32_t builtinCode);
|
||||
|
||||
// Returns true if this delegate is configured to use a specific set of devices.
|
||||
// If the acceleratorName in the delegate options is equal to "nnrt-reference"
|
||||
// this method will return true only if the excludeNnrtReference is true.
|
||||
extern bool IsUseTargetDevice(
|
||||
NnrtDelegate::Options delegateOptions, bool excludeNnrtReference = false);
|
||||
|
||||
// Fills the given result vector with the list of devices the given delegate
|
||||
// is referring to.
|
||||
// There are three possible results,
|
||||
// - An empty array (not the full list of available accelerators,
|
||||
// for efficiency reasons) if no accelerator is chosen and the
|
||||
// disallowNnrtCpu delegate option is false.
|
||||
// - A single element array with the target processor, if an accelerator name
|
||||
// is specified in the delegate options.
|
||||
// - The target available device on device.
|
||||
extern TfLiteStatus GetTargetDevice(TfLiteContext* context, TfLiteDelegate* delegate,
|
||||
const NnrtApi* nnrt, size_t& dev);
|
||||
|
||||
// Transpose demension following fixed axis.
|
||||
// If exist -1 in destAxis, return kTfLiteError.
|
||||
extern TfLiteStatus TransposeDims(TfLiteContext* context, const int32_t* dims, uint32_t dimCount,
|
||||
std::vector<int32_t> destAxis, std::vector<int32_t>& weightDims);
|
||||
|
||||
// Get Tensor size by byte.
|
||||
// Calculate Tesnorsize by mul all dimension in dims.
|
||||
// Return kTfLiteError if element dimension is less 0.
|
||||
extern TfLiteStatus GetTensorSize(TfLiteContext* context, const int32_t* dims, int32_t dimCount, int64_t& tensorSize);
|
||||
|
||||
// Transpose dimension for Tensor.
|
||||
// Only change NHWC format tensor to CHWN format tensor, and
|
||||
// the capacity of result vec must equal to input tensor size.
|
||||
template <class T>
|
||||
TfLiteStatus TransposeTensor(TfLiteContext* context, int32_t tensorIndex, const int32_t* dims,
|
||||
T* transposeTensor)
|
||||
{
|
||||
TF_LITE_ENSURE_EQ(context, dims != nullptr, true);
|
||||
|
||||
// NHWC -> CHWN
|
||||
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
|
||||
const T* tensorData = reinterpret_cast<T*>(tensor->data.data);
|
||||
const int32_t batch = dims[DEPTHWISE_WEIGHT_BATCH_DIMENSION];
|
||||
const int32_t height = dims[DEPTHWISE_WEIGHT_HEIGHT_DIMENSION];
|
||||
const int32_t width = dims[DEPTHWISE_WEIGHT_WIDTH_DIMENSION];
|
||||
const int32_t channel = dims[DEPTHWISE_WEIGHT_CHANNEL_DIMENSION];
|
||||
|
||||
for (int32_t c = 0; c < channel; ++c) {
|
||||
for (int32_t j = 0; j < height * width; ++j) {
|
||||
for (int32_t n = 0; n < batch; ++n) {
|
||||
int32_t newPos = c * (height * width) * batch + j * batch + n;
|
||||
int32_t orgPos = n * (height * width) * channel + j * channel + c;
|
||||
*(transposeTensor + newPos) = *(tensorData + orgPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
};
|
||||
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
using unorderedTypeMap = std::unordered_map<int32_t, int32_t>;
|
||||
|
||||
extern const std::vector<int32_t> ACTIVATE_FUSE_TYPE_LIST;
|
||||
|
||||
extern const unorderedTypeMap TFLITE_TYPE_TO_NNRT_TYPE;
|
||||
|
||||
const int32_t INVALID_INDEX = -1;
|
||||
|
||||
const int32_t OH_NN_UNSUPPORT_OPS = -1;
|
||||
|
||||
const int32_t OH_NN_FUSE_UNSUPPORTED = -1;
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_UTILS_H
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
|
||||
#define TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/minimal_logging.h"
|
||||
|
||||
#include "nnrt_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace delegate {
|
||||
namespace nnrt {
|
||||
constexpr uint32_t QUANT_NUMBITS = 8;
|
||||
|
||||
class TensorMapping {
|
||||
public:
|
||||
// Given a TFLite index return the NN index. If it doesn't exist
|
||||
// return -1.
|
||||
int32_t LiteIndexToNn(int32_t index) const
|
||||
{
|
||||
const int64_t maxSize = m_liteTensorToNnTensor.size();
|
||||
if (index >= 0 && index < maxSize) {
|
||||
return m_liteTensorToNnTensor[index];
|
||||
} else {
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
// NN API uses non tensor tensors instead of structs. This creates one
|
||||
// and returns the index. It uses a std::vector and resizes it as needed
|
||||
// keeping -1 to unmapped values. Intermediate tensors likely will not
|
||||
// be mapped.
|
||||
const int32_t AddNewNonTensorTensor()
|
||||
{
|
||||
return m_nextNnTensorIndex++;
|
||||
}
|
||||
|
||||
// Add a new mapping from `tfliteIndex` and return the NN API tensor index.
|
||||
int32_t AddNewNnTensorIndex(int32_t tfliteIndex)
|
||||
{
|
||||
const int64_t currentSize = m_liteTensorToNnTensor.size();
|
||||
if (tfliteIndex >= currentSize) {
|
||||
m_liteTensorToNnTensor.resize(tfliteIndex + 1, INVALID_INDEX);
|
||||
}
|
||||
const int32_t newTensorIndex = m_nextNnTensorIndex++;
|
||||
m_liteTensorToNnTensor[tfliteIndex] = newTensorIndex;
|
||||
return newTensorIndex;
|
||||
}
|
||||
|
||||
// Get nn tensor tensor tensor num.
|
||||
int32_t GetTensorTensorNum() const
|
||||
{
|
||||
return m_nextNnTensorIndex;
|
||||
}
|
||||
|
||||
// Given a TFLite index returns a TFLite type to which a tensor must be
|
||||
// converted during copying the data to the memory allocated for NN API.
|
||||
// kTfLiteNoType means no conversion is needed.
|
||||
TfLiteType GetEqualLiteTypeFromLiteIndex(int32_t index) const
|
||||
{
|
||||
const int64_t maxSize = m_indexToTypeConversion.size();
|
||||
if (index >= 0 && index < maxSize)
|
||||
return m_indexToTypeConversion[index];
|
||||
else
|
||||
return kTfLiteNoType;
|
||||
}
|
||||
|
||||
// Add a new mapping from TFLite index to a type conversion.
|
||||
void AddTypeConversion(int32_t tfliteIndex, TfLiteType tfliteType)
|
||||
{
|
||||
const int64_t currentSize = m_indexToTypeConversion.size();
|
||||
if (tfliteIndex >= currentSize) {
|
||||
m_indexToTypeConversion.resize(tfliteIndex + 1, kTfLiteNoType);
|
||||
}
|
||||
m_indexToTypeConversion[tfliteIndex] = tfliteType;
|
||||
}
|
||||
|
||||
// Convert TFLite tensor quant params to NNRT tensor quant params
|
||||
TfLiteStatus ConvertQuantParams(TfLiteContext* context, int32_t tensorIndex, OH_NN_QuantParam& quantParam)
|
||||
{
|
||||
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
|
||||
TfLiteType tfType = tensor->type;
|
||||
if ((tfType != kTfLiteFloat32) && (tfType != kTfLiteFloat16) && (tfType != kTfLiteBool) &&
|
||||
(tfType != kTfLiteInt32) && (tfType != kTfLiteUInt8) && (tfType != kTfLiteInt8)) {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[TENSOR_MAPPING] type %s is not supported.", TfLiteTypeGetName(tensor->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (tensor->quantization.type) {
|
||||
TfLiteAffineQuantization* params = reinterpret_cast<TfLiteAffineQuantization*>(tensor->quantization.params);
|
||||
int number = params->scale->size;
|
||||
std::vector<double> scale;
|
||||
for (int i = 0; i < number; ++i) {
|
||||
scale.emplace_back(static_cast<double>(params->scale->data[i]));
|
||||
}
|
||||
m_scale.emplace_back(scale);
|
||||
quantParam.scale = m_scale.back().data();
|
||||
quantParam.zeroPoint = params->zero_point->data;
|
||||
quantParam.quantCount = number;
|
||||
m_numBits.emplace_back(number, QUANT_NUMBITS);
|
||||
quantParam.numBits = m_numBits.back().data();
|
||||
} else {
|
||||
quantParam.quantCount = 0;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Convert TFLite tensor type to NNRT tensor type
|
||||
TfLiteStatus ConvertType(TfLiteContext* context, int32_t tensorIndex, int32_t tensorFlags, OH_NN_DataType& nnType)
|
||||
{
|
||||
const bool scalarAsTensor = tensorFlags & NN_TENSOR_FLAG_SCALAR_AS_TENSOR;
|
||||
TfLiteTensor* tensor = &(context->tensors[tensorIndex]);
|
||||
TfLiteType nnTypeEquivalent = GetEqualLiteTypeFromLiteIndex(tensorIndex);
|
||||
if (tensor->type == kTfLiteFloat32) {
|
||||
nnType = OH_NN_FLOAT32;
|
||||
} else if (tensor->type == kTfLiteFloat16) {
|
||||
nnType = OH_NN_FLOAT16;
|
||||
if (scalarAsTensor) {
|
||||
nnType = OH_NN_FLOAT32;
|
||||
AddTypeConversion(tensorIndex, kTfLiteFloat32);
|
||||
}
|
||||
} else if (tensor->type == kTfLiteInt32) {
|
||||
nnType = OH_NN_INT32;
|
||||
} else if (tensor->type == kTfLiteBool) {
|
||||
nnType = OH_NN_INT8;
|
||||
} else if (tensor->type == kTfLiteUInt8) {
|
||||
nnType = (nnTypeEquivalent == kTfLiteInt32) ? OH_NN_INT32 : OH_NN_INT8;
|
||||
} else if (tensor->type == kTfLiteInt8) {
|
||||
nnType = (nnTypeEquivalent == kTfLiteInt32) ? OH_NN_INT32 : OH_NN_UINT8;
|
||||
} else {
|
||||
TFLITE_LOG_PROD(TFLITE_LOG_ERROR,
|
||||
"[TENSOR_MAPPING] type %s is not supported.", TfLiteTypeGetName(tensor->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
private:
|
||||
// Next index of nnrt tensor
|
||||
int32_t m_nextNnTensorIndex = 0;
|
||||
|
||||
// Mapping from lite index. Use a std::vector for speed and code size
|
||||
// rather than a map.
|
||||
std::vector<int32_t> m_liteTensorToNnTensor;
|
||||
|
||||
// Mapping from lite index to a type which tensor must be converted to during
|
||||
// the copying of the data to the memory allocated for NN API. kTfLiteNoType
|
||||
// means no conversion is needed. Use an std::vector for speed and code size
|
||||
// rather than a map.
|
||||
std::vector<TfLiteType> m_indexToTypeConversion;
|
||||
|
||||
std::vector<std::vector<uint32_t>> m_numBits;
|
||||
|
||||
std::vector<std::vector<double>> m_scale;
|
||||
};
|
||||
} // namespace nnrt
|
||||
} // namespace delegate
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_DELEGATES_NNRT_TENSOR_MAPPING_H
|
||||
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Header path
|
||||
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
|
||||
set(TOOLS_INC ${LOCAL_DIRECTORY_PATH}/tflite/tools)
|
||||
set(TFLITE_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include)
|
||||
set(TFLITE_FLATBUFFER_INC ${LOCAL_DIRECTORY_PATH}/lib_3rd_nnrt_tflite/include/tensorflow/lite)
|
||||
include_directories(${NNRT_DEMO_HOME} ${TFLITE_INC} ${OHOS_INC} ${TOOLS_INC} ${TFLITE_FLATBUFFER_INC} ${LOCAL_DIRECTORY_PATH})
|
||||
|
||||
# Scr path
|
||||
aux_source_directory(${NNRT_DEMO_HOME} NNRT_DEMO_SRCS)
|
||||
file(GLOB TOOLS_SRCS "${TOOLS_INC}/*.cpp")
|
||||
|
||||
LINK_DIRECTORIES(${TFLITE_LIB_PATH}/com/arm64-v8a/lib/)
|
||||
add_executable(label_classify ${NNRT_DEMO_SRCS} ${TOOLS_SRCS})
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,relro,-z,now,-z,noexecstack")
|
||||
target_link_libraries(label_classify ${LOCAL_DIRECTORY_PATH}/lib/libnnrt_implementation.so)
|
||||
target_link_libraries(label_classify ${LOCAL_DIRECTORY_PATH}/lib/libnnrt_delegate.so)
|
||||
target_link_libraries(label_classify -ltensorflow-lite)
|
||||
|
||||
set (EXECUTABLE_OUTPUT_PATH ${LOCAL_DIRECTORY_PATH}/output)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "label_classify.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "tensorflow/lite/kernels/register.h"
|
||||
#include "tensorflow/lite/optional_debug_tools.h"
|
||||
#include "tensorflow/lite/string_util.h"
|
||||
#include "tensorflow/lite/tools/command_line_flags.h"
|
||||
#include "tensorflow/lite/tools/delegates/delegate_provider.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
using TfLiteDelegatePtr = tflite::Interpreter::TfLiteDelegatePtr;
|
||||
using ProvidedDelegateList = tflite::tools::ProvidedDelegateList;
|
||||
constexpr int BASE_NUMBER = 10;
|
||||
constexpr int CONVERSION_RATE = 1000;
|
||||
static struct option LONG_OPTIONS[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"use_nnrt", required_argument, nullptr, 'a'},
|
||||
{"count", required_argument, nullptr, 'c'},
|
||||
{"image", required_argument, nullptr, 'i'},
|
||||
{"labels", required_argument, nullptr, 'l'},
|
||||
{"tflite_model", required_argument, nullptr, 'm'},
|
||||
{"num_results", required_argument, nullptr, 'n'},
|
||||
{"input_mean", required_argument, nullptr, 'b'},
|
||||
{"input_std", required_argument, nullptr, 's'},
|
||||
{"verbose", required_argument, nullptr, 'v'},
|
||||
{"warmup_nums", required_argument, nullptr, 'w'},
|
||||
{"print_result", required_argument, nullptr, 'z'},
|
||||
{"input_shape", required_argument, nullptr, 'p'},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
};
|
||||
|
||||
class DelegateProviders {
|
||||
public:
|
||||
DelegateProviders() : m_delegateListUtil(¶ms)
|
||||
{
|
||||
m_delegateListUtil.AddAllDelegateParams(); // Add all registered delegate params to the contained 'params_'.
|
||||
}
|
||||
|
||||
~DelegateProviders() {}
|
||||
|
||||
bool InitFromCmdlineArgs(int32_t* argc, const char** argv)
|
||||
{
|
||||
std::vector<tflite::Flag> flags;
|
||||
m_delegateListUtil.AppendCmdlineFlags(&flags);
|
||||
|
||||
const bool parseResult = Flags::Parse(argc, argv, flags);
|
||||
if (!parseResult) {
|
||||
std::string usage = Flags::Usage(argv[0], flags);
|
||||
LOG(ERROR) << usage;
|
||||
}
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
void MergeSettingsIntoParams(const Settings& settings)
|
||||
{
|
||||
if (settings.accel) {
|
||||
if (!params.HasParam("use_nnrt")) {
|
||||
LOG(WARN) << "NNRT deleate execution provider isn't linked or NNRT "
|
||||
<< "delegate isn't supported on the platform!";
|
||||
} else {
|
||||
params.Set<bool>("use_nnrt", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ProvidedDelegateList::ProvidedDelegate> CreateAllDelegates() const
|
||||
{
|
||||
return m_delegateListUtil.CreateAllRankedDelegates();
|
||||
}
|
||||
|
||||
private:
|
||||
// Contain delegate-related parameters that are initialized from command-line flags.
|
||||
tflite::tools::ToolParams params;
|
||||
|
||||
// A helper to create TfLite delegates.
|
||||
ProvidedDelegateList m_delegateListUtil;
|
||||
};
|
||||
|
||||
void PrepareModel(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter,
|
||||
DelegateProviders& delegateProviders)
|
||||
{
|
||||
const std::vector<int32_t> inputs = interpreter->inputs();
|
||||
const std::vector<int32_t> outputs = interpreter->outputs();
|
||||
|
||||
if (settings.verbose) {
|
||||
LOG(INFO) << "number of inputs: " << inputs.size();
|
||||
LOG(INFO) << "number of outputs: " << outputs.size();
|
||||
}
|
||||
|
||||
std::map<int, std::vector<int>> neededInputShapes;
|
||||
if (settings.inputShape != "") {
|
||||
if (FilterDynamicInputs(settings, interpreter, neededInputShapes) != kTfLiteOk) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delegateProviders.MergeSettingsIntoParams(settings);
|
||||
auto delegates = delegateProviders.CreateAllDelegates();
|
||||
|
||||
for (auto& delegate : delegates) {
|
||||
const auto delegateName = delegate.provider->GetName();
|
||||
if (interpreter->ModifyGraphWithDelegate(std::move(delegate.delegate)) != kTfLiteOk) {
|
||||
LOG(ERROR) << "Failed to apply " << delegateName << " delegate.";
|
||||
return;
|
||||
} else {
|
||||
LOG(INFO) << "Applied " << delegateName << " delegate.";
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.inputShape != "") {
|
||||
for (const auto& inputShape : neededInputShapes) {
|
||||
if (IsEqualShape(inputShape.first, inputShape.second, interpreter)) {
|
||||
LOG(WARNING) << "The input shape is same as the model shape, not resize.";
|
||||
continue;
|
||||
}
|
||||
if (interpreter->ResizeInputTensor(inputShape.first, inputShape.second) != kTfLiteOk) {
|
||||
LOG(ERROR) << "Fail to resize index " << inputShape.first << ".";
|
||||
return;
|
||||
} else {
|
||||
LOG(INFO) << "Susccess to resize index " << inputShape.first << ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (interpreter->AllocateTensors() != kTfLiteOk) {
|
||||
LOG(ERROR) << "Failed to allocate tensors!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.verbose) {
|
||||
PrintInterpreterState(interpreter.get());
|
||||
}
|
||||
}
|
||||
|
||||
void LogInterpreterParams(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter)
|
||||
{
|
||||
if (!interpreter) {
|
||||
LOG(ERROR) << "Failed to construct interpreter";
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.verbose) {
|
||||
LOG(INFO) << "tensors size: " << interpreter->tensors_size();
|
||||
LOG(INFO) << "nodes size: " << interpreter->nodes_size();
|
||||
LOG(INFO) << "inputs: " << interpreter->inputs().size();
|
||||
LOG(INFO) << "input(0) name: " << interpreter->GetInputName(0);
|
||||
|
||||
size_t tSize = interpreter->tensors_size();
|
||||
for (size_t i = 0; i < tSize; ++i) {
|
||||
if (interpreter->tensor(i)->name) {
|
||||
LOG(INFO) << i << ": " << interpreter->tensor(i)->name << ", " << interpreter->tensor(i)->bytes <<
|
||||
", " << interpreter->tensor(i)->type << ", " << interpreter->tensor(i)->params.scale << ", " <<
|
||||
interpreter->tensor(i)->params.zero_point;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InferenceModel(Settings& settings, DelegateProviders& delegateProviders)
|
||||
{
|
||||
if (!settings.modelName.c_str()) {
|
||||
LOG(ERROR) << "no model file name";
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<tflite::FlatBufferModel> model;
|
||||
std::unique_ptr<tflite::Interpreter> interpreter;
|
||||
model = tflite::FlatBufferModel::BuildFromFile(settings.modelName.c_str());
|
||||
if (!model) {
|
||||
LOG(ERROR) << "Failed to mmap model " << settings.modelName;
|
||||
return;
|
||||
}
|
||||
|
||||
settings.model = model.get();
|
||||
model->error_reporter();
|
||||
tflite::ops::builtin::BuiltinOpResolver resolver;
|
||||
tflite::InterpreterBuilder(*model, resolver)(&interpreter);
|
||||
if (!interpreter) {
|
||||
LOG(ERROR) << "Failed to construct interpreter, please check the model.";
|
||||
return;
|
||||
}
|
||||
|
||||
LogInterpreterParams(settings, interpreter);
|
||||
|
||||
// set settings input type
|
||||
PrepareModel(settings, interpreter, delegateProviders);
|
||||
std::vector<int> imageSize { 224, 224, 3};
|
||||
ImportData(settings, imageSize, interpreter);
|
||||
|
||||
if (settings.loopCount > 0 && settings.numberOfWarmupRuns > 0) {
|
||||
LOG(INFO) << "Warm-up for " << settings.numberOfWarmupRuns << " times";
|
||||
for (int32_t i = 0; i < settings.numberOfWarmupRuns; ++i) {
|
||||
if (interpreter->Invoke() != kTfLiteOk) {
|
||||
LOG(ERROR) << "Failed to invoke tflite!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval startTime, stopTime;
|
||||
LOG(INFO) << "Invoke for " << settings.loopCount << " times";
|
||||
gettimeofday(&startTime, nullptr);
|
||||
for (int32_t i = 0; i < settings.loopCount; ++i) {
|
||||
if (interpreter->Invoke() != kTfLiteOk) {
|
||||
LOG(ERROR) << "Failed to invoke tflite!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gettimeofday(&stopTime, nullptr);
|
||||
LOG(INFO) << "invoked, average time: " <<
|
||||
(GetUs(stopTime) - GetUs(startTime)) / (settings.loopCount * CONVERSION_RATE) << " ms";
|
||||
AnalysisResults(settings, interpreter);
|
||||
}
|
||||
|
||||
void DisplayUsage()
|
||||
{
|
||||
LOG(INFO) << "label_classify\n"
|
||||
<< "\t--help, -h: show the usage of the demo\n"
|
||||
<< "\t--use_nnrt, -a: [0|1], use NNRT or not\n"
|
||||
<< "\t--input_mean, -b: input mean\n"
|
||||
<< "\t--count, -c: loop interpreter->Invoke() for certain times\n"
|
||||
<< "\t--image, -i: image_name.bmp\n"
|
||||
<< "\t--labels, -l: labels for the model\n"
|
||||
<< "\t--tflite_model, -m: modelName.tflite\n"
|
||||
<< "\t--num_results, -n: number of results to show\n"
|
||||
<< "\t--input_std, -s: input standard deviation\n"
|
||||
<< "\t--verbose, -v: [0|1] print more information\n"
|
||||
<< "\t--warmup_nums, -w: number of warmup runs\n"
|
||||
<< "\t--print_result, -z: flag to print results\n"
|
||||
<< "\t--input_shape, -p: Indicates the specified dynamic input node and the corresponding shape.\n";
|
||||
}
|
||||
|
||||
void InitSettings(int32_t argc, char** argv, Settings& settings)
|
||||
{
|
||||
// getopt_long stores the option index here.
|
||||
int32_t optionIndex = 0;
|
||||
while ((optionIndex = getopt_long(argc, argv, "a:b:c:h:i:l:m:n:p:s:v:w:z:", LONG_OPTIONS, nullptr)) != -1) {
|
||||
switch (optionIndex) {
|
||||
case 'a':
|
||||
settings.accel = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'b':
|
||||
settings.inputMean = strtod(optarg, nullptr);
|
||||
break;
|
||||
case 'c':
|
||||
settings.loopCount = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'i':
|
||||
settings.inputBmpName = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
settings.labelsFileName = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
settings.modelName = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
settings.numberOfResults = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'p':
|
||||
settings.inputShape = optarg;
|
||||
break;
|
||||
case 's':
|
||||
settings.inputStd = strtod(optarg, nullptr);
|
||||
break;
|
||||
case 'v':
|
||||
settings.verbose = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'w':
|
||||
settings.numberOfWarmupRuns = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'z':
|
||||
settings.printResult = strtol(optarg, nullptr, BASE_NUMBER);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
// getopt_long already printed an error message.
|
||||
DisplayUsage();
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Main(int32_t argc, char** argv)
|
||||
{
|
||||
DelegateProviders delegateProviders;
|
||||
bool parseResult = delegateProviders.InitFromCmdlineArgs(&argc, const_cast<const char**>(argv));
|
||||
if (!parseResult) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Settings settings;
|
||||
InitSettings(argc, argv, settings);
|
||||
InferenceModel(settings, delegateProviders);
|
||||
return 0;
|
||||
}
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
int32_t main(int32_t argc, char** argv)
|
||||
{
|
||||
return tflite::label_classify::Main(argc, argv);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
|
||||
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "tensorflow/lite/model_builder.h"
|
||||
#include "tensorflow/lite/string_type.h"
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
struct Settings {
|
||||
tflite::FlatBufferModel* model;
|
||||
bool verbose = false;
|
||||
bool accel = false;
|
||||
bool printResult = false;
|
||||
TfLiteType inputType = kTfLiteFloat32;
|
||||
int32_t loopCount = 1;
|
||||
float inputMean = 127.5f;
|
||||
float inputStd = 127.5f;
|
||||
string modelName = "./mbv2.tflite";
|
||||
string inputBmpName = "./grace_hopper.bmp";
|
||||
string labelsFileName = "./labels.txt";
|
||||
string inputShape = "";
|
||||
int32_t numberOfThreads = 1;
|
||||
int32_t numberOfResults = 5;
|
||||
int32_t numberOfWarmupRuns = 0;
|
||||
};
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_H
|
||||
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Header path
|
||||
set(OHOS_INC ${LOCAL_DIRECTORY_PATH}/../../interfaces/kits/c)
|
||||
include_directories(${NNRT_INTERFACE_HOME} ${OHOS_INC})
|
||||
|
||||
# Scr path
|
||||
file(GLOB NNRT_SRCS "${NNRT_INTERFACE_HOME}/*.cpp")
|
||||
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LOCAL_DIRECTORY_PATH}/lib)
|
||||
add_library(nnrt_implementation SHARED ${NNRT_SRCS})
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nnrt_implementation.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace tflite {
|
||||
// These function parameters are guaranteed to be nullptr by the caller
|
||||
template<class T>
|
||||
void LoadFunction(void* handle, const char* name, T* nnrtFunction)
|
||||
{
|
||||
if (name == nullptr) {
|
||||
NNRT_LOG("nnrt error: the function %s does not exist.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
void* fn = dlsym(handle, name);
|
||||
if (fn == nullptr) {
|
||||
NNRT_LOG("nnrt error: unable to open function %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
*nnrtFunction = reinterpret_cast<T>(fn);
|
||||
return;
|
||||
}
|
||||
|
||||
const NnrtApi LoadNnrt()
|
||||
{
|
||||
NnrtApi nnrt;
|
||||
nnrt.nnrtExists = false;
|
||||
void* libNeuralNetworks = nullptr;
|
||||
|
||||
// Assumes there can be multiple instances of NN API
|
||||
static std::string nnrtLibraryName = "libneural_network_runtime.z.so";
|
||||
libNeuralNetworks = dlopen(nnrtLibraryName.c_str(), RTLD_LAZY | RTLD_NODELETE);
|
||||
if (libNeuralNetworks == nullptr) {
|
||||
NNRT_LOG("nnrt error: unable to open library %s", nnrtLibraryName.c_str());
|
||||
return nnrt;
|
||||
} else {
|
||||
nnrt.nnrtExists = true;
|
||||
}
|
||||
|
||||
// NNModel
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_Construct", &nnrt.OH_NNModel_Construct);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_AddTensor", &nnrt.OH_NNModel_AddTensor);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_SetTensorData", &nnrt.OH_NNModel_SetTensorData);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_AddOperation", &nnrt.OH_NNModel_AddOperation);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_SpecifyInputsAndOutputs", &nnrt.OH_NNModel_SpecifyInputsAndOutputs);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_Finish", &nnrt.OH_NNModel_Finish);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_Destroy", &nnrt.OH_NNModel_Destroy);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNModel_GetAvailableOperations", &nnrt.OH_NNModel_GetAvailableOperations);
|
||||
|
||||
// NNCompilation
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Construct", &nnrt.OH_NNCompilation_Construct);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetDevice", &nnrt.OH_NNCompilation_SetDevice);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetCache", &nnrt.OH_NNCompilation_SetCache);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetPerformanceMode", &nnrt.OH_NNCompilation_SetPerformanceMode);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_SetPriority", &nnrt.OH_NNCompilation_SetPriority);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_EnableFloat16", &nnrt.OH_NNCompilation_EnableFloat16);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Build", &nnrt.OH_NNCompilation_Build);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNCompilation_Destroy", &nnrt.OH_NNCompilation_Destroy);
|
||||
|
||||
// NNExecutor
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Construct", &nnrt.OH_NNExecutor_Construct);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetInput", &nnrt.OH_NNExecutor_SetInput);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetOutput", &nnrt.OH_NNExecutor_SetOutput);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_GetOutputShape", &nnrt.OH_NNExecutor_GetOutputShape);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Run", &nnrt.OH_NNExecutor_Run);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_AllocateInputMemory", &nnrt.OH_NNExecutor_AllocateInputMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_AllocateOutputMemory", &nnrt.OH_NNExecutor_AllocateOutputMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_DestroyInputMemory", &nnrt.OH_NNExecutor_DestroyInputMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_DestroyOutputMemory", &nnrt.OH_NNExecutor_DestroyOutputMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetInputWithMemory", &nnrt.OH_NNExecutor_SetInputWithMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_SetOutputWithMemory", &nnrt.OH_NNExecutor_SetOutputWithMemory);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNExecutor_Destroy", &nnrt.OH_NNExecutor_Destroy);
|
||||
|
||||
// NNDevice
|
||||
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetAllDevicesID", &nnrt.OH_NNDevice_GetAllDevicesID);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetName", &nnrt.OH_NNDevice_GetName);
|
||||
LoadFunction(libNeuralNetworks, "OH_NNDevice_GetType", &nnrt.OH_NNDevice_GetType);
|
||||
|
||||
return nnrt;
|
||||
}
|
||||
|
||||
const NnrtApi* NnrtImplementation()
|
||||
{
|
||||
static const NnrtApi nnrt = LoadNnrt();
|
||||
if (!nnrt.nnrtExists) {
|
||||
return nullptr;
|
||||
}
|
||||
return &nnrt;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
|
||||
#define TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include "neural_network_runtime_type.h"
|
||||
|
||||
namespace tflite {
|
||||
#define NNRT_LOG(format, ...) fprintf(stderr, format "\n", __VA_ARGS__)
|
||||
|
||||
struct NnrtApi {
|
||||
// This indicates the availability of nnrt library. If it is false, it means that loading
|
||||
// the nnrt library failed and tflite will not use nnrt to run the model, vice versa.
|
||||
bool nnrtExists;
|
||||
|
||||
// Create model interface
|
||||
OH_NNModel* (*OH_NNModel_Construct)(void);
|
||||
OH_NN_ReturnCode (*OH_NNModel_AddTensor)(OH_NNModel* model, const OH_NN_Tensor* nnTensor);
|
||||
OH_NN_ReturnCode (*OH_NNModel_SetTensorData)(OH_NNModel* model, uint32_t index, const void* buffer,
|
||||
size_t length);
|
||||
OH_NN_ReturnCode (*OH_NNModel_AddOperation)(OH_NNModel* model, OH_NN_OperationType op,
|
||||
const OH_NN_UInt32Array* paramIndices, const OH_NN_UInt32Array* inputIndices,
|
||||
const OH_NN_UInt32Array* outputIndices);
|
||||
OH_NN_ReturnCode (*OH_NNModel_SpecifyInputsAndOutputs)(OH_NNModel* model, const OH_NN_UInt32Array* inputIndices,
|
||||
const OH_NN_UInt32Array* outputIndices);
|
||||
OH_NN_ReturnCode (*OH_NNModel_Finish)(OH_NNModel* model);
|
||||
void (*OH_NNModel_Destroy)(OH_NNModel** model);
|
||||
OH_NN_ReturnCode (*OH_NNModel_GetAvailableOperations)(OH_NNModel* model, size_t deviceID, const bool** isSupported,
|
||||
uint32_t* opCount);
|
||||
// Compilation interface
|
||||
OH_NNCompilation* (*OH_NNCompilation_Construct)(const OH_NNModel* model);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_SetCache)(OH_NNCompilation* compilation, const char* cacheDir,
|
||||
uint32_t version);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_SetPerformanceMode)(OH_NNCompilation* compilation,
|
||||
OH_NN_PerformanceMode performanceMode);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_SetPriority)(OH_NNCompilation* compilation, OH_NN_Priority priority);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_EnableFloat16)(OH_NNCompilation* compilation, bool enablefloat16);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_SetDevice)(OH_NNCompilation* compilation, size_t deviceID);
|
||||
OH_NN_ReturnCode (*OH_NNCompilation_Build)(OH_NNCompilation* compilation);
|
||||
void (*OH_NNCompilation_Destroy)(OH_NNCompilation** compilation);
|
||||
// Executor interface
|
||||
OH_NNExecutor* (*OH_NNExecutor_Construct)(OH_NNCompilation* compilation);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_SetInput)(OH_NNExecutor* executor, uint32_t inputIndex,
|
||||
const OH_NN_Tensor* nnTensor, const void* buffer, size_t length);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_SetOutput)(const OH_NNExecutor* executor, uint32_t outputIndex, void* buffer,
|
||||
size_t length);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_GetOutputShape)(const OH_NNExecutor* executor, uint32_t outputIndex,
|
||||
const uint32_t** dimensions, uint32_t* dimensionCount);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_Run)(OH_NNExecutor* executor);
|
||||
OH_NN_Memory* (*OH_NNExecutor_AllocateInputMemory)(OH_NNExecutor* executor, uint32_t inputIndex, size_t length);
|
||||
OH_NN_Memory* (*OH_NNExecutor_AllocateOutputMemory)(OH_NNExecutor* executor, uint32_t outputIndex, size_t length);
|
||||
void (*OH_NNExecutor_DestroyOutputMemory)(OH_NNExecutor* executor, uint32_t outputIndex, OH_NN_Memory** memory);
|
||||
void (*OH_NNExecutor_DestroyInputMemory)(OH_NNExecutor* executor, uint32_t inputIndex, OH_NN_Memory** memory);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_SetInputWithMemory)(OH_NNExecutor* executor, uint32_t inputIndex,
|
||||
const OH_NN_Tensor* nnTensor, const OH_NN_Memory* memory);
|
||||
OH_NN_ReturnCode (*OH_NNExecutor_SetOutputWithMemory)(OH_NNExecutor* executor, uint32_t outputIndex,
|
||||
const OH_NN_Memory* memory);
|
||||
void (*OH_NNExecutor_Destroy)(OH_NNExecutor** executor);
|
||||
// Device interface
|
||||
OH_NN_ReturnCode (*OH_NNDevice_GetAllDevicesID)(const size_t** allDevicesID, uint32_t* deviceCount);
|
||||
OH_NN_ReturnCode (*OH_NNDevice_GetName)(size_t deviceID, const char** name);
|
||||
OH_NN_ReturnCode (*OH_NNDevice_GetType)(size_t deviceID, OH_NN_DeviceType* deviceType);
|
||||
};
|
||||
|
||||
const NnrtApi* NnrtImplementation();
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_NNRT_IMPLEMENTATION_H
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tflite/tools/bitmap_helpers.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "tflite/tools/log.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
void DecodeBmp(const uint8_t* input, int32_t rowSize, ImageInfo imageInfo, bool topDown, std::vector<uint8_t>& output)
|
||||
{
|
||||
ColorChannelOffset colorChannelOffset = { BLUE_OFFSET, GREEN_OFFSET, ALPHA_OFFSET };
|
||||
for (int32_t i = 0; i < imageInfo.height; ++i) {
|
||||
int32_t srcPos;
|
||||
int32_t dstPos;
|
||||
|
||||
for (int32_t j = 0; j < imageInfo.width; j++) {
|
||||
if (!topDown) {
|
||||
srcPos = ((imageInfo.height - 1 - i) * rowSize) + j * imageInfo.channels;
|
||||
} else {
|
||||
srcPos = i * rowSize + j * imageInfo.channels;
|
||||
}
|
||||
|
||||
dstPos = (i * imageInfo.width + j) * imageInfo.channels;
|
||||
|
||||
switch (imageInfo.channels) {
|
||||
case GRAYSCALE_DIM:
|
||||
output[dstPos] = input[srcPos];
|
||||
break;
|
||||
case BGR_DIM:
|
||||
// BGR -> RGB
|
||||
output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
|
||||
output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
|
||||
output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
|
||||
break;
|
||||
case BGRA_DIM:
|
||||
// BGRA -> RGBA
|
||||
output[dstPos] = input[srcPos + colorChannelOffset.blueOffset];
|
||||
output[dstPos + colorChannelOffset.greenOffset] = input[srcPos + colorChannelOffset.greenOffset];
|
||||
output[dstPos + colorChannelOffset.blueOffset] = input[srcPos];
|
||||
output[dstPos + colorChannelOffset.alphaOffset] = input[srcPos + colorChannelOffset.alphaOffset];
|
||||
break;
|
||||
default:
|
||||
LOG(FATAL) << "Unexpected number of channels: " << imageInfo.channels;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void ReadBmp(const std::string& inputBmpName, ImageInfo& imageInfo, Settings* s, std::vector<uint8_t>& inputImage)
|
||||
{
|
||||
int32_t begin, end;
|
||||
std::ifstream file(inputBmpName, std::ios::in | std::ios::binary);
|
||||
if (!file) {
|
||||
LOG(FATAL) << "input file " << inputBmpName << " not found";
|
||||
return;
|
||||
}
|
||||
|
||||
begin = file.tellg();
|
||||
file.seekg(0, std::ios::end);
|
||||
end = file.tellg();
|
||||
size_t len = end - begin;
|
||||
if (s->verbose) {
|
||||
LOG(INFO) << "len: " << len;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> img_bytes(len);
|
||||
BmpAddressOffset bmpAddressOffset = { HEADER_ADDRESS_OFFSET, WIDTH_ADDRESS_OFFSET,
|
||||
HEIGHT_ADDRESS_OFFSET, BBP_ADDRESS_OFFSET };
|
||||
file.seekg(0, std::ios::beg);
|
||||
file.read(reinterpret_cast<char*>(img_bytes.data()), len);
|
||||
const int32_t headerSize =
|
||||
*(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.headerAddressOffset));
|
||||
imageInfo.width = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.widthAddressOffset));
|
||||
imageInfo.height =
|
||||
abs(*(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.heightAddressOffset)));
|
||||
const int32_t bpp = *(reinterpret_cast<const int32_t*>(img_bytes.data() + bmpAddressOffset.bbpAddressOffset));
|
||||
imageInfo.channels = bpp / BIT_TO_BYTE;
|
||||
inputImage.resize(imageInfo.height * imageInfo.width * imageInfo.channels);
|
||||
|
||||
if (s->verbose) {
|
||||
LOG(INFO) << "width, height, channels: " << imageInfo.width << ", " << imageInfo.height << ", "
|
||||
<< imageInfo.channels;
|
||||
}
|
||||
|
||||
// there may be padding bytes when the width is not a multiple of 4 bytes.
|
||||
// 8 * channels == bits per pixel
|
||||
const int32_t rowSize = ((8 * imageInfo.channels * imageInfo.width + 31) >> 5) << 2;
|
||||
|
||||
// if height is negative, data layout is top down. otherwise, it's bottom up.
|
||||
bool topDown = (imageInfo.height < 0);
|
||||
|
||||
// Decode image, allocating tensor once the image size is known.
|
||||
const uint8_t* bmpPixels = &img_bytes[headerSize];
|
||||
DecodeBmp(bmpPixels, rowSize, imageInfo, topDown, inputImage);
|
||||
return;
|
||||
}
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
|
||||
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
|
||||
|
||||
#include "tflite/label_classify/label_classify.h"
|
||||
#include "tensorflow/lite/builtin_op_data.h"
|
||||
#include "tensorflow/lite/interpreter.h"
|
||||
#include "tensorflow/lite/kernels/register.h"
|
||||
#include "tensorflow/lite/string_util.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
const int INPUT_NUMBER = 2;
|
||||
const int OUPUT_NUMBER = 1;
|
||||
const int INT8_OFFSET_NUMBER = 128;
|
||||
const int BIT_TO_BYTE = 8;
|
||||
const int BLUE_OFFSET = 2;
|
||||
const int GREEN_OFFSET = 1;
|
||||
const int ALPHA_OFFSET = 3;
|
||||
const int HEADER_ADDRESS_OFFSET = 10;
|
||||
const int WIDTH_ADDRESS_OFFSET = 18;
|
||||
const int HEIGHT_ADDRESS_OFFSET = 22;
|
||||
const int BBP_ADDRESS_OFFSET = 28;
|
||||
enum ChannelDim : int {
|
||||
GRAYSCALE_DIM = 1,
|
||||
BGR_DIM = 3,
|
||||
BGRA_DIM = 4
|
||||
};
|
||||
|
||||
struct BmpAddressOffset {
|
||||
int headerAddressOffset = 0;
|
||||
int widthAddressOffset = 0;
|
||||
int heightAddressOffset = 0;
|
||||
int bbpAddressOffset = 0;
|
||||
};
|
||||
|
||||
struct ColorChannelOffset {
|
||||
int blueOffset = 0;
|
||||
int greenOffset = 0;
|
||||
int alphaOffset = 0;
|
||||
};
|
||||
|
||||
struct ImageInfo {
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
int32_t channels = 0;
|
||||
};
|
||||
|
||||
void ReadBmp(const std::string& input_bmp_name, ImageInfo& imageInfo, Settings* s, std::vector<uint8_t>& input);
|
||||
|
||||
template <typename T>
|
||||
void Resize(T* out, uint8_t* in, ImageInfo inputImageInfo, ImageInfo wantedImageInfo, Settings* s)
|
||||
{
|
||||
std::unique_ptr<Interpreter> interpreter = std::make_unique<Interpreter>();
|
||||
|
||||
int32_t baseIndex = 0;
|
||||
int32_t outputIndex = 2;
|
||||
|
||||
// two inputs: input and new_sizes
|
||||
interpreter->AddTensors(INPUT_NUMBER, &baseIndex);
|
||||
// one output
|
||||
interpreter->AddTensors(OUPUT_NUMBER, &baseIndex);
|
||||
// set input and output tensors
|
||||
interpreter->SetInputs({ 0, 1 });
|
||||
interpreter->SetOutputs({ 2 });
|
||||
|
||||
// set parameters of tensors
|
||||
TfLiteQuantizationParams quant;
|
||||
interpreter->SetTensorParametersReadWrite(0, kTfLiteFloat32, "input",
|
||||
{ 1, inputImageInfo.height, inputImageInfo.width, inputImageInfo.channels }, quant);
|
||||
interpreter->SetTensorParametersReadWrite(1, kTfLiteInt32, "new_size", { 2 }, quant);
|
||||
interpreter->SetTensorParametersReadWrite(outputIndex, kTfLiteFloat32, "output",
|
||||
{ 1, wantedImageInfo.height, wantedImageInfo.width, wantedImageInfo.channels }, quant);
|
||||
|
||||
ops::builtin::BuiltinOpResolver resolver;
|
||||
const TfLiteRegistration* resizeOp = resolver.FindOp(BuiltinOperator_RESIZE_BILINEAR, 1);
|
||||
auto* params = reinterpret_cast<TfLiteResizeBilinearParams*>(malloc(sizeof(TfLiteResizeBilinearParams)));
|
||||
if (params == nullptr) {
|
||||
LOG(ERROR) << "Malloc memory failed in BitmapHelperslmpl.";
|
||||
return;
|
||||
}
|
||||
params->align_corners = false;
|
||||
params->half_pixel_centers = false;
|
||||
interpreter->AddNodeWithParameters({ 0, 1 }, { 2 }, nullptr, 0, params, resizeOp, nullptr);
|
||||
interpreter->AllocateTensors();
|
||||
|
||||
// fill input image
|
||||
// in[] are integers, cannot do memcpy() directly
|
||||
auto input = interpreter->typed_tensor<float>(0);
|
||||
|
||||
for (int32_t i = 0; i < inputImageInfo.height * inputImageInfo.width * inputImageInfo.channels; ++i) {
|
||||
input[i] = in[i];
|
||||
}
|
||||
|
||||
// fill new_sizes
|
||||
interpreter->typed_tensor<int32_t>(1)[0] = wantedImageInfo.height;
|
||||
interpreter->typed_tensor<int32_t>(1)[1] = wantedImageInfo.width;
|
||||
interpreter->Invoke();
|
||||
auto output = interpreter->typed_tensor<float>(2);
|
||||
for (int32_t i = 0; i < wantedImageInfo.height * wantedImageInfo.width * wantedImageInfo.channels; ++i) {
|
||||
switch (s->inputType) {
|
||||
case kTfLiteFloat32:
|
||||
out[i] = (output[i] - s->inputMean) / s->inputStd;
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
out[i] = static_cast<int8_t>(output[i] - INT8_OFFSET_NUMBER);
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
out[i] = static_cast<uint8_t>(output[i]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiation
|
||||
template void Resize<float>(float*, uint8_t*, ImageInfo, ImageInfo, Settings*);
|
||||
template void Resize<int8_t>(int8_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
|
||||
template void Resize<uint8_t>(uint8_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
|
||||
template void Resize<int64_t>(int64_t*, uint8_t*, ImageInfo, ImageInfo, Settings*);
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_BITMAP_HELPERS_H
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
|
||||
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
template <class T>
|
||||
void GetTopN(T* prediction, int32_t predictionSize, size_t numResults, float threshold,
|
||||
std::vector<std::pair<float, int32_t>>* topResults, TfLiteType inputType)
|
||||
{
|
||||
// Will contain top N results in ascending order.
|
||||
std::priority_queue<std::pair<float, int32_t>, std::vector<std::pair<float, int32_t>>,
|
||||
std::greater<std::pair<float, int32_t>>>
|
||||
topResultPQ;
|
||||
|
||||
const long count = predictionSize; // NOLINT(runtime/int32_t)
|
||||
float value = 0.0;
|
||||
float intNormalizedFactor = 256.0;
|
||||
float uintNormalizedFactor = 255.0;
|
||||
uint32_t offsetNumber = 128;
|
||||
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
switch (inputType) {
|
||||
case kTfLiteFloat32:
|
||||
value = prediction[i];
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
value = (prediction[i] + offsetNumber) / intNormalizedFactor;
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
value = prediction[i] / uintNormalizedFactor;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Only add it if it beats the threshold and has a chance at being in the top N.
|
||||
if (value < threshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
topResultPQ.push(std::pair<float, int32_t>(value, i));
|
||||
|
||||
// If at capacity, kick the smallest value out.
|
||||
if (topResultPQ.size() > numResults) {
|
||||
topResultPQ.pop();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy to output vector and reverse into descending order.
|
||||
while (!topResultPQ.empty()) {
|
||||
topResults->push_back(topResultPQ.top());
|
||||
topResultPQ.pop();
|
||||
}
|
||||
|
||||
std::reverse(topResults->begin(), topResults->end());
|
||||
}
|
||||
|
||||
// explicit instantiation so that we can use them otherwhere
|
||||
template void GetTopN<float>(float*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
|
||||
template void GetTopN<int8_t>(int8_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
|
||||
template void GetTopN<uint8_t>(uint8_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
|
||||
template void GetTopN<int64_t>(int64_t*, int32_t, size_t, float, std::vector<std::pair<float, int32_t>>*, TfLiteType);
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_GET_TOP_N_H
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
|
||||
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
class Log {
|
||||
std::stringstream stream_;
|
||||
|
||||
public:
|
||||
explicit Log(const char* severity)
|
||||
{
|
||||
stream_ << severity << ": ";
|
||||
}
|
||||
std::stringstream& Stream()
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
~Log()
|
||||
{
|
||||
std::cerr << stream_.str() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
#define LOG(severity) tflite::label_classify::Log(#severity).Stream()
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_LOG_H
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sys/time.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "tflite/tools/bitmap_helpers.h"
|
||||
#include "tflite/tools/get_topn.h"
|
||||
#include "tflite/tools/log.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
constexpr int32_t DATA_PRINT_NUM = 1000;
|
||||
constexpr int32_t DATA_EACHLINE_NUM = 1000;
|
||||
constexpr int32_t SECOND_TO_MICROSECOND_RATIO = 1000000;
|
||||
constexpr uint8_t WEIGHT_DIMENSION = 2;
|
||||
constexpr uint8_t CHANNEL_DIMENSION = 3;
|
||||
|
||||
double GetUs(struct timeval t)
|
||||
{
|
||||
return (t.tv_sec * SECOND_TO_MICROSECOND_RATIO + t.tv_usec);
|
||||
}
|
||||
|
||||
TfLiteStatus ReadLabelsFile(const string& fileName, std::vector<string>& result, size_t& foundLabelCount)
|
||||
{
|
||||
std::ifstream file(fileName);
|
||||
if (!file) {
|
||||
LOG(ERROR) << "Labels file " << fileName << " not found";
|
||||
return kTfLiteError;
|
||||
}
|
||||
result.clear();
|
||||
string line;
|
||||
while (std::getline(file, line)) {
|
||||
result.push_back(line);
|
||||
}
|
||||
foundLabelCount = result.size();
|
||||
const int32_t padding = 16;
|
||||
while (result.size() % padding) {
|
||||
result.emplace_back();
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void GetInputNameAndShape(string& inputShapeString, std::map<string, std::vector<int>>& userInputShapes)
|
||||
{
|
||||
if (inputShapeString == "") {
|
||||
return;
|
||||
}
|
||||
int pos = inputShapeString.find_last_of(":");
|
||||
string userInputName = inputShapeString.substr(0, pos);
|
||||
|
||||
string dimString = inputShapeString.substr(pos + 1);
|
||||
int dimPos = dimString.find(",");
|
||||
std::vector<int> inputDims;
|
||||
while (dimPos != dimString.npos) {
|
||||
inputDims.push_back(std::stoi(dimString.substr(0, dimPos)));
|
||||
dimString = dimString.substr(dimPos + 1);
|
||||
dimPos = dimString.find(",");
|
||||
}
|
||||
inputDims.push_back(std::stoi(dimString));
|
||||
userInputShapes.insert(std::map<string, std::vector<int>>::value_type(userInputName, inputDims));
|
||||
}
|
||||
|
||||
TfLiteStatus FilterDynamicInputs(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter,
|
||||
std::map<int, std::vector<int>>& neededInputShapes)
|
||||
{
|
||||
std::vector<int> inputIndexes = interpreter->inputs();
|
||||
std::map<string, int> nameIndexs;
|
||||
for (int i = 0; i < inputIndexes.size(); i++) {
|
||||
LOG(INFO) << "input index: " << inputIndexes[i];
|
||||
nameIndexs.insert(std::map<string, int>::value_type(interpreter->GetInputName(i), inputIndexes[i]));
|
||||
}
|
||||
|
||||
if (settings.inputShape.find(":") == settings.inputShape.npos) {
|
||||
LOG(ERROR) << "The format of input shapes string is not supported.";
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Get input names and shapes
|
||||
std::map<string, std::vector<int>> userInputShapes;
|
||||
string inputShapeString = settings.inputShape;
|
||||
int pos = inputShapeString.find(";");
|
||||
while (pos != inputShapeString.npos) {
|
||||
GetInputNameAndShape(inputShapeString, userInputShapes);
|
||||
inputShapeString = inputShapeString.substr(pos + 1);
|
||||
pos = inputShapeString.find(";");
|
||||
}
|
||||
GetInputNameAndShape(inputShapeString, userInputShapes);
|
||||
|
||||
for (const auto& inputShape : userInputShapes) {
|
||||
string inputName = inputShape.first;
|
||||
auto findName = nameIndexs.find(inputName);
|
||||
if (findName == nameIndexs.end()) {
|
||||
LOG(ERROR) << "The input name is error: " << inputShape.first << ".";
|
||||
return kTfLiteError;
|
||||
} else {
|
||||
neededInputShapes.insert(std::map<int, std::vector<int>>::value_type(findName->second, inputShape.second));
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <class T> void PrintData(T* data, int32_t dataSize, int32_t printSize)
|
||||
{
|
||||
if (printSize > dataSize) {
|
||||
printSize = dataSize;
|
||||
}
|
||||
for (int32_t i = 0; i < printSize; ++i) {
|
||||
std::cout << static_cast<float>(*(data + i)) << "\t";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void PrintResult(std::unique_ptr<tflite::Interpreter>& interpreter)
|
||||
{
|
||||
for (int32_t index = 0; index < interpreter->outputs().size(); ++index) {
|
||||
int32_t output_index = interpreter->outputs()[index];
|
||||
TfLiteIntArray* outputsDims = interpreter->tensor(output_index)->dims;
|
||||
int32_t dimSize = outputsDims->size;
|
||||
int32_t outputTensorSize = 1;
|
||||
for (int32_t i = 0; i < dimSize; ++i) {
|
||||
outputTensorSize *= outputsDims->data[i];
|
||||
}
|
||||
|
||||
TfLiteTensor* outputTensor = interpreter->tensor(output_index);
|
||||
switch (outputTensor->type) {
|
||||
case kTfLiteFloat32:
|
||||
PrintData<float>(interpreter->typed_output_tensor<float>(index), outputTensorSize, DATA_PRINT_NUM);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
PrintData<int32_t>(interpreter->typed_output_tensor<int32_t>(index), outputTensorSize, DATA_PRINT_NUM);
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
PrintData<uint8_t>(interpreter->typed_output_tensor<uint8_t>(index), outputTensorSize, DATA_PRINT_NUM);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
PrintData<int8_t>(interpreter->typed_output_tensor<int8_t>(index), outputTensorSize, DATA_PRINT_NUM);
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Unsupportted tensor datatype: " << outputTensor->type << "!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnalysisResults(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter)
|
||||
{
|
||||
const float threshold = 0.001f;
|
||||
std::vector<std::pair<float, int32_t>> topResults;
|
||||
|
||||
if (settings.printResult) {
|
||||
LOG(INFO) << "Outputs Data:";
|
||||
PrintResult(interpreter);
|
||||
}
|
||||
|
||||
int32_t output = interpreter->outputs()[0];
|
||||
TfLiteIntArray* outputDims = interpreter->tensor(output)->dims;
|
||||
// assume output dims to be something like (1, 1, ... ,size)
|
||||
auto outputSize = outputDims->data[outputDims->size - 1];
|
||||
|
||||
auto tfType = interpreter->tensor(output)->type;
|
||||
switch (tfType) {
|
||||
case kTfLiteFloat32:
|
||||
GetTopN<float>(interpreter->typed_output_tensor<float>(0), outputSize, settings.numberOfResults, threshold,
|
||||
&topResults, settings.inputType);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
GetTopN<int8_t>(interpreter->typed_output_tensor<int8_t>(0), outputSize, settings.numberOfResults,
|
||||
threshold, &topResults, settings.inputType);
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
GetTopN<uint8_t>(interpreter->typed_output_tensor<uint8_t>(0), outputSize, settings.numberOfResults,
|
||||
threshold, &topResults, settings.inputType);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
GetTopN<int64_t>(interpreter->typed_output_tensor<int64_t>(0), outputSize, settings.numberOfResults,
|
||||
threshold, &topResults, settings.inputType);
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "cannot handle output type " << tfType << " yet";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<string> labels;
|
||||
size_t labelCount;
|
||||
|
||||
if (ReadLabelsFile(settings.labelsFileName, labels, labelCount) != kTfLiteOk) {
|
||||
return;
|
||||
}
|
||||
for (const auto& result : topResults) {
|
||||
const float confidence = result.first;
|
||||
const int32_t index = result.second;
|
||||
LOG(INFO) << confidence << ": " << index << " " << labels[index];
|
||||
}
|
||||
}
|
||||
|
||||
void ImportData(Settings& settings, std::vector<int>& imageSize, std::unique_ptr<tflite::Interpreter>& interpreter)
|
||||
{
|
||||
ImageInfo inputImageInfo = {imageSize[0], imageSize[1], imageSize[2]};
|
||||
std::vector<uint8_t> in;
|
||||
ReadBmp(settings.inputBmpName, inputImageInfo, &settings, in);
|
||||
|
||||
int32_t input = interpreter->inputs()[0];
|
||||
if (settings.verbose) {
|
||||
LOG(INFO) << "input: " << input;
|
||||
}
|
||||
|
||||
// get input dimension from the model.
|
||||
TfLiteIntArray* dims = interpreter->tensor(input)->dims;
|
||||
ImageInfo wantedimageInfo;
|
||||
wantedimageInfo.height = dims->data[1];
|
||||
wantedimageInfo.width = dims->data[WEIGHT_DIMENSION];
|
||||
wantedimageInfo.channels = (dims->size > CHANNEL_DIMENSION) ? dims->data[CHANNEL_DIMENSION] : 1;
|
||||
|
||||
settings.inputType = interpreter->tensor(input)->type;
|
||||
switch (settings.inputType) {
|
||||
case kTfLiteFloat32:
|
||||
Resize<float>(interpreter->typed_tensor<float>(input), in.data(), inputImageInfo, wantedimageInfo,
|
||||
&settings);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
Resize<int8_t>(interpreter->typed_tensor<int8_t>(input), in.data(), inputImageInfo, wantedimageInfo,
|
||||
&settings);
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
Resize<uint8_t>(interpreter->typed_tensor<uint8_t>(input), in.data(), inputImageInfo, wantedimageInfo,
|
||||
&settings);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
Resize<int64_t>(interpreter->typed_tensor<int64_t>(input), in.data(), inputImageInfo, wantedimageInfo,
|
||||
&settings);
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "cannot handle input type " << settings.inputType << " yet";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEqualShape(int tensorIndex, const std::vector<int>& dims, std::unique_ptr<tflite::Interpreter>& interpreter)
|
||||
{
|
||||
TfLiteTensor* tensor = interpreter->tensor(tensorIndex);
|
||||
for (int i = 0; i < tensor->dims->size; ++i) {
|
||||
if (tensor->dims->data[i] != dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
|
||||
#define TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
|
||||
|
||||
#include "../label_classify/label_classify.h"
|
||||
|
||||
#include "sys/time.h"
|
||||
|
||||
#include "tensorflow/lite/model_builder.h"
|
||||
#include "tensorflow/lite/string_type.h"
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#include "tensorflow/lite/interpreter.h"
|
||||
|
||||
#include "neural_network_runtime.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace label_classify {
|
||||
double GetUs(struct timeval t);
|
||||
TfLiteStatus ReadLabelsFile(const string& fileName, std::vector<string>& result, size_t& foundLabelCount);
|
||||
TfLiteStatus FilterDynamicInputs(Settings& settings,
|
||||
std::unique_ptr<tflite::Interpreter>& interpreter, std::map<int, std::vector<int>>& neededInputShapes);
|
||||
bool IsEqualShape(int tensorIndex, const std::vector<int>& dim, std::unique_ptr<tflite::Interpreter>& interpreter);
|
||||
void GetInputNameAndShape(string &inputShapeString, std::map<string, std::vector<int>>& userInputShapes);
|
||||
void PrintResult(std::unique_ptr<tflite::Interpreter>& interpreter);
|
||||
void AnalysisResults(Settings& settings, std::unique_ptr<tflite::Interpreter>& interpreter);
|
||||
void ImportData(Settings& settings, std::vector<int>& imageSize, std::unique_ptr<tflite::Interpreter>& interpreter);
|
||||
} // namespace label_classify
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_EXAMPLES_LABEL_CLASSIFY_UTILS_H
|
||||
@@ -0,0 +1,329 @@
|
||||
# NNRt开发指导
|
||||
|
||||
## NNRt开发概述
|
||||
|
||||
### 功能简介
|
||||
|
||||
神经网络运行时部件(NNRt)是跨设备的AI运行时框架,作为端侧推理框架和专用加速芯片的中间桥梁,为端侧推理框架提供了统一的Native接口,使能端侧推理框架在专有加速芯片上推理;为芯片厂商提供了统一的HDI接口,使能专有加速芯片接入OpenHarmony社区生态。
|
||||
|
||||
本文介绍芯片厂商如何在将专有加速芯片接入NNRt,接入OpenHarmony社区生态。
|
||||
|
||||
### 基本概念
|
||||
在开发前,开发者需要先了解以下概念,以便更好地理解全文内容:
|
||||
|
||||
- NNRt:Neural Network Runtime,神经网络运行时,是本指导主要介绍的部件。
|
||||
- OHOS:OpenHarmony Operating System,开源鸿蒙操作系统。
|
||||
- HDI:Hardware Device Interface,硬件设备接口,是OHOS中系统组件与芯片组件通信的接口。
|
||||
- IDL: Interface Description Language,接口描述语言,是HDI接口的语言格式。
|
||||
|
||||
### 约束与限制
|
||||
- 系统版本:OpenHarmony 3.2及以上。
|
||||
- 开发环境:Ubuntu 18.04及以上。
|
||||
- 接入设备:OpenHarmony定义的标准设备。
|
||||
|
||||
### 运作机制
|
||||
NNRt通过HDI接口实现与设备芯片的对接,由HDI接口实现跨进程通信。
|
||||
|
||||
**图1** NNRt架构图
|
||||
|
||||

|
||||
|
||||
整个架构主要分为三层,AI应用在应用层,AI推理框架和神经网络运行时在系统层,设备服务在芯片层。AI应用要在专用加速芯片上完成模型推理,需要经过AI推理框架和神经网络运行时才能调用到底层的芯片设备,而神经网络运行时就是负责适配底层各种芯片设备,它开放了标准统一的南向接口,众多的第三方芯片设备都可以通过HDI接口接入OHOS。
|
||||
|
||||
程序运行时,AI应用、AI推理框架、神经网络运行时都在同一个进程,底层设备服务在另一个进程,进程间是通过IPC的机制通信,神经网络运行时根据南向HDI接口实现了HDI Client,服务端也需要根据南向HDI接口实现HDI Service。
|
||||
|
||||
## NNRt开发指导
|
||||
|
||||
### 场景介绍
|
||||
下文以rk3568芯片为例,展示rk3568 CPU如何通过HDI接口接入NNRt,并完成AI模型推理。
|
||||
|
||||
### 开发流程
|
||||
适配操作的整体流程如下:
|
||||
|
||||
**图2** NNRt适配流程
|
||||
|
||||

|
||||
|
||||
### 开发步骤
|
||||
开发者具体可通过以下步骤在芯片侧对接NNRt:
|
||||
1. 开源社区下载OpenHarmony的代码,编译drivers_interface部件,生成HDI接口的头文件。
|
||||
- [下载源码](../get-code/sourcecode-acquire.md)。
|
||||
- 编译接口IDL文件。
|
||||
```shell
|
||||
./build.sh --product-name rk3568 –ccache --target-cpu arm64 --build-target=drivers_interface_nnrt
|
||||
```
|
||||
|
||||
--target-cpu arm64:是64位编译选项,若编译32位,则不需添加--target-cpu arm64
|
||||
|
||||
编译之后,可以在```out/rk3568/gen/drivers/interface/nnrt```目录下找到生成的头文件,默认生成C++头文件,若需要生成C头文件,则修改```drivers/interface/nnrt/v1_0/BUILD.gn```文件中的language。
|
||||
```shell
|
||||
language = "c"
|
||||
```
|
||||
|
||||
生成头文件目录如下所示:
|
||||
```text
|
||||
out/rk3568/gen/drivers/interface/nnrt
|
||||
└── v1_0
|
||||
├── drivers_interface_nnrt__libnnrt_proxy_1.0_external_deps_temp.json
|
||||
├── drivers_interface_nnrt__libnnrt_stub_1.0_external_deps_temp.json
|
||||
├── innrt_device.h # 设备接口头文件
|
||||
├── iprepared_model.h # 编译AI模型对象头文件
|
||||
├── libnnrt_proxy_1.0__notice.d
|
||||
├── libnnrt_stub_1.0__notice.d
|
||||
├── model_types.cpp # AI模型结构定义实现文件
|
||||
├── model_types.h # AI模型结构定义头文件
|
||||
├── nnrt_device_driver.cpp # 设备驱动实现参考样例
|
||||
├── nnrt_device_proxy.cpp
|
||||
├── nnrt_device_proxy.h
|
||||
├── nnrt_device_service.cpp # 设备服务端实现参考样例
|
||||
├── nnrt_device_service.h # 设备服务端头文件
|
||||
├── nnrt_device_stub.cpp
|
||||
├── nnrt_device_stub.h
|
||||
├── nnrt_types.cpp # 数据类型定义实现文件
|
||||
├── nnrt_types.h # 数据类型定义头文件
|
||||
├── node_attr_types.cpp # AI模型算子属性定义实现文件
|
||||
├── node_attr_types.h # AI模型算子属性定义
|
||||
├── prepared_model_proxy.cpp
|
||||
├── prepared_model_proxy.h
|
||||
├── prepared_model_service.cpp # 编译AI模型对象服务端实现参考样例
|
||||
├── prepared_model_service.h # 编译AI模型对象服务端头文件
|
||||
├── prepared_model_stub.cpp
|
||||
└── prepared_model_stub.h
|
||||
```
|
||||
|
||||
2. 实现HDI服务
|
||||
- 在drivers/peripheral目录下新建开发目录,用于HDI服务开发,开发目录结构如下所示。
|
||||
```text
|
||||
drivers/peripheral/nnrt
|
||||
├── BUILD.gn # 代码编译脚本文件
|
||||
├── bundle.json
|
||||
└── hdi_cpu_service # 自定义目录
|
||||
├── BUILD.gn # 代码编译脚本文件
|
||||
├── include
|
||||
│ ├── nnrt_device_service.h # 设备服务端头文件
|
||||
│ ├── node_functions.h # 非必须,由具体实现决定
|
||||
│ ├── node_registry.h # 非必须,由具体实现决定
|
||||
│ └── prepared_model_service.h # 编译AI模型对象服务端头文件
|
||||
└── src
|
||||
├── nnrt_device_driver.cpp # 设备驱动实现文件
|
||||
├── nnrt_device_service.cpp # 设备服务端实现文件
|
||||
├── nnrt_device_stub.cpp # 非必须,由具体实现决定
|
||||
├── node_attr_types.cpp # 非必须,由具体实现决定
|
||||
├── node_functions.cpp # 非必须,由具体实现决定
|
||||
├── node_registry.cpp # 非必须,由具体实现决定
|
||||
└── prepared_model_service.cpp # 编译AI模型对象服务端实现文件
|
||||
```
|
||||
|
||||
- 实现设备驱动,无特殊需求可直接使用步骤1中生成的nnrt_device_driver.cpp文件,否则根据具体驱动开发。
|
||||
- 实现服务接口,主要实现nnrt_device_service.cpp和prepared_model_service.cpp文件,接口定义可以参考```drivers/interface/nnrt```。
|
||||
|
||||
- 编译驱动和服务实现为共享库。
|
||||
在```drivers/peripheral/nnrt/hdi_cpu_service/```下新建```BUILD.gn```文件,对驱动入口和服务实现编译为共享库。
|
||||
|
||||
```shell
|
||||
import("//build/ohos.gni")
|
||||
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
|
||||
|
||||
ohos_shared_library("libnnrt_service_1.0") {
|
||||
include_dirs = []
|
||||
sources = [
|
||||
"src/nnrt_device_service.cpp",
|
||||
"src/prepared_model_service.cpp",
|
||||
"src/node_registry.cpp",
|
||||
"src/node_functions.cpp",
|
||||
"src/node_attr_types.cpp"
|
||||
]
|
||||
public_deps = [ "//drivers/interface/nnrt/v1_0:nnrt_idl_headers" ]
|
||||
external_deps = [
|
||||
"hdf_core:libhdf_utils",
|
||||
"hiviewdfx_hilog_native:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"c_utils:utils",
|
||||
]
|
||||
|
||||
install_images = [ chipset_base_dir ]
|
||||
subsystem_name = "hdf"
|
||||
part_name = "drivers_peripheral_nnrt"
|
||||
}
|
||||
|
||||
ohos_shared_library("libnnrt_driver") {
|
||||
include_dirs = []
|
||||
sources = [ "src/nnr_device_driver.cpp" ]
|
||||
deps = [ "//drivers/peripheral/nnrt/hdi_cpu_service:libnnrt_service_1.0" ]
|
||||
|
||||
external_deps = [
|
||||
"hdf_core:libhdf_host",
|
||||
"hdf_core:libhdf_ipc_adapter",
|
||||
"hdf_core:libhdf_utils",
|
||||
"hiviewdfx_hilog_native:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"c_utils:utils",
|
||||
]
|
||||
|
||||
install_images = [ chipset_base_dir ]
|
||||
subsystem_name = "hdf"
|
||||
part_name = "drivers_peripheral_nnrt"
|
||||
}
|
||||
|
||||
group("hdf_nnrt_service") {
|
||||
deps = [
|
||||
":libnnrt_driver",
|
||||
":libnnrt_service_1.0",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
将```group("hdf_nnrt_service")```添加到```drivers/peripheral/nnrt/BUILD.gn```文件中
|
||||
```shell
|
||||
if (defined(ohos_lite)) {
|
||||
group("nnrt_entry") {
|
||||
deps = [ ]
|
||||
}
|
||||
} else {
|
||||
group("nnrt_entry") {
|
||||
deps = [
|
||||
"./hdi_cpu_service:hdf_nnrt_service",
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新建```drivers/peripheral/nnrt/bundle.json```用于定义新增的```drivers_peripheral_nnrt```部件。
|
||||
```json
|
||||
{
|
||||
"name": "drivers_peripheral_nnrt",
|
||||
"description": "Neural network runtime device driver",
|
||||
"version": "3.2",
|
||||
"license": "Apache License 2.0",
|
||||
"component": {
|
||||
"name": "drivers_peripheral_nnrt",
|
||||
"subsystem": "hdf",
|
||||
"syscap": [""],
|
||||
"adapter_system_type": ["standard"],
|
||||
"rom": "1024KB",
|
||||
"ram": "2048KB",
|
||||
"deps": {
|
||||
"components": [
|
||||
"ipc",
|
||||
"hdf_core",
|
||||
"hiviewdfx_hilog_native",
|
||||
"c_utils"
|
||||
],
|
||||
"third_part": [
|
||||
"bounds_checking_function"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"sub_component": [
|
||||
"//drivers/peripheral/nnrt:nnrt_entry"
|
||||
],
|
||||
"test": [
|
||||
],
|
||||
"inner_kits": [
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 声明HDI服务
|
||||
在对应产品的uhdf hcs配置文件中声明用户态驱动与服务,本例中rk3568对应在```vendor/hihope/rk3568/hdf_config/uhdf/device_info.hcs```文件中新增如下配置:
|
||||
```text
|
||||
nnrt :: host {
|
||||
hostName = "nnrt_host";
|
||||
priority = 50;
|
||||
uid = "";
|
||||
gid = "";
|
||||
caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];
|
||||
nnrt_device :: device {
|
||||
device0 :: deviceNode {
|
||||
policy = 2;
|
||||
priority = 100;
|
||||
moduleName = "libnnrt_driver.z.so";
|
||||
serviceName = "nnrt_device_service";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
注意:修改hcs文件后请删除out目录重新编译,才能生效。
|
||||
|
||||
4. 配置host进程用户和组
|
||||
对于新增host进程的场景,需要新增配置对应进程的用户ID和组ID。 进程的用户ID在文件```base/startup/init/services/etc/passwd```中配置,进程的组ID在文件```base/startup/init/services/etc/group```中配置。
|
||||
```text
|
||||
# 在base/startup/init/services/etc/passwd新增
|
||||
nnrt_host:x:3311:3311:::/bin/false
|
||||
|
||||
# 在base/startup/init/services/etc/group新增
|
||||
nnrt_host:x:3311:
|
||||
```
|
||||
完成上述所有配置后,全量编译版本后应该可以观察到新增host进程启动,也可以通过hilog输出检索新增的服务名称nnrt_interface_service观察到服务发布成功
|
||||
|
||||
5. SELinux配置
|
||||
OHOS已经开启SELinux特性,需要对新增的进程和服务配置相应的SELinux规则,用于运行host进程启动访问某些资源、发布HDI服务。对于调用者来说,也需要配置SELinux规则运行获取和调用某个HDI服务。
|
||||
|
||||
在```base/security/selinux/sepolicy/ohos_policy/drivers/adapter/vendor/type.te```文件中配置nnrt_host进程安全上下文
|
||||
```text
|
||||
# 新增
|
||||
type nnrt_host, hdfdomain, domain;
|
||||
```
|
||||
|
||||
由于SeLinux是白名单访问的权限机制,需要根据实际权限需求配置,将服务启动起来之后,通过以下dmesg命令可能查看avc告警,
|
||||
avc告警会给出缺少的权限,SeLinux的配置也可以参考[OpenHarmony SeLinux子系统的说明](https://gitee.com/openharmony/security_selinux/blob/master/README.md)
|
||||
```shell
|
||||
hdc_std shell
|
||||
dmesg | grep nnrt
|
||||
```
|
||||
|
||||
新建nnrt_host.te配置文件,将权限配置到nnrt_host.te文件中
|
||||
```shell
|
||||
# 创建nnrt文件夹
|
||||
mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt
|
||||
|
||||
# 创建vendor文件夹
|
||||
mkdir base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor
|
||||
|
||||
# 创建nnrt_host.te文件
|
||||
touch base/security/selinux/sepolicy/ohos_policy/drivers/peripheral/nnrt/vendor/nnrt_host.te
|
||||
```
|
||||
|
||||
然后再将所需的权限写入nnrt_host.te文件中,比如:
|
||||
```text
|
||||
allow nnrt_host dev_hdf_kevent:chr_file { ioctl };
|
||||
allow nnrt_host hilog_param:file { read };
|
||||
allow nnrt_host sh:binder { transfer };
|
||||
allow nnrt_host dev_ashmem_file:chr_file { open };
|
||||
allow sh nnrt_host:fd { use };
|
||||
```
|
||||
|
||||
6. 删除out目录编译整个系统
|
||||
```shell
|
||||
./build.sh --product-name rk3568 –ccache --jobs=4
|
||||
```
|
||||
|
||||
|
||||
### 调测验证
|
||||
服务开发完成后,可以使用XTS用例验证基本功能和兼容性,开发者可通过以下步骤进行验证:
|
||||
1. 开源社区下载[OpenHarmony代码](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/get-code/sourcecode-acquire.md),相关用例在test/xts/hats/hdf/nnrt目录下。
|
||||
2. 编译XTS用例。
|
||||
```shell
|
||||
cd test/xts/hats
|
||||
./build.sh suite=hats system_size=standard --product-name rk3568
|
||||
```
|
||||
编译好的测试用例会输出到out/rk3568/suites/hats/testcases/HatsHdfNnrtFunctionTest
|
||||
|
||||
|
||||
3. 将测试用例push到设备上。
|
||||
```shell
|
||||
# 将测试用例可执行文件推送到设备上,HatsHdfNnrtFunctionTest是测试用例可执行文件。
|
||||
hdc_std file send out/rk3568/suites/hats/testcases/HatsHdfNnrtFunctionTest /data/local/tmp/
|
||||
|
||||
# 给测试用例可执行文件加上权限。
|
||||
hdc_std shell "chmod +x /data/local/tmp/HatsHdfNnrtFunctionTest"
|
||||
|
||||
# 执行测试用例
|
||||
hdc_std shell "/data/local/tmp/HatsHdfNnrtFunctionTest"
|
||||
```
|
||||
|
||||
### 开发实例
|
||||
完整[Demo实例](xxx, Demo暂时还在黄区代码仓,超链接需等Demo开源后补充)可以参考社区实现。
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,24 @@
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if (defined(ohos_lite)) {
|
||||
group("nnrt_entry") {
|
||||
deps = [ ]
|
||||
}
|
||||
} else {
|
||||
group("nnrt_entry") {
|
||||
deps = [
|
||||
"./hdi_cpu_service:hdf_nnrt_service",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import("//build/ohos.gni")
|
||||
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
|
||||
|
||||
ohos_prebuilt_shared_library("mindspore_demo") {
|
||||
source = "//drivers/peripheral/nnrt/mindspore/mindspore/libmindspore-lite.huawei.so"
|
||||
|
||||
install_images = [chipset_base_dir]
|
||||
subsystem_name = "hdf"
|
||||
part_name = "drivers_peripheral_nnrt"
|
||||
}
|
||||
|
||||
ohos_shared_library("libnnrt_device_service_1.0") {
|
||||
include_dirs = [
|
||||
"//drivers/peripheral/nnrt/hdi_cpu_service/include",
|
||||
"//drivers/peripheral/nnrt/mindspore",
|
||||
"//third_party/flatbuffers/include",
|
||||
"//commonlibrary/c_utils/base/include"
|
||||
]
|
||||
sources = [
|
||||
"src/nnrt_device_service.cpp",
|
||||
"src/prepared_model_service.cpp",
|
||||
"src/node_registry.cpp",
|
||||
"src/node_functions.cpp",
|
||||
"src/shared_buffer_parser.cpp",
|
||||
"src/validation.cpp"
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//drivers/interface/nnrt/v1_0:nnrt_idl_headers",
|
||||
"//drivers/interface/nnrt/v1_0:libnnrt_stub_1.0",
|
||||
":mindspore_demo"
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"hdf_core:libhdf_utils",
|
||||
"hiviewdfx_hilog_native:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"c_utils:utils"
|
||||
]
|
||||
|
||||
install_images = [ chipset_base_dir ]
|
||||
subsystem_name = "hdf"
|
||||
part_name = "drivers_peripheral_nnrt"
|
||||
}
|
||||
|
||||
ohos_shared_library("libnnrt_driver") {
|
||||
include_dirs = []
|
||||
sources = [
|
||||
"src/nnrt_device_driver.cpp"
|
||||
]
|
||||
deps = [
|
||||
"//drivers/peripheral/nnrt/hdi_cpu_service:libnnrt_device_service_1.0",
|
||||
"//drivers/interface/nnrt/v1_0:libnnrt_stub_1.0"
|
||||
]
|
||||
|
||||
external_deps = [
|
||||
"hdf_core:libhdf_host",
|
||||
"hdf_core:libhdf_ipc_adapter",
|
||||
"hdf_core:libhdf_utils",
|
||||
"hiviewdfx_hilog_native:libhilog",
|
||||
"ipc:ipc_single",
|
||||
"c_utils:utils",
|
||||
"hdf_core:libhdi"
|
||||
]
|
||||
|
||||
install_images = [ chipset_base_dir ]
|
||||
subsystem_name = "hdf"
|
||||
part_name = "drivers_peripheral_nnrt"
|
||||
}
|
||||
|
||||
group("hdf_nnrt_service") {
|
||||
deps = [
|
||||
":mindspore_demo",
|
||||
":libnnrt_driver",
|
||||
":libnnrt_device_service_1.0",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
|
||||
#define OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "v1_0/innrt_device.h"
|
||||
#include "ashmem.h"
|
||||
#include "include/api/model.h"
|
||||
|
||||
#include "mindspore_schema/model_generated.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
class NnrtDeviceService : public INnrtDevice {
|
||||
public:
|
||||
NnrtDeviceService() = default;
|
||||
virtual ~NnrtDeviceService();
|
||||
|
||||
int32_t GetDeviceName(std::string& name) override;
|
||||
|
||||
int32_t GetVendorName(std::string& name) override;
|
||||
|
||||
int32_t GetDeviceType(DeviceType& deviceType) override;
|
||||
|
||||
int32_t GetDeviceStatus(DeviceStatus& status) override;
|
||||
|
||||
int32_t GetSupportedOperation(const Model& model, std::vector<bool>& ops) override;
|
||||
|
||||
int32_t IsFloat16PrecisionSupported(bool& isSupported) override;
|
||||
|
||||
int32_t IsPerformanceModeSupported(bool& isSupported) override;
|
||||
|
||||
int32_t IsPrioritySupported(bool& isSupported) override;
|
||||
|
||||
int32_t IsDynamicInputSupported(bool& isSupported) override;
|
||||
|
||||
int32_t PrepareModel(const Model& model, const ModelConfig& config, sptr<IPreparedModel>& preparedModel) override;
|
||||
|
||||
int32_t IsModelCacheSupported(bool& isSupported) override;
|
||||
|
||||
int32_t PrepareModelFromModelCache(const std::vector<SharedBuffer>& modelCache, const ModelConfig& config,
|
||||
sptr<IPreparedModel>& preparedModel) override;
|
||||
|
||||
int32_t AllocateBuffer(uint32_t length, SharedBuffer& buffer) override;
|
||||
|
||||
int32_t ReleaseBuffer(const SharedBuffer& buffer) override;
|
||||
|
||||
private:
|
||||
int32_t ValidateModelConfig(const ModelConfig& config) const;
|
||||
int32_t ValidateModel(const Model& model) const;
|
||||
std::shared_ptr<mindspore::schema::MetaGraphT> TransModelToGraph(const Model& model) const;
|
||||
std::unique_ptr<mindspore::schema::TensorT> TransTensor(const Tensor& tensor) const;
|
||||
std::unique_ptr<mindspore::schema::CNodeT> TransNode(const Node& node) const;
|
||||
std::unique_ptr<mindspore::schema::SubGraphT> TransSubGraph(const SubGraph& graph, const size_t numTensor) const;
|
||||
std::shared_ptr<mindspore::Context> TransModelConfig(const ModelConfig& config) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<mindspore::Model> m_model {nullptr};
|
||||
std::unordered_map<int, sptr<Ashmem>> m_ashmems;
|
||||
};
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
|
||||
#endif // OHOS_HDI_NNRT_V1_0_NNRTDEVICESERVICE_H
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNR_NODE_FUNCTIONS_H
|
||||
#define OHOS_HDI_NNR_NODE_FUNCTIONS_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "hdf_base.h"
|
||||
#include "utils/hdf_log.h"
|
||||
|
||||
#include "node_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
template<typename T>
|
||||
int32_t ParsePrimitive(const std::vector<int8_t>& primitive, T& attr,
|
||||
std::function<bool(OHOS::MessageParcel&, T&)> parseFunc)
|
||||
{
|
||||
if (primitive.empty()) {
|
||||
HDF_LOGE("Primitive data is empty.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
OHOS::MessageParcel parcelData;
|
||||
bool ret = parcelData.WriteBuffer(primitive.data(), primitive.size());
|
||||
if (!ret) {
|
||||
HDF_LOGE("Write data to MessageParcel failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
ret = parseFunc(parcelData, attr);
|
||||
if (!ret) {
|
||||
HDF_LOGE("Unmarshalling data failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetAddPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetAvgPoolPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetConcatPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetConv2dPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetFullConnectionPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetMaxPoolFusionPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetMatMulFusionPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetSoftmaxPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetReshapePrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetScaleFusionPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetActivationPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetQuantDTypeCastPrimitive(const std::vector<int8_t>& primitive);
|
||||
PrimUniquePtr GetMulFusionPrimitive(const std::vector<int8_t>& primitive);
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_HDI_NNR_NODE_FUNCTIONS_H
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNR_NODE_REGISTRY_H
|
||||
#define OHOS_HDI_NNR_NODE_REGISTRY_H
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "v1_0/nnrt_types.h"
|
||||
#include "mindspore_schema/model_generated.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
using PrimUniquePtr = std::unique_ptr<mindspore::schema::PrimitiveT>;
|
||||
class NodeRegistry {
|
||||
public:
|
||||
struct Registrar {
|
||||
Registrar() = delete;
|
||||
Registrar(NodeType type, std::function<PrimUniquePtr(const std::vector<int8_t>&)> nodeFunc);
|
||||
};
|
||||
|
||||
public:
|
||||
static NodeRegistry& GetSingleton();
|
||||
std::function<PrimUniquePtr(const std::vector<int8_t>&)> GetNodeFunc(NodeType type) const;
|
||||
bool IsNodeTypeExist(NodeType type) const;
|
||||
|
||||
private:
|
||||
NodeRegistry() {};
|
||||
NodeRegistry(const NodeRegistry&) = delete;
|
||||
NodeRegistry& operator=(const NodeRegistry&) = delete;
|
||||
|
||||
private:
|
||||
std::unordered_map<NodeType, std::function<PrimUniquePtr(const std::vector<int8_t>&)>> m_nodeRegs;
|
||||
};
|
||||
|
||||
#define REGISTER_NODE(nodeName, nodeType, funcPtr) static NodeRegistry::Registrar g_##nodeName(nodeType, funcPtr)
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_HDI_NNR_NODE_REGISTRY_H
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
|
||||
#define OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
|
||||
|
||||
#include "v1_0/iprepared_model.h"
|
||||
#include "include/api/data_type.h"
|
||||
#include "include/api/context.h"
|
||||
#include "include/api/types.h"
|
||||
#include "include/api/model.h"
|
||||
#include "mindspore_schema/model_generated.h"
|
||||
#include "ashmem.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
constexpr int DYNAMIC_SHAPE_FLAG = -1;
|
||||
class PreparedModelService : public IPreparedModel {
|
||||
public:
|
||||
PreparedModelService() = default;
|
||||
|
||||
virtual ~PreparedModelService();
|
||||
|
||||
explicit PreparedModelService(std::shared_ptr<mindspore::Context> context);
|
||||
|
||||
int32_t Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph);
|
||||
|
||||
int32_t Compile(const void* modelBuffer, size_t length);
|
||||
|
||||
int32_t ExportModelCache(std::vector<SharedBuffer>& modelCache) override;
|
||||
|
||||
int32_t Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough) override;
|
||||
|
||||
private:
|
||||
int32_t SetInputs(const std::vector<IOTensor>& inputs);
|
||||
int32_t SetOutputs(const std::vector<IOTensor>& outputs);
|
||||
int32_t GetMSInputsAndOutputs();
|
||||
int32_t CompareTensor(const IOTensor& tensor, const mindspore::MSTensor& msTensor);
|
||||
sptr<Ashmem> ParseBuffer(const SharedBuffer& buffer);
|
||||
int32_t UpdateOutput(const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough);
|
||||
void ResetInputAndOutput();
|
||||
|
||||
private:
|
||||
std::shared_ptr<mindspore::schema::MetaGraphT> m_graph {nullptr};
|
||||
std::shared_ptr<mindspore::Context> m_context {nullptr};
|
||||
flatbuffers::FlatBufferBuilder m_builder;
|
||||
std::shared_ptr<mindspore::Model> m_model {nullptr};
|
||||
sptr<Ashmem> m_cacheBuffer {nullptr};
|
||||
std::vector<sptr<Ashmem>> m_inputAshmems;
|
||||
std::vector<mindspore::MSTensor> m_inputs;
|
||||
std::vector<sptr<Ashmem>> m_outputAshmems;
|
||||
std::vector<mindspore::MSTensor> m_outputs;
|
||||
std::vector<std::vector<int64_t>> m_inputDims;
|
||||
bool m_isDynamicShape {false};
|
||||
};
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
|
||||
#endif // OHOS_HDI_NNR_V1_0_PREPAREDMODELSERVICE_H
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
|
||||
#define OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
|
||||
|
||||
#include "ashmem.h"
|
||||
#include "v1_0/nnrt_types.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
namespace {
|
||||
const int INVALID_FD = -1;
|
||||
}
|
||||
|
||||
class SharedBufferParser {
|
||||
public:
|
||||
SharedBufferParser() {};
|
||||
~SharedBufferParser();
|
||||
|
||||
int32_t Init(const SharedBuffer& buffer);
|
||||
int32_t Init(const std::string& name, int32_t size);
|
||||
void* GetBufferPtr();
|
||||
SharedBuffer GetBuffer();
|
||||
|
||||
private:
|
||||
SharedBuffer m_buffer;
|
||||
sptr<Ashmem> m_ashptr {nullptr};
|
||||
void* m_bufferAddr {nullptr};
|
||||
};
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
#endif // OHOS_HDI_NNR_V1_0_SHARED_BUFFER_PARSER_H
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNRT_VALIDATION_H
|
||||
#define OHOS_HDI_NNRT_VALIDATION_H
|
||||
|
||||
#include "v1_0/nnrt_types.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
int32_t ValidatePerformanceMode(PerformanceMode mode);
|
||||
int32_t ValidatePriority(Priority priority);
|
||||
int32_t ValidateDataType(DataType dataType);
|
||||
int32_t ValidateFormat(Format format);
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
#endif // OHOS_HDI_NNRT_VALIDATION_H
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <hdf_base.h>
|
||||
#include <hdf_device_desc.h>
|
||||
#include <hdf_log.h>
|
||||
#include <hdf_sbuf_ipc.h>
|
||||
#include "v1_0/nnrt_device_stub.h"
|
||||
|
||||
using namespace OHOS::HDI::Nnrt::V1_0;
|
||||
|
||||
struct HdfNnrtDeviceHost {
|
||||
struct IDeviceIoService ioService;
|
||||
OHOS::sptr<OHOS::IRemoteObject> stub;
|
||||
};
|
||||
|
||||
static int32_t NnrtDeviceDriverDispatch(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data,
|
||||
struct HdfSBuf *reply)
|
||||
{
|
||||
auto *hdfNnrtDeviceHost = CONTAINER_OF(client->device->service, struct HdfNnrtDeviceHost, ioService);
|
||||
|
||||
OHOS::MessageParcel *dataParcel = nullptr;
|
||||
OHOS::MessageParcel *replyParcel = nullptr;
|
||||
OHOS::MessageOption option;
|
||||
|
||||
if (SbufToParcel(data, &dataParcel) != HDF_SUCCESS) {
|
||||
HDF_LOGE("%{public}s:invalid data sbuf object to dispatch", __func__);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
if (SbufToParcel(reply, &replyParcel) != HDF_SUCCESS) {
|
||||
HDF_LOGE("%{public}s:invalid reply sbuf object to dispatch", __func__);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return hdfNnrtDeviceHost->stub->SendRequest(cmdId, *dataParcel, *replyParcel, option);
|
||||
}
|
||||
|
||||
static int HdfNnrtDeviceDriverInit(struct HdfDeviceObject *deviceObject)
|
||||
{
|
||||
HDF_LOGI("HdfNnrtDeviceDriverInit enter");
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
static int HdfNnrtDeviceDriverBind(struct HdfDeviceObject *deviceObject)
|
||||
{
|
||||
HDF_LOGI("HdfNnrtDeviceDriverBind enter");
|
||||
|
||||
auto *hdfNnrtDeviceHost = new (std::nothrow) HdfNnrtDeviceHost;
|
||||
if (hdfNnrtDeviceHost == nullptr) {
|
||||
HDF_LOGE("%{public}s: failed to create create HdfNnrtDeviceHost object", __func__);
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
hdfNnrtDeviceHost->ioService.Dispatch = NnrtDeviceDriverDispatch;
|
||||
hdfNnrtDeviceHost->ioService.Open = NULL;
|
||||
hdfNnrtDeviceHost->ioService.Release = NULL;
|
||||
|
||||
auto serviceImpl = INnrtDevice::Get(true);
|
||||
if (serviceImpl == nullptr) {
|
||||
HDF_LOGE("%{public}s: failed to get of implement service", __func__);
|
||||
delete hdfNnrtDeviceHost;
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
hdfNnrtDeviceHost->stub = OHOS::HDI::ObjectCollector::GetInstance().GetOrNewObject(serviceImpl,
|
||||
INnrtDevice::GetDescriptor());
|
||||
if (hdfNnrtDeviceHost->stub == nullptr) {
|
||||
HDF_LOGE("%{public}s: failed to get stub object", __func__);
|
||||
delete hdfNnrtDeviceHost;
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
deviceObject->service = &hdfNnrtDeviceHost->ioService;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
static void HdfNnrtDeviceDriverRelease(struct HdfDeviceObject *deviceObject)
|
||||
{
|
||||
HDF_LOGI("HdfNnrtDeviceDriverRelease enter");
|
||||
if (deviceObject->service == nullptr) {
|
||||
HDF_LOGE("HdfNnrtDeviceDriverRelease not initted");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *hdfNnrtDeviceHost = CONTAINER_OF(deviceObject->service, struct HdfNnrtDeviceHost, ioService);
|
||||
delete hdfNnrtDeviceHost;
|
||||
}
|
||||
|
||||
struct HdfDriverEntry g_nnrtdeviceDriverEntry = {
|
||||
.moduleVersion = 1,
|
||||
.moduleName = "nnrt",
|
||||
.Bind = HdfNnrtDeviceDriverBind,
|
||||
.Init = HdfNnrtDeviceDriverInit,
|
||||
.Release = HdfNnrtDeviceDriverRelease,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
HDF_INIT(g_nnrtdeviceDriverEntry);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nnrt_device_service.h"
|
||||
|
||||
#include <hdf_base.h>
|
||||
#include "utils/hdf_log.h"
|
||||
#include "ashmem.h"
|
||||
#include "securec.h"
|
||||
|
||||
#include "node_registry.h"
|
||||
#include "prepared_model_service.h"
|
||||
#include "shared_buffer_parser.h"
|
||||
#include "validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
extern "C" INnrtDevice *NnrtDeviceImplGetInstance(void)
|
||||
{
|
||||
return new (std::nothrow) NnrtDeviceService();
|
||||
}
|
||||
|
||||
NnrtDeviceService::~NnrtDeviceService()
|
||||
{
|
||||
for (auto ash : m_ashmems) {
|
||||
ash.second->UnmapAshmem();
|
||||
ash.second->CloseAshmem();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::GetDeviceName(std::string& name)
|
||||
{
|
||||
name = "RK3568-CPU";
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::GetVendorName(std::string& name)
|
||||
{
|
||||
name = "Rockchip";
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::GetDeviceType(DeviceType& deviceType)
|
||||
{
|
||||
deviceType = DeviceType::CPU;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::GetDeviceStatus(DeviceStatus& status)
|
||||
{
|
||||
status = DeviceStatus::AVAILABLE;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::GetSupportedOperation(const Model& model, std::vector<bool>& ops)
|
||||
{
|
||||
size_t nodeSize = model.nodes.size();
|
||||
auto nodes = model.nodes;
|
||||
ops.resize(nodeSize, false);
|
||||
auto& regInstance = NodeRegistry::GetSingleton();
|
||||
for (size_t i = 0; i < nodeSize; i++) {
|
||||
ops[i] = regInstance.IsNodeTypeExist(nodes[i].nodeType);
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::IsFloat16PrecisionSupported(bool& isSupported)
|
||||
{
|
||||
isSupported = true;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::IsPerformanceModeSupported(bool& isSupported)
|
||||
{
|
||||
isSupported = true;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::IsPrioritySupported(bool& isSupported)
|
||||
{
|
||||
isSupported = false;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::IsDynamicInputSupported(bool& isSupported)
|
||||
{
|
||||
isSupported = true;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::PrepareModel(const Model& model, const ModelConfig& config,
|
||||
sptr<IPreparedModel>& preparedModel)
|
||||
{
|
||||
auto ret = ValidateModel(model);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Model is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto graph = TransModelToGraph(model);
|
||||
if (graph == nullptr) {
|
||||
HDF_LOGE("Transfrom model to graph failed.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
ret = ValidateModelConfig(config);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("ModelConfig is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto context = TransModelConfig(config);
|
||||
sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
|
||||
if (service == nullptr) {
|
||||
HDF_LOGE("Create new PreparedModelService instance failed.");
|
||||
return HDF_ERR_MALLOC_FAIL;
|
||||
}
|
||||
|
||||
ret = service->Compile(graph);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Prepared model failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
preparedModel = service;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::IsModelCacheSupported(bool& isSupported)
|
||||
{
|
||||
isSupported = true;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::PrepareModelFromModelCache(const std::vector<SharedBuffer>& modelCache,
|
||||
const ModelConfig& config, sptr<IPreparedModel>& preparedModel)
|
||||
{
|
||||
HDF_LOGD("Using cache to prepare model.");
|
||||
|
||||
// modelCache must be 1, because PreparedModel only export one cache file.
|
||||
if (modelCache.size() != 1) {
|
||||
HDF_LOGE("The size of modelCache vector is not valid, it should be one elememt in that vector.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
SharedBufferParser parser;
|
||||
auto ret = parser.Init(modelCache[0]);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse modle buffer failed.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
void* modelBuffer = parser.GetBufferPtr();
|
||||
auto context = TransModelConfig(config);
|
||||
sptr<PreparedModelService> service = new (std::nothrow) PreparedModelService(context);
|
||||
if (service == nullptr) {
|
||||
HDF_LOGE("Create new instance PreparedModelService failed.");
|
||||
return HDF_ERR_MALLOC_FAIL;
|
||||
}
|
||||
|
||||
ret = service->Compile(modelBuffer, modelCache[0].dataSize);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Prepared model failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
preparedModel = service;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::AllocateBuffer(uint32_t length, SharedBuffer& buffer)
|
||||
{
|
||||
sptr<Ashmem> ashptr = Ashmem::CreateAshmem("allocateBuffer", length);
|
||||
if (ashptr == nullptr) {
|
||||
HDF_LOGE("Create shared memory failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
if (!ashptr->MapReadAndWriteAshmem()) {
|
||||
HDF_LOGE("Map allocate buffer failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
buffer.fd = ashptr->GetAshmemFd();
|
||||
buffer.bufferSize = ashptr->GetAshmemSize();
|
||||
buffer.offset = 0;
|
||||
buffer.dataSize = length;
|
||||
|
||||
m_ashmems[buffer.fd] = ashptr;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::ReleaseBuffer(const SharedBuffer& buffer)
|
||||
{
|
||||
// parser will close current fd.
|
||||
SharedBufferParser parser;
|
||||
auto ret = parser.Init(buffer);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse buffer failed.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
for (auto& ash : m_ashmems) {
|
||||
ash.second->UnmapAshmem();
|
||||
ash.second->CloseAshmem();
|
||||
}
|
||||
m_ashmems.clear();
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::ValidateModelConfig(const ModelConfig& config) const
|
||||
{
|
||||
if (!ValidatePerformanceMode(config.mode)) {
|
||||
HDF_LOGE("PerformanceMode is invalid. mode=%d", config.mode);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!ValidatePriority(config.priority)) {
|
||||
HDF_LOGE("Priority is invalid. priority=%d", config.priority);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NnrtDeviceService::ValidateModel(const Model& model) const
|
||||
{
|
||||
if (model.allTensors.empty()) {
|
||||
HDF_LOGE("Model has no tensors.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (model.subGraph.empty()) {
|
||||
HDF_LOGE("Model has no subGraphs.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (model.nodes.empty()) {
|
||||
HDF_LOGE("Model has no nodes.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (model.inputIndex.empty()) {
|
||||
HDF_LOGE("Model has no input.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (model.outputIndex.empty()) {
|
||||
HDF_LOGE("Model has no output.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
size_t tensorSize = model.allTensors.size();
|
||||
for (auto index : model.inputIndex) {
|
||||
if (index > tensorSize) {
|
||||
HDF_LOGE("Input index is invalid, index=%u", index);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto index : model.outputIndex) {
|
||||
if (index > tensorSize) {
|
||||
HDF_LOGE("Output index is invalid, index=%u", index);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
std::shared_ptr<mindspore::schema::MetaGraphT> NnrtDeviceService::TransModelToGraph(const Model& model) const
|
||||
{
|
||||
auto metaGraph = std::make_shared<mindspore::schema::MetaGraphT>();
|
||||
metaGraph->name = model.name;
|
||||
metaGraph->version = mindspore::Version();
|
||||
|
||||
std::unique_ptr<mindspore::schema::TensorT> transTensor{nullptr};
|
||||
for (auto tensor : model.allTensors) {
|
||||
transTensor = TransTensor(tensor);
|
||||
if (transTensor == nullptr) {
|
||||
HDF_LOGE("Transform tensor failed.");
|
||||
return nullptr;
|
||||
}
|
||||
metaGraph->allTensors.emplace_back(std::move(transTensor));
|
||||
}
|
||||
metaGraph->inputIndex = model.inputIndex;
|
||||
metaGraph->outputIndex = model.outputIndex;
|
||||
|
||||
// Transform node
|
||||
std::unique_ptr<mindspore::schema::CNodeT> transNode {nullptr};
|
||||
for (auto& node : model.nodes) {
|
||||
transNode = TransNode(node);
|
||||
if (transNode == nullptr) {
|
||||
HDF_LOGE("Transform node failed, node name=%{public}s", node.name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
metaGraph->nodes.emplace_back(std::move(transNode));
|
||||
}
|
||||
|
||||
// Transform subgraph
|
||||
const size_t numTensor = model.allTensors.size();
|
||||
for (auto graph : model.subGraph) {
|
||||
metaGraph->subGraph.emplace_back(TransSubGraph(graph, numTensor));
|
||||
}
|
||||
return metaGraph;
|
||||
}
|
||||
|
||||
std::unique_ptr<mindspore::schema::TensorT> NnrtDeviceService::TransTensor(const Tensor& tensor) const
|
||||
{
|
||||
if (!ValidateDataType(tensor.dataType)) {
|
||||
HDF_LOGE("DataType of tensor is invalid. dataType=%d", tensor.dataType);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ValidateFormat(tensor.format)) {
|
||||
HDF_LOGE("Format of tensor is invalid. format=%d", tensor.format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto schemaTensor = std::make_unique<mindspore::schema::TensorT>();
|
||||
schemaTensor->name = tensor.name;
|
||||
schemaTensor->dataType = static_cast<int32_t>(tensor.dataType);
|
||||
schemaTensor->format = static_cast<mindspore::schema::Format>(tensor.format);
|
||||
schemaTensor->dims = tensor.dims;
|
||||
for (auto param : tensor.quantParams) {
|
||||
auto quantParam = std::make_unique<mindspore::schema::QuantParamT>();
|
||||
quantParam->scale = param.scale;
|
||||
quantParam->zeroPoint = param.zeroPoint;
|
||||
quantParam->numBits = param.numBits;
|
||||
quantParam->inited = true;
|
||||
schemaTensor->quantParams.emplace_back(std::move(quantParam));
|
||||
}
|
||||
|
||||
if (tensor.data.fd != INVALID_FD) {
|
||||
SharedBufferParser parser;
|
||||
auto ret = parser.Init(tensor.data);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse tensor data failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = parser.GetBufferPtr();
|
||||
schemaTensor->data.resize(tensor.data.dataSize);
|
||||
auto memRet = memcpy_s(const_cast<uint8_t*>(schemaTensor->data.data()),
|
||||
tensor.data.dataSize, data, tensor.data.dataSize);
|
||||
if (memRet != EOK) {
|
||||
HDF_LOGW("Copy tensor data failed.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return schemaTensor;
|
||||
}
|
||||
|
||||
std::unique_ptr<mindspore::schema::CNodeT> NnrtDeviceService::TransNode(const Node& node) const
|
||||
{
|
||||
auto cnode = std::make_unique<mindspore::schema::CNodeT>();
|
||||
cnode->name = node.name;
|
||||
cnode->inputIndex = node.inputIndex;
|
||||
cnode->outputIndex = node.outputIndex;
|
||||
cnode->quantType = static_cast<mindspore::schema::QuantType>(node.quantType);
|
||||
|
||||
auto& regInstance = NodeRegistry::GetSingleton();
|
||||
auto parseFunc = regInstance.GetNodeFunc(node.nodeType);
|
||||
auto primitive = parseFunc(node.nodeAttr);
|
||||
if (primitive == nullptr) {
|
||||
HDF_LOGE("Parse primitve data failed. node name=%{public}s", node.name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cnode->primitive = std::move(primitive);
|
||||
return cnode;
|
||||
}
|
||||
|
||||
std::unique_ptr<mindspore::schema::SubGraphT> NnrtDeviceService::TransSubGraph(const SubGraph& graph,
|
||||
const size_t numTensor) const
|
||||
{
|
||||
auto subGraph = std::make_unique<mindspore::schema::SubGraphT>();
|
||||
subGraph->name = graph.name;
|
||||
subGraph->inputIndices = graph.inputIndices;
|
||||
subGraph->outputIndices = graph.outputIndices;
|
||||
subGraph->nodeIndices = graph.nodeIndices;
|
||||
subGraph->tensorIndices.reserve(numTensor);
|
||||
for (auto i = 0; i < numTensor; i++) {
|
||||
subGraph->tensorIndices.emplace_back(static_cast<uint32_t>(i));
|
||||
}
|
||||
return subGraph;
|
||||
}
|
||||
|
||||
std::shared_ptr<mindspore::Context> NnrtDeviceService::TransModelConfig(const ModelConfig& config) const
|
||||
{
|
||||
auto context = std::make_shared<mindspore::Context>();
|
||||
const int cpuThreadNum = 2;
|
||||
const int cpuNoAffinities = 0;
|
||||
const int cpuBigCore = 1;
|
||||
const int cpuLittleCore = 2;
|
||||
context->SetThreadNum(cpuThreadNum);
|
||||
|
||||
int mode = cpuNoAffinities;
|
||||
switch (config.mode) {
|
||||
case PerformanceMode::PERFORMANCE_LOW:
|
||||
case PerformanceMode::PERFORMANCE_MEDIUM:
|
||||
mode = cpuLittleCore;
|
||||
break;
|
||||
case PerformanceMode::PERFORMANCE_HIGH:
|
||||
case PerformanceMode::PERFORMANCE_EXTREME:
|
||||
mode = cpuBigCore;
|
||||
break;
|
||||
default:
|
||||
mode = cpuNoAffinities;
|
||||
}
|
||||
context->SetThreadAffinity(mode);
|
||||
|
||||
auto cpuInfo = std::make_shared<mindspore::CPUDeviceInfo>();
|
||||
cpuInfo->SetEnableFP16(config.enableFloat16);
|
||||
auto& deviceInfos = context->MutableDeviceInfo();
|
||||
deviceInfos.emplace_back(cpuInfo);
|
||||
return context;
|
||||
}
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "node_functions.h"
|
||||
|
||||
#include "node_registry.h"
|
||||
#include <message_parcel.h>
|
||||
#include <v1_0/node_attr_types.h>
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
PrimUniquePtr GetAddPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
AddFusion addAttr;
|
||||
auto ret = ParsePrimitive<AddFusion>(primitive, addAttr, AddFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of AddFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_AddFusion;
|
||||
auto attr = new (std::nothrow) mindspore::schema::AddFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create AddFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(addAttr.activationType);
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetAvgPoolPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
AvgPoolFusion avgPoolAttr;
|
||||
auto ret = ParsePrimitive<AvgPoolFusion>(primitive, avgPoolAttr, AvgPoolFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of AvgPoolFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_AvgPoolFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::AvgPoolFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create AvgPoolFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
attr->kernel_size = avgPoolAttr.kernelSize;
|
||||
attr->strides = avgPoolAttr.strides;
|
||||
attr->pad = avgPoolAttr.pad;
|
||||
attr->pad_mode = static_cast<mindspore::schema::PadMode>(avgPoolAttr.padMode);
|
||||
attr->round_mode = static_cast<mindspore::schema::RoundMode>(avgPoolAttr.roundMode);
|
||||
attr->format = static_cast<mindspore::schema::Format>(avgPoolAttr.format);
|
||||
attr->global = avgPoolAttr.global;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(avgPoolAttr.activationType);
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetConcatPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
Concat concatAttr;
|
||||
auto ret = ParsePrimitive<Concat>(primitive, concatAttr, ConcatBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of Concat operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_Concat;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::ConcatT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create concat primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
attr->axis = concatAttr.axis;
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetConv2dPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
Conv2DFusion conv2dAttr;
|
||||
auto ret = ParsePrimitive<Conv2DFusion>(primitive, conv2dAttr, Conv2DFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of Conv2DFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_Conv2DFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::Conv2DFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create Conv2DFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->kernel_size = conv2dAttr.kernelSize;
|
||||
attr->stride = conv2dAttr.stride;
|
||||
attr->dilation = conv2dAttr.dilation;
|
||||
attr->pad_mode = static_cast<mindspore::schema::PadMode>(conv2dAttr.padMode);
|
||||
attr->pad_list = conv2dAttr.padList;
|
||||
attr->group = conv2dAttr.group;
|
||||
attr->in_channel = conv2dAttr.inChannel;
|
||||
attr->out_channel = conv2dAttr.outChannel;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(conv2dAttr.activationType);
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetFullConnectionPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
FullConnection fullConnAttr;
|
||||
auto ret = ParsePrimitive<FullConnection>(primitive, fullConnAttr, FullConnectionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of FullConnection operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_FullConnection;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::FullConnectionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create FullConnection primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->has_bias = fullConnAttr.hasBias;
|
||||
attr->use_axis = fullConnAttr.useAxis;
|
||||
attr->axis = fullConnAttr.axis;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(fullConnAttr.activationType);
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetMaxPoolFusionPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
MaxPoolFusion maxPoolAttr;
|
||||
auto ret = ParsePrimitive<MaxPoolFusion>(primitive, maxPoolAttr, MaxPoolFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of MaxPoolFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_MaxPoolFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::MaxPoolFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create MaxPoolFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->kernel_size = maxPoolAttr.kernelSize;
|
||||
attr->strides = maxPoolAttr.strides;
|
||||
attr->pad = maxPoolAttr.pad;
|
||||
attr->pad_mode = static_cast<mindspore::schema::PadMode>(maxPoolAttr.padMode);
|
||||
attr->format = static_cast<mindspore::schema::Format>(maxPoolAttr.format);
|
||||
attr->global = maxPoolAttr.global;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(maxPoolAttr.activationType);
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetMatMulFusionPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
MatMulFusion matmulAttr;
|
||||
auto ret = ParsePrimitive<MatMulFusion>(primitive, matmulAttr, MatMulFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of MatMulFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_MatMulFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::MatMulFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create MatMulFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->transpose_a = matmulAttr.transposeA;
|
||||
attr->transpose_b = matmulAttr.transposeB;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(matmulAttr.activationType);
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetSoftmaxPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
Softmax softmaxAttr;
|
||||
auto ret = ParsePrimitive<Softmax>(primitive, softmaxAttr, SoftmaxBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of Softmax operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_Softmax;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::SoftmaxT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create Softmax primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->axis = softmaxAttr.axis;
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetReshapePrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
Reshape reshapeAttr;
|
||||
auto ret = ParsePrimitive<Reshape>(primitive, reshapeAttr, ReshapeBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of Reshape operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_Reshape;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::ReshapeT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create Reshape primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetScaleFusionPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
ScaleFusion scaleAttr;
|
||||
auto ret = ParsePrimitive<ScaleFusion>(primitive, scaleAttr, ScaleFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of ScaleFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_ScaleFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::ScaleFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create ScaleFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->axis = scaleAttr.axis;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(scaleAttr.activationType);
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetActivationPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
Activation actAttr;
|
||||
auto ret = ParsePrimitive<Activation>(primitive, actAttr, ActivationBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of Activation operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_Activation;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::ActivationT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create Activation primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->alpha = actAttr.alpha;
|
||||
attr->min_val = actAttr.minVal;
|
||||
attr->max_val = actAttr.maxVal;
|
||||
attr->approximate = actAttr.approximate;
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(actAttr.activationType);
|
||||
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetQuantDTypeCastPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
QuantDTypeCast quantAttr;
|
||||
auto ret = ParsePrimitive<QuantDTypeCast>(primitive, quantAttr, QuantDTypeCastBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of QuantDTypeCast operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_QuantDTypeCast;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::QuantDTypeCastT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create QuantDTypeCast primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->src_t = quantAttr.srcT;
|
||||
attr->dst_t = quantAttr.dstT;
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
PrimUniquePtr GetMulFusionPrimitive(const std::vector<int8_t>& primitive)
|
||||
{
|
||||
MulFusion mulAttr;
|
||||
auto ret = ParsePrimitive<MulFusion>(primitive, mulAttr, MulFusionBlockUnmarshalling);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse primitive data of MulFusion operator failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto prim = std::make_unique<mindspore::schema::PrimitiveT>();
|
||||
prim->value.type = mindspore::schema::PrimitiveType_MulFusion;
|
||||
|
||||
auto attr = new (std::nothrow) mindspore::schema::MulFusionT;
|
||||
if (attr == nullptr) {
|
||||
HDF_LOGE("Create MulFusion primitive failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attr->activation_type = static_cast<mindspore::schema::ActivationType>(mulAttr.activationType);
|
||||
prim->value.value = attr;
|
||||
return prim;
|
||||
}
|
||||
|
||||
REGISTER_NODE(Activation, NodeType::NODE_TYPE_ACTIVATION, GetActivationPrimitive);
|
||||
REGISTER_NODE(AddFusion, NodeType::NODE_TYPE_ADD_FUSION, GetAddPrimitive);
|
||||
REGISTER_NODE(AvgPoolFusion, NodeType::NODE_TYPE_AVGPOOL_FUSION, GetAvgPoolPrimitive);
|
||||
REGISTER_NODE(Concat, NodeType::NODE_TYPE_CONCAT, GetConcatPrimitive);
|
||||
REGISTER_NODE(Conv2DFusion, NodeType::NODE_TYPE_CONV2D_FUSION, GetConv2dPrimitive);
|
||||
REGISTER_NODE(FullConnection, NodeType::NODE_TYPE_FULL_CONNECTION, GetFullConnectionPrimitive);
|
||||
REGISTER_NODE(MaxPoolFusion, NodeType::NODE_TYPE_MAX_POOL_FUSION, GetMaxPoolFusionPrimitive);
|
||||
REGISTER_NODE(MatMulFusion, NodeType::NODE_TYPE_MATMUL_FUSION, GetMatMulFusionPrimitive);
|
||||
REGISTER_NODE(Reshape, NodeType::NODE_TYPE_RESHAPE, GetReshapePrimitive);
|
||||
REGISTER_NODE(Softmax, NodeType::NODE_TYPE_SOFTMAX, GetSoftmaxPrimitive);
|
||||
REGISTER_NODE(ScaleFusion, NodeType::NODE_TYPE_SCALE_FUSION, GetScaleFusionPrimitive);
|
||||
REGISTER_NODE(QuantDTypeCast, NodeType::NODE_TYPE_QUANT_DTYPE_CAST, GetQuantDTypeCastPrimitive);
|
||||
REGISTER_NODE(MulFusion, NodeType::NODE_TYPE_MUL_FUSION, GetMulFusionPrimitive);
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "node_registry.h"
|
||||
|
||||
#include "utils/hdf_log.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
NodeRegistry& NodeRegistry::GetSingleton()
|
||||
{
|
||||
static NodeRegistry registry;
|
||||
return registry;
|
||||
}
|
||||
|
||||
NodeRegistry::Registrar::Registrar(NodeType type, std::function<PrimUniquePtr(const std::vector<int8_t>&)> nodeFunc)
|
||||
{
|
||||
auto& registry = NodeRegistry::GetSingleton();
|
||||
if (registry.m_nodeRegs.find(type) != registry.m_nodeRegs.end()) {
|
||||
HDF_LOGW("Node has been registered. nodeType=%d", type);
|
||||
} else {
|
||||
registry.m_nodeRegs[type] = nodeFunc;
|
||||
}
|
||||
}
|
||||
|
||||
std::function<PrimUniquePtr(const std::vector<int8_t>&)> NodeRegistry::GetNodeFunc(NodeType type) const
|
||||
{
|
||||
if (m_nodeRegs.find(type) == m_nodeRegs.end()) {
|
||||
HDF_LOGW("Node type is not found. nodeType=%d", type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_nodeRegs.at(type);
|
||||
}
|
||||
|
||||
bool NodeRegistry::IsNodeTypeExist(NodeType type) const
|
||||
{
|
||||
if (m_nodeRegs.find(type) == m_nodeRegs.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "prepared_model_service.h"
|
||||
|
||||
#include <hdf_base.h>
|
||||
#include "securec.h"
|
||||
#include "utils/hdf_log.h"
|
||||
|
||||
#include "shared_buffer_parser.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
PreparedModelService::PreparedModelService(std::shared_ptr<mindspore::Context> context)
|
||||
: m_context(context) {}
|
||||
|
||||
PreparedModelService::~PreparedModelService()
|
||||
{
|
||||
if (m_cacheBuffer != nullptr) {
|
||||
m_cacheBuffer->CloseAshmem();
|
||||
}
|
||||
|
||||
for (auto& inputAsh : m_inputAshmems) {
|
||||
inputAsh->UnmapAshmem();
|
||||
inputAsh->CloseAshmem();
|
||||
}
|
||||
|
||||
for (auto& outputAsh : m_outputAshmems) {
|
||||
outputAsh->UnmapAshmem();
|
||||
outputAsh->CloseAshmem();
|
||||
}
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::ExportModelCache(std::vector<SharedBuffer>& modelCache)
|
||||
{
|
||||
if (!modelCache.empty()) {
|
||||
HDF_LOGE("The parameters of ExportModelCache should be an empty vector.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (m_cacheBuffer != nullptr) {
|
||||
auto fd = m_cacheBuffer->GetAshmemFd();
|
||||
auto size = m_cacheBuffer->GetAshmemSize();
|
||||
|
||||
// SharedBuffer: fd, bufferSize, offset, dataSize
|
||||
modelCache.emplace_back(SharedBuffer{fd, size, 0, size});
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
auto size = m_builder.GetSize();
|
||||
auto buffer = m_builder.GetBufferPointer();
|
||||
const char* name = m_graph != nullptr ? m_graph->name.c_str() : "CacheModel";
|
||||
sptr<Ashmem> cache = Ashmem::CreateAshmem(name, size);
|
||||
if (cache == nullptr) {
|
||||
HDF_LOGE("Create shared memory failed.");
|
||||
return HDF_ERR_MALLOC_FAIL;
|
||||
}
|
||||
|
||||
bool ret = cache->MapReadAndWriteAshmem();
|
||||
if (!ret) {
|
||||
HDF_LOGE("Map fd to write cache failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
ret = cache->WriteToAshmem(buffer, size, 0);
|
||||
cache->UnmapAshmem();
|
||||
if (!ret) {
|
||||
HDF_LOGE("Write cache failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
m_cacheBuffer = cache;
|
||||
|
||||
// SharedBuffer: fd, bufferSize, offset, dataSize
|
||||
modelCache.emplace_back(SharedBuffer {cache->GetAshmemFd(), cache->GetAshmemSize(), 0, cache->GetAshmemSize()});
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
|
||||
{
|
||||
auto ret = SetInputs(inputs);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Inputs tensor is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!m_isDynamicShape) {
|
||||
ret = SetOutputs(outputs);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Output tensor is invalid.");
|
||||
ResetInputAndOutput();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
auto msRet = m_model->Predict(m_inputs, &m_outputs);
|
||||
if (msRet != mindspore::kSuccess) {
|
||||
HDF_LOGE("Run model failed.");
|
||||
ResetInputAndOutput();
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
ret = UpdateOutput(outputs, outputsDims, isOutputBufferEnough);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Update output dimension or data failed.");
|
||||
ResetInputAndOutput();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ResetInputAndOutput();
|
||||
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::UpdateOutput(const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
|
||||
{
|
||||
bool isEnough {true};
|
||||
size_t outputSize = m_outputs.size();
|
||||
isOutputBufferEnough.resize(outputSize, true);
|
||||
for (size_t i = 0; i < outputSize; i++) {
|
||||
auto& msOutput = m_outputs[i];
|
||||
auto& output = outputs[i];
|
||||
|
||||
auto msShape = msOutput.Shape();
|
||||
outputsDims.emplace_back(msShape.begin(), msShape.end());
|
||||
|
||||
auto dataSize = msOutput.DataSize();
|
||||
if (dataSize > output.data.bufferSize) {
|
||||
HDF_LOGE("Output buffer is not enough. actual size %{public}zu, buffer size %{public}u",
|
||||
dataSize, output.data.bufferSize);
|
||||
isOutputBufferEnough[i] = false;
|
||||
isEnough= false;
|
||||
}
|
||||
|
||||
if (isEnough && m_isDynamicShape) {
|
||||
auto msData = msOutput.MutableData();
|
||||
SharedBufferParser parser;
|
||||
auto ret = parser.Init(output.data);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Parse %zu th output data failed.", i);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
auto data = parser.GetBufferPtr();
|
||||
auto memRet = memcpy_s(data, dataSize, msData, dataSize);
|
||||
if (memRet != EOK) {
|
||||
HDF_LOGE("Copy output memory failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
void PreparedModelService::ResetInputAndOutput()
|
||||
{
|
||||
for (auto& msInput : m_inputs) {
|
||||
msInput.SetData(nullptr);
|
||||
}
|
||||
|
||||
if (!m_isDynamicShape) {
|
||||
for (auto& msOutput : m_outputs) {
|
||||
msOutput.SetData(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::Compile(std::shared_ptr<mindspore::schema::MetaGraphT> graph)
|
||||
{
|
||||
if (graph == nullptr) {
|
||||
HDF_LOGE("Graph cannot be nullptr");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
for (auto i : graph->inputIndex) {
|
||||
auto inputShape = graph->allTensors[i]->dims;
|
||||
auto iter = std::find(inputShape.begin(), inputShape.end(), DYNAMIC_SHAPE_FLAG);
|
||||
if (iter != inputShape.end()) {
|
||||
m_isDynamicShape = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto offset = mindspore::schema::MetaGraph::Pack(m_builder, graph.get());
|
||||
m_builder.Finish(offset);
|
||||
mindspore::schema::FinishMetaGraphBuffer(m_builder, offset);
|
||||
auto modelSize = m_builder.GetSize();
|
||||
uint8_t* modelBuffer = m_builder.GetBufferPointer();
|
||||
if (modelBuffer == nullptr) {
|
||||
HDF_LOGE("Model is invalid.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
m_model = std::make_shared<mindspore::Model>();
|
||||
mindspore::Status msRet = m_model->Build(modelBuffer, modelSize, mindspore::kMindIR, m_context);
|
||||
if (msRet != mindspore::kSuccess) {
|
||||
HDF_LOGE("Prepare model failed, please make sure model is validate.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
auto ret = GetMSInputsAndOutputs();
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Model without inputs or outputs is invalid.");
|
||||
return ret;
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::Compile(const void* modelBuffer, size_t length)
|
||||
{
|
||||
if (modelBuffer == nullptr || length == 0) {
|
||||
HDF_LOGE("ModelBuffer cannot be nullptr and length cannot be zero.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
m_model = std::make_shared<mindspore::Model>();
|
||||
mindspore::Status msRet = m_model->Build(modelBuffer, length, mindspore::kMindIR, m_context);
|
||||
if (msRet != mindspore::kSuccess) {
|
||||
HDF_LOGE("Prepare model from cache failed, please make sure model cache is valid.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
auto ret = GetMSInputsAndOutputs();
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Model without inputs or outputs is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (auto input : m_inputs) {
|
||||
auto shapes = input.Shape();
|
||||
if (std::find(shapes.begin(), shapes.end(), DYNAMIC_SHAPE_FLAG) != shapes.end()) {
|
||||
m_isDynamicShape = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::SetInputs(const std::vector<IOTensor>& inputs)
|
||||
{
|
||||
if (inputs.size() != m_inputs.size()) {
|
||||
HDF_LOGE("inputs size is invalid. expect: %zu, actual: %zu", m_inputs.size(), inputs.size());
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
for (auto& ash : m_inputAshmems) {
|
||||
ash->UnmapAshmem();
|
||||
ash->CloseAshmem();
|
||||
}
|
||||
m_inputAshmems.clear();
|
||||
|
||||
int32_t ret {0};
|
||||
size_t inputSize = m_inputs.size();
|
||||
std::vector<std::vector<int64_t>> tmpAllDims;
|
||||
for (size_t i = 0; i < inputSize; i++) {
|
||||
auto& input = inputs[i];
|
||||
auto& msInput = m_inputs[i];
|
||||
ret = CompareTensor(input, msInput);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Inputs tensor is not match that of model. Please check input tensor.");
|
||||
return ret;
|
||||
}
|
||||
tmpAllDims.emplace_back(input.dimensions.begin(), input.dimensions.end());
|
||||
}
|
||||
|
||||
if (m_isDynamicShape) {
|
||||
auto msRet = m_model->Resize(m_inputs, tmpAllDims);
|
||||
if (msRet != mindspore::kSuccess) {
|
||||
HDF_LOGE("Resize for dynamic inputs failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
ret = GetMSInputsAndOutputs();
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Get ms inputs or outputs failed after resize.");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < inputSize; i++) {
|
||||
auto& input = inputs[i];
|
||||
auto& msInput = m_inputs[i];
|
||||
sptr<Ashmem> ashptr = ParseBuffer(input.data);
|
||||
if (ashptr == nullptr) {
|
||||
HDF_LOGE("Parse %zuth input data failed.", i);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
auto data = const_cast<void*>(ashptr->ReadFromAshmem(input.data.dataSize, 0));
|
||||
msInput.SetData(data);
|
||||
m_inputAshmems.emplace_back(ashptr);
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::SetOutputs(const std::vector<IOTensor>& outputs)
|
||||
{
|
||||
HDF_LOGI("Start Set outputs, m_outputs size=%zu", m_outputs.size());
|
||||
if (outputs.size() != m_outputs.size()) {
|
||||
HDF_LOGE("outputs size is invalid. expect: %{public}zu, actual: %{public}zu", m_outputs.size(), outputs.size());
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
for (auto ash : m_outputAshmems) {
|
||||
ash->UnmapAshmem();
|
||||
ash->CloseAshmem();
|
||||
}
|
||||
m_outputAshmems.clear();
|
||||
|
||||
for (size_t i = 0; i < m_outputs.size(); i++) {
|
||||
auto& output = outputs[i];
|
||||
auto& msOutput = m_outputs[i];
|
||||
|
||||
sptr<Ashmem> ashptr = ParseBuffer(output.data);
|
||||
if (ashptr == nullptr) {
|
||||
HDF_LOGE("Parse %{public}zu th output data failed.", i);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
auto data = const_cast<void*>(ashptr->ReadFromAshmem(output.data.dataSize, 0));
|
||||
msOutput.SetAllocator(nullptr);
|
||||
msOutput.SetData(data);
|
||||
m_outputAshmems.emplace_back(ashptr);
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::GetMSInputsAndOutputs()
|
||||
{
|
||||
m_inputs = m_model->GetInputs();
|
||||
if (m_inputs.empty()) {
|
||||
HDF_LOGE("Get inputs failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
m_outputs = m_model->GetOutputs();
|
||||
if (m_outputs.empty()) {
|
||||
HDF_LOGE("Get outputs failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t PreparedModelService::CompareTensor(const IOTensor& tensor, const mindspore::MSTensor& msTensor)
|
||||
{
|
||||
auto dataType = static_cast<DataType>(msTensor.DataType());
|
||||
if (tensor.dataType != dataType) {
|
||||
HDF_LOGE("Data type of tensor is not match that of model.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
auto format = static_cast<Format>(msTensor.format());
|
||||
if (tensor.format != format) {
|
||||
HDF_LOGE("Format of tensor is not match that of model.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tensor.dimensions.size(); i++) {
|
||||
if (msTensor.Shape()[i] != DYNAMIC_SHAPE_FLAG && tensor.dimensions[i] != msTensor.Shape()[i]) {
|
||||
HDF_LOGE("The Shape of tensor is not match that of model.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
sptr<Ashmem> PreparedModelService::ParseBuffer(const SharedBuffer& buffer)
|
||||
{
|
||||
if (buffer.fd == -1) {
|
||||
HDF_LOGE("Invalid buffer fd, it cannot be -1.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HDF_LOGW("NNRT buffer fd=%{public}d, length=%{public}u", buffer.fd, buffer.dataSize);
|
||||
|
||||
sptr<Ashmem> ashptr = new (std::nothrow) Ashmem(buffer.fd, buffer.bufferSize);
|
||||
if (ashptr == nullptr) {
|
||||
HDF_LOGE("Create shared memory failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ashptr->MapReadAndWriteAshmem()) {
|
||||
HDF_LOGE("Map buffer fd to address failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const void* data = ashptr->ReadFromAshmem(buffer.dataSize, buffer.offset);
|
||||
if (data == nullptr) {
|
||||
HDF_LOGE("Get data address failed.");
|
||||
ashptr->UnmapAshmem();
|
||||
ashptr->CloseAshmem();
|
||||
return nullptr;
|
||||
}
|
||||
return ashptr;
|
||||
}
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef OHOS_HDI_NNR_V1_0_UTILS_H
|
||||
#define OHOS_HDI_NNR_V1_0_UTILS_H
|
||||
|
||||
#include "shared_buffer_parser.h"
|
||||
|
||||
#include <hdf_base.h>
|
||||
#include "ashmem.h"
|
||||
#include "v1_0/nnrt_types.h"
|
||||
#include "utils/hdf_log.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
SharedBufferParser::~SharedBufferParser()
|
||||
{
|
||||
if (m_ashptr != nullptr) {
|
||||
m_ashptr->UnmapAshmem();
|
||||
m_ashptr->CloseAshmem();
|
||||
m_bufferAddr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t SharedBufferParser::Init(const std::string& name, int32_t size)
|
||||
{
|
||||
HDF_LOGI("Init SharedBufferParser from name and size.");
|
||||
sptr<Ashmem> ashptr = Ashmem::CreateAshmem(name.c_str(), size);
|
||||
if (ashptr == nullptr) {
|
||||
HDF_LOGE("Create ashmen from size failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
SharedBuffer buffer;
|
||||
buffer.fd = ashptr->GetAshmemFd();
|
||||
buffer.bufferSize = ashptr->GetAshmemSize();
|
||||
buffer.offset = 0;
|
||||
buffer.dataSize = size;
|
||||
|
||||
auto ret = Init(buffer);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
HDF_LOGE("Init SharedBufferParser failed.");
|
||||
return ret;
|
||||
}
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
int32_t SharedBufferParser::Init(const SharedBuffer& buffer)
|
||||
{
|
||||
if (buffer.fd == INVALID_FD) {
|
||||
HDF_LOGE("Invalid buffer fd, it cannot be %{public}d.", INVALID_FD);
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
m_ashptr = new (std::nothrow) Ashmem(buffer.fd, buffer.bufferSize);
|
||||
if (m_ashptr == nullptr) {
|
||||
HDF_LOGE("Create ashmem failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
if (!m_ashptr->MapReadAndWriteAshmem()) {
|
||||
HDF_LOGE("Map buffer fd to address failed.");
|
||||
return HDF_FAILURE;
|
||||
}
|
||||
|
||||
auto bufferAddr = m_ashptr->ReadFromAshmem(buffer.dataSize, buffer.offset);
|
||||
if (bufferAddr == nullptr) {
|
||||
HDF_LOGE("Invalid dataSize or offset of SharedBuffer.");
|
||||
return HDF_ERR_INVALID_PARAM;
|
||||
}
|
||||
m_bufferAddr = const_cast<void*>(bufferAddr);
|
||||
|
||||
m_buffer = buffer;
|
||||
return HDF_SUCCESS;
|
||||
}
|
||||
|
||||
void* SharedBufferParser::GetBufferPtr()
|
||||
{
|
||||
return m_bufferAddr;
|
||||
}
|
||||
|
||||
SharedBuffer SharedBufferParser::GetBuffer()
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
} // V1_0
|
||||
} // Nnrt
|
||||
} // HDI
|
||||
} // OHOS
|
||||
#endif // OHOS_HDI_NNR_V1_0_UTILS_H
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace HDI {
|
||||
namespace Nnrt {
|
||||
namespace V1_0 {
|
||||
int32_t ValidatePerformanceMode(PerformanceMode mode)
|
||||
{
|
||||
if (mode < PerformanceMode::PERFORMANCE_NONE || mode > PerformanceMode::PERFORMANCE_EXTREME) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t ValidatePriority(Priority priority)
|
||||
{
|
||||
if (priority < Priority::PRIORITY_NONE || priority > Priority::PRIORITY_HIGH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t ValidateDataType(DataType dataType)
|
||||
{
|
||||
if (dataType < DataType::DATA_TYPE_UNKNOWN || dataType > DataType::DATA_TYPE_FLOAT64) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataType > DataType::DATA_TYPE_UNKNOWN && dataType < DataType::DATA_TYPE_BOOL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataType > DataType::DATA_TYPE_BOOL && dataType < DataType::DATA_TYPE_INT8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataType > DataType::DATA_TYPE_UINT64 && dataType < DataType::DATA_TYPE_FLOAT16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t ValidateFormat(Format format)
|
||||
{
|
||||
if (format < Format::FORMAT_NONE || format > Format::FORMAT_NHWC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace V1_0
|
||||
} // namespace Nnrt
|
||||
} // namespace HDI
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,133 @@
|
||||
# Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import("//build/ohos.gni")
|
||||
|
||||
config("nnrt_config") {
|
||||
cflags_cc = [ "-fexceptions" ]
|
||||
}
|
||||
|
||||
nnrt_sources = [
|
||||
"native/device_manager.cpp",
|
||||
"native/device_registrar.cpp",
|
||||
"native/hdi_device.cpp",
|
||||
"native/hdi_prepared_model.cpp",
|
||||
"native/memory_manager.cpp",
|
||||
"native/transform.cpp",
|
||||
"native/nn_tensor.cpp",
|
||||
"native/validation.cpp",
|
||||
"native/inner_model.cpp",
|
||||
"native/compilation.cpp",
|
||||
"native/execution_plan.cpp",
|
||||
"native/executor.cpp",
|
||||
"native/neural_network_runtime.cpp",
|
||||
"native/ops_builder.cpp",
|
||||
"native/ops_registry.cpp",
|
||||
]
|
||||
|
||||
ops_sources = [
|
||||
"native/ops/add_builder.cpp",
|
||||
"native/ops/argmax_builder.cpp",
|
||||
"native/ops/avgpool_builder.cpp",
|
||||
"native/ops/pooling_builder.cpp",
|
||||
"native/ops/batch_to_space_nd_builder.cpp",
|
||||
"native/ops/bias_add_builder.cpp",
|
||||
"native/ops/cast_builder.cpp",
|
||||
"native/ops/concat_builder.cpp",
|
||||
"native/ops/conv2d_builder.cpp",
|
||||
"native/ops/conv2d_transpose_builder.cpp",
|
||||
"native/ops/depthwise_conv2d_native_builder.cpp",
|
||||
"native/ops/div_builder.cpp",
|
||||
"native/ops/eltwise_builder.cpp",
|
||||
"native/ops/expandims_builder.cpp",
|
||||
"native/ops/fullconnection_builder.cpp",
|
||||
"native/ops/maxpool_builder.cpp",
|
||||
"native/ops/slice_builder.cpp",
|
||||
"native/ops/softmax_builder.cpp",
|
||||
"native/ops/space_to_batch_nd_builder.cpp",
|
||||
"native/ops/split_builder.cpp",
|
||||
"native/ops/sqrt_builder.cpp",
|
||||
"native/ops/squared_difference_builder.cpp",
|
||||
"native/ops/squeeze_builder.cpp",
|
||||
"native/ops/stack_builder.cpp",
|
||||
"native/ops/strided_slice_builder.cpp",
|
||||
"native/ops/sub_builder.cpp",
|
||||
"native/ops/tanh_builder.cpp",
|
||||
"native/ops/tile_builder.cpp",
|
||||
"native/ops/top_k_builder.cpp",
|
||||
"native/ops/transpose_builder.cpp",
|
||||
"native/ops/unsqueeze_builder.cpp",
|
||||
"native/ops/batchnorm_builder.cpp",
|
||||
"native/ops/fill_builder.cpp",
|
||||
"native/ops/matmul_builder.cpp",
|
||||
"native/ops/gather_builder.cpp",
|
||||
"native/ops/gelu_builder.cpp",
|
||||
"native/ops/hswish_builder.cpp",
|
||||
"native/ops/layernorm_builder.cpp",
|
||||
"native/ops/maximum_builder.cpp",
|
||||
"native/ops/lessequal_builder.cpp",
|
||||
"native/ops/mul_builder.cpp",
|
||||
"native/ops/onehot_builder.cpp",
|
||||
"native/ops/pad_builder.cpp",
|
||||
"native/ops/pow_builder.cpp",
|
||||
"native/ops/prelu_builder.cpp",
|
||||
"native/ops/quant_dtype_cast_builder.cpp",
|
||||
"native/ops/reduceall_builder.cpp",
|
||||
"native/ops/reducemean_builder.cpp",
|
||||
"native/ops/reduceprod_builder.cpp",
|
||||
"native/ops/relu_builder.cpp",
|
||||
"native/ops/relu6_builder.cpp",
|
||||
"native/ops/reshape_builder.cpp",
|
||||
"native/ops/resize_bilinear_builder.cpp",
|
||||
"native/ops/rsqrt_builder.cpp",
|
||||
"native/ops/scale_builder.cpp",
|
||||
"native/ops/shape_builder.cpp",
|
||||
"native/ops/sigmoid_builder.cpp",
|
||||
]
|
||||
|
||||
ohos_shared_library("libneural_network_runtime") {
|
||||
sources = nnrt_sources
|
||||
sources += ops_sources
|
||||
include_dirs = [
|
||||
"//commonlibrary/c_utils/base/include",
|
||||
"//drivers/hdf_core/adapter/uhdf/posix/include",
|
||||
"//drivers/hdf_core/adapter/uhdf2/include/hdi",
|
||||
"//drivers/hdf_core/adapter/uhdf2/ipc/include",
|
||||
"//drivers/hdf_core/framework/include/core",
|
||||
"//drivers/hdf_core/framework/include/utils",
|
||||
"//drivers/hdf_core/framework/core/common/include/host",
|
||||
"//foundation/ai/neural_network_runtime",
|
||||
"//foundation/communication/ipc/interfaces/innerkits/ipc_core/include",
|
||||
"//third_party/googletest/googletest/include/gtest",
|
||||
"//third_party/mindspore/mindspore/lite/mindir/include"
|
||||
]
|
||||
|
||||
install_images = [
|
||||
"system",
|
||||
"updater"
|
||||
]
|
||||
|
||||
public_configs = [ ":nnrt_config" ]
|
||||
|
||||
external_deps = [
|
||||
"hilog_native:libhilog",
|
||||
"hitrace_native:libhitracechain",
|
||||
"c_utils:utils",
|
||||
"hdf_core:libhdf_utils",
|
||||
"drivers_interface_nnrt:libnnrt_proxy_1.0",
|
||||
"mindspore:mindir"
|
||||
]
|
||||
|
||||
subsystem_name = "ai"
|
||||
part_name = "neural_network_runtime"
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "compilation.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdio>
|
||||
#include <sys/types.h>
|
||||
#include <fstream>
|
||||
#include <climits>
|
||||
|
||||
#include "common/utils.h"
|
||||
#include "common/scoped_trace.h"
|
||||
#include "validation.h"
|
||||
#include "device_manager.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
constexpr int MAX_MODEL_SIZE = 200 * 1024 * 1024; // 200MB
|
||||
constexpr int OCT_UNIT = 8;
|
||||
constexpr int NULL_PTR_LENGTH = 0;
|
||||
constexpr int NUMBER_CACHE_INFO_MEMBERS = 3;
|
||||
|
||||
// CRC16 Table is created based on the Polynomial of G(x) = x^16 + x^12 + x^15 + 1 and
|
||||
// CRC register initialization value of "0" (0x0000)
|
||||
static const unsigned short CRC16_TAB[256] = {
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
|
||||
};
|
||||
|
||||
Compilation::Compilation(const InnerModel* innerModel)
|
||||
: m_liteGraph(innerModel->GetLiteGraphs()),
|
||||
m_inputTensors(innerModel->GetInputTensors()),
|
||||
m_outputTensors(innerModel->GetOutputTensors()) {}
|
||||
|
||||
OH_NN_ReturnCode Compilation::SetDevice(size_t deviceId)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("Cannot set deviceId after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
auto& deviceManager = DeviceManager::GetInstance();
|
||||
std::shared_ptr<Device> availableDevice = deviceManager.GetDevice(deviceId);
|
||||
if (availableDevice == nullptr) {
|
||||
LOGE("[Compilation] DeviceId does not exist, deviceId=%zu", deviceId);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<bool> supportedList;
|
||||
OH_NN_ReturnCode ret = availableDevice->GetSupportedOperation(m_liteGraph, supportedList);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] SetDevice failed, error happened when getting supported operation.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (bool isSupport : supportedList) {
|
||||
if (!isSupport) {
|
||||
LOGE("[Compilation] SetDevice failed, current device not support the model, device id: %zu.", deviceId);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
bool supportDynamic;
|
||||
ret = availableDevice->IsDynamicInputSupported(supportDynamic);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] SetDevice failed, error happened when checking whether device supports dynamic input.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (IsDynamicShape() && (!supportDynamic)) {
|
||||
LOGE("[Compilation] SetDevice failed."
|
||||
"The device does not support dynamic shape inputs, but the model has dynamic inputs.");
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
m_device = availableDevice;
|
||||
m_deviceId = deviceId;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::SetCacheDir(const std::string& cacheModelPath, uint32_t version)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("Cannot set cache after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_device == nullptr) {
|
||||
LOGE("The parameter of m_device is nullptr, please call SetDevice function before calling SetCacheDir.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
bool isSupportedCache {false};
|
||||
OH_NN_ReturnCode ret = m_device->IsModelCacheSupported(isSupportedCache);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Fail to query whether the device is available to save cache model.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!isSupportedCache) {
|
||||
LOGE("[Compilation] The device is unavailable to save cache model.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
char realPathRes[PATH_MAX];
|
||||
const char* filePath = realpath(cacheModelPath.c_str(), realPathRes);
|
||||
if (filePath == nullptr) {
|
||||
LOGE("[Compilation] The cache model path is invalid.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
struct stat fileInfo;
|
||||
if (stat(filePath, &fileInfo) != 0) {
|
||||
LOGE("[Compilation] The cache directory does not exist or cannot be accessed.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!(fileInfo.st_mode & S_IFDIR)) {
|
||||
LOGE("[Compilation] The cache model path is not a directory.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_cachePath = (std::string)filePath + "/";
|
||||
m_version = version;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::SetPerformance(OH_NN_PerformanceMode performance)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Compilation] Cannot set performance after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_device == nullptr) {
|
||||
LOGE("Cannot set performance before set device, please set device first");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
bool isSupportedPerformance {false};
|
||||
OH_NN_ReturnCode ret = m_device->IsPerformanceModeSupported(isSupportedPerformance);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!isSupportedPerformance) {
|
||||
LOGE("[Compilation] This device %zu is not support performance setting.", m_deviceId);
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (!Validation::ValidatePerformanceMode(performance)) {
|
||||
LOGE("[Compilation] SetPerformance passed invalid performance=%d", performance);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_performance = performance;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::SetPriority(OH_NN_Priority priority)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Compilation] Cannot set priority after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_device == nullptr) {
|
||||
LOGE("Cannot set priority before set device, please set device first");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
bool isSupportedPriority {false};
|
||||
OH_NN_ReturnCode ret = m_device->IsPrioritySupported(isSupportedPriority);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!isSupportedPriority) {
|
||||
LOGE("[Compilation] This device %zu is not support priority setting.", m_deviceId);
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (!Validation::ValidatePriority(priority)) {
|
||||
LOGE("[Compilation] SetPriority passed invalid priority=%d", priority);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_priority = priority;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::SetEnableFp16(bool isFp16)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Compilation] Cannot enable float16 after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_device == nullptr) {
|
||||
LOGE("Cannot set enable fp16 before set device, please set device first");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
bool isSupportedFp16 {false};
|
||||
OH_NN_ReturnCode ret = m_device->IsFloat16PrecisionSupported(isSupportedFp16);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Call device %zu failed.", m_deviceId);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!isSupportedFp16) {
|
||||
LOGE("[Compilation] This device %zu is not support float16 precision setting.", m_deviceId);
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
m_enableFp16 = isFp16;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
unsigned short Compilation::GetCrc16(const unsigned char* buffer, size_t length) const
|
||||
{
|
||||
unsigned short crc16 = 0;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
uint8_t tableIndex = ((crc16 >> OCT_UNIT) ^ *buffer++) & 0x00ff;
|
||||
crc16 = (crc16 << OCT_UNIT) ^ CRC16_TAB[tableIndex];
|
||||
}
|
||||
return crc16;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::GenerateCacheInfo(uint32_t cacheSize, std::unique_ptr<uint64_t[]>& cacheInfo) const
|
||||
{
|
||||
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
|
||||
std::ofstream cacheInfoStream(cacheInfoPath, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
if (cacheInfoStream.fail()) {
|
||||
LOGE("[Compilation] Model cache info file is invalid.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
if (!cacheInfoStream.write(reinterpret_cast<const char*>(cacheInfo.get()), cacheSize)) {
|
||||
LOGE("[Compilation] Fail to write cache info.");
|
||||
cacheInfoStream.close();
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
cacheInfoStream.close();
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::GenerateCacheModel(size_t cacheNumber, std::unique_ptr<uint64_t[]>& cacheInfo,
|
||||
std::vector<ModelBuffer> modelBuffer) const
|
||||
{
|
||||
auto cacheInfoPtr = cacheInfo.get();
|
||||
*cacheInfoPtr++ = static_cast<uint64_t>(cacheNumber);
|
||||
*cacheInfoPtr++ = static_cast<uint64_t>(m_version);
|
||||
*cacheInfoPtr++ = static_cast<uint64_t>(m_deviceId);
|
||||
for (uint32_t i = 0; i < cacheNumber; ++i) {
|
||||
std::string cacheModelFile = m_cachePath + std::to_string(i) + ".nncache";
|
||||
std::ofstream cacheModelStream(cacheModelFile, std::ios::binary | std::ios::out | std::ios::trunc);
|
||||
if (cacheModelStream.fail()) {
|
||||
LOGE("[Compilation] Model cache file is invalid.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
uint64_t checkSum = static_cast<uint64_t>(GetCrc16(static_cast<const unsigned char*>(modelBuffer[i].buffer),
|
||||
modelBuffer[i].length));
|
||||
*cacheInfoPtr++ = checkSum;
|
||||
if (!cacheModelStream.write(static_cast<const char*>(modelBuffer[i].buffer), modelBuffer[i].length)) {
|
||||
LOGE("[Compilation] Fail to write cache model.");
|
||||
cacheModelStream.close();
|
||||
return OH_NN_FAILED;
|
||||
};
|
||||
|
||||
cacheModelStream.close();
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::GenerateCacheFiles(const std::vector<ModelBuffer>& modelBuffer) const
|
||||
{
|
||||
const size_t cacheNumber = modelBuffer.size();
|
||||
uint32_t cacheSize = NUMBER_CACHE_INFO_MEMBERS + cacheNumber;
|
||||
std::unique_ptr<uint64_t[]> cacheInfo = std::make_unique<uint64_t[]>(cacheSize);
|
||||
if (cacheInfo == nullptr) {
|
||||
LOGE("Fail to create cacheInfo instance.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = GenerateCacheModel(cacheNumber, cacheInfo, modelBuffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t infoCharNumber = cacheSize * sizeof(uint64_t);
|
||||
ret = GenerateCacheInfo(infoCharNumber, cacheInfo);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::GetCacheFileLength(std::ifstream& ifs, int& fsize) const
|
||||
{
|
||||
ifs.seekg(0, std::ios::end);
|
||||
if (!ifs.good()) {
|
||||
LOGE("[Compilation] Fail to set the position of the next character to be extracted from the input stream.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
int handleValue = ifs.tellg();
|
||||
if (handleValue == -1) {
|
||||
LOGE("[Compilation] Unable to get position of the input stream.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
if ((handleValue > MAX_MODEL_SIZE) || (handleValue == NULL_PTR_LENGTH)) {
|
||||
LOGE("[Compilation] Unable to read huge or empty input stream, get cache file size=%d", handleValue);
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
fsize = handleValue;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::ReadCacheModelFile(const std::string& file, ModelBuffer& modelBuffer) const
|
||||
{
|
||||
std::ifstream ifs(file.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!ifs) {
|
||||
LOGE("[Compilation] Fail to open cache file.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
int fsize {-1};
|
||||
OH_NN_ReturnCode ret = GetCacheFileLength(ifs, fsize);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
ifs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ifs.seekg(0, std::ios::beg);
|
||||
if (!ifs.good()) {
|
||||
LOGE("[Compilation] Fail to set the position of the next character to be extracted"
|
||||
"from the cache model stream.");
|
||||
ifs.close();
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
char* ptr = static_cast<char*>(m_device->AllocateBuffer(fsize));
|
||||
if (ptr == nullptr) {
|
||||
LOGE("[Compilation] Fail to create file buffer.");
|
||||
ifs.close();
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
ifs.read(ptr, fsize);
|
||||
if (!ifs.good()) {
|
||||
LOGE("[Compilation] Fail to read the characters from the cache model stream.");
|
||||
ifs.close();
|
||||
m_device->ReleaseBuffer(ptr);
|
||||
ptr = nullptr;
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
modelBuffer.buffer = ptr;
|
||||
modelBuffer.length = fsize;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::CheckCacheInfo(ModelCacheInfo& modelCacheInfo, const std::string& cacheInfoPath) const
|
||||
{
|
||||
std::ifstream infoCacheFile(cacheInfoPath.c_str(), std::ios::in | std::ios::binary);
|
||||
if (!infoCacheFile) {
|
||||
LOGE("[Compilation] Openning cache info file failed.");
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
int charNumber = NUMBER_CACHE_INFO_MEMBERS * sizeof(uint64_t);
|
||||
if (!infoCacheFile.read((char*)&(modelCacheInfo), charNumber)) {
|
||||
LOGE("[Compilation] Fail to get the content of info cache file.");
|
||||
infoCacheFile.close();
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
// modelCacheInfo.deviceId type is int64_t,
|
||||
// it is transformed from size_t value, so the transform here will not truncate value.
|
||||
size_t deviceId = static_cast<size_t>(modelCacheInfo.deviceId);
|
||||
if (deviceId != m_deviceId) {
|
||||
LOGE("[Compilation] The deviceId=%zu in the cache files is different from current deviceId=%zu,"
|
||||
"please change the cache directory or current deviceId.", deviceId, m_deviceId);
|
||||
infoCacheFile.close();
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> modelCheckSum;
|
||||
modelCheckSum.resize(modelCacheInfo.fileNumber);
|
||||
modelCacheInfo.modelCheckSum.resize(modelCacheInfo.fileNumber);
|
||||
if (!infoCacheFile.read((char*)&modelCheckSum[0], modelCacheInfo.fileNumber * sizeof(uint64_t))) {
|
||||
LOGE("[Compilation] The info cache file has been changed.");
|
||||
infoCacheFile.close();
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < modelCacheInfo.fileNumber; ++i) {
|
||||
modelCacheInfo.modelCheckSum[i] = static_cast<unsigned short>(modelCheckSum[i]);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::RemoveCacheFiles(uint32_t fileNumber) const
|
||||
{
|
||||
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
|
||||
if (remove(cacheInfoPath.c_str()) == -1) {
|
||||
LOGE("[Compilation] Fail to remove the file %s, please delete the file manually.", cacheInfoPath.c_str());
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
LOGI("[Compilation] Succeed to remove the file cache_info.nncach.");
|
||||
|
||||
for (uint32_t i = 0; i < fileNumber; ++i) {
|
||||
std::string fileName = std::to_string(i) + ".nncache";
|
||||
std::string cacheModelPath = m_cachePath + fileName;
|
||||
if (access(cacheModelPath.c_str(), 0) != 0) {
|
||||
LOGW("[Compilation] The file %s does not exist, no need to delete the file.", cacheModelPath.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (remove(cacheModelPath.c_str()) == -1) {
|
||||
LOGE("[Compilation] Fail to remove the file %s, please delete the file manually.", cacheModelPath.c_str());
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
LOGI("[Compilation] Succeed to remove the file %s", cacheModelPath.c_str());
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::CheckCacheModel(const ModelCacheInfo& modelCacheInfo,
|
||||
std::vector<ModelBuffer>& modelBuffers) const
|
||||
{
|
||||
for (uint32_t i = 0; i < modelCacheInfo.fileNumber; ++i) {
|
||||
std::string cacheModelPath = m_cachePath + std::to_string(i) + ".nncache";
|
||||
if (access(cacheModelPath.c_str(), 0) != 0) {
|
||||
LOGE("[Compilation] The cache model file %s does not exist.", cacheModelPath.c_str());
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
ModelBuffer modelBuffer;
|
||||
OH_NN_ReturnCode ret = ReadCacheModelFile(cacheModelPath, modelBuffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Read cache model file failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (GetCrc16(static_cast<const unsigned char*>(modelBuffer.buffer),
|
||||
modelBuffer.length) != modelCacheInfo.modelCheckSum[i]) {
|
||||
LOGE("[Compilation] The cache model file %s has been changed.", cacheModelPath.c_str());
|
||||
return OH_NN_INVALID_FILE;
|
||||
}
|
||||
|
||||
modelBuffers.emplace_back(std::move(modelBuffer));
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::NormalBuild(std::shared_ptr<PreparedModel>& preparedModel)
|
||||
{
|
||||
ModelConfig config {m_enableFp16, m_performance, m_priority};
|
||||
OH_NN_ReturnCode ret = m_device->PrepareModel(m_liteGraph, config, preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Preparing model failed when normally building.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_executionPlan = CreateSharedPtr<ExecutionPlan>(preparedModel, m_device);
|
||||
if (m_executionPlan == nullptr) {
|
||||
LOGE("Fail to create ExecutionPlan instance.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::GenCacheBuild(std::shared_ptr<PreparedModel>& preparedModel)
|
||||
{
|
||||
OH_NN_ReturnCode ret = NormalBuild(preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Preparing model failed when generating cache.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<ModelBuffer> modelBuffers;
|
||||
ret = preparedModel->ExportModelCache(modelBuffers);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Export model cache failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GenerateCacheFiles(modelBuffers);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Generate cache files failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOGI("[Compilation] Export model cache successfully.");
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::ReGenCacheBuild(uint32_t fileNumber, std::shared_ptr<PreparedModel>& preparedModel)
|
||||
{
|
||||
OH_NN_ReturnCode ret = RemoveCacheFiles(fileNumber);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GenCacheBuild(preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Generating cache building failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOGI("[Compilation] Update model cache successfully.");
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::LoadCacheBuild(std::shared_ptr<PreparedModel>& preparedModel,
|
||||
const ModelCacheInfo& cacheInfo)
|
||||
{
|
||||
std::vector<ModelBuffer> modelBuffers;
|
||||
OH_NN_ReturnCode ret = CheckCacheModel(cacheInfo, modelBuffers);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Checking cache model failed.");
|
||||
for (size_t i = 0; i < modelBuffers.size(); ++i) {
|
||||
m_device->ReleaseBuffer(modelBuffers[i].buffer);
|
||||
modelBuffers[i].buffer = nullptr;
|
||||
modelBuffers[i].length = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ModelConfig config {m_enableFp16, m_performance, m_priority};
|
||||
ret = m_device->PrepareModelFromModelCache(modelBuffers, config, preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Compilation] Preparing model from cache failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOGI("[Compilation] Load cache successfully.");
|
||||
|
||||
m_executionPlan = CreateSharedPtr<ExecutionPlan>(preparedModel, m_device);
|
||||
if (m_executionPlan == nullptr) {
|
||||
LOGE("Fail to create ExecutionPlan instance.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::InnerBuild()
|
||||
{
|
||||
OH_NN_ReturnCode ret;
|
||||
std::shared_ptr<PreparedModel> preparedModel;
|
||||
if (m_cachePath.empty()) {
|
||||
ret = NormalBuild(preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Fail to normally build.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
std::string cacheInfoPath = m_cachePath + "cache_info.nncache";
|
||||
if (access(cacheInfoPath.c_str(), 0) != 0) {
|
||||
ret = GenCacheBuild(preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Fail to build in generating cache mode.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
ModelCacheInfo cacheInfo;
|
||||
ret = CheckCacheInfo(cacheInfo, cacheInfoPath);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (m_version > cacheInfo.version) {
|
||||
ret = ReGenCacheBuild(cacheInfo.fileNumber, preparedModel);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
if (m_version < cacheInfo.version) {
|
||||
LOGE("[Compilation] The current version is lower than the cache files, please set a higher version.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
ret = LoadCacheBuild(preparedModel, cacheInfo);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Compilation::Build()
|
||||
{
|
||||
NNRT_TRACE_NAME("Compilation");
|
||||
if (m_isBuild) {
|
||||
LOGE("[Compilation] Cannot enable float16 after compilation finish.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_device == nullptr) {
|
||||
LOGE("The parameter of m_device is nullptr, please call SetDevice function before build model.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = InnerBuild();
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
std::shared_ptr<ExecutionPlan> Compilation::GetExecutionPlan() const
|
||||
{
|
||||
return m_executionPlan;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NNTensor>> Compilation::GetInputTensors() const
|
||||
{
|
||||
return m_inputTensors;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NNTensor>> Compilation::GetOutputTensors() const
|
||||
{
|
||||
return m_outputTensors;
|
||||
}
|
||||
|
||||
bool Compilation::IsBuild() const
|
||||
{
|
||||
return m_isBuild;
|
||||
}
|
||||
|
||||
bool Compilation::IsDynamicShape() const
|
||||
{
|
||||
for (size_t i = 0; i < m_inputTensors.size(); ++i) {
|
||||
if (m_inputTensors[i]->IsDynamicShape()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_COMPILATION_H
|
||||
#define NEURAL_NETWORK_RUNTIME_COMPILATION_H
|
||||
|
||||
#include "inner_model.h"
|
||||
#include "execution_plan.h"
|
||||
|
||||
#include "interfaces/oem/cpp_api/device.h"
|
||||
#include "interfaces/oem/cpp_api/cpp_type.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
struct ModelCacheInfo {
|
||||
uint64_t fileNumber = 0;
|
||||
uint64_t version = 0;
|
||||
uint64_t deviceId = 0;
|
||||
std::vector<unsigned short> modelCheckSum;
|
||||
};
|
||||
|
||||
class Compilation {
|
||||
public:
|
||||
explicit Compilation(const InnerModel* innerModel);
|
||||
|
||||
OH_NN_ReturnCode SetDevice(size_t deviceId);
|
||||
OH_NN_ReturnCode SetCacheDir(const std::string& cacheModelPath, uint32_t version);
|
||||
OH_NN_ReturnCode SetPerformance(OH_NN_PerformanceMode performance);
|
||||
OH_NN_ReturnCode SetPriority(OH_NN_Priority priority);
|
||||
OH_NN_ReturnCode SetEnableFp16(bool isFp16);
|
||||
|
||||
OH_NN_ReturnCode Build();
|
||||
|
||||
bool IsBuild() const;
|
||||
bool IsDynamicShape() const;
|
||||
std::vector<std::shared_ptr<NNTensor>>GetInputTensors() const;
|
||||
std::vector<std::shared_ptr<NNTensor>>GetOutputTensors() const;
|
||||
std::shared_ptr<ExecutionPlan> GetExecutionPlan() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<mindspore::lite::LiteGraph> m_liteGraph {nullptr};
|
||||
OH_NN_Priority m_priority {OH_NN_PRIORITY_NONE};
|
||||
OH_NN_PerformanceMode m_performance {OH_NN_PERFORMANCE_NONE};
|
||||
bool m_enableFp16 {false};
|
||||
std::shared_ptr<Device> m_device {nullptr};
|
||||
std::string m_cachePath;
|
||||
uint32_t m_version {0};
|
||||
size_t m_deviceId {0};
|
||||
bool m_isBuild {false};
|
||||
std::shared_ptr<ExecutionPlan> m_executionPlan {nullptr};
|
||||
std::vector<std::shared_ptr<NNTensor>> m_inputTensors;
|
||||
std::vector<std::shared_ptr<NNTensor>> m_outputTensors;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode GenerateCacheFiles(const std::vector<ModelBuffer>& modelBuffer) const;
|
||||
OH_NN_ReturnCode GenerateCacheModel(size_t cacheNumber, std::unique_ptr<uint64_t[]>& cacheInfo,
|
||||
std::vector<ModelBuffer> modelBuffer) const;
|
||||
OH_NN_ReturnCode GenerateCacheInfo(uint32_t cacheSize, std::unique_ptr<uint64_t[]>& cacheInfo) const;
|
||||
OH_NN_ReturnCode CheckCacheInfo(ModelCacheInfo& modelCacheInfo, const std::string& cacheInfoPath) const;
|
||||
OH_NN_ReturnCode ReadCacheModelFile(const std::string& file, ModelBuffer& modelBuffer) const;
|
||||
OH_NN_ReturnCode RemoveCacheFiles(uint32_t fileNumber) const;
|
||||
unsigned short GetCrc16(const unsigned char* buffer, size_t length) const;
|
||||
OH_NN_ReturnCode CheckCacheModel(const ModelCacheInfo& modelCacheInfo,
|
||||
std::vector<ModelBuffer>& modelBuffers) const;
|
||||
OH_NN_ReturnCode NormalBuild(std::shared_ptr<PreparedModel>& preparedModel);
|
||||
OH_NN_ReturnCode GenCacheBuild(std::shared_ptr<PreparedModel>& preparedModel);
|
||||
OH_NN_ReturnCode ReGenCacheBuild(uint32_t fileNumber, std::shared_ptr<PreparedModel>& preparedModel);
|
||||
OH_NN_ReturnCode LoadCacheBuild(std::shared_ptr<PreparedModel>& preparedModel, const ModelCacheInfo& cacheInfo);
|
||||
OH_NN_ReturnCode InnerBuild();
|
||||
OH_NN_ReturnCode GetCacheFileLength(std::ifstream& ifs, int& fsize) const;
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_COMPILATION_H
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "device_manager.h"
|
||||
|
||||
#include "hdi_interfaces.h"
|
||||
#include "hdi_device.h"
|
||||
#include "common/log.h"
|
||||
#include "common/utils.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
const std::vector<size_t>& DeviceManager::GetAllDeviceId()
|
||||
{
|
||||
m_tmpDeviceIds.clear();
|
||||
std::shared_ptr<Device> device {nullptr};
|
||||
for (auto iter = m_devices.begin(); iter != m_devices.end(); ++iter) {
|
||||
device = iter->second;
|
||||
if (!IsValidDevice(device)) {
|
||||
continue;
|
||||
}
|
||||
m_tmpDeviceIds.emplace_back(iter->first);
|
||||
}
|
||||
return m_tmpDeviceIds;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> DeviceManager::GetDevice(size_t deviceId) const
|
||||
{
|
||||
auto iter = m_devices.find(deviceId);
|
||||
if (iter == m_devices.end()) {
|
||||
LOGE("DeviceId is not found, deviceId=%zu", deviceId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
const std::string& DeviceManager::GetDeviceName(size_t deviceId)
|
||||
{
|
||||
m_tmpDeviceName.clear();
|
||||
auto iter = m_devices.find(deviceId);
|
||||
if (iter == m_devices.end()) {
|
||||
LOGE("DeviceId is not found, deviceId=%zu", deviceId);
|
||||
return m_tmpDeviceName;
|
||||
}
|
||||
|
||||
std::string deviceName;
|
||||
auto ret = iter->second->GetDeviceName(deviceName);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Get device name failed.");
|
||||
return m_tmpDeviceName;
|
||||
}
|
||||
|
||||
std::string vendorName;
|
||||
ret = iter->second->GetVendorName(vendorName);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Get vendor name failed.");
|
||||
return m_tmpDeviceName;
|
||||
}
|
||||
|
||||
m_tmpDeviceName = GenUniqueName(deviceName, vendorName);
|
||||
return m_tmpDeviceName;
|
||||
}
|
||||
|
||||
std::string DeviceManager::GenUniqueName(const std::string& deviceName, const std::string& vendorName) const
|
||||
{
|
||||
return deviceName + "_" + vendorName;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode DeviceManager::RegisterDevice(std::function<std::shared_ptr<Device>()> creator)
|
||||
{
|
||||
auto regDevice = creator();
|
||||
if (regDevice == nullptr) {
|
||||
LOGE("Cannot create device, register device failed.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!IsValidDevice(regDevice)) {
|
||||
LOGE("Device is not avaliable.");
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
|
||||
std::string deviceName;
|
||||
auto ret = regDevice->GetDeviceName(deviceName);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Get device name failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string vendorName;
|
||||
ret = regDevice->GetVendorName(vendorName);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Get vendor name failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::lock_guard<std::mutex> lock(m_mtx);
|
||||
std::string uniqueName = GenUniqueName(deviceName, vendorName);
|
||||
auto setResult = m_uniqueName.emplace(uniqueName);
|
||||
if (!setResult.second) {
|
||||
LOGE("Device already exists, cannot register again. deviceName=%s, vendorName=%s",
|
||||
deviceName.c_str(), vendorName.c_str());
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
m_devices.emplace(std::hash<std::string>{}(uniqueName), regDevice);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void DeviceManager::DiscoverHDIDevices()
|
||||
{
|
||||
// only one device from HDI now.
|
||||
OHOS::sptr<V1_0::INnrtDevice> iDevice = V1_0::INnrtDevice::Get();
|
||||
if (iDevice == nullptr) {
|
||||
LOGW("Get HDI device failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string deviceName;
|
||||
std::string vendorName;
|
||||
auto hdiRet = iDevice->GetDeviceName(deviceName);
|
||||
if (hdiRet != HDF_SUCCESS) {
|
||||
LOGW("Get device name failed. ErrorCode=%d", hdiRet);
|
||||
return;
|
||||
}
|
||||
hdiRet = iDevice->GetVendorName(vendorName);
|
||||
if (hdiRet != HDF_SUCCESS) {
|
||||
LOGW("Get vendor name failed. ErrorCode=%d", hdiRet);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string uniqueName = GenUniqueName(deviceName, vendorName);
|
||||
const std::lock_guard<std::mutex> lock(m_mtx);
|
||||
auto setResult = m_uniqueName.emplace(uniqueName);
|
||||
if (!setResult.second) {
|
||||
LOGW("Device already exists, cannot register again. deviceName=%s, vendorName=%s",
|
||||
deviceName.c_str(), vendorName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> device = CreateSharedPtr<HDIDevice>(iDevice);
|
||||
if (device == nullptr) {
|
||||
LOGW("Failed to register device, because fail to create device instance.");
|
||||
return;
|
||||
}
|
||||
m_devices.emplace(std::hash<std::string>{}(uniqueName), device);
|
||||
}
|
||||
|
||||
bool DeviceManager::IsValidDevice(std::shared_ptr<Device> device) const
|
||||
{
|
||||
DeviceStatus status {DeviceStatus::UNKNOWN};
|
||||
auto ret = device->GetDeviceStatus(status);
|
||||
if (ret != OH_NN_SUCCESS || status == DeviceStatus::UNKNOWN || status == DeviceStatus::OFFLINE) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "interfaces/oem/cpp_api/device.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime_type.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class DeviceManager {
|
||||
public:
|
||||
const std::vector<size_t>& GetAllDeviceId();
|
||||
std::shared_ptr<Device> GetDevice(size_t deviceId) const;
|
||||
const std::string& GetDeviceName(size_t deviceId);
|
||||
|
||||
// register device from C++ API
|
||||
OH_NN_ReturnCode RegisterDevice(std::function<std::shared_ptr<Device>()> creator);
|
||||
|
||||
static DeviceManager& GetInstance()
|
||||
{
|
||||
static DeviceManager instance;
|
||||
instance.DiscoverHDIDevices();
|
||||
return instance;
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceManager() = default;
|
||||
DeviceManager(const DeviceManager&) = delete;
|
||||
DeviceManager& operator=(const DeviceManager&) = delete;
|
||||
|
||||
void DiscoverHDIDevices();
|
||||
std::string GenUniqueName(const std::string& deviceName, const std::string& vendorName) const;
|
||||
bool IsValidDevice(std::shared_ptr<Device> device) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> m_uniqueName;
|
||||
// key is device id, it is the unique number.
|
||||
std::unordered_map<size_t, std::shared_ptr<Device>> m_devices;
|
||||
std::mutex m_mtx;
|
||||
|
||||
std::string m_tmpDeviceName;
|
||||
std::vector<size_t> m_tmpDeviceIds;
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_DEVICE_MANAGER_H
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "interfaces/oem/cpp_api/device_registrar.h"
|
||||
|
||||
#include "device_manager.h"
|
||||
#include "common/log.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
DeviceRegistrar::DeviceRegistrar(const CreateDevice creator)
|
||||
{
|
||||
auto& deviceManager = DeviceManager::GetInstance();
|
||||
auto ret = deviceManager.RegisterDevice(creator);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGW("Register device failed. ErrorCode=%d", ret);
|
||||
}
|
||||
}
|
||||
} // NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "execution_plan.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/log.h"
|
||||
#include "interfaces/oem/cpp_api/cpp_type.h"
|
||||
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
OH_NN_ReturnCode ExecutionPlan::Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
|
||||
std::vector<std::shared_ptr<NNTensor>>& outputTensors)
|
||||
{
|
||||
OH_NN_ReturnCode ret {OH_NN_FAILED};
|
||||
IOTensor tensor;
|
||||
std::vector<IOTensor> inputIOTensors;
|
||||
size_t inputSize = inputTensors.size();
|
||||
size_t outputSize = outputTensors.size();
|
||||
for (size_t i = 0; i < inputSize; ++i) {
|
||||
inputTensors[i]->ConvertToIOTensor(tensor);
|
||||
inputIOTensors.emplace_back(std::move(tensor));
|
||||
}
|
||||
|
||||
std::vector<IOTensor> outputIOTensors;
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
outputTensors[i]->ConvertToIOTensor(tensor);
|
||||
outputIOTensors.emplace_back(std::move(tensor));
|
||||
}
|
||||
|
||||
std::vector<std::vector<int32_t>> outputsDims;
|
||||
std::vector<bool> isSufficientDataBuffer;
|
||||
ret = m_preparedModel->Run(inputIOTensors, outputIOTensors, outputsDims, isSufficientDataBuffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("PrepardModel Run() failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Check if the output buffer is sufficient
|
||||
bool bufferFailed {false};
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
if (!isSufficientDataBuffer[i]) {
|
||||
// Print all output indices with insufficient buffer, don't return until traversing all outputs.
|
||||
LOGE("Run failed, Output %zu does not have enough buffer to store the data.", i);
|
||||
bufferFailed = true;
|
||||
}
|
||||
}
|
||||
if (bufferFailed) {
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
// Set the output NNTensor's dimensions from output IOTensor if it is dynamic.
|
||||
// NNTensor::SetDimensions will check if the tensor buffer is enough for the new dimensions.
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
ret = outputTensors[i]->SetDimensions(outputsDims[i]);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Run failed, error happened when setting output tensor's dimensions, output id: %zu.", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Device> ExecutionPlan::GetInputDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Device> ExecutionPlan::GetOutputDevice() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
} // NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_EXECUTION_PLAN_H
|
||||
#define NEURAL_NETWORK_RUNTIME_EXECUTION_PLAN_H
|
||||
|
||||
#include "frameworks/native/nn_tensor.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime_type.h"
|
||||
#include "interfaces/oem/cpp_api/prepared_model.h"
|
||||
#include "interfaces/oem/cpp_api/device.h"
|
||||
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class ExecutionPlan {
|
||||
public:
|
||||
ExecutionPlan(std::shared_ptr<PreparedModel> preparedModel, std::shared_ptr<Device> device)
|
||||
: m_preparedModel(preparedModel),
|
||||
m_device(device) {};
|
||||
|
||||
OH_NN_ReturnCode Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
|
||||
std::vector<std::shared_ptr<NNTensor>>& outputTensors);
|
||||
|
||||
std::shared_ptr<Device> GetInputDevice() const;
|
||||
std::shared_ptr<Device> GetOutputDevice() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<PreparedModel> m_preparedModel {nullptr};
|
||||
std::shared_ptr<Device> m_device {nullptr};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif
|
||||
@@ -0,0 +1,555 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "executor.h"
|
||||
|
||||
#include "securec.h"
|
||||
|
||||
#include "common/utils.h"
|
||||
#include "common/scoped_trace.h"
|
||||
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
Executor::Executor(const Compilation* compilation)
|
||||
: m_modelInputs(compilation->GetInputTensors()),
|
||||
m_modelOutputs(compilation->GetOutputTensors()),
|
||||
m_executionPlan(compilation->GetExecutionPlan()) {}
|
||||
|
||||
OH_NN_ReturnCode Executor::BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
|
||||
std::shared_ptr<NNTensor> inputTensor) const
|
||||
{
|
||||
// Note: inputs have only shapes info.
|
||||
if (index >= m_modelInputs.size()) {
|
||||
LOGE("BuildInputTensor failed, input index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Build a tensor from nnTensor.
|
||||
auto ret = inputTensor->BuildFromOHNNTensor(nnTensor);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildInputTensor failed, please check input nnTensor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (inputTensor->IsDynamicShape()) {
|
||||
LOGE("BuildInputTensor failed, input nnTensor should has certain dimensions which cannot contain -1.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!m_modelInputs[index]->CompareAttribute(*inputTensor)) {
|
||||
LOGE("BuildInputTensor failed, input has different attributes from the one in the constructed model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
inputTensor->SetName(m_modelInputs[index]->GetName());
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::SetInputTensorWithCurrentBuffer(uint32_t index,
|
||||
std::shared_ptr<NNTensor> inputTensor,
|
||||
const void* buffer,
|
||||
size_t dataLength,
|
||||
size_t curBufferLength)
|
||||
{
|
||||
void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
|
||||
errno_t status = memcpy_s(curBuffer, dataLength, buffer, dataLength);
|
||||
// Current buffer inside m_inputTensors is managed by executor, no need to release if memcpy failed.
|
||||
if (status != EOK) {
|
||||
LOGE("SetInputTensorWithCurrentBuffe failed, copy data from user buffer to device buffer failed. "
|
||||
"Error code: %d.", status);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
// Set the new tensor with the buffer of current tensor
|
||||
inputTensor->SetBuffer(curBuffer, curBufferLength);
|
||||
|
||||
// The memory is reused here. Thus, current tensor's buffer must set to nullptr, in case the memory is released
|
||||
// twice.
|
||||
m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
|
||||
|
||||
// Set to the new tensor, and release current one.
|
||||
m_inputTensors[index].tensor = inputTensor;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void Executor::SetInputTensorWithNewBuffer(uint32_t index,
|
||||
std::shared_ptr<NNTensor> inputTensor,
|
||||
const void* inputBuffer,
|
||||
size_t length,
|
||||
bool isInnerMem)
|
||||
{
|
||||
// Release the memory inside the tensor first, if it is allocated by Executor during SetInput().
|
||||
if (m_inputTensors.find(index) != m_inputTensors.end()) {
|
||||
if (m_inputTensors[index].isInnerMem) {
|
||||
void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
|
||||
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
|
||||
inputDevice->ReleaseBuffer(curBuffer);
|
||||
}
|
||||
// Set current tensor's buffer to nullptr in case the NNTensor release the driver memory in destruction.
|
||||
m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
|
||||
}
|
||||
|
||||
// Set new input tensor data buffer
|
||||
inputTensor->SetBuffer(inputBuffer, length);
|
||||
|
||||
// Create or update the input tensor
|
||||
ExeTensor exeTensor{inputTensor, nullptr, 0, isInnerMem};
|
||||
m_inputTensors[index] = exeTensor;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length)
|
||||
{
|
||||
std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
|
||||
if (inputTensor == nullptr) {
|
||||
LOGE("SetInput failed, error happened when creating NNTensor.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
auto ret = BuildInputTensor(index, nnTensor, inputTensor);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("SetInput failed, please check input index or nnTensor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// dataLength will be larger than 0 after BuildInputTensor()
|
||||
size_t dataLength = inputTensor->GetDataLength();
|
||||
if (length == 0 || length < dataLength) {
|
||||
LOGE("SetInput failed, the given buffer length is too small to store the input nnTensor data.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Get length of current buffer if it is allocate by SetInput() before.
|
||||
size_t curBufferLength = 0;
|
||||
if ((m_inputTensors.find(index) != m_inputTensors.end()) && (m_inputTensors[index].isInnerMem)) {
|
||||
curBufferLength = m_inputTensors[index].tensor->GetBufferLength();
|
||||
}
|
||||
|
||||
// (dataLength <= curBufferLength) returns true if and only if current buffer is allocated by SetInput() before
|
||||
// and is larger than user buffer.
|
||||
if (dataLength <= curBufferLength) {
|
||||
ret = SetInputTensorWithCurrentBuffer(index, inputTensor, buffer, dataLength, curBufferLength);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("SetInput failed, error happened when setting input with current buffer.");
|
||||
return ret;
|
||||
}
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer needs to allocated or reallocated if:
|
||||
*
|
||||
* - Current buffer is not enough.
|
||||
* - SetInput() has not been called for the input before.
|
||||
* - The buffer held in m_inputTensors is allocated and set by CreateInputMemory() and SetInputFromMemory().
|
||||
*/
|
||||
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
|
||||
void* inputBuffer = inputDevice->AllocateBuffer(length);
|
||||
if (inputBuffer == nullptr) {
|
||||
LOGE("SetInput failed, error happened when allocating input device buffer.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
errno_t status = memcpy_s(inputBuffer, dataLength, buffer, dataLength);
|
||||
if (status != EOK) {
|
||||
LOGE("SetInput failed, copy data from user buffer failed. Error code: %d.", status);
|
||||
inputDevice->ReleaseBuffer(inputBuffer);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
SetInputTensorWithNewBuffer(index, inputTensor, inputBuffer, length, true);
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::SetInputFromMemory(uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory)
|
||||
{
|
||||
// Build a input tensor
|
||||
std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
|
||||
if (inputTensor == nullptr) {
|
||||
LOGE("SetInputFromMemory failed, error happened when creating NNTensor.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
auto ret = BuildInputTensor(index, nnTensor, inputTensor);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("SetInputFromMemory failed, please check input index or nnTensor");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// check data length
|
||||
size_t dataLength = inputTensor->GetDataLength();
|
||||
if (memory.length == 0 || memory.length < dataLength) {
|
||||
LOGE("SetInputFromMemory failed,"
|
||||
" the length in the given memory is too small to store the input nnTensor data.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
SetInputTensorWithNewBuffer(index, inputTensor, const_cast<const void*>(memory.data), memory.length, false);
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::SetOutput(uint32_t index, void* buffer, size_t length)
|
||||
{
|
||||
if (index >= m_modelOutputs.size()) {
|
||||
LOGE("SetOutput failed, output index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
size_t dataLength = m_modelOutputs[index]->GetDataLength();
|
||||
if (length == 0 || length < dataLength) {
|
||||
LOGE("SetOutput failed, the given buffer length is too small to store the output tensor data.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// If output tensor does not exist, or inner device buffer size is not enough,
|
||||
// or device buffer is set by SetOutputFromMemory() before,
|
||||
// allocate a new device buffer and set it to output tensor, and update the user buffer.
|
||||
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
|
||||
if (m_outputTensors.find(index) != m_outputTensors.end()) {
|
||||
if (m_outputTensors[index].isInnerMem) {
|
||||
size_t curBufferLength = m_outputTensors[index].tensor->GetBufferLength();
|
||||
if (length <= curBufferLength) {
|
||||
// If current device buffer size is enough, only update the user buffer.
|
||||
m_outputTensors[index].userBuffer = buffer;
|
||||
m_outputTensors[index].userBufferLength = length;
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
} else {
|
||||
// If current device buffer size is not enough,
|
||||
// release current device buffer and then allocate a new one below.
|
||||
void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
|
||||
outputDevice->ReleaseBuffer(curBuffer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If output tensor does not exist, create a new null output tensor.
|
||||
ExeTensor exeTensor;
|
||||
m_outputTensors[index] = exeTensor;
|
||||
m_outputTensors[index].tensor = m_modelOutputs[index];
|
||||
}
|
||||
|
||||
void* deviceOutputBuffer = outputDevice->AllocateBuffer(length);
|
||||
if (deviceOutputBuffer == nullptr) {
|
||||
LOGE("SetOutput failed, allocating output device buffer failed.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
m_outputTensors[index].tensor->SetBuffer(deviceOutputBuffer, length);
|
||||
m_outputTensors[index].userBuffer = buffer;
|
||||
m_outputTensors[index].userBufferLength = length;
|
||||
m_outputTensors[index].isInnerMem = true;
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory)
|
||||
{
|
||||
if (index >= m_modelOutputs.size()) {
|
||||
LOGE("SetOutputFromMemory failed, output index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
size_t dataLength = m_modelOutputs[index]->GetDataLength();
|
||||
if (memory.length == 0 || memory.length < dataLength) {
|
||||
LOGE("SetOutputFromMemory failed, the memory is too small to store the output tensor data.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (m_outputTensors.find(index) != m_outputTensors.end()) {
|
||||
if (m_outputTensors[index].isInnerMem) {
|
||||
// If it is inner buffer, releate it
|
||||
void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
|
||||
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
|
||||
outputDevice->ReleaseBuffer(curBuffer);
|
||||
}
|
||||
} else {
|
||||
// If output tensor does not exist, create a new null output tensor.
|
||||
ExeTensor exeTensor;
|
||||
m_outputTensors[index] = exeTensor;
|
||||
m_outputTensors[index].tensor = m_modelOutputs[index];
|
||||
}
|
||||
|
||||
// Set the output tensor with memory
|
||||
m_outputTensors[index].tensor->SetBuffer(const_cast<const void*>(memory.data), memory.length);
|
||||
m_outputTensors[index].userBuffer = nullptr;
|
||||
m_outputTensors[index].userBufferLength = 0;
|
||||
m_outputTensors[index].isInnerMem = false;
|
||||
m_isRun = false;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::GetOutputShape(uint32_t index, int32_t** dimensions, uint32_t& dimensionCount)
|
||||
{
|
||||
if (!m_isRun) {
|
||||
LOGE("GetOutputShape failed, cannot get output dimensions before Run.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (index >= m_modelOutputs.size()) {
|
||||
LOGE("GetOutputShape failed, output index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (m_outputTensors.find(index) == m_outputTensors.end()) {
|
||||
LOGE("GetOutputShape failed, output has not been set. Output index: %u.", index);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_outputDimensions[index] = m_outputTensors[index].tensor->GetDimensions();
|
||||
*dimensions = m_outputDimensions[index].data();
|
||||
dimensionCount = m_outputDimensions[index].size();
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
|
||||
{
|
||||
if (index >= m_modelInputs.size()) {
|
||||
LOGE("CreateInputMemory failed, input index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Allocate device buffer
|
||||
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
|
||||
void* deviceInputBuffer = inputDevice->AllocateBuffer(length);
|
||||
if (deviceInputBuffer == nullptr) {
|
||||
LOGE("CreateInputMemory failed, allocating intput device buffer failed.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
*memory = new(std::nothrow) OH_NN_Memory{deviceInputBuffer, length};
|
||||
if (*memory == nullptr) {
|
||||
LOGE("CreateInputMemory failed, constructing OH_NN_Memory failed.");
|
||||
inputDevice->ReleaseBuffer(deviceInputBuffer);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
// Save the buffer address for check when destroying it.
|
||||
m_inputCreatedMem[index].emplace_back(deviceInputBuffer);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::DestroyInputMemory(uint32_t index, OH_NN_Memory** memory)
|
||||
{
|
||||
if (index >= m_modelInputs.size()) {
|
||||
LOGE("DestroyInputMemory failed, input index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (m_inputCreatedMem.find(index) == m_inputCreatedMem.end()) {
|
||||
LOGE("DestroyInputMemory failed, the memory has not been created with the index.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<void*>& inputCreatedMem = m_inputCreatedMem[index];
|
||||
auto pos = std::find(inputCreatedMem.begin(), inputCreatedMem.end(), (*memory)->data);
|
||||
if (pos == inputCreatedMem.end()) {
|
||||
LOGE("DestroyInputMemory failed, the index does not match the memory.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> inputDevice = m_executionPlan->GetInputDevice();
|
||||
auto ret = inputDevice->ReleaseBuffer((*memory)->data);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Release input buffer failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inputCreatedMem.erase(pos);
|
||||
delete *memory;
|
||||
*memory = nullptr;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
|
||||
{
|
||||
if (index >= m_modelOutputs.size()) {
|
||||
LOGE("CreateOutputMemory failed, output index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Allocate device buffer
|
||||
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
|
||||
void* deviceOutputBuffer = outputDevice->AllocateBuffer(length);
|
||||
if (deviceOutputBuffer == nullptr) {
|
||||
LOGE("CreateOutputMemory failed, allocating output device buffer failed.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
*memory = new(std::nothrow) OH_NN_Memory{deviceOutputBuffer, length};
|
||||
if (*memory == nullptr) {
|
||||
LOGE("CreateOutputMemory failed, constructing OH_NN_Memory failed.");
|
||||
outputDevice->ReleaseBuffer(deviceOutputBuffer);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
// Save the buffer address for check when destroying it.
|
||||
m_outputCreatedMem[index].emplace_back(deviceOutputBuffer);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory)
|
||||
{
|
||||
if (index >= m_modelOutputs.size()) {
|
||||
LOGE("DestroyOutputMemory failed, output index is out of range.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (m_outputCreatedMem.find(index) == m_outputCreatedMem.end()) {
|
||||
LOGE("DestroyOutputMemory failed, the memory has not been created with the index.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<void*>& outputCreatedMem = m_outputCreatedMem[index];
|
||||
auto pos = std::find(outputCreatedMem.begin(), outputCreatedMem.end(), (*memory)->data);
|
||||
if (pos == outputCreatedMem.end()) {
|
||||
LOGE("DestroyOutputMemory failed, the index does not match the memory.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> outputDevice = m_executionPlan->GetOutputDevice();
|
||||
auto ret = outputDevice->ReleaseBuffer((*memory)->data);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Release output buffer failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
outputCreatedMem.erase(pos);
|
||||
delete *memory;
|
||||
*memory = nullptr;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
OH_NN_ReturnCode Executor::Run()
|
||||
{
|
||||
NNRT_TRACE_NAME("Execution");
|
||||
if (m_modelInputs.size() != m_inputTensors.size()) {
|
||||
LOGE("Run failed, some input tensors have not been set.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
if (m_modelOutputs.size() != m_outputTensors.size()) {
|
||||
LOGE("Run failed, some output tensors have not been set.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Build the NNTensor pointer vector: inputTensors and outputTensors
|
||||
std::vector<std::shared_ptr<NNTensor>> inputTensors;
|
||||
std::vector<std::shared_ptr<NNTensor>> outputTensors;
|
||||
size_t inputSize = m_inputTensors.size();
|
||||
size_t outputSize = m_outputTensors.size();
|
||||
for (size_t i = 0; i < inputSize; ++i) {
|
||||
inputTensors.emplace_back(m_inputTensors[i].tensor);
|
||||
}
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
outputTensors.emplace_back(m_outputTensors[i].tensor);
|
||||
}
|
||||
|
||||
// Predict
|
||||
auto ret = m_executionPlan->Run(inputTensors, outputTensors);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Run failed, error happened when executing the inference.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
errno_t status{EOK};
|
||||
// Copy inner device buffer to user buffer if using SetOutput()
|
||||
for (size_t i = 0; i < outputSize; ++i) {
|
||||
if (m_outputTensors[i].isInnerMem) {
|
||||
auto size = outputTensors[i]->GetDataLength();
|
||||
if (size > m_outputTensors[i].userBufferLength) {
|
||||
LOGE("Output buffer size is not enough. Your size=%zu, but actual output size=%zu.",
|
||||
m_outputTensors[i].userBufferLength, size);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* deviceBuffer = outputTensors[i]->GetBuffer();
|
||||
if (deviceBuffer == nullptr) {
|
||||
LOGE("Output buffer is nullptr.");
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
status = memcpy_s(m_outputTensors[i].userBuffer, m_outputTensors[i].userBufferLength, deviceBuffer, size);
|
||||
if (status != EOK) {
|
||||
LOGE("Run failed, memory copy from device buffer to user buffer failed. Error code: %d.", status);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_isRun = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
{
|
||||
std::shared_ptr<Device> inputDevice;
|
||||
for (auto& it : m_inputTensors) {
|
||||
inputDevice = m_executionPlan->GetInputDevice();
|
||||
if ((it.second).isInnerMem) {
|
||||
inputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
|
||||
}
|
||||
(it.second).tensor->SetBuffer(nullptr, 0);
|
||||
(it.second).tensor.reset();
|
||||
(it.second).userBuffer = nullptr;
|
||||
}
|
||||
m_inputTensors.clear();
|
||||
|
||||
std::shared_ptr<Device> outputDevice;
|
||||
for (auto& it : m_outputTensors) {
|
||||
outputDevice = m_executionPlan->GetOutputDevice();
|
||||
if ((it.second).isInnerMem) {
|
||||
outputDevice->ReleaseBuffer((it.second).tensor->GetBuffer());
|
||||
}
|
||||
(it.second).tensor->SetBuffer(nullptr, 0);
|
||||
(it.second).tensor.reset();
|
||||
(it.second).userBuffer = nullptr;
|
||||
}
|
||||
m_outputTensors.clear();
|
||||
|
||||
for (auto& it : m_inputCreatedMem) {
|
||||
it.second.clear();
|
||||
}
|
||||
m_inputCreatedMem.clear();
|
||||
|
||||
for (auto& it : m_outputCreatedMem) {
|
||||
it.second.clear();
|
||||
}
|
||||
m_outputCreatedMem.clear();
|
||||
|
||||
m_outputDimensions.clear();
|
||||
m_modelInputs.clear();
|
||||
m_modelOutputs.clear();
|
||||
}
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_EXECUTOR_H
|
||||
#define NEURAL_NETWORK_RUNTIME_EXECUTOR_H
|
||||
|
||||
#include "compilation.h"
|
||||
#include "execution_plan.h"
|
||||
#include "nn_tensor.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime.h"
|
||||
#include "interfaces/oem/cpp_api/device.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class Executor {
|
||||
public:
|
||||
explicit Executor(const Compilation* compilation);
|
||||
~Executor();
|
||||
|
||||
OH_NN_ReturnCode SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length);
|
||||
OH_NN_ReturnCode SetInputFromMemory(uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory);
|
||||
OH_NN_ReturnCode SetOutput(uint32_t index, void* buffer, size_t length);
|
||||
OH_NN_ReturnCode SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory);
|
||||
OH_NN_ReturnCode GetOutputShape(uint32_t index, int32_t** dimensions, uint32_t& dimensionCount);
|
||||
|
||||
OH_NN_ReturnCode CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory);
|
||||
OH_NN_ReturnCode CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory);
|
||||
OH_NN_ReturnCode DestroyInputMemory(uint32_t index, OH_NN_Memory** memory);
|
||||
OH_NN_ReturnCode DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory);
|
||||
|
||||
OH_NN_ReturnCode Run();
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
|
||||
std::shared_ptr<NNTensor> inputTensor) const;
|
||||
OH_NN_ReturnCode SetInputTensorWithCurrentBuffer(uint32_t index, std::shared_ptr<NNTensor> inputTensor,
|
||||
const void* buffer, size_t dataLength, size_t curBufferLength);
|
||||
void SetInputTensorWithNewBuffer(uint32_t index, std::shared_ptr<NNTensor> inputTensor,
|
||||
const void* inputBuffer, size_t length, bool isInnerMem);
|
||||
|
||||
private:
|
||||
struct ExeTensor {
|
||||
std::shared_ptr<NNTensor> tensor;
|
||||
void* userBuffer;
|
||||
size_t userBufferLength;
|
||||
bool isInnerMem;
|
||||
};
|
||||
bool m_isRun {false};
|
||||
std::vector<std::shared_ptr<NNTensor>> m_modelInputs;
|
||||
std::vector<std::shared_ptr<NNTensor>> m_modelOutputs;
|
||||
std::shared_ptr<ExecutionPlan> m_executionPlan {nullptr};
|
||||
std::unordered_map<int, std::vector<int32_t>> m_outputDimensions;
|
||||
std::unordered_map<int, ExeTensor> m_inputTensors;
|
||||
std::unordered_map<int, ExeTensor> m_outputTensors;
|
||||
std::unordered_map<int, std::vector<void*>> m_inputCreatedMem;
|
||||
std::unordered_map<int, std::vector<void*>> m_outputCreatedMem;
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hdi_device.h"
|
||||
|
||||
#include "hdf_base.h"
|
||||
#include "mindir.h"
|
||||
|
||||
#include "hdi_prepared_model.h"
|
||||
#include "memory_manager.h"
|
||||
#include "transform.h"
|
||||
#include "common/log.h"
|
||||
#include "common/utils.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
HDIDevice::HDIDevice(OHOS::sptr<V1_0::INnrtDevice> device) : m_iDevice(device)
|
||||
{
|
||||
device->GetVersion(m_hdiVersion.first, m_hdiVersion.second);
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::GetDeviceName(std::string& name)
|
||||
{
|
||||
auto ret = m_iDevice->GetDeviceName(name);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Get HDI device name failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::GetVendorName(std::string& name)
|
||||
{
|
||||
auto ret = m_iDevice->GetVendorName(name);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Get HDI device vendor name failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::GetDeviceType(OH_NN_DeviceType& deviceType)
|
||||
{
|
||||
V1_0::DeviceType iDeviceType;
|
||||
auto ret = m_iDevice->GetDeviceType(iDeviceType);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Get HDI device type failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
|
||||
deviceType = HDIToNN::TransHDIDeviceType(iDeviceType);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::GetDeviceStatus(DeviceStatus& status)
|
||||
{
|
||||
V1_0::DeviceStatus iDeviceStatus;
|
||||
auto ret = m_iDevice->GetDeviceStatus(iDeviceStatus);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Get HDI device status failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
status = HDIToNN::TransHDIDeviceStatus(iDeviceStatus);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,
|
||||
std::vector<bool>& ops)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("Model is nullptr, cannot query supported operation.");
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
V1_0::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
|
||||
size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
|
||||
int32_t hdiRet {0};
|
||||
if (tensorSize > 0) {
|
||||
hdiRet = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
|
||||
if (hdiRet != HDF_SUCCESS || tensorBuffer.fd == INVALID_FD) {
|
||||
LOGE("Allocate tensor buffer error when get supported operation. ErrorCode: %d", hdiRet);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
auto iModel = mindspore::lite::MindIR_LiteGraph_To_Model(model.get(), tensorBuffer);
|
||||
if (iModel == nullptr) {
|
||||
LOGE("Parse litegraph to hdi model failed.");
|
||||
ReleaseSharedBuffer(tensorBuffer);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
hdiRet = m_iDevice->GetSupportedOperation(*iModel, ops);
|
||||
|
||||
mindspore::lite::MindIR_Model_Destroy(&iModel);
|
||||
auto ret = ReleaseSharedBuffer(tensorBuffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Release tensorBuffer failed.");
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
if (hdiRet != HDF_SUCCESS) {
|
||||
LOGE("Get supported operation failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::IsFloat16PrecisionSupported(bool& isSupported)
|
||||
{
|
||||
auto ret = m_iDevice->IsFloat16PrecisionSupported(isSupported);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Query fp16 precision supported failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::IsPerformanceModeSupported(bool& isSupported)
|
||||
{
|
||||
auto ret = m_iDevice->IsPerformanceModeSupported(isSupported);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Query performance mode supported failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::IsPrioritySupported(bool& isSupported)
|
||||
{
|
||||
auto ret = m_iDevice->IsPrioritySupported(isSupported);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Query priority supported failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::IsDynamicInputSupported(bool& isSupported)
|
||||
{
|
||||
auto ret = m_iDevice->IsDynamicInputSupported(isSupported);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Query dynamic input supported failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::IsModelCacheSupported(bool& isSupported)
|
||||
{
|
||||
auto ret = m_iDevice->IsModelCacheSupported(isSupported);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Query cache model supported failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
|
||||
const ModelConfig& config,
|
||||
std::shared_ptr<PreparedModel>& preparedModel)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("Model is nullptr, cannot prepare model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
V1_0::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
|
||||
size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
|
||||
int32_t hdiRet {0};
|
||||
if (tensorSize > 0) {
|
||||
hdiRet = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
|
||||
if (hdiRet != HDF_SUCCESS || tensorBuffer.fd == INVALID_FD) {
|
||||
LOGE("Allocate tensor buffer error when prepare model. ErrorCode: %d", hdiRet);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
V1_0::Model* iModel = mindspore::lite::MindIR_LiteGraph_To_Model(model.get(), tensorBuffer);
|
||||
if (iModel == nullptr) {
|
||||
LOGE("Parse litegraph to hdi model failed.");
|
||||
ReleaseSharedBuffer(tensorBuffer);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
V1_0::ModelConfig iModelConfig;
|
||||
iModelConfig.enableFloat16 = config.enableFloat16;
|
||||
iModelConfig.mode = NNToHDI::TransPerformanceMode(config.mode);
|
||||
iModelConfig.priority = NNToHDI::TransPriority(config.priority);
|
||||
OHOS::sptr<V1_0::IPreparedModel> iPreparedModel;
|
||||
|
||||
auto preparedRet = m_iDevice->PrepareModel(*iModel, iModelConfig, iPreparedModel);
|
||||
|
||||
mindspore::lite::MindIR_Model_Destroy(&iModel);
|
||||
auto ret = ReleaseSharedBuffer(tensorBuffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Release tensorBuffer failed.");
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
if (preparedRet != HDF_SUCCESS || iPreparedModel == nullptr) {
|
||||
LOGE("Prepare model failed. ErrorCode=%d", preparedRet);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
preparedModel = CreateSharedPtr<HDIPreparedModel>(iPreparedModel);
|
||||
if (preparedModel == nullptr) {
|
||||
LOGE("Prepare model failed, because fail to create preparedModel instance.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::PrepareModelFromModelCache(const std::vector<ModelBuffer>& modelCache,
|
||||
const ModelConfig& config,
|
||||
std::shared_ptr<PreparedModel>& preparedModel)
|
||||
{
|
||||
std::vector<V1_0::SharedBuffer> iBuffers;
|
||||
auto memManager = MemoryManager::GetInstance();
|
||||
Memory memory;
|
||||
OH_NN_ReturnCode ret;
|
||||
size_t modelCacheSize = modelCache.size();
|
||||
for (size_t i = 0; i < modelCacheSize; i++) {
|
||||
ret = memManager->GetMemory(modelCache[i].buffer, memory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("The %zuth model cache is invalid. Please put valid model cache.", i + 1);
|
||||
return ret;
|
||||
}
|
||||
iBuffers.emplace_back(V1_0::SharedBuffer {memory.fd, memory.length, 0, memory.length});
|
||||
}
|
||||
|
||||
V1_0::ModelConfig iModelConfig;
|
||||
iModelConfig.enableFloat16 = config.enableFloat16;
|
||||
iModelConfig.mode = NNToHDI::TransPerformanceMode(config.mode);
|
||||
iModelConfig.priority = NNToHDI::TransPriority(config.priority);
|
||||
|
||||
OHOS::sptr<V1_0::IPreparedModel> iPreparedModel;
|
||||
auto hdiRet = m_iDevice->PrepareModelFromModelCache(iBuffers, iModelConfig, iPreparedModel);
|
||||
if (hdiRet != HDF_SUCCESS) {
|
||||
LOGE("Prepare model from cache failed. ErrorCode=%d", hdiRet);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
|
||||
preparedModel = CreateSharedPtr<HDIPreparedModel>(iPreparedModel);
|
||||
if (preparedModel == nullptr) {
|
||||
LOGE("Prepare model from model cache failed, because fail to create preparedModel instance.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void* HDIDevice::AllocateBuffer(size_t length)
|
||||
{
|
||||
if (length == 0) {
|
||||
LOGE("The length param is invalid, length=0");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
V1_0::SharedBuffer buffer;
|
||||
auto ret = m_iDevice->AllocateBuffer(length, buffer);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Allocate buffer error. ErrorCode: %d", ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto memManager = MemoryManager::GetInstance();
|
||||
auto addr = memManager->MapMemory(buffer.fd, length);
|
||||
if (addr == nullptr) {
|
||||
LOGE("Map fd to address failed.");
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::ReleaseBuffer(const void* buffer)
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Buffer is nullptr, no need to release.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto memManager = MemoryManager::GetInstance();
|
||||
Memory memory;
|
||||
auto ret = memManager->GetMemory(buffer, memory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Invalid Buffer, it is not NNRt buffer.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
V1_0::SharedBuffer hdiBuffer {memory.fd, memory.length, 0, memory.length};
|
||||
auto deviceResult = m_iDevice->ReleaseBuffer(hdiBuffer);
|
||||
if (deviceResult != HDF_SUCCESS) {
|
||||
LOGE("Device release buffer error. ErrorCode: %d", deviceResult);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
ret = memManager->UnMapMemory(buffer);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Unmap memory failed.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIDevice::ReleaseSharedBuffer(const V1_0::SharedBuffer& buffer)
|
||||
{
|
||||
if (buffer.fd == INVALID_FD) {
|
||||
LOGI("No need to release. fd=%d", INVALID_FD);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
auto ret = m_iDevice->ReleaseBuffer(buffer);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Device release buffer error. ErrorCode=%d", ret);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
|
||||
#define NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
|
||||
|
||||
#include "refbase.h"
|
||||
#include "hdi_interfaces.h"
|
||||
|
||||
#include "interfaces/oem/cpp_api/device.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class HDIDevice : public Device {
|
||||
public:
|
||||
explicit HDIDevice(OHOS::sptr<V1_0::INnrtDevice> device);
|
||||
|
||||
OH_NN_ReturnCode GetDeviceName(std::string& name) override;
|
||||
OH_NN_ReturnCode GetVendorName(std::string& name) override;
|
||||
OH_NN_ReturnCode GetDeviceType(OH_NN_DeviceType& deviceType) override;
|
||||
OH_NN_ReturnCode GetDeviceStatus(DeviceStatus& status) override;
|
||||
OH_NN_ReturnCode GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,
|
||||
std::vector<bool>& ops) override;
|
||||
|
||||
OH_NN_ReturnCode IsFloat16PrecisionSupported(bool& isSupported) override;
|
||||
OH_NN_ReturnCode IsPerformanceModeSupported(bool& isSupported) override;
|
||||
OH_NN_ReturnCode IsPrioritySupported(bool& isSupported) override;
|
||||
OH_NN_ReturnCode IsDynamicInputSupported(bool& isSupported) override;
|
||||
OH_NN_ReturnCode IsModelCacheSupported(bool& isSupported) override;
|
||||
|
||||
OH_NN_ReturnCode PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
|
||||
const ModelConfig& config,
|
||||
std::shared_ptr<PreparedModel>& preparedModel) override;
|
||||
OH_NN_ReturnCode PrepareModelFromModelCache(const std::vector<ModelBuffer>& modelCache,
|
||||
const ModelConfig& config,
|
||||
std::shared_ptr<PreparedModel>& preparedModel) override;
|
||||
|
||||
void* AllocateBuffer(size_t length) override;
|
||||
OH_NN_ReturnCode ReleaseBuffer(const void* buffer) override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode ReleaseSharedBuffer(const V1_0::SharedBuffer& buffer);
|
||||
|
||||
private:
|
||||
// first: major version, second: minor version
|
||||
std::pair<uint32_t, uint32_t> m_hdiVersion;
|
||||
OHOS::sptr<V1_0::INnrtDevice> m_iDevice {nullptr};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_HDI_DEVICE_H
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
|
||||
#define NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
|
||||
|
||||
#include <v1_0/nnrt_types.h>
|
||||
#include <v1_0/innrt_device.h>
|
||||
#include <v1_0/iprepared_model.h>
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace V1_0 = OHOS::HDI::Nnrt::V1_0;
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_HDI_INTERFACES_H
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hdi_prepared_model.h"
|
||||
|
||||
#include "common/log.h"
|
||||
#include "memory_manager.h"
|
||||
#include "transform.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
HDIPreparedModel::HDIPreparedModel(OHOS::sptr<V1_0::IPreparedModel> hdiPreparedModel)
|
||||
: m_hdiPreparedModel(hdiPreparedModel)
|
||||
{
|
||||
hdiPreparedModel->GetVersion(m_hdiVersion.first, m_hdiVersion.second);
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIPreparedModel::ExportModelCache(std::vector<ModelBuffer>& modelCache)
|
||||
{
|
||||
if (!modelCache.empty()) {
|
||||
LOGE("The vector of modelCache should be empty. size=%zu", modelCache.size());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<V1_0::SharedBuffer> iBuffers;
|
||||
auto ret = m_hdiPreparedModel->ExportModelCache(iBuffers);
|
||||
if (ret != HDF_SUCCESS) {
|
||||
LOGE("Export model cache failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
|
||||
auto memManager = MemoryManager::GetInstance();
|
||||
for (size_t i = 0; i < iBuffers.size(); i++) {
|
||||
auto addr = memManager->MapMemory(iBuffers[i].fd, iBuffers[i].bufferSize);
|
||||
if (addr == nullptr) {
|
||||
LOGE("Export the %zuth model cache failed, cannot not map fd to address.", i + 1);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
ModelBuffer modelbuffer {addr, iBuffers[i].bufferSize};
|
||||
modelCache.emplace_back(modelbuffer);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode HDIPreparedModel::Run(const std::vector<IOTensor>& inputs, const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims, std::vector<bool>& isOutputBufferEnough)
|
||||
{
|
||||
V1_0::IOTensor iTensor;
|
||||
std::vector<V1_0::IOTensor> iInputTensors;
|
||||
for (auto& input: inputs) {
|
||||
iTensor = NNToHDI::TransIOTensor(input);
|
||||
if (iTensor.data.fd == INVALID_FD) {
|
||||
LOGE("Transform inputs tensor failed, cannot find data file descriptor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
iInputTensors.emplace_back(iTensor);
|
||||
}
|
||||
|
||||
std::vector<V1_0::IOTensor> iOutputTensors;
|
||||
for (auto& output: outputs) {
|
||||
iTensor = NNToHDI::TransIOTensor(output);
|
||||
if (iTensor.data.fd == INVALID_FD) {
|
||||
LOGE("Transform outputs tensor failed, cannot find data file descriptor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
iOutputTensors.emplace_back(iTensor);
|
||||
}
|
||||
|
||||
auto ret = m_hdiPreparedModel->Run(iInputTensors, iOutputTensors, outputsDims, isOutputBufferEnough);
|
||||
if (ret != HDF_SUCCESS || outputsDims.empty()) {
|
||||
LOGE("Run model failed. ErrorCode=%d", ret);
|
||||
return OH_NN_UNAVALIDABLE_DEVICE;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
|
||||
#define NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "refbase.h"
|
||||
#include "hdi_interfaces.h"
|
||||
#include "interfaces/oem/cpp_api/prepared_model.h"
|
||||
#include "interfaces/oem/cpp_api/cpp_type.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class HDIPreparedModel : public PreparedModel {
|
||||
public:
|
||||
explicit HDIPreparedModel(OHOS::sptr<V1_0::IPreparedModel> hdiPreparedModel);
|
||||
|
||||
OH_NN_ReturnCode ExportModelCache(std::vector<ModelBuffer>& modelCache) override;
|
||||
|
||||
OH_NN_ReturnCode Run(const std::vector<IOTensor>& inputs,
|
||||
const std::vector<IOTensor>& outputs,
|
||||
std::vector<std::vector<int32_t>>& outputsDims,
|
||||
std::vector<bool>& isOutputBufferEnough) override;
|
||||
|
||||
private:
|
||||
// first: major version, second: minor version
|
||||
std::pair<uint32_t, uint32_t> m_hdiVersion;
|
||||
OHOS::sptr<V1_0::IPreparedModel> m_hdiPreparedModel {nullptr};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_HDI_PREPARED_MODEL_H
|
||||
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "inner_model.h"
|
||||
|
||||
#include <new>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "securec.h"
|
||||
|
||||
#include "common/utils.h"
|
||||
#include "common/scoped_trace.h"
|
||||
#include "device_manager.h"
|
||||
#include "hdi_device.h"
|
||||
#include "validation.h"
|
||||
#include "ops_builder.h"
|
||||
#include "ops_registry.h"
|
||||
#include "transform.h"
|
||||
|
||||
namespace MSLITE = mindspore::lite;
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
const std::string NNR_MODEL = "NNR_Model";
|
||||
const std::string LOADED_NNR_MODEL = "Loaded_NNR_Model";
|
||||
|
||||
namespace {
|
||||
class LiteGraphDeleter {
|
||||
public:
|
||||
void operator()(MSLITE::LiteGraph* liteGraph) const
|
||||
{
|
||||
MindIR_LiteGraph_Destroy(&liteGraph);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<NNTensor> ConstructNNTensorFromLiteGraphTensor(const MSLITE::TensorPtr msTensor)
|
||||
{
|
||||
MSLITE::DataType msDataType = MSLITE::MindIR_Tensor_GetDataType(msTensor);
|
||||
OH_NN_DataType dataType = MSToNN::TransformDataType(msDataType);
|
||||
std::vector<int32_t> msDims = MSLITE::MindIR_Tensor_GetDims(msTensor);
|
||||
std::vector<MSLITE::QuantParam> msQuantParams = MSLITE::MindIR_Tensor_GetQuantParams(msTensor);
|
||||
std::vector<QuantParam> nnQuantParams = MSToNN::TransformQuantParams(msQuantParams);
|
||||
|
||||
std::shared_ptr<NNTensor> nnTensor = CreateSharedPtr<NNTensor>();
|
||||
if (nnTensor == nullptr) {
|
||||
LOGE("ConstructNNTensorFromLiteGraphTensor failed, error happened when creating NNTensor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = nnTensor->Build(dataType, msDims, nnQuantParams, OH_NN_TENSOR);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("ConstructNNTensorFromLiteGraphTensor failed, error happened when building NNTensor with attributes.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nnTensor;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ConstructNNTensorsFromLiteGraph(const MSLITE::LiteGraph* liteGraph,
|
||||
const std::vector<uint32_t>& indices,
|
||||
std::vector<std::shared_ptr<NNTensor>>& nnTensors)
|
||||
{
|
||||
if (indices.empty()) {
|
||||
LOGE("ConstructNNTensorsFromLiteGraph failed, passed empty indices list.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
uint32_t maximumIndex = *(std::max_element(indices.begin(), indices.end()));
|
||||
if (maximumIndex >= liteGraph->all_tensors_.size()) {
|
||||
LOGE("ConstructNNTensorsFromLiteGraph failed, index exceed size of all_tensors inside liteGraph.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::shared_ptr<NNTensor> nnTensor;
|
||||
for (uint32_t i : indices) {
|
||||
nnTensor = ConstructNNTensorFromLiteGraphTensor(liteGraph->all_tensors_[i]);
|
||||
if (nnTensor == nullptr) {
|
||||
LOGE("ConstructNNTensorsFromLiteGraph failed, failed to construct NNTensor from LiteGraphTensor.");
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
nnTensors.emplace_back(nnTensor);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
InnerModel::InnerModel() {}
|
||||
|
||||
bool InnerModel::IsBuild() const
|
||||
{
|
||||
return (m_liteGraph != nullptr);
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::BuildFromLiteGraph(const MSLITE::LiteGraph* liteGraph)
|
||||
{
|
||||
NNRT_TRACE_NAME("Build model from lite graph");
|
||||
if (liteGraph == nullptr) {
|
||||
LOGE("BuildFromLiteGraph failed, passed empty liteGraph.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("BuildFromLiteGraph failed, liteGraph has been built or loaded before.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (!m_allTensors.empty() || !m_ops.empty()) {
|
||||
LOGE("BuildFromLiteGraph failed, please LoadLiteGraph without adding tensor and operations.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
m_inputTensors.clear();
|
||||
OH_NN_ReturnCode ret = ConstructNNTensorsFromLiteGraph(liteGraph, liteGraph->input_indices_, m_inputTensors);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromLiteGraph failed, error happened when constructing input NNTensors from liteGraph.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_outputTensors.clear();
|
||||
ret = ConstructNNTensorsFromLiteGraph(liteGraph, liteGraph->output_indices_, m_outputTensors);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromLiteGraph failed, error happened when constructing output NNTensors from liteGraph.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_liteGraph.reset(const_cast<MSLITE::LiteGraph*>(liteGraph), LiteGraphDeleter());
|
||||
m_liteGraph->name_ = LOADED_NNR_MODEL;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::AddTensor(const OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("AddTensor failed, AddTensor is forbidden after Finish() or LoadLiteGraph() has been called.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
std::shared_ptr<NNTensor> tensor = CreateSharedPtr<NNTensor>();
|
||||
if (tensor == nullptr) {
|
||||
LOGE("AddTensor failed, error happened when creating NNTensor.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = tensor->BuildFromOHNNTensor(nnTensor);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("AddTensor failed, error happened when build NNTensor from OH_NN_Tensor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The NNTensor is named as "Tensor: <tensor index>"".
|
||||
tensor->SetName("Tensor: " + std::to_string(m_allTensors.size()));
|
||||
m_allTensors.emplace_back(tensor);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
// DOTO: 圈复杂度待优化
|
||||
OH_NN_ReturnCode InnerModel::SetTensorValue(uint32_t index, const void* buffer, size_t length)
|
||||
{
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("SetTensorValue failed, SetTensorValue is forbidden after Finish() or LoadLiteGraph() has been called.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (index >= m_allTensors.size()) {
|
||||
LOGE("SetTensorValue failed, passed index %u out of the number of added tensors.", index);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const std::shared_ptr<NNTensor> tensor = m_allTensors[index];
|
||||
if (tensor->GetBuffer() != nullptr) {
|
||||
LOGE("SetTensorValue failed, tensor has been set value twice. Tensor index: %u.", index);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (buffer == nullptr) {
|
||||
LOGW("SetTensorValue passed empty buffer, which makes no effect.");
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
if (tensor->IsDynamicShape()) {
|
||||
LOGE("SetTensorValue failed, cannot set value to tensor with dynamic shape.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (length != tensor->GetDataLength()) {
|
||||
LOGE("SetTensorValue failed, get buffer length %zu different from the byte size of tensor %zu.",
|
||||
length, tensor->GetDataLength());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Data will be released inside NNTensor if it is set inside NNTensor using SetBuffer().
|
||||
void* data = new (std::nothrow) char[length];
|
||||
if (data == nullptr) {
|
||||
LOGE("SetTensorValue failed, please check whether it runs out of memory.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
errno_t ret = memcpy_s(data, length, buffer, length);
|
||||
if (ret != EOK) {
|
||||
LOGE("SetTensorValue failed, please the information of error number %d from memcpy_s.", ret);
|
||||
delete [] reinterpret_cast<char*>(data);
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
tensor->SetBuffer(data, length);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::ValidateInputAndOutput(
|
||||
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices) const
|
||||
{
|
||||
OH_NN_ReturnCode ret = ValidateTensorArray(inputIndices);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("ValidateInputAndOutput failed, please check input indices.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ValidateTensorArray(outputIndices);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("ValidateInputAndOutput failed, please check output indices.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (inputIndices.size == 0) {
|
||||
LOGE("ValidateInputAndOutput failed, passed empty input indices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (outputIndices.size == 0) {
|
||||
LOGE("ValidateInputAndOutput failed, passed empty output indices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::shared_ptr<NNTensor> tensor{nullptr};
|
||||
for (uint32_t i = 0; i < inputIndices.size; i++) {
|
||||
tensor = m_allTensors[inputIndices.data[i]];
|
||||
if (tensor->GetType() != OH_NN_TENSOR) {
|
||||
LOGE("ValidateInputAndOutput failed, tensor set as input should has type of OH_NN_TENSOR, but receive %d."
|
||||
"Tensor index: %u.", tensor->GetType(), i);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < outputIndices.size; i++) {
|
||||
tensor = m_allTensors[outputIndices.data[i]];
|
||||
if (tensor->GetType() != OH_NN_TENSOR) {
|
||||
LOGE("ValidateInputAndOutput failed, tensor set as output should has type of OH_NN_TENSOR, but receive %d."
|
||||
"Tensor index: %u.", tensor->GetType(), i);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of inputIndices and outputIndices are usually small, so O(n**2) iteration is fine.
|
||||
for (uint32_t i = 0; i < inputIndices.size; i++) {
|
||||
for (uint32_t j = 0; j < outputIndices.size; j++) {
|
||||
if (inputIndices.data[i] == outputIndices.data[j]) {
|
||||
LOGE("ValidateInputAndOutput failed, should not set an tensor as input and output at the same time, "
|
||||
"input index %u, output index %u", inputIndices.data[i], outputIndices.data[j]);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
/* Check whether the indices exceed the number of added tensors. */
|
||||
OH_NN_ReturnCode InnerModel::ValidateTensorArray(const OH_NN_UInt32Array& indices) const
|
||||
{
|
||||
OH_NN_ReturnCode ret = Validation::ValidateArray(indices.data, indices.size);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("ValidateTensorArray failed, please check the validity of indices.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < indices.size; i++) {
|
||||
if (indices.data[i] >= m_allTensors.size()) {
|
||||
LOGE("ValidateTensors failed, index %u is out of the number of added tensors.", indices.data[i]);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::AddOperation(OH_NN_OperationType opType, const OH_NN_UInt32Array& paramIndices,
|
||||
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices)
|
||||
{
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("AddOperation failed, AddOperation is forbidden after after Finish() or LoadLiteGraph() has been called.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = ValidateInputAndOutput(inputIndices, outputIndices);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("AddOperation failed, please check inputIndices and outputIndices.");
|
||||
return ret;
|
||||
}
|
||||
std::vector<uint32_t> inputs = ConstructVectorFromArray(inputIndices.data, inputIndices.size);
|
||||
std::vector<uint32_t> outputs = ConstructVectorFromArray(outputIndices.data, outputIndices.size);
|
||||
|
||||
ret = ValidateTensorArray(paramIndices);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("AddOperation failed, please check paramIndices.");
|
||||
return ret;
|
||||
}
|
||||
std::vector<uint32_t> parameters = ConstructVectorFromArray(paramIndices.data, paramIndices.size);
|
||||
|
||||
Ops::OpsRegistry& opsRegistry = Ops::OpsRegistry::GetSingleton();
|
||||
std::unique_ptr<Ops::OpsBuilder> opsBuilder = opsRegistry.GetOpsBuilder(opType);
|
||||
if (opsBuilder == nullptr) {
|
||||
LOGE("AddOperation failed, cannot add operation of type: %d.", opType);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
ret = opsBuilder->Build(parameters, inputs, outputs, m_allTensors);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("AddOperation failed, error happens when build operations.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_ops.emplace_back(std::move(opsBuilder));
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::SpecifyInputsAndOutputs(
|
||||
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices)
|
||||
{
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("SpecifyInputsAndOutputs failed, "
|
||||
"SpecifyInputsAndOutputs is forbidden after Finish() or LoadLiteGraph() has been called.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (!m_inputTensors.empty()) {
|
||||
LOGE("SpecifyInputsAndOutputs failed, SpecifyInputsAndOutputs should not be called twice.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = ValidateInputAndOutput(inputIndices, outputIndices);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("SpecifyInputsAndOutputs failed, please check inputIndices and outputIndices.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_inputIndices = ConstructVectorFromArray(inputIndices.data, inputIndices.size);
|
||||
m_outputIndices = ConstructVectorFromArray(outputIndices.data, outputIndices.size);
|
||||
|
||||
for (uint32_t i : m_inputIndices) {
|
||||
m_inputTensors.emplace_back(m_allTensors[i]);
|
||||
}
|
||||
|
||||
for (uint32_t i : m_outputIndices) {
|
||||
m_outputTensors.emplace_back(m_allTensors[i]);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::Build()
|
||||
{
|
||||
NNRT_TRACE_NAME("Build model");
|
||||
if (m_liteGraph != nullptr) {
|
||||
LOGE("Build failed,"
|
||||
" OH_NNModel is not allowed to build again after Build() or BuildFromLiteGraph() has been called.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_allTensors.empty()) {
|
||||
LOGE("Build failed, no OH_NN_Tensor has been added. Must call AddTensor before Build().");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (m_ops.empty()) {
|
||||
LOGE("Build failed, no operation has beed added. Must call AddOperation before Build().");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if ((m_inputIndices.empty()) || (m_outputIndices.empty())) {
|
||||
LOGE("Build failed, inputs and outputs are unspecified. Must call SpecifyInputsAndOutputs before Build().");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
MSLITE::LiteGraph* pLiteGraph = new (std::nothrow) MSLITE::LiteGraph();
|
||||
if (pLiteGraph == nullptr) {
|
||||
LOGE("Build failed, error happend when creating LiteGraph.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
m_liteGraph.reset(pLiteGraph, LiteGraphDeleter());
|
||||
|
||||
m_liteGraph->name_ = NNR_MODEL;
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> modelIDToGraphID;
|
||||
AddTensorsToLiteGraph(modelIDToGraphID);
|
||||
|
||||
OH_NN_ReturnCode ret = AddNodesToLiteGraph(modelIDToGraphID);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// subGraph will be released by LiteGraph if it is added into instance of LiteGraph.
|
||||
MSLITE::LiteGraph::SubGraph* subGraph = new (std::nothrow) MSLITE::LiteGraph::SubGraph();
|
||||
if (subGraph == nullptr) {
|
||||
LOGE("AddNodesToLiteGraph failed, error happened when creating subgraph.");
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
subGraph->name_ = "NNRt_SubGraph"; // Name of subGraph
|
||||
subGraph->input_indices_ = m_liteGraph->input_indices_;
|
||||
subGraph->output_indices_ = m_liteGraph->output_indices_;
|
||||
uint32_t nodeCount = static_cast<uint32_t>(m_ops.size()); // m_ops.size() smaller than UINT32_MAX
|
||||
for (uint32_t i = 0; i < nodeCount; i++) {
|
||||
subGraph->node_indices_.emplace_back(i);
|
||||
}
|
||||
m_liteGraph->sub_graphs_.emplace_back(subGraph);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void InnerModel::AddTensorsToLiteGraph(std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID)
|
||||
{
|
||||
uint32_t graphID = 0;
|
||||
LiteGraphTensorPtr tensor(nullptr, DestroyLiteGraphTensor);
|
||||
size_t tensorCount = m_allTensors.size();
|
||||
for (size_t i = 0; i < tensorCount; i++) {
|
||||
const std::shared_ptr<NNTensor>& nnTensor = m_allTensors[i];
|
||||
// If the tensor is used as operation parameter, it will not convert to the tensor of LiteGraph.
|
||||
if (nnTensor->IsOpParameter()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tensor = nnTensor->ConvertToLiteGraphTensor();
|
||||
m_liteGraph->all_tensors_.emplace_back(tensor.release());
|
||||
modelIDToGraphID[i] = graphID++;
|
||||
}
|
||||
|
||||
// Note: Indices in m_inputIndices and m_outputIndices have been checked in SpecifyInputAndOutput(), there is no
|
||||
// need to check twice.
|
||||
std::vector<uint32_t>& inputIndices = m_liteGraph->input_indices_;
|
||||
for (uint32_t index : m_inputIndices) {
|
||||
inputIndices.emplace_back(modelIDToGraphID.at(index));
|
||||
}
|
||||
|
||||
std::vector<uint32_t>& outputIndices = m_liteGraph->output_indices_;
|
||||
for (uint32_t index : m_outputIndices) {
|
||||
outputIndices.emplace_back(modelIDToGraphID.at(index));
|
||||
}
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::AddNodesToLiteGraph(const std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID)
|
||||
{
|
||||
MSLITE::LiteGraph::Node* node{nullptr};
|
||||
size_t opCount = m_ops.size();
|
||||
Ops::LiteGraphPrimitvePtr primitive = {nullptr, DestroyLiteGraphTensor};
|
||||
for (size_t i = 0; i < opCount; i++) {
|
||||
std::unique_ptr<Ops::OpsBuilder>& op = m_ops[i];
|
||||
// node will be released by LiteGraph if it is added into instance of LiteGraph.
|
||||
node = new(std::nothrow) MSLITE::LiteGraph::Node();
|
||||
if (node == nullptr) {
|
||||
LOGE("AddNodesToLiteGraph failed, error happened when creating LiteGraph tensor.");
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
node->name_ = op->GetName() + ":" + std::to_string(i);
|
||||
node->quant_type_ = NNToMS::TransformQuantType(op->GetQuantType());
|
||||
|
||||
op->GetInputIndex(node->input_indices_, modelIDToGraphID);
|
||||
op->GetOutputIndex(node->output_indices_, modelIDToGraphID);
|
||||
|
||||
primitive = op->GetPrimitive();
|
||||
if (primitive == nullptr) {
|
||||
LOGE("Build %s primitive failed.", op->GetName().c_str());
|
||||
delete node;
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
node->primitive_ = primitive.release();
|
||||
m_liteGraph->all_nodes_.emplace_back(node);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode InnerModel::GetSupportedOperations(size_t deviceID, const bool** isSupported, uint32_t& opCount)
|
||||
{
|
||||
if (m_liteGraph == nullptr) {
|
||||
LOGE("GetSupportedOperations failed. GetSupportedOperations() must be called after Finish().");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
DeviceManager& deviceManager = DeviceManager::GetInstance();
|
||||
|
||||
std::shared_ptr<Device> device = deviceManager.GetDevice(deviceID);
|
||||
if (device == nullptr) {
|
||||
LOGE("GetSupportedOperations failed, retrieve device failed.");
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
std::vector<bool> supportedOperations;
|
||||
OH_NN_ReturnCode ret = device->GetSupportedOperation(m_liteGraph, supportedOperations);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("GetSupportedOperations failed, error happened when get supported operations from devices.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_supportedOperations.clear();
|
||||
std::copy(supportedOperations.begin(), supportedOperations.end(), std::back_inserter(m_supportedOperations));
|
||||
|
||||
*isSupported = reinterpret_cast<bool*>(m_supportedOperations.data());
|
||||
opCount = m_supportedOperations.size();
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
std::shared_ptr<MSLITE::LiteGraph> InnerModel::GetLiteGraphs() const
|
||||
{
|
||||
return m_liteGraph;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NNTensor>> InnerModel::GetInputTensors() const
|
||||
{
|
||||
return m_inputTensors;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<NNTensor>> InnerModel::GetOutputTensors() const
|
||||
{
|
||||
return m_outputTensors;
|
||||
}
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
|
||||
#define NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "mindir.h"
|
||||
#include "ops_builder.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
class InnerModel {
|
||||
public:
|
||||
InnerModel();
|
||||
|
||||
bool IsBuild() const;
|
||||
OH_NN_ReturnCode BuildFromLiteGraph(const mindspore::lite::LiteGraph* liteGraph);
|
||||
OH_NN_ReturnCode AddTensor(const OH_NN_Tensor& nnTensor);
|
||||
OH_NN_ReturnCode SetTensorValue(uint32_t index, const void* buffer, size_t length);
|
||||
OH_NN_ReturnCode AddOperation(OH_NN_OperationType opType,
|
||||
const OH_NN_UInt32Array& paramIndices,
|
||||
const OH_NN_UInt32Array& inputIndices,
|
||||
const OH_NN_UInt32Array& outputIndices);
|
||||
OH_NN_ReturnCode GetSupportedOperations(size_t deviceID, const bool** isSupported, uint32_t& opCount);
|
||||
OH_NN_ReturnCode SpecifyInputsAndOutputs(
|
||||
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices);
|
||||
OH_NN_ReturnCode Build();
|
||||
std::vector<std::shared_ptr<NNTensor>> GetInputTensors() const;
|
||||
std::vector<std::shared_ptr<NNTensor>> GetOutputTensors() const;
|
||||
std::shared_ptr<mindspore::lite::LiteGraph> GetLiteGraphs() const;
|
||||
|
||||
private:
|
||||
void AddTensorsToLiteGraph(std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID);
|
||||
OH_NN_ReturnCode AddNodesToLiteGraph(const std::unordered_map<uint32_t, uint32_t>& modelIDToGraphID);
|
||||
OH_NN_ReturnCode ValidateInputAndOutput(
|
||||
const OH_NN_UInt32Array& inputIndices, const OH_NN_UInt32Array& outputIndices) const;
|
||||
OH_NN_ReturnCode ValidateTensorArray(const OH_NN_UInt32Array& indices) const;
|
||||
|
||||
private:
|
||||
std::vector<char> m_supportedOperations; // std::vector<bool> not support data(), use std::vector<char> instead.
|
||||
std::vector<uint32_t> m_inputIndices;
|
||||
std::vector<uint32_t> m_outputIndices;
|
||||
std::vector<std::unique_ptr<Ops::OpsBuilder>> m_ops;
|
||||
std::vector<std::shared_ptr<NNTensor>> m_allTensors;
|
||||
std::vector<std::shared_ptr<NNTensor>> m_inputTensors; // Used to pass input tensors to compilation.
|
||||
std::vector<std::shared_ptr<NNTensor>> m_outputTensors; // Used to pass output tensors to compilation.
|
||||
std::shared_ptr<mindspore::lite::LiteGraph> m_liteGraph {nullptr};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_INNER_MODEL_H
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#include "memory_manager.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "interfaces/oem/cpp_api/cpp_type.h"
|
||||
#include "common/log.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
void* MemoryManager::MapMemory(int fd, size_t length)
|
||||
{
|
||||
if (fd < 0) {
|
||||
LOGE("Invalid fd, fd must greater than 0.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (length <= 0 || length > ALLOCATE_BUFFER_LIMIT) {
|
||||
LOGE("Invalid buffer size, it must greater than 0 and less than 1Gb. length=%zu", length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* addr = mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (addr == MAP_FAILED) {
|
||||
LOGE("Map fd to address failed.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mtx);
|
||||
Memory memory {fd, addr, length};
|
||||
m_memorys.emplace(addr, memory);
|
||||
return addr;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode MemoryManager::UnMapMemory(const void* buffer)
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Buffer is nullptr, no need to release.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto iter = m_memorys.find(buffer);
|
||||
if (iter == m_memorys.end()) {
|
||||
LOGE("This buffer is not found, cannot release.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& memory = m_memorys[buffer];
|
||||
auto unmapResult = munmap(const_cast<void*>(memory.data), memory.length);
|
||||
if (unmapResult != 0) {
|
||||
LOGE("Unmap memory failed. Please try again.");
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
memory.data = nullptr;
|
||||
|
||||
if (close(memory.fd) != 0) {
|
||||
LOGE("Close memory fd failed. fd=%d", memory.fd);
|
||||
return OH_NN_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mtx);
|
||||
m_memorys.erase(iter);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode MemoryManager::GetMemory(const void* buffer, Memory& memory) const
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
LOGE("Memory is nullptr.");
|
||||
return OH_NN_NULL_PTR;
|
||||
}
|
||||
|
||||
auto iter = m_memorys.find(buffer);
|
||||
if (iter == m_memorys.end()) {
|
||||
LOGE("Memory is not found.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
memory.fd = iter->second.fd;
|
||||
memory.data = buffer;
|
||||
memory.length = iter->second.length;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
} // NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
#include "interfaces/kits/c/neural_network_runtime_type.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
const int INVALID_FD = -1;
|
||||
|
||||
struct Memory {
|
||||
int fd;
|
||||
const void* data;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
class MemoryManager {
|
||||
public:
|
||||
~MemoryManager() = default;
|
||||
|
||||
void* MapMemory(int fd, size_t length);
|
||||
OH_NN_ReturnCode UnMapMemory(const void* buffer);
|
||||
OH_NN_ReturnCode GetMemory(const void* buffer, Memory& memory) const;
|
||||
|
||||
static MemoryManager* GetInstance()
|
||||
{
|
||||
static MemoryManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryManager() {};
|
||||
MemoryManager(const MemoryManager&) = delete;
|
||||
MemoryManager& operator=(const MemoryManager&) = delete;
|
||||
|
||||
private:
|
||||
// key: OH_NN_Memory, value: fd
|
||||
std::unordered_map<const void*, Memory> m_memorys;
|
||||
std::mutex m_mtx;
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_MEMORY_MANAGER_H
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "interfaces/innerkits/c/neural_network_runtime_inner.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime.h"
|
||||
|
||||
#include "compilation.h"
|
||||
#include "device_manager.h"
|
||||
#include "executor.h"
|
||||
#include "inner_model.h"
|
||||
#include "common/log.h"
|
||||
|
||||
|
||||
using namespace OHOS::NeuralNetworkRuntime;
|
||||
|
||||
#define NNRT_API __attribute__((visibility("default")))
|
||||
|
||||
NNRT_API OH_NNModel *OH_NNModel_Construct(void)
|
||||
{
|
||||
InnerModel *innerModel = new(std::nothrow) InnerModel();
|
||||
if (innerModel == nullptr) {
|
||||
LOGE("OH_NNModel_Construct failed, please check whether it has enough memory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NNModel *nnModel = reinterpret_cast<OH_NNModel*>(innerModel);
|
||||
return nnModel;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_AddTensor(OH_NNModel *model, const OH_NN_Tensor *tensor)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_AddTensor failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor == nullptr) {
|
||||
LOGE("OH_NNModel_AddTensor failed, passed nullptr to tensor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->AddTensor(*tensor);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_AddOperation(OH_NNModel *model,
|
||||
OH_NN_OperationType op,
|
||||
const OH_NN_UInt32Array *paramIndices,
|
||||
const OH_NN_UInt32Array *inputIndices,
|
||||
const OH_NN_UInt32Array *outputIndices)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_AddOperation failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (paramIndices == nullptr) {
|
||||
LOGE("OH_NNModel_AddOperation failed, passed nullptr to paramIndices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (inputIndices == nullptr) {
|
||||
LOGE("OH_NNModel_AddOperation failed, passed nullptr to inputIndices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (outputIndices == nullptr) {
|
||||
LOGE("OH_NNModel_AddOperation failed, passed nullptr to outputIndices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->AddOperation(op, *paramIndices, *inputIndices, *outputIndices);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_SetTensorData(OH_NNModel *model,
|
||||
uint32_t index,
|
||||
const void *dataBuffer,
|
||||
size_t length)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_SetTensorData failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (dataBuffer == nullptr) {
|
||||
LOGE("OH_NNModel_SetTensorData failed, passed nullptr to dataBuffer, which has no effect.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
LOGE("OH_NNModel_SetTensorData failed, passed dataBuffer with length 0, which has no effect.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->SetTensorValue(index, dataBuffer, length);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_SpecifyInputsAndOutputs(OH_NNModel *model,
|
||||
const OH_NN_UInt32Array *inputIndices,
|
||||
const OH_NN_UInt32Array *outputIndices)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (inputIndices == nullptr) {
|
||||
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to inputIndices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (outputIndices == nullptr) {
|
||||
LOGE("OH_NNModel_SpecifyInputsAndOutputs failed, passed nullptr to outputIndices.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->SpecifyInputsAndOutputs(*inputIndices, *outputIndices);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_Finish(OH_NNModel *model)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_Finish failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->Build();
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_BuildFromLiteGraph(OH_NNModel *model, const void *liteGraph)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_BuildFromLiteGraph failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (liteGraph == nullptr) {
|
||||
LOGE("OH_NNModel_BuildFromLiteGraph failed, passed nullptr to liteGraph.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto *pLiteGraph = static_cast<const mindspore::lite::LiteGraph*>(liteGraph);
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
|
||||
// Once the innerModel built from the liteGraph successfully, the innerModel
|
||||
// owns the liteGraph, in which case, the invoker should not delete
|
||||
// the liteGraph actively. Otherwise, the invoker still has the ownership.
|
||||
return innerModel->BuildFromLiteGraph(pLiteGraph);
|
||||
}
|
||||
|
||||
NNRT_API void OH_NNModel_Destroy(OH_NNModel **model)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGW("OH_NNModel_Destroy has no effect, passed nullptr to model.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*model == nullptr) {
|
||||
LOGW("OH_NNModel_Destroy has no effect, passed nullptr to *model.");
|
||||
return;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(*model);
|
||||
delete innerModel;
|
||||
*model = nullptr;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNModel_GetAvailableOperations(OH_NNModel *model,
|
||||
size_t deviceID,
|
||||
const bool **isAvailable,
|
||||
uint32_t *opCount)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to model.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (isAvailable == nullptr) {
|
||||
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to isAvailable.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (*isAvailable != nullptr) {
|
||||
LOGE("OH_NNModel_GetAvailableOperations failed, *isAvailable is not nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (opCount == nullptr) {
|
||||
LOGE("OH_NNModel_GetAvailableOperations failed, passed nullptr to opCount.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
InnerModel *innerModel = reinterpret_cast<InnerModel*>(model);
|
||||
return innerModel->GetSupportedOperations(deviceID, isAvailable, *opCount);
|
||||
}
|
||||
|
||||
NNRT_API OH_NNCompilation *OH_NNCompilation_Construct(const OH_NNModel *model)
|
||||
{
|
||||
if (model == nullptr) {
|
||||
LOGE("OH_NNCompilation_Construct failed, passed nullptr to model.");
|
||||
return nullptr;
|
||||
}
|
||||
const InnerModel *innerModel = reinterpret_cast<const InnerModel*>(model);
|
||||
|
||||
if (!innerModel->IsBuild()) {
|
||||
LOGE("OH_NNCompilation_Construct failed, should call OH_NNModel_Finish before creating compilation.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compilation *compilation = new(std::nothrow) Compilation(innerModel);
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_Construct failed, please check whether it has enough memory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NNCompilation* nnCompilation = reinterpret_cast<OH_NNCompilation*>(compilation);
|
||||
return nnCompilation;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetDevice(OH_NNCompilation *compilation, size_t deviceID)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_SetDevice failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->SetDevice(deviceID);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetCache(OH_NNCompilation *compilation,
|
||||
const char *cachePath,
|
||||
uint32_t version)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_SetCache failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (cachePath == nullptr) {
|
||||
LOGE("OH_NNCompilation_SetCache failed, passed nullptr to cachePath.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->SetCacheDir(cachePath, version);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetPerformanceMode(OH_NNCompilation *compilation,
|
||||
OH_NN_PerformanceMode performanceMode)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_SetPerformanceMode failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->SetPerformance(performanceMode);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_SetPriority(OH_NNCompilation *compilation,
|
||||
OH_NN_Priority priority)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_SetPriority failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->SetPriority(priority);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_EnableFloat16(OH_NNCompilation *compilation, bool enableFloat16)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_EnableFloat16 failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->SetEnableFp16(enableFloat16);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNCompilation_Build(OH_NNCompilation *compilation)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNCompilation_Build failed, passed nullptr to compilation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Compilation* innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
return innerCompilation->Build();
|
||||
}
|
||||
|
||||
NNRT_API void OH_NNCompilation_Destroy(OH_NNCompilation **compilation)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGW("OH_NNCompilation_Destroy has no effect, passed nullptr to compilation.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*compilation == nullptr) {
|
||||
LOGW("OH_NNCompilation_Destroy has no effect, passed nullptr to *compilation.");
|
||||
return;
|
||||
}
|
||||
|
||||
Compilation *innerCompilation = reinterpret_cast<Compilation*>(*compilation);
|
||||
delete innerCompilation;
|
||||
*compilation = nullptr;
|
||||
}
|
||||
|
||||
NNRT_API OH_NNExecutor *OH_NNExecutor_Construct(OH_NNCompilation *compilation)
|
||||
{
|
||||
if (compilation == nullptr) {
|
||||
LOGE("OH_NNExecutor_Construct failed, passed nullptr to compilation.");
|
||||
return nullptr;
|
||||
}
|
||||
Compilation *innerCompilation = reinterpret_cast<Compilation*>(compilation);
|
||||
|
||||
if (!innerCompilation->IsBuild()) {
|
||||
LOGE("OH_NNExecutor_Construct failed, should call OH_NNCompilation_Build before creating executor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Executor* executor = new(std::nothrow) Executor(innerCompilation);
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_Construct failed, please check whether it has enough memory.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NNExecutor* nnExecutor = reinterpret_cast<OH_NNExecutor*>(executor);
|
||||
return nnExecutor;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetInput(OH_NNExecutor *executor,
|
||||
uint32_t inputIndex,
|
||||
const OH_NN_Tensor *tensor,
|
||||
const void *dataBuffer,
|
||||
size_t length)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to tensor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (dataBuffer == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInput failed, passed nullptr to dataBuffer.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
LOGE("OH_NNExecutor_SetInput failed, dataBuffer length is 0.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->SetInput(inputIndex, *tensor, dataBuffer, length);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetOutput(OH_NNExecutor *executor,
|
||||
uint32_t outputIndex,
|
||||
void *dataBuffer,
|
||||
size_t length)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetOutput failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (dataBuffer == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetOutput failed, passed nullptr to dataBuffer.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
LOGE("OH_NNExecutor_SetOutput failed, dataBuffer length is 0.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->SetOutput(outputIndex, dataBuffer, length);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_GetOutputShape(OH_NNExecutor *executor,
|
||||
uint32_t outputIndex,
|
||||
int32_t **shape,
|
||||
uint32_t *shapeLength)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (shape == nullptr) {
|
||||
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to shape.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (*shape != nullptr) {
|
||||
LOGE("OH_NNExecutor_GetOutputShape failed, *shape is not nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (shapeLength == nullptr) {
|
||||
LOGE("OH_NNExecutor_GetOutputShape failed, passed nullptr to shapeLength.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor* innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->GetOutputShape(outputIndex, shape, *shapeLength);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_Run(OH_NNExecutor *executor)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_Run failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->Run();
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_Memory *OH_NNExecutor_AllocateInputMemory(OH_NNExecutor *executor, uint32_t inputIndex, size_t length)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_AllocateInputMemory failed, passed nullptr to executor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
LOGW("OH_NNExecutor_AllocateInputMemory has no effect, passed length equals 0.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NN_Memory *nnMemory = nullptr;
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
OH_NN_ReturnCode ret = innerExecutor->CreateInputMemory(inputIndex, length, &nnMemory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("OH_NNExecutor_AllocateInputMemory failed, error happened when creating input memory in executor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nnMemory;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_Memory *OH_NNExecutor_AllocateOutputMemory(OH_NNExecutor *executor, uint32_t outputIndex, size_t length)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_AllocateOutputMemory failed, passed nullptr to executor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
LOGW("OH_NNExecutor_AllocateOutputMemory has no effect, passed length equals 0.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OH_NN_Memory *nnMemory = nullptr;
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
OH_NN_ReturnCode ret = innerExecutor->CreateOutputMemory(outputIndex, length, &nnMemory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("OH_NNExecutor_AllocateOutputMemory failed, error happened when creating output memory in executor.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nnMemory;
|
||||
}
|
||||
|
||||
NNRT_API void OH_NNExecutor_DestroyInputMemory(OH_NNExecutor *executor, uint32_t inputIndex, OH_NN_Memory **memory)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_DestroyInputMemory failed, passed nullptr to executor.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (memory == nullptr) {
|
||||
LOGW("OH_NNExecutor_DestroyInputMemory has no effect, passed nullptr to memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*memory == nullptr) {
|
||||
LOGW("OH_NNExecutor_DestroyInputMemory has no effect, passed nullptr to *memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
OH_NN_ReturnCode ret = innerExecutor->DestroyInputMemory(inputIndex, memory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("OH_NNExecutor_DestroyInputMemory failed, error happened when destroying input memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
*memory = nullptr;
|
||||
}
|
||||
|
||||
NNRT_API void OH_NNExecutor_DestroyOutputMemory(OH_NNExecutor *executor, uint32_t outputIndex, OH_NN_Memory **memory)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_DestroyOutputMemory failed, passed nullptr to executor.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (memory == nullptr) {
|
||||
LOGW("OH_NNExecutor_DestroyOutputMemory has no effect, passed nullptr to memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*memory == nullptr) {
|
||||
LOGW("OH_NNExecutor_DestroyOutputMemory has no effect, passed nullptr to *memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
OH_NN_ReturnCode ret = innerExecutor->DestroyOutputMemory(outputIndex, memory);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("OH_NNExecutor_DestroyOutputMemory failed, error happened when destroying output memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
*memory = nullptr;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetInputWithMemory(OH_NNExecutor *executor,
|
||||
uint32_t inputIndex,
|
||||
const OH_NN_Tensor *tensor,
|
||||
const OH_NN_Memory *memory)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to tensor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (memory == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetInputWithMemory failed, passed nullptr to memory.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->SetInputFromMemory(inputIndex, *tensor, *memory);
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNExecutor_SetOutputWithMemory(OH_NNExecutor *executor,
|
||||
uint32_t outputIndex,
|
||||
const OH_NN_Memory *memory)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetOutputWithMemory failed, passed nullptr to executor.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (memory == nullptr) {
|
||||
LOGE("OH_NNExecutor_SetOutputWithMemory failed, passed nullptr to memory.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(executor);
|
||||
return innerExecutor->SetOutputFromMemory(outputIndex, *memory);
|
||||
}
|
||||
|
||||
NNRT_API void OH_NNExecutor_Destroy(OH_NNExecutor **executor)
|
||||
{
|
||||
if (executor == nullptr) {
|
||||
LOGW("OH_NNExecutor_Destroy has no effect, since executor is nullptr.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*executor) == nullptr) {
|
||||
LOGW("OH_NNExecutor_Destroy has no effect, since *executor is nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
Executor *innerExecutor = reinterpret_cast<Executor*>(*executor);
|
||||
delete innerExecutor;
|
||||
*executor = nullptr;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetAllDevicesID(const size_t **allDevicesID, uint32_t *deviceCount)
|
||||
{
|
||||
if (allDevicesID == nullptr) {
|
||||
LOGE("OH_NNDevice_GetAllDevicesID failed, passed nullptr to allDevicesID.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((*allDevicesID) != nullptr) {
|
||||
LOGE("OH_NNDevice_GetAllDevicesID failed, *allDevicesID should be nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (deviceCount == nullptr) {
|
||||
LOGE("OH_NNDevice_GetAllDevicesID failed, passed nullptr to deviceCount.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DeviceManager& deviceManager = DeviceManager::GetInstance();
|
||||
const std::vector<size_t>& allDevices = deviceManager.GetAllDeviceId();
|
||||
|
||||
if (allDevices.empty()) {
|
||||
LOGW("OH_NNDevice_GetAllDevicesID got no device.");
|
||||
*allDevicesID = nullptr;
|
||||
*deviceCount = 0;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
*allDevicesID = allDevices.data();
|
||||
// allDevices.size() will not exceed UINT32_MAX, it is safe to cast to uint32_t.
|
||||
*deviceCount = static_cast<uint32_t>(allDevices.size());
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetName(size_t deviceID, const char **name)
|
||||
{
|
||||
if (name == nullptr) {
|
||||
LOGE("OH_NNDevice_GetName failed, passed nullptr to name.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((*name) != nullptr) {
|
||||
LOGE("OH_NNDevice_GetName failed, *name should be nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
DeviceManager& deviceManager = DeviceManager::GetInstance();
|
||||
const std::string& deviceName = deviceManager.GetDeviceName(deviceID);
|
||||
if (deviceName.empty()) {
|
||||
LOGE("OH_NNDevice_GetName failed, error happened when getting name of deviceID %zu.", deviceID);
|
||||
*name = nullptr;
|
||||
return OH_NN_FAILED;
|
||||
}
|
||||
|
||||
*name = deviceName.data();
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
NNRT_API OH_NN_ReturnCode OH_NNDevice_GetType(size_t deviceID, OH_NN_DeviceType* deviceType)
|
||||
{
|
||||
DeviceManager& deviceManager = DeviceManager::GetInstance();
|
||||
std::shared_ptr<Device> device = deviceManager.GetDevice(deviceID);
|
||||
if (device == nullptr) {
|
||||
LOGE("OH_NNDevice_GetName failed, passed invalid deviceID.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (deviceType == nullptr) {
|
||||
LOGE("OH_NNDevice_GetType failed, passed nullptr to deviceType.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = device->GetDeviceType(*deviceType);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("OH_NNDevice_GetType failed, device id: %zu.", deviceID);
|
||||
return ret;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
#include "nn_tensor.h"
|
||||
#include "validation.h"
|
||||
#include "transform.h"
|
||||
#include "common/log.h"
|
||||
#include "mindir.h"
|
||||
#include "mindir_types.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
const uint32_t SUPPORT_NUM_BIT = 8; // Currently support 8-bit quantization only
|
||||
const uint32_t INVALID_NUM_BIT = 0;
|
||||
|
||||
void DestroyLiteGraphTensor(void* tensor)
|
||||
{
|
||||
mindspore::lite::MindIR_Tensor_Destroy(&tensor);
|
||||
}
|
||||
|
||||
NNTensor::~NNTensor()
|
||||
{
|
||||
if (m_buffer != nullptr) {
|
||||
delete [] reinterpret_cast<char*>(m_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
NNTensor::NNTensor(NNTensor&& tensor) noexcept
|
||||
{
|
||||
*this = std::move(tensor);
|
||||
}
|
||||
|
||||
NNTensor& NNTensor::operator=(NNTensor&& tensor) noexcept
|
||||
{
|
||||
if (this == &tensor) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
m_type = tensor.m_type;
|
||||
m_dataType = tensor.m_dataType;
|
||||
m_format = tensor.m_format;
|
||||
m_name = std::move(tensor.m_name);
|
||||
m_dimensions = std::move(tensor.m_dimensions);
|
||||
m_quantParams = std::move(tensor.m_quantParams);
|
||||
m_elementCount = tensor.m_elementCount;
|
||||
m_isDynamicShape = tensor.m_isDynamicShape;
|
||||
m_isOpParameter = tensor.m_isOpParameter;
|
||||
m_buffer = tensor.m_buffer;
|
||||
m_bufferLength = tensor.m_bufferLength;
|
||||
m_dataLength = tensor.m_dataLength;
|
||||
|
||||
tensor.m_buffer = nullptr;
|
||||
tensor.m_bufferLength = 0;
|
||||
tensor.m_dataLength = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::Build(OH_NN_DataType dataType,
|
||||
const std::vector<int32_t>& dimensions,
|
||||
const std::vector<QuantParam>& quantParam,
|
||||
OH_NN_TensorType type)
|
||||
{
|
||||
m_type = type;
|
||||
|
||||
if (!Validation::ValidateTensorDataType(dataType)) {
|
||||
LOGE("Build failed, passed invalid data type.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_dataType = dataType;
|
||||
|
||||
OH_NN_ReturnCode ret = ParseDimensions(dimensions);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Build failed, passed invalid dimensions.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ParseQuantParams(quantParam);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("Build failed, please check quantParam.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
m_type = nnTensor.type;
|
||||
|
||||
if (!Validation::ValidateTensorDataType(nnTensor.dataType)) {
|
||||
LOGE("BuildFromOHNNTensor failed, passed invalid data type: %d.", nnTensor.dataType);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_dataType = nnTensor.dataType;
|
||||
|
||||
if (!Validation::ValidateTensorType(nnTensor.type)) {
|
||||
LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor type: %d.", nnTensor.type);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = ParseDimensions(nnTensor);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromOHNNTensor failed, passed invalid nnTensor dimensions.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ParseQuantParams(nnTensor.quantParam);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromOHNNTensor failed, please check quantParam in nnTensor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::ParseDimensions(const std::vector<int32_t>& dimensions)
|
||||
{
|
||||
// Temporary variable to check overflow.
|
||||
uint64_t absoluteDim {0};
|
||||
uint64_t elementCount {1};
|
||||
uint64_t dataLength {static_cast<uint64_t>(GetTypeSize(m_dataType))};
|
||||
m_isDynamicShape = false;
|
||||
for (int32_t dim : dimensions) {
|
||||
if (dim < -1 || dim == 0) {
|
||||
LOGE("ParseDimension failed, dimension of OH_NN_Tensor cannot be 0 or less than -1, receive %d.", dim);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_isDynamicShape = m_isDynamicShape || (dim == -1);
|
||||
absoluteDim = static_cast<uint64_t>(abs(dim));
|
||||
elementCount *= absoluteDim;
|
||||
dataLength *= absoluteDim;
|
||||
|
||||
if (dataLength > UINT32_MAX) {
|
||||
LOGE("ParseDimension failed, expected data length of tensor exceed limit %u.", UINT32_MAX);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_isDynamicShape) {
|
||||
// If tensor has dynamic shape, m_elementCount and m_dataLength take 0.
|
||||
m_elementCount = 0;
|
||||
m_dataLength = 0;
|
||||
} else {
|
||||
m_elementCount = static_cast<uint32_t>(elementCount);
|
||||
m_dataLength = static_cast<size_t>(dataLength);
|
||||
}
|
||||
|
||||
m_dimensions = std::move(dimensions);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::ParseDimensions(const OH_NN_Tensor& nnTensor)
|
||||
{
|
||||
OH_NN_ReturnCode ret = Validation::ValidateArray(nnTensor.dimensions, nnTensor.dimensionCount);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromOHNNTensor failed, please check dimension and dimensionCount in NNTensor.");
|
||||
return ret;
|
||||
}
|
||||
std::vector<int32_t> dimensions = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
|
||||
|
||||
ret = ParseDimensions(dimensions);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("BuildFromOHNNTensor failed, passed invalid dimension info.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::ParseQuantParams(const OH_NN_QuantParam* quantParam)
|
||||
{
|
||||
if (quantParam == nullptr) {
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
if ((quantParam->numBits == nullptr) || (quantParam->scale == nullptr) || (quantParam->zeroPoint == nullptr)) {
|
||||
LOGE("ParseQuantParams failed, scale or zeroPoint is nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
std::vector<QuantParam> tmpQuantParam;
|
||||
uint32_t numBits{0};
|
||||
double scale{0.0};
|
||||
int32_t zeroPoint{0};
|
||||
for (uint32_t i = 0; i < quantParam->quantCount; i++) {
|
||||
numBits = quantParam->numBits[i];
|
||||
scale = quantParam->scale[i];
|
||||
zeroPoint = quantParam->zeroPoint[i];
|
||||
tmpQuantParam.emplace_back((QuantParam){numBits, scale, zeroPoint});
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ret = ParseQuantParams(tmpQuantParam);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("ParseQuantParams failed, please numBits in NNTensor.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::ParseQuantParams(const std::vector<QuantParam>& quantParams)
|
||||
{
|
||||
for (const QuantParam& param : quantParams) {
|
||||
// Only support 8-bit quantization in NNR version 1.0
|
||||
if ((param.numBits != SUPPORT_NUM_BIT) || (param.numBits == INVALID_NUM_BIT)) {
|
||||
LOGE("ParseQuantParams failed, get invalid numBits %d.", param.numBits);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
m_quantParams = quantParams;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void NNTensor::IdentifyOpParameter()
|
||||
{
|
||||
m_isOpParameter = true;
|
||||
}
|
||||
|
||||
void NNTensor::SetName(const std::string& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
// Buffer set inside NNTensor will be released during deconstruction, make sure the buffer won't be released twice.
|
||||
void NNTensor::SetBuffer(const void* buffer, size_t length)
|
||||
{
|
||||
// copy pointer instead of memory copying
|
||||
m_buffer = const_cast<void*>(buffer);
|
||||
m_bufferLength = length;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode NNTensor::SetDimensions(const std::vector<int32_t>& dimensions)
|
||||
{
|
||||
size_t expectedDimensionCount = m_dimensions.size();
|
||||
size_t dimensionCount = dimensions.size();
|
||||
if (dimensionCount != expectedDimensionCount) {
|
||||
LOGE("Passed dimensions have different dimension counts from NNTensor, expected %zu, but passed %zu.",
|
||||
expectedDimensionCount, dimensionCount);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto ret = ParseDimensions(dimensions);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("SetDimemsions failed, passed invalid dimension info.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_dimensions = dimensions;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_TensorType NNTensor::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::string NNTensor::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void* NNTensor::GetBuffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
size_t NNTensor::GetBufferLength() const
|
||||
{
|
||||
return m_bufferLength;
|
||||
}
|
||||
|
||||
size_t NNTensor::GetDataLength() const
|
||||
{
|
||||
return m_dataLength;
|
||||
}
|
||||
|
||||
OH_NN_DataType NNTensor::GetDataType() const
|
||||
{
|
||||
return m_dataType;
|
||||
}
|
||||
|
||||
uint32_t NNTensor::GetElementCount() const
|
||||
{
|
||||
return m_elementCount;
|
||||
}
|
||||
|
||||
std::vector<int32_t> NNTensor::GetDimensions() const
|
||||
{
|
||||
return m_dimensions;
|
||||
}
|
||||
|
||||
OH_NN_Format NNTensor::GetFormat() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
std::vector<QuantParam> NNTensor::GetQuantParam() const
|
||||
{
|
||||
return m_quantParams;
|
||||
}
|
||||
|
||||
LiteGraphTensorPtr NNTensor::ConvertToLiteGraphTensor() const
|
||||
{
|
||||
mindspore::lite::DataType dataType = NNToMS::TransformDataType(m_dataType);
|
||||
mindspore::lite::Format format = NNToMS::TransformFormat(m_format);
|
||||
const uint8_t* buffer = static_cast<const uint8_t*>(m_buffer);
|
||||
std::vector<uint8_t> data = ConstructVectorFromArray(buffer, m_dataLength);
|
||||
|
||||
std::vector<mindspore::lite::QuantParam> quantParams;
|
||||
mindspore::lite::QuantParam msQuantParam;
|
||||
for (const QuantParam& param : m_quantParams) {
|
||||
msQuantParam = {param.zeroPoint, param.scale, param.numBits};
|
||||
quantParams.emplace_back(std::move(msQuantParam));
|
||||
}
|
||||
|
||||
mindspore::lite::TensorPtr tensor = mindspore::lite::MindIR_Tensor_Create(
|
||||
m_name, dataType, m_dimensions, format, data, quantParams);
|
||||
if (tensor == nullptr) {
|
||||
LOGE("ConvertToLiteGraphTensor failed, please check attributes of NNTensor.");
|
||||
return {nullptr, DestroyLiteGraphTensor};
|
||||
}
|
||||
|
||||
LiteGraphTensorPtr liteGraphTensor(tensor, DestroyLiteGraphTensor);
|
||||
return liteGraphTensor;
|
||||
}
|
||||
|
||||
void NNTensor::ConvertToIOTensor(IOTensor& tensor) const
|
||||
{
|
||||
tensor.dataType = m_dataType;
|
||||
tensor.format = m_format;
|
||||
tensor.dimensions = m_dimensions;
|
||||
tensor.data = const_cast<void*>(m_buffer);
|
||||
tensor.length = m_bufferLength;
|
||||
}
|
||||
|
||||
bool NNTensor::IsDynamicShape() const
|
||||
{
|
||||
return m_isDynamicShape;
|
||||
}
|
||||
|
||||
bool NNTensor::IsQuantTensor() const
|
||||
{
|
||||
return (m_quantParams.size() > 0);
|
||||
}
|
||||
|
||||
bool NNTensor::IsScalar() const
|
||||
{
|
||||
return (m_dimensions.empty());
|
||||
}
|
||||
|
||||
bool NNTensor::IsOpParameter() const
|
||||
{
|
||||
return m_isOpParameter;
|
||||
}
|
||||
|
||||
bool NNTensor::CompareAttribute(const NNTensor& tensor) const
|
||||
{
|
||||
if (m_dataType != tensor.GetDataType()) {
|
||||
LOGI("Tensors have different data type: %d and %d.", m_dataType, tensor.GetDataType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_format != tensor.GetFormat()) {
|
||||
LOGI("Tensors have different format: %d and %d.", m_format, tensor.GetFormat());
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<int32_t> dimensions = tensor.GetDimensions();
|
||||
if (m_dimensions.size() != dimensions.size()) {
|
||||
LOGI("Tensors have differents dimension counts: %zu and %zu.", m_dimensions.size(), dimensions.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < dimensions.size(); i++) {
|
||||
if (m_dimensions[i] != -1 && m_dimensions[i] != dimensions[i]) {
|
||||
LOGI("Tensors have different dimension: dimension index: %u, dimension value: %d and %d.",
|
||||
i, m_dimensions[i], dimensions[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_type != tensor.GetType()) {
|
||||
LOGI("Tensors have different type: %d and %d.", m_type, tensor.GetType());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // NeuralNetworkRuntime
|
||||
} // OHOS
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
|
||||
#define NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "interfaces/oem/cpp_api/cpp_type.h"
|
||||
#include "interfaces/kits/c/neural_network_runtime.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
using LiteGraphTensorPtr = std::unique_ptr<void, void(*)(void*)>;
|
||||
|
||||
void DestroyLiteGraphTensor(void* tensor);
|
||||
|
||||
class NNTensor {
|
||||
public:
|
||||
NNTensor() = default;
|
||||
~NNTensor();
|
||||
NNTensor(NNTensor&& tensor) noexcept;
|
||||
NNTensor& operator=(NNTensor&& tensor) noexcept;
|
||||
// Copy construction and assignment is not allowed in case of double-free of m_buffer
|
||||
NNTensor(const NNTensor& tensor) = delete;
|
||||
NNTensor& operator=(const NNTensor& tensor) = delete;
|
||||
|
||||
OH_NN_ReturnCode BuildFromOHNNTensor(const OH_NN_Tensor& nnTensor);
|
||||
OH_NN_ReturnCode Build(OH_NN_DataType dataType,
|
||||
const std::vector<int32_t>& dimensions,
|
||||
const std::vector<QuantParam>& quantParam,
|
||||
OH_NN_TensorType type);
|
||||
void IdentifyOpParameter();
|
||||
|
||||
void SetName(const std::string& name);
|
||||
void SetBuffer(const void* buffer, size_t length);
|
||||
OH_NN_ReturnCode SetDimensions(const std::vector<int32_t>& dimensions);
|
||||
|
||||
std::string GetName() const;
|
||||
OH_NN_TensorType GetType() const;
|
||||
void* GetBuffer() const;
|
||||
// Return complete buffer length
|
||||
size_t GetBufferLength() const;
|
||||
// Return actual data length, since the data can be store in a larger buffer
|
||||
size_t GetDataLength() const;
|
||||
OH_NN_DataType GetDataType() const;
|
||||
uint32_t GetElementCount() const;
|
||||
std::vector<int32_t> GetDimensions() const;
|
||||
OH_NN_Format GetFormat() const;
|
||||
std::vector<QuantParam> GetQuantParam() const;
|
||||
LiteGraphTensorPtr ConvertToLiteGraphTensor() const;
|
||||
void ConvertToIOTensor(IOTensor& tensor) const;
|
||||
|
||||
bool IsDynamicShape() const;
|
||||
bool IsQuantTensor() const;
|
||||
bool IsScalar() const;
|
||||
bool IsOpParameter() const;
|
||||
bool CompareAttribute(const NNTensor& tensor) const;
|
||||
|
||||
private:
|
||||
// Used in BuildFromOHNNTensor()
|
||||
OH_NN_ReturnCode ParseQuantParams(const OH_NN_QuantParam* quantParams);
|
||||
OH_NN_ReturnCode ParseDimensions(const OH_NN_Tensor& nnTensor);
|
||||
// Used in Build()
|
||||
OH_NN_ReturnCode ParseQuantParams(const std::vector<QuantParam>& quantParams);
|
||||
OH_NN_ReturnCode ParseDimensions(const std::vector<int32_t>& dimensions);
|
||||
|
||||
private:
|
||||
OH_NN_TensorType m_type {OH_NN_TENSOR};
|
||||
OH_NN_DataType m_dataType {OH_NN_FLOAT32};
|
||||
OH_NN_Format m_format {OH_NN_FORMAT_NHWC};
|
||||
std::string m_name;
|
||||
std::vector<int32_t> m_dimensions;
|
||||
std::vector<QuantParam> m_quantParams;
|
||||
uint32_t m_elementCount {0};
|
||||
bool m_isDynamicShape {false};
|
||||
bool m_isOpParameter {false};
|
||||
void* m_buffer {nullptr};
|
||||
size_t m_bufferLength {0};
|
||||
size_t m_dataLength {0};
|
||||
};
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_NN_TENSOR_H
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "add_builder.h"
|
||||
|
||||
#include "frameworks/native/transform.h"
|
||||
#include "frameworks/native/validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 2;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const std::string OP_NAME = "Add";
|
||||
|
||||
AddBuilder::AddBuilder() {}
|
||||
|
||||
AddBuilder::~AddBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode AddBuilder::SetActivation(std::shared_ptr<NNTensor>& tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT8) {
|
||||
LOGE("[Add] SetActivation failed, the activationType should be type OH_NN_INT8.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Add] SetActivation GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
int8_t* fuseData = static_cast<int8_t*>(buffer);
|
||||
if (!Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*fuseData))) {
|
||||
LOGE("[Add] SetActivation failed, fuse activation type is invalid.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_activationType = NNToMS::TransfromFusionType(static_cast<OH_NN_FuseType>(*fuseData));
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode AddBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Add] Build failed, operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
auto ret = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Add] Build failed, the input or output index of Add operation is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
for (uint32_t i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_ADD_ACTIVATIONTYPE:
|
||||
ret = SetActivation(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[Add] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Add] Build failed, passed invalid param.");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_name = OP_NAME;
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr AddBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[Add] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_AddFusion_CreatePrimitive(m_activationType);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive) ;
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(AddBuilder, OH_NN_OPS_ADD);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class AddBuilder : public OpsBuilder {
|
||||
public:
|
||||
AddBuilder();
|
||||
~AddBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetActivation(std::shared_ptr<NNTensor>& tensor);
|
||||
|
||||
private:
|
||||
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_ADD_BUILDER_H
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "argmax_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 1;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const std::string OP_NAME = "ArgMax";
|
||||
|
||||
ArgMaxBuilder::ArgMaxBuilder() {}
|
||||
|
||||
ArgMaxBuilder::~ArgMaxBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode ArgMaxBuilder::SetAxis(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[ArgMax] SetAxis failed, the axis should be type HNN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[ArgMax] SetAxis GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_axis = *(static_cast<int64_t*>(buffer));
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ArgMaxBuilder::SetKeepdims(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_BOOL) {
|
||||
LOGE("[ArgMax] SetKeepdims failed, the keep_dims should be type HNN_BOOL.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[ArgMax] SetKeepdims GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_keepDims = *(static_cast<bool*>(buffer));
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build method.
|
||||
* 1.build primitive of ops.
|
||||
* 2.build inputIndex of ops.
|
||||
* 3.build outputIndex of ops.
|
||||
*/
|
||||
OH_NN_ReturnCode ArgMaxBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[ArgMax] Build failed, build operation has been completed, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[ArgMax] Build failed, passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
const std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_ARG_MAX_AXIS:
|
||||
returnCode = SetAxis(tensor);
|
||||
break;
|
||||
case OH_NN_ARG_MAX_KEEPDIMS:
|
||||
returnCode = SetKeepdims(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[ArgMax] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[ArgMax] Build failed, passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_name = OP_NAME;
|
||||
m_isBuild = true;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr ArgMaxBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[ArgMax] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_ArgMaxFusion_CreatePrimitive(m_axis, m_topK, m_keepDims, m_outMaxValue);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
REGISTER_OPS(ArgMaxBuilder, OH_NN_OPS_ARG_MAX);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class ArgMaxBuilder : public OpsBuilder {
|
||||
public:
|
||||
ArgMaxBuilder();
|
||||
~ArgMaxBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetAxis(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetKeepdims(std::shared_ptr<NNTensor> tensor);
|
||||
|
||||
private:
|
||||
int64_t m_axis {-1};
|
||||
int64_t m_topK {1};
|
||||
bool m_keepDims {false};
|
||||
bool m_outMaxValue {false};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_ARGMAX_BUILDER_H
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "avgpool_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const std::string OP_NAME = "AvgPool";
|
||||
|
||||
AvgPoolBuilder::AvgPoolBuilder() {}
|
||||
|
||||
AvgPoolBuilder::~AvgPoolBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode AvgPoolBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
OH_NN_ReturnCode returnCode = PoolingBuild(paramsIndex, inputsIndex, outputsIndex, allTensors);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[AvgPool] Build failed, the PoolingBuild failed.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr AvgPoolBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[AvgPool] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_AvgPoolFusion_CreatePrimitive(m_kernelSize, m_strides, m_pad,
|
||||
m_padMode, m_roundMode, m_format, m_global, m_activationType);
|
||||
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(AvgPoolBuilder, OH_NN_OPS_AVG_POOL);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_AVGPOOL_BUILDER_H
|
||||
#define NEURAL_NETWORK_AVGPOOL_BUILDER_H
|
||||
|
||||
#include "pooling_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class AvgPoolBuilder : public PoolingBuilder {
|
||||
public:
|
||||
AvgPoolBuilder();
|
||||
~AvgPoolBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_AVGPOOL_BUILDER_H
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "batch_to_space_nd_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 1;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const int CROPS_ROWS = 2;
|
||||
static const int CROPS_COLUMN = 2;
|
||||
static const std::string OP_NAME = "BatchToSpaceND";
|
||||
|
||||
BatchToSpaceNDBuilder::BatchToSpaceNDBuilder() {}
|
||||
|
||||
BatchToSpaceNDBuilder::~BatchToSpaceNDBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode BatchToSpaceNDBuilder::SetInputBlock(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[BatchToSpaceND] SetInputBlock failed, the BlockSize should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[BatchToSpaceND] SetInputBlock GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
int64_t* pBlockSize = static_cast<int64_t*>(buffer);
|
||||
|
||||
uint32_t elementCount = tensor->GetElementCount();
|
||||
for (uint32_t i = 0; i < elementCount; ++i) {
|
||||
m_blockSize.emplace_back(*pBlockSize);
|
||||
++pBlockSize;
|
||||
}
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode BatchToSpaceNDBuilder::SetInputCrops(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[BatchToSpaceND] SetInputCrops failed, the Crops should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[BatchToSpaceND] SetInputCrops GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
int64_t* pCropsData = static_cast<int64_t*>(buffer);
|
||||
|
||||
std::vector<std::vector<int64_t>> cropsData;
|
||||
for (int i = 0; i < CROPS_ROWS; i++) {
|
||||
std::vector<int64_t> vect_data;
|
||||
vect_data.reserve(CROPS_COLUMN);
|
||||
for (int j = 0; j < CROPS_COLUMN; j++) {
|
||||
vect_data.push_back(*pCropsData++);
|
||||
}
|
||||
cropsData.push_back(vect_data);
|
||||
}
|
||||
m_crops = cropsData;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode BatchToSpaceNDBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[BatchToSpaceND] Build failed, operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[BatchToSpaceND] Build failed, passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_BATCH_TO_SPACE_ND_BLOCKSIZE:
|
||||
returnCode = SetInputBlock(tensor);
|
||||
break;
|
||||
case OH_NN_BATCH_TO_SPACE_ND_CROPS:
|
||||
returnCode = SetInputCrops(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[BatchToSpaceND] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[BatchToSpaceND] Build failed, passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr BatchToSpaceNDBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[BatchToSpaceND] Cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_BatchToSpaceND_CreatePrimitive(m_blockSize, m_crops);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(BatchToSpaceNDBuilder, OH_NN_OPS_BATCH_TO_SPACE_ND);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
|
||||
#define NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class BatchToSpaceNDBuilder : public OpsBuilder {
|
||||
public:
|
||||
BatchToSpaceNDBuilder();
|
||||
~BatchToSpaceNDBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetBatchToSpaceInput(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
OH_NN_ReturnCode SetInputBlock(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetInputCrops(std::shared_ptr<NNTensor> tensor);
|
||||
|
||||
private:
|
||||
std::vector<int64_t> m_blockSize;
|
||||
std::vector<std::vector<int64_t>> m_crops;
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_BATCHTOSPACEND_BUILDER_H
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "batchnorm_builder.h"
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 5;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const int SCALAR_LENGTH = 1;
|
||||
const std::string OP_NAME = "BatchNorm";
|
||||
|
||||
BatchNormBuilder::BatchNormBuilder() {}
|
||||
|
||||
BatchNormBuilder::~BatchNormBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode BatchNormBuilder::SetEpsilon(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
if (tensor->GetDataType() != OH_NN_FLOAT32) {
|
||||
LOGE("[BatchNorm] SetEpsilon failed, the Epsilon should be type OH_NN_FLOAT32.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetElementCount() != SCALAR_LENGTH) {
|
||||
LOGE("[BatchNorm] SetEpsilon failed, the Epsilon shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[BatchNorm] SetEpsilon failed, the epsilon passed a empty buffer.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_epsilon = *static_cast<float*>(buffer);
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode BatchNormBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[BatchNorm] Build failed, batchNorm operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[BatchNorm] Build failed, passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_BATCH_NORM_EPSILON:
|
||||
returnCode = SetEpsilon(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[BatchNorm] Parameter Type is invalid, type=%d", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[BatchNorm] BatchNorm Build failed,, Passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr BatchNormBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[BatchNorm] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_FusedBatchNorm_CreatePrimitive(m_epsilon);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(BatchNormBuilder, OH_NN_OPS_BATCH_NORM);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class BatchNormBuilder : public OpsBuilder {
|
||||
public:
|
||||
BatchNormBuilder();
|
||||
~BatchNormBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetEpsilon(std::shared_ptr<NNTensor> tensor);
|
||||
|
||||
private:
|
||||
float m_epsilon{1e-7};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_BATHNORM_BUILDER_H
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "bias_add_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 2;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const std::string OP_NAME = "BiasAdd";
|
||||
|
||||
BiasAddBuilder::BiasAddBuilder() {}
|
||||
|
||||
BiasAddBuilder::~BiasAddBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode BiasAddBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[BiasAdd] Build failed, biasAdd operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[BiasAdd] Build failed, passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
if (!paramsIndex.empty()) {
|
||||
LOGE("[BiasAdd] Build failed, expects no parameters");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr BiasAddBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[BiasAdd] Build failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_BiasAdd_CreatePrimitive();
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(BiasAddBuilder, OH_NN_OPS_BIAS_ADD);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class BiasAddBuilder : public OpsBuilder {
|
||||
public:
|
||||
BiasAddBuilder();
|
||||
~BiasAddBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_BIASADD_BUILDER_H
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "cast_builder.h"
|
||||
|
||||
#include "frameworks/native/transform.h"
|
||||
#include "frameworks/native/validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static const int INPUT_NUM = 2;
|
||||
static const int OUTPUT_NUM = 1;
|
||||
static const int INPUT_TYPE = 1;
|
||||
static const std::string OP_NAME = "Cast";
|
||||
|
||||
CastBuilder::CastBuilder() {}
|
||||
|
||||
CastBuilder::~CastBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode CastBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Cast] Build failed, operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
auto ret = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (ret != OH_NN_SUCCESS) {
|
||||
LOGE("[Cast] Build failed, the input or output index of Cast operation is invalid.");
|
||||
return ret;
|
||||
}
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
auto castType = allTensors[inputsIndex[INPUT_TYPE]]->GetBuffer();
|
||||
if (castType == nullptr) {
|
||||
LOGE("[Cast] Build castType GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
OH_NN_DataType* castTypeInt = reinterpret_cast<OH_NN_DataType *>(castType);
|
||||
if (!Validation::ValidateTensorDataType(*castTypeInt)) {
|
||||
LOGE("[Cast] Type of cast operator is not validation.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
*castTypeInt = (OH_NN_DataType)NNToHDI::TransDataType(*castTypeInt);
|
||||
|
||||
if (!paramsIndex.empty()) {
|
||||
LOGE("[Cast] Cast expects no parameters");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr CastBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[Cast] Cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_Cast_CreatePrimitive();
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(CastBuilder, OH_NN_OPS_CAST);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class CastBuilder : public OpsBuilder {
|
||||
public:
|
||||
CastBuilder();
|
||||
~CastBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_CAST_BUILDER_H
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "concat_builder.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static constexpr int MINIMUM_INTPUT = 2;
|
||||
static constexpr int OUTPUT_NUM = 1;
|
||||
static constexpr int AXIS_LENGTH = 1;
|
||||
static const std::string OP_NAME = "Concat";
|
||||
|
||||
ConcatBuilder::ConcatBuilder() {}
|
||||
|
||||
ConcatBuilder::~ConcatBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode ConcatBuilder::SetAxis(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetElementCount() != AXIS_LENGTH) {
|
||||
LOGE("[Concat] SetAxis failed, the Activation shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Concat] SetAxis failed, the axis should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Concat] SetAxis GetBuffer return nullptr.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_axis = *(static_cast<int64_t*>(buffer));
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ConcatBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Concat] Build failed, operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
if (inputsIndex.size() < MINIMUM_INTPUT) {
|
||||
LOGE("[Concat] Build failed, Concat need more than one inputs.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (outputsIndex.size() != OUTPUT_NUM) {
|
||||
LOGE("[Concat] Build failed, The number of index of outputs not equal to 1.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = SetInputsAndOutputs(inputsIndex, outputsIndex, allTensors);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Concat] Build failed, set inputs or outputs failed.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_CONCAT_AXIS:
|
||||
returnCode = SetAxis(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[Concat] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Concat] Build failed, passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode ConcatBuilder::SetInputsAndOutputs(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
size_t allTensorsSize = allTensors.size();
|
||||
for (auto index : inputsIndex) {
|
||||
if (index >= allTensorsSize) {
|
||||
LOGE("[Concat] Invalid input index, it is out of range %zu.", allTensorsSize);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto index : outputsIndex) {
|
||||
if (index >= allTensorsSize) {
|
||||
LOGE("[Concat] Invalid output index, it is out of range %zu.", allTensorsSize);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
m_inputsIndex.clear();
|
||||
m_inputsIndex = inputsIndex;
|
||||
|
||||
m_outputsIndex.clear();
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr ConcatBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[Concat] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = mindspore::lite::MindIR_Concat_CreatePrimitive(m_axis);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(ConcatBuilder, OH_NN_OPS_CONCAT);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
|
||||
|
||||
#include "mindir.h"
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class ConcatBuilder : public OpsBuilder {
|
||||
public:
|
||||
ConcatBuilder();
|
||||
~ConcatBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetAxis(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetInputsAndOutputs(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
private:
|
||||
int64_t m_axis{0};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_CONCAT_BUILDER_H
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "conv2d_builder.h"
|
||||
|
||||
#include "frameworks/native/transform.h"
|
||||
#include "frameworks/native/validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static constexpr int INPUT_NUM = 3;
|
||||
static constexpr int OUTPUT_NUM = 1;
|
||||
static constexpr int CONV2D_INPUT_WEIGHT = 1;
|
||||
static constexpr int WEIGHT_SIZE = 4;
|
||||
static constexpr int OUT_CHANNEL_INDEX = 0;
|
||||
static constexpr int IN_CHANNEL_INDEX = 3;
|
||||
static constexpr int KERNEL_HEIGHT_INDEX = 1;
|
||||
static constexpr int KERNEL_WEIGHT_INDEX = 2;
|
||||
static constexpr int PAD_MODE_GET = 1;
|
||||
static constexpr int PAD_LIST_GET = 4;
|
||||
static constexpr int SCALAR_LENGTH = 1;
|
||||
static const std::string OP_NAME = "Conv2D";
|
||||
|
||||
Conv2DBuilder::Conv2DBuilder() {}
|
||||
|
||||
Conv2DBuilder::~Conv2DBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetInputAndOutput(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Conv2d] SetInputAndOutput failed, passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetChannel(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
// set inChannel, outChannel, kernelSize
|
||||
auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
|
||||
if (weightShape.size() != WEIGHT_SIZE) {
|
||||
LOGE("[Conv2d] SetChannel failed, the dimension of weight should be %d", WEIGHT_SIZE);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_inChannel = weightShape[IN_CHANNEL_INDEX];
|
||||
m_outChannel = weightShape[OUT_CHANNEL_INDEX];
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void Conv2DBuilder::SetKernelSize(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
// set inChannel, outChannel, kernelSize
|
||||
auto weightShape = allTensors[inputsIndex[CONV2D_INPUT_WEIGHT]]->GetDimensions();
|
||||
|
||||
m_kernelSize.clear();
|
||||
m_kernelSize.emplace_back(weightShape[KERNEL_HEIGHT_INDEX]);
|
||||
m_kernelSize.emplace_back(weightShape[KERNEL_WEIGHT_INDEX]);
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetStrides(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Strides
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2d] SetStrides failed, the Strides should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2d] SetStrides GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
const int64_t* pStrides = reinterpret_cast<const int64_t*>(buffer);
|
||||
int stridesSize = tensor->GetElementCount();
|
||||
m_strides.assign(pStrides, pStrides + stridesSize);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetDilation(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Dilation
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2d] SetDilation failed, the Dilation should have type OH_NN_INT64");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2d] SetDilation GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
const int64_t* pDilation = reinterpret_cast<const int64_t*>(buffer);
|
||||
int dilationSize = tensor->GetElementCount();
|
||||
m_dilation.assign(pDilation, pDilation + dilationSize);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetPad(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
bool isPadMode = false;
|
||||
if (tensor->GetElementCount() == PAD_MODE_GET) {
|
||||
isPadMode = true;
|
||||
} else if (tensor->GetElementCount() != PAD_LIST_GET) {
|
||||
LOGE("[Conv2d] SetPad failed, inputs should be 1 for padMode and 4 for padList.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2d] SetPadList GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Set PadMode or PadList
|
||||
if (isPadMode) {
|
||||
if (tensor->GetDataType() != OH_NN_INT8) {
|
||||
LOGE("[Conv2d] SetPad failed, the PadMode should have type OH_NN_INT8.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
int8_t* pPad = static_cast<int8_t*>(buffer);
|
||||
if (!OHOS::NeuralNetworkRuntime::Validation::ValidatePadMode(*pPad)) {
|
||||
LOGE("[Conv2d] SetPad failed, invalid pad mode.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_padMode = NNToMS::TransformPadModeValue(*pPad);
|
||||
} else {
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2d] SetPad failed, the PadList should have type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
int64_t* pPadList = static_cast<int64_t*>(buffer);
|
||||
int padListSize = tensor->GetElementCount();
|
||||
m_pad.assign(pPadList, pPadList + padListSize);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetGroup(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Group
|
||||
if (tensor->GetElementCount() != SCALAR_LENGTH) {
|
||||
LOGE("[Conv2d] SetGroup failed, The Group shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2d] SetGroup failed, The Group should have type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_group = *static_cast<int64_t*>(buffer);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::SetActavitation(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetElementCount() != SCALAR_LENGTH) {
|
||||
LOGE("[Conv2d] SetActavitation failed, the ActivationType shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT8) {
|
||||
LOGE("[Conv2d] SetActavitation failed, the ActivationType should have type OH_NN_INT8.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2d] SetGroup GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
int8_t* pFuseData = static_cast<int8_t*>(buffer);
|
||||
if (!OHOS::NeuralNetworkRuntime::Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*pFuseData))) {
|
||||
LOGE("[Conv2d] SetActavitation failed, activation input is invalid.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_activationType = NNToMS::TransfromFusionType((OH_NN_FuseType)(*pFuseData));
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex, const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Conv2d] Build failed, Conv2D operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = SetInputAndOutput(inputsIndex, outputsIndex, allTensors);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
returnCode = SetChannel(inputsIndex, allTensors);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
SetKernelSize(inputsIndex, allTensors);
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i];
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_CONV2D_STRIDES:
|
||||
returnCode = SetStrides(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_DILATION:
|
||||
returnCode = SetDilation(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_PAD_MODE:
|
||||
case OH_NN_CONV2D_PAD:
|
||||
returnCode = SetPad(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_GROUP:
|
||||
returnCode = SetGroup(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_ACTIVATION_TYPE:
|
||||
returnCode = SetActavitation(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[Conv2D] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Conv2D] Build failed, Passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr Conv2DBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[Conv2d] GetPrimitive failed, Cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
auto primitive = MindIR_Conv2DFusion_CreatePrimitive(m_kernelSize, m_strides,
|
||||
m_dilation, m_padMode, m_pad, m_group, m_inChannel, m_outChannel, m_activationType);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(Conv2DBuilder, OH_NN_OPS_CONV2D);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
#include "mindir.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class Conv2DBuilder : public OpsBuilder {
|
||||
public:
|
||||
Conv2DBuilder();
|
||||
~Conv2DBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetInputAndOutput(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
OH_NN_ReturnCode SetChannel(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
void SetKernelSize(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
OH_NN_ReturnCode SetStrides(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetDilation(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetPad(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetGroup(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetActavitation(std::shared_ptr<NNTensor> tensor);
|
||||
|
||||
private:
|
||||
int64_t m_group{1};
|
||||
int64_t m_inChannel{0};
|
||||
int64_t m_outChannel{0};
|
||||
std::vector<int64_t> m_kernelSize;
|
||||
std::vector<int64_t> m_strides;
|
||||
std::vector<int64_t> m_pad;
|
||||
std::vector<int64_t> m_dilation;
|
||||
mindspore::lite::PadMode m_padMode{mindspore::lite::PAD_MODE_PAD};
|
||||
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
#endif // NEURAL_NETWORK_RUNTIME_CONV2D_BUILDER_H
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "conv2d_transpose_builder.h"
|
||||
|
||||
#include "frameworks/native/transform.h"
|
||||
#include "frameworks/native/validation.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
static constexpr int INPUT_NUM = 3;
|
||||
static constexpr int OUTPUT_NUM = 1;
|
||||
static constexpr int INPUT_WEIGHT = 1;
|
||||
static constexpr int WEIGHT_SIZE = 4;
|
||||
static constexpr int OUT_CHANNEL_INDEX = 0;
|
||||
static constexpr int IN_CHANNEL_INDEX = 3;
|
||||
static constexpr int KERNEL_HEIGHT_INDEX = 1;
|
||||
static constexpr int KERNEL_WEIGHT_INDEX = 2;
|
||||
static constexpr int PAD_MODE_PARAM_NUM = 1;
|
||||
static constexpr int PAD_LIST_PARAM_NUM = 4;
|
||||
static constexpr int SCALAR_LENGTH = 1;
|
||||
static const std::string OP_NAME = "Conv2DTranspose";
|
||||
|
||||
Conv2DTransposeBuilder::Conv2DTransposeBuilder() {}
|
||||
|
||||
Conv2DTransposeBuilder::~Conv2DTransposeBuilder() {}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetInput(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
OH_NN_ReturnCode returnCode = CheckIOIndex(inputsIndex, outputsIndex, allTensors, INPUT_NUM, OUTPUT_NUM);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Conv2dTranspose] SetInput failed, Passed invalid input or output index.");
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
m_inputsIndex = inputsIndex;
|
||||
m_outputsIndex = outputsIndex;
|
||||
|
||||
// set inChannel, outChannel, kernelSize
|
||||
auto weightShape = allTensors[inputsIndex[INPUT_WEIGHT]]->GetDimensions();
|
||||
if (weightShape.size() != WEIGHT_SIZE) {
|
||||
LOGE("[Conv2dTranspose] SetInput failed, the dimension of weight should be %d", WEIGHT_SIZE);
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
m_inChannel = weightShape[IN_CHANNEL_INDEX];
|
||||
m_outChannel = weightShape[OUT_CHANNEL_INDEX];
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
void Conv2DTransposeBuilder::SetKernelSize(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
auto weightShape = allTensors[inputsIndex[INPUT_WEIGHT]]->GetDimensions();
|
||||
|
||||
m_kernelSize.clear();
|
||||
m_kernelSize.emplace_back(weightShape[KERNEL_HEIGHT_INDEX]);
|
||||
m_kernelSize.emplace_back(weightShape[KERNEL_WEIGHT_INDEX]);
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetStrides(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Strides
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2DTranspose] SetStrides failed, the Strides should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetStrides GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
const int64_t* pStrides = reinterpret_cast<const int64_t*>(buffer);
|
||||
int elementSize = tensor->GetElementCount();
|
||||
m_strides.assign(pStrides, pStrides + elementSize);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetDilation(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Dilation
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2DTranspose] SetDilation failed, the Dilation should be type OH_NN_INT64");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetDilation GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
const int64_t* pDilation = reinterpret_cast<const int64_t*>(buffer);
|
||||
int dilationSize = tensor->GetElementCount();
|
||||
m_dilation.assign(pDilation, pDilation + dilationSize);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetPad(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
bool isPadMode = false;
|
||||
if (tensor->GetElementCount() == PAD_MODE_PARAM_NUM) {
|
||||
isPadMode = true;
|
||||
} else if (tensor->GetElementCount() != PAD_LIST_PARAM_NUM) {
|
||||
LOGE("[Conv2DTranspose] SetPad failed, the inputs should be 1 if using padMode or 4 if using padList.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetPadMode GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Set PadMode or PadList
|
||||
if (isPadMode) {
|
||||
if (tensor->GetDataType() != OH_NN_INT8) {
|
||||
LOGE("[Conv2DTranspose] SetPad failed, the PadMode should have type OH_NN_INT8.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
int8_t* pPad = static_cast<int8_t*>(buffer);
|
||||
if (!OHOS::NeuralNetworkRuntime::Validation::ValidatePadMode(*pPad)) {
|
||||
LOGE("[Conv2DTranspose] SetPad failed, invalid pad mode.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_padMode = NNToMS::TransformPadModeValue(*pPad);
|
||||
} else {
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2DTranspose] SetPad failed, the PadList should have type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const int64_t* pPadList = reinterpret_cast<const int64_t*>(buffer);
|
||||
int padListPadSize = tensor->GetElementCount();
|
||||
m_padList.assign(pPadList, pPadList + padListPadSize);
|
||||
}
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetGroup(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set Group
|
||||
if (tensor->GetElementCount() != SCALAR_LENGTH) {
|
||||
LOGE("[Conv2dTranspose] SetGroup failed, the Group shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2dTranspose] SetGroup failed, the Group should have type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetGroup GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_group = *reinterpret_cast<const int64_t*>(buffer);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetOutPadding(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
// Set outputPadding
|
||||
if (tensor->GetDataType() != OH_NN_INT64) {
|
||||
LOGE("[Conv2DTranspose] SetOutPadding failed, the outputPadding should be type OH_NN_INT64.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetOutPadding GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
const int64_t* pOutputPadding = reinterpret_cast<const int64_t*>(buffer);
|
||||
int outputPadSize = tensor->GetElementCount();
|
||||
m_outputPaddings.assign(pOutputPadding, pOutputPadding + outputPadSize);
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::SetActivation(std::shared_ptr<NNTensor> tensor)
|
||||
{
|
||||
tensor->IdentifyOpParameter();
|
||||
|
||||
if (tensor->GetElementCount() != SCALAR_LENGTH) {
|
||||
LOGE("[Conv2DTranspose] SetActivation failed, the ActivationType shoule be a scalar");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (tensor->GetDataType() != OH_NN_INT8) {
|
||||
LOGE("[Conv2DTranspose] SetActivation failed, the ActivationType should have type OH_NN_INT8.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
void* buffer = tensor->GetBuffer();
|
||||
if (buffer == nullptr) {
|
||||
LOGE("[Conv2DTranspose] SetOutPadding GetBuffer return nullptr");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
int8_t* pFuseData = static_cast<int8_t*>(buffer);
|
||||
if (!OHOS::NeuralNetworkRuntime::Validation::ValidateFuseType(static_cast<OH_NN_FuseType>(*pFuseData))) {
|
||||
LOGE("[Conv2DTranspose] SetActivation failed, activation input is invalid.");
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
m_activationType = NNToMS::TransfromFusionType((OH_NN_FuseType)(*pFuseData));
|
||||
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode Conv2DTransposeBuilder::Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors)
|
||||
{
|
||||
if (m_isBuild) {
|
||||
LOGE("[Conv2DTranspose] Build failed, conv2DTranspose operation has been build, cannot build again.");
|
||||
return OH_NN_OPERATION_FORBIDDEN;
|
||||
}
|
||||
|
||||
OH_NN_ReturnCode returnCode = SetInput(inputsIndex, outputsIndex, allTensors);
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
SetKernelSize(inputsIndex, allTensors);
|
||||
|
||||
for (int i : paramsIndex) {
|
||||
std::shared_ptr<NNTensor> tensor = allTensors[i]; // 参数 tensor
|
||||
switch (tensor->GetType()) {
|
||||
case OH_NN_CONV2D_TRANSPOSE_STRIDES:
|
||||
returnCode = SetStrides(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_TRANSPOSE_DILATION:
|
||||
returnCode = SetDilation(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_TRANSPOSE_PAD_MODE:
|
||||
case OH_NN_CONV2D_TRANSPOSE_PAD:
|
||||
returnCode = SetPad(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_TRANSPOSE_GROUP:
|
||||
returnCode = SetGroup(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_TRANSPOSE_OUTPUT_PADDINGS:
|
||||
returnCode = SetOutPadding(tensor);
|
||||
break;
|
||||
case OH_NN_CONV2D_TRANSPOSE_ACTIVATION_TYPE:
|
||||
returnCode = SetActivation(tensor);
|
||||
break;
|
||||
default:
|
||||
LOGE("[Conv2DTranspose] Build failed, param invalid, type = %d.", tensor->GetType());
|
||||
return OH_NN_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (returnCode != OH_NN_SUCCESS) {
|
||||
LOGE("[Conv2DTranspose] Build failed, passed invalid param.");
|
||||
return returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
// The quantization type of the first output determinies that of the operator.
|
||||
SetQuantType(outputsIndex, allTensors);
|
||||
|
||||
m_isBuild = true;
|
||||
m_name = OP_NAME;
|
||||
return OH_NN_SUCCESS;
|
||||
}
|
||||
|
||||
LiteGraphPrimitvePtr Conv2DTransposeBuilder::GetPrimitive()
|
||||
{
|
||||
if (!m_isBuild) {
|
||||
LOGE("[Conv2DTranspose] GetPrimitive failed, cannot get primitive before call build.");
|
||||
return {nullptr, DestroyLiteGraphPrimitive};
|
||||
}
|
||||
|
||||
void* primitive = MindIR_Conv2dTransposeFusion_CreatePrimitive(m_kernelSize,
|
||||
m_strides, m_dilation, m_padMode, m_padList, m_group, m_inChannel, m_outChannel,
|
||||
m_activationType, m_outputPaddings);
|
||||
LiteGraphPrimitvePtr graphPrimitivePtr(primitive, DestroyLiteGraphPrimitive);
|
||||
return graphPrimitivePtr;
|
||||
}
|
||||
|
||||
REGISTER_OPS(Conv2DTransposeBuilder, OH_NN_OPS_CONV2D_TRANSPOSE);
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H
|
||||
#define NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H
|
||||
|
||||
#include "frameworks/native/ops_builder.h"
|
||||
#include "frameworks/native/ops_registry.h"
|
||||
#include "mindir.h"
|
||||
|
||||
namespace OHOS {
|
||||
namespace NeuralNetworkRuntime {
|
||||
namespace Ops {
|
||||
class Conv2DTransposeBuilder : public OpsBuilder {
|
||||
public:
|
||||
Conv2DTransposeBuilder();
|
||||
~Conv2DTransposeBuilder() override;
|
||||
OH_NN_ReturnCode Build(const std::vector<uint32_t>& paramsIndex,
|
||||
const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors) override;
|
||||
|
||||
LiteGraphPrimitvePtr GetPrimitive() override;
|
||||
|
||||
private:
|
||||
OH_NN_ReturnCode SetInput(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<uint32_t>& outputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
void SetKernelSize(const std::vector<uint32_t>& inputsIndex,
|
||||
const std::vector<std::shared_ptr<NNTensor>>& allTensors);
|
||||
OH_NN_ReturnCode SetStrides(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetDilation(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetPad(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetGroup(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetOutPadding(std::shared_ptr<NNTensor> tensor);
|
||||
OH_NN_ReturnCode SetActivation(std::shared_ptr<NNTensor> tensor);
|
||||
|
||||
private:
|
||||
int64_t m_group{1};
|
||||
int64_t m_inChannel{0};
|
||||
int64_t m_outChannel{0};
|
||||
std::vector<int64_t> m_kernelSize;
|
||||
std::vector<int64_t> m_strides;
|
||||
std::vector<int64_t> m_padList;
|
||||
std::vector<int64_t> m_dilation;
|
||||
std::vector<int64_t> m_outputPaddings;
|
||||
mindspore::lite::PadMode m_padMode{mindspore::lite::PAD_MODE_PAD};
|
||||
mindspore::lite::ActivationType m_activationType{mindspore::lite::ACTIVATION_TYPE_NO_ACTIVATION};
|
||||
};
|
||||
} // namespace Ops
|
||||
} // namespace NeuralNetworkRuntime
|
||||
} // namespace OHOS
|
||||
|
||||
#endif // NEURAL_NETWORK_RUNTIME_CONV2DTRANSPOSE_BUILDER_H
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user