blob: 2eb5edabe0bb087f77ad8e3b7e056b96540ef4cb [file] [log] [blame]
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Matthew Sloyan080ffd82023-04-24 12:53:04 +01005
6#include <OpaqueDelegateUtils.hpp>
7#include <SharedFunctions.hpp>
8
9#include <tensorflow/lite/builtin_ops.h>
10#include <tensorflow/lite/c/builtin_op_data.h>
11#include <tensorflow/lite/c/common.h>
12#include <tensorflow/lite/minimal_logging.h>
13
14namespace armnnOpaqueDelegate
15{
16
17TfLiteStatus VisitConv2dOperator(DelegateData& delegateData,
18 TfLiteOpaqueContext* tfLiteContext,
19 TfLiteOpaqueNode* tfLiteNode,
20 int nodeIndex,
21 int32_t operatorCode)
22{
23 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
24 if (numInputs < 2)
25 {
26 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
27 tfLiteContext,
28 "TfLiteArmnnOpaqueDelegate: Minimum number of inputs (%d != %d) in node #%d",
29 2, numInputs, nodeIndex);
30 return kTfLiteError;
31 }
32 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
33
34 // Gather input indices and use to get input tensor.
35 const int* inputTensors;
36 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
37 {
38 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
39 tfLiteContext,
40 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
41 nodeIndex);
42 return kTfLiteError;
43 }
44
45 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
46 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
47 {
48 return kTfLiteError;
49 }
Matthew Sloyan080ffd82023-04-24 12:53:04 +010050
51 // Use input indices to get filter tensor.
52 const TfLiteOpaqueTensor* tfLiteFilterTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
Matthew Sloyan2b04ec32023-04-26 11:42:46 +010053 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
Matthew Sloyan080ffd82023-04-24 12:53:04 +010054 {
Matthew Sloyan080ffd82023-04-24 12:53:04 +010055 return kTfLiteError;
56 }
57
58 // Gather output indices and use to get output tensors.
59 int numOutputs = 0;
60 const int* outputTensors;
61 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
62 {
63 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
64 tfLiteContext,
65 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
66 nodeIndex);
67 return kTfLiteError;
68 }
69
70 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
71 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
72 {
73 return kTfLiteError;
74 }
Matthew Sloyan080ffd82023-04-24 12:53:04 +010075
76 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
77 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteFilterTensor);
78 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
79
80 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConvParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
81 TfLiteFusedActivation activationType = kTfLiteActNone;
82 if (tfLiteNodeParameters)
83 {
84 activationType = tfLiteNodeParameters->activation;
85 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData,
86 tfLiteContext,
87 outputTensorInfo,
88 outputTensorInfo,
89 activationType);
90 if(activationStatus != kTfLiteOk)
91 {
92 return kTfLiteError;
93 }
94 }
95
96 armnn::TensorInfo biasTensorInfo;
97 const TfLiteOpaqueTensor* tfLiteBiasTensor = nullptr;
98
99 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
100 if(biasEnabled)
101 {
102 // Use input indices to get bias tensor.
103 tfLiteBiasTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[2]);
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100104 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100105 {
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100106 return kTfLiteError;
107 }
108 biasTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteBiasTensor);
109 }
110 else
111 {
112 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
113 }
114
115 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
116
117 armnn::Convolution2dDescriptor descriptor;
118 descriptor.m_BiasEnabled = biasEnabled;
119 descriptor.m_StrideX = NonNegative(tfLiteNodeParameters->stride_width, nodeIndex);
120 descriptor.m_StrideY = NonNegative(tfLiteNodeParameters->stride_height, nodeIndex);
121 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
122 descriptor.m_DilationX = NonNegative(tfLiteNodeParameters->dilation_width_factor, nodeIndex);
123 descriptor.m_DilationY = NonNegative(tfLiteNodeParameters->dilation_height_factor, nodeIndex);
124
125 // TfLite uses NHWC tensors
126 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
127 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
128
129 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
130 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
131
132 // Calculate padding
133 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
134 descriptor.m_PadTop, descriptor.m_PadBottom, tfLiteNodeParameters->padding);
135 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
136 descriptor.m_PadLeft, descriptor.m_PadRight, tfLiteNodeParameters->padding);
137
138 armnn::BackendId setBackend;
139 if (!delegateData.m_Network)
140 {
141 bool isSupported = false;
142 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("CONV2D",
143 tfLiteContext,
144 IsConvolution2dSupported,
145 delegateData.m_Backends,
146 isSupported,
147 setBackend,
148 inputTensorInfo,
149 outputTensorInfo,
150 descriptor,
151 filterTensorInfo,
152 optionalBiasInfo);
153 return isSupported ? kTfLiteOk : kTfLiteError;
154 }
155
156 // Set up filter and biases
157 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution2dLayer(descriptor);
158 layer->SetBackendId(setBackend);
159
160 if(filterTensorInfo.IsConstant())
161 {
162 auto filter = CreateConstTensor(tfLiteFilterTensor, filterTensorInfo);
163
164 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
165 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
166 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
167 }
168
169 if (biasEnabled)
170 {
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100171 if (biasTensorInfo.IsConstant())
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100172 {
173 auto biasTensor = CreateConstTensor(tfLiteBiasTensor, biasTensorInfo);
174 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
175 ARMNN_ASSERT(biasLayer != nullptr);
176 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
177 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
178 }
179 }
180
181 // The data input can also be constant, so we must check that this is also allocated to an input slot
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100182 if (inputTensorInfo.IsConstant())
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100183 {
184 auto input = CreateConstTensor(tfLiteInputTensor, inputTensorInfo);
185
186 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
187 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
188 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
189 }
190
191 ARMNN_ASSERT(layer != nullptr);
192
193 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
194 outputSlot.SetTensorInfo(outputTensorInfo);
195
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100196 if (Connect(layer, tfLiteContext, tfLiteNode, delegateData) != kTfLiteOk)
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100197 {
198 return kTfLiteError;
199 }
200
201 if (!tfLiteNodeParameters)
202 {
203 // No Activation
204 return kTfLiteOk;
205 }
206
207 // Check and Create activation
208 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
209}
210
211TfLiteStatus VisitDepthwiseConv2dOperator(DelegateData& delegateData,
212 TfLiteOpaqueContext* tfLiteContext,
213 TfLiteOpaqueNode* tfLiteNode,
214 int nodeIndex,
215 int32_t operatorCode)
216{
217 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
218 if (numInputs < 2)
219 {
220 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
221 tfLiteContext,
222 "TfLiteArmnnOpaqueDelegate: Minimum number of inputs (%d != %d) in node #%d",
223 2, numInputs, nodeIndex);
224 return kTfLiteError;
225 }
226 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
227
228 // Gather input indices and use to get input tensor.
229 const int* inputTensors;
230 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
231 {
232 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
233 tfLiteContext,
234 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
235 nodeIndex);
236 return kTfLiteError;
237 }
238
239 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
240 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
241 {
242 return kTfLiteError;
243 }
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100244
245 // Use input indices to get filter tensor.
246 const TfLiteOpaqueTensor* tfLiteFilterTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
Matthew Sloyan2b04ec32023-04-26 11:42:46 +0100247 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100248 {
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100249 return kTfLiteError;
250 }
251
252 // Gather output indices and use to get output tensors.
253 int numOutputs = 0;
254 const int* outputTensors;
255 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
256 {
257 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
258 tfLiteContext,
259 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
260 nodeIndex);
261 return kTfLiteError;
262 }
263
264 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
265 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
266 {
267 return kTfLiteError;
268 }
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100269
270 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
271 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteFilterTensor);
272 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
273
274 auto* tfLiteNodeParameters =
275 reinterpret_cast<TfLiteDepthwiseConvParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
276
277 TfLiteFusedActivation activationType = kTfLiteActNone;
278 if (tfLiteNodeParameters)
279 {
280 activationType = tfLiteNodeParameters->activation;
281 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData,
282 tfLiteContext,
283 outputTensorInfo,
284 outputTensorInfo,
285 activationType);
286 if(activationStatus != kTfLiteOk)
287 {
288 return kTfLiteError;
289 }
290 }
291
292 armnn::TensorInfo biasTensorInfo;
293 const TfLiteOpaqueTensor* tfLiteBiasTensor = nullptr;
294
295 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
296 if(biasEnabled)
297 {
298 // Use input indices to get bias tensor.
299 tfLiteBiasTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[2]);
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100300 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100301 {
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100302 return kTfLiteError;
303 }
304 biasTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteBiasTensor);
305 }
306 else
307 {
308 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
309 }
310
311 armnn::DepthwiseConvolution2dDescriptor descriptor;
312 descriptor.m_BiasEnabled = biasEnabled;
313 descriptor.m_StrideX = NonNegative(tfLiteNodeParameters->stride_width, nodeIndex);
314 descriptor.m_StrideY = NonNegative(tfLiteNodeParameters->stride_height, nodeIndex);
315 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
316 descriptor.m_DilationX = NonNegative(tfLiteNodeParameters->dilation_width_factor, nodeIndex);
317 descriptor.m_DilationY = NonNegative(tfLiteNodeParameters->dilation_height_factor, nodeIndex);
318
319 // Assuming input is NHWC
320 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
321 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
322
323 // TensorflowLite weights come in the format [1, H, W, I * M]
324 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
325 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
326
327 // Calculate padding
328 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
329 descriptor.m_PadTop, descriptor.m_PadBottom, tfLiteNodeParameters->padding);
330 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
331 descriptor.m_PadLeft, descriptor.m_PadRight, tfLiteNodeParameters->padding);
332
333 armnn::BackendId setBackend;
334 if (!delegateData.m_Network)
335 {
336 bool isSupported = false;
337 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("DEPTHWISE_CONV2D",
338 tfLiteContext,
339 IsDepthwiseConvolutionSupported,
340 delegateData.m_Backends,
341 isSupported,
342 setBackend,
343 inputTensorInfo,
344 outputTensorInfo,
345 descriptor,
346 filterTensorInfo,
347 armnn::Optional<armnn::TensorInfo>(biasTensorInfo));
348 return isSupported ? kTfLiteOk : kTfLiteError;
349 }
350
351 armnn::IConnectableLayer* layer = delegateData.m_Network->AddDepthwiseConvolution2dLayer(descriptor);
352 layer->SetBackendId(setBackend);
353
354 if(filterTensorInfo.IsConstant())
355 {
356 // For depthwise the weights layout is the same as for tflite [1, H, W, I*M]. No permutation required.
357 auto filter = CreateConstTensor(tfLiteFilterTensor, filterTensorInfo);
358
359 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
360 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
361 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
362 }
363
364 if (biasEnabled)
365 {
366 if(biasTensorInfo.IsConstant())
367 {
368 auto biasTensor = CreateConstTensor(tfLiteBiasTensor, biasTensorInfo);
369
370 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
371 ARMNN_ASSERT(biasLayer != nullptr);
372 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
373 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
374 }
375 }
376
377 // The data input can also be constant, so we must check that this is also allocated to an input slot
378 if(inputTensorInfo.IsConstant())
379 {
380 auto input = CreateConstTensor(tfLiteInputTensor, inputTensorInfo);
381
382 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
383 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
384 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
385 }
386
387 ARMNN_ASSERT(layer != nullptr);
388
389 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
390 outputSlot.SetTensorInfo(outputTensorInfo);
391
392 if(Connect(layer, tfLiteContext, tfLiteNode, delegateData) != kTfLiteOk)
393 {
394 return kTfLiteError;
395 }
396
397 if (!tfLiteNodeParameters)
398 {
399 // No Activation
400 return kTfLiteOk;
401 }
402 // Check and create activation
403 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
404}
405
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100406TfLiteStatus VisitConv3dOperator(DelegateData& delegateData,
407 TfLiteOpaqueContext* tfLiteContext,
408 TfLiteOpaqueNode* tfLiteNode,
409 int nodeIndex,
410 int32_t operatorCode)
411{
412 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
413 if (numInputs < 2)
414 {
415 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
416 tfLiteContext, "TfLiteArmnnOpaqueDelegate: Minimum number of inputs (%d != %d) in node #%d",
417 2, numInputs, nodeIndex);
418 return kTfLiteError;
419 }
420 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
421
422 armnn::Convolution3dDescriptor descriptor;
423 auto* params = reinterpret_cast<TfLiteConv3DParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
424
425 bool biasEnabled = IsOptionalOperandPresent(tfLiteNode, 2);
426 descriptor.m_BiasEnabled = biasEnabled;
427 descriptor.m_DataLayout = armnn::DataLayout::NDHWC;
428 descriptor.m_StrideX = NonNegative(params->stride_width, nodeIndex);
429 descriptor.m_StrideY = NonNegative(params->stride_height, nodeIndex);
430 descriptor.m_StrideZ = NonNegative(params->stride_depth, nodeIndex);
431 descriptor.m_DilationX = NonNegative(params->dilation_width_factor, nodeIndex);
432 descriptor.m_DilationY = NonNegative(params->dilation_height_factor, nodeIndex);
433 descriptor.m_DilationZ = NonNegative(params->dilation_depth_factor, nodeIndex);
434
435 // Gather input indices and use to get input tensor.
436 const int* inputTensors;
437 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
438 {
439 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
440 tfLiteContext,
441 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
442 nodeIndex);
443 return kTfLiteError;
444 }
445
446 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
447 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
448 {
449 return kTfLiteError;
450 }
451
452 // Use input indices to get filter tensor.
453 const TfLiteOpaqueTensor* tfLiteFilterTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
454 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
455 {
456 return kTfLiteError;
457 }
458
459 // Gather output indices and use to get output tensors.
460 int numOutputs = 0;
461 const int* outputTensors;
462 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
463 {
464 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
465 tfLiteContext,
466 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
467 nodeIndex);
468 return kTfLiteError;
469 }
470
471 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
472 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
473 {
474 return kTfLiteError;
475 }
476
477 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
478 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
479
480 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteConv3DParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
481 TfLiteFusedActivation activationType=kTfLiteActNone;
482 if (tfLiteNodeParameters)
483 {
484 activationType = tfLiteNodeParameters->activation;
485 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData, tfLiteContext, outputTensorInfo,
486 outputTensorInfo, activationType);
487 if(activationStatus != kTfLiteOk)
488 {
489 return kTfLiteError;
490 }
491
492 }
493
494 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteFilterTensor);
495
496 armnn::TensorInfo biasTensorInfo;
497 const TfLiteOpaqueTensor* tfLiteBiasTensor = nullptr;
498
499 if (biasEnabled)
500 {
501 // Use input indices to get bias tensor.
502 tfLiteBiasTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[2]);
503 if (!IsValid(tfLiteContext, tfLiteBiasTensor, operatorCode, nodeIndex))
504 {
505 return kTfLiteError;
506 }
507 biasTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteBiasTensor);
508 }
509 else
510 {
511 biasTensorInfo = armnn::TensorInfo(armnn::TensorShape({1}), GetDataType(tfLiteInputTensor));
512 }
513
514 armnn::Optional<armnn::TensorInfo> optionalBiasInfo(biasTensorInfo);
515
516 // TfLite uses NDHWC tensors
517 const unsigned int inputDepth = inputTensorInfo.GetShape()[1];
518 const unsigned int inputHeight = inputTensorInfo.GetShape()[2];
519 const unsigned int inputWidth = inputTensorInfo.GetShape()[3];
520
521 // Assuming the filter is DHWIO : Depth, Height, Width, OutputChannels, InputChannels
522 const unsigned int filterDepth = filterTensorInfo.GetShape()[0];
523 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
524 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
525
526 // Calculate padding
527 CalcPadding(inputDepth, filterDepth, descriptor.m_StrideZ, descriptor.m_DilationZ,
528 descriptor.m_PadFront, descriptor.m_PadBack, params->padding);
529 CalcPadding(inputHeight, filterHeight, descriptor.m_StrideY, descriptor.m_DilationY,
530 descriptor.m_PadTop, descriptor.m_PadBottom, params->padding);
531 CalcPadding(inputWidth, filterWidth, descriptor.m_StrideX, descriptor.m_DilationX,
532 descriptor.m_PadLeft, descriptor.m_PadRight, params->padding);
533
534 // If the m_Network is a nullptr, this signals that a prerequisite TfLite callback is required to clarify the
535 // support for the operator
536 // If supported, VisitConvolutionOperator will be called again to add the layer to the network as seen below.
537 armnn::BackendId setBackend;
538 if (!delegateData.m_Network)
539 {
540 bool isSupported = false;
541 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("CONV3D",
542 tfLiteContext,
543 IsConvolution3dSupported,
544 delegateData.m_Backends,
545 isSupported,
546 setBackend,
547 inputTensorInfo,
548 outputTensorInfo,
549 descriptor,
550 filterTensorInfo,
551 optionalBiasInfo);
552 return isSupported ? kTfLiteOk : kTfLiteError;
553 }
554
555 armnn::IConnectableLayer* layer = delegateData.m_Network->AddConvolution3dLayer(descriptor);
556 layer->SetBackendId(setBackend);
557 ARMNN_ASSERT(layer != nullptr);
558
559 // Add a constant layer for weights and biases if inputs are constant,
560 // which are connected to the Convolution3d layer as inputs.
561 if (filterTensorInfo.IsConstant())
562 {
563 auto filter = CreateConstTensor(tfLiteFilterTensor,
564 filterTensorInfo);
565
566 armnn::IConnectableLayer* weightsLayer = delegateData.m_Network->AddConstantLayer(filter);
567 ARMNN_ASSERT(weightsLayer != nullptr);
568
569 weightsLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1u));
570 weightsLayer->GetOutputSlot(0).SetTensorInfo(filterTensorInfo);
571 }
572
573 if (biasEnabled)
574 {
575 if (biasTensorInfo.IsConstant())
576 {
577 auto biasTensor = CreateConstTensor(tfLiteBiasTensor, biasTensorInfo);
578
579 armnn::IConnectableLayer* biasLayer = delegateData.m_Network->AddConstantLayer(biasTensor);
580 ARMNN_ASSERT(biasLayer != nullptr);
581
582 biasLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(2u));
583 biasLayer->GetOutputSlot(0).SetTensorInfo(biasTensorInfo);
584 }
585 }
586
587 // The data input can also be constant, so we must check that this is also allocated to an input slot
588 if (inputTensorInfo.IsConstant())
589 {
590 auto input = CreateConstTensor(tfLiteInputTensor, inputTensorInfo);
591
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100592 armnn::IConnectableLayer* inputLayer = delegateData.m_Network->AddConstantLayer(input);
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100593 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
594 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
595 }
596
597 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
598 outputSlot.SetTensorInfo(outputTensorInfo);
599
600 if (Connect(layer, tfLiteContext, tfLiteNode, delegateData) != kTfLiteOk)
601 {
602 return kTfLiteError;
603 }
604
605 if (!tfLiteNodeParameters)
606 {
607 // No Activation
608 return kTfLiteOk;
609 }
610
611 // Check and create activation
612 return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData);
613}
614
615
616
617TfLiteStatus VisitTransposeConv2dOperator(DelegateData& delegateData,
618 TfLiteOpaqueContext* tfLiteContext,
619 TfLiteOpaqueNode* tfLiteNode,
620 int nodeIndex,
621 int32_t operatorCode)
622{
623 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 3, nodeIndex));
624 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
625
626 armnn::TransposeConvolution2dDescriptor descriptor;
627 auto* parameters = reinterpret_cast<TfLiteTransposeConvParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
628 descriptor.m_BiasEnabled = false;
629 descriptor.m_StrideX = NonNegative(parameters->stride_width, nodeIndex);
630 descriptor.m_StrideY = NonNegative(parameters->stride_height, nodeIndex);
631 descriptor.m_DataLayout = armnn::DataLayout::NHWC;
632
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100633 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
634 // Gather input indices and use to get input tensor.
635 const int* inputTensors;
636 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
637 {
638 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
639 tfLiteContext,
640 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
641 nodeIndex);
642 return kTfLiteError;
643 }
644
645 const TfLiteOpaqueTensor* tfLiteOutputShapeTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext,
646 inputTensors[0]);
647 if (!IsValid(tfLiteContext, tfLiteOutputShapeTensor, operatorCode, nodeIndex))
648 {
649 return kTfLiteError;
650 }
651
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100652 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[2]);
653 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
654 {
655 return kTfLiteError;
656 }
657
658 const TfLiteOpaqueTensor* tfLiteFilterTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
659 if (!IsValid(tfLiteContext, tfLiteFilterTensor, operatorCode, nodeIndex))
660 {
661 return kTfLiteError;
662 }
663
664 // Gather output indices and use to get output tensors.
665 int numOutputs = 0;
666 const int* outputTensors;
667 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
668 {
669 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
670 tfLiteContext,
671 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
672 nodeIndex);
673 return kTfLiteError;
674 }
675 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
676 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
677 {
678 return kTfLiteError;
679 }
680
681 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
682 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
683 const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteFilterTensor);
684
685 // TfLite uses NHWC tensors
686 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
687 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
688
689 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
690 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
691
Matthew Sloyanc52190a2023-05-08 11:33:55 +0100692 // This block determines the output shape of the transpose convolution.
693 // If the output shape tensor is a constant, we can access the data at load time and set the shape of the layer.
694 // If this is not constant, we do not have access to the shape data, so we have to use infer output shape.
695 if (IsConstantTensor(tfLiteOutputShapeTensor))
696 {
697 const armnn::TensorInfo outputShapeTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputShapeTensor);
698 std::vector<int32_t> outputShape(outputShapeTensorInfo.GetNumElements());
699 if (outputShapeTensorInfo.GetDataType() == armnn::DataType::Signed32)
700 {
701 for(unsigned int i=0; i < outputShapeTensorInfo.GetNumElements(); ++i)
702 {
703 outputShape[i] = static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLiteOutputShapeTensor))[i];
704 }
705 }
706
707 if (outputShapeTensorInfo.GetDataType() == armnn::DataType::QAsymmU8)
708 {
709 for(unsigned int i=0; i < outputShapeTensorInfo.GetNumElements(); ++i)
710 {
711 outputShape[i] = static_cast<uint8_t*>(TfLiteOpaqueTensorData(tfLiteOutputShapeTensor))[i];
712 }
713 }
714
715 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
716 for (int dimension : outputShape)
717 {
718 descriptor.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
719 }
720 descriptor.m_OutputShapeEnabled = true;
721
722 // TfLite uses NHWC tensors
723 const unsigned int outputHeight = descriptor.m_OutputShape[1];
724 const unsigned int outputWidth = descriptor.m_OutputShape[2];
725
726 CalcPadding(inputHeight,
727 filterHeight,
728 descriptor.m_StrideY,
729 1, // DilationY
730 descriptor.m_PadTop,
731 descriptor.m_PadBottom,
732 parameters->padding,
733 outputHeight);
734
735 CalcPadding(inputWidth,
736 filterWidth,
737 descriptor.m_StrideX,
738 1, // DilationX
739 descriptor.m_PadLeft,
740 descriptor.m_PadRight,
741 parameters->padding,
742 outputWidth);
743 }
744 else
745 {
746 CalcPadding(inputHeight,
747 filterHeight,
748 descriptor.m_StrideY,
749 1, // DilationY
750 descriptor.m_PadTop,
751 descriptor.m_PadBottom,
752 parameters->padding);
753
754 CalcPadding(inputWidth,
755 filterWidth,
756 descriptor.m_StrideX,
757 1, // DilationX
758 descriptor.m_PadLeft,
759 descriptor.m_PadRight,
760 parameters->padding);
761 }
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100762
763 // Set up filter
764 auto filterTensor = CreateConstTensor(tfLiteFilterTensor,
765 filterTensorInfo);
766 armnn::BackendId setBackend;
767 if (!delegateData.m_Network)
768 {
769 bool isSupported = false;
770 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("TRANSPOSE_CONV2D",
771 tfLiteContext,
772 IsTransposeConvolution2dSupported,
773 delegateData.m_Backends,
774 isSupported,
775 setBackend,
776 inputTensorInfo,
777 outputTensorInfo,
778 descriptor,
779 filterTensorInfo,
780 armnn::EmptyOptional());
781 return isSupported ? kTfLiteOk : kTfLiteError;
782 }
783
784 armnn::IConnectableLayer* layer = delegateData.m_Network->AddTransposeConvolution2dLayer(descriptor,
785 filterTensor,
786 armnn::EmptyOptional());
787 layer->SetBackendId(setBackend);
788 ARMNN_ASSERT(layer != nullptr);
789
790 // The data input can be constant, so we must check that this is allocated to an input slot
791 if(inputTensorInfo.IsConstant())
792 {
793 auto input = CreateConstTensor(tfLiteInputTensor, inputTensorInfo);
794
795 armnn::IConnectableLayer *inputLayer = delegateData.m_Network->AddConstantLayer(input);
796 inputLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0u));
797 inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
798 }
799
800 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
801 outputSlot.SetTensorInfo(outputTensorInfo);
802
803
804 // Connect
805 if (delegateData.m_OutputSlotForNode[static_cast<unsigned int>(inputTensors[2])] != nullptr)
806 {
807 delegateData.m_OutputSlotForNode[static_cast<unsigned int>(inputTensors[2])]->
808 Connect(layer->GetInputSlot(0));
809 }
810
811 if (Connect(layer, tfLiteContext, tfLiteNode, delegateData) != kTfLiteOk)
812 {
813 return kTfLiteError;
814 }
815
816 return kTfLiteOk;
817}
818
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100819TfLiteStatus VisitConvolutionOperator(DelegateData& delegateData,
820 TfLiteOpaqueContext* tfLiteContext,
821 TfLiteOpaqueNode* tfLiteNode,
822 int nodeIndex,
823 int32_t operatorCode)
824{
825 switch(operatorCode)
826 {
827 case kTfLiteBuiltinConv2d:
828 return VisitConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100829 case kTfLiteBuiltinConv3d:
830 return VisitConv3dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100831 case kTfLiteBuiltinDepthwiseConv2d:
832 return VisitDepthwiseConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Francis Murtagh3a9e7ba2023-04-26 15:58:39 +0100833 case kTfLiteBuiltinTransposeConv:
834 return VisitTransposeConv2dOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
Matthew Sloyan080ffd82023-04-24 12:53:04 +0100835 default:
836 return kTfLiteError;
837 }
838}
839
840}