1 /*
2  * Copyright (c) 2022 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 #include "hdi_device_v2_1.h"
17 
18 #include "hdf_base.h"
19 #include "mindir.h"
20 #include "securec.h"
21 
22 #include "hdi_prepared_model_v2_1.h"
23 #include "lite_graph_to_hdi_model_v2_1.h"
24 #include "hdi_returncode_utils_v2_1.h"
25 #include "memory_manager.h"
26 #include "transform.h"
27 #include "common/log.h"
28 #include "common/utils.h"
29 
30 namespace OHOS {
31 namespace NeuralNetworkRuntime {
32 const size_t OFFLINE_MODEL_MINIMUM_INPUT_SIZE = 2;
33 
34 namespace {
TransHDIDeviceV2_1Type(const V2_1::DeviceType & iDeviceType)35 OH_NN_DeviceType TransHDIDeviceV2_1Type(const V2_1::DeviceType& iDeviceType)
36 {
37     switch (iDeviceType) {
38         case V2_1::DeviceType::CPU:
39             return OH_NN_CPU;
40         case V2_1::DeviceType::GPU:
41             return OH_NN_GPU;
42         case V2_1::DeviceType::ACCELERATOR:
43             return OH_NN_ACCELERATOR;
44         default:
45             return OH_NN_OTHERS;
46     }
47 }
48 
TransHDIDeviceV2_1Status(const V2_1::DeviceStatus & iDeviceStatus)49 DeviceStatus TransHDIDeviceV2_1Status(const V2_1::DeviceStatus& iDeviceStatus)
50 {
51     switch (iDeviceStatus) {
52         case V2_1::DeviceStatus::AVAILABLE:
53             return DeviceStatus::AVAILABLE;
54         case V2_1::DeviceStatus::BUSY:
55             return DeviceStatus::BUSY;
56         case V2_1::DeviceStatus::OFFLINE:
57             return DeviceStatus::OFFLINE;
58         default:
59             return DeviceStatus::UNKNOWN;
60     }
61 }
62 
TransPerformanceMode(const OH_NN_PerformanceMode & mode)63 V2_1::PerformanceMode TransPerformanceMode(const OH_NN_PerformanceMode& mode)
64 {
65     switch (mode) {
66         case OH_NN_PERFORMANCE_LOW:
67             return V2_1::PerformanceMode::PERFORMANCE_LOW;
68         case OH_NN_PERFORMANCE_MEDIUM:
69             return V2_1::PerformanceMode::PERFORMANCE_MEDIUM;
70         case OH_NN_PERFORMANCE_HIGH:
71             return V2_1::PerformanceMode::PERFORMANCE_HIGH;
72         case OH_NN_PERFORMANCE_EXTREME:
73             return V2_1::PerformanceMode::PERFORMANCE_EXTREME;
74         default:
75             return V2_1::PerformanceMode::PERFORMANCE_NONE;
76     }
77 }
78 
TransPriority(const OH_NN_Priority & priority)79 V2_1::Priority TransPriority(const OH_NN_Priority& priority)
80 {
81     switch (priority) {
82         case OH_NN_PRIORITY_LOW:
83             return V2_1::Priority::PRIORITY_LOW;
84         case OH_NN_PRIORITY_MEDIUM:
85             return V2_1::Priority::PRIORITY_MEDIUM;
86         case OH_NN_PRIORITY_HIGH:
87             return V2_1::Priority::PRIORITY_HIGH;
88         default:
89             return V2_1::Priority::PRIORITY_NONE;
90     }
91 }
92 
IsOfflineModel(std::shared_ptr<const mindspore::lite::LiteGraph> liteGraph,bool & isOfflineModel)93 OH_NN_ReturnCode IsOfflineModel(std::shared_ptr<const mindspore::lite::LiteGraph> liteGraph, bool& isOfflineModel)
94 {
95     isOfflineModel = false; // Initialize the returned value
96     if (liteGraph == nullptr) {
97         LOGE("LiteGraph is empty when identifying the offline model.");
98         return OH_NN_NULL_PTR;
99     }
100 
101     if (liteGraph->all_nodes_.size() == 0) {
102         LOGE("Find empty node in the model.");
103         return OH_NN_INVALID_PARAMETER;
104     }
105 
106     // If the model consists of more than 1 node, it will not be considered as offline model.
107     if (liteGraph->all_nodes_.size() > 1) {
108         isOfflineModel = false;
109         return OH_NN_SUCCESS;
110     }
111 
112     const mindspore::lite::LiteGraph::Node* pNode = liteGraph->all_nodes_[0];
113     if (pNode == nullptr) {
114         LOGE("Find invalid node in the model.");
115         return OH_NN_NULL_PTR;
116     }
117 
118     const mindspore::lite::NodeType& nodeType = mindspore::lite::MindIR_Primitive_GetType(pNode->primitive_);
119     if (nodeType == mindspore::lite::NodeType::NODE_TYPE_CUSTOM) {
120         isOfflineModel = true;
121     }
122 
123     return OH_NN_SUCCESS;
124 }
125 }  // unamed namespace
126 
HDIDeviceV2_1(OHOS::sptr<V2_1::INnrtDevice> device)127 HDIDeviceV2_1::HDIDeviceV2_1(OHOS::sptr<V2_1::INnrtDevice> device) : m_iDevice(device)
128 {}
129 
GetDeviceName(std::string & name)130 OH_NN_ReturnCode HDIDeviceV2_1::GetDeviceName(std::string& name)
131 {
132     auto ret = m_iDevice->GetDeviceName(name);
133     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
134         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get HDI device name failed");
135     }
136     return OH_NN_SUCCESS;
137 }
138 
GetVendorName(std::string & name)139 OH_NN_ReturnCode HDIDeviceV2_1::GetVendorName(std::string& name)
140 {
141     auto ret = m_iDevice->GetVendorName(name);
142     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
143         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get HDI vendor name failed");
144     }
145     return OH_NN_SUCCESS;
146 }
147 
GetVersion(std::string & version)148 OH_NN_ReturnCode HDIDeviceV2_1::GetVersion(std::string& version)
149 {
150     auto ret = m_iDevice->GetVersion(m_hdiVersion.first, m_hdiVersion.second);
151     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
152         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get HDI version failed");
153     }
154     version = 'v' + std::to_string(m_hdiVersion.first) + '_' + std::to_string(m_hdiVersion.second);
155     return OH_NN_SUCCESS;
156 }
157 
GetDeviceType(OH_NN_DeviceType & deviceType)158 OH_NN_ReturnCode HDIDeviceV2_1::GetDeviceType(OH_NN_DeviceType& deviceType)
159 {
160     V2_1::DeviceType iDeviceType;
161     auto ret = m_iDevice->GetDeviceType(iDeviceType);
162     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
163         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get HDI device type failed");
164     }
165 
166     deviceType = TransHDIDeviceV2_1Type(iDeviceType);
167     return OH_NN_SUCCESS;
168 }
169 
GetDeviceStatus(DeviceStatus & status)170 OH_NN_ReturnCode HDIDeviceV2_1::GetDeviceStatus(DeviceStatus& status)
171 {
172     V2_1::DeviceStatus iDeviceStatus;
173     auto ret = m_iDevice->GetDeviceStatus(iDeviceStatus);
174     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
175         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get HDI device status failed");
176     }
177     status = TransHDIDeviceV2_1Status(iDeviceStatus);
178     return OH_NN_SUCCESS;
179 }
180 
GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,std::vector<bool> & ops)181 OH_NN_ReturnCode HDIDeviceV2_1::GetSupportedOperation(std::shared_ptr<const mindspore::lite::LiteGraph> model,
182     std::vector<bool>& ops)
183 {
184     if (model == nullptr) {
185         LOGE("Model is nullptr, cannot query supported operation.");
186         return OH_NN_NULL_PTR;
187     }
188 
189     bool isOfflineModel {false};
190     OH_NN_ReturnCode innerRet = IsOfflineModel(model, isOfflineModel);
191     if (innerRet != OH_NN_SUCCESS) {
192         LOGE("Check offline model failed.");
193         return innerRet;
194     }
195 
196     // Permanently return a [true] array for offline model.
197     if (isOfflineModel) {
198         ops.clear();
199         ops.emplace_back(true);
200         return OH_NN_SUCCESS;
201     }
202 
203     OHOS::HDI::Nnrt::V2_1::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
204     size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
205     int32_t ret {0};
206     if (tensorSize > 0) {
207         ret = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
208         if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS || tensorBuffer.fd == INVALID_FD) {
209             return CheckReturnCode_V2_1(ret, OH_NN_FAILED, "Allocate tensor buffer error when get supported operation");
210         }
211     }
212 
213     auto iModel = NNRt_V2_1::LiteGraph_To_HDIModel(model.get(), tensorBuffer);
214     if (iModel == nullptr) {
215         LOGE("Parse litegraph to hdi model failed.");
216         ReleaseSharedBuffer(tensorBuffer);
217         return OH_NN_FAILED;
218     }
219 
220     ret = m_iDevice->GetSupportedOperation(*iModel, ops);
221 
222     NNRt_V2_1::HDIModel_Destroy(&iModel);
223     innerRet = ReleaseSharedBuffer(tensorBuffer);
224     if (innerRet != OH_NN_SUCCESS) {
225         LOGE("Release tensorBuffer failed.");
226         return OH_NN_FAILED;
227     }
228     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
229         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Get supported operation failed");
230     }
231     return OH_NN_SUCCESS;
232 }
233 
IsFloat16PrecisionSupported(bool & isSupported)234 OH_NN_ReturnCode HDIDeviceV2_1::IsFloat16PrecisionSupported(bool& isSupported)
235 {
236     auto ret = m_iDevice->IsFloat16PrecisionSupported(isSupported);
237     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
238         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Query fp16 precision supported failed");
239     }
240     return OH_NN_SUCCESS;
241 }
242 
IsPerformanceModeSupported(bool & isSupported)243 OH_NN_ReturnCode HDIDeviceV2_1::IsPerformanceModeSupported(bool& isSupported)
244 {
245     auto ret = m_iDevice->IsPerformanceModeSupported(isSupported);
246     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
247         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Query performance mode supported failed");
248     }
249     return OH_NN_SUCCESS;
250 }
251 
IsPrioritySupported(bool & isSupported)252 OH_NN_ReturnCode HDIDeviceV2_1::IsPrioritySupported(bool& isSupported)
253 {
254     auto ret = m_iDevice->IsPrioritySupported(isSupported);
255     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
256         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Query priority supported failed");
257     }
258     return OH_NN_SUCCESS;
259 }
260 
IsDynamicInputSupported(bool & isSupported)261 OH_NN_ReturnCode HDIDeviceV2_1::IsDynamicInputSupported(bool& isSupported)
262 {
263     auto ret = m_iDevice->IsDynamicInputSupported(isSupported);
264     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
265         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Query dynamic input supported failed");
266     }
267     return OH_NN_SUCCESS;
268 }
269 
IsModelCacheSupported(bool & isSupported)270 OH_NN_ReturnCode HDIDeviceV2_1::IsModelCacheSupported(bool& isSupported)
271 {
272     auto ret = m_iDevice->IsModelCacheSupported(isSupported);
273     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
274         return CheckReturnCode_V2_1(ret, OH_NN_UNAVAILABLE_DEVICE, "Query cache model supported failed");
275     }
276     return OH_NN_SUCCESS;
277 }
278 
PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,const ModelConfig & config,std::shared_ptr<PreparedModel> & preparedModel)279 OH_NN_ReturnCode HDIDeviceV2_1::PrepareModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
280     const ModelConfig& config, std::shared_ptr<PreparedModel>& preparedModel)
281 {
282     if (model == nullptr) {
283         LOGE("Model is nullptr, cannot prepare model.");
284         return OH_NN_INVALID_PARAMETER;
285     }
286 
287     OHOS::HDI::Nnrt::V2_1::SharedBuffer tensorBuffer {INVALID_FD, 0, 0, 0};
288     size_t tensorSize = mindspore::lite::MindIR_LiteGraph_GetConstTensorSize(model.get());
289     int32_t ret {0};
290     if (tensorSize > 0) {
291         ret = m_iDevice->AllocateBuffer(tensorSize, tensorBuffer);
292         if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS || tensorBuffer.fd == INVALID_FD) {
293             return CheckReturnCode_V2_1(ret, OH_NN_FAILED, "Allocate tensor buffer error when prepare model");
294         }
295     }
296 
297     V2_1::Model* iModel = NNRt_V2_1::LiteGraph_To_HDIModel(model.get(), tensorBuffer);
298     if (iModel == nullptr) {
299         LOGE("Parse litegraph to hdi model failed.");
300         ReleaseSharedBuffer(tensorBuffer);
301         return OH_NN_FAILED;
302     }
303 
304     V2_1::ModelConfig iModelConfig;
305     iModelConfig.enableFloat16 = config.enableFloat16;
306     iModelConfig.mode = TransPerformanceMode(config.mode);
307     iModelConfig.priority = TransPriority(config.priority);
308     OHOS::sptr<V2_1::IPreparedModel> iPreparedModel;
309 
310     ret = m_iDevice->PrepareModel(*iModel, iModelConfig, iPreparedModel);
311 
312     NNRt_V2_1::HDIModel_Destroy(&iModel);
313     auto innerRet = ReleaseSharedBuffer(tensorBuffer);
314     if (innerRet != OH_NN_SUCCESS) {
315         LOGE("Release tensorBuffer failed.");
316         return OH_NN_FAILED;
317     }
318     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS || iPreparedModel == nullptr) {
319         return CheckReturnCode_V2_1(ret, OH_NN_FAILED, "Prepare model failed");
320     }
321 
322     preparedModel = CreateSharedPtr<HDIPreparedModelV2_1>(iPreparedModel);
323     if (preparedModel == nullptr) {
324         LOGE("Prepare model failed, because fail to create preparedModel instance.");
325         return OH_NN_MEMORY_ERROR;
326     }
327 
328     return OH_NN_SUCCESS;
329 }
330 
PrepareModel(const void * metaGraph,const ModelConfig & config,std::shared_ptr<PreparedModel> & preparedModel)331 OH_NN_ReturnCode HDIDeviceV2_1::PrepareModel(const void* metaGraph,
332                                              const ModelConfig& config,
333                                              std::shared_ptr<PreparedModel>& preparedModel)
334 {
335     return OH_NN_OPERATION_FORBIDDEN;
336 }
337 
PrepareModelFromModelCache(const std::vector<Buffer> & modelCache,const ModelConfig & config,std::shared_ptr<PreparedModel> & preparedModel,bool & isUpdatable)338 OH_NN_ReturnCode HDIDeviceV2_1::PrepareModelFromModelCache(const std::vector<Buffer>& modelCache,
339     const ModelConfig& config, std::shared_ptr<PreparedModel>& preparedModel, bool& isUpdatable)
340 {
341     std::vector<V2_1::SharedBuffer> iBuffers;
342     auto memManager = MemoryManager::GetInstance();
343     Memory memory;
344     OH_NN_ReturnCode ret;
345     size_t modelCacheSize = modelCache.size();
346     for (size_t i = 0; i < modelCacheSize; i++) {
347         ret = memManager->GetMemory(modelCache[i].data, memory);
348         if (ret != OH_NN_SUCCESS) {
349             LOGE("The %{public}zuth model cache is invalid. Please put valid model cache.", i + 1);
350             return ret;
351         }
352         iBuffers.emplace_back(V2_1::SharedBuffer {memory.fd, memory.length, 0, memory.length});
353     }
354 
355     V2_1::ModelConfig iModelConfig;
356     iModelConfig.enableFloat16 = config.enableFloat16;
357     iModelConfig.mode = TransPerformanceMode(config.mode);
358     iModelConfig.priority = TransPriority(config.priority);
359 
360     OHOS::sptr<V2_1::IPreparedModel> iPreparedModel;
361     auto nnrtRet = m_iDevice->PrepareModelFromModelCache(iBuffers, iModelConfig, iPreparedModel);
362     if (nnrtRet != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
363         return CheckReturnCode_V2_1(nnrtRet, OH_NN_FAILED, "Prepare model from cache failed");
364     }
365 
366     preparedModel = CreateSharedPtr<HDIPreparedModelV2_1>(iPreparedModel);
367     if (preparedModel == nullptr) {
368         LOGE("Prepare model from model cache failed, because fail to create preparedModel instance.");
369         return OH_NN_MEMORY_ERROR;
370     }
371     return OH_NN_SUCCESS;
372 }
373 
AllocateBuffer(size_t length)374 void* HDIDeviceV2_1::AllocateBuffer(size_t length)
375 {
376     if (length == 0) {
377         LOGE("The length param is invalid, length=0");
378         return nullptr;
379     }
380 
381     V2_1::SharedBuffer buffer;
382     auto ret = m_iDevice->AllocateBuffer(length, buffer);
383     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
384         return CheckReturnCode_V2_1(ret, nullptr, "Allocate buffer error");
385     }
386 
387     auto memManager = MemoryManager::GetInstance();
388     auto addr = memManager->MapMemory(buffer.fd, length);
389     if (addr == nullptr) {
390         LOGE("Map fd to address failed.");
391         m_iDevice->ReleaseBuffer(buffer);
392     }
393     return addr;
394 }
395 
AllocateBuffer(size_t length,int & fd)396 OH_NN_ReturnCode HDIDeviceV2_1::AllocateBuffer(size_t length, int& fd)
397 {
398     if (length == 0) {
399         LOGE("The length param is invalid, length=0");
400         return OH_NN_INVALID_PARAMETER;
401     }
402 
403     V2_1::SharedBuffer buffer;
404     auto ret = m_iDevice->AllocateBuffer(length, buffer);
405     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
406         return CheckReturnCode_V2_1(ret, OH_NN_MEMORY_ERROR, "Allocate buffer error");
407     }
408 
409     fd = buffer.fd;
410     return OH_NN_SUCCESS;
411 }
412 
ReleaseBuffer(int fd,size_t length)413 OH_NN_ReturnCode HDIDeviceV2_1::ReleaseBuffer(int fd, size_t length)
414 {
415     V2_1::SharedBuffer hdiBuffer {fd, length, 0, length};
416     auto deviceResult = m_iDevice->ReleaseBuffer(hdiBuffer);
417     if (deviceResult != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
418         return CheckReturnCode_V2_1(deviceResult, OH_NN_FAILED, "Device release buffer error");
419     }
420     return OH_NN_SUCCESS;
421 }
422 
AllocateTensorBuffer(size_t length,std::shared_ptr<TensorDesc> tensor)423 void* HDIDeviceV2_1::AllocateTensorBuffer(size_t length, std::shared_ptr<TensorDesc> tensor)
424 {
425     return AllocateBuffer(length);
426 }
427 
AllocateTensorBuffer(size_t length,std::shared_ptr<NNTensor> tensor)428 void* HDIDeviceV2_1::AllocateTensorBuffer(size_t length, std::shared_ptr<NNTensor> tensor)
429 {
430     return AllocateBuffer(length);
431 }
432 
ReleaseBuffer(const void * buffer)433 OH_NN_ReturnCode HDIDeviceV2_1::ReleaseBuffer(const void* buffer)
434 {
435     if (buffer == nullptr) {
436         LOGE("Buffer is nullptr, no need to release.");
437         return OH_NN_INVALID_PARAMETER;
438     }
439 
440     auto memManager = MemoryManager::GetInstance();
441     Memory memory;
442     auto ret = memManager->GetMemory(buffer, memory);
443     if (ret != OH_NN_SUCCESS) {
444         LOGE("Invalid Buffer, it is not NNRt buffer.");
445         return ret;
446     }
447 
448     V2_1::SharedBuffer hdiBuffer {memory.fd, memory.length, 0, memory.length};
449     auto deviceResult = m_iDevice->ReleaseBuffer(hdiBuffer);
450     if (deviceResult != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
451         return CheckReturnCode_V2_1(deviceResult, OH_NN_FAILED, "Device release buffer error");
452     }
453 
454     ret = memManager->UnMapMemory(buffer);
455     if (ret != OH_NN_SUCCESS) {
456         LOGE("Unmap memory failed.");
457         return ret;
458     }
459 
460     return OH_NN_SUCCESS;
461 }
462 
ReleaseSharedBuffer(const V2_1::SharedBuffer & buffer)463 OH_NN_ReturnCode HDIDeviceV2_1::ReleaseSharedBuffer(const V2_1::SharedBuffer& buffer)
464 {
465     if (buffer.fd == INVALID_FD) {
466         LOGI("No need to release. fd=%{public}d", INVALID_FD);
467         return OH_NN_SUCCESS;
468     }
469 
470     auto ret = m_iDevice->ReleaseBuffer(buffer);
471     if (ret != V2_1::NNRT_ReturnCode::NNRT_SUCCESS) {
472         return CheckReturnCode_V2_1(ret, OH_NN_FAILED, "Device release buffer error");
473     }
474     return OH_NN_SUCCESS;
475 }
476 
GetOfflineModelFromLiteGraph(std::shared_ptr<const mindspore::lite::LiteGraph> graph,std::vector<std::vector<uint8_t>> & offlineModels)477 OH_NN_ReturnCode HDIDeviceV2_1::GetOfflineModelFromLiteGraph(std::shared_ptr<const mindspore::lite::LiteGraph> graph,
478                                                              std::vector<std::vector<uint8_t>>& offlineModels)
479 {
480     // graph has been checked in PrepareOfflineModel, no need to check twice.
481     offlineModels.clear();
482 
483     const size_t inputNum = graph->all_nodes_[0]->input_indices_.size();
484     if (inputNum < OFFLINE_MODEL_MINIMUM_INPUT_SIZE) {
485         LOGE("LiteGraph with offline model should have at least two input tensors, only get %zu.", inputNum);
486         return OH_NN_INVALID_PARAMETER;
487     }
488 
489     // The offline model is integrated into the last input tensor.
490     uint32_t index = graph->all_nodes_[0]->input_indices_[inputNum - 1];
491     mindspore::lite::TensorPtr pTensor = graph->all_tensors_[index];
492     std::vector<uint8_t> offlineModel = mindspore::lite::MindIR_Tensor_GetData(pTensor);
493     if (offlineModel.size() == (size_t) 0) {
494         LOGE("Offline model has size of 0, please check the ms model.");
495         return OH_NN_INVALID_PARAMETER;
496     }
497     offlineModels.emplace_back(std::move(offlineModel));
498 
499     return OH_NN_SUCCESS;
500 }
501 
AllocateDeviceBufferForOfflineModel(const std::vector<std::vector<uint8_t>> & offlineModels,std::vector<Buffer> & deviceBuffers)502 OH_NN_ReturnCode HDIDeviceV2_1::AllocateDeviceBufferForOfflineModel(
503     const std::vector<std::vector<uint8_t>>& offlineModels, std::vector<Buffer>& deviceBuffers)
504 {
505     // offlineModels is guaranteed to have at least one element in GetOfflineModelFromLiteGraph, no need to check size.
506     deviceBuffers.clear();
507 
508     for (const std::vector<uint8_t>& offlineModel : offlineModels) {
509         const size_t offlineModelSize = offlineModel.size();
510 
511         void* newModelBuffer = AllocateBuffer(offlineModelSize);
512         if (newModelBuffer == nullptr) {
513             // Release allocated model buffer if error happens.
514             OH_NN_ReturnCode status {OH_NN_SUCCESS};
515             for (const Buffer& deviceBuffer : deviceBuffers) {
516                 status = ReleaseBuffer(deviceBuffer.data);
517                 if (status != OH_NN_SUCCESS) {
518                     LOGE("Release shared buffer of offline model failed.");
519                     return status;
520                 }
521             }
522 
523             deviceBuffers.clear();
524             LOGE("Error happens when allocating shared buffer for offline model.");
525             return OH_NN_MEMORY_ERROR;
526         }
527 
528         Buffer modelBuffer {nullptr, 0};
529         modelBuffer.data = newModelBuffer;
530         modelBuffer.length = offlineModelSize;
531         deviceBuffers.emplace_back(modelBuffer);
532     }
533 
534     return OH_NN_SUCCESS;
535 }
536 
CopyOfflineModelToDevice(const std::vector<std::vector<uint8_t>> & offlineModels,std::vector<Buffer> & deviceBuffers)537 OH_NN_ReturnCode HDIDeviceV2_1::CopyOfflineModelToDevice(const std::vector<std::vector<uint8_t>>& offlineModels,
538                                                          std::vector<Buffer>& deviceBuffers)
539 {
540     if (offlineModels.size() != deviceBuffers.size()) {
541         LOGE("CopyOfflineModelToDevice failed, number of offlineModels not equal to allocated buffers.");
542         return OH_NN_INVALID_PARAMETER;
543     }
544 
545     const void* offlineModel {nullptr};
546     size_t offlineModelSize {0};
547     void* deviceBuffer {nullptr};
548     size_t deviceBufferSize {0};
549 
550     size_t offlineModelsSize = offlineModels.size();
551     for (size_t i = 0; i < offlineModelsSize; i++) {
552         offlineModel = offlineModels[i].data();
553         offlineModelSize = offlineModels[i].size();
554         deviceBuffer = deviceBuffers[i].data;
555         deviceBufferSize = deviceBuffers[i].length;
556 
557         // Copy offline model to shared buffer of device.
558         errno_t errorCode = memcpy_s(deviceBuffer, deviceBufferSize, offlineModel, offlineModelSize);
559         if (errorCode != EOK) {
560             LOGE("Error happened when copy offline model to device buffer. Error code: %d.", errorCode);
561             return OH_NN_MEMORY_ERROR;
562         }
563     }
564 
565     return OH_NN_SUCCESS;
566 }
567 
PrepareOfflineModel(std::vector<Buffer> & deviceBuffers,const ModelConfig & config,const std::map<std::string,std::vector<int8_t>> & extensions,std::shared_ptr<PreparedModel> & preparedModel)568 OH_NN_ReturnCode HDIDeviceV2_1::PrepareOfflineModel(std::vector<Buffer>& deviceBuffers,
569                                                     const ModelConfig& config,
570                                                     const std::map<std::string, std::vector<int8_t>>& extensions,
571                                                     std::shared_ptr<PreparedModel>& preparedModel)
572 {
573     V2_1::ModelConfig iModelConfig;
574     iModelConfig.enableFloat16 = config.enableFloat16;
575     iModelConfig.mode = TransPerformanceMode(config.mode);
576     iModelConfig.priority = TransPriority(config.priority);
577     iModelConfig.extensions = extensions;
578     OHOS::sptr<V2_1::IPreparedModel> iPreparedModel;
579 
580     std::vector<V2_1::SharedBuffer> iBuffers;
581     auto memManager = MemoryManager::GetInstance();
582     Memory memory;
583     OH_NN_ReturnCode ret;
584     size_t numOfflineModel = deviceBuffers.size();
585     for (size_t i = 0; i < numOfflineModel; i++) {
586         ret = memManager->GetMemory(deviceBuffers[i].data, memory);
587         if (ret != OH_NN_SUCCESS) {
588             LOGE("Retrieve the memory of %zuth device buffer failed.", i);
589             return ret;
590         }
591         iBuffers.emplace_back(V2_1::SharedBuffer {memory.fd, memory.length, 0, memory.length});
592     }
593 
594     auto preparedRet = m_iDevice->PrepareOfflineModel(iBuffers, iModelConfig, iPreparedModel);
595 
596     // Release allocated model buffer after prepare model.
597     OH_NN_ReturnCode status {OH_NN_SUCCESS};
598     for (const Buffer& deviceBuffer : deviceBuffers) {
599         status = ReleaseBuffer(deviceBuffer.data);
600         if (status != OH_NN_SUCCESS) {
601             LOGE("Release shared buffer of offline model failed.");
602             return status;
603         }
604     }
605     deviceBuffers.clear();
606 
607     if (preparedRet != V2_1::NNRT_ReturnCode::NNRT_SUCCESS || iPreparedModel == nullptr) {
608         return CheckReturnCode_V2_1(preparedRet, OH_NN_FAILED, "Prepare offline model failed");
609     }
610 
611     preparedModel = CreateSharedPtr<HDIPreparedModelV2_1>(iPreparedModel);
612     if (preparedModel == nullptr) {
613         LOGE("Prepare model failed, because fail to create preparedModel instance.");
614         return OH_NN_MEMORY_ERROR;
615     }
616 
617     return OH_NN_SUCCESS;
618 }
619 
PrepareOfflineModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,const ModelConfig & config,std::shared_ptr<PreparedModel> & preparedModel)620 OH_NN_ReturnCode HDIDeviceV2_1::PrepareOfflineModel(std::shared_ptr<const mindspore::lite::LiteGraph> model,
621                                                     const ModelConfig& config,
622                                                     std::shared_ptr<PreparedModel>& preparedModel)
623 {
624     if (model == nullptr) {
625         LOGE("LiteGraph is empty when identifying the offline model.");
626         return OH_NN_NULL_PTR;
627     }
628 
629     std::vector<std::vector<uint8_t>> offlineModels;
630     OH_NN_ReturnCode status = GetOfflineModelFromLiteGraph(model, offlineModels);
631     if (status != OH_NN_SUCCESS) {
632         LOGE("Error happens when getting offline models from lite graph.");
633         return status;
634     }
635 
636     std::vector<Buffer> deviceBuffers;
637     status = AllocateDeviceBufferForOfflineModel(offlineModels, deviceBuffers);
638     if (status != OH_NN_SUCCESS) {
639         LOGE("Error happens when allocating device buffers for offline model.");
640         return status;
641     }
642 
643     status = CopyOfflineModelToDevice(offlineModels, deviceBuffers);
644     if (status != OH_NN_SUCCESS) {
645         LOGE("Error happened when copying offline models to device buffers.");
646 
647         OH_NN_ReturnCode ret {OH_NN_SUCCESS};
648         // Release allocated model buffer if error happens.
649         for (const Buffer& deviceBuffer : deviceBuffers) {
650             ret = ReleaseBuffer(deviceBuffer.data);
651             if (ret != OH_NN_SUCCESS) {
652                 LOGE("Releasing device buffer failed after copying offline models to device buffers failed.");
653                 return ret;
654             }
655         }
656 
657         return status;
658     }
659 
660     // Retrieve offline model configs from Custom primitive and insert to extensions.
661     std::string key;
662     std::vector<uint8_t> valueFromCustomPrimitive;
663     std::vector<int8_t> value;
664     std::map<std::string, std::vector<int8_t>> extensions;
665     std::vector<const mindspore::schema::Attribute*> attributes =
666         mindspore::lite::MindIR_Custom_GetAttr(model->all_nodes_[0]->primitive_);
667     for (const auto& attribute : attributes) {
668         key = mindspore::lite::MindIR_Attribute_GetName(*attribute);
669         valueFromCustomPrimitive = mindspore::lite::MindIR_Attribute_GetData(*attribute);
670         value.assign(valueFromCustomPrimitive.begin(), valueFromCustomPrimitive.end());
671         extensions.insert(std::pair<std::string, std::vector<int8_t>>(key, value));
672     }
673 
674     status = PrepareOfflineModel(deviceBuffers, config, extensions, preparedModel);
675     if (status != OH_NN_SUCCESS) {
676         LOGE("PrepareOfflineModel failed.");
677         return status;
678     }
679 
680     return OH_NN_SUCCESS;
681 }
682 } // namespace NeuralNetworkRuntime
683 } // namespace OHOS
684