blob: f88cb388be0db4ffcba82987b94feb67e59cade0 [file] [log] [blame]
Giuseppe Rossinid7647d42018-07-17 18:13:13 +01001/*
Georgios Pinitasdea2d2d2018-12-19 16:23:17 +00002 * Copyright (c) 2018-2019 ARM Limited.
Giuseppe Rossinid7647d42018-07-17 18:13:13 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "arm_compute/runtime/CL/functions/CLPadLayer.h"
25
26#include "arm_compute/core/CL/ICLTensor.h"
27#include "arm_compute/core/Types.h"
Manuel Bottini60f39112019-03-18 15:25:15 +000028#include "arm_compute/core/utils/misc/ShapeCalculator.h"
Giuseppe Rossinid7647d42018-07-17 18:13:13 +010029#include "support/ToolchainSupport.h"
30
31namespace arm_compute
32{
33CLPadLayer::CLPadLayer()
Manuel Bottini60f39112019-03-18 15:25:15 +000034 : _copy_kernel(), _mode(), _padding(), _memset_kernel(), _num_dimensions(0), _slice_functions(nullptr), _concat_functions(nullptr), _slice_results(nullptr), _concat_results(nullptr)
Giuseppe Rossinid7647d42018-07-17 18:13:13 +010035{
36}
37
Manuel Bottini60f39112019-03-18 15:25:15 +000038void CLPadLayer::configure_constant_mode(ICLTensor *input, ICLTensor *output, const PaddingList &padding, const PixelValue constant_value)
39{
40 // Set the pages of the output to the constant_value.
41 _memset_kernel.configure(output, constant_value);
42
43 // Fill out padding list with zeroes.
44 PaddingList padding_extended = padding;
45 for(size_t i = padding.size(); i < TensorShape::num_max_dimensions; i++)
46 {
47 padding_extended.emplace_back(PaddingInfo{ 0, 0 });
48 }
49
50 // Create a window within the output tensor where the input will be copied.
51 Window copy_window = Window();
52 for(uint32_t i = 0; i < output->info()->num_dimensions(); ++i)
53 {
54 copy_window.set(i, Window::Dimension(padding_extended[i].first, padding_extended[i].first + input->info()->dimension(i), 1));
55 }
56 // Copy the input to the output, leaving the padding filled with the constant_value.
57 _copy_kernel.configure(input, output, PaddingList(), &copy_window);
58}
59
60void CLPadLayer::configure_reflect_symmetric_mode(ICLTensor *input, ICLTensor *output)
61{
62 int64_t last_padding_dimension = _padding.size() - 1;
63 // Reflecting can be performed by effectively unfolding the input as follows:
64 // For each dimension starting at DimX:
65 // Create a before and after slice, which values depend on the selected padding mode
66 // Concatenate the before and after padding with the tensor to be padded
67
68 // Two strided slice functions will be required for each dimension padded as well as a
69 // concatenate function and the tensors to hold the temporary results.
70 _slice_functions = arm_compute::support::cpp14::make_unique<CLStridedSlice[]>(2 * _num_dimensions);
71 _slice_results = arm_compute::support::cpp14::make_unique<CLTensor[]>(2 * _num_dimensions);
72 _concat_functions = arm_compute::support::cpp14::make_unique<CLConcatenateLayer[]>(_num_dimensions);
73 _concat_results = arm_compute::support::cpp14::make_unique<CLTensor[]>(_num_dimensions - 1);
74 Coordinates starts_before, ends_before, starts_after, ends_after, strides;
75 ICLTensor *prev = input;
76 for(uint32_t i = 0; i < _num_dimensions; ++i)
77 {
78 // Values in strides from the previous dimensions need to be set to 1 to avoid reversing again.
79 if(i > 0)
80 {
81 strides.set(i - 1, 1);
82 }
83
84 if(_padding[i].first > 0 || _padding[i].second > 0)
85 {
86 // Set the starts, ends, and strides values for the current dimension.
87 // Due to the bit masks passed to strided slice, the values below the current dimension in
88 // starts and ends will be ignored so do not need to be modified.
89 if(_mode == PaddingMode::REFLECT)
90 {
91 starts_before.set(i, _padding[i].first);
92 ends_before.set(i, 0);
93 starts_after.set(i, input->info()->dimension(i) - 2);
94 ends_after.set(i, input->info()->dimension(i) - _padding[i].second - 2);
95 strides.set(i, -1);
96 }
97 else
98 {
99 starts_before.set(i, _padding[i].first - 1);
100 ends_before.set(i, -1);
101 starts_after.set(i, input->info()->dimension(i) - 1);
102 ends_after.set(i, input->info()->dimension(i) - _padding[i].second - 1);
103 strides.set(i, -1);
104 }
105
106 // Strided slice wraps negative indexes around to the end of the range,
107 // instead this should indicate use of the full range and so the bit mask will be modified.
108 const int32_t begin_mask_before = starts_before[i] < 0 ? ~0 : ~(1u << i);
109 const int32_t end_mask_before = ends_before[i] < 0 ? ~0 : ~(1u << i);
110 const int32_t begin_mask_after = starts_after[i] < 0 ? ~0 : ~(1u << i);
111 const int32_t end_mask_after = ends_after[i] < 0 ? ~0 : ~(1u << i);
112
113 // Reflect the input values for the padding before and after the input.
114 std::vector<ICLTensor *> concat_vector;
115 if(_padding[i].first > 0)
116 {
117 if(i < prev->info()->num_dimensions())
118 {
119 _slice_functions[2 * i].configure(prev, &_slice_results[2 * i], starts_before, ends_before, strides, begin_mask_before, end_mask_before);
120 concat_vector.push_back(&_slice_results[2 * i]);
121 }
122 else
123 {
124 // Performing the slice is unnecessary if the result would simply be a copy of the tensor.
125 concat_vector.push_back(prev);
126 }
127 }
128 concat_vector.push_back(prev);
129 if(_padding[i].second > 0)
130 {
131 if(i < prev->info()->num_dimensions())
132 {
133 _slice_functions[2 * i + 1].configure(prev, &_slice_results[2 * i + 1], starts_after, ends_after, strides, begin_mask_after, end_mask_after);
134 concat_vector.push_back(&_slice_results[2 * i + 1]);
135 }
136 else
137 {
138 // Performing the slice is unnecessary if the result would simply be a copy of the tensor.
139 concat_vector.push_back(prev);
140 }
141 }
142 // Concatenate the padding before and after with the input.
143 ICLTensor *out = (static_cast<int32_t>(i) == last_padding_dimension) ? output : &_concat_results[i];
144 _concat_functions[i].configure(concat_vector, out, get_index_data_layout_dimension(prev->info()->data_layout(), i));
145 prev = out;
146 }
147 }
148 for(uint32_t i = 0; i < _num_dimensions; ++i)
149 {
150 if((static_cast<int32_t>(i) != last_padding_dimension))
151 {
152 _concat_results[i].allocator()->allocate();
153 }
154 _slice_results[2 * i].allocator()->allocate();
155 _slice_results[2 * i + 1].allocator()->allocate();
156 }
157}
158
Usama Arif8cf8c112019-03-14 15:36:54 +0000159void CLPadLayer::configure(ICLTensor *input, ICLTensor *output, const PaddingList &padding, PixelValue constant_value, PaddingMode mode)
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100160{
Manuel Bottini60f39112019-03-18 15:25:15 +0000161 ARM_COMPUTE_ERROR_THROW_ON(validate(input->info(), output->info(), padding, constant_value, mode));
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100162
Manuel Bottini60f39112019-03-18 15:25:15 +0000163 _padding = padding;
164 _mode = mode;
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100165
Manuel Bottini60f39112019-03-18 15:25:15 +0000166 TensorShape padded_shape = misc::shape_calculator::compute_padded_shape(input->info()->tensor_shape(), _padding);
167
168 auto_init_if_empty(*output->info(), input->info()->clone()->set_tensor_shape(padded_shape));
169
170 // Find the last dimension requiring padding so that it is known when to write to output and whether any padding is applied.
171 int64_t last_padding_dimension = _padding.size() - 1;
172 for(; last_padding_dimension >= 0; --last_padding_dimension)
173 {
174 if(_padding[last_padding_dimension].first > 0 || _padding[last_padding_dimension].second > 0)
175 {
176 break;
177 }
178 }
179 _num_dimensions = last_padding_dimension + 1;
180 if(_num_dimensions > 0)
181 {
182 switch(_mode)
183 {
184 case PaddingMode::CONSTANT:
185 {
186 configure_constant_mode(input, output, padding, constant_value);
187 break;
188 }
189 case PaddingMode::REFLECT:
190 case PaddingMode::SYMMETRIC:
191 {
192 configure_reflect_symmetric_mode(input, output);
193 break;
194 }
195 default:
196 ARM_COMPUTE_ERROR("Padding mode not supported.");
197 }
198 }
199 else
200 {
201 // Copy the input to the whole output if no padding is applied
202 _copy_kernel.configure(input, output);
203 }
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100204}
205
Usama Arif8cf8c112019-03-14 15:36:54 +0000206Status CLPadLayer::validate(const ITensorInfo *input, const ITensorInfo *output, const PaddingList &padding, PixelValue constant_value, PaddingMode mode)
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100207{
Manuel Bottini60f39112019-03-18 15:25:15 +0000208 ARM_COMPUTE_RETURN_ERROR_ON(padding.size() > input->num_dimensions());
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100209
Manuel Bottini60f39112019-03-18 15:25:15 +0000210 TensorShape padded_shape = misc::shape_calculator::compute_padded_shape(input->tensor_shape(), padding);
211
212 // Use CLCopyKernel and CLMemsetKernel to validate all padding modes as this includes all of the shape and info validation.
213 PaddingList padding_extended = padding;
214 for(size_t i = padding.size(); i < TensorShape::num_max_dimensions; i++)
215 {
216 padding_extended.emplace_back(PaddingInfo{ 0, 0 });
217 }
218
219 Window copy_window = Window();
220 for(uint32_t i = 0; i < padded_shape.num_dimensions(); ++i)
221 {
222 copy_window.set(i, Window::Dimension(padding_extended[i].first, padding_extended[i].first + input->dimension(i), 1));
223 }
224 if(output->total_size() > 0)
225 {
226 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output->tensor_shape(), padded_shape);
227 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(output, input);
228 ARM_COMPUTE_RETURN_ON_ERROR(CLCopyKernel::validate(input, output, PaddingList(), &copy_window));
229 ARM_COMPUTE_RETURN_ON_ERROR(CLMemsetKernel::validate(output, constant_value));
230 }
231 else
232 {
233 ARM_COMPUTE_RETURN_ON_ERROR(CLCopyKernel::validate(input, &input->clone()->set_tensor_shape(padded_shape), PaddingList(), &copy_window));
234 ARM_COMPUTE_RETURN_ON_ERROR(CLMemsetKernel::validate(&input->clone()->set_tensor_shape(padded_shape), constant_value));
235 }
236
237 switch(mode)
238 {
239 case PaddingMode::CONSTANT:
240 {
241 break;
242 }
243 case PaddingMode::REFLECT:
244 case PaddingMode::SYMMETRIC:
245 {
246 for(uint32_t i = 0; i < padding.size(); ++i)
247 {
248 if(mode == PaddingMode::REFLECT)
249 {
250 ARM_COMPUTE_RETURN_ERROR_ON(padding[i].first >= input->dimension(i));
251 ARM_COMPUTE_RETURN_ERROR_ON(padding[i].second >= input->dimension(i));
252 }
253 else
254 {
255 ARM_COMPUTE_RETURN_ERROR_ON(padding[i].first > input->dimension(i));
256 ARM_COMPUTE_RETURN_ERROR_ON(padding[i].second > input->dimension(i));
257 }
258 }
259 break;
260 }
261 default:
262 {
263 ARM_COMPUTE_ERROR("Invalid mode");
264 }
265 }
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100266 return Status{};
267}
268
269void CLPadLayer::run()
270{
Manuel Bottini60f39112019-03-18 15:25:15 +0000271 if(_num_dimensions > 0)
272 {
273 switch(_mode)
274 {
275 case PaddingMode::CONSTANT:
276 {
277 CLScheduler::get().enqueue(_memset_kernel, false);
278 CLScheduler::get().enqueue(_copy_kernel, true);
279 break;
280 }
281 case PaddingMode::REFLECT:
282 case PaddingMode::SYMMETRIC:
283 {
284 for(uint32_t i = 0; i < _num_dimensions; ++i)
285 {
286 if(_padding[i].first > 0 || _padding[i].second > 0)
287 {
288 if(_padding[i].first > 0 && _slice_results[2 * i].info()->total_size() > 0)
289 {
290 _slice_functions[2 * i].run();
291 }
292 if(_padding[i].second > 0 && _slice_results[2 * i + 1].info()->total_size() > 0)
293 {
294 _slice_functions[2 * i + 1].run();
295 }
296 CLScheduler::get().sync();
297 _concat_functions[i].run();
298 CLScheduler::get().sync();
299 }
300 }
301 break;
302 }
303 default:
304 ARM_COMPUTE_ERROR("Padding mode not supported.");
305 }
306 }
307 else
308 {
309 CLScheduler::get().enqueue(_copy_kernel, true);
310 }
Giuseppe Rossinid7647d42018-07-17 18:13:13 +0100311}
312} // namespace arm_compute