2 * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <inference_engine_error.h>
18 #include "inference_engine_mlapi_private.h"
26 namespace InferenceEngineImpl
30 InferenceMLAPI::InferenceMLAPI(void) :
35 mDesignated_outputs(),
39 mOutputTensorBuffer(),
48 InferenceMLAPI::~InferenceMLAPI()
50 mDesignated_inputs.clear();
51 std::vector<std::string>().swap(mDesignated_inputs);
53 mDesignated_outputs.clear();
54 std::vector<std::string>().swap(mDesignated_outputs);
56 ml_single_close(mSingle);
59 int InferenceMLAPI::SetPrivateData(void *data)
63 inference_backend_type_e type =
64 *(static_cast<inference_backend_type_e *>(data));
66 if (INFERENCE_BACKEND_NONE >= type || INFERENCE_BACKEND_MAX <= type) {
67 LOGE("Invalid backend type.");
68 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
75 return INFERENCE_ENGINE_ERROR_NONE;
78 int InferenceMLAPI::SetTargetDevices(int types)
82 LOGI("Inference targets are, ");
83 if (types & INFERENCE_TARGET_CPU) {
84 mTargetDevice |= INFERENCE_TARGET_CPU;
88 if (types & INFERENCE_TARGET_GPU) {
89 mTargetDevice |= INFERENCE_TARGET_GPU;
93 if (types & INFERENCE_TARGET_CUSTOM) {
94 mTargetDevice |= INFERENCE_TARGET_CUSTOM;
100 return INFERENCE_ENGINE_ERROR_NONE;
103 int InferenceMLAPI::Load(std::vector<std::string> model_paths,
104 inference_model_format_e model_format)
108 std::string model_str(model_paths[0]);
110 // TODO. Set NNFW backend type and HW type properly.
112 ml_nnfw_type_e nnfw_type;
113 ml_nnfw_hw_e nnfw_hw;
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.");
126 LOGE("Invalid target device type.");
127 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
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;
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];
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;
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;
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");
163 LOGE("Invalid inference target device type.");
164 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
167 LOGI("NNFW tensor filter will be used.");
171 LOGE("Invalid plugin type.");
172 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
175 LOGI("Model name = %s", model_str.c_str());
177 // TODO. create ml_tensor_info for input and output tensor and pass
178 // them as parameters of ml_single_open function.
180 int ret = ml_single_open(&mSingle, model_str.c_str(), NULL, NULL,
182 if (ret != ML_ERROR_NONE) {
183 LOGE("Failed to request ml_single_open(%d).", ret);
184 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
189 return INFERENCE_ENGINE_ERROR_NONE;
192 int InferenceMLAPI::GetInputTensorBuffers(
193 std::vector<inference_engine_tensor_buffer> &buffers)
197 // TODO. Implement this function according to a given ML Single API backend properly.
201 return INFERENCE_ENGINE_ERROR_NONE;
204 int InferenceMLAPI::GetOutputTensorBuffers(
205 std::vector<inference_engine_tensor_buffer> &buffers)
209 // TODO. Implement this function according to a given ML Single API backend properly.
213 return INFERENCE_ENGINE_ERROR_NONE;
216 int InferenceMLAPI::GetInputLayerProperty(
217 inference_engine_layer_property &property)
221 ml_tensors_info_h in_info = NULL;
223 // TODO. Need to check if model file loading is done.
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;
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;
238 LOGI("input tensor count = %u", cnt);
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;
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).",
251 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
254 LOGI("input tensor type = %d", in_type);
256 int type = ConvertTensorType(in_type);
258 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
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).",
265 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
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]);
275 LOGI("input tensor size = %zu", in_size);
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).",
281 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
284 LOGI("input tensor name = %s", in_name);
286 tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
287 tensor_info.size = in_size;
289 property.tensor_infos.push_back(tensor_info);
291 // TODO. Compare tensor info from engine to one from a given property.
294 property.layer_names = mInputProperty.layer_names;
298 return INFERENCE_ENGINE_ERROR_NONE;
301 int InferenceMLAPI::GetOutputLayerProperty(
302 inference_engine_layer_property &property)
306 ml_tensors_info_h out_info = NULL;
308 // TODO. Need to check if model file loading is done.
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;
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;
323 LOGI("output tensor count = %u", cnt);
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;
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).",
336 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
339 LOGI("output tensor type = %d", out_type);
341 int type = ConvertTensorType(out_type);
343 return INFERENCE_ENGINE_ERROR_NOT_SUPPORTED;
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).",
350 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
355 LOGI("Output tensor dimension:");
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]);
360 if (out_dim[shape_idx] == 1 && shape_size == 0)
361 shape_size = shape_idx;
363 LOGI("%d", out_dim[shape_idx]);
366 LOGI("Shape size of output tensor : %d", shape_size);
367 LOGI("Reversed output tensor dimension:");
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]);
375 LOGI("output tensor size = %zu", out_size);
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).",
381 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
384 LOGI("output tensor name = %s", out_name);
386 tensor_info.data_type = static_cast<inference_tensor_data_type_e>(type);
387 tensor_info.size = out_size;
389 property.tensor_infos.push_back(tensor_info);
391 // TODO. Compare tensor info from engine to one from a given property.
394 property.layer_names = mOutputProperty.layer_names;
398 return INFERENCE_ENGINE_ERROR_NONE;
401 int InferenceMLAPI::SetInputLayerProperty(
402 inference_engine_layer_property &property)
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());
413 mDesignated_inputs.clear();
414 std::vector<std::string>().swap(mDesignated_inputs);
416 // TODO. Request input property information to a given ML Single API of nnstreamer backend,
417 // and set it instead of user-given one,
419 mDesignated_inputs = property.layer_names;
420 mInputProperty = property;
424 return INFERENCE_ENGINE_ERROR_NONE;
427 int InferenceMLAPI::SetOutputLayerProperty(
428 inference_engine_layer_property &property)
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());
439 mDesignated_outputs.clear();
440 std::vector<std::string>().swap(mDesignated_outputs);
442 // TODO. Request output property information to a given ML Single API of nnstreamer backend,
443 // and set it instead of user-given one,
445 mDesignated_outputs = property.layer_names;
446 mOutputProperty = property;
450 return INFERENCE_ENGINE_ERROR_NONE;
453 int InferenceMLAPI::GetBackendCapacity(inference_engine_capacity *capacity)
457 if (capacity == NULL) {
458 LOGE("Bad pointer.");
459 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
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;
466 capacity->supported_accel_devices = INFERENCE_TARGET_GPU |
467 INFERENCE_TARGET_CPU;
472 return INFERENCE_ENGINE_ERROR_NONE;
475 int InferenceMLAPI::CheckTensorBuffers(
476 std::vector<inference_engine_tensor_buffer> &input_buffers,
477 std::vector<inference_engine_tensor_buffer> &output_buffers)
483 return INFERENCE_ENGINE_ERROR_NONE;
486 int InferenceMLAPI::ConvertTensorType(int tensor_type)
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;
502 LOGE("Tensor type(%d) is invalid.", tensor_type);
503 return INFERENCE_ENGINE_ERROR_INVALID_PARAMETER;
511 int InferenceMLAPI::Run(
512 std::vector<inference_engine_tensor_buffer> &input_buffers,
513 std::vector<inference_engine_tensor_buffer> &output_buffers)
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) {
523 ml_tensors_info_h in_info = NULL;
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;
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;
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;
545 ml_tensors_info_h out_info = NULL;
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;
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;
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).",
569 return INFERENCE_ENGINE_ERROR_INVALID_OPERATION;
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;
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;
588 LOGI("Output tensor[%u] = %zu", i, output_buffers[0].size);
593 return INFERENCE_ENGINE_ERROR_NONE;
598 class IInferenceEngineCommon *EngineCommonInit(void)
602 InferenceMLAPI *engine = new InferenceMLAPI();
609 void EngineCommonDestroy(class IInferenceEngineCommon *engine)
619 } /* InferenceEngineImpl */