benchmarking with a simple command: ffmpeg -i 1080p.mp4 -vf unsharp=la=3:ca=3 -an -f null /dev/null with the patch, the fps increase from 50 to 120 on my local machine (i7-6770HQ).
Unified · Split
| @@ -37,7 +37,8 @@ typedef struct UnsharpFilterParam { · 36 unmodified lines | |||
| 37 | int steps_y; ///< vertical step count | 37 | int steps_y; ///< vertical step count |
| 38 | int scalebits; ///< bits to shift pixel | 38 | int scalebits; ///< bits to shift pixel |
| 39 | int32_t halfscale; ///< amount to add to pixel | 39 | int32_t halfscale; ///< amount to add to pixel |
| 40 | uint32_t *sc[MAX_MATRIX_SIZE - 1]; ///< finite state machine storage | 40 | uint32_t *sr; ///< finite state machine storage within a row |
| 41 | uint32_t **sc; ///< finite state machine storage across rows | ||
| 41 | } UnsharpFilterParam; | 42 | } UnsharpFilterParam; |
| 42 | 43 | ||
| 43 | typedef struct UnsharpContext { | 44 | typedef struct UnsharpContext { |
| @@ -47,6 +48,7 @@ typedef struct UnsharpContext { · 3 unmodified lines | |||
| 47 | UnsharpFilterParam luma; ///< luma parameters (width, height, amount) | 48 | UnsharpFilterParam luma; ///< luma parameters (width, height, amount) |
| 48 | UnsharpFilterParam chroma; ///< chroma parameters (width, height, amount) | 49 | UnsharpFilterParam chroma; ///< chroma parameters (width, height, amount) |
| 49 | int hsub, vsub; | 50 | int hsub, vsub; |
| 51 | int nb_threads; | ||
| 50 | int opencl; | 52 | int opencl; |
| 51 | int (* apply_unsharp)(AVFilterContext *ctx, AVFrame *in, AVFrame *out); | 53 | int (* apply_unsharp)(AVFilterContext *ctx, AVFrame *in, AVFrame *out); |
| 52 | } UnsharpContext; | 54 | } UnsharpContext; |
| @@ -47,15 +47,22 @@ · 46 unmodified lines | |||
| 47 | #include "libavutil/pixdesc.h" | 47 | #include "libavutil/pixdesc.h" |
| 48 | #include "unsharp.h" | 48 | #include "unsharp.h" |
| 49 | 49 | ||
| 50 | static void apply_unsharp( uint8_t *dst, int dst_stride, | 50 | typedef struct TheadData { |
| 51 | const uint8_t *src, int src_stride, | 51 | UnsharpFilterParam *fp; |
| 52 | int width, int height, UnsharpFilterParam *fp) | 52 | uint8_t *dst; |
| 53 | const uint8_t *src; | ||
| 54 | int dst_stride; | ||
| 55 | int src_stride; | ||
| 56 | int width; | ||
| 57 | int height; | ||
| 58 | } ThreadData; | ||
| 59 | |||
| 60 | static int unsharp_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) | ||
| 53 | { | 61 | { |
| 62 | ThreadData *td = arg; | ||
| 63 | UnsharpFilterParam *fp = td->fp; | ||
| 54 | uint32_t **sc = fp->sc; | 64 | uint32_t **sc = fp->sc; |
| 55 | uint32_t sr[MAX_MATRIX_SIZE - 1], tmp1, tmp2; | 65 | uint32_t *sr = fp->sr; |
| 56 | |||
| 57 | int32_t res; | ||
| 58 | int x, y, z; | ||
| 59 | const uint8_t *src2 = NULL; //silence a warning | 66 | const uint8_t *src2 = NULL; //silence a warning |
| 60 | const int amount = fp->amount; | 67 | const int amount = fp->amount; |
| 61 | const int steps_x = fp->steps_x; | 68 | const int steps_x = fp->steps_x; |
| @@ -63,30 +70,54 @@ static void apply_unsharp( uint8_t *dst, int dst_stride, · 1 unmodified line | |||
| 63 | const int scalebits = fp->scalebits; | 70 | const int scalebits = fp->scalebits; |
| 64 | const int32_t halfscale = fp->halfscale; | 71 | const int32_t halfscale = fp->halfscale; |
| 65 | 72 | ||
| 73 | uint8_t *dst = td->dst; | ||
| 74 | const uint8_t *src = td->src; | ||
| 75 | const int dst_stride = td->dst_stride; | ||
| 76 | const int src_stride = td->src_stride; | ||
| 77 | const int width = td->width; | ||
| 78 | const int height = td->height; | ||
| 79 | const int sc_offset = jobnr * 2 * steps_y; | ||
| 80 | const int sr_offset = jobnr * (MAX_MATRIX_SIZE - 1); | ||
| 81 | const int slice_start = (height * jobnr) / nb_jobs; | ||
| 82 | const int slice_end = (height * (jobnr+1)) / nb_jobs; | ||
| 83 | |||
| 84 | int32_t res; | ||
| 85 | int x, y, z; | ||
| 86 | uint32_t tmp1, tmp2; | ||
| 87 | |||
| 66 | if (!amount) { | 88 | if (!amount) { |
| 67 | av_image_copy_plane(dst, dst_stride, src, src_stride, width, height); | 89 | av_image_copy_plane(dst + slice_start * dst_stride, dst_stride, |
| 68 | return; | 90 | src + slice_start * src_stride, src_stride, |
| 91 | width, slice_end - slice_start); | ||
| 92 | return 0; | ||
| 69 | } | 93 | } |
| 70 | 94 | ||
| 71 | for (y = 0; y < 2 * steps_y; y++) | 95 | for (y = 0; y < 2 * steps_y; y++) |
| 72 | memset(sc[y], 0, sizeof(sc[y][0]) * (width + 2 * steps_x)); | 96 | memset(sc[sc_offset + y], 0, sizeof(sc[y][0]) * (width + 2 * steps_x)); |
| 73 | 97 | ||
| 74 | for (y = -steps_y; y < height + steps_y; y++) { | 98 | // if this is not the first tile, we start from (slice_start - steps_y), |
| 99 | // so we can get smooth result at slice boundary | ||
| 100 | if (slice_start > steps_y) { | ||
| 101 | src += (slice_start - steps_y) * src_stride; | ||
| 102 | dst += (slice_start - steps_y) * dst_stride; | ||
| 103 | } | ||
| 104 | |||
| 105 | for (y = -steps_y + slice_start; y < steps_y + slice_end; y++) { | ||
| 75 | if (y < height) | 106 | if (y < height) |
| 76 | src2 = src; | 107 | src2 = src; |
| 77 | 108 | ||
| 78 | memset(sr, 0, sizeof(sr[0]) * (2 * steps_x - 1)); | 109 | memset(sr + sr_offset, 0, sizeof(sr[0]) * (2 * steps_x - 1)); |
| 79 | for (x = -steps_x; x < width + steps_x; x++) { | 110 | for (x = -steps_x; x < width + steps_x; x++) { |
| 80 | tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; | 111 | tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; |
| 81 | for (z = 0; z < steps_x * 2; z += 2) { | 112 | for (z = 0; z < steps_x * 2; z += 2) { |
| 82 | tmp2 = sr[z + 0] + tmp1; sr[z + 0] = tmp1; | 113 | tmp2 = sr[sr_offset + z + 0] + tmp1; sr[sr_offset + z + 0] = tmp1; |
| 83 | tmp1 = sr[z + 1] + tmp2; sr[z + 1] = tmp2; | 114 | tmp1 = sr[sr_offset + z + 1] + tmp2; sr[sr_offset + z + 1] = tmp2; |
| 84 | } | 115 | } |
| 85 | for (z = 0; z < steps_y * 2; z += 2) { | 116 | for (z = 0; z < steps_y * 2; z += 2) { |
| 86 | tmp2 = sc[z + 0][x + steps_x] + tmp1; sc[z + 0][x + steps_x] = tmp1; | 117 | tmp2 = sc[sc_offset + z + 0][x + steps_x] + tmp1; sc[sc_offset + z + 0][x + steps_x] = tmp1; |
| 87 | tmp1 = sc[z + 1][x + steps_x] + tmp2; sc[z + 1][x + steps_x] = tmp2; | 118 | tmp1 = sc[sc_offset + z + 1][x + steps_x] + tmp2; sc[sc_offset + z + 1][x + steps_x] = tmp2; |
| 88 | } | 119 | } |
| 89 | if (x >= steps_x && y >= steps_y) { | 120 | if (x >= steps_x && y >= (steps_y + slice_start)) { |
| 90 | const uint8_t *srx = src - steps_y * src_stride + x - steps_x; | 121 | const uint8_t *srx = src - steps_y * src_stride + x - steps_x; |
| 91 | uint8_t *dsx = dst - steps_y * dst_stride + x - steps_x; | 122 | uint8_t *dsx = dst - steps_y * dst_stride + x - steps_x; |
| 92 | 123 | ||
| @@ -99,6 +130,7 @@ static void apply_unsharp( uint8_t *dst, int dst_stride, · 6 unmodified lines | |||
| 99 | src += src_stride; | 130 | src += src_stride; |
| 100 | } | 131 | } |
| 101 | } | 132 | } |
| 133 | return 0; | ||
| 102 | } | 134 | } |
| 103 | 135 | ||
| 104 | static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) | 136 | static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) |
| @@ -107,6 +139,8 @@ static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) · 2 unmodified lines | |||
| 107 | UnsharpContext *s = ctx->priv; | 139 | UnsharpContext *s = ctx->priv; |
| 108 | int i, plane_w[3], plane_h[3]; | 140 | int i, plane_w[3], plane_h[3]; |
| 109 | UnsharpFilterParam *fp[3]; | 141 | UnsharpFilterParam *fp[3]; |
| 142 | ThreadData td; | ||
| 143 | |||
| 110 | plane_w[0] = inlink->w; | 144 | plane_w[0] = inlink->w; |
| 111 | plane_w[1] = plane_w[2] = AV_CEIL_RSHIFT(inlink->w, s->hsub); | 145 | plane_w[1] = plane_w[2] = AV_CEIL_RSHIFT(inlink->w, s->hsub); |
| 112 | plane_h[0] = inlink->h; | 146 | plane_h[0] = inlink->h; |
| @@ -114,7 +148,14 @@ static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) · 1 unmodified line | |||
| 114 | fp[0] = &s->luma; | 148 | fp[0] = &s->luma; |
| 115 | fp[1] = fp[2] = &s->chroma; | 149 | fp[1] = fp[2] = &s->chroma; |
| 116 | for (i = 0; i < 3; i++) { | 150 | for (i = 0; i < 3; i++) { |
| 117 | apply_unsharp(out->data[i], out->linesize[i], in->data[i], in->linesize[i], plane_w[i], plane_h[i], fp[i]); | 151 | td.fp = fp[i]; |
| 152 | td.dst = out->data[i]; | ||
| 153 | td.src = in->data[i]; | ||
| 154 | td.width = plane_w[i]; | ||
| 155 | td.height = plane_h[i]; | ||
| 156 | td.dst_stride = out->linesize[i]; | ||
| 157 | td.src_stride = in->linesize[i]; | ||
| 158 | ctx->internal->execute(ctx, unsharp_slice, &td, NULL, FFMIN(plane_h[i], s->nb_threads)); | ||
| 118 | } | 159 | } |
| 119 | return 0; | 160 | return 0; |
| 120 | } | 161 | } |
| @@ -163,6 +204,7 @@ static int query_formats(AVFilterContext *ctx) · 42 unmodified lines | |||
| 163 | static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const char *effect_type, int width) | 204 | static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const char *effect_type, int width) |
| 164 | { | 205 | { |
| 165 | int z; | 206 | int z; |
| 207 | UnsharpContext *s = ctx->priv; | ||
| 166 | const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen"; | 208 | const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen"; |
| 167 | 209 | ||
| 168 | if (!(fp->msize_x & fp->msize_y & 1)) { | 210 | if (!(fp->msize_x & fp->msize_y & 1)) { |
| @@ -175,7 +217,12 @@ static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const · 6 unmodified lines | |||
| 175 | av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", | 217 | av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", |
| 176 | effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0); | 218 | effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0); |
| 177 | 219 | ||
| 178 | for (z = 0; z < 2 * fp->steps_y; z++) | 220 | fp->sr = av_malloc_array((MAX_MATRIX_SIZE - 1) * s->nb_threads, sizeof(uint32_t)); |
| 221 | fp->sc = av_malloc_array(2 * fp->steps_y * s->nb_threads, sizeof(uint32_t **)); | ||
| 222 | if (!fp->sr || !fp->sc) | ||
| 223 | return AVERROR(ENOMEM); | ||
| 224 | |||
| 225 | for (z = 0; z < 2 * fp->steps_y * s->nb_threads; z++) | ||
| 179 | if (!(fp->sc[z] = av_malloc_array(width + 2 * fp->steps_x, | 226 | if (!(fp->sc[z] = av_malloc_array(width + 2 * fp->steps_x, |
| 180 | sizeof(*(fp->sc[z]))))) | 227 | sizeof(*(fp->sc[z]))))) |
| 181 | return AVERROR(ENOMEM); | 228 | return AVERROR(ENOMEM); |
| @@ -192,6 +239,11 @@ static int config_props(AVFilterLink *link) · 10 unmodified lines | |||
| 192 | s->hsub = desc->log2_chroma_w; | 239 | s->hsub = desc->log2_chroma_w; |
| 193 | s->vsub = desc->log2_chroma_h; | 240 | s->vsub = desc->log2_chroma_h; |
| 194 | 241 | ||
| 242 | // ensure (height / nb_threads) > 4 * steps_y, | ||
| 243 | // so that we don't have too much overlap between two threads | ||
| 244 | s->nb_threads = FFMIN(ff_filter_get_nb_threads(link->dst), | ||
| 245 | link->h / (4 * s->luma.steps_y)); | ||
| 246 | |||
| 195 | ret = init_filter_param(link->dst, &s->luma, "luma", link->w); | 247 | ret = init_filter_param(link->dst, &s->luma, "luma", link->w); |
| 196 | if (ret < 0) | 248 | if (ret < 0) |
| 197 | return ret; | 249 | return ret; |
| @@ -202,20 +254,22 @@ static int config_props(AVFilterLink *link) · 4 unmodified lines | |||
| 202 | return 0; | 254 | return 0; |
| 203 | } | 255 | } |
| 204 | 256 | ||
| 205 | static void free_filter_param(UnsharpFilterParam *fp) | 257 | static void free_filter_param(UnsharpFilterParam *fp, int nb_threads) |
| 206 | { | 258 | { |
| 207 | int z; | 259 | int z; |
| 208 | 260 | ||
| 209 | for (z = 0; z < 2 * fp->steps_y; z++) | 261 | for (z = 0; z < 2 * fp->steps_y * nb_threads; z++) |
| 210 | av_freep(&fp->sc[z]); | 262 | av_freep(&fp->sc[z]); |
| 263 | av_freep(&fp->sc); | ||
| 264 | av_freep(&fp->sr); | ||
| 211 | } | 265 | } |
| 212 | 266 | ||
| 213 | static av_cold void uninit(AVFilterContext *ctx) | 267 | static av_cold void uninit(AVFilterContext *ctx) |
| 214 | { | 268 | { |
| 215 | UnsharpContext *s = ctx->priv; | 269 | UnsharpContext *s = ctx->priv; |
| 216 | 270 | ||
| 217 | free_filter_param(&s->luma); | 271 | free_filter_param(&s->luma, s->nb_threads); |
| 218 | free_filter_param(&s->chroma); | 272 | free_filter_param(&s->chroma, s->nb_threads); |
| 219 | } | 273 | } |
| 220 | 274 | ||
| 221 | static int filter_frame(AVFilterLink *link, AVFrame *in) | 275 | static int filter_frame(AVFilterLink *link, AVFrame *in) |
| @@ -294,5 +348,5 @@ AVFilter ff_vf_unsharp = { · 72 unmodified lines | |||
| 294 | .query_formats = query_formats, | 348 | .query_formats = query_formats, |
| 295 | .inputs = avfilter_vf_unsharp_inputs, | 349 | .inputs = avfilter_vf_unsharp_inputs, |
| 296 | .outputs = avfilter_vf_unsharp_outputs, | 350 | .outputs = avfilter_vf_unsharp_outputs, |
| 297 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, | 351 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, |
| 298 | }; | 352 | }; |