diff --git a/src/layer/convolutiondepthwise.cpp b/src/layer/convolutiondepthwise.cpp index 206785cb7..a31e03026 100644 --- a/src/layer/convolutiondepthwise.cpp +++ b/src/layer/convolutiondepthwise.cpp @@ -760,6 +760,9 @@ int ConvolutionDepthWise::forward(const VkMat& bottom_blob, VkMat& top_blob, VkC return 0; } + // record + cmd.record_prepare_compute_barrier(top_blob); + const int channels_g = channels / group; const int num_output_g = num_output / packing / group; diff --git a/src/layer/deconvolutiondepthwise.cpp b/src/layer/deconvolutiondepthwise.cpp index 367893625..19773c1a4 100644 --- a/src/layer/deconvolutiondepthwise.cpp +++ b/src/layer/deconvolutiondepthwise.cpp @@ -13,6 +13,7 @@ // specific language governing permissions and limitations under the License. #include "deconvolutiondepthwise.h" +#include "layer_type.h" namespace ncnn { @@ -22,6 +23,22 @@ DeconvolutionDepthWise::DeconvolutionDepthWise() { one_blob_only = true; support_inplace = false; + support_vulkan = true; + +#if NCNN_VULKAN + pipeline_deconvolutiondepthwise = 0; + pipeline_deconvolutiondepthwise_pack4 = 0; +#endif // NCNN_VULKAN +} + +DeconvolutionDepthWise::~DeconvolutionDepthWise() +{ +#if NCNN_VULKAN + for (int i=0; i<(int)deconvolution_group_ops.size(); i++) + delete deconvolution_group_ops[i]; + + deconvolution_group_ops.clear(); +#endif // NCNN_VULKAN } int DeconvolutionDepthWise::load_param(const ParamDict& pd) @@ -55,6 +72,65 @@ int DeconvolutionDepthWise::load_model(const ModelBin& mb) return -100; } +#if NCNN_VULKAN + const int maxk = kernel_w * kernel_h; + int channels = (weight_data_size / group) / maxk / (num_output / group) * group; + + // group deconvolution + if (!(channels == group && group == num_output)) + { + // create Deconvolution op for each group + + for (int i=0; i<(int)deconvolution_group_ops.size(); i++) + delete deconvolution_group_ops[i]; + + deconvolution_group_ops.clear(); + + const int channels_g = channels / group; + const int num_output_g = num_output / group; + + deconvolution_group_ops.resize(group); + + for (int g=0; gvkdev = vkdev; + + // set param + ncnn::ParamDict pd; + pd.set(0, num_output_g);// num_output + pd.set(1, kernel_w); + pd.set(11, kernel_h); + pd.set(2, dilation_w); + pd.set(12, dilation_h); + pd.set(3, stride_w); + pd.set(13, stride_h); + pd.set(4, 0);// pad_w + pd.set(14, 0);// pad_h + pd.set(5, bias_term); + pd.set(6, maxk * channels_g * num_output_g);// weight_data_size + + pd.use_vulkan_compute = 1; + + op->load_param(pd); + + // set weights + ncnn::Mat weights[2]; + weights[0] = weight_data_g; + weights[1] = bias_data_g; + + op->load_model(ModelBinFromMatArray(weights)); + + deconvolution_group_ops[g] = op; + } + } +#endif // NCNN_VULKAN + return 0; } @@ -212,4 +288,204 @@ int DeconvolutionDepthWise::forward(const Mat& bottom_blob, Mat& top_blob, const return 0; } +#if NCNN_VULKAN +int DeconvolutionDepthWise::upload_model(VkTransfer& cmd) +{ + const int maxk = kernel_w * kernel_h; + int channels = (weight_data_size / group) / maxk / (num_output / group) * group; + + Mat weight_data_transposed(weight_data.w); + { + float* pt = weight_data_transposed; + const float* p = weight_data; + + for (int i=0; i<(channels/group)*(num_output/group)*group; i++) + { + for (int k=0; kupload_model(cmd); + } + + return 0; +} + +int DeconvolutionDepthWise::create_pipeline() +{ + const int maxk = kernel_w * kernel_h; + int channels = (weight_data_size / group) / maxk / (num_output / group) * group; + + // depth-wise + if (channels == group && group == num_output) + { + pipeline_deconvolutiondepthwise = new Pipeline(vkdev); + pipeline_deconvolutiondepthwise->set_optimal_local_size_xyz(32, 32, num_output); + + std::vector specializations(8); + specializations[0].i = kernel_w; + specializations[1].i = kernel_h; + specializations[2].i = dilation_w; + specializations[3].i = dilation_h; + specializations[4].i = stride_w; + specializations[5].i = stride_h; + specializations[6].i = bias_term; + specializations[7].i = group; + + pipeline_deconvolutiondepthwise->create("deconvolutiondepthwise", specializations, 4, 10); + + // pack4 + if (num_output % 4 == 0) + { + pipeline_deconvolutiondepthwise_pack4 = new Pipeline(vkdev); + pipeline_deconvolutiondepthwise_pack4->set_optimal_local_size_xyz(32, 32, std::max(1, num_output / 4)); + pipeline_deconvolutiondepthwise_pack4->create("deconvolutiondepthwise_pack4", specializations, 4, 10); + } + + return 0; + } + + for (int g=0; gcreate_pipeline(); + } + + return 0; +} + +int DeconvolutionDepthWise::destroy_pipeline() +{ + for (int g=0; g<(int)deconvolution_group_ops.size(); g++) + { + deconvolution_group_ops[g]->destroy_pipeline(); + } + + delete pipeline_deconvolutiondepthwise; + pipeline_deconvolutiondepthwise = 0; + + delete pipeline_deconvolutiondepthwise_pack4; + pipeline_deconvolutiondepthwise_pack4 = 0; + + return 0; +} + +int DeconvolutionDepthWise::forward(const VkMat& bottom_blob, VkMat& top_blob, VkCompute& cmd, const Option& opt) const +{ + int w = bottom_blob.w; + int h = bottom_blob.h; + int channels = bottom_blob.c; + size_t elemsize = bottom_blob.elemsize; + int packing = bottom_blob.packing; + + const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1; + const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1; + + int outw = (w - 1) * stride_w + kernel_extent_w; + int outh = (h - 1) * stride_h + kernel_extent_h; + + // TODO assert num_output % packing == 0 + + top_blob.create(outw, outh, num_output / packing, elemsize, packing, opt.blob_vkallocator, opt.staging_vkallocator); + if (top_blob.empty()) + return -100; + +// fprintf(stderr, "DeconvolutionDepthWise::forward %p %p\n", bottom_blob.buffer(), top_blob.buffer()); + + // depth-wise + if (channels == group / packing && group / packing == num_output / packing) + { + std::vector bindings(4); + bindings[0] = bottom_blob; + bindings[1] = top_blob; + bindings[2] = packing == 4 ? weight_data_gpu_pack4 : weight_data_gpu; + bindings[3] = bias_term ? (packing == 4 ? bias_data_gpu_pack4 : bias_data_gpu) : weight_data_gpu;// TODO use dummy buffer + + std::vector constants(10); + constants[0].i = bottom_blob.dims; + constants[1].i = bottom_blob.w; + constants[2].i = bottom_blob.h; + constants[3].i = bottom_blob.c; + constants[4].i = bottom_blob.cstep; + constants[5].i = top_blob.dims; + constants[6].i = top_blob.w; + constants[7].i = top_blob.h; + constants[8].i = top_blob.c; + constants[9].i = top_blob.cstep; + + const Pipeline* pipeline = packing == 4 ? pipeline_deconvolutiondepthwise_pack4 : pipeline_deconvolutiondepthwise; + + // record + cmd.record_prepare_compute_barrier(bottom_blob); + cmd.record_prepare_compute_barrier(top_blob); + cmd.record_pipeline(pipeline, bindings, constants, top_blob); + + return 0; + } + + // record + cmd.record_prepare_compute_barrier(top_blob); + + const int channels_g = channels / group; + const int num_output_g = num_output / packing / group; + + for (int g=0; gforward(bottom_blob_bordered_g, top_blob_g, cmd, opt_g); + } + + return 0; +} +#endif // NCNN_VULKAN + } // namespace ncnn diff --git a/src/layer/deconvolutiondepthwise.h b/src/layer/deconvolutiondepthwise.h index 674cb2c47..8cab20802 100644 --- a/src/layer/deconvolutiondepthwise.h +++ b/src/layer/deconvolutiondepthwise.h @@ -23,6 +23,7 @@ class DeconvolutionDepthWise : public Layer { public: DeconvolutionDepthWise(); + ~DeconvolutionDepthWise(); virtual int load_param(const ParamDict& pd); @@ -30,6 +31,15 @@ public: virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const; +#if NCNN_VULKAN + virtual int upload_model(VkTransfer& cmd); + + virtual int create_pipeline(); + virtual int destroy_pipeline(); + + virtual int forward(const VkMat& bottom_blob, VkMat& top_blob, VkCompute& cmd, const Option& opt) const; +#endif // NCNN_VULKAN + public: // param int num_output; @@ -49,6 +59,20 @@ public: // model Mat weight_data; Mat bias_data; + +#if NCNN_VULKAN + VkMat weight_data_gpu; + VkMat bias_data_gpu; + + std::vector deconvolution_group_ops; + + Pipeline* pipeline_deconvolutiondepthwise; + + VkMat weight_data_gpu_pack4; + VkMat bias_data_gpu_pack4; + Pipeline* pipeline_deconvolutiondepthwise_pack4; +#endif // NCNN_VULKAN + }; } // namespace ncnn diff --git a/src/layer/shader/deconvolutiondepthwise.comp b/src/layer/shader/deconvolutiondepthwise.comp new file mode 100644 index 000000000..1e81c4c56 --- /dev/null +++ b/src/layer/shader/deconvolutiondepthwise.comp @@ -0,0 +1,105 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#version 450 + +layout (constant_id = 0) const int kernel_w = 1; +layout (constant_id = 1) const int kernel_h = 1; +layout (constant_id = 2) const int dilation_w = 1; +layout (constant_id = 3) const int dilation_h = 1; +layout (constant_id = 4) const int stride_w = 1; +layout (constant_id = 5) const int stride_h = 1; +layout (constant_id = 6) const int bias_term = 0; +layout (constant_id = 7) const int group = 1; + +layout (local_size_x_id = 233) in; +layout (local_size_y_id = 234) in; +layout (local_size_z_id = 235) in; + +layout (binding = 0) readonly buffer bottom_blob { float bottom_blob_data[]; }; +layout (binding = 1) writeonly buffer top_blob { float top_blob_data[]; }; +layout (binding = 2) readonly buffer weight_blob { float weight_data[]; }; +layout (binding = 3) readonly buffer bias_blob { float bias_data[]; }; + +layout (push_constant) uniform parameter +{ + int dims; + int w; + int h; + int c; + int cstep; + + int outdims; + int outw; + int outh; + int outc; + int outcstep; +} p; + +void main() +{ + int gx = int(gl_GlobalInvocationID.x); + int gy = int(gl_GlobalInvocationID.y); + int gz = int(gl_GlobalInvocationID.z); + + if (gx >= p.outw || gy >= p.outh || gz >= p.outc) + return; + + float sum; + + if (bias_term == 1) + { + sum = bias_data[gz]; + } + else + { + sum = 0.f; + } + + const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1; + const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1; + + // depth-wise deconvolution + int v_offset_0 = gz * p.cstep; + int w_offset_0 = gz * kernel_w * kernel_h; + + for (int y = 0; y < kernel_h; y++) + { + int sys = (gy + y * dilation_h - (kernel_extent_h - 1)); + if (sys % stride_h != 0) + continue; + + int sy = sys / stride_h; + if (sy < 0 || sy >= p.h) + continue; + + for (int x = 0; x < kernel_w; x++) + { + int sxs = (gx + x * dilation_w - (kernel_extent_w - 1)); + if (sxs % stride_w != 0) + continue; + + int sx = sxs / stride_w; + if (sx < 0 || sx >= p.w) + continue; + + int v_offset = v_offset_0 + sy * p.w + sx; + int w_offset = w_offset_0 + y * kernel_w + x; + + sum += weight_data[w_offset] * bottom_blob_data[v_offset]; + } + } + + top_blob_data[gz * p.outcstep + gy * p.outw + gx] = sum; +} diff --git a/src/layer/shader/deconvolutiondepthwise_pack4.comp b/src/layer/shader/deconvolutiondepthwise_pack4.comp new file mode 100644 index 000000000..8dff3851a --- /dev/null +++ b/src/layer/shader/deconvolutiondepthwise_pack4.comp @@ -0,0 +1,109 @@ +// Tencent is pleased to support the open source community by making ncnn available. +// +// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#version 450 + +layout (constant_id = 0) const int kernel_w = 1; +layout (constant_id = 1) const int kernel_h = 1; +layout (constant_id = 2) const int dilation_w = 1; +layout (constant_id = 3) const int dilation_h = 1; +layout (constant_id = 4) const int stride_w = 1; +layout (constant_id = 5) const int stride_h = 1; +layout (constant_id = 6) const int bias_term = 0; +layout (constant_id = 7) const int group = 1; + +layout (local_size_x_id = 233) in; +layout (local_size_y_id = 234) in; +layout (local_size_z_id = 235) in; + +layout (binding = 0) readonly buffer bottom_blob { vec4 bottom_blob_data[]; }; +layout (binding = 1) writeonly buffer top_blob { vec4 top_blob_data[]; }; +layout (binding = 2) readonly buffer weight_blob { vec4 weight_data[]; }; +layout (binding = 3) readonly buffer bias_blob { vec4 bias_data[]; }; + +layout (push_constant) uniform parameter +{ + int dims; + int w; + int h; + int c; + int cstep; + + int outdims; + int outw; + int outh; + int outc; + int outcstep; +} p; + +void main() +{ + int gx = int(gl_GlobalInvocationID.x); + int gy = int(gl_GlobalInvocationID.y); + int gz = int(gl_GlobalInvocationID.z); + + if (gx >= p.outw || gy >= p.outh || gz >= p.outc) + return; + + vec4 sum; + + if (bias_term == 1) + { + sum = bias_data[gz]; + } + else + { + sum = vec4(0.f); + } + + const int kernel_extent_w = dilation_w * (kernel_w - 1) + 1; + const int kernel_extent_h = dilation_h * (kernel_h - 1) + 1; + + // depth-wise deconvolution + int v_offset_0 = gz * p.cstep; + int w_offset_0 = gz * kernel_w * kernel_h; + + for (int y = 0; y < kernel_h; y++) + { + int sys = (gy + y * dilation_h - (kernel_extent_h - 1)); + if (sys % stride_h != 0) + continue; + + int sy = sys / stride_h; + if (sy < 0 || sy >= p.h) + continue; + + for (int x = 0; x < kernel_w; x++) + { + int sxs = (gx + x * dilation_w - (kernel_extent_w - 1)); + if (sxs % stride_w != 0) + continue; + + int sx = sxs / stride_w; + if (sx < 0 || sx >= p.w) + continue; + + int v_offset = v_offset_0 + sy * p.w + sx; + int w_offset = w_offset_0 + y * kernel_w + x; + + vec4 v = bottom_blob_data[v_offset]; + + vec4 k = weight_data[w_offset]; + + sum += v * k; + } + } + + top_blob_data[gz * p.outcstep + gy * p.outw + gx] = sum; +}