Unified · Split
| @@ -16,6 +16,7 @@ version <next>: · 15 unmodified lines | ||
| 16 | 16 | - atilt audio filter |
| 17 | 17 | - grayworld video filter |
| 18 | 18 | - AV1 Low overhead bitstream format muxer |
| 19 | +- swscale slice threading | |
| 19 | 20 | |
| 20 | 21 | |
| 21 | 22 | version 4.4: |
| @@ -81,6 +81,9 @@ static const AVOption swscale_options[] = { · 80 unmodified lines | ||
| 81 | 81 | { "uniform_color", "blend onto a uniform color", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ALPHA_BLEND_UNIFORM},INT_MIN, INT_MAX, VE, "alphablend" }, |
| 82 | 82 | { "checkerboard", "blend onto a checkerboard", 0, AV_OPT_TYPE_CONST, { .i64 = SWS_ALPHA_BLEND_CHECKERBOARD},INT_MIN, INT_MAX, VE, "alphablend" }, |
| 83 | 83 | |
| 84 | + { "threads", "number of threads", OFFSET(nb_threads), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, INT_MAX, VE, "threads" }, | |
| 85 | + { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, .flags = VE, "threads" }, | |
| 86 | + | |
| 84 | 87 | { NULL } |
| 85 | 88 | }; |
| 86 | 89 | |
| @@ -1113,6 +1113,9 @@ int sws_send_slice(struct SwsContext *c, unsigned int slice_start, · 1112 unmodified lines | ||
| 1113 | 1113 | |
| 1114 | 1114 | unsigned int sws_receive_slice_alignment(const struct SwsContext *c) |
| 1115 | 1115 | { |
| 1116 | + if (c->slice_ctx) | |
| 1117 | + return c->slice_ctx[0]->dst_slice_align; | |
| 1118 | + | |
| 1116 | 1119 | return c->dst_slice_align; |
| 1117 | 1120 | } |
| 1118 | 1121 | |
| @@ -1136,6 +1139,27 @@ int sws_receive_slice(struct SwsContext *c, unsigned int slice_start, · 17 unmodified lines | ||
| 1136 | 1139 | return AVERROR(EINVAL); |
| 1137 | 1140 | } |
| 1138 | 1141 | |
| 1142 | + if (c->slicethread) { | |
| 1143 | + int nb_jobs = c->slice_ctx[0]->dither == SWS_DITHER_ED ? 1 : c->nb_slice_ctx; | |
| 1144 | + int ret = 0; | |
| 1145 | + | |
| 1146 | + c->dst_slice_start = slice_start; | |
| 1147 | + c->dst_slice_height = slice_height; | |
| 1148 | + | |
| 1149 | + avpriv_slicethread_execute(c->slicethread, nb_jobs, 0); | |
| 1150 | + | |
| 1151 | + for (int i = 0; i < c->nb_slice_ctx; i++) { | |
| 1152 | + if (c->slice_err[i] < 0) { | |
| 1153 | + ret = c->slice_err[i]; | |
| 1154 | + break; | |
| 1155 | + } | |
| 1156 | + } | |
| 1157 | + | |
| 1158 | + memset(c->slice_err, 0, c->nb_slice_ctx * sizeof(*c->slice_err)); | |
| 1159 | + | |
| 1160 | + return ret; | |
| 1161 | + } | |
| 1162 | + | |
| 1139 | 1163 | for (int i = 0; i < FF_ARRAY_ELEMS(dst) && c->frame_dst->data[i]; i++) { |
| 1140 | 1164 | dst[i] = c->frame_dst->data[i] + |
| 1141 | 1165 | c->frame_dst->linesize[i] * (slice_start >> c->chrDstVSubSample); |
| @@ -1173,6 +1197,41 @@ int attribute_align_arg sws_scale(struct SwsContext *c, · 31 unmodified lines | ||
| 1173 | 1197 | int srcSliceH, uint8_t *const dst[], |
| 1174 | 1198 | const int dstStride[]) |
| 1175 | 1199 | { |
| 1200 | + if (c->nb_slice_ctx) | |
| 1201 | + c = c->slice_ctx[0]; | |
| 1202 | + | |
| 1176 | 1203 | return scale_internal(c, srcSlice, srcStride, srcSliceY, srcSliceH, |
| 1177 | 1204 | dst, dstStride, 0, c->dstH); |
| 1178 | 1205 | } |
| 1206 | + | |
| 1207 | +void ff_sws_slice_worker(void *priv, int jobnr, int threadnr, | |
| 1208 | + int nb_jobs, int nb_threads) | |
| 1209 | +{ | |
| 1210 | + SwsContext *parent = priv; | |
| 1211 | + SwsContext *c = parent->slice_ctx[threadnr]; | |
| 1212 | + | |
| 1213 | + const int slice_height = FFALIGN(FFMAX((parent->dst_slice_height + nb_jobs - 1) / nb_jobs, 1), | |
| 1214 | + c->dst_slice_align); | |
| 1215 | + const int slice_start = jobnr * slice_height; | |
| 1216 | + const int slice_end = FFMIN((jobnr + 1) * slice_height, parent->dst_slice_height); | |
| 1217 | + int err = 0; | |
| 1218 | + | |
| 1219 | + if (slice_end > slice_start) { | |
| 1220 | + uint8_t *dst[4] = { NULL }; | |
| 1221 | + | |
| 1222 | + for (int i = 0; i < FF_ARRAY_ELEMS(dst) && parent->frame_dst->data[i]; i++) { | |
| 1223 | + const int vshift = (i == 1 || i == 2) ? c->chrDstVSubSample : 0; | |
| 1224 | + const ptrdiff_t offset = parent->frame_dst->linesize[i] * | |
| 1225 | + ((slice_start + parent->dst_slice_start) >> vshift); | |
| 1226 | + | |
| 1227 | + dst[i] = parent->frame_dst->data[i] + offset; | |
| 1228 | + } | |
| 1229 | + | |
| 1230 | + err = scale_internal(c, (const uint8_t * const *)parent->frame_src->data, | |
| 1231 | + parent->frame_src->linesize, 0, c->srcH, | |
| 1232 | + dst, parent->frame_dst->linesize, | |
| 1233 | + parent->dst_slice_start + slice_start, slice_end - slice_start); | |
| 1234 | + } | |
| 1235 | + | |
| 1236 | + parent->slice_err[threadnr] = err; | |
| 1237 | +} | |
| @@ -33,6 +33,7 @@ · 32 unmodified lines | ||
| 33 | 33 | #include "libavutil/mem_internal.h" |
| 34 | 34 | #include "libavutil/pixfmt.h" |
| 35 | 35 | #include "libavutil/pixdesc.h" |
| 36 | +#include "libavutil/slicethread.h" | |
| 36 | 37 | #include "libavutil/ppc/util_altivec.h" |
| 37 | 38 | |
| 38 | 39 | #define STR(s) AV_TOSTRING(s) // AV_STRINGIFY is too long |
| @@ -300,6 +301,15 @@ typedef struct SwsContext { · 261 unmodified lines | ||
| 300 | 301 | */ |
| 301 | 302 | const AVClass *av_class; |
| 302 | 303 | |
| 304 | + AVSliceThread *slicethread; | |
| 305 | + struct SwsContext **slice_ctx; | |
| 306 | + int *slice_err; | |
| 307 | + int nb_slice_ctx; | |
| 308 | + | |
| 309 | + // values passed to current sws_receive_slice() call | |
| 310 | + unsigned int dst_slice_start; | |
| 311 | + unsigned int dst_slice_height; | |
| 312 | + | |
| 303 | 313 | /** |
| 304 | 314 | * Note that src, dst, srcStride, dstStride will be copied in the |
| 305 | 315 | * sws_scale() wrapper so they can be freely modified here. |
| @@ -325,6 +335,7 @@ typedef struct SwsContext { · 19 unmodified lines | ||
| 325 | 335 | int chrDstVSubSample; ///< Binary logarithm of vertical subsampling factor between luma/alpha and chroma planes in destination image. |
| 326 | 336 | int vChrDrop; ///< Binary logarithm of extra vertical subsampling factor in source image chroma planes specified by user. |
| 327 | 337 | int sliceDir; ///< Direction that slices are fed to the scaler (1 = top-to-bottom, -1 = bottom-to-top). |
| 338 | + int nb_threads; ///< Number of threads used for scaling | |
| 328 | 339 | double param[2]; ///< Input parameters for scaling algorithms that need them. |
| 329 | 340 | |
| 330 | 341 | AVFrame *frame_src; |
| @@ -1082,6 +1093,9 @@ void ff_init_vscale_pfn(SwsContext *c, yuv2planar1_fn yuv2plane1, yuv2planarX_fn · 751 unmodified lines | ||
| 1082 | 1093 | yuv2interleavedX_fn yuv2nv12cX, yuv2packed1_fn yuv2packed1, yuv2packed2_fn yuv2packed2, |
| 1083 | 1094 | yuv2packedX_fn yuv2packedX, yuv2anyX_fn yuv2anyX, int use_mmx); |
| 1084 | 1095 | |
| 1096 | +void ff_sws_slice_worker(void *priv, int jobnr, int threadnr, | |
| 1097 | + int nb_jobs, int nb_threads); | |
| 1098 | + | |
| 1085 | 1099 | //number of extra lines to process |
| 1086 | 1100 | #define MAX_LINES_AHEAD 4 |
| 1087 | 1101 | |
| @@ -49,6 +49,7 @@ · 48 unmodified lines | ||
| 49 | 49 | #include "libavutil/mathematics.h" |
| 50 | 50 | #include "libavutil/opt.h" |
| 51 | 51 | #include "libavutil/pixdesc.h" |
| 52 | +#include "libavutil/slicethread.h" | |
| 52 | 53 | #include "libavutil/thread.h" |
| 53 | 54 | #include "libavutil/aarch64/cpu.h" |
| 54 | 55 | #include "libavutil/ppc/cpu.h" |
| @@ -871,6 +872,18 @@ int sws_setColorspaceDetails(struct SwsContext *c, const int inv_table[4], · 816 unmodified lines | ||
| 871 | 872 | const AVPixFmtDescriptor *desc_src; |
| 872 | 873 | int need_reinit = 0; |
| 873 | 874 | |
| 875 | + if (c->nb_slice_ctx) { | |
| 876 | + for (int i = 0; i < c->nb_slice_ctx; i++) { | |
| 877 | + int ret = sws_setColorspaceDetails(c->slice_ctx[i], inv_table, | |
| 878 | + srcRange, table, dstRange, | |
| 879 | + brightness, contrast, saturation); | |
| 880 | + if (ret < 0) | |
| 881 | + return ret; | |
| 882 | + } | |
| 883 | + | |
| 884 | + return 0; | |
| 885 | + } | |
| 886 | + | |
| 874 | 887 | handle_formats(c); |
| 875 | 888 | desc_dst = av_pix_fmt_desc_get(c->dstFormat); |
| 876 | 889 | desc_src = av_pix_fmt_desc_get(c->srcFormat); |
| @@ -1005,6 +1018,12 @@ int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table, · 128 unmodified lines | ||
| 1005 | 1018 | if (!c ) |
| 1006 | 1019 | return -1; |
| 1007 | 1020 | |
| 1021 | + if (c->nb_slice_ctx) { | |
| 1022 | + return sws_getColorspaceDetails(c->slice_ctx[0], inv_table, srcRange, | |
| 1023 | + table, dstRange, brightness, contrast, | |
| 1024 | + saturation); | |
| 1025 | + } | |
| 1026 | + | |
| 1008 | 1027 | *inv_table = c->srcColorspaceTable; |
| 1009 | 1028 | *table = c->dstColorspaceTable; |
| 1010 | 1029 | *srcRange = range_override_needed(c->srcFormat) ? 1 : c->srcRange; |
| @@ -1170,6 +1189,58 @@ static enum AVPixelFormat alphaless_fmt(enum AVPixelFormat fmt) · 159 unmodified lines | ||
| 1170 | 1189 | } |
| 1171 | 1190 | } |
| 1172 | 1191 | |
| 1192 | +static int context_init_threaded(SwsContext *c, | |
| 1193 | + SwsFilter *src_filter, SwsFilter *dst_filter) | |
| 1194 | +{ | |
| 1195 | + int ret; | |
| 1196 | + | |
| 1197 | + ret = avpriv_slicethread_create(&c->slicethread, (void*)c, | |
| 1198 | + ff_sws_slice_worker, NULL, c->nb_threads); | |
| 1199 | + if (ret == AVERROR(ENOSYS)) { | |
| 1200 | + c->nb_threads = 1; | |
| 1201 | + return 0; | |
| 1202 | + } else if (ret < 0) | |
| 1203 | + return ret; | |
| 1204 | + | |
| 1205 | + c->nb_threads = ret; | |
| 1206 | + | |
| 1207 | + c->slice_ctx = av_mallocz_array(c->nb_threads, sizeof(*c->slice_ctx)); | |
| 1208 | + c->slice_err = av_mallocz_array(c->nb_threads, sizeof(*c->slice_err)); | |
| 1209 | + if (!c->slice_ctx || !c->slice_err) | |
| 1210 | + return AVERROR(ENOMEM); | |
| 1211 | + | |
| 1212 | + for (int i = 0; i < c->nb_threads; i++) { | |
| 1213 | + c->slice_ctx[i] = sws_alloc_context(); | |
| 1214 | + if (!c->slice_ctx[i]) | |
| 1215 | + return AVERROR(ENOMEM); | |
| 1216 | + | |
| 1217 | + ret = av_opt_copy((void*)c->slice_ctx[i], (void*)c); | |
| 1218 | + if (ret < 0) | |
| 1219 | + return ret; | |
| 1220 | + | |
| 1221 | + c->slice_ctx[i]->nb_threads = 1; | |
| 1222 | + | |
| 1223 | + ret = sws_init_context(c->slice_ctx[i], src_filter, dst_filter); | |
| 1224 | + if (ret < 0) | |
| 1225 | + return ret; | |
| 1226 | + | |
| 1227 | + c->nb_slice_ctx++; | |
| 1228 | + | |
| 1229 | + if (c->slice_ctx[i]->dither == SWS_DITHER_ED) { | |
| 1230 | + av_log(c, AV_LOG_VERBOSE, | |
| 1231 | + "Error-diffusion dither is in use, scaling will be single-threaded."); | |
| 1232 | + break; | |
| 1233 | + } | |
| 1234 | + } | |
| 1235 | + | |
| 1236 | + c->frame_src = av_frame_alloc(); | |
| 1237 | + c->frame_dst = av_frame_alloc(); | |
| 1238 | + if (!c->frame_src || !c->frame_dst) | |
| 1239 | + return AVERROR(ENOMEM); | |
| 1240 | + | |
| 1241 | + return 0; | |
| 1242 | +} | |
| 1243 | + | |
| 1173 | 1244 | av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, |
| 1174 | 1245 | SwsFilter *dstFilter) |
| 1175 | 1246 | { |
| @@ -1192,6 +1263,13 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter, · 16 unmodified lines | ||
| 1192 | 1263 | static const float float_mult = 1.0f / 255.0f; |
| 1193 | 1264 | static AVOnce rgb2rgb_once = AV_ONCE_INIT; |
| 1194 | 1265 | |
| 1266 | + if (c->nb_threads != 1) { | |
| 1267 | + ret = context_init_threaded(c, srcFilter, dstFilter); | |
| 1268 | + if (ret < 0 || c->nb_threads > 1) | |
| 1269 | + return ret; | |
| 1270 | + // threading disabled in this build, init as single-threaded | |
| 1271 | + } | |
| 1272 | + | |
| 1195 | 1273 | cpu_flags = av_get_cpu_flags(); |
| 1196 | 1274 | flags = c->flags; |
| 1197 | 1275 | emms_c(); |
| @@ -2254,6 +2332,13 @@ void sws_freeContext(SwsContext *c) · 1056 unmodified lines | ||
| 2254 | 2332 | if (!c) |
| 2255 | 2333 | return; |
| 2256 | 2334 | |
| 2335 | + for (i = 0; i < c->nb_slice_ctx; i++) | |
| 2336 | + sws_freeContext(c->slice_ctx[i]); | |
| 2337 | + av_freep(&c->slice_ctx); | |
| 2338 | + av_freep(&c->slice_err); | |
| 2339 | + | |
| 2340 | + avpriv_slicethread_free(&c->slicethread); | |
| 2341 | + | |
| 2257 | 2342 | for (i = 0; i < 4; i++) |
| 2258 | 2343 | av_freep(&c->dither_error[i]); |
| 2259 | 2344 | |