From f00e2c8752266d00423710dc78a2099be691594e Mon Sep 17 00:00:00 2001 From: Yongjoo Ahn Date: Tue, 10 Jun 2025 14:24:43 +0900 Subject: [PATCH] [vivante] Refactor the updated vivante code - Refactor and fix memory leaks Signed-off-by: Yongjoo Ahn --- src/hal-backend-ml-vivante.cc | 673 +++++++++++++++++----------------- 1 file changed, 332 insertions(+), 341 deletions(-) diff --git a/src/hal-backend-ml-vivante.cc b/src/hal-backend-ml-vivante.cc index 4fb56f4..951a4a7 100644 --- a/src/hal-backend-ml-vivante.cc +++ b/src/hal-backend-ml-vivante.cc @@ -1,7 +1,5 @@ /* SPDX-License-Identifier: Apache-2.0 */ -#include - #include #include #include @@ -14,56 +12,109 @@ #include "hal-backend-ml-util.h" -/* Helper to convert VSI type string from JSON to vsi_nn_type_e */ -static vsi_nn_type_e -vivante_vsi_type_from_string (const gchar * vsi_type_str) -{ - if (!vsi_type_str) return VSI_NN_TYPE_NONE; - - /** @todo Add 4-bit types when it's available */ +/** + * @brief Private handle for the Vivante instance. + */ +typedef struct _vivante_handle_s { + char *model_path; /* .nb file path */ + char *so_path; /* .so file path (for .so based model loading) */ + char *json_path; /* .json file path (for JSON based model loading) */ + gboolean use_json_for_graph; + gboolean has_post_process; /** @deprecated Do not use it. */ - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT8") == 0) return VSI_NN_TYPE_INT8; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT16") == 0) return VSI_NN_TYPE_INT16; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT32") == 0) return VSI_NN_TYPE_INT32; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT64") == 0) return VSI_NN_TYPE_INT64; + GstTensorsInfo inputInfo; + GstTensorsInfo outputInfo; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT8") == 0) return VSI_NN_TYPE_UINT8; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT16") == 0) return VSI_NN_TYPE_UINT16; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT32") == 0) return VSI_NN_TYPE_UINT32; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT64") == 0) return VSI_NN_TYPE_UINT64; + vsi_nn_graph_t *graph; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT16") == 0) return VSI_NN_TYPE_FLOAT16; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT32") == 0) return VSI_NN_TYPE_FLOAT32; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT64") == 0) return VSI_NN_TYPE_FLOAT64; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_BFLOAT16") == 0) return VSI_NN_TYPE_BFLOAT16; + /* Handles for JSON based model loading */ + vsi_nn_context_t ctx; - if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_BOOL8") == 0) return VSI_NN_TYPE_BOOL8; + /* Handles for .so based model loading */ + void *dl_handle; /* dlopened model so */ + vsi_nn_graph_t *(*model_specific_vnn_CreateNeuralNetwork) (const char *); + void (*model_specific_vnn_ReleaseNeuralNetwork) (vsi_nn_graph_t *); + vsi_status (*model_specific_vnn_PostProcessNeuralNetwork) (vsi_nn_graph_t *); /** @deprecated */ +} vivante_handle_s; - g_warning ("[vivante backend] Unknown VSI tensor type string from JSON: %s", vsi_type_str); +/* =================================================================== + * Forward Declarations of Static Helper Functions + * =================================================================== + */ +static void _json_release_neural_network (vivante_handle_s *self); +static int _json_create_neural_network (vivante_handle_s *self); +static int _so_create_neural_network (vivante_handle_s *self); + +/* =================================================================== + * Type Conversion Helpers + * =================================================================== + */ +/** @brief Converts VSI type string from JSON to vsi_nn_type_e enum. */ +static vsi_nn_type_e +vivante_vsi_type_from_string (const gchar *vsi_type_str) +{ + if (!vsi_type_str) + return VSI_NN_TYPE_NONE; + + /** @todo Add 4-bit types when available */ + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT8") == 0) + return VSI_NN_TYPE_INT8; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT8") == 0) + return VSI_NN_TYPE_UINT8; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT16") == 0) + return VSI_NN_TYPE_INT16; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT16") == 0) + return VSI_NN_TYPE_UINT16; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT32") == 0) + return VSI_NN_TYPE_INT32; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT32") == 0) + return VSI_NN_TYPE_UINT32; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_INT64") == 0) + return VSI_NN_TYPE_INT64; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_UINT64") == 0) + return VSI_NN_TYPE_UINT64; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT16") == 0) + return VSI_NN_TYPE_FLOAT16; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT32") == 0) + return VSI_NN_TYPE_FLOAT32; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_FLOAT64") == 0) + return VSI_NN_TYPE_FLOAT64; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_BFLOAT16") == 0) + return VSI_NN_TYPE_BFLOAT16; + if (g_ascii_strcasecmp (vsi_type_str, "VSI_NN_TYPE_BOOL8") == 0) + return VSI_NN_TYPE_BOOL8; + + g_warning ("[vivante] Unknown VSI tensor type string from JSON: %s", vsi_type_str); return VSI_NN_TYPE_NONE; } -/* Helper to convert VSI quantization type string from JSON to vsi_nn_qnt_type_e */ +/** @brief Converts VSI quantization type string from JSON to vsi_nn_qnt_type_e enum. */ static vsi_nn_qnt_type_e -vivante_qnt_type_from_string (const gchar * qnt_str) +vivante_qnt_type_from_string (const gchar *qnt_str) { - if (!qnt_str) return VSI_NN_QNT_TYPE_NONE; - if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_NONE") == 0) return VSI_NN_QNT_TYPE_NONE; - if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_DFP") == 0) return VSI_NN_QNT_TYPE_DFP; - if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC") == 0) return VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC; - if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC") == 0) return VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC; - if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC") == 0) return VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC; - - /** @todo Add VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_ASYMMETRIC when it's available */ - - g_warning ("[vivante backend] Unknown VSI quantization type string from JSON: %s", qnt_str); + if (!qnt_str) + return VSI_NN_QNT_TYPE_NONE; + if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_NONE") == 0) + return VSI_NN_QNT_TYPE_NONE; + if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_DFP") == 0) + return VSI_NN_QNT_TYPE_DFP; + if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC") == 0) + return VSI_NN_QNT_TYPE_AFFINE_ASYMMETRIC; + if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC") == 0) + return VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_SYMMETRIC; + if (g_ascii_strcasecmp (qnt_str, "VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC") == 0) + return VSI_NN_QNT_TYPE_AFFINE_SYMMETRIC; + + /** @todo Add VSI_NN_QNT_TYPE_AFFINE_PERCHANNEL_ASYMMETRIC when available */ + g_warning ("[vivante] Unknown VSI quantization type string from JSON: %s", qnt_str); return VSI_NN_QNT_TYPE_NONE; } +/** @brief Converts vsi_nn_type_e to the framework's tensor_type. */ static tensor_type -convert_tensortype (vsi_nn_type_e tensor_type) +convert_to_tensor_type (vsi_nn_type_e vsi_type) { - switch (tensor_type) { + switch (vsi_type) { case VSI_NN_TYPE_BOOL8: case VSI_NN_TYPE_INT8: return _NNS_INT8; @@ -93,334 +144,297 @@ convert_tensortype (vsi_nn_type_e tensor_type) case VSI_NN_TYPE_FLOAT64: return _NNS_FLOAT64; default: - break; + g_warning ("[vivante] Unsupported vsi_nn_type_e: %d", vsi_type); + return _NNS_END; } - return _NNS_END; } -typedef struct _vivante_handle_s { - char *model_path; /* .nb file path */ - char *so_path; /* .so file path (for .so based model loading) */ - char *json_path; /* .json file path (for JSON based model loading) */ - gboolean using_json_for_graph_setup; - - GstTensorsInfo inputInfo; - GstTensorsInfo outputInfo; - - vsi_nn_graph_t *graph; - - /* handles for json based model loading */ - vsi_nn_context_t ctx; - - /* handles for .so based model loading */ - void *handle; /* dlopened model so */ - vsi_nn_graph_t *(*result_vnn_CreateNeuralNetwork) (const char *); - void (*result_vnn_ReleaseNeuralNetwork) (vsi_nn_graph_t *); - vsi_status (*postProcessFunc) (vsi_nn_graph_t *); - int postProcess; /** @todo Add postProcess custom prop */ -} vivante_handle_s; - +/* =================================================================== + * JSON Parsing and Graph Creation Helpers + * =================================================================== + */ +/** @brief Parses tensor attributes from a JSON object into a vsi_nn_tensor_attr_t struct. */ static int -ml_vivante_init (void **backend_private) +_helper_parse_tensor_attributes (JsonObject *tensor_obj, vsi_nn_tensor_attr_t *vsi_attr) { - vivante_handle_s *vivante = g_new0 (vivante_handle_s, 1); + memset (vsi_attr, 0, sizeof (vsi_nn_tensor_attr_t)); + vsi_attr->vtl = FALSE; + vsi_attr->is_const = FALSE; + + // Size and dim_num + JsonArray *size_array = json_object_get_array_member (tensor_obj, "size"); + if (!size_array) { + g_critical ("[vivante] Tensor in JSON missing 'size' array."); + return HAL_ML_ERROR_INVALID_PARAMETER; + } + vsi_attr->dim_num = json_array_get_length (size_array); + if (vsi_attr->dim_num == 0 || vsi_attr->dim_num > VSI_NN_MAX_DIM_NUM) { + g_critical ("[vivante] Invalid tensor 'dim_num': %u", vsi_attr->dim_num); + return HAL_ML_ERROR_INVALID_PARAMETER; + } + for (guint i = 0; i < vsi_attr->dim_num; ++i) { + vsi_attr->size[i] = json_array_get_int_element (size_array, i); + } - gst_tensors_info_init (&vivante->inputInfo); - gst_tensors_info_init (&vivante->outputInfo); + // Dtype object + JsonObject *dtype_obj = json_object_get_object_member (tensor_obj, "dtype"); + if (!dtype_obj) { + g_critical ("[vivante] Tensor in JSON missing 'dtype' object."); + return HAL_ML_ERROR_INVALID_PARAMETER; + } + + // Required: vx_type + const gchar *vx_type_str = json_object_get_string_member (dtype_obj, "vx_type"); + if (!vx_type_str) { + g_critical ("[vivante] 'dtype' missing required 'vx_type' key."); + return HAL_ML_ERROR_INVALID_PARAMETER; + } + vsi_attr->dtype.vx_type = vivante_vsi_type_from_string (vx_type_str); - vivante->using_json_for_graph_setup = TRUE; + // Optional fields with defaults + vsi_attr->dtype.qnt_type + = vivante_qnt_type_from_string (json_object_get_string_member_with_default ( + dtype_obj, "qnt_type", "VSI_NN_QNT_TYPE_NONE")); + vsi_attr->dtype.fl = json_object_get_int_member_with_default (dtype_obj, "fl", 0); + vsi_attr->dtype.zero_point + = json_object_get_int_member_with_default (dtype_obj, "zero_point", 0); + vsi_attr->dtype.scale + = json_object_get_double_member_with_default (dtype_obj, "scale", 0.0f); + + /** @todo Parse scales, scale_dim, etc. for PERCHANNEL quantization */ - *backend_private = vivante; return HAL_ML_ERROR_NONE; } +/** @brief Creates and sets up the neural network graph using a JSON definition file. */ static int _json_create_neural_network (vivante_handle_s *self) { + g_autofree gchar *json_string = NULL; + g_autoptr (GError) err = NULL; + g_autoptr (JsonParser) parser = NULL; + JsonNode *root_node = NULL; + JsonObject *root_obj = NULL; + JsonArray *input_array = NULL, *output_array = NULL; + guint input_tensors_num = 0, output_tensors_num = 0; + vsi_nn_node_t *node = NULL; + self->ctx = vsi_nn_CreateContext (); - if (self->ctx == NULL) { - g_critical ("[vivante backend] Failed to create context"); + if (!self->ctx) { + g_critical ("[vivante] Failed to create VSI context."); return HAL_ML_ERROR_RUNTIME_ERROR; } - // 1. Get string from the json file - g_autofree gchar *json_string = NULL; - if (!g_file_get_contents (self->json_path, &json_string, NULL, NULL)) { - g_critical ("[vivante backend] Failed to read JSON file %s", self->json_path); + if (!g_file_get_contents (self->json_path, &json_string, NULL, &err)) { + g_critical ("[vivante] Failed to read JSON file '%s': %s", self->json_path, + err ? err->message : "Unknown error"); return HAL_ML_ERROR_IO_ERROR; } - // 2. Parse json string - g_autoptr (GError) err = NULL; - g_autoptr (JsonParser) parser = json_parser_new (); - if (!parser) { - g_critical ("[vivante backend] Failed to create JsonParser. OOM?"); - return HAL_ML_ERROR_OUT_OF_MEMORY; - } - + parser = json_parser_new (); if (!json_parser_load_from_data (parser, json_string, -1, &err)) { - g_critical ("[vivante backend] Failed to parse json. error: %s", err ? err->message : "Unknown error"); + g_critical ("[vivante] Failed to parse JSON: %s", err ? err->message : "Unknown error"); return HAL_ML_ERROR_INVALID_PARAMETER; } - // 3. Get the root node and the object - JsonNode *root; - JsonObject *root_obj; - root = json_parser_get_root (parser); - if (!root) { - g_critical ("[vivante backend] Failed to get root node"); - return HAL_ML_ERROR_RUNTIME_ERROR; - } - - if (!JSON_NODE_HOLDS_OBJECT (root)) { - g_critical ("[vivante backend] Root node is not an object"); + root_node = json_parser_get_root (parser); + if (!root_node || !JSON_NODE_HOLDS_OBJECT (root_node)) { + g_critical ("[vivante] JSON root is not a valid object."); return HAL_ML_ERROR_INVALID_PARAMETER; } + root_obj = json_node_get_object (root_node); - root_obj = json_node_get_object (root); - if (!root_obj) { - g_critical ("[vivante backend] Failed to get object from root node"); - return HAL_ML_ERROR_RUNTIME_ERROR; - } - - // 4. Get the number of inputs and outputs - guint input_tensors_num = 0U; - guint output_tensors_num = 0U; - - if (!json_object_has_member (root_obj, "input_tensors")) { - g_critical ("[vivante backend] Missing necessary key 'input_tensors' in JSON");; - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - JsonArray *input_array = json_object_get_array_member (root_obj, "input_tensors"); - if (!input_array) { - g_critical ("[vivante backend] Failed to get array from 'input_tensors'"); + input_array = json_object_get_array_member (root_obj, "input_tensors"); + output_array = json_object_get_array_member (root_obj, "output_tensors"); + if (!input_array || !output_array) { + g_critical ("[vivante] JSON must contain 'input_tensors' and 'output_tensors' arrays."); return HAL_ML_ERROR_INVALID_PARAMETER; } input_tensors_num = json_array_get_length (input_array); - - if (!json_object_has_member (root_obj, "output_tensors")) { - g_critical ("[vivante backend] Missing necessary array 'output_tensors' in JSON");; - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - JsonArray *output_array = json_object_get_array_member (root_obj, "output_tensors"); - if (!output_array) { - g_critical ("[vivante backend] Failed to get array from 'output_tensors'"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } - output_tensors_num = json_array_get_length (output_array); - // 5. Set up graphs - vsi_nn_graph_t *graph = vsi_nn_CreateGraph (self->ctx, input_tensors_num + output_tensors_num + output_tensors_num, 1 /* NBG node */); - if (graph == NULL) { - g_critical ("[vivante backend] Failed to create graph"); - return HAL_ML_ERROR_RUNTIME_ERROR; - } + const guint node_num = 1U; /* single NBG node */ + const guint const_tensors_num = 0U; /** @todo support this */ + const guint normal_tensors_num = input_tensors_num + output_tensors_num; + const guint virtual_tensors_num = output_tensors_num; - if (!vsi_nn_SetGraphInputs (graph, NULL, input_tensors_num)) { - g_critical ("[vivante backend] Failed to set graph inputs"); - return HAL_ML_ERROR_RUNTIME_ERROR; + self->graph = vsi_nn_CreateGraph (self->ctx, + normal_tensors_num + virtual_tensors_num + const_tensors_num, node_num); + if (!self->graph) { + g_critical ("[vivante] Failed to create VSI graph."); + goto cleanup; } - if (!vsi_nn_SetGraphOutputs (graph, NULL, output_tensors_num)) { - g_critical ("[vivante backend] Failed to set graph outputs"); - return HAL_ML_ERROR_RUNTIME_ERROR; + + if (!vsi_nn_SetGraphInputs (self->graph, NULL, input_tensors_num) + || !vsi_nn_SetGraphOutputs (self->graph, NULL, output_tensors_num)) { + g_critical ("[vivante] Failed to set graph inputs/outputs."); + goto cleanup; } - // 6. Set a single vsi_nn_node - /** @todo Assuming there is only 1 node for each nb model. Find any exceptions. */ - vsi_nn_node_t *node = NULL; - node = vsi_nn_AddNode(graph, VSI_NN_OP_NBG, input_tensors_num, output_tensors_num, NULL); + node = vsi_nn_AddNode ( + self->graph, VSI_NN_OP_NBG, input_tensors_num, output_tensors_num, NULL); if (!node) { - g_critical ("[vivante backend] Failed to add node"); - return HAL_ML_ERROR_RUNTIME_ERROR; + g_critical ("[vivante] Failed to add NBG node to graph."); + goto cleanup; } - node->uid = 0; node->nn_param.nbg.type = VSI_NN_NBG_FILE; node->nn_param.nbg.url = self->model_path; - auto _parse_tensor_attributes_from_json = [&] (JsonObject *tensor_obj, vsi_nn_tensor_attr_t *vsi_attr) -> int { - memset (vsi_attr, 0, sizeof (vsi_nn_tensor_attr_t)); - if (json_object_has_member (tensor_obj, "id")) { - // do something? - } - - // initialize some boolean attributes - vsi_attr->vtl = FALSE; - vsi_attr->is_const = FALSE; - vsi_attr->is_created_from_handle = FALSE; - vsi_attr->is_handle_malloc_by_ovxlib = FALSE; - - // parse size and dim_num - if (!json_object_has_member (tensor_obj, "size")) { - } - JsonArray *size_array = json_object_get_array_member (tensor_obj, "size"); - if (!size_array) { - g_critical ("[vivante backend] Failed to get array from 'size' in JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - vsi_attr->dim_num = json_array_get_length (size_array); - if (vsi_attr->dim_num == 0U || vsi_attr->dim_num > VSI_NN_MAX_DIM_NUM) { - g_critical ("[vivante backend] Invalid value for 'size' in JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - for (guint i = 0; i < vsi_attr->dim_num; ++i) { - gint64 dim = json_array_get_int_element (size_array, i); - if (dim <= 0) { - g_critical ("[vivante backend] Invalid value for 'dim' in JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - vsi_attr->size[i] = (vsi_size_t) dim; - } - - // parse dtype - if (!json_object_has_member (tensor_obj, "dtype")) { - g_critical ("[vivante backend] Missing necessary key 'dtype' in JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } - - JsonObject *dtype_obj = json_object_get_object_member (tensor_obj, "dtype"); - - // parse dtype.fmt - vsi_attr->dtype.fmt = VSI_NN_DIM_FMT_NCHW; - if (!json_object_has_member (dtype_obj, "fmt")) { - g_warning ("[vivante backend] Missing key 'fmt' in 'dtype' in JSON. Default: 'VSI_NN_DIM_FMT_NCHW'"); - } else { - // @todo parse dtype.fmt - } - - // parse dtype.vx_type - if (!json_object_has_member (dtype_obj, "vx_type")) { - g_critical ("[vivante backend] Missing necessary key 'vx_type' in 'dtype' in JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; - } else { - vsi_attr->dtype.vx_type = vivante_vsi_type_from_string (json_object_get_string_member (dtype_obj, "vx_type")); - } - - // parse dtype.qnt_type - vsi_attr->dtype.qnt_type = VSI_NN_QNT_TYPE_NONE; - if (!json_object_has_member (dtype_obj, "qnt_type")) { - g_warning ("[vivante backend] Missing key 'qnt_type' in 'dtype' in JSON. Default: 'VSI_NN_QNT_TYPE_NONE'"); - } else { - vsi_attr->dtype.qnt_type = vivante_qnt_type_from_string (json_object_get_string_member (dtype_obj, "qnt_type")); - } - - // parse dtype.fl for dynamic fixed point - vsi_attr->dtype.fl = 0; - if (!json_object_has_member (dtype_obj, "fl")) { - g_warning ("[vivante backend] Missing key 'fl' in 'dtype' in JSON. Default: 0"); - } else { - vsi_attr->dtype.fl = (int8_t) json_object_get_int_member (dtype_obj, "fl"); - } - - // parse dtype.zero_point for affine asymmetric - vsi_attr->dtype.zero_point = 0; - if (!json_object_has_member (dtype_obj, "zero_point")) { - g_warning ("[vivante backend] Missing key 'zero_point' in 'dtype' in JSON. Default: 0"); - } else { - vsi_attr->dtype.zero_point = (int32_t) json_object_get_int_member (dtype_obj, "zero_point"); - } - - // parse dtype.scale for affine asymmetric - vsi_attr->dtype.scale = 0.0f; - if (!json_object_has_member (dtype_obj, "scale")) { - g_warning ("[vivante backend] Missing key 'scale' in 'dtype' in JSON. Default: 0.0f"); - } else { - vsi_attr->dtype.scale = (float) json_object_get_double_member (dtype_obj, "scale"); - } - - /** @todo parse scales, scale_dim, channel_dim, zero_points, zero_points_dim for AFFINE_PERCHANNEL_SYMMETRIC */ - - return HAL_ML_ERROR_NONE; - }; - - // 7. Set up input tensors + // Set up input tensors for (guint i = 0; i < input_tensors_num; ++i) { vsi_nn_tensor_attr_t tensor_attr; // parse attr data from json JsonObject *tensor_obj = json_array_get_object_element (input_array, i); - if (_parse_tensor_attributes_from_json (tensor_obj, &tensor_attr) != HAL_ML_ERROR_NONE) { - g_critical ("[vivante backend] Failed to parse tensor attributes from JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; + if (_helper_parse_tensor_attributes (tensor_obj, &tensor_attr) != HAL_ML_ERROR_NONE) { + g_critical ("[vivante] Failed to parse tensor attributes from JSON"); + goto cleanup; } // Add the tensor to the graph - vsi_nn_tensor_id_t vsi_input_id = vsi_nn_AddTensor (graph, VSI_NN_TENSOR_ID_AUTO, &tensor_attr, NULL); + vsi_nn_tensor_id_t vsi_input_id + = vsi_nn_AddTensor (self->graph, VSI_NN_TENSOR_ID_AUTO, &tensor_attr, NULL); if (vsi_input_id == VSI_NN_TENSOR_ID_NA) { - g_critical ("[vivante backend] Failed to add input tensor #%u", i); - return HAL_ML_ERROR_RUNTIME_ERROR; + g_critical ("[vivante] Failed to add input tensor #%u", i); + goto cleanup; } - g_info ("[vivante backend] Added input tensor #%u with id %u", i, vsi_input_id); + g_info ("[vivante] Added input tensor #%u with id %u", i, vsi_input_id); node->input.tensors[i] = vsi_input_id; - graph->input.tensors[i] = vsi_input_id; + self->graph->input.tensors[i] = vsi_input_id; } for (guint i = 0; i < input_tensors_num; ++i) { - g_info ("[vivante backend] print Input tensor #%u:", graph->input.tensors[i]); - vsi_nn_tensor_t *tensor = vsi_nn_GetTensor (graph, graph->input.tensors[i]); - vsi_nn_PrintTensor (tensor, graph->input.tensors[i]); + g_info ("[vivante] print Input tensor #%u:", self->graph->input.tensors[i]); + vsi_nn_tensor_t *tensor + = vsi_nn_GetTensor (self->graph, self->graph->input.tensors[i]); + vsi_nn_PrintTensor (tensor, self->graph->input.tensors[i]); } - // 8. Set up output tensors + // Set up output tensors for (guint i = 0; i < output_tensors_num; ++i) { vsi_nn_tensor_attr_t tensor_attr; // parse attr data from json JsonObject *tensor_obj = json_array_get_object_element (output_array, i); - if (_parse_tensor_attributes_from_json (tensor_obj, &tensor_attr) != HAL_ML_ERROR_NONE) { - g_critical ("[vivante backend] Failed to parse tensor attributes from JSON"); - return HAL_ML_ERROR_INVALID_PARAMETER; + if (_helper_parse_tensor_attributes (tensor_obj, &tensor_attr) != HAL_ML_ERROR_NONE) { + g_critical ("[vivante] Failed to parse tensor attributes from JSON"); + goto cleanup; } // Add the tensor to the graph - vsi_nn_tensor_id_t vsi_output_id = vsi_nn_AddTensor (graph, VSI_NN_TENSOR_ID_AUTO, &tensor_attr, NULL); + vsi_nn_tensor_id_t vsi_output_id + = vsi_nn_AddTensor (self->graph, VSI_NN_TENSOR_ID_AUTO, &tensor_attr, NULL); if (vsi_output_id == VSI_NN_TENSOR_ID_NA) { - g_critical ("[vivante backend] Failed to add output tensor #%u", i); - return HAL_ML_ERROR_RUNTIME_ERROR; + g_critical ("[vivante] Failed to add output tensor #%u", i); + goto cleanup; } - g_info ("[vivante backend] Added output tensor #%u with id %u", i, vsi_output_id); + g_info ("[vivante] Added output tensor #%u with id %u", i, vsi_output_id); node->output.tensors[i] = vsi_output_id; - graph->output.tensors[i] = vsi_output_id; + self->graph->output.tensors[i] = vsi_output_id; } for (guint i = 0; i < output_tensors_num; ++i) { - g_info ("[vivante backend] print output tensor #%u:", graph->output.tensors[i]); - vsi_nn_tensor_t *tensor = vsi_nn_GetTensor (graph, graph->output.tensors[i]); - vsi_nn_PrintTensor (tensor, graph->output.tensors[i]); + g_info ("[vivante] print output tensor #%u:", self->graph->output.tensors[i]); + vsi_nn_tensor_t *tensor + = vsi_nn_GetTensor (self->graph, self->graph->output.tensors[i]); + vsi_nn_PrintTensor (tensor, self->graph->output.tensors[i]); } - // 9. Setup graph - if (vsi_nn_SetupGraph (graph, FALSE) != VSI_SUCCESS) { - g_critical ("[vivante backend] Failed to setup graph"); - return HAL_ML_ERROR_RUNTIME_ERROR; + // setup graph + if (vsi_nn_SetupGraph (self->graph, FALSE) != VSI_SUCCESS) { + g_critical ("[vivante] Failed to setup VSI graph."); + goto cleanup; } - self->graph = graph; - return HAL_ML_ERROR_NONE; + +cleanup: + _json_release_neural_network (self); + return HAL_ML_ERROR_RUNTIME_ERROR; } -static int +/** @brief Releases all resources associated with a JSON-based graph. */ +static void _json_release_neural_network (vivante_handle_s *self) { + if (self->graph) { + vsi_nn_ReleaseGraph (&self->graph); + self->graph = NULL; + } if (self->ctx) { vsi_nn_ReleaseContext (&self->ctx); self->ctx = NULL; } +} - if (self->graph) { - vsi_nn_ReleaseGraph (&self->graph); - self->graph = NULL; +/* =================================================================== + * Shared Library (.so) Loading Helpers + * =================================================================== + */ + +/** @brief Creates the neural network from a pre-compiled shared library. */ +static int +_so_create_neural_network (vivante_handle_s *self) +{ + self->dl_handle = dlopen (self->so_path, RTLD_NOW); + if (!self->dl_handle) { + g_critical ("[vivante] Failed to load shared library '%s': %s", + self->so_path, dlerror ()); + return HAL_ML_ERROR_RUNTIME_ERROR; + } + + self->model_specific_vnn_CreateNeuralNetwork = (vsi_nn_graph_t * (*) (const char *) ) + dlsym (self->dl_handle, "vnn_CreateNeuralNetwork"); + self->model_specific_vnn_ReleaseNeuralNetwork + = (void (*) (vsi_nn_graph_t *)) dlsym (self->dl_handle, "vnn_ReleaseNeuralNetwork"); + + if (!self->model_specific_vnn_CreateNeuralNetwork + || !self->model_specific_vnn_ReleaseNeuralNetwork) { + g_critical ("[vivante] Could not find required symbols in '%s'", self->so_path); + dlclose (self->dl_handle); + self->dl_handle = NULL; + return HAL_ML_ERROR_RUNTIME_ERROR; + } + + if (self->has_post_process) { + self->model_specific_vnn_PostProcessNeuralNetwork = (vsi_status (*) ( + vsi_nn_graph_t *)) dlsym (self->dl_handle, "vnn_PostProcessNeuralNetwork"); + if (!self->model_specific_vnn_PostProcessNeuralNetwork) { + g_warning ("[vivante] 'postProcess' was requested, but symbol 'vnn_PostProcessNeuralNetwork' not found."); + self->has_post_process = FALSE; + } + } + + self->graph = self->model_specific_vnn_CreateNeuralNetwork (self->model_path); + if (!self->graph) { + g_critical ("[vivante] vnn_CreateNeuralNetwork failed for model '%s'", self->model_path); + return HAL_ML_ERROR_RUNTIME_ERROR; } + + return HAL_ML_ERROR_NONE; +} + +/* =================================================================== + * Main HAL Implementation Functions + * =================================================================== + */ +static int +ml_vivante_init (void **backend_private) +{ + vivante_handle_s *vivante = g_new0 (vivante_handle_s, 1); + + gst_tensors_info_init (&vivante->inputInfo); + gst_tensors_info_init (&vivante->outputInfo); + + vivante->use_json_for_graph = TRUE; + vivante->has_post_process = FALSE; + + *backend_private = vivante; + return HAL_ML_ERROR_NONE; } static int @@ -429,18 +443,19 @@ ml_vivante_deinit (void *backend_private) vivante_handle_s *vivante = (vivante_handle_s *) backend_private; if (!vivante) { - g_critical ("[vivante backend] invalid backend_private"); + g_critical ("[vivante] invalid backend_private"); return HAL_ML_ERROR_INVALID_PARAMETER; } - if (vivante->using_json_for_graph_setup) { + if (vivante->use_json_for_graph) { _json_release_neural_network (vivante); } else { - if (vivante->graph) - vivante->result_vnn_ReleaseNeuralNetwork (vivante->graph); - - if (vivante->handle) - dlclose (vivante->handle); + if (vivante->graph && vivante->model_specific_vnn_ReleaseNeuralNetwork) { + vivante->model_specific_vnn_ReleaseNeuralNetwork (vivante->graph); + } + if (vivante->dl_handle) { + dlclose (vivante->dl_handle); + } } gst_tensors_info_free (&vivante->inputInfo); @@ -460,11 +475,17 @@ ml_vivante_configure_instance (void *backend_private, const void *prop_) const GstTensorFilterProperties *prop = (const GstTensorFilterProperties *) prop_; vivante_handle_s *vivante = (vivante_handle_s *) backend_private; - if (!vivante) { - g_critical ("[vivante backend] invalid backend_private"); + if (!vivante || !prop) { + g_critical ("[vivante] invalid backend_private"); return HAL_ML_ERROR_INVALID_PARAMETER; } + vivante->model_path = g_strdup (prop->model_files[0]); + // Default loading strategy: if more than one model file is given, assume .so loading. + if (prop->num_models > 1) { + vivante->use_json_for_graph = FALSE; + } + /* Parse custom properties */ if (prop->custom_properties) { gchar **options = g_strsplit (prop->custom_properties, ",", -1); @@ -477,9 +498,9 @@ ml_vivante_configure_instance (void *backend_private, const void *prop_) g_strstrip (option[1]); if (g_ascii_strcasecmp (option[0], "json") == 0) { - vivante->using_json_for_graph_setup = TRUE; + vivante->use_json_for_graph = TRUE; vivante->json_path = g_strdup (option[1]); - g_info ("[vivante backend] Using JSON for graph setup with JSON file: %s", option[1]); + g_info ("[vivante] Using JSON for graph setup: %s", vivante->json_path); } else { g_warning ("Unknown option (%s).", options[op]); } @@ -491,58 +512,34 @@ ml_vivante_configure_instance (void *backend_private, const void *prop_) g_strfreev (options); } - vivante->model_path = g_strdup (prop->model_files[0]); - if (prop->num_models > 1) { - vivante->using_json_for_graph_setup = FALSE; - } - - - /* do model loading */ - if (vivante->using_json_for_graph_setup) { - /* .json based initializing */ - int error = _json_create_neural_network (vivante); - if (error != HAL_ML_ERROR_NONE) { - g_critical ("[vivante backend] Failed to create neural network from JSON"); - _json_release_neural_network (vivante); - return error; + /* Load model based on the determined strategy JSON vs so */ + if (vivante->use_json_for_graph) { + if (!vivante->json_path || !g_file_test (vivante->json_path, G_FILE_TEST_IS_REGULAR)) { + g_critical ("[vivante] JSON loading was selected, but no JSON path was provided via 'json:' custom property."); + return HAL_ML_ERROR_INVALID_PARAMETER; + } + if (_json_create_neural_network (vivante) != HAL_ML_ERROR_NONE) { + g_critical ("[vivante] Failed to create VSI graph."); } - g_info ("[vivante backend] Model loaded successfully from JSON!"); } else { - /* setup .so */ - vivante->so_path = g_strdup (prop->model_files[1]); - - vivante->handle = dlopen (vivante->so_path, RTLD_NOW); - if (!vivante->handle) { - g_critical ("Failed to load shared library: %s", vivante->so_path); - return HAL_ML_ERROR_RUNTIME_ERROR; + if (prop->num_models <= 1) { + g_critical ("[vivante] .so loading requires a second model file (the .so path)."); + return HAL_ML_ERROR_INVALID_PARAMETER; } - - /* get symbols */ - vivante->result_vnn_ReleaseNeuralNetwork = (void (*) (vsi_nn_graph_t *)) dlsym ( - vivante->handle, "vnn_ReleaseNeuralNetwork"); - vivante->result_vnn_CreateNeuralNetwork = (vsi_nn_graph_t * (*) (const char *) ) - dlsym (vivante->handle, "vnn_CreateNeuralNetwork"); - - if (vivante->postProcess) { - vivante->postProcessFunc = (vsi_status (*) (vsi_nn_graph_t *)) dlsym ( - vivante->handle, "vnn_PostProcessNeuralNetwork"); + vivante->so_path = g_strdup (prop->model_files[1]); + if (_so_create_neural_network (vivante) != HAL_ML_ERROR_NONE) { + g_critical ("[vivante] Failed to create VSI graph."); } - - /* .so based initializing */ - vivante->graph = vivante->result_vnn_CreateNeuralNetwork (vivante->model_path); } /* setting input and output tensors info */ - gst_tensors_info_init (&vivante->inputInfo); - gst_tensors_info_init (&vivante->outputInfo); - vivante->inputInfo.num_tensors = vivante->graph->input.num; for (unsigned int i = 0; i < vivante->graph->input.num; i++) { vsi_nn_tensor_t *i_tensor = vsi_nn_GetTensor (vivante->graph, vivante->graph->input.tensors[i]); GstTensorInfo *info = gst_tensors_info_get_nth_info (&vivante->inputInfo, i); - info->type = convert_tensortype (i_tensor->attr.dtype.vx_type); + info->type = convert_to_tensor_type (i_tensor->attr.dtype.vx_type); info->name = g_strdup_printf ("%i", vivante->graph->input.tensors[i]); for (unsigned int j = 0; j < i_tensor->attr.dim_num; ++j) { info->dimension[j] = i_tensor->attr.size[j]; @@ -555,7 +552,7 @@ ml_vivante_configure_instance (void *backend_private, const void *prop_) = vsi_nn_GetTensor (vivante->graph, vivante->graph->output.tensors[i]); GstTensorInfo *info = gst_tensors_info_get_nth_info (&vivante->outputInfo, i); - info->type = convert_tensortype (o_tensor->attr.dtype.vx_type); + info->type = convert_to_tensor_type (o_tensor->attr.dtype.vx_type); info->name = g_strdup_printf ("%i", vivante->graph->output.tensors[i]); for (unsigned int j = 0; j < o_tensor->attr.dim_num; ++j) { info->dimension[j] = o_tensor->attr.size[j]; @@ -573,7 +570,7 @@ ml_vivante_invoke (void *backend_private, const void *input_, void *output_) vivante_handle_s *vivante = (vivante_handle_s *) backend_private; if (!vivante) { - g_critical ("[vivante backend] invalid backend_private"); + g_critical ("[vivante] invalid backend_private"); return HAL_ML_ERROR_INVALID_PARAMETER; } @@ -581,18 +578,18 @@ ml_vivante_invoke (void *backend_private, const void *input_, void *output_) vsi_nn_tensor_t *tensor = vsi_nn_GetTensor (vivante->graph, vivante->graph->input.tensors[i]); if (vsi_nn_CopyDataToTensor (vivante->graph, tensor, (uint8_t *) input[i].data) != VSI_SUCCESS) { - g_critical ("[vivante backend] Failed to copy data to tensor"); + g_critical ("[vivante] Failed to copy data to tensor"); return HAL_ML_ERROR_RUNTIME_ERROR; } } if (vsi_nn_RunGraph (vivante->graph) != VSI_SUCCESS) { - g_critical ("[vivante backend] Failed to run graph"); + g_critical ("[vivante] Failed to run graph"); return HAL_ML_ERROR_RUNTIME_ERROR; } - if (vivante->postProcess) - vivante->postProcessFunc (vivante->graph); + if (vivante->has_post_process) + vivante->model_specific_vnn_PostProcessNeuralNetwork (vivante->graph); for (unsigned int i = 0; i < vivante->graph->output.num; i++) { vsi_nn_tensor_t *out_tensor @@ -619,26 +616,20 @@ ml_vivante_get_framework_info (void *backend_private, void *fw_info) } static int -ml_vivante_get_model_info (void *backend_private, int ops_, void *in_info_, void *out_info_) +ml_vivante_get_model_info (void *backend_private, int ops, void *in_info, void *out_info) { - int ops = (model_info_ops) ops_; - GstTensorsInfo *in_info = (GstTensorsInfo *) in_info_; - GstTensorsInfo *out_info = (GstTensorsInfo *) out_info_; vivante_handle_s *vivante = (vivante_handle_s *) backend_private; - - if (!vivante) { - g_critical ("[vivante backend] invalid backend_private"); + if (!vivante) return HAL_ML_ERROR_INVALID_PARAMETER; - } - gst_tensors_info_copy (in_info, &vivante->inputInfo); - gst_tensors_info_copy (out_info, &vivante->outputInfo); + gst_tensors_info_copy ((GstTensorsInfo *) in_info, &vivante->inputInfo); + gst_tensors_info_copy ((GstTensorsInfo *) out_info, &vivante->outputInfo); return HAL_ML_ERROR_NONE; } static int -ml_vivante_event_handler (void *backend_private, int ops_, void *data_) +ml_vivante_event_handler (void *backend_private, int ops, void *data) { return HAL_ML_ERROR_NOT_SUPPORTED; } -- 2.34.1