aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Kelly <mike.kelly@arm.com>2019-06-11 16:35:25 +0100
committerMike Kelly <mike.kelly@arm.com>2019-06-11 16:35:25 +0100
commitb5fdf38f0c6596958fab2b84882f2792a31e585a (patch)
treed6b578b51c1923c759653d8a04efa90923ad4dd8
parentb92f8901fc34749337ea7a9ad7a2717fc9490de5 (diff)
downloadandroid-nn-driver-b5fdf38f0c6596958fab2b84882f2792a31e585a.tar.gz
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 <sadik.armagan@arm.com> Signed-off-by: Mike Kelly <mike.kelly@arm.com> Change-Id: I62856deab24e106f72cccce09468db4971756fa6
-rw-r--r--1.0/HalPolicy.cpp455
-rw-r--r--1.0/HalPolicy.hpp8
-rw-r--r--1.1/HalPolicy.cpp110
-rw-r--r--1.2/ArmnnDriver.hpp208
-rw-r--r--1.2/ArmnnDriverImpl.cpp206
-rw-r--r--1.2/ArmnnDriverImpl.hpp34
-rw-r--r--1.2/HalPolicy.cpp144
-rw-r--r--1.2/HalPolicy.hpp31
-rw-r--r--Android.mk210
-rw-r--r--ArmnnDriver.hpp22
-rw-r--r--ArmnnDriverImpl.cpp117
-rw-r--r--ArmnnDriverImpl.hpp6
-rw-r--r--ArmnnPreparedModel.cpp8
-rw-r--r--ArmnnPreparedModel.hpp14
-rw-r--r--ArmnnPreparedModel_1_2.cpp486
-rw-r--r--ArmnnPreparedModel_1_2.hpp80
-rw-r--r--ConversionUtils.hpp556
-rw-r--r--ModelToINetworkConverter.cpp15
-rw-r--r--RequestThread.cpp44
-rw-r--r--RequestThread.hpp11
-rw-r--r--Utils.cpp48
-rw-r--r--Utils.hpp18
-rw-r--r--android.hardware.neuralnetworks@1.2-service-armnn.rc4
-rw-r--r--test/GenericLayerTests.cpp2
24 files changed, 2237 insertions, 600 deletions
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<Operand, OperandType, Operation, Model>(operation, model, data);
case V1_0::OperationType::DEPTHWISE_CONV_2D:
- return ConvertDepthwiseConv2d(operation, model, data);
+ return ValidateDepthwiseConv2dParameters(operation)
+ && ConvertDepthwiseConv2d<Operand, OperandType, Operation, Model>(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<Operand>(operation, 0, model, data);
+ LayerInputHandle input1 = ConvertToLayerInputHandle<Operand>(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<Operand, OperandType>(operation, 2, activationFunction, model, data))
{
return Fail("%s: Operation has invalid inputs", __func__);
}
- const Operand* outputOperand = GetOutputOperand(operation, 0, model);
+ const Operand* outputOperand = GetOutputOperand<Operand>(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<Operand>(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<Operand, OperandType>(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<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<armnn::TensorInfo> 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<armnn::ConstTensor>(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<armnn::TensorInfo> 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<armnn::ConstTensor>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 1, model, data); // 2D
+ ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<Operand>(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<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(operation, 1, OperandType::INT32, descriptor.m_NormSize, model, data) ||
+ !GetInputFloat32<Operand, OperandType>(operation, 2, descriptor.m_K, model, data) ||
+ !GetInputFloat32<Operand, OperandType>(operation, 3, descriptor.m_Alpha, model, data) ||
+ !GetInputFloat32<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(operation, 20, activation, model, data) ||
+ !GetInputScalar<Operand, OperandType>(operation, 21, OperandType::FLOAT32, cellClip, model, data) ||
+ !GetInputScalar<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 0, *layer, 0, model, data) &&
+ SetupAndTrackLayerOutputSlot<Operand>(operation, 1, *layer, 1, model, data) &&
+ SetupAndTrackLayerOutputSlot<Operand>(operation, 2, *layer, 2, model, data) &&
+ SetupAndTrackLayerOutputSlot<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(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<Operand, OperandType>(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<Operand>(operation, 0, model, data);
+ LayerInputHandle input1 = ConvertToLayerInputHandle<Operand>(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<Operand, OperandType>(operation, 2, activationFunction, model, data))
{
return Fail("%s: Operation has invalid inputs", __func__);
}
- const Operand* outputOperand = GetOutputOperand(operation, 0, model);
+ const Operand* outputOperand = GetOutputOperand<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(operation, 0, model);
+ const Operand* requestedShapeOperand = GetInputOperand<Operand>(operation, 1, model);
+ const Operand* outputOperand = GetOutputOperand<Operand>(operation, 0, model);
if (inputOperand == nullptr
|| requestedShapeOperand == nullptr
@@ -1305,7 +1098,7 @@ bool HalPolicy::ConvertReshape(const Operation& operation, const Model& model, C
}
std::vector<int32_t> targetDimensions;
- if (!GetTensorInt32Values(*requestedShapeOperand, targetDimensions, model, data))
+ if (!GetTensorInt32Values<Operand, OperandType>(*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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(operation, 1, OperandType::INT32, desc.m_TargetHeight, model, data)
+ || !GetInputScalar<Operand, OperandType>(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<Operand>(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<Operand>(operation, 0, model, data);
+ LayerInputHandle input1 = ConvertToLayerInputHandle<Operand>(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<Operand, OperandType>(operation, 2, activationFunction, model, data))
{
return Fail("%s: Operation has invalid inputs", __func__);
}
- const Operand* outputOperand = GetOutputOperand(operation, 0, model);
+ const Operand* outputOperand = GetOutputOperand<Operand>(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<Operand>(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<Operand>(operation, 0, model, data);
+ LayerInputHandle input1 = ConvertToLayerInputHandle<Operand>(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<Operand, OperandType>(operation, 2, activationFunction, model, data))
{
return Fail("%s: Operation has invalid inputs", __func__);
}
- const Operand* outputOperand = GetOutputOperand(operation, 0, model);
+ const Operand* outputOperand = GetOutputOperand<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 1, model);
if (!axisOperand)
{
return Fail("%s: Could not read input 1", __func__);
}
std::vector<int32_t> axis;
- if (!GetTensorInt32Values(*axisOperand, axis, model, data))
+ if (!GetTensorInt32Values<Operand, OperandType>(*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<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<HalPolicy::Operand>(*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<int32_t> paddings;
- GetTensorInt32Values(*paddingsOperand, paddings, model, data);
+ GetTensorInt32Values<Operand, OperandType>(*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<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 1, model);
+ const Operand* paddingsOperand = GetInputOperand<Operand>(operation, 2, model);
- armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand(*blockShapeOperand);
+ armnn::TensorShape blockShapeOperandShape = GetTensorShapeForOperand<Operand>(*blockShapeOperand);
if (blockShapeOperandShape.GetNumDimensions() != 1 || blockShapeOperandShape.GetNumElements() != spatialDim)
{
return Fail("%s: Operation has invalid block shape operand: expected shape [%d]", __func__, spatialDim);
}
std::vector<int32_t> blockShape;
- GetTensorInt32Values(*blockShapeOperand, blockShape, model, data);
+ GetTensorInt32Values<Operand, OperandType>(*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<std::pair<unsigned int, unsigned int>> paddingList;
std::vector<int32_t> paddings;
- GetTensorInt32Values(*paddingsOperand, paddings, model, data);
+ GetTensorInt32Values<Operand, OperandType>(*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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<Operand, OperandType>(*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<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 1, model);
+ const Operand* endOperand = GetInputOperand<Operand>(operation, 2, model);
+ const Operand* stridesOperand = GetInputOperand<Operand>(operation, 3, model);
std::vector<int32_t> beginValues;
std::vector<int32_t> 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<int32_t>& operandValues)
{
- if (!GetTensorInt32Values(operand, operandValues, model, data))
+ if (!GetTensorInt32Values<Operand, OperandType>(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<Operand, OperandType>(operation, 4, descriptor.m_BeginMask, model, data)
+ || !GetInputInt32<Operand, OperandType>(operation, 5, descriptor.m_EndMask, model, data)
+ || !GetInputInt32<Operand, OperandType>(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<Operand>(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<Operand>(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<Operand>(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<Operand>(operation, 1, model, false);
std::vector<int32_t> perm(rank);
if (!permOperand)
@@ -616,7 +616,7 @@ bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model,
}
else
{
- GetTensorInt32Values(*permOperand, perm, model, data);
+ GetTensorInt32Values<Operand, OperandType>(*permOperand, perm, model, data);
}
std::vector<uint32_t> 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<Operand>(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<Operand>(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<Operand>(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<Operand>(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<int32_t> block;
- if (!GetTensorInt32Values(*blockOperand, block, model, data))
+ if (!GetTensorInt32Values<Operand, OperandType>(*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<Operand>(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<Operand>(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 <HalInterfaces.h>
+
+#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 <log/log.h>
+
+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<uint8_t, ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN>;
+
+public:
+ Return<void> getCapabilities(V1_0::IDevice::getCapabilities_cb cb) override
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getCapabilities()");
+
+ return hal_1_0::ArmnnDriverImpl::getCapabilities(m_Runtime, cb);
+ }
+
+ Return<void> getSupportedOperations(const V1_0::Model& model,
+ V1_0::IDevice::getSupportedOperations_cb cb) override
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()");
+
+ return armnn_driver::ArmnnDriverImpl<hal_1_0::HalPolicy>::getSupportedOperations(m_Runtime,
+ m_Options,
+ model,
+ cb);
+ }
+
+ Return<ErrorStatus> prepareModel(const V1_0::Model& model,
+ const android::sp<V1_0::IPreparedModelCallback>& cb) override
+ {
+ ALOGV("hal_1_2::ArmnnDriver::prepareModel()");
+
+ return armnn_driver::ArmnnDriverImpl<hal_1_0::HalPolicy>::prepareModel(m_Runtime,
+ m_ClTunedParameters,
+ m_Options,
+ model,
+ cb);
+ }
+
+ Return<void> 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<void> 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<hal_1_1::HalPolicy>::getSupportedOperations(m_Runtime,
+ m_Options,
+ model,
+ cb);
+ }
+
+ Return<ErrorStatus> prepareModel_1_1(const V1_1::Model& model,
+ V1_1::ExecutionPreference preference,
+ const android::sp<V1_0::IPreparedModelCallback>& 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<hal_1_1::HalPolicy>::prepareModel(m_Runtime,
+ m_ClTunedParameters,
+ m_Options,
+ model,
+ cb,
+ model.relaxComputationFloat32toFloat16
+ && m_Options.GetFp16Enabled());
+ }
+
+ Return<DeviceStatus> getStatus() override
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getStatus()");
+
+ return armnn_driver::ArmnnDriverImpl<hal_1_2::HalPolicy>::getStatus();
+ }
+
+ Return<void> getVersionString(getVersionString_cb cb)
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()");
+
+ cb(ErrorStatus::NONE, "ArmNN");
+ return Void();
+ }
+
+ Return<void> getType(getType_cb cb)
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getType()");
+
+ cb(ErrorStatus::NONE, V1_2::DeviceType::CPU);
+ return Void();
+ }
+
+ Return<ErrorStatus> prepareModelFromCache(
+ const android::hardware::hidl_vec<android::hardware::hidl_handle>&,
+ const android::hardware::hidl_vec<android::hardware::hidl_handle>&,
+ const HidlToken&,
+ const sp<V1_2::IPreparedModelCallback>& callback)
+ {
+ ALOGV("hal_1_2::ArmnnDriver::prepareModelFromCache()");
+ callback->notify_1_2(ErrorStatus::GENERAL_FAILURE, nullptr);
+ return ErrorStatus::GENERAL_FAILURE;
+ }
+
+ Return<ErrorStatus> prepareModel_1_2(const V1_2::Model& model, V1_1::ExecutionPreference preference,
+ const android::hardware::hidl_vec<android::hardware::hidl_handle>&,
+ const android::hardware::hidl_vec<android::hardware::hidl_handle>&, const HidlToken&,
+ const android::sp<V1_2::IPreparedModelCallback>& 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<void> getSupportedExtensions(getSupportedExtensions_cb cb)
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getSupportedExtensions()");
+ cb(ErrorStatus::NONE, {/* No extensions. */});
+ return Void();
+ }
+
+ Return<void> 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<void> getSupportedOperations_1_2(const V1_2::Model& model,
+ getSupportedOperations_1_2_cb cb)
+ {
+ ALOGV("hal_1_2::ArmnnDriver::getSupportedOperations()");
+
+ return armnn_driver::ArmnnDriverImpl<hal_1_2::HalPolicy>::getSupportedOperations(m_Runtime,
+ m_Options,
+ model,
+ cb);
+ }
+
+ Return<void> 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 <log/log.h>
+
+namespace
+{
+
+const char *g_RelaxedFloat32toFloat16PerformanceExecTime = "ArmNN.relaxedFloat32toFloat16Performance.execTime";
+void NotifyCallbackAndCheck(const sp<V1_2::IPreparedModelCallback>& callback,
+ ErrorStatus errorStatus,
+ const sp<V1_2::IPreparedModel>& preparedModelPtr)
+{
+ Return<void> 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<ErrorStatus> FailPrepareModel(ErrorStatus error,
+ const std::string& message,
+ const sp<V1_2::IPreparedModelCallback>& callback)
+{
+ ALOGW("ArmnnDriverImpl::prepareModel: %s", message.c_str());
+ NotifyCallbackAndCheck(callback, error, nullptr);
+ return error;
+}
+
+} // anonymous namespace
+
+namespace armnn_driver
+{
+namespace hal_1_2
+{
+
+Return<ErrorStatus> ArmnnDriverImpl::prepareArmnnModel_1_2(const armnn::IRuntimePtr& runtime,
+ const armnn::IGpuAccTunedParametersPtr& clTunedParameters,
+ const DriverOptions& options,
+ const V1_2::Model& model,
+ const sp<V1_2::IPreparedModelCallback>& 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<unsigned int> unsupportedOperations;
+ ModelToINetworkConverter<HalPolicy> 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<std::string> 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<hal_1_2::HalPolicy::Model>(*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<ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>> preparedModel(
+ new ArmnnPreparedModel_1_2<hal_1_2::HalPolicy>(
+ 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<void> 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 <HalInterfaces.h>
+
+#include "../DriverOptions.hpp"
+
+#include <armnn/ArmNN.hpp>
+
+namespace armnn_driver
+{
+namespace hal_1_2
+{
+
+class ArmnnDriverImpl
+{
+public:
+ static Return<ErrorStatus> prepareArmnnModel_1_2(const armnn::IRuntimePtr& runtime,
+ const armnn::IGpuAccTunedParametersPtr& clTunedParameters,
+ const DriverOptions& options,
+ const V1_2::Model& model,
+ const android::sp<V1_2::IPreparedModelCallback>& cb,
+ bool float32ToFloat16 = false);
+
+ static Return<void> 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<V1_0::OperationType>(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<V1_1::OperationType>(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<V1_0::OperationType>(type);
+}
+
+V1_1::OperationType CastToV1_1(V1_2::OperationType type)
+{
+ return static_cast<V1_1::OperationType>(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<Operand, OperandType, Operation, Model>(operation, model, data);
+ case V1_2::OperationType::DEPTHWISE_CONV_2D:
+ return ConvertDepthwiseConv2d<Operand, OperandType, Operation, Model>(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 <HalInterfaces.h>
+
+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 <log/log.h>
-#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 <ValidateHal.h>
+#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 <ValidateHal.h>
#include <log/log.h>
using namespace std;
@@ -35,7 +34,7 @@ void NotifyCallbackAndCheck(const sp<V1_0::IPreparedModelCallback>& callback,
if (!returned.isOk())
{
ALOGE("ArmnnDriverImpl::prepareModel: hidl callback failed to return properly: %s ",
- returned.description().c_str());
+ returned.description().c_str());
}
}
@@ -48,60 +47,13 @@ Return<ErrorStatus> FailPrepareModel(ErrorStatus error,
return error;
}
+
} // namespace
namespace armnn_driver
{
template<typename HalPolicy>
-Return<void> ArmnnDriverImpl<HalPolicy>::getSupportedOperations(const armnn::IRuntimePtr& runtime,
- const DriverOptions& options,
- const HalModel& model,
- HalGetSupportedOperations_cb cb)
-{
- ALOGV("ArmnnDriverImpl::getSupportedOperations()");
-
- vector<bool> 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<HalPolicy> 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<bool>) 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<typename HalPolicy>
Return<ErrorStatus> ArmnnDriverImpl<HalPolicy>::prepareModel(
const armnn::IRuntimePtr& runtime,
const armnn::IGpuAccTunedParametersPtr& clTunedParameters,
@@ -199,7 +151,7 @@ Return<ErrorStatus> ArmnnDriverImpl<HalPolicy>::prepareModel(
}
unique_ptr<ArmnnPreparedModel<HalPolicy>> preparedModel(
- new ArmnnPreparedModel<HalPolicy>(
+ new ArmnnPreparedModel<HalPolicy>(
netId,
runtime.get(),
model,
@@ -234,6 +186,54 @@ Return<ErrorStatus> ArmnnDriverImpl<HalPolicy>::prepareModel(
}
template<typename HalPolicy>
+Return<void> ArmnnDriverImpl<HalPolicy>::getSupportedOperations(const armnn::IRuntimePtr& runtime,
+ const DriverOptions& options,
+ const HalModel& model,
+ HalGetSupportedOperations_cb cb)
+{
+ ALOGV("ArmnnDriverImpl::getSupportedOperations()");
+
+ vector<bool> 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<HalPolicy> 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<bool>) 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<typename HalPolicy>
Return<DeviceStatus> ArmnnDriverImpl<HalPolicy>::getStatus()
{
ALOGV("ArmnnDriver::getStatus()");
@@ -251,4 +251,9 @@ template class ArmnnDriverImpl<hal_1_0::HalPolicy>;
template class ArmnnDriverImpl<hal_1_1::HalPolicy>;
#endif
+#ifdef ARMNN_ANDROID_NN_V1_2
+template class ArmnnDriverImpl<hal_1_1::HalPolicy>;
+template class ArmnnDriverImpl<hal_1_2::HalPolicy>;
+#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 <HalInterfaces.h>
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<DeviceStatus> 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 <log/log.h>
#include <OperationsUtils.h>
-#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 <ValidateHal.h>
@@ -89,7 +89,7 @@ namespace armnn_driver
{
template<typename HalVersion>
-RequestThread<HalVersion> ArmnnPreparedModel<HalVersion>::m_RequestThread;
+RequestThread<ArmnnPreparedModel, HalVersion> ArmnnPreparedModel<HalVersion>::m_RequestThread;
template<typename HalVersion>
template <typename TensorBindingCollection>
@@ -318,4 +318,8 @@ template class ArmnnPreparedModel<hal_1_0::HalPolicy>;
template class ArmnnPreparedModel<hal_1_1::HalPolicy>;
#endif
+#ifdef ARMNN_ANDROID_NN_V1_2
+template class ArmnnPreparedModel<hal_1_1::HalPolicy>;
+template class ArmnnPreparedModel<hal_1_2::HalPolicy>;
+#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 <typename TensorBindingCollection>
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<HalVersion> m_RequestThread;
- uint32_t m_RequestCount;
- const std::string& m_RequestInputsAndOutputsDumpDir;
- const bool m_GpuProfilingEnabled;
+ static RequestThread<ArmnnPreparedModel, HalVersion> 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 <boost/format.hpp>
+#include <log/log.h>
+#include <OperationsUtils.h>
+#include <ExecutionBurstServer.h>
+#include <ValidateHal.h>
+
+#include <cassert>
+#include <cinttypes>
+
+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<V1_0::IExecutionCallback>& callback, ErrorStatus errorStatus,
+ std::string callingFunction)
+{
+ Return<void> 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<V1_2::IExecutionCallback>& callback, ErrorStatus errorStatus,
+ std::string callingFunction)
+{
+ Return<void> 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<typename HalVersion>
+RequestThread<ArmnnPreparedModel_1_2, HalVersion> ArmnnPreparedModel_1_2<HalVersion>::m_RequestThread;
+
+template<typename HalVersion>
+template<typename TensorBindingCollection>
+void ArmnnPreparedModel_1_2<HalVersion>::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<typename HalVersion>
+ArmnnPreparedModel_1_2<HalVersion>::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<typename HalVersion>
+ArmnnPreparedModel_1_2<HalVersion>::~ArmnnPreparedModel_1_2()
+{
+ // Get a hold of the profiler used by this model.
+ std::shared_ptr<armnn::IProfiler> 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<typename HalVersion>
+Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute(const Request& request,
+ const ::android::sp<V1_0::IExecutionCallback>& callback)
+{
+ return Execute<V1_0::IExecutionCallback>(request, callback);
+}
+
+template<typename HalVersion>
+Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::execute_1_2(const Request& request,
+ MeasureTiming,
+ const sp<V1_2::IExecutionCallback>& callback)
+{
+ return Execute<V1_2::IExecutionCallback>(request, callback);
+}
+
+template<typename HalVersion>
+Return<void> ArmnnPreparedModel_1_2<HalVersion>::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<armnn::InputTensors>();
+ auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
+
+ // 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<std::vector<android::nn::RunTimePoolInfo>>();
+
+ 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<typename HalVersion>
+Return<void> ArmnnPreparedModel_1_2<HalVersion>::configureExecutionBurst(
+ const sp<V1_2::IBurstCallback>& callback,
+ const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+ V1_2::IPreparedModel::configureExecutionBurst_cb cb)
+{
+ ALOGV("ArmnnPreparedModel_1_2::configureExecutionBurst");
+ const sp<V1_2::IBurstContext> burst =
+ ExecutionBurstServer::create(callback, requestChannel, resultChannel, this);
+
+ if (burst == nullptr) {
+ cb(ErrorStatus::GENERAL_FAILURE, {});
+ } else {
+ cb(ErrorStatus::NONE, burst);
+ }
+ return Void();
+}
+
+template<typename HalVersion>
+void ArmnnPreparedModel_1_2<HalVersion>::ExecuteGraph(
+ std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
+ std::shared_ptr<armnn::InputTensors>& pInputTensors,
+ std::shared_ptr<armnn::OutputTensors>& pOutputTensors,
+ const ::android::sp<V1_0::IExecutionCallback>& 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<typename HalVersion>
+bool ArmnnPreparedModel_1_2<HalVersion>::ExecuteWithDummyInputs()
+{
+ std::vector<std::vector<char>> 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<typename HalVersion>
+template<typename ExecutionCallback>
+Return <ErrorStatus> ArmnnPreparedModel_1_2<HalVersion>::Execute(const Request& request,
+ const sp<ExecutionCallback>& 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<std::uintptr_t>(callback.get()));
+ }
+
+ // allocate the tensors on the heap, as they are passed to the request thread
+ auto pInputTensors = std::make_shared<armnn::InputTensors>();
+ auto pOutputTensors = std::make_shared<armnn::OutputTensors>();
+
+ // 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<std::vector<android::nn::RunTimePoolInfo>>();
+
+ 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<hal_1_2::HalPolicy>;
+#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 <NeuralNetworks.h>
+#include <armnn/ArmNN.hpp>
+
+#include <string>
+#include <vector>
+
+namespace armnn_driver
+{
+
+template <typename HalVersion>
+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<ErrorStatus> execute(const Request& request,
+ const ::android::sp<V1_0::IExecutionCallback>& callback) override;
+
+ virtual Return<ErrorStatus> execute_1_2(const Request& request, MeasureTiming measure,
+ const sp<V1_2::IExecutionCallback>& callback) override;
+
+ virtual Return<void> executeSynchronously(const Request &request,
+ MeasureTiming measure,
+ V1_2::IPreparedModel::executeSynchronously_cb cb) override;
+
+ virtual Return<void> configureExecutionBurst(
+ const sp<V1_2::IBurstCallback>& callback,
+ const android::hardware::MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
+ const android::hardware::MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
+ configureExecutionBurst_cb cb) override;
+
+ /// execute the graph prepared from the request
+ void ExecuteGraph(std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& pMemPools,
+ std::shared_ptr<armnn::InputTensors>& pInputTensors,
+ std::shared_ptr<armnn::OutputTensors>& pOutputTensors,
+ const ::android::sp<V1_0::IExecutionCallback>& callback);
+
+ /// Executes this model with dummy inputs (e.g. all zeroes).
+ /// \return false on failure, otherwise true
+ bool ExecuteWithDummyInputs();
+
+private:
+ template <typename ExecutionCallback>
+ Return <ErrorStatus> Execute(const Request &request, const sp <ExecutionCallback> &callback);
+
+ template <typename TensorBindingCollection>
+ 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<ArmnnPreparedModel_1_2, HalVersion> 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 <armnn/ArmNN.hpp>
+#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<typename Operand>
+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<typename HalOperation, typename HalModel>
-const V1_0::Operand* GetInputOperand(const HalOperation& operation, uint32_t inputIndex, const HalModel& model,
- bool failOnIndexOutOfBounds = true)
+template<typename HalOperand, typename HalOperation, typename HalModel>
+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<typename HalOperation, typename HalModel>
-const V1_0::Operand* GetOutputOperand(const HalOperation& operation, uint32_t outputIndex, const HalModel& model)
+template<typename HalOperand, typename HalOperation, typename HalModel>
+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<typename HalModel>
-ConstTensorPin ConvertOperandToConstTensorPin(const V1_0::Operand& operand,
+template<typename HalOperand, typename HalModel>
+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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperation, typename HalModel>
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<HalOperand>(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<typename HalModel>
-const void* GetOperandValueReadOnlyAddress(const V1_0::Operand& operand,
+template<typename HalOperand, typename HalModel>
+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<typename HalOperation, typename HalModel, typename OutputType>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel, typename OutputType>
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<HalOperand>(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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(operation, inputIndex, HalOperandType::INT32, outValue, model,
+ data);
}
-
-template<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(operation, inputIndex, HalOperandType::FLOAT32, outValue, model,
+ data);
}
-
-template<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(operation,
+ inputIndex,
+ HalOperandType::INT32,
+ outActivationFunction,
+ model,
+ data);
}
-template<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(operation,
+ inputIndex,
+ HalOperandType::INT32,
+ outActivationFunction,
+ model,
+ data);
}
-template<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(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<typename HalModel>
-bool GetTensorInt32Values(const V1_0::Operand& operand,
+template<typename HalOperand, typename HalOperandType, typename HalModel>
+bool GetTensorInt32Values(const HalOperand& operand,
std::vector<int32_t>& 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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand, HalOperandType>(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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperation, typename HalModel>
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<HalOperand>(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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperation, typename HalModel>
+bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
+ uint32_t operationOutputIndex,
+ armnn::IConnectableLayer& layer,
+ uint32_t layerOutputIndex,
+ const HalModel& model,
+ ConversionData& data)
+{
+ const HalOperand* outputOperand = GetOutputOperand<HalOperand>(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<typename HalOperand, typename HalOperation, typename HalModel>
+armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
+ uint32_t inputIndex,
+ const HalModel& model,
+ ConversionData& data)
+{
+ const HalOperand* operand = GetInputOperand<HalOperand>(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<const bool*>(valueAddress)))
+ {
+ return armnn::DataLayout::NCHW;
+ }
+ else
+ {
+ return armnn::DataLayout::NHWC;
+ }
+}
+
+template<typename HalOperand, typename HalOperation, typename HalModel>
+bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
+ uint32_t outputIndex,
+ armnn::IConnectableLayer& layer,
+ const HalModel& model,
+ ConversionData& data)
+{
+ return SetupAndTrackLayerOutputSlot<HalOperand>(operation, outputIndex, layer, outputIndex, model, data);
+}
+
+template<typename HalOperand, typename HalOperation, typename HalModel>
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<HalOperand>(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<HalOperand>(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<typename HalOperation, typename HalModel>
-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<HalOperand>(operation, 0, *layer, model, data);
}
-template<typename HalOperation, typename HalModel>
-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<typename HalOperation, typename HalModel>
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
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<HalOperand>(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<HalOperand>(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<HalOperand, HalOperandType>(operation, 1, scheme, model, data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight,
+ model, data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(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<HalOperand, HalOperandType>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight,
+ model, data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(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<HalOperand>(operation, 0, *endLayer, model, data);
+}
+
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
+bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
+{
+ LayerInputHandle input = ConvertToLayerInputHandle<HalOperand>(operation, 0, model, data);
+ if (!input.IsValid())
+ {
+ return Fail("%s: Operation has invalid inputs", __func__);
+ }
+
+ const HalOperand* output = GetOutputOperand<HalOperand>(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<HalOperand>(operation, 1, model, data);
+ const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalOperand>(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<HalOperand, HalOperandType>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(operation, 9, activation, model, data))
+ {
+ return Fail("%s: Operation has invalid inputs", __func__);
+ }
+ desc.m_DataLayout = OptionalDataLayout<HalOperand>(operation, 10, model, data);
+ }
+ else if (operation.inputs.size() >= 7)
+ {
+ android::nn::PaddingScheme paddingScheme;
+ if (!GetInputPaddingScheme<HalOperand, HalOperandType>(operation, 3, paddingScheme, model, data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_StrideX,
+ model, data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(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<HalOperand>(operation, 7, model, data);
+ }
+ else
+ {
+ return Fail("%s: Unsupported number of operation inputs", __func__);
+ }
+
+ desc.m_BiasEnabled = true;
+ armnn::Optional<armnn::TensorInfo> 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<armnn::ConstTensor>(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<HalOperand>(operation, 0, *endLayer, model, data);
+}
+
+template<typename HalOperand, typename HalOperandType, typename HalOperation, typename HalModel>
+bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
+{
+ LayerInputHandle input = ConvertToLayerInputHandle<HalOperand>(operation, 0, model, data);
+
+ if (!input.IsValid())
+ {
+ return Fail("%s: Operation has invalid inputs", __func__);
+ }
+
+ const HalOperand* output = GetOutputOperand<HalOperand>(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<HalOperand>(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<HalOperand>(operation, 11, model, data);
+ }
+ else if (operation.inputs.size() >= 9)
+ {
+ desc.m_DataLayout = OptionalDataLayout<HalOperand>(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<HalOperand>(operation, 1, model, data,
+ HWIMToMIHW, &weightsShape);
+
+ // Bias is a 1D tensor
+ const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalOperand>(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<HalOperand, HalOperandType>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(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<HalOperand, HalOperandType>(operation, 3, paddingScheme, model, data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model,
+ data)
+ || !GetInputScalar<HalOperand, HalOperandType>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model,
+ data)
+ || !GetInputActivationFunction<HalOperand, HalOperandType>(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<armnn::TensorInfo> 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<armnn::ConstTensor>(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<HalOperand>(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<typename HalPolicy>
void ModelToINetworkConverter<HalPolicy>::Convert()
{
using HalModel = typename HalPolicy::Model;
+ using Operand = typename HalPolicy::Operand;
+ using OperandType = typename HalPolicy::OperationType;
ALOGV("ModelToINetworkConverter::Convert(): %s", GetModelSummary<HalModel>(m_Model).c_str());
@@ -68,7 +70,7 @@ void ModelToINetworkConverter<HalPolicy>::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<HalPolicy>::Convert()
m_Data.m_OutputSlotForOperand[inputIndex] = &outputSlot;
}
}
- catch (UnsupportedOperand& e)
+ catch (UnsupportedOperand<OperandType>& 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<HalPolicy>::Convert()
{
ok = HalPolicy::ConvertOperation(operation, m_Model, m_Data);
}
- catch (UnsupportedOperand& e)
+ catch (UnsupportedOperand<OperandType>& 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<HalPolicy>::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<hal_1_0::HalPolicy>;
template class ModelToINetworkConverter<hal_1_1::HalPolicy>;
#endif
+#ifdef ARMNN_ANDROID_NN_V1_2
+template class ModelToINetworkConverter<hal_1_1::HalPolicy>;
+template class ModelToINetworkConverter<hal_1_2::HalPolicy>;
+#endif
+
} // 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 <boost/assert.hpp>
#include <log/log.h>
@@ -17,15 +21,15 @@ using namespace android;
namespace armnn_driver
{
-template<typename HalVersion>
-RequestThread<HalVersion>::RequestThread()
+template <template <typename HalVersion> class PreparedModel, typename HalVersion>
+RequestThread<PreparedModel, HalVersion>::RequestThread()
{
ALOGV("RequestThread::RequestThread()");
m_Thread = std::make_unique<std::thread>(&RequestThread::Process, this);
}
-template<typename HalVersion>
-RequestThread<HalVersion>::~RequestThread()
+template <template <typename HalVersion> class PreparedModel, typename HalVersion>
+RequestThread<PreparedModel, HalVersion>::~RequestThread()
{
ALOGV("RequestThread::~RequestThread()");
@@ -50,12 +54,12 @@ RequestThread<HalVersion>::~RequestThread()
catch (const std::exception&) { } // Swallow any exception.
}
-template<typename HalVersion>
-void RequestThread<HalVersion>::PostMsg(ArmnnPreparedModel<HalVersion>* model,
- std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& memPools,
- std::shared_ptr<armnn::InputTensors>& inputTensors,
- std::shared_ptr<armnn::OutputTensors>& outputTensors,
- const ::android::sp<V1_0::IExecutionCallback>& callback)
+template <template <typename HalVersion> class PreparedModel, typename HalVersion>
+void RequestThread<PreparedModel, HalVersion>::PostMsg(PreparedModel<HalVersion>* model,
+ std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& memPools,
+ std::shared_ptr<armnn::InputTensors>& inputTensors,
+ std::shared_ptr<armnn::OutputTensors>& outputTensors,
+ const ::android::sp<V1_0::IExecutionCallback>& callback)
{
ALOGV("RequestThread::PostMsg(...)");
auto data = std::make_shared<AsyncExecuteData>(model,
@@ -67,8 +71,8 @@ void RequestThread<HalVersion>::PostMsg(ArmnnPreparedModel<HalVersion>* model,
PostMsg(pMsg);
}
-template<typename HalVersion>
-void RequestThread<HalVersion>::PostMsg(std::shared_ptr<ThreadMsg>& pMsg)
+template <template <typename HalVersion> class PreparedModel, typename HalVersion>
+void RequestThread<PreparedModel, HalVersion>::PostMsg(std::shared_ptr<ThreadMsg>& pMsg)
{
ALOGV("RequestThread::PostMsg(pMsg)");
// Add a message to the queue and notify the request thread
@@ -77,8 +81,8 @@ void RequestThread<HalVersion>::PostMsg(std::shared_ptr<ThreadMsg>& pMsg)
m_Cv.notify_one();
}
-template<typename HalVersion>
-void RequestThread<HalVersion>::Process()
+template <template <typename HalVersion> class PreparedModel, typename HalVersion>
+void RequestThread<PreparedModel, HalVersion>::Process()
{
ALOGV("RequestThread::Process()");
while (true)
@@ -103,7 +107,7 @@ void RequestThread<HalVersion>::Process()
{
ALOGV("RequestThread::Process() - request");
// invoke the asynchronous execution method
- ArmnnPreparedModel<HalVersion>* model = pMsg->data->m_Model;
+ PreparedModel<HalVersion>* model = pMsg->data->m_Model;
model->ExecuteGraph(pMsg->data->m_MemPools,
pMsg->data->m_InputTensors,
pMsg->data->m_OutputTensors,
@@ -135,10 +139,16 @@ void RequestThread<HalVersion>::Process()
/// Class template specializations
///
-template class RequestThread<hal_1_0::HalPolicy>;
+template class RequestThread<ArmnnPreparedModel, hal_1_0::HalPolicy>;
#ifdef ARMNN_ANDROID_NN_V1_1
-template class RequestThread<hal_1_1::HalPolicy>;
+template class RequestThread<armnn_driver::ArmnnPreparedModel, hal_1_1::HalPolicy>;
+#endif
+
+#ifdef ARMNN_ANDROID_NN_V1_2
+template class RequestThread<ArmnnPreparedModel, hal_1_1::HalPolicy>;
+template class RequestThread<ArmnnPreparedModel, hal_1_2::HalPolicy>;
+template class RequestThread<ArmnnPreparedModel_1_2, hal_1_2::HalPolicy>;
#endif
} // namespace armnn_driver
diff --git a/RequestThread.hpp b/RequestThread.hpp
index 67069205..dc1b535a 100644
--- a/RequestThread.hpp
+++ b/RequestThread.hpp
@@ -19,10 +19,7 @@
namespace armnn_driver
{
-template<typename HalVersion>
-class ArmnnPreparedModel;
-
-template<typename HalVersion>
+template<template <typename HalVersion> class PreparedModel, typename HalVersion>
class RequestThread
{
public:
@@ -38,7 +35,7 @@ public:
/// @param[in] inputTensors pointer to the input tensors for the request
/// @param[in] outputTensors pointer to the output tensors for the request
/// @param[in] callback the android notification callback
- void PostMsg(armnn_driver::ArmnnPreparedModel<HalVersion>* model,
+ void PostMsg(PreparedModel<HalVersion>* model,
std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& memPools,
std::shared_ptr<armnn::InputTensors>& inputTensors,
std::shared_ptr<armnn::OutputTensors>& outputTensors,
@@ -51,7 +48,7 @@ private:
/// storage for a prepared model and args for the asyncExecute call
struct AsyncExecuteData
{
- AsyncExecuteData(ArmnnPreparedModel<HalVersion>* model,
+ AsyncExecuteData(PreparedModel<HalVersion>* model,
std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>>& memPools,
std::shared_ptr<armnn::InputTensors>& inputTensors,
std::shared_ptr<armnn::OutputTensors>& outputTensors,
@@ -64,7 +61,7 @@ private:
{
}
- armnn_driver::ArmnnPreparedModel<HalVersion>* m_Model;
+ PreparedModel<HalVersion>* m_Model;
std::shared_ptr<std::vector<::android::nn::RunTimePoolInfo>> m_MemPools;
std::shared_ptr<armnn::InputTensors> m_InputTensors;
std::shared_ptr<armnn::OutputTensors> m_OutputTensors;
diff --git a/Utils.cpp b/Utils.cpp
index f5599f7d..c3c6310b 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -63,7 +63,7 @@ void* GetMemoryFromPool(DataLocation location, const std::vector<android::nn::Ru
// Type android::nn::RunTimePoolInfo has changed between Android O and Android P, where
// "buffer" has been made private and must be accessed via the accessor method "getBuffer".
-#if defined(ARMNN_ANDROID_P) // Use the new Android P implementation.
+#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q) // Use the new Android implementation.
uint8_t* memPoolBuffer = memPool.getBuffer();
#else // Fallback to the old Android O implementation.
uint8_t* memPoolBuffer = memPool.buffer;
@@ -90,7 +90,7 @@ armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
type = armnn::DataType::Signed32;
break;
default:
- throw UnsupportedOperand(operand.type);
+ throw UnsupportedOperand<V1_0::OperandType>(operand.type);
}
armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
@@ -101,12 +101,56 @@ armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand)
return ret;
}
+#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
+
+armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand)
+{
+ armnn::DataType type;
+
+ switch (operand.type)
+ {
+ case V1_2::OperandType::TENSOR_FLOAT32:
+ type = armnn::DataType::Float32;
+ break;
+ case V1_2::OperandType::TENSOR_QUANT8_ASYMM:
+ type = armnn::DataType::QuantisedAsymm8;
+ break;
+ case V1_2::OperandType::TENSOR_QUANT16_SYMM:
+ type = armnn::DataType::QuantisedSymm16;
+ break;
+ case V1_2::OperandType::TENSOR_INT32:
+ type = armnn::DataType::Signed32;
+ break;
+ default:
+ throw UnsupportedOperand<V1_2::OperandType>(operand.type);
+ }
+
+ armnn::TensorInfo ret(operand.dimensions.size(), operand.dimensions.data(), type);
+
+ ret.SetQuantizationScale(operand.scale);
+ ret.SetQuantizationOffset(operand.zeroPoint);
+
+ return ret;
+}
+
+#endif
+
std::string GetOperandSummary(const V1_0::Operand& operand)
{
return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
toString(operand.type);
}
+#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
+
+std::string GetOperandSummary(const V1_2::Operand& operand)
+{
+ return android::hardware::details::arrayToString(operand.dimensions, operand.dimensions.size()) + " " +
+ toString(operand.type);
+}
+
+#endif
+
using DumpElementFunction = void (*)(const armnn::ConstTensor& tensor,
unsigned int elementIndex,
std::ofstream& fileStream);
diff --git a/Utils.hpp b/Utils.hpp
index 42fef3fb..5aac4716 100644
--- a/Utils.hpp
+++ b/Utils.hpp
@@ -4,7 +4,6 @@
//
#pragma once
-
#include <armnn/ArmNN.hpp>
#include <CpuExecutor.h>
@@ -21,20 +20,25 @@
namespace V1_0 = ::android::hardware::neuralnetworks::V1_0;
+#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
+namespace V1_2 = ::android::hardware::neuralnetworks::V1_2;
+#endif
+
namespace armnn_driver
{
extern const armnn::PermutationVector g_DontPermute;
+template <typename OperandType>
class UnsupportedOperand: public std::runtime_error
{
public:
- UnsupportedOperand(const V1_0::OperandType type)
+ UnsupportedOperand(const OperandType type)
: std::runtime_error("Operand type is unsupported")
, m_type(type)
{}
- V1_0::OperandType m_type;
+ OperandType m_type;
};
/// Swizzles tensor data in @a input according to the dimension mappings.
@@ -48,8 +52,16 @@ void* GetMemoryFromPool(DataLocation location,
/// Can throw UnsupportedOperand
armnn::TensorInfo GetTensorInfoForOperand(const V1_0::Operand& operand);
+#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
+armnn::TensorInfo GetTensorInfoForOperand(const V1_2::Operand& operand);
+#endif
+
std::string GetOperandSummary(const V1_0::Operand& operand);
+#ifdef ARMNN_ANDROID_NN_V1_2 // Using ::android::hardware::neuralnetworks::V1_2
+std::string GetOperandSummary(const V1_2::Operand& operand);
+#endif
+
template <typename HalModel>
std::string GetModelSummary(const HalModel& model)
{
diff --git a/android.hardware.neuralnetworks@1.2-service-armnn.rc b/android.hardware.neuralnetworks@1.2-service-armnn.rc
new file mode 100644
index 00000000..58894465
--- /dev/null
+++ b/android.hardware.neuralnetworks@1.2-service-armnn.rc
@@ -0,0 +1,4 @@
+service neuralnetworks_hal_service_armnn /vendor/bin/hw/android.hardware.neuralnetworks@1.2-service-armnn
+ class hal
+ user system
+ group system
diff --git a/test/GenericLayerTests.cpp b/test/GenericLayerTests.cpp
index 63198b4c..ccd4caa2 100644
--- a/test/GenericLayerTests.cpp
+++ b/test/GenericLayerTests.cpp
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(GetSupportedOperations)
driver->getSupportedOperations(model1, cb);
-#if defined(ARMNN_ANDROID_P)
+#if defined(ARMNN_ANDROID_P) || defined(ARMNN_ANDROID_Q)
// In Android P, android::nn::validateModel returns INVALID_ARGUMENT, because of the wrong number of inputs for the
// fully connected layer (1 instead of 4)
BOOST_TEST((int)errorStatus == (int)ErrorStatus::INVALID_ARGUMENT);