Add ARMNN and TFLITE backend support
[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_NONE >= type || INFERENCE_BACKEND_MAX <= 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                 case INFERENCE_BACKEND_ARMNN:
143                 case INFERENCE_BACKEND_TFLITE:
144                         if (mPluginType == INFERENCE_BACKEND_ONE)
145                                 nnfw_type = ML_NNFW_TYPE_NNFW;
146                         if (mPluginType == INFERENCE_BACKEND_ARMNN)
147                                 nnfw_type = ML_NNFW_TYPE_ARMNN;
148                         if (mPluginType == INFERENCE_BACKEND_TFLITE)
149                                 nnfw_type = ML_NNFW_TYPE_TENSORFLOW_LITE;
150
151                         if (access(model_str.c_str(), R_OK)) {
152                                 LOGE("model file path in [%s]", model_str.c_str());
153                                 return INFERENCE_ENGINE_ERROR_INVALID_PATH;
154                         }
155
156                         if (mTargetDevice == INFERENCE_TARGET_CPU) {
157                                 nnfw_hw = ML_NNFW_HW_CPU_NEON;
158                                 LOGI("Target device is NEON.");
159                         } else if (mTargetDevice == INFERENCE_TARGET_GPU) {
160                                 nnfw_hw = ML_NNFW_HW_GPU;
161                                 LOGI("Target device is GPU");
162                         } else {
163                                 LOGE("Invalid inference target device type.");
164                                 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
165                         }
166
167                         LOGI("NNFW tensor filter will be used.");
168                         break;
169                 // TODO.
170                 default:
171                         LOGE("Invalid plugin type.");
172                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
173                 }
174
175                 LOGI("Model name = %s", model_str.c_str());
176
177                 // TODO. create ml_tensor_info for input and output tensor and pass
178                 //               them as parameters of ml_single_open function.
179
180                 int ret = ml_single_open(&mSingle, model_str.c_str(), NULL, NULL,
181                                                                  nnfw_type, nnfw_hw);
182                 if (ret != ML_ERROR_NONE) {
183                         LOGE("Failed to request ml_single_open(%d).", ret);
184                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
185                 }
186
187                 LOGI("LEAVE");
188
189                 return INFERENCE_ENGINE_ERROR_NONE;
190         }
191
192         int InferenceMLAPI::GetInputTensorBuffers(
193                         std::vector<inference_engine_tensor_buffer> &buffers)
194         {
195                 LOGI("ENTER");
196
197                 // TODO. Implement this function according to a given ML Single API backend properly.
198
199                 LOGI("LEAVE");
200
201                 return INFERENCE_ENGINE_ERROR_NONE;
202         }
203
204         int InferenceMLAPI::GetOutputTensorBuffers(
205                         std::vector<inference_engine_tensor_buffer> &buffers)
206         {
207                 LOGI("ENTER");
208
209                 // TODO. Implement this function according to a given ML Single API backend properly.
210
211                 LOGI("LEAVE");
212
213                 return INFERENCE_ENGINE_ERROR_NONE;
214         }
215
216         int InferenceMLAPI::GetInputLayerProperty(
217                         inference_engine_layer_property &property)
218         {
219                 LOGI("ENTER");
220
221                 ml_tensors_info_h in_info = NULL;
222
223                 // TODO. Need to check if model file loading is done.
224
225                 int ret = ml_single_get_input_info(mSingle, &in_info);
226                 if (ret != ML_ERROR_NONE) {
227                         LOGE("Failed to request ml_single_get_input_info(%d).", ret);
228                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
229                 }
230
231                 unsigned int cnt;
232                 ret = ml_tensors_info_get_count(in_info, &cnt);
233                 if (ret != ML_ERROR_NONE) {
234                         LOGE("Failed to request ml_tensors_info_get_count(%d).", ret);
235                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
236                 }
237
238                 LOGI("input tensor count = %u", cnt);
239
240                 for (unsigned int i = 0; i < cnt; ++i) {
241                         inference_engine_tensor_info tensor_info;
242                         ml_tensor_type_e in_type;
243                         unsigned int in_dim[ML_TENSOR_RANK_LIMIT];
244                         char *in_name = NULL;
245                         size_t in_size = 1;
246
247                         ret = ml_tensors_info_get_tensor_type(in_info, i, &in_type);
248                         if (ret != ML_ERROR_NONE) {
249                                 LOGE("Failed to request ml_tensors_info_get_tensor_type(%d).",
250                                          ret);
251                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
252                         }
253
254                         LOGI("input tensor type = %d", in_type);
255
256                         int type = ConvertTensorType(in_type);
257                         if (type == -1) {
258                                 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
259                         }
260
261                         ret = ml_tensors_info_get_tensor_dimension(in_info, i, in_dim);
262                         if (ret != ML_ERROR_NONE) {
263                                 LOGE("Failed to request ml_tensors_info_get_tensor_dimension(%d).",
264                                          ret);
265                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
266                         }
267
268                         LOGI("Input tensor dimension:");
269                         for (unsigned int shape_idx = 0; shape_idx < ML_TENSOR_RANK_LIMIT; ++shape_idx) {
270                                 tensor_info.shape.push_back(in_dim[shape_idx]);
271                                 in_size *= static_cast<size_t>(in_dim[shape_idx]);
272                                 LOGI("%u", in_dim[shape_idx]);
273                         }
274
275                         LOGI("input tensor size = %zu", in_size);
276
277                         ret = ml_tensors_info_get_tensor_name(in_info, i, &in_name);
278                         if (ret != ML_ERROR_NONE) {
279                                 LOGE("Failed to request ml_tensors_info_get_tensor_name(%d).",
280                                          ret);
281                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
282                         }
283
284                         LOGI("input tensor name = %s", in_name);
285
286                         tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
287                         tensor_info.size = in_size;
288
289                         property.tensor_infos.push_back(tensor_info);
290
291                         // TODO. Compare tensor info from engine to one from a given property.
292                 }
293
294                 property.layer_names = mInputProperty.layer_names;
295
296                 LOGI("LEAVE");
297
298                 return INFERENCE_ENGINE_ERROR_NONE;
299         }
300
301         int InferenceMLAPI::GetOutputLayerProperty(
302                         inference_engine_layer_property &property)
303         {
304                 LOGI("ENTER");
305
306                 ml_tensors_info_h out_info = NULL;
307
308                 // TODO. Need to check if model file loading is done.
309
310                 int ret = ml_single_get_output_info(mSingle, &out_info);
311                 if (ret != ML_ERROR_NONE) {
312                         LOGE("Failed to request ml_single_get_output_info(%d).", ret);
313                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
314                 }
315
316                 unsigned int cnt;
317                 ret = ml_tensors_info_get_count(out_info, &cnt);
318                 if (ret != ML_ERROR_NONE) {
319                         LOGE("Failed to request ml_tensors_info_get_count(%d).", ret);
320                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
321                 }
322
323                 LOGI("output tensor count = %u", cnt);
324
325                 for (unsigned int i = 0; i < cnt; ++i) {
326                         inference_engine_tensor_info tensor_info;
327                         ml_tensor_type_e out_type;
328                         unsigned int out_dim[ML_TENSOR_RANK_LIMIT];
329                         char *out_name = NULL;
330                         size_t out_size = 1;
331
332                         ret = ml_tensors_info_get_tensor_type(out_info, i, &out_type);
333                         if (ret != ML_ERROR_NONE) {
334                                 LOGE("Failed to request ml_tensors_info_get_tensor_type(%d).",
335                                          ret);
336                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
337                         }
338
339                         LOGI("output tensor type = %d", out_type);
340
341                         int type = ConvertTensorType(out_type);
342                         if (type == -1) {
343                                 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
344                         }
345
346                         ret = ml_tensors_info_get_tensor_dimension(out_info, i, out_dim);
347                         if (ret != ML_ERROR_NONE) {
348                                 LOGE("Failed to request ml_tensors_info_get_tensor_dimension(%d).",
349                                          ret);
350                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
351                         }
352
353                         int shape_size = 0;
354
355                         LOGI("Output tensor dimension:");
356
357                         for (unsigned int shape_idx = 0; shape_idx < ML_TENSOR_RANK_LIMIT; ++shape_idx) {
358                                 out_size *= static_cast<size_t>(out_dim[shape_idx]);
359
360                                 if (out_dim[shape_idx] == 1 && shape_size == 0)
361                                         shape_size = shape_idx;
362
363                                 LOGI("%d", out_dim[shape_idx]);
364                         }
365
366                         LOGI("Shape size of output tensor : %d", shape_size);
367                         LOGI("Reversed output tensor dimension:");
368
369                         // Reverse shape order.
370                         for (int idx = shape_size; idx >= 0; --idx) {
371                                 tensor_info.shape.push_back(out_dim[idx]);
372                                 LOGI("%u", out_dim[idx]);
373                         }
374
375                         LOGI("output tensor size = %zu", out_size);
376
377                         ret = ml_tensors_info_get_tensor_name(out_info, i, &out_name);
378                         if (ret != ML_ERROR_NONE) {
379                                 LOGE("Failed to request ml_tensors_info_get_tensor_name(%d).",
380                                          ret);
381                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
382                         }
383
384                         LOGI("output tensor name = %s", out_name);
385
386                         tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
387                         tensor_info.size = out_size;
388
389                         property.tensor_infos.push_back(tensor_info);
390
391                         // TODO. Compare tensor info from engine to one from a given property.
392                 }
393
394                 property.layer_names = mOutputProperty.layer_names;
395
396                 LOGI("LEAVE");
397
398                 return INFERENCE_ENGINE_ERROR_NONE;
399         }
400
401         int InferenceMLAPI::SetInputLayerProperty(
402                         inference_engine_layer_property &property)
403         {
404                 LOGI("ENTER");
405
406                 std::vector<std::string>::iterator iter;
407                 for (iter = property.layer_names.begin();
408                          iter != property.layer_names.end(); iter++) {
409                         std::string name = *iter;
410                         LOGI("input layer name = %s", name.c_str());
411                 }
412
413                 mDesignated_inputs.clear();
414                 std::vector<std::string>().swap(mDesignated_inputs);
415
416                 // TODO. Request input property information to a given ML Single API of nnstreamer backend,
417                 // and set it instead of user-given one,
418
419                 mDesignated_inputs = property.layer_names;
420                 mInputProperty = property;
421
422                 LOGI("LEAVE");
423
424                 return INFERENCE_ENGINE_ERROR_NONE;
425         }
426
427         int InferenceMLAPI::SetOutputLayerProperty(
428                         inference_engine_layer_property &property)
429         {
430                 LOGI("ENTER");
431
432                 std::vector<std::string>::iterator iter;
433                 for (iter = property.layer_names.begin();
434                          iter != property.layer_names.end(); iter++) {
435                         std::string name = *iter;
436                         LOGI("output layer name = %s", name.c_str());
437                 }
438
439                 mDesignated_outputs.clear();
440                 std::vector<std::string>().swap(mDesignated_outputs);
441
442                 // TODO. Request output property information to a given ML Single API of nnstreamer backend,
443                 // and set it instead of user-given one,
444
445                 mDesignated_outputs = property.layer_names;
446                 mOutputProperty = property;
447
448                 LOGI("LEAVE");
449
450                 return INFERENCE_ENGINE_ERROR_NONE;
451         }
452
453         int InferenceMLAPI::GetBackendCapacity(inference_engine_capacity *capacity)
454         {
455                 LOGI("ENTER");
456
457                 if (capacity == NULL) {
458                         LOGE("Bad pointer.");
459                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
460                 }
461
462                 // TODO. flag supported accel device types according to a given ML Single API of nnstreamer backend.
463                 if (mPluginType == INFERENCE_BACKEND_MLAPI) {
464                         capacity->supported_accel_devices = INFERENCE_TARGET_CUSTOM;
465                 } else {
466                         capacity->supported_accel_devices = INFERENCE_TARGET_GPU |
467                                                                                                 INFERENCE_TARGET_CPU;
468                 }
469
470                 LOGI("LEAVE");
471
472                 return INFERENCE_ENGINE_ERROR_NONE;
473         }
474
475         int InferenceMLAPI::CheckTensorBuffers(
476                         std::vector<inference_engine_tensor_buffer> &input_buffers,
477                         std::vector<inference_engine_tensor_buffer> &output_buffers)
478         {
479                 LOGI("ENTER");
480
481                 LOGI("LEAVE");
482
483                 return INFERENCE_ENGINE_ERROR_NONE;
484         }
485
486         int InferenceMLAPI::ConvertTensorType(int tensor_type)
487         {
488                 LOGI("ENTER");
489
490                 switch (tensor_type) {
491                 case ML_TENSOR_TYPE_FLOAT32:
492                         return INFERENCE_TENSOR_DATA_TYPE_FLOAT32;
493                 case ML_TENSOR_TYPE_UINT8:
494                         return INFERENCE_TENSOR_DATA_TYPE_UINT8;
495                 case ML_TENSOR_TYPE_UINT16:
496                         return INFERENCE_TENSOR_DATA_TYPE_UINT16;
497                 case ML_TENSOR_TYPE_INT64:
498                         return INFERENCE_TENSOR_DATA_TYPE_INT64;
499                 case ML_TENSOR_TYPE_UINT64:
500                         return INFERENCE_TENSOR_DATA_TYPE_UINT64;
501                 default:
502                         LOGE("Tensor type(%d) is invalid.", tensor_type);
503                         return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
504                 }
505
506                 LOGI("LEAVE");
507
508                 return -1;
509         }
510
511         int InferenceMLAPI::Run(
512                         std::vector<inference_engine_tensor_buffer> &input_buffers,
513                         std::vector<inference_engine_tensor_buffer> &output_buffers)
514         {
515                 LOGI("ENTER");
516
517                 // Make sure to check if tensor buffer count and binding info one are same.
518                 int err = CheckTensorBuffers(input_buffers, output_buffers);
519                 if (err != INFERENCE_ENGINE_ERROR_NONE) {
520                         return err;
521                 }
522
523                 ml_tensors_info_h in_info = NULL;
524
525                 err = ml_single_get_input_info(mSingle, &in_info);
526                 if (err != ML_ERROR_NONE) {
527                         LOGE("Failed to request ml_single_get_input_info(%d).", err);
528                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
529                 }
530
531                 ml_tensors_data_h input_data = NULL;
532                 err = ml_tensors_data_create(in_info, &input_data);
533                 if (err != ML_ERROR_NONE) {
534                         LOGE("Failed to request ml_tensors_data_create(%d).", err);
535                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
536                 }
537
538                 unsigned int in_cnt;
539                 err = ml_tensors_info_get_count(in_info, &in_cnt);
540                 if (err != ML_ERROR_NONE) {
541                         LOGE("Failed to request ml_tensors_info_get_count(%d).", err);
542                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
543                 }
544
545                 ml_tensors_info_h out_info = NULL;
546
547                 err = ml_single_get_output_info(mSingle, &out_info);
548                 if (err != ML_ERROR_NONE) {
549                         LOGE("Failed to request ml_single_get_output_info(%d).", err);
550                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
551                 }
552
553                 unsigned int out_cnt;
554                 err = ml_tensors_info_get_count(out_info, &out_cnt);
555                 if (err != ML_ERROR_NONE) {
556                         LOGE("Failed to request ml_tensors_info_get_count(%d).", err);
557                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
558                 }
559
560                 for (unsigned int i = 0; i < in_cnt; ++i) {
561                         LOGI("index(%d) : buffer = %p, size = %zu\n", i,
562                                  input_buffers[i].buffer, input_buffers[i].size);
563                         err = ml_tensors_data_set_tensor_data(input_data, i,
564                                                                                                   input_buffers[i].buffer,
565                                                                                                   input_buffers[i].size);
566                         if (err != ML_ERROR_NONE) {
567                                 LOGE("Failed to request ml_tensors_data_set_tensor_data(%d).",
568                                          err);
569                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
570                         }
571                 }
572
573                 ml_tensors_data_h output_data = NULL;
574                 err = ml_single_invoke(mSingle, input_data, &output_data);
575                 if (err != ML_ERROR_NONE) {
576                         LOGE("Failed to request ml_single_invoke(%d).", err);
577                         return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
578                 }
579
580                 for (unsigned int i = 0; i < out_cnt; ++i) {
581                         err = ml_tensors_data_get_tensor_data(
582                                 output_data, i, (void **) &output_buffers[i].buffer,
583                                 &output_buffers[i].size);
584                         if (err != ML_ERROR_NONE) {
585                                 LOGE("Failed to request ml_tensors_data_get_tensor_data(%d).", err);
586                                 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
587                         }
588                         LOGI("Output tensor[%u] = %zu", i, output_buffers[0].size);
589                 }
590
591                 LOGI("LEAVE");
592
593                 return INFERENCE_ENGINE_ERROR_NONE;
594         }
595
596         extern "C"
597         {
598                 class IInferenceEngineCommon *EngineCommonInit(void)
599                 {
600                         LOGI("ENTER");
601
602                         InferenceMLAPI *engine = new InferenceMLAPI();
603
604                         LOGI("LEAVE");
605
606                         return engine;
607                 }
608
609                 void EngineCommonDestroy(class IInferenceEngineCommon *engine)
610                 {
611                         LOGI("ENTER");
612
613                         delete engine;
614
615                         LOGI("LEAVE");
616                 }
617         }
618 } /* MLAPIImpl */
619 } /* InferenceEngineImpl */