315 lines
16 KiB
Common Lisp
315 lines
16 KiB
Common Lisp
#ifdef MNN_SUPPORT_FP16
|
||
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
|
||
#endif
|
||
|
||
__constant sampler_t SAMPLER = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;
|
||
#define GLOBAL_SIZE_2_DIMS __private const int global_size_dim0, __private const int global_size_dim1,
|
||
#define DEAL_NON_UNIFORM_DIM2(input1, input2) \
|
||
if (input1 >= global_size_dim0 || input2 >= global_size_dim1) { \
|
||
return; \
|
||
}
|
||
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT8
|
||
// convert kernel : from int8 buffer(oihw) to int8 image(oc/4 h w , ic oc4)
|
||
__kernel void conv2d_filter_buffer_to_nc4hw4_buffer_int8(GLOBAL_SIZE_2_DIMS
|
||
__global const char *input_ptr,
|
||
__private const int output_channel,
|
||
__private const int2 kernel_shape,
|
||
__private const int ic_h_w_size,
|
||
__private const int height_width_size,
|
||
__global char *output) {
|
||
int image_width_idx = get_global_id(0); // ic
|
||
int image_height_idx = get_global_id(1); // oc/4 h w
|
||
|
||
DEAL_NON_UNIFORM_DIM2(image_width_idx, image_height_idx);
|
||
|
||
const int input_channel_4_idx = image_width_idx;
|
||
const int output_channel_4_idx = (image_height_idx / height_width_size) * 4;
|
||
const int height_width_idx = image_height_idx % height_width_size;
|
||
const int buffer_height_idx = height_width_idx / kernel_shape.y;
|
||
const int buffer_width_idx = height_width_idx % kernel_shape.y;
|
||
|
||
const int buffer_offset = output_channel_4_idx * ic_h_w_size + input_channel_4_idx * height_width_size +
|
||
buffer_height_idx * kernel_shape.y + buffer_width_idx;
|
||
|
||
char4 output_values = 0;
|
||
if (output_channel_4_idx < output_channel) {
|
||
const int remain_channel = output_channel - output_channel_4_idx;
|
||
if (remain_channel >= 4) {
|
||
int offset = buffer_offset;
|
||
output_values.x = (char)(*(input_ptr + offset));
|
||
offset = mad24(1, ic_h_w_size, offset);
|
||
output_values.y = (char)(*(input_ptr + offset));
|
||
offset += ic_h_w_size;
|
||
output_values.z = (char)(*(input_ptr + offset));
|
||
offset += ic_h_w_size;
|
||
output_values.w = (char)(*(input_ptr + offset));
|
||
} else if (remain_channel == 3) {
|
||
int offset = buffer_offset;
|
||
output_values.x = (char)(*(input_ptr + offset));
|
||
offset = mad24(1, ic_h_w_size, offset);
|
||
output_values.y = (char)(*(input_ptr + offset));
|
||
offset += ic_h_w_size;
|
||
output_values.z = (char)(*(input_ptr + offset));
|
||
|
||
} else if (remain_channel == 2) {
|
||
int offset = buffer_offset;
|
||
output_values.x = (char)(*(input_ptr + offset));
|
||
offset = mad24(1, ic_h_w_size, offset);
|
||
output_values.y = (char)(*(input_ptr + offset));
|
||
} else if (remain_channel == 1) {
|
||
int offset = buffer_offset;
|
||
output_values.x = (char)(*(input_ptr + offset));
|
||
}
|
||
}
|
||
const int out_offset = (image_width_idx*height_width_size*((output_channel+3)/4)+image_height_idx)*4;
|
||
vstore4(output_values, 0, output+out_offset);
|
||
}
|
||
#endif
|
||
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT4
|
||
// convert kernel : from int8 buffer(oihw) to int4 image(oc/4 h w , ic oc4)
|
||
__kernel void conv2d_filter_buffer_to_nc4hw4_buffer_int4(GLOBAL_SIZE_2_DIMS
|
||
__global const uchar *input_ptr,
|
||
__private const int output_channel,
|
||
__private const int2 kernel_shape,
|
||
__private const int ic_h_w_size,
|
||
__private const int height_width_size,
|
||
__global uchar *output) {
|
||
int image_width_idx = get_global_id(0); // ic
|
||
int image_height_idx = get_global_id(1); // oc/4 h w
|
||
|
||
DEAL_NON_UNIFORM_DIM2(image_width_idx, image_height_idx);
|
||
|
||
const int input_channel_4_idx = image_width_idx;
|
||
const int output_channel_4_idx = (image_height_idx / height_width_size) * 4;
|
||
const int height_width_idx = image_height_idx % height_width_size;
|
||
const int buffer_height_idx = height_width_idx / kernel_shape.y;
|
||
const int buffer_width_idx = height_width_idx % kernel_shape.y;
|
||
|
||
const int buffer_offset = output_channel_4_idx * ic_h_w_size + input_channel_4_idx * height_width_size + buffer_height_idx * kernel_shape.y + buffer_width_idx;
|
||
int index0 = buffer_offset, index1 = buffer_offset + ic_h_w_size, index2 = buffer_offset + 2 * ic_h_w_size, index3 = buffer_offset + 3 * ic_h_w_size;
|
||
|
||
uchar2 output_values_int4 = (uchar2)(0, 0);
|
||
uchar s0 = input_ptr[index0/2];
|
||
uchar s1 = output_channel_4_idx + 1 >= output_channel ? 0 : input_ptr[index1/2];
|
||
uchar s2 = output_channel_4_idx + 1 >= output_channel ? 0 : input_ptr[index2/2];
|
||
uchar s3 = output_channel_4_idx + 1 >= output_channel ? 0 : input_ptr[index3/2];
|
||
output_values_int4.x = ((index0 % 2) == 0 ? (s0 & 0xf0) : (s0 << 4)) | ((index1 % 2) == 0 ? (s1 >> 4) : (s1 & 0x0f));
|
||
output_values_int4.y = ((index2 % 2) == 0 ? (s2 & 0xf0) : (s2 << 4)) | ((index3 % 2) == 0 ? (s3 >> 4) : (s3 & 0x0f));
|
||
|
||
const int out_offset = (image_width_idx*height_width_size*((output_channel+3)/4)+image_height_idx)*2;
|
||
vstore2(output_values_int4, 0, output+out_offset);
|
||
}
|
||
#endif
|
||
|
||
__kernel void conv2d_1x1_weight_quant_image(GLOBAL_SIZE_2_DIMS
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT4
|
||
__global const uchar *input_ptr,
|
||
#else
|
||
__global const char *input_ptr,
|
||
#endif
|
||
__write_only image2d_t output,
|
||
__private const int input_channel,
|
||
__private const int output_channel) {
|
||
|
||
int x = get_global_id(0); // ic / 4
|
||
int y = get_global_id(1); // oc / 8
|
||
|
||
DEAL_NON_UNIFORM_DIM2(x, y);
|
||
const int xin = x << 2;
|
||
const int yin = y << 3;
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT4
|
||
uchar16 out = 0;
|
||
uchar *out_ptr = (uchar*)&out;
|
||
for(int i = 0; i < 4; ++i){
|
||
int index0 = yin * input_channel + xin + i;
|
||
int index1 = (yin + 1) * input_channel + xin + i;
|
||
int index2 = (yin + 2) * input_channel + xin + i;
|
||
int index3 = (yin + 3) * input_channel + xin + i;
|
||
int index4 = (yin + 4) * input_channel + xin + i;
|
||
int index5 = (yin + 5) * input_channel + xin + i;
|
||
int index6 = (yin + 6) * input_channel + xin + i;
|
||
int index7 = (yin + 7) * input_channel + xin + i;
|
||
uchar s0 = input_ptr[index0/2];
|
||
uchar s1 = input_ptr[index1/2];
|
||
uchar s2 = input_ptr[index2/2];
|
||
uchar s3 = input_ptr[index3/2];
|
||
uchar s4 = input_ptr[index4/2];
|
||
uchar s5 = input_ptr[index5/2];
|
||
uchar s6 = input_ptr[index6/2];
|
||
uchar s7 = input_ptr[index7/2];
|
||
out_ptr[i * 4] = ((index0 % 2) == 0 ? (s0 & 0xf0) : (s0 << 4)) | ((index1 % 2) == 0 ? (s1 >> 4) : (s1 & 0x0f));
|
||
out_ptr[i * 4 + 1] = ((index2 % 2) == 0 ? (s2 & 0xf0) : (s2 << 4)) | ((index3 % 2) == 0 ? (s3 >> 4) : (s3 & 0x0f));
|
||
out_ptr[i * 4 + 2] = ((index4 % 2) == 0 ? (s4 & 0xf0) : (s4 << 4)) | ((index5 % 2) == 0 ? (s5 >> 4) : (s5 & 0x0f));
|
||
out_ptr[i * 4 + 3] = ((index6 % 2) == 0 ? (s6 & 0xf0) : (s6 << 4)) | ((index7 % 2) == 0 ? (s7 >> 4) : (s7 & 0x0f));
|
||
}
|
||
write_imagei(output, (int2)(x, y), as_int4(out));
|
||
#else
|
||
const int inputOffset = yin * input_channel + xin;
|
||
char4 s0 = vload4(0, input_ptr + inputOffset);
|
||
char4 s1 = vload4(0, input_ptr + inputOffset + input_channel);
|
||
char4 s2 = vload4(0, input_ptr + inputOffset + input_channel * 2);
|
||
char4 s3 = vload4(0, input_ptr + inputOffset + input_channel * 3);
|
||
char4 s4 = vload4(0, input_ptr + inputOffset + input_channel * 4);
|
||
char4 s5 = vload4(0, input_ptr + inputOffset + input_channel * 5);
|
||
char4 s6 = vload4(0, input_ptr + inputOffset + input_channel * 6);
|
||
char4 s7 = vload4(0, input_ptr + inputOffset + input_channel * 7);
|
||
char16 out0 = (char16)(s0.s0, s1.s0, s2.s0, s3.s0, s4.s0, s5.s0, s6.s0, s7.s0, s0.s1, s1.s1, s2.s1, s3.s1, s4.s1, s5.s1, s6.s1, s7.s1);
|
||
char16 out1 = (char16)(s0.s2, s1.s2, s2.s2, s3.s2, s4.s2, s5.s2, s6.s2, s7.s2, s0.s3, s1.s3, s2.s3, s3.s3, s4.s3, s5.s3, s6.s3, s7.s3);
|
||
write_imagei(output, (int2)(x * 2, y), as_int4(out0));
|
||
write_imagei(output, (int2)(x * 2 + 1, y), as_int4(out1));
|
||
#endif
|
||
}
|
||
|
||
__kernel void conv2d_1x1_weight_quant_buffer(GLOBAL_SIZE_2_DIMS
|
||
#if defined(USE_LOW_BIT_WEIGHT_INT4)
|
||
__global const uchar *input_ptr,
|
||
#else
|
||
__global const char *input_ptr,
|
||
#endif
|
||
__global char *output_ptr,
|
||
__private const int input_channel,
|
||
__private const int output_channel) {
|
||
int x = get_global_id(0); // ic / 4
|
||
int y = get_global_id(1); // oc / 8
|
||
|
||
DEAL_NON_UNIFORM_DIM2(x, y);
|
||
const int xin = x << 2;
|
||
const int yin = y << 3;
|
||
const int outputChannelC8 = (output_channel + 7) >> 3;
|
||
const int inputChannelC4 = (input_channel + 3) >> 2;
|
||
#if defined(USE_LOW_BIT_WEIGHT_INT2)
|
||
// 2bit packed: 8 bytes per (4IC, 8OC) tile.
|
||
// For each IC i in 0..3, byte 2*i covers OC[0..3] (bits [7:6]=OC0, [5:4]=OC1, [3:2]=OC2, [1:0]=OC3),
|
||
// byte 2*i+1 covers OC[4..7]. Stored value = signed_weight + 2 in [0,3].
|
||
__global uchar* outU = (__global uchar*)output_ptr + (y * inputChannelC4 + x) * 8;
|
||
for (int i = 0; i < 4; ++i) {
|
||
for (int k = 0; k < 2; ++k) {
|
||
uchar packed = 0;
|
||
for (int j = 0; j < 4; ++j) {
|
||
int oc = yin + k * 4 + j;
|
||
int ic = xin + i;
|
||
int v = 0;
|
||
if (oc < output_channel && ic < input_channel) {
|
||
v = (int)input_ptr[oc * input_channel + ic] + 2;
|
||
}
|
||
packed |= ((uchar)(v & 3)) << (6 - j * 2);
|
||
}
|
||
outU[i * 2 + k] = packed;
|
||
}
|
||
}
|
||
#elif defined(USE_LOW_BIT_WEIGHT_INT3)
|
||
// 3bit packed: 12 bytes per (4IC, 8OC) tile.
|
||
// Bytes 0..7 = low 2 bits (same layout as w2 above).
|
||
// Bytes 8..11 = high 1 bit, byte i covers IC i with bit position (7-(k*4+j)) for OC (k*4+j).
|
||
// Stored value = signed_weight + 4 in [0,7].
|
||
__global uchar* outU = (__global uchar*)output_ptr + (y * inputChannelC4 + x) * 12;
|
||
for (int i = 0; i < 4; ++i) {
|
||
uchar hi = 0;
|
||
for (int k = 0; k < 2; ++k) {
|
||
uchar packed_lo = 0;
|
||
for (int j = 0; j < 4; ++j) {
|
||
int oc = yin + k * 4 + j;
|
||
int ic = xin + i;
|
||
int v = 0;
|
||
if (oc < output_channel && ic < input_channel) {
|
||
v = (int)input_ptr[oc * input_channel + ic] + 4;
|
||
}
|
||
packed_lo |= ((uchar)(v & 3)) << (6 - j * 2);
|
||
hi |= ((uchar)((v >> 2) & 1)) << (7 - (k * 4 + j));
|
||
}
|
||
outU[i * 2 + k] = packed_lo;
|
||
}
|
||
outU[8 + i] = hi;
|
||
}
|
||
#elif defined(USE_LOW_BIT_WEIGHT_INT4)
|
||
uchar16 out = 0;
|
||
uchar *out_ptr = (uchar*)&out;
|
||
for(int i = 0; i < 4; ++i){
|
||
int index0 = yin * input_channel + xin + i;
|
||
int index1 = (yin + 1) * input_channel + xin + i;
|
||
int index2 = (yin + 2) * input_channel + xin + i;
|
||
int index3 = (yin + 3) * input_channel + xin + i;
|
||
int index4 = (yin + 4) * input_channel + xin + i;
|
||
int index5 = (yin + 5) * input_channel + xin + i;
|
||
int index6 = (yin + 6) * input_channel + xin + i;
|
||
int index7 = (yin + 7) * input_channel + xin + i;
|
||
uchar s0 = input_ptr[index0/2];
|
||
uchar s1 = input_ptr[index1/2];
|
||
uchar s2 = input_ptr[index2/2];
|
||
uchar s3 = input_ptr[index3/2];
|
||
uchar s4 = input_ptr[index4/2];
|
||
uchar s5 = input_ptr[index5/2];
|
||
uchar s6 = input_ptr[index6/2];
|
||
uchar s7 = input_ptr[index7/2];
|
||
out_ptr[i * 4] = ((index0 % 2) == 0 ? (s0 & 0xf0) : (s0 << 4)) | ((index1 % 2) == 0 ? (s1 >> 4) : (s1 & 0x0f));
|
||
out_ptr[i * 4 + 1] = ((index2 % 2) == 0 ? (s2 & 0xf0) : (s2 << 4)) | ((index3 % 2) == 0 ? (s3 >> 4) : (s3 & 0x0f));
|
||
out_ptr[i * 4 + 2] = ((index4 % 2) == 0 ? (s4 & 0xf0) : (s4 << 4)) | ((index5 % 2) == 0 ? (s5 >> 4) : (s5 & 0x0f));
|
||
out_ptr[i * 4 + 3] = ((index6 % 2) == 0 ? (s6 & 0xf0) : (s6 << 4)) | ((index7 % 2) == 0 ? (s7 >> 4) : (s7 & 0x0f));
|
||
}
|
||
const int outputOffset = (y * inputChannelC4 + x) * 16;
|
||
vstore16(as_char16(out),0,output_ptr+outputOffset);
|
||
#else
|
||
const int inputOffset = yin * input_channel + xin;
|
||
char4 s0 = vload4(0, input_ptr + inputOffset);
|
||
char4 s1 = vload4(0, input_ptr + inputOffset + input_channel);
|
||
char4 s2 = vload4(0, input_ptr + inputOffset + input_channel * 2);
|
||
char4 s3 = vload4(0, input_ptr + inputOffset + input_channel * 3);
|
||
char4 s4 = vload4(0, input_ptr + inputOffset + input_channel * 4);
|
||
char4 s5 = vload4(0, input_ptr + inputOffset + input_channel * 5);
|
||
char4 s6 = vload4(0, input_ptr + inputOffset + input_channel * 6);
|
||
char4 s7 = vload4(0, input_ptr + inputOffset + input_channel * 7);
|
||
char16 out0 = (char16)(s0.s0, s1.s0, s2.s0, s3.s0, s4.s0, s5.s0, s6.s0, s7.s0, s0.s1, s1.s1, s2.s1, s3.s1, s4.s1, s5.s1, s6.s1, s7.s1);
|
||
char16 out1 = (char16)(s0.s2, s1.s2, s2.s2, s3.s2, s4.s2, s5.s2, s6.s2, s7.s2, s0.s3, s1.s3, s2.s3, s3.s3, s4.s3, s5.s3, s6.s3, s7.s3);
|
||
const int outputOffset = (y * inputChannelC4 + x) * 8 * 4;
|
||
vstore16(out0, 0, output_ptr + outputOffset);
|
||
vstore16(out1, 0, output_ptr + outputOffset + 16);
|
||
#endif
|
||
}
|
||
|
||
__kernel void conv2d_1x1_ic_oc_weight_quant_buffer(GLOBAL_SIZE_2_DIMS
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT4
|
||
__global const uchar *input_ptr,
|
||
__global uchar *output_ptr, //(Ci/packCin, Co/packCout, packCin, packCout)
|
||
#else
|
||
__global const char *input_ptr,
|
||
__global char *output_ptr, //(Ci/packCin, Co/packCout, packCin, packCout)
|
||
#endif
|
||
__private const int input_channel,
|
||
__private const int output_channel,
|
||
__private const int icPack,
|
||
__private const int ocPack) {
|
||
int x = get_global_id(0); // ic / icPack
|
||
int y = get_global_id(1); // oc / ocPack
|
||
|
||
DEAL_NON_UNIFORM_DIM2(x, y);
|
||
const int xin = x * icPack;
|
||
const int yin = y * ocPack;
|
||
const int inputChannelC4 = (input_channel + icPack - 1) / icPack;
|
||
const int outputChannelC4 = (output_channel + ocPack - 1) / ocPack;
|
||
#ifdef USE_LOW_BIT_WEIGHT_INT4
|
||
const int inputOffset = (yin * input_channel + xin) / 2;
|
||
const int outputOffset = ((x * outputChannelC4 + y) * icPack * ocPack) / 2;
|
||
for(int i = 0; i < icPack; ++i){
|
||
for(int j = 0; j < ocPack / 2; ++j){
|
||
int index0 = (yin + j * 2) * input_channel + xin + i;
|
||
int index1 = (yin + j * 2 + 1) * input_channel + xin + i;
|
||
uchar s0 = input_ptr[index0/2];
|
||
uchar s1 = input_ptr[index1/2];
|
||
s0 = (index0 % 2) == 0 ? (s0 & 0xf0) : ((s0 & 0x0f) << 4);
|
||
s1 = (index1 % 2) == 0 ? (s1 >> 4) : (s1 & 0x0f);
|
||
output_ptr[outputOffset + i * (ocPack / 2) + j] = s0 | s1;
|
||
}
|
||
}
|
||
#else
|
||
const int inputOffset = yin * input_channel + xin;
|
||
const int outputOffset = (x * outputChannelC4 + y) * icPack * ocPack;
|
||
for(int i = 0; i < icPack; ++i){
|
||
for(int j = 0; j < ocPack; ++j){
|
||
output_ptr[outputOffset + i * ocPack + j] = input_ptr[inputOffset + j * input_channel + i];
|
||
}
|
||
}
|
||
#endif
|
||
}
|