• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "Operations"
18 
19 #include "DepthwiseConv2D.h"
20 
21 #include <algorithm>
22 #include <vector>
23 
24 #include "OperationResolver.h"
25 #include "Operations.h"
26 #include "Tracing.h"
27 
28 #ifdef NN_INCLUDE_CPU_IMPLEMENTATION
29 #pragma clang diagnostic push
30 #pragma clang diagnostic ignored "-Wunused-parameter"
31 #include <tensorflow/lite/kernels/internal/optimized/depthwiseconv_uint8.h>
32 #include <tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h>
33 #pragma clang diagnostic pop
34 
35 #include "CpuOperationUtils.h"
36 #endif  // NN_INCLUDE_CPU_IMPLEMENTATION
37 
38 namespace android {
39 namespace nn {
40 namespace depthwise_conv_2d {
41 
42 #ifdef NN_INCLUDE_CPU_IMPLEMENTATION
43 namespace {
44 
45 struct DepthwiseConv2dParam {
46     int32_t padding_left, padding_right;
47     int32_t padding_top, padding_bottom;
48     int32_t stride_width, stride_height;
49     int32_t dilation_width_factor = 1, dilation_height_factor = 1;
50     int32_t depth_multiplier;
51     int32_t activation;
52     bool useNchw = false;
53 
initializeandroid::nn::depthwise_conv_2d::__anon8596ca100111::DepthwiseConv2dParam54     bool initialize(const IOperationExecutionContext* context) {
55         uint32_t inCount = context->getNumInputs();
56         int32_t padding_implicit = 0;
57         bool useImplicitPadding = false;
58         if ((inCount >= 9 && context->getInputType(8) == OperandType::BOOL) || inCount == 8) {
59             padding_implicit = context->getInputValue<int32_t>(3);
60             stride_width = context->getInputValue<int32_t>(4);
61             stride_height = context->getInputValue<int32_t>(5);
62             depth_multiplier = context->getInputValue<int32_t>(6);
63             activation = context->getInputValue<int32_t>(7);
64             if (inCount >= 9) {
65                 useNchw = context->getInputValue<bool>(8);
66             }
67             if (inCount == 11) {
68                 dilation_width_factor = context->getInputValue<int32_t>(9);
69                 dilation_height_factor = context->getInputValue<int32_t>(10);
70             }
71             useImplicitPadding = true;
72         } else if (inCount >= 11 && context->getInputType(8) == OperandType::INT32) {
73             padding_left = context->getInputValue<int32_t>(3);
74             padding_right = context->getInputValue<int32_t>(4);
75             padding_top = context->getInputValue<int32_t>(5);
76             padding_bottom = context->getInputValue<int32_t>(6);
77             stride_width = context->getInputValue<int32_t>(7);
78             stride_height = context->getInputValue<int32_t>(8);
79             depth_multiplier = context->getInputValue<int32_t>(9);
80             activation = context->getInputValue<int32_t>(10);
81             if (inCount >= 12) {
82                 useNchw = context->getInputValue<bool>(11);
83             }
84             if (inCount == 14) {
85                 dilation_width_factor = context->getInputValue<int32_t>(12);
86                 dilation_height_factor = context->getInputValue<int32_t>(13);
87             }
88         } else {
89             NN_RET_CHECK_FAIL() << "Unsupported input spec for operation " << kOperationName;
90         }
91         if (useImplicitPadding) {
92             Shape inputShape = context->getInputShape(kInputTensor);
93             Shape filterShape = context->getInputShape(kFilterTensor);
94             int32_t input_width = getSizeOfDimension(inputShape, useNchw ? 3 : 2);
95             int32_t input_height = getSizeOfDimension(inputShape, useNchw ? 2 : 1);
96             int32_t filter_width = getSizeOfDimension(filterShape, 2);
97             int32_t filter_height = getSizeOfDimension(filterShape, 1);
98             calculateExplicitPadding(input_width, stride_width, dilation_width_factor, filter_width,
99                                      padding_implicit, &padding_left, &padding_right);
100             calculateExplicitPadding(input_height, stride_height, dilation_height_factor,
101                                      filter_height, padding_implicit, &padding_top,
102                                      &padding_bottom);
103         }
104         NN_RET_CHECK_GE(padding_left, 0);
105         NN_RET_CHECK_GE(padding_right, 0);
106         NN_RET_CHECK_GE(padding_top, 0);
107         NN_RET_CHECK_GE(padding_bottom, 0);
108         NN_RET_CHECK_GT(stride_width, 0);
109         NN_RET_CHECK_GT(stride_height, 0);
110         NN_RET_CHECK_GT(dilation_width_factor, 0);
111         NN_RET_CHECK_GT(dilation_height_factor, 0);
112         NN_RET_CHECK_GT(depth_multiplier, 0);
113         NN_RET_CHECK_GE(activation, 0);
114         return true;
115     }
116 };
117 
118 #define ANDROID_NN_DEPTHWISE_CONV_PARAMETERS                                     \
119     [[maybe_unused]] uint32_t height = getSizeOfDimension(inputShape, 1);        \
120     [[maybe_unused]] uint32_t width = getSizeOfDimension(inputShape, 2);         \
121     [[maybe_unused]] uint32_t filterHeight = getSizeOfDimension(filterShape, 1); \
122     [[maybe_unused]] uint32_t filterWidth = getSizeOfDimension(filterShape, 2);  \
123     [[maybe_unused]] uint32_t outHeight = getSizeOfDimension(outputShape, 1);    \
124     [[maybe_unused]] uint32_t outWidth = getSizeOfDimension(outputShape, 2);     \
125                                                                                  \
126     uint32_t paddingHeight = (uint32_t)paddingTop;                               \
127     uint32_t paddingWidth = (uint32_t)paddingLeft;
128 
depthwiseConvNhwc(const float * inputData,const Shape & inputShape,const float * filterData,const Shape & filterShape,const float * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t,int32_t paddingTop,int32_t,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,float * outputData,const Shape & outputShape)129 bool depthwiseConvNhwc(const float* inputData, const Shape& inputShape, const float* filterData,
130                        const Shape& filterShape, const float* biasData, const Shape& biasShape,
131                        int32_t paddingLeft, int32_t /*paddingRight*/, int32_t paddingTop,
132                        int32_t /*paddingBottom*/, int32_t strideWidth, int32_t strideHeight,
133                        int32_t dilationWidthFactor, int32_t dilationHeightFactor,
134                        int32_t depthMultiplier, int32_t activation, float* outputData,
135                        const Shape& outputShape) {
136     NNTRACE_TRANS("depthwiseConvFloat32");
137 
138     ANDROID_NN_DEPTHWISE_CONV_PARAMETERS
139 
140     float output_activation_min, output_activation_max;
141     CalculateActivationRangeFloat(activation, &output_activation_min, &output_activation_max);
142 
143     tflite::DepthwiseParams params{
144             .padding_values = {static_cast<int16>(paddingWidth), static_cast<int16>(paddingHeight),
145                                0 /*width_offset*/, 0 /*height_offset*/},
146             .stride_width = static_cast<int16>(strideWidth),
147             .stride_height = static_cast<int16>(strideHeight),
148             .dilation_width_factor = static_cast<int16>(dilationWidthFactor),
149             .dilation_height_factor = static_cast<int16>(dilationHeightFactor),
150             .depth_multiplier = static_cast<int16>(depthMultiplier),
151             .float_activation_min = output_activation_min,
152             .float_activation_max = output_activation_max,
153     };
154     NNTRACE_COMP_SWITCH("optimized_ops::DepthwiseConv");
155     tflite::reference_ops::DepthwiseConv(params, convertShapeToTflshape(inputShape), inputData,
156                                          convertShapeToTflshape(filterShape), filterData,
157                                          convertShapeToTflshape(biasShape), biasData,
158                                          convertShapeToTflshape(outputShape), outputData);
159 
160     return true;
161 }
162 
depthwiseConvNhwc(const _Float16 * inputData,const Shape & inputShape,const _Float16 * filterData,const Shape & filterShape,const _Float16 * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t paddingRight,int32_t paddingTop,int32_t paddingBottom,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,_Float16 * outputData,const Shape & outputShape)163 bool depthwiseConvNhwc(const _Float16* inputData, const Shape& inputShape,
164                        const _Float16* filterData, const Shape& filterShape,
165                        const _Float16* biasData, const Shape& biasShape, int32_t paddingLeft,
166                        int32_t paddingRight, int32_t paddingTop, int32_t paddingBottom,
167                        int32_t strideWidth, int32_t strideHeight, int32_t dilationWidthFactor,
168                        int32_t dilationHeightFactor, int32_t depthMultiplier, int32_t activation,
169                        _Float16* outputData, const Shape& outputShape) {
170     NNTRACE_TRANS("depthwiseConvFloat16");
171     std::vector<float> inputDataFloat32(getNumberOfElements(inputShape));
172     convertFloat16ToFloat32(inputData, &inputDataFloat32);
173     std::vector<float> filterDataFloat32(getNumberOfElements(filterShape));
174     convertFloat16ToFloat32(filterData, &filterDataFloat32);
175     std::vector<float> biasDataFloat32(getNumberOfElements(biasShape));
176     convertFloat16ToFloat32(biasData, &biasDataFloat32);
177 
178     std::vector<float> outputDataFloat32(getNumberOfElements(outputShape));
179     depthwiseConvNhwc(inputDataFloat32.data(), inputShape, filterDataFloat32.data(), filterShape,
180                       biasDataFloat32.data(), biasShape, paddingLeft, paddingRight, paddingTop,
181                       paddingBottom, strideWidth, strideHeight, dilationWidthFactor,
182                       dilationHeightFactor, depthMultiplier, activation, outputDataFloat32.data(),
183                       outputShape);
184 
185     convertFloat32ToFloat16(outputDataFloat32, outputData);
186     return true;
187 }
188 
depthwiseConvNhwc(const uint8_t * inputData,const Shape & inputShape,const uint8_t * filterData,const Shape & filterShape,const int32_t * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t,int32_t paddingTop,int32_t,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,uint8_t * outputData,const Shape & outputShape)189 bool depthwiseConvNhwc(const uint8_t* inputData, const Shape& inputShape, const uint8_t* filterData,
190                        const Shape& filterShape, const int32_t* biasData, const Shape& biasShape,
191                        int32_t paddingLeft, int32_t /*paddingRight*/, int32_t paddingTop,
192                        int32_t /*paddingBottom*/, int32_t strideWidth, int32_t strideHeight,
193                        int32_t dilationWidthFactor, int32_t dilationHeightFactor,
194                        int32_t depthMultiplier, int32_t activation, uint8_t* outputData,
195                        const Shape& outputShape) {
196     NNTRACE_TRANS("depthwiseConvQuant8");
197 
198     ANDROID_NN_DEPTHWISE_CONV_PARAMETERS
199 
200     double real_multiplier = 0.0;
201     int32_t output_multiplier = 0;
202     int32_t output_shift = 0;
203     int32_t output_activation_min = 0;
204     int32_t output_activation_max = 0;
205 
206     NN_RET_CHECK(GetQuantizedConvolutionMultiplier(inputShape, filterShape, biasShape, outputShape,
207                                                    &real_multiplier));
208     int exponent;
209     NN_RET_CHECK(QuantizeMultiplier(real_multiplier, &output_multiplier, &exponent));
210     output_shift = -exponent;
211     CalculateActivationRangeUint8(activation, outputShape, &output_activation_min,
212                                   &output_activation_max);
213 
214     tflite::DepthwiseParams params{
215             .padding_values = {static_cast<int16>(paddingWidth), static_cast<int16>(paddingHeight),
216                                0 /*width_offset*/, 0 /*height_offset*/},
217             .stride_width = static_cast<int16>(strideWidth),
218             .stride_height = static_cast<int16>(strideHeight),
219             .dilation_width_factor = static_cast<int16>(dilationWidthFactor),
220             .dilation_height_factor = static_cast<int16>(dilationHeightFactor),
221             .depth_multiplier = static_cast<int16>(depthMultiplier),
222             .input_offset = -inputShape.offset,
223             .weights_offset = -filterShape.offset,
224             .output_offset = outputShape.offset,
225             .output_multiplier = output_multiplier,
226             .output_shift = -output_shift,
227             .quantized_activation_min = output_activation_min,
228             .quantized_activation_max = output_activation_max,
229     };
230     NNTRACE_COMP_SWITCH("optimized_ops::DepthwiseConv");
231     tflite::reference_ops::DepthwiseConv(params, convertShapeToTflshape(inputShape), inputData,
232                                          convertShapeToTflshape(filterShape), filterData,
233                                          convertShapeToTflshape(biasShape), biasData,
234                                          convertShapeToTflshape(outputShape), outputData);
235     return true;
236 }
237 
238 // Passing input, filter and output shapes by value, so that we can change the
239 // offsets without modifying the actual shapes.
depthwiseConvNhwc(const int8_t * inputData,Shape inputShape,const int8_t * filterData,Shape filterShape,const int32_t * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t paddingRight,int32_t paddingTop,int32_t paddingBottom,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,int8_t * outputData,Shape outputShape)240 bool depthwiseConvNhwc(const int8_t* inputData, Shape inputShape, const int8_t* filterData,
241                        Shape filterShape, const int32_t* biasData, const Shape& biasShape,
242                        int32_t paddingLeft, int32_t paddingRight, int32_t paddingTop,
243                        int32_t paddingBottom, int32_t strideWidth, int32_t strideHeight,
244                        int32_t dilationWidthFactor, int32_t dilationHeightFactor,
245                        int32_t depthMultiplier, int32_t activation, int8_t* outputData,
246                        Shape outputShape) {
247     NNTRACE_TRANS("depthwiseConvQuant8");
248 
249     std::vector<uint8_t> unsignedInput(getNumberOfElements(inputShape));
250     convertInt8ToUInt8(inputData, &unsignedInput);
251     inputShape.offset += 128;
252 
253     std::vector<uint8_t> unsignedFilter(getNumberOfElements(filterShape));
254     convertInt8ToUInt8(filterData, &unsignedFilter);
255     filterShape.offset += 128;
256 
257     std::vector<uint8_t> unsignedOutput(getNumberOfElements(outputShape));
258     outputShape.offset += 128;
259 
260     NN_RET_CHECK(depthwiseConvNhwc(unsignedInput.data(), inputShape, unsignedFilter.data(),
261                                    filterShape, biasData, biasShape, paddingLeft, paddingRight,
262                                    paddingTop, paddingBottom, strideWidth, strideHeight,
263                                    dilationWidthFactor, dilationHeightFactor, depthMultiplier,
264                                    activation, unsignedOutput.data(), outputShape));
265 
266     convertUInt8ToInt8(unsignedOutput, outputData);
267 
268     return true;
269 }
270 
271 template <typename T>
depthwiseConvQuant8PerChannelNhwc(const T * inputData,const Shape & inputShape,const int8_t * filterData,const Shape & filterShape,const float * filterScales,const int32_t * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t,int32_t paddingTop,int32_t,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,T * outputData,const Shape & outputShape)272 bool depthwiseConvQuant8PerChannelNhwc(
273         const T* inputData, const Shape& inputShape, const int8_t* filterData,
274         const Shape& filterShape, const float* filterScales, const int32_t* biasData,
275         const Shape& biasShape, int32_t paddingLeft, int32_t /*paddingRight*/, int32_t paddingTop,
276         int32_t /*paddingBottom*/, int32_t strideWidth, int32_t strideHeight,
277         int32_t dilationWidthFactor, int32_t dilationHeightFactor,
278 
279         int32_t depthMultiplier, int32_t activation, T* outputData, const Shape& outputShape) {
280     NNTRACE_TRANS("depthwiseConvQuant8");
281 
282     [[maybe_unused]] uint32_t paddingHeight = (uint32_t)paddingTop;
283     [[maybe_unused]] uint32_t paddingWidth = (uint32_t)paddingLeft;
284 
285     uint32_t numBatches = getSizeOfDimension(inputShape, 0);
286     uint32_t inputHeight = getSizeOfDimension(inputShape, 1);
287     uint32_t inputWidth = getSizeOfDimension(inputShape, 2);
288     uint32_t inputDepth = getSizeOfDimension(inputShape, 3);
289     uint32_t filterHeight = getSizeOfDimension(filterShape, 1);
290     uint32_t filterWidth = getSizeOfDimension(filterShape, 2);
291     uint32_t filterDepth = getSizeOfDimension(filterShape, 3);
292     uint32_t outputHeight = getSizeOfDimension(outputShape, 1);
293     uint32_t outputWidth = getSizeOfDimension(outputShape, 2);
294     uint32_t outputDepth = getSizeOfDimension(outputShape, 3);
295 
296     int32_t inputOffset = -inputShape.offset;
297     int32_t outputOffset = outputShape.offset;
298 
299     auto realMultiplier = std::vector<double>(outputDepth, .0f);
300     auto outputMultiplier = std::vector<int32_t>(outputDepth, 0);
301     auto outputShift = std::vector<int32_t>(outputDepth, .0f);
302 
303     for (uint32_t i = 0; i < outputDepth; ++i) {
304         Shape filterChannelShape = filterShape;
305         filterChannelShape.scale = filterScales[i];
306         Shape biasChannelShape = biasShape;
307         biasChannelShape.scale = filterScales[i] * inputShape.scale;
308         NN_RET_CHECK(GetQuantizedConvolutionMultiplier(
309                 inputShape, filterChannelShape, biasChannelShape, outputShape, &realMultiplier[i]));
310         int exponent;
311         NN_RET_CHECK(QuantizeMultiplier(realMultiplier[i], &outputMultiplier[i], &exponent));
312         outputShift[i] = -exponent;
313     }
314 
315     int32_t output_activation_min = 0, output_activation_max = 0;
316     CalculateActivationRange<T>(activation, outputShape, &output_activation_min,
317                                 &output_activation_max);
318 
319     const T* inputBase = inputData;
320     T* outPtr = outputData;
321     for (uint32_t b = 0; b < numBatches; b++) {
322         for (uint32_t h = 0; h < outputHeight; h++) {
323             for (uint32_t w = 0; w < outputWidth; w++) {
324                 for (uint32_t ic = 0; ic < inputDepth; ic++) {
325                     for (int32_t m = 0; m < depthMultiplier; m++) {
326                         int32_t wInputOrigin = static_cast<int32_t>(w) * strideWidth - paddingLeft;
327                         int32_t hInputOrigin = static_cast<int32_t>(h) * strideHeight - paddingTop;
328                         const int oc = m + ic * depthMultiplier;
329 
330                         int32_t sum = 0.0f;
331                         for (uint32_t i = 0; i < filterHeight; i++) {
332                             for (uint32_t j = 0; j < filterWidth; j++) {
333                                 int32_t hInput = hInputOrigin +
334                                                  dilationHeightFactor * static_cast<int32_t>(i);
335                                 int32_t wInput = wInputOrigin +
336                                                  dilationWidthFactor * static_cast<int32_t>(j);
337 
338                                 if (hInput >= 0 && hInput < static_cast<int32_t>(inputHeight) &&
339                                     wInput >= 0 && wInput < static_cast<int32_t>(inputWidth)) {
340                                     uint32_t filterIndex =
341                                             i * filterWidth * filterDepth + j * filterDepth + oc;
342                                     uint32_t inputIndex = hInput * inputWidth * inputDepth +
343                                                           wInput * inputDepth + ic;
344                                     sum += (static_cast<int32_t>(filterData[filterIndex])) *
345                                            (static_cast<int32_t>(inputBase[inputIndex]) +
346                                             inputOffset);
347                                 }
348                             }
349                         }
350 
351                         sum += biasData[oc];
352                         sum = tflite::MultiplyByQuantizedMultiplier(sum, outputMultiplier[oc],
353                                                                     -outputShift[oc]);
354                         sum += outputOffset;
355                         sum = std::max(std::min(sum, output_activation_max), output_activation_min);
356                         outPtr[m] = static_cast<T>(sum);
357                     }
358                     outPtr += depthMultiplier;
359                 }
360             }
361         }
362         inputBase += inputHeight * inputWidth * inputDepth;
363     }
364 
365     return true;
366 }
367 
368 template <typename T_Input, typename T_Filter, typename T_Bias>
depthwiseConv(const T_Input * inputData,const Shape & inputShape,const T_Filter * filterData,const Shape & filterShape,const T_Bias * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t paddingRight,int32_t paddingTop,int32_t paddingBottom,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,bool useNchw,T_Input * outputData,const Shape & outputShape)369 bool depthwiseConv(const T_Input* inputData, const Shape& inputShape, const T_Filter* filterData,
370                    const Shape& filterShape, const T_Bias* biasData, const Shape& biasShape,
371                    int32_t paddingLeft, int32_t paddingRight, int32_t paddingTop,
372                    int32_t paddingBottom, int32_t strideWidth, int32_t strideHeight,
373                    int32_t dilationWidthFactor, int32_t dilationHeightFactor,
374                    int32_t depthMultiplier, int32_t activation, bool useNchw, T_Input* outputData,
375                    const Shape& outputShape) {
376     InputWithLayout<T_Input> input(useNchw);
377     OutputWithLayout<T_Input> output(useNchw);
378     NN_RET_CHECK(input.initialize(inputData, inputShape));
379     NN_RET_CHECK(output.initialize(outputData, outputShape));
380     NN_RET_CHECK(depthwiseConvNhwc(input.getNhwcBuffer(), input.getNhwcShape(), filterData,
381                                    filterShape, biasData, biasShape, paddingLeft, paddingRight,
382                                    paddingTop, paddingBottom, strideWidth, strideHeight,
383                                    dilationWidthFactor, dilationHeightFactor, depthMultiplier,
384                                    activation, output.getNhwcBuffer(), output.getNhwcShape()));
385     NN_RET_CHECK(output.commit());
386     return true;
387 }
388 
389 template <typename T>
depthwiseConvQuant8PerChannel(const T * inputData,const Shape & inputShape,const int8_t * filterData,const Shape & filterShape,const float * filterScales,const int32_t * biasData,const Shape & biasShape,int32_t paddingLeft,int32_t paddingRight,int32_t paddingTop,int32_t paddingBottom,int32_t strideWidth,int32_t strideHeight,int32_t dilationWidthFactor,int32_t dilationHeightFactor,int32_t depthMultiplier,int32_t activation,bool useNchw,T * outputData,const Shape & outputShape)390 bool depthwiseConvQuant8PerChannel(const T* inputData, const Shape& inputShape,
391                                    const int8_t* filterData, const Shape& filterShape,
392                                    const float* filterScales, const int32_t* biasData,
393                                    const Shape& biasShape, int32_t paddingLeft,
394                                    int32_t paddingRight, int32_t paddingTop, int32_t paddingBottom,
395                                    int32_t strideWidth, int32_t strideHeight,
396                                    int32_t dilationWidthFactor, int32_t dilationHeightFactor,
397                                    int32_t depthMultiplier, int32_t activation, bool useNchw,
398                                    T* outputData, const Shape& outputShape) {
399     InputWithLayout<T> input(useNchw);
400     OutputWithLayout<T> output(useNchw);
401     NN_RET_CHECK(input.initialize(inputData, inputShape));
402     NN_RET_CHECK(output.initialize(outputData, outputShape));
403     NN_RET_CHECK(depthwiseConvQuant8PerChannelNhwc(
404             input.getNhwcBuffer(), input.getNhwcShape(), filterData, filterShape, filterScales,
405             biasData, biasShape, paddingLeft, paddingRight, paddingTop, paddingBottom, strideWidth,
406             strideHeight, dilationWidthFactor, dilationHeightFactor, depthMultiplier, activation,
407             output.getNhwcBuffer(), output.getNhwcShape()));
408     NN_RET_CHECK(output.commit());
409     return true;
410 }
411 
412 #undef ANDROID_NN_DEPTHWISE_CONV_PARAMETERS
413 
414 }  // namespace
415 
prepare(IOperationExecutionContext * context)416 bool prepare(IOperationExecutionContext* context) {
417     Shape input = context->getInputShape(kInputTensor);
418     Shape filter = context->getInputShape(kFilterTensor);
419     Shape bias = context->getInputShape(kBiasTensor);
420 
421     if (filter.type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
422         NN_RET_CHECK(input.type == OperandType::TENSOR_QUANT8_ASYMM ||
423                      input.type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED);
424     } else {
425         NN_RET_CHECK(input.type == filter.type);
426     }
427     if (input.type == OperandType::TENSOR_QUANT8_ASYMM ||
428         input.type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
429         NN_RET_CHECK(bias.type == OperandType::TENSOR_INT32);
430     } else {
431         NN_RET_CHECK(input.type == bias.type);
432     }
433     NN_RET_CHECK_EQ(getNumberOfDimensions(input), 4u);
434     NN_RET_CHECK_EQ(getNumberOfDimensions(filter), 4u);
435     NN_RET_CHECK_EQ(getNumberOfDimensions(bias), 1u);
436     NN_RET_CHECK_EQ(getSizeOfDimension(filter, 0), 1u);
437     NN_RET_CHECK_EQ(getSizeOfDimension(filter, 3), getSizeOfDimension(bias, 0));
438 
439     DepthwiseConv2dParam param;
440     NN_RET_CHECK(param.initialize(context));
441 
442     uint32_t batches = getSizeOfDimension(input, 0);
443     uint32_t height = getSizeOfDimension(input, param.useNchw ? 2 : 1);
444     uint32_t width = getSizeOfDimension(input, param.useNchw ? 3 : 2);
445     uint32_t channels_in = getSizeOfDimension(input, param.useNchw ? 1 : 3);
446     uint32_t channels_out = getSizeOfDimension(filter, 3);
447     uint32_t filterHeight = getSizeOfDimension(filter, 1);
448     uint32_t filterWidth = getSizeOfDimension(filter, 2);
449 
450     NN_OPS_CHECK(param.depth_multiplier * channels_in == channels_out);
451     int32_t effectiveFilterWidth = (filterWidth - 1) * param.dilation_width_factor + 1;
452     int32_t effectiveFilterHeight = (filterHeight - 1) * param.dilation_height_factor + 1;
453     NN_RET_CHECK_GT(effectiveFilterWidth, param.padding_left);
454     NN_RET_CHECK_GT(effectiveFilterWidth, param.padding_right);
455     NN_RET_CHECK_GT(effectiveFilterHeight, param.padding_top);
456     NN_RET_CHECK_GT(effectiveFilterHeight, param.padding_bottom);
457 
458     uint32_t outHeight =
459             computeOutSize(height, filterHeight, param.stride_height, param.dilation_height_factor,
460                            param.padding_top, param.padding_bottom);
461     uint32_t outWidth =
462             computeOutSize(width, filterWidth, param.stride_width, param.dilation_width_factor,
463                            param.padding_left, param.padding_right);
464 
465     Shape output = context->getOutputShape(kOutputTensor);
466     output.type = input.type;
467     if (param.useNchw) {
468         output.dimensions = {batches, channels_out, outHeight, outWidth};
469     } else {
470         output.dimensions = {batches, outHeight, outWidth, channels_out};
471     }
472     return context->setOutputShape(kOutputTensor, output);
473 }
474 
execute(IOperationExecutionContext * context)475 bool execute(IOperationExecutionContext* context) {
476     // Bypass execution in the case of zero-sized input.
477     if (getNumberOfElements(context->getOutputShape(kOutputTensor)) == 0) return true;
478     DepthwiseConv2dParam param;
479     NN_RET_CHECK(param.initialize(context));
480     switch (context->getInputType(kInputTensor)) {
481         case OperandType::TENSOR_FLOAT32:
482             return depthwiseConv(context->getInputBuffer<float>(kInputTensor),
483                                  context->getInputShape(kInputTensor),
484                                  context->getInputBuffer<float>(kFilterTensor),
485                                  context->getInputShape(kFilterTensor),
486                                  context->getInputBuffer<float>(kBiasTensor),
487                                  context->getInputShape(kBiasTensor), param.padding_left,
488                                  param.padding_right, param.padding_top, param.padding_bottom,
489                                  param.stride_width, param.stride_height,
490                                  param.dilation_width_factor, param.dilation_height_factor,
491                                  param.depth_multiplier, param.activation, param.useNchw,
492                                  context->getOutputBuffer<float>(kOutputTensor),
493                                  context->getOutputShape(kOutputTensor));
494         case OperandType::TENSOR_FLOAT16:
495             return depthwiseConv(context->getInputBuffer<_Float16>(kInputTensor),
496                                  context->getInputShape(kInputTensor),
497                                  context->getInputBuffer<_Float16>(kFilterTensor),
498                                  context->getInputShape(kFilterTensor),
499                                  context->getInputBuffer<_Float16>(kBiasTensor),
500                                  context->getInputShape(kBiasTensor), param.padding_left,
501                                  param.padding_right, param.padding_top, param.padding_bottom,
502                                  param.stride_width, param.stride_height,
503                                  param.dilation_width_factor, param.dilation_height_factor,
504                                  param.depth_multiplier, param.activation, param.useNchw,
505                                  context->getOutputBuffer<_Float16>(kOutputTensor),
506                                  context->getOutputShape(kOutputTensor));
507         case OperandType::TENSOR_QUANT8_ASYMM:
508             if (context->getInputType(kFilterTensor) ==
509                 OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
510                 return depthwiseConvQuant8PerChannel(
511                         context->getInputBuffer<uint8_t>(kInputTensor),
512                         context->getInputShape(kInputTensor),
513                         context->getInputBuffer<int8_t>(kFilterTensor),
514                         context->getInputShape(kFilterTensor),
515                         std::get<Operand::SymmPerChannelQuantParams>(
516                                 context->getInputExtraParams(kFilterTensor))
517                                 .scales.data(),
518                         context->getInputBuffer<int32_t>(kBiasTensor),
519                         context->getInputShape(kBiasTensor), param.padding_left,
520                         param.padding_right, param.padding_top, param.padding_bottom,
521                         param.stride_width, param.stride_height, param.dilation_width_factor,
522                         param.dilation_height_factor, param.depth_multiplier, param.activation,
523                         param.useNchw, context->getOutputBuffer<uint8_t>(kOutputTensor),
524                         context->getOutputShape(kOutputTensor));
525             } else if (context->getInputType(kFilterTensor) == OperandType::TENSOR_QUANT8_ASYMM) {
526                 return depthwiseConv(context->getInputBuffer<uint8_t>(kInputTensor),
527                                      context->getInputShape(kInputTensor),
528                                      context->getInputBuffer<uint8_t>(kFilterTensor),
529                                      context->getInputShape(kFilterTensor),
530                                      context->getInputBuffer<int32_t>(kBiasTensor),
531                                      context->getInputShape(kBiasTensor), param.padding_left,
532                                      param.padding_right, param.padding_top, param.padding_bottom,
533                                      param.stride_width, param.stride_height,
534                                      param.dilation_width_factor, param.dilation_height_factor,
535                                      param.depth_multiplier, param.activation, param.useNchw,
536                                      context->getOutputBuffer<uint8_t>(kOutputTensor),
537                                      context->getOutputShape(kOutputTensor));
538             } else {
539                 NN_RET_CHECK_FAIL() << "Unsupported filter type for operation " << kOperationName;
540             }
541         case OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
542             if (context->getInputType(kFilterTensor) ==
543                 OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
544                 return depthwiseConvQuant8PerChannel(
545                         context->getInputBuffer<int8_t>(kInputTensor),
546                         context->getInputShape(kInputTensor),
547                         context->getInputBuffer<int8_t>(kFilterTensor),
548                         context->getInputShape(kFilterTensor),
549                         std::get<Operand::SymmPerChannelQuantParams>(
550                                 context->getInputExtraParams(kFilterTensor))
551                                 .scales.data(),
552                         context->getInputBuffer<int32_t>(kBiasTensor),
553                         context->getInputShape(kBiasTensor), param.padding_left,
554                         param.padding_right, param.padding_top, param.padding_bottom,
555                         param.stride_width, param.stride_height, param.dilation_width_factor,
556                         param.dilation_height_factor, param.depth_multiplier, param.activation,
557                         param.useNchw, context->getOutputBuffer<int8_t>(kOutputTensor),
558                         context->getOutputShape(kOutputTensor));
559             } else if (context->getInputType(kFilterTensor) ==
560                        OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
561                 return depthwiseConv(context->getInputBuffer<int8_t>(kInputTensor),
562                                      context->getInputShape(kInputTensor),
563                                      context->getInputBuffer<int8_t>(kFilterTensor),
564                                      context->getInputShape(kFilterTensor),
565                                      context->getInputBuffer<int32_t>(kBiasTensor),
566                                      context->getInputShape(kBiasTensor), param.padding_left,
567                                      param.padding_right, param.padding_top, param.padding_bottom,
568                                      param.stride_width, param.stride_height,
569                                      param.dilation_width_factor, param.dilation_height_factor,
570                                      param.depth_multiplier, param.activation, param.useNchw,
571                                      context->getOutputBuffer<int8_t>(kOutputTensor),
572                                      context->getOutputShape(kOutputTensor));
573             } else {
574                 NN_RET_CHECK_FAIL() << "Unsupported filter type for operation " << kOperationName;
575             }
576         default:
577             NN_RET_CHECK_FAIL() << "Unsupported tensor type for operation " << kOperationName;
578     }
579 }
580 #endif  // NN_INCLUDE_CPU_IMPLEMENTATION
581 
582 }  // namespace depthwise_conv_2d
583 
584 NN_REGISTER_OPERATION_DEFAULT_VALIDATION(DEPTHWISE_CONV_2D, depthwise_conv_2d::prepare,
585                                          depthwise_conv_2d::execute, .allowZeroSizedInput = true);
586 
587 }  // namespace nn
588 }  // namespace android
589