// // Copyright © 2017 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #define LOG_TAG "ArmnnDriver" #include "ModelToINetworkConverter.hpp" #include "Utils.hpp" #include #include #ifdef ARMNN_ANDROID_S #include #endif namespace armnn_driver { template ModelToINetworkConverter::ModelToINetworkConverter(const std::vector& backends, const HalModel& model, const std::set& forcedUnsupportedOperations) : m_Data(backends) , m_Model(model) , m_ForcedUnsupportedOperations(forcedUnsupportedOperations) , m_ConversionResult(ConversionResult::Success) { try { Convert(); } catch (std::exception& e) { m_ConversionResult = ConversionResult::UnsupportedFeature; ALOGE("%s: Unexpected exception: %s", __func__, e.what()); } } template void ModelToINetworkConverter::Convert() { using HalModel = typename HalPolicy::Model; using HalOperand = typename HalPolicy::Operand; using HalOperandType = typename HalPolicy::OperandType; ALOGV("ModelToINetworkConverter::Convert(): %s", GetModelSummary(m_Model).c_str()); // map the memory pool into shared pointers m_Data.m_MemPools.clear(); #if !defined(ARMNN_ANDROID_S) if (!setRunTimePoolInfosFromHidlMemories(&m_Data.m_MemPools, m_Model.pools)) #else if (!setRunTimePoolInfosFromCanonicalMemories(&m_Data.m_MemPools, uncheckedConvert(m_Model.pools))) #endif { Fail("%s: Setting of run time pool infos from Hidl Memories has failed.", __func__); m_ConversionResult = ConversionResult::ErrorMappingPools; return; } uint32_t totalPoolSize = 0; for (auto&& pool : m_Model.pools) { totalPoolSize += pool.size(); } using NetworkOptions = std::vector; NetworkOptions networkOptions; armnn::BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod", { { "InferAndValidate", true } }); networkOptions.push_back(shapeInferenceMethodOption); // Create armnn::INetwork m_Data.m_Network = armnn::INetwork::Create(networkOptions); // add operations to it // track which layer outputs each operand ALOGV("ModelToINetworkConverter::Convert(): m_OutputSlotForOperand"); m_Data.m_OutputSlotForOperand = std::vector(getMainModel(m_Model).operands.size(), nullptr); try { ALOGV("ModelToINetworkConverter::Convert(): for getMainModel(m_Model).inputIndexes.size()"); for (uint32_t i = 0; i < getMainModel(m_Model).inputIndexes.size(); i++) { ALOGV("ModelToINetworkConverter::Convert(): getMainModel(m_Model).inputIndexes[i]"); // inputs in android nn are represented by operands uint32_t inputIndex = getMainModel(m_Model).inputIndexes[i]; ALOGV("ModelToINetworkConverter::Convert(): getMainModel(m_Model).operands[inputIndex];"); const HalOperand& operand = getMainModel(m_Model).operands[inputIndex]; ALOGV("ModelToINetworkConverter::Convert(): GetTensorInfoForOperand(operand)"); const std::string layerName = "Input_" + std::to_string(i); ALOGV("ModelToINetworkConverter::Convert(): m_Data.m_Network->AddInputLayer(i, layerName.c_str())"); armnn::IConnectableLayer* layer = m_Data.m_Network->AddInputLayer(i, layerName.c_str()); ALOGV("ModelToINetworkConverter::Convert(): layer->GetOutputSlot(0)"); armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0); ALOGV("ModelToINetworkConverter::Convert(): outputSlot.SetTensorInfo(GetTensorInfoForOperand(operand))"); outputSlot.SetTensorInfo(GetTensorInfoForOperand(operand)); ALOGV("ModelToINetworkConverter::Convert(): m_Data.m_OutputSlotForOperand[inputIndex] = &outputSlot"); // store for later layers m_Data.m_OutputSlotForOperand[inputIndex] = &outputSlot; } } catch (UnsupportedOperand& e) { Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str()); m_ConversionResult = ConversionResult::UnsupportedFeature; } catch (const armnn::InvalidArgumentException& e) { Fail("%s: Failed to convert input operand to TensorShape: %s", __func__, e.what()); m_ConversionResult = ConversionResult::UnsupportedFeature; } bool UnsupportedDynamicOperation = false; for (uint32_t operationIdx = 0; operationIdx < getMainModel(m_Model).operations.size(); operationIdx++) { const auto& operation = getMainModel(m_Model).operations[operationIdx]; bool ok = true; if (m_ForcedUnsupportedOperations.find(operationIdx) != m_ForcedUnsupportedOperations.end()) { Fail("%s: Operation at index %i has been forced to be unsupported.", __func__, operationIdx); ok = false; } if (ok) { try { ok = HalPolicy::ConvertOperation(operation, m_Model, m_Data); } catch (UnsupportedOperand& e) { Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str()); ok = false; } catch (const armnn::InvalidArgumentException& e) { Fail("%s: Failed to convert operation in %s", __func__, e.what()); ok = false; } } // Store whether this operation was successfully converted. m_OperationSupported.emplace(operationIdx, ok); // Any single operation failing will fail the entire conversion. // We still need to continue and check the other ones. if (!ok) { if (m_Data.m_DynamicInputsEncountered) { Fail("%s: The unsupported operation at index %i has dynamic inputs.", __func__, operationIdx); UnsupportedDynamicOperation = true; } m_ConversionResult = ConversionResult::UnsupportedFeature; } m_Data.m_DynamicInputsEncountered = false; } // Due to the NNAPI partitioner not supporting partition boundaries of unknown size, // any operations who's outputs connect to an unsupported operation with with dynamic inputs // will cause a failure. // The simplest solution to this problem is to not support any operations in a model containing // an unsupported operation with with dynamic inputs. if (UnsupportedDynamicOperation) { Fail("%s: Unsupported operation with dynamic inputs found. Retroactively setting all operations to unsupported", __func__); for (auto& operation : m_OperationSupported) { operation.second = false; } } try { if (m_ConversionResult == ConversionResult::Success) { for (uint32_t i = 0; i < getMainModel(m_Model).outputIndexes.size(); i++) { // outputs in android nn are represented by operands uint32_t outputIndex = getMainModel(m_Model).outputIndexes[i]; const std::string layerName = "Output_" + std::to_string(i); armnn::IConnectableLayer* layer = m_Data.m_Network->AddOutputLayer(i, layerName.c_str()); if (!m_Data.m_OutputSlotForOperand[outputIndex]) { Fail("%s: OutputSlot %i does not exist", __func__, outputIndex); m_ConversionResult = ConversionResult::UnsupportedFeature; break; } m_Data.m_OutputSlotForOperand[outputIndex]->Connect(layer->GetInputSlot(0)); } } } catch (const armnn::InvalidArgumentException& e) { Fail("%s: Failed to convert output operand to TensorShape: %s", __func__, e.what()); m_ConversionResult = ConversionResult::UnsupportedFeature; } } template bool ModelToINetworkConverter::IsOperationSupported(uint32_t operationIndex) const { std::map::const_iterator it = m_OperationSupported.find(operationIndex); if (it == m_OperationSupported.end()) { return Fail("%s: Unrecognised Operation Index: %i", __func__, operationIndex); } return it->second; } /// /// Class template specializations /// template class ModelToINetworkConverter; #ifdef ARMNN_ANDROID_NN_V1_1 template class ModelToINetworkConverter; #endif #ifdef ARMNN_ANDROID_NN_V1_2 template class ModelToINetworkConverter; template class ModelToINetworkConverter; #endif #ifdef ARMNN_ANDROID_NN_V1_3 template class ModelToINetworkConverter; template class ModelToINetworkConverter; template class ModelToINetworkConverter; #endif } // armnn_driver