aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Sloyan <matthew.sloyan@arm.com>2021-09-08 13:05:51 +0100
committerMatthew Sloyan <matthew.sloyan@arm.com>2021-10-01 15:27:01 +0100
commitb63a31170aee1d28267d83a4bc67b57708fb6b05 (patch)
tree16cea0a872939be749b72f45ad125964439bc40e
parenteb852bb9e45b1db42a26001ece11ec7cc1f2bbfe (diff)
downloadarmnn-b63a31170aee1d28267d83a4bc67b57708fb6b05.tar.gz
IVGCVSW-6163 Add Conv3d FrontEnd and Ref Implementation
* Added front-end * Added Reference workload * Added Serializer & Deserializer support * Added unit tests * Added NDHWC DataLayout Signed-off-by: Matthew Sloyan <matthew.sloyan@arm.com> Change-Id: Iec4d39e7433b5334d52fa44cf8efc6bcd39319d8
-rw-r--r--Android.mk1
-rw-r--r--CMakeLists.txt3
-rw-r--r--docs/01_02_deserializer_serializer.dox1
-rw-r--r--docs/05_operator_list.dox43
-rw-r--r--include/armnn/BackendHelper.hpp7
-rw-r--r--include/armnn/Descriptors.hpp68
-rw-r--r--include/armnn/DescriptorsFwd.hpp1
-rw-r--r--include/armnn/INetwork.hpp11
-rw-r--r--include/armnn/Types.hpp5
-rw-r--r--include/armnn/TypesUtils.hpp7
-rw-r--r--include/armnn/backends/ILayerSupport.hpp7
-rw-r--r--include/armnnUtils/DataLayoutIndexed.hpp2
-rw-r--r--src/armnn/BackendHelper.cpp15
-rw-r--r--src/armnn/LayersFwd.hpp2
-rw-r--r--src/armnn/Network.cpp49
-rw-r--r--src/armnn/Network.hpp5
-rw-r--r--src/armnn/SerializeLayerParameters.cpp27
-rw-r--r--src/armnn/SerializeLayerParameters.hpp5
-rw-r--r--src/armnn/layers/Convolution3dLayer.cpp172
-rw-r--r--src/armnn/layers/Convolution3dLayer.hpp68
-rw-r--r--src/armnn/test/InferOutputTests.cpp3
-rw-r--r--src/armnn/test/InferOutputTests.hpp37
-rw-r--r--src/armnnDeserializer/Deserializer.cpp55
-rw-r--r--src/armnnDeserializer/Deserializer.hpp1
-rw-r--r--src/armnnDeserializer/test/DeserializeConvolution3d.cpp223
-rw-r--r--src/armnnSerializer/ArmnnSchema.fbs29
-rw-r--r--src/armnnSerializer/ArmnnSchema_generated.h308
-rw-r--r--src/armnnSerializer/Serializer.cpp58
-rw-r--r--src/armnnSerializer/Serializer.hpp9
-rw-r--r--src/armnnSerializer/SerializerUtils.cpp2
-rw-r--r--src/armnnSerializer/test/SerializerTests.cpp55
-rw-r--r--src/armnnUtils/DataLayoutIndexed.cpp6
-rw-r--r--src/backends/backendsCommon/LayerSupportBase.cpp10
-rw-r--r--src/backends/backendsCommon/LayerSupportBase.hpp7
-rw-r--r--src/backends/backendsCommon/WorkloadData.cpp61
-rw-r--r--src/backends/backendsCommon/WorkloadData.hpp15
-rw-r--r--src/backends/backendsCommon/WorkloadFactory.cpp38
-rw-r--r--src/backends/backendsCommon/WorkloadFactory.hpp5
-rw-r--r--src/backends/backendsCommon/common.mk1
-rw-r--r--src/backends/backendsCommon/test/CMakeLists.txt2
-rw-r--r--src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp2
-rw-r--r--src/backends/backendsCommon/test/LayerTests.hpp1
-rw-r--r--src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp1038
-rw-r--r--src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp141
-rw-r--r--src/backends/reference/RefLayerSupport.cpp70
-rw-r--r--src/backends/reference/RefLayerSupport.hpp7
-rw-r--r--src/backends/reference/RefWorkloadFactory.cpp6
-rw-r--r--src/backends/reference/RefWorkloadFactory.hpp3
-rw-r--r--src/backends/reference/backend.mk2
-rw-r--r--src/backends/reference/test/RefLayerTests.cpp33
-rw-r--r--src/backends/reference/workloads/CMakeLists.txt4
-rw-r--r--src/backends/reference/workloads/Conv3dImpl.cpp151
-rw-r--r--src/backends/reference/workloads/Conv3dImpl.hpp38
-rw-r--r--src/backends/reference/workloads/RefConvolution3dWorkload.cpp76
-rw-r--r--src/backends/reference/workloads/RefConvolution3dWorkload.hpp38
-rw-r--r--src/backends/reference/workloads/RefWorkloads.hpp1
56 files changed, 3000 insertions, 35 deletions
diff --git a/Android.mk b/Android.mk
index 69fe9eefb4..7137baf79e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -161,6 +161,7 @@ LOCAL_SRC_FILES := \
src/armnn/layers/ConcatLayer.cpp \
src/armnn/layers/ConstantLayer.cpp \
src/armnn/layers/Convolution2dLayer.cpp \
+ src/armnn/layers/Convolution3dLayer.cpp \
src/armnn/layers/ConvertBf16ToFp32Layer.cpp \
src/armnn/layers/ConvertFp16ToFp32Layer.cpp \
src/armnn/layers/ConvertFp32ToBf16Layer.cpp \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f28c2f7d39..0989e12ad5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -231,6 +231,8 @@ list(APPEND armnn_sources
src/armnn/layers/ConstantLayer.cpp
src/armnn/layers/Convolution2dLayer.hpp
src/armnn/layers/Convolution2dLayer.cpp
+ src/armnn/layers/Convolution3dLayer.hpp
+ src/armnn/layers/Convolution3dLayer.cpp
src/armnn/layers/ConvertBf16ToFp32Layer.cpp
src/armnn/layers/ConvertBf16ToFp32Layer.hpp
src/armnn/layers/ConvertFp16ToFp32Layer.hpp
@@ -793,6 +795,7 @@ if(BUILD_UNIT_TESTS)
src/armnnDeserializer/test/DeserializeComparison.cpp
src/armnnDeserializer/test/DeserializeConstant.cpp
src/armnnDeserializer/test/DeserializeConvolution2d.cpp
+ src/armnnDeserializer/test/DeserializeConvolution3d.cpp
src/armnnDeserializer/test/DeserializeDepthToSpace.cpp
src/armnnDeserializer/test/DeserializeDepthwiseConv2d.cpp
src/armnnDeserializer/test/DeserializeDivision.cpp
diff --git a/docs/01_02_deserializer_serializer.dox b/docs/01_02_deserializer_serializer.dox
index 55259ddeb8..6bd0d4a1a2 100644
--- a/docs/01_02_deserializer_serializer.dox
+++ b/docs/01_02_deserializer_serializer.dox
@@ -30,6 +30,7 @@ The Arm NN SDK Serializer currently supports the following layers:
- Concat
- Constant
- Convolution2d
+- Convolution3d
- DepthToSpace
- DepthwiseConvolution2d
- Dequantize
diff --git a/docs/05_operator_list.dox b/docs/05_operator_list.dox
index 527cd92465..90aee130bf 100644
--- a/docs/05_operator_list.dox
+++ b/docs/05_operator_list.dox
@@ -837,6 +837,49 @@ where N = batches, C = channels, H = height, W = width
<tr><td>QUANTIZEDSYMM8PERAXIS
</table>
<tr>
+ <td rowspan="3">Convolution3dLayer
+ <td rowspan="3" style="width:200px;"> Layer to compute a 3D convolution operation.
+ <td rowspan="3">
+ <ul>
+ <li>N/A
+ </ul>
+ <td>CpuRef
+ <td>
+ <ul>
+ <li>NDHWC
+ </ul>
+ <td>
+ <table>
+ <tr><th>
+ <tr><td>BFLOAT16
+ <tr><td>FLOAT16
+ <tr><td>FLOAT32
+ <tr><td>QASYMMS8
+ <tr><td>QASYMMU8
+ <tr><td>QSYMMS8
+ <tr><td>QSYMMS16
+ </table>
+<tr>
+ <td>CpuAcc
+ <td>
+ <ul>
+ <li>N/A
+ </ul>
+ <td>
+ <ul>
+ <li>N/A
+ </ul>
+<tr>
+ <td>GpuAcc
+ <td>
+ <ul>
+ <li>N/A
+ </ul>
+ <td>
+ <ul>
+ <li>N/A
+ </ul>
+<tr>
<td rowspan="1">DebugLayer
<td rowspan="1" style="width:200px;"> Layer to print out inter layer tensor information.
<td rowspan="1">
diff --git a/include/armnn/BackendHelper.hpp b/include/armnn/BackendHelper.hpp
index e3478a79c5..80676deed2 100644
--- a/include/armnn/BackendHelper.hpp
+++ b/include/armnn/BackendHelper.hpp
@@ -108,6 +108,13 @@ public:
const Optional<TensorInfo>& biases,
Optional<std::string&> reasonIfUnsupported = EmptyOptional());
+ bool IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported = EmptyOptional());
+
bool IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported = EmptyOptional());
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index d571f2297b..9a5128a127 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -468,6 +468,74 @@ struct Convolution2dDescriptor : BaseDescriptor
DataLayout m_DataLayout;
};
+/// A Convolution3dDescriptor for the Convolution3dLayer.
+struct Convolution3dDescriptor : BaseDescriptor
+{
+ Convolution3dDescriptor()
+ : m_PadLeft(0)
+ , m_PadRight(0)
+ , m_PadTop(0)
+ , m_PadBottom(0)
+ , m_PadFront(0)
+ , m_PadBack(0)
+ , m_StrideX(1)
+ , m_StrideY(1)
+ , m_StrideZ(1)
+ , m_DilationX(1)
+ , m_DilationY(1)
+ , m_DilationZ(1)
+ , m_BiasEnabled(false)
+ , m_DataLayout(DataLayout::NDHWC)
+ {}
+
+ bool operator ==(const Convolution3dDescriptor& rhs) const
+ {
+ return m_PadLeft == rhs.m_PadLeft &&
+ m_PadRight == rhs.m_PadRight &&
+ m_PadTop == rhs.m_PadTop &&
+ m_PadBottom == rhs.m_PadBottom &&
+ m_PadFront == rhs.m_PadFront &&
+ m_PadBack == rhs.m_PadBack &&
+ m_StrideX == rhs.m_StrideX &&
+ m_StrideY == rhs.m_StrideY &&
+ m_StrideZ == rhs.m_StrideZ &&
+ m_DilationX == rhs.m_DilationX &&
+ m_DilationY == rhs.m_DilationY &&
+ m_DilationZ == rhs.m_DilationZ &&
+ m_BiasEnabled == rhs.m_BiasEnabled &&
+ m_DataLayout == rhs.m_DataLayout;
+ }
+
+ /// Padding left value in the width dimension.
+ uint32_t m_PadLeft;
+ /// Padding right value in the width dimension.
+ uint32_t m_PadRight;
+ /// Padding top value in the height dimension.
+ uint32_t m_PadTop;
+ /// Padding bottom value in the height dimension.
+ uint32_t m_PadBottom;
+ /// Padding front value in the depth dimension.
+ uint32_t m_PadFront;
+ /// Padding back value in the depth dimension.
+ uint32_t m_PadBack;
+ /// Stride value when proceeding through input for the width dimension.
+ uint32_t m_StrideX;
+ /// Stride value when proceeding through input for the height dimension.
+ uint32_t m_StrideY;
+ /// Stride value when proceeding through input for the depth dimension.
+ uint32_t m_StrideZ;
+ /// Dilation along x axis
+ uint32_t m_DilationX;
+ /// Dilation along y axis
+ uint32_t m_DilationY;
+ /// Dilation along z axis
+ uint32_t m_DilationZ;
+ /// Enable/disable bias.
+ bool m_BiasEnabled;
+ /// The data layout to be used (NDHWC).
+ DataLayout m_DataLayout;
+};
+
/// A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
struct DepthwiseConvolution2dDescriptor : BaseDescriptor
{
diff --git a/include/armnn/DescriptorsFwd.hpp b/include/armnn/DescriptorsFwd.hpp
index 396b7285fd..5c4615d6bb 100644
--- a/include/armnn/DescriptorsFwd.hpp
+++ b/include/armnn/DescriptorsFwd.hpp
@@ -16,6 +16,7 @@ struct BatchToSpaceNdDescriptor;
struct ChannelShuffleDescriptor;
struct ComparisonDescriptor;
struct Convolution2dDescriptor;
+struct Convolution3dDescriptor;
struct DepthwiseConvolution2dDescriptor;
struct DetectionPostProcessDescriptor;
struct ElementwiseUnaryDescriptor;
diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp
index 37aeaf47fe..8ec8de0600 100644
--- a/include/armnn/INetwork.hpp
+++ b/include/armnn/INetwork.hpp
@@ -241,6 +241,17 @@ public:
const ConstTensor& biases,
const char* name = nullptr);
+ /// Adds a 3D convolution layer to the network.
+ /// @param convolution3dDescriptor - Description of the 3D convolution layer.
+ /// @param weights - Tensor for the weights data.
+ /// @param biases - Optional tensor for the bias data. If specified, must match the output tensor shape.
+ /// @param name - Optional name for the layer.
+ /// @return - Interface for configuring the layer.
+ IConnectableLayer* AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor,
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name = nullptr);
+
/// Adds a depth to space layer to the network.
/// @param depthToSpaceDescriptor - Parameters for the depth to space operation.
/// @param name - Optional name for the layer.
diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp
index 2fab6b44a9..ef52368365 100644
--- a/include/armnn/Types.hpp
+++ b/include/armnn/Types.hpp
@@ -53,7 +53,8 @@ enum class DataType
enum class DataLayout
{
NCHW = 1,
- NHWC = 2
+ NHWC = 2,
+ NDHWC = 3
};
/// Define the behaviour of the internal profiler when outputting network details
@@ -422,6 +423,8 @@ using InferenceTimingPair = std::pair<HighResolutionClock, HighResolutionClock>;
X(Shape) \
X(UnidirectionalSequenceLstm) \
X(ChannelShuffle) \
+ X(Convolution3d) \
+
// New layers should be added at last to minimize instability.
/// When adding a new layer, adapt also the LastLayer enum value in the
diff --git a/include/armnn/TypesUtils.hpp b/include/armnn/TypesUtils.hpp
index 20fcbbd784..b644daafd8 100644
--- a/include/armnn/TypesUtils.hpp
+++ b/include/armnn/TypesUtils.hpp
@@ -218,9 +218,10 @@ constexpr const char* GetDataLayoutName(DataLayout dataLayout)
{
switch (dataLayout)
{
- case DataLayout::NCHW: return "NCHW";
- case DataLayout::NHWC: return "NHWC";
- default: return "Unknown";
+ case DataLayout::NCHW: return "NCHW";
+ case DataLayout::NHWC: return "NHWC";
+ case DataLayout::NDHWC: return "NDHWC";
+ default: return "Unknown";
}
}
diff --git a/include/armnn/backends/ILayerSupport.hpp b/include/armnn/backends/ILayerSupport.hpp
index f511ee4c89..3744f316b1 100644
--- a/include/armnn/backends/ILayerSupport.hpp
+++ b/include/armnn/backends/ILayerSupport.hpp
@@ -106,6 +106,13 @@ public:
const Optional<TensorInfo>& biases,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const = 0;
+ virtual bool IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const = 0;
+
virtual bool IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const = 0;
diff --git a/include/armnnUtils/DataLayoutIndexed.hpp b/include/armnnUtils/DataLayoutIndexed.hpp
index b26f22043b..163d34b159 100644
--- a/include/armnnUtils/DataLayoutIndexed.hpp
+++ b/include/armnnUtils/DataLayoutIndexed.hpp
@@ -23,6 +23,7 @@ public:
unsigned int GetChannelsIndex() const { return m_ChannelsIndex; }
unsigned int GetHeightIndex() const { return m_HeightIndex; }
unsigned int GetWidthIndex() const { return m_WidthIndex; }
+ unsigned int GetDepthIndex() const { return m_DepthIndex; }
inline unsigned int GetIndex(const armnn::TensorShape& shape,
unsigned int batchIndex, unsigned int channelIndex,
@@ -63,6 +64,7 @@ private:
unsigned int m_ChannelsIndex;
unsigned int m_HeightIndex;
unsigned int m_WidthIndex;
+ unsigned int m_DepthIndex;
};
/// Equality methods
diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp
index c17d076955..1616fd1aad 100644
--- a/src/armnn/BackendHelper.cpp
+++ b/src/armnn/BackendHelper.cpp
@@ -282,6 +282,21 @@ bool LayerSupportHandle::IsConvolution2dSupported(const TensorInfo& input,
reasonIfUnsupported.value());
}
+bool LayerSupportHandle::IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported)
+{
+ return m_LayerSupport->IsConvolution3dSupported(input,
+ output,
+ descriptor,
+ weights,
+ biases,
+ reasonIfUnsupported.value());
+}
+
bool LayerSupportHandle::IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported)
diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp
index 6f39ca0508..49c39b3985 100644
--- a/src/armnn/LayersFwd.hpp
+++ b/src/armnn/LayersFwd.hpp
@@ -21,6 +21,7 @@
#include "layers/ConvertFp32ToBf16Layer.hpp"
#include "layers/ConvertFp32ToFp16Layer.hpp"
#include "layers/Convolution2dLayer.hpp"
+#include "layers/Convolution3dLayer.hpp"
#include "layers/DebugLayer.hpp"
#include "layers/DepthToSpaceLayer.hpp"
#include "layers/DepthwiseConvolution2dLayer.hpp"
@@ -119,6 +120,7 @@ DECLARE_LAYER(ConvertFp16ToFp32)
DECLARE_LAYER(ConvertFp32ToBf16)
DECLARE_LAYER(ConvertFp32ToFp16)
DECLARE_LAYER(Convolution2d)
+DECLARE_LAYER(Convolution3d)
DECLARE_LAYER(Debug)
DECLARE_LAYER(DepthToSpace)
DECLARE_LAYER(DepthwiseConvolution2d)
diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp
index 84097176e7..4070802be8 100644
--- a/src/armnn/Network.cpp
+++ b/src/armnn/Network.cpp
@@ -113,6 +113,15 @@ IConnectableLayer* INetwork::AddConvolution2dLayer(const Convolution2dDescriptor
}
+IConnectableLayer* INetwork::AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor,
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name)
+{
+ return pNetworkImpl->AddConvolution3dLayer(convolution3dDescriptor, weights, biases, name);
+}
+
+
IConnectableLayer* INetwork::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
const char* name)
{
@@ -1991,22 +2000,21 @@ IConnectableLayer* NetworkImpl::AddConvolution2dLayer(const Convolution2dDescrip
return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
}
-IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayerImpl(
- const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
- const ConstTensor& weights,
- const Optional<ConstTensor>& biases,
- const char* name)
+IConnectableLayer* NetworkImpl::AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor,
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name)
{
- if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
+ if (convolution3dDescriptor.m_BiasEnabled && !biases.has_value())
{
- throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
+ throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
}
- const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
+ const auto layer = m_Graph->AddLayer<Convolution3dLayer>(convolution3dDescriptor, name);
layer->m_Weight = std::make_shared<ScopedTensorHandle>(weights);
- if (convolution2dDescriptor.m_BiasEnabled)
+ if (convolution3dDescriptor.m_BiasEnabled)
{
layer->m_Bias = std::make_shared<ScopedTensorHandle>(biases.value());
}
@@ -2020,6 +2028,29 @@ IConnectableLayer* NetworkImpl::AddDepthToSpaceLayer(const DepthToSpaceDescripto
return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
}
+IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayerImpl(
+ const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name)
+{
+ if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
+ {
+ throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
+ }
+
+ const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
+
+ layer->m_Weight = std::make_shared<ScopedTensorHandle>(weights);
+
+ if (convolution2dDescriptor.m_BiasEnabled)
+ {
+ layer->m_Bias = std::make_shared<ScopedTensorHandle>(biases.value());
+ }
+
+ return layer;
+}
+
IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayer(
const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
const ConstTensor& weights,
diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp
index 67c5b5af52..11759c71de 100644
--- a/src/armnn/Network.hpp
+++ b/src/armnn/Network.hpp
@@ -89,6 +89,11 @@ public:
const ConstTensor& biases,
const char* name = nullptr);
+ IConnectableLayer* AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor,
+ const ConstTensor& weights,
+ const Optional<ConstTensor>& biases,
+ const char* name = nullptr);
+
IConnectableLayer* AddConstantLayer(const ConstTensor& input, const char* name = nullptr);
IConnectableLayer* AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
diff --git a/src/armnn/SerializeLayerParameters.cpp b/src/armnn/SerializeLayerParameters.cpp
index 73e0cbce78..da2c39d4b6 100644
--- a/src/armnn/SerializeLayerParameters.cpp
+++ b/src/armnn/SerializeLayerParameters.cpp
@@ -101,6 +101,33 @@ void StringifyLayerParameters<Convolution2dDescriptor>::Serialize(ParameterStrin
fn("DataLayout", GetDataLayoutName(desc.m_DataLayout));
}
+void StringifyLayerParameters<Convolution3dDescriptor>::Serialize(ParameterStringifyFunction& fn,
+ const Convolution3dDescriptor& desc)
+{
+ {
+ std::stringstream ss;
+ ss << "(" << desc.m_PadTop << "," << desc.m_PadLeft
+ << "," << desc.m_PadBottom << "," << desc.m_PadRight
+ << "," << desc.m_PadFront << "," << desc.m_PadBack << ")";
+ fn("Padding(T,L,B,R,F,B)",ss.str());
+ }
+
+ {
+ std::stringstream ss;
+ ss << "(" << desc.m_StrideX << "," << desc.m_StrideY << "," << desc.m_StrideZ << ")";
+ fn("Stride(X,Y,Z)", ss.str());
+ }
+
+ {
+ std::stringstream ss;
+ ss << "(" << desc.m_DilationX << "," << desc.m_DilationY << "," << desc.m_DilationZ << ")";
+ fn("Dilation(X,Y)", ss.str());
+ }
+
+ fn("BiasEnabled",(desc.m_BiasEnabled ? "true" : "false"));
+ fn("DataLayout", GetDataLayoutName(desc.m_DataLayout));
+}
+
void StringifyLayerParameters<DetectionPostProcessDescriptor>::Serialize(ParameterStringifyFunction& fn,
const DetectionPostProcessDescriptor& desc)
{
diff --git a/src/armnn/SerializeLayerParameters.hpp b/src/armnn/SerializeLayerParameters.hpp
index f8fe5e2992..8a3630ce9d 100644
--- a/src/armnn/SerializeLayerParameters.hpp
+++ b/src/armnn/SerializeLayerParameters.hpp
@@ -55,6 +55,11 @@ template <> struct StringifyLayerParameters<Convolution2dDescriptor>
static void Serialize(ParameterStringifyFunction& fn, const Convolution2dDescriptor& desc);
};
+template <> struct StringifyLayerParameters<Convolution3dDescriptor>
+{
+ static void Serialize(ParameterStringifyFunction& fn, const Convolution3dDescriptor& desc);
+};
+
template <> struct StringifyLayerParameters<DetectionPostProcessDescriptor>
{
static void Serialize(ParameterStringifyFunction& fn, const DetectionPostProcessDescriptor& desc);
diff --git a/src/armnn/layers/Convolution3dLayer.cpp b/src/armnn/layers/Convolution3dLayer.cpp
new file mode 100644
index 0000000000..0e38c0b129
--- /dev/null
+++ b/src/armnn/layers/Convolution3dLayer.cpp
@@ -0,0 +1,172 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Convolution3dLayer.hpp"
+#include "LayerCloneBase.hpp"
+
+#include <armnnUtils/DataLayoutIndexed.hpp>
+
+#include <backendsCommon/TensorHandle.hpp>
+
+using namespace armnnUtils;
+
+namespace armnn
+{
+
+Convolution3dLayer::Convolution3dLayer(const Convolution3dDescriptor& param, const char* name)
+ : LayerWithParameters(1, 1, LayerType::Convolution3d, param, name)
+{
+}
+
+void Convolution3dLayer::SerializeLayerParameters(ParameterStringifyFunction& fn) const
+{
+ const std::vector<TensorShape>& inputShapes =
+ {
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ m_Weight->GetTensorInfo().GetShape()
+ };
+
+ // Conv3d Filter Layout: [D,H,W,I,O]
+ const TensorShape filterShape = inputShapes[1];
+ DataLayoutIndexed dataLayoutIndex(m_Param.m_DataLayout);
+ unsigned int filterDepth = filterShape[0];
+ unsigned int filterHeight = filterShape[1];
+ unsigned int filterWidth = filterShape[2];
+ unsigned int inChannels = filterShape[3];
+ unsigned int outChannels = filterShape[4];
+
+ fn("FilterDepth",std::to_string(filterDepth));
+ fn("FilterHeight",std::to_string(filterHeight));
+ fn("FilterWidth",std::to_string(filterWidth));
+ fn("InputChannels",std::to_string(inChannels));
+ fn("OutputChannels",std::to_string(outChannels));
+
+ LayerWithParameters<Convolution3dDescriptor>::SerializeLayerParameters(fn);
+}
+
+std::unique_ptr<IWorkload> Convolution3dLayer::CreateWorkload(const IWorkloadFactory& factory) const
+{
+ // At this level constant data should not be released.
+ ARMNN_ASSERT_MSG(m_Weight != nullptr, "Convolution3dLayer: Weights data should not be null.");
+
+ Convolution3dQueueDescriptor descriptor;
+ descriptor.m_Weight = m_Weight.get();
+
+ if (m_Param.m_BiasEnabled)
+ {
+ ARMNN_ASSERT_MSG(m_Bias != nullptr, "Convolution3dLayer: Bias data should not be null.");
+ descriptor.m_Bias = m_Bias.get();
+ }
+
+ SetAdditionalInfo(descriptor);
+
+ return factory.CreateConvolution3d(descriptor, PrepInfoAndDesc(descriptor));
+}
+
+Convolution3dLayer* Convolution3dLayer::Clone(Graph& graph) const
+{
+ auto layer = CloneBase<Convolution3dLayer>(graph, m_Param, GetName());
+
+ layer->m_Weight = m_Weight ? m_Weight : nullptr;
+
+ if (layer->m_Param.m_BiasEnabled)
+ {
+ layer->m_Bias = m_Bias ? m_Bias : nullptr;
+ }
+
+ return std::move(layer);
+}
+
+std::vector<TensorShape> Convolution3dLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
+{
+ ARMNN_ASSERT(inputShapes.size() == 2);
+ const TensorShape& inputShape = inputShapes[0];
+ const TensorShape& filterShape = inputShapes[1];
+
+ ARMNN_ASSERT_MSG(inputShape.GetNumDimensions() == 5, "Convolutions will always have 5D input.");
+
+ ARMNN_ASSERT( m_Param.m_StrideX > 0);
+ ARMNN_ASSERT( m_Param.m_StrideY > 0);
+ ARMNN_ASSERT( m_Param.m_StrideZ > 0);
+
+ DataLayoutIndexed dataLayoutIndex(m_Param.m_DataLayout);
+
+ unsigned int inWidth = inputShape[dataLayoutIndex.GetWidthIndex()];
+ unsigned int inHeight = inputShape[dataLayoutIndex.GetHeightIndex()];
+ unsigned int inDepth = inputShape[dataLayoutIndex.GetDepthIndex()];
+ unsigned int inBatchSize = inputShape[0];
+
+ // Conv3d Filter Layout: [D,H,W,I,O]
+ unsigned int filterDepth = filterShape[0];
+ unsigned int dilatedFilterDepth = filterDepth + (m_Param.m_DilationZ - 1) * (filterDepth - 1);
+ unsigned int readDepth = (inDepth + m_Param.m_PadFront + m_Param.m_PadBack) - dilatedFilterDepth;
+ unsigned int outDepth = 1 + (readDepth / m_Param.m_StrideZ);
+
+ unsigned int filterHeight = filterShape[1];
+ unsigned int dilatedFilterHeight = filterHeight + (m_Param.m_DilationY - 1) * (filterHeight - 1);
+ unsigned int readHeight = (inHeight + m_Param.m_PadTop + m_Param.m_PadBottom) - dilatedFilterHeight;
+ unsigned int outHeight = 1 + (readHeight / m_Param.m_StrideY);
+
+ unsigned int filterWidth = filterShape[2];
+ unsigned int dilatedFilterWidth = filterWidth + (m_Param.m_DilationX - 1) * (filterWidth - 1);
+ unsigned int readWidth = (inWidth + m_Param.m_PadLeft + m_Param.m_PadRight) - dilatedFilterWidth;
+ unsigned int outWidth = 1 + (readWidth / m_Param.m_StrideX);
+
+ unsigned int outChannels = filterShape[4];
+ unsigned int outBatchSize = inBatchSize;
+
+ TensorShape tensorShape = TensorShape( { outBatchSize, outDepth, outHeight, outWidth, outChannels } );
+
+ return std::vector<TensorShape>({ tensorShape });
+}
+
+void Convolution3dLayer::ValidateTensorShapesFromInputs()
+{
+ VerifyLayerConnections(1, CHECK_LOCATION());
+
+ const TensorShape& outputShape = GetOutputSlot(0).GetTensorInfo().GetShape();
+
+ VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod);
+
+ // check if we m_Weight data is not nullptr
+ ARMNN_ASSERT_MSG(m_Weight != nullptr, "Convolution3dLayer: Weights data should not be null.");
+
+ auto inferredShapes = InferOutputShapes({
+ GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(),
+ m_Weight->GetTensorInfo().GetShape() });
+
+ ARMNN_ASSERT(inferredShapes.size() == 1);
+
+ ValidateAndCopyShape(outputShape, inferredShapes[0], m_ShapeInferenceMethod, "Convolution3dLayer");
+}
+
+Layer::ConstantTensors Convolution3dLayer::GetConstantTensorsByRef()
+{
+ return {m_Weight, m_Bias};
+}
+
+ARMNN_NO_DEPRECATE_WARN_BEGIN
+void Convolution3dLayer::Accept(ILayerVisitor& visitor) const
+{
+ IgnoreUnused(visitor);
+ throw armnn::Exception("Convolution3dLayer: VisitConvolution3dLayer is not implemented");
+}
+ARMNN_NO_DEPRECATE_WARN_END
+
+void Convolution3dLayer::ExecuteStrategy(IStrategy& strategy) const
+{
+ ManagedConstTensorHandle managedWeight(m_Weight);
+ std::vector<armnn::ConstTensor> constTensors { { managedWeight.GetTensorInfo(), managedWeight.Map() } };
+
+ ManagedConstTensorHandle managedBias(m_Bias);
+ if (GetParameters().m_BiasEnabled)
+ {
+ constTensors.emplace_back(ConstTensor(managedBias.GetTensorInfo(), managedBias.Map()));
+ }
+
+ strategy.ExecuteStrategy(this, GetParameters(), constTensors, GetName());
+}
+
+} // namespace armnn
diff --git a/src/armnn/layers/Convolution3dLayer.hpp b/src/armnn/layers/Convolution3dLayer.hpp
new file mode 100644
index 0000000000..bef5715098
--- /dev/null
+++ b/src/armnn/layers/Convolution3dLayer.hpp
@@ -0,0 +1,68 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "LayerWithParameters.hpp"
+
+namespace armnn
+{
+
+class ScopedTensorHandle;
+
+/// This layer represents a convolution 3d operation.
+class Convolution3dLayer : public LayerWithParameters<Convolution3dDescriptor>
+{
+public:
+
+ /// A unique pointer to store Weight values.
+ std::shared_ptr<ConstTensorHandle> m_Weight;
+ /// A unique pointer to store Bias values.
+ std::shared_ptr<ConstTensorHandle> m_Bias;
+
+ /// Makes a workload for the Convolution3d type.
+ /// @param [in] graph The graph where this layer can be found.
+ /// @param [in] factory The workload factory which will create the workload.
+ /// @return A pointer to the created workload, or nullptr if not created.
+ virtual std::unique_ptr<IWorkload> CreateWorkload(const IWorkloadFactory& factory) const override;
+
+ /// Creates a dynamically-allocated copy of this layer.
+ /// @param [in] graph The graph into which this layer is being cloned.
+ Convolution3dLayer* Clone(Graph& graph) const override;
+
+ /// Check if the input tensor shape(s)
+ /// will lead to a valid configuration of @ref Convolution3dLayer.
+ /// @param [in] shapeInferenceMethod Indicates if output shape shall be overwritten or just validated.
+ void ValidateTensorShapesFromInputs() override;
+
+ /// By default returns inputShapes if the number of inputs are equal to number of outputs,
+ /// otherwise infers the output shapes from given input shapes and layer properties.
+ /// @param [in] inputShapes The input shapes layer has.
+ /// @return A vector to the inferred output shape.
+ std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
+
+ ARMNN_NO_DEPRECATE_WARN_BEGIN
+ void Accept(ILayerVisitor& visitor) const override;
+ ARMNN_NO_DEPRECATE_WARN_END
+
+ void ExecuteStrategy(IStrategy& strategy) const override;
+
+ void SerializeLayerParameters(ParameterStringifyFunction& fn) const override;
+
+protected:
+ /// Constructor to create a Convolution3dLayer.
+ /// @param [in] param Convolution3dDescriptor to configure the convolution3d operation.
+ /// @param [in] name Optional name for the layer.
+ Convolution3dLayer(const Convolution3dDescriptor& param, const char* name);
+
+ /// Default destructor
+ ~Convolution3dLayer() = default;
+
+ /// Retrieve the handles to the constant values stored by the layer.
+ /// @return A vector of the constant tensors stored by this layer.
+ ConstantTensors GetConstantTensorsByRef() override;
+};
+
+} // namespace
diff --git a/src/armnn/test/InferOutputTests.cpp b/src/armnn/test/InferOutputTests.cpp
index 81ad7b2d38..5365b831cf 100644
--- a/src/armnn/test/InferOutputTests.cpp
+++ b/src/armnn/test/InferOutputTests.cpp
@@ -38,6 +38,9 @@ ARMNN_SIMPLE_TEST_CASE(StackValidateTensorShapesFromInputsNoMatch, StackValidate
// Convolution2D
ARMNN_SIMPLE_TEST_CASE(Convolution2dInferOutputShape, Convolution2dInferOutputShapeTest)
+// Convolution3D
+ARMNN_SIMPLE_TEST_CASE(Convolution3dInferOutputShape, Convolution3dInferOutputShapeTest)
+
// DepthwiseConvolution2D
ARMNN_SIMPLE_TEST_CASE(DepthwiseConvolution2dInferOutputShape, DepthwiseConvolution2dInferOutputShapeTest)
diff --git a/src/armnn/test/InferOutputTests.hpp b/src/armnn/test/InferOutputTests.hpp
index 6e2676ec8e..e2c854551f 100644
--- a/src/armnn/test/InferOutputTests.hpp
+++ b/src/armnn/test/InferOutputTests.hpp
@@ -464,6 +464,43 @@ void Convolution2dInferOutputShapeTest()
CHECK(expectedOutputShape == convolution2dLayer->InferOutputShapes(shapes).at(0));
}
+void Convolution3dInferOutputShapeTest()
+{
+ armnn::Graph graph;
+
+ armnn::Convolution3dDescriptor descriptor;
+ descriptor.m_DilationX = 1;
+ descriptor.m_DilationY = 1;
+ descriptor.m_DilationZ = 1;
+ descriptor.m_PadTop = 1;
+ descriptor.m_PadBottom = 1;
+ descriptor.m_PadLeft = 1;
+ descriptor.m_PadRight = 1;
+ descriptor.m_PadFront = 1;
+ descriptor.m_PadBack = 1;
+ descriptor.m_StrideX = 2;
+ descriptor.m_StrideY = 2;
+ descriptor.m_StrideZ = 2;
+ descriptor.m_DataLayout = armnn::DataLayout::NDHWC;
+
+ armnn::Convolution3dLayer* const convolution3dLayer =
+ graph.AddLayer<armnn::Convolution3dLayer>(descriptor, "convolution3d");
+
+ std::vector<armnn::TensorShape> shapes;
+ const std::vector<unsigned int> inputSize = {1, 5, 5, 5, 1};
+ armnn::TensorShape inputShape(5, inputSize.data());
+ shapes.push_back(inputShape);
+
+ const std::vector<unsigned int> filterSize = {3, 3, 3, 1, 1 };
+ armnn::TensorShape filterShape(5, filterSize.data());
+ shapes.push_back(filterShape);
+
+ const std::vector<unsigned int> expectedOutputSizes = {1, 3, 3, 3, 1};
+ armnn::TensorShape expectedOutputShape(5, expectedOutputSizes.data());
+
+ CHECK(expectedOutputShape == convolution3dLayer->InferOutputShapes(shapes).at(0));
+}
+
void TransposeConvolution2dInferOutputShapeTest()
{
armnn::Graph graph;
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index 13415814a2..eaeab780e4 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -221,6 +221,7 @@ m_ParserFunctions(Layer_MAX+1, &IDeserializer::DeserializerImpl::ParseUnsupporte
m_ParserFunctions[Layer_ConcatLayer] = &DeserializerImpl::ParseConcat;
m_ParserFunctions[Layer_ConstantLayer] = &DeserializerImpl::ParseConstant;
m_ParserFunctions[Layer_Convolution2dLayer] = &DeserializerImpl::ParseConvolution2d;
+ m_ParserFunctions[Layer_Convolution3dLayer] = &DeserializerImpl::ParseConvolution3d;
m_ParserFunctions[Layer_DepthToSpaceLayer] = &DeserializerImpl::ParseDepthToSpace;
m_ParserFunctions[Layer_DepthwiseConvolution2dLayer] = &DeserializerImpl::ParseDepthwiseConvolution2d;
m_ParserFunctions[Layer_DequantizeLayer] = &DeserializerImpl::ParseDequantize;
@@ -304,6 +305,8 @@ LayerBaseRawPtr IDeserializer::DeserializerImpl::GetBaseLayer(const GraphPtr& gr
return graphPtr->layers()->Get(layerIndex)->layer_as_ConstantLayer()->base();
case Layer::Layer_Convolution2dLayer:
return graphPtr->layers()->Get(layerIndex)->layer_as_Convolution2dLayer()->base();
+ case Layer::Layer_Convolution3dLayer:
+ return graphPtr->layers()->Get(layerIndex)->layer_as_Convolution3dLayer()->base();
case Layer::Layer_DepthToSpaceLayer:
return graphPtr->layers()->Get(layerIndex)->layer_as_DepthToSpaceLayer()->base();
case Layer::Layer_DepthwiseConvolution2dLayer:
@@ -444,6 +447,8 @@ armnn::DataLayout ToDataLayout(armnnSerializer::DataLayout dataLayout)
{
case armnnSerializer::DataLayout::DataLayout_NHWC:
return armnn::DataLayout::NHWC;
+ case armnnSerializer::DataLayout::DataLayout_NDHWC:
+ return armnn::DataLayout::NDHWC;
case armnnSerializer::DataLayout::DataLayout_NCHW:
default:
return armnn::DataLayout::NCHW;
@@ -1392,6 +1397,56 @@ void IDeserializer::DeserializerImpl::ParseConvolution2d(GraphPtr graph, unsigne
RegisterOutputSlots(graph, layerIndex, layer);
}
+void IDeserializer::DeserializerImpl::ParseConvolution3d(GraphPtr graph, unsigned int layerIndex)
+{
+ CHECK_LAYERS(graph, 0, layerIndex);
+ auto inputs = GetInputs(graph, layerIndex);
+ CHECK_LOCATION();
+ CHECK_VALID_SIZE(inputs.size(), 1);
+
+ auto outputs = GetOutputs(graph, layerIndex);
+ CHECK_VALID_SIZE(outputs.size(), 1);
+
+ auto serializerLayer = graph->layers()->Get(layerIndex)->layer_as_Convolution3dLayer();
+ auto layerName = GetLayerName(graph, layerIndex);
+ auto serializerDescriptor = serializerLayer->descriptor();
+
+ armnn::Convolution3dDescriptor descriptor;
+ descriptor.m_PadLeft = serializerDescriptor->padLeft();
+ descriptor.m_PadRight = serializerDescriptor->padRight();
+ descriptor.m_PadTop = serializerDescriptor->padTop();
+ descriptor.m_PadBottom = serializerDescriptor->padBottom();
+ descriptor.m_PadFront = serializerDescriptor->padFront();
+ descriptor.m_PadBack = serializerDescriptor->padBack();
+ descriptor.m_StrideX = serializerDescriptor->strideX();
+ descriptor.m_StrideY = serializerDescriptor->strideY();
+ descriptor.m_StrideZ = serializerDescriptor->strideZ();
+ descriptor.m_DilationX = serializerDescriptor->dilationX();
+ descriptor.m_DilationY = serializerDescriptor->dilationY();
+ descriptor.m_DilationZ = serializerDescriptor->dilationZ();
+ descriptor.m_BiasEnabled = serializerDescriptor->biasEnabled();;
+ descriptor.m_DataLayout = ToDataLayout(serializerDescriptor->dataLayout());
+
+ armnn::ConstTensor weights = ToConstTensor(serializerLayer->weights());
+ armnn::ConstTensor biases;
+
+ armnn::Optional<armnn::ConstTensor> optionalBiases = armnn::EmptyOptional();
+ if (descriptor.m_BiasEnabled)
+ {
+ biases = ToConstTensor(serializerLayer->biases());
+ optionalBiases = armnn::Optional<armnn::ConstTensor>(biases);
+ }
+ IConnectableLayer* layer = m_Network->AddConvolution3dLayer(descriptor,
+ weights,
+ optionalBiases,
+ layerName.c_str());
+ armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
+ layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+ RegisterInputSlots(graph, layerIndex, layer);
+ RegisterOutputSlots(graph, layerIndex, layer);
+}
+
void IDeserializer::DeserializerImpl::ParseDepthToSpace(GraphPtr graph, unsigned int layerIndex)
{
CHECK_LAYERS(graph, 0, layerIndex);
diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp
index a07e41ff76..d2291c07a7 100644
--- a/src/armnnDeserializer/Deserializer.hpp
+++ b/src/armnnDeserializer/Deserializer.hpp
@@ -93,6 +93,7 @@ private:
void ParseConcat(GraphPtr graph, unsigned int layerIndex);
void ParseConstant(GraphPtr graph, unsigned int layerIndex);
void ParseConvolution2d(GraphPtr graph, unsigned int layerIndex);
+ void ParseConvolution3d(GraphPtr graph, unsigned int layerIndex);
void ParseDepthToSpace(GraphPtr graph, unsigned int layerIndex);
void ParseDepthwiseConvolution2d(GraphPtr graph, unsigned int layerIndex);
void ParseDequantize(GraphPtr graph, unsigned int layerIndex);
diff --git a/src/armnnDeserializer/test/DeserializeConvolution3d.cpp b/src/armnnDeserializer/test/DeserializeConvolution3d.cpp
new file mode 100644
index 0000000000..057ab6fbda
--- /dev/null
+++ b/src/armnnDeserializer/test/DeserializeConvolution3d.cpp
@@ -0,0 +1,223 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "ParserFlatbuffersSerializeFixture.hpp"
+#include <armnnDeserializer/IDeserializer.hpp>
+
+#include <string>
+
+TEST_SUITE("Deserializer_Convolution3d")
+{
+struct Convolution3dFixture : public ParserFlatbuffersSerializeFixture
+{
+ explicit Convolution3dFixture(const std::string& inputShape,
+ const std::string& outputShape,
+ const std::string& weightsShape,
+ const std::string& dataType)
+ {
+ m_JsonString = R"(
+ {
+ inputIds: [0],
+ outputIds: [2],
+ layers: [
+ {
+ layer_type: "InputLayer",
+ layer: {
+ base: {
+ layerBindingId: 0,
+ base: {
+ layerName: "InputLayer",
+ layerType: "Input",
+ inputSlots: [{
+ index: 0,
+ connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+ }],
+ outputSlots: [
+ {
+ index: 0,
+ tensorInfo: {
+ dimensions: )" + inputShape + R"(,
+ dataType: )" + dataType + R"(,
+ quantizationScale: 0.1,
+ dimensionSpecificity: [
+ true,
+ true,
+ true,
+ true,
+ true
+ ]
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ layer_type: "Convolution3dLayer",
+ layer: {
+ base: {
+ index: 1,
+ layerName: "convolution3d",
+ layerType: "Convolution2d",
+ inputSlots: [
+ {
+ index: 0,
+ connection: {
+ sourceLayerIndex: 0,
+ outputSlotIndex: 0
+ }
+ }
+ ],
+ outputSlots: [
+ {
+ index: 0,
+ tensorInfo: {
+ dimensions: )" + outputShape + R"(,
+ dataType: )" + dataType + R"(,
+ quantizationScale: 0.1,
+ dimensionSpecificity: [
+ true,
+ true,
+ true,
+ true,
+ true
+ ]
+ }
+ }
+ ]
+ },
+ descriptor: {
+ strideX: 2,
+ strideY: 2,
+ strideZ: 2
+ },
+ weights: {
+ info: {
+ dimensions: )" + weightsShape + R"(,
+ dataType: )" + dataType + R"(,
+ quantizationScale: 0.1,
+ dimensionSpecificity: [
+ true,
+ true,
+ true,
+ true,
+ true
+ ]
+ },
+ data_type: "ByteData",
+ data: {
+ data: [
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1,
+
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0
+ ]
+ }
+ }
+ }
+ },
+ {
+ layer_type: "OutputLayer",
+ layer: {
+ base: {
+ layerBindingId: 2,
+ base: {
+ index: 2,
+ layerName: "OutputLayer",
+ layerType: "Output",
+ inputSlots: [
+ {
+ connection: {
+ sourceLayerIndex: 1,
+ outputSlotIndex: 0
+ }
+ }
+ ],
+ outputSlots: [{
+ index: 0,
+ tensorInfo: {
+ dimensions: )" + outputShape + R"(,
+ dataType: )" + dataType + R"(
+ },
+ }]
+ }
+ }
+ }
+ }
+ ],
+ featureVersions: {
+ bindingIdsScheme: 1,
+ weightsLayoutScheme: 1,
+ constantTensorsAsInputs: 1
+ }
+ }
+ )";
+ Setup();
+ }
+
+
+};
+
+struct SimpleConvolution3dFixture : Convolution3dFixture
+{
+ SimpleConvolution3dFixture() : Convolution3dFixture(
+ "[ 1, 5, 5, 5, 1 ]",
+ "[ 1, 2, 2, 2, 1 ]",
+ "[ 3, 3, 3, 1, 1 ]",
+ "QAsymmS8") {}
+};
+
+TEST_CASE_FIXTURE(SimpleConvolution3dFixture, "Convolution3dInt8")
+{
+ RunTest<5, armnn::DataType::QAsymmS8>(
+ 0,
+ {{"InputLayer", { 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19,
+
+ 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44,
+
+ 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69,
+
+ 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99,
+
+ 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124
+ }}},
+ {{"OutputLayer", {5, 7,
+
+ 14, 16,
+
+ 50, 52,
+
+ 59, 61}}});
+}
+
+}
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index 740090bcc8..77982888c8 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -45,7 +45,8 @@ enum DataType : byte {
enum DataLayout : byte {
NHWC = 0,
- NCHW = 1
+ NCHW = 1,
+ NDHWC = 2
}
enum ReduceOperation: byte {
@@ -177,6 +178,7 @@ enum LayerType : uint {
Shape = 62,
UnidirectionalSequenceLstm = 63,
ChannelShuffle = 64,
+ Convolution3d = 65,
}
// Base layer table to be used as part of other layers
@@ -282,6 +284,30 @@ table Convolution2dDescriptor {
dataLayout:DataLayout = NCHW;
}
+table Convolution3dLayer {
+ base:LayerBase;
+ descriptor:Convolution3dDescriptor;
+ weights:ConstTensor;
+ biases:ConstTensor;
+}
+
+table Convolution3dDescriptor {
+ padLeft:uint;
+ padRight:uint;
+ padTop:uint;
+ padBottom:uint;
+ padFront:uint;
+ padBack:uint;
+ strideX:uint;
+ strideY:uint;
+ strideZ:uint;
+ dilationX:uint = 1;
+ dilationY:uint = 1;
+ dilationZ:uint = 1;
+ biasEnabled:bool = false;
+ dataLayout:DataLayout = NDHWC;
+}
+
table DepthToSpaceLayer {
base:LayerBase;
descriptor:DepthToSpaceDescriptor;
@@ -1012,6 +1038,7 @@ union Layer {
ShapeLayer,
UnidirectionalSequenceLstmLayer,
ChannelShuffleLayer,
+ Convolution3dLayer,
}
table AnyLayer {
diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h
index 653ea6a1e5..8234aa9c47 100644
--- a/src/armnnSerializer/ArmnnSchema_generated.h
+++ b/src/armnnSerializer/ArmnnSchema_generated.h
@@ -86,6 +86,12 @@ struct Convolution2dLayerBuilder;
struct Convolution2dDescriptor;
struct Convolution2dDescriptorBuilder;
+struct Convolution3dLayer;
+struct Convolution3dLayerBuilder;
+
+struct Convolution3dDescriptor;
+struct Convolution3dDescriptorBuilder;
+
struct DepthToSpaceLayer;
struct DepthToSpaceLayerBuilder;
@@ -533,29 +539,32 @@ inline const char *EnumNameDataType(DataType e) {
enum DataLayout {
DataLayout_NHWC = 0,
DataLayout_NCHW = 1,
+ DataLayout_NDHWC = 2,
DataLayout_MIN = DataLayout_NHWC,
- DataLayout_MAX = DataLayout_NCHW
+ DataLayout_MAX = DataLayout_NDHWC
};
-inline const DataLayout (&EnumValuesDataLayout())[2] {
+inline const DataLayout (&EnumValuesDataLayout())[3] {
static const DataLayout values[] = {
DataLayout_NHWC,
- DataLayout_NCHW
+ DataLayout_NCHW,
+ DataLayout_NDHWC
};
return values;
}
inline const char * const *EnumNamesDataLayout() {
- static const char * const names[3] = {
+ static const char * const names[4] = {
"NHWC",
"NCHW",
+ "NDHWC",
nullptr
};
return names;
}
inline const char *EnumNameDataLayout(DataLayout e) {
- if (flatbuffers::IsOutRange(e, DataLayout_NHWC, DataLayout_NCHW)) return "";
+ if (flatbuffers::IsOutRange(e, DataLayout_NHWC, DataLayout_NDHWC)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesDataLayout()[index];
}
@@ -757,11 +766,12 @@ enum LayerType {
LayerType_Shape = 62,
LayerType_UnidirectionalSequenceLstm = 63,
LayerType_ChannelShuffle = 64,
+ LayerType_Convolution3d = 65,
LayerType_MIN = LayerType_Addition,
- LayerType_MAX = LayerType_ChannelShuffle
+ LayerType_MAX = LayerType_Convolution3d
};
-inline const LayerType (&EnumValuesLayerType())[65] {
+inline const LayerType (&EnumValuesLayerType())[66] {
static const LayerType values[] = {
LayerType_Addition,
LayerType_Input,
@@ -827,13 +837,14 @@ inline const LayerType (&EnumValuesLayerType())[65] {
LayerType_Cast,
LayerType_Shape,
LayerType_UnidirectionalSequenceLstm,
- LayerType_ChannelShuffle
+ LayerType_ChannelShuffle,
+ LayerType_Convolution3d
};
return values;
}
inline const char * const *EnumNamesLayerType() {
- static const char * const names[66] = {
+ static const char * const names[67] = {
"Addition",
"Input",
"Multiplication",
@@ -899,13 +910,14 @@ inline const char * const *EnumNamesLayerType() {
"Shape",
"UnidirectionalSequenceLstm",
"ChannelShuffle",
+ "Convolution3d",
nullptr
};
return names;
}
inline const char *EnumNameLayerType(LayerType e) {
- if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_ChannelShuffle)) return "";
+ if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_Convolution3d)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLayerType()[index];
}
@@ -1250,11 +1262,12 @@ enum Layer {
Layer_ShapeLayer = 63,
Layer_UnidirectionalSequenceLstmLayer = 64,
Layer_ChannelShuffleLayer = 65,
+ Layer_Convolution3dLayer = 66,
Layer_MIN = Layer_NONE,
- Layer_MAX = Layer_ChannelShuffleLayer
+ Layer_MAX = Layer_Convolution3dLayer
};
-inline const Layer (&EnumValuesLayer())[66] {
+inline const Layer (&EnumValuesLayer())[67] {
static const Layer values[] = {
Layer_NONE,
Layer_ActivationLayer,
@@ -1321,13 +1334,14 @@ inline const Layer (&EnumValuesLayer())[66] {
Layer_CastLayer,
Layer_ShapeLayer,
Layer_UnidirectionalSequenceLstmLayer,
- Layer_ChannelShuffleLayer
+ Layer_ChannelShuffleLayer,
+ Layer_Convolution3dLayer
};
return values;
}
inline const char * const *EnumNamesLayer() {
- static const char * const names[67] = {
+ static const char * const names[68] = {
"NONE",
"ActivationLayer",
"AdditionLayer",
@@ -1394,13 +1408,14 @@ inline const char * const *EnumNamesLayer() {
"ShapeLayer",
"UnidirectionalSequenceLstmLayer",
"ChannelShuffleLayer",
+ "Convolution3dLayer",
nullptr
};
return names;
}
inline const char *EnumNameLayer(Layer e) {
- if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_ChannelShuffleLayer)) return "";
+ if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_Convolution3dLayer)) return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesLayer()[index];
}
@@ -1669,6 +1684,10 @@ template<> struct LayerTraits<armnnSerializer::ChannelShuffleLayer> {
static const Layer enum_value = Layer_ChannelShuffleLayer;
};
+template<> struct LayerTraits<armnnSerializer::Convolution3dLayer> {
+ static const Layer enum_value = Layer_Convolution3dLayer;
+};
+
bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer type);
bool VerifyLayerVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
@@ -3227,6 +3246,254 @@ inline flatbuffers::Offset<Convolution2dDescriptor> CreateConvolution2dDescripto
return builder_.Finish();
}
+struct Convolution3dLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef Convolution3dLayerBuilder Builder;
+ enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+ VT_BASE = 4,
+ VT_DESCRIPTOR = 6,
+ VT_WEIGHTS = 8,
+ VT_BIASES = 10
+ };
+ const armnnSerializer::LayerBase *base() const {
+ return GetPointer<const armnnSerializer::LayerBase *>(VT_BASE);
+ }
+ const armnnSerializer::Convolution3dDescriptor *descriptor() const {
+ return GetPointer<const armnnSerializer::Convolution3dDescriptor *>(VT_DESCRIPTOR);
+ }
+ const armnnSerializer::ConstTensor *weights() const {
+ return GetPointer<const armnnSerializer::ConstTensor *>(VT_WEIGHTS);
+ }
+ const armnnSerializer::ConstTensor *biases() const {
+ return GetPointer<const armnnSerializer::ConstTensor *>(VT_BIASES);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyOffset(verifier, VT_BASE) &&
+ verifier.VerifyTable(base()) &&
+ VerifyOffset(verifier, VT_DESCRIPTOR) &&
+ verifier.VerifyTable(descriptor()) &&
+ VerifyOffset(verifier, VT_WEIGHTS) &&
+ verifier.VerifyTable(weights()) &&
+ VerifyOffset(verifier, VT_BIASES) &&
+ verifier.VerifyTable(biases()) &&
+ verifier.EndTable();
+ }
+};
+
+struct Convolution3dLayerBuilder {
+ typedef Convolution3dLayer Table;
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_base(flatbuffers::Offset<armnnSerializer::LayerBase> base) {
+ fbb_.AddOffset(Convolution3dLayer::VT_BASE, base);
+ }
+ void add_descriptor(flatbuffers::Offset<armnnSerializer::Convolution3dDescriptor> descriptor) {
+ fbb_.AddOffset(Convolution3dLayer::VT_DESCRIPTOR, descriptor);
+ }
+ void add_weights(flatbuffers::Offset<armnnSerializer::ConstTensor> weights) {
+ fbb_.AddOffset(Convolution3dLayer::VT_WEIGHTS, weights);
+ }
+ void add_biases(flatbuffers::Offset<armnnSerializer::ConstTensor> biases) {
+ fbb_.AddOffset(Convolution3dLayer::VT_BIASES, biases);
+ }
+ explicit Convolution3dLayerBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+ : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ Convolution3dLayerBuilder &operator=(const Convolution3dLayerBuilder &);
+ flatbuffers::Offset<Convolution3dLayer> Finish() {
+ const auto end = fbb_.EndTable(start_);
+ auto o = flatbuffers::Offset<Convolution3dLayer>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<Convolution3dLayer> CreateConvolution3dLayer(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<armnnSerializer::LayerBase> base = 0,
+ flatbuffers::Offset<armnnSerializer::Convolution3dDescriptor> descriptor = 0,
+ flatbuffers::Offset<armnnSerializer::ConstTensor> weights = 0,
+ flatbuffers::Offset<armnnSerializer::ConstTensor> biases = 0) {
+ Convolution3dLayerBuilder builder_(_fbb);
+ builder_.add_biases(biases);
+ builder_.add_weights(weights);
+ builder_.add_descriptor(descriptor);
+ builder_.add_base(base);
+ return builder_.Finish();
+}
+
+struct Convolution3dDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ typedef Convolution3dDescriptorBuilder Builder;
+ enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+ VT_PADLEFT = 4,
+ VT_PADRIGHT = 6,
+ VT_PADTOP = 8,
+ VT_PADBOTTOM = 10,
+ VT_PADFRONT = 12,
+ VT_PADBACK = 14,
+ VT_STRIDEX = 16,
+ VT_STRIDEY = 18,
+ VT_STRIDEZ = 20,
+ VT_DILATIONX = 22,
+ VT_DILATIONY = 24,
+ VT_DILATIONZ = 26,
+ VT_BIASENABLED = 28,
+ VT_DATALAYOUT = 30
+ };
+ uint32_t padLeft() const {
+ return GetField<uint32_t>(VT_PADLEFT, 0);
+ }
+ uint32_t padRight() const {
+ return GetField<uint32_t>(VT_PADRIGHT, 0);
+ }
+ uint32_t padTop() const {
+ return GetField<uint32_t>(VT_PADTOP, 0);
+ }
+ uint32_t padBottom() const {
+ return GetField<uint32_t>(VT_PADBOTTOM, 0);
+ }
+ uint32_t padFront() const {
+ return GetField<uint32_t>(VT_PADFRONT, 0);
+ }
+ uint32_t padBack() const {
+ return GetField<uint32_t>(VT_PADBACK, 0);
+ }
+ uint32_t strideX() const {
+ return GetField<uint32_t>(VT_STRIDEX, 0);
+ }
+ uint32_t strideY() const {
+ return GetField<uint32_t>(VT_STRIDEY, 0);
+ }
+ uint32_t strideZ() const {
+ return GetField<uint32_t>(VT_STRIDEZ, 0);
+ }
+ uint32_t dilationX() const {
+ return GetField<uint32_t>(VT_DILATIONX, 1);
+ }
+ uint32_t dilationY() const {
+ return GetField<uint32_t>(VT_DILATIONY, 1);
+ }
+ uint32_t dilationZ() const {
+ return GetField<uint32_t>(VT_DILATIONZ, 1);
+ }
+ bool biasEnabled() const {
+ return GetField<uint8_t>(VT_BIASENABLED, 0) != 0;
+ }
+ armnnSerializer::DataLayout dataLayout() const {
+ return static_cast<armnnSerializer::DataLayout>(GetField<int8_t>(VT_DATALAYOUT, 2));
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<uint32_t>(verifier, VT_PADLEFT) &&
+ VerifyField<uint32_t>(verifier, VT_PADRIGHT) &&
+ VerifyField<uint32_t>(verifier, VT_PADTOP) &&
+ VerifyField<uint32_t>(verifier, VT_PADBOTTOM) &&
+ VerifyField<uint32_t>(verifier, VT_PADFRONT) &&
+ VerifyField<uint32_t>(verifier, VT_PADBACK) &&
+ VerifyField<uint32_t>(verifier, VT_STRIDEX) &&
+ VerifyField<uint32_t>(verifier, VT_STRIDEY) &&
+ VerifyField<uint32_t>(verifier, VT_STRIDEZ) &&
+ VerifyField<uint32_t>(verifier, VT_DILATIONX) &&
+ VerifyField<uint32_t>(verifier, VT_DILATIONY) &&
+ VerifyField<uint32_t>(verifier, VT_DILATIONZ) &&
+ VerifyField<uint8_t>(verifier, VT_BIASENABLED) &&
+ VerifyField<int8_t>(verifier, VT_DATALAYOUT) &&
+ verifier.EndTable();
+ }
+};
+
+struct Convolution3dDescriptorBuilder {
+ typedef Convolution3dDescriptor Table;
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_padLeft(uint32_t padLeft) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADLEFT, padLeft, 0);
+ }
+ void add_padRight(uint32_t padRight) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADRIGHT, padRight, 0);
+ }
+ void add_padTop(uint32_t padTop) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADTOP, padTop, 0);
+ }
+ void add_padBottom(uint32_t padBottom) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADBOTTOM, padBottom, 0);
+ }
+ void add_padFront(uint32_t padFront) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADFRONT, padFront, 0);
+ }
+ void add_padBack(uint32_t padBack) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_PADBACK, padBack, 0);
+ }
+ void add_strideX(uint32_t strideX) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_STRIDEX, strideX, 0);
+ }
+ void add_strideY(uint32_t strideY) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_STRIDEY, strideY, 0);
+ }
+ void add_strideZ(uint32_t strideZ) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_STRIDEZ, strideZ, 0);
+ }
+ void add_dilationX(uint32_t dilationX) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_DILATIONX, dilationX, 1);
+ }
+ void add_dilationY(uint32_t dilationY) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_DILATIONY, dilationY, 1);
+ }
+ void add_dilationZ(uint32_t dilationZ) {
+ fbb_.AddElement<uint32_t>(Convolution3dDescriptor::VT_DILATIONZ, dilationZ, 1);
+ }
+ void add_biasEnabled(bool biasEnabled) {
+ fbb_.AddElement<uint8_t>(Convolution3dDescriptor::VT_BIASENABLED, static_cast<uint8_t>(biasEnabled), 0);
+ }
+ void add_dataLayout(armnnSerializer::DataLayout dataLayout) {
+ fbb_.AddElement<int8_t>(Convolution3dDescriptor::VT_DATALAYOUT, static_cast<int8_t>(dataLayout), 2);
+ }
+ explicit Convolution3dDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+ : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ Convolution3dDescriptorBuilder &operator=(const Convolution3dDescriptorBuilder &);
+ flatbuffers::Offset<Convolution3dDescriptor> Finish() {
+ const auto end = fbb_.EndTable(start_);
+ auto o = flatbuffers::Offset<Convolution3dDescriptor>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<Convolution3dDescriptor> CreateConvolution3dDescriptor(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ uint32_t padLeft = 0,
+ uint32_t padRight = 0,
+ uint32_t padTop = 0,
+ uint32_t padBottom = 0,
+ uint32_t padFront = 0,
+ uint32_t padBack = 0,
+ uint32_t strideX = 0,
+ uint32_t strideY = 0,
+ uint32_t strideZ = 0,
+ uint32_t dilationX = 1,
+ uint32_t dilationY = 1,
+ uint32_t dilationZ = 1,
+ bool biasEnabled = false,
+ armnnSerializer::DataLayout dataLayout = armnnSerializer::DataLayout_NDHWC) {
+ Convolution3dDescriptorBuilder builder_(_fbb);
+ builder_.add_dilationZ(dilationZ);
+ builder_.add_dilationY(dilationY);
+ builder_.add_dilationX(dilationX);
+ builder_.add_strideZ(strideZ);
+ builder_.add_strideY(strideY);
+ builder_.add_strideX(strideX);
+ builder_.add_padBack(padBack);
+ builder_.add_padFront(padFront);
+ builder_.add_padBottom(padBottom);
+ builder_.add_padTop(padTop);
+ builder_.add_padRight(padRight);
+ builder_.add_padLeft(padLeft);
+ builder_.add_dataLayout(dataLayout);
+ builder_.add_biasEnabled(biasEnabled);
+ return builder_.Finish();
+}
+
struct DepthToSpaceLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
typedef DepthToSpaceLayerBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@@ -9963,6 +10230,9 @@ struct AnyLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
const armnnSerializer::ChannelShuffleLayer *layer_as_ChannelShuffleLayer() const {
return layer_type() == armnnSerializer::Layer_ChannelShuffleLayer ? static_cast<const armnnSerializer::ChannelShuffleLayer *>(layer()) : nullptr;
}
+ const armnnSerializer::Convolution3dLayer *layer_as_Convolution3dLayer() const {
+ return layer_type() == armnnSerializer::Layer_Convolution3dLayer ? static_cast<const armnnSerializer::Convolution3dLayer *>(layer()) : nullptr;
+ }
bool Verify(flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) &&
VerifyField<uint8_t>(verifier, VT_LAYER_TYPE) &&
@@ -10232,6 +10502,10 @@ template<> inline const armnnSerializer::ChannelShuffleLayer *AnyLayer::layer_as
return layer_as_ChannelShuffleLayer();
}
+template<> inline const armnnSerializer::Convolution3dLayer *AnyLayer::layer_as<armnnSerializer::Convolution3dLayer>() const {
+ return layer_as_Convolution3dLayer();
+}
+
struct AnyLayerBuilder {
typedef AnyLayer Table;
flatbuffers::FlatBufferBuilder &fbb_;
@@ -10722,6 +10996,10 @@ inline bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer
auto ptr = reinterpret_cast<const armnnSerializer::ChannelShuffleLayer *>(obj);
return verifier.VerifyTable(ptr);
}
+ case Layer_Convolution3dLayer: {
+ auto ptr = reinterpret_cast<const armnnSerializer::Convolution3dLayer *>(obj);
+ return verifier.VerifyTable(ptr);
+ }
default: return true;
}
}
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index 9a3a270de5..efaf9f81cd 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -397,6 +397,54 @@ void SerializerStrategy::SerializeConvolution2dLayer(const armnn::IConnectableLa
CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_Convolution2dLayer);
}
+// Build FlatBuffer for Convolution2dLayer
+void SerializerStrategy::SerializeConvolution3dLayer(const armnn::IConnectableLayer* layer,
+ const armnn::Convolution3dDescriptor& descriptor,
+ const std::vector<armnn::ConstTensor>& constants,
+ const char* name)
+{
+ IgnoreUnused(name);
+
+ const armnn::ConstTensor weights = constants[0];
+
+ // Create FlatBuffer BaseLayer
+ auto flatBufferBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Convolution2d);
+
+ auto flatBufferDescriptor = CreateConvolution3dDescriptor(m_flatBufferBuilder,
+ descriptor.m_PadLeft,
+ descriptor.m_PadRight,
+ descriptor.m_PadTop,
+ descriptor.m_PadBottom,
+ descriptor.m_PadFront,
+ descriptor.m_PadBack,
+ descriptor.m_StrideX,
+ descriptor.m_StrideY,
+ descriptor.m_StrideZ,
+ descriptor.m_DilationX,
+ descriptor.m_DilationY,
+ descriptor.m_DilationZ,
+ descriptor.m_BiasEnabled,
+ GetFlatBufferDataLayout(descriptor.m_DataLayout));
+ auto flatBufferWeightsConstTensorInfo = CreateConstTensorInfo(weights);
+ flatbuffers::Offset<serializer::ConstTensor> flatBufferBiasesConstTensorInfo;
+
+ if (constants.size() > 1)
+ {
+ const armnn::ConstTensor biases = constants[1];
+ flatBufferBiasesConstTensorInfo = CreateConstTensorInfo(biases);
+ }
+
+ // Create the FlatBuffer Convolution2dLayer
+ auto flatBufferLayer = CreateConvolution3dLayer(m_flatBufferBuilder,
+ flatBufferBaseLayer,
+ flatBufferDescriptor,
+ flatBufferWeightsConstTensorInfo,
+ flatBufferBiasesConstTensorInfo);
+
+ // Add the AnyLayer to the FlatBufferLayers
+ CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_Convolution3dLayer);
+}
+
void SerializerStrategy::SerializeDepthToSpaceLayer(const armnn::IConnectableLayer* layer,
const armnn::DepthToSpaceDescriptor& descriptor,
const char* name)
@@ -2054,6 +2102,16 @@ void SerializerStrategy::ExecuteStrategy(const armnn::IConnectableLayer* layer,
name);
break;
}
+ case armnn::LayerType::Convolution3d :
+ {
+ const armnn::Convolution3dDescriptor& layerDescriptor =
+ static_cast<const armnn::Convolution3dDescriptor&>(descriptor);
+ SerializeConvolution3dLayer(layer,
+ layerDescriptor,
+ constants,
+ name);
+ break;
+ }
case armnn::LayerType::DepthToSpace :
{
const armnn::DepthToSpaceDescriptor& layerDescriptor =
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
index 43fb0f46b1..1161095c33 100644
--- a/src/armnnSerializer/Serializer.hpp
+++ b/src/armnnSerializer/Serializer.hpp
@@ -144,12 +144,17 @@ private:
const char* name = nullptr);
void SerializeConstantLayer(const armnn::IConnectableLayer* layer,
- const std::vector<armnn::ConstTensor>& contants,
+ const std::vector<armnn::ConstTensor>& constants,
const char* name = nullptr);
void SerializeConvolution2dLayer(const armnn::IConnectableLayer* layer,
const armnn::Convolution2dDescriptor& descriptor,
- const std::vector<armnn::ConstTensor>& contants,
+ const std::vector<armnn::ConstTensor>& constants,
+ const char* name = nullptr);
+
+ void SerializeConvolution3dLayer(const armnn::IConnectableLayer* layer,
+ const armnn::Convolution3dDescriptor& descriptor,
+ const std::vector<armnn::ConstTensor>& constants,
const char* name = nullptr);
void SerializeDepthToSpaceLayer(const armnn::IConnectableLayer* layer,
diff --git a/src/armnnSerializer/SerializerUtils.cpp b/src/armnnSerializer/SerializerUtils.cpp
index 85ce01d132..fca6db8449 100644
--- a/src/armnnSerializer/SerializerUtils.cpp
+++ b/src/armnnSerializer/SerializerUtils.cpp
@@ -97,6 +97,8 @@ armnnSerializer::DataLayout GetFlatBufferDataLayout(armnn::DataLayout dataLayout
{
case armnn::DataLayout::NHWC:
return armnnSerializer::DataLayout::DataLayout_NHWC;
+ case armnn::DataLayout::NDHWC:
+ return armnnSerializer::DataLayout::DataLayout_NDHWC;
case armnn::DataLayout::NCHW:
default:
return armnnSerializer::DataLayout::DataLayout_NCHW;
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index cd7fd5ca5b..2f8fd73717 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -439,6 +439,61 @@ TEST_CASE("SerializeConvolution2dWithPerAxisParams")
deserializedNetwork->ExecuteStrategy(verifier);
}
+TEST_CASE("SerializeConvolution3d")
+{
+ const std::string layerName("convolution3d");
+ const armnn::TensorInfo inputInfo ({ 1, 5, 5, 5, 1 }, armnn::DataType::Float32);
+ const armnn::TensorInfo outputInfo({ 1, 2, 2, 2, 1 }, armnn::DataType::Float32);
+
+ const armnn::TensorInfo weightsInfo({ 3, 3, 3, 1, 1 }, armnn::DataType::Float32);
+ const armnn::TensorInfo biasesInfo ({ 1 }, armnn::DataType::Float32);
+
+ std::vector<float> weightsData = GenerateRandomData<float>(weightsInfo.GetNumElements());
+ armnn::ConstTensor weights(weightsInfo, weightsData);
+
+ std::vector<float> biasesData = GenerateRandomData<float>(biasesInfo.GetNumElements());
+ armnn::ConstTensor biases(biasesInfo, biasesData);
+
+ armnn::Convolution3dDescriptor descriptor;
+ descriptor.m_PadLeft = 0;
+ descriptor.m_PadRight = 0;
+ descriptor.m_PadTop = 0;
+ descriptor.m_PadBottom = 0;
+ descriptor.m_PadFront = 0;
+ descriptor.m_PadBack = 0;
+ descriptor.m_DilationX = 1;
+ descriptor.m_DilationY = 1;
+ descriptor.m_DilationZ = 1;
+ descriptor.m_StrideX = 2;
+ descriptor.m_StrideY = 2;
+ descriptor.m_StrideZ = 2;
+ descriptor.m_BiasEnabled = true;
+ descriptor.m_DataLayout = armnn::DataLayout::NDHWC;
+
+ armnn::INetworkPtr network = armnn::INetwork::Create();
+ armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
+ armnn::IConnectableLayer* const convLayer =
+ network->AddConvolution3dLayer(descriptor,
+ weights,
+ armnn::Optional<armnn::ConstTensor>(biases),
+ layerName.c_str());
+ armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
+
+ inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0));
+ convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+
+ inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
+ convLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+ armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+ CHECK(deserializedNetwork);
+
+ const std::vector<armnn::ConstTensor>& constants {weights, biases};
+ LayerVerifierBaseWithDescriptorAndConstants<armnn::Convolution3dDescriptor> verifier(
+ layerName, {inputInfo}, {outputInfo}, descriptor, constants);
+ deserializedNetwork->ExecuteStrategy(verifier);
+}
+
TEST_CASE("SerializeDepthToSpace")
{
const std::string layerName("depthToSpace");
diff --git a/src/armnnUtils/DataLayoutIndexed.cpp b/src/armnnUtils/DataLayoutIndexed.cpp
index 18c005a7c3..c1c98fc0fd 100644
--- a/src/armnnUtils/DataLayoutIndexed.cpp
+++ b/src/armnnUtils/DataLayoutIndexed.cpp
@@ -25,6 +25,12 @@ DataLayoutIndexed::DataLayoutIndexed(armnn::DataLayout dataLayout)
m_HeightIndex = 2;
m_WidthIndex = 3;
break;
+ case armnn::DataLayout::NDHWC:
+ m_DepthIndex = 1;
+ m_HeightIndex = 2;
+ m_WidthIndex = 3;
+ m_ChannelsIndex = 4;
+ break;
default:
throw armnn::InvalidArgumentException("Unknown DataLayout value: " +
std::to_string(static_cast<int>(dataLayout)));
diff --git a/src/backends/backendsCommon/LayerSupportBase.cpp b/src/backends/backendsCommon/LayerSupportBase.cpp
index 2753c927d5..2c3f827622 100644
--- a/src/backends/backendsCommon/LayerSupportBase.cpp
+++ b/src/backends/backendsCommon/LayerSupportBase.cpp
@@ -165,6 +165,16 @@ bool LayerSupportBase::IsConvolution2dSupported(const TensorInfo&, // input
return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
}
+bool LayerSupportBase::IsConvolution3dSupported(const TensorInfo&, // input
+ const TensorInfo&, // output
+ const Convolution3dDescriptor&, // descriptor
+ const TensorInfo&, // weights
+ const Optional<TensorInfo>&, // biases
+ Optional<std::string&> reasonIfUnsupported) const
+{
+ return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported);
+}
+
bool LayerSupportBase::IsDebugSupported(const TensorInfo&, // input
const TensorInfo&, // output
Optional<std::string&> reasonIfUnsupported) const
diff --git a/src/backends/backendsCommon/LayerSupportBase.hpp b/src/backends/backendsCommon/LayerSupportBase.hpp
index cc68a220e2..240b1dab73 100644
--- a/src/backends/backendsCommon/LayerSupportBase.hpp
+++ b/src/backends/backendsCommon/LayerSupportBase.hpp
@@ -94,6 +94,13 @@ public:
const Optional<TensorInfo>& biases,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+ bool IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+
bool IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp
index a6def847fa..fe22133104 100644
--- a/src/backends/backendsCommon/WorkloadData.cpp
+++ b/src/backends/backendsCommon/WorkloadData.cpp
@@ -1322,6 +1322,67 @@ void Convolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) co
}
}
+void Convolution3dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
+{
+ const std::string descriptorName{"Convolution3dQueueDescriptor"};
+
+ ValidateNumInputs(workloadInfo, descriptorName, 1);
+ ValidateNumOutputs(workloadInfo, descriptorName, 1);
+
+ const TensorInfo& inputTensorInfo = workloadInfo.m_InputTensorInfos[0];
+ const TensorInfo& outputTensorInfo = workloadInfo.m_OutputTensorInfos[0];
+
+ ValidateTensorNumDimensions(inputTensorInfo, descriptorName, 5, "input");
+ ValidateTensorNumDimensions(outputTensorInfo, descriptorName, 5, "output");
+
+ ValidatePointer(m_Weight, descriptorName, "weight");
+
+ const TensorInfo& weightTensorInfo = m_Weight->GetTensorInfo();
+ ValidateTensorNumDimensions(weightTensorInfo, descriptorName, 5, "weight");
+
+ ValidateWeightDataType(inputTensorInfo, weightTensorInfo, descriptorName);
+
+ Optional<TensorInfo> optionalBiasTensorInfo;
+ if (m_Parameters.m_BiasEnabled)
+ {
+ ValidatePointer(m_Bias, descriptorName, "bias");
+
+ optionalBiasTensorInfo = MakeOptional<TensorInfo>(m_Bias->GetTensorInfo());
+ const TensorInfo& biasTensorInfo = optionalBiasTensorInfo.value();
+
+ ValidateTensorDataType(biasTensorInfo, GetBiasDataType(inputTensorInfo.GetDataType()), descriptorName, "bias");
+ ValidateBiasTensorQuantization(biasTensorInfo, inputTensorInfo, weightTensorInfo, descriptorName);
+ }
+
+ if (m_Parameters.m_StrideX <= 0 || m_Parameters.m_StrideY <= 0 || m_Parameters.m_StrideZ <= 0 )
+ {
+ throw InvalidArgumentException(
+ fmt::format("{}: strideX (provided {}), strideY (provided {}) or strideZ (provided {})"
+ "cannot be either negative or 0.",
+ descriptorName, m_Parameters.m_StrideX, m_Parameters.m_StrideY, m_Parameters.m_StrideZ));
+ }
+
+ ValidatePerAxisQuantization(inputTensorInfo,
+ outputTensorInfo,
+ weightTensorInfo,
+ optionalBiasTensorInfo,
+ descriptorName);
+
+ std::vector<DataType> supportedTypes =
+ {
+ DataType::BFloat16,
+ DataType::Float16,
+ DataType::Float32,
+ DataType::QAsymmS8,
+ DataType::QAsymmU8,
+ DataType::QSymmS16,
+ DataType::QSymmS8
+ };
+
+ ValidateDataTypes(inputTensorInfo, supportedTypes, descriptorName);
+ ValidateTensorDataTypesMatch(inputTensorInfo, outputTensorInfo, descriptorName, "input", "output");
+}
+
void DepthwiseConvolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const
{
const std::string descriptorName{"DepthwiseConvolution2dQueueDescriptor"};
diff --git a/src/backends/backendsCommon/WorkloadData.hpp b/src/backends/backendsCommon/WorkloadData.hpp
index b90c29c1b4..896081ecfd 100644
--- a/src/backends/backendsCommon/WorkloadData.hpp
+++ b/src/backends/backendsCommon/WorkloadData.hpp
@@ -208,6 +208,21 @@ struct Convolution2dQueueDescriptor : QueueDescriptorWithParameters<Convolution2
void Validate(const WorkloadInfo& workloadInfo) const;
};
+// Convolution 2D layer workload data.
+struct Convolution3dQueueDescriptor : QueueDescriptorWithParameters<Convolution3dDescriptor>
+{
+ Convolution3dQueueDescriptor()
+ : m_Weight(nullptr)
+ , m_Bias(nullptr)
+ {
+ }
+
+ const ConstTensorHandle* m_Weight;
+ const ConstTensorHandle* m_Bias;
+
+ void Validate(const WorkloadInfo& workloadInfo) const;
+};
+
/// Depthwise Convolution 2D layer workload data.
///
/// @note
diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp
index 00263eca04..666f83de71 100644
--- a/src/backends/backendsCommon/WorkloadFactory.cpp
+++ b/src/backends/backendsCommon/WorkloadFactory.cpp
@@ -225,14 +225,13 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId,
const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
- const Convolution2dDescriptor& descriptor = cLayer->GetParameters();
+ const Convolution2dDescriptor& descriptor = cLayer->GetParameters();
// Construct optional biases object based on the value of m_BiasEnabled
Optional<TensorInfo> biases;
if (descriptor.m_BiasEnabled)
{
- biases =
- OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
+ biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
}
result = layerSupportObject.IsConvolution2dSupported(
@@ -244,6 +243,33 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId,
reason);
break;
}
+ case LayerType::Convolution3d:
+ {
+ auto cLayer = PolymorphicDowncast<const Convolution3dLayer*>(&layer);
+
+ const TensorInfo input = OverrideDataType(layer.GetInputSlot(0).GetConnection()->GetTensorInfo(),
+ dataType);
+ const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType);
+ ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr);
+
+ const Convolution3dDescriptor& descriptor = cLayer->GetParameters();
+
+ // Construct optional biases object based on the value of m_BiasEnabled
+ Optional<TensorInfo> biases;
+ if (descriptor.m_BiasEnabled)
+ {
+ biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType));
+ }
+
+ result = layerSupportObject.IsConvolution3dSupported(
+ input,
+ output,
+ descriptor,
+ OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType),
+ biases,
+ reason);
+ break;
+ }
case LayerType::Debug:
{
const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo();
@@ -1570,6 +1596,12 @@ std::unique_ptr<IWorkload> IWorkloadFactory::CreateConvolution2d(const Convoluti
return std::unique_ptr<IWorkload>();
}
+std::unique_ptr<IWorkload> IWorkloadFactory::CreateConvolution3d(const Convolution3dQueueDescriptor& /*descriptor*/,
+ const WorkloadInfo& /*info*/) const
+{
+ return std::unique_ptr<IWorkload>();
+}
+
std::unique_ptr<IWorkload> IWorkloadFactory::CreateDebug(const DebugQueueDescriptor& /*descriptor*/,
const WorkloadInfo& /*info*/) const
{
diff --git a/src/backends/backendsCommon/WorkloadFactory.hpp b/src/backends/backendsCommon/WorkloadFactory.hpp
index e84657ea13..c16fcb882b 100644
--- a/src/backends/backendsCommon/WorkloadFactory.hpp
+++ b/src/backends/backendsCommon/WorkloadFactory.hpp
@@ -115,7 +115,10 @@ public:
const WorkloadInfo& info) const;
virtual std::unique_ptr<IWorkload> CreateConvolution2d(const Convolution2dQueueDescriptor& descriptor,
- const WorkloadInfo& info) const;
+ const WorkloadInfo& info) const;
+
+ virtual std::unique_ptr<IWorkload> CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const;
virtual std::unique_ptr<IWorkload> CreateDebug(const DebugQueueDescriptor& descriptor,
const WorkloadInfo& info) const;
diff --git a/src/backends/backendsCommon/common.mk b/src/backends/backendsCommon/common.mk
index 7d3558c804..8c1037ad19 100644
--- a/src/backends/backendsCommon/common.mk
+++ b/src/backends/backendsCommon/common.mk
@@ -50,6 +50,7 @@ COMMON_TEST_SOURCES := \
test/layerTests/ConcatTestImpl.cpp \
test/layerTests/ConstantTestImpl.cpp \
test/layerTests/Conv2dTestImpl.cpp \
+ test/layerTests/Conv3dTestImpl.cpp \
test/layerTests/ConvertBf16ToFp32TestImpl.cpp \
test/layerTests/ConvertFp16ToFp32TestImpl.cpp \
test/layerTests/ConvertFp32ToBf16TestImpl.cpp \
diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt
index c17ab0fb0f..ea33513097 100644
--- a/src/backends/backendsCommon/test/CMakeLists.txt
+++ b/src/backends/backendsCommon/test/CMakeLists.txt
@@ -75,6 +75,8 @@ list(APPEND armnnBackendsCommonUnitTests_sources
layerTests/ConstantTestImpl.hpp
layerTests/Conv2dTestImpl.cpp
layerTests/Conv2dTestImpl.hpp
+ layerTests/Conv3dTestImpl.cpp
+ layerTests/Conv3dTestImpl.hpp
layerTests/ConvertBf16ToFp32TestImpl.cpp
layerTests/ConvertBf16ToFp32TestImpl.hpp
layerTests/ConvertFp16ToFp32TestImpl.cpp
diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
index c2d21842f2..76312ce984 100644
--- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
+++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
@@ -638,6 +638,8 @@ DECLARE_LAYER_POLICY_1_PARAM(ConvertFp32ToFp16)
DECLARE_LAYER_POLICY_2_PARAM(Convolution2d)
+DECLARE_LAYER_POLICY_2_PARAM(Convolution3d)
+
DECLARE_LAYER_POLICY_1_PARAM(MemCopy)
DECLARE_LAYER_POLICY_1_PARAM(MemImport)
diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp
index 9f1fa88b16..0dcd3d1564 100644
--- a/src/backends/backendsCommon/test/LayerTests.hpp
+++ b/src/backends/backendsCommon/test/LayerTests.hpp
@@ -20,6 +20,7 @@
#include <backendsCommon/test/layerTests/ConvertFp32ToBf16TestImpl.hpp>
#include <backendsCommon/test/layerTests/ConvertFp32ToFp16TestImpl.hpp>
#include <backendsCommon/test/layerTests/Conv2dTestImpl.hpp>
+#include <backendsCommon/test/layerTests/Conv3dTestImpl.hpp>
#include <backendsCommon/test/layerTests/ConstantTestImpl.hpp>
#include <backendsCommon/test/layerTests/DebugTestImpl.hpp>
#include <backendsCommon/test/layerTests/DepthToSpaceTestImpl.hpp>
diff --git a/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp
new file mode 100644
index 0000000000..2f02189051
--- /dev/null
+++ b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp
@@ -0,0 +1,1038 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Conv3dTestImpl.hpp"
+
+#include <QuantizeHelper.hpp>
+
+#include <armnnUtils/DataLayoutIndexed.hpp>
+
+#include <backendsCommon/TensorHandle.hpp>
+
+#include <backendsCommon/test/TensorCopyUtils.hpp>
+#include <backendsCommon/test/WorkloadTestUtils.hpp>
+
+#include <test/TensorHelpers.hpp>
+
+using namespace armnnUtils;
+
+//
+// Helper templates
+//
+
+// Helper template that returns a quantized bias depending on the number of output channels.
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+std::vector<T> GetBiasData(bool biasEnabled, float qScale, armnn::TensorInfo outputInfo, armnn::DataLayout layout)
+{
+ if(!biasEnabled)
+ {
+ return std::vector<T>();
+ }
+ else
+ {
+ const armnnUtils::DataLayoutIndexed dataLayoutIndexed(layout);
+ const unsigned int outputChannels = outputInfo.GetShape()[dataLayoutIndexed.GetChannelsIndex()];
+
+ switch (outputChannels)
+ {
+ case 1:
+ {
+ return QuantizedVector<T>({2}, qScale, 0);
+ }
+ case 2:
+ default:
+ {
+ return QuantizedVector<T>({0, 2}, qScale, 0);
+ }
+ }
+ }
+}
+
+// Modifies a std::vector in-place using a specified bias.
+template<typename T, typename B>
+void ApplyBiasToData(std::vector<T>& v, const std::vector<B>& bias,
+ float vScale, int32_t vOffset,
+ float bScale, int32_t bOffset)
+{
+ ARMNN_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
+ "Invalid type and parameter combination.");
+ ARMNN_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
+ "Invalid type and parameter combination.");
+
+ for (uint32_t i = 0; i < bias.size(); ++i)
+ {
+ for (long unsigned int j = i; j < v.size(); j+=bias.size())
+ {
+ // Note we need to dequantize and re-quantize the image value and the bias.
+ float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
+
+ T& outRef = v[j];
+ float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
+ outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
+ }
+ }
+}
+
+// Set the quantization scale and offset values for data types.
+template<armnn::DataType ArmnnType>
+void SetScaleOffset(float& qScale, int32_t& qOffset)
+{
+ switch (ArmnnType)
+ {
+ case armnn::DataType::QAsymmU8:
+ {
+ qScale = 0.1f;
+ qOffset = 128;
+ break;
+ }
+ case armnn::DataType::QAsymmS8:
+ case armnn::DataType::QSymmS16:
+ {
+ qScale = 0.1f;
+ qOffset = 0;
+ break;
+ }
+ case armnn::DataType::BFloat16:
+ case armnn::DataType::Float16:
+ case armnn::DataType::Float32:
+ default:
+ {
+ qScale = 0.f;
+ qOffset = 0;
+ break;
+ }
+ }
+}
+
+// Create a vector from 0 to size and quantize (if required).
+template <typename T>
+std::vector<T> CreateQuantizedData(int32_t size, float qScale, int32_t qOffset)
+{
+ std::vector<float> data;
+ for (int32_t i = 0; i < size; ++i)
+ {
+ data.push_back(static_cast<float>(i));
+ }
+
+ return QuantizedVector<T>(data, qScale, qOffset);
+}
+
+// Create a vector from 0 to size divided and then quantized (if required) to create smaller floating point values.
+template <typename T>
+std::vector<T> CreateSmallQuantizedData(int32_t size, float divisor, float qScale, int32_t qOffset)
+{
+ std::vector<float> data;
+ for (int32_t i = 0; i < size; ++i)
+ {
+ float value = static_cast<float>(i);
+ data.push_back(value/divisor);
+ }
+
+ return QuantizedVector<T>(data, qScale, qOffset);;
+}
+
+//
+// Convolution3d implementations
+//
+
+template<armnn::DataType ArmnnType,
+ armnn::DataType ArmnnBType,
+ typename T = armnn::ResolveType<ArmnnType>,
+ typename B = armnn::ResolveType<ArmnnBType>>
+LayerTestResult<T, 5> SimpleConvolution3dTestImpl(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ const std::vector<T>& input,
+ const std::vector<T>& kernel,
+ const std::vector<B>& bias,
+ const std::vector<T>& outputExpected,
+ const armnn::TensorShape& inputShape,
+ const armnn::TensorShape& kernelShape,
+ const armnn::TensorShape& outputExpectedShape,
+ const armnn::DataLayout dataLayout,
+ float qScale,
+ int32_t qOffset,
+ uint32_t strideX = 1,
+ uint32_t strideY = 1,
+ uint32_t strideZ = 1,
+ uint32_t dilationX = 1,
+ uint32_t dilationY = 1,
+ uint32_t dilationZ = 1,
+ uint32_t padLeft = 0,
+ uint32_t padTop = 0,
+ uint32_t padRight = 0,
+ uint32_t padBottom = 0,
+ uint32_t padFront = 0,
+ uint32_t padBack = 0)
+{
+ unsigned int inputNum = armnn::numeric_cast<unsigned int>(inputShape[0]);
+ unsigned int inputDepth = armnn::numeric_cast<unsigned int>(inputShape[1]);
+ unsigned int inputHeight = armnn::numeric_cast<unsigned int>(inputShape[2]);
+ unsigned int inputWidth = armnn::numeric_cast<unsigned int>(inputShape[3]);
+ unsigned int inputChannels = armnn::numeric_cast<unsigned int>(inputShape[4]);
+
+ // Conv3d weights/kernel layout: [D,H,W,I,O]
+ unsigned int kernelDepth = armnn::numeric_cast<unsigned int>(kernelShape[0]);
+ unsigned int kernelHeight = armnn::numeric_cast<unsigned int>(kernelShape[1]);
+ unsigned int kernelWidth = armnn::numeric_cast<unsigned int>(kernelShape[2]);
+ unsigned int kernelInChannels = armnn::numeric_cast<unsigned int>(kernelShape[3]);
+ unsigned int kernelOutChannels = armnn::numeric_cast<unsigned int>(kernelShape[4]);
+
+ unsigned int outputNum = armnn::numeric_cast<unsigned int>(outputExpectedShape[0]);
+ unsigned int outputDepth = armnn::numeric_cast<unsigned int>(outputExpectedShape[1]);
+ unsigned int outputHeight = armnn::numeric_cast<unsigned int>(outputExpectedShape[2]);
+ unsigned int outputWidth = armnn::numeric_cast<unsigned int>(outputExpectedShape[3]);
+ unsigned int outputChannels = armnn::numeric_cast<unsigned int>(outputExpectedShape[4]);
+
+ bool biasEnabled = bias.size() > 0;
+
+ // If a bias is used, its size must equal the number of output channels.
+ ARMNN_ASSERT(!biasEnabled || bias.size() == outputChannels);
+
+ // Creates the tensors.
+ armnn::TensorInfo inputTensorInfo({inputNum, inputDepth, inputHeight, inputWidth, inputChannels}, ArmnnType);
+ armnn::TensorInfo outputTensorInfo({outputNum, outputDepth, outputHeight, outputWidth, outputChannels}, ArmnnType);
+ armnn::TensorInfo kernelDesc({kernelDepth, kernelHeight, kernelWidth, kernelInChannels, kernelOutChannels},
+ ArmnnType);
+ armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, ArmnnBType);
+
+ // Set quantization parameters if the requested type is a quantized type.
+ if(armnn::IsQuantizedType<T>())
+ {
+ inputTensorInfo.SetQuantizationScale(qScale);
+ inputTensorInfo.SetQuantizationOffset(qOffset);
+ outputTensorInfo.SetQuantizationScale(qScale);
+ outputTensorInfo.SetQuantizationOffset(qOffset);
+ kernelDesc.SetQuantizationScale(qScale);
+ kernelDesc.SetQuantizationOffset(qOffset);
+ biasDesc.SetQuantizationScale(qScale*qScale);
+ biasDesc.SetQuantizationOffset(0);
+ }
+
+ // Construct the input data.
+ std::vector<T> inputData;
+ inputData.assign(input.data(), input.data() + inputNum*inputDepth*inputHeight*inputWidth*inputChannels);
+
+ // Construct the output data and apply bias if needed.
+ std::vector<T> outputData;
+ outputData.assign(outputExpected.data(), outputExpected.data() +
+ outputNum*outputDepth*outputHeight*outputWidth*outputChannels);
+
+ if (biasEnabled)
+ {
+ ApplyBiasToData(outputData, bias,
+ outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
+ biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset());
+ }
+
+ std::vector<T> actualOutput(outputTensorInfo.GetNumElements());
+
+ std::unique_ptr<armnn::ITensorHandle> inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
+ std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
+
+ armnn::ScopedTensorHandle weightsTensor(kernelDesc);
+ AllocateAndCopyDataToITensorHandle(&weightsTensor, kernel.data());
+
+ armnn::ScopedTensorHandle biasTensor(biasDesc);
+ if (biasEnabled)
+ {
+ AllocateAndCopyDataToITensorHandle(&biasTensor, bias.data());
+ }
+
+ armnn::Convolution3dQueueDescriptor data;
+ data.m_Weight = &weightsTensor;
+ data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
+ data.m_Parameters.m_StrideX = strideX;
+ data.m_Parameters.m_StrideY = strideY;
+ data.m_Parameters.m_StrideZ = strideZ;
+ data.m_Parameters.m_PadLeft = padLeft;
+ data.m_Parameters.m_PadRight = padRight;
+ data.m_Parameters.m_PadTop = padTop;
+ data.m_Parameters.m_PadBottom = padBottom;
+ data.m_Parameters.m_PadFront = padFront;
+ data.m_Parameters.m_PadBack = padBack;
+ data.m_Parameters.m_DilationX = dilationX;
+ data.m_Parameters.m_DilationY = dilationY;
+ data.m_Parameters.m_DilationZ = dilationZ;
+ data.m_Parameters.m_DataLayout = dataLayout;
+ data.m_Parameters.m_BiasEnabled = biasEnabled;
+
+ armnn::WorkloadInfo info;
+ AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
+ AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
+
+ std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution3d(data, info);
+ inputHandle->Allocate();
+ outputHandle->Allocate();
+
+ CopyDataToITensorHandle(inputHandle.get(), inputData.data());
+
+ ExecuteWorkload(*workload, memoryManager);
+
+ CopyDataFromITensorHandle(actualOutput.data(), outputHandle.get());
+
+ return LayerTestResult<T, 5>(actualOutput,
+ outputData,
+ outputHandle->GetShape(),
+ outputTensorInfo.GetShape());
+}
+
+template<armnn::DataType ArmnnType,
+ armnn::DataType ArmnnBType,
+ typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 5> SimpleConvolution3d3x3x3TestCommon(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale;
+ int32_t qOffset;
+ SetScaleOffset<ArmnnType>(qScale, qOffset);
+
+ armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 1 }, ArmnnType);
+ std::vector<T> input = CreateQuantizedData<T>(125, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, ArmnnType);
+ std::vector<T> kernel = QuantizedVector<T>(
+ {
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1,
+
+ 0, 0, 0,
+ 0, 1, 0,
+ 0, 0, 0,
+
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1,
+ },
+ qScale, qOffset);
+
+ armnn::TensorInfo outputDesc({ 1, 3, 3, 3, 1 }, ArmnnType);
+ std::vector<T> outputData = QuantizedVector<T>(
+ {
+ 589, 608, 627,
+ 684, 703, 722,
+ 779, 798, 817,
+
+ 1064, 1083, 1102,
+ 1159, 1178, 1197,
+ 1254, 1273, 1292,
+
+ 1539, 1558, 1577,
+ 1634, 1653, 1672,
+ 1729, 1748, 1767
+ },
+ qScale, qOffset);
+
+ return SimpleConvolution3dTestImpl<ArmnnType, ArmnnBType>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<ArmnnBType>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset
+ );
+}
+
+template<armnn::DataType ArmnnType,
+ armnn::DataType ArmnnBType,
+ typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 5> Convolution3d2x2x2Strides3x5x5TestCommon(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale;
+ int32_t qOffset;
+ SetScaleOffset<ArmnnType>(qScale, qOffset);
+
+ armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 1 }, ArmnnType);
+ std::vector<T> input = CreateQuantizedData<T>(300, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 3, 5, 5, 1, 1 }, ArmnnType);
+ std::vector<T> kernel = QuantizedVector<T>(
+ {
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+
+ 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2,
+ },
+ qScale, qOffset);
+
+ armnn::TensorInfo outputDesc({ 1, 1, 3, 3, 1 }, ArmnnType);
+ std::vector<T> outputData = QuantizedVector<T>(
+ {
+ 11650, 11800, 11950,
+
+ 13150, 13300, 13450,
+
+ 14650, 14800, 14950
+ },
+ qScale, qOffset);
+
+ return SimpleConvolution3dTestImpl<ArmnnType, ArmnnBType>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<ArmnnBType>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset,
+ 2, // strideX
+ 2, // strideY
+ 2 // strideZ
+ );
+}
+
+template<armnn::DataType ArmnnType,
+ armnn::DataType ArmnnBType,
+ typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 5> Convolution3d2x2x2Dilation2x2x2TestCommon(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale;
+ int32_t qOffset;
+ SetScaleOffset<ArmnnType>(qScale, qOffset);
+
+ armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 2 }, ArmnnType);
+ std::vector<T> input = CreateQuantizedData<T>(250, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 2, 2, 2, 2, 2 }, ArmnnType);
+ std::vector<T> kernel = QuantizedVector<T>(
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1,
+ 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1,
+ },
+ qScale, qOffset);
+
+ // Since the dilation rate is 3 this will dilate the kernel to be 4x4,
+ // therefore the output will be 2x2
+ armnn::TensorInfo outputDesc({ 1, 2, 2, 2, 2 }, ArmnnType);
+ std::vector<T> outputData = QuantizedVector<T>(
+ {
+ -1124, 974,
+ -1148, 978,
+
+ -1244, 994,
+ -1268, 998,
+
+ -1724, 1074,
+ -1748, 1078,
+
+ -1844, 1094,
+ -1868, 1098
+ },
+ qScale, qOffset);
+
+ return SimpleConvolution3dTestImpl<ArmnnType, ArmnnBType>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<ArmnnBType>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset,
+ 1, // strideX
+ 1, // strideY
+ 1, // strideZ
+ 3, // dilationX
+ 3, // dilationY
+ 3 // dilationZ
+ );
+}
+
+template<armnn::DataType ArmnnType,
+ armnn::DataType ArmnnBType,
+ typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 5> Convolution3dPaddingSame3x3x3TestCommon(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale;
+ int32_t qOffset;
+ SetScaleOffset<ArmnnType>(qScale, qOffset);
+
+ armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 1 }, ArmnnType);
+ std::vector<T> input = CreateQuantizedData<T>(125, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, ArmnnType);
+ std::vector<T> kernel = QuantizedVector<T>(
+ {
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1,
+
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1,
+ },
+ qScale, qOffset);
+
+ armnn::TensorInfo outputDesc({ 1, 5, 5, 5, 1 }, ArmnnType);
+ std::vector<T> outputData = QuantizedVector<T>(
+ {
+ 112, 171, 177, 183, 124,
+ 183, 279, 288, 297, 201,
+ 213, 324, 333, 342, 231,
+ 243, 369, 378, 387, 261,
+ 172, 261, 267, 273, 184,
+
+ 224, 342, 354, 366, 248,
+ 366, 558, 576, 594, 402,
+ 426, 648, 666, 684, 462,
+ 486, 738, 756, 774, 522,
+ 344, 522, 534, 546, 368,
+
+ 424, 642, 654, 666, 448,
+ 666, 1008, 1026, 1044, 702,
+ 726, 1098, 1116, 1134, 762,
+ 786, 1188, 1206, 1224, 822,
+ 544, 822, 834, 846, 568,
+ 624, 942, 954, 966, 648,
+
+ 966, 1458, 1476, 1494, 1002,
+ 1026, 1548, 1566, 1584, 1062,
+ 1086, 1638, 1656, 1674, 1122,
+ 744, 1122, 1134, 1146, 768,
+ 312, 471, 477, 483, 324,
+ 483, 729, 738, 747, 501,
+ 513, 774, 783, 792, 531,
+ 543, 819, 828, 837, 561,
+ 372, 561, 567, 573, 384
+ },
+ qScale, qOffset);
+
+ return SimpleConvolution3dTestImpl<ArmnnType, ArmnnBType>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<ArmnnBType>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset,
+ 1, // strideX
+ 1, // strideY
+ 1, // strideZ
+ 1, // dilationX
+ 1, // dilationY
+ 1, // dilationZ
+ 1, // padLeft
+ 1, // padTop
+ 1, // padRight
+ 1, // padBottom
+ 1, // padFront
+ 1 // padBack
+ );
+}
+
+LayerTestResult<float, 5> Convolution3dStrideDilationPadding3x3x3TestCommonFloat32(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale = 0.f;
+ int32_t qOffset = 0;
+
+ armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 2 }, armnn::DataType::Float32);
+ std::vector<float> input = CreateSmallQuantizedData<float>(600, 100.0f, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 3, 3, 3, 2, 2 }, armnn::DataType::Float32);
+ std::vector<float> kernel = CreateSmallQuantizedData<float>(108, 100.0f, qScale, qOffset);
+
+ // Since the dilation rate is 2 this will dilate the kernel to be 5x5: d(K-1)+1 --> 2 x (3-1) + 1 = 5,
+ // therefore the output will be 1x4x4: (I − K + 2P)/S +1 => trunc((10 - 3 + 2x2 )/3 + 1))
+ // where, dilation size = d = 2; kernel size = K = 3; input size = I = 10; padding size = P = 2; stride = S = 3
+ armnn::TensorInfo outputDesc({ 1, 1, 4, 4, 2 }, armnn::DataType::Float32);
+ std::vector<float> outputData =
+ {
+ 12.0312f, 12.2268f, 17.7512f, 18.0494f,
+ 18.176f, 18.4814f, 5.6912f, 5.7938f,
+ 19.1664f, 19.5078f, 28.119f, 28.6383f,
+ 28.6914f, 29.2215f, 8.9094f, 9.0873f,
+
+ 23.1264f, 23.5398f, 33.843f, 34.4703f,
+ 34.4154f, 35.0535f, 10.6734f, 10.8873f,
+ 6.2712f, 6.417f, 9.0718f, 9.2929f,
+ 9.2194f, 9.4441f, 2.7862f, 2.8615f
+ };
+
+ return SimpleConvolution3dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<armnn::DataType::Float32>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset,
+ 3, // strideX
+ 3, // strideY
+ 3, // strideZ
+ 2, // dilationX
+ 2, // dilationY
+ 2, // dilationZ
+ 1, // padLeft
+ 1, // padTop
+ 1, // padRight
+ 1, // padBottom
+ 1, // padFront
+ 1 // padBack
+ );
+}
+
+LayerTestResult<float, 5> Convolution3d2x2x2Stride3x3x3SmallTestCommonFloat32(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ float qScale = 0.f;
+ int32_t qOffset = 0;
+
+ armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 1 }, armnn::DataType::Float32);
+ std::vector<float> input = CreateSmallQuantizedData<float>(300, 100.0f, qScale, qOffset);
+
+ armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, armnn::DataType::Float32);
+ std::vector<float> kernel =
+ {
+ 0.125977f, 0.150391f, 0.101562f,
+ 0.0585938f, 0.0864258f, 0.043457f,
+ 0.034668f, 0.0322266f, 0.0385742f,
+
+ 0.125977f, 0.150391f, -0.101562f,
+ -0.0585938f,-0.0864258f,-0.043457f,
+ -0.0104630f, 0.0154114f, 0.0013768f,
+
+ 0.0344238f, 0.035644f, 0.0495605f,
+ 0.0683594f, 0.099121f, -0.0461426f,
+ -0.0996094f,-0.126953f, -0.043457f,
+ };
+
+ armnn::TensorInfo outputDesc({ 1, 1, 4, 4, 1 }, armnn::DataType::Float32);
+ std::vector<float> outputData =
+ {
+ -0.08156067f, -0.06891209f, -0.05589598f, -0.04310101f,
+ 0.04584253f, 0.05855697f, 0.07129729f, 0.08325434f,
+ 0.17304349f, 0.18521416f, 0.19818866f, 0.21096253f,
+ 0.29965734f, 0.312698f, 0.32547557f, 0.33818722f
+ };
+
+ return SimpleConvolution3dTestImpl<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<armnn::DataType::Float32>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset,
+ 2, // strideX
+ 2, // strideY
+ 2 // strideZ
+ );
+}
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x3x3TestCommonFloat16(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ using namespace half_float::literal;
+
+ float qScale = 0.f;
+ int32_t qOffset = 0;
+
+ armnn::TensorInfo inputDesc({ 1, 2, 3, 3, 2 }, armnn::DataType::Float16);
+ const std::vector<armnn::Half> input =
+ {
+ 1._h, 2._h, 3._h,
+ 4._h, 5._h, 6._h,
+
+ 7._h, 8._h, 9._h,
+ 10._h, 11._h, 12._h,
+
+ 13._h, 14._h, 15._h,
+ 16._h, 17._h, 18._h,
+
+ 19._h, 20._h, 21._h,
+ 22._h, 23._h, 24._h,
+
+ 25._h, 26._h, 27._h,
+ 28._h, 29._h, 30._h,
+
+ 31._h, 32._h, 33._h,
+ 34._h, 35._h, 36._h
+ };
+
+ armnn::TensorInfo kernelDesc({ 2, 2, 2, 2, 2 }, armnn::DataType::Float16);
+ std::vector<armnn::Half> kernel =
+ {
+ -1._h, -1._h, -1._h, -1._h, -1._h, -1._h, -1._h, -1._h,
+ -1._h, -1._h, -1._h, 1._h, 1._h, 1._h, -1._h, -1._h,
+ 1._h, 1._h, -1._h, 1._h, -1._h, 1._h, -1._h, 1._h,
+ -1._h, -1._h, -1._h, 1._h, -1._h, 1._h, -1._h, 1._h,
+ };
+
+ armnn::TensorInfo outputDesc({ 1, 1, 2, 2, 2 }, armnn::DataType::Float16);
+ std::vector<armnn::Half> outputData =
+ {
+ -176._h, 128._h,
+ -200._h, 132._h,
+
+ -248._h, 140._h,
+ -272._h, 144._h
+ };
+
+ return SimpleConvolution3dTestImpl<armnn::DataType::Float16, armnn::DataType::Float16>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<armnn::DataType::Float16>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset
+ );
+}
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x2x2SmallTestCommonFloat16(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled,
+ armnn::DataLayout dataLayout)
+{
+ using namespace half_float::literal;
+
+ float qScale = 0.f;
+ int32_t qOffset = 0;
+
+ armnn::TensorInfo inputDesc({ 1, 2, 4, 4, 1 }, armnn::DataType::Float16);
+ const std::vector<armnn::Half> input =
+ {
+ 0.0367984_h, 0.0380895_h, 0.0420157_h, 0.0675631_h,
+ 0.0938920_h, 0.0476106_h, 0.1035490_h, 0.1260370_h,
+ 0.0461647_h, 0.0883828_h, 0.1159540_h, 0.0498519_h,
+ 0.0104630_h, 0.0154114_h, 0.00137681_h, 0.0344238_h,
+
+ 0.0356445_h, 0.0495605_h, 0.0683594_h, 0.0991211_h,
+ 0.0461426_h, 0.0996094_h, 0.1269530_h, 0.0393066_h,
+ 0.103516_h, 0.032544_h, 0.124334_h, 0.0564566_h,
+ 0.0123544_h, 0.0461647_h, 0.0883828_h, 0.1159540_h,
+ };
+
+ armnn::TensorInfo kernelDesc({ 2, 2, 2, 1, 1 }, armnn::DataType::Float16);
+ std::vector<armnn::Half> kernel =
+ {
+ -0.126184_h, -0.150468_h,
+ -0.101412_h, -0.0586369_h,
+
+ -0.0435089_h, 0.0347555_h,
+ 0.0323111_h, 0.0385381_h
+ };
+
+ armnn::TensorInfo outputDesc({ 1, 1, 3, 3, 1 }, armnn::DataType::Float16);
+ std::vector<armnn::Half> outputData =
+ {
+ -0.01718917_h, -0.01370182_h, -0.02727737_h,
+
+ -0.02282543_h, -0.03144084_h, -0.04468598_h,
+
+ -0.02228982_h, -0.02244923_h, -0.02042268_h
+ };
+
+ return SimpleConvolution3dTestImpl<armnn::DataType::Float16, armnn::DataType::Float16>(
+ workloadFactory,
+ memoryManager,
+ tensorHandleFactory,
+ input,
+ kernel,
+ GetBiasData<armnn::DataType::Float16>(biasEnabled, qScale * qScale, outputDesc, dataLayout),
+ outputData,
+ inputDesc.GetShape(),
+ kernelDesc.GetShape(),
+ outputDesc.GetShape(),
+ dataLayout,
+ qScale,
+ qOffset
+ );
+}
+
+LayerTestResult<float, 5> SimpleConvolution3d3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return SimpleConvolution3d3x3x3TestCommon<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int8_t, 5> SimpleConvolution3d3x3x3Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return SimpleConvolution3d3x3x3TestCommon<armnn::DataType::QAsymmS8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<uint8_t, 5> SimpleConvolution3d3x3x3Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return SimpleConvolution3d3x3x3TestCommon<armnn::DataType::QAsymmU8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int16_t, 5> SimpleConvolution3d3x3x3Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return SimpleConvolution3d3x3x3TestCommon<armnn::DataType::QSymmS16, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+
+LayerTestResult<float, 5> Convolution3d2x2x2Strides3x5x5Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Strides3x5x5TestCommon<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int8_t, 5> Convolution3d2x2x2Strides3x5x5Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Strides3x5x5TestCommon<armnn::DataType::QAsymmS8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<uint8_t, 5> Convolution3d2x2x2Strides3x5x5Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Strides3x5x5TestCommon<armnn::DataType::QAsymmU8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int16_t, 5> Convolution3d2x2x2Strides3x5x5Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Strides3x5x5TestCommon<armnn::DataType::QSymmS16, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<float, 5> Convolution3d2x2x2Dilation2x2x2Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Dilation2x2x2TestCommon<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int8_t, 5> Convolution3d2x2x2Dilation2x2x2Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Dilation2x2x2TestCommon<armnn::DataType::QAsymmS8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<uint8_t, 5> Convolution3d2x2x2Dilation2x2x2Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Dilation2x2x2TestCommon<armnn::DataType::QAsymmU8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int16_t, 5> Convolution3d2x2x2Dilation2x2x2Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Dilation2x2x2TestCommon<armnn::DataType::QSymmS16, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<float, 5> Convolution3dPaddingSame3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3dPaddingSame3x3x3TestCommon<armnn::DataType::Float32, armnn::DataType::Float32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int8_t, 5> Convolution3dPaddingSame3x3x3Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3dPaddingSame3x3x3TestCommon<armnn::DataType::QAsymmS8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<uint8_t, 5> Convolution3dPaddingSame3x3x3Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3dPaddingSame3x3x3TestCommon<armnn::DataType::QAsymmU8, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<int16_t, 5> Convolution3dPaddingSame3x3x3Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3dPaddingSame3x3x3TestCommon<armnn::DataType::QSymmS16, armnn::DataType::Signed32>(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<float, 5> Convolution3dStrideDilationPadding3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3dStrideDilationPadding3x3x3TestCommonFloat32(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<float, 5> Convolution3d2x2x2Stride3x3x3SmallFloat32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2Stride3x3x3SmallTestCommonFloat32(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x3x3Float16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x3x3TestCommonFloat16(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x2x2SmallFloat16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled)
+{
+ return Convolution3d2x2x2SmallTestCommonFloat16(
+ workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC);
+}
diff --git a/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp
new file mode 100644
index 0000000000..45a6786e39
--- /dev/null
+++ b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp
@@ -0,0 +1,141 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "LayerTestResult.hpp"
+
+#include <Half.hpp>
+
+#include <ResolveType.hpp>
+
+#include <armnn/Types.hpp>
+
+#include <armnn/backends/IBackendInternal.hpp>
+#include <backendsCommon/WorkloadFactory.hpp>
+
+//
+// Convolution3d
+//
+
+LayerTestResult<float, 5> SimpleConvolution3d3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int8_t , 5> SimpleConvolution3d3x3x3Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<u_int8_t, 5> SimpleConvolution3d3x3x3Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int16_t, 5> SimpleConvolution3d3x3x3Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<float, 5> Convolution3d2x2x2Strides3x5x5Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int8_t , 5> Convolution3d2x2x2Strides3x5x5Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<u_int8_t, 5> Convolution3d2x2x2Strides3x5x5Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int16_t, 5> Convolution3d2x2x2Strides3x5x5Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<float, 5> Convolution3d2x2x2Dilation2x2x2Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int8_t , 5> Convolution3d2x2x2Dilation2x2x2Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<u_int8_t, 5> Convolution3d2x2x2Dilation2x2x2Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int16_t, 5> Convolution3d2x2x2Dilation2x2x2Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<float, 5> Convolution3dPaddingSame3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int8_t , 5> Convolution3dPaddingSame3x3x3Int8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<u_int8_t, 5> Convolution3dPaddingSame3x3x3Uint8Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<int16_t, 5> Convolution3dPaddingSame3x3x3Int16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<float, 5> Convolution3dStrideDilationPadding3x3x3Float32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<float, 5> Convolution3d2x2x2Stride3x3x3SmallFloat32Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x3x3Float16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
+
+LayerTestResult<armnn::Half, 5> Convolution3d2x2x2SmallFloat16Test(
+ armnn::IWorkloadFactory& workloadFactory,
+ const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+ const armnn::ITensorHandleFactory& tensorHandleFactory,
+ bool biasEnabled);
diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp
index aaf9aa0e7c..c0ede678bf 100644
--- a/src/backends/reference/RefLayerSupport.cpp
+++ b/src/backends/reference/RefLayerSupport.cpp
@@ -605,6 +605,76 @@ bool RefLayerSupport::IsConvolution2dSupported(const TensorInfo& input,
return supported;
}
+bool RefLayerSupport::IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported) const
+{
+ bool supported = true;
+
+ // Define supported types.
+ std::array<DataType,7> supportedTypes =
+ {
+ DataType::BFloat16,
+ DataType::Float32,
+ DataType::Float16,
+ DataType::QAsymmS8,
+ DataType::QAsymmU8,
+ DataType::QSymmS8,
+ DataType::QSymmS16
+ };
+
+ supported &= CheckSupportRule(TypeAnyOf(input, supportedTypes), reasonIfUnsupported,
+ "Reference Convolution3d: input is not a supported type.");
+
+ supported &= CheckSupportRule(TypeAnyOf(output, supportedTypes), reasonIfUnsupported,
+ "Reference Convolution3d: output is not a supported type.");
+
+ supported &= CheckSupportRule(TypesAreEqual(input, output), reasonIfUnsupported,
+ "Reference Convolution3d: input and output types mismatched.");
+
+ const DataType inputType = input.GetDataType();
+ if (IsQuantized8BitType(inputType))
+ {
+ std::array<DataType, 3> supportedWeightTypes =
+ {
+ DataType::QAsymmS8,
+ DataType::QAsymmU8,
+ DataType::QSymmS8
+ };
+
+ supported &= CheckSupportRule(TypeAnyOf(weights, supportedWeightTypes), reasonIfUnsupported,
+ "Reference Convolution3d: weights type not supported for quantized input.");
+ }
+ else
+ {
+ supported &= CheckSupportRule(TypeAnyOf(weights, supportedTypes), reasonIfUnsupported,
+ "Reference Convolution3d: weights is not a supported type.");
+
+ supported &= CheckSupportRule(TypesAreEqual(input, weights), reasonIfUnsupported,
+ "Reference Convolution3d: input and weights types mismatched.");
+ }
+
+ if (biases.has_value())
+ {
+ std::array<DataType,4> biasesSupportedTypes =
+ {
+ DataType::BFloat16,
+ DataType::Float32,
+ DataType::Float16,
+ DataType::Signed32
+ };
+
+ supported &= CheckSupportRule(TypeAnyOf(biases.value(), biasesSupportedTypes), reasonIfUnsupported,
+ "Reference Convolution3d: biases is not a supported type.");
+ }
+ IgnoreUnused(descriptor);
+
+ return supported;
+}
+
bool RefLayerSupport::IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported) const
diff --git a/src/backends/reference/RefLayerSupport.hpp b/src/backends/reference/RefLayerSupport.hpp
index 2693dc1779..627418e3e1 100644
--- a/src/backends/reference/RefLayerSupport.hpp
+++ b/src/backends/reference/RefLayerSupport.hpp
@@ -92,6 +92,13 @@ public:
const Optional<TensorInfo>& biases,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+ bool IsConvolution3dSupported(const TensorInfo& input,
+ const TensorInfo& output,
+ const Convolution3dDescriptor& descriptor,
+ const TensorInfo& weights,
+ const Optional<TensorInfo>& biases,
+ Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+
bool IsDebugSupported(const TensorInfo& input,
const TensorInfo& output,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp
index 681b73a748..18a5af277f 100644
--- a/src/backends/reference/RefWorkloadFactory.cpp
+++ b/src/backends/reference/RefWorkloadFactory.cpp
@@ -241,6 +241,12 @@ std::unique_ptr<IWorkload> RefWorkloadFactory::CreateConvolution2d(const Convolu
return std::make_unique<RefConvolution2dWorkload>(descriptor, info);
}
+std::unique_ptr<IWorkload> RefWorkloadFactory::CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const
+{
+ return std::make_unique<RefConvolution3dWorkload>(descriptor, info);
+}
+
std::unique_ptr<IWorkload> RefWorkloadFactory::CreateDebug(const DebugQueueDescriptor& descriptor,
const WorkloadInfo& info) const
{
diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp
index fe3eb54795..d00d3ca822 100644
--- a/src/backends/reference/RefWorkloadFactory.hpp
+++ b/src/backends/reference/RefWorkloadFactory.hpp
@@ -115,6 +115,9 @@ public:
std::unique_ptr<IWorkload> CreateConvolution2d(const Convolution2dQueueDescriptor& descriptor,
const WorkloadInfo& info) const override;
+ std::unique_ptr<IWorkload> CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const override;
+
std::unique_ptr<IWorkload> CreateDebug(const DebugQueueDescriptor& descriptor,
const WorkloadInfo& info) const override;
diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk
index 2dc2bc4919..7d6c59a273 100644
--- a/src/backends/reference/backend.mk
+++ b/src/backends/reference/backend.mk
@@ -27,6 +27,7 @@ BACKEND_SOURCES := \
workloads/BatchToSpaceNd.cpp \
workloads/Broadcast.cpp \
workloads/ConvImpl.cpp \
+ workloads/Conv3dImpl.cpp \
workloads/Debug.cpp \
workloads/DepthToSpace.cpp \
workloads/DetectionPostProcess.cpp \
@@ -58,6 +59,7 @@ BACKEND_SOURCES := \
workloads/RefConvertFp32ToBf16Workload.cpp \
workloads/RefConvertFp32ToFp16Workload.cpp \
workloads/RefConvolution2dWorkload.cpp \
+ workloads/RefConvolution3dWorkload.cpp \
workloads/RefDebugWorkload.cpp \
workloads/RefDepthToSpaceWorkload.cpp \
workloads/RefDepthwiseConvolution2dWorkload.cpp \
diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp
index 4afee79c85..f5d388d007 100644
--- a/src/backends/reference/test/RefLayerTests.cpp
+++ b/src/backends/reference/test/RefLayerTests.cpp
@@ -208,6 +208,39 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution2d3x3Stride2x2BFloat16SmallValue,
false,
DataLayout::NHWC);
+// Convolution 3d
+ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Float32, SimpleConvolution3d3x3x3Float32Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Int8, SimpleConvolution3d3x3x3Int8Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Uint8, SimpleConvolution3d3x3x3Uint8Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Int16, SimpleConvolution3d3x3x3Int16Test, false)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5Float32, Convolution3d2x2x2Strides3x5x5Float32Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestInt8, Convolution3d2x2x2Strides3x5x5Int8Test, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestUint8, Convolution3d2x2x2Strides3x5x5Uint8Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestInt16, Convolution3d2x2x2Strides3x5x5Int16Test, true)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3Float32, Convolution3dPaddingSame3x3x3Float32Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestInt8, Convolution3dPaddingSame3x3x3Int8Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestUint8, Convolution3dPaddingSame3x3x3Uint8Test, false)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestInt16, Convolution3dPaddingSame3x3x3Int16Test, false)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2Float32, Convolution3d2x2x2Dilation2x2x2Float32Test, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestInt8, Convolution3d2x2x2Dilation2x2x2Int8Test, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestUint8, Convolution3d2x2x2Dilation2x2x2Uint8Test, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestInt16, Convolution3d2x2x2Dilation2x2x2Int16Test, true)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dStrideDilationPadding3x3x3Float32,
+ Convolution3dStrideDilationPadding3x3x3Float32Test,
+ true)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Stride3x3x3SmallTestFloat32,
+ Convolution3d2x2x2Stride3x3x3SmallFloat32Test,
+ false)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x3x3TestFloat16, Convolution3d2x3x3Float16Test, true)
+ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2SmallTestFloat16, Convolution3d2x2x2SmallFloat16Test, false)
+
+
// Depthwise Convolution
ARMNN_AUTO_TEST_CASE_WITH_THF(DepthwiseConvolution2d, DepthwiseConvolution2dTest, true, DataLayout::NCHW)
ARMNN_AUTO_TEST_CASE_WITH_THF(DepthwiseConvolution2dUint8, DepthwiseConvolution2dUint8Test, true, DataLayout::NCHW)
diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt
index 0ab8c6b0bb..e169c03ad8 100644
--- a/src/backends/reference/workloads/CMakeLists.txt
+++ b/src/backends/reference/workloads/CMakeLists.txt
@@ -18,6 +18,8 @@ list(APPEND armnnRefBackendWorkloads_sources
Broadcast.hpp
ConvImpl.cpp
ConvImpl.hpp
+ Conv3dImpl.cpp
+ Conv3dImpl.hpp
Debug.cpp
Debug.hpp
Decoders.hpp
@@ -87,6 +89,8 @@ list(APPEND armnnRefBackendWorkloads_sources
RefConvertFp32ToFp16Workload.hpp
RefConvolution2dWorkload.cpp
RefConvolution2dWorkload.hpp
+ RefConvolution3dWorkload.cpp
+ RefConvolution3dWorkload.hpp
RefElementwiseWorkload.cpp
RefElementwiseWorkload.hpp
RefDebugWorkload.cpp
diff --git a/src/backends/reference/workloads/Conv3dImpl.cpp b/src/backends/reference/workloads/Conv3dImpl.cpp
new file mode 100644
index 0000000000..484d887cfc
--- /dev/null
+++ b/src/backends/reference/workloads/Conv3dImpl.cpp
@@ -0,0 +1,151 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Conv3dImpl.hpp"
+
+namespace armnn
+{
+
+void Convolve3d(const TensorShape& rInputShape,
+ Decoder<float>& rInputDecoder,
+ const TensorShape& rOutputShape,
+ Encoder<float>& rOutputEncoder,
+ const TensorShape& rFilterShape,
+ Decoder<float>& rFilterDecoder,
+ bool biasEnabled,
+ Decoder<float>* pBiasDecoder,
+ DataLayout dataLayout,
+ unsigned int paddingTop,
+ unsigned int paddingLeft,
+ unsigned int paddingFront,
+ unsigned int xStride,
+ unsigned int yStride,
+ unsigned int zStride,
+ unsigned int xDilation,
+ unsigned int yDilation,
+ unsigned int zDilation)
+{
+ if (biasEnabled && !pBiasDecoder)
+ {
+ throw InvalidArgumentException("Bias is enabled but the bias data is invalid");
+ }
+ const armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout);
+
+ const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex();
+ const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex();
+ const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex();
+ const unsigned int depthIndex = dataLayoutIndexed.GetDepthIndex();
+
+ const unsigned int inChannels = rInputShape[channelsIndex];
+ const unsigned int outChannels = rOutputShape[channelsIndex];
+
+ const unsigned int batchSize = rOutputShape[0];
+ const unsigned int outputHeight = rOutputShape[heightIndex];
+ const unsigned int outputWidth = rOutputShape[widthIndex];
+ const unsigned int outputDepth = rOutputShape[depthIndex];
+ const unsigned int inputHeight = rInputShape[heightIndex];
+ const unsigned int inputWidth = rInputShape[widthIndex];
+ const unsigned int inputDepth = rInputShape[depthIndex];
+
+ // Conv3d weights layout: [D,H,W,I,O]
+ const unsigned int filterDepth = rFilterShape[0];
+ const unsigned int filterHeight = rFilterShape[1];
+ const unsigned int filterWidth = rFilterShape[2];
+
+ const std::vector<float> inputVec = rInputDecoder.DecodeTensor(rInputShape);
+ const std::vector<float> filterVec = rFilterDecoder.DecodeTensor(rFilterShape);
+
+ const TensorShape biasShape{outChannels};
+ const std::vector<float> biasVec = biasEnabled ? pBiasDecoder->DecodeTensor(biasShape) : std::vector<float>();
+
+ for (unsigned int batchIdx = 0; batchIdx < batchSize; batchIdx++)
+ {
+ for (unsigned int zOutput = 0; zOutput < outputDepth; zOutput++)
+ {
+ for (unsigned int xOutput = 0; xOutput < outputWidth; xOutput++)
+ {
+ for (unsigned int yOutput = 0; yOutput < outputHeight; yOutput++)
+ {
+ for (unsigned int cOutput = 0; cOutput < outChannels; cOutput++)
+ {
+ // This loop goes over each output element.
+ float sum = 0.0f;
+
+ // Loop over each input channel.
+ for (unsigned int zFilter = 0; zFilter < filterDepth; zFilter++)
+ {
+ for (unsigned int yFilter = 0; yFilter < filterHeight; yFilter++)
+ {
+ for (unsigned int xFilter = 0; xFilter < filterWidth; xFilter++)
+ {
+ for (unsigned int cInput = 0; cInput < inChannels; cInput++)
+ {
+ // This loop goes over each input element for each output element.
+ unsigned int filterIndex = 0;
+
+ // Conv3d weights layout: [D,H,W,I,O]
+ // Keep this implementation, as using DataLayoutIndexed::GetIndex
+ // causes large performance regression.
+ filterIndex = zFilter * filterHeight * filterWidth * inChannels * outChannels +
+ yFilter * filterWidth * inChannels * outChannels +
+ xFilter * inChannels * outChannels +
+ cInput * outChannels +
+ cOutput;
+
+ unsigned int yInput = yOutput * yStride + yFilter * yDilation;
+ unsigned int xInput = xOutput * xStride + xFilter * xDilation;
+ unsigned int zInput = zOutput * zStride + zFilter * zDilation;
+
+ float inputValue;
+
+ // Check if we're in the padding.
+ if (yInput < paddingTop || yInput >= inputHeight + paddingTop ||
+ xInput < paddingLeft || xInput >= inputWidth + paddingLeft ||
+ zInput < paddingFront || zInput >= inputDepth + paddingFront)
+ {
+ inputValue = 0.0f;
+ }
+ else
+ {
+ unsigned int inputIndex = 0;
+
+ // Keep this implementation, as using DataLayoutIndexed::GetIndex
+ // causes large performance regression.
+ inputIndex = batchIdx * inputDepth * inputHeight * inputWidth * inChannels +
+ (zInput-paddingFront) * inputHeight * inputWidth * inChannels +
+ (yInput-paddingTop) * inputWidth * inChannels +
+ (xInput-paddingLeft) * inChannels +
+ cInput;
+
+ inputValue = inputVec[inputIndex];
+ }
+
+ sum += filterVec[filterIndex] * inputValue;
+ }
+ }
+ }
+ }
+
+ if (biasEnabled)
+ {
+ sum += biasVec[cOutput];
+ }
+
+ unsigned int outIdx = batchIdx * outputDepth * outputHeight * outputWidth * outChannels +
+ zOutput * outputHeight * outputWidth * outChannels +
+ yOutput * outputWidth * outChannels +
+ xOutput * outChannels +
+ cOutput;
+
+ rOutputEncoder[outIdx];
+ rOutputEncoder.Set(sum);
+ }
+ }
+ }
+ }
+ }
+}
+
+} // namespace armnn
diff --git a/src/backends/reference/workloads/Conv3dImpl.hpp b/src/backends/reference/workloads/Conv3dImpl.hpp
new file mode 100644
index 0000000000..5cf2ed942d
--- /dev/null
+++ b/src/backends/reference/workloads/Conv3dImpl.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "BaseIterator.hpp"
+#include "Decoders.hpp"
+#include "Encoders.hpp"
+
+#include <armnn/Tensor.hpp>
+
+#include <armnnUtils/DataLayoutIndexed.hpp>
+
+namespace armnn
+{
+
+void Convolve3d(const TensorShape& rInputShape,
+ Decoder<float>& rInputDecoder,
+ const TensorShape& rOutputShape,
+ Encoder<float>& rOutputEncoder,
+ const TensorShape& rFilterShape,
+ Decoder<float>& rFilterDecoder,
+ bool biasEnabled,
+ Decoder<float>* pBiasDecoder,
+ DataLayout dataLayout,
+ unsigned int paddingTop,
+ unsigned int paddingLeft,
+ unsigned int paddingFront,
+ unsigned int xStride,
+ unsigned int yStride,
+ unsigned int zStride,
+ unsigned int xDilation,
+ unsigned int yDilation,
+ unsigned int zDilation);
+
+} //namespace armnn
diff --git a/src/backends/reference/workloads/RefConvolution3dWorkload.cpp b/src/backends/reference/workloads/RefConvolution3dWorkload.cpp
new file mode 100644
index 0000000000..ea425daec9
--- /dev/null
+++ b/src/backends/reference/workloads/RefConvolution3dWorkload.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RefConvolution3dWorkload.hpp"
+
+#include "Conv3dImpl.hpp"
+#include "RefWorkloadUtils.hpp"
+
+#include "Profiling.hpp"
+
+namespace armnn
+{
+RefConvolution3dWorkload::RefConvolution3dWorkload(
+ const Convolution3dQueueDescriptor& descriptor, const WorkloadInfo& info)
+ : BaseWorkload<Convolution3dQueueDescriptor>(descriptor, info)
+{
+ WorkloadInfo detailsInfo;
+ detailsInfo.m_InputTensorInfos = info.m_InputTensorInfos;
+ detailsInfo.m_OutputTensorInfos = info.m_OutputTensorInfos;
+ detailsInfo.m_WeightsTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Weight->GetTensorInfo());
+ if (descriptor.m_Parameters.m_BiasEnabled)
+ {
+ detailsInfo.m_BiasTensorInfo = armnn::Optional<armnn::TensorInfo>(descriptor.m_Bias->GetTensorInfo());
+ }
+
+ // Report Profiling Details
+ ARMNN_REPORT_PROFILING_WORKLOAD_DESC("RefConvolution3dWorkload_Construct",
+ descriptor.m_Parameters,
+ detailsInfo,
+ this->GetGuid());
+
+ m_Weight = std::make_unique<ScopedTensorHandle>(*( descriptor.m_Weight ));
+ const TensorInfo& rFilterInfo = m_Weight->GetTensorInfo();
+
+ m_FilterShape = rFilterInfo.GetShape();
+ m_FilterDecoder = MakeDecoder<float>(rFilterInfo, m_Weight.get()->Map(true));
+
+ if ( descriptor.m_Parameters.m_BiasEnabled )
+ {
+ m_Bias = std::make_unique<ScopedTensorHandle>(*( descriptor.m_Bias ));
+ const TensorInfo& biasInfo = m_Bias->GetTensorInfo();
+ m_BiasDecoder = MakeDecoder<float>(biasInfo, m_Bias->Map(true));
+ }
+}
+
+void RefConvolution3dWorkload::Execute() const
+{
+ Execute(m_Data.m_Inputs, m_Data.m_Outputs);
+}
+
+void RefConvolution3dWorkload::ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor)
+{
+ Execute(workingMemDescriptor.m_Inputs, workingMemDescriptor.m_Outputs);
+}
+
+void RefConvolution3dWorkload::Execute(std::vector<ITensorHandle*> inputs, std::vector<ITensorHandle*> outputs) const
+{
+ ARMNN_SCOPED_PROFILING_EVENT_GUID(Compute::CpuRef, "RefConvolution3dWorkload_Execute", this->GetGuid());
+
+ std::unique_ptr<Decoder<float>> inputDecoder = MakeDecoder<float>(GetTensorInfo(inputs[0]), inputs[0]->Map());
+ std::unique_ptr<Encoder<float>> outputEncoder = MakeEncoder<float>(GetTensorInfo(outputs[0]), outputs[0]->Map());
+
+ const TensorShape& inputShape = GetTensorInfo(inputs[0]).GetShape();
+ const TensorShape& outputShape = GetTensorInfo(outputs[0]).GetShape();
+
+ Convolve3d(inputShape, *inputDecoder, outputShape, *outputEncoder, m_FilterShape,
+ *m_FilterDecoder, m_Data.m_Parameters.m_BiasEnabled, m_BiasDecoder.get(),
+ m_Data.m_Parameters.m_DataLayout,
+ m_Data.m_Parameters.m_PadTop, m_Data.m_Parameters.m_PadLeft, m_Data.m_Parameters.m_PadFront,
+ m_Data.m_Parameters.m_StrideX, m_Data.m_Parameters.m_StrideY, m_Data.m_Parameters.m_StrideZ,
+ m_Data.m_Parameters.m_DilationX, m_Data.m_Parameters.m_DilationY, m_Data.m_Parameters.m_DilationZ);
+}
+
+} //namespace armnn
diff --git a/src/backends/reference/workloads/RefConvolution3dWorkload.hpp b/src/backends/reference/workloads/RefConvolution3dWorkload.hpp
new file mode 100644
index 0000000000..0373a8b900
--- /dev/null
+++ b/src/backends/reference/workloads/RefConvolution3dWorkload.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <backendsCommon/Workload.hpp>
+#include <backendsCommon/WorkloadData.hpp>
+#include "Decoders.hpp"
+#include "Encoders.hpp"
+
+namespace armnn
+{
+
+class RefConvolution3dWorkload : public BaseWorkload<Convolution3dQueueDescriptor>
+{
+public:
+ explicit RefConvolution3dWorkload(const Convolution3dQueueDescriptor& descriptor,
+ const WorkloadInfo& info);
+
+
+ void Execute() const override;
+ void ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor) override;
+
+private:
+ void Execute(std::vector<ITensorHandle*> inputs, std::vector<ITensorHandle*> outputs) const;
+ std::unique_ptr<ScopedTensorHandle> m_Weight;
+ std::unique_ptr<ScopedTensorHandle> m_Bias;
+
+ std::unique_ptr<Decoder<float>> m_FilterDecoder;
+ std::unique_ptr<Decoder<float>> m_BiasDecoder;
+
+ TensorShape m_FilterShape;
+};
+
+} //namespace armnn
+
diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp
index 1cf84eed9e..ed3aa90e5f 100644
--- a/src/backends/reference/workloads/RefWorkloads.hpp
+++ b/src/backends/reference/workloads/RefWorkloads.hpp
@@ -22,6 +22,7 @@
#include "RefChannelShuffleWorkload.hpp"
#include "RefComparisonWorkload.hpp"
#include "RefConvolution2dWorkload.hpp"
+#include "RefConvolution3dWorkload.hpp"
#include "RefConstantWorkload.hpp"
#include "RefConcatWorkload.hpp"
#include "RefConvertBf16ToFp32Workload.hpp"