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