From 7c02a77d250ff24514e029d931f7bc16b3810504 Mon Sep 17 00:00:00 2001 From: Lenny Wang Date: Thu, 7 Nov 2013 15:15:49 -0600 Subject: avfilter/unsharp: added optimized opencl kernels Reviewed-by: Wei Gao Signed-off-by: Michael Niedermayer --- libavfilter/unsharp.h | 6 +- libavfilter/unsharp_opencl.c | 180 +++++++++++++++++++++++++++--------- libavfilter/unsharp_opencl_kernel.h | 153 +++++++++++++++++++++++++++++- 3 files changed, 293 insertions(+), 46 deletions(-) (limited to 'libavfilter') diff --git a/libavfilter/unsharp.h b/libavfilter/unsharp.h index 2738243cea..867892085d 100644 --- a/libavfilter/unsharp.h +++ b/libavfilter/unsharp.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Wei Gao + * Copyright (C) 2013 Lenny Wang * * This file is part of FFmpeg. * @@ -35,7 +36,9 @@ typedef struct { cl_command_queue command_queue; cl_program program; - cl_kernel kernel; + cl_kernel kernel_default; + cl_kernel kernel_luma; + cl_kernel kernel_chroma; cl_mem cl_luma_mask; cl_mem cl_chroma_mask; int in_plane_size[8]; @@ -45,6 +48,7 @@ typedef struct { size_t cl_inbuf_size; cl_mem cl_outbuf; size_t cl_outbuf_size; + int use_fast_kernels; } UnsharpOpenclContext; #endif diff --git a/libavfilter/unsharp_opencl.c b/libavfilter/unsharp_opencl.c index 77b59371a8..fff16ab520 100644 --- a/libavfilter/unsharp_opencl.c +++ b/libavfilter/unsharp_opencl.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Wei Gao + * Copyright (C) 2013 Lenny Wang * * This file is part of FFmpeg. * @@ -28,6 +29,7 @@ #include "libavutil/opencl_internal.h" #define PLANE_NUM 3 +#define ROUND_TO_16(a) ((((a- 1)/16)+1)*16) static inline void add_mask_counter(uint32_t *dst, uint32_t *counter1, uint32_t *counter2, int len) { @@ -135,6 +137,13 @@ static int generate_mask(AVFilterContext *ctx) step_x[1] = unsharp->chroma.steps_x; step_y[0] = unsharp->luma.steps_y; step_y[1] = unsharp->chroma.steps_y; + + /* use default kernel if any matrix dim larger than 8 due to limited local mem size */ + if (step_x[0]>8 || step_x[1]>8 || step_y[0]>8 || step_y[1]>8) + unsharp->opencl_ctx.use_fast_kernels = 0; + else + unsharp->opencl_ctx.use_fast_kernels = 1; + if (!mask_matrix[0] || !mask_matrix[1]) { av_log(ctx, AV_LOG_ERROR, "Luma mask and chroma mask should not be NULL\n"); return AVERROR(EINVAL); @@ -153,45 +162,107 @@ int ff_opencl_apply_unsharp(AVFilterContext *ctx, AVFrame *in, AVFrame *out) AVFilterLink *link = ctx->inputs[0]; UnsharpContext *unsharp = ctx->priv; cl_int status; + FFOpenclParam kernel1 = {0}; + FFOpenclParam kernel2 = {0}; + int width = link->w; + int height = link->h; int cw = FF_CEIL_RSHIFT(link->w, unsharp->hsub); int ch = FF_CEIL_RSHIFT(link->h, unsharp->vsub); - const size_t global_work_size = link->w * link->h + 2 * ch * cw; - FFOpenclParam opencl_param = {0}; + size_t globalWorkSize1d = width * height + 2 * ch * cw; + size_t globalWorkSize2dLuma[2]; + size_t globalWorkSize2dChroma[2]; + size_t localWorkSize2d[2] = {16, 16}; - opencl_param.ctx = ctx; - opencl_param.kernel = unsharp->opencl_ctx.kernel; - ret = ff_opencl_set_parameter(&opencl_param, - FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_inbuf), - FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_outbuf), - FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_luma_mask), - FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_chroma_mask), - FF_OPENCL_PARAM_INFO(unsharp->luma.amount), - FF_OPENCL_PARAM_INFO(unsharp->chroma.amount), - FF_OPENCL_PARAM_INFO(unsharp->luma.steps_x), - FF_OPENCL_PARAM_INFO(unsharp->luma.steps_y), - FF_OPENCL_PARAM_INFO(unsharp->chroma.steps_x), - FF_OPENCL_PARAM_INFO(unsharp->chroma.steps_y), - FF_OPENCL_PARAM_INFO(unsharp->luma.scalebits), - FF_OPENCL_PARAM_INFO(unsharp->chroma.scalebits), - FF_OPENCL_PARAM_INFO(unsharp->luma.halfscale), - FF_OPENCL_PARAM_INFO(unsharp->chroma.halfscale), - FF_OPENCL_PARAM_INFO(in->linesize[0]), - FF_OPENCL_PARAM_INFO(in->linesize[1]), - FF_OPENCL_PARAM_INFO(out->linesize[0]), - FF_OPENCL_PARAM_INFO(out->linesize[1]), - FF_OPENCL_PARAM_INFO(link->h), - FF_OPENCL_PARAM_INFO(link->w), - FF_OPENCL_PARAM_INFO(ch), - FF_OPENCL_PARAM_INFO(cw), - NULL); - if (ret < 0) - return ret; - status = clEnqueueNDRangeKernel(unsharp->opencl_ctx.command_queue, - unsharp->opencl_ctx.kernel, 1, NULL, - &global_work_size, NULL, 0, NULL, NULL); - if (status != CL_SUCCESS) { - av_log(ctx, AV_LOG_ERROR, "OpenCL run kernel error occurred: %s\n", av_opencl_errstr(status)); - return AVERROR_EXTERNAL; + if (unsharp->opencl_ctx.use_fast_kernels) { + globalWorkSize2dLuma[0] = (size_t)ROUND_TO_16(width); + globalWorkSize2dLuma[1] = (size_t)ROUND_TO_16(height); + globalWorkSize2dChroma[0] = (size_t)ROUND_TO_16(cw); + globalWorkSize2dChroma[1] = (size_t)(2*ROUND_TO_16(ch)); + + kernel1.ctx = ctx; + kernel1.kernel = unsharp->opencl_ctx.kernel_luma; + ret = ff_opencl_set_parameter(&kernel1, + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_inbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_outbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_luma_mask), + FF_OPENCL_PARAM_INFO(unsharp->luma.amount), + FF_OPENCL_PARAM_INFO(unsharp->luma.scalebits), + FF_OPENCL_PARAM_INFO(unsharp->luma.halfscale), + FF_OPENCL_PARAM_INFO(in->linesize[0]), + FF_OPENCL_PARAM_INFO(out->linesize[0]), + FF_OPENCL_PARAM_INFO(width), + FF_OPENCL_PARAM_INFO(height), + NULL); + if (ret < 0) + return ret; + + kernel2.ctx = ctx; + kernel2.kernel = unsharp->opencl_ctx.kernel_chroma; + ret = ff_opencl_set_parameter(&kernel2, + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_inbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_outbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_chroma_mask), + FF_OPENCL_PARAM_INFO(unsharp->chroma.amount), + FF_OPENCL_PARAM_INFO(unsharp->chroma.scalebits), + FF_OPENCL_PARAM_INFO(unsharp->chroma.halfscale), + FF_OPENCL_PARAM_INFO(in->linesize[0]), + FF_OPENCL_PARAM_INFO(in->linesize[1]), + FF_OPENCL_PARAM_INFO(out->linesize[0]), + FF_OPENCL_PARAM_INFO(out->linesize[1]), + FF_OPENCL_PARAM_INFO(link->w), + FF_OPENCL_PARAM_INFO(link->h), + FF_OPENCL_PARAM_INFO(cw), + FF_OPENCL_PARAM_INFO(ch), + NULL); + if (ret < 0) + return ret; + status = clEnqueueNDRangeKernel(unsharp->opencl_ctx.command_queue, + unsharp->opencl_ctx.kernel_luma, 2, NULL, + globalWorkSize2dLuma, localWorkSize2d, 0, NULL, NULL); + status |=clEnqueueNDRangeKernel(unsharp->opencl_ctx.command_queue, + unsharp->opencl_ctx.kernel_chroma, 2, NULL, + globalWorkSize2dChroma, localWorkSize2d, 0, NULL, NULL); + if (status != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "OpenCL run kernel error occurred: %s\n", av_opencl_errstr(status)); + return AVERROR_EXTERNAL; + } + } else { /* use default kernel */ + kernel1.ctx = ctx; + kernel1.kernel = unsharp->opencl_ctx.kernel_default; + + ret = ff_opencl_set_parameter(&kernel1, + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_inbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_outbuf), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_luma_mask), + FF_OPENCL_PARAM_INFO(unsharp->opencl_ctx.cl_chroma_mask), + FF_OPENCL_PARAM_INFO(unsharp->luma.amount), + FF_OPENCL_PARAM_INFO(unsharp->chroma.amount), + FF_OPENCL_PARAM_INFO(unsharp->luma.steps_x), + FF_OPENCL_PARAM_INFO(unsharp->luma.steps_y), + FF_OPENCL_PARAM_INFO(unsharp->chroma.steps_x), + FF_OPENCL_PARAM_INFO(unsharp->chroma.steps_y), + FF_OPENCL_PARAM_INFO(unsharp->luma.scalebits), + FF_OPENCL_PARAM_INFO(unsharp->chroma.scalebits), + FF_OPENCL_PARAM_INFO(unsharp->luma.halfscale), + FF_OPENCL_PARAM_INFO(unsharp->chroma.halfscale), + FF_OPENCL_PARAM_INFO(in->linesize[0]), + FF_OPENCL_PARAM_INFO(in->linesize[1]), + FF_OPENCL_PARAM_INFO(out->linesize[0]), + FF_OPENCL_PARAM_INFO(out->linesize[1]), + FF_OPENCL_PARAM_INFO(link->h), + FF_OPENCL_PARAM_INFO(link->w), + FF_OPENCL_PARAM_INFO(ch), + FF_OPENCL_PARAM_INFO(cw), + NULL); + if (ret < 0) + return ret; + status = clEnqueueNDRangeKernel(unsharp->opencl_ctx.command_queue, + unsharp->opencl_ctx.kernel_default, 1, NULL, + &globalWorkSize1d, NULL, 0, NULL, NULL); + if (status != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "OpenCL run kernel error occurred: %s\n", av_opencl_errstr(status)); + return AVERROR_EXTERNAL; + } } clFinish(unsharp->opencl_ctx.command_queue); return av_opencl_buffer_read_image(out->data, unsharp->opencl_ctx.out_plane_size, @@ -202,6 +273,7 @@ int ff_opencl_apply_unsharp(AVFilterContext *ctx, AVFrame *in, AVFrame *out) int ff_opencl_unsharp_init(AVFilterContext *ctx) { int ret = 0; + char build_opts[96]; UnsharpContext *unsharp = ctx->priv; ret = av_opencl_init(NULL); if (ret < 0) @@ -225,16 +297,36 @@ int ff_opencl_unsharp_init(AVFilterContext *ctx) av_log(ctx, AV_LOG_ERROR, "Unable to get OpenCL command queue in filter 'unsharp'\n"); return AVERROR(EINVAL); } - unsharp->opencl_ctx.program = av_opencl_compile("unsharp", NULL); + snprintf(build_opts, 96, "-D LU_RADIUS_X=%d -D LU_RADIUS_Y=%d -D CH_RADIUS_X=%d -D CH_RADIUS_Y=%d", + 2*unsharp->luma.steps_x+1, 2*unsharp->luma.steps_y+1, 2*unsharp->chroma.steps_x+1, 2*unsharp->chroma.steps_y+1); + unsharp->opencl_ctx.program = av_opencl_compile("unsharp", build_opts); if (!unsharp->opencl_ctx.program) { av_log(ctx, AV_LOG_ERROR, "OpenCL failed to compile program 'unsharp'\n"); return AVERROR(EINVAL); } - if (!unsharp->opencl_ctx.kernel) { - unsharp->opencl_ctx.kernel = clCreateKernel(unsharp->opencl_ctx.program, "unsharp", &ret); - if (ret != CL_SUCCESS) { - av_log(ctx, AV_LOG_ERROR, "OpenCL failed to create kernel 'unsharp'\n"); - return AVERROR(EINVAL); + if (unsharp->opencl_ctx.use_fast_kernels) { + if (!unsharp->opencl_ctx.kernel_luma) { + unsharp->opencl_ctx.kernel_luma = clCreateKernel(unsharp->opencl_ctx.program, "unsharp_luma", &ret); + if (ret != CL_SUCCESS) { + av_log(ctx, AV_LOG_ERROR, "OpenCL failed to create kernel 'unsharp_luma'\n"); + return ret; + } + } + if (!unsharp->opencl_ctx.kernel_chroma) { + unsharp->opencl_ctx.kernel_chroma = clCreateKernel(unsharp->opencl_ctx.program, "unsharp_chroma", &ret); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "OpenCL failed to create kernel 'unsharp_chroma'\n"); + return ret; + } + } + } + else { + if (!unsharp->opencl_ctx.kernel_default) { + unsharp->opencl_ctx.kernel_default = clCreateKernel(unsharp->opencl_ctx.program, "unsharp_default", &ret); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "OpenCL failed to create kernel 'unsharp_default'\n"); + return ret; + } } } return ret; @@ -247,7 +339,9 @@ void ff_opencl_unsharp_uninit(AVFilterContext *ctx) av_opencl_buffer_release(&unsharp->opencl_ctx.cl_outbuf); av_opencl_buffer_release(&unsharp->opencl_ctx.cl_luma_mask); av_opencl_buffer_release(&unsharp->opencl_ctx.cl_chroma_mask); - clReleaseKernel(unsharp->opencl_ctx.kernel); + clReleaseKernel(unsharp->opencl_ctx.kernel_default); + clReleaseKernel(unsharp->opencl_ctx.kernel_luma); + clReleaseKernel(unsharp->opencl_ctx.kernel_chroma); clReleaseProgram(unsharp->opencl_ctx.program); unsharp->opencl_ctx.command_queue = NULL; av_opencl_uninit(); diff --git a/libavfilter/unsharp_opencl_kernel.h b/libavfilter/unsharp_opencl_kernel.h index 0cc8e906b4..9c4fd65031 100644 --- a/libavfilter/unsharp_opencl_kernel.h +++ b/libavfilter/unsharp_opencl_kernel.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2013 Wei Gao + * Copyright (C) 2013 Lenny Wang * * This file is part of FFmpeg. * @@ -32,7 +33,156 @@ inline unsigned char clip_uint8(int a) return a; } -kernel void unsharp(global unsigned char *src, +kernel void unsharp_luma( + global unsigned char *src, + global unsigned char *dst, + global int *mask, + int amount, + int scalebits, + int halfscale, + int src_stride, + int dst_stride, + int width, + int height) +{ + int2 threadIdx, blockIdx, globalIdx; + threadIdx.x = get_local_id(0); + threadIdx.y = get_local_id(1); + blockIdx.x = get_group_id(0); + blockIdx.y = get_group_id(1); + globalIdx.x = get_global_id(0); + globalIdx.y = get_global_id(1); + + if (!amount) { + if (globalIdx.x < width && globalIdx.y < height) + dst[globalIdx.x + globalIdx.y*dst_stride] = src[globalIdx.x + globalIdx.y*src_stride]; + return; + } + + local uchar l[32][32]; + local int lc[LU_RADIUS_X*LU_RADIUS_Y]; + int indexIx, indexIy, i, j; + + for(i = 0; i <= 1; i++) { + indexIy = -8 + (blockIdx.y + i) * 16 + threadIdx.y; + indexIy = indexIy < 0 ? 0 : indexIy; + indexIy = indexIy >= height ? height - 1: indexIy; + for(j = 0; j <= 1; j++) { + indexIx = -8 + (blockIdx.x + j) * 16 + threadIdx.x; + indexIx = indexIx < 0 ? 0 : indexIx; + indexIx = indexIx >= width ? width - 1: indexIx; + l[i*16 + threadIdx.y][j*16 + threadIdx.x] = src[indexIy*src_stride + indexIx]; + } + } + + int indexL = threadIdx.y*16 + threadIdx.x; + if (indexL < LU_RADIUS_X*LU_RADIUS_Y) + lc[indexL] = mask[indexL]; + barrier(CLK_LOCAL_MEM_FENCE); + + int idx, idy, maskIndex; + int sum = 0; + int steps_x = LU_RADIUS_X/2; + int steps_y = LU_RADIUS_Y/2; + + \n#pragma unroll\n + for (i = -steps_y; i <= steps_y; i++) { + idy = 8 + i + threadIdx.y; + \n#pragma unroll\n + for (j = -steps_x; j <= steps_x; j++) { + idx = 8 + j + threadIdx.x; + maskIndex = (i + steps_y)*LU_RADIUS_X + j + steps_x; + sum += (int)l[idy][idx] * lc[maskIndex]; + } + } + int temp = (int)l[threadIdx.y + 8][threadIdx.x + 8]; + int res = temp + (((temp - (int)((sum + halfscale) >> scalebits)) * amount) >> 16); + if (globalIdx.x < width && globalIdx.y < height) + dst[globalIdx.x + globalIdx.y*dst_stride] = clip_uint8(res); +} + +kernel void unsharp_chroma( + global unsigned char *src_y, + global unsigned char *dst_y, + global int *mask, + int amount, + int scalebits, + int halfscale, + int src_stride_lu, + int src_stride_ch, + int dst_stride_lu, + int dst_stride_ch, + int width, + int height, + int cw, + int ch) +{ + global unsigned char *dst_u = dst_y + height * dst_stride_lu; + global unsigned char *dst_v = dst_u + ch * dst_stride_ch; + global unsigned char *src_u = src_y + height * src_stride_lu; + global unsigned char *src_v = src_u + ch * src_stride_ch; + int2 threadIdx, blockIdx, globalIdx; + threadIdx.x = get_local_id(0); + threadIdx.y = get_local_id(1); + blockIdx.x = get_group_id(0); + blockIdx.y = get_group_id(1); + globalIdx.x = get_global_id(0); + globalIdx.y = get_global_id(1); + int padch = get_global_size(1)/2; + global unsigned char *src = globalIdx.y>=padch ? src_v : src_u; + global unsigned char *dst = globalIdx.y>=padch ? dst_v : dst_u; + + blockIdx.y = globalIdx.y>=padch ? blockIdx.y - get_num_groups(1)/2 : blockIdx.y; + globalIdx.y = globalIdx.y>=padch ? globalIdx.y - padch : globalIdx.y; + + if (!amount) { + if (globalIdx.x < cw && globalIdx.y < ch) + dst[globalIdx.x + globalIdx.y*dst_stride_ch] = src[globalIdx.x + globalIdx.y*src_stride_ch]; + return; + } + + local uchar l[32][32]; + local int lc[CH_RADIUS_X*CH_RADIUS_Y]; + int indexIx, indexIy, i, j; + for(i = 0; i <= 1; i++) { + indexIy = -8 + (blockIdx.y + i) * 16 + threadIdx.y; + indexIy = indexIy < 0 ? 0 : indexIy; + indexIy = indexIy >= ch ? ch - 1: indexIy; + for(j = 0; j <= 1; j++) { + indexIx = -8 + (blockIdx.x + j) * 16 + threadIdx.x; + indexIx = indexIx < 0 ? 0 : indexIx; + indexIx = indexIx >= cw ? cw - 1: indexIx; + l[i*16 + threadIdx.y][j*16 + threadIdx.x] = src[indexIy * src_stride_ch + indexIx]; + } + } + + int indexL = threadIdx.y*16 + threadIdx.x; + if (indexL < CH_RADIUS_X*CH_RADIUS_Y) + lc[indexL] = mask[indexL]; + barrier(CLK_LOCAL_MEM_FENCE); + + int idx, idy, maskIndex; + int sum = 0; + int steps_x = CH_RADIUS_X/2; + int steps_y = CH_RADIUS_Y/2; + + \n#pragma unroll\n + for (i = -steps_y; i <= steps_y; i++) { + idy = 8 + i + threadIdx.y; + \n#pragma unroll\n + for (j = -steps_x; j <= steps_x; j++) { + idx = 8 + j + threadIdx.x; + maskIndex = (i + steps_y)*CH_RADIUS_X + j + steps_x; + sum += (int)l[idy][idx] * lc[maskIndex]; + } + } + int temp = (int)l[threadIdx.y + 8][threadIdx.x + 8]; + int res = temp + (((temp - (int)((sum + halfscale) >> scalebits)) * amount) >> 16); + if (globalIdx.x < cw && globalIdx.y < ch) + dst[globalIdx.x + globalIdx.y*dst_stride_ch] = clip_uint8(res); +} + +kernel void unsharp_default(global unsigned char *src, global unsigned char *dst, const global unsigned int *mask_lu, const global unsigned int *mask_ch, @@ -131,7 +281,6 @@ kernel void unsharp(global unsigned char *src, temp_dst[x + y * temp_dst_stride] = temp_src[x + y * temp_src_stride]; } } - ); #endif /* AVFILTER_UNSHARP_OPENCL_KERNEL_H */ -- cgit v1.2.3