blob: fa686a6f295f31e9c037c5715e58038bb2a305b8 [file] [log] [blame]
arovir01b0717b52018-09-05 17:03:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
Sadik Armagan2050c232019-07-23 16:59:58 +01008#include "OutputShapeUtils.hpp"
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01009#include "Utils.hpp"
10
arovir01b0717b52018-09-05 17:03:25 +010011#include <armnn/ArmNN.hpp>
Ferran Balaguerd30093c2019-07-09 17:04:47 +010012#include <armnn/ILayerSupport.hpp>
13#include <armnn/BackendHelper.hpp>
arovir01b0717b52018-09-05 17:03:25 +010014
Mike Kellyb5fdf382019-06-11 16:35:25 +010015#include "armnn/src/armnnUtils/DataLayoutIndexed.hpp"
arovir01b0717b52018-09-05 17:03:25 +010016#include "armnn/src/armnnUtils/Permute.hpp"
arovir01b0717b52018-09-05 17:03:25 +010017
18#include <ActivationFunctor.h>
19#include <CpuExecutor.h>
20#include <OperationsUtils.h>
21
22#include <boost/assert.hpp>
23#include <boost/core/ignore_unused.hpp>
Aron Virginas-Tar0e7ab542019-04-10 15:02:31 +010024#include <boost/numeric/conversion/cast.hpp>
arovir01b0717b52018-09-05 17:03:25 +010025#include <boost/test/tools/floating_point_comparison.hpp>
26
27#include <log/log.h>
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010028#include <vector>
arovir01b0717b52018-09-05 17:03:25 +010029
30namespace armnn_driver
31{
32
33///
34/// Helper classes
35///
36
37struct ConversionData
38{
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010039 ConversionData(const std::vector<armnn::BackendId>& backends)
40 : m_Backends(backends)
41 , m_Network(nullptr, nullptr)
arovir01b0717b52018-09-05 17:03:25 +010042 {}
43
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +010044 const std::vector<armnn::BackendId> m_Backends;
arovir01b0717b52018-09-05 17:03:25 +010045 armnn::INetworkPtr m_Network;
46 std::vector<armnn::IOutputSlot*> m_OutputSlotForOperand;
47 std::vector<android::nn::RunTimePoolInfo> m_MemPools;
48};
49
50class LayerInputHandle
51{
52public:
53 LayerInputHandle();
54 LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo);
55
56 bool IsValid() const;
57
58 void Connect(armnn::IInputSlot& inputSlot);
59
60 const armnn::TensorInfo& GetTensorInfo() const;
61
62private:
63 armnn::IOutputSlot* m_OutputSlot;
64 bool m_Valid;
65 armnn::TensorInfo m_TensorInfo;
66};
67
68class ConstTensorPin
69{
70public:
71 // Creates an invalid tensor pin (can be used to signal errors)
72 // The optional flag can be set to indicate the tensor values were missing, but it was otherwise valid
73 ConstTensorPin(bool optional = false);
74
75 // @param tensorInfo TensorInfo associated with the tensor.
76 // @param valueStart Start address of tensor data. Belongs to one of the memory pools associated with
77 // the model being converted.
78 // @param numBytes Number of bytes for the tensor data.
79 ConstTensorPin(const armnn::TensorInfo& tensorInfo, const void* valueStart, uint32_t numBytes,
80 const armnn::PermutationVector& mappings);
81
82 ConstTensorPin(const ConstTensorPin& other) = delete;
83 ConstTensorPin(ConstTensorPin&& other) = default;
84
85 bool IsValid() const;
86 bool IsOptional() const;
87
88 const armnn::ConstTensor& GetConstTensor() const;
89 const armnn::ConstTensor* GetConstTensorPtr() const;
90
91private:
92 armnn::ConstTensor m_ConstTensor;
93
94 // Owned memory for swizzled tensor data, only required if the tensor needed
95 // swizzling. Otherwise, @ref m_ConstTensor will reference memory from one of
96 // the pools associated with the model being converted.
97 std::vector<uint8_t> m_SwizzledTensorData;
98
99 // optional flag to indicate that an invalid tensor pin is not an error, but the optional values were not given
100 bool m_Optional;
101};
102
103} // namespace armnn_driver
104
105///
106/// Utility functions
107///
108
109namespace
110{
111
112using namespace armnn_driver;
113using namespace android::nn;
114
115// Convenience function to log the reason for failing to convert a model.
116// @return Always returns false (so that it can be used by callers as a quick way to signal an error and return)
117template<class... Args>
118static bool Fail(const char* formatStr, Args&&... args)
119{
120 ALOGD(formatStr, std::forward<Args>(args)...);
121 return false;
122}
123
Ferran Balaguerd30093c2019-07-09 17:04:47 +0100124// Convenience macro to call an Is*Supported function and log caller name together with reason for lack of support.
125// Called as: FORWARD_LAYER_SUPPORT_FUNC(__func__, Is*Supported, backends, a, b, c, d, e)
126#define FORWARD_LAYER_SUPPORT_FUNC(funcName, func, backends, supported, ...) \
127 std::string reasonIfUnsupported; \
128 try { \
129 for (auto&& backendId : backends) \
130 { \
131 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
132 if (layerSupportObject) \
133 { \
134 supported = \
135 layerSupportObject->func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
136 if (supported) \
137 { \
138 break; \
139 } \
140 else \
141 { \
142 if (reasonIfUnsupported.size() > 0) \
143 { \
144 ALOGD("%s: not supported by armnn: %s", funcName, reasonIfUnsupported.c_str()); \
145 } \
146 else \
147 { \
148 ALOGD("%s: not supported by armnn", funcName); \
149 } \
150 } \
151 } \
152 else \
153 { \
154 ALOGD("%s: backend not registered: %s", funcName, backendId.Get().c_str()); \
155 } \
156 } \
157 if (!supported) \
158 { \
159 ALOGD("%s: not supported by any specified backend", funcName); \
160 } \
161 } catch (const armnn::InvalidArgumentException &e) { \
162 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
arovir01b0717b52018-09-05 17:03:25 +0100163 }
Nattapat Chaimanowongd5fd9762019-04-04 13:33:10 +0100164
Mike Kellyb5fdf382019-06-11 16:35:25 +0100165template<typename Operand>
166armnn::TensorShape GetTensorShapeForOperand(const Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100167{
168 return armnn::TensorShape(operand.dimensions.size(), operand.dimensions.data());
169}
170
Matthew Bentham912b3622019-05-03 15:49:14 +0100171inline bool IsOperandTypeSupportedForTensors(V1_0::OperandType type)
arovir01b0717b52018-09-05 17:03:25 +0100172{
Matthew Bentham912b3622019-05-03 15:49:14 +0100173 return type == V1_0::OperandType::TENSOR_FLOAT32 ||
174 type == V1_0::OperandType::TENSOR_QUANT8_ASYMM ||
175 type == V1_0::OperandType::TENSOR_INT32;
arovir01b0717b52018-09-05 17:03:25 +0100176}
177
Mike Kellyb5fdf382019-06-11 16:35:25 +0100178#ifdef ARMNN_ANDROID_NN_V1_2
179
180inline bool IsOperandTypeSupportedForTensors(V1_2::OperandType type)
181{
182 return type == V1_2::OperandType::BOOL ||
183 type == V1_2::OperandType::TENSOR_FLOAT16 ||
184 type == V1_2::OperandType::TENSOR_FLOAT32 ||
185 type == V1_2::OperandType::TENSOR_QUANT8_ASYMM ||
186 type == V1_2::OperandType::TENSOR_QUANT16_SYMM ||
187 type == V1_2::OperandType::TENSOR_INT32;
188}
189
190#endif
191
192inline bool IsBool(V1_0::Operand)
193{
194 return false;
195}
196
197#ifdef ARMNN_ANDROID_NN_V1_2
198
199inline bool IsBool(V1_2::Operand operand)
200{
201 return operand.type == V1_2::OperandType::BOOL;
202}
203
204#endif
205
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100206template<typename LayerHandleType>
207armnn::IConnectableLayer& AddReshapeLayer(armnn::INetwork& network, LayerHandleType& inputLayer,
208 armnn::TensorInfo reshapeInfo)
209{
210 armnn::ReshapeDescriptor reshapeDescriptor;
211 reshapeDescriptor.m_TargetShape = reshapeInfo.GetShape();
212
213 armnn::IConnectableLayer* reshapeLayer = network.AddReshapeLayer(reshapeDescriptor);
214 BOOST_ASSERT(reshapeLayer != nullptr);
215
216 // Attach the input layer to the reshape layer
217 inputLayer.Connect(reshapeLayer->GetInputSlot(0));
218 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapeInfo);
219
220 return *reshapeLayer;
221}
222
223void BroadcastTensor(LayerInputHandle& input0, LayerInputHandle& input1,
224 armnn::IConnectableLayer* startLayer, armnn::INetwork& network)
arovir01b0717b52018-09-05 17:03:25 +0100225{
226 BOOST_ASSERT(startLayer != nullptr);
arovir01b0717b52018-09-05 17:03:25 +0100227
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100228 const armnn::TensorInfo& inputInfo0 = input0.GetTensorInfo();
229 const armnn::TensorInfo& inputInfo1 = input1.GetTensorInfo();
230
231 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
232 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
233
234 if (inputDimensions0 == inputDimensions1)
arovir01b0717b52018-09-05 17:03:25 +0100235 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100236 // The inputs have the same number of dimensions, simply connect them to the given layer as they are
237 input0.Connect(startLayer->GetInputSlot(0));
238 input1.Connect(startLayer->GetInputSlot(1));
239
240 return;
241 }
242
243 // Since the number of dimensions do not match then we need to add degenerate dimensions
244 // to the "smaller" tensor using a reshape, while keeping the order of the inputs.
245
246 unsigned int maxInputDimensions = std::max(inputDimensions0, inputDimensions1);
247 unsigned int sizeDifference = std::abs(boost::numeric_cast<int>(inputDimensions0) -
248 boost::numeric_cast<int>(inputDimensions1));
249
250 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
251 LayerInputHandle& smallInputHandle = input0IsSmaller ? input0 : input1;
252 const armnn::TensorInfo& smallInfo = smallInputHandle.GetTensorInfo();
253
254 const armnn::TensorShape& smallShape = smallInfo.GetShape();
255 std::vector<unsigned int> reshapedDimensions(maxInputDimensions, 1);
256 for (unsigned int i = sizeDifference; i < maxInputDimensions; i++)
257 {
258 reshapedDimensions[i] = smallShape[i - sizeDifference];
259 }
260
261 armnn::TensorInfo reshapedInfo = smallInfo;
262 reshapedInfo.SetShape(armnn::TensorShape{ boost::numeric_cast<unsigned int>(reshapedDimensions.size()),
263 reshapedDimensions.data() });
264 armnn::IConnectableLayer& reshapeLayer = AddReshapeLayer(network, smallInputHandle, reshapedInfo);
265
266 if (input0IsSmaller)
267 {
268 // Input0 is the "smaller" tensor, connect the reshape layer as follows:
269 //
270 // Input0 Input1
arovir01b0717b52018-09-05 17:03:25 +0100271 // | |
272 // Reshape |
273 // \ /
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100274 // StartLayer
arovir01b0717b52018-09-05 17:03:25 +0100275
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100276 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
277 input1.Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100278 }
279 else
280 {
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100281 // Input1 is the "smaller" tensor, connect the reshape layer as follows:
282 //
283 // Input0 Input1
284 // | |
285 // | Reshape
286 // \ /
287 // StartLayer
288
arovir01b0717b52018-09-05 17:03:25 +0100289 input0.Connect(startLayer->GetInputSlot(0));
Matteo Martincigh0bd89a82019-07-02 16:53:10 +0100290 reshapeLayer.GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
arovir01b0717b52018-09-05 17:03:25 +0100291 }
292}
293
294void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t& outPadHead, uint32_t& outPadTail,
295 android::nn::PaddingScheme scheme)
296{
297 int32_t padHead;
298 int32_t padTail;
299 calculateExplicitPadding(input, stride, kernel, scheme, &padHead, &padTail);
300 outPadHead = boost::numeric_cast<uint32_t>(padHead);
301 outPadTail = boost::numeric_cast<uint32_t>(padTail);
302}
303
Mike Kelly86b36d42019-07-12 16:39:33 +0100304#ifdef ARMNN_ANDROID_NN_V1_2
305
306void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t dilation, uint32_t& outPadHead,
307 uint32_t& outPadTail, android::nn::PaddingScheme scheme)
308{
309 int32_t padHead;
310 int32_t padTail;
311 calculateExplicitPadding(input, stride, dilation, kernel, scheme, &padHead, &padTail);
312 outPadHead = boost::numeric_cast<uint32_t>(padHead);
313 outPadTail = boost::numeric_cast<uint32_t>(padTail);
314}
315
316#endif
317
Matthew Bentham912b3622019-05-03 15:49:14 +0100318Shape GetOperandShape(const V1_0::Operand& operand)
arovir01b0717b52018-09-05 17:03:25 +0100319{
320 Shape shape;
Matthew Bentham912b3622019-05-03 15:49:14 +0100321 shape.type = OperandType(operand.type);
arovir01b0717b52018-09-05 17:03:25 +0100322 shape.dimensions = operand.dimensions;
323 shape.scale = operand.scale;
324 shape.offset = operand.zeroPoint;
325 return shape;
326}
327
328// ArmNN requires the bias scale to be equal to the product of the weight and input scales, which is also
329// what AndroidNN requires. However for some of the AndroidNN tests the values don't exactly match so
330// we accept some tolerance. We don't want to ArmNN itself to accept these inconsistencies as it is up to the user
331// (us, in this case) to ensure they match.
332void SanitizeBiasQuantizationScale(armnn::TensorInfo& biasInfo,
333 const armnn::TensorInfo& weightInfo, const armnn::TensorInfo& inputInfo)
334{
335 const float expectedBiasScale = weightInfo.GetQuantizationScale() * inputInfo.GetQuantizationScale();
336 if (biasInfo.GetQuantizationScale() != expectedBiasScale)
337 {
338 boost::math::fpc::close_at_tolerance<float> comparer(boost::math::fpc::percent_tolerance(1.0f));
339 if (comparer(biasInfo.GetQuantizationScale(), expectedBiasScale))
340 {
341 ALOGW("Bias quantization scale has been modified to match input*weights");
342 biasInfo.SetQuantizationScale(expectedBiasScale);
343 }
344 }
345}
346
347// 4D Tensor Permutations
348const armnn::PermutationVector IdentityPermutation4D({ 0U, 1U, 2U, 3U });
349const armnn::PermutationVector NHWCToArmNN({ 0U, 2U, 3U, 1U });
350const armnn::PermutationVector ArmNNToNHWC({ 0U, 3U, 1U, 2U });
351const armnn::PermutationVector SwapDim1And2({ 0U, 2U, 1U, 3U });
352
353// 3D Permutation Vectors
354const armnn::PermutationVector IdentityPermutation3D({ 0U, 1U, 2U });
355const armnn::PermutationVector RotateTensorLeft({ 2U, 0U, 1U });
356const armnn::PermutationVector RotateTensorRight({ 1U, 2U, 0U });
357
358template<typename OSlot>
359armnn::IConnectableLayer& AddPermuteLayer(armnn::INetwork& network, OSlot& input,
360 const armnn::PermutationVector& mappings)
361{
362 // Add swizzle layer
363 armnn::IConnectableLayer* const layer = network.AddPermuteLayer(mappings);
364
365 BOOST_ASSERT(layer != nullptr);
366
367 // Connect input to swizzle layer
368 input.Connect(layer->GetInputSlot(0));
369
370 // Setup swizzled output
371 const armnn::TensorInfo outInfo = armnnUtils::Permuted(input.GetTensorInfo(), mappings);
372 layer->GetOutputSlot(0).SetTensorInfo(outInfo);
373
374 return *layer;
375}
376
377void SwizzleIn(armnn::INetwork& network, LayerInputHandle& input, armnn::IConnectableLayer& layer, unsigned int index)
378{
379 // Add swizzle layer
380 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, input, NHWCToArmNN);
381 // Connect swizzled input to layer
382 swizzleLayer.GetOutputSlot(0).Connect(layer.GetInputSlot(index));
383}
384
385armnn::IConnectableLayer& DeswizzleOut(armnn::INetwork& network, armnn::IConnectableLayer& layer, unsigned int index)
386{
387 // Add deswizzle layer
388 armnn::IConnectableLayer& deswizzleLayer = AddPermuteLayer(network, layer.GetOutputSlot(index), ArmNNToNHWC);
389 return deswizzleLayer;
390}
391
392// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
393armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network,
394 LayerInputHandle& input,
395 armnn::IConnectableLayer& firstLayer,
396 armnn::IConnectableLayer& lastLayer)
397{
398 SwizzleIn(network, input, firstLayer, 0);
399 return DeswizzleOut(network, lastLayer, 0);
400}
401
402// only suitable for input/output slot index 0, for other slots, use SwizzleIn and DeswizzleOut directly
403armnn::IConnectableLayer& SwizzleInDeswizzleOut(armnn::INetwork& network, LayerInputHandle& input,
404 armnn::IConnectableLayer& layer)
405{
406 return SwizzleInDeswizzleOut(network, input, layer, layer);
407}
408
409bool ValidateConcatOutputShape(const std::vector<armnn::TensorShape> & inputShapes,
410 const armnn::TensorShape & outputShape,
411 uint32_t concatDim)
412{
413 // Validate the output shape is correct given the input shapes (which have just been validated)
414 unsigned int numDimensions = inputShapes[0].GetNumDimensions();
415 if (outputShape.GetNumDimensions() != numDimensions)
416 {
417 return Fail("%s: Output shape has wrong number of dimensions", __func__);
418 }
419
420 unsigned int outputSizeAlongConcatenatedDimension = 0;
421 for (unsigned int i = 0; i < inputShapes.size(); i++)
422 {
423 outputSizeAlongConcatenatedDimension += inputShapes[i][concatDim];
424 }
425
426 for (unsigned int i = 0; i < numDimensions; ++i)
427 {
428 if (i == concatDim)
429 {
430 if (outputShape[i] != outputSizeAlongConcatenatedDimension)
431 {
432 return Fail(
433 "%s: Invalid output shape for dimension %d (%d != %d)",
434 __func__,
435 i,
436 outputShape[i],
437 outputSizeAlongConcatenatedDimension);
438 }
439 }
440 else
441 {
442 if (outputShape[i] != inputShapes[0][i])
443 {
444 return Fail("%s: Invalid output shape", __func__);
445 }
446 }
447 }
448
449 return true;
450}
451
452bool RequiresReshape(armnn::TensorShape & inputShape)
453{
454 return inputShape.GetNumDimensions() < 3;
455}
456
arovir01b0717b52018-09-05 17:03:25 +0100457void SwizzleInputs(armnn::INetwork& network,
458 std::vector<LayerInputHandle>& inputs,
459 std::vector<armnn::TensorShape>& inputShapes,
460 const armnn::PermutationVector& mapping)
461{
462 if (!mapping.IsEqual(IdentityPermutation4D))
463 {
464 size_t nInputs = inputs.size();
465 for (size_t i=0; i<nInputs; ++i)
466 {
467 // add swizzle layer
468 armnn::IConnectableLayer& swizzleLayer = AddPermuteLayer(network, inputs[i], mapping);
469 auto& outputSlot = swizzleLayer.GetOutputSlot(0);
470 auto& outputInfo = outputSlot.GetTensorInfo();
471 // replace inputs with the swizzled ones
472 inputs[i] = LayerInputHandle(true, &outputSlot, outputInfo);
473 inputShapes[i] = inputs[i].GetTensorInfo().GetShape();
474 }
475 }
476}
477
narpra01f176d5a2018-11-18 20:17:48 +0000478bool CreateConcatPermutationParameters(const unsigned int numberOfDimensions,
479 int32_t & concatDimension,
480 std::pair<armnn::PermutationVector, armnn::PermutationVector> & permutationPair)
arovir01b0717b52018-09-05 17:03:25 +0100481{
narpra01f176d5a2018-11-18 20:17:48 +0000482 bool needPermute = false;
arovir01b0717b52018-09-05 17:03:25 +0100483 BOOST_ASSERT(numberOfDimensions >= 3);
484
485 // ArmNN uses Compute Library subtensors to perform concatenation
narpra01f176d5a2018-11-18 20:17:48 +0000486 // This only works when concatenating along dimension 0, 1 or 3 for a 4-D tensor,
487 // or along dimension 0 or 2 for a 3-D tensor.
488 if (numberOfDimensions == 4 && concatDimension == 2)
arovir01b0717b52018-09-05 17:03:25 +0100489 {
narpra01f176d5a2018-11-18 20:17:48 +0000490 concatDimension = 1;
491 permutationPair = std::make_pair(SwapDim1And2, SwapDim1And2);
492 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100493 }
narpra01f176d5a2018-11-18 20:17:48 +0000494 else if (numberOfDimensions == 3 && concatDimension == 1)
arovir01b0717b52018-09-05 17:03:25 +0100495 {
narpra01f176d5a2018-11-18 20:17:48 +0000496 concatDimension = 0;
497 permutationPair = std::make_pair(RotateTensorLeft, RotateTensorRight);
498 needPermute = true;
arovir01b0717b52018-09-05 17:03:25 +0100499 }
narpra01f176d5a2018-11-18 20:17:48 +0000500 return needPermute;
arovir01b0717b52018-09-05 17:03:25 +0100501}
502
503} // anonymous namespace
504
505namespace armnn_driver
506{
507
508//// Creates an ArmNN activation layer and connects it to the given layer, if the
509//// passed in AndroidNN activation function requires so.
510//// @return The end layer of the sequence of layers built for the given AndroidNN
511//// activation function or nullptr if an error occurred (e.g. unsupported activation).
512//// Note that the end layer matches the input layer if no activation is required
513//// (the sequence of layers has length 1).
514armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
515 ActivationFn activation,
516 armnn::IConnectableLayer* prevLayer,
517 ConversionData& data);
518
519} // namespace armnn_driver
520
521///
522/// Utility templates
523///
524
525namespace armnn_driver
526{
527
528using namespace android::nn;
529
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100530template<typename HalPolicy,
531 typename HalOperand = typename HalPolicy::Operand,
532 typename HalOperation = typename HalPolicy::Operation,
533 typename HalModel = typename HalPolicy::Model>
534const HalOperand* GetInputOperand(const HalOperation& operation,
535 uint32_t inputIndex,
536 const HalModel& model,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100537 bool failOnIndexOutOfBounds = true)
arovir01b0717b52018-09-05 17:03:25 +0100538{
539 if (inputIndex >= operation.inputs.size())
540 {
saoste01b8471482018-10-10 09:44:51 +0100541 if (failOnIndexOutOfBounds)
542 {
543 Fail("%s: invalid input index: %i out of %i", __func__, inputIndex, operation.inputs.size());
544 }
arovir01b0717b52018-09-05 17:03:25 +0100545 return nullptr;
546 }
547
548 BOOST_ASSERT(operation.inputs[inputIndex] < model.operands.size()); // Model should have been validated beforehand
549 return &model.operands[operation.inputs[inputIndex]];
550}
551
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100552template<typename HalPolicy,
553 typename HalOperand = typename HalPolicy::Operand,
554 typename HalOperation = typename HalPolicy::Operation,
555 typename HalModel = typename HalPolicy::Model>
556const HalOperand* GetOutputOperand(const HalOperation& operation,
557 uint32_t outputIndex,
558 const HalModel& model)
arovir01b0717b52018-09-05 17:03:25 +0100559{
560 if (outputIndex >= operation.outputs.size())
561 {
562 Fail("%s: invalid output index: %i out of %i", __func__, outputIndex, operation.outputs.size());
563 return nullptr;
564 }
565
566 // Model should have been validated beforehand
567 BOOST_ASSERT(operation.outputs[outputIndex] < model.operands.size());
568
569 return &model.operands[operation.outputs[outputIndex]];
570}
571
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100572template<typename HalPolicy,
573 typename HalOperand = typename HalPolicy::Operand,
574 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100575const void* GetOperandValueReadOnlyAddress(const HalOperand& operand,
Matthew Bentham912b3622019-05-03 15:49:14 +0100576 const HalModel& model,
577 const ConversionData& data,
Kevin Mayf29a2c52019-03-14 11:56:32 +0000578 bool optional = false)
arovir01b0717b52018-09-05 17:03:25 +0100579{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100580 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
arovir01b0717b52018-09-05 17:03:25 +0100581
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100582 const void* valueStart = nullptr;
arovir01b0717b52018-09-05 17:03:25 +0100583 switch (operand.lifetime)
584 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100585 case HalOperandLifeTime::CONSTANT_COPY:
arovir01b0717b52018-09-05 17:03:25 +0100586 {
587 // Constant found in model.operandValues
588 valueStart = &model.operandValues[operand.location.offset];
589 break;
590 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100591 case HalOperandLifeTime::CONSTANT_REFERENCE:
arovir01b0717b52018-09-05 17:03:25 +0100592 {
593 // Constant specified via a Memory object
594 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
595 break;
596 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100597 case HalOperandLifeTime::NO_VALUE:
Kevin Mayf29a2c52019-03-14 11:56:32 +0000598 {
599 // An optional input tensor with no values is not an error so should not register as a fail
600 if (optional)
601 {
602 valueStart = nullptr;
603 break;
604 }
Matthew Bentham912b3622019-05-03 15:49:14 +0100605 [[fallthrough]];
Kevin Mayf29a2c52019-03-14 11:56:32 +0000606 }
arovir01b0717b52018-09-05 17:03:25 +0100607 default:
608 {
609 // Unsupported/invalid (e.g. can't get value of an input to the model)
610 Fail("%s: unsupported/invalid operand lifetime: %s",
611 __func__, toString(operand.lifetime).c_str());
612 valueStart = nullptr;
613 }
614 }
615
616 return valueStart;
617}
618
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100619template<typename HalPolicy,
Aron Virginas-Tar7a6d11b2019-07-03 15:27:08 +0100620 typename HalOperation = typename HalPolicy::Operation,
621 typename HalModel = typename HalPolicy::Model,
622 typename HalOperandType = typename HalPolicy::OperandType>
623bool GetOperandType(const HalOperation& operation,
624 uint32_t inputIndex,
625 const HalModel& model,
626 HalOperandType& type)
627{
628 using HalOperand = typename HalPolicy::Operand;
629
630 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
631 if (!operand)
632 {
633 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
634 }
635
636 type = operand->type;
637 return true;
638}
639
640template<typename HalPolicy,
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100641 typename HalOperand = typename HalPolicy::Operand,
642 typename HalModel = typename HalPolicy::Model>
643ConstTensorPin ConvertOperandToConstTensorPin(const HalOperand& operand,
644 const HalModel& model,
645 const ConversionData& data,
646 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
647 const armnn::TensorShape* overrideTensorShape = nullptr,
648 bool optional = false)
649{
650 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
651
652 if (!IsOperandTypeSupportedForTensors(operand.type))
653 {
654 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand.type).c_str());
655 return ConstTensorPin();
656 }
657
658 if (!optional &&
659 operand.lifetime != HalOperandLifeTime::CONSTANT_COPY &&
660 operand.lifetime != HalOperandLifeTime::CONSTANT_REFERENCE &&
661 operand.lifetime != HalOperandLifeTime::NO_VALUE)
662 {
663 Fail("%s: invalid operand lifetime: %s", __func__, toString(operand.lifetime).c_str());
664 return ConstTensorPin();
665 }
666
667 const void* const valueStart = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data, optional);
668 if (!valueStart)
669 {
670 if (optional)
671 {
672 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
673 return ConstTensorPin(true);
674 }
675 // mandatory tensor with no values
676 Fail("%s: failed to get operand address", __func__);
677 return ConstTensorPin();
678 }
679
680 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
681 if (overrideTensorShape != nullptr)
682 {
683 tensorInfo.SetShape(*overrideTensorShape);
684 }
685 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
686}
687
688template<typename HalPolicy,
689 typename HalOperation = typename HalPolicy::Operation,
690 typename HalModel = typename HalPolicy::Model>
691ConstTensorPin ConvertOperationInputToConstTensorPin(const HalOperation& operation,
692 uint32_t inputIndex,
693 const HalModel& model,
694 const ConversionData& data,
695 const armnn::PermutationVector& dimensionMappings = g_DontPermute,
696 const armnn::TensorShape* overrideTensorShape = nullptr,
697 bool optional = false)
698{
699 using HalOperand = typename HalPolicy::Operand;
700
701 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
702 if (!operand)
703 {
704 Fail("%s: failed to get input operand: index=%u", __func__, inputIndex);
705 return ConstTensorPin();
706 }
707 return ConvertOperandToConstTensorPin<HalPolicy>(*operand,
708 model,
709 data,
710 dimensionMappings,
711 overrideTensorShape,
712 optional);
713}
714
715template<typename HalPolicy,
716 typename OutputType,
717 typename HalOperandType = typename HalPolicy::OperandType,
718 typename HalOperation = typename HalPolicy::Operation,
719 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100720bool GetInputScalar(const HalOperation& operation,
721 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100722 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100723 OutputType& outValue,
724 const HalModel& model,
725 const ConversionData& data)
726{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100727 using HalOperand = typename HalPolicy::Operand;
728
729 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100730 if (!operand)
731 {
732 return Fail("%s: invalid input operand at index %i", __func__, inputIndex);
733 }
734
735 if (operand->type != type)
736 {
737 return Fail("%s: unexpected operand type: %s (should be %s)",
738 __func__, toString(operand->type).c_str(), toString(type).c_str());
739 }
740
741 if (operand->location.length != sizeof(OutputType))
742 {
743 return Fail("%s: incorrect operand location length: %i (should be %i)",
744 __func__, operand->location.length, sizeof(OutputType));
745 }
746
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100747 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100748 if (!valueAddress)
749 {
750 return Fail("%s: failed to get address for operand", __func__);
751 }
752
753 outValue = *(static_cast<const OutputType*>(valueAddress));
754 return true;
755}
756
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100757template<typename HalPolicy,
758 typename HalOperation = typename HalPolicy::Operation,
759 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100760bool GetInputInt32(const HalOperation& operation,
761 uint32_t inputIndex,
762 int32_t& outValue,
763 const HalModel& model,
764 const ConversionData& data)
765{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100766 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::INT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100767}
768
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100769template<typename HalPolicy,
770 typename HalOperation = typename HalPolicy::Operation,
771 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100772bool GetInputFloat32(const HalOperation& operation,
773 uint32_t inputIndex,
774 float& outValue,
775 const HalModel& model,
776 const ConversionData& data)
777{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100778 return GetInputScalar<HalPolicy>(operation, inputIndex, HalPolicy::OperandType::FLOAT32, outValue, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100779}
780
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100781template<typename HalPolicy,
782 typename HalOperation = typename HalPolicy::Operation,
783 typename HalOperandType = typename HalPolicy::OperandType,
784 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100785bool GetInputActivationFunctionImpl(const HalOperation& operation,
786 uint32_t inputIndex,
Mike Kellyb5fdf382019-06-11 16:35:25 +0100787 HalOperandType type,
arovir01b0717b52018-09-05 17:03:25 +0100788 ActivationFn& outActivationFunction,
789 const HalModel& model,
790 const ConversionData& data)
791{
Mike Kellyb5fdf382019-06-11 16:35:25 +0100792 if (type != HalOperandType::INT32 && type != HalOperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100793 {
794 return Fail("%s: unexpected operand type: %s (should be %s or %s)",
795 __func__,
796 toString(type).c_str(),
797 toString(OperandType::INT32).c_str(),
798 toString(OperandType::TENSOR_INT32).c_str());
799 }
800
801 int32_t activationFunctionAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100802 if (!GetInputScalar<HalPolicy>(operation, inputIndex, type, activationFunctionAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100803 {
804 return Fail("%s: failed to get activation input value", __func__);
805 }
806 outActivationFunction = static_cast<ActivationFn>(activationFunctionAsInt);
807 return true;
808}
809
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100810template<typename HalPolicy,
811 typename HalOperation = typename HalPolicy::Operation,
812 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100813bool GetInputActivationFunction(const HalOperation& operation,
814 uint32_t inputIndex,
815 ActivationFn& outActivationFunction,
816 const HalModel& model,
817 const ConversionData& data)
818{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100819 return GetInputActivationFunctionImpl<HalPolicy>(operation,
820 inputIndex,
821 HalPolicy::OperandType::INT32,
822 outActivationFunction,
823 model,
824 data);
arovir01b0717b52018-09-05 17:03:25 +0100825}
826
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100827template<typename HalPolicy,
828 typename HalOperation = typename HalPolicy::Operation,
829 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100830bool GetInputActivationFunctionFromTensor(const HalOperation& operation,
831 uint32_t inputIndex,
832 ActivationFn& outActivationFunction,
833 const HalModel& model,
834 const ConversionData& data)
835{
836 // This only accepts a 1-D tensor of size 1
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100837 return GetInputActivationFunctionImpl<HalPolicy>(operation,
838 inputIndex,
839 HalPolicy::OperandType::INT32,
840 outActivationFunction,
841 model,
842 data);
arovir01b0717b52018-09-05 17:03:25 +0100843}
844
845
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100846template<typename HalPolicy,
847 typename HalOperation = typename HalPolicy::Operation,
848 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100849bool GetOptionalInputActivation(const HalOperation& operation,
850 uint32_t inputIndex,
851 ActivationFn& activationFunction,
852 const HalModel& model,
853 const ConversionData& data)
854{
855 if (operation.inputs.size() <= inputIndex)
856 {
857 activationFunction = ActivationFn::kActivationNone;
858 }
859 else
860 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100861 if (!GetInputActivationFunction<HalPolicy>(operation, inputIndex, activationFunction, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100862 {
863 return Fail("%s: Operation has invalid inputs", __func__);
864 }
865 }
866 return true;
867}
868
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100869template<typename HalPolicy,
870 typename ConvolutionDescriptor,
871 typename HalOperation = typename HalPolicy::Operation,
872 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100873bool GetOptionalConvolutionDilationParams(const HalOperation& operation,
874 uint32_t dilationXIndex,
875 ConvolutionDescriptor& descriptor,
876 const HalModel& model,
877 const ConversionData& data)
878{
879 bool success = true;
880 if (operation.inputs.size() >= dilationXIndex + 2)
881 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100882 success &= GetInputScalar<HalPolicy>(operation,
883 dilationXIndex,
884 HalPolicy::OperandType::INT32,
885 descriptor.m_DilationX,
886 model,
887 data);
888 success &= GetInputScalar<HalPolicy>(operation,
889 dilationXIndex + 1,
890 HalPolicy::OperandType::INT32,
891 descriptor.m_DilationY,
892 model,
893 data);
Aron Virginas-Tar07c7c9a2019-06-12 14:03:35 +0100894 }
895
896 return success;
897}
898
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100899template<typename HalPolicy,
900 typename HalOperand = typename HalPolicy::Operand,
901 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +0100902bool GetTensorInt32Values(const HalOperand& operand,
arovir01b0717b52018-09-05 17:03:25 +0100903 std::vector<int32_t>& outValues,
904 const HalModel& model,
905 const ConversionData& data)
906{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100907 if (operand.type != HalPolicy::OperandType::TENSOR_INT32)
arovir01b0717b52018-09-05 17:03:25 +0100908 {
909 return Fail("%s: invalid operand type: %s", __func__, toString(operand.type).c_str());
910 }
911
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100912 const void* startAddress = GetOperandValueReadOnlyAddress<HalPolicy>(operand, model, data);
arovir01b0717b52018-09-05 17:03:25 +0100913 if (!startAddress)
914 {
915 return Fail("%s: failed to get operand address", __func__, operand.type);
916 }
917
918 // Check number of bytes is sensible
919 const uint32_t numBytes = operand.location.length;
920 if (numBytes % sizeof(int32_t) != 0)
921 {
922 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
923 __func__, numBytes, sizeof(int32_t));
924 }
925
926 outValues.resize(numBytes / sizeof(int32_t));
927 memcpy(outValues.data(), startAddress, numBytes);
928 return true;
929}
930
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100931template<typename HalPolicy,
932 typename HalOperation = typename HalPolicy::Operation,
933 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100934bool GetInputPaddingScheme(const HalOperation& operation,
935 uint32_t inputIndex,
936 PaddingScheme& outPaddingScheme,
937 const HalModel& model,
938 const ConversionData& data)
939{
940 int32_t paddingSchemeAsInt;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100941 if (!GetInputInt32<HalPolicy>(operation, inputIndex, paddingSchemeAsInt, model, data))
arovir01b0717b52018-09-05 17:03:25 +0100942 {
943 return Fail("%s: failed to get padding scheme input value", __func__);
944 }
945
946 outPaddingScheme = static_cast<android::nn::PaddingScheme>(paddingSchemeAsInt);
947 return true;
948}
949
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100950template<typename HalPolicy,
951 typename HalOperation = typename HalPolicy::Operation,
952 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +0100953LayerInputHandle ConvertToLayerInputHandle(const HalOperation& operation,
954 uint32_t inputIndex,
955 const HalModel& model,
956 ConversionData& data)
957{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100958 using HalOperand = typename HalPolicy::Operand;
Sadik Armagan44bcc022019-06-18 17:21:36 +0100959 using HalOperandType = typename HalPolicy::OperandType;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +0100960 using HalOperandLifeTime = typename HalPolicy::OperandLifeTime;
961
962 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
arovir01b0717b52018-09-05 17:03:25 +0100963 if (!operand)
964 {
965 Fail("%s: failed to get input operand %i", __func__, inputIndex);
966 return LayerInputHandle();
967 }
968
969 if (!IsOperandTypeSupportedForTensors(operand->type))
970 {
971 Fail("%s: unsupported operand type for tensor %s", __func__, toString(operand->type).c_str());
972 return LayerInputHandle();
973 }
974
Sadik Armagan44bcc022019-06-18 17:21:36 +0100975 try
arovir01b0717b52018-09-05 17:03:25 +0100976 {
Sadik Armagan44bcc022019-06-18 17:21:36 +0100977 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
Aron Virginas-Tar573a8fa2019-07-23 14:01:37 +0100978 if (IsDynamicTensor(operandTensorInfo))
979 {
980 Fail("%s: dynamic input tensors are not supported", __func__);
981 return LayerInputHandle();
982 }
arovir01b0717b52018-09-05 17:03:25 +0100983
Sadik Armagan44bcc022019-06-18 17:21:36 +0100984 switch (operand->lifetime)
arovir01b0717b52018-09-05 17:03:25 +0100985 {
Sadik Armagan44bcc022019-06-18 17:21:36 +0100986 case HalOperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
987 case HalOperandLifeTime::MODEL_INPUT:
988 case HalOperandLifeTime::MODEL_OUTPUT:
arovir01b0717b52018-09-05 17:03:25 +0100989 {
Sadik Armagan44bcc022019-06-18 17:21:36 +0100990 // The tensor is either an operand internal to the model, or a model input.
991 // It can be associated with an ArmNN output slot for an existing layer.
992
993 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
994 const uint32_t operandIndex = operation.inputs[inputIndex];
995 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
996 break;
997 }
998 case HalOperandLifeTime::CONSTANT_COPY:
999 case HalOperandLifeTime::CONSTANT_REFERENCE:
1000 {
1001 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
1002 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin<HalPolicy>(*operand, model, data);
1003 if (tensorPin.IsValid())
arovir01b0717b52018-09-05 17:03:25 +01001004 {
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001005 bool isSupported = false;
1006 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1007 IsConstantSupported,
1008 data.m_Backends,
1009 isSupported,
1010 tensorPin.GetConstTensor().GetInfo());
1011 if (isSupported)
Sadik Armagan44bcc022019-06-18 17:21:36 +01001012 {
1013 return LayerInputHandle();
1014 }
1015
1016 armnn::IConnectableLayer* constantLayer =
1017 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
1018 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
1019 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
1020
1021 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
1022 }
1023 else
1024 {
1025 Fail("%s: invalid operand tensor", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001026 return LayerInputHandle();
1027 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001028 break;
arovir01b0717b52018-09-05 17:03:25 +01001029 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001030 default:
arovir01b0717b52018-09-05 17:03:25 +01001031 {
Sadik Armagan44bcc022019-06-18 17:21:36 +01001032 // Unsupported lifetime for an input tensor
1033 Fail("%s: unsupported lifetime for input tensor: %s",
1034 __func__, toString(operand->lifetime).c_str());
arovir01b0717b52018-09-05 17:03:25 +01001035 return LayerInputHandle();
1036 }
arovir01b0717b52018-09-05 17:03:25 +01001037 }
Sadik Armagan44bcc022019-06-18 17:21:36 +01001038 }
1039 catch (UnsupportedOperand<HalOperandType>& e)
1040 {
1041 Fail("%s: Operand type %s not supported in ArmnnDriver", __func__, toString(e.m_type).c_str());
1042 return LayerInputHandle();
arovir01b0717b52018-09-05 17:03:25 +01001043 }
1044}
1045
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001046template<typename HalPolicy,
1047 typename HalOperation = typename HalPolicy::Operation,
1048 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001049bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1050 uint32_t operationOutputIndex,
1051 armnn::IConnectableLayer& layer,
1052 uint32_t layerOutputIndex,
1053 const HalModel& model,
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001054 ConversionData& data,
1055 const armnn::Optional<armnn::TensorInfo>& outputInfo = armnn::EmptyOptional())
Mike Kellyb5fdf382019-06-11 16:35:25 +01001056{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001057 using HalOperand = typename HalPolicy::Operand;
1058
1059 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, operationOutputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001060 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
1061 {
1062 return false;
1063 }
1064
1065 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
1066
1067 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1068 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1069
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001070 if (outputInfo.has_value())
1071 {
1072 outputSlot.SetTensorInfo(outputInfo.value());
1073 ALOGD("Output info overwritten");
1074 }
1075 else
1076 {
1077 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
1078 }
Mike Kellyb5fdf382019-06-11 16:35:25 +01001079
1080 return true;
1081}
1082
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001083template<typename HalPolicy,
1084 typename HalOperation = typename HalPolicy::Operation,
1085 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001086armnn::DataLayout OptionalDataLayout(const HalOperation& operation,
1087 uint32_t inputIndex,
1088 const HalModel& model,
1089 ConversionData& data)
1090{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001091 using HalOperand = typename HalPolicy::Operand;
1092
1093 const HalOperand* operand = GetInputOperand<HalPolicy>(operation, inputIndex, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001094 if (!operand)
1095 {
1096 return armnn::DataLayout::NHWC;
1097 }
1098
1099 if (!IsBool(*operand))
1100 {
1101 return armnn::DataLayout::NHWC;
1102 }
1103
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001104 const void* valueAddress = GetOperandValueReadOnlyAddress<HalPolicy>(*operand, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001105 if (!valueAddress)
1106 {
1107 return armnn::DataLayout::NHWC;
1108 }
1109
1110 if (*(static_cast<const bool*>(valueAddress)))
1111 {
1112 return armnn::DataLayout::NCHW;
1113 }
1114 else
1115 {
1116 return armnn::DataLayout::NHWC;
1117 }
1118}
1119
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001120template<typename HalPolicy,
1121 typename HalOperation = typename HalPolicy::Operation,
1122 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001123bool SetupAndTrackLayerOutputSlot(const HalOperation& operation,
1124 uint32_t outputIndex,
1125 armnn::IConnectableLayer& layer,
1126 const HalModel& model,
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001127 ConversionData& data,
1128 const armnn::Optional<armnn::TensorInfo>& outputInfo = armnn::EmptyOptional())
Mike Kellyb5fdf382019-06-11 16:35:25 +01001129{
Aron Virginas-Tarf03fcf02019-07-09 17:44:24 +01001130 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1131 outputIndex,
1132 layer,
1133 outputIndex,
1134 model,
1135 data,
1136 outputInfo);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001137}
1138
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001139template<typename HalPolicy,
1140 typename HalOperation = typename HalPolicy::Operation,
1141 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001142bool ConvertToActivation(const HalOperation& operation,
1143 const char* operationName,
1144 const armnn::ActivationDescriptor& activationDesc,
1145 const HalModel& model,
1146 ConversionData& data)
1147{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001148 using HalOperand = typename HalPolicy::Operand;
1149
1150 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001151 if (!input.IsValid())
1152 {
1153 return Fail("%s: Input 0 is invalid", operationName);
1154 }
1155
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001156 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001157 if (!outputOperand)
1158 {
1159 return false;
1160 }
Sadik Armagan2050c232019-07-23 16:59:58 +01001161 armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
1162 if (IsDynamicTensor(outInfo))
1163 {
1164 ALOGD("Output shape not set, will infer from input");
1165 outInfo.SetShape(input.GetTensorInfo().GetShape());
1166 }
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001167
1168 bool isSupported = false;
1169 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1170 IsActivationSupported,
1171 data.m_Backends,
1172 isSupported,
1173 input.GetTensorInfo(),
1174 outInfo,
1175 activationDesc);
1176 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001177 {
1178 return false;
1179 }
1180
1181 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
1182 BOOST_ASSERT(layer != nullptr);
1183 input.Connect(layer->GetInputSlot(0));
1184
Sadik Armagan2050c232019-07-23 16:59:58 +01001185 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1186 0,
1187 *layer,
1188 model,
1189 data,armnn::Optional<armnn::TensorInfo>(outInfo));
arovir01b0717b52018-09-05 17:03:25 +01001190}
1191
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001192template<typename HalPolicy,
1193 typename HalOperation = typename HalPolicy::Operation,
1194 typename HalModel = typename HalPolicy::Model>
Aron Virginas-Tarcb8ac842019-07-05 15:47:07 +01001195bool ConvertPaddings(const HalOperation& operation,
1196 const HalModel& model,
1197 ConversionData& data,
1198 unsigned int rank,
1199 armnn::PadDescriptor& padDescriptor)
1200{
1201 using HalOperand = typename HalPolicy::Operand;
1202
1203 const HalOperand* paddingsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
1204 if (!paddingsOperand)
1205 {
1206 return Fail("%s: Could not read paddings operand", __func__);
1207 }
1208
1209 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
1210 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
1211 {
1212 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
1213 }
1214
1215 std::vector<int32_t> paddings;
1216 GetTensorInt32Values<HalPolicy>(*paddingsOperand, paddings, model, data);
1217
1218 // add padding for each dimension of input tensor.
1219 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
1220 {
1221 int paddingBeforeInput = paddings[i];
1222 int paddingAfterInput = paddings[i + 1];
1223
1224 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
1225 {
1226 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
1227 }
1228
1229 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
1230 }
1231
1232 return true;
1233}
1234
1235template<typename HalPolicy,
1236 typename HalOperation = typename HalPolicy::Operation,
1237 typename HalModel = typename HalPolicy::Model>
arovir01b0717b52018-09-05 17:03:25 +01001238bool ConvertPooling2d(const HalOperation& operation,
1239 const char* operationName,
1240 armnn::PoolingAlgorithm poolType,
1241 const HalModel& model,
1242 ConversionData& data)
1243{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001244 using HalOperand = typename HalPolicy::Operand;
1245 using HalOperandType = typename HalPolicy::OperandType;
1246
1247 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
arovir01b0717b52018-09-05 17:03:25 +01001248 if (!input.IsValid())
1249 {
1250 return Fail("%s: Could not read input 0", operationName);
1251 }
1252
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001253 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
arovir01b0717b52018-09-05 17:03:25 +01001254 if (!output)
1255 {
1256 return Fail("%s: Could not read output 0", __func__);
1257 }
1258
1259 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1260 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
1261
arovir01b0717b52018-09-05 17:03:25 +01001262 armnn::Pooling2dDescriptor desc;
1263 desc.m_PoolType = poolType;
1264 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001265 desc.m_DataLayout = armnn::DataLayout::NHWC;
arovir01b0717b52018-09-05 17:03:25 +01001266
1267 ActivationFn activation;
1268
1269 if (operation.inputs.size() == 7)
1270 {
1271 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
1272 android::nn::PaddingScheme scheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001273 if (!GetInputPaddingScheme<HalPolicy>(operation, 1, scheme, model, data) ||
1274 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1275 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1276 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1277 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1278 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001279 {
1280 return Fail("%s: Operation has invalid inputs", operationName);
1281 }
1282
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001283 const unsigned int inputWidth = inputInfo.GetShape()[2];
1284 const unsigned int inputHeight = inputInfo.GetShape()[1];
arovir01b0717b52018-09-05 17:03:25 +01001285
1286 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
1287 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
1288 }
1289 else
1290 {
1291 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001292 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1293 !GetInputScalar<HalPolicy>(operation, 2, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1294 !GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1295 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1296 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1297 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1298 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_PoolWidth, model, data) ||
1299 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_PoolHeight, model, data) ||
1300 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data))
arovir01b0717b52018-09-05 17:03:25 +01001301 {
1302 return Fail("%s: Operation has invalid inputs", operationName);
1303 }
1304 }
1305
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001306 bool isSupported = false;
1307 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1308 IsPooling2dSupported,
1309 data.m_Backends,
1310 isSupported,
1311 inputInfo,
1312 outputInfo,
1313 desc);
1314 if (!isSupported)
arovir01b0717b52018-09-05 17:03:25 +01001315 {
Éanna Ó Catháin3d1059c2018-10-11 15:53:04 +01001316 return false;
arovir01b0717b52018-09-05 17:03:25 +01001317 }
arovir01b0717b52018-09-05 17:03:25 +01001318
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001319 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
1320 if (!pooling2dLayer)
arovir01b0717b52018-09-05 17:03:25 +01001321 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001322 return Fail("%s: AddPooling2dLayer failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001323 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001324
1325 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, pooling2dLayer, data);
1326 if (!endLayer)
arovir01b0717b52018-09-05 17:03:25 +01001327 {
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001328 return Fail("%s: ProcessActivation failed", __func__);
arovir01b0717b52018-09-05 17:03:25 +01001329 }
Matteo Martincigh39fc5472018-10-26 16:39:28 +01001330
1331 input.Connect(pooling2dLayer->GetInputSlot(0));
1332
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001333 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *endLayer, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001334}
1335
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001336template<typename HalPolicy,
1337 typename HalOperation = typename HalPolicy::Operation,
1338 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001339bool ConvertConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1340{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001341 using HalOperand = typename HalPolicy::Operand;
1342 using HalOperandType = typename HalPolicy::OperandType;
1343
1344 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001345 if (!input.IsValid())
1346 {
1347 return Fail("%s: Operation has invalid inputs", __func__);
1348 }
1349
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001350 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001351 if (!output)
1352 {
1353 return Fail("%s: Could not read output 0", __func__);
1354 }
1355
1356 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Sadik Armagan2050c232019-07-23 16:59:58 +01001357 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001358
1359 // ArmNN does not currently support non-fixed weights or bias
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001360 const ConstTensorPin weightsPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 1, model, data);
1361 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001362
1363 if (!weightsPin.IsValid() || !biasPin.IsValid())
1364 {
1365 return Fail("%s: Operation has invalid inputs", __func__);
1366 }
1367
1368 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1369 armnn::ConstTensor bias = biasPin.GetConstTensor();
1370 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1371
1372 armnn::Convolution2dDescriptor desc;
1373 desc.m_DataLayout = armnn::DataLayout::NHWC;
1374 ActivationFn activation;
1375
1376 if (operation.inputs.size() >= 10)
1377 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001378 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1379 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1380 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1381 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1382 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1383 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1384 !GetInputActivationFunction<HalPolicy>(operation, 9, activation, model, data) ||
1385 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 11, desc, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001386 {
1387 return Fail("%s: Operation has invalid inputs", __func__);
1388 }
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001389 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 10, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001390 }
1391 else if (operation.inputs.size() >= 7)
1392 {
1393 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001394 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1395 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1396 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1397 !GetInputActivationFunction<HalPolicy>(operation, 6, activation, model, data) ||
1398 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 8, desc, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001399 {
1400 return Fail("%s: Operation has invalid inputs", __func__);
1401 }
1402
1403 const uint32_t kernelX = weights.GetShape()[2];
1404 const uint32_t kernelY = weights.GetShape()[1];
1405 const uint32_t inputX = inputInfo.GetShape()[2];
1406 const uint32_t inputY = inputInfo.GetShape()[1];
1407
1408 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1409 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1410
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001411 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 7, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001412 }
1413 else
1414 {
1415 return Fail("%s: Unsupported number of operation inputs", __func__);
1416 }
1417
1418 desc.m_BiasEnabled = true;
1419 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1420
Sadik Armagan2050c232019-07-23 16:59:58 +01001421 if (IsDynamicTensor(outputInfo))
1422 {
1423 try
1424 {
1425 ALOGD("Output shape not set, will infer from inputs");
1426 outputInfo.SetShape(InferConvolution2dOutputShape(inputInfo.GetShape(),
1427 weights.GetInfo().GetShape(),
1428 desc));
1429 }
1430 catch (armnn::Exception& e)
1431 {
1432 return Fail("%s: Could not infer dynamic output shape: %s", __func__, e.what());
1433 }
1434 }
1435
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001436 bool isSupported = false;
1437 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1438 IsConvolution2dSupported,
1439 data.m_Backends,
1440 isSupported,
1441 inputInfo,
1442 outputInfo,
1443 desc,
1444 weights.GetInfo(),
1445 biases);
1446 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001447 {
1448 return false;
1449 }
1450
1451 armnn::IConnectableLayer* startLayer =
1452 data.m_Network->AddConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1453
1454 if (!startLayer)
1455 {
1456 return Fail("%s: AddConvolution2dLayer failed", __func__);
1457 }
1458
1459 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1460
1461 if (!endLayer)
1462 {
1463 return Fail("%s: ProcessActivation failed", __func__);
1464 }
1465
1466 input.Connect(startLayer->GetInputSlot(0));
1467
Sadik Armagan2050c232019-07-23 16:59:58 +01001468 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1469 0,
1470 *endLayer,
1471 model,
1472 data,
1473 armnn::Optional<armnn::TensorInfo>(outputInfo));
Mike Kellyb5fdf382019-06-11 16:35:25 +01001474}
1475
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001476template<typename HalPolicy,
1477 typename HalOperation = typename HalPolicy::Operation,
1478 typename HalModel = typename HalPolicy::Model>
Mike Kellyb5fdf382019-06-11 16:35:25 +01001479bool ConvertDepthwiseConv2d(const HalOperation& operation, const HalModel& model, ConversionData& data)
1480{
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001481 using HalOperand = typename HalPolicy::Operand;
1482 using HalOperandType = typename HalPolicy::OperandType;
1483
1484 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001485
1486 if (!input.IsValid())
1487 {
1488 return Fail("%s: Operation has invalid inputs", __func__);
1489 }
1490
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001491 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001492
1493 if (!output)
1494 {
1495 return Fail("%s: Could not read output 0", __func__);
1496 }
1497
1498 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
Sadik Armagan2050c232019-07-23 16:59:58 +01001499 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001500
1501 // ArmNN does not currently support non-fixed weights or bias
1502
1503 // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ]
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001504 const HalOperand* weightsOperand = GetInputOperand<HalPolicy>(operation, 1, model);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001505
1506 if (weightsOperand == nullptr)
1507 {
1508 return Fail("%s: Operand is invalid", __func__);
1509 }
1510 armnn::DepthwiseConvolution2dDescriptor desc;
1511 desc.m_DataLayout = armnn::DataLayout::NHWC;
1512
1513 // Look ahead to find the optional DataLayout, if present
1514 if (operation.inputs.size() >= 12)
1515 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001516 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 11, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001517 }
1518 else if (operation.inputs.size() >= 9)
1519 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001520 desc.m_DataLayout = OptionalDataLayout<HalPolicy>(operation, 8, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001521 }
1522
1523 armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout);
1524 unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
1525 unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
1526 unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
1527
1528 // Reinterpret weight data as [ H, W, I, M ]
1529 armnn::TensorShape weightsShape({ weightsOperand->dimensions[1],
1530 weightsOperand->dimensions[2],
1531 inputInfo.GetShape()[channelsIndex],
1532 weightsOperand->dimensions[3] / inputInfo.GetShape()[channelsIndex] });
1533
1534 // Swizzle weight data [ H, W, I, M ] -> [ M, I, H, W ]
1535 const armnn::PermutationVector HWIMToMIHW = { 2U, 3U, 1U, 0U };
1536
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001537 const ConstTensorPin weightsPin =
1538 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
1539 1,
1540 model,
1541 data,
1542 HWIMToMIHW,
1543 &weightsShape);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001544
1545 // Bias is a 1D tensor
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001546 const ConstTensorPin biasPin = ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
Mike Kellyb5fdf382019-06-11 16:35:25 +01001547
1548 if (!weightsPin.IsValid() || !biasPin.IsValid())
1549 {
1550 return Fail("%s: Operation has invalid inputs", __func__);
1551 }
1552
1553 armnn::ConstTensor weights = weightsPin.GetConstTensor();
1554 armnn::ConstTensor bias = biasPin.GetConstTensor();
1555 SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo);
1556
1557 ActivationFn activation;
1558
1559 if (operation.inputs.size() >= 11)
1560 {
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001561 if (!GetInputScalar<HalPolicy>(operation, 3, HalOperandType::INT32, desc.m_PadLeft, model, data) ||
1562 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_PadRight, model, data) ||
1563 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_PadTop, model, data) ||
1564 !GetInputScalar<HalPolicy>(operation, 6, HalOperandType::INT32, desc.m_PadBottom, model, data) ||
1565 !GetInputScalar<HalPolicy>(operation, 7, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1566 !GetInputScalar<HalPolicy>(operation, 8, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1567 !GetInputActivationFunction<HalPolicy>(operation, 10, activation, model, data) ||
1568 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 12, desc, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001569 {
1570 return Fail("%s: Operation has invalid inputs", __func__);
1571 }
1572 }
1573 else if (operation.inputs.size() >= 8)
1574 {
1575 android::nn::PaddingScheme paddingScheme;
Aron Virginas-Tarcd700e42019-06-14 14:54:52 +01001576 if (!GetInputPaddingScheme<HalPolicy>(operation, 3, paddingScheme, model, data) ||
1577 !GetInputScalar<HalPolicy>(operation, 4, HalOperandType::INT32, desc.m_StrideX, model, data) ||
1578 !GetInputScalar<HalPolicy>(operation, 5, HalOperandType::INT32, desc.m_StrideY, model, data) ||
1579 !GetInputActivationFunction<HalPolicy>(operation, 7, activation, model, data) ||
1580 !GetOptionalConvolutionDilationParams<HalPolicy>(operation, 9, desc, model, data))
Mike Kellyb5fdf382019-06-11 16:35:25 +01001581 {
1582 return Fail("%s: Operation has invalid inputs", __func__);
1583 }
1584
1585 const uint32_t kernelX = weights.GetShape()[3];
1586 const uint32_t kernelY = weights.GetShape()[2];
1587 const uint32_t inputX = inputInfo.GetShape()[widthIndex];
1588 const uint32_t inputY = inputInfo.GetShape()[heightIndex];
1589
1590 CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme);
1591 CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme);
1592 }
1593 else
1594 {
1595 return Fail("%s: Unsupported number of operation inputs", __func__);
1596 }
1597
1598 desc.m_BiasEnabled = true;
1599 armnn::Optional<armnn::TensorInfo> biases(bias.GetInfo());
1600
Sadik Armagan2050c232019-07-23 16:59:58 +01001601 if (IsDynamicTensor(outputInfo))
1602 {
1603 try
1604 {
1605 ALOGD("Output shape not set, will infer from inputs");
1606 outputInfo.SetShape(InferDepthwiseConvolution2dOutputShape(inputInfo.GetShape(),
1607 weights.GetInfo().GetShape(),
1608 desc));
1609 }
1610 catch (armnn::Exception& e)
1611 {
1612 return Fail("%s: Could not infer dynamic output shape: %s", __func__, e.what());
1613 }
1614 }
1615
Ferran Balaguerd30093c2019-07-09 17:04:47 +01001616 bool isSupported = false;
1617 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1618 IsDepthwiseConvolutionSupported,
1619 data.m_Backends,
1620 isSupported,
1621 inputInfo,
1622 outputInfo,
1623 desc,
1624 weights.GetInfo(),
1625 biases);
1626 if (!isSupported)
Mike Kellyb5fdf382019-06-11 16:35:25 +01001627 {
1628 return false;
1629 }
1630
1631 armnn::IConnectableLayer* startLayer =
1632 data.m_Network->AddDepthwiseConvolution2dLayer(desc, weights, armnn::Optional<armnn::ConstTensor>(bias));
1633 if (!startLayer)
1634 {
1635 return Fail("%s: AddDepthwiseConvolution2dLayer failed", __func__);
1636 }
1637
1638 armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data);
1639 if (!endLayer)
1640 {
1641 return Fail("%s: ProcessActivation failed", __func__);
1642 }
1643
1644 input.Connect(startLayer->GetInputSlot(0));
1645
Sadik Armagan2050c232019-07-23 16:59:58 +01001646 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1647 0,
1648 *endLayer,
1649 model,
1650 data,
1651 armnn::Optional<armnn::TensorInfo>(outputInfo));
arovir01b0717b52018-09-05 17:03:25 +01001652}
1653
Mike Kelly3c673942019-07-25 09:26:06 +01001654template<typename HalPolicy,
1655 typename HalOperation = typename HalPolicy::Operation,
1656 typename HalOperand = typename HalPolicy::Operand,
1657 typename HalModel = typename HalPolicy::Model>
1658bool ConvertPad(HalOperation& operation, const HalModel& model, ConversionData& data)
1659{
1660 ALOGV("hal_1_1::HalPolicy::ConvertPad()");
1661
1662 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
1663 if (!input.IsValid())
1664 {
1665 return Fail("%s: Operation has invalid inputs", __func__);
1666 }
1667
1668 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
1669 unsigned int rank = inputInfo.GetNumDimensions();
1670
1671 armnn::PadDescriptor descriptor;
1672 if (!ConvertPaddings<HalPolicy>(operation, model, data, rank, descriptor))
1673 {
1674 return Fail("%s: Could not convert paddings", __func__);
1675 }
1676
1677 // Before Android Q, the pad value for ANEURALNETWORKS_TENSOR_QUANT8_ASYMM was undefined. Since Android Q the pad
1678 // value must be "logical zero" we set it to be equal to the QuantizationOffset so effectively it ends up as
1679 // (QuantizationOffset - QuantizationOffset) * scale = 0.
1680 if (inputInfo.GetDataType() == armnn::DataType::QuantisedAsymm8)
1681 {
1682 descriptor.m_PadValue = inputInfo.GetQuantizationOffset();
1683 }
1684
1685 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
1686 if (!output)
1687 {
1688 return Fail("%s: Could not read output", __func__);
1689 }
1690
1691 armnn::TensorInfo outputInfo = GetTensorInfoForOperand(*output);
1692 if (IsDynamicTensor(outputInfo))
1693 {
1694 ALOGD("Output shape not set, will infer from inputs");
1695 outputInfo.SetShape(InferPadOutputShape(inputInfo.GetShape(), descriptor.m_PadList));
1696 }
1697
1698 bool isSupported = false;
1699 FORWARD_LAYER_SUPPORT_FUNC(__func__,
1700 IsPadSupported,
1701 data.m_Backends,
1702 isSupported,
1703 inputInfo,
1704 outputInfo,
1705 descriptor);
1706 if (!isSupported)
1707 {
1708 return false;
1709 }
1710
1711 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
1712 assert(layer != nullptr);
1713 input.Connect(layer->GetInputSlot(0));
1714 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1715
1716 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation,
1717 0,
1718 *layer,
1719 model,
1720 data,
1721 armnn::Optional<armnn::TensorInfo>(outputInfo));
1722}
1723
saoste01b8471482018-10-10 09:44:51 +01001724} // namespace armnn_driver