History

avfilter/vf_unsharp: enable slice threading

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).

Ruiling Song committed 7 years ago

Commit 94ceeba1 parent bbad0bc2 files · +81 −25Browse files

Unified · Split

2 files
  1. libavfilter/unsharp.h +3 −1
  2. libavfilter/vf_unsharp.c +78 −24
libavfilter/unsharp.h+3−1
@@ -37,7 +37,8 @@ typedef struct UnsharpFilterParam { · 36 unmodified lines
37 int steps_y; ///< vertical step count37 int steps_y; ///< vertical step count
38 int scalebits; ///< bits to shift pixel38 int scalebits; ///< bits to shift pixel
39 int32_t halfscale; ///< amount to add to pixel39 int32_t halfscale; ///< amount to add to pixel
40 uint32_t *sc[MAX_MATRIX_SIZE - 1]; ///< finite state machine storage40 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
43typedef struct UnsharpContext {44typedef 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;
libavfilter/vf_unsharp.c+78−24
@@ -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
50static void apply_unsharp( uint8_t *dst, int dst_stride,50typedef 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
60static 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 warning66 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
104static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out)136static 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
163static int init_filter_param(AVFilterContext *ctx, UnsharpFilterParam *fp, const char *effect_type, int width)204static 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
205static void free_filter_param(UnsharpFilterParam *fp)257static 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
213static av_cold void uninit(AVFilterContext *ctx)267static 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
221static int filter_frame(AVFilterLink *link, AVFrame *in)275static 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};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t