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