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 "common/log.h"
20 #include "cpp_type.h"
21
22 #include "securec.h"
23 #include "common/utils.h"
24 #include "common/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 LOGW("NNExecutor::CheckInputDimRanges failed, current version don't support get input dim ranges.");
352 return OH_NN_OPERATION_FORBIDDEN;
353 }
354
355 if (inputSize != minInputDims.size()) {
356 LOGE("NNExecutor::CheckInputDimRanges failed, size of minInputDims:%{public}zu is not equal to "
357 "inputSize:%{public}zu.", minInputDims.size(), inputSize);
358 return OH_NN_INVALID_PARAMETER;
359 }
360
361 if (inputSize != maxInputDims.size()) {
362 LOGE("NNExecutor::CheckInputDimRanges failed, size of maxInputDims:%{public}zu is not equal to "
363 "inputSize:%{public}zu.", maxInputDims.size(), inputSize);
364 return OH_NN_INVALID_PARAMETER;
365 }
366
367 const NNTensor2_0* nnTensor = nullptr;
368 OH_NN_ReturnCode ret {OH_NN_FAILED};
369 for (size_t i = 0; i < inputSize; ++i) {
370 const std::vector<uint32_t>& minSingleInputDims = minInputDims[i];
371 const std::vector<uint32_t>& maxSingleInputDims = maxInputDims[i];
372 nnTensor = reinterpret_cast<const NNTensor2_0*>(inputTensors[i]);
373 if (nnTensor == nullptr) {
374 LOGE("NNExecutor::CheckInputDimRanges failed, input %{public}zu is nullptr.", i);
375 return OH_NN_NULL_PTR;
376 }
377 ret = nnTensor->CheckDimRanges(minSingleInputDims, maxSingleInputDims);
378 if (ret != OH_NN_SUCCESS) {
379 LOGE("NNExecutor::CheckInputDimRanges failed, failed to check input dim ranges of input %{public}zu", i);
380 return ret;
381 }
382 }
383
384 return OH_NN_SUCCESS;
385 }
386
CompareAttribute(const std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType> & tensorDesc,const NNTensor & tensor) const387 bool NNExecutor::CompareAttribute(
388 const std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>& tensorDesc, const NNTensor& tensor) const
389 {
390 OH_NN_DataType dataType;
391 auto ret = tensorDesc.first->GetDataType(&dataType);
392 if (ret != OH_NN_SUCCESS) {
393 LOGE("CompareAttribute failed, failed to get data type from tensor desc.");
394 return false;
395 }
396 if (dataType != tensor.GetDataType()) {
397 LOGI("Tensors have different data type: %d and %d.", dataType, tensor.GetDataType());
398 return false;
399 }
400
401 int32_t* shape {nullptr};
402 size_t shapeNum {0};
403 ret = tensorDesc.first->GetShape(&shape, &shapeNum);
404 if (ret != OH_NN_SUCCESS) {
405 LOGE("CompareAttribute failed, failed to get shape from tensor desc.");
406 return false;
407 }
408 const std::vector<int32_t> dimensions = tensor.GetDimensions();
409 if (shapeNum != dimensions.size()) {
410 LOGI("Tensors have differents dimension counts: %zu and %zu.", shapeNum, dimensions.size());
411 return false;
412 }
413
414 size_t dimensionsSize = dimensions.size();
415 for (size_t i = 0; i < dimensionsSize; i++) {
416 if ((shape[i] != -1) && (shape[i] != dimensions[i])) {
417 LOGI("Tensors have different dimension: dimension index: %zu, dimension value: %d and %d.",
418 i, shape[i], dimensions[i]);
419 return false;
420 }
421 }
422
423 if (tensorDesc.second != tensor.GetType()) {
424 LOGI("Tensors have different type: %{public}d and %{public}d.", tensorDesc.second, tensor.GetType());
425 return false;
426 }
427
428 return true;
429 }
430
BuildInputTensor(uint32_t index,const OH_NN_Tensor & nnTensor,std::shared_ptr<NNTensor> inputTensor) const431 OH_NN_ReturnCode NNExecutor::BuildInputTensor(uint32_t index, const OH_NN_Tensor& nnTensor,
432 std::shared_ptr<NNTensor> inputTensor) const
433 {
434 // Note: inputs have only shapes info.
435 if (index >= m_inputTensorDescs.size()) {
436 LOGE("BuildInputTensor failed, input index is out of range.");
437 return OH_NN_INVALID_PARAMETER;
438 }
439 if (m_inputTensorDescs[index].first == nullptr) {
440 LOGE("BuildInputTensor failed, tensor desc of input %{public}u is nullptr.", index);
441 return OH_NN_INVALID_PARAMETER;
442 }
443
444 // Build a tensor from nnTensor.
445 auto ret = inputTensor->BuildFromOHNNTensor(nnTensor);
446 if (ret != OH_NN_SUCCESS) {
447 LOGE("BuildInputTensor failed, please check input nnTensor.");
448 return ret;
449 }
450
451 if (inputTensor->IsDynamicShape()) {
452 LOGE("BuildInputTensor failed, input nnTensor should has certain dimensions which cannot contain -1.");
453 return OH_NN_INVALID_PARAMETER;
454 }
455
456 OH_NN_Format format;
457 ret = m_inputTensorDescs[index].first->GetFormat(&format);
458 if (ret != OH_NN_SUCCESS) {
459 LOGE("BuildInputTensor failed, failed to get tensor format from desc.");
460 return ret;
461 }
462 inputTensor->SetFormat(format);
463
464 if (!CompareAttribute(m_inputTensorDescs[index], *inputTensor)) {
465 LOGE("BuildInputTensor failed, input has different attributes from the one in the constructed model.");
466 return OH_NN_INVALID_PARAMETER;
467 }
468
469 const char* name {nullptr};
470 ret = m_inputTensorDescs[index].first->GetName(&name);
471 if (ret != OH_NN_SUCCESS) {
472 LOGE("BuildInputTensor failed, failed to get tensor name from desc.");
473 return ret;
474 }
475 inputTensor->SetName(name);
476 return OH_NN_SUCCESS;
477 }
478
479
SetInputTensorWithCurrentBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * buffer,size_t dataLength,size_t curBufferLength)480 OH_NN_ReturnCode NNExecutor::SetInputTensorWithCurrentBuffer(uint32_t index,
481 std::shared_ptr<NNTensor> inputTensor, const void* buffer, size_t dataLength, size_t curBufferLength)
482 {
483 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
484 errno_t status = memcpy_s(curBuffer, dataLength, buffer, dataLength);
485 // Current buffer inside m_inputTensors is managed by executor, no need to release if memcpy failed.
486 if (status != EOK) {
487 LOGE("SetInputTensorWithCurrentBuffe failed, copy data from user buffer to device buffer failed. "
488 "Error code: %d.", status);
489 return OH_NN_MEMORY_ERROR;
490 }
491
492 // Set the new tensor with the buffer of current tensor
493 inputTensor->SetBuffer(curBuffer, curBufferLength);
494
495 // The memory is reused here. Thus, current tensor's buffer must set to nullptr, in case the memory is released
496 // twice.
497 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
498
499 // Set to the new tensor, and release current one.
500 m_inputTensors[index].tensor = inputTensor;
501 return OH_NN_SUCCESS;
502 }
503
504
SetInputTensorWithNewBuffer(uint32_t index,std::shared_ptr<NNTensor> inputTensor,const void * inputBuffer,size_t length,bool isInnerMem)505 void NNExecutor::SetInputTensorWithNewBuffer(uint32_t index,
506 std::shared_ptr<NNTensor> inputTensor, const void* inputBuffer, size_t length, bool isInnerMem)
507 {
508 // Release the memory inside the tensor first, if it is allocated by Executor during SetInput().
509 if (m_inputTensors.find(index) != m_inputTensors.end()) {
510 if (m_inputTensors[index].isInnerMem) {
511 void* curBuffer = m_inputTensors[index].tensor->GetBuffer();
512 m_device->ReleaseBuffer(curBuffer);
513 }
514 // Set current tensor's buffer to nullptr in case the NNTensor release the driver memory in destruction.
515 m_inputTensors[index].tensor->SetBuffer(nullptr, 0);
516 }
517
518 // Set new input tensor data buffer
519 inputTensor->SetBuffer(inputBuffer, length);
520
521 // Create or update the input tensor
522 ExeTensor exeTensor{inputTensor, nullptr, 0, isInnerMem};
523 m_inputTensors[index] = exeTensor;
524 }
525
526
CheckInputDimRanges(uint32_t index,const OH_NN_Tensor & nnTensor) const527 OH_NN_ReturnCode NNExecutor::CheckInputDimRanges(uint32_t index, const OH_NN_Tensor& nnTensor) const
528 {
529 std::vector<std::vector<uint32_t>> minInputDims;
530 std::vector<std::vector<uint32_t>> maxInputDims;
531 auto ret = m_preparedModel->GetInputDimRanges(minInputDims, maxInputDims);
532 if (ret != OH_NN_SUCCESS) {
533 LOGE("Get the dimension ranges of input %u failed. ErrorCode=%d", index, ret);
534 return ret;
535 }
536
537 if (index >= minInputDims.size()) {
538 LOGE("index is %u, which exceeds the size of minInputDims:%zu.", index, minInputDims.size());
539 return OH_NN_INVALID_PARAMETER;
540 }
541
542 if (index >= maxInputDims.size()) {
543 LOGE("index is %u, which exceeds the size of maxInputDims:%zu.", index, maxInputDims.size());
544 return OH_NN_INVALID_PARAMETER;
545 }
546
547 const std::vector<uint32_t>& minSingleInputDims = minInputDims[index];
548 const std::vector<uint32_t>& maxSingleInputDims = maxInputDims[index];
549
550 std::vector<int32_t> tensorShape = ConstructVectorFromArray(nnTensor.dimensions, nnTensor.dimensionCount);
551 size_t tensorShapeSize = tensorShape.size();
552 if (minSingleInputDims.size() != tensorShapeSize || maxSingleInputDims.size() != tensorShapeSize) {
553 LOGE("Size of minSingleInputDims, maxSingleInputDims and tensorShape of input %u are not equal.", index);
554 return OH_NN_INVALID_PARAMETER;
555 }
556
557 for (size_t j = 0; j < tensorShapeSize; ++j) {
558 // Dimensions cannot be negative
559 if (tensorShape[j] < 0) {
560 LOGE("Dimension %zu of input %u is %d.", j, index, tensorShape[j]);
561 return OH_NN_INVALID_PARAMETER;
562 }
563 uint32_t dim = static_cast<uint32_t>(tensorShape[j]);
564 if (dim < minSingleInputDims[j] || dim > maxSingleInputDims[j]) {
565 LOGE("Dimension %zu of input %u is %u, which is out of range [%u, %u]",
566 j, index, dim, minSingleInputDims[j], maxSingleInputDims[j]);
567 return OH_NN_INVALID_PARAMETER;
568 }
569 }
570
571 return OH_NN_SUCCESS;
572 }
573
574
SetInput(uint32_t index,const OH_NN_Tensor & nnTensor,const void * buffer,size_t length)575 OH_NN_ReturnCode NNExecutor::SetInput(uint32_t index, const OH_NN_Tensor& nnTensor, const void* buffer, size_t length)
576 {
577 auto nnRet = CheckInputDimRanges(index, nnTensor);
578 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
579 LOGI("Skip input dimension bounds check.");
580 } else if (nnRet != OH_NN_SUCCESS) {
581 LOGE("SetInput failed, Check the range of the %uth input dimension ranges failed.", index);
582 return nnRet;
583 }
584
585 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
586 if (inputTensor == nullptr) {
587 LOGE("SetInput failed, error happened when creating NNTensor.");
588 return OH_NN_MEMORY_ERROR;
589 }
590
591 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
592 if (ret != OH_NN_SUCCESS) {
593 LOGE("SetInput failed, please check input index or nnTensor.");
594 return ret;
595 }
596
597 // dataLength will be larger than 0 after BuildInputTensor()
598 size_t dataLength = inputTensor->GetDataLength();
599 if (length == 0 || length < dataLength) {
600 LOGE("SetInput failed, the given buffer length is too small to store the input nnTensor data.");
601 return OH_NN_INVALID_PARAMETER;
602 }
603
604 // Get length of current buffer if it is allocate by SetInput() before.
605 size_t curBufferLength = 0;
606 if ((m_inputTensors.find(index) != m_inputTensors.end()) && (m_inputTensors[index].isInnerMem)) {
607 curBufferLength = m_inputTensors[index].tensor->GetBufferLength();
608 }
609
610 // (dataLength <= curBufferLength) returns true if and only if current buffer is allocated by SetInput() before
611 // and is larger than user buffer.
612 if (dataLength <= curBufferLength) {
613 ret = SetInputTensorWithCurrentBuffer(index, inputTensor, buffer, dataLength, curBufferLength);
614 if (ret != OH_NN_SUCCESS) {
615 LOGE("SetInput failed, error happened when setting input with current buffer.");
616 return ret;
617 }
618 m_isRun = false;
619 return OH_NN_SUCCESS;
620 }
621
622 /**
623 * Buffer needs to allocated or reallocated if:
624 *
625 * - Current buffer is not enough.
626 * - SetInput() has not been called for the input before.
627 * - The buffer held in m_inputTensors is allocated and set by CreateInputMemory() and SetInputFromMemory().
628 */
629 void* inputBuffer = m_device->AllocateTensorBuffer(length, inputTensor);
630 if (inputBuffer == nullptr) {
631 LOGE("SetInput failed, error happened when allocating input device buffer.");
632 return OH_NN_MEMORY_ERROR;
633 }
634
635 errno_t status = memcpy_s(inputBuffer, dataLength, buffer, dataLength);
636 if (status != EOK) {
637 LOGE("SetInput failed, copy data from user buffer failed. Error code: %d.", status);
638 m_device->ReleaseBuffer(inputBuffer);
639 return OH_NN_MEMORY_ERROR;
640 }
641
642 SetInputTensorWithNewBuffer(index, inputTensor, inputBuffer, length, true);
643 m_isRun = false;
644 return OH_NN_SUCCESS;
645 }
646
SetInputFromMemory(uint32_t index,const OH_NN_Tensor & nnTensor,const OH_NN_Memory & memory)647 OH_NN_ReturnCode NNExecutor::SetInputFromMemory(
648 uint32_t index, const OH_NN_Tensor& nnTensor, const OH_NN_Memory& memory)
649 {
650 auto nnRet = CheckInputDimRanges(index, nnTensor);
651 if (nnRet == OH_NN_OPERATION_FORBIDDEN) {
652 LOGI("Skip input dimension bounds check.");
653 } else if (nnRet != OH_NN_SUCCESS) {
654 LOGE("SetInputFromMemory failed, Check the range of the %uth input dimension ranges failed.", index);
655 return nnRet;
656 }
657
658 // Build a input tensor
659 std::shared_ptr<NNTensor> inputTensor = CreateSharedPtr<NNTensor>();
660 if (inputTensor == nullptr) {
661 LOGE("SetInputFromMemory failed, error happened when creating NNTensor.");
662 return OH_NN_MEMORY_ERROR;
663 }
664
665 auto ret = BuildInputTensor(index, nnTensor, inputTensor);
666 if (ret != OH_NN_SUCCESS) {
667 LOGE("SetInputFromMemory failed, please check input index or nnTensor");
668 return ret;
669 }
670
671 // check data length
672 size_t dataLength = inputTensor->GetDataLength();
673 if (memory.length == 0 || memory.length < dataLength) {
674 LOGE("SetInputFromMemory failed,"
675 " the length in the given memory is too small to store the input nnTensor data.");
676 return OH_NN_INVALID_PARAMETER;
677 }
678
679 SetInputTensorWithNewBuffer(index, inputTensor, const_cast<const void*>(memory.data), memory.length, false);
680 m_isRun = false;
681 return OH_NN_SUCCESS;
682 }
683
BuildNNTensorFromDesc(const std::pair<std::shared_ptr<TensorDesc>,OH_NN_TensorType> & tensorDesc)684 std::shared_ptr<NNTensor> NNExecutor::BuildNNTensorFromDesc(
685 const std::pair<std::shared_ptr<TensorDesc>, OH_NN_TensorType>& tensorDesc)
686 {
687 std::shared_ptr<NNTensor> tensor = CreateSharedPtr<NNTensor>();
688 if (tensor == nullptr) {
689 LOGE("BuildNNTensorFromDesc failed, error happened when creating NNTensor.");
690 return nullptr;
691 }
692
693 // Build a tensor from nnTensor.
694 NN_TensorDesc* tensorDescCast = reinterpret_cast<NN_TensorDesc*>(tensorDesc.first.get());
695 auto ret = tensor->BuildFromTensorDesc(tensorDescCast);
696 if (ret != OH_NN_SUCCESS) {
697 LOGE("BuildNNTensorFromDesc failed, please check input nnTensor.");
698 return nullptr;
699 }
700
701 OH_NN_Format format;
702 tensorDesc.first->GetFormat(&format);
703 if (ret != OH_NN_SUCCESS) {
704 LOGE("BuildNNTensorFromDesc failed, failed to get tensor format from desc.");
705 return nullptr;
706 }
707 tensor->SetFormat(format);
708
709 ret = tensor->SetTensorType(tensorDesc.second);
710 if (ret != OH_NN_SUCCESS) {
711 LOGE("BuildNNTensorFromDesc failed, failed to set tensor type.");
712 return nullptr;
713 }
714
715 if (!CompareAttribute(tensorDesc, *tensor)) {
716 LOGE("BuildNNTensorFromDesc failed, input has different attributes from the one in the constructed model.");
717 return nullptr;
718 }
719
720 const char* name {nullptr};
721 ret = tensorDesc.first->GetName(&name);
722 if (ret != OH_NN_SUCCESS) {
723 LOGE("BuildNNTensorFromDesc failed, failed to get tensor name from desc.");
724 return nullptr;
725 }
726 tensor->SetName(name);
727 return tensor;
728 }
729
SetOutput(uint32_t index,void * buffer,size_t length)730 OH_NN_ReturnCode NNExecutor::SetOutput(uint32_t index, void* buffer, size_t length)
731 {
732 if (index >= m_outputTensorDescs.size()) {
733 LOGE("SetOutput failed, output index is out of range.");
734 return OH_NN_INVALID_PARAMETER;
735 }
736 if (m_outputTensorDescs[index].first == nullptr) {
737 LOGE("NNExecutor::SetOutput failed, tensor desc of output %{public}u is nullptr.", index);
738 return OH_NN_INVALID_PARAMETER;
739 }
740
741 size_t dataLength {0};
742 auto ret = m_outputTensorDescs[index].first->GetByteSize(&dataLength);
743 if (ret != OH_NN_SUCCESS) {
744 LOGE("SetOutputFromMemory failed, failed to get byte size from tensor desc.");
745 return ret;
746 }
747 if (length == 0 || length < dataLength) {
748 LOGE("SetOutput failed, the given buffer length is too small to store the output tensor data.");
749 return OH_NN_INVALID_PARAMETER;
750 }
751
752 // If output tensor does not exist, or inner device buffer size is not enough,
753 // or device buffer is set by SetOutputFromMemory() before,
754 // allocate a new device buffer and set it to output tensor, and update the user buffer.
755 if (m_outputTensors.find(index) != m_outputTensors.end()) {
756 if (m_outputTensors[index].isInnerMem) {
757 size_t curBufferLength = m_outputTensors[index].tensor->GetBufferLength();
758 if (length <= curBufferLength) {
759 // If current device buffer size is enough, only update the user buffer.
760 m_outputTensors[index].userBuffer = buffer;
761 m_outputTensors[index].userBufferLength = length;
762 m_isRun = false;
763 return OH_NN_SUCCESS;
764 } else {
765 // If current device buffer size is not enough,
766 // release current device buffer and then allocate a new one below.
767 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
768 m_device->ReleaseBuffer(curBuffer);
769 }
770 }
771 } else {
772 // If output tensor does not exist, create a new null output tensor.
773 ExeTensor exeTensor;
774 m_outputTensors[index] = exeTensor;
775 m_outputTensors[index].tensor = BuildNNTensorFromDesc(m_outputTensorDescs[index]);
776 if (m_outputTensors[index].tensor == nullptr) {
777 LOGE("SetOutput failed, failed to build nntensor from desc.");
778 return OH_NN_NULL_PTR;
779 }
780 }
781
782 void* deviceOutputBuffer = m_device->AllocateTensorBuffer(length, m_outputTensorDescs[index].first);
783 if (deviceOutputBuffer == nullptr) {
784 LOGE("SetOutput failed, allocating output device buffer failed.");
785 return OH_NN_MEMORY_ERROR;
786 }
787
788 m_outputTensors[index].tensor->SetBuffer(deviceOutputBuffer, length);
789 m_outputTensors[index].userBuffer = buffer;
790 m_outputTensors[index].userBufferLength = length;
791 m_outputTensors[index].isInnerMem = true;
792 m_isRun = false;
793 return OH_NN_SUCCESS;
794 }
795
796
SetOutputFromMemory(uint32_t index,const OH_NN_Memory & memory)797 OH_NN_ReturnCode NNExecutor::SetOutputFromMemory(uint32_t index, const OH_NN_Memory& memory)
798 {
799 if (index >= m_outputTensorDescs.size()) {
800 LOGE("SetOutputFromMemory failed, output index is out of range.");
801 return OH_NN_INVALID_PARAMETER;
802 }
803 if (m_outputTensorDescs[index].first == nullptr) {
804 LOGE("NNExecutor::SetOutputFromMemory failed, tensor desc of output %{public}u is nullptr.", index);
805 return OH_NN_INVALID_PARAMETER;
806 }
807
808 size_t dataLength {0};
809 auto ret = m_outputTensorDescs[index].first->GetByteSize(&dataLength);
810 if (ret != OH_NN_SUCCESS) {
811 LOGE("SetOutputFromMemory failed, failed to get byte size from tensor desc.");
812 return ret;
813 }
814 if (memory.length == 0 || memory.length < dataLength) {
815 LOGE("SetOutputFromMemory failed, the memory is too small to store the output tensor data.");
816 return OH_NN_INVALID_PARAMETER;
817 }
818
819 if (m_outputTensors.find(index) != m_outputTensors.end()) {
820 if (m_outputTensors[index].isInnerMem) {
821 // If it is inner buffer, releate it
822 void* curBuffer = m_outputTensors[index].tensor->GetBuffer();
823 m_device->ReleaseBuffer(curBuffer);
824 }
825 } else {
826 // If output tensor does not exist, create a new null output tensor.
827 ExeTensor exeTensor;
828 m_outputTensors[index] = exeTensor;
829 m_outputTensors[index].tensor = BuildNNTensorFromDesc(m_outputTensorDescs[index]);
830 if (m_outputTensors[index].tensor == nullptr) {
831 LOGE("SetOutputFromMemory failed, failed to build nntensor from desc.");
832 return OH_NN_NULL_PTR;
833 }
834 }
835
836 // Set the output tensor with memory
837 m_outputTensors[index].tensor->SetBuffer(const_cast<const void*>(memory.data), memory.length);
838 m_outputTensors[index].userBuffer = nullptr;
839 m_outputTensors[index].userBufferLength = 0;
840 m_outputTensors[index].isInnerMem = false;
841 m_isRun = false;
842 return OH_NN_SUCCESS;
843 }
844
CreateInputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)845 OH_NN_ReturnCode NNExecutor::CreateInputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
846 {
847 if (index >= m_inputTensorDescs.size()) {
848 LOGE("CreateInputMemory failed, input index is out of range.");
849 return OH_NN_INVALID_PARAMETER;
850 }
851 if (m_inputTensorDescs[index].first == nullptr) {
852 LOGE("CreateInputMemory failed, tensor desc of input %{public}u is nullptr.", index);
853 return OH_NN_INVALID_PARAMETER;
854 }
855
856 // Allocate device buffer
857 void* deviceInputBuffer = m_device->AllocateTensorBuffer(length, m_inputTensorDescs[index].first);
858 if (deviceInputBuffer == nullptr) {
859 LOGE("CreateInputMemory failed, allocating intput device buffer failed.");
860 return OH_NN_MEMORY_ERROR;
861 }
862
863 *memory = new(std::nothrow) OH_NN_Memory{deviceInputBuffer, length};
864 if (*memory == nullptr) {
865 LOGE("CreateInputMemory failed, constructing OH_NN_Memory failed.");
866 m_device->ReleaseBuffer(deviceInputBuffer);
867 return OH_NN_MEMORY_ERROR;
868 }
869
870 // Save the buffer address for check when destroying it.
871 m_inputCreatedMem[index].emplace_back(deviceInputBuffer);
872
873 return OH_NN_SUCCESS;
874 }
875
876
DestroyInputMemory(uint32_t index,OH_NN_Memory ** memory)877 OH_NN_ReturnCode NNExecutor::DestroyInputMemory(uint32_t index, OH_NN_Memory** memory)
878 {
879 if (index >= m_inputTensorDescs.size()) {
880 LOGE("DestroyInputMemory failed, input index is out of range.");
881 return OH_NN_INVALID_PARAMETER;
882 }
883
884 if (m_inputCreatedMem.find(index) == m_inputCreatedMem.end()) {
885 LOGE("DestroyInputMemory failed, the memory has not been created with the index.");
886 return OH_NN_INVALID_PARAMETER;
887 }
888
889 std::vector<void*>& inputCreatedMem = m_inputCreatedMem[index];
890 auto pos = std::find(inputCreatedMem.begin(), inputCreatedMem.end(), (*memory)->data);
891 if (pos == inputCreatedMem.end()) {
892 LOGE("DestroyInputMemory failed, the index does not match the memory.");
893 return OH_NN_INVALID_PARAMETER;
894 }
895
896 auto ret = m_device->ReleaseBuffer((*memory)->data);
897 if (ret != OH_NN_SUCCESS) {
898 LOGE("Release input buffer failed.");
899 return ret;
900 }
901
902 inputCreatedMem.erase(pos);
903 delete *memory;
904 *memory = nullptr;
905
906 return OH_NN_SUCCESS;
907 }
908
909
CreateOutputMemory(uint32_t index,size_t length,OH_NN_Memory ** memory)910 OH_NN_ReturnCode NNExecutor::CreateOutputMemory(uint32_t index, size_t length, OH_NN_Memory** memory)
911 {
912 if (index >= m_outputTensorDescs.size()) {
913 LOGE("CreateOutputMemory failed, output index is out of range.");
914 return OH_NN_INVALID_PARAMETER;
915 }
916 if (m_outputTensorDescs[index].first == nullptr) {
917 LOGE("NNExecutor::CreateOutputMemory failed, tensor desc of output %{public}u is nullptr.", index);
918 return OH_NN_INVALID_PARAMETER;
919 }
920
921 // Allocate device buffer
922 void* deviceOutputBuffer = m_device->AllocateTensorBuffer(length, m_outputTensorDescs[index].first);
923 if (deviceOutputBuffer == nullptr) {
924 LOGE("CreateOutputMemory failed, allocating output device buffer failed.");
925 return OH_NN_MEMORY_ERROR;
926 }
927
928 *memory = new(std::nothrow) OH_NN_Memory{deviceOutputBuffer, length};
929 if (*memory == nullptr) {
930 LOGE("CreateOutputMemory failed, constructing OH_NN_Memory failed.");
931 m_device->ReleaseBuffer(deviceOutputBuffer);
932 return OH_NN_MEMORY_ERROR;
933 }
934
935 // Save the buffer address for check when destroying it.
936 m_outputCreatedMem[index].emplace_back(deviceOutputBuffer);
937
938 return OH_NN_SUCCESS;
939 }
940
941
DestroyOutputMemory(uint32_t index,OH_NN_Memory ** memory)942 OH_NN_ReturnCode NNExecutor::DestroyOutputMemory(uint32_t index, OH_NN_Memory** memory)
943 {
944 if (index >= m_outputTensorDescs.size()) {
945 LOGE("DestroyOutputMemory failed, output index is out of range.");
946 return OH_NN_INVALID_PARAMETER;
947 }
948
949 if (m_outputCreatedMem.find(index) == m_outputCreatedMem.end()) {
950 LOGE("DestroyOutputMemory failed, the memory has not been created with the index.");
951 return OH_NN_INVALID_PARAMETER;
952 }
953
954 std::vector<void*>& outputCreatedMem = m_outputCreatedMem[index];
955 auto pos = std::find(outputCreatedMem.begin(), outputCreatedMem.end(), (*memory)->data);
956 if (pos == outputCreatedMem.end()) {
957 LOGE("DestroyOutputMemory failed, the index does not match the memory.");
958 return OH_NN_INVALID_PARAMETER;
959 }
960
961 auto ret = m_device->ReleaseBuffer((*memory)->data);
962 if (ret != OH_NN_SUCCESS) {
963 LOGE("Release output buffer failed.");
964 return ret;
965 }
966
967 outputCreatedMem.erase(pos);
968 delete *memory;
969 *memory = nullptr;
970
971 return OH_NN_SUCCESS;
972 }
973
Run(const std::vector<std::shared_ptr<NNTensor>> & inputTensors,std::vector<std::shared_ptr<NNTensor>> & outputTensors)974 OH_NN_ReturnCode NNExecutor::Run(const std::vector<std::shared_ptr<NNTensor>>& inputTensors,
975 std::vector<std::shared_ptr<NNTensor>>& outputTensors)
976 {
977 OH_NN_ReturnCode ret {OH_NN_FAILED};
978 IOTensor tensor;
979 std::vector<IOTensor> inputIOTensors;
980 size_t inputSize = inputTensors.size();
981 size_t outputSize = outputTensors.size();
982 for (size_t i = 0; i < inputSize; ++i) {
983 inputTensors[i]->ConvertToIOTensor(tensor);
984 inputIOTensors.emplace_back(std::move(tensor));
985 }
986
987 std::vector<IOTensor> outputIOTensors;
988 for (size_t i = 0; i < outputSize; ++i) {
989 outputTensors[i]->ConvertToIOTensor(tensor);
990 outputIOTensors.emplace_back(std::move(tensor));
991 }
992
993 std::vector<std::vector<int32_t>> outputsDims;
994 std::vector<bool> isSufficientDataBuffer;
995 ret = m_preparedModel->Run(inputIOTensors, outputIOTensors, outputsDims, isSufficientDataBuffer);
996 if (ret != OH_NN_SUCCESS) {
997 LOGE("PrepardModel Run() failed.");
998 return ret;
999 }
1000
1001 // Set the output NNTensor's dimensions from output IOTensor if it is dynamic.
1002 // NNTensor::SetDimensions will check if the tensor buffer is enough for the new dimensions.
1003 if (outputsDims.size() != outputSize) {
1004 LOGE("ExecutionPlan run failed, size of outputsDims is not equal to outputTensors.");
1005 return OH_NN_INVALID_PARAMETER;
1006 }
1007 for (size_t i = 0; i < outputSize; ++i) {
1008 ret = outputTensors[i]->SetDimensions(outputsDims[i]);
1009 if (ret != OH_NN_SUCCESS) {
1010 LOGE("Run failed, error happened when setting output tensor's dimensions, output id: %zu.", i);
1011 return ret;
1012 }
1013 ret = m_outputTensorDescs[i].first->SetShape(outputsDims[i].data(), outputsDims[i].size());
1014 if (ret != OH_NN_SUCCESS) {
1015 LOGE("Run failed, error happened when setting inner output tensor's dimensions,"
1016 " output id: %zu.", i);
1017 return ret;
1018 }
1019 }
1020
1021 return OH_NN_SUCCESS;
1022 }
1023
Run()1024 OH_NN_ReturnCode NNExecutor::Run()
1025 {
1026 NNRT_TRACE_NAME("Execution");
1027 if (m_inputTensorDescs.size() != m_inputTensors.size()) {
1028 LOGE("Run failed, some input tensors have not been set.");
1029 return OH_NN_INVALID_PARAMETER;
1030 }
1031 if (m_outputTensorDescs.size() != m_outputTensors.size()) {
1032 LOGE("Run failed, some output tensors have not been set.");
1033 return OH_NN_INVALID_PARAMETER;
1034 }
1035
1036 // Build the NNTensor pointer vector: inputTensors and outputTensors
1037 std::vector<std::shared_ptr<NNTensor>> inputTensors;
1038 std::vector<std::shared_ptr<NNTensor>> outputTensors;
1039 size_t inputSize = m_inputTensors.size();
1040 size_t outputSize = m_outputTensors.size();
1041 for (size_t i = 0; i < inputSize; ++i) {
1042 inputTensors.emplace_back(m_inputTensors[i].tensor);
1043 }
1044 for (size_t i = 0; i < outputSize; ++i) {
1045 outputTensors.emplace_back(m_outputTensors[i].tensor);
1046 }
1047
1048 // Predict
1049 auto ret = Run(inputTensors, outputTensors);
1050 if (ret != OH_NN_SUCCESS) {
1051 LOGE("Run failed, error happened when executing the inference.");
1052 return ret;
1053 }
1054
1055 errno_t status{EOK};
1056 // Copy inner device buffer to user buffer if using SetOutput()
1057 for (size_t i = 0; i < outputSize; ++i) {
1058 if (m_outputTensors[i].isInnerMem) {
1059 auto size = outputTensors[i]->GetDataLength();
1060 if (size > m_outputTensors[i].userBufferLength) {
1061 LOGE("Output buffer size is not enough. Your size=%zu, but actual output size=%zu.",
1062 m_outputTensors[i].userBufferLength, size);
1063 return OH_NN_INVALID_PARAMETER;
1064 }
1065
1066 void* deviceBuffer = outputTensors[i]->GetBuffer();
1067 if (deviceBuffer == nullptr) {
1068 LOGE("Output buffer is nullptr.");
1069 return OH_NN_FAILED;
1070 }
1071
1072 status = memcpy_s(m_outputTensors[i].userBuffer, m_outputTensors[i].userBufferLength, deviceBuffer, size);
1073 if (status != EOK) {
1074 LOGE("Run failed, memory copy from device buffer to user buffer failed. Error code: %d.", status);
1075 return OH_NN_MEMORY_ERROR;
1076 }
1077 }
1078 }
1079
1080 m_isRun = true;
1081 return OH_NN_SUCCESS;
1082 }
1083
~NNExecutor()1084 NNExecutor::~NNExecutor()
1085 {
1086 for (auto& it : m_inputTensors) {
1087 if ((it.second).isInnerMem) {
1088 m_device->ReleaseBuffer((it.second).tensor->GetBuffer());
1089 }
1090 (it.second).tensor->SetBuffer(nullptr, 0);
1091 (it.second).tensor.reset();
1092 (it.second).userBuffer = nullptr;
1093 }
1094 m_inputTensors.clear();
1095
1096 for (auto& it : m_outputTensors) {
1097 if ((it.second).isInnerMem) {
1098 m_device->ReleaseBuffer((it.second).tensor->GetBuffer());
1099 }
1100 (it.second).tensor->SetBuffer(nullptr, 0);
1101 (it.second).tensor.reset();
1102 (it.second).userBuffer = nullptr;
1103 }
1104 m_outputTensors.clear();
1105
1106 for (auto& it : m_inputCreatedMem) {
1107 it.second.clear();
1108 }
1109 m_inputCreatedMem.clear();
1110
1111 for (auto& it : m_outputCreatedMem) {
1112 it.second.clear();
1113 }
1114 m_outputCreatedMem.clear();
1115
1116 if (m_executorConfig != nullptr) {
1117 delete m_executorConfig;
1118 m_executorConfig = nullptr;
1119 }
1120 }
1121 } // namespace NeuralNetworkRuntime
1122 } // namespace OHOS
1123