| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (C) 2024 Niklas Haas | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public | ||
| 8 | * License as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 14 | * Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public | ||
| 17 | * License along with FFmpeg; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include "libavutil/avassert.h" | ||
| 22 | #include "libavutil/cpu.h" | ||
| 23 | #include "libavutil/error.h" | ||
| 24 | #include "libavutil/hwcontext.h" | ||
| 25 | #include "libavutil/imgutils.h" | ||
| 26 | #include "libavutil/macros.h" | ||
| 27 | #include "libavutil/mem.h" | ||
| 28 | #include "libavutil/opt.h" | ||
| 29 | #include "libavutil/pixdesc.h" | ||
| 30 | #include "libavutil/refstruct.h" | ||
| 31 | #include "libavutil/slicethread.h" | ||
| 32 | |||
| 33 | #include "libswscale/swscale.h" | ||
| 34 | #include "libswscale/format.h" | ||
| 35 | |||
| 36 | #include "cms.h" | ||
| 37 | #include "lut3d.h" | ||
| 38 | #include "swscale_internal.h" | ||
| 39 | #include "graph.h" | ||
| 40 | #include "ops.h" | ||
| 41 | #include "ops_dispatch.h" | ||
| 42 | #if CONFIG_VULKAN | ||
| 43 | #include "vulkan/ops.h" | ||
| 44 | #endif | ||
| 45 | |||
| 46 | 224858 | int ff_sws_pass_aligned_width(const SwsPass *pass, int width) | |
| 47 | { | ||
| 48 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 224858 times.
|
224858 | if (!pass) |
| 49 | ✗ | return width; | |
| 50 | |||
| 51 | 224858 | size_t aligned_w = width; | |
| 52 | 224858 | aligned_w = FFALIGN(aligned_w, pass->output->width_align); | |
| 53 | 224858 | aligned_w += pass->output->width_pad; | |
| 54 |
1/2✓ Branch 0 taken 224858 times.
✗ Branch 1 not taken.
|
224858 | return aligned_w <= INT_MAX ? aligned_w : width; |
| 55 | } | ||
| 56 | |||
| 57 | /* Allocates (or refs) one buffer per plane */ | ||
| 58 | 33156 | static int frame_alloc_planes_ref(AVFrame *dst, const AVFrame *src, | |
| 59 | const int plane_copy[4]) | ||
| 60 | { | ||
| 61 | 33156 | int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX, | |
| 62 | 33156 | dst->format, 0, NULL); | |
| 63 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
|
33156 | if (ret < 0) |
| 64 | ✗ | return ret; | |
| 65 | |||
| 66 | 33156 | const int align = av_cpu_max_align(); | |
| 67 | 33156 | const int aligned_w = FFALIGN(dst->width + 1, align); /* add space for over-write */ | |
| 68 | 33156 | ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w); | |
| 69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
|
33156 | if (ret < 0) |
| 70 | ✗ | return ret; | |
| 71 | |||
| 72 | ptrdiff_t linesize1[4]; | ||
| 73 |
2/2✓ Branch 0 taken 132624 times.
✓ Branch 1 taken 33156 times.
|
165780 | for (int i = 0; i < 4; i++) |
| 74 | 132624 | linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align); | |
| 75 | |||
| 76 | size_t sizes[4]; | ||
| 77 | 33156 | ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1); | |
| 78 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
|
33156 | if (ret < 0) |
| 79 | ✗ | return ret; | |
| 80 | |||
| 81 |
2/2✓ Branch 0 taken 120296 times.
✓ Branch 1 taken 3207 times.
|
123503 | for (int i = 0; i < 4; i++) { |
| 82 |
2/2✓ Branch 0 taken 29949 times.
✓ Branch 1 taken 90347 times.
|
120296 | if (!sizes[i]) |
| 83 | 29949 | break; | |
| 84 | 90347 | int src_idx = plane_copy[i]; | |
| 85 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 90347 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
90347 | if (src_idx >= 0 && src) { |
| 86 | /* Ref the source plane instead of allocating a new buffer */ | ||
| 87 | ✗ | dst->buf[i] = av_buffer_ref(src->buf[src_idx]); | |
| 88 | ✗ | if (!dst->buf[i]) | |
| 89 | ✗ | return AVERROR(ENOMEM); | |
| 90 | ✗ | dst->data[i] = src->data[src_idx]; | |
| 91 | ✗ | dst->linesize[i] = src->linesize[src_idx]; | |
| 92 | ✗ | continue; | |
| 93 | } | ||
| 94 | |||
| 95 | 90347 | AVBufferRef *buf = av_buffer_alloc(sizes[i]); | |
| 96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 90347 times.
|
90347 | if (!buf) |
| 97 | ✗ | return AVERROR(ENOMEM); | |
| 98 | 90347 | dst->data[i] = buf->data; | |
| 99 | 90347 | dst->buf[i] = buf; | |
| 100 | } | ||
| 101 | |||
| 102 | 33156 | return 0; | |
| 103 | } | ||
| 104 | |||
| 105 | #if CONFIG_VULKAN | ||
| 106 | static int pass_alloc_output_hw(SwsPass *pass, AVFrame *avframe, | ||
| 107 | AVBufferRef *dev_ref) | ||
| 108 | { | ||
| 109 | SwsPassBuffer *buffer = pass->output; | ||
| 110 | AVBufferRef *frames_ref = av_hwframe_ctx_alloc(dev_ref); | ||
| 111 | if (!frames_ref) | ||
| 112 | return AVERROR(ENOMEM); | ||
| 113 | |||
| 114 | AVHWFramesContext *hwfc = (AVHWFramesContext *)frames_ref->data; | ||
| 115 | hwfc->format = AV_PIX_FMT_VULKAN; | ||
| 116 | hwfc->sw_format = pass->format; | ||
| 117 | hwfc->width = buffer->width; | ||
| 118 | hwfc->height = buffer->height; | ||
| 119 | |||
| 120 | int ret = av_hwframe_ctx_init(frames_ref); | ||
| 121 | if (ret >= 0) { | ||
| 122 | avframe->format = AV_PIX_FMT_VULKAN; | ||
| 123 | ret = av_hwframe_get_buffer(frames_ref, avframe, 0); | ||
| 124 | } | ||
| 125 | av_buffer_unref(&frames_ref); | ||
| 126 | return ret; | ||
| 127 | } | ||
| 128 | #endif | ||
| 129 | |||
| 130 | 126848 | static int pass_alloc_output(SwsPass *pass) | |
| 131 | { | ||
| 132 |
3/4✓ Branch 0 taken 33156 times.
✓ Branch 1 taken 93692 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 33156 times.
|
126848 | if (!pass || pass->output->avframe) |
| 133 | 93692 | return 0; | |
| 134 | |||
| 135 | 33156 | SwsPassBuffer *buffer = pass->output; | |
| 136 | 33156 | AVFrame *avframe = av_frame_alloc(); | |
| 137 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
|
33156 | if (!avframe) |
| 138 | ✗ | return AVERROR(ENOMEM); | |
| 139 | 33156 | avframe->width = buffer->width; | |
| 140 | 33156 | avframe->height = buffer->height; | |
| 141 | |||
| 142 | int ret; | ||
| 143 | |||
| 144 | #if CONFIG_VULKAN | ||
| 145 | const SwsGraph *graph = pass->graph; | ||
| 146 | if (graph->src.hw_format == AV_PIX_FMT_VULKAN && | ||
| 147 | graph->dst.hw_format == AV_PIX_FMT_VULKAN) { | ||
| 148 | AVBufferRef *dev_ref = ff_sws_vk_device_ref(graph->ctx); | ||
| 149 | if (dev_ref) { | ||
| 150 | ret = pass_alloc_output_hw(pass, avframe, dev_ref); | ||
| 151 | if (ret >= 0) | ||
| 152 | goto done; | ||
| 153 | av_frame_unref(avframe); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | #endif | ||
| 157 | |||
| 158 | 33156 | const AVFrame *src = NULL; | |
| 159 |
2/2✓ Branch 0 taken 9638 times.
✓ Branch 1 taken 23518 times.
|
33156 | if (pass->input) |
| 160 | 9638 | src = pass->input->output->avframe; | |
| 161 | |||
| 162 | 33156 | avframe->format = pass->format; | |
| 163 | 33156 | ret = frame_alloc_planes_ref(avframe, src, buffer->plane_copy); | |
| 164 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33156 times.
|
33156 | if (ret < 0) { |
| 165 | ✗ | av_frame_free(&avframe); | |
| 166 | ✗ | return ret; | |
| 167 | } | ||
| 168 | |||
| 169 | #if CONFIG_VULKAN | ||
| 170 | done: | ||
| 171 | #endif | ||
| 172 | 33156 | buffer->avframe = avframe; | |
| 173 | 33156 | ff_sws_frame_from_avframe(&buffer->frame, avframe); | |
| 174 | 33156 | return 0; | |
| 175 | } | ||
| 176 | |||
| 177 | 126856 | static void free_buffer(AVRefStructOpaque opaque, void *obj) | |
| 178 | { | ||
| 179 | 126856 | SwsPassBuffer *buffer = obj; | |
| 180 | 126856 | av_frame_free(&buffer->avframe); | |
| 181 | 126856 | } | |
| 182 | |||
| 183 | 126856 | static void pass_free(SwsPass *pass) | |
| 184 | { | ||
| 185 |
2/2✓ Branch 0 taken 125400 times.
✓ Branch 1 taken 1456 times.
|
126856 | if (pass->free) |
| 186 | 125400 | pass->free(pass->priv); | |
| 187 | 126856 | av_refstruct_unref(&pass->output); | |
| 188 | 126856 | av_free(pass); | |
| 189 | 126856 | } | |
| 190 | |||
| 191 | 126856 | int ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt, | |
| 192 | int width, int height, SwsPass *input, | ||
| 193 | int lines, int align, | ||
| 194 | SwsPassFunc run, SwsPassSetup setup, | ||
| 195 | void *priv, void (*free_cb)(void *priv), | ||
| 196 | SwsPass **out_pass) | ||
| 197 | { | ||
| 198 | int ret; | ||
| 199 | 126856 | SwsPass *pass = av_mallocz(sizeof(*pass)); | |
| 200 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
|
126856 | if (!pass) { |
| 201 | ✗ | if (free_cb) | |
| 202 | ✗ | free_cb(priv); | |
| 203 | ✗ | return AVERROR(ENOMEM); | |
| 204 | } | ||
| 205 | |||
| 206 |
1/2✓ Branch 0 taken 126856 times.
✗ Branch 1 not taken.
|
126856 | if (!lines) |
| 207 | 126856 | lines = height; | |
| 208 | |||
| 209 | 126856 | pass->graph = graph; | |
| 210 | 126856 | pass->run = run; | |
| 211 | 126856 | pass->setup = setup; | |
| 212 | 126856 | pass->priv = priv; | |
| 213 | 126856 | pass->free = free_cb; | |
| 214 | 126856 | pass->format = fmt; | |
| 215 | 126856 | pass->lines = lines; | |
| 216 | 126856 | pass->input = input; | |
| 217 | 126856 | pass->output = av_refstruct_alloc_ext(sizeof(*pass->output), 0, NULL, free_buffer); | |
| 218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
|
126856 | if (!pass->output) { |
| 219 | ✗ | ret = AVERROR(ENOMEM); | |
| 220 | ✗ | goto fail; | |
| 221 | } | ||
| 222 | |||
| 223 | 126856 | pass->output->height = height; | |
| 224 | 126856 | pass->output->width = width; | |
| 225 | 126856 | pass->output->width_align = 1; | |
| 226 | 126856 | memset(pass->output->plane_copy, -1, sizeof(pass->output->plane_copy)); | |
| 227 | |||
| 228 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 126837 times.
|
126856 | if (!align) { |
| 229 | 19 | pass->slice_h = pass->lines; | |
| 230 | 19 | pass->num_slices = 1; | |
| 231 | } else { | ||
| 232 | 126837 | pass->slice_h = (pass->lines + graph->num_threads - 1) / graph->num_threads; | |
| 233 | 126837 | pass->slice_h = FFALIGN(pass->slice_h, align); | |
| 234 | 126837 | pass->num_slices = (pass->lines + pass->slice_h - 1) / pass->slice_h; | |
| 235 | } | ||
| 236 | |||
| 237 | 126856 | ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass); | |
| 238 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126856 times.
|
126856 | if (ret < 0) |
| 239 | ✗ | goto fail; | |
| 240 | |||
| 241 | 126856 | *out_pass = pass; | |
| 242 | 126856 | return 0; | |
| 243 | |||
| 244 | ✗ | fail: | |
| 245 | ✗ | pass_free(pass); | |
| 246 | ✗ | return ret; | |
| 247 | } | ||
| 248 | |||
| 249 | 85890 | void ff_sws_pass_link_output(SwsPass *dst, const SwsPass *src) | |
| 250 | { | ||
| 251 |
4/6✓ Branch 0 taken 85890 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8948 times.
✓ Branch 3 taken 76942 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8948 times.
|
85890 | if (!dst || !src || dst == src) |
| 252 | 76942 | return; | |
| 253 | |||
| 254 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8948 times.
|
8948 | av_assert0(dst->format == src->format); |
| 255 | 8948 | SwsPassBuffer *keep = src->output, *drop = dst->output; | |
| 256 | |||
| 257 | av_assert1(keep->width == drop->width); | ||
| 258 | av_assert1(keep->height == drop->height); | ||
| 259 | 8948 | keep->width_align = FFMAX(keep->width_align, drop->width_align); | |
| 260 | 8948 | keep->width_pad = FFMAX(keep->width_pad, drop->width_pad); | |
| 261 | |||
| 262 |
2/2✓ Branch 0 taken 35792 times.
✓ Branch 1 taken 8948 times.
|
44740 | for (int i = 0; i < FF_ARRAY_ELEMS(keep->plane_copy); i++) { |
| 263 |
2/2✓ Branch 0 taken 35784 times.
✓ Branch 1 taken 8 times.
|
35792 | if (keep->plane_copy[i] < 0) |
| 264 | 35784 | keep->plane_copy[i] = drop->plane_copy[i]; | |
| 265 | 8 | else if (drop->plane_copy[i] >= 0) | |
| 266 | av_assert1(keep->plane_copy[i] == drop->plane_copy[i]); | ||
| 267 | } | ||
| 268 | |||
| 269 | 8948 | av_refstruct_replace(&dst->output, src->output); | |
| 270 | } | ||
| 271 | |||
| 272 | 680762 | static void frame_shift(const SwsFrame *f, const int y, uint8_t *data[4]) | |
| 273 | { | ||
| 274 |
2/2✓ Branch 0 taken 2723048 times.
✓ Branch 1 taken 680762 times.
|
3403810 | for (int i = 0; i < 4; i++) { |
| 275 |
2/2✓ Branch 0 taken 1582347 times.
✓ Branch 1 taken 1140701 times.
|
2723048 | if (f->data[i]) |
| 276 | 1582347 | data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i]; | |
| 277 | else | ||
| 278 | 1140701 | data[i] = NULL; | |
| 279 | } | ||
| 280 | 680762 | } | |
| 281 | |||
| 282 | ✗ | static void run_copy(const SwsFrame *out, const SwsFrame *in, int y, int h, | |
| 283 | const SwsPass *pass) | ||
| 284 | { | ||
| 285 | uint8_t *in_data[4], *out_data[4]; | ||
| 286 | ✗ | frame_shift(in, y, in_data); | |
| 287 | ✗ | frame_shift(out, y, out_data); | |
| 288 | |||
| 289 | ✗ | for (int i = 0; i < 4 && out_data[i]; i++) { | |
| 290 | ✗ | const int lines = h >> ff_fmt_vshift(in->format, i); | |
| 291 | av_assert1(in_data[i]); | ||
| 292 | |||
| 293 | ✗ | if (in_data[i] == out_data[i]) { | |
| 294 | ✗ | av_assert0(in->linesize[i] == out->linesize[i]); | |
| 295 | ✗ | } else if (in->linesize[i] == out->linesize[i]) { | |
| 296 | ✗ | memcpy(out_data[i], in_data[i], lines * out->linesize[i]); | |
| 297 | } else { | ||
| 298 | ✗ | const int linesize = FFMIN(out->linesize[i], in->linesize[i]); | |
| 299 | ✗ | for (int j = 0; j < lines; j++) { | |
| 300 | ✗ | memcpy(out_data[i], in_data[i], linesize); | |
| 301 | ✗ | in_data[i] += in->linesize[i]; | |
| 302 | ✗ | out_data[i] += out->linesize[i]; | |
| 303 | } | ||
| 304 | } | ||
| 305 | } | ||
| 306 | ✗ | } | |
| 307 | |||
| 308 | 1213 | static void run_rgb0(const SwsFrame *out, const SwsFrame *in, int y, int h, | |
| 309 | const SwsPass *pass) | ||
| 310 | { | ||
| 311 | 1213 | SwsInternal *c = pass->priv; | |
| 312 | 1213 | const int x0 = c->src0Alpha - 1; | |
| 313 | 1213 | const int w4 = 4 * out->width; | |
| 314 | 1213 | const int src_stride = in->linesize[0]; | |
| 315 | 1213 | const int dst_stride = out->linesize[0]; | |
| 316 | 1213 | const uint8_t *src = in->data[0] + y * src_stride; | |
| 317 | 1213 | uint8_t *dst = out->data[0] + y * dst_stride; | |
| 318 | |||
| 319 |
2/2✓ Branch 0 taken 116598 times.
✓ Branch 1 taken 1213 times.
|
117811 | for (int y = 0; y < h; y++) { |
| 320 | 116598 | memcpy(dst, src, w4 * sizeof(*dst)); | |
| 321 |
2/2✓ Branch 0 taken 13606866 times.
✓ Branch 1 taken 116598 times.
|
13723464 | for (int x = x0; x < w4; x += 4) |
| 322 | 13606866 | dst[x] = 0xFF; | |
| 323 | |||
| 324 | 116598 | src += src_stride; | |
| 325 | 116598 | dst += dst_stride; | |
| 326 | } | ||
| 327 | 1213 | } | |
| 328 | |||
| 329 | 2579 | static void run_xyz2rgb(const SwsFrame *out, const SwsFrame *in, int y, int h, | |
| 330 | const SwsPass *pass) | ||
| 331 | { | ||
| 332 | 2579 | const SwsInternal *c = pass->priv; | |
| 333 | 2579 | c->xyz12Torgb48(c, out->data[0] + y * out->linesize[0], out->linesize[0], | |
| 334 | 2579 | in->data[0] + y * in->linesize[0], in->linesize[0], | |
| 335 | 2579 | out->width, h); | |
| 336 | 2579 | } | |
| 337 | |||
| 338 | 2644 | static void run_rgb2xyz(const SwsFrame *out, const SwsFrame *in, int y, int h, | |
| 339 | const SwsPass *pass) | ||
| 340 | { | ||
| 341 | 2644 | const SwsInternal *c = pass->priv; | |
| 342 | 2644 | c->rgb48Toxyz12(c, out->data[0] + y * out->linesize[0], out->linesize[0], | |
| 343 | 2644 | in->data[0] + y * in->linesize[0], in->linesize[0], | |
| 344 | 2644 | out->width, h); | |
| 345 | 2644 | } | |
| 346 | |||
| 347 | /*********************************************************************** | ||
| 348 | * Internal ff_swscale() wrapper. This reuses the legacy scaling API. * | ||
| 349 | * This is considered fully deprecated, and will be replaced by a full * | ||
| 350 | * reimplementation ASAP. * | ||
| 351 | ***********************************************************************/ | ||
| 352 | |||
| 353 | 39510 | static void free_legacy_swscale(void *priv) | |
| 354 | { | ||
| 355 | 39510 | SwsContext *sws = priv; | |
| 356 | 39510 | sws_free_context(&sws); | |
| 357 | 39510 | } | |
| 358 | |||
| 359 | 140081 | static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in, | |
| 360 | const SwsPass *pass) | ||
| 361 | { | ||
| 362 | 140081 | SwsContext *sws = pass->priv; | |
| 363 | 140081 | SwsInternal *c = sws_internal(sws); | |
| 364 |
5/6✓ Branch 0 taken 132786 times.
✓ Branch 1 taken 7295 times.
✓ Branch 2 taken 883 times.
✓ Branch 3 taken 131903 times.
✓ Branch 4 taken 883 times.
✗ Branch 5 not taken.
|
140081 | if (sws->flags & SWS_BITEXACT && sws->dither == SWS_DITHER_ED && c->dither_error[0]) { |
| 365 |
2/2✓ Branch 0 taken 3532 times.
✓ Branch 1 taken 883 times.
|
4415 | for (int i = 0; i < 4; i++) |
| 366 | 3532 | memset(c->dither_error[i], 0, sizeof(c->dither_error[0][0]) * (sws->dst_w + 2)); | |
| 367 | } | ||
| 368 | |||
| 369 |
2/2✓ Branch 1 taken 6214 times.
✓ Branch 2 taken 133867 times.
|
140081 | if (usePal(sws->src_format)) |
| 370 | 6214 | ff_update_palette(c, (const uint32_t *) in->data[1]); | |
| 371 | |||
| 372 | 140081 | return 0; | |
| 373 | } | ||
| 374 | |||
| 375 | 680762 | static inline SwsContext *slice_ctx(const SwsPass *pass, int y) | |
| 376 | { | ||
| 377 | 680762 | SwsContext *sws = pass->priv; | |
| 378 | 680762 | SwsInternal *parent = sws_internal(sws); | |
| 379 |
2/2✓ Branch 0 taken 72356 times.
✓ Branch 1 taken 608406 times.
|
680762 | if (pass->num_slices == 1) |
| 380 | 72356 | return sws; | |
| 381 | |||
| 382 | av_assert1(parent->nb_slice_ctx == pass->num_slices); | ||
| 383 | 608406 | sws = parent->slice_ctx[y / pass->slice_h]; | |
| 384 | |||
| 385 |
2/2✓ Branch 1 taken 35126 times.
✓ Branch 2 taken 573280 times.
|
608406 | if (usePal(sws->src_format)) { |
| 386 | 35126 | SwsInternal *sub = sws_internal(sws); | |
| 387 | 35126 | memcpy(sub->pal_yuv, parent->pal_yuv, sizeof(sub->pal_yuv)); | |
| 388 | 35126 | memcpy(sub->pal_rgb, parent->pal_rgb, sizeof(sub->pal_rgb)); | |
| 389 | } | ||
| 390 | |||
| 391 | 608406 | return sws; | |
| 392 | } | ||
| 393 | |||
| 394 | 97267 | static void run_legacy_unscaled(const SwsFrame *out, const SwsFrame *in, | |
| 395 | int y, int h, const SwsPass *pass) | ||
| 396 | { | ||
| 397 | 97267 | SwsContext *sws = slice_ctx(pass, y); | |
| 398 | 97267 | SwsInternal *c = sws_internal(sws); | |
| 399 | uint8_t *in_data[4]; | ||
| 400 | 97267 | frame_shift(in, y, in_data); | |
| 401 | |||
| 402 | 97267 | c->convert_unscaled(c, (const uint8_t *const *) in_data, in->linesize, y, h, | |
| 403 | 97267 | out->data, out->linesize); | |
| 404 | 97267 | } | |
| 405 | |||
| 406 | 583495 | static void run_legacy_swscale(const SwsFrame *out, const SwsFrame *in, | |
| 407 | int y, int h, const SwsPass *pass) | ||
| 408 | { | ||
| 409 | 583495 | SwsContext *sws = slice_ctx(pass, y); | |
| 410 | 583495 | SwsInternal *c = sws_internal(sws); | |
| 411 | uint8_t *out_data[4]; | ||
| 412 | 583495 | frame_shift(out, y, out_data); | |
| 413 | |||
| 414 | 583495 | ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0, | |
| 415 | 583495 | sws->src_h, out_data, out->linesize, y, h); | |
| 416 | 583495 | } | |
| 417 | |||
| 418 | ✗ | static void run_legacy_lut3d(const SwsFrame *out, const SwsFrame *in, | |
| 419 | int y, int h, const SwsPass *pass) | ||
| 420 | { | ||
| 421 | ✗ | const SwsLut3D *lut = pass->graph->lut3d; | |
| 422 | uint8_t *in_data[4], *out_data[4]; | ||
| 423 | ✗ | frame_shift(in, y, in_data); | |
| 424 | ✗ | frame_shift(out, y, out_data); | |
| 425 | |||
| 426 | ✗ | ff_sws_lut3d_apply_rgba64(lut, in_data[0], in->linesize[0], out_data[0], | |
| 427 | ✗ | out->linesize[0], out->width, h); | |
| 428 | ✗ | } | |
| 429 | |||
| 430 | 158036 | static void legacy_chr_pos(SwsGraph *graph, int *chr_pos, int override, int *warned) | |
| 431 | { | ||
| 432 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 158036 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
158036 | if (override == -513 || override == *chr_pos) |
| 433 | 158036 | return; | |
| 434 | |||
| 435 | ✗ | if (!*warned) { | |
| 436 | ✗ | av_log(NULL, AV_LOG_WARNING, | |
| 437 | "Setting chroma position directly is deprecated, make sure " | ||
| 438 | "the frame is tagged with the correct chroma location.\n"); | ||
| 439 | ✗ | *warned = 1; | |
| 440 | } | ||
| 441 | |||
| 442 | ✗ | *chr_pos = override; | |
| 443 | } | ||
| 444 | |||
| 445 | /* Takes over ownership of `sws` */ | ||
| 446 | 39511 | static int init_legacy_subpass(SwsGraph *graph, SwsContext *sws, | |
| 447 | SwsPass *input, SwsPass **output) | ||
| 448 | { | ||
| 449 | 39511 | SwsInternal *c = sws_internal(sws); | |
| 450 | 39511 | const int src_w = sws->src_w, src_h = sws->src_h; | |
| 451 | 39511 | const int dst_w = sws->dst_w, dst_h = sws->dst_h; | |
| 452 |
4/4✓ Branch 0 taken 25462 times.
✓ Branch 1 taken 14049 times.
✓ Branch 2 taken 21078 times.
✓ Branch 3 taken 4384 times.
|
39511 | const int unscaled = src_w == dst_w && src_h == dst_h; |
| 453 | 39511 | int align = c->dst_slice_align; | |
| 454 | 39511 | SwsPass *pass = NULL; | |
| 455 | int ret; | ||
| 456 | |||
| 457 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39510 times.
|
39511 | if (c->cascaded_context[0]) { |
| 458 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | const int num_cascaded = c->cascaded_context[2] ? 3 : 2; |
| 459 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (int i = 0; i < num_cascaded; i++) { |
| 460 | 2 | const int is_last = i + 1 == num_cascaded; | |
| 461 | |||
| 462 | /* Steal cascaded context, so we can manage its lifetime independently */ | ||
| 463 | 2 | SwsContext *sub = c->cascaded_context[i]; | |
| 464 | 2 | c->cascaded_context[i] = NULL; | |
| 465 | |||
| 466 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | ret = init_legacy_subpass(graph, sub, input, is_last ? output : &input); |
| 467 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (ret < 0) |
| 468 | ✗ | break; | |
| 469 | } | ||
| 470 | |||
| 471 | 1 | sws_free_context(&sws); | |
| 472 | 1 | return ret; | |
| 473 | } | ||
| 474 | |||
| 475 |
3/4✓ Branch 0 taken 19 times.
✓ Branch 1 taken 39491 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
|
39510 | if (sws->dither == SWS_DITHER_ED && !c->convert_unscaled) |
| 476 | 19 | align = 0; /* disable slice threading */ | |
| 477 | |||
| 478 |
6/6✓ Branch 0 taken 1136 times.
✓ Branch 1 taken 38374 times.
✓ Branch 2 taken 1132 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 1117 times.
✓ Branch 6 taken 15 times.
|
39510 | if (c->src0Alpha && !c->dst0Alpha && isALPHA(sws->dst_format)) { |
| 479 | 1117 | ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGBA, src_w, src_h, input, | |
| 480 | 0, 1, run_rgb0, NULL, c, NULL, &input); | ||
| 481 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1117 times.
|
1117 | if (ret < 0) { |
| 482 | ✗ | sws_free_context(&sws); | |
| 483 | ✗ | return ret; | |
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 |
5/6✓ Branch 0 taken 14 times.
✓ Branch 1 taken 39496 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 12 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
39510 | if (c->srcXYZ && !(c->dstXYZ && unscaled)) { |
| 488 | 14 | ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, src_w, src_h, input, | |
| 489 | 0, 1, run_xyz2rgb, NULL, c, NULL, &input); | ||
| 490 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | if (ret < 0) { |
| 491 | ✗ | sws_free_context(&sws); | |
| 492 | ✗ | return ret; | |
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | 39510 | ret = ff_sws_graph_add_pass(graph, sws->dst_format, dst_w, dst_h, input, 0, align, | |
| 497 |
2/2✓ Branch 0 taken 3767 times.
✓ Branch 1 taken 35743 times.
|
39510 | c->convert_unscaled ? run_legacy_unscaled : run_legacy_swscale, |
| 498 | setup_legacy_swscale, sws, free_legacy_swscale, &pass); | ||
| 499 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39510 times.
|
39510 | if (ret < 0) |
| 500 | ✗ | return ret; | |
| 501 | 39510 | pass->backend = SWS_BACKEND_LEGACY; | |
| 502 | |||
| 503 | /** | ||
| 504 | * For slice threading, we need to create sub contexts, similar to how | ||
| 505 | * swscale normally handles it internally. The most important difference | ||
| 506 | * is that we handle cascaded contexts before threaded contexts; whereas | ||
| 507 | * context_init_threaded() does it the other way around. | ||
| 508 | */ | ||
| 509 | |||
| 510 |
2/2✓ Branch 0 taken 2588 times.
✓ Branch 1 taken 36922 times.
|
39510 | if (pass->num_slices > 1) { |
| 511 | 2588 | c->slice_ctx = av_calloc(pass->num_slices, sizeof(*c->slice_ctx)); | |
| 512 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2588 times.
|
2588 | if (!c->slice_ctx) |
| 513 | ✗ | return AVERROR(ENOMEM); | |
| 514 | |||
| 515 |
2/2✓ Branch 0 taken 23191 times.
✓ Branch 1 taken 2588 times.
|
25779 | for (int i = 0; i < pass->num_slices; i++) { |
| 516 | SwsContext *slice; | ||
| 517 | SwsInternal *c2; | ||
| 518 | 23191 | slice = c->slice_ctx[i] = sws_alloc_context(); | |
| 519 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
|
23191 | if (!slice) |
| 520 | ✗ | return AVERROR(ENOMEM); | |
| 521 | 23191 | c->nb_slice_ctx++; | |
| 522 | |||
| 523 | 23191 | c2 = sws_internal(slice); | |
| 524 | 23191 | c2->parent = sws; | |
| 525 | |||
| 526 | 23191 | ret = av_opt_copy(slice, sws); | |
| 527 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
|
23191 | if (ret < 0) |
| 528 | ✗ | return ret; | |
| 529 | |||
| 530 | 23191 | ret = ff_sws_init_single_context(slice, NULL, NULL); | |
| 531 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23191 times.
|
23191 | if (ret < 0) |
| 532 | ✗ | return ret; | |
| 533 | |||
| 534 | 23191 | sws_setColorspaceDetails(slice, c->srcColorspaceTable, | |
| 535 | 23191 | slice->src_range, c->dstColorspaceTable, | |
| 536 | slice->dst_range, c->brightness, c->contrast, | ||
| 537 | c->saturation); | ||
| 538 | |||
| 539 |
2/2✓ Branch 0 taken 92764 times.
✓ Branch 1 taken 23191 times.
|
115955 | for (int i = 0; i < FF_ARRAY_ELEMS(c->srcColorspaceTable); i++) { |
| 540 | 92764 | c2->srcColorspaceTable[i] = c->srcColorspaceTable[i]; | |
| 541 | 92764 | c2->dstColorspaceTable[i] = c->dstColorspaceTable[i]; | |
| 542 | } | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 |
5/6✓ Branch 0 taken 38 times.
✓ Branch 1 taken 39472 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
39510 | if (c->dstXYZ && !(c->srcXYZ && unscaled)) { |
| 547 | 38 | ret = ff_sws_graph_add_pass(graph, AV_PIX_FMT_RGB48, dst_w, dst_h, pass, | |
| 548 | 0, 1, run_rgb2xyz, NULL, c, NULL, &pass); | ||
| 549 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
|
38 | if (ret < 0) |
| 550 | ✗ | return ret; | |
| 551 | } | ||
| 552 | |||
| 553 | 39510 | *output = pass; | |
| 554 | 39510 | return 0; | |
| 555 | } | ||
| 556 | |||
| 557 | static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src, | ||
| 558 | SwsPass *input, SwsPass **output); | ||
| 559 | |||
| 560 | 73994 | static int add_legacy_sws_pass(SwsGraph *graph, const SwsFormat *src, | |
| 561 | const SwsFormat *dst, const SwsLut3D *lut3d, | ||
| 562 | SwsPass *input, SwsPass **output) | ||
| 563 | { | ||
| 564 | 73994 | int ret, warned = 0; | |
| 565 | 73994 | SwsContext *const ctx = graph->ctx; | |
| 566 | 73994 | const SwsBackend backend = ff_sws_enabled_backends(ctx); | |
| 567 |
2/2✓ Branch 0 taken 32710 times.
✓ Branch 1 taken 41284 times.
|
73994 | if (!(backend & SWS_BACKEND_LEGACY)) |
| 568 | 32710 | return AVERROR(ENOTSUP); | |
| 569 |
2/4✓ Branch 0 taken 41284 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 41284 times.
|
41284 | if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE) |
| 570 | ✗ | return AVERROR(ENOTSUP); | |
| 571 | |||
| 572 | /* Re-check this here because this might not be excluded if the caller was | ||
| 573 | * testing against multiple backends */ | ||
| 574 |
4/4✓ Branch 1 taken 39517 times.
✓ Branch 2 taken 1767 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 39509 times.
|
41284 | if (!sws_isSupportedInput(src->format) || !sws_isSupportedOutput(dst->format)) |
| 575 | 1775 | return AVERROR(ENOTSUP); | |
| 576 | |||
| 577 | /* If we need to apply a 3D LUT, add it as an explicit input prepass */ | ||
| 578 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39509 times.
|
39509 | if (lut3d) { |
| 579 | ✗ | ret = add_legacy_3dlut_pass(graph, src, input, &input); | |
| 580 | ✗ | if (ret < 0) | |
| 581 | ✗ | return ret; | |
| 582 | |||
| 583 | ✗ | SwsFormat tmp = *src; | |
| 584 | ✗ | tmp.format = input->format; | |
| 585 | ✗ | tmp.color = lut3d->map.dst; | |
| 586 | ✗ | return add_legacy_sws_pass(graph, &tmp, dst, NULL, input, output); | |
| 587 | } | ||
| 588 | |||
| 589 | 39509 | SwsContext *sws = sws_alloc_context(); | |
| 590 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39509 times.
|
39509 | if (!sws) |
| 591 | ✗ | return AVERROR(ENOMEM); | |
| 592 | |||
| 593 | 39509 | sws->flags = ctx->flags; | |
| 594 | 39509 | sws->dither = ctx->dither; | |
| 595 | 39509 | sws->alpha_blend = ctx->alpha_blend; | |
| 596 | 39509 | sws->gamma_flag = ctx->gamma_flag; | |
| 597 | 39509 | sws->scaler = ctx->scaler; | |
| 598 | 39509 | sws->scaler_sub = ctx->scaler_sub; | |
| 599 | |||
| 600 | 39509 | sws->src_w = src->width; | |
| 601 | 39509 | sws->src_h = src->height; | |
| 602 | 39509 | sws->src_format = src->format; | |
| 603 | 39509 | sws->src_range = src->range == AVCOL_RANGE_JPEG; | |
| 604 | |||
| 605 | 39509 | sws->dst_w = dst->width; | |
| 606 | 39509 | sws->dst_h = dst->height; | |
| 607 | 39509 | sws->dst_format = dst->format; | |
| 608 | 39509 | sws->dst_range = dst->range == AVCOL_RANGE_JPEG; | |
| 609 | 39509 | ff_sws_chroma_pos(src, &graph->incomplete, &sws->src_h_chr_pos, &sws->src_v_chr_pos); | |
| 610 | 39509 | ff_sws_chroma_pos(dst, &graph->incomplete, &sws->dst_h_chr_pos, &sws->dst_v_chr_pos); | |
| 611 | |||
| 612 | 39509 | graph->incomplete |= src->range == AVCOL_RANGE_UNSPECIFIED; | |
| 613 | 39509 | graph->incomplete |= dst->range == AVCOL_RANGE_UNSPECIFIED; | |
| 614 | |||
| 615 | /* Allow overriding chroma position with the legacy API */ | ||
| 616 | 39509 | legacy_chr_pos(graph, &sws->src_h_chr_pos, ctx->src_h_chr_pos, &warned); | |
| 617 | 39509 | legacy_chr_pos(graph, &sws->src_v_chr_pos, ctx->src_v_chr_pos, &warned); | |
| 618 | 39509 | legacy_chr_pos(graph, &sws->dst_h_chr_pos, ctx->dst_h_chr_pos, &warned); | |
| 619 | 39509 | legacy_chr_pos(graph, &sws->dst_v_chr_pos, ctx->dst_v_chr_pos, &warned); | |
| 620 | |||
| 621 | /* Explicitly strip chroma offsets when not subsampling, because it | ||
| 622 | * interferes with the operation of flags like SWS_FULL_CHR_H_INP */ | ||
| 623 |
2/2✓ Branch 0 taken 35462 times.
✓ Branch 1 taken 4047 times.
|
39509 | if (!src->desc->log2_chroma_w) |
| 624 | 35462 | sws->src_h_chr_pos = -513; | |
| 625 |
2/2✓ Branch 0 taken 35841 times.
✓ Branch 1 taken 3668 times.
|
39509 | if (!src->desc->log2_chroma_h) |
| 626 | 35841 | sws->src_v_chr_pos = -513; | |
| 627 |
2/2✓ Branch 0 taken 37452 times.
✓ Branch 1 taken 2057 times.
|
39509 | if (!dst->desc->log2_chroma_w) |
| 628 | 37452 | sws->dst_h_chr_pos = -513; | |
| 629 |
2/2✓ Branch 0 taken 38254 times.
✓ Branch 1 taken 1255 times.
|
39509 | if (!dst->desc->log2_chroma_h) |
| 630 | 38254 | sws->dst_v_chr_pos = -513; | |
| 631 | |||
| 632 |
2/2✓ Branch 0 taken 79018 times.
✓ Branch 1 taken 39509 times.
|
118527 | for (int i = 0; i < SWS_NUM_SCALER_PARAMS; i++) |
| 633 | 79018 | sws->scaler_params[i] = ctx->scaler_params[i]; | |
| 634 | |||
| 635 | 39509 | ret = sws_init_context(sws, NULL, NULL); | |
| 636 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39509 times.
|
39509 | if (ret < 0) { |
| 637 | ✗ | sws_free_context(&sws); | |
| 638 | ✗ | return ret; | |
| 639 | } | ||
| 640 | |||
| 641 | /* Set correct color matrices */ | ||
| 642 | { | ||
| 643 | int in_full, out_full, brightness, contrast, saturation; | ||
| 644 | const int *inv_table, *table; | ||
| 645 | 39509 | sws_getColorspaceDetails(sws, (int **)&inv_table, &in_full, | |
| 646 | (int **)&table, &out_full, | ||
| 647 | &brightness, &contrast, &saturation); | ||
| 648 | |||
| 649 | 39509 | inv_table = sws_getCoefficients(src->csp); | |
| 650 | 39509 | table = sws_getCoefficients(dst->csp); | |
| 651 | |||
| 652 |
2/2✓ Branch 0 taken 19659 times.
✓ Branch 1 taken 19850 times.
|
59168 | graph->incomplete |= src->csp != dst->csp && |
| 653 |
2/2✓ Branch 0 taken 17973 times.
✓ Branch 1 taken 1686 times.
|
19659 | (src->csp == AVCOL_SPC_UNSPECIFIED || |
| 654 |
2/2✓ Branch 0 taken 17962 times.
✓ Branch 1 taken 11 times.
|
17973 | dst->csp == AVCOL_SPC_UNSPECIFIED); |
| 655 | |||
| 656 | 39509 | sws_setColorspaceDetails(sws, inv_table, in_full, table, out_full, | |
| 657 | brightness, contrast, saturation); | ||
| 658 | } | ||
| 659 | |||
| 660 | 39509 | return init_legacy_subpass(graph, sws, input, output); | |
| 661 | } | ||
| 662 | |||
| 663 | ✗ | static int add_legacy_3dlut_pass(SwsGraph *graph, const SwsFormat *src, | |
| 664 | SwsPass *input, SwsPass **output) | ||
| 665 | { | ||
| 666 | int ret; | ||
| 667 | |||
| 668 | ✗ | const SwsLut3D *lut3d = graph->lut3d; | |
| 669 | ✗ | if (!lut3d) | |
| 670 | ✗ | return 0; | |
| 671 | |||
| 672 | ✗ | const enum AVPixelFormat fmt = AV_PIX_FMT_RGBA64; | |
| 673 | ✗ | if (src->format != fmt) { | |
| 674 | ✗ | SwsFormat tmp = *src; | |
| 675 | ✗ | tmp.format = fmt; | |
| 676 | ✗ | ret = add_legacy_sws_pass(graph, src, &tmp, NULL, input, &input); | |
| 677 | ✗ | if (ret < 0) | |
| 678 | ✗ | return ret; | |
| 679 | } | ||
| 680 | |||
| 681 | ✗ | ret = ff_sws_graph_add_pass(graph, fmt, src->width, src->height, | |
| 682 | input, 0, 1, run_legacy_lut3d, NULL, NULL, NULL, | ||
| 683 | output); | ||
| 684 | ✗ | if (ret < 0) | |
| 685 | ✗ | return ret; | |
| 686 | |||
| 687 | ✗ | return 0; | |
| 688 | } | ||
| 689 | |||
| 690 | /********************************* | ||
| 691 | * Format conversion and scaling * | ||
| 692 | *********************************/ | ||
| 693 | |||
| 694 | 45113 | static int add_ops_convert_pass(SwsGraph *graph, const SwsFormat *src, | |
| 695 | const SwsFormat *dst, const SwsLut3D *lut3d, | ||
| 696 | SwsPass *input, SwsPass **output) | ||
| 697 | { | ||
| 698 | #if CONFIG_UNSTABLE | ||
| 699 | 45113 | SwsContext *ctx = graph->ctx; | |
| 700 | |||
| 701 | /* Preemptively skip the ops list generation if the backend was | ||
| 702 | * constrained to the legacy implementation only. This would | ||
| 703 | * normally also fail in ff_sws_compile_pass() with the same | ||
| 704 | * error, but this way saves a bit of unnecessary overhead */ | ||
| 705 | 45113 | const SwsBackend backends = ff_sws_enabled_backends(ctx); | |
| 706 |
2/2✓ Branch 0 taken 140 times.
✓ Branch 1 taken 44973 times.
|
45113 | if (backends == SWS_BACKEND_LEGACY) |
| 707 | 140 | return AVERROR(ENOTSUP); | |
| 708 | |||
| 709 | SwsOpList *ops; | ||
| 710 | 44973 | int ret = ff_sws_op_list_generate(ctx, src, dst, lut3d, &ops, &graph->incomplete); | |
| 711 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 44973 times.
|
44973 | if (ret < 0) |
| 712 | ✗ | return ret; | |
| 713 | |||
| 714 | 44973 | av_log(ctx, AV_LOG_VERBOSE, "Conversion pass for %s -> %s:\n", | |
| 715 | 44973 | av_get_pix_fmt_name(src->format), av_get_pix_fmt_name(dst->format)); | |
| 716 | |||
| 717 | 44973 | av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n"); | |
| 718 | 44973 | ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops); | |
| 719 | |||
| 720 | 44973 | const int flags = SWS_OP_FLAG_OPTIMIZE | SWS_OP_FLAG_SPLIT_MEMCPY; | |
| 721 | 44973 | return ff_sws_compile_pass(graph, NULL, &ops, flags, input, output); | |
| 722 | #else | ||
| 723 | return AVERROR(ENOTSUP); | ||
| 724 | #endif | ||
| 725 | } | ||
| 726 | |||
| 727 | 84482 | static bool prefer_ops_backend(SwsContext *ctx, const SwsFormat *src, const SwsFormat *dst) | |
| 728 | { | ||
| 729 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84482 times.
|
84482 | if (ctx->flags & SWS_UNSTABLE) |
| 730 | ✗ | return true; | |
| 731 |
4/4✓ Branch 1 taken 77285 times.
✓ Branch 2 taken 7197 times.
✓ Branch 4 taken 3431 times.
✓ Branch 5 taken 73854 times.
|
84482 | if (isFloat(src->format) || isFloat(dst->format)) |
| 732 | 10628 | return true; /* ops backend has better support for float formats */ | |
| 733 | 73854 | return false; /* default to legacy for stability reasons */ | |
| 734 | } | ||
| 735 | |||
| 736 | 84482 | static int add_convert_pass(SwsGraph *graph, const SwsFormat *src, | |
| 737 | const SwsFormat *dst, const SwsLut3D *lut3d, | ||
| 738 | SwsPass *input, SwsPass **output) | ||
| 739 | { | ||
| 740 | 84482 | SwsContext *ctx = graph->ctx; | |
| 741 | int ret; | ||
| 742 | |||
| 743 |
2/2✓ Branch 1 taken 10628 times.
✓ Branch 2 taken 73854 times.
|
84482 | if (prefer_ops_backend(ctx, src, dst)) { |
| 744 | 10628 | ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output); | |
| 745 |
2/2✓ Branch 0 taken 140 times.
✓ Branch 1 taken 10488 times.
|
10628 | if (ret == AVERROR(ENOTSUP)) |
| 746 | 140 | ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output); | |
| 747 | } else { | ||
| 748 | 73854 | ret = add_legacy_sws_pass(graph, src, dst, lut3d, input, output); | |
| 749 |
2/2✓ Branch 0 taken 34485 times.
✓ Branch 1 taken 39369 times.
|
73854 | if (ret == AVERROR(ENOTSUP)) |
| 750 | 34485 | ret = add_ops_convert_pass(graph, src, dst, lut3d, input, output); | |
| 751 | } | ||
| 752 | |||
| 753 | 84482 | return ret; | |
| 754 | } | ||
| 755 | |||
| 756 | /************************** | ||
| 757 | * Gamut and tone mapping * | ||
| 758 | **************************/ | ||
| 759 | |||
| 760 | 84748 | static int generate_3dlut(SwsGraph *graph, SwsFormat *src, SwsFormat *dst) | |
| 761 | { | ||
| 762 | 84748 | SwsColorMap map = {0}; | |
| 763 | |||
| 764 | /** | ||
| 765 | * Grayspace does not really have primaries, so just force the use of | ||
| 766 | * the equivalent other primary set to avoid a conversion. Technically, | ||
| 767 | * this does affect the weights used for the Grayscale conversion, but | ||
| 768 | * in practise, that should give the expected results more often than not. | ||
| 769 | */ | ||
| 770 |
2/2✓ Branch 1 taken 5820 times.
✓ Branch 2 taken 78928 times.
|
84748 | if (isGray(dst->format)) { |
| 771 | 5820 | dst->color = src->color; | |
| 772 |
2/2✓ Branch 1 taken 10070 times.
✓ Branch 2 taken 68858 times.
|
78928 | } else if (isGray(src->format)) { |
| 773 | 10070 | src->color = dst->color; | |
| 774 | } | ||
| 775 | |||
| 776 | /* Fully infer color spaces before color mapping logic */ | ||
| 777 | 84748 | graph->incomplete |= ff_infer_colors(&src->color, &dst->color); | |
| 778 | |||
| 779 | 84748 | map.intent = graph->ctx->intent; | |
| 780 | 84748 | map.src = src->color; | |
| 781 | 84748 | map.dst = dst->color; | |
| 782 | |||
| 783 |
1/2✓ Branch 1 taken 84748 times.
✗ Branch 2 not taken.
|
84748 | if (ff_sws_color_map_noop(&map)) |
| 784 | 84748 | return 0; | |
| 785 | |||
| 786 | ✗ | if (src->hw_format != AV_PIX_FMT_NONE || dst->hw_format != AV_PIX_FMT_NONE) | |
| 787 | ✗ | return AVERROR(ENOTSUP); | |
| 788 | |||
| 789 | ✗ | graph->lut3d = ff_sws_lut3d_alloc(); | |
| 790 | ✗ | if (!graph->lut3d) | |
| 791 | ✗ | return AVERROR(ENOMEM); | |
| 792 | |||
| 793 | ✗ | return ff_sws_lut3d_generate(graph->lut3d, &map); | |
| 794 | } | ||
| 795 | |||
| 796 | /*************************************** | ||
| 797 | * Main filter graph construction code * | ||
| 798 | ***************************************/ | ||
| 799 | |||
| 800 | 84748 | static int init_passes(SwsGraph *graph) | |
| 801 | { | ||
| 802 | 84748 | SwsFormat src = graph->src; | |
| 803 | 84748 | SwsFormat dst = graph->dst; | |
| 804 | 84748 | SwsPass *pass = NULL; /* read from main input image */ | |
| 805 | int ret; | ||
| 806 | |||
| 807 | 84748 | ret = generate_3dlut(graph, &src, &dst); | |
| 808 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
|
84748 | if (ret < 0) |
| 809 | ✗ | return ret; | |
| 810 | |||
| 811 |
3/4✓ Branch 1 taken 266 times.
✓ Branch 2 taken 84482 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 266 times.
|
84748 | if (!ff_fmt_equal(&src, &dst) || graph->lut3d) { |
| 812 | 84482 | ret = add_convert_pass(graph, &src, &dst, graph->lut3d, pass, &pass); | |
| 813 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 84478 times.
|
84482 | if (ret < 0) |
| 814 | 4 | return ret; | |
| 815 | } | ||
| 816 | |||
| 817 |
2/2✓ Branch 0 taken 287 times.
✓ Branch 1 taken 84457 times.
|
84744 | if (!pass) { |
| 818 | /* No passes were added, so no operations were necessary */ | ||
| 819 | 287 | graph->noop = 1; | |
| 820 | |||
| 821 | 287 | const int nb_planes = av_pix_fmt_count_planes(dst.format); | |
| 822 |
2/2✓ Branch 0 taken 855 times.
✓ Branch 1 taken 287 times.
|
1142 | for (int i = 0; i < nb_planes; i++) |
| 823 | 855 | graph->plane_copy[i] = i; | |
| 824 | |||
| 825 | /* Add threaded memcpy pass */ | ||
| 826 | 287 | return ff_sws_graph_add_pass(graph, dst.format, dst.width, dst.height, | |
| 827 | pass, 0, 1, run_copy, NULL, NULL, NULL, &pass); | ||
| 828 | } | ||
| 829 | |||
| 830 | /* Compute end-to-end plane copy map */ | ||
| 831 |
2/2✓ Branch 0 taken 126561 times.
✓ Branch 1 taken 84457 times.
|
211018 | for (int n = 0; n < graph->num_passes; n++) { |
| 832 | 126561 | const SwsPass *pass = graph->passes[n]; | |
| 833 | /* This pass writes to an output buffer other than the image | ||
| 834 | * output, or copies from the output of a different pass */ | ||
| 835 |
3/4✓ Branch 0 taken 126561 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 33156 times.
✓ Branch 3 taken 93405 times.
|
126561 | if (pass->output->avframe || pass->input) |
| 836 | 33156 | continue; | |
| 837 |
2/2✓ Branch 0 taken 373620 times.
✓ Branch 1 taken 93405 times.
|
467025 | for (int i = 0; i < FF_ARRAY_ELEMS(graph->plane_copy); i++) { |
| 838 | 373620 | const int idx = pass->output->plane_copy[i]; | |
| 839 |
2/2✓ Branch 0 taken 373477 times.
✓ Branch 1 taken 143 times.
|
373620 | if (idx < 0) |
| 840 | 373477 | continue; | |
| 841 |
2/2✓ Branch 0 taken 135 times.
✓ Branch 1 taken 8 times.
|
143 | if (graph->plane_copy[i] < 0) { |
| 842 | 135 | graph->plane_copy[i] = idx; | |
| 843 | 135 | av_log(graph->ctx, AV_LOG_DEBUG, "Plane %d passthrough from " | |
| 844 | "plane %d\n", i, idx); | ||
| 845 | } else { | ||
| 846 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | av_assert0(graph->plane_copy[i] == idx); |
| 847 | } | ||
| 848 | } | ||
| 849 | } | ||
| 850 | |||
| 851 | 84457 | return 0; | |
| 852 | } | ||
| 853 | |||
| 854 | 613608 | static int sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs, | |
| 855 | int nb_threads) | ||
| 856 | { | ||
| 857 | 613608 | SwsGraph *graph = priv; | |
| 858 | 613608 | const SwsPass *pass = graph->exec.pass; | |
| 859 | 613608 | const int slice_y = jobnr * pass->slice_h; | |
| 860 | 613608 | const int slice_h = FFMIN(pass->slice_h, pass->lines - slice_y); | |
| 861 | |||
| 862 | 613608 | pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass); | |
| 863 | 613608 | return 0; | |
| 864 | } | ||
| 865 | |||
| 866 | 6412 | SwsGraph *ff_sws_graph_alloc(void) | |
| 867 | { | ||
| 868 | 6412 | return av_mallocz(sizeof(SwsGraph)); | |
| 869 | } | ||
| 870 | |||
| 871 | 91164 | static void graph_uninit(SwsGraph *graph) | |
| 872 | { | ||
| 873 | 91164 | avpriv_slicethread_free(&graph->slicethread); | |
| 874 | |||
| 875 |
2/2✓ Branch 0 taken 126848 times.
✓ Branch 1 taken 91164 times.
|
218012 | for (int i = 0; i < graph->num_passes; i++) |
| 876 | 126848 | pass_free(graph->passes[i]); | |
| 877 | 91164 | av_free(graph->passes); | |
| 878 | |||
| 879 | 91164 | av_refstruct_unref(&graph->lut3d); | |
| 880 | |||
| 881 | 91164 | memset(graph, 0, sizeof(*graph)); | |
| 882 | 91164 | } | |
| 883 | |||
| 884 | 84748 | int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst, | |
| 885 | const SwsFormat *src) | ||
| 886 | { | ||
| 887 | int ret; | ||
| 888 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
|
84748 | if (graph->ctx) { |
| 889 | ✗ | av_log(ctx, AV_LOG_ERROR, "Graph is already initialized\n"); | |
| 890 | ✗ | return AVERROR(EINVAL); | |
| 891 | } | ||
| 892 | |||
| 893 | 84748 | graph->ctx = ctx; | |
| 894 | 84748 | graph->src = *src; | |
| 895 | 84748 | graph->dst = *dst; | |
| 896 | 84748 | graph->opts_copy = *ctx; | |
| 897 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
|
84748 | av_assert0(src->interlaced == dst->interlaced); |
| 898 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 84748 times.
|
84748 | av_assert0(src->field == dst->field); |
| 899 | 84748 | memset(graph->plane_copy, -1, sizeof(graph->plane_copy)); | |
| 900 | |||
| 901 |
2/2✓ Branch 0 taken 82155 times.
✓ Branch 1 taken 2593 times.
|
84748 | if (ctx->threads == 1) { |
| 902 | 82155 | graph->num_threads = 1; | |
| 903 | } else { | ||
| 904 | 2593 | ret = avpriv_slicethread_create2(&graph->slicethread, (void *) graph, | |
| 905 | sws_graph_worker, NULL, ctx->threads); | ||
| 906 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2593 times.
|
2593 | if (ret == AVERROR(ENOSYS)) { |
| 907 | /* Fall back to single threaded operation */ | ||
| 908 | ✗ | graph->num_threads = 1; | |
| 909 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2593 times.
|
2593 | } else if (ret < 0) { |
| 910 | ✗ | goto error; | |
| 911 | } else { | ||
| 912 | 2593 | graph->num_threads = ret; | |
| 913 | } | ||
| 914 | } | ||
| 915 | |||
| 916 | 84748 | ret = init_passes(graph); | |
| 917 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 84744 times.
|
84748 | if (ret < 0) |
| 918 | 4 | goto error; | |
| 919 | |||
| 920 | /* Resolve output buffers for all intermediate passes */ | ||
| 921 |
2/2✓ Branch 0 taken 126848 times.
✓ Branch 1 taken 84744 times.
|
211592 | for (int i = 0; i < graph->num_passes; i++) { |
| 922 | 126848 | graph->backend |= graph->passes[i]->backend; | |
| 923 | 126848 | ret = pass_alloc_output(graph->passes[i]->input); | |
| 924 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126848 times.
|
126848 | if (ret < 0) |
| 925 | ✗ | goto error; | |
| 926 | } | ||
| 927 | |||
| 928 | 84744 | return 0; | |
| 929 | |||
| 930 | 4 | error: | |
| 931 | 4 | graph_uninit(graph); | |
| 932 | 4 | return ret; | |
| 933 | } | ||
| 934 | |||
| 935 | 4 | void ff_sws_graph_rollback(SwsGraph *graph, int since_idx) | |
| 936 | { | ||
| 937 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
12 | for (int i = since_idx; i < graph->num_passes; i++) |
| 938 | 8 | pass_free(graph->passes[i]); | |
| 939 | 4 | graph->num_passes = since_idx; | |
| 940 | 4 | } | |
| 941 | |||
| 942 | 403553 | void ff_sws_graph_free(SwsGraph **pgraph) | |
| 943 | { | ||
| 944 | 403553 | SwsGraph *graph = *pgraph; | |
| 945 |
2/2✓ Branch 0 taken 397141 times.
✓ Branch 1 taken 6412 times.
|
403553 | if (!graph) |
| 946 | 397141 | return; | |
| 947 | |||
| 948 | 6412 | graph_uninit(graph); | |
| 949 | 6412 | av_free(graph); | |
| 950 | 6412 | *pgraph = NULL; | |
| 951 | } | ||
| 952 | |||
| 953 | /* Tests only options relevant to SwsGraph */ | ||
| 954 | 142055 | static int opts_equal(const SwsContext *c1, const SwsContext *c2) | |
| 955 | { | ||
| 956 | 282609 | return c1->flags == c2->flags && | |
| 957 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->threads == c2->threads && |
| 958 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->dither == c2->dither && |
| 959 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->alpha_blend == c2->alpha_blend && |
| 960 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->gamma_flag == c2->gamma_flag && |
| 961 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->src_h_chr_pos == c2->src_h_chr_pos && |
| 962 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->src_v_chr_pos == c2->src_v_chr_pos && |
| 963 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->dst_h_chr_pos == c2->dst_h_chr_pos && |
| 964 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->dst_v_chr_pos == c2->dst_v_chr_pos && |
| 965 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->intent == c2->intent && |
| 966 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->scaler == c2->scaler && |
| 967 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | c1->scaler_sub == c2->scaler_sub && |
| 968 |
3/4✓ Branch 0 taken 140554 times.
✓ Branch 1 taken 1501 times.
✓ Branch 2 taken 140554 times.
✗ Branch 3 not taken.
|
423163 | c1->backends == c2->backends && |
| 969 |
1/2✓ Branch 0 taken 140554 times.
✗ Branch 1 not taken.
|
140554 | !memcmp(c1->scaler_params, c2->scaler_params, sizeof(c1->scaler_params)); |
| 970 | |||
| 971 | } | ||
| 972 | |||
| 973 | 225302 | int ff_sws_graph_reinit(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst, | |
| 974 | const SwsFormat *src) | ||
| 975 | { | ||
| 976 |
6/6✓ Branch 1 taken 180343 times.
✓ Branch 2 taken 44959 times.
✓ Branch 4 taken 142055 times.
✓ Branch 5 taken 38288 times.
✓ Branch 6 taken 140554 times.
✓ Branch 7 taken 1501 times.
|
367357 | if (ff_fmt_equal(&graph->src, src) && ff_fmt_equal(&graph->dst, dst) && |
| 977 | 142055 | opts_equal(ctx, &graph->opts_copy)) | |
| 978 | { | ||
| 979 | 140554 | ff_sws_graph_update_metadata(graph, &src->color); | |
| 980 | 140554 | return 0; | |
| 981 | } | ||
| 982 | |||
| 983 | 84748 | graph_uninit(graph); | |
| 984 | 84748 | return ff_sws_graph_init(graph, ctx, dst, src); | |
| 985 | } | ||
| 986 | |||
| 987 | 140554 | void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color) | |
| 988 | { | ||
| 989 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 140554 times.
|
140554 | if (!color) |
| 990 | ✗ | return; | |
| 991 | |||
| 992 | 140554 | ff_color_update_dynamic(&graph->src.color, color); | |
| 993 | |||
| 994 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 140554 times.
|
140554 | if (graph->lut3d) |
| 995 | ✗ | ff_sws_lut3d_update(graph->lut3d, &graph->src.color); | |
| 996 | } | ||
| 997 | |||
| 998 | 370414 | static void get_field(SwsGraph *graph, const SwsFormat *fmt, | |
| 999 | const AVFrame *avframe, SwsFrame *frame) | ||
| 1000 | { | ||
| 1001 | 370414 | ff_sws_frame_from_avframe(frame, avframe); | |
| 1002 | |||
| 1003 |
1/2✓ Branch 0 taken 370414 times.
✗ Branch 1 not taken.
|
370414 | if (!(avframe->flags & AV_FRAME_FLAG_INTERLACED)) { |
| 1004 | av_assert1(!fmt->field); | ||
| 1005 | 370414 | return; | |
| 1006 | } | ||
| 1007 | |||
| 1008 | ✗ | if (fmt->field == FIELD_BOTTOM) { | |
| 1009 | /* Odd rows, offset by one line */ | ||
| 1010 | ✗ | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); | |
| 1011 | ✗ | for (int i = 0; i < 4; i++) { | |
| 1012 | ✗ | if (frame->data[i]) | |
| 1013 | ✗ | frame->data[i] += frame->linesize[i]; | |
| 1014 | ✗ | if (desc->flags & AV_PIX_FMT_FLAG_PAL) | |
| 1015 | ✗ | break; | |
| 1016 | } | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | /* Take only every second line */ | ||
| 1020 | ✗ | for (int i = 0; i < 4; i++) | |
| 1021 | ✗ | frame->linesize[i] <<= 1; | |
| 1022 | |||
| 1023 | ✗ | frame->height = (frame->height + (fmt->field == FIELD_TOP)) >> 1; | |
| 1024 | } | ||
| 1025 | |||
| 1026 | 185207 | int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src) | |
| 1027 | { | ||
| 1028 |
2/4✓ Branch 0 taken 185207 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 185207 times.
|
185207 | av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format); |
| 1029 |
2/4✓ Branch 0 taken 185207 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 185207 times.
|
185207 | av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format); |
| 1030 | |||
| 1031 | SwsFrame src_field, dst_field; | ||
| 1032 | 185207 | get_field(graph, &graph->dst, dst, &dst_field); | |
| 1033 | 185207 | get_field(graph, &graph->src, src, &src_field); | |
| 1034 | |||
| 1035 |
2/2✓ Branch 0 taken 228363 times.
✓ Branch 1 taken 185207 times.
|
413570 | for (int i = 0; i < graph->num_passes; i++) { |
| 1036 | 228363 | const SwsPass *pass = graph->passes[i]; | |
| 1037 | 228363 | graph->exec.pass = pass; | |
| 1038 |
2/2✓ Branch 0 taken 34060 times.
✓ Branch 1 taken 194303 times.
|
228363 | graph->exec.input = pass->input ? &pass->input->output->frame : &src_field; |
| 1039 |
2/2✓ Branch 0 taken 34060 times.
✓ Branch 1 taken 194303 times.
|
228363 | graph->exec.output = pass->output->avframe ? &pass->output->frame : &dst_field; |
| 1040 |
2/2✓ Branch 0 taken 226551 times.
✓ Branch 1 taken 1812 times.
|
228363 | if (pass->setup) { |
| 1041 | 226551 | int ret = pass->setup(graph->exec.output, graph->exec.input, pass); | |
| 1042 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 226551 times.
|
226551 | if (ret < 0) |
| 1043 | ✗ | return ret; | |
| 1044 | } | ||
| 1045 | |||
| 1046 |
2/2✓ Branch 0 taken 160060 times.
✓ Branch 1 taken 68303 times.
|
228363 | if (pass->num_slices == 1) { |
| 1047 | 160060 | pass->run(graph->exec.output, graph->exec.input, 0, pass->lines, pass); | |
| 1048 | } else { | ||
| 1049 | 68303 | avpriv_slicethread_execute2(graph->slicethread, pass->num_slices, 0); | |
| 1050 | } | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | 185207 | return 0; | |
| 1054 | } | ||
| 1055 |