IVGCVSW-6469 Add MirrorPad FrontEnd and Ref Support

 * Added PaddingMode enum to PaddingDescriptor to enable Symmetric and
   Reflect padding.
 * Added Symmetric and Reflect Ref implementation.
 * Added Serializer & Deserializer support.
 * Added unit tests.

Signed-off-by: Matthew Sloyan <matthew.sloyan@arm.com>
Change-Id: I4bed907b31742b32ccefe5e8ca39a6f1e5bd9dee
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index c577a11..40de349 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -619,9 +619,16 @@
     descriptor:PadDescriptor;
 }
 
+enum PaddingMode : byte {
+    Constant  = 0,
+    Reflect   = 1,
+    Symmetric = 2
+}
+
 table PadDescriptor {
     padList:[uint];
     padValue:float = 0;
+    paddingMode:PaddingMode = Constant;
 }
 
 /// @deprecated Use ElementwiseUnaryLayer instead
diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h
index 712ad28..7747f9e 100644
--- a/src/armnnSerializer/ArmnnSchema_generated.h
+++ b/src/armnnSerializer/ArmnnSchema_generated.h
@@ -1198,6 +1198,39 @@
   return EnumNamesNormalizationAlgorithmMethod()[index];
 }
 
+enum PaddingMode {
+  PaddingMode_Constant = 0,
+  PaddingMode_Reflect = 1,
+  PaddingMode_Symmetric = 2,
+  PaddingMode_MIN = PaddingMode_Constant,
+  PaddingMode_MAX = PaddingMode_Symmetric
+};
+
+inline const PaddingMode (&EnumValuesPaddingMode())[3] {
+  static const PaddingMode values[] = {
+    PaddingMode_Constant,
+    PaddingMode_Reflect,
+    PaddingMode_Symmetric
+  };
+  return values;
+}
+
+inline const char * const *EnumNamesPaddingMode() {
+  static const char * const names[4] = {
+    "Constant",
+    "Reflect",
+    "Symmetric",
+    nullptr
+  };
+  return names;
+}
+
+inline const char *EnumNamePaddingMode(PaddingMode e) {
+  if (flatbuffers::IsOutRange(e, PaddingMode_Constant, PaddingMode_Symmetric)) return "";
+  const size_t index = static_cast<size_t>(e);
+  return EnumNamesPaddingMode()[index];
+}
+
 enum Layer {
   Layer_NONE = 0,
   Layer_ActivationLayer = 1,
@@ -6383,7 +6416,8 @@
   typedef PadDescriptorBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
     VT_PADLIST = 4,
-    VT_PADVALUE = 6
+    VT_PADVALUE = 6,
+    VT_PADDINGMODE = 8
   };
   const flatbuffers::Vector<uint32_t> *padList() const {
     return GetPointer<const flatbuffers::Vector<uint32_t> *>(VT_PADLIST);
@@ -6391,11 +6425,15 @@
   float padValue() const {
     return GetField<float>(VT_PADVALUE, 0.0f);
   }
+  armnnSerializer::PaddingMode paddingMode() const {
+    return static_cast<armnnSerializer::PaddingMode>(GetField<int8_t>(VT_PADDINGMODE, 0));
+  }
   bool Verify(flatbuffers::Verifier &verifier) const {
     return VerifyTableStart(verifier) &&
            VerifyOffset(verifier, VT_PADLIST) &&
            verifier.VerifyVector(padList()) &&
            VerifyField<float>(verifier, VT_PADVALUE) &&
+           VerifyField<int8_t>(verifier, VT_PADDINGMODE) &&
            verifier.EndTable();
   }
 };
@@ -6410,6 +6448,9 @@
   void add_padValue(float padValue) {
     fbb_.AddElement<float>(PadDescriptor::VT_PADVALUE, padValue, 0.0f);
   }
+  void add_paddingMode(armnnSerializer::PaddingMode paddingMode) {
+    fbb_.AddElement<int8_t>(PadDescriptor::VT_PADDINGMODE, static_cast<int8_t>(paddingMode), 0);
+  }
   explicit PadDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
         : fbb_(_fbb) {
     start_ = fbb_.StartTable();
@@ -6425,22 +6466,26 @@
 inline flatbuffers::Offset<PadDescriptor> CreatePadDescriptor(
     flatbuffers::FlatBufferBuilder &_fbb,
     flatbuffers::Offset<flatbuffers::Vector<uint32_t>> padList = 0,
-    float padValue = 0.0f) {
+    float padValue = 0.0f,
+    armnnSerializer::PaddingMode paddingMode = armnnSerializer::PaddingMode_Constant) {
   PadDescriptorBuilder builder_(_fbb);
   builder_.add_padValue(padValue);
   builder_.add_padList(padList);
+  builder_.add_paddingMode(paddingMode);
   return builder_.Finish();
 }
 
 inline flatbuffers::Offset<PadDescriptor> CreatePadDescriptorDirect(
     flatbuffers::FlatBufferBuilder &_fbb,
     const std::vector<uint32_t> *padList = nullptr,
-    float padValue = 0.0f) {
+    float padValue = 0.0f,
+    armnnSerializer::PaddingMode paddingMode = armnnSerializer::PaddingMode_Constant) {
   auto padList__ = padList ? _fbb.CreateVector<uint32_t>(*padList) : 0;
   return armnnSerializer::CreatePadDescriptor(
       _fbb,
       padList__,
-      padValue);
+      padValue,
+      paddingMode);
 }
 
 /// @deprecated Use ElementwiseUnaryLayer instead
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index 84a9d53..c087843 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -894,7 +894,8 @@
 
     auto flatBufferPadDesc = serializer::CreatePadDescriptor(m_flatBufferBuilder,
                                                              m_flatBufferBuilder.CreateVector(padList),
-                                                             padDescriptor.m_PadValue);
+                                                             padDescriptor.m_PadValue,
+                                                             GetFlatBufferPaddingMode(padDescriptor.m_PaddingMode));
 
     auto flatBufferPadLayer = serializer::CreatePadLayer(m_flatBufferBuilder,
                                                          flatBufferBaseLayer,
diff --git a/src/armnnSerializer/SerializerUtils.cpp b/src/armnnSerializer/SerializerUtils.cpp
index 5ad2771..49ce721 100644
--- a/src/armnnSerializer/SerializerUtils.cpp
+++ b/src/armnnSerializer/SerializerUtils.cpp
@@ -170,6 +170,19 @@
     }
 }
 
+armnnSerializer::PaddingMode GetFlatBufferPaddingMode(armnn::PaddingMode paddingMode)
+{
+    switch (paddingMode)
+    {
+        case armnn::PaddingMode::Reflect:
+            return armnnSerializer::PaddingMode::PaddingMode_Reflect;
+        case armnn::PaddingMode::Symmetric:
+            return armnnSerializer::PaddingMode::PaddingMode_Symmetric;
+        default:
+            return armnnSerializer::PaddingMode::PaddingMode_Constant;
+    }
+}
+
 armnnSerializer::NormalizationAlgorithmChannel GetFlatBufferNormalizationAlgorithmChannel(
     armnn::NormalizationAlgorithmChannel normalizationAlgorithmChannel)
 {
diff --git a/src/armnnSerializer/SerializerUtils.hpp b/src/armnnSerializer/SerializerUtils.hpp
index 5517986..07cdc2a 100644
--- a/src/armnnSerializer/SerializerUtils.hpp
+++ b/src/armnnSerializer/SerializerUtils.hpp
@@ -27,6 +27,8 @@
 
 armnnSerializer::PaddingMethod GetFlatBufferPaddingMethod(armnn::PaddingMethod paddingMethod);
 
+armnnSerializer::PaddingMode GetFlatBufferPaddingMode(armnn::PaddingMode paddingMode);
+
 armnnSerializer::NormalizationAlgorithmChannel GetFlatBufferNormalizationAlgorithmChannel(
     armnn::NormalizationAlgorithmChannel normalizationAlgorithmChannel);
 
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 2bffe0b..e32b908 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -1684,6 +1684,36 @@
     deserializedNetwork->ExecuteStrategy(verifier);
 }
 
+TEST_CASE("SerializePadReflect")
+{
+    const std::string layerName("padReflect");
+    const armnn::TensorInfo inputTensorInfo = armnn::TensorInfo({1, 2, 3, 4}, armnn::DataType::Float32);
+    const armnn::TensorInfo outputTensorInfo = armnn::TensorInfo({1, 3, 5, 7}, armnn::DataType::Float32);
+
+    armnn::PadDescriptor desc({{0, 0}, {1, 0}, {1, 1}, {1, 2}});
+    desc.m_PaddingMode = armnn::PaddingMode::Reflect;
+
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
+    armnn::IConnectableLayer* const padLayer = network->AddPadLayer(desc, layerName.c_str());
+    armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
+
+    inputLayer->GetOutputSlot(0).Connect(padLayer->GetInputSlot(0));
+    padLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+
+    inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
+    padLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+    CHECK(deserializedNetwork);
+
+    LayerVerifierBaseWithDescriptor<armnn::PadDescriptor> verifier(layerName,
+                                                                   {inputTensorInfo},
+                                                                   {outputTensorInfo},
+                                                                   desc);
+    deserializedNetwork->ExecuteStrategy(verifier);
+}
+
 TEST_CASE("EnsurePadBackwardCompatibility")
 {
     // The PadDescriptor is being extended with a float PadValue (so a value other than 0