• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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