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