1 /*
2 * Copyright (c) 2017-2020 Arm Limited.
3 *
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/NEON/functions/NEDirectConvolutionLayer.h"
25
26 #include "arm_compute/core/PixelValue.h"
27 #include "arm_compute/core/Utils.h"
28 #include "arm_compute/core/Validate.h"
29 #include "arm_compute/runtime/NEON/NEScheduler.h"
30 #include "src/core/NEON/kernels/NEDirectConvolutionLayerKernel.h"
31 #include "src/core/NEON/kernels/NEDirectConvolutionLayerOutputStageKernel.h"
32 #include "src/core/NEON/kernels/NEFillBorderKernel.h"
33 #include "support/MemorySupport.h"
34
35 namespace arm_compute
36 {
37 NEDirectConvolutionLayer::~NEDirectConvolutionLayer() = default;
38
NEDirectConvolutionLayer(std::shared_ptr<IMemoryManager> memory_manager)39 NEDirectConvolutionLayer::NEDirectConvolutionLayer(std::shared_ptr<IMemoryManager> memory_manager)
40 : _memory_group(std::move(memory_manager)), _output_stage_kernel(), _conv_kernel(), _input_border_handler(), _activationlayer_function(), _accumulator(), _has_bias(false),
41 _is_activationlayer_enabled(false), _dim_split(Window::DimZ), _is_padding_required()
42 {
43 }
44
configure(ITensor * input,const ITensor * weights,const ITensor * bias,ITensor * output,const PadStrideInfo & conv_info,const ActivationLayerInfo & act_info)45 void NEDirectConvolutionLayer::configure(ITensor *input, const ITensor *weights, const ITensor *bias, ITensor *output, const PadStrideInfo &conv_info, const ActivationLayerInfo &act_info)
46 {
47 ARM_COMPUTE_ERROR_ON(input->info()->data_layout() == DataLayout::UNKNOWN);
48 _output_stage_kernel = arm_compute::support::cpp14::make_unique<NEDirectConvolutionLayerOutputStageKernel>();
49 _conv_kernel = arm_compute::support::cpp14::make_unique<NEDirectConvolutionLayerKernel>();
50 _input_border_handler = arm_compute::support::cpp14::make_unique<NEFillBorderKernel>();
51
52 // Free accumulator
53 if(_accumulator.buffer() != nullptr)
54 {
55 _accumulator.allocator()->free();
56 }
57
58 _dim_split = input->info()->data_layout() == DataLayout::NCHW ? Window::DimZ : Window::DimY;
59
60 // Check if bias should be added in the convolution result
61 _has_bias = (bias != nullptr);
62
63 _conv_kernel->configure(input, weights, output, conv_info);
64 if(_has_bias)
65 {
66 _output_stage_kernel->configure(output, bias);
67 }
68 _is_padding_required = !_conv_kernel->border_size().empty();
69
70 if(_is_padding_required)
71 {
72 // Add zero padding XY
73 _input_border_handler->configure(input, _conv_kernel->border_size(), BorderMode::CONSTANT, PixelValue(static_cast<float>(0.f)));
74 }
75
76 //Configure Activation Layer
77 _is_activationlayer_enabled = act_info.enabled();
78 if(_is_activationlayer_enabled)
79 {
80 _activationlayer_function.configure(output, nullptr, act_info);
81 }
82 }
83
validate(const ITensorInfo * input,const ITensorInfo * weights,const ITensorInfo * bias,const ITensorInfo * output,const PadStrideInfo & conv_info,const ActivationLayerInfo & act_info)84 Status NEDirectConvolutionLayer::validate(const ITensorInfo *input, const ITensorInfo *weights, const ITensorInfo *bias, const ITensorInfo *output, const PadStrideInfo &conv_info,
85 const ActivationLayerInfo &act_info)
86 {
87 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output);
88
89 // output might not be initialized since it can be an intermediate tensor of another layer
90 DataType data_type = input->data_type();
91 TensorInfo accumulator(output->clone()->set_is_resizable(true).reset_padding().set_data_type(data_type));
92
93 // Validate Convolution kernel
94 ARM_COMPUTE_RETURN_ON_ERROR(NEDirectConvolutionLayerKernel::validate(input, weights, &accumulator, conv_info));
95
96 if(bias != nullptr)
97 {
98 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(weights, bias);
99 ARM_COMPUTE_RETURN_ERROR_ON_MSG(bias->dimension(0) != weights->dimension(3),
100 "Biases size and number of input feature maps should match");
101 ARM_COMPUTE_RETURN_ERROR_ON_MSG(bias->num_dimensions() > 1, "Biases should be one dimensional");
102 }
103
104 // Validate bias kernel
105 ARM_COMPUTE_RETURN_ON_ERROR(NEDirectConvolutionLayerOutputStageKernel::validate(&accumulator, bias, output));
106
107 if(act_info.enabled())
108 {
109 ARM_COMPUTE_RETURN_ON_ERROR(NEActivationLayer::validate(output, nullptr, act_info));
110 }
111
112 return Status{};
113 }
114
run()115 void NEDirectConvolutionLayer::run()
116 {
117 MemoryGroupResourceScope scope_mg(_memory_group);
118
119 if(_is_padding_required)
120 {
121 NEScheduler::get().schedule(_input_border_handler.get(), Window::DimZ);
122 }
123 NEScheduler::get().schedule(_conv_kernel.get(), _dim_split);
124 if(_has_bias)
125 {
126 NEScheduler::get().schedule(_output_stage_kernel.get(), Window::DimY);
127 }
128
129 if(_is_activationlayer_enabled)
130 {
131 _activationlayer_function.run();
132 }
133 }
134 } // namespace arm_compute
135