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