• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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