From b5fdf38f0c6596958fab2b84882f2792a31e585a Mon Sep 17 00:00:00 2001 From: Mike Kelly Date: Tue, 11 Jun 2019 16:35:25 +0100 Subject: IVGCVSW-3181 Add HAL 1.2 support to android-nn-driver * Updated Android.mk to build HAL 1.2 driver * Added 1.2 HalPolicy and ArmnnDriver * Added 1.2 ArmnnPreparedModel * Updated converters and utilities to accept new HAL 1.2 operands and operand types. Signed-off-by: Sadik Armagan Signed-off-by: Mike Kelly Change-Id: I62856deab24e106f72cccce09468db4971756fa6 --- 1.0/HalPolicy.cpp | 455 +++++------------ 1.0/HalPolicy.hpp | 8 +- 1.1/HalPolicy.cpp | 110 ++-- 1.2/ArmnnDriver.hpp | 208 ++++++++ 1.2/ArmnnDriverImpl.cpp | 206 ++++++++ 1.2/ArmnnDriverImpl.hpp | 34 ++ 1.2/HalPolicy.cpp | 144 ++++++ 1.2/HalPolicy.hpp | 31 ++ Android.mk | 210 +++++++- ArmnnDriver.hpp | 22 +- ArmnnDriverImpl.cpp | 117 ++--- ArmnnDriverImpl.hpp | 6 + ArmnnPreparedModel.cpp | 8 +- ArmnnPreparedModel.hpp | 14 +- ArmnnPreparedModel_1_2.cpp | 486 ++++++++++++++++++ ArmnnPreparedModel_1_2.hpp | 80 +++ ConversionUtils.hpp | 556 +++++++++++++++++---- ModelToINetworkConverter.cpp | 15 +- RequestThread.cpp | 44 +- RequestThread.hpp | 11 +- Utils.cpp | 48 +- Utils.hpp | 18 +- ...id.hardware.neuralnetworks@1.2-service-armnn.rc | 4 + test/GenericLayerTests.cpp | 2 +- 24 files changed, 2237 insertions(+), 600 deletions(-) create mode 100644 1.2/ArmnnDriver.hpp create mode 100644 1.2/ArmnnDriverImpl.cpp create mode 100644 1.2/ArmnnDriverImpl.hpp create mode 100644 1.2/HalPolicy.cpp create mode 100644 1.2/HalPolicy.hpp create mode 100644 ArmnnPreparedModel_1_2.cpp create mode 100644 ArmnnPreparedModel_1_2.hpp create mode 100644 android.hardware.neuralnetworks@1.2-service-armnn.rc diff --git a/1.0/HalPolicy.cpp b/1.0/HalPolicy.cpp index 5f421076..8acb0d4b 100644 --- a/1.0/HalPolicy.cpp +++ b/1.0/HalPolicy.cpp @@ -25,9 +25,11 @@ bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, case V1_0::OperationType::CONCATENATION: return ConvertConcatenation(operation, model, data); case V1_0::OperationType::CONV_2D: - return ConvertConv2d(operation, model, data); + return ValidateConv2dParameters(operation) + && ConvertConv2d(operation, model, data); case V1_0::OperationType::DEPTHWISE_CONV_2D: - return ConvertDepthwiseConv2d(operation, model, data); + return ValidateDepthwiseConv2dParameters(operation) + && ConvertDepthwiseConv2d(operation, model, data); case V1_0::OperationType::DEQUANTIZE: return ConvertDequantize(operation, model, data); case V1_0::OperationType::FLOOR: @@ -68,10 +70,28 @@ bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, } } +bool HalPolicy::ValidateConv2dParameters(const Operation &operation) +{ + if (operation.inputs.size() != 10 && operation.inputs.size() != 7) + { + return Fail("%s: Unsupported number of operation inputs", __func__); + } + return true; +} + +bool HalPolicy::ValidateDepthwiseConv2dParameters(const Operation &operation) +{ + if (operation.inputs.size() != 11 && operation.inputs.size() != 8) + { + return Fail("%s: Unsupported number of operation inputs", __func__); + } + return true; +} + bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); - LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); + LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); if (!input0.IsValid() || !input1.IsValid()) { @@ -81,12 +101,12 @@ bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, Conve // The FuseActivation parameter is always the input index 2 // and it should be optional ActivationFn activationFunction; - if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) + if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return false; @@ -113,7 +133,7 @@ bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, Conve if (endLayer != nullptr) { BroadcastTensor(input0, input1, startLayer, *data.m_Network); - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } else { @@ -123,7 +143,7 @@ bool HalPolicy::ConvertAdd(const Operation& operation, const Model& model, Conve bool HalPolicy::ConvertAveragePool2d(const Operation& operation, const Model& model, ConversionData& data) { - return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Average, model, data); + return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Average, model, data); } bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data) @@ -138,12 +158,12 @@ bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& mo const std::size_t numInputTensors = operation.inputs.size() - 1; int32_t concatDim; - if (!GetInputScalar(operation, numInputTensors, OperandType::INT32, concatDim, model, data)) + if (!GetInputScalar(operation, numInputTensors, OperandType::INT32, concatDim, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* const outputOperand = GetOutputOperand(operation, 0, model); + const Operand* const outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return Fail("%s: Operation has no outputs", __func__); @@ -179,14 +199,14 @@ bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& mo for (uint32_t i = 0; i < numInputTensors; ++i) { - const Operand* const operand = GetInputOperand(operation, i, model); + const Operand* const operand = GetInputOperand(operation, i, model); if (!operand) { return Fail("%s: Operation has invalid inputs", __func__); } armnn::TensorShape operandShape = GetTensorShapeForOperand(*operand); - LayerInputHandle operandInputHandle = ConvertToLayerInputHandle(operation, i, model, data); + LayerInputHandle operandInputHandle = ConvertToLayerInputHandle(operation, i, model, data); if (operandShape.GetNumDimensions() == 0) { @@ -346,250 +366,19 @@ bool HalPolicy::ConvertConcatenation(const Operation& operation, const Model& mo ); } - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); -} - -bool HalPolicy::ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data) -{ - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); - if (!input.IsValid()) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - const Operand* output = GetOutputOperand(operation, 0, model); - if (!output) - { - return Fail("%s: Could not read output 0", __func__); - } - - const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); - const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); - - // ArmNN does not currently support non-fixed weights or bias - const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); - const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); - - if (!weightsPin.IsValid() || !biasPin.IsValid()) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - armnn::ConstTensor weights = weightsPin.GetConstTensor(); - armnn::ConstTensor bias = biasPin.GetConstTensor(); - SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo); - - armnn::Convolution2dDescriptor desc; - desc.m_DataLayout = armnn::DataLayout::NHWC; - ActivationFn activation; - - if (operation.inputs.size() == 10) - { - if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) || - !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) || - !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) || - !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) || - !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) || - !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) || - !GetInputActivationFunction(operation, 9, activation, model, data)) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - } - else if (operation.inputs.size() == 7) - { - android::nn::PaddingScheme paddingScheme; - if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) || - !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) || - !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) || - !GetInputActivationFunction(operation, 6, activation, model, data)) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - const uint32_t kernelX = weights.GetShape()[2]; - const uint32_t kernelY = weights.GetShape()[1]; - const uint32_t inputX = inputInfo.GetShape()[2]; - const uint32_t inputY = inputInfo.GetShape()[1]; - - CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme); - CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme); - } - else - { - return Fail("%s: Unsupported number of operation inputs", __func__); - } - - desc.m_BiasEnabled = true; - armnn::Optional biases(bias.GetInfo()); - - if (!IsLayerSupportedForAnyBackend(__func__, - armnn::IsConvolution2dSupported, - data.m_Backends, - inputInfo, - outputInfo, - desc, - weights.GetInfo(), - biases)) - { - return false; - } - - armnn::IConnectableLayer* startLayer = - data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional(bias)); - - if (!startLayer) - { - return Fail("%s: AddConvolution2dLayer failed", __func__); - } - - armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data); - - if (!endLayer) - { - return Fail("%s: ProcessActivation failed", __func__); - } - - input.Connect(startLayer->GetInputSlot(0)); - - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); -} - -bool HalPolicy::ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data) -{ - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); - if (!input.IsValid()) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - const Operand* output = GetOutputOperand(operation, 0, model); - if (!output) - { - return Fail("%s: Could not read output 0", __func__); - } - - const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); - const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); - - // ArmNN does not currently support non-fixed weights or bias - - // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ] - const Operand* weightsOperand = GetInputOperand(operation, 1, model); - - if (weightsOperand == nullptr) - { - return Fail("%s: Operand is invalid", __func__); - } - - // Reinterpret weight data as [ H, W, I, M ] - armnn::TensorShape weightsShape({ weightsOperand->dimensions[1], - weightsOperand->dimensions[2], - inputInfo.GetShape()[3], - weightsOperand->dimensions[3] / inputInfo.GetShape()[3] }); - - // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ] - const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U }; - - const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data, - HWIMToMIHW, &weightsShape); - - // Bias is a 1D tensor - const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); - - if (!weightsPin.IsValid() || !biasPin.IsValid()) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - armnn::ConstTensor weights = weightsPin.GetConstTensor(); - armnn::ConstTensor bias = biasPin.GetConstTensor(); - SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo); - - armnn::DepthwiseConvolution2dDescriptor desc; - desc.m_DataLayout = armnn::DataLayout::NHWC; - ActivationFn activation; - - if (operation.inputs.size() == 11) - { - if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) || - !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) || - !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) || - !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) || - !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) || - !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) || - !GetInputActivationFunction(operation, 10, activation, model, data)) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - } - else if (operation.inputs.size() == 8) - { - android::nn::PaddingScheme paddingScheme; - if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) || - !GetInputScalar(operation, 4, OperandType::INT32, desc.m_StrideX, model, data) || - !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideY, model, data) || - !GetInputActivationFunction(operation, 7, activation, model, data)) - { - return Fail("%s: Operation has invalid inputs", __func__); - } - - const uint32_t kernelX = weights.GetShape()[3]; - const uint32_t kernelY = weights.GetShape()[2]; - const uint32_t inputX = inputInfo.GetShape()[2]; - const uint32_t inputY = inputInfo.GetShape()[1]; - - CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme); - CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme); - } - else - { - return Fail("%s: Unsupported number of operation inputs", __func__); - } - - desc.m_BiasEnabled = true; - armnn::Optional biases(bias.GetInfo()); - - if (!IsLayerSupportedForAnyBackend(__func__, - armnn::IsDepthwiseConvolutionSupported, - data.m_Backends, - inputInfo, - outputInfo, - desc, - weights.GetInfo(), - biases)) - { - return false; - } - - armnn::IConnectableLayer* startLayer = - data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional(bias)); - if (!startLayer) - { - return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__); - } - - armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data); - if (!endLayer) - { - return Fail("%s: ProcessActivation failed", __func__); - } - - input.Connect(startLayer->GetInputSlot(0)); - - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid input", __func__); } - const Operand* const outputOperand = GetOutputOperand(operation, 0, model); + const Operand* const outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return Fail("%s: Operation has invalid outputs", __func__); @@ -608,18 +397,18 @@ bool HalPolicy::ConvertDequantize(const Operation& operation, const Model& model assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* const outputOperand = GetOutputOperand(operation, 0, model); + const Operand* const outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return Fail("%s: Operation has invalid outputs", __func__); @@ -638,18 +427,18 @@ bool HalPolicy::ConvertFloor(const Operation& operation, const Model& model, Con assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -659,8 +448,8 @@ bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& m const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); // ArmNN does not currently support non-fixed weights or bias - ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); // 2D - ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); // 1D + ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); // 2D + ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); // 1D if (!weightsPin.IsValid() || !biasPin.IsValid()) { @@ -682,7 +471,7 @@ bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& m SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), reshapedInfo); ActivationFn activationFunction; - if (!GetInputActivationFunction(operation, 3, activationFunction, model, data)) + if (!GetInputActivationFunction(operation, 3, activationFunction, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } @@ -725,7 +514,7 @@ bool HalPolicy::ConvertFullyConnected(const Operation& operation, const Model& m input.Connect(startLayer->GetInputSlot(0)); } - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } else { @@ -737,13 +526,13 @@ bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -759,10 +548,10 @@ bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation, descriptor.m_NormMethodType = armnn::NormalizationAlgorithmMethod::LocalBrightness; if (!input.IsValid() || - !GetInputScalar(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) || - !GetInputFloat32(operation, 2, descriptor.m_K, model, data) || - !GetInputFloat32(operation, 3, descriptor.m_Alpha, model, data) || - !GetInputFloat32(operation, 4, descriptor.m_Beta, model, data)) + !GetInputScalar(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) || + !GetInputFloat32(operation, 2, descriptor.m_K, model, data) || + !GetInputFloat32(operation, 3, descriptor.m_Alpha, model, data) || + !GetInputFloat32(operation, 4, descriptor.m_Beta, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } @@ -786,7 +575,7 @@ bool HalPolicy::ConvertLocalResponseNormalization(const Operation& operation, assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, ConversionData& data) @@ -794,7 +583,7 @@ bool HalPolicy::ConvertLogistic(const Operation& operation, const Model& model, armnn::ActivationDescriptor desc; desc.m_Function = armnn::ActivationFunction::Sigmoid; - return ConvertToActivation(operation, __func__, desc, model, data); + return ConvertToActivation(operation, __func__, desc, model, data); } bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, ConversionData& data) @@ -802,19 +591,19 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv // Inputs: // 00: The input: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, input_size], where // “batch_size” corresponds to the batching dimension, and “input_size” is the size of the input. - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Could not read input 0: input", __func__); } // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. - LayerInputHandle outputStateIn = ConvertToLayerInputHandle(operation, 18, model, data); + LayerInputHandle outputStateIn = ConvertToLayerInputHandle(operation, 18, model, data); if (!outputStateIn.IsValid()) { return Fail("%s: Could not read input 18: outputStateIn", __func__); } // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units]. - LayerInputHandle cellStateIn = ConvertToLayerInputHandle(operation, 19, model, data); + LayerInputHandle cellStateIn = ConvertToLayerInputHandle(operation, 19, model, data); if (!cellStateIn.IsValid()) { return Fail("%s: Could not read input 19: cellStateIn", __func__); @@ -823,29 +612,33 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv // Get the mandatory input tensors: // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, input_size]. - const ConstTensorPin inputToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); + const ConstTensorPin inputToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 2, model, + data); // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units, input_size]. - const ConstTensorPin inputToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 3, model, data); + const ConstTensorPin inputToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 3, model, + data); // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, input_size]. - const ConstTensorPin inputToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 4, model, data); + const ConstTensorPin inputToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 4, model, + data); // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size]. - const ConstTensorPin recurrentToForgetWeightsPin = - ConvertOperationInputToConstTensorPin(operation, 6, model, data); + const ConstTensorPin recurrentToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 6, + model, data); // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size]. - const ConstTensorPin recurrentToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 7, model, data); + const ConstTensorPin recurrentToCellWeightsPin = ConvertOperationInputToConstTensorPin(operation, 7, model, + data); // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size]. const ConstTensorPin recurrentToOutputWeightsPin = - ConvertOperationInputToConstTensorPin(operation, 8, model, data); + ConvertOperationInputToConstTensorPin(operation, 8, model, data); // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin forgetGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 13, model, data); + const ConstTensorPin forgetGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 13, model, data); // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin cellBiasPin = ConvertOperationInputToConstTensorPin(operation, 14, model, data); + const ConstTensorPin cellBiasPin = ConvertOperationInputToConstTensorPin(operation, 14, model, data); // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin outputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 15, model, data); + const ConstTensorPin outputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 15, model, data); if (!inputToForgetWeightsPin.IsValid() || !inputToCellWeightsPin.IsValid() || @@ -863,31 +656,31 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv // Get the optional input tensors: // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, input_size], where “num_units” corresponds to the number of cell units. - const ConstTensorPin inputToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin inputToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, + data, g_DontPermute, nullptr, true); // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e., // “num_units”), or the second dimension of the “projection_weights”, if defined. - const ConstTensorPin recurrentToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 5, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin recurrentToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 5, + model, data, g_DontPermute, nullptr, true); // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin cellToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 9, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin cellToInputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 9, model, + data, g_DontPermute, nullptr, true); // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin cellToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 10, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin cellToForgetWeightsPin = ConvertOperationInputToConstTensorPin(operation, 10, model, + data, g_DontPermute, nullptr, true); // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin cellToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 11, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin cellToOutputWeightsPin = ConvertOperationInputToConstTensorPin(operation, 11, model, + data, g_DontPermute, nullptr, true); // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [num_units]. - const ConstTensorPin inputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 12, model, data, + const ConstTensorPin inputGateBiasPin = ConvertOperationInputToConstTensorPin(operation, 12, model, data, g_DontPermute, nullptr, true); // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape // [output_size, num_units]. - const ConstTensorPin projectionWeightsPin = ConvertOperationInputToConstTensorPin(operation, 16, model, data, - g_DontPermute, nullptr, true); + const ConstTensorPin projectionWeightsPin = ConvertOperationInputToConstTensorPin(operation, 16, model, + data, g_DontPermute, nullptr, true); // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [output_size]. - const ConstTensorPin projectionBiasPin = ConvertOperationInputToConstTensorPin(operation, 17, model, data, + const ConstTensorPin projectionBiasPin = ConvertOperationInputToConstTensorPin(operation, 17, model, data, g_DontPermute, nullptr, true); if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional()) || @@ -912,9 +705,9 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv ActivationFn activation; float cellClip; float projClip; - if (!GetInputActivationFunctionFromTensor(operation, 20, activation, model, data) || - !GetInputScalar(operation, 21, OperandType::FLOAT32, cellClip, model, data) || - !GetInputScalar(operation, 22, OperandType::FLOAT32, projClip, model, data)) + if (!GetInputActivationFunctionFromTensor(operation, 20, activation, model, data) || + !GetInputScalar(operation, 21, OperandType::FLOAT32, cellClip, model, data) || + !GetInputScalar(operation, 22, OperandType::FLOAT32, projClip, model, data)) { return Fail("%s: Operation has invalid scalar inputs", __func__); } @@ -922,26 +715,26 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv // Outputs: // 00: The scratch buffer: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units * 4] with // CIFG, or [batch_size, num_units * 3] without CIFG. - const Operand* scratchBuffer = GetOutputOperand(operation, 0, model); + const Operand* scratchBuffer = GetOutputOperand(operation, 0, model); if (!scratchBuffer) { return Fail("%s: Could not read output 0: scratchBuffer", __func__); } // 01: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. - const Operand* outputStateOut = GetOutputOperand(operation, 1, model); + const Operand* outputStateOut = GetOutputOperand(operation, 1, model); if (!outputStateOut) { return Fail("%s: Could not read output 1: outputStateOut", __func__); } // 02: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, num_units]. - const Operand* cellStateOut = GetOutputOperand(operation, 2, model); + const Operand* cellStateOut = GetOutputOperand(operation, 2, model); if (!cellStateOut) { return Fail("%s: Could not read output 2: cellStateOut", __func__); } // 03: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_FLOAT32, of shape [batch_size, output_size]. This is // effectively the same as the current “output state (out)” value. - const Operand* output = GetOutputOperand(operation, 3, model); + const Operand* output = GetOutputOperand(operation, 3, model); if (!output) { return Fail("%s: Could not read output 3: output", __func__); @@ -1101,21 +894,21 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv outputStateIn.Connect(layer->GetInputSlot(1)); cellStateIn.Connect(layer->GetInputSlot(2)); - return (SetupAndTrackLayerOutputSlot(operation, 0, *layer, 0, model, data) && - SetupAndTrackLayerOutputSlot(operation, 1, *layer, 1, model, data) && - SetupAndTrackLayerOutputSlot(operation, 2, *layer, 2, model, data) && - SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data)); + return (SetupAndTrackLayerOutputSlot(operation, 0, *layer, 0, model, data) && + SetupAndTrackLayerOutputSlot(operation, 1, *layer, 1, model, data) && + SetupAndTrackLayerOutputSlot(operation, 2, *layer, 2, model, data) && + SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data)); } bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -1141,23 +934,23 @@ bool HalPolicy::ConvertL2Normalization(const Operation& operation, const Model& assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertL2Pool2d(const Operation& operation, const Model& model, ConversionData& data) { - return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::L2, model, data); + return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::L2, model, data); } bool HalPolicy::ConvertMaxPool2d(const Operation& operation, const Model& model, ConversionData& data) { - return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Max, model, data); + return ConvertPooling2d(operation, __func__, armnn::PoolingAlgorithm::Max, model, data); } bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); - LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); + LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); if (!input0.IsValid() || !input1.IsValid()) { @@ -1167,12 +960,12 @@ bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, Conve // The FuseActivation parameter is always the input index 2 // and it should be optional ActivationFn activationFunction; - if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) + if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (outputOperand == nullptr) { @@ -1200,7 +993,7 @@ bool HalPolicy::ConvertMul(const Operation& operation, const Model& model, Conve if (endLayer != nullptr) { BroadcastTensor(input0, input1, startLayer, *data.m_Network); - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } else { @@ -1213,7 +1006,7 @@ bool HalPolicy::ConvertReLu(const Operation& operation, const Model& model, Conv armnn::ActivationDescriptor desc; desc.m_Function = armnn::ActivationFunction::ReLu; - return ConvertToActivation(operation, __func__, desc, model, data); + return ConvertToActivation(operation, __func__, desc, model, data); } bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, ConversionData& data) @@ -1223,7 +1016,7 @@ bool HalPolicy::ConvertReLu1(const Operation& operation, const Model& model, Con desc.m_A = 1.0f; desc.m_B = -1.0f; - return ConvertToActivation(operation, __func__, desc, model, data); + return ConvertToActivation(operation, __func__, desc, model, data); } bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, ConversionData& data) @@ -1232,18 +1025,18 @@ bool HalPolicy::ConvertReLu6(const Operation& operation, const Model& model, Con desc.m_Function = armnn::ActivationFunction::BoundedReLu; desc.m_A = 6.0f; - return ConvertToActivation(operation, __func__, desc, model, data); + return ConvertToActivation(operation, __func__, desc, model, data); } bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return Fail("%s: Operation has no outputs", __func__); @@ -1252,7 +1045,7 @@ bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, C const armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand); armnn::SoftmaxDescriptor desc; - if (!GetInputFloat32(operation, 1, desc.m_Beta, model, data)) + if (!GetInputFloat32(operation, 1, desc.m_Beta, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } @@ -1271,7 +1064,7 @@ bool HalPolicy::ConvertSoftmax(const Operation& operation, const Model& model, C assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, ConversionData& data) @@ -1281,14 +1074,14 @@ bool HalPolicy::ConvertTanH(const Operation& operation, const Model& model, Conv desc.m_A = 1.0f; // android nn does not support tanH parameters desc.m_B = 1.0f; // set to 1.0f for unity scaling - return ConvertToActivation(operation, __func__, desc, model, data); + return ConvertToActivation(operation, __func__, desc, model, data); } bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, ConversionData& data) { - const Operand* inputOperand = GetInputOperand(operation, 0, model); - const Operand* requestedShapeOperand = GetInputOperand(operation, 1, model); - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* inputOperand = GetInputOperand(operation, 0, model); + const Operand* requestedShapeOperand = GetInputOperand(operation, 1, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (inputOperand == nullptr || requestedShapeOperand == nullptr @@ -1305,7 +1098,7 @@ bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, C } std::vector targetDimensions; - if (!GetTensorInt32Values(*requestedShapeOperand, targetDimensions, model, data)) + if (!GetTensorInt32Values(*requestedShapeOperand, targetDimensions, model, data)) { return Fail("%s: Could not read values of input 1", __func__); } @@ -1326,7 +1119,7 @@ bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, C return Fail("%s: Shape of output operand does not match resolved requested shape", __func__); } - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Could not read input 0", __func__); @@ -1349,18 +1142,18 @@ bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, C assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Could not read input 0", __func__); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -1382,8 +1175,8 @@ bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& m } - if ( !GetInputScalar(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data) - || !GetInputScalar(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data)) + if ( !GetInputScalar(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data) + || !GetInputScalar(operation, 2, OperandType::INT32, desc.m_TargetWidth, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } @@ -1395,7 +1188,7 @@ bool HalPolicy::ConvertResizeBilinear(const Operation& operation, const Model& m layer->GetOutputSlot(0).SetTensorInfo(outputInfo); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } diff --git a/1.0/HalPolicy.hpp b/1.0/HalPolicy.hpp index c09dc8a8..844b67c7 100644 --- a/1.0/HalPolicy.hpp +++ b/1.0/HalPolicy.hpp @@ -35,10 +35,6 @@ private: static bool ConvertConcatenation(const Operation& operation, const Model& model, ConversionData& data); - static bool ConvertConv2d(const Operation& operation, const Model& model, ConversionData& data); - - static bool ConvertDepthwiseConv2d(const Operation& operation, const Model& model, ConversionData& data); - static bool ConvertDequantize(const Operation& operation, const Model& model, ConversionData& data); static bool ConvertFloor(const Operation& operation, const Model& model, ConversionData& data); @@ -74,6 +70,10 @@ private: static bool ConvertReshape(const Operation& operation, const Model& model, ConversionData& data); static bool ConvertResizeBilinear(const Operation& operation, const Model& model, ConversionData& data); + + static bool ValidateConv2dParameters(const Operation& operation); + + static bool ValidateDepthwiseConv2dParameters(const Operation& operation); }; } // namespace hal_1_0 diff --git a/1.1/HalPolicy.cpp b/1.1/HalPolicy.cpp index 294c77ce..2584e327 100644 --- a/1.1/HalPolicy.cpp +++ b/1.1/HalPolicy.cpp @@ -102,8 +102,8 @@ bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); - LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); + LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); if (!input0.IsValid() || !input1.IsValid()) { @@ -113,12 +113,12 @@ bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, Conve // The FuseActivation parameter is always the input index 2 // and it should be optional ActivationFn activationFunction; - if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) + if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return false; @@ -145,7 +145,7 @@ bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, Conve if (endLayer) { BroadcastTensor(input0, input1, startLayer, *data.m_Network); - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } return Fail("%s: ProcessActivation failed", __func__); @@ -153,8 +153,8 @@ bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, Conve bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); - LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); + LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data); if (!input0.IsValid() || !input1.IsValid()) { @@ -164,12 +164,12 @@ bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, Conve // The FuseActivation parameter is always the input index 2 // and it should be optional ActivationFn activationFunction; - if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) + if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* outputOperand = GetOutputOperand(operation, 0, model); + const Operand* outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return false; @@ -196,7 +196,7 @@ bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, Conve if (endLayer) { BroadcastTensor(input0, input1, startLayer, *data.m_Network); - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } return Fail("%s: ProcessActivation failed", __func__); @@ -204,20 +204,20 @@ bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, Conve bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* axisOperand = GetInputOperand(operation, 1, model); + const Operand* axisOperand = GetInputOperand(operation, 1, model); if (!axisOperand) { return Fail("%s: Could not read input 1", __func__); } std::vector axis; - if (!GetTensorInt32Values(*axisOperand, axis, model, data)) + if (!GetTensorInt32Values(*axisOperand, axis, model, data)) { return Fail("%s: Input 1 has invalid values", __func__); } @@ -233,7 +233,7 @@ bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, Conv // Get the "keep dims" flag. int32_t keepDims = 0; - if (!GetInputInt32(operation, 2, keepDims, model, data)) + if (!GetInputInt32(operation, 2, keepDims, model, data)) { return Fail("%s: Could not read input 2", __func__); } @@ -242,7 +242,7 @@ bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, Conv descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end()); descriptor.m_KeepDims = keepDims > 0; - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -264,12 +264,12 @@ bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, Conv assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { @@ -278,7 +278,7 @@ bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, Conve const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); - const Operand* paddingsOperand = GetInputOperand(operation, 1, model); + const Operand* paddingsOperand = GetInputOperand(operation, 1, model); if (!paddingsOperand) { @@ -286,14 +286,14 @@ bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, Conve } unsigned int rank = inputInfo.GetNumDimensions(); - armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand); + armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand); if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2) { return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank); } std::vector paddings; - GetTensorInt32Values(*paddingsOperand, paddings, model, data); + GetTensorInt32Values(*paddingsOperand, paddings, model, data); // add padding for each dimension of input tensor. armnn::PadDescriptor descriptor; @@ -308,7 +308,7 @@ bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, Conve descriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -331,12 +331,12 @@ bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, Conve input.Connect(layer->GetInputSlot(0)); layer->GetOutputSlot(0).SetTensorInfo(outputInfo); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { @@ -352,17 +352,17 @@ bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& m Fail("%s: Only inputs with rank 4 are supported", __func__); } - const Operand* blockShapeOperand = GetInputOperand(operation, 1, model); - const Operand* paddingsOperand = GetInputOperand(operation, 2, model); + const Operand* blockShapeOperand = GetInputOperand(operation, 1, model); + const Operand* paddingsOperand = GetInputOperand(operation, 2, model); - armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand); + armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand); if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim) { return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim); } std::vector blockShape; - GetTensorInt32Values(*blockShapeOperand, blockShape, model, data); + GetTensorInt32Values(*blockShapeOperand, blockShape, model, data); if (std::any_of(blockShape.cbegin(), blockShape.cend(), [](int32_t i){ return i < 1; })) { return Fail("%s: Block shape must be at least 1 in all dimensions.", __func__); @@ -376,7 +376,7 @@ bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& m std::vector> paddingList; std::vector paddings; - GetTensorInt32Values(*paddingsOperand, paddings, model, data); + GetTensorInt32Values(*paddingsOperand, paddings, model, data); for (unsigned int i = 0; i < paddings.size() - 1; i += 2) { int paddingBeforeInput = paddings[i]; @@ -394,7 +394,7 @@ bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& m descriptor.m_BlockShape.assign(blockShape.cbegin(), blockShape.cend()); descriptor.m_PadList.assign(paddingList.cbegin(), paddingList.cend()); - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -415,12 +415,12 @@ bool HalPolicy::ConvertSpaceToBatchNd(const Operation& operation, const Model& m assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { @@ -437,7 +437,7 @@ bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, C // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure // if the operand index is out of bounds. - const Operand* axisOperand = GetInputOperand(operation, 1, model, false); + const Operand* axisOperand = GetInputOperand(operation, 1, model, false); const uint32_t dimensionSequence[] = { 0, 1, 2, 3 }; @@ -449,7 +449,7 @@ bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, C } else { - GetTensorInt32Values(*axisOperand, axis, model, data); + GetTensorInt32Values(*axisOperand, axis, model, data); } @@ -472,7 +472,7 @@ bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, C armnn::ReshapeDescriptor reshapeDesc; reshapeDesc.m_TargetShape = outputInfo.GetShape(); - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -491,12 +491,12 @@ bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, C assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); @@ -509,9 +509,9 @@ bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& mod Fail("%s: Inputs with rank greater than 4 are not supported", __func__); } - const Operand* beginOperand = GetInputOperand(operation, 1, model); - const Operand* endOperand = GetInputOperand(operation, 2, model); - const Operand* stridesOperand = GetInputOperand(operation, 3, model); + const Operand* beginOperand = GetInputOperand(operation, 1, model); + const Operand* endOperand = GetInputOperand(operation, 2, model); + const Operand* stridesOperand = GetInputOperand(operation, 3, model); std::vector beginValues; std::vector endValues; @@ -520,7 +520,7 @@ bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& mod // The length of the beginOperand, endOperand and stridesOperand must be of a rank(input) auto ValidateInputOperands = [&] (const Operand& operand, std::vector& operandValues) { - if (!GetTensorInt32Values(operand, operandValues, model, data)) + if (!GetTensorInt32Values(operand, operandValues, model, data)) { return false; } @@ -553,14 +553,14 @@ bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& mod descriptor.m_DataLayout = armnn::DataLayout::NHWC; // Get the "begin_mask", "end_mask", and "shrink_axis_mask" flags - if (!GetInputInt32(operation, 4, descriptor.m_BeginMask, model, data) - || !GetInputInt32(operation, 5, descriptor.m_EndMask, model, data) - || !GetInputInt32(operation, 6, descriptor.m_ShrinkAxisMask, model, data)) + if (!GetInputInt32(operation, 4, descriptor.m_BeginMask, model, data) + || !GetInputInt32(operation, 5, descriptor.m_EndMask, model, data) + || !GetInputInt32(operation, 6, descriptor.m_ShrinkAxisMask, model, data)) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -581,12 +581,12 @@ bool HalPolicy::ConvertStridedSlice(const Operation& operation, const Model& mod assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { @@ -603,7 +603,7 @@ bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure // if the operand index is out of bounds. - const Operand* permOperand = GetInputOperand(operation, 1, model, false); + const Operand* permOperand = GetInputOperand(operation, 1, model, false); std::vector perm(rank); if (!permOperand) @@ -616,7 +616,7 @@ bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, } else { - GetTensorInt32Values(*permOperand, perm, model, data); + GetTensorInt32Values(*permOperand, perm, model, data); } std::vector outputDims(perm.begin(), perm.begin() + rank); @@ -632,7 +632,7 @@ bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, armnn::PermuteDescriptor permuteDesc; permuteDesc.m_DimMappings = permutationVector; - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -654,18 +654,18 @@ bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Operation has invalid inputs", __func__); } - const Operand* blockOperand = GetInputOperand(operation, 1, model); + const Operand* blockOperand = GetInputOperand(operation, 1, model); if (!blockOperand) { return Fail("%s: Could not read input 1", __func__); @@ -673,7 +673,7 @@ bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& m // Convert the block operand to int32 std::vector block; - if (!GetTensorInt32Values(*blockOperand, block, model, data)) + if (!GetTensorInt32Values(*blockOperand, block, model, data)) { return Fail("%s: Input 1 has invalid values", __func__); } @@ -699,7 +699,7 @@ bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& m // Setting crops to 0,0 0,0 as it is not supported in Android NN API batchToSpaceNdDesc.m_Crops = {{0, 0}, {0, 0}}; - const Operand* output = GetOutputOperand(operation, 0, model); + const Operand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -721,7 +721,7 @@ bool HalPolicy::ConvertBatchToSpaceNd(const Operation& operation, const Model& m assert(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } diff --git a/1.2/ArmnnDriver.hpp b/1.2/ArmnnDriver.hpp new file mode 100644 index 00000000..7460f396 --- /dev/null +++ b/1.2/ArmnnDriver.hpp @@ -0,0 +1,208 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include "../ArmnnDevice.hpp" +#include "ArmnnDriverImpl.hpp" +#include "HalPolicy.hpp" + +#include "../ArmnnDriverImpl.hpp" +#include "../1.2/ArmnnDriverImpl.hpp" +#include "../1.2/HalPolicy.hpp" +#include "../1.1/ArmnnDriverImpl.hpp" +#include "../1.1/HalPolicy.hpp" +#include "../1.0/ArmnnDriverImpl.hpp" +#include "../1.0/HalPolicy.hpp" + +#include + +namespace armnn_driver +{ +namespace hal_1_2 +{ + +class ArmnnDriver : public ArmnnDevice, public V1_2::IDevice +{ +public: + ArmnnDriver(DriverOptions options) + : ArmnnDevice(std::move(options)) + { + ALOGV("hal_1_2::ArmnnDriver::ArmnnDriver()"); + } + ~ArmnnDriver() {} + + using HidlToken = android::hardware::hidl_array; + +public: + Return getCapabilities(V1_0::IDevice::getCapabilities_cb cb) override + { + ALOGV("hal_1_2::ArmnnDriver::getCapabilities()"); + + return hal_1_0::ArmnnDriverImpl::getCapabilities(m_Runtime, cb); + } + + Return getSupportedOperations(const V1_0::Model& model, + V1_0::IDevice::getSupportedOperations_cb cb) override + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()"); + + return armnn_driver::ArmnnDriverImpl::getSupportedOperations(m_Runtime, + m_Options, + model, + cb); + } + + Return prepareModel(const V1_0::Model& model, + const android::sp& cb) override + { + ALOGV("hal_1_2::ArmnnDriver::prepareModel()"); + + return armnn_driver::ArmnnDriverImpl::prepareModel(m_Runtime, + m_ClTunedParameters, + m_Options, + model, + cb); + } + + Return getCapabilities_1_1(V1_1::IDevice::getCapabilities_1_1_cb cb) override + { + ALOGV("hal_1_2::ArmnnDriver::getCapabilities_1_1()"); + + return hal_1_1::ArmnnDriverImpl::getCapabilities_1_1(m_Runtime, cb); + } + + Return getSupportedOperations_1_1(const V1_1::Model& model, + V1_1::IDevice::getSupportedOperations_1_1_cb cb) override + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations_1_1()"); + return armnn_driver::ArmnnDriverImpl::getSupportedOperations(m_Runtime, + m_Options, + model, + cb); + } + + Return prepareModel_1_1(const V1_1::Model& model, + V1_1::ExecutionPreference preference, + const android::sp& cb) override + { + ALOGV("hal_1_2::ArmnnDriver::prepareModel_1_1()"); + + if (!(preference == ExecutionPreference::LOW_POWER || + preference == ExecutionPreference::FAST_SINGLE_ANSWER || + preference == ExecutionPreference::SUSTAINED_SPEED)) + { + ALOGV("hal_1_2::ArmnnDriver::prepareModel_1_1: Invalid execution preference"); + cb->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return ErrorStatus::INVALID_ARGUMENT; + } + + return armnn_driver::ArmnnDriverImpl::prepareModel(m_Runtime, + m_ClTunedParameters, + m_Options, + model, + cb, + model.relaxComputationFloat32toFloat16 + && m_Options.GetFp16Enabled()); + } + + Return getStatus() override + { + ALOGV("hal_1_2::ArmnnDriver::getStatus()"); + + return armnn_driver::ArmnnDriverImpl::getStatus(); + } + + Return getVersionString(getVersionString_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()"); + + cb(ErrorStatus::NONE, "ArmNN"); + return Void(); + } + + Return getType(getType_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getType()"); + + cb(ErrorStatus::NONE, V1_2::DeviceType::CPU); + return Void(); + } + + Return prepareModelFromCache( + const android::hardware::hidl_vec&, + const android::hardware::hidl_vec&, + const HidlToken&, + const sp& callback) + { + ALOGV("hal_1_2::ArmnnDriver::prepareModelFromCache()"); + callback->notify_1_2(ErrorStatus::GENERAL_FAILURE, nullptr); + return ErrorStatus::GENERAL_FAILURE; + } + + Return prepareModel_1_2(const V1_2::Model& model, V1_1::ExecutionPreference preference, + const android::hardware::hidl_vec&, + const android::hardware::hidl_vec&, const HidlToken&, + const android::sp& cb) + { + ALOGV("hal_1_2::ArmnnDriver::prepareModel_1_1()"); + + if (!(preference == ExecutionPreference::LOW_POWER || + preference == ExecutionPreference::FAST_SINGLE_ANSWER || + preference == ExecutionPreference::SUSTAINED_SPEED)) + { + ALOGV("hal_1_2::ArmnnDriver::prepareModel_1_1: Invalid execution preference"); + cb->notify(ErrorStatus::INVALID_ARGUMENT, nullptr); + return ErrorStatus::INVALID_ARGUMENT; + } + + return ArmnnDriverImpl::prepareArmnnModel_1_2(m_Runtime, + m_ClTunedParameters, + m_Options, + model, + cb, + model.relaxComputationFloat32toFloat16 + && m_Options.GetFp16Enabled()); + } + + Return getSupportedExtensions(getSupportedExtensions_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedExtensions()"); + cb(ErrorStatus::NONE, {/* No extensions. */}); + return Void(); + } + + Return getCapabilities_1_2(getCapabilities_1_2_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getCapabilities()"); + + return hal_1_2::ArmnnDriverImpl::getCapabilities_1_2(m_Runtime, cb); + } + + Return getSupportedOperations_1_2(const V1_2::Model& model, + getSupportedOperations_1_2_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()"); + + return armnn_driver::ArmnnDriverImpl::getSupportedOperations(m_Runtime, + m_Options, + model, + cb); + } + + Return getNumberOfCacheFilesNeeded(getNumberOfCacheFilesNeeded_cb cb) + { + ALOGV("hal_1_2::ArmnnDriver::getSupportedExtensions()"); + + // Set both numbers to be 0 for cache not supported. + cb(ErrorStatus::NONE, 0, 0); + return Void(); + } +}; + +} // namespace hal_1_2 +} // namespace armnn_driver \ No newline at end of file diff --git a/1.2/ArmnnDriverImpl.cpp b/1.2/ArmnnDriverImpl.cpp new file mode 100644 index 00000000..97cfa5de --- /dev/null +++ b/1.2/ArmnnDriverImpl.cpp @@ -0,0 +1,206 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "ArmnnDriverImpl.hpp" +#include "../ArmnnPreparedModel_1_2.hpp" +#include "../ModelToINetworkConverter.hpp" +#include "../SystemPropertiesUtils.hpp" + +#include + +namespace +{ + +const char *g_RelaxedFloat32toFloat16PerformanceExecTime = "ArmNN.relaxedFloat32toFloat16Performance.execTime"; +void NotifyCallbackAndCheck(const sp& callback, + ErrorStatus errorStatus, + const sp& preparedModelPtr) +{ + Return returned = callback->notify(errorStatus, preparedModelPtr); + // This check is required, if the callback fails and it isn't checked it will bring down the service + if (!returned.isOk()) + { + ALOGE("ArmnnDriverImpl::prepareModel: hidl callback failed to return properly: %s ", + returned.description().c_str()); + } +} + +Return FailPrepareModel(ErrorStatus error, + const std::string& message, + const sp& callback) +{ + ALOGW("ArmnnDriverImpl::prepareModel: %s", message.c_str()); + NotifyCallbackAndCheck(callback, error, nullptr); + return error; +} + +} // anonymous namespace + +namespace armnn_driver +{ +namespace hal_1_2 +{ + +Return ArmnnDriverImpl::prepareArmnnModel_1_2(const armnn::IRuntimePtr& runtime, + const armnn::IGpuAccTunedParametersPtr& clTunedParameters, + const DriverOptions& options, + const V1_2::Model& model, + const sp& cb, + bool float32ToFloat16) +{ + ALOGV("ArmnnDriverImpl::prepareModel()"); + + if (cb.get() == nullptr) + { + ALOGW("ArmnnDriverImpl::prepareModel: Invalid callback passed to prepareModel"); + return ErrorStatus::INVALID_ARGUMENT; + } + + if (!runtime) + { + return FailPrepareModel(ErrorStatus::DEVICE_UNAVAILABLE, "Device unavailable", cb); + } + + if (!android::nn::validateModel(model)) + { + return FailPrepareModel(ErrorStatus::INVALID_ARGUMENT, "Invalid model passed as input", cb); + } + + // Deliberately ignore any unsupported operations requested by the options - + // at this point we're being asked to prepare a model that we've already declared support for + // and the operation indices may be different to those in getSupportedOperations anyway. + std::set unsupportedOperations; + ModelToINetworkConverter modelConverter(options.GetBackends(), + model, + unsupportedOperations); + + if (modelConverter.GetConversionResult() != ConversionResult::Success) + { + FailPrepareModel(ErrorStatus::GENERAL_FAILURE, "ModelToINetworkConverter failed", cb); + return ErrorStatus::NONE; + } + + // Optimize the network + armnn::IOptimizedNetworkPtr optNet(nullptr, nullptr); + armnn::OptimizerOptions OptOptions; + OptOptions.m_ReduceFp32ToFp16 = float32ToFloat16; + + std::vector errMessages; + try + { + optNet = armnn::Optimize(*modelConverter.GetINetwork(), + options.GetBackends(), + runtime->GetDeviceSpec(), + OptOptions, + errMessages); + } + catch (armnn::Exception &e) + { + std::stringstream message; + message << "armnn::Exception (" << e.what() << ") caught from optimize."; + FailPrepareModel(ErrorStatus::GENERAL_FAILURE, message.str(), cb); + return ErrorStatus::NONE; + } + + // Check that the optimized network is valid. + if (!optNet) + { + std::stringstream message; + message << "Invalid optimized network"; + for (const std::string& msg : errMessages) + { + message << "\n" << msg; + } + FailPrepareModel(ErrorStatus::GENERAL_FAILURE, message.str(), cb); + return ErrorStatus::NONE; + } + + // Export the optimized network graph to a dot file if an output dump directory + // has been specified in the drivers' arguments. + ExportNetworkGraphToDotFile(*optNet, options.GetRequestInputsAndOutputsDumpDir(), + model); + + // Load it into the runtime. + armnn::NetworkId netId = 0; + try + { + if (runtime->LoadNetwork(netId, move(optNet)) != armnn::Status::Success) + { + return FailPrepareModel(ErrorStatus::GENERAL_FAILURE, "Network could not be loaded", cb); + } + } + catch (armnn::Exception& e) + { + std::stringstream message; + message << "armnn::Exception (" << e.what()<< ") caught from LoadNetwork."; + FailPrepareModel(ErrorStatus::GENERAL_FAILURE, message.str(), cb); + return ErrorStatus::NONE; + } + + std::unique_ptr> preparedModel( + new ArmnnPreparedModel_1_2( + netId, + runtime.get(), + model, + options.GetRequestInputsAndOutputsDumpDir(), + options.IsGpuProfilingEnabled())); + + // Run a single 'dummy' inference of the model. This means that CL kernels will get compiled (and tuned if + // this is enabled) before the first 'real' inference which removes the overhead of the first inference. + if (!preparedModel->ExecuteWithDummyInputs()) + { + return FailPrepareModel(ErrorStatus::GENERAL_FAILURE, "Network could not be executed", cb); + } + + if (clTunedParameters && + options.GetClTunedParametersMode() == armnn::IGpuAccTunedParameters::Mode::UpdateTunedParameters) + { + // Now that we've done one inference the CL kernel parameters will have been tuned, so save the updated file. + try + { + clTunedParameters->Save(options.GetClTunedParametersFile().c_str()); + } + catch (const armnn::Exception& error) + { + ALOGE("ArmnnDriverImpl::prepareModel: Failed to save CL tuned parameters file '%s': %s", + options.GetClTunedParametersFile().c_str(), error.what()); + } + } + + NotifyCallbackAndCheck(cb, ErrorStatus::NONE, preparedModel.release()); + + return ErrorStatus::NONE; +} + +Return ArmnnDriverImpl::getCapabilities_1_2(const armnn::IRuntimePtr& runtime, + V1_2::IDevice::getCapabilities_1_2_cb cb) +{ + ALOGV("hal_1_2::ArmnnDriverImpl::getCapabilities()"); + + V1_2::Capabilities capabilities; + + if (runtime) + { + capabilities.relaxedFloat32toFloat16PerformanceScalar.execTime = + ParseSystemProperty(g_RelaxedFloat32toFloat16PerformanceExecTime, .1f); + + capabilities.relaxedFloat32toFloat16PerformanceTensor.execTime = + ParseSystemProperty(g_RelaxedFloat32toFloat16PerformanceExecTime, .1f); + + cb(ErrorStatus::NONE, capabilities); + } + else + { + capabilities.relaxedFloat32toFloat16PerformanceScalar.execTime = 0; + capabilities.relaxedFloat32toFloat16PerformanceTensor.execTime = 0; + + cb(ErrorStatus::DEVICE_UNAVAILABLE, capabilities); + } + + return Void(); +} + +} // namespace hal_1_2 +} // namespace armnn_driver \ No newline at end of file diff --git a/1.2/ArmnnDriverImpl.hpp b/1.2/ArmnnDriverImpl.hpp new file mode 100644 index 00000000..b3c65079 --- /dev/null +++ b/1.2/ArmnnDriverImpl.hpp @@ -0,0 +1,34 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include "../DriverOptions.hpp" + +#include + +namespace armnn_driver +{ +namespace hal_1_2 +{ + +class ArmnnDriverImpl +{ +public: + static Return prepareArmnnModel_1_2(const armnn::IRuntimePtr& runtime, + const armnn::IGpuAccTunedParametersPtr& clTunedParameters, + const DriverOptions& options, + const V1_2::Model& model, + const android::sp& cb, + bool float32ToFloat16 = false); + + static Return getCapabilities_1_2(const armnn::IRuntimePtr& runtime, + V1_2::IDevice::getCapabilities_1_2_cb cb); +}; + +} // namespace hal_1_2 +} // namespace armnn_driver \ No newline at end of file diff --git a/1.2/HalPolicy.cpp b/1.2/HalPolicy.cpp new file mode 100644 index 00000000..abc0cfca --- /dev/null +++ b/1.2/HalPolicy.cpp @@ -0,0 +1,144 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "HalPolicy.hpp" + +#include "../1.0/HalPolicy.hpp" +#include "../1.1/HalPolicy.hpp" + +namespace armnn_driver +{ +namespace hal_1_2 +{ + +bool HandledByV1_0(V1_2::OperationType operationType) +{ + switch (static_cast(operationType)) + { + case V1_0::OperationType::ADD: + case V1_0::OperationType::AVERAGE_POOL_2D: + case V1_0::OperationType::CONCATENATION: + case V1_0::OperationType::DEPTH_TO_SPACE: + case V1_0::OperationType::DEQUANTIZE: + case V1_0::OperationType::EMBEDDING_LOOKUP: + case V1_0::OperationType::FLOOR: + case V1_0::OperationType::FULLY_CONNECTED: + case V1_0::OperationType::HASHTABLE_LOOKUP: + case V1_0::OperationType::L2_NORMALIZATION: + case V1_0::OperationType::L2_POOL_2D: + case V1_0::OperationType::LOCAL_RESPONSE_NORMALIZATION: + case V1_0::OperationType::LOGISTIC: + case V1_0::OperationType::LSH_PROJECTION: + case V1_0::OperationType::LSTM: + case V1_0::OperationType::MAX_POOL_2D: + case V1_0::OperationType::MUL: + case V1_0::OperationType::RELU: + case V1_0::OperationType::RELU1: + case V1_0::OperationType::RELU6: + case V1_0::OperationType::RESHAPE: + case V1_0::OperationType::RESIZE_BILINEAR: + case V1_0::OperationType::RNN: + case V1_0::OperationType::SOFTMAX: + case V1_0::OperationType::SPACE_TO_DEPTH: + case V1_0::OperationType::SVDF: + case V1_0::OperationType::TANH: + case V1_0::OperationType::OEM_OPERATION: + return true; + default: + return false; + } +} + +bool HandledByV1_1(V1_2::OperationType operationType) +{ + if (HandledByV1_0(operationType)) + { + return true; + } + switch (static_cast(operationType)) + { + case V1_1::OperationType::BATCH_TO_SPACE_ND: + case V1_1::OperationType::DIV: + case V1_1::OperationType::MEAN: + case V1_1::OperationType::PAD: + case V1_1::OperationType::SPACE_TO_BATCH_ND: + case V1_1::OperationType::SQUEEZE: + case V1_1::OperationType::STRIDED_SLICE: + case V1_1::OperationType::SUB: + case V1_1::OperationType::TRANSPOSE: + return true; + default: + return false; + } +} + +bool HandledByV1_0(const V1_2::Operation& operation) +{ + return HandledByV1_0(operation.type); +} + +bool HandledByV1_1(const V1_2::Operation& operation) +{ + return HandledByV1_1(operation.type); +} + +V1_0::OperationType CastToV1_0(V1_2::OperationType type) +{ + return static_cast(type); +} + +V1_1::OperationType CastToV1_1(V1_2::OperationType type) +{ + return static_cast(type); +} + +V1_0::Operation ConvertToV1_0(const V1_2::Operation& operation) +{ + V1_0::Operation op; + op.type = CastToV1_0(operation.type); + op.inputs = operation.inputs; + op.outputs = operation.outputs; + return op; +} + +V1_1::Operation ConvertToV1_1(const V1_2::Operation& operation) +{ + V1_1::Operation op; + op.type = CastToV1_1(operation.type); + op.inputs = operation.inputs; + op.outputs = operation.outputs; + return op; +} + +bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data) +{ + if (HandledByV1_0(operation) && compliantWithV1_0(model)) + { + hal_1_0::HalPolicy::Operation v10Operation = ConvertToV1_0(operation); + hal_1_0::HalPolicy::Model v10Model = convertToV1_0(model); + + return hal_1_0::HalPolicy::ConvertOperation(v10Operation, v10Model, data); + } + else if (HandledByV1_1(operation) && compliantWithV1_1(model)) + { + hal_1_1::HalPolicy::Operation v11Operation = ConvertToV1_1(operation); + hal_1_1::HalPolicy::Model v11Model = convertToV1_1(model); + + return hal_1_1::HalPolicy::ConvertOperation(v11Operation, v11Model, data); + } + switch (operation.type) + { + case V1_2::OperationType::CONV_2D: + return ConvertConv2d(operation, model, data); + case V1_2::OperationType::DEPTHWISE_CONV_2D: + return ConvertDepthwiseConv2d(operation, model, data); + default: + return Fail("%s: Operation type %s not supported in ArmnnDriver", + __func__, toString(operation.type).c_str()); + } +} + +} // namespace hal_1_2 +} // namespace armnn_driver \ No newline at end of file diff --git a/1.2/HalPolicy.hpp b/1.2/HalPolicy.hpp new file mode 100644 index 00000000..d27e4c7a --- /dev/null +++ b/1.2/HalPolicy.hpp @@ -0,0 +1,31 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "../ConversionUtils.hpp" + +#include + +namespace armnn_driver +{ +namespace hal_1_2 +{ + +class HalPolicy +{ +public: + using Model = V1_2::Model; + using Operand = V1_2::Operand; + using Operation = V1_2::Operation; + using OperationType = V1_2::OperationType; + using ExecutionCallback = V1_2::IExecutionCallback; + using getSupportedOperations_cb = V1_2::IDevice::getSupportedOperations_1_2_cb; + + static bool ConvertOperation(const Operation& operation, const Model& model, ConversionData& data); +}; + +} // namespace hal_1_2 +} // namespace armnn_driver \ No newline at end of file diff --git a/Android.mk b/Android.mk index 8d2fe9f7..8f598ca7 100644 --- a/Android.mk +++ b/Android.mk @@ -25,6 +25,12 @@ P_OR_LATER := 1 Q_OR_LATER := 1 endif # PLATFORM_VERSION == Q +CPP_VERSION := c++14 + +ifeq ($(Q_OR_LATER),1) +CPP_VERSION := c++17 +endif + # Configure these paths if you move the source or Khronos headers ARMNN_HEADER_PATH := $(LOCAL_PATH)/armnn/include ARMNN_UTILS_HEADER_PATH := $(LOCAL_PATH)/armnn/src/armnnUtils @@ -68,7 +74,7 @@ LOCAL_C_INCLUDES := \ $(NN_HEADER_PATH) LOCAL_CFLAGS := \ - -std=c++14 \ + -std=$(CPP_VERSION) \ -fexceptions \ -Werror \ -Wno-format-security @@ -85,6 +91,11 @@ LOCAL_CFLAGS+= \ -UNDEBUG endif # ARMNN_DRIVER_DEBUG == 1 +ifeq ($(Q_OR_LATER),1) +LOCAL_CFLAGS += \ + -DBOOST_NO_AUTO_PTR +endif # PLATFORM_VERSION == Q or later + ifeq ($(ARMNN_COMPUTE_CL_ENABLED),1) LOCAL_CFLAGS += \ -DARMCOMPUTECL_ENABLED @@ -179,7 +190,7 @@ LOCAL_C_INCLUDES := \ $(NN_HEADER_PATH) LOCAL_CFLAGS := \ - -std=c++14 \ + -std=$(CPP_VERSION) \ -fexceptions \ -Werror \ -Wno-format-security \ @@ -191,6 +202,11 @@ LOCAL_CFLAGS+= \ -UNDEBUG endif # ARMNN_DRIVER_DEBUG == 1 +ifeq ($(Q_OR_LATER),1) +LOCAL_CFLAGS += \ + -DBOOST_NO_AUTO_PTR +endif # PLATFORM_VERSION == Q or later + ifeq ($(ARMNN_COMPUTE_CL_ENABLED),1) LOCAL_CFLAGS += \ -DARMCOMPUTECL_ENABLED @@ -255,6 +271,108 @@ include $(BUILD_STATIC_LIBRARY) endif # PLATFORM_VERSION == 9 +ifeq ($(Q_OR_LATER),1) +# The following target is available starting from Android Q + +####################### +# libarmnn-driver@1.2 # +####################### +include $(CLEAR_VARS) + +LOCAL_MODULE := libarmnn-driver@1.2 +LOCAL_MODULE_TAGS := optional +LOCAL_ARM_MODE := arm +LOCAL_PROPRIETARY_MODULE := true +# Mark source files as dependent on Android.mk +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +LOCAL_C_INCLUDES := \ + $(ARMNN_HEADER_PATH) \ + $(ARMNN_UTILS_HEADER_PATH) \ + $(OPENCL_HEADER_PATH) \ + $(NN_HEADER_PATH) + +LOCAL_CFLAGS := \ + -std=$(CPP_VERSION) \ + -fexceptions \ + -Werror \ + -Wno-format-security \ + -DARMNN_ANDROID_Q \ + -DARMNN_ANDROID_NN_V1_2 + +ifeq ($(ARMNN_DRIVER_DEBUG),1) +LOCAL_CFLAGS+= \ + -UNDEBUG +endif # ARMNN_DRIVER_DEBUG == 1 + +ifeq ($(Q_OR_LATER),1) +LOCAL_CFLAGS += \ + -DBOOST_NO_AUTO_PTR +endif # PLATFORM_VERSION == Q or later + +ifeq ($(ARMNN_COMPUTE_CL_ENABLED),1) +LOCAL_CFLAGS += \ + -DARMCOMPUTECL_ENABLED +endif # ARMNN_COMPUTE_CL_ENABLED == 1 + +ifeq ($(ARMNN_COMPUTE_NEON_ENABLED),1) +LOCAL_CFLAGS += \ + -DARMCOMPUTENEON_ENABLED +endif # ARMNN_COMPUTE_NEON_ENABLED == 1 + +LOCAL_SRC_FILES := \ + 1.0/ArmnnDriverImpl.cpp \ + 1.0/HalPolicy.cpp \ + 1.1/ArmnnDriverImpl.cpp \ + 1.1/HalPolicy.cpp \ + 1.2/ArmnnDriverImpl.cpp \ + 1.2/HalPolicy.cpp \ + ArmnnDriverImpl.cpp \ + DriverOptions.cpp \ + ArmnnDevice.cpp \ + ArmnnPreparedModel.cpp \ + ArmnnPreparedModel_1_2.cpp \ + ModelToINetworkConverter.cpp \ + RequestThread.cpp \ + Utils.cpp \ + ConversionUtils.cpp + +LOCAL_STATIC_LIBRARIES := \ + libneuralnetworks_common \ + libboost_log \ + libboost_program_options \ + libboost_system \ + libboost_thread \ + armnn-arm_compute + +LOCAL_WHOLE_STATIC_LIBRARIES := libarmnn + +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libhidlbase \ + libhidltransport \ + libhidlmemory \ + liblog \ + libutils \ + libnativewindow \ + libui \ + libfmq \ + libcutils \ + android.hidl.allocator@1.0 \ + android.hidl.memory@1.0 \ + android.hardware.neuralnetworks@1.0 \ + android.hardware.neuralnetworks@1.1 \ + android.hardware.neuralnetworks@1.2 + +ifeq ($(ARMNN_COMPUTE_CL_ENABLED),1) +LOCAL_SHARED_LIBRARIES+= \ + libOpenCL +endif + +include $(BUILD_STATIC_LIBRARY) + +endif # PLATFORM_VERSION == Q + ##################################################### # android.hardware.neuralnetworks@1.0-service-armnn # ##################################################### @@ -279,13 +397,18 @@ LOCAL_C_INCLUDES := \ $(NN_HEADER_PATH) LOCAL_CFLAGS := \ - -std=c++14 \ + -std=$(CPP_VERSION) \ -fexceptions ifeq ($(ARMNN_DRIVER_DEBUG),1) LOCAL_CFLAGS += \ -UNDEBUG endif # ARMNN_DRIVER_DEBUG == 1 +ifeq ($(Q_OR_LATER),1) +LOCAL_CFLAGS += \ + -DBOOST_NO_AUTO_PTR +endif # PLATFORM_VERSION == Q or later + LOCAL_SRC_FILES := \ service.cpp @@ -363,7 +486,7 @@ LOCAL_C_INCLUDES := \ $(NN_HEADER_PATH) LOCAL_CFLAGS := \ - -std=c++14 \ + -std=$(CPP_VERSION) \ -fexceptions \ -DARMNN_ANDROID_NN_V1_1 ifeq ($(ARMNN_DRIVER_DEBUG),1) @@ -371,6 +494,11 @@ LOCAL_CFLAGS += \ -UNDEBUG endif # ARMNN_DRIVER_DEBUG == 1 +ifeq ($(Q_OR_LATER),1) +LOCAL_CFLAGS += \ + -DBOOST_NO_AUTO_PTR +endif # PLATFORM_VERSION == Q or later + LOCAL_SRC_FILES := \ service.cpp @@ -418,6 +546,80 @@ include $(BUILD_EXECUTABLE) endif # PLATFORM_VERSION == 9 +ifeq ($(Q_OR_LATER),1) +# The following target is available starting from Android Q + +##################################################### +# android.hardware.neuralnetworks@1.2-service-armnn # +##################################################### +include $(CLEAR_VARS) + +LOCAL_MODULE := android.hardware.neuralnetworks@1.2-service-armnn +LOCAL_INIT_RC := android.hardware.neuralnetworks@1.2-service-armnn.rc +LOCAL_MODULE_TAGS := optional +LOCAL_ARM_MODE := arm +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_PROPRIETARY_MODULE := true +# Mark source files as dependent on Android.mk +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +LOCAL_C_INCLUDES := \ + $(ARMNN_HEADER_PATH) \ + $(NN_HEADER_PATH) + +LOCAL_CFLAGS := \ + -std=$(CPP_VERSION) \ + -fexceptions \ + -DARMNN_ANDROID_NN_V1_2 \ + -DBOOST_NO_AUTO_PTR +ifeq ($(ARMNN_DRIVER_DEBUG),1) +LOCAL_CFLAGS += \ + -UNDEBUG +endif # ARMNN_DRIVER_DEBUG == 1 + +LOCAL_SRC_FILES := \ + service.cpp + +LOCAL_STATIC_LIBRARIES := \ + libneuralnetworks_common \ + libboost_log \ + libboost_program_options \ + libboost_system \ + libboost_thread \ + armnn-arm_compute + +LOCAL_WHOLE_STATIC_LIBRARIES := \ + libarmnn-driver@1.2 + +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libhidlbase \ + libhidltransport \ + libhidlmemory \ + libdl \ + libhardware \ + liblog \ + libtextclassifier_hash \ + libutils \ + libnativewindow \ + libui \ + libfmq \ + libcutils \ + android.hidl.allocator@1.0 \ + android.hidl.memory@1.0 \ + android.hardware.neuralnetworks@1.0 \ + android.hardware.neuralnetworks@1.1 \ + android.hardware.neuralnetworks@1.2 + +ifeq ($(ARMNN_COMPUTE_CL_ENABLED),1) +LOCAL_SHARED_LIBRARIES+= \ + libOpenCL +endif + +include $(BUILD_EXECUTABLE) + +endif # PLATFORM_VERSION == Q + ########################## # armnn module and tests # ########################## diff --git a/ArmnnDriver.hpp b/ArmnnDriver.hpp index 7c6e5d0b..d961f861 100644 --- a/ArmnnDriver.hpp +++ b/ArmnnDriver.hpp @@ -9,7 +9,27 @@ #include -#ifdef ARMNN_ANDROID_NN_V1_1 // Using ::android::hardware::neuralnetworks::V1_1 +#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2 + +#include "1.1/ArmnnDriver.hpp" +#include "1.2/ArmnnDriver.hpp" + +namespace armnn_driver +{ + +class ArmnnDriver : public hal_1_2::ArmnnDriver +{ +public: + ArmnnDriver(DriverOptions options) + : hal_1_2::ArmnnDriver(std::move(options)) + { + ALOGV("ArmnnDriver::ArmnnDriver()"); + } + ~ArmnnDriver() {} +}; + +} // namespace armnn_driver +#elif ARMNN_ANDROID_NN_V1_1 // Using ::android::hardware::neuralnetworks::V1_1 #include "1.1/ArmnnDriver.hpp" diff --git a/ArmnnDriverImpl.cpp b/ArmnnDriverImpl.cpp index cb772478..64188bbf 100644 --- a/ArmnnDriverImpl.cpp +++ b/ArmnnDriverImpl.cpp @@ -7,15 +7,14 @@ #include "ArmnnDriverImpl.hpp" #include "ArmnnPreparedModel.hpp" -#include "ModelToINetworkConverter.hpp" -#include "SystemPropertiesUtils.hpp" -#if defined(ARMNN_ANDROID_P) -// The headers of the ML framework have changed between Android O and Android P. -// The validation functions have been moved into their own header, ValidateHal.h. -#include +#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2 +#include "ArmnnPreparedModel_1_2.hpp" #endif +#include "ModelToINetworkConverter.hpp" +#include "SystemPropertiesUtils.hpp" +#include #include using namespace std; @@ -35,7 +34,7 @@ void NotifyCallbackAndCheck(const sp& callback, if (!returned.isOk()) { ALOGE("ArmnnDriverImpl::prepareModel: hidl callback failed to return properly: %s ", - returned.description().c_str()); + returned.description().c_str()); } } @@ -48,59 +47,12 @@ Return FailPrepareModel(ErrorStatus error, return error; } + } // namespace namespace armnn_driver { -template -Return ArmnnDriverImpl::getSupportedOperations(const armnn::IRuntimePtr& runtime, - const DriverOptions& options, - const HalModel& model, - HalGetSupportedOperations_cb cb) -{ - ALOGV("ArmnnDriverImpl::getSupportedOperations()"); - - vector result; - - if (!runtime) - { - cb(ErrorStatus::DEVICE_UNAVAILABLE, result); - return Void(); - } - - // Run general model validation, if this doesn't pass we shouldn't analyse the model anyway. - if (!android::nn::validateModel(model)) - { - cb(ErrorStatus::INVALID_ARGUMENT, result); - return Void(); - } - - // Attempt to convert the model to an ArmNN input network (INetwork). - ModelToINetworkConverter modelConverter(options.GetBackends(), - model, - options.GetForcedUnsupportedOperations()); - - if (modelConverter.GetConversionResult() != ConversionResult::Success - && modelConverter.GetConversionResult() != ConversionResult::UnsupportedFeature) - { - cb(ErrorStatus::GENERAL_FAILURE, result); - return Void(); - } - - // Check each operation if it was converted successfully and copy the flags - // into the result (vector) that we need to return to Android. - result.reserve(model.operations.size()); - for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); operationIdx++) - { - bool operationSupported = modelConverter.IsOperationSupported(operationIdx); - result.push_back(operationSupported); - } - - cb(ErrorStatus::NONE, result); - return Void(); -} - template Return ArmnnDriverImpl::prepareModel( const armnn::IRuntimePtr& runtime, @@ -199,7 +151,7 @@ Return ArmnnDriverImpl::prepareModel( } unique_ptr> preparedModel( - new ArmnnPreparedModel( + new ArmnnPreparedModel( netId, runtime.get(), model, @@ -233,6 +185,54 @@ Return ArmnnDriverImpl::prepareModel( return ErrorStatus::NONE; } +template +Return ArmnnDriverImpl::getSupportedOperations(const armnn::IRuntimePtr& runtime, + const DriverOptions& options, + const HalModel& model, + HalGetSupportedOperations_cb cb) +{ + ALOGV("ArmnnDriverImpl::getSupportedOperations()"); + + vector result; + + if (!runtime) + { + cb(ErrorStatus::DEVICE_UNAVAILABLE, result); + return Void(); + } + + // Run general model validation, if this doesn't pass we shouldn't analyse the model anyway. + if (!android::nn::validateModel(model)) + { + cb(ErrorStatus::INVALID_ARGUMENT, result); + return Void(); + } + + // Attempt to convert the model to an ArmNN input network (INetwork). + ModelToINetworkConverter modelConverter(options.GetBackends(), + model, + options.GetForcedUnsupportedOperations()); + + if (modelConverter.GetConversionResult() != ConversionResult::Success + && modelConverter.GetConversionResult() != ConversionResult::UnsupportedFeature) + { + cb(ErrorStatus::GENERAL_FAILURE, result); + return Void(); + } + + // Check each operation if it was converted successfully and copy the flags + // into the result (vector) that we need to return to Android. + result.reserve(model.operations.size()); + for (uint32_t operationIdx = 0; operationIdx < model.operations.size(); operationIdx++) + { + bool operationSupported = modelConverter.IsOperationSupported(operationIdx); + result.push_back(operationSupported); + } + + cb(ErrorStatus::NONE, result); + return Void(); +} + template Return ArmnnDriverImpl::getStatus() { @@ -251,4 +251,9 @@ template class ArmnnDriverImpl; template class ArmnnDriverImpl; #endif +#ifdef ARMNN_ANDROID_NN_V1_2 +template class ArmnnDriverImpl; +template class ArmnnDriverImpl; +#endif + } // namespace armnn_driver diff --git a/ArmnnDriverImpl.hpp b/ArmnnDriverImpl.hpp index b2808ebb..49f0975e 100644 --- a/ArmnnDriverImpl.hpp +++ b/ArmnnDriverImpl.hpp @@ -10,6 +10,11 @@ #include namespace V1_0 = ::android::hardware::neuralnetworks::V1_0; +namespace V1_1 = ::android::hardware::neuralnetworks::V1_1; + +#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2 +namespace V1_2 = ::android::hardware::neuralnetworks::V1_2; +#endif namespace armnn_driver { @@ -36,6 +41,7 @@ public: bool float32ToFloat16 = false); static Return getStatus(); + }; } // namespace armnn_driver diff --git a/ArmnnPreparedModel.cpp b/ArmnnPreparedModel.cpp index f183caff..3256836e 100644 --- a/ArmnnPreparedModel.cpp +++ b/ArmnnPreparedModel.cpp @@ -12,7 +12,7 @@ #include #include -#if defined(ARMNN_ANDROID_P) +#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q) // The headers of the ML framework have changed between Android O and Android P. // The validation functions have been moved into their own header, ValidateHal.h. #include @@ -89,7 +89,7 @@ namespace armnn_driver { template -RequestThread ArmnnPreparedModel::m_RequestThread; +RequestThread ArmnnPreparedModel::m_RequestThread; template template @@ -318,4 +318,8 @@ template class ArmnnPreparedModel; template class ArmnnPreparedModel; #endif +#ifdef ARMNN_ANDROID_NN_V1_2 +template class ArmnnPreparedModel; +template class ArmnnPreparedModel; +#endif } // namespace armnn_driver diff --git a/ArmnnPreparedModel.hpp b/ArmnnPreparedModel.hpp index 7a65d52b..275af316 100644 --- a/ArmnnPreparedModel.hpp +++ b/ArmnnPreparedModel.hpp @@ -49,15 +49,15 @@ private: template void DumpTensorsIfRequired(char const* tensorNamePrefix, const TensorBindingCollection& tensorBindings); - armnn::NetworkId m_NetworkId; - armnn::IRuntime* m_Runtime; - HalModel m_Model; + armnn::NetworkId m_NetworkId; + armnn::IRuntime* m_Runtime; + HalModel m_Model; // There must be a single RequestThread for all ArmnnPreparedModel objects to ensure serial execution of workloads // It is specific to this class, so it is declared as static here - static RequestThread m_RequestThread; - uint32_t m_RequestCount; - const std::string& m_RequestInputsAndOutputsDumpDir; - const bool m_GpuProfilingEnabled; + static RequestThread m_RequestThread; + uint32_t m_RequestCount; + const std::string& m_RequestInputsAndOutputsDumpDir; + const bool m_GpuProfilingEnabled; }; } diff --git a/ArmnnPreparedModel_1_2.cpp b/ArmnnPreparedModel_1_2.cpp new file mode 100644 index 00000000..f03d69d9 --- /dev/null +++ b/ArmnnPreparedModel_1_2.cpp @@ -0,0 +1,486 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#define LOG_TAG "ArmnnDriver" + +#include "ArmnnPreparedModel_1_2.hpp" +#include "Utils.hpp" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace android; +using namespace android::hardware; + +static const Timing g_NoTiming = {.timeOnDevice = UINT64_MAX, .timeInDriver = UINT64_MAX}; + +namespace { + +using namespace armnn_driver; + +void NotifyCallbackAndCheck(const ::android::sp& callback, ErrorStatus errorStatus, + std::string callingFunction) +{ + Return returned = callback->notify(errorStatus); + // This check is required, if the callback fails and it isn't checked it will bring down the service + if (!returned.isOk()) + { + ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s", + callingFunction.c_str(), returned.description().c_str()); + } +} + +void NotifyCallbackAndCheck(const ::android::sp& callback, ErrorStatus errorStatus, + std::string callingFunction) +{ + Return returned = callback->notify(errorStatus); + // This check is required, if the callback fails and it isn't checked it will bring down the service + if (!returned.isOk()) + { + ALOGE("ArmnnDriver::%s: hidl callback failed to return properly: %s", + callingFunction.c_str(), returned.description().c_str()); + } +} + +bool ValidateRequestArgument(const RequestArgument& requestArg, const armnn::TensorInfo& tensorInfo) +{ + if (requestArg.dimensions.size() != 0) + { + if (requestArg.dimensions.size() != tensorInfo.GetNumDimensions()) + { + ALOGE("Mismatched dimensions (request argument: %zu, expected: %u)", + requestArg.dimensions.size(), tensorInfo.GetNumDimensions()); + return false; + } + + for (unsigned int d = 0; d < tensorInfo.GetNumDimensions(); ++d) + { + if (requestArg.dimensions[d] != tensorInfo.GetShape()[d]) + { + ALOGE("Mismatched size for dimension %d (request argument: %u, expected %u)", + d, requestArg.dimensions[d], tensorInfo.GetShape()[d]); + return false; + } + } + } + + return true; +} + +armnn::Tensor GetTensorForRequestArgument(const RequestArgument& requestArg, + const armnn::TensorInfo& tensorInfo, + const std::vector<::android::nn::RunTimePoolInfo>& requestPools) +{ + if (!ValidateRequestArgument(requestArg, tensorInfo)) + { + return armnn::Tensor(); + } + + return armnn::Tensor(tensorInfo, GetMemoryFromPool(requestArg.location, requestPools)); +} + +inline std::string BuildTensorName(const char* tensorNamePrefix, std::size_t index) +{ + return tensorNamePrefix + std::to_string(index); +} + +} // anonymous namespace + +using namespace android::hardware; + +namespace armnn_driver +{ + +template +RequestThread ArmnnPreparedModel_1_2::m_RequestThread; + +template +template +void ArmnnPreparedModel_1_2::DumpTensorsIfRequired(char const* tensorNamePrefix, + const TensorBindingCollection& tensorBindings) +{ + if (!m_RequestInputsAndOutputsDumpDir.empty()) + { + const std::string requestName = boost::str(boost::format("%1%_%2%.dump") % m_NetworkId % m_RequestCount); + for (std::size_t i = 0u; i < tensorBindings.size(); ++i) + { + DumpTensor(m_RequestInputsAndOutputsDumpDir, + requestName, + BuildTensorName(tensorNamePrefix, i), + tensorBindings[i].second); + } + } +} + +template +ArmnnPreparedModel_1_2::ArmnnPreparedModel_1_2(armnn::NetworkId networkId, + armnn::IRuntime* runtime, + const V1_2::Model& model, + const std::string& requestInputsAndOutputsDumpDir, + const bool gpuProfilingEnabled) + : m_NetworkId(networkId) + , m_Runtime(runtime) + , m_Model(model) + , m_RequestCount(0) + , m_RequestInputsAndOutputsDumpDir(requestInputsAndOutputsDumpDir) + , m_GpuProfilingEnabled(gpuProfilingEnabled) +{ + // Enable profiling if required. + m_Runtime->GetProfiler(m_NetworkId)->EnableProfiling(m_GpuProfilingEnabled); +} + +template +ArmnnPreparedModel_1_2::~ArmnnPreparedModel_1_2() +{ + // Get a hold of the profiler used by this model. + std::shared_ptr profiler = m_Runtime->GetProfiler(m_NetworkId); + + // Unload the network associated with this model. + m_Runtime->UnloadNetwork(m_NetworkId); + + // Dump the profiling info to a file if required. + DumpJsonProfilingIfRequired(m_GpuProfilingEnabled, m_RequestInputsAndOutputsDumpDir, m_NetworkId, profiler.get()); +} + +template +Return ArmnnPreparedModel_1_2::execute(const Request& request, + const ::android::sp& callback) +{ + return Execute(request, callback); +} + +template +Return ArmnnPreparedModel_1_2::execute_1_2(const Request& request, + MeasureTiming, + const sp& callback) +{ + return Execute(request, callback); +} + +template +Return ArmnnPreparedModel_1_2::executeSynchronously(const Request& request, + MeasureTiming, + V1_2::IPreparedModel::executeSynchronously_cb cb) +{ + ALOGV("ArmnnPreparedModel_1_2::executeSynchronously(): %s", GetModelSummary(m_Model).c_str()); + m_RequestCount++; + + if (cb == nullptr) + { + ALOGE("ArmnnPreparedModel_1_2::executeSynchronously invalid callback passed"); + return Void(); + } + + if (!android::nn::validateRequest(request, m_Model)) + { + cb(ErrorStatus::INVALID_ARGUMENT, {}, g_NoTiming); + return Void(); + } + + // allocate the tensors on the heap, as they are passed to the request thread + auto pInputTensors = std::make_shared(); + auto pOutputTensors = std::make_shared(); + + // map the memory pool into shared pointers + // use a shared memory pools vector on the heap, as it is passed to the request thread + auto pMemPools = std::make_shared>(); + + if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools)) + { + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + + // add the inputs and outputs with their data + try + { + pInputTensors->reserve(request.inputs.size()); + for (unsigned int i = 0; i < request.inputs.size(); i++) + { + const auto& inputArg = request.inputs[i]; + + const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i); + const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools); + + if (inputTensor.GetMemoryArea() == nullptr) + { + ALOGE("Cannot execute request. Error converting request input %u to tensor", i); + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + + pInputTensors->emplace_back(i, inputTensor); + } + + pOutputTensors->reserve(request.outputs.size()); + for (unsigned int i = 0; i < request.outputs.size(); i++) + { + const auto& outputArg = request.outputs[i]; + + const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i); + const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools); + + if (outputTensor.GetMemoryArea() == nullptr) + { + ALOGE("Cannot execute request. Error converting request output %u to tensor", i); + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + + pOutputTensors->emplace_back(i, outputTensor); + } + } + catch (armnn::Exception& e) + { + ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what()); + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() before Execution"); + + DumpTensorsIfRequired("Input", *pInputTensors); + + // run it + try + { + armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors); + + if (status != armnn::Status::Success) + { + ALOGW("EnqueueWorkload failed"); + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + } + catch (armnn::Exception& e) + { + ALOGW("armnn::Exception caught from EnqueueWorkload: %s", e.what()); + cb(ErrorStatus::GENERAL_FAILURE, {}, g_NoTiming); + return Void(); + } + + DumpTensorsIfRequired("Output", *pOutputTensors); + + // Commit output buffers. + // Note that we update *all* pools, even if they aren't actually used as outputs - + // this is simpler and is what the CpuExecutor does. + for (android::nn::RunTimePoolInfo& pool : *pMemPools) + { + pool.update(); + } + ALOGV("ArmnnPreparedModel_1_2::executeSynchronously() after Execution"); + cb(ErrorStatus::NONE, {}, g_NoTiming); + return Void(); +} + +template +Return ArmnnPreparedModel_1_2::configureExecutionBurst( + const sp& callback, + const MQDescriptorSync& requestChannel, + const MQDescriptorSync& resultChannel, + V1_2::IPreparedModel::configureExecutionBurst_cb cb) +{ + ALOGV("ArmnnPreparedModel_1_2::configureExecutionBurst"); + const sp burst = + ExecutionBurstServer::create(callback, requestChannel, resultChannel, this); + + if (burst == nullptr) { + cb(ErrorStatus::GENERAL_FAILURE, {}); + } else { + cb(ErrorStatus::NONE, burst); + } + return Void(); +} + +template +void ArmnnPreparedModel_1_2::ExecuteGraph( + std::shared_ptr>& pMemPools, + std::shared_ptr& pInputTensors, + std::shared_ptr& pOutputTensors, + const ::android::sp& callback) +{ + ALOGV("ArmnnPreparedModel_1_2::ExecuteGraph(...)"); + + DumpTensorsIfRequired("Input", *pInputTensors); + + // run it + try + { + armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, *pInputTensors, *pOutputTensors); + if (status != armnn::Status::Success) + { + ALOGW("EnqueueWorkload failed"); + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, "ArmnnPreparedModel_1_2::ExecuteGraph"); + return; + } + } + catch (armnn::Exception& e) + { + ALOGW("armnn::Exception caught from EnqueueWorkload: %s", e.what()); + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, "ArmnnPreparedModel_1_2::ExecuteGraph"); + return; + } + + DumpTensorsIfRequired("Output", *pOutputTensors); + + // Commit output buffers. + // Note that we update *all* pools, even if they aren't actually used as outputs - + // this is simpler and is what the CpuExecutor does. + for (android::nn::RunTimePoolInfo& pool : *pMemPools) + { + pool.update(); + } + + NotifyCallbackAndCheck(callback, ErrorStatus::NONE, "ExecuteGraph"); +} + +template +bool ArmnnPreparedModel_1_2::ExecuteWithDummyInputs() +{ + std::vector> storage; + armnn::InputTensors inputTensors; + for (unsigned int i = 0; i < m_Model.inputIndexes.size(); i++) + { + const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i); + storage.emplace_back(inputTensorInfo.GetNumBytes()); + const armnn::ConstTensor inputTensor(inputTensorInfo, storage.back().data()); + + inputTensors.emplace_back(i, inputTensor); + } + + armnn::OutputTensors outputTensors; + for (unsigned int i = 0; i < m_Model.outputIndexes.size(); i++) + { + const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i); + storage.emplace_back(outputTensorInfo.GetNumBytes()); + const armnn::Tensor outputTensor(outputTensorInfo, storage.back().data()); + + outputTensors.emplace_back(i, outputTensor); + } + + try + { + armnn::Status status = m_Runtime->EnqueueWorkload(m_NetworkId, inputTensors, outputTensors); + if (status != armnn::Status::Success) + { + ALOGW("ExecuteWithDummyInputs: EnqueueWorkload failed"); + return false; + } + } + catch (armnn::Exception& e) + { + ALOGW("ExecuteWithDummyInputs: armnn::Exception caught from EnqueueWorkload: %s", e.what()); + return false; + } + return true; +} + +template +template +Return ArmnnPreparedModel_1_2::Execute(const Request& request, + const sp& callback) +{ + ALOGV("ArmnnPreparedModel_1_2::execute(): %s", GetModelSummary(m_Model).c_str()); + m_RequestCount++; + + if (callback.get() == nullptr) + { + ALOGE("ArmnnPreparedModel_1_2::execute invalid callback passed"); + return ErrorStatus::INVALID_ARGUMENT; + } + + if (!android::nn::validateRequest(request, m_Model)) + { + NotifyCallbackAndCheck(callback, ErrorStatus::INVALID_ARGUMENT, "ArmnnPreparedModel_1_2::execute"); + return ErrorStatus::INVALID_ARGUMENT; + } + + if (!m_RequestInputsAndOutputsDumpDir.empty()) + { + ALOGD("Dumping inputs and outputs for request %" PRIuPTR, reinterpret_cast(callback.get())); + } + + // allocate the tensors on the heap, as they are passed to the request thread + auto pInputTensors = std::make_shared(); + auto pOutputTensors = std::make_shared(); + + // map the memory pool into shared pointers + // use a shared memory pools vector on the heap, as it is passed to the request thread + auto pMemPools = std::make_shared>(); + + if (!setRunTimePoolInfosFromHidlMemories(pMemPools.get(), request.pools)) + { + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, "ArmnnPreparedModel_1_2::execute"); + return ErrorStatus::GENERAL_FAILURE; + } + + // add the inputs and outputs with their data + try + { + pInputTensors->reserve(request.inputs.size()); + for (unsigned int i = 0; i < request.inputs.size(); i++) + { + const auto& inputArg = request.inputs[i]; + + const armnn::TensorInfo inputTensorInfo = m_Runtime->GetInputTensorInfo(m_NetworkId, i); + const armnn::Tensor inputTensor = GetTensorForRequestArgument(inputArg, inputTensorInfo, *pMemPools); + + if (inputTensor.GetMemoryArea() == nullptr) + { + ALOGE("Cannot execute request. Error converting request input %u to tensor", i); + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, + "ArmnnPreparedModel_1_2::execute"); + return ErrorStatus::GENERAL_FAILURE; + } + + pInputTensors->emplace_back(i, inputTensor); + } + + pOutputTensors->reserve(request.outputs.size()); + for (unsigned int i = 0; i < request.outputs.size(); i++) + { + const auto& outputArg = request.outputs[i]; + + const armnn::TensorInfo outputTensorInfo = m_Runtime->GetOutputTensorInfo(m_NetworkId, i); + const armnn::Tensor outputTensor = GetTensorForRequestArgument(outputArg, outputTensorInfo, *pMemPools); + if (outputTensor.GetMemoryArea() == nullptr) + + { + ALOGE("Cannot execute request. Error converting request output %u to tensor", i); + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, + "ArmnnPreparedModel_1_2::execute"); + return ErrorStatus::GENERAL_FAILURE; + } + + pOutputTensors->emplace_back(i, outputTensor); + } + } + catch (armnn::Exception& e) + { + ALOGW("armnn::Exception caught while preparing for EnqueueWorkload: %s", e.what()); + NotifyCallbackAndCheck(callback, ErrorStatus::GENERAL_FAILURE, "ArmnnPreparedModel_1_2::execute"); + return ErrorStatus::GENERAL_FAILURE; + } + + ALOGV("ArmnnPreparedModel_1_2::execute(...) before PostMsg"); + // post the request for asynchronous execution + m_RequestThread.PostMsg(this, pMemPools, pInputTensors, pOutputTensors, callback); + ALOGV("ArmnnPreparedModel_1_2::execute(...) after PostMsg"); + + return ErrorStatus::NONE; +} + + +#ifdef ARMNN_ANDROID_NN_V1_2 +template class ArmnnPreparedModel_1_2; +#endif + +} // namespace armnn_driver diff --git a/ArmnnPreparedModel_1_2.hpp b/ArmnnPreparedModel_1_2.hpp new file mode 100644 index 00000000..4e883b6b --- /dev/null +++ b/ArmnnPreparedModel_1_2.hpp @@ -0,0 +1,80 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "ArmnnDriver.hpp" +#include "ArmnnDriverImpl.hpp" +#include "RequestThread.hpp" +#include "ModelToINetworkConverter.hpp" + +#include +#include + +#include +#include + +namespace armnn_driver +{ + +template +class ArmnnPreparedModel_1_2 : public V1_2::IPreparedModel +{ +public: + using HalModel = typename V1_2::Model; + + ArmnnPreparedModel_1_2(armnn::NetworkId networkId, + armnn::IRuntime* runtime, + const HalModel& model, + const std::string& requestInputsAndOutputsDumpDir, + const bool gpuProfilingEnabled); + + virtual ~ArmnnPreparedModel_1_2(); + + virtual Return execute(const Request& request, + const ::android::sp& callback) override; + + virtual Return execute_1_2(const Request& request, MeasureTiming measure, + const sp& callback) override; + + virtual Return executeSynchronously(const Request &request, + MeasureTiming measure, + V1_2::IPreparedModel::executeSynchronously_cb cb) override; + + virtual Return configureExecutionBurst( + const sp& callback, + const android::hardware::MQDescriptorSync& requestChannel, + const android::hardware::MQDescriptorSync& resultChannel, + configureExecutionBurst_cb cb) override; + + /// execute the graph prepared from the request + void ExecuteGraph(std::shared_ptr>& pMemPools, + std::shared_ptr& pInputTensors, + std::shared_ptr& pOutputTensors, + const ::android::sp& callback); + + /// Executes this model with dummy inputs (e.g. all zeroes). + /// \return false on failure, otherwise true + bool ExecuteWithDummyInputs(); + +private: + template + Return Execute(const Request &request, const sp &callback); + + template + void DumpTensorsIfRequired(char const* tensorNamePrefix, const TensorBindingCollection& tensorBindings); + + armnn::NetworkId m_NetworkId; + armnn::IRuntime* m_Runtime; + V1_2::Model m_Model; + // There must be a single RequestThread for all ArmnnPreparedModel objects to ensure serial execution of workloads + // It is specific to this class, so it is declared as static here + static RequestThread m_RequestThread; + uint32_t m_RequestCount; + const std::string& m_RequestInputsAndOutputsDumpDir; + const bool m_GpuProfilingEnabled; +}; + +} diff --git a/ConversionUtils.hpp b/ConversionUtils.hpp index 57c46160..3ad15d30 100644 --- a/ConversionUtils.hpp +++ b/ConversionUtils.hpp @@ -7,6 +7,7 @@ #include +#include "armnn/src/armnnUtils/DataLayoutIndexed.hpp" #include "armnn/src/armnnUtils/Permute.hpp" #include "Utils.hpp" @@ -159,7 +160,8 @@ bool IsLayerSupportedForAnyBackend(const char* funcName, return false; } -armnn::TensorShape GetTensorShapeForOperand(const V1_0::Operand& operand) +template +armnn::TensorShape GetTensorShapeForOperand(const Operand& operand) { return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data()); } @@ -171,6 +173,34 @@ inline bool IsOperandTypeSupportedForTensors(V1_0::OperandType type) type == V1_0::OperandType::TENSOR_INT32; } +#ifdef ARMNN_ANDROID_NN_V1_2 + +inline bool IsOperandTypeSupportedForTensors(V1_2::OperandType type) +{ + return type == V1_2::OperandType::BOOL || + type == V1_2::OperandType::TENSOR_FLOAT16 || + type == V1_2::OperandType::TENSOR_FLOAT32 || + type == V1_2::OperandType::TENSOR_QUANT8_ASYMM || + type == V1_2::OperandType::TENSOR_QUANT16_SYMM || + type == V1_2::OperandType::TENSOR_INT32; +} + +#endif + +inline bool IsBool(V1_0::Operand) +{ + return false; +} + +#ifdef ARMNN_ANDROID_NN_V1_2 + +inline bool IsBool(V1_2::Operand operand) +{ + return operand.type == V1_2::OperandType::BOOL; +} + +#endif + void BroadcastTensor(LayerInputHandle& input0, LayerInputHandle& input1, armnn::IConnectableLayer* startLayer, armnn::INetwork& network) { @@ -462,9 +492,9 @@ namespace armnn_driver using namespace android::nn; -template -const V1_0::Operand* GetInputOperand(const HalOperation& operation, uint32_t inputIndex, const HalModel& model, - bool failOnIndexOutOfBounds = true) +template +const HalOperand* GetInputOperand(const HalOperation& operation, uint32_t inputIndex, const HalModel& model, + bool failOnIndexOutOfBounds = true) { if (inputIndex >= operation.inputs.size()) { @@ -479,8 +509,8 @@ const V1_0::Operand* GetInputOperand(const HalOperation& operation, uint32_t inp return &model.operands[operation.inputs[inputIndex]]; } -template -const V1_0::Operand* GetOutputOperand(const HalOperation& operation, uint32_t outputIndex, const HalModel& model) +template +const HalOperand* GetOutputOperand(const HalOperation& operation, uint32_t outputIndex, const HalModel& model) { if (outputIndex >= operation.outputs.size()) { @@ -494,8 +524,8 @@ const V1_0::Operand* GetOutputOperand(const HalOperation& operation, uint32_t ou return &model.operands[operation.outputs[outputIndex]]; } -template -ConstTensorPin ConvertOperandToConstTensorPin(const V1_0::Operand& operand, +template +ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand, const HalModel& model, const ConversionData& data, const armnn::PermutationVector& dimensionMappings = g_DontPermute, @@ -538,7 +568,7 @@ ConstTensorPin ConvertOperandToConstTensorPin(const V1_0::Operand& operand, return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings); } -template +template ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation, uint32_t inputIndex, const HalModel& model, @@ -547,7 +577,7 @@ ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operati const armnn::TensorShape* overrideTensorShape = nullptr, bool optional = false) { - const V1_0::Operand* operand = GetInputOperand(operation, inputIndex, model); + const HalOperand* operand = GetInputOperand(operation, inputIndex, model); if (!operand) { Fail("%s: failed to get input operand: index=%u", __func__, inputIndex); @@ -561,8 +591,8 @@ ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operati optional); } -template -const void* GetOperandValueReadOnlyAddress(const V1_0::Operand& operand, +template +const void* GetOperandValueReadOnlyAddress(const HalOperand& operand, const HalModel& model, const ConversionData& data, bool optional = false) @@ -605,15 +635,15 @@ const void* GetOperandValueReadOnlyAddress(const V1_0::Operand& operand, return valueStart; } -template +template bool GetInputScalar(const HalOperation& operation, uint32_t inputIndex, - V1_0::OperandType type, + HalOperandType type, OutputType& outValue, const HalModel& model, const ConversionData& data) { - const V1_0::Operand* operand = GetInputOperand(operation, inputIndex, model); + const HalOperand* operand = GetInputOperand(operation, inputIndex, model); if (!operand) { return Fail("%s: invalid input operand at index %i", __func__, inputIndex); @@ -641,37 +671,37 @@ bool GetInputScalar(const HalOperation& operation, return true; } -template +template bool GetInputInt32(const HalOperation& operation, uint32_t inputIndex, int32_t& outValue, const HalModel& model, const ConversionData& data) { - return GetInputScalar(operation, inputIndex,V1_0::OperandType::INT32, outValue, model, data); + return GetInputScalar(operation, inputIndex, HalOperandType::INT32, outValue, model, + data); } - -template +template bool GetInputFloat32(const HalOperation& operation, uint32_t inputIndex, float& outValue, const HalModel& model, const ConversionData& data) { - return GetInputScalar(operation, inputIndex,V1_0::OperandType::FLOAT32, outValue, model, data); + return GetInputScalar(operation, inputIndex, HalOperandType::FLOAT32, outValue, model, + data); } - -template +template bool GetInputActivationFunctionImpl(const HalOperation& operation, uint32_t inputIndex, - V1_0::OperandType type, + HalOperandType type, ActivationFn& outActivationFunction, const HalModel& model, const ConversionData& data) { - if (type !=V1_0::OperandType::INT32 && type !=V1_0::OperandType::TENSOR_INT32) + if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32) { return Fail("%s: unexpected operand type: %s (should be %s or %s)", __func__, @@ -681,7 +711,7 @@ bool GetInputActivationFunctionImpl(const HalOperation& operation, } int32_t activationFunctionAsInt; - if (!GetInputScalar(operation, inputIndex, type, activationFunctionAsInt, model, data)) + if (!GetInputScalar(operation, inputIndex, type, activationFunctionAsInt, model, data)) { return Fail("%s: failed to get activation input value", __func__); } @@ -689,23 +719,22 @@ bool GetInputActivationFunctionImpl(const HalOperation& operation, return true; } - -template +template bool GetInputActivationFunction(const HalOperation& operation, uint32_t inputIndex, ActivationFn& outActivationFunction, const HalModel& model, const ConversionData& data) { - return GetInputActivationFunctionImpl(operation, - inputIndex, - V1_0::OperandType::INT32, - outActivationFunction, - model, - data); + return GetInputActivationFunctionImpl(operation, + inputIndex, + HalOperandType::INT32, + outActivationFunction, + model, + data); } -template +template bool GetInputActivationFunctionFromTensor(const HalOperation& operation, uint32_t inputIndex, ActivationFn& outActivationFunction, @@ -713,16 +742,16 @@ bool GetInputActivationFunctionFromTensor(const HalOperation& operation, const ConversionData& data) { // This only accepts a 1-D tensor of size 1 - return GetInputActivationFunctionImpl(operation, - inputIndex, - V1_0::OperandType::INT32, - outActivationFunction, - model, - data); + return GetInputActivationFunctionImpl(operation, + inputIndex, + HalOperandType::INT32, + outActivationFunction, + model, + data); } -template +template bool GetOptionalInputActivation(const HalOperation& operation, uint32_t inputIndex, ActivationFn& activationFunction, @@ -735,7 +764,8 @@ bool GetOptionalInputActivation(const HalOperation& operation, } else { - if (!GetInputActivationFunction(operation, inputIndex, activationFunction, model, data)) + if (!GetInputActivationFunction(operation, inputIndex, activationFunction, model, + data)) { return Fail("%s: Operation has invalid inputs", __func__); } @@ -743,13 +773,13 @@ bool GetOptionalInputActivation(const HalOperation& operation, return true; } -template -bool GetTensorInt32Values(const V1_0::Operand& operand, +template +bool GetTensorInt32Values(const HalOperand& operand, std::vector& outValues, const HalModel& model, const ConversionData& data) { - if (operand.type !=V1_0::OperandType::TENSOR_INT32) + if (operand.type != HalOperandType::TENSOR_INT32) { return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str()); } @@ -773,7 +803,7 @@ bool GetTensorInt32Values(const V1_0::Operand& operand, return true; } -template +template bool GetInputPaddingScheme(const HalOperation& operation, uint32_t inputIndex, PaddingScheme& outPaddingScheme, @@ -781,7 +811,7 @@ bool GetInputPaddingScheme(const HalOperation& operation, const ConversionData& data) { int32_t paddingSchemeAsInt; - if (!GetInputInt32(operation, inputIndex, paddingSchemeAsInt, model, data)) + if (!GetInputInt32(operation, inputIndex, paddingSchemeAsInt, model, data)) { return Fail("%s: failed to get padding scheme input value", __func__); } @@ -790,13 +820,13 @@ bool GetInputPaddingScheme(const HalOperation& operation, return true; } -template +template LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation, uint32_t inputIndex, const HalModel& model, ConversionData& data) { - const V1_0::Operand* operand = GetInputOperand(operation, inputIndex, model); + const HalOperand* operand = GetInputOperand(operation, inputIndex, model); if (!operand) { Fail("%s: failed to get input operand %i", __func__, inputIndex); @@ -863,20 +893,87 @@ LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation, } } -template +template +bool SetupAndTrackLayerOutputSlot(const HalOperation& operation, + uint32_t operationOutputIndex, + armnn::IConnectableLayer& layer, + uint32_t layerOutputIndex, + const HalModel& model, + ConversionData& data) +{ + const HalOperand* outputOperand = GetOutputOperand(operation, operationOutputIndex, model); + if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots())) + { + return false; + } + + armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex); + + const uint32_t operandIndex = operation.outputs[operationOutputIndex]; + data.m_OutputSlotForOperand[operandIndex] = &outputSlot; + + outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand)); + + return true; +} + +template +armnn::DataLayout OptionalDataLayout(const HalOperation& operation, + uint32_t inputIndex, + const HalModel& model, + ConversionData& data) +{ + const HalOperand* operand = GetInputOperand(operation, inputIndex, model); + if (!operand) + { + return armnn::DataLayout::NHWC; + } + + if (!IsBool(*operand)) + { + return armnn::DataLayout::NHWC; + } + + const void* valueAddress = GetOperandValueReadOnlyAddress(*operand, model, data); + if (!valueAddress) + { + return armnn::DataLayout::NHWC; + } + + if (*(static_cast(valueAddress))) + { + return armnn::DataLayout::NCHW; + } + else + { + return armnn::DataLayout::NHWC; + } +} + +template +bool SetupAndTrackLayerOutputSlot(const HalOperation& operation, + uint32_t outputIndex, + armnn::IConnectableLayer& layer, + const HalModel& model, + ConversionData& data) +{ + return SetupAndTrackLayerOutputSlot(operation, outputIndex, layer, outputIndex, model, data); +} + +template bool ConvertToActivation(const HalOperation& operation, const char* operationName, const armnn::ActivationDescriptor& activationDesc, const HalModel& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Input 0 is invalid", operationName); } - const V1_0::Operand* outputOperand = GetOutputOperand(operation, 0, model); + const HalOperand* outputOperand = GetOutputOperand(operation, 0, model); if (!outputOperand) { return false; @@ -896,57 +993,23 @@ bool ConvertToActivation(const HalOperation& operation, BOOST_ASSERT(layer != nullptr); input.Connect(layer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); -} - -template -bool SetupAndTrackLayerOutputSlot(const HalOperation& operation, - uint32_t operationOutputIndex, - armnn::IConnectableLayer& layer, - uint32_t layerOutputIndex, - const HalModel& model, - ConversionData& data) -{ - const V1_0::Operand* outputOperand = GetOutputOperand(operation, operationOutputIndex, model); - if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots())) - { - return false; - } - - armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex); - - const uint32_t operandIndex = operation.outputs[operationOutputIndex]; - data.m_OutputSlotForOperand[operandIndex] = &outputSlot; - - outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand)); - - return true; + return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data); } -template -bool SetupAndTrackLayerOutputSlot(const HalOperation& operation, - uint32_t outputIndex, - armnn::IConnectableLayer& layer, - const HalModel& model, - ConversionData& data) -{ - return SetupAndTrackLayerOutputSlot(operation, outputIndex, layer, outputIndex, model, data); -} - -template +template bool ConvertPooling2d(const HalOperation& operation, const char* operationName, armnn::PoolingAlgorithm poolType, const HalModel& model, ConversionData& data) { - LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); if (!input.IsValid()) { return Fail("%s: Could not read input 0", operationName); } - const V1_0::Operand* output = GetOutputOperand(operation, 0, model); + const HalOperand* output = GetOutputOperand(operation, 0, model); if (!output) { return Fail("%s: Could not read output 0", __func__); @@ -966,12 +1029,16 @@ bool ConvertPooling2d(const HalOperation& operation, { // one input, 6 parameters (padding, stridex, stridey, width, height, activation type) android::nn::PaddingScheme scheme; - if (!GetInputPaddingScheme(operation, 1, scheme, model, data) - || !GetInputScalar(operation, 2,V1_0::OperandType::INT32, desc.m_StrideX, model, data) - || !GetInputScalar(operation, 3,V1_0::OperandType::INT32, desc.m_StrideY, model, data) - || !GetInputScalar(operation, 4,V1_0::OperandType::INT32, desc.m_PoolWidth, model, data) - || !GetInputScalar(operation, 5,V1_0::OperandType::INT32, desc.m_PoolHeight, model, data) - || !GetInputActivationFunction(operation, 6, activation, model, data)) + if (!GetInputPaddingScheme(operation, 1, scheme, model, data) + || !GetInputScalar(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, + data) + || !GetInputScalar(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, + data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, + model, data) + || !GetInputActivationFunction(operation, 6, activation, model, data)) { return Fail("%s: Operation has invalid inputs", operationName); } @@ -985,15 +1052,23 @@ bool ConvertPooling2d(const HalOperation& operation, else { // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type) - if (!GetInputScalar(operation, 1,V1_0::OperandType::INT32, desc.m_PadLeft, model, data) - || !GetInputScalar(operation, 2,V1_0::OperandType::INT32, desc.m_PadRight, model, data) - || !GetInputScalar(operation, 3,V1_0::OperandType::INT32, desc.m_PadTop, model, data) - || !GetInputScalar(operation, 4,V1_0::OperandType::INT32, desc.m_PadBottom, model, data) - || !GetInputScalar(operation, 5,V1_0::OperandType::INT32, desc.m_StrideX, model, data) - || !GetInputScalar(operation, 6,V1_0::OperandType::INT32, desc.m_StrideY, model, data) - || !GetInputScalar(operation, 7,V1_0::OperandType::INT32, desc.m_PoolWidth, model, data) - || !GetInputScalar(operation, 8,V1_0::OperandType::INT32, desc.m_PoolHeight, model, data) - || !GetInputActivationFunction(operation, 9, activation, model, data)) + if (!GetInputScalar(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, + data) + || !GetInputScalar(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, + data) + || !GetInputScalar(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, + data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, + data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, + data) + || !GetInputScalar(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputScalar(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, + data) + || !GetInputScalar(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, + model, data) + || !GetInputActivationFunction(operation, 9, activation, model, data)) { return Fail("%s: Operation has invalid inputs", operationName); } @@ -1023,7 +1098,276 @@ bool ConvertPooling2d(const HalOperation& operation, input.Connect(pooling2dLayer->GetInputSlot(0)); - return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); +} + +template +bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data) +{ + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + if (!input.IsValid()) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + const HalOperand* output = GetOutputOperand(operation, 0, model); + if (!output) + { + return Fail("%s: Could not read output 0", __func__); + } + + const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); + const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); + + // ArmNN does not currently support non-fixed weights or bias + const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data); + const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); + + if (!weightsPin.IsValid() || !biasPin.IsValid()) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + armnn::ConstTensor weights = weightsPin.GetConstTensor(); + armnn::ConstTensor bias = biasPin.GetConstTensor(); + SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo); + + armnn::Convolution2dDescriptor desc; + desc.m_DataLayout = armnn::DataLayout::NHWC; + ActivationFn activation; + + if (operation.inputs.size() >= 10) + { + if (!GetInputScalar(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, + data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, + data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, + data) + || !GetInputScalar(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, + data) + || !GetInputScalar(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, + data) + || !GetInputScalar(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputActivationFunction(operation, 9, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + desc.m_DataLayout = OptionalDataLayout(operation, 10, model, data); + } + else if (operation.inputs.size() >= 7) + { + android::nn::PaddingScheme paddingScheme; + if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_StrideX, + model, data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputActivationFunction(operation, 6, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + const uint32_t kernelX = weights.GetShape()[2]; + const uint32_t kernelY = weights.GetShape()[1]; + const uint32_t inputX = inputInfo.GetShape()[2]; + const uint32_t inputY = inputInfo.GetShape()[1]; + + CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme); + CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme); + + desc.m_DataLayout = OptionalDataLayout(operation, 7, model, data); + } + else + { + return Fail("%s: Unsupported number of operation inputs", __func__); + } + + desc.m_BiasEnabled = true; + armnn::Optional biases(bias.GetInfo()); + + if (!IsLayerSupportedForAnyBackend(__func__, + armnn::IsConvolution2dSupported, + data.m_Backends, + inputInfo, + outputInfo, + desc, + weights.GetInfo(), + biases)) + { + return false; + } + + armnn::IConnectableLayer* startLayer = + data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional(bias)); + + if (!startLayer) + { + return Fail("%s: AddConvolution2dLayer failed", __func__); + } + + armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data); + + if (!endLayer) + { + return Fail("%s: ProcessActivation failed", __func__); + } + + input.Connect(startLayer->GetInputSlot(0)); + + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); +} + +template +bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data) +{ + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + + if (!input.IsValid()) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + const HalOperand* output = GetOutputOperand(operation, 0, model); + + if (!output) + { + return Fail("%s: Could not read output 0", __func__); + } + + const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); + const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); + + // ArmNN does not currently support non-fixed weights or bias + + // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ] + const HalOperand* weightsOperand = GetInputOperand(operation, 1, model); + + if (weightsOperand == nullptr) + { + return Fail("%s: Operand is invalid", __func__); + } + armnn::DepthwiseConvolution2dDescriptor desc; + desc.m_DataLayout = armnn::DataLayout::NHWC; + + // Look ahead to find the optional DataLayout, if present + if (operation.inputs.size() >= 12) + { + desc.m_DataLayout = OptionalDataLayout(operation, 11, model, data); + } + else if (operation.inputs.size() >= 9) + { + desc.m_DataLayout = OptionalDataLayout(operation, 8, model, data); + } + + armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout); + unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex(); + unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex(); + unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex(); + + // Reinterpret weight data as [ H, W, I, M ] + armnn::TensorShape weightsShape({ weightsOperand->dimensions[1], + weightsOperand->dimensions[2], + inputInfo.GetShape()[channelsIndex], + weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] }); + + // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ] + const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U }; + + const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin(operation, 1, model, data, + HWIMToMIHW, &weightsShape); + + // Bias is a 1D tensor + const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin(operation, 2, model, data); + + if (!weightsPin.IsValid() || !biasPin.IsValid()) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + armnn::ConstTensor weights = weightsPin.GetConstTensor(); + armnn::ConstTensor bias = biasPin.GetConstTensor(); + SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo); + + ActivationFn activation; + + if (operation.inputs.size() >= 11) + { + if (!GetInputScalar(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, + data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, + data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, + data) + || !GetInputScalar(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, + data) + || !GetInputScalar(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, + data) + || !GetInputScalar(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputActivationFunction(operation, 10, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + } + else if (operation.inputs.size() >= 8) + { + android::nn::PaddingScheme paddingScheme; + if (!GetInputPaddingScheme(operation, 3, paddingScheme, model, data) + || !GetInputScalar(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, + data) + || !GetInputScalar(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, + data) + || !GetInputActivationFunction(operation, 7, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + const uint32_t kernelX = weights.GetShape()[3]; + const uint32_t kernelY = weights.GetShape()[2]; + const uint32_t inputX = inputInfo.GetShape()[widthIndex]; + const uint32_t inputY = inputInfo.GetShape()[heightIndex]; + + CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme); + CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme); + } + else + { + return Fail("%s: Unsupported number of operation inputs", __func__); + } + + desc.m_BiasEnabled = true; + armnn::Optional biases(bias.GetInfo()); + + if (!IsLayerSupportedForAnyBackend(__func__, + armnn::IsDepthwiseConvolutionSupported, + data.m_Backends, + inputInfo, + outputInfo, + desc, + weights.GetInfo(), + biases)) + { + return false; + } + + armnn::IConnectableLayer* startLayer = + data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional(bias)); + if (!startLayer) + { + return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__); + } + + armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data); + if (!endLayer) + { + return Fail("%s: ProcessActivation failed", __func__); + } + + input.Connect(startLayer->GetInputSlot(0)); + + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); } } // namespace armnn_driver diff --git a/ModelToINetworkConverter.cpp b/ModelToINetworkConverter.cpp index 2a79a279..96a65604 100644 --- a/ModelToINetworkConverter.cpp +++ b/ModelToINetworkConverter.cpp @@ -37,6 +37,8 @@ template void ModelToINetworkConverter::Convert() { using HalModel = typename HalPolicy::Model; + using Operand = typename HalPolicy::Operand; + using OperandType = typename HalPolicy::OperationType; ALOGV("ModelToINetworkConverter::Convert(): %s", GetModelSummary(m_Model).c_str()); @@ -68,7 +70,7 @@ void ModelToINetworkConverter::Convert() { // inputs in android nn are represented by operands uint32_t inputIndex = m_Model.inputIndexes[i]; - const V1_0::Operand& operand = m_Model.operands[inputIndex]; + const Operand& operand = m_Model.operands[inputIndex]; const armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand); armnn::IConnectableLayer* layer = m_Data.m_Network->AddInputLayer(i); @@ -79,7 +81,7 @@ void ModelToINetworkConverter::Convert() m_Data.m_OutputSlotForOperand[inputIndex] = &outputSlot; } } - catch (UnsupportedOperand& e) + catch (UnsupportedOperand& e) { Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str()); m_ConversionResult = ConversionResult::UnsupportedFeature; @@ -107,7 +109,7 @@ void ModelToINetworkConverter::Convert() { ok = HalPolicy::ConvertOperation(operation, m_Model, m_Data); } - catch (UnsupportedOperand& e) + catch (UnsupportedOperand& e) { Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str()); ok = false; @@ -137,7 +139,7 @@ void ModelToINetworkConverter::Convert() { // outputs in android nn are represented by operands uint32_t outputIndex = m_Model.outputIndexes[i]; - const V1_0::Operand& operand = m_Model.operands[outputIndex]; + const Operand& operand = m_Model.operands[outputIndex]; const armnn::TensorInfo& tensor = GetTensorInfoForOperand(operand); armnn::IConnectableLayer* layer = m_Data.m_Network->AddOutputLayer(i); @@ -171,4 +173,9 @@ template class ModelToINetworkConverter; template class ModelToINetworkConverter; #endif +#ifdef ARMNN_ANDROID_NN_V1_2 +template class ModelToINetworkConverter; +template class ModelToINetworkConverter; +#endif + } // armnn_driver diff --git a/RequestThread.cpp b/RequestThread.cpp index bc1eccc0..4b646034 100644 --- a/RequestThread.cpp +++ b/RequestThread.cpp @@ -8,6 +8,10 @@ #include "RequestThread.hpp" #include "ArmnnPreparedModel.hpp" +#ifdef ARMNN_ANDROID_NN_V1_2 +#include "ArmnnPreparedModel_1_2.hpp" +#endif + #include #include @@ -17,15 +21,15 @@ using namespace android; namespace armnn_driver { -template -RequestThread::RequestThread() +template