blob: e307bb9be36cc2e3e8fa8b99ce3481bd142f9852 [file] [log] [blame]
Sadik Armagan62483be2020-10-23 17:14:43 +01001//
Sadik Armagan90a119b2022-08-05 16:12:49 +01002// Copyright © 2022 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
Sadik Armagan32ca1442020-11-13 17:51:56 +00008#include "DelegateUtils.hpp"
9
Sadik Armagan62483be2020-10-23 17:14:43 +010010#include <tensorflow/lite/builtin_ops.h>
11#include <tensorflow/lite/c/builtin_op_data.h>
12#include <tensorflow/lite/c/common.h>
13#include <tensorflow/lite/minimal_logging.h>
Sadik Armagan32ca1442020-11-13 17:51:56 +000014#include "tensorflow/lite/kernels/internal/tensor.h"
Sadik Armagan62483be2020-10-23 17:14:43 +010015
16namespace armnnDelegate
17{
18
Sadik Armagan32ca1442020-11-13 17:51:56 +000019TfLiteStatus VisitConv2dOperator(DelegateData& delegateData,
20 TfLiteContext* tfLiteContext,
21 TfLiteNode* tfLiteNode,
22 int nodeIndex,
23 int32_t operatorCode)
24{
25 auto numInputs = tfLiteNode->inputs->size;
26 if (numInputs < 2)
27 {
28 TF_LITE_MAYBE_KERNEL_LOG(
29 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
30 2, numInputs, nodeIndex);
31 return kTfLiteError;
32 }
33 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
34
35 armnn::Convolution2dDescriptor descriptor;
36 const auto params = reinterpret_cast<TfLiteConvParams*>(tfLiteNode->builtin_data);
37
Mike Kelly84d63782022-05-06 12:14:16 +010038 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Sadik Armagan32ca1442020-11-13 17:51:56 +000039 descriptor.m_BiasEnabled = biasEnabled;
40 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
41 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
42 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
43 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
44 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
45
46 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
47 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
48 if(!IsValid(&tfLiteTensors[tfLiteNode->inputs->data[0]]))
49 {
50 TF_LITE_MAYBE_KERNEL_LOG(
51 tfLiteContext,
52 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
53 operatorCode, nodeIndex);
54 return kTfLiteError;
55 }
56 if (IsDynamicTensor(tfLiteInputTensor))
57 {
58 TF_LITE_MAYBE_KERNEL_LOG(
59 tfLiteContext,
60 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
61 operatorCode, nodeIndex);
62 return kTfLiteError;
63 }
64 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
65 if(!IsValid(&tfLiteOutputTensor))
66 {
67 TF_LITE_MAYBE_KERNEL_LOG(
68 tfLiteContext,
69 "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
70 operatorCode, nodeIndex);
71 return kTfLiteError;
72 }
73 if (IsDynamicTensor(tfLiteOutputTensor))
74 {
75 TF_LITE_MAYBE_KERNEL_LOG(
76 tfLiteContext,
77 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
78 operatorCode, nodeIndex);
79 return kTfLiteError;
80 }
81
82 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
83 if(!IsValid(&tfLiteFilterTensor))
84 {
85 TF_LITE_MAYBE_KERNEL_LOG(
86 tfLiteContext,
87 "TfLiteArmnnDelegate: Invalid filter tensor in operator #%d node #%d: ",
88 operatorCode, nodeIndex);
89 return kTfLiteError;
90 }
91 if (IsDynamicTensor(tfLiteFilterTensor))
92 {
93 TF_LITE_MAYBE_KERNEL_LOG(
94 tfLiteContext,
95 "TfLiteArmnnDelegate: Dynamic filter tensors are not supported in node #%d: ",
96 nodeIndex);
97 return kTfLiteError;
98 }
99
100 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100101 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000102
103 armnn::TensorInfo filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
104
105 armnn::TensorInfo biasTensorInfo;
106 if(biasEnabled)
107 {
108 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
109 if(!IsValid(&tfLiteBiasTensor))
110 {
111 TF_LITE_MAYBE_KERNEL_LOG(
112 tfLiteContext,
113 "TfLiteArmnnDelegate: Invalid bias tensor in operator #%d node #%d: ",
114 operatorCode, nodeIndex);
115 return kTfLiteError;
116 }
117 if (IsDynamicTensor(tfLiteBiasTensor))
118 {
119 TF_LITE_MAYBE_KERNEL_LOG(
120 tfLiteContext,
121 "TfLiteArmnnDelegate: Dynamic bias tensors are not supported in node #%d: ",
122 nodeIndex);
123 return kTfLiteError;
124 }
125 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
126 }
127 else
128 {
129 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
130 }
131
132 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
133
134 // TfLite uses NHWC tensors
135 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
136 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
137
138 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
139 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
140
141 // Calculate padding
142 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
143 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
144 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
145 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
146
Cathal Corbett53837672022-09-01 11:34:37 +0100147 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000148 if (!delegateData.m_Network)
149 {
150 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000151 FORWARD_LAYER_SUPPORT_FUNC("CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000152 tfLiteContext,
153 IsConvolution2dSupported,
154 delegateData.m_Backends,
155 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100156 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000157 inputTensorInfo,
158 outputTensorInfo,
159 descriptor,
160 filterTensorInfo,
161 optionalBiasInfo);
162 return isSupported ? kTfLiteOk : kTfLiteError;
163 }
164
Sadik Armagan32ca1442020-11-13 17:51:56 +0000165 // Set up filter and biases
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100166 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution2dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100167 layer->SetBackendId(setBackend);
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100168
Sadik Armagan90a119b2022-08-05 16:12:49 +0100169 if(tflite::IsConstantTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[1]]))
170 {
171 auto filter =
172 CreateConstTensor(&tfLiteContext->tensors[tfLiteNode->inputs->data[1]],
173 filterTensorInfo,
174 armnn::Optional<armnn::PermutationVector &>());
Sadik Armagan32ca1442020-11-13 17:51:56 +0000175
Sadik Armagan90a119b2022-08-05 16:12:49 +0100176 armnn::IConnectableLayer *weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
177 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
178 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
179 }
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100180
181 if (biasEnabled)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000182 {
Keith Davisb4dd5cc2022-04-07 11:32:00 +0100183 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
184 if(tflite::IsConstantTensor(&tfLiteBiasTensor))
185 {
186 auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, biasTensorInfo);
187 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
188 ARMNN_ASSERT(biasLayer != nullptr);
189 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
190 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
191 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000192 }
193
194 ARMNN_ASSERT(layer != nullptr);
195
196 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
197 outputSlot.SetTensorInfo(outputTensorInfo);
198
199 Connect(layer, tfLiteNode, delegateData);
200
201 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConvParams*>(tfLiteNode->builtin_data);
202 if (!tfLiteNodeParameters)
203 {
204 // No Activation
205 return kTfLiteOk;
206 }
207 // Check activation
208 TfLiteFusedActivation activationType = tfLiteNodeParameters->activation;
209 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
210
211}
212
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100213// Conv3d is only correctly supported for external delegates from TF Lite v2.6, as there was a breaking bug in v2.5.
214#if defined(ARMNN_POST_TFLITE_2_5)
215TfLiteStatus VisitConv3dOperator(DelegateData& delegateData,
216 TfLiteContext* tfLiteContext,
217 TfLiteNode* tfLiteNode,
218 int nodeIndex,
219 int32_t operatorCode)
220{
221 auto numInputs = tfLiteNode->inputs->size;
222 if (numInputs < 2)
223 {
224 TF_LITE_MAYBE_KERNEL_LOG(
225 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
226 2, numInputs, nodeIndex);
227 return kTfLiteError;
228 }
229 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
230
231 armnn::Convolution3dDescriptor descriptor;
232 const auto params = reinterpret_cast<TfLiteConv3DParams*>(tfLiteNode->builtin_data);
233
Mike Kelly84d63782022-05-06 12:14:16 +0100234 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100235 descriptor.m_BiasEnabled = biasEnabled;
236 descriptor.m_DataLayout = armnn::DataLayout::NDHWC;
237 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
238 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
239 descriptor.m_StrideZ = NonNegative(params->stride_depth, nodeIndex);
240 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
241 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
242 descriptor.m_DilationZ = NonNegative(params->dilation_depth_factor, nodeIndex);
243
244 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
245 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
246 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
247 {
248 return kTfLiteError;
249 }
250
251 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
252 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
253 {
254 return kTfLiteError;
255 }
256
257 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
258 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
259 {
260 return kTfLiteError;
261 }
262
263 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100264 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100265
266 armnn::TensorInfo filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
267
268 armnn::TensorInfo biasTensorInfo;
269 if(biasEnabled)
270 {
271 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
272 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
273 {
274 return kTfLiteError;
275 }
276 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
277 }
278 else
279 {
280 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
281 }
282
283 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
284
285 // TfLite uses NDHWC tensors
286 const unsigned int inputDepth = inputTensorInfo.GetShape()[1];
287 const unsigned int inputHeight = inputTensorInfo.GetShape()[2];
288 const unsigned int inputWidth = inputTensorInfo.GetShape()[3];
289
290 // Assuming the filter is DHWIO : Depth, Height, Width, OutputChannels, InputChannels
291 const unsigned int filterDepth = filterTensorInfo.GetShape()[0];
292 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
293 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
294
295 // Calculate padding
296 CalcPadding(inputDepth, filterDepth, descriptor.m_StrideZ, descriptor.m_DilationZ,
297 descriptor.m_PadFront, descriptor.m_PadBack, params->padding);
298 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
299 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
300 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
301 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
302
303 // If the m_Network is a nullptr, this signals that a prerequisite TfLite callback is required to clarify the
304 // support for the operator
305 // If supported, VisitConvolutionOperator will be called again to add the layer to the network as seen below.
Cathal Corbett53837672022-09-01 11:34:37 +0100306 armnn::BackendId setBackend;
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100307 if (!delegateData.m_Network)
308 {
309 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000310 FORWARD_LAYER_SUPPORT_FUNC("CONV3D",
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100311 tfLiteContext,
312 IsConvolution3dSupported,
313 delegateData.m_Backends,
314 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100315 setBackend,
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100316 inputTensorInfo,
317 outputTensorInfo,
318 descriptor,
319 filterTensorInfo,
320 optionalBiasInfo);
321 return isSupported ? kTfLiteOk : kTfLiteError;
322 }
323
324 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution3dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100325 layer->SetBackendId(setBackend);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100326 ARMNN_ASSERT(layer != nullptr);
327
328 // Add a constant layer for weights and biases if inputs are constant,
329 // which are connected to the Convolution3d layer as inputs.
330 if (tflite::IsConstantTensor(&tfLiteFilterTensor))
331 {
332 auto filter = CreateConstTensor(&tfLiteFilterTensor,
333 filterTensorInfo,
334 armnn::Optional<armnn::PermutationVector&>());
335
336 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
337 ARMNN_ASSERT(weightsLayer != nullptr);
338
339 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
340 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
341 }
342
343 if(biasEnabled)
344 {
345 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
346 if(tflite::IsConstantTensor(&tfLiteBiasTensor))
347 {
348 auto biases = CreateConstTensor(&tfLiteBiasTensor,
349 biasTensorInfo,
350 armnn::Optional<armnn::PermutationVector&>());
351
352 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biases);
353 ARMNN_ASSERT(biasLayer != nullptr);
354
355 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
356 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
357 }
358 }
359
360 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
361 outputSlot.SetTensorInfo(outputTensorInfo);
362
363 Connect(layer, tfLiteNode, delegateData);
364
365 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConv3DParams*>(tfLiteNode->builtin_data);
366 if (!tfLiteNodeParameters)
367 {
368 // No Activation
369 return kTfLiteOk;
370 }
371
372 // Check activation
373 TfLiteFusedActivation activationType = tfLiteNodeParameters->activation;
374 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
375}
376#endif
377
Sadik Armagan32ca1442020-11-13 17:51:56 +0000378TfLiteStatus VisitDepthwiseConv2dOperator(DelegateData& delegateData,
379 TfLiteContext* tfLiteContext,
380 TfLiteNode* tfLiteNode,
381 int nodeIndex,
382 int32_t operatorCode)
383{
384 auto numInputs = tfLiteNode->inputs->size;
385 if (numInputs < 2)
386 {
387 TF_LITE_MAYBE_KERNEL_LOG(
388 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
389 2, numInputs, nodeIndex);
390 return kTfLiteError;
391 }
392 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
393
Mike Kelly84d63782022-05-06 12:14:16 +0100394 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000395
396 armnn::DepthwiseConvolution2dDescriptor descriptor;
397 const auto params = reinterpret_cast<TfLiteDepthwiseConvParams*>(tfLiteNode->builtin_data);
398
399 descriptor.m_BiasEnabled = biasEnabled;
400 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
401 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
402 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
403 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
404 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
405
406 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
407 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
408 if(!IsValid(&tfLiteInputTensor))
409 {
410 TF_LITE_MAYBE_KERNEL_LOG(
411 tfLiteContext,
412 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
413 operatorCode, nodeIndex);
414 return kTfLiteError;
415 }
416 if (IsDynamicTensor(tfLiteInputTensor))
417 {
418 TF_LITE_MAYBE_KERNEL_LOG(
419 tfLiteContext,
420 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
421 operatorCode, nodeIndex);
422 return kTfLiteError;
423 }
424 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
425 if(!IsValid(&tfLiteOutputTensor))
426 {
427 TF_LITE_MAYBE_KERNEL_LOG(
428 tfLiteContext,
429 "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
430 operatorCode, nodeIndex);
431 return kTfLiteError;
432 }
433 if (IsDynamicTensor(tfLiteOutputTensor))
434 {
435 TF_LITE_MAYBE_KERNEL_LOG(
436 tfLiteContext,
437 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
438 operatorCode, nodeIndex);
439 return kTfLiteError;
440 }
441
442 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
443 if(!IsValid(&tfLiteFilterTensor))
444 {
445 TF_LITE_MAYBE_KERNEL_LOG(
446 tfLiteContext,
447 "TfLiteArmnnDelegate: Invalid filter tensor in operator #%d node #%d: ",
448 operatorCode, nodeIndex);
449 return kTfLiteError;
450 }
451 if (IsDynamicTensor(tfLiteFilterTensor))
452 {
453 TF_LITE_MAYBE_KERNEL_LOG(
454 tfLiteContext,
455 "TfLiteArmnnDelegate: Dynamic filter tensors are not supported in node #%d: ",
456 nodeIndex);
457 return kTfLiteError;
458 }
459
460 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100461 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000462
Jan Eilers7612bd62021-04-06 17:29:03 +0100463 armnn::TensorInfo filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000464
465 // Assuming input is NHWC
466 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
467 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
468
469 // TensorflowLite weights come in the format [1, H, W, I * M]
470 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
471 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
472
Sadik Armagan32ca1442020-11-13 17:51:56 +0000473 // Calculate padding
474 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
475 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
476 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
477 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
478
479 armnn::TensorInfo biasTensorInfo;
480 if(biasEnabled)
481 {
482 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
483 if(!IsValid(&tfLiteBiasTensor))
484 {
485 TF_LITE_MAYBE_KERNEL_LOG(
486 tfLiteContext,
487 "TfLiteArmnnDelegate: Invalid bias tensor in operator #%d node #%d: ",
488 operatorCode, nodeIndex);
489 return kTfLiteError;
490 }
491 if (IsDynamicTensor(tfLiteBiasTensor))
492 {
493 TF_LITE_MAYBE_KERNEL_LOG(
494 tfLiteContext,
495 "TfLiteArmnnDelegate: Dynamic bias tensors are not supported in node #%d: ",
496 nodeIndex);
497 return kTfLiteError;
498 }
499 biasTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteBiasTensor);
500 }
501 else
502 {
503 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
504 }
505
Cathal Corbett53837672022-09-01 11:34:37 +0100506 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000507 if (!delegateData.m_Network)
508 {
509 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000510 FORWARD_LAYER_SUPPORT_FUNC("DEPTHWISE_CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000511 tfLiteContext,
512 IsDepthwiseConvolutionSupported,
513 delegateData.m_Backends,
514 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100515 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000516 inputTensorInfo,
517 outputTensorInfo,
518 descriptor,
Sadik Armagan90a119b2022-08-05 16:12:49 +0100519 filterTensorInfo,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000520 armnn::Optional<armnn::TensorInfo>(biasTensorInfo));
521 return isSupported ? kTfLiteOk : kTfLiteError;
522 }
523
Cathal Corbett06902652022-04-14 17:55:11 +0100524 armnn::IConnectableLayer* layer = delegateData.m_Network->AddDepthwiseConvolution2dLayer(descriptor);
Cathal Corbett53837672022-09-01 11:34:37 +0100525 layer->SetBackendId(setBackend);
Narumol Prangnawarat16725422020-11-20 16:17:48 +0000526
Sadik Armagan90a119b2022-08-05 16:12:49 +0100527 if(tflite::IsConstantTensor(&tfLiteFilterTensor))
528 {
529 // For depthwise the weights layout is the same as for tflite [1, H, W, I*M]. No permutation required.
530 auto filter = CreateConstTensor(&tfLiteFilterTensor, filterTensorInfo);
531
532 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
533 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
534 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
535 }
Cathal Corbett06902652022-04-14 17:55:11 +0100536
537 if (biasEnabled)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000538 {
Cathal Corbett06902652022-04-14 17:55:11 +0100539 const TfLiteTensor& tfLiteBiasTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
540 if(tflite::IsConstantTensor(&tfLiteBiasTensor))
541 {
542 auto biasTensor = CreateConstTensor(&tfLiteBiasTensor, biasTensorInfo);
543 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
544 ARMNN_ASSERT(biasLayer != nullptr);
545 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
546 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
547 }
Sadik Armagan32ca1442020-11-13 17:51:56 +0000548 }
549
550 ARMNN_ASSERT(layer != nullptr);
551
552 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
553 outputSlot.SetTensorInfo(outputTensorInfo);
554
555 Connect(layer, tfLiteNode, delegateData);
556 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteDepthwiseConvParams*>(tfLiteNode->builtin_data);
557 if (!tfLiteNodeParameters)
558 {
559 // No Activation
560 return kTfLiteOk;
561 }
562 // Check activation
563 TfLiteFusedActivation activationType = tfLiteNodeParameters->activation;
564 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
565}
566
567TfLiteStatus VisitTransposeConv2dOperator(DelegateData& delegateData,
568 TfLiteContext* tfLiteContext,
569 TfLiteNode* tfLiteNode,
570 int nodeIndex,
571 int32_t operatorCode)
572{
573 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 3, nodeIndex));
574 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
575
576 armnn::TransposeConvolution2dDescriptor descriptor;
577 auto* parameters = reinterpret_cast<TfLiteTransposeConvParams*>(tfLiteNode->builtin_data);
578 descriptor.m_BiasEnabled = false;
579 descriptor.m_StrideX = NonNegative(parameters->stride_width, nodeIndex);
580 descriptor.m_StrideY = NonNegative(parameters->stride_height, nodeIndex);
581 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
582
583 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
584 const TfLiteTensor& tfLiteOutputShapeTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
585 if(!IsValid(&tfLiteOutputShapeTensor))
586 {
587 TF_LITE_MAYBE_KERNEL_LOG(
588 tfLiteContext,
589 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
590 operatorCode, nodeIndex);
591 return kTfLiteError;
592 }
593 if (IsDynamicTensor(tfLiteOutputShapeTensor))
594 {
595 TF_LITE_MAYBE_KERNEL_LOG(
596 tfLiteContext,
597 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
598 operatorCode, nodeIndex);
599 return kTfLiteError;
600 }
601
602 armnn::TensorInfo tensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputShapeTensor);
603 std::vector<int32_t> outputShape(tensorInfo.GetNumElements());
604 if (tensorInfo.GetDataType() == armnn::DataType::Signed32)
605 {
606 for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
607 {
608 outputShape[i] = ::tflite::GetTensorData<int32_t>(&tfLiteOutputShapeTensor)[i];
609 }
610 }
611
612 if (tensorInfo.GetDataType() == armnn::DataType::QAsymmU8)
613 {
614 for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
615 {
616 outputShape[i] = ::tflite::GetTensorData<uint8_t>(&tfLiteOutputShapeTensor)[i];
617 }
618 }
619 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
620 for (int dimension : outputShape)
621 {
622 descriptor.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
623 }
624 descriptor.m_OutputShapeEnabled = true;
625
626 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[2]];
627 if(!IsValid(&tfLiteInputTensor))
628 {
629 TF_LITE_MAYBE_KERNEL_LOG(
630 tfLiteContext,
631 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
632 operatorCode, nodeIndex);
633 return kTfLiteError;
634 }
635 if (IsDynamicTensor(tfLiteInputTensor))
636 {
637 TF_LITE_MAYBE_KERNEL_LOG(
638 tfLiteContext,
639 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
640 operatorCode, nodeIndex);
641 return kTfLiteError;
642 }
643
644 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
645 if(!IsValid(&tfLiteOutputTensor))
646 {
647 TF_LITE_MAYBE_KERNEL_LOG(
648 tfLiteContext,
649 "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
650 operatorCode, nodeIndex);
651 return kTfLiteError;
652 }
653 if (IsDynamicTensor(tfLiteOutputTensor))
654 {
655 TF_LITE_MAYBE_KERNEL_LOG(
656 tfLiteContext,
657 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
658 operatorCode, nodeIndex);
659 return kTfLiteError;
660 }
661
662 const TfLiteTensor& tfLiteFilterTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
663 if(!IsValid(&tfLiteFilterTensor))
664 {
665 TF_LITE_MAYBE_KERNEL_LOG(
666 tfLiteContext,
667 "TfLiteArmnnDelegate: Invalid filter tensor in operator #%d node #%d: ",
668 operatorCode, nodeIndex);
669 return kTfLiteError;
670 }
671 if (IsDynamicTensor(tfLiteFilterTensor))
672 {
673 TF_LITE_MAYBE_KERNEL_LOG(
674 tfLiteContext,
675 "TfLiteArmnnDelegate: Dynamic filter tensors are not supported in operator #%d node #%d: ",
676 operatorCode, nodeIndex);
677 return kTfLiteError;
678 }
679
680 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100681 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000682 armnn::TensorInfo filterTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteFilterTensor);
683
684 // TfLite uses NHWC tensors
685 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
686 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
687
688 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
689 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
690
691 // Calculate padding
692 CalcPadding(inputHeight,
693 filterHeight,
694 descriptor.m_StrideY,
695 1, // dilation y
696 descriptor.m_PadTop,
697 descriptor.m_PadBottom,
698 parameters->padding);
699 CalcPadding(inputWidth,
700 filterWidth,
701 descriptor.m_StrideX,
702 1, // dilation x
703 descriptor.m_PadLeft,
704 descriptor.m_PadRight,
705 parameters->padding);
706
707 // Set up filter
708 auto filterTensor = CreateConstTensor(&tfLiteFilterTensor,
709 filterTensorInfo,
710 armnn::Optional<armnn::PermutationVector&>());
Cathal Corbett53837672022-09-01 11:34:37 +0100711 armnn::BackendId setBackend;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000712 if (!delegateData.m_Network)
713 {
714 bool isSupported = false;
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000715 FORWARD_LAYER_SUPPORT_FUNC("TRANSPOSE_CONV2D",
Sadik Armagan32ca1442020-11-13 17:51:56 +0000716 tfLiteContext,
717 IsTransposeConvolution2dSupported,
718 delegateData.m_Backends,
719 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100720 setBackend,
Sadik Armagan32ca1442020-11-13 17:51:56 +0000721 inputTensorInfo,
722 outputTensorInfo,
723 descriptor,
724 filterTensorInfo,
725 armnn::EmptyOptional());
726 return isSupported ? kTfLiteOk : kTfLiteError;
727 }
728
729 armnn::IConnectableLayer* layer = delegateData.m_Network->AddTransposeConvolution2dLayer(descriptor,
730 filterTensor,
731 armnn::EmptyOptional());
Cathal Corbett53837672022-09-01 11:34:37 +0100732 layer->SetBackendId(setBackend);
Sadik Armagan32ca1442020-11-13 17:51:56 +0000733 ARMNN_ASSERT(layer != nullptr);
734
735 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
736 outputSlot.SetTensorInfo(outputTensorInfo);
737
738 // Connect
Keith Davis892fafe2020-11-26 17:40:35 +0000739 if (delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->inputs->data[2])] != nullptr)
Sadik Armagan32ca1442020-11-13 17:51:56 +0000740 {
Keith Davis892fafe2020-11-26 17:40:35 +0000741 delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->inputs->data[2])]->
742 Connect(layer->GetInputSlot(0));
Sadik Armagan32ca1442020-11-13 17:51:56 +0000743 }
744
745 // Prepare output slots
746 for (unsigned int outputIndex = 0; outputIndex < layer->GetNumOutputSlots(); ++outputIndex)
747 {
748 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(outputIndex);
Keith Davis892fafe2020-11-26 17:40:35 +0000749 delegateData.m_OutputSlotForNode[static_cast<unsigned int>(tfLiteNode->outputs->data[outputIndex])] =
750 &outputSlot;
Sadik Armagan32ca1442020-11-13 17:51:56 +0000751 }
752 return kTfLiteOk;
753}
754
Sadik Armagan62483be2020-10-23 17:14:43 +0100755TfLiteStatus VisitConvolutionOperator(DelegateData& delegateData,
756 TfLiteContext* tfLiteContext,
757 TfLiteNode* tfLiteNode,
758 int nodeIndex,
759 int32_t operatorCode)
760{
Sadik Armagan32ca1442020-11-13 17:51:56 +0000761 switch(operatorCode)
762 {
763 case kTfLiteBuiltinConv2d:
764 return VisitConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Matthew Sloyan81ec9942021-10-12 10:26:30 +0100765// Conv3d is only correctly supported for external delegates from TF Lite v2.6, as there was a breaking bug in v2.5.
766#if defined(ARMNN_POST_TFLITE_2_5)
767 case kTfLiteBuiltinConv3d:
768 return VisitConv3dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
769#endif
Sadik Armagan32ca1442020-11-13 17:51:56 +0000770 case kTfLiteBuiltinDepthwiseConv2d:
771 return VisitDepthwiseConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
772 case kTfLiteBuiltinTransposeConv:
773 return VisitTransposeConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
774 default:
775 return kTfLiteError;
776 }
Sadik Armagan62483be2020-10-23 17:14:43 +0100777}
778
779} // namespace armnnDelegate