IVGCVSW-2043 - Merger using ACL for innermost concat axis
[platform/upstream/armnn.git] / src / armnn / layers / MergerLayer.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include "MergerLayer.hpp"
6 #include "LayerCloneBase.hpp"
7
8 #include <armnn/TypesUtils.hpp>
9 #include <backendsCommon/WorkloadData.hpp>
10 #include <backendsCommon/WorkloadFactory.hpp>
11
12 #include <queue>
13
14 namespace armnn
15 {
16
17 MergerLayer::MergerLayer(const OriginsDescriptor& param, const char* name)
18     : LayerWithParameters(param.GetNumViews(), 1, LayerType::Merger, param, name)
19 {
20 }
21
22 std::unique_ptr<IWorkload> MergerLayer::CreateWorkload(const Graph& graph, const IWorkloadFactory& factory) const
23 {
24     MergerQueueDescriptor descriptor;
25
26     // Copies the view origins to the descriptor.
27     descriptor.m_ViewOrigins.reserve(m_Param.GetNumViews());
28     for (unsigned int i = 0; i < m_Param.GetNumViews(); ++i)
29     {
30         descriptor.m_ViewOrigins.emplace_back(
31             std::vector<unsigned int>(m_Param.GetViewOrigin(i), m_Param.GetViewOrigin(i) + m_Param.GetNumDimensions()));
32     }
33
34     return factory.CreateMerger(descriptor, PrepInfoAndDesc(descriptor, graph));
35 }
36
37 void MergerLayer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory)
38 {
39     //If sub tensors are supported than the merger
40     //just needs to make sure that the outputs of the prev layer
41     //are made subtensors of the output of the merger layer.
42     m_OutputHandlers[0].CreateTensorHandles(factory);
43
44     signed long innerAxis = m_Param.GetNumDimensions() - m_Param.GetConcatAxis();
45
46     if (factory.SupportsSubTensors() && innerAxis != 1)
47     {
48         std::queue<MergerLayer*> m_MergerLayers;
49
50         m_MergerLayers.push(this);
51         while (!m_MergerLayers.empty())
52         {
53             MergerLayer* currentLayer = m_MergerLayers.front();
54             ITensorHandle* parentTensor = currentLayer->GetOutputHandler(0).GetData();
55
56             m_MergerLayers.pop();
57
58             const unsigned int numInputSlots = currentLayer->GetNumInputSlots();
59             for (unsigned int i = 0; i < numInputSlots; ++i)
60             {
61                 OutputSlot* slot = currentLayer->GetInputSlot(i).GetConnectedOutputSlot();
62                 OutputHandler& outputHandler = slot->GetOutputHandler();
63                 outputHandler.SetData(factory.CreateSubTensorHandle(*parentTensor,
64                                                                     outputHandler.GetTensorInfo().GetShape(),
65                                                                     currentLayer->m_Param.GetViewOrigin(i)));
66
67                 Layer& inputLayer = slot->GetOwningLayer();
68                 if (inputLayer.GetType() == LayerType::Merger)
69                 {
70                     m_MergerLayers.push(boost::polymorphic_downcast<MergerLayer*>(&inputLayer));
71                 }
72             }
73         }
74     }
75 }
76
77 MergerLayer* MergerLayer::Clone(Graph& graph) const
78 {
79     return CloneBase<MergerLayer>(graph, m_Param, GetName());
80 }
81
82 std::vector<TensorShape> MergerLayer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
83 {
84     BOOST_ASSERT(inputShapes.size() == m_Param.GetNumViews());
85
86     unsigned int numDims = m_Param.GetNumDimensions();
87     for (unsigned int i=0; i< inputShapes.size(); i++)
88     {
89         auto& inputShape = inputShapes[i];
90
91         ConditionalThrowIfNotEqual<LayerValidationException>(
92             "MergerLayer: Num Dimensions must match all inputs.",
93             numDims,
94             inputShape.GetNumDimensions());
95     }
96
97     // Finds the bounding box (extents) of all the views.
98     std::vector<unsigned int> extentMin(numDims);
99     std::vector<unsigned int> extentMax(numDims);
100     for (unsigned int i = 0; i < inputShapes.size(); i++)
101     {
102         const uint32_t* origin = m_Param.GetViewOrigin(i);
103         const armnn::TensorShape& shape = inputShapes[i];
104         for (unsigned int d = 0; d < numDims; d++)
105         {
106             extentMin[d] = std::min(extentMin[d], origin[d]);
107             extentMax[d] = std::max(extentMax[d], origin[d] + shape[d]);
108         }
109     }
110
111     // Checks that the bounding box starts at the origin.
112     if (!std::all_of(extentMin.begin(), extentMin.end(), [](unsigned int s) { return s == 0; }))
113     {
114         throw LayerValidationException("MergerLayer: there is no view that starts at the origin");
115     }
116
117     // Checks that there are no overlaps of views (this would lead to undefined output at those locations).
118     // Checks each pair of views against each other
119     // (and doesn't bother to check against self, or check the same pair both ways round).
120     for (unsigned int a = 0; a < inputShapes.size(); a++)
121     {
122         const uint32_t* aOrigin = m_Param.GetViewOrigin(a);
123         const armnn::TensorShape& aShape = inputShapes[a];
124         for (unsigned int b = 0; b < a; b++)
125         {
126             const uint32_t* bOrigin = m_Param.GetViewOrigin(b);
127             const armnn::TensorShape& bShape = inputShapes[b];
128
129             bool allAxesOverlap = true;
130             for (unsigned int d = 0; d < numDims && allAxesOverlap; d++)
131             {
132                 unsigned int a1 = aOrigin[d];
133                 unsigned int a2 = aOrigin[d] + aShape[d];
134
135                 unsigned int b1 = bOrigin[d];
136                 unsigned int b2 = bOrigin[d] + bShape[d];
137
138                 if (a2 <= b1 || b2 <= a1)
139                 {
140                     allAxesOverlap = false;
141                 }
142             }
143             if (allAxesOverlap)
144             {
145                 throw LayerValidationException("MergerLayer: Some views overlap.");
146             }
147         }
148     }
149
150     // Checks that there are no "holes", i.e. regions of the output which is not covered by a view.
151     // Because we already checked that there are no overlaps, this can be done simply by checking that
152     // the total 'volume' of the views is the same as the output.
153     unsigned int totalViewsVolume = 0;
154     for (unsigned int i = 0; i < inputShapes.size(); i++)
155     {
156         totalViewsVolume += inputShapes[i].GetNumElements();
157     }
158     unsigned int outputVolume = 1;
159     for (unsigned int d = 0; d < numDims; d++)
160     {
161         outputVolume *= (extentMax[d] - extentMin[d]);
162     }
163
164     ConditionalThrowIfNotEqual<LayerValidationException>(
165         "MergerLayer: there are some gaps between views",
166         totalViewsVolume,
167         outputVolume);
168
169     return std::vector<TensorShape>({ TensorShape({numDims, extentMax.data()}) });
170 }
171
172 void MergerLayer::ValidateTensorShapesFromInputs()
173 {
174     // Validates Merger layer.
175     ConditionalThrowIfNotEqual<LayerValidationException>(
176         "MergerLayer: Num Inputs must match num views.",
177         m_Param.GetNumViews(),
178         GetNumInputSlots());
179
180     VerifyLayerConnections(m_Param.GetNumViews(), CHECK_LOCATION());
181
182     std::vector<TensorShape> inputShapes;
183     for (uint i = 0; i < GetNumInputSlots(); ++i)
184     {
185         inputShapes.push_back(GetInputSlot(i).GetConnection()->GetTensorInfo().GetShape());
186     }
187
188     auto inferredShapes = InferOutputShapes(inputShapes);
189
190     BOOST_ASSERT(inferredShapes.size() == 1);
191
192     ConditionalThrowIfNotEqual<LayerValidationException>(
193         "MergerLayer: TensorShape set on OutputSlot[0] does not match the inferred shape.",
194         GetOutputSlot(0).GetTensorInfo().GetShape(),
195         inferredShapes[0]);
196 }
197
198 } // namespace armnn armnn