*
* This is the per-NN-framework plugin (Tizen nnfw) for tensor_filter.
*
- * @todo Check if nnfw supports dynamic input dimension. (if so, we need to supply setInputDim)
- * @todo Decide whether to let nnfw allocate output buffers or we will feed output buffers to nnfw.
- * @todo NYI: configuring backends
* @todo NYI: support quant8,bool type
*
*/
#define NNFW_SRCN_BACKEND "srcn"
#define NNFW_DEFAULT_BACKEND NNFW_CPU_BACKEND
+/** Maximum rank allowed for tensor dimension */
+#define NNFW_TENSOR_RANK_LIMIT 6
+
static const gchar *nnfw_accl_support[] = {
ACCL_AUTO_STR,
ACCL_DEFAULT_STR,
void **private_data);
static int nnfw_tensors_info_get (const nnfw_pdata *pdata,
const gboolean is_input, GstTensorsInfo * info);
+static int nnfw_tensor_type_from_gst (const tensor_type type,
+ NNFW_TYPE * nnfw_type);
/**
* @brief parse user given input to extract accelerator to be used by nnfw
goto session_exit;
}
- if (nnfw_tensors_info_get (pdata, TRUE, &pdata->in_info) ||
- nnfw_tensors_info_get (pdata, FALSE, &pdata->out_info)) {
- err = -EINVAL;
- g_printerr ("Error retrieving input/output info from nnfw-runtime.");
+ err = nnfw_tensors_info_get (pdata, TRUE, &pdata->in_info);
+ if (err) {
+ g_printerr ("Error retrieving input info from nnfw-runtime.");
+ goto session_exit;
+ }
+
+ err = nnfw_tensors_info_get (pdata, FALSE, &pdata->out_info);
+ if (err) {
+ g_printerr ("Error retrieving output info from nnfw-runtime.");
goto session_exit;
}
}
/**
+ * @brief register/set input tensor info with nnfw
+ * @param[in] pdata private data for nnfw opened instance
+ * @param[in] tensors_info info of tensors to be registered
+ * @param[in] tensor_idx idx of the tensor to be registered
+ * @return 0 on success, errno on failure
+ */
+static int
+nnfw_tensor_info_set (const nnfw_pdata *pdata, const GstTensorsInfo * tensors_info,
+ guint tensor_idx)
+{
+ struct nnfw_tensorinfo nnfw_info;
+ gint err;
+ gint idx;
+ const GstTensorInfo *info = &tensors_info->info[tensor_idx];
+
+ err = nnfw_tensor_type_from_gst (info->type, &nnfw_info.dtype);
+ if (err)
+ return err;
+
+ nnfw_info.rank = gst_tensor_info_get_rank (info);
+
+ /** reverse the order of dimension */
+ for (idx = nnfw_info.rank-1; idx >= 0; idx--)
+ nnfw_info.dims[nnfw_info.rank - idx - 1] = info->dimension[idx];
+
+ for (idx = NNFW_TENSOR_RANK_LIMIT-1; idx >= nnfw_info.rank; idx--)
+ nnfw_info.dims[idx] = 0;
+
+ nnfw_apply_tensorinfo (pdata->session, tensor_idx, nnfw_info);
+
+ return 0;
+}
+
+/**
* @brief get nnfw tensor info in gst format info format from private data
* @param[in] pdata private data for nnfw opened instance
* @param[in] is_input to get info about input/output
int err;
guint idx;
- g_return_val_if_fail (info != NULL, -EINVAL);
- g_return_val_if_fail (pdata != NULL, -EINVAL);
- g_return_val_if_fail (pdata->session != NULL, -EINVAL);
-
/** First get number of outputs */
if (is_input)
status = nnfw_input_size (pdata->session, &info->num_tensors);
g_return_val_if_fail (info != NULL, -EINVAL);
gst_tensors_info_copy (info, &pdata->out_info);
+
return 0;
}
/**
- * @brief Convert from nnfw type to gst tensor type
+ * @brief The standard tensor_filter callback
+ */
+static int
+nnfw_setInputDim (const GstTensorFilterProperties * prop, void **private_data,
+ const GstTensorsInfo * in_info, GstTensorsInfo * out_info)
+{
+ nnfw_pdata *pdata;
+ int err, idx;
+ GstTensorsInfo updated_info;
+
+ g_return_val_if_fail (private_data != NULL, -EINVAL);
+ g_return_val_if_fail (in_info != NULL, -EINVAL);
+ g_return_val_if_fail (out_info != NULL, -EINVAL);
+
+ pdata = (nnfw_pdata *) *private_data;
+ g_return_val_if_fail (pdata != NULL, -EINVAL);
+
+ if (in_info->num_tensors != pdata->in_info.num_tensors)
+ return -EPERM;
+
+ for (idx = 0; idx < pdata->in_info.num_tensors; idx ++) {
+ err = nnfw_tensor_info_set (pdata, in_info, idx);
+ if (err)
+ goto error;
+ }
+
+ err = nnfw_tensors_info_get (pdata, TRUE, &updated_info);
+ if (err || !gst_tensors_info_is_equal (in_info, &updated_info))
+ goto error;
+
+ err = nnfw_tensors_info_get (pdata, FALSE, out_info);
+ if (err)
+ goto error;
+
+ gst_tensors_info_copy (&pdata->in_info, in_info);
+ gst_tensors_info_copy (&pdata->out_info, out_info);
+
+ return 0;
+
+error:
+ g_printerr ("Unable to set the provided input tensor info");
+ /** Reset input dimensions */
+ for (idx = 0; idx < pdata->in_info.num_tensors; idx ++) {
+ nnfw_tensor_info_set (pdata, &pdata->in_info, idx);
+ }
+
+ return err;
+}
+
+/**
+ * @brief Convert from gst tensor type to NNFW type
* @param[in] type type given in gst format
* @param[out] nnfw_type container to receive type in nnfw tensor format
* @return 0 on sucess, negative errno on error
g_return_val_if_fail (pdata != NULL, -EINVAL);
g_return_val_if_fail (pdata->session != NULL, -EPERM);
- if (is_input == TRUE && prop->input_configured == TRUE)
- num_tensors = prop->input_meta.num_tensors;
- else if (is_input == FALSE && prop->output_configured == TRUE)
- num_tensors = prop->output_meta.num_tensors;
- else {
- GstTensorsInfo info;
- err = nnfw_tensors_info_get (pdata, is_input, &info);
- num_tensors = info.num_tensors;
- }
+ if (is_input)
+ num_tensors = pdata->in_info.num_tensors;
+ else
+ num_tensors = pdata->out_info.num_tensors;
for (idx = 0; idx < num_tensors; idx++) {
err = nnfw_tensor_type_from_gst (mem[idx].type, &nnfw_type);
.invoke_NN = nnfw_invoke,
.getInputDimension = nnfw_getInputDim,
.getOutputDimension = nnfw_getOutputDim,
+ .setInputDimension = nnfw_setInputDim,
.open = nnfw_open,
.close = nnfw_close,
};
}
/**
+ * @brief Set input dimensions with nnfw subplugin
+ * @todo enable when nnfw_set_dimension API is interally implemented
+ */
+TEST (nnstreamer_nnfw_runtime_raw_functions, DISABLED_set_dimension)
+{
+ int ret;
+ void *data = NULL;
+ GstTensorsInfo in_info, out_info, res;
+ GstTensorMemory input, output;
+ gchar *model_file;
+ int tensor_size;
+
+ model_file = get_model_file ();
+ ASSERT_TRUE (model_file != nullptr);
+ const gchar *model_files[] = {
+ model_file, NULL,
+ };
+ GstTensorFilterProperties prop = {
+ .fwname = "nnfw",
+ .fw_opened = 0,
+ .model_files = model_files,
+ .num_models = 1,
+ };
+
+ const GstTensorFilterFramework *sp = nnstreamer_filter_find ("nnfw");
+ EXPECT_NE (sp, (void *) NULL);
+
+ /** set input dimension without open */
+ ret = sp->setInputDimension (&prop, &data, &in_info, &out_info);
+ EXPECT_NE (ret, 0);
+
+ ret = sp->open (&prop, &data);
+ EXPECT_EQ (ret, 0);
+
+ tensor_size = 5;
+
+ res.num_tensors = 1;
+ res.info[0].type = _NNS_INT32;
+ res.info[0].dimension[0] = tensor_size;
+ res.info[0].dimension[1] = 1;
+ res.info[0].dimension[2] = 1;
+ res.info[0].dimension[3] = 1;
+
+ /** get input/output dimension successfully */
+ ret = sp->getInputDimension (&prop, &data, &in_info);
+ EXPECT_EQ (ret, 0);
+
+ EXPECT_EQ (res.num_tensors, in_info.num_tensors);
+ EXPECT_NE (res.info[0].type, in_info.info[0].type);
+ EXPECT_NE (res.info[0].dimension[0], in_info.info[0].dimension[0]);
+ EXPECT_EQ (res.info[0].dimension[1], in_info.info[0].dimension[1]);
+ EXPECT_EQ (res.info[0].dimension[2], in_info.info[0].dimension[2]);
+ EXPECT_EQ (res.info[0].dimension[3], in_info.info[0].dimension[3]);
+
+ ret = sp->setInputDimension (&prop, &data, &in_info, &out_info);
+ EXPECT_EQ (ret, 0);
+
+ /** get input/output dimension successfully */
+ ret = sp->getInputDimension (&prop, &data, &in_info);
+ EXPECT_EQ (ret, 0);
+
+ EXPECT_EQ (res.num_tensors, in_info.num_tensors);
+ EXPECT_EQ (res.info[0].type, in_info.info[0].type);
+ EXPECT_EQ (res.info[0].dimension[0], in_info.info[0].dimension[0]);
+ EXPECT_EQ (res.info[0].dimension[1], in_info.info[0].dimension[1]);
+ EXPECT_EQ (res.info[0].dimension[2], in_info.info[0].dimension[2]);
+ EXPECT_EQ (res.info[0].dimension[3], in_info.info[0].dimension[3]);
+
+ ret = sp->getOutputDimension (&prop, &data, &out_info);
+ EXPECT_EQ (ret, 0);
+
+ EXPECT_EQ (res.num_tensors, out_info.num_tensors);
+ EXPECT_EQ (res.info[0].type, out_info.info[0].type);
+ EXPECT_EQ (res.info[0].dimension[0], out_info.info[0].dimension[0]);
+ EXPECT_EQ (res.info[0].dimension[1], out_info.info[0].dimension[1]);
+ EXPECT_EQ (res.info[0].dimension[2], out_info.info[0].dimension[2]);
+ EXPECT_EQ (res.info[0].dimension[3], out_info.info[0].dimension[3]);
+
+ input.size = gst_tensor_info_get_size (&in_info.info[0]);
+ output.size = gst_tensor_info_get_size (&out_info.info[0]);
+
+ input.type = in_info.info[0].type;
+ output.type = out_info.info[0].type;
+
+ input.data = g_malloc (input.size);
+ output.data = g_malloc (output.size);
+
+ /* generate dummy data */
+ for (int idx = 0; idx < tensor_size; idx++)
+ ((gint32 *) input.data)[idx] = idx;
+
+ ret = sp->invoke_NN (&prop, &data, &input, &output);
+ EXPECT_EQ (ret, 0);
+
+ for (int idx = 0; idx < tensor_size; idx++)
+ EXPECT_EQ (((gint32 *) output.data)[idx], idx + 2);
+
+ g_free (input.data);
+ g_free (output.data);
+
+ sp->close (&prop, &data);
+ g_free (model_file);
+}
+
+/**
* @brief Test nnfw subplugin with successful invoke
*/
TEST (nnstreamer_nnfw_runtime_raw_functions, invoke)