#include "deconvolution_riscv.h"
#include "cpu.h"
#include "layer_type.h"
#if __riscv_vector
#include <riscv_vector.h>
#endif
#include "riscv_activation.h"
#include "riscv_usability.h"
namespace ncnn {
#if __riscv_vector
#include "deconvolution_packn.h"
#include "deconvolution_pack1ton.h"
#include "deconvolution_packnto1.h"
#if __riscv_zfh
#include "deconvolution_fp16s.h"
#include "deconvolution_packn_fp16s.h"
#include "deconvolution_pack1ton_fp16s.h"
#include "deconvolution_packnto1_fp16s.h"
#endif
#endif
Deconvolution_riscv::Deconvolution_riscv()
{
#if __riscv_vector
support_packing = true;
#if __riscv_zfh
support_fp16_storage = true;
#endif
#endif
}
int Deconvolution_riscv::create_pipeline(const Option& opt)
{
if (dynamic_weight)
return 0;
#if __riscv_vector && __riscv_zfh
if (opt.use_fp16_storage)
{
return create_pipeline_fp16s(opt);
}
#endif
#if __riscv_vector
const int packn = csrr_vlenb() / 4;
#endif
const int maxk = kernel_w * kernel_h;
int num_input = weight_data_size / maxk / num_output;
Mat weight_data_transposed(weight_data.w);
{
float* pt = weight_data_transposed;
const float* p = weight_data;
for (int i = 0; i < num_input * num_output; i++)
{
for (int k = 0; k < maxk; k++)
{
pt[maxk - 1 - k] = p[k];
}
p += maxk;
pt += maxk;
}
}
int elempack = 1;
int out_elempack = 1;
#if __riscv_vector
if (opt.use_packing_layout)
{
elempack = num_input % packn == 0 ? packn : 1;
out_elempack = num_output % packn == 0 ? packn : 1;
}
#endif
{
Mat weight_data_r2 = weight_data_transposed.reshape(maxk, num_input, num_output);
weight_data_tm.create(maxk, num_input / elempack, num_output / out_elempack, (size_t)4u * elempack * out_elempack, elempack * out_elempack);
for (int q = 0; q + (out_elempack - 1) < num_output; q += out_elempack)
{
float* g00 = weight_data_tm.channel(q / out_elempack);
for (int p = 0; p + (elempack - 1) < num_input; p += elempack)
{
for (int k = 0; k < maxk; k++)
{
for (int i = 0; i < elempack; i++)
{
for (int j = 0; j < out_elempack; j++)
{
const float* k00 = weight_data_r2.channel(q + j).row(p + i);
g00[0] = k00[k];
g00++;
}
}
}
}
}
}
#if __riscv_vector
if (elempack == packn && out_elempack == packn)
{
}
if (elempack == 1 && out_elempack == packn)
{
}
if (elempack == packn && out_elempack == 1)
{
}
#endif
if (elempack == 1 && out_elempack == 1)
{
}
if (opt.lightmode)
weight_data.release();
return 0;
}
int Deconvolution_riscv::destroy_pipeline(const Option& opt)
{
return 0;
}
int Deconvolution_riscv::forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
{
int elembits = bottom_blob.elembits();
#if __riscv_vector && __riscv_zfh
if (opt.use_fp16_storage && elembits == 16)
{
if (opt.use_fp16_arithmetic)
return forward_fp16sa(bottom_blob, top_blob, opt);
else
return forward_fp16s(bottom_blob, top_blob, opt);
}
#endif
#if __riscv_vector
const int packn = csrr_vlenb() / 4;
#endif
int w = bottom_blob.w;
int h = bottom_blob.h;
int channels = bottom_blob.c;
size_t elemsize = bottom_blob.elemsize;
int elempack = bottom_blob.elempack;
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 + output_pad_right;
int outh = (h - 1) * stride_h + kernel_extent_h + output_pad_bottom;
int out_elempack = 1;
#if __riscv_vector
if (opt.use_packing_layout)
{
out_elempack = num_output % packn == 0 ? packn : 1;
}
#endif
size_t out_elemsize = elemsize / elempack * out_elempack;
Mat top_blob_bordered;
if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0 || (output_w > 0 && output_h > 0))
{
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.workspace_allocator);
}
else
{
top_blob_bordered = top_blob;
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.blob_allocator);
}
if (top_blob_bordered.empty())
return -100;
const int maxk = kernel_w * kernel_h;
#if __riscv_vector
if (elempack == packn && out_elempack == packn)
{
{
deconvolution_packn_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == 1 && out_elempack == packn)
{
{
deconvolution_pack1ton_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == packn && out_elempack == 1)
{
{
deconvolution_packnto1_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
#endif
if (elempack == 1 && out_elempack == 1)
{
{
#pragma omp parallel for num_threads(opt.num_threads)
for (int p = 0; p < num_output; p++)
{
float* outptr = top_blob_bordered.channel(p);
for (int i = 0; i < outh; i++)
{
for (int j = 0; j < outw; j++)
{
float sum = 0.f;
if (bias_term)
{
sum = bias_data[p];
}
const float* kptr = (const float*)weight_data_tm.channel(p);
for (int q = 0; q < channels; q++)
{
const Mat m = bottom_blob.channel(q);
for (int y = 0; y < kernel_h; y++)
{
int sys = (i + y * dilation_h - (kernel_extent_h - 1));
if (sys < 0 || sys % stride_h != 0)
continue;
int sy = sys / stride_h;
if (sy >= h)
continue;
const float* sptr = m.row(sy);
for (int x = 0; x < kernel_w; x++)
{
int sxs = (j + x * dilation_w - (kernel_extent_w - 1));
if (sxs < 0 || sxs % stride_w != 0)
continue;
int sx = sxs / stride_w;
if (sx >= w)
continue;
float val = sptr[sx];
int k = y * kernel_w + x;
float w = kptr[k];
sum += val * w;
}
}
kptr += maxk;
}
sum = activation_ss(sum, activation_type, activation_params);
outptr[j] = sum;
}
outptr += outw;
}
}
}
}
cut_padding(top_blob_bordered, top_blob, opt);
if (top_blob.empty())
return -100;
return 0;
}
int Deconvolution_riscv::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const
{
const Mat& bottom_blob = bottom_blobs[0];
const Mat& _weight_data = bottom_blobs[1];
Mat& top_blob = top_blobs[0];
const int _num_input = bottom_blob.c * bottom_blob.elempack;
const int _kernel_w = _weight_data.w;
const int _kernel_h = _weight_data.h;
const int _num_output = _weight_data.d * 1;
Mat weight_data_flattened;
flatten(_weight_data, weight_data_flattened, opt);
if (weight_data_flattened.empty())
return -100;
#if NCNN_RVV
if (opt.use_fp16_storage && cpu_support_riscv_v() && cpu_support_riscv_zfh() && weight_data_flattened.elembits() == 16)
{
Mat weight_data_flattened_fp32;
cast_float16_to_float32(weight_data_flattened, weight_data_flattened_fp32, opt);
weight_data_flattened = weight_data_flattened_fp32;
}
#endif
weight_data_flattened.w *= weight_data_flattened.elempack;
weight_data_flattened.elemsize /= weight_data_flattened.elempack;
weight_data_flattened.elempack = 1;
Mat weight_data_transposed;
{
weight_data_transposed.create(_kernel_w * _kernel_h * _num_output * _num_input / 1, 4u, opt.workspace_allocator);
if (weight_data_transposed.empty())
return -100;
const int outch_g = _num_output / 1;
const int inch_g = _num_input / 1;
const int maxk = _kernel_h * _kernel_w;
for (int g = 0; g < 1; g++)
{
float* wg2 = (float*)weight_data_transposed + g * outch_g * inch_g * maxk;
const float* wg = (const float*)weight_data_flattened + g * inch_g * outch_g * maxk;
for (int i = 0; i < outch_g; i++)
{
for (int j = 0; j < inch_g; j++)
{
for (int k = 0; k < maxk; k++)
{
wg2[(i * inch_g + j) * maxk + k] = wg[(j * outch_g + i) * maxk + k];
}
}
}
}
}
Mat bias_data_flattened;
if (bias_term)
{
const Mat& _bias_data = bottom_blobs[2];
flatten(_bias_data, bias_data_flattened, opt);
if (bias_data_flattened.empty())
return -100;
#if NCNN_RVV
if (opt.use_fp16_storage && cpu_support_riscv_v() && cpu_support_riscv_zfh() && bias_data_flattened.elembits() == 16)
{
Mat bias_data_flattened_fp32;
cast_float16_to_float32(bias_data_flattened, bias_data_flattened_fp32, opt);
bias_data_flattened = bias_data_flattened_fp32;
}
#endif
bias_data_flattened.w *= bias_data_flattened.elempack;
bias_data_flattened.elemsize /= bias_data_flattened.elempack;
bias_data_flattened.elempack = 1;
}
ncnn::Layer* op = ncnn::create_layer_cpu(ncnn::LayerType::Deconvolution);
ncnn::ParamDict pd;
pd.set(0, _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, pad_left);
pd.set(15, pad_right);
pd.set(14, pad_top);
pd.set(16, pad_bottom);
pd.set(18, output_pad_right);
pd.set(19, output_pad_bottom);
pd.set(20, output_w);
pd.set(21, output_h);
pd.set(5, bias_term);
pd.set(6, weight_data_transposed.w);
pd.set(9, activation_type);
pd.set(10, activation_params);
op->load_param(pd);
ncnn::Mat weights[2];
weights[0] = weight_data_transposed;
weights[1] = bias_data_flattened;
op->load_model(ncnn::ModelBinFromMatArray(weights));
op->create_pipeline(opt);
op->forward(bottom_blob, top_blob, opt);
op->destroy_pipeline(opt);
delete op;
return 0;
}
#if __riscv_vector && __riscv_zfh
int Deconvolution_riscv::create_pipeline_fp16s(const Option& opt)
{
const int packn = csrr_vlenb() / 2;
const int maxk = kernel_w * kernel_h;
const int num_input = weight_data_size / maxk / num_output;
int elempack = 1;
int out_elempack = 1;
if (opt.use_packing_layout)
{
elempack = num_input % packn == 0 ? packn : 1;
out_elempack = num_output % packn == 0 ? packn : 1;
}
Mat weight_data_transposed(weight_data.w);
{
float* pt = weight_data_transposed;
const float* p = weight_data;
for (int i = 0; i < num_input * num_output; i++)
{
for (int k = 0; k < maxk; k++)
{
pt[maxk - 1 - k] = p[k];
}
p += maxk;
pt += maxk;
}
}
{
Mat weight_data_r2 = weight_data_transposed.reshape(maxk, num_input, num_output);
weight_data_tm.create(maxk, num_input / elempack, num_output / out_elempack, (size_t)2u * elempack * out_elempack, elempack * out_elempack);
for (int q = 0; q + (out_elempack - 1) < num_output; q += out_elempack)
{
__fp16* g00 = weight_data_tm.channel(q / out_elempack);
for (int p = 0; p + (elempack - 1) < num_input; p += elempack)
{
for (int k = 0; k < maxk; k++)
{
for (int i = 0; i < elempack; i++)
{
for (int j = 0; j < out_elempack; j++)
{
const float* k00 = weight_data_r2.channel(q + j).row(p + i);
g00[0] = (__fp16)k00[k];
g00++;
}
}
}
}
}
}
if (elempack == packn && out_elempack == packn)
{
}
if (elempack == 1 && out_elempack == packn)
{
}
if (elempack == packn && out_elempack == 1)
{
}
if (elempack == 1 && out_elempack == 1)
{
}
ncnn::cast_float32_to_float16(bias_data, bias_data_fp16, opt);
if (opt.lightmode)
weight_data.release();
return 0;
}
int Deconvolution_riscv::forward_fp16s(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
{
const int packn = csrr_vlenb() / 2;
int w = bottom_blob.w;
int h = bottom_blob.h;
int channels = bottom_blob.c;
size_t elemsize = bottom_blob.elemsize;
int elempack = bottom_blob.elempack;
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 + output_pad_right;
int outh = (h - 1) * stride_h + kernel_extent_h + output_pad_bottom;
int out_elempack = (opt.use_packing_layout && num_output % packn == 0) ? packn : 1;
size_t out_elemsize = elemsize / elempack * out_elempack;
Mat top_blob_bordered;
if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0 || (output_w > 0 && output_h > 0))
{
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.workspace_allocator);
}
else
{
top_blob_bordered = top_blob;
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.blob_allocator);
}
if (top_blob_bordered.empty())
return -100;
if (elempack == packn && out_elempack == packn)
{
{
deconvolution_packn_fp16s_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == 1 && out_elempack == packn)
{
{
deconvolution_pack1ton_fp16s_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == packn && out_elempack == 1)
{
{
deconvolution_packnto1_fp16s_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == 1 && out_elempack == 1)
{
{
deconvolution_fp16s(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
cut_padding(top_blob_bordered, top_blob, opt);
if (top_blob.empty())
return -100;
return 0;
}
int Deconvolution_riscv::forward_fp16sa(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const
{
const int packn = csrr_vlenb() / 2;
int w = bottom_blob.w;
int h = bottom_blob.h;
int channels = bottom_blob.c;
size_t elemsize = bottom_blob.elemsize;
int elempack = bottom_blob.elempack;
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 + output_pad_right;
int outh = (h - 1) * stride_h + kernel_extent_h + output_pad_bottom;
int out_elempack = (opt.use_packing_layout && num_output % packn == 0) ? packn : 1;
size_t out_elemsize = elemsize / elempack * out_elempack;
Mat top_blob_bordered;
if (pad_left > 0 || pad_right > 0 || pad_top > 0 || pad_bottom > 0 || (output_w > 0 && output_h > 0))
{
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.workspace_allocator);
}
else
{
top_blob_bordered = top_blob;
top_blob_bordered.create(outw, outh, num_output / out_elempack, out_elemsize, out_elempack, opt.blob_allocator);
}
if (top_blob_bordered.empty())
return -100;
if (elempack == packn && out_elempack == packn)
{
{
deconvolution_packn_fp16sa_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data_fp16, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == 1 && out_elempack == packn)
{
{
deconvolution_pack1ton_fp16sa_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data_fp16, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == packn && out_elempack == 1)
{
{
deconvolution_packnto1_fp16sa_rvv(bottom_blob, top_blob_bordered, weight_data_tm, bias_data_fp16, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
if (elempack == 1 && out_elempack == 1)
{
{
deconvolution_fp16s(bottom_blob, top_blob_bordered, weight_data_tm, bias_data, kernel_w, kernel_h, dilation_w, dilation_h, stride_w, stride_h, activation_type, activation_params, opt);
}
}
cut_padding(top_blob_bordered, top_blob, opt);
if (top_blob.empty())
return -100;
return 0;
}
#endif
}