blob: a44f9eef1da4df85dda8282e0c7e30ce43efbfa2 [file] [log] [blame]
Sadik Armagan62483be2020-10-23 17:14:43 +01001//
Ryan OShea3ad2e142023-01-13 10:19:20 +00002// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved.
Sadik Armagan62483be2020-10-23 17:14:43 +01003// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
Matthew Sloyan11572322023-03-16 10:17:51 +00008#include <ClassicDelegateUtils.hpp>
9#include <SharedFunctions.hpp>
Sadik Armagan32ca1442020-11-13 17:51:56 +000010
Sadik Armagan62483be2020-10-23 17:14:43 +010011#include <tensorflow/lite/builtin_ops.h>
12#include <tensorflow/lite/c/builtin_op_data.h>
13#include <tensorflow/lite/c/common.h>
14#include <tensorflow/lite/minimal_logging.h>
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +000015#include <tensorflow/lite/kernels/internal/tensor.h>
Sadik Armagan62483be2020-10-23 17:14:43 +010016
17namespace armnnDelegate
18{
19
Sadik Armagan32ca1442020-11-13 17:51:56 +000020TfLiteStatus VisitConv2dOperator(DelegateData& delegateData,
21 TfLiteContext* tfLiteContext,
22 TfLiteNode* tfLiteNode,
23 int nodeIndex,
24 int32_t operatorCode)
25{
26 auto numInputs = tfLiteNode->inputs->size;
27 if (numInputs < 2)
28 {
29 TF_LITE_MAYBE_KERNEL_LOG(
30 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
31 2, numInputs, nodeIndex);
32 return kTfLiteError;
33 }
34 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
35
36 armnn::Convolution2dDescriptor descriptor;
37 const auto params = reinterpret_cast<TfLiteConvParams*>(tfLiteNode->builtin_data);
38
Mike Kelly84d63782022-05-06 12:14:16 +010039 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Sadik Armagan32ca1442020-11-13 17:51:56 +000040 descriptor.m_BiasEnabled = biasEnabled;
41 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
42 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
43 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
44 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
45 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
46
47 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
48 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +010049 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +000050 {
Sadik Armagan32ca1442020-11-13 17:51:56 +000051 return kTfLiteError;
52 }
Matthew Sloyanc52190a2023-05-08 11:33:55 +010053
Sadik Armagan32ca1442020-11-13 17:51:56 +000054 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +010055 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +000056 {
Sadik Armagan32ca1442020-11-13 17:51:56 +000057 return kTfLiteError;
58 }
59
60 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +010061 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +000062 {
Sadik Armagan32ca1442020-11-13 17:51:56 +000063 return kTfLiteError;
64 }
65
66 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +010067 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan32ca1442020-11-13 17:51:56 +000068
Ryan OShea3ad2e142023-01-13 10:19:20 +000069 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConvParams*>(tfLiteNode->builtin_data);
Ryan OShea475c7a82023-01-30 14:24:15 +000070 TfLiteFusedActivation activationType=kTfLiteActNone;
Ryan OShea3ad2e142023-01-13 10:19:20 +000071 if (tfLiteNodeParameters)
72 {
73 activationType = tfLiteNodeParameters->activation;
Ryan OShea3ad2e142023-01-13 10:19:20 +000074 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData, tfLiteContext, outputTensorInfo,
75 outputTensorInfo, activationType);
76 if(activationStatus != kTfLiteOk)
77 {
78 return kTfLiteError;
79 }
80
81 }
82
Ryan OShea4c231de2023-01-17 15:19:20 +000083 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
Sadik Armagan32ca1442020-11-13 17:51:56 +000084
85 armnn::TensorInfo biasTensorInfo;
86 if(biasEnabled)
87 {
88 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +010089 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +000090 {
Sadik Armagan32ca1442020-11-13 17:51:56 +000091 return kTfLiteError;
92 }
93 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
94 }
95 else
96 {
97 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
98 }
99
100 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
101
102 // TfLite uses NHWC tensors
103 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
104 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
105
106 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
107 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
108
109 // Calculate padding
110 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
111 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
112 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
113 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
114
Cathal Corbett53837672022-09-01 11:34:37 +0100115 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000116 if (!delegateData.m_Network)
117 {
118 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000119 FORWARD_LAYER_SUPPORT_FUNC("CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000120 tfLiteContext,
121 IsConvolution2dSupported,
122 delegateData.m_Backends,
123 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100124 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000125 inputTensorInfo,
126 outputTensorInfo,
127 descriptor,
128 filterTensorInfo,
129 optionalBiasInfo);
130 return isSupported ? kTfLiteOk : kTfLiteError;
131 }
132
Sadik Armagan32ca1442020-11-13 17:51:56 +0000133 // Set up filter and biases
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100134 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution2dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100135 layer->SetBackendId(setBackend);
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100136
Ryan OShea4c231de2023-01-17 15:19:20 +0000137 if(filterTensorInfo.IsConstant())
Sadik Armagan90a119b2022-08-05 16:12:49 +0100138 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100139 auto filter = CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[1]], filterTensorInfo);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000140
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100141 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100142 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
143 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
144 }
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100145
146 if (biasEnabled)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000147 {
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100148 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Ryan OShea4c231de2023-01-17 15:19:20 +0000149 if(biasTensorInfo.IsConstant())
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100150 {
151 auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, biasTensorInfo);
152 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
153 ARMNN_ASSERT(biasLayer != nullptr);
154 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
155 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
156 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000157 }
158
Ryan OShea4c231de2023-01-17 15:19:20 +0000159 // The data input can also be constant, so we must check that this is also allocated to an input slot
160 if(inputTensorInfo.IsConstant())
161 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100162 auto input = CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[0]], inputTensorInfo);
Ryan OShea4c231de2023-01-17 15:19:20 +0000163
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100164 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
Ryan OShea4c231de2023-01-17 15:19:20 +0000165 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
166 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
167 }
168
Sadik Armagan32ca1442020-11-13 17:51:56 +0000169 ARMNN_ASSERT(layer != nullptr);
170
171 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
172 outputSlot.SetTensorInfo(outputTensorInfo);
173
Ryan OSheaa544f0f2023-01-25 18:10:20 +0000174 if(Connect(layer, tfLiteNode, delegateData) != kTfLiteOk)
175 {
176 return kTfLiteError;
177 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000178
Sadik Armagan32ca1442020-11-13 17:51:56 +0000179 if (!tfLiteNodeParameters)
180 {
181 // No Activation
182 return kTfLiteOk;
183 }
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100184
Ryan OShea3ad2e142023-01-13 10:19:20 +0000185 // Check and Create activation
Sadik Armagan32ca1442020-11-13 17:51:56 +0000186 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000187}
188
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100189// Conv3d is only correctly supported for external delegates from TF Lite v2.6, as there was a breaking bug in v2.5.
190#if defined(ARMNN_POST_TFLITE_2_5)
191TfLiteStatus VisitConv3dOperator(DelegateData& delegateData,
192 TfLiteContext* tfLiteContext,
193 TfLiteNode* tfLiteNode,
194 int nodeIndex,
195 int32_t operatorCode)
196{
197 auto numInputs = tfLiteNode->inputs->size;
198 if (numInputs < 2)
199 {
200 TF_LITE_MAYBE_KERNEL_LOG(
201 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
202 2, numInputs, nodeIndex);
203 return kTfLiteError;
204 }
205 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
206
207 armnn::Convolution3dDescriptor descriptor;
208 const auto params = reinterpret_cast<TfLiteConv3DParams*>(tfLiteNode->builtin_data);
209
Mike Kelly84d63782022-05-06 12:14:16 +0100210 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100211 descriptor.m_BiasEnabled = biasEnabled;
212 descriptor.m_DataLayout = armnn::DataLayout::NDHWC;
213 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
214 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
215 descriptor.m_StrideZ = NonNegative(params->stride_depth, nodeIndex);
216 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
217 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
218 descriptor.m_DilationZ = NonNegative(params->dilation_depth_factor, nodeIndex);
219
220 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
221 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
222 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
223 {
224 return kTfLiteError;
225 }
226
227 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
228 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
229 {
230 return kTfLiteError;
231 }
232
233 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
234 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
235 {
236 return kTfLiteError;
237 }
238
239 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100240 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100241
Ryan OShea3ad2e142023-01-13 10:19:20 +0000242 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConv3DParams*>(tfLiteNode->builtin_data);
Ryan OShea475c7a82023-01-30 14:24:15 +0000243 TfLiteFusedActivation activationType=kTfLiteActNone;
Ryan OShea3ad2e142023-01-13 10:19:20 +0000244 if (tfLiteNodeParameters)
245 {
246 activationType = tfLiteNodeParameters->activation;
Ryan OShea3ad2e142023-01-13 10:19:20 +0000247 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData, tfLiteContext, outputTensorInfo,
248 outputTensorInfo, activationType);
249 if(activationStatus != kTfLiteOk)
250 {
251 return kTfLiteError;
252 }
253
254 }
255
Ryan OShea4c231de2023-01-17 15:19:20 +0000256 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100257
258 armnn::TensorInfo biasTensorInfo;
259 if(biasEnabled)
260 {
261 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
262 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
263 {
264 return kTfLiteError;
265 }
266 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
267 }
268 else
269 {
270 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
271 }
272
273 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
274
275 // TfLite uses NDHWC tensors
276 const unsigned int inputDepth = inputTensorInfo.GetShape()[1];
277 const unsigned int inputHeight = inputTensorInfo.GetShape()[2];
278 const unsigned int inputWidth = inputTensorInfo.GetShape()[3];
279
280 // Assuming the filter is DHWIO : Depth, Height, Width, OutputChannels, InputChannels
281 const unsigned int filterDepth = filterTensorInfo.GetShape()[0];
282 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
283 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
284
285 // Calculate padding
286 CalcPadding(inputDepth, filterDepth, descriptor.m_StrideZ, descriptor.m_DilationZ,
287 descriptor.m_PadFront, descriptor.m_PadBack, params->padding);
288 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
289 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
290 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
291 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
292
293 // If the m_Network is a nullptr, this signals that a prerequisite TfLite callback is required to clarify the
294 // support for the operator
295 // If supported, VisitConvolutionOperator will be called again to add the layer to the network as seen below.
Cathal Corbett53837672022-09-01 11:34:37 +0100296 armnn::BackendId setBackend;
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100297 if (!delegateData.m_Network)
298 {
299 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000300 FORWARD_LAYER_SUPPORT_FUNC("CONV3D",
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100301 tfLiteContext,
302 IsConvolution3dSupported,
303 delegateData.m_Backends,
304 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100305 setBackend,
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100306 inputTensorInfo,
307 outputTensorInfo,
308 descriptor,
309 filterTensorInfo,
310 optionalBiasInfo);
311 return isSupported ? kTfLiteOk : kTfLiteError;
312 }
313
314 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution3dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100315 layer->SetBackendId(setBackend);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100316 ARMNN_ASSERT(layer != nullptr);
317
318 // Add a constant layer for weights and biases if inputs are constant,
319 // which are connected to the Convolution3d layer as inputs.
Ryan OShea4c231de2023-01-17 15:19:20 +0000320 if (filterTensorInfo.IsConstant())
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100321 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100322 auto filter = CreateConstTensor(&tfLiteFilterTensor, filterTensorInfo);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100323
324 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
325 ARMNN_ASSERT(weightsLayer != nullptr);
326
327 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
328 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
329 }
330
331 if(biasEnabled)
332 {
333 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Ryan OShea4c231de2023-01-17 15:19:20 +0000334 if(biasTensorInfo.IsConstant())
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100335 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100336 auto biases = CreateConstTensor(&tfLiteBiasTensor, biasTensorInfo);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100337
338 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biases);
339 ARMNN_ASSERT(biasLayer != nullptr);
340
341 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
342 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
343 }
344 }
345
Ryan OShea4c231de2023-01-17 15:19:20 +0000346 // The data input can also be constant, so we must check that this is also allocated to an input slot
347 if(inputTensorInfo.IsConstant())
348 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100349 auto input = CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[0]], inputTensorInfo);
Ryan OShea4c231de2023-01-17 15:19:20 +0000350
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100351 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
Ryan OShea4c231de2023-01-17 15:19:20 +0000352 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
353 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
354 }
355
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100356 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
357 outputSlot.SetTensorInfo(outputTensorInfo);
358
Ryan OSheaa544f0f2023-01-25 18:10:20 +0000359 if(Connect(layer, tfLiteNode, delegateData) != kTfLiteOk)
360 {
361 return kTfLiteError;
362 }
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100363
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100364 if (!tfLiteNodeParameters)
365 {
366 // No Activation
367 return kTfLiteOk;
368 }
369
Ryan OShea3ad2e142023-01-13 10:19:20 +0000370 // Check and create activation
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100371 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
372}
373#endif
374
Sadik Armagan32ca1442020-11-13 17:51:56 +0000375TfLiteStatus VisitDepthwiseConv2dOperator(DelegateData& delegateData,
376 TfLiteContext* tfLiteContext,
377 TfLiteNode* tfLiteNode,
378 int nodeIndex,
379 int32_t operatorCode)
380{
381 auto numInputs = tfLiteNode->inputs->size;
382 if (numInputs < 2)
383 {
384 TF_LITE_MAYBE_KERNEL_LOG(
385 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
386 2, numInputs, nodeIndex);
387 return kTfLiteError;
388 }
389 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
390
Mike Kelly84d63782022-05-06 12:14:16 +0100391 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000392
393 armnn::DepthwiseConvolution2dDescriptor descriptor;
394 const auto params = reinterpret_cast<TfLiteDepthwiseConvParams*>(tfLiteNode->builtin_data);
395
396 descriptor.m_BiasEnabled = biasEnabled;
397 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
398 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
399 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
400 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
401 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
402
403 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
404 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100405 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000406 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000407 return kTfLiteError;
408 }
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100409
Sadik Armagan32ca1442020-11-13 17:51:56 +0000410 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100411 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000412 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000413 return kTfLiteError;
414 }
415
416 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100417 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000418 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000419 return kTfLiteError;
420 }
421
422 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100423 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000424
Ryan OShea3ad2e142023-01-13 10:19:20 +0000425 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteDepthwiseConvParams *>(tfLiteNode->builtin_data);
Ryan OShea475c7a82023-01-30 14:24:15 +0000426 TfLiteFusedActivation activationType = kTfLiteActNone;
Ryan OShea3ad2e142023-01-13 10:19:20 +0000427 if (tfLiteNodeParameters)
428 {
429 activationType = tfLiteNodeParameters->activation;
Ryan OShea3ad2e142023-01-13 10:19:20 +0000430 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData, tfLiteContext, outputTensorInfo,
431 outputTensorInfo, activationType);
432 if(activationStatus != kTfLiteOk)
433 {
434 return kTfLiteError;
435 }
436
437 }
438
Ryan OShea4c231de2023-01-17 15:19:20 +0000439 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000440
441 // Assuming input is NHWC
442 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
443 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
444
445 // TensorflowLite weights come in the format [1, H, W, I * M]
446 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
447 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
448
Sadik Armagan32ca1442020-11-13 17:51:56 +0000449 // Calculate padding
450 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
451 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
452 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
453 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
454
455 armnn::TensorInfo biasTensorInfo;
456 if(biasEnabled)
457 {
458 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100459 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000460 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000461 return kTfLiteError;
462 }
463 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
464 }
465 else
466 {
467 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
468 }
469
Cathal Corbett53837672022-09-01 11:34:37 +0100470 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000471 if (!delegateData.m_Network)
472 {
473 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000474 FORWARD_LAYER_SUPPORT_FUNC("DEPTHWISE_CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000475 tfLiteContext,
476 IsDepthwiseConvolutionSupported,
477 delegateData.m_Backends,
478 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100479 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000480 inputTensorInfo,
481 outputTensorInfo,
482 descriptor,
Sadik Armagan90a119b2022-08-05 16:12:49 +0100483 filterTensorInfo,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000484 armnn::Optional<armnn::TensorInfo>(biasTensorInfo));
485 return isSupported ? kTfLiteOk : kTfLiteError;
486 }
487
Cathal Corbett06902652022-04-14 17:55:11 +0100488 armnn::IConnectableLayer* layer = delegateData.m_Network->AddDepthwiseConvolution2dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100489 layer->SetBackendId(setBackend);
Narumol Prangnawarat16725422020-11-20 16:17:48 +0000490
Ryan OShea4c231de2023-01-17 15:19:20 +0000491 if(filterTensorInfo.IsConstant())
Sadik Armagan90a119b2022-08-05 16:12:49 +0100492 {
493 // For depthwise the weights layout is the same as for tflite [1, H, W, I*M]. No permutation required.
494 auto filter = CreateConstTensor(&tfLiteFilterTensor, filterTensorInfo);
495
496 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
497 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
498 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
499 }
Cathal Corbett06902652022-04-14 17:55:11 +0100500
501 if (biasEnabled)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000502 {
Cathal Corbett06902652022-04-14 17:55:11 +0100503 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Ryan OShea4c231de2023-01-17 15:19:20 +0000504 if(biasTensorInfo.IsConstant())
Cathal Corbett06902652022-04-14 17:55:11 +0100505 {
506 auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, biasTensorInfo);
507 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
508 ARMNN_ASSERT(biasLayer != nullptr);
509 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
510 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
511 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000512 }
513
Ryan OShea4c231de2023-01-17 15:19:20 +0000514 // The data input can also be constant, so we must check that this is also allocated to an input slot
515 if(inputTensorInfo.IsConstant())
516 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100517 auto input = CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[0]], inputTensorInfo);
Ryan OShea4c231de2023-01-17 15:19:20 +0000518
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100519 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
Ryan OShea4c231de2023-01-17 15:19:20 +0000520 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
521 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
522 }
523
Sadik Armagan32ca1442020-11-13 17:51:56 +0000524 ARMNN_ASSERT(layer != nullptr);
525
526 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
527 outputSlot.SetTensorInfo(outputTensorInfo);
528
Ryan OSheaa544f0f2023-01-25 18:10:20 +0000529 if(Connect(layer, tfLiteNode, delegateData) != kTfLiteOk)
530 {
531 return kTfLiteError;
532 }
533
Sadik Armagan32ca1442020-11-13 17:51:56 +0000534 if (!tfLiteNodeParameters)
535 {
536 // No Activation
537 return kTfLiteOk;
538 }
Ryan OShea3ad2e142023-01-13 10:19:20 +0000539 // Check and create activation
Sadik Armagan32ca1442020-11-13 17:51:56 +0000540 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
541}
542
543TfLiteStatus VisitTransposeConv2dOperator(DelegateData& delegateData,
544 TfLiteContext* tfLiteContext,
545 TfLiteNode* tfLiteNode,
546 int nodeIndex,
547 int32_t operatorCode)
548{
549 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 3, nodeIndex));
550 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
551
552 armnn::TransposeConvolution2dDescriptor descriptor;
553 auto* parameters = reinterpret_cast<TfLiteTransposeConvParams*>(tfLiteNode->builtin_data);
554 descriptor.m_BiasEnabled = false;
555 descriptor.m_StrideX = NonNegative(parameters->stride_width, nodeIndex);
556 descriptor.m_StrideY = NonNegative(parameters->stride_height, nodeIndex);
557 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
558
559 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
560 const TfLiteTensor& tfLiteOutputShapeTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100561 if (!IsValid(tfLiteContext, tfLiteOutputShapeTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000562 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000563 return kTfLiteError;
564 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000565
566 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100567 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000568 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000569 return kTfLiteError;
570 }
571
572 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100573 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000574 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000575 return kTfLiteError;
576 }
577
578 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100579 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
Sadik Armagan32ca1442020-11-13 17:51:56 +0000580 {
Sadik Armagan32ca1442020-11-13 17:51:56 +0000581 return kTfLiteError;
582 }
583
584 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100585 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Ryan OShea4c231de2023-01-17 15:19:20 +0000586 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000587
588 // TfLite uses NHWC tensors
589 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
590 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
591
592 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
593 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
594
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100595 // This block determines the output shape of the transpose convolution.
596 // If the output shape tensor is a constant, we can access the data at load time and set the shape of the layer.
597 // If this is not constant, we do not have access to the shape data, so we have to use infer output shape.
598 if (tflite::IsConstantTensor(&tfLiteOutputShapeTensor))
599 {
600 const armnn::TensorInfo outputShapeTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputShapeTensor);
601 std::vector<int32_t> outputShape(outputShapeTensorInfo.GetNumElements());
602 if (outputShapeTensorInfo.GetDataType() == armnn::DataType::Signed32)
603 {
604 for(unsigned int i=0; i < outputShapeTensorInfo.GetNumElements(); ++i)
605 {
606 outputShape[i] = ::tflite::GetTensorData<int32_t>(&tfLiteOutputShapeTensor)[i];
607 }
608 }
609
610 if (outputShapeTensorInfo.GetDataType() == armnn::DataType::QAsymmU8)
611 {
612 for(unsigned int i=0; i < outputShapeTensorInfo.GetNumElements(); ++i)
613 {
614 outputShape[i] = ::tflite::GetTensorData<uint8_t>(&tfLiteOutputShapeTensor)[i];
615 }
616 }
617 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
618 for (int dimension : outputShape)
619 {
620 descriptor.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
621 }
622 descriptor.m_OutputShapeEnabled = true;
623
624 // TfLite uses NHWC tensors
625 const unsigned int outputHeight = descriptor.m_OutputShape[1];
626 const unsigned int outputWidth = descriptor.m_OutputShape[2];
627
628 CalcPadding(inputHeight,
629 filterHeight,
630 descriptor.m_StrideY,
631 1, // DilationY
632 descriptor.m_PadTop,
633 descriptor.m_PadBottom,
634 parameters->padding,
635 outputHeight);
636
637 CalcPadding(inputWidth,
638 filterWidth,
639 descriptor.m_StrideX,
640 1, // DilationX
641 descriptor.m_PadLeft,
642 descriptor.m_PadRight,
643 parameters->padding,
644 outputWidth);
645 }
646 else
647 {
648 CalcPadding(inputHeight,
649 filterHeight,
650 descriptor.m_StrideY,
651 1, // DilationY
652 descriptor.m_PadTop,
653 descriptor.m_PadBottom,
654 parameters->padding);
655
656 CalcPadding(inputWidth,
657 filterWidth,
658 descriptor.m_StrideX,
659 1, // DilationX
660 descriptor.m_PadLeft,
661 descriptor.m_PadRight,
662 parameters->padding);
663 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000664
665 // Set up filter
666 auto filterTensor = CreateConstTensor(&tfLiteFilterTensor,
Ryan OShea4c231de2023-01-17 15:19:20 +0000667 filterTensorInfo);
Cathal Corbett53837672022-09-01 11:34:37 +0100668 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000669 if (!delegateData.m_Network)
670 {
671 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000672 FORWARD_LAYER_SUPPORT_FUNC("TRANSPOSE_CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000673 tfLiteContext,
674 IsTransposeConvolution2dSupported,
675 delegateData.m_Backends,
676 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100677 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000678 inputTensorInfo,
679 outputTensorInfo,
680 descriptor,
681 filterTensorInfo,
682 armnn::EmptyOptional());
683 return isSupported ? kTfLiteOk : kTfLiteError;
684 }
685
686 armnn::IConnectableLayer* layer = delegateData.m_Network->AddTransposeConvolution2dLayer(descriptor,
687 filterTensor,
688 armnn::EmptyOptional());
Cathal Corbett53837672022-09-01 11:34:37 +0100689 layer->SetBackendId(setBackend);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000690 ARMNN_ASSERT(layer != nullptr);
691
Ryan OShea4c231de2023-01-17 15:19:20 +0000692 // The data input can be constant, so we must check that this is allocated to an input slot
693 if(inputTensorInfo.IsConstant())
694 {
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100695 auto input = CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[2]], inputTensorInfo);
Ryan OShea4c231de2023-01-17 15:19:20 +0000696
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100697 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
Ryan OShea4c231de2023-01-17 15:19:20 +0000698 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
699 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
700 }
701
Sadik Armagan32ca1442020-11-13 17:51:56 +0000702 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
703 outputSlot.SetTensorInfo(outputTensorInfo);
704
705 // Connect
Keith Davis892fafe2020-11-26 17:40:35 +0000706 if (delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->inputs->data[2])] != nullptr)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000707 {
Keith Davis892fafe2020-11-26 17:40:35 +0000708 delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->inputs->data[2])]->
709 Connect(layer->GetInputSlot(0));
Sadik Armagan32ca1442020-11-13 17:51:56 +0000710 }
711
712 // Prepare output slots
713 for (unsigned int outputIndex = 0; outputIndex < layer->GetNumOutputSlots(); ++outputIndex)
714 {
715 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(outputIndex);
Keith Davis892fafe2020-11-26 17:40:35 +0000716 delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->outputs->data[outputIndex])] =
717 &outputSlot;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000718 }
719 return kTfLiteOk;
720}
721
Sadik Armagan62483be2020-10-23 17:14:43 +0100722TfLiteStatus VisitConvolutionOperator(DelegateData& delegateData,
723 TfLiteContext* tfLiteContext,
724 TfLiteNode* tfLiteNode,
725 int nodeIndex,
726 int32_t operatorCode)
727{
Sadik Armagan32ca1442020-11-13 17:51:56 +0000728 switch(operatorCode)
729 {
730 case kTfLiteBuiltinConv2d:
731 return VisitConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100732// Conv3d is only correctly supported for external delegates from TF Lite v2.6, as there was a breaking bug in v2.5.
733#if defined(ARMNN_POST_TFLITE_2_5)
734 case kTfLiteBuiltinConv3d:
735 return VisitConv3dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
736#endif
Sadik Armagan32ca1442020-11-13 17:51:56 +0000737 case kTfLiteBuiltinDepthwiseConv2d:
738 return VisitDepthwiseConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
739 case kTfLiteBuiltinTransposeConv:
740 return VisitTransposeConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
741 default:
742 return kTfLiteError;
743 }
Sadik Armagan62483be2020-10-23 17:14:43 +0100744}
745
746} // namespace armnnDelegate