Merge "Support two more tensor type" into tizen
[platform/core/multimedia/inference-engine-mlapi.git] / src / inference_engine_mlapi.cpp
1 /**
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <inference_engine_error.h>
18 #include "inference_engine_mlapi_private.h"
19
20 #include <fstream>
21 #include <iostream>
22 #include <unistd.h>
23 #include <time.h>
24 #include <queue>
25
26 namespace InferenceEngineImpl
27 {
28 namespace MLAPIImpl
29 {
30         InferenceMLAPI::InferenceMLAPI(void) :
31                         mPluginType(),
32                         mTargetDevice(),
33                         mSingle(),
34                         mDesignated_inputs(),
35                         mDesignated_outputs(),
36                         mInputProperty(),
37                         mOutputProperty(),
38                         mInputTensorBuffer(),
39                         mOutputTensorBuffer(),
40                         mInputTensorInfo(),
41                         mOutputTensorInfo()
42         {
43                 LOGI("ENTER");
44
45                 LOGI("LEAVE");
46         }
47
48         InferenceMLAPI::~InferenceMLAPI()
49         {
50                 mDesignated_inputs.clear();
51                 std::vector<std::string>().swap(mDesignated_inputs);
52
53                 mDesignated_outputs.clear();
54                 std::vector<std::string>().swap(mDesignated_outputs);
55
56                 ml_single_close(mSingle);
57         }
58
59         int InferenceMLAPI::SetPrivateData(void *data)
60         {
61                 LOGI("ENTER");
62
63                 inference_backend_type_e type =
64                                 *(static_cast<inference_backend_type_e *>(data));
65
66                 if (INFERENCE_BACKEND_ONE != type && INFERENCE_BACKEND_MLAPI != type) {
67                         LOGE("Invalid backend type.");
68                         return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
69                 }
70
71                 mPluginType = type;
72
73                 LOGI("LEAVE");
74
75                 return INFERENCE_ENGINE_ERROR_NONE;
76         }
77
78         int InferenceMLAPI::SetTargetDevices(int types)
79         {
80                 LOGI("ENTER");
81
82                 LOGI("Inference targets are, ");
83                 if (types & INFERENCE_TARGET_CPU) {
84                         mTargetDevice |= INFERENCE_TARGET_CPU;
85                         LOGI("CPU");
86                 }
87
88                 if (types & INFERENCE_TARGET_GPU) {
89                         mTargetDevice |= INFERENCE_TARGET_GPU;
90                         LOGI("GPU");
91                 }
92
93                 if (types & INFERENCE_TARGET_CUSTOM) {
94                         mTargetDevice |= INFERENCE_TARGET_CUSTOM;
95                         LOGI("NPU");
96                 }
97
98                 LOGI("LEAVE");
99
100                 return INFERENCE_ENGINE_ERROR_NONE;
101         }
102
103         int InferenceMLAPI::Load(std::vector<std::string> model_paths,
104                                                          inference_model_format_e model_format)
105         {
106                 LOGI("ENTER");
107
108                 std::string model_str(model_paths[0]);
109
110                 // TODO. Set NNFW backend type and HW type properly.
111
112                 ml_nnfw_type_e nnfw_type;
113                 ml_nnfw_hw_e nnfw_hw;
114
115                 switch (mPluginType) {
116                 case INFERENCE_BACKEND_MLAPI:
117                         // For now, backend type is MLAPI and target device type is CUSTOM then
118                         // we will use Vivante NPU.
119                         // TODO. other NPU should be considered later. I.e., SRNPU.
120                         if ((mTargetDevice & INFERENCE_TARGET_CUSTOM) ==
121                                 INFERENCE_TARGET_CUSTOM) {
122                                 nnfw_type = ML_NNFW_TYPE_VIVANTE;
123                                 nnfw_hw = ML_NNFW_HW_ANY;
124                                 LOGI("Vivante tensor filter will be used.");
125                         } else {
126                                 LOGE("Invalid target device type.");
127                                 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
128                         }
129
130                         if (access(model_str.c_str(), R_OK) ||
131                                         access(model_paths[1].c_str(), R_OK)) {
132                                 LOGE("model file path in [%s,%s]", model_str.c_str(),
133                                                                                                    model_paths[1].c_str());
134                                 return INFERENCE_ENGINE_ERROR_INVALID_PATH;
135                         }
136
137                         // ML Single API of MLAPI requires model_paths rule like below,
138                         // "so library file path,nb model file path" or vise versa.
139                         model_str += "," + model_paths[1];
140                         break;
141                 case INFERENCE_BACKEND_ONE:
142                         nnfw_type = ML_NNFW_TYPE_NNFW;
143
144                         if (access(model_str.c_str(), R_OK)) {
145                                 LOGE("model file path in [%s]", model_str.c_str());
146                                 return INFERENCE_ENGINE_ERROR_INVALID_PATH;
147                         }
148
149                         if (mTargetDevice == INFERENCE_TARGET_CPU) {
150                                 nnfw_hw = ML_NNFW_HW_CPU_NEON;
151                                 LOGI("Target device is NEON.");
152                         } else if (mTargetDevice == INFERENCE_TARGET_GPU) {
153                                 nnfw_hw = ML_NNFW_HW_GPU;
154                                 LOGI("Target device is GPU");
155                         } else {
156                                 LOGE("Invalid inference target device type.");
157                                 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
158                         }
159
160                         LOGI("NNFW tensor filter will be used.");
161                         break;
162                 // TODO.
163                 default:
164                         LOGE("Invalid plugin type.");
165                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
166                 }
167
168                 LOGI("Model name = %s", model_str.c_str());
169
170                 // TODO. create ml_tensor_info for input and output tensor and pass
171                 //               them as parameters of ml_single_open function.
172
173                 int ret = ml_single_open(&mSingle, model_str.c_str(), NULL, NULL,
174                                                                  nnfw_type, nnfw_hw);
175                 if (ret != ML_ERROR_NONE) {
176                         LOGE("Failed to request ml_single_open(%d).", ret);
177                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
178                 }
179
180                 LOGI("LEAVE");
181
182                 return INFERENCE_ENGINE_ERROR_NONE;
183         }
184
185         int InferenceMLAPI::GetInputTensorBuffers(
186                         std::vector<inference_engine_tensor_buffer> &buffers)
187         {
188                 LOGI("ENTER");
189
190                 // TODO. Implement this function according to a given ML Single API backend properly.
191
192                 LOGI("LEAVE");
193
194                 return INFERENCE_ENGINE_ERROR_NONE;
195         }
196
197         int InferenceMLAPI::GetOutputTensorBuffers(
198                         std::vector<inference_engine_tensor_buffer> &buffers)
199         {
200                 LOGI("ENTER");
201
202                 // TODO. Implement this function according to a given ML Single API backend properly.
203
204                 LOGI("LEAVE");
205
206                 return INFERENCE_ENGINE_ERROR_NONE;
207         }
208
209         int InferenceMLAPI::GetInputLayerProperty(
210                         inference_engine_layer_property &property)
211         {
212                 LOGI("ENTER");
213
214                 ml_tensors_info_h in_info = NULL;
215
216                 // TODO. Need to check if model file loading is done.
217
218                 int ret = ml_single_get_input_info(mSingle, &in_info);
219                 if (ret != ML_ERROR_NONE) {
220                         LOGE("Failed to request ml_single_get_input_info(%d).", ret);
221                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
222                 }
223
224                 unsigned int cnt;
225                 ret = ml_tensors_info_get_count(in_info, &cnt);
226                 if (ret != ML_ERROR_NONE) {
227                         LOGE("Failed to request ml_tensors_info_get_count(%d).", ret);
228                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
229                 }
230
231                 LOGI("input tensor count = %u", cnt);
232
233                 for (unsigned int i = 0; i < cnt; ++i) {
234                         inference_engine_tensor_info tensor_info;
235                         ml_tensor_type_e in_type;
236                         unsigned int in_dim[ML_TENSOR_RANK_LIMIT];
237                         char *in_name = NULL;
238                         size_t in_size = 1;
239
240                         ret = ml_tensors_info_get_tensor_type(in_info, i, &in_type);
241                         if (ret != ML_ERROR_NONE) {
242                                 LOGE("Failed to request ml_tensors_info_get_tensor_type(%d).",
243                                          ret);
244                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
245                         }
246
247                         LOGI("input tensor type = %d", in_type);
248
249                         int type = ConvertTensorType(in_type);
250                         if (type == -1) {
251                                 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
252                         }
253
254                         ret = ml_tensors_info_get_tensor_dimension(in_info, i, in_dim);
255                         if (ret != ML_ERROR_NONE) {
256                                 LOGE("Failed to request ml_tensors_info_get_tensor_dimension(%d).",
257                                          ret);
258                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
259                         }
260
261                         LOGI("Input tensor dimension:");
262                         for (unsigned int shape_idx = 0; shape_idx < ML_TENSOR_RANK_LIMIT; ++shape_idx) {
263                                 tensor_info.shape.push_back(in_dim[shape_idx]);
264                                 in_size *= static_cast<size_t>(in_dim[shape_idx]);
265                                 LOGI("%u", in_dim[shape_idx]);
266                         }
267
268                         LOGI("input tensor size = %zu", in_size);
269
270                         ret = ml_tensors_info_get_tensor_name(in_info, i, &in_name);
271                         if (ret != ML_ERROR_NONE) {
272                                 LOGE("Failed to request ml_tensors_info_get_tensor_name(%d).",
273                                          ret);
274                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
275                         }
276
277                         LOGI("input tensor name = %s", in_name);
278
279                         tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
280                         tensor_info.size = in_size;
281
282                         property.tensor_infos.push_back(tensor_info);
283
284                         // TODO. Compare tensor info from engine to one from a given property.
285                 }
286
287                 property.layer_names = mInputProperty.layer_names;
288
289                 LOGI("LEAVE");
290
291                 return INFERENCE_ENGINE_ERROR_NONE;
292         }
293
294         int InferenceMLAPI::GetOutputLayerProperty(
295                         inference_engine_layer_property &property)
296         {
297                 LOGI("ENTER");
298
299                 ml_tensors_info_h out_info = NULL;
300
301                 // TODO. Need to check if model file loading is done.
302
303                 int ret = ml_single_get_output_info(mSingle, &out_info);
304                 if (ret != ML_ERROR_NONE) {
305                         LOGE("Failed to request ml_single_get_output_info(%d).", ret);
306                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
307                 }
308
309                 unsigned int cnt;
310                 ret = ml_tensors_info_get_count(out_info, &cnt);
311                 if (ret != ML_ERROR_NONE) {
312                         LOGE("Failed to request ml_tensors_info_get_count(%d).", ret);
313                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
314                 }
315
316                 LOGI("output tensor count = %u", cnt);
317
318                 for (unsigned int i = 0; i < cnt; ++i) {
319                         inference_engine_tensor_info tensor_info;
320                         ml_tensor_type_e out_type;
321                         unsigned int out_dim[ML_TENSOR_RANK_LIMIT];
322                         char *out_name = NULL;
323                         size_t out_size = 1;
324
325                         ret = ml_tensors_info_get_tensor_type(out_info, i, &out_type);
326                         if (ret != ML_ERROR_NONE) {
327                                 LOGE("Failed to request ml_tensors_info_get_tensor_type(%d).",
328                                          ret);
329                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
330                         }
331
332                         LOGI("output tensor type = %d", out_type);
333
334                         int type = ConvertTensorType(out_type);
335                         if (type == -1) {
336                                 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
337                         }
338
339                         ret = ml_tensors_info_get_tensor_dimension(out_info, i, out_dim);
340                         if (ret != ML_ERROR_NONE) {
341                                 LOGE("Failed to request ml_tensors_info_get_tensor_dimension(%d).",
342                                          ret);
343                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
344                         }
345
346                         int shape_size = 0;
347
348                         LOGI("Output tensor dimension:");
349
350                         for (unsigned int shape_idx = 0; shape_idx < ML_TENSOR_RANK_LIMIT; ++shape_idx) {
351                                 out_size *= static_cast<size_t>(out_dim[shape_idx]);
352
353                                 if (out_dim[shape_idx] == 1 && shape_size == 0)
354                                         shape_size = shape_idx;
355
356                                 LOGI("%d", out_dim[shape_idx]);
357                         }
358
359                         LOGI("Shape size of output tensor : %d", shape_size);
360                         LOGI("Reversed output tensor dimension:");
361
362                         // Reverse shape order.
363                         for (int idx = shape_size; idx >= 0; --idx) {
364                                 tensor_info.shape.push_back(out_dim[idx]);
365                                 LOGI("%u", out_dim[idx]);
366                         }
367
368                         LOGI("output tensor size = %zu", out_size);
369
370                         ret = ml_tensors_info_get_tensor_name(out_info, i, &out_name);
371                         if (ret != ML_ERROR_NONE) {
372                                 LOGE("Failed to request ml_tensors_info_get_tensor_name(%d).",
373                                          ret);
374                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
375                         }
376
377                         LOGI("output tensor name = %s", out_name);
378
379                         tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
380                         tensor_info.size = out_size;
381
382                         property.tensor_infos.push_back(tensor_info);
383
384                         // TODO. Compare tensor info from engine to one from a given property.
385                 }
386
387                 property.layer_names = mOutputProperty.layer_names;
388
389                 LOGI("LEAVE");
390
391                 return INFERENCE_ENGINE_ERROR_NONE;
392         }
393
394         int InferenceMLAPI::SetInputLayerProperty(
395                         inference_engine_layer_property &property)
396         {
397                 LOGI("ENTER");
398
399                 std::vector<std::string>::iterator iter;
400                 for (iter = property.layer_names.begin();
401                          iter != property.layer_names.end(); iter++) {
402                         std::string name = *iter;
403                         LOGI("input layer name = %s", name.c_str());
404                 }
405
406                 mDesignated_inputs.clear();
407                 std::vector<std::string>().swap(mDesignated_inputs);
408
409                 // TODO. Request input property information to a given ML Single API of nnstreamer backend,
410                 // and set it instead of user-given one,
411
412                 mDesignated_inputs = property.layer_names;
413                 mInputProperty = property;
414
415                 LOGI("LEAVE");
416
417                 return INFERENCE_ENGINE_ERROR_NONE;
418         }
419
420         int InferenceMLAPI::SetOutputLayerProperty(
421                         inference_engine_layer_property &property)
422         {
423                 LOGI("ENTER");
424
425                 std::vector<std::string>::iterator iter;
426                 for (iter = property.layer_names.begin();
427                          iter != property.layer_names.end(); iter++) {
428                         std::string name = *iter;
429                         LOGI("output layer name = %s", name.c_str());
430                 }
431
432                 mDesignated_outputs.clear();
433                 std::vector<std::string>().swap(mDesignated_outputs);
434
435                 // TODO. Request output property information to a given ML Single API of nnstreamer backend,
436                 // and set it instead of user-given one,
437
438                 mDesignated_outputs = property.layer_names;
439                 mOutputProperty = property;
440
441                 LOGI("LEAVE");
442
443                 return INFERENCE_ENGINE_ERROR_NONE;
444         }
445
446         int InferenceMLAPI::GetBackendCapacity(inference_engine_capacity *capacity)
447         {
448                 LOGI("ENTER");
449
450                 if (capacity == NULL) {
451                         LOGE("Bad pointer.");
452                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
453                 }
454
455                 // TODO. flag supported accel device types according to a given ML Single API of nnstreamer backend.
456                 if (mPluginType == INFERENCE_BACKEND_MLAPI) {
457                         capacity->supported_accel_devices = INFERENCE_TARGET_CUSTOM;
458                 } else {
459                         capacity->supported_accel_devices = INFERENCE_TARGET_GPU |
460                                                                                                 INFERENCE_TARGET_CPU;
461                 }
462
463                 LOGI("LEAVE");
464
465                 return INFERENCE_ENGINE_ERROR_NONE;
466         }
467
468         int InferenceMLAPI::CheckTensorBuffers(
469                         std::vector<inference_engine_tensor_buffer> &input_buffers,
470                         std::vector<inference_engine_tensor_buffer> &output_buffers)
471         {
472                 LOGI("ENTER");
473
474                 LOGI("LEAVE");
475
476                 return INFERENCE_ENGINE_ERROR_NONE;
477         }
478
479         int InferenceMLAPI::ConvertTensorType(int tensor_type)
480         {
481                 LOGI("ENTER");
482
483                 switch (tensor_type) {
484                 case ML_TENSOR_TYPE_FLOAT32:
485                         return INFERENCE_TENSOR_DATA_TYPE_FLOAT32;
486                 case ML_TENSOR_TYPE_UINT8:
487                         return INFERENCE_TENSOR_DATA_TYPE_UINT8;
488                 case ML_TENSOR_TYPE_UINT16:
489                         return INFERENCE_TENSOR_DATA_TYPE_UINT16;
490                 case ML_TENSOR_TYPE_INT64:
491                         return INFERENCE_TENSOR_DATA_TYPE_INT64;
492                 case ML_TENSOR_TYPE_UINT64:
493                         return INFERENCE_TENSOR_DATA_TYPE_UINT64;
494                 default:
495                         LOGE("Tensor type(%d) is invalid.", tensor_type);
496                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
497                 }
498
499                 LOGI("LEAVE");
500
501                 return -1;
502         }
503
504         int InferenceMLAPI::Run(
505                         std::vector<inference_engine_tensor_buffer> &input_buffers,
506                         std::vector<inference_engine_tensor_buffer> &output_buffers)
507         {
508                 LOGI("ENTER");
509
510                 // Make sure to check if tensor buffer count and binding info one are same.
511                 int err = CheckTensorBuffers(input_buffers, output_buffers);
512                 if (err != INFERENCE_ENGINE_ERROR_NONE) {
513                         return err;
514                 }
515
516                 ml_tensors_info_h in_info = NULL;
517
518                 err = ml_single_get_input_info(mSingle, &in_info);
519                 if (err != ML_ERROR_NONE) {
520                         LOGE("Failed to request ml_single_get_input_info(%d).", err);
521                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
522                 }
523
524                 ml_tensors_data_h input_data = NULL;
525                 err = ml_tensors_data_create(in_info, &input_data);
526                 if (err != ML_ERROR_NONE) {
527                         LOGE("Failed to request ml_tensors_data_create(%d).", err);
528                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
529                 }
530
531                 unsigned int in_cnt;
532                 err = ml_tensors_info_get_count(in_info, &in_cnt);
533                 if (err != ML_ERROR_NONE) {
534                         LOGE("Failed to request ml_tensors_info_get_count(%d).", err);
535                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
536                 }
537
538                 ml_tensors_info_h out_info = NULL;
539
540                 err = ml_single_get_output_info(mSingle, &out_info);
541                 if (err != ML_ERROR_NONE) {
542                         LOGE("Failed to request ml_single_get_output_info(%d).", err);
543                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
544                 }
545
546                 unsigned int out_cnt;
547                 err = ml_tensors_info_get_count(out_info, &out_cnt);
548                 if (err != ML_ERROR_NONE) {
549                         LOGE("Failed to request ml_tensors_info_get_count(%d).", err);
550                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
551                 }
552
553                 for (unsigned int i = 0; i < in_cnt; ++i) {
554                         LOGI("index(%d) : buffer = %p, size = %zu\n", i,
555                                  input_buffers[i].buffer, input_buffers[i].size);
556                         err = ml_tensors_data_set_tensor_data(input_data, i,
557                                                                                                   input_buffers[i].buffer,
558                                                                                                   input_buffers[i].size);
559                         if (err != ML_ERROR_NONE) {
560                                 LOGE("Failed to request ml_tensors_data_set_tensor_data(%d).",
561                                          err);
562                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
563                         }
564                 }
565
566                 ml_tensors_data_h output_data = NULL;
567                 err = ml_single_invoke(mSingle, input_data, &output_data);
568                 if (err != ML_ERROR_NONE) {
569                         LOGE("Failed to request ml_single_invoke(%d).", err);
570                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
571                 }
572
573                 for (unsigned int i = 0; i < out_cnt; ++i) {
574                         err = ml_tensors_data_get_tensor_data(
575                                 output_data, i, (void **) &output_buffers[i].buffer,
576                                 &output_buffers[i].size);
577                         if (err != ML_ERROR_NONE) {
578                                 LOGE("Failed to request ml_tensors_data_get_tensor_data(%d).", err);
579                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
580                         }
581                         LOGI("Output tensor[%u] = %zu", i, output_buffers[0].size);
582                 }
583
584                 LOGI("LEAVE");
585
586                 return INFERENCE_ENGINE_ERROR_NONE;
587         }
588
589         extern "C"
590         {
591                 class IInferenceEngineCommon *EngineCommonInit(void)
592                 {
593                         LOGI("ENTER");
594
595                         InferenceMLAPI *engine = new InferenceMLAPI();
596
597                         LOGI("LEAVE");
598
599                         return engine;
600                 }
601
602                 void EngineCommonDestroy(class IInferenceEngineCommon *engine)
603                 {
604                         LOGI("ENTER");
605
606                         delete engine;
607
608                         LOGI("LEAVE");
609                 }
610         }
611 } /* MLAPIImpl */
612 } /* InferenceEngineImpl */