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 | 37 | int steps_y; ///< vertical step count |
| 38 | 38 | int scalebits; ///< bits to shift pixel |
| 39 | 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 | 42 | } UnsharpFilterParam; |
| 42 | 43 | |
| 43 | 44 | typedef struct UnsharpContext { |
| @@ -47,6 +48,7 @@ typedef struct UnsharpContext { · 3 unmodified lines | ||
| 47 | 48 | UnsharpFilterParam luma; ///< luma parameters (width, height, amount) |
| 48 | 49 | UnsharpFilterParam chroma; ///< chroma parameters (width, height, amount) |
| 49 | 50 | int hsub, vsub; |
| 51 | + int nb_threads; | |
| 50 | 52 | int opencl; |
| 51 | 53 | int (* apply_unsharp)(AVFilterContext *ctx, AVFrame *in, AVFrame *out); |
| 52 | 54 | } UnsharpContext; |
| @@ -47,15 +47,22 @@ · 46 unmodified lines | ||
| 47 | 47 | #include "libavutil/pixdesc.h" |
| 48 | 48 | #include "unsharp.h" |
| 49 | 49 | |
| 50 | −static void apply_unsharp( uint8_t *dst, int dst_stride, | |
| 51 | − const uint8_t *src, int src_stride, | |
| 52 | − int width, int height, UnsharpFilterParam *fp) | |
| 50 | +typedef struct TheadData { | |
| 51 | + 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 | 64 | uint32_t **sc = fp->sc; |
| 55 | − uint32_t sr[MAX_MATRIX_SIZE - 1], tmp1, tmp2; | |
| 56 | − | |
| 57 | − int32_t res; | |
| 58 | − int x, y, z; | |
| 65 | + uint32_t *sr = fp->sr; | |
| 59 | 66 | const uint8_t *src2 = NULL; //silence a warning |
| 60 | 67 | const int amount = fp->amount; |
| 61 | 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 | 70 | const int scalebits = fp->scalebits; |
| 64 | 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 | 88 | if (!amount) { |
| 67 | − av_image_copy_plane(dst, dst_stride, src, src_stride, width, height); | |
| 68 | − return; | |
| 89 | + av_image_copy_plane(dst + slice_start * dst_stride, dst_stride, | |
| 90 | + src + slice_start * src_stride, src_stride, | |
| 91 | + width, slice_end - slice_start); | |
| 92 | + return 0; | |
| 69 | 93 | } |
| 70 | 94 | |
| 71 | 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 | 106 | if (y < height) |
| 76 | 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 | 110 | for (x = -steps_x; x < width + steps_x; x++) { |
| 80 | 111 | tmp1 = x <= 0 ? src2[0] : x >= width ? src2[width-1] : src2[x]; |
| 81 | 112 | for (z = 0; z < steps_x * 2; z += 2) { |
| 82 | − tmp2 = sr[z + 0] + tmp1; sr[z + 0] = tmp1; | |
| 83 | − tmp1 = sr[z + 1] + tmp2; sr[z + 1] = tmp2; | |
| 113 | + tmp2 = sr[sr_offset + z + 0] + tmp1; sr[sr_offset + z + 0] = tmp1; | |
| 114 | + tmp1 = sr[sr_offset + z + 1] + tmp2; sr[sr_offset + z + 1] = tmp2; | |
| 84 | 115 | } |
| 85 | 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; | |
| 87 | − tmp1 = sc[z + 1][x + steps_x] + tmp2; sc[z + 1][x + steps_x] = tmp2; | |
| 117 | + tmp2 = sc[sc_offset + z + 0][x + steps_x] + tmp1; sc[sc_offset + z + 0][x + steps_x] = tmp1; | |
| 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 | 121 | const uint8_t *srx = src - steps_y * src_stride + x - steps_x; |
| 91 | 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 | 130 | src += src_stride; |
| 100 | 131 | } |
| 101 | 132 | } |
| 133 | + return 0; | |
| 102 | 134 | } |
| 103 | 135 | |
| 104 | 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 | 139 | UnsharpContext *s = ctx->priv; |
| 108 | 140 | int i, plane_w[3], plane_h[3]; |
| 109 | 141 | UnsharpFilterParam *fp[3]; |
| 142 | + ThreadData td; | |
| 143 | + | |
| 110 | 144 | plane_w[0] = inlink->w; |
| 111 | 145 | plane_w[1] = plane_w[2] = AV_CEIL_RSHIFT(inlink->w, s->hsub); |
| 112 | 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 | 148 | fp[0] = &s->luma; |
| 115 | 149 | fp[1] = fp[2] = &s->chroma; |
| 116 | 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 | 160 | return 0; |
| 120 | 161 | } |
| @@ -163,6 +204,7 @@ static int query_formats(AVFilterContext *ctx) · 42 unmodified lines | ||
| 163 | 204 | static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const char *effect_type, int width) |
| 164 | 205 | { |
| 165 | 206 | int z; |
| 207 | + UnsharpContext *s = ctx->priv; | |
| 166 | 208 | const char *effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen"; |
| 167 | 209 | |
| 168 | 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 | 217 | av_log(ctx, AV_LOG_VERBOSE, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", |
| 176 | 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 | 226 | if (!(fp->sc[z] = av_malloc_array(width + 2 * fp->steps_x, |
| 180 | 227 | sizeof(*(fp->sc[z]))))) |
| 181 | 228 | return AVERROR(ENOMEM); |
| @@ -192,6 +239,11 @@ static int config_props(AVFilterLink *link) · 10 unmodified lines | ||
| 192 | 239 | s->hsub = desc->log2_chroma_w; |
| 193 | 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 | 247 | ret = init_filter_param(link->dst, &s->luma, "luma", link->w); |
| 196 | 248 | if (ret < 0) |
| 197 | 249 | return ret; |
| @@ -202,20 +254,22 @@ static int config_props(AVFilterLink *link) · 4 unmodified lines | ||
| 202 | 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 | 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 | 262 | av_freep(&fp->sc[z]); |
| 263 | + av_freep(&fp->sc); | |
| 264 | + av_freep(&fp->sr); | |
| 211 | 265 | } |
| 212 | 266 | |
| 213 | 267 | static av_cold void uninit(AVFilterContext *ctx) |
| 214 | 268 | { |
| 215 | 269 | UnsharpContext *s = ctx->priv; |
| 216 | 270 | |
| 217 | − free_filter_param(&s->luma); | |
| 218 | − free_filter_param(&s->chroma); | |
| 271 | + free_filter_param(&s->luma, s->nb_threads); | |
| 272 | + free_filter_param(&s->chroma, s->nb_threads); | |
| 219 | 273 | } |
| 220 | 274 | |
| 221 | 275 | static int filter_frame(AVFilterLink *link, AVFrame *in) |
| @@ -294,5 +348,5 @@ AVFilter ff_vf_unsharp = { · 72 unmodified lines | ||
| 294 | 348 | .query_formats = query_formats, |
| 295 | 349 | .inputs = avfilter_vf_unsharp_inputs, |
| 296 | 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 | }; |