| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| |
| #define LOG_TAG "ArmnnDriver" |
| |
| #include "ModelToINetworkConverter.hpp" |
| #include "Utils.hpp" |
| |
| #include <log/log.h> |
| #include <type_traits> |
| |
| namespace armnn_driver |
| { |
| |
| template<typename HalPolicy> |
| ModelToINetworkConverter<HalPolicy>::ModelToINetworkConverter(const std::vector<armnn::BackendId>& backends, |
| const HalModel& model, |
| const std::set<unsigned int>& 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()); |
| assert(false); |
| } |
| } |
| |
| template<typename HalPolicy> |
| void ModelToINetworkConverter<HalPolicy>::Convert() |
| { |
| using HalModel = typename HalPolicy::Model; |
| using HalOperand = typename HalPolicy::Operand; |
| using HalOperandType = typename HalPolicy::OperandType; |
| |
| ALOGV("ModelToINetworkConverter::Convert(): %s", GetModelSummary<HalModel>(m_Model).c_str()); |
| |
| // map the memory pool into shared pointers |
| m_Data.m_MemPools.clear(); |
| if (!setRunTimePoolInfosFromHidlMemories(&m_Data.m_MemPools, m_Model.pools)) |
| { |
| 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<armnn::BackendOptions>; |
| 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<armnn::IOutputSlot*>(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 armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand); |
| ALOGV("ModelToINetworkConverter::Convert(): m_Data.m_Network->AddInputLayer(i)"); |
| armnn::IConnectableLayer* layer = m_Data.m_Network->AddInputLayer(i); |
| |
| 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<HalOperandType>& 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<HalOperandType>& 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 HalOperand& operand = getMainModel(m_Model).operands[outputIndex]; |
| const armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand); |
| armnn::IConnectableLayer* layer = m_Data.m_Network->AddOutputLayer(i); |
| |
| assert(m_Data.m_OutputSlotForOperand[outputIndex]); |
| 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<typename HalPolicy> |
| bool ModelToINetworkConverter<HalPolicy>::IsOperationSupported(uint32_t operationIndex) const |
| { |
| std::map<uint32_t, bool>::const_iterator it = m_OperationSupported.find(operationIndex); |
| assert(it != m_OperationSupported.end()); |
| return it->second; |
| } |
| |
| /// |
| /// Class template specializations |
| /// |
| |
| template class ModelToINetworkConverter<hal_1_0::HalPolicy>; |
| |
| #ifdef ARMNN_ANDROID_NN_V1_1 |
| template class ModelToINetworkConverter<hal_1_1::HalPolicy>; |
| #endif |
| |
| #ifdef ARMNN_ANDROID_NN_V1_2 |
| template class ModelToINetworkConverter<hal_1_1::HalPolicy>; |
| template class ModelToINetworkConverter<hal_1_2::HalPolicy>; |
| #endif |
| |
| #ifdef ARMNN_ANDROID_NN_V1_3 |
| template class ModelToINetworkConverter<hal_1_1::HalPolicy>; |
| template class ModelToINetworkConverter<hal_1_2::HalPolicy>; |
| template class ModelToINetworkConverter<hal_1_3::HalPolicy>; |
| #endif |
| |
| } // armnn_driver |