1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16
17 #include "nnexecutor.h"
18 #include "nntensor.h"
19 #include "log.h"
20 #include "cpp_type.h"
21
22 #include "securec.h"
23 #include "utils.h"
24 #include "scoped_trace.h"
25 #include "transform.h"
26
27 namespace OHOS {
28 namespace NeuralNetworkRuntime {
NNExecutor(size_t backendID,std::shared_ptr<Device> device,std::shared_ptr<PreparedModel> preparedModel,const std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & inputTensorDescs,const std::vector<std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType>> & outputTensorDescs)29 NNExecutor::NNExecutor(size_t backendID, std::shared_ptr<Device> device, std::shared_ptr<PreparedModel> preparedModel,
30 const std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& inputTensorDescs,
31 const std::vector<std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>>& outputTensorDescs)
32 : m_backendID(backendID),
33 m_device(device),
34 m_preparedModel(preparedModel),
35 m_inputTensorDescs(inputTensorDescs),
36 m_outputTensorDescs(outputTensorDescs) {}
37
GetInputDimVec() const38 OH_NN_ReturnCode NNExecutor::GetInputDimVec() const
39 {
40 std::vector<std::vector<uint32_t>> minInputDimsVec;
41 std::vector<std::vector<uint32_t>> maxInputDimsVec;
42 OH_NN_ReturnCode oldRet = m_preparedModel->GetInputDimRanges(minInputDimsVec, maxInputDimsVec);
43 if (oldRet != OH_NN_SUCCESS) {
44 LOGW("GetInputDimVec failed, current version don't support get input dim ranges.");
45 return OH_NN_OPERATION_FORBIDDEN;
46 }
47 size_t inputSize = minInputDimsVec.size();
48 if (inputSize != maxInputDimsVec.size()) {
49 LOGE("GetInputDimVece failed, size of minInputDimsVec is not equal to maxInputDimsVec.");
50 return OH_NN_INVALID_PARAMETER;
51 }
52 for (size_t i = 0; i < inputSize; i++) {
53 std::vector<size_t> minInputDimVec;
54 std::vector<size_t> maxInputDimVec;
55 size_t minInputDimVecSize = minInputDimsVec[i].size();
56 if (minInputDimVecSize != maxInputDimsVec[i].size()) {
57 LOGE("GetInputDimVec failed, size of the min input dims is not equal to the max input"
58 " dims of the %{public}zuth input.", i);
59 return OH_NN_INVALID_PARAMETER;
60 }
61 for (size_t j = 0; j < minInputDimVecSize; j++) {
62 minInputDimVec.emplace_back(static_cast<size_t>(minInputDimsVec[i][j]));
63 maxInputDimVec.emplace_back(static_cast<size_t>(maxInputDimsVec[i][j]));
64 }
65 m_minInputDimsVec.emplace_back(minInputDimVec);
66 m_maxInputDimsVec.emplace_back(maxInputDimVec);
67 }
68 return OH_NN_SUCCESS;
69 }
70
GetInputDimRange(size_t inputIndex,size_t ** minInputDims,size_t ** maxInputDims,size_t * shapeNum) const71 OH_NN_ReturnCode NNExecutor::GetInputDimRange(
72 size_t inputIndex, size_t** minInputDims, size_t** maxInputDims, size_t* shapeNum) const
73 {
74 if (minInputDims == nullptr) {
75 LOGE("NNExecutor::GetInputDimRange failed, minInputDims is nullptr.");
76 return OH_NN_INVALID_PARAMETER;
77 }
78 if (maxInputDims == nullptr) {
79 LOGE("NNExecutor::GetInputDimRange failed, maxInputDims is nullptr.");
80 return OH_NN_INVALID_PARAMETER;
81 }
82 if (shapeNum == nullptr) {
83 LOGE("NNExecutor::GetInputDimRange failed, shapeNum is nullptr.");
84 return OH_NN_INVALID_PARAMETER;
85 }
86
87 if (m_minInputDimsVec.empty()) {
88 OH_NN_ReturnCode ret = GetInputDimVec();
89 if (ret != OH_NN_SUCCESS) {
90 LOGE("NNExecutor::GetInputDimRange failed, GetInputDimVec failed.");
91 return ret;
92 }
93 }
94
95 if (inputIndex >= m_minInputDimsVec.size()) {
96 LOGE("NNExecutor::GetInputDimRange failed, inputIndex[%{public}zu] is out of range.", inputIndex);
97 return OH_NN_INVALID_PARAMETER;
98 }
99
100 *shapeNum = m_minInputDimsVec[inputIndex].size();
101 if (*shapeNum != m_maxInputDimsVec[inputIndex].size()) {
102 LOGE("NNExecutor::GetInputDimRange failed, size of the min input dims is not equal to the max input"
103 " dims of the %{public}zuth input.", inputIndex);
104 return OH_NN_INVALID_PARAMETER;
105 }
106 *minInputDims = m_minInputDimsVec[inputIndex].data();
107 *maxInputDims = m_maxInputDimsVec[inputIndex].data();
108 return OH_NN_SUCCESS;
109 }
110
GetOutputShape(uint32_t outputIndex,int32_t ** shape,uint32_t * shapeNum) const111 OH_NN_ReturnCode NNExecutor::GetOutputShape(uint32_t outputIndex, int32_t** shape, uint32_t* shapeNum) const
112 {
113 if (outputIndex >= m_outputTensorDescs.size()) {
114 LOGE("NNExecutor::GetOutputShape failed, outputIndex must be smaller than m_outputTensorDescs.size.");
115 return OH_NN_INVALID_PARAMETER;
116 }
117 if (m_outputTensorDescs[outputIndex].first == nullptr) {
118 LOGE("NNExecutor::GetOutputShape failed, tensor desc of output %{public}u is nullptr.", outputIndex);
119 return OH_NN_INVALID_PARAMETER;
120 }
121
122 auto tensorDesc = m_outputTensorDescs[outputIndex].first;
123 size_t shapeNumTmp = 0;
124 auto ret = tensorDesc->GetShape(shape, &shapeNumTmp);
125 if (ret != OH_NN_SUCCESS) {
126 LOGE("NNExecutor::GetOutputShape failed, failed to get shape from tensor desc.");
127 return ret;
128 }
129 *shapeNum = static_cast<uint32_t>(shapeNumTmp);
130
131 return OH_NN_SUCCESS;
132 }
133
GetInputNum() const134 size_t NNExecutor::GetInputNum() const
135 {
136 return m_inputTensorDescs.size();
137 }
138
GetOutputNum() const139 size_t NNExecutor::GetOutputNum() const
140 {
141 return m_outputTensorDescs.size();
142 }
143
CreateInputTensorDesc(size_t index) const144 NN_TensorDesc* NNExecutor::CreateInputTensorDesc(size_t index) const
145 {
146 if (index >= m_inputTensorDescs.size()) {
147 LOGE("NNExecutor::CreateInputTensorDesc failed, index must be smaller than m_inputTensorDescs.size.");
148 return nullptr;
149 }
150 if (m_inputTensorDescs[index].first == nullptr) {
151 LOGE("NNExecutor::CreateInputTensorDesc failed, tensor desc of input %{public}zu is nullptr.", index);
152 return nullptr;
153 }
154
155 TensorDesc* tensorDescImpl = new (std::nothrow) TensorDesc();
156 if (tensorDescImpl == nullptr) {
157 LOGE("NNExecutor::CreateInputTensorDesc failed, failed to create tensor desc.");
158 return nullptr;
159 }
160
161 // Copy the member attributes to new tensor description
162 *tensorDescImpl = *(m_inputTensorDescs[index].first.get());
163
164 return reinterpret_cast<NN_TensorDesc*>(tensorDescImpl);
165 }
166
CreateOutputTensorDesc(size_t index) const167 NN_TensorDesc* NNExecutor::CreateOutputTensorDesc(size_t index) const
168 {
169 if (index >= m_outputTensorDescs.size()) {
170 LOGE("NNExecutor::CreateOutputTensorDesc failed, index must be smaller than m_outputTensorDescs.size.");
171 return nullptr;
172 }
173 if (m_outputTensorDescs[index].first == nullptr) {
174 LOGE("NNExecutor::CreateOutputTensorDesc failed, tensor desc of output %{public}zu is nullptr.", index);
175 return nullptr;
176 }
177
178 TensorDesc* tensorDescImpl = new (std::nothrow) TensorDesc();
179 if (tensorDescImpl == nullptr) {
180 LOGE("NNExecutor::CreateOutputTensorDesc failed, failed to create tensor desc.");
181 return nullptr;
182 }
183
184 // Copy the member attributes to new tensor description
185 *tensorDescImpl = *(m_outputTensorDescs[index].first.get());
186
187 return reinterpret_cast<NN_TensorDesc*>(tensorDescImpl);
188 }
189
SetExtensionConfig(const std::unordered_map<std::string,std::vector<char>> & configs)190 OH_NN_ReturnCode NNExecutor::SetExtensionConfig(const std::unordered_map<std::string, std::vector<char>>& configs)
191 {
192 if (m_executorConfig == nullptr) {
193 m_executorConfig = new (std::nothrow) ExecutorConfig();
194 if (m_executorConfig == nullptr) {
195 LOGE("[NNExecutor] SetExtensionConfig, m_executorConfig create failed.");
196 return OH_NN_FAILED;
197 }
198 }
199
200 for (auto config : configs) {
201 char* configData = reinterpret_cast<char*>(config.second.data());
202 if (configData == nullptr) {
203 LOGE("[NNExecutor] SetExtensionConfig, key: %s, configData is nullptr.", config.first.c_str());
204 return OH_NN_FAILED;
205 }
206
207 if (!config.first.compare("callingPid")) {
208 m_executorConfig->callingPid = std::atoi(configData);
209 LOGD("[NNExecutor] SetExtensionConfig, callingPid: %{public}d.", m_executorConfig->callingPid);
210 }
211
212 if (!config.first.compare("hiaiModelId")) {
213 m_executorConfig->hiaiModelId = std::atoi(configData);
214 LOGD("[NNExecutor] SetExtensionConfig, hiaiModelId: %{public}d.", m_executorConfig->hiaiModelId);
215 }
216
217 if (!config.first.compare("isNeedModelLatency")) {
218 m_executorConfig->isNeedModelLatency = static_cast<bool>(*configData);
219 LOGD("[NNExecutor] SetExtensionConfig, isNeedModelLatency: %{public}d.",
220 m_executorConfig->isNeedModelLatency);
221 }
222 }
223
224 return OH_NN_SUCCESS;
225 }
226
GetExecutorConfig() const227 ExecutorConfig* NNExecutor::GetExecutorConfig() const
228 {
229 return m_executorConfig;
230 }
231
SetOnRunDone(NN_OnRunDone onRunDone)232 OH_NN_ReturnCode NNExecutor::SetOnRunDone(NN_OnRunDone onRunDone)
233 {
234 LOGE("NNExecutor::SetOnRunDone failed, SetOnRunDone is not supported.");
235 return OH_NN_OPERATION_FORBIDDEN;
236 }
237
SetOnServiceDied(NN_OnServiceDied onServiceDied)238 OH_NN_ReturnCode NNExecutor::SetOnServiceDied(NN_OnServiceDied onServiceDied)
239 {
240 LOGE("NNExecutor::SetOnServiceDied failed, SetOnServiceDied is not supported.");
241 return OH_NN_OPERATION_FORBIDDEN;
242 }
243
RunSync(NN_Tensor * inputTensors[],size_t inputSize,NN_Tensor * outputTensors[],size_t outputSize)244 OH_NN_ReturnCode NNExecutor::RunSync(NN_Tensor* inputTensors[], size_t inputSize,
245 NN_Tensor* outputTensors[], size_t outputSize)
246 {
247 if (m_inputTensorDescs.size() != inputSize) {
248 LOGE("NNExecutor::RunSync failed, inputSize:%{public}zu is not equal to model input size:%{public}zu",
249 inputSize, m_inputTensorDescs.size());
250 return OH_NN_INVALID_PARAMETER;
251 }
252 if (m_outputTensorDescs.size() != outputSize) {
253 LOGE("NNExecutor::RunSync failed, outputSize:%{public}zu is not equal to model output size:%{public}zu",
254 outputSize, m_outputTensorDescs.size());
255 return OH_NN_INVALID_PARAMETER;
256 }
257
258 OH_NN_ReturnCode ret {OH_NN_FAILED};
259 ret = CheckInputDimRanges(inputTensors, inputSize);
260 if (ret != OH_NN_OPERATION_FORBIDDEN && ret != OH_NN_SUCCESS) {
261 LOGE("NNExecutor::RunSync failed, failed to check input dim ranges.");
262 return ret;
263 }
264
265 OHOS::NeuralNetworkRuntime::IOTensor tensor;
266 std::vector<NN_Tensor*> inputTensorsVec;
267 for (size_t i = 0; i < inputSize; ++i) {
268 if (inputTensors[i] == nullptr) {
269 LOGE("NNExecutor::RunSync failed, input[%{public}zu] is nullptr.", i);
270 return OH_NN_INVALID_PARAMETER;
271 }
272 inputTensorsVec.emplace_back(inputTensors[i]);
273 }
274
275 std::vector<NN_Tensor*> outputTensorsVec;
276 for (size_t i = 0; i < outputSize; ++i) {
277 if (outputTensors[i] == nullptr) {
278 LOGE("NNExecutor::RunSync failed, output[%{public}zu] is nullptr.", i);
279 return OH_NN_INVALID_PARAMETER;
280 }
281 outputTensorsVec.emplace_back(outputTensors[i]);
282 }
283
284 std::vector<std::vector<int32_t>> outputsDims;
285 std::vector<bool> isSufficientDataBuffer;
286
287 ret = m_preparedModel->Run(inputTensorsVec, outputTensorsVec, outputsDims, isSufficientDataBuffer);
288 if (ret != OH_NN_SUCCESS) {
289 LOGE("NNExecutor::RunSync failed, failed to run in prepared model.");
290 return ret;
291 }
292
293 // Set the output NNTensor2_0's dimensions from output IOTensor if it is dynamic.
294 // NNTensor2_0::SetDimensions will check if the tensor buffer is enough for the new dimensions.
295 if (outputsDims.size() != outputSize) {
296 LOGE("NNExecutor::RunSync failed, size of outputsDims is not equal to outputTensors.");
297 return OH_NN_INVALID_PARAMETER;
298 }
299 for (size_t i = 0; i < outputSize; ++i) {
300 NNTensor2_0* nnTensor = reinterpret_cast<NNTensor2_0*>(outputTensors[i]);
301 TensorDesc* nnTensorDesc = nnTensor->GetTensorDesc();
302 if (nnTensorDesc == nullptr) {
303 LOGE("NNExecutor::RunSync failed, failed to get desc from tensor.");
304 return OH_NN_NULL_PTR;
305 }
306 ret = nnTensorDesc->SetShape(outputsDims[i].data(), outputsDims[i].size());
307 if (ret != OH_NN_SUCCESS) {
308 LOGE("NNExecutor::RunSync failed, error happened when setting output tensor's dimensions,"
309 " output id: %zu.", i);
310 return ret;
311 }
312 ret = m_outputTensorDescs[i].first->SetShape(outputsDims[i].data(), outputsDims[i].size());
313 if (ret != OH_NN_SUCCESS) {
314 LOGE("NNExecutor::RunSync failed, error happened when setting inner output tensor's dimensions,"
315 " output id: %zu.", i);
316 return ret;
317 }
318 }
319 return OH_NN_SUCCESS;
320 }
321
RunAsync(NN_Tensor * inputTensors[],size_t inputSize,NN_Tensor * outputTensors[],size_t outputSize,int32_t timeout,void * userData)322 OH_NN_ReturnCode NNExecutor::RunAsync(NN_Tensor* inputTensors[], size_t inputSize,
323 NN_Tensor* outputTensors[], size_t outputSize, int32_t timeout, void* userData)
324 {
325 LOGE("NNExecutor::RunAsync failed, RunAsync is not supported.");
326 return OH_NN_OPERATION_FORBIDDEN;
327 }
328
GetModelID(uint32_t & modelId) const329 OH_NN_ReturnCode NNExecutor::GetModelID(uint32_t& modelId) const
330 {
331 OH_NN_ReturnCode ret = m_preparedModel->GetModelID(modelId);
332 if (ret != OH_NN_SUCCESS) {
333 LOGE("GetModelID failed, some error happen when get model id for device.");
334 return ret;
335 }
336
337 return OH_NN_SUCCESS;
338 }
339
GetBackendID()340 size_t NNExecutor::GetBackendID()
341 {
342 return m_backendID;
343 }
344
CheckInputDimRanges(NN_Tensor * inputTensors[],size_t inputSize)345 OH_NN_ReturnCode NNExecutor::CheckInputDimRanges(NN_Tensor* inputTensors[], size_t inputSize)
346 {
347 std::vector<std::vector<uint32_t>> minInputDims;
348 std::vector<std::vector<uint32_t>> maxInputDims;
349 OH_NN_ReturnCode oldRet = m_preparedModel->GetInputDimRanges(minInputDims, maxInputDims);
350 if (oldRet != OH_NN_SUCCESS) {
351 return OH_NN_OPERATION_FORBIDDEN;
352 }
353
354 if (inputSize != minInputDims.size()) {
355 LOGE("NNExecutor::CheckInputDimRanges failed, size of minInputDims:%{public}zu is not equal to "
356 "inputSize:%{public}zu.", minInputDims.size(), inputSize);
357 return OH_NN_INVALID_PARAMETER;
358 }
359
360 if (inputSize != maxInputDims.size()) {
361 LOGE("NNExecutor::CheckInputDimRanges failed, size of maxInputDims:%{public}zu is not equal to "
362 "inputSize:%{public}zu.", maxInputDims.size(), inputSize);
363 return OH_NN_INVALID_PARAMETER;
364 }
365
366 const NNTensor2_0* nnTensor = nullptr;
367 OH_NN_ReturnCode ret {OH_NN_FAILED};
368 for (size_t i = 0; i < inputSize; ++i) {
369 const std::vector<uint32_t>& minSingleInputDims = minInputDims[i];
370 const std::vector<uint32_t>& maxSingleInputDims = maxInputDims[i];
371 nnTensor = reinterpret_cast<const NNTensor2_0*>(inputTensors[i]);
372 if (nnTensor == nullptr) {
373 LOGE("NNExecutor::CheckInputDimRanges failed, input %{public}zu is nullptr.", i);
374 return OH_NN_NULL_PTR;
375 }
376 ret = nnTensor->CheckDimRanges(minSingleInputDims, maxSingleInputDims);
377 if (ret != OH_NN_SUCCESS) {
378 LOGE("NNExecutor::CheckInputDimRanges failed, failed to check input dim ranges of input %{public}zu", i);
379 return ret;
380 }
381 }
382
383 return OH_NN_SUCCESS;
384 }
385
CompareAttribute(const std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType> & tensorDesc,const NNTensor & tensor) const386 bool NNExecutor::CompareAttribute(
387 const std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>& tensorDesc, const NNTensor& tensor) const
388 {
389 OH_NN_DataType dataType;
390 auto ret = tensorDesc.first->GetDataType(&dataType);
391 if (ret != OH_NN_SUCCESS) {
392 LOGE("CompareAttribute failed, failed to get data type from tensor desc.");
393 return false;
394 }
395 if (dataType != tensor.GetDataType()) {
396 LOGI("Tensors have different data type: %d and %d.", dataType, tensor.GetDataType());
397 return false;
398 }
399
400 int32_t* shape {nullptr};
401 size_t shapeNum {0};
402 ret = tensorDesc.first->GetShape(&shape, &shapeNum);
403 if (ret != OH_NN_SUCCESS) {
404 LOGE("CompareAttribute failed, failed to get shape from tensor desc.");
405 return false;
406 }
407 const std::vector<int32_t> dimensions = tensor.GetDimensions();
408 if (shapeNum != dimensions.size()) {
409 LOGI("Tensors have differents dimension counts: %zu and %zu.", shapeNum, dimensions.size());
410 return false;
411 }
412
413 size_t dimensionsSize = dimensions.size();
414 for (size_t i = 0; i < dimensionsSize; i++) {
415 if ((shape[i] != -1) && (shape[i] != dimensions[i])) {
416 LOGI("Tensors have different dimension: dimension index: %zu, dimension value: %d and %d.",
417 i, shape[i], dimensions[i]);
418 return false;
419 }
420 }
421
422 if (tensorDesc.second != tensor.GetType()) {
423 LOGI("Tensors have different type: %{public}d and %{public}d.", tensorDesc.second, tensor.GetType());
424 return false;
425 }
426
427 return true;
428 }
429
BuildInputTensor(uint32_t index,const OH_NN_Tensor & nnTensor,std::shared_ptr<NNTensor> inputTensor) const430 OH_NN_ReturnCode NNExecutor::BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
431 std::shared_ptr<NNTensor> inputTensor) const
432 {
433 // Note: inputs have only shapes info.
434 if (index >= m_inputTensorDescs.size()) {
435 LOGE("BuildInputTensor failed, input index is out of range.");
436 return OH_NN_INVALID_PARAMETER;
437 }
438 if (m_inputTensorDescs[index].first == nullptr) {
439 LOGE("BuildInputTensor failed, tensor desc of input %{public}u is nullptr.", index);
440 return OH_NN_INVALID_PARAMETER;
441 }
442
443 // Build a tensor from nnTensor.
444 auto ret = inputTensor->BuildFromOHNNTensor(nnTensor);
445 if (ret != OH_NN_SUCCESS) {
446 LOGE("BuildInputTensor failed, please check input nnTensor.");
447 return ret;
448 }
449
450 if (inputTensor->IsDynamicShape()) {
451 LOGE("BuildInputTensor failed, input nnTensor should has certain dimensions which cannot contain -1.");
452 return OH_NN_INVALID_PARAMETER;
453 }
454
455 OH_NN_Format format;
456 ret = m_inputTensorDescs[index].first->GetFormat(&format);
457 if (ret != OH_NN_SUCCESS) {
458 LOGE("BuildInputTensor failed, failed to get tensor format from desc.");
459 return ret;
460 }
461 inputTensor->SetFormat(format);
462
463 if (!CompareAttribute(m_inputTensorDescs[index], *inputTensor)) {
464 LOGE("BuildInputTensor failed, input has different attributes from the one in the constructed model.");
465 return OH_NN_INVALID_PARAMETER;
466 }
467
468 const char* name {nullptr};
469 ret = m_inputTensorDescs[index].first->GetName(&name);
470 if (ret != OH_NN_SUCCESS) {
471 LOGE("BuildInputTensor failed, failed to get tensor name from desc.");
472 return ret;
473 }
474 inputTensor->SetName(name);
475 return OH_NN_SUCCESS;
476 }
477
478
SetInputTensorWithCurrentBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * buffer,size_t dataLength,size_t curBufferLength)479 OH_NN_ReturnCode NNExecutor::SetInputTensorWithCurrentBuffer(uint32_t index,
480 std::shared_ptr<NNTensor> inputTensor, const void* buffer, size_t dataLength, size_t curBufferLength)
481 {
482 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
483 errno_t status = memcpy_s(curBuffer, dataLength, buffer, dataLength);
484 // Current buffer inside m_inputTensors is managed by executor, no need to release if memcpy failed.
485 if (status != EOK) {
486 LOGE("SetInputTensorWithCurrentBuffe failed, copy data from user buffer to device buffer failed. "
487 "Error code: %d.", status);
488 return OH_NN_MEMORY_ERROR;
489 }
490
491 // Set the new tensor with the buffer of current tensor
492 inputTensor->SetBuffer(curBuffer, curBufferLength);
493
494 // The memory is reused here. Thus, current tensor's buffer must set to nullptr, in case the memory is released
495 // twice.
496 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
497
498 // Set to the new tensor, and release current one.
499 m_inputTensors[index].tensor = inputTensor;
500 return OH_NN_SUCCESS;
501 }
502
503
SetInputTensorWithNewBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * inputBuffer,size_t length,bool isInnerMem)504 void NNExecutor::SetInputTensorWithNewBuffer(uint32_t index,
505 std::shared_ptr<NNTensor> inputTensor, const void* inputBuffer, size_t length, bool isInnerMem)
506 {
507 // Release the memory inside the tensor first, if it is allocated by Executor during SetInput().
508 if (m_inputTensors.find(index) != m_inputTensors.end()) {
509 if (m_inputTensors[index].isInnerMem) {
510 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
511 m_device->ReleaseBuffer(curBuffer);
512 }
513 // Set current tensor's buffer to nullptr in case the NNTensor release the driver memory in destruction.
514 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
515 }
516
517 // Set new input tensor data buffer
518 inputTensor->SetBuffer(inputBuffer, length);
519
520 // Create or update the input tensor
521 ExeTensor exeTensor{inputTensor, nullptr, 0, isInnerMem};
522 m_inputTensors[index] = exeTensor;
523 }
524
525
CheckInputDimRanges(uint32_t index,const OH_NN_Tensor & nnTensor) const526 OH_NN_ReturnCode NNExecutor::CheckInputDimRanges(uint32_t index, const OH_NN_Tensor& nnTensor) const
527 {
528 std::vector<std::vector<uint32_t>> minInputDims;
529 std::vector<std::vector<uint32_t>> maxInputDims;
530 auto ret = m_preparedModel->GetInputDimRanges(minInputDims, maxInputDims);
531 if (ret != OH_NN_SUCCESS) {
532 LOGE("Get the dimension ranges of input %u failed. ErrorCode=%d", index, ret);
533 return ret;
534 }
535
536 if (index >= minInputDims.size()) {
537 LOGE("index is %u, which exceeds the size of minInputDims:%zu.", index, minInputDims.size());
538 return OH_NN_INVALID_PARAMETER;
539 }
540
541 if (index >= maxInputDims.size()) {
542 LOGE("index is %u, which exceeds the size of maxInputDims:%zu.", index, maxInputDims.size());
543 return OH_NN_INVALID_PARAMETER;
544 }
545
546 const std::vector<uint32_t>& minSingleInputDims = minInputDims[index];
547 const std::vector<uint32_t>& maxSingleInputDims = maxInputDims[index];
548
549 std::vector<int32_t> tensorShape = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
550 size_t tensorShapeSize = tensorShape.size();
551 if (minSingleInputDims.size() != tensorShapeSize || maxSingleInputDims.size() != tensorShapeSize) {
552 LOGE("Size of minSingleInputDims, maxSingleInputDims and tensorShape of input %u are not equal.", index);
553 return OH_NN_INVALID_PARAMETER;
554 }
555
556 for (size_t j = 0; j < tensorShapeSize; ++j) {
557 // Dimensions cannot be negative
558 if (tensorShape[j] < 0) {
559 LOGE("Dimension %zu of input %u is %d.", j, index, tensorShape[j]);
560 return OH_NN_INVALID_PARAMETER;
561 }
562 uint32_t dim = static_cast<uint32_t>(tensorShape[j]);
563 if (dim < minSingleInputDims[j] || dim > maxSingleInputDims[j]) {
564 LOGE("Dimension %zu of input %u is %u, which is out of range [%u, %u]",
565 j, index, dim, minSingleInputDims[j], maxSingleInputDims[j]);
566 return OH_NN_INVALID_PARAMETER;
567 }
568 }
569
570 return OH_NN_SUCCESS;
571 }
572
573
SetInput(uint32_t index,const OH_NN_Tensor & nnTensor,const void * buffer,size_t length)574 OH_NN_ReturnCode NNExecutor::SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length)
575 {
576 auto nnRet = CheckInputDimRanges(index, nnTensor);
577 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
578 LOGI("Skip input dimension bounds check.");
579 } else if (nnRet != OH_NN_SUCCESS) {
580 LOGE("SetInput failed, Check the range of the %uth input dimension ranges failed.", index);
581 return nnRet;
582 }
583
584 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
585 if (inputTensor == nullptr) {
586 LOGE("SetInput failed, error happened when creating NNTensor.");
587 return OH_NN_MEMORY_ERROR;
588 }
589
590 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
591 if (ret != OH_NN_SUCCESS) {
592 LOGE("SetInput failed, please check input index or nnTensor.");
593 return ret;
594 }
595
596 // dataLength will be larger than 0 after BuildInputTensor()
597 size_t dataLength = inputTensor->GetDataLength();
598 if (length == 0 || length < dataLength) {
599 LOGE("SetInput failed, the given buffer length is too small to store the input nnTensor data.");
600 return OH_NN_INVALID_PARAMETER;
601 }
602
603 // Get length of current buffer if it is allocate by SetInput() before.
604 size_t curBufferLength = 0;
605 if ((m_inputTensors.find(index) != m_inputTensors.end()) && (m_inputTensors[index].isInnerMem)) {
606 curBufferLength = m_inputTensors[index].tensor->GetBufferLength();
607 }
608
609 // (dataLength <= curBufferLength) returns true if and only if current buffer is allocated by SetInput() before
610 // and is larger than user buffer.
611 if (dataLength <= curBufferLength) {
612 ret = SetInputTensorWithCurrentBuffer(index, inputTensor, buffer, dataLength, curBufferLength);
613 if (ret != OH_NN_SUCCESS) {
614 LOGE("SetInput failed, error happened when setting input with current buffer.");
615 return ret;
616 }
617 m_isRun = false;
618 return OH_NN_SUCCESS;
619 }
620
621 /**
622 * Buffer needs to allocated or reallocated if:
623 *
624 * - Current buffer is not enough.
625 * - SetInput() has not been called for the input before.
626 * - The buffer held in m_inputTensors is allocated and set by CreateInputMemory() and SetInputFromMemory().
627 */
628 void* inputBuffer = m_device->AllocateTensorBuffer(length, inputTensor);
629 if (inputBuffer == nullptr) {
630 LOGE("SetInput failed, error happened when allocating input device buffer.");
631 return OH_NN_MEMORY_ERROR;
632 }
633
634 errno_t status = memcpy_s(inputBuffer, dataLength, buffer, dataLength);
635 if (status != EOK) {
636 LOGE("SetInput failed, copy data from user buffer failed. Error code: %d.", status);
637 m_device->ReleaseBuffer(inputBuffer);
638 return OH_NN_MEMORY_ERROR;
639 }
640
641 SetInputTensorWithNewBuffer(index, inputTensor, inputBuffer, length, true);
642 m_isRun = false;
643 return OH_NN_SUCCESS;
644 }
645
SetInputFromMemory(uint32_t index,const OH_NN_Tensor & nnTensor,const OH_NN_Memory & memory)646 OH_NN_ReturnCode NNExecutor::SetInputFromMemory(
647 uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory)
648 {
649 auto nnRet = CheckInputDimRanges(index, nnTensor);
650 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
651 LOGI("Skip input dimension bounds check.");
652 } else if (nnRet != OH_NN_SUCCESS) {
653 LOGE("SetInputFromMemory failed, Check the range of the %uth input dimension ranges failed.", index);
654 return nnRet;
655 }
656
657 // Build a input tensor
658 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
659 if (inputTensor == nullptr) {
660 LOGE("SetInputFromMemory failed, error happened when creating NNTensor.");
661 return OH_NN_MEMORY_ERROR;
662 }
663
664 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
665 if (ret != OH_NN_SUCCESS) {
666 LOGE("SetInputFromMemory failed, please check input index or nnTensor");
667 return ret;
668 }
669
670 // check data length
671 size_t dataLength = inputTensor->GetDataLength();
672 if (memory.length == 0 || memory.length < dataLength) {
673 LOGE("SetInputFromMemory failed,"
674 " the length in the given memory is too small to store the input nnTensor data.");
675 return OH_NN_INVALID_PARAMETER;
676 }
677
678 SetInputTensorWithNewBuffer(index, inputTensor, const_cast<const void*>(memory.data), memory.length, false);
679 m_isRun = false;
680 return OH_NN_SUCCESS;
681 }
682
BuildNNTensorFromDesc(const std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType> & tensorDesc)683 std::shared_ptr<NNTensor> NNExecutor::BuildNNTensorFromDesc(
684 const std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>& tensorDesc)
685 {
686 std::shared_ptr<NNTensor> tensor = CreateSharedPtr<NNTensor>();
687 if (tensor == nullptr) {
688 LOGE("BuildNNTensorFromDesc failed, error happened when creating NNTensor.");
689 return nullptr;
690 }
691
692 // Build a tensor from nnTensor.
693 NN_TensorDesc* tensorDescCast = reinterpret_cast<NN_TensorDesc*>(tensorDesc.first.get());
694 auto ret = tensor->BuildFromTensorDesc(tensorDescCast);
695 if (ret != OH_NN_SUCCESS) {
696 LOGE("BuildNNTensorFromDesc failed, please check input nnTensor.");
697 return nullptr;
698 }
699
700 OH_NN_Format format;
701 tensorDesc.first->GetFormat(&format);
702 if (ret != OH_NN_SUCCESS) {
703 LOGE("BuildNNTensorFromDesc failed, failed to get tensor format from desc.");
704 return nullptr;
705 }
706 tensor->SetFormat(format);
707
708 ret = tensor->SetTensorType(tensorDesc.second);
709 if (ret != OH_NN_SUCCESS) {
710 LOGE("BuildNNTensorFromDesc failed, failed to set tensor type.");
711 return nullptr;
712 }
713
714 if (!CompareAttribute(tensorDesc, *tensor)) {
715 LOGE("BuildNNTensorFromDesc failed, input has different attributes from the one in the constructed model.");
716 return nullptr;
717 }
718
719 const char* name {nullptr};
720 ret = tensorDesc.first->GetName(&name);
721 if (ret != OH_NN_SUCCESS) {
722 LOGE("BuildNNTensorFromDesc failed, failed to get tensor name from desc.");
723 return nullptr;
724 }
725 tensor->SetName(name);
726 return tensor;
727 }
728
SetOutput(uint32_t index,void * buffer,size_t length)729 OH_NN_ReturnCode NNExecutor::SetOutput(uint32_t index, void* buffer, size_t length)
730 {
731 if (index >= m_outputTensorDescs.size()) {
732 LOGE("SetOutput failed, output index is out of range.");
733 return OH_NN_INVALID_PARAMETER;
734 }
735 if (m_outputTensorDescs[index].first == nullptr) {
736 LOGE("NNExecutor::SetOutput failed, tensor desc of output %{public}u is nullptr.", index);
737 return OH_NN_INVALID_PARAMETER;
738 }
739
740 size_t dataLength {0};
741 auto ret = m_outputTensorDescs[index].first->GetByteSize(&dataLength);
742 if (ret != OH_NN_SUCCESS) {
743 LOGE("SetOutputFromMemory failed, failed to get byte size from tensor desc.");
744 return ret;
745 }
746 if (length == 0 || length < dataLength) {
747 LOGE("SetOutput failed, the given buffer length is too small to store the output tensor data.");
748 return OH_NN_INVALID_PARAMETER;
749 }
750
751 // If output tensor does not exist, or inner device buffer size is not enough,
752 // or device buffer is set by SetOutputFromMemory() before,
753 // allocate a new device buffer and set it to output tensor, and update the user buffer.
754 if (m_outputTensors.find(index) != m_outputTensors.end()) {
755 if (m_outputTensors[index].isInnerMem) {
756 size_t curBufferLength = m_outputTensors[index].tensor->GetBufferLength();
757 if (length <= curBufferLength) {
758 // If current device buffer size is enough, only update the user buffer.
759 m_outputTensors[index].userBuffer = buffer;
760 m_outputTensors[index].userBufferLength = length;
761 m_isRun = false;
762 return OH_NN_SUCCESS;
763 } else {
764 // If current device buffer size is not enough,
765 // release current device buffer and then allocate a new one below.
766 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
767 m_device->ReleaseBuffer(curBuffer);
768 }
769 }
770 } else {
771 // If output tensor does not exist, create a new null output tensor.
772 ExeTensor exeTensor;
773 m_outputTensors[index] = exeTensor;
774 m_outputTensors[index].tensor = BuildNNTensorFromDesc(m_outputTensorDescs[index]);
775 if (m_outputTensors[index].tensor == nullptr) {
776 LOGE("SetOutput failed, failed to build nntensor from desc.");
777 return OH_NN_NULL_PTR;
778 }
779 }
780
781 void* deviceOutputBuffer = m_device->AllocateTensorBuffer(length, m_outputTensorDescs[index].first);
782 if (deviceOutputBuffer == nullptr) {
783 LOGE("SetOutput failed, allocating output device buffer failed.");
784 return OH_NN_MEMORY_ERROR;
785 }
786
787 m_outputTensors[index].tensor->SetBuffer(deviceOutputBuffer, length);
788 m_outputTensors[index].userBuffer = buffer;
789 m_outputTensors[index].userBufferLength = length;
790 m_outputTensors[index].isInnerMem = true;
791 m_isRun = false;
792 return OH_NN_SUCCESS;
793 }
794
795
SetOutputFromMemory(uint32_t index,const OH_NN_Memory & memory)796 OH_NN_ReturnCode NNExecutor::SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory)
797 {
798 if (index >= m_outputTensorDescs.size()) {
799 LOGE("SetOutputFromMemory failed, output index is out of range.");
800 return OH_NN_INVALID_PARAMETER;
801 }
802 if (m_outputTensorDescs[index].first == nullptr) {
803 LOGE("NNExecutor::SetOutputFromMemory failed, tensor desc of output %{public}u is nullptr.", index);
804 return OH_NN_INVALID_PARAMETER;
805 }
806
807 size_t dataLength {0};
808 auto ret = m_outputTensorDescs[index].first->GetByteSize(&dataLength);
809 if (ret != OH_NN_SUCCESS) {
810 LOGE("SetOutputFromMemory failed, failed to get byte size from tensor desc.");
811 return ret;
812 }
813 if (memory.length == 0 || memory.length < dataLength) {
814 LOGE("SetOutputFromMemory failed, the memory is too small to store the output tensor data.");
815 return OH_NN_INVALID_PARAMETER;
816 }
817
818 if (m_outputTensors.find(index) != m_outputTensors.end()) {
819 if (m_outputTensors[index].isInnerMem) {
820 // If it is inner buffer, releate it
821 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
822 m_device->ReleaseBuffer(curBuffer);
823 }
824 } else {
825 // If output tensor does not exist, create a new null output tensor.
826 ExeTensor exeTensor;
827 m_outputTensors[index] = exeTensor;
828 m_outputTensors[index].tensor = BuildNNTensorFromDesc(m_outputTensorDescs[index]);
829 if (m_outputTensors[index].tensor == nullptr) {
830 LOGE("SetOutputFromMemory failed, failed to build nntensor from desc.");
831 return OH_NN_NULL_PTR;
832 }
833 }
834
835 // Set the output tensor with memory
836 m_outputTensors[index].tensor->SetBuffer(const_cast<const void*>(memory.data), memory.length);
837 m_outputTensors[index].userBuffer = nullptr;
838 m_outputTensors[index].userBufferLength = 0;
839 m_outputTensors[index].isInnerMem = false;
840 m_isRun = false;
841 return OH_NN_SUCCESS;
842 }
843
CreateInputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)844 OH_NN_ReturnCode NNExecutor::CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
845 {
846 if (index >= m_inputTensorDescs.size()) {
847 LOGE("CreateInputMemory failed, input index is out of range.");
848 return OH_NN_INVALID_PARAMETER;
849 }
850 if (m_inputTensorDescs[index].first == nullptr) {
851 LOGE("CreateInputMemory failed, tensor desc of input %{public}u is nullptr.", index);
852 return OH_NN_INVALID_PARAMETER;
853 }
854
855 // Allocate device buffer
856 void* deviceInputBuffer = m_device->AllocateTensorBuffer(length, m_inputTensorDescs[index].first);
857 if (deviceInputBuffer == nullptr) {
858 LOGE("CreateInputMemory failed, allocating intput device buffer failed.");
859 return OH_NN_MEMORY_ERROR;
860 }
861
862 *memory = new(std::nothrow) OH_NN_Memory{deviceInputBuffer, length};
863 if (*memory == nullptr) {
864 LOGE("CreateInputMemory failed, constructing OH_NN_Memory failed.");
865 m_device->ReleaseBuffer(deviceInputBuffer);
866 return OH_NN_MEMORY_ERROR;
867 }
868
869 // Save the buffer address for check when destroying it.
870 m_inputCreatedMem[index].emplace_back(deviceInputBuffer);
871
872 return OH_NN_SUCCESS;
873 }
874
875
DestroyInputMemory(uint32_t index,OH_NN_Memory ** memory)876 OH_NN_ReturnCode NNExecutor::DestroyInputMemory(uint32_t index, OH_NN_Memory** memory)
877 {
878 if (index >= m_inputTensorDescs.size()) {
879 LOGE("DestroyInputMemory failed, input index is out of range.");
880 return OH_NN_INVALID_PARAMETER;
881 }
882
883 if (m_inputCreatedMem.find(index) == m_inputCreatedMem.end()) {
884 LOGE("DestroyInputMemory failed, the memory has not been created with the index.");
885 return OH_NN_INVALID_PARAMETER;
886 }
887
888 std::vector<void*>& inputCreatedMem = m_inputCreatedMem[index];
889 auto pos = std::find(inputCreatedMem.begin(), inputCreatedMem.end(), (*memory)->data);
890 if (pos == inputCreatedMem.end()) {
891 LOGE("DestroyInputMemory failed, the index does not match the memory.");
892 return OH_NN_INVALID_PARAMETER;
893 }
894
895 auto ret = m_device->ReleaseBuffer((*memory)->data);
896 if (ret != OH_NN_SUCCESS) {
897 LOGE("Release input buffer failed.");
898 return ret;
899 }
900
901 inputCreatedMem.erase(pos);
902 delete *memory;
903 *memory = nullptr;
904
905 return OH_NN_SUCCESS;
906 }
907
908
CreateOutputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)909 OH_NN_ReturnCode NNExecutor::CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
910 {
911 if (index >= m_outputTensorDescs.size()) {
912 LOGE("CreateOutputMemory failed, output index is out of range.");
913 return OH_NN_INVALID_PARAMETER;
914 }
915 if (m_outputTensorDescs[index].first == nullptr) {
916 LOGE("NNExecutor::CreateOutputMemory failed, tensor desc of output %{public}u is nullptr.", index);
917 return OH_NN_INVALID_PARAMETER;
918 }
919
920 // Allocate device buffer
921 void* deviceOutputBuffer = m_device->AllocateTensorBuffer(length, m_outputTensorDescs[index].first);
922 if (deviceOutputBuffer == nullptr) {
923 LOGE("CreateOutputMemory failed, allocating output device buffer failed.");
924 return OH_NN_MEMORY_ERROR;
925 }
926
927 *memory = new(std::nothrow) OH_NN_Memory{deviceOutputBuffer, length};
928 if (*memory == nullptr) {
929 LOGE("CreateOutputMemory failed, constructing OH_NN_Memory failed.");
930 m_device->ReleaseBuffer(deviceOutputBuffer);
931 return OH_NN_MEMORY_ERROR;
932 }
933
934 // Save the buffer address for check when destroying it.
935 m_outputCreatedMem[index].emplace_back(deviceOutputBuffer);
936
937 return OH_NN_SUCCESS;
938 }
939
940
DestroyOutputMemory(uint32_t index,OH_NN_Memory ** memory)941 OH_NN_ReturnCode NNExecutor::DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory)
942 {
943 if (index >= m_outputTensorDescs.size()) {
944 LOGE("DestroyOutputMemory failed, output index is out of range.");
945 return OH_NN_INVALID_PARAMETER;
946 }
947
948 if (m_outputCreatedMem.find(index) == m_outputCreatedMem.end()) {
949 LOGE("DestroyOutputMemory failed, the memory has not been created with the index.");
950 return OH_NN_INVALID_PARAMETER;
951 }
952
953 std::vector<void*>& outputCreatedMem = m_outputCreatedMem[index];
954 auto pos = std::find(outputCreatedMem.begin(), outputCreatedMem.end(), (*memory)->data);
955 if (pos == outputCreatedMem.end()) {
956 LOGE("DestroyOutputMemory failed, the index does not match the memory.");
957 return OH_NN_INVALID_PARAMETER;
958 }
959
960 auto ret = m_device->ReleaseBuffer((*memory)->data);
961 if (ret != OH_NN_SUCCESS) {
962 LOGE("Release output buffer failed.");
963 return ret;
964 }
965
966 outputCreatedMem.erase(pos);
967 delete *memory;
968 *memory = nullptr;
969
970 return OH_NN_SUCCESS;
971 }
972
Run(const std::vector<std::shared_ptr<NNTensor>> & inputTensors,std::vector<std::shared_ptr<NNTensor>> & outputTensors)973 OH_NN_ReturnCode NNExecutor::Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
974 std::vector<std::shared_ptr<NNTensor>>& outputTensors)
975 {
976 OH_NN_ReturnCode ret {OH_NN_FAILED};
977 IOTensor tensor;
978 std::vector<IOTensor> inputIOTensors;
979 size_t inputSize = inputTensors.size();
980 size_t outputSize = outputTensors.size();
981 for (size_t i = 0; i < inputSize; ++i) {
982 inputTensors[i]->ConvertToIOTensor(tensor);
983 inputIOTensors.emplace_back(std::move(tensor));
984 }
985
986 std::vector<IOTensor> outputIOTensors;
987 for (size_t i = 0; i < outputSize; ++i) {
988 outputTensors[i]->ConvertToIOTensor(tensor);
989 outputIOTensors.emplace_back(std::move(tensor));
990 }
991
992 std::vector<std::vector<int32_t>> outputsDims;
993 std::vector<bool> isSufficientDataBuffer;
994 ret = m_preparedModel->Run(inputIOTensors, outputIOTensors, outputsDims, isSufficientDataBuffer);
995 if (ret != OH_NN_SUCCESS) {
996 LOGE("PrepardModel Run() failed.");
997 return ret;
998 }
999
1000 // Set the output NNTensor's dimensions from output IOTensor if it is dynamic.
1001 // NNTensor::SetDimensions will check if the tensor buffer is enough for the new dimensions.
1002 if (outputsDims.size() != outputSize) {
1003 LOGE("ExecutionPlan run failed, size of outputsDims is not equal to outputTensors.");
1004 return OH_NN_INVALID_PARAMETER;
1005 }
1006 for (size_t i = 0; i < outputSize; ++i) {
1007 ret = outputTensors[i]->SetDimensions(outputsDims[i]);
1008 if (ret != OH_NN_SUCCESS) {
1009 LOGE("Run failed, error happened when setting output tensor's dimensions, output id: %zu.", i);
1010 return ret;
1011 }
1012 ret = m_outputTensorDescs[i].first->SetShape(outputsDims[i].data(), outputsDims[i].size());
1013 if (ret != OH_NN_SUCCESS) {
1014 LOGE("Run failed, error happened when setting inner output tensor's dimensions,"
1015 " output id: %zu.", i);
1016 return ret;
1017 }
1018 }
1019
1020 return OH_NN_SUCCESS;
1021 }
1022
Run()1023 OH_NN_ReturnCode NNExecutor::Run()
1024 {
1025 NNRT_TRACE_NAME("Execution");
1026 if (m_inputTensorDescs.size() != m_inputTensors.size()) {
1027 LOGE("Run failed, some input tensors have not been set.");
1028 return OH_NN_INVALID_PARAMETER;
1029 }
1030 if (m_outputTensorDescs.size() != m_outputTensors.size()) {
1031 LOGE("Run failed, some output tensors have not been set.");
1032 return OH_NN_INVALID_PARAMETER;
1033 }
1034
1035 // Build the NNTensor pointer vector: inputTensors and outputTensors
1036 std::vector<std::shared_ptr<NNTensor>> inputTensors;
1037 std::vector<std::shared_ptr<NNTensor>> outputTensors;
1038 size_t inputSize = m_inputTensors.size();
1039 size_t outputSize = m_outputTensors.size();
1040 for (size_t i = 0; i < inputSize; ++i) {
1041 inputTensors.emplace_back(m_inputTensors[i].tensor);
1042 }
1043 for (size_t i = 0; i < outputSize; ++i) {
1044 outputTensors.emplace_back(m_outputTensors[i].tensor);
1045 }
1046
1047 // Predict
1048 auto ret = Run(inputTensors, outputTensors);
1049 if (ret != OH_NN_SUCCESS) {
1050 LOGE("Run failed, error happened when executing the inference.");
1051 return ret;
1052 }
1053
1054 errno_t status{EOK};
1055 // Copy inner device buffer to user buffer if using SetOutput()
1056 for (size_t i = 0; i < outputSize; ++i) {
1057 if (m_outputTensors[i].isInnerMem) {
1058 auto size = outputTensors[i]->GetDataLength();
1059 if (size > m_outputTensors[i].userBufferLength) {
1060 LOGE("Output buffer size is not enough. Your size=%zu, but actual output size=%zu.",
1061 m_outputTensors[i].userBufferLength, size);
1062 return OH_NN_INVALID_PARAMETER;
1063 }
1064
1065 void* deviceBuffer = outputTensors[i]->GetBuffer();
1066 if (deviceBuffer == nullptr) {
1067 LOGE("Output buffer is nullptr.");
1068 return OH_NN_FAILED;
1069 }
1070
1071 status = memcpy_s(m_outputTensors[i].userBuffer, m_outputTensors[i].userBufferLength, deviceBuffer, size);
1072 if (status != EOK) {
1073 LOGE("Run failed, memory copy from device buffer to user buffer failed. Error code: %d.", status);
1074 return OH_NN_MEMORY_ERROR;
1075 }
1076 }
1077 }
1078
1079 m_isRun = true;
1080 return OH_NN_SUCCESS;
1081 }
1082
~NNExecutor()1083 NNExecutor::~NNExecutor()
1084 {
1085 for (auto& it : m_inputTensors) {
1086 if ((it.second).isInnerMem) {
1087 m_device->ReleaseBuffer((it.second).tensor->GetBuffer());
1088 }
1089 (it.second).tensor->SetBuffer(nullptr, 0);
1090 (it.second).tensor.reset();
1091 (it.second).userBuffer = nullptr;
1092 }
1093 m_inputTensors.clear();
1094
1095 for (auto& it : m_outputTensors) {
1096 if ((it.second).isInnerMem) {
1097 m_device->ReleaseBuffer((it.second).tensor->GetBuffer());
1098 }
1099 (it.second).tensor->SetBuffer(nullptr, 0);
1100 (it.second).tensor.reset();
1101 (it.second).userBuffer = nullptr;
1102 }
1103 m_outputTensors.clear();
1104
1105 for (auto& it : m_inputCreatedMem) {
1106 it.second.clear();
1107 }
1108 m_inputCreatedMem.clear();
1109
1110 for (auto& it : m_outputCreatedMem) {
1111 it.second.clear();
1112 }
1113 m_outputCreatedMem.clear();
1114
1115 if (m_executorConfig != nullptr) {
1116 delete m_executorConfig;
1117 m_executorConfig = nullptr;
1118 }
1119 }
1120 } // namespace NeuralNetworkRuntime
1121 } // namespace OHOS
1122