FFmpeg
Loading...
Searching...
No Matches
vf_fruc_vulkan.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 Philip Langdale <philipl@overt.org>
3 *
4 * Based on vf_framerate - Copyright (C) 2012 Mark Himsley
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23/**
24 * @file
25 * Frame rate up-conversion filter that synthesises intermediate frames using
26 * the NVIDIA Vulkan optical flow extension (VK_NV_optical_flow).
27 */
28
29#include "libavutil/avassert.h"
30#include "libavutil/eval.h"
31#include "libavutil/internal.h"
32#include "libavutil/mem.h"
33#include "libavutil/opt.h"
34#include "libavutil/pixdesc.h"
35
36#include "vulkan_filter.h"
37#include "filters.h"
38#include "video.h"
39
40extern const unsigned char ff_fruc_grayscale_comp_spv_data[];
41extern const unsigned int ff_fruc_grayscale_comp_spv_len;
42extern const unsigned char ff_fruc_interpolate_comp_spv_data[];
43extern const unsigned int ff_fruc_interpolate_comp_spv_len;
44
45static const char *const var_names[] = {
46 "source_fps",
47 NULL
48};
49
54
55typedef struct GrayscalePushData {
56 float luma_weights[4][4]; ///< per-plane RGB->Y weights (dotted with each plane's texel)
57 int32_t planes; ///< number of input planes sampled per frame
59
60typedef struct InterpolatePushData {
61 float t;
63 float luma_weights[4][4]; ///< per-plane RGB->Y weights, matching the grayscale pass
64 float plane_size[4][2]; ///< visible texel extent of each plane
66
67/* Double-buffer the per-pair optical flow resources so one pair's flow execution
68 * overlaps the previous pair's interpolations instead of stalling on shared
69 * images. The session bakes in its image bindings, so each slot owns its session,
70 * grayscale and flow images. Two suffice: the flow engine is serial. */
71#define FRUC_NB_SLOTS 2
72
73typedef struct FRUCFlowSlot {
74 VkOpticalFlowSessionNV session;
75
76 VkImage gray_img[2]; ///< grayscale inputs (INPUT, REFERENCE)
77 VkDeviceMemory gray_mem[2];
78 VkImageView gray_view[2];
79
80 VkImage flow_img[2]; ///< [0] forward, [1] backward
81 VkDeviceMemory flow_mem[2];
82 VkImageView flow_view[2]; ///< native (SFIXED5) view, bound to the OF session
83 VkImageView flow_sint_view[2]; ///< R16G16_SINT reinterpret view for sampling
84
85 /* sem_interp value reached by the pair that last used this slot; the next
86 * pair to reuse it waits here before overwriting the flow images. Zero (the
87 * initial value) is satisfied immediately, covering the first use. */
88 uint64_t interp_done;
90
91typedef struct FRUCVulkanContext {
93
94 AVVulkanDeviceQueueFamily *qf; ///< compute queue family
95 AVVulkanDeviceQueueFamily *qf_of; ///< optical flow queue family
96
97 FFVkExecPool e; ///< compute execution pool
98 FFVkExecPool e_of; ///< optical flow execution pool
99
102 VkSampler sampler; ///< linear sampler for the video planes
103 VkSampler flow_sampler; ///< nearest sampler for the flow vectors
104
105 /* Timeline semaphores order the pipelined cross-queue submissions and make
106 * each stage's writes visible to the next. Timeline is required for two
107 * reasons:
108 * * Each optical flow source pair may yield multiple interpolations, and
109 * each interpolation should wait on the flow. Multi-wait requires timeline
110 * semaphores
111 * * We don't know how many interpolations will be done from a single optical
112 * flow pair ahead of time, so we cannot simply signal completion after the
113 * last one. Instead we increment a timeline semaphore after each one and
114 * then the next flow calculation waits on the final timeline value. */
115 VkSemaphore sem_gray; ///< grayscale (compute) -> optical flow
116 VkSemaphore sem_flow; ///< optical flow -> interpolation (compute)
117 VkSemaphore sem_interp; ///< interpolation reads -> next pair optical flow
118 uint64_t gen; ///< source pair generation (sem_gray/sem_flow value)
119 uint64_t interp_value; ///< monotonic interpolation counter (sem_interp value)
120
121 /* Optical flow session parameters (images live per-slot, see slots[]). */
122 VkOpticalFlowGridSizeFlagsNV grid_bit;
124 VkFormat input_format; ///< grayscale input format
125 VkFormat flow_format; ///< flow vector format
126
127 /* Tuning options. */
128 int perf_level; ///< VkOpticalFlowPerformanceLevelNV
129 int opt_grid_size; ///< requested grid in pixels (0 = finest)
130
131 int width; ///< luma width
132 int height; ///< luma height
135 float luma_weights[4][4]; ///< RGB->Y weights for the grayscale pass
136 int gray_planes; ///< number of input planes the grayscale pass samples
137
138 /* Double-buffered optical flow resources, indexed by (gen % FRUC_NB_SLOTS). */
140
141 int flow_valid; ///< flow computed for current (f0, f1) pair
142
143 // parameters
144 char *requested_frame_rate; ///< output fps as an expression
145 AVRational dest_frame_rate; ///< output frames per second
146
147 AVRational srce_time_base; ///< timebase of source
148 AVRational dest_time_base; ///< timebase of destination
149
151
152 AVFrame *f0; ///< last frame
153 AVFrame *f1; ///< current frame
154 int64_t pts0; ///< last frame pts in dest_time_base
155 int64_t pts1; ///< current frame pts in dest_time_base
156 int64_t delta; ///< pts1 to pts0 delta
157 int flush; ///< 1 if the filter is being flushed
158 int64_t start_pts; ///< pts of the first output frame
159 int64_t n; ///< output frame counter
161
162#define OFFSET(x) offsetof(FRUCVulkanContext, x)
163#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
164
166 { "fps", "A string describing the desired output frame rate",
167 OFFSET(requested_frame_rate), AV_OPT_TYPE_STRING, { .str = "60" }, 0, 0, FLAGS },
168 { "perf", "Optical flow performance level (quality versus speed)",
169 OFFSET(perf_level), AV_OPT_TYPE_INT, { .i64 = VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_SLOW_NV },
170 VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_SLOW_NV, VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_FAST_NV,
171 FLAGS, .unit = "perf" },
172 { "slow", "Highest quality, slowest", 0, AV_OPT_TYPE_CONST,
173 { .i64 = VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_SLOW_NV }, 0, 0, FLAGS, .unit = "perf" },
174 { "medium", "Balanced quality and speed", 0, AV_OPT_TYPE_CONST,
175 { .i64 = VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_MEDIUM_NV }, 0, 0, FLAGS, .unit = "perf" },
176 { "fast", "Lowest quality, fastest", 0, AV_OPT_TYPE_CONST,
177 { .i64 = VK_OPTICAL_FLOW_PERFORMANCE_LEVEL_FAST_NV }, 0, 0, FLAGS, .unit = "perf" },
178 { "grid", "Optical flow output grid size in pixels (coarser is faster)",
179 OFFSET(opt_grid_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, FLAGS, .unit = "grid" },
180 { "auto", "Finest grid the device supports", 0, AV_OPT_TYPE_CONST,
181 { .i64 = 0 }, 0, 0, FLAGS, .unit = "grid" },
182 { "1", "1x1", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "grid" },
183 { "2", "2x2", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, .unit = "grid" },
184 { "4", "4x4", 0, AV_OPT_TYPE_CONST, { .i64 = 4 }, 0, 0, FLAGS, .unit = "grid" },
185 { "8", "8x8", 0, AV_OPT_TYPE_CONST, { .i64 = 8 }, 0, 0, FLAGS, .unit = "grid" },
186 { NULL }
187};
188
190
191static VkFormat pick_of_format(FRUCVulkanContext *s, VkOpticalFlowUsageFlagsNV usage,
192 VkFormat preferred)
193{
194 FFVulkanContext *vkctx = &s->vkctx;
195 FFVulkanFunctions *vk = &vkctx->vkfn;
196 VkOpticalFlowImageFormatInfoNV info = {
197 .sType = VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_INFO_NV,
198 .usage = usage,
199 };
200 VkOpticalFlowImageFormatPropertiesNV *props;
201 VkFormat result = VK_FORMAT_UNDEFINED;
202 uint32_t count = 0;
203
204 vk->GetPhysicalDeviceOpticalFlowImageFormatsNV(vkctx->hwctx->phys_dev, &info,
205 &count, NULL);
206 if (!count)
207 return VK_FORMAT_UNDEFINED;
208
209 props = av_calloc(count, sizeof(*props));
210 if (!props)
211 return VK_FORMAT_UNDEFINED;
212 for (uint32_t i = 0; i < count; i++)
213 props[i].sType = VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_PROPERTIES_NV;
214
215 vk->GetPhysicalDeviceOpticalFlowImageFormatsNV(vkctx->hwctx->phys_dev, &info,
216 &count, props);
217
218 result = props[0].format;
219 for (uint32_t i = 0; i < count; i++) {
220 av_log(s, AV_LOG_VERBOSE, "Optical flow usage 0x%x supports format %d\n",
221 usage, props[i].format);
222 if (props[i].format == preferred) {
223 result = preferred;
224 break;
225 }
226 }
227
228 av_free(props);
229 return result;
230}
231
232static int create_of_image(FRUCVulkanContext *s, VkImage *img, VkDeviceMemory *mem,
233 VkImageView *view, VkFormat format, int width, int height,
234 VkOpticalFlowUsageFlagsNV of_usage, VkImageUsageFlags usage,
235 VkImageCreateFlags create_flags)
236{
237 FFVulkanContext *vkctx = &s->vkctx;
238 FFVulkanFunctions *vk = &vkctx->vkfn;
239 AVVulkanDeviceContext *hwctx = vkctx->hwctx;
240 VkResult ret;
241 int err;
242
243 VkOpticalFlowImageFormatInfoNV of_info = {
244 .sType = VK_STRUCTURE_TYPE_OPTICAL_FLOW_IMAGE_FORMAT_INFO_NV,
245 .usage = of_usage,
246 };
247 VkImageViewCreateInfo view_info = {
248 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
249 .viewType = VK_IMAGE_VIEW_TYPE_2D,
250 .format = format,
251 .components = ff_comp_identity_map,
252 .subresourceRange = {
253 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
254 .levelCount = 1,
255 .layerCount = 1,
256 },
257 };
258
259 /* Exclusive sharing: the images are used by both the compute and the
260 * optical flow queue family, and maintenance9 - which init_filter()
261 * requires - preserves their contents across the two without explicit
262 * queue family ownership transfers. */
263 err = ff_vk_image_create(vkctx, img, mem, width, height, format, 1,
264 VK_IMAGE_TILING_OPTIMAL, usage, create_flags,
265 &of_info);
266 if (err < 0)
267 return err;
268
269 view_info.image = *img;
270 ret = vk->CreateImageView(hwctx->act_dev, &view_info, hwctx->alloc, view);
271 if (ret != VK_SUCCESS) {
272 av_log(s, AV_LOG_ERROR, "Failed to create optical flow image view: %s\n",
273 ff_vk_ret2str(ret));
274 return AVERROR_EXTERNAL;
275 }
276
277 return 0;
278}
279
280/* Transition the persistent optical flow images to VK_IMAGE_LAYOUT_GENERAL,
281 * which is the layout they remain in for the lifetime of the filter. */
283{
284 FFVulkanContext *vkctx = &s->vkctx;
285 FFVulkanFunctions *vk = &vkctx->vkfn;
286 FFVkExecContext *exec = ff_vk_exec_get(vkctx, &s->e);
287 VkImageMemoryBarrier2 bar[4 * FRUC_NB_SLOTS];
288 int nb_bar = 0;
289 int err;
290
291 err = ff_vk_exec_start(vkctx, exec);
292 if (err < 0)
293 return err;
294
295 for (int slot = 0; slot < FRUC_NB_SLOTS; slot++) {
296 FRUCFlowSlot *fs = &s->slots[slot];
297 VkImage imgs[4] = { fs->gray_img[0], fs->gray_img[1],
298 fs->flow_img[0], fs->flow_img[1] };
299
300 for (int i = 0; i < 4; i++) {
301 bar[nb_bar++] = (VkImageMemoryBarrier2) {
302 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
303 .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
304 .srcAccessMask = 0,
305 .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
306 .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
307 .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
308 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
309 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
310 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
311 .image = imgs[i],
312 .subresourceRange = {
313 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
314 .levelCount = 1,
315 .layerCount = 1,
316 },
317 };
318 }
319 }
320
321 vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
322 .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
323 .pImageMemoryBarriers = bar,
324 .imageMemoryBarrierCount = nb_bar,
325 });
326
327 err = ff_vk_exec_submit(vkctx, exec);
328 if (err < 0)
329 return err;
330 ff_vk_exec_wait(vkctx, exec);
331
332 return 0;
333}
334
336 int comp)
337{
338 const AVComponentDescriptor *c = &desc->comp[comp];
339
340 switch (vkfmt) {
341 /* These are all sampled as their logical components, whatever order the
342 * pix fmt uses to lay them out in host memory, so the component index is
343 * already the channel. */
344 case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
345 case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
346 case VK_FORMAT_B8G8R8_UNORM:
347 case VK_FORMAT_B8G8R8A8_UNORM:
348 return comp;
349 default:
350 return c->offset / ((c->depth + 7) / 8);
351 }
352}
353
355{
356 const AVComponentDescriptor *c = &desc->comp[0];
357
358 switch (vkfmt) {
359 /* xv30 packs V, Y, U, so luma is the logical G channel. */
360 case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
361 return 1;
362 default:
363 return c->offset / ((c->depth + 7) / 8);
364 }
365}
366
367static av_cold int check_sw_format(AVFilterContext *avctx, enum AVPixelFormat sw_format)
368{
369 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(sw_format);
370
371 if (!desc || !av_vkfmt_from_pixfmt(sw_format))
372 return AVERROR(EINVAL);
373
374 if (desc->flags & AV_PIX_FMT_FLAG_BAYER) {
375 av_log(avctx, AV_LOG_ERROR, "Bayer input (%s) is not supported\n",
376 desc->name);
377 return AVERROR(ENOTSUP);
378 }
379
380 if (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components > 1 &&
381 !(desc->flags & AV_PIX_FMT_FLAG_PLANAR) &&
382 (desc->log2_chroma_w || desc->log2_chroma_h)) {
383 av_log(avctx, AV_LOG_ERROR, "Subsampled packed YUV input (%s) is not "
384 "supported\n", desc->name);
385 return AVERROR(ENOTSUP);
386 }
387
388 /* Everything is sampled and stored through FF_VK_REP_FLOAT views. Formats
389 * with more than 16 bits of integer per component (rgb96, rgba128, gray32,
390 * gbrap32) have no float representation at all, so the image views would
391 * fail to be created. */
392 if (desc->comp[0].depth > 16 && !(desc->flags & AV_PIX_FMT_FLAG_FLOAT)) {
393 av_log(avctx, AV_LOG_ERROR, "Input format %s has no floating point "
394 "shader representation\n", desc->name);
395 return AVERROR(ENOTSUP);
396 }
397
398 return 0;
399}
400
401/* Whether an optimally tiled image last used by queue family "from" keeps its
402 * contents when used by queue family "to" without an explicit ownership
403 * transfer. */
404static int qf_transfer_preserves(FFVulkanContext *vkctx, uint32_t from, uint32_t to)
405{
406 uint32_t mask;
407
408 if (from >= (uint32_t)vkctx->tot_nb_qfs || to >= 32)
409 return 0;
410
411 mask = vkctx->ownership_props[from].optimalImageTransferToQueueFamilies;
412 return !!(mask & (1U << to));
413}
414
416{
417 int err;
418 FRUCVulkanContext *s = avctx->priv;
419 FFVulkanContext *vkctx = &s->vkctx;
420 FFVulkanFunctions *vk = &vkctx->vkfn;
421 const int planes = av_pix_fmt_count_planes(vkctx->output_format);
422 VkOpticalFlowGridSizeFlagsNV grids;
423 VkResult ret;
424
425 /* The core Vulkan support covers more formats than we can actually support
426 * in the filter, so reject anything we can't handle up front.
427 */
428 RET(check_sw_format(avctx, vkctx->output_format));
429
430 s->width = vkctx->output_width;
431 s->height = vkctx->output_height;
432
433 /* Optical flow tracks luma. YUV carries it directly, RGB has it derived so the
434 * flow follows brightness. The value only feeds the flow engine and is never
435 * written out, so a fixed BT.709 matrix suffices for all RGB inputs. */
436 {
438 const VkFormat *vkfmts = av_vkfmt_from_pixfmt(vkctx->output_format);
439 static const float bt709[3] = { 0.2126f, 0.7152f, 0.0722f }; /* R, G, B */
440
441 memset(s->luma_weights, 0, sizeof(s->luma_weights));
442 s->gray_planes = 1;
443
444 if (desc->flags & AV_PIX_FMT_FLAG_PLANAR) {
445 if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
446 /* One component per plane: weight each plane by its component's
447 * coefficient (comp[c] is R, G, B for c = 0, 1, 2). */
448 s->gray_planes = av_pix_fmt_count_planes(vkctx->output_format);
449 for (int c = 0; c < 3; c++)
450 s->luma_weights[desc->comp[c].plane][0] = bt709[c];
451 } else {
452 /* Planar and semi-planar YUV: plane 0 is luma already. */
453 s->luma_weights[0][0] = 1.0f;
454 }
455 } else if (desc->nb_components > 1) {
456 /* Packed: every component shares plane 0's texel, so the weights
457 * select channels rather than planes. */
458 if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
459 for (int c = 0; c < 3; c++)
460 s->luma_weights[0][packed_rgb_channel(desc, vkfmts[0], c)] = bt709[c];
461 } else {
462 /* Packed 4:4:4 YUV: luma is a single component, but not
463 * necessarily the first channel. */
464 s->luma_weights[0][packed_luma_channel(desc, vkfmts[0])] = 1.0f;
465 }
466 } else {
467 /* Gray: the one channel is luma. */
468 s->luma_weights[0][0] = 1.0f;
469 }
470
471 /* Special handling is required for formats that store fewer than
472 * 16bits of data in 16bits of storage. Some of these formats align the
473 * bits to the LSB end, and if these values are interpreted directly as
474 * 16bit values, they will be incorrect. While we could imagine passing
475 * the depth/shift information separately to the shader, we can apply
476 * the adjustment to the luma weights instead.
477 *
478 * Adjustment is not done if shift+depth == 16. In this case, the data
479 * is MSB aligned, and should be treated as a 16bit value.
480 */
481 for (int p = 0; p < s->gray_planes; p++) {
483 float scale;
484
485 if (vkfmts[p] != VK_FORMAT_R16_UNORM)
486 continue;
487
488 for (int c = 0; c < desc->nb_components; c++) {
489 if (desc->comp[c].plane == p) {
490 comp = &desc->comp[c];
491 break;
492 }
493 }
494 if (!comp)
495 return AVERROR_BUG;
496
497 if (comp->shift + comp->depth == 16)
498 continue;
499
500 scale = 65535.0f / (((1U << comp->depth) - 1U) << comp->shift);
501 for (int c = 0; c < 4; c++)
502 s->luma_weights[p][c] *= scale;
503 }
504 }
505
506 if (!(vkctx->extensions & FF_VK_EXT_OPTICAL_FLOW)) {
507 av_log(avctx, AV_LOG_ERROR, "Vulkan device does not support the "
508 "VK_NV_optical_flow extension\n");
509 return AVERROR(ENOTSUP);
510 }
511
512 /* The extension being enabled is not enough; its feature has to have been
513 * enabled at device creation too. Our own device setup does that, but a
514 * device handed to us by the caller may not have. */
515 {
516 const VkPhysicalDeviceOpticalFlowFeaturesNV *of;
517 of = ff_vk_find_struct(vkctx->hwctx->device_features.pNext,
518 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_OPTICAL_FLOW_FEATURES_NV);
519 if (!of || !of->opticalFlow) {
520 av_log(avctx, AV_LOG_ERROR, "Vulkan device was created without the "
521 "opticalFlow feature enabled\n");
522 return AVERROR(ENOTSUP);
523 }
524 }
525
526 /* The optical flow images must be passed between the compute and optical
527 * flow queue families; maintenance9 allows us to avoid the explicit
528 * ownership transfers that are otherwise required. */
529 if (!(vkctx->extensions & FF_VK_EXT_MAINTENANCE_9)) {
530 av_log(avctx, AV_LOG_ERROR, "Vulkan device does not support the "
531 "VK_KHR_maintenance9 extension\n");
532 return AVERROR(ENOTSUP);
533 }
534
535 {
536 const VkPhysicalDeviceMaintenance9FeaturesKHR *m9;
537 m9 = ff_vk_find_struct(vkctx->hwctx->device_features.pNext,
538 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_9_FEATURES_KHR);
539 if (!m9 || !m9->maintenance9) {
540 av_log(avctx, AV_LOG_ERROR, "Vulkan device was created without the "
541 "maintenance9 feature enabled\n");
542 return AVERROR(ENOTSUP);
543 }
544 }
545
546 s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
547 if (!s->qf) {
548 av_log(avctx, AV_LOG_ERROR, "Device has no compute queues\n");
549 return AVERROR(ENOTSUP);
550 }
551
552 s->qf_of = ff_vk_qf_find(vkctx, VK_QUEUE_OPTICAL_FLOW_BIT_NV, 0);
553 if (!s->qf_of) {
554 av_log(avctx, AV_LOG_ERROR, "Device has no optical flow queues\n");
555 return AVERROR(ENOTSUP);
556 }
557
558 /* Even with maintenance9 enabled, we can't assume that we can do implicit
559 * ownership transfers between the queues we care about; the driver doesn't
560 * have to support this, so we must check for declared support. */
561 if (s->qf_of->idx != s->qf->idx &&
562 (!qf_transfer_preserves(vkctx, s->qf->idx, s->qf_of->idx) ||
563 !qf_transfer_preserves(vkctx, s->qf_of->idx, s->qf->idx))) {
564 av_log(avctx, AV_LOG_ERROR, "The compute (%d) and optical flow (%d) queue "
565 "families cannot exchange optimally tiled images without explicit "
566 "queue family ownership transfers\n", s->qf->idx, s->qf_of->idx);
567 return AVERROR(ENOTSUP);
568 }
569
570 RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, FF_VK_DEFAULT_EXEC_CONTEXTS, 0, 0, 0, NULL));
571 /* One optical flow context per slot so that the optical flow execution for
572 * the next frame pair can be recorded and submitted without first
573 * host-waiting the previous pair's execution to retire its command buffer. */
574 RET(ff_vk_exec_pool_init(vkctx, s->qf_of, &s->e_of,
575 FRUC_NB_SLOTS, 0, 0, 0, NULL));
576 RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, VK_FILTER_LINEAR));
577 /* Flow is sampled through an integer view and its format has no linear
578 * filtering support, so use nearest. Requires normalised coords and as we
579 * have only one mip level, the mipmap filtering mode is irrelevant. */
580 RET(ff_vk_init_sampler(vkctx, &s->flow_sampler, 0, VK_FILTER_NEAREST));
581
582 {
583 VkSemaphoreTypeCreateInfo sem_type_info = {
584 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
585 .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
586 .initialValue = 0,
587 };
588 VkSemaphoreCreateInfo sem_info = {
589 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
590 .pNext = &sem_type_info,
591 };
592 if (vk->CreateSemaphore(vkctx->hwctx->act_dev, &sem_info,
593 vkctx->hwctx->alloc, &s->sem_gray) != VK_SUCCESS ||
594 vk->CreateSemaphore(vkctx->hwctx->act_dev, &sem_info,
595 vkctx->hwctx->alloc, &s->sem_flow) != VK_SUCCESS ||
596 vk->CreateSemaphore(vkctx->hwctx->act_dev, &sem_info,
597 vkctx->hwctx->alloc, &s->sem_interp) != VK_SUCCESS) {
598 av_log(avctx, AV_LOG_ERROR, "Failed to create synchronization semaphores\n");
599 return AVERROR_EXTERNAL;
600 }
601 }
602
603 /* Select the optical flow image formats and grid size. */
604 s->input_format = pick_of_format(s, VK_OPTICAL_FLOW_USAGE_INPUT_BIT_NV,
605 VK_FORMAT_R8_UNORM);
606 if (s->input_format != VK_FORMAT_R8_UNORM) {
607 av_log(avctx, AV_LOG_ERROR, "Optical flow R8 input format unavailable\n");
608 return AVERROR(ENOTSUP);
609 }
610 /* The engine emits flow vectors as signed fixed point (SFIXED5); theoretically
611 * it could be something else, but this is all we've seen on real hardware. */
612 s->flow_format = pick_of_format(s, VK_OPTICAL_FLOW_USAGE_OUTPUT_BIT_NV,
613 VK_FORMAT_R16G16_S10_5_NV);
614 if (s->flow_format != VK_FORMAT_R16G16_S10_5_NV) {
615 av_log(avctx, AV_LOG_ERROR, "Optical flow SFIXED5 vector format "
616 "unavailable (got %d)\n", s->flow_format);
617 return AVERROR(ENOTSUP);
618 }
619
620 static const struct {
621 int size;
622 VkOpticalFlowGridSizeFlagsNV bit;
623 } grid_map[] = {
624 { 1, VK_OPTICAL_FLOW_GRID_SIZE_1X1_BIT_NV },
625 { 2, VK_OPTICAL_FLOW_GRID_SIZE_2X2_BIT_NV },
626 { 4, VK_OPTICAL_FLOW_GRID_SIZE_4X4_BIT_NV },
627 { 8, VK_OPTICAL_FLOW_GRID_SIZE_8X8_BIT_NV },
628 };
629
630 grids = vkctx->optical_flow_props.supportedOutputGridSizes;
631 if (s->opt_grid_size) {
632 /* Honour an explicit grid request, erroring if the device lacks it. */
633 VkOpticalFlowGridSizeFlagsNV want = 0;
634 for (int i = 0; i < FF_ARRAY_ELEMS(grid_map); i++)
635 if (grid_map[i].size == s->opt_grid_size)
636 want = grid_map[i].bit;
637 if (!want || !(grids & want)) {
638 av_log(avctx, AV_LOG_ERROR, "Requested optical flow grid size %d is not "
639 "supported by the device (supported mask 0x%x)\n",
640 s->opt_grid_size, grids);
641 return AVERROR(ENOTSUP);
642 }
643 s->grid_size = s->opt_grid_size;
644 s->grid_bit = want;
645 } else {
646 /* Auto: pick the finest (smallest) grid the device supports. */
647 s->grid_size = 0;
648 for (int i = 0; i < FF_ARRAY_ELEMS(grid_map); i++) {
649 if (grids & grid_map[i].bit) {
650 s->grid_size = grid_map[i].size;
651 s->grid_bit = grid_map[i].bit;
652 break;
653 }
654 }
655 if (!s->grid_size) {
656 av_log(avctx, AV_LOG_ERROR, "No supported optical flow output grid size\n");
657 return AVERROR(ENOTSUP);
658 }
659 }
660
661 s->flow_width = (s->width + s->grid_size - 1) / s->grid_size;
662 s->flow_height = (s->height + s->grid_size - 1) / s->grid_size;
663
664 av_log(avctx, AV_LOG_INFO, "optical flow: perf %d, grid %d, flow %dx%d, bidir=%d, "
665 "min %dx%d max %dx%d\n", s->perf_level, s->grid_size, s->flow_width, s->flow_height,
666 vkctx->optical_flow_props.bidirectionalFlowSupported,
667 vkctx->optical_flow_props.minWidth, vkctx->optical_flow_props.minHeight,
668 vkctx->optical_flow_props.maxWidth, vkctx->optical_flow_props.maxHeight);
669
670 {
671 const VkPhysicalDeviceOpticalFlowPropertiesNV *ofp = &vkctx->optical_flow_props;
672
673 if ((uint32_t)s->width < ofp->minWidth || (uint32_t)s->width > ofp->maxWidth ||
674 (uint32_t)s->height < ofp->minHeight || (uint32_t)s->height > ofp->maxHeight) {
675 av_log(avctx, AV_LOG_ERROR, "Frame size %dx%d is outside the range the "
676 "device optical flow engine supports (%ux%u to %ux%u)\n",
677 s->width, s->height,
678 ofp->minWidth, ofp->minHeight, ofp->maxWidth, ofp->maxHeight);
679 return AVERROR(ENOTSUP);
680 }
681 }
682
683 if (!vkctx->optical_flow_props.bidirectionalFlowSupported) {
684 av_log(avctx, AV_LOG_ERROR, "Device optical flow engine does not support "
685 "bidirectional flow, which this filter requires\n");
686 return AVERROR(ENOTSUP);
687 }
688
689 /* Create the persistent optical flow images and sessions, one set per slot
690 * so consecutive source pairs round-robin between independent resources. */
691 for (int slot = 0; slot < FRUC_NB_SLOTS; slot++) {
692 FRUCFlowSlot *fs = &s->slots[slot];
693
694 RET(create_of_image(s, &fs->gray_img[0], &fs->gray_mem[0], &fs->gray_view[0],
695 s->input_format, s->width, s->height,
696 VK_OPTICAL_FLOW_USAGE_INPUT_BIT_NV,
697 VK_IMAGE_USAGE_STORAGE_BIT, 0));
698 RET(create_of_image(s, &fs->gray_img[1], &fs->gray_mem[1], &fs->gray_view[1],
699 s->input_format, s->width, s->height,
700 VK_OPTICAL_FLOW_USAGE_INPUT_BIT_NV,
701 VK_IMAGE_USAGE_STORAGE_BIT, 0));
702 /* The flow images must be created mutable, as we need to sample the raw
703 * integers through an R16G16_SINT view. SFIXED5 advertises SAMPLED_IMAGE,
704 * but if you try and use a float sampler, it will read the values as
705 * R16G16_SFLOAT, resulting in garbage. So we have to read the raw bits and
706 * rescale them (value/32) ourselves. */
707 RET(create_of_image(s, &fs->flow_img[0], &fs->flow_mem[0], &fs->flow_view[0],
708 s->flow_format, s->flow_width, s->flow_height,
709 VK_OPTICAL_FLOW_USAGE_OUTPUT_BIT_NV,
710 VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT));
711 RET(create_of_image(s, &fs->flow_img[1], &fs->flow_mem[1], &fs->flow_view[1],
712 s->flow_format, s->flow_width, s->flow_height,
713 VK_OPTICAL_FLOW_USAGE_OUTPUT_BIT_NV,
714 VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT));
715
716 for (int i = 0; i < 2; i++) {
717 VkImageViewCreateInfo view_info = {
718 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
719 .image = fs->flow_img[i],
720 .viewType = VK_IMAGE_VIEW_TYPE_2D,
721 .format = VK_FORMAT_R16G16_SINT,
722 .components = ff_comp_identity_map,
723 .subresourceRange = {
724 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
725 .levelCount = 1,
726 .layerCount = 1,
727 },
728 };
729 if (vk->CreateImageView(vkctx->hwctx->act_dev, &view_info,
730 vkctx->hwctx->alloc, &fs->flow_sint_view[i]) != VK_SUCCESS) {
731 av_log(avctx, AV_LOG_ERROR, "Failed to create flow SINT view\n");
732 return AVERROR_EXTERNAL;
733 }
734 }
735
736 ret = vk->CreateOpticalFlowSessionNV(vkctx->hwctx->act_dev,
737 &(VkOpticalFlowSessionCreateInfoNV) {
738 .sType = VK_STRUCTURE_TYPE_OPTICAL_FLOW_SESSION_CREATE_INFO_NV,
739 .width = s->width,
740 .height = s->height,
741 .imageFormat = s->input_format,
742 .flowVectorFormat = s->flow_format,
743 .outputGridSize = s->grid_bit,
744 .performanceLevel = s->perf_level,
745 .flags = VK_OPTICAL_FLOW_SESSION_CREATE_BOTH_DIRECTIONS_BIT_NV,
746 }, vkctx->hwctx->alloc, &fs->session);
747 if (ret != VK_SUCCESS) {
748 av_log(avctx, AV_LOG_ERROR, "Failed to create optical flow session: %s\n",
749 ff_vk_ret2str(ret));
750 return AVERROR_EXTERNAL;
751 }
752
753 static const VkOpticalFlowSessionBindingPointNV binding_points[] = {
754 VK_OPTICAL_FLOW_SESSION_BINDING_POINT_INPUT_NV,
755 VK_OPTICAL_FLOW_SESSION_BINDING_POINT_REFERENCE_NV,
756 VK_OPTICAL_FLOW_SESSION_BINDING_POINT_FLOW_VECTOR_NV,
757 VK_OPTICAL_FLOW_SESSION_BINDING_POINT_BACKWARD_FLOW_VECTOR_NV,
758 };
759 const VkImageView binding_views[] = {
760 fs->gray_view[0], fs->gray_view[1],
761 fs->flow_view[0], fs->flow_view[1],
762 };
763 for (int i = 0; i < FF_ARRAY_ELEMS(binding_points); i++) {
764 ret = vk->BindOpticalFlowSessionImageNV(vkctx->hwctx->act_dev, fs->session,
765 binding_points[i], binding_views[i], VK_IMAGE_LAYOUT_GENERAL);
766 if (ret != VK_SUCCESS) {
767 av_log(avctx, AV_LOG_ERROR, "Failed to bind optical flow session "
768 "image: %s\n", ff_vk_ret2str(ret));
769 return AVERROR_EXTERNAL;
770 }
771 }
772 }
773
775
776 /* Grayscale extraction shader. */
777 ff_vk_shader_load(&s->grayscale, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
778 (uint32_t []) { 32, 32, 1 }, 0);
779 ff_vk_shader_add_push_const(&s->grayscale, 0, sizeof(GrayscalePushData),
780 VK_SHADER_STAGE_COMPUTE_BIT);
781 {
783 {
784 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
785 .dimensions = 2,
786 .elems = s->gray_planes,
787 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
788 .samplers = DUP_SAMPLER(s->sampler),
789 },
790 {
791 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
792 .dimensions = 2,
793 .elems = s->gray_planes,
794 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
795 .samplers = DUP_SAMPLER(s->sampler),
796 },
797 {
798 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
799 .mem_layout = "r8",
800 .mem_quali = "writeonly",
801 .dimensions = 2,
802 .elems = 2,
803 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
804 },
805 };
806 ff_vk_shader_add_descriptor_set(vkctx, &s->grayscale, desc, 3, 0);
807 }
808 RET(ff_vk_shader_link(vkctx, &s->grayscale,
811 RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->grayscale));
812
813 /* Motion compensated interpolation shader. */
814 ff_vk_shader_load(&s->interpolate, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
815 (uint32_t []) { 32, 32, 1 }, 0);
816 ff_vk_shader_add_push_const(&s->interpolate, 0, sizeof(InterpolatePushData),
817 VK_SHADER_STAGE_COMPUTE_BIT);
818 {
820 {
821 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
822 .dimensions = 2,
823 .elems = planes,
824 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
825 .samplers = DUP_SAMPLER(s->sampler),
826 },
827 {
828 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
829 .dimensions = 2,
830 .elems = planes,
831 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
832 .samplers = DUP_SAMPLER(s->sampler),
833 },
834 {
835 .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
836 .mem_quali = "writeonly",
837 .dimensions = 2,
838 .elems = planes,
839 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
840 },
841 {
842 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
843 .dimensions = 2,
844 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
845 .samplers = DUP_SAMPLER(s->flow_sampler),
846 },
847 {
848 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
849 .dimensions = 2,
850 .stages = VK_SHADER_STAGE_COMPUTE_BIT,
851 .samplers = DUP_SAMPLER(s->flow_sampler),
852 },
853 };
854 ff_vk_shader_add_descriptor_set(vkctx, &s->interpolate, desc, 5, 0);
855 }
856 RET(ff_vk_shader_link(vkctx, &s->interpolate,
859 RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->interpolate));
860
861fail:
862 return err;
863}
864
865static void of_image_barrier(VkImageMemoryBarrier2 *bar, VkImage img,
866 VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access,
867 VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access)
868{
869 *bar = (VkImageMemoryBarrier2) {
870 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
871 .srcStageMask = src_stage,
872 .srcAccessMask = src_access,
873 .dstStageMask = dst_stage,
874 .dstAccessMask = dst_access,
875 .oldLayout = VK_IMAGE_LAYOUT_GENERAL,
876 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
877 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
878 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
879 .image = img,
880 .subresourceRange = {
881 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
882 .levelCount = 1,
883 .layerCount = 1,
884 },
885 };
886}
887
888/* Compute the forward and backward optical flow between f0 and f1. */
890{
891 int err;
892 FRUCVulkanContext *s = avctx->priv;
893 FFVulkanContext *vkctx = &s->vkctx;
894 FFVulkanFunctions *vk = &vkctx->vkfn;
895 FFVkExecContext *exec;
896 VkImageView f0_views[AV_NUM_DATA_POINTERS];
897 VkImageView f1_views[AV_NUM_DATA_POINTERS];
898 /* f0 and f1 each contribute one barrier per VkImage (a multi-image
899 * sw_format such as planar RGB or a separate alpha plane has several),
900 * plus the two single-image grayscale targets. */
901 VkImageMemoryBarrier2 img_bar[2 * AV_NUM_DATA_POINTERS + 2];
902 int nb_img_bar;
903
904 /* This pair's generation; sem_gray and sem_flow are signalled with it. */
905 s->gen++;
906
907 /* Round-robin slot for this pair's optical flow resources. */
908 FRUCFlowSlot *fs = &s->slots[s->gen % FRUC_NB_SLOTS];
909
910 /* --- Grayscale extraction on the compute queue. --- */
911 exec = ff_vk_exec_get(vkctx, &s->e);
912 err = ff_vk_exec_start(vkctx, exec);
913 if (err < 0)
914 return err;
915
916 RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->f0,
917 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
918 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
919 RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->f1,
920 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
921 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
922 /* Wait for the prior occupant of this slot (FRUC_NB_SLOTS pairs ago) to
923 * finish reading its grayscale images on the flow engine before overwriting
924 * them; the slot's first uses have no prior occupant. */
925 if (s->gen > FRUC_NB_SLOTS)
926 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_flow, s->gen - FRUC_NB_SLOTS,
927 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
928 /* Serialize the sem_gray signals: the exec pool may round-robin consecutive
929 * generations onto different queues, which have no implicit ordering, so
930 * wait for the previous generation's signal before emitting this one.
931 * Otherwise generation N+1 could signal the smaller value N+1 before N,
932 * which is invalid for a timeline semaphore. Value 0 is the initial state. */
933 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_gray, s->gen - 1,
934 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
935 ff_vk_exec_add_dep_signal_sem(vkctx, exec, s->sem_gray, s->gen,
936 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
937 RET(ff_vk_create_imageviews(vkctx, exec, f0_views, s->f0, FF_VK_REP_FLOAT));
938 RET(ff_vk_create_imageviews(vkctx, exec, f1_views, s->f1, FF_VK_REP_FLOAT));
939
940 for (int p = 0; p < s->gray_planes; p++) {
941 ff_vk_shader_update_img(vkctx, exec, &s->grayscale, 0, 0, p,
942 f0_views[p], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
943 s->sampler);
944 ff_vk_shader_update_img(vkctx, exec, &s->grayscale, 0, 1, p,
945 f1_views[p], VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
946 s->sampler);
947 }
948 ff_vk_shader_update_img(vkctx, exec, &s->grayscale, 0, 2, 0,
949 fs->gray_view[0], VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
950 ff_vk_shader_update_img(vkctx, exec, &s->grayscale, 0, 2, 1,
951 fs->gray_view[1], VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
952
953 ff_vk_exec_bind_shader(vkctx, exec, &s->grayscale);
954 {
956 memcpy(pd.luma_weights, s->luma_weights, sizeof(pd.luma_weights));
957 pd.planes = s->gray_planes;
958 ff_vk_shader_update_push_const(vkctx, exec, &s->grayscale,
959 VK_SHADER_STAGE_COMPUTE_BIT, 0,
960 sizeof(GrayscalePushData), &pd);
961 }
962
963 nb_img_bar = 0;
964 ff_vk_frame_barrier(vkctx, exec, s->f0, img_bar, &nb_img_bar,
965 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
966 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
967 VK_ACCESS_SHADER_READ_BIT,
968 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
969 VK_QUEUE_FAMILY_IGNORED);
970 ff_vk_frame_barrier(vkctx, exec, s->f1, img_bar, &nb_img_bar,
971 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
972 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
973 VK_ACCESS_SHADER_READ_BIT,
974 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
975 VK_QUEUE_FAMILY_IGNORED);
976 of_image_barrier(&img_bar[nb_img_bar++], fs->gray_img[0],
977 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0,
978 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);
979 of_image_barrier(&img_bar[nb_img_bar++], fs->gray_img[1],
980 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0,
981 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT);
982
983 vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
984 .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
985 .pImageMemoryBarriers = img_bar,
986 .imageMemoryBarrierCount = nb_img_bar,
987 });
988
989 vk->CmdDispatch(exec->buf,
990 FFALIGN(s->width, s->grayscale.lg_size[0]) / s->grayscale.lg_size[0],
991 FFALIGN(s->height, s->grayscale.lg_size[1]) / s->grayscale.lg_size[1],
992 1);
993
994 /* sem_gray orders the optical flow submission after this one
995 * and makes the grayscale writes visible across the queue boundary. */
996 RET(ff_vk_exec_submit(vkctx, exec));
997
998 /* --- Optical flow execution on the optical flow queue. --- */
999 exec = ff_vk_exec_get(vkctx, &s->e_of);
1000 err = ff_vk_exec_start(vkctx, exec);
1001 if (err < 0)
1002 return err;
1003
1004 /* Wait for the grayscale writes, signal once the flow has been written. */
1005 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_gray, s->gen,
1006 VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV);
1007 /* Wait for the slot's prior occupant to finish sampling its flow images
1008 * (fs->interp_done) before overwriting them; 0 is the initial state of an
1009 * unused slot and is satisfied immediately. */
1010 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_interp, fs->interp_done,
1011 VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV);
1012 /* Serialize the sem_flow signals for the same reason as sem_gray above: the
1013 * optical flow contexts may span multiple queues, so wait for the previous
1014 * generation's flow signal before signaling this one to keep the timeline
1015 * values monotonic. Value 0 is the initial state. */
1016 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_flow, s->gen - 1,
1017 VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV);
1018 ff_vk_exec_add_dep_signal_sem(vkctx, exec, s->sem_flow, s->gen,
1019 VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV);
1020
1021 /* The flow images stay in VK_IMAGE_LAYOUT_GENERAL and the semaphores handle
1022 * cross-queue visibility, so no image barriers are needed here. */
1023 vk->CmdOpticalFlowExecuteNV(exec->buf, fs->session,
1024 &(VkOpticalFlowExecuteInfoNV) {
1025 .sType = VK_STRUCTURE_TYPE_OPTICAL_FLOW_EXECUTE_INFO_NV,
1026 });
1027
1028 /* sem_flow orders the interpolation after the flow execution
1029 * and makes the flow writes visible on the compute queue. */
1030 RET(ff_vk_exec_submit(vkctx, exec));
1031
1032 s->flow_valid = 1;
1033 return 0;
1034
1035fail:
1036 ff_vk_exec_discard(vkctx, exec);
1037 return err;
1038}
1039
1040/* Visible texel extent of a plane: plane 0 (and non-planar / alpha) is full size,
1041 * chroma planes are subsampled. Mirrors hwcontext_vulkan's get_plane_wh, which is
1042 * how the frames context sizes each plane's image. */
1043static void plane_wh(const AVPixFmtDescriptor *desc, int width, int height,
1044 int plane, uint32_t *w, uint32_t *h)
1045{
1046 int sub = plane && plane != 3 && (desc->flags & AV_PIX_FMT_FLAG_PLANAR) &&
1047 !(desc->flags & AV_PIX_FMT_FLAG_RGB);
1048
1049 *w = sub ? AV_CEIL_RSHIFT(width, desc->log2_chroma_w) : width;
1050 *h = sub ? AV_CEIL_RSHIFT(height, desc->log2_chroma_h) : height;
1051}
1052
1053/* Produce the motion compensated output frame at temporal position t. */
1054static int interpolate_frame(AVFilterContext *avctx, AVFrame *out, float t)
1055{
1056 int err;
1057 FRUCVulkanContext *s = avctx->priv;
1058 FFVulkanContext *vkctx = &s->vkctx;
1059 FFVulkanFunctions *vk = &vkctx->vkfn;
1060 FFVkExecContext *exec;
1061 VkImageView f0_views[AV_NUM_DATA_POINTERS];
1062 VkImageView f1_views[AV_NUM_DATA_POINTERS];
1063 VkImageView out_views[AV_NUM_DATA_POINTERS];
1064 /* out, f0 and f1 each contribute one barrier per VkImage (a multi-image
1065 * sw_format such as planar RGB or a separate alpha plane has several),
1066 * plus the two single-image flow fields. */
1067 VkImageMemoryBarrier2 img_bar[3 * AV_NUM_DATA_POINTERS + 2];
1068 int nb_img_bar;
1070 InterpolatePushData pd = {
1071 .t = t,
1072 .planes = av_pix_fmt_count_planes(vkctx->output_format),
1073 };
1074 memcpy(pd.luma_weights, s->luma_weights, sizeof(pd.luma_weights));
1075 /* The shader works in the visible frame's coordinate space; the source and
1076 * output images may each be allocated larger than that. */
1077 for (int i = 0; i < pd.planes; i++) {
1078 uint32_t w, h;
1079 plane_wh(desc, s->width, s->height, i, &w, &h);
1080 pd.plane_size[i][0] = w;
1081 pd.plane_size[i][1] = h;
1082 }
1083
1084 /* Not via RET: the fail label discards deps on exec, which is not yet
1085 * acquired here, and compute_flow cleans up its own exec on failure. */
1086 if (!s->flow_valid) {
1087 err = compute_flow(avctx);
1088 if (err < 0)
1089 return err;
1090 }
1091
1092 /* Same slot compute_flow selected for this pair's generation. */
1093 FRUCFlowSlot *fs = &s->slots[s->gen % FRUC_NB_SLOTS];
1094
1095 exec = ff_vk_exec_get(vkctx, &s->e);
1096 err = ff_vk_exec_start(vkctx, exec);
1097 if (err < 0)
1098 return err;
1099
1100 /* Every interpolation of this pair waits on the same flow result, keyed by
1101 * the pair generation. */
1102 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_flow, s->gen,
1103 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
1104 /* Chain sem_interp: wait the previous value, signal the next. Keeps the
1105 * signals monotonic and lets the next pair's optical flow fence on the final
1106 * value before overwriting the flow images. */
1107 ff_vk_exec_add_dep_wait_sem(vkctx, exec, s->sem_interp, s->interp_value,
1108 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
1109 ff_vk_exec_add_dep_signal_sem(vkctx, exec, s->sem_interp, ++s->interp_value,
1110 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
1111 /* Record this pair's value so the next occupant of the slot can fence on it. */
1112 fs->interp_done = s->interp_value;
1113
1114 RET(ff_vk_exec_add_dep_frame(vkctx, exec, out,
1115 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1116 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
1117 RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->f0,
1118 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1119 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
1120 RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->f1,
1121 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1122 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
1123
1124 RET(ff_vk_create_imageviews(vkctx, exec, f0_views, s->f0, FF_VK_REP_FLOAT));
1125 RET(ff_vk_create_imageviews(vkctx, exec, f1_views, s->f1, FF_VK_REP_FLOAT));
1126 RET(ff_vk_create_imageviews(vkctx, exec, out_views, out, FF_VK_REP_FLOAT));
1127
1128 ff_vk_shader_update_img_array(vkctx, exec, &s->interpolate, s->f0, f0_views,
1129 0, 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1130 s->sampler);
1131 ff_vk_shader_update_img_array(vkctx, exec, &s->interpolate, s->f1, f1_views,
1132 0, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1133 s->sampler);
1134 ff_vk_shader_update_img_array(vkctx, exec, &s->interpolate, out, out_views,
1135 0, 2, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
1136 ff_vk_shader_update_img(vkctx, exec, &s->interpolate, 0, 3, 0,
1137 fs->flow_sint_view[0], VK_IMAGE_LAYOUT_GENERAL, s->flow_sampler);
1138 ff_vk_shader_update_img(vkctx, exec, &s->interpolate, 0, 4, 0,
1139 fs->flow_sint_view[1], VK_IMAGE_LAYOUT_GENERAL, s->flow_sampler);
1140
1141 ff_vk_exec_bind_shader(vkctx, exec, &s->interpolate);
1142 ff_vk_shader_update_push_const(vkctx, exec, &s->interpolate,
1143 VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pd), &pd);
1144
1145 nb_img_bar = 0;
1146 ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar,
1147 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1148 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
1149 VK_ACCESS_SHADER_WRITE_BIT,
1150 VK_IMAGE_LAYOUT_GENERAL,
1151 VK_QUEUE_FAMILY_IGNORED);
1152 ff_vk_frame_barrier(vkctx, exec, s->f0, img_bar, &nb_img_bar,
1153 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1154 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
1155 VK_ACCESS_SHADER_READ_BIT,
1156 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1157 VK_QUEUE_FAMILY_IGNORED);
1158 ff_vk_frame_barrier(vkctx, exec, s->f1, img_bar, &nb_img_bar,
1159 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1160 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
1161 VK_ACCESS_SHADER_READ_BIT,
1162 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
1163 VK_QUEUE_FAMILY_IGNORED);
1164 of_image_barrier(&img_bar[nb_img_bar++], fs->flow_img[0],
1165 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0,
1166 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT);
1167 of_image_barrier(&img_bar[nb_img_bar++], fs->flow_img[1],
1168 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0,
1169 VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT);
1170
1171 vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
1172 .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
1173 .pImageMemoryBarriers = img_bar,
1174 .imageMemoryBarrierCount = nb_img_bar,
1175 });
1176
1177 vk->CmdDispatch(exec->buf,
1178 FFALIGN(s->width, s->interpolate.lg_size[0]) / s->interpolate.lg_size[0],
1179 FFALIGN(s->height, s->interpolate.lg_size[1]) / s->interpolate.lg_size[1],
1180 1);
1181
1182 return ff_vk_exec_submit(vkctx, exec);
1183
1184fail:
1185 ff_vk_exec_discard(vkctx, exec);
1186 return err;
1187}
1188
1190{
1191 int err;
1192 FRUCVulkanContext *s = avctx->priv;
1193 FFVulkanContext *vkctx = &s->vkctx;
1194 FFVulkanFunctions *vk = &vkctx->vkfn;
1195 FFVkExecContext *exec;
1196 AVVkFrame *src_vk = (AVVkFrame *)src->data[0];
1197 AVVkFrame *out_vk = (AVVkFrame *)out->data[0];
1199 const int nb_planes = av_pix_fmt_count_planes(vkctx->output_format);
1200 const int src_nb_images = ff_vk_count_images(src_vk);
1201 const int out_nb_images = ff_vk_count_images(out_vk);
1202 /* src and out each contribute one barrier per VkImage; a multi-image
1203 * sw_format such as planar RGB or a separate alpha plane has several. */
1204 VkImageMemoryBarrier2 img_bar[2 * AV_NUM_DATA_POINTERS];
1205 int nb_img_bar = 0;
1206
1207 exec = ff_vk_exec_get(vkctx, &s->e);
1208 err = ff_vk_exec_start(vkctx, exec);
1209 if (err < 0)
1210 return err;
1211
1212 RET(ff_vk_exec_add_dep_frame(vkctx, exec, src,
1213 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1214 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT));
1215 RET(ff_vk_exec_add_dep_frame(vkctx, exec, out,
1216 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1217 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT));
1218
1219 ff_vk_frame_barrier(vkctx, exec, src, img_bar, &nb_img_bar,
1220 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1221 VK_PIPELINE_STAGE_2_COPY_BIT,
1222 VK_ACCESS_2_TRANSFER_READ_BIT,
1223 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1224 VK_QUEUE_FAMILY_IGNORED);
1225 ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar,
1226 VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
1227 VK_PIPELINE_STAGE_2_COPY_BIT,
1228 VK_ACCESS_2_TRANSFER_WRITE_BIT,
1229 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1230 VK_QUEUE_FAMILY_IGNORED);
1231
1232 vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
1233 .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
1234 .pImageMemoryBarriers = img_bar,
1235 .imageMemoryBarrierCount = nb_img_bar,
1236 });
1237
1238 for (int i = 0; i < nb_planes; i++) {
1239 uint32_t w, h;
1240 plane_wh(desc, s->width, s->height, i, &w, &h);
1241 VkImageCopy region = {
1242 .srcSubresource = { .aspectMask = ff_vk_aspect_flag(src, i), .layerCount = 1 },
1243 .dstSubresource = { .aspectMask = ff_vk_aspect_flag(out, i), .layerCount = 1 },
1244 .extent = { w, h, 1 },
1245 };
1246 vk->CmdCopyImage(exec->buf,
1247 src_vk->img[FFMIN(i, src_nb_images - 1)],
1248 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1249 out_vk->img[FFMIN(i, out_nb_images - 1)],
1250 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1251 1, &region);
1252 }
1253
1254 return ff_vk_exec_submit(vkctx, exec);
1255
1256fail:
1257 ff_vk_exec_discard(vkctx, exec);
1258 return err;
1259}
1260
1261/* Emit src unchanged. The frame must be copied rather than cloned: a clone keeps
1262 * the input's frames context, and the frame has to match the output context the
1263 * downstream link is configured for. */
1265{
1266 AVFilterLink *outlink = ctx->outputs[0];
1267 int ret;
1268
1269 *work = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1270 if (!*work)
1271 return AVERROR(ENOMEM);
1272 ret = av_frame_copy_props(*work, src);
1273 if (ret < 0)
1274 goto fail;
1275 ret = copy_frame(ctx, *work, src);
1276 if (ret < 0)
1277 goto fail;
1278 return 0;
1279fail:
1280 av_frame_free(work);
1281 return ret;
1282}
1283
1285{
1286 FRUCVulkanContext *s = ctx->priv;
1287 AVFilterLink *outlink = ctx->outputs[0];
1288 int64_t work_pts;
1289 int64_t interpolate8;
1290 int ret;
1291
1292 if (!s->f1)
1293 return 0;
1294 if (!s->f0 && !s->flush)
1295 return 0;
1296
1297 work_pts = s->start_pts + av_rescale_q(s->n, av_inv_q(s->dest_frame_rate),
1298 s->dest_time_base);
1299
1300 if (work_pts >= s->pts1 && !s->flush)
1301 return 0;
1302
1303 if (!s->f0) {
1304 av_assert1(s->flush);
1305 ret = passthrough_frame(ctx, &s->work, s->f1);
1306 if (ret < 0)
1307 return ret;
1308 /* We are flushing, so f1 is not needed once passed through. Free it so
1309 * the flush terminates instead of re-emitting it every activation. */
1310 av_frame_free(&s->f1);
1311 } else {
1312 if (work_pts >= s->pts1 + s->delta && s->flush)
1313 return 0;
1314
1315 interpolate8 = av_rescale(work_pts - s->pts0, 256, s->delta);
1316 if (interpolate8 >= 256) {
1317 ret = passthrough_frame(ctx, &s->work, s->f1);
1318 if (ret < 0)
1319 return ret;
1320 } else if (interpolate8 <= 0) {
1321 ret = passthrough_frame(ctx, &s->work, s->f0);
1322 if (ret < 0)
1323 return ret;
1324 } else {
1325 float t = (float)(work_pts - s->pts0) / (float)s->delta;
1326 s->work = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1327 if (!s->work)
1328 return AVERROR(ENOMEM);
1329 ret = av_frame_copy_props(s->work, s->f0);
1330 if (ret < 0) {
1331 av_frame_free(&s->work);
1332 return ret;
1333 }
1334 ret = interpolate_frame(ctx, s->work, t);
1335 if (ret < 0) {
1336 av_frame_free(&s->work);
1337 return ret;
1338 }
1339 }
1340 }
1341
1342 s->work->pts = work_pts;
1343 s->n++;
1344
1345 return 1;
1346}
1347
1349{
1350 int ret, status;
1351 AVFilterLink *inlink = ctx->inputs[0];
1352 AVFilterLink *outlink = ctx->outputs[0];
1353 FRUCVulkanContext *s = ctx->priv;
1354 AVFrame *inpicref;
1355 int64_t pts;
1356
1357 FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink);
1358
1359retry:
1360 ret = process_work_frame(ctx);
1361 if (ret < 0)
1362 return ret;
1363 else if (ret == 1)
1364 return ff_filter_frame(outlink, s->work);
1365
1366 ret = ff_inlink_consume_frame(inlink, &inpicref);
1367 if (ret < 0)
1368 return ret;
1369
1370 if (inpicref) {
1371 if (inpicref->flags & AV_FRAME_FLAG_INTERLACED)
1372 av_log(ctx, AV_LOG_WARNING, "Interlaced frame found - the output will not be correct.\n");
1373
1374 if (inpicref->pts == AV_NOPTS_VALUE) {
1375 av_log(ctx, AV_LOG_WARNING, "Ignoring frame without PTS.\n");
1376 av_frame_free(&inpicref);
1377 }
1378 }
1379
1380 if (inpicref) {
1381 pts = av_rescale_q(inpicref->pts, s->srce_time_base, s->dest_time_base);
1382
1383 if (s->f1 && pts == s->pts1) {
1384 av_log(ctx, AV_LOG_WARNING, "Ignoring frame with same PTS.\n");
1385 av_frame_free(&inpicref);
1386 }
1387 }
1388
1389 if (inpicref) {
1390 av_frame_free(&s->f0);
1391 s->f0 = s->f1;
1392 s->pts0 = s->pts1;
1393 s->f1 = inpicref;
1394 s->pts1 = pts;
1395 s->delta = s->pts1 - s->pts0;
1396 s->flow_valid = 0;
1397
1398 if (s->delta < 0) {
1399 av_log(ctx, AV_LOG_WARNING, "PTS discontinuity.\n");
1400 s->start_pts = s->pts1;
1401 s->n = 0;
1402 av_frame_free(&s->f0);
1403 }
1404
1405 if (s->start_pts == AV_NOPTS_VALUE)
1406 s->start_pts = s->pts1;
1407
1408 goto retry;
1409 }
1410
1411 if (ff_inlink_acknowledge_status(inlink, &status, &pts)) {
1412 if (!s->flush) {
1413 s->flush = 1;
1414 goto retry;
1415 }
1416 ff_outlink_set_status(outlink, status, pts);
1417 return 0;
1418 }
1419
1420 FF_FILTER_FORWARD_WANTED(outlink, inlink);
1421
1422 return FFERROR_NOT_READY;
1423}
1424
1425static int config_input(AVFilterLink *inlink)
1426{
1427 AVFilterContext *ctx = inlink->dst;
1428 FRUCVulkanContext *s = ctx->priv;
1429
1430 s->srce_time_base = inlink->time_base;
1431
1432 return ff_vk_filter_config_input(inlink);
1433}
1434
1435static int config_output(AVFilterLink *outlink)
1436{
1437 AVFilterContext *ctx = outlink->src;
1438 AVFilterLink *inlink = ctx->inputs[0];
1439 FilterLink *il = ff_filter_link(inlink);
1440 FilterLink *ol = ff_filter_link(outlink);
1441 FRUCVulkanContext *s = ctx->priv;
1442 double var_values[VARS_NB], res;
1443 int err;
1444 int exact;
1445
1446 ff_dlog(ctx, "config_output()\n");
1447
1448 ff_dlog(ctx,
1449 "config_output() input time base:%u/%u (%f)\n",
1450 ctx->inputs[0]->time_base.num,ctx->inputs[0]->time_base.den,
1451 av_q2d(ctx->inputs[0]->time_base));
1452
1453 // The fps option is an expression evaluated against the source frame rate
1454 var_values[VAR_SOURCE_FPS] = av_q2d(il->frame_rate);
1455 err = av_expr_parse_and_eval(&res, s->requested_frame_rate,
1456 var_names, var_values,
1457 NULL, NULL, NULL, NULL, NULL, 0, ctx);
1458 if (err < 0)
1459 return err;
1460
1461 s->dest_frame_rate = av_d2q(res, INT_MAX);
1462 if (s->dest_frame_rate.num <= 0 || s->dest_frame_rate.den <= 0) {
1464 "Invalid output frame rate '%s' (must evaluate to a positive value)\n",
1465 s->requested_frame_rate);
1466 return AVERROR(EINVAL);
1467 }
1468
1469 // make sure timebase is small enough to hold the framerate
1470
1471 exact = av_reduce(&s->dest_time_base.num, &s->dest_time_base.den,
1472 av_gcd((int64_t)s->srce_time_base.num * s->dest_frame_rate.num,
1473 (int64_t)s->srce_time_base.den * s->dest_frame_rate.den ),
1474 (int64_t)s->srce_time_base.den * s->dest_frame_rate.num, INT_MAX);
1475
1476 /* The source-timebase-derived reduction above can collapse to a zero time
1477 * base (av_reduce() bounds its result, so the numerator can underflow to
1478 * zero, leaving 0/1) when the source timebase shares no useful factors with
1479 * the requested rate, which would make the output timebase unusable. Fall
1480 * back to the plain 1/fps timebase in that case so a valid timebase is
1481 * always produced. */
1482 if (!s->dest_time_base.num || !s->dest_time_base.den) {
1483 exact = av_reduce(&s->dest_time_base.num, &s->dest_time_base.den,
1484 s->dest_frame_rate.den, s->dest_frame_rate.num, INT_MAX);
1485 }
1486
1488 "time base:%u/%u -> %u/%u exact:%d\n",
1489 s->srce_time_base.num, s->srce_time_base.den,
1490 s->dest_time_base.num, s->dest_time_base.den, exact);
1491 if (!exact) {
1492 av_log(ctx, AV_LOG_WARNING, "Timebase conversion is not exact\n");
1493 }
1494
1495 err = ff_vk_filter_config_output(outlink);
1496 if (err < 0)
1497 return err;
1498
1499 ol->frame_rate = s->dest_frame_rate;
1500 outlink->time_base = s->dest_time_base;
1501
1502 ff_dlog(ctx,
1503 "config_output() output time base:%u/%u (%f) w:%d h:%d\n",
1504 outlink->time_base.num, outlink->time_base.den,
1505 av_q2d(outlink->time_base),
1506 outlink->w, outlink->h);
1507
1508 return init_filter(ctx);
1509}
1510
1511static av_cold int init(AVFilterContext *avctx)
1512{
1513 FRUCVulkanContext *s = avctx->priv;
1514
1515 s->start_pts = AV_NOPTS_VALUE;
1516
1517 return ff_vk_filter_init(avctx);
1518}
1519
1520static av_cold void uninit(AVFilterContext *avctx)
1521{
1522 FRUCVulkanContext *s = avctx->priv;
1523 FFVulkanContext *vkctx = &s->vkctx;
1524 FFVulkanFunctions *vk = &vkctx->vkfn;
1525
1526 /* Free the execution pools first: this waits for every submitted command
1527 * buffer to retire (ff_vk_exec_pool_free fences each context). The pooled
1528 * submissions wait on and signal the timeline semaphores and reference the
1529 * optical flow images below, so those objects must outlive the wait — destroying
1530 * an in-use semaphore would leave a submission's fence permanently unsignaled
1531 * and deadlock the wait. */
1532 ff_vk_exec_pool_free(vkctx, &s->e);
1533 ff_vk_exec_pool_free(vkctx, &s->e_of);
1534
1535 if (s->sem_gray)
1536 vk->DestroySemaphore(vkctx->hwctx->act_dev, s->sem_gray, vkctx->hwctx->alloc);
1537 if (s->sem_flow)
1538 vk->DestroySemaphore(vkctx->hwctx->act_dev, s->sem_flow, vkctx->hwctx->alloc);
1539 if (s->sem_interp)
1540 vk->DestroySemaphore(vkctx->hwctx->act_dev, s->sem_interp, vkctx->hwctx->alloc);
1541 for (int slot = 0; slot < FRUC_NB_SLOTS; slot++) {
1542 FRUCFlowSlot *fs = &s->slots[slot];
1543 if (fs->session)
1544 vk->DestroyOpticalFlowSessionNV(vkctx->hwctx->act_dev, fs->session,
1545 vkctx->hwctx->alloc);
1546 for (int i = 0; i < 2; i++) {
1547 if (fs->gray_view[i])
1548 vk->DestroyImageView(vkctx->hwctx->act_dev, fs->gray_view[i], vkctx->hwctx->alloc);
1549 ff_vk_image_free(vkctx, &fs->gray_img[i], &fs->gray_mem[i]);
1550 if (fs->flow_view[i])
1551 vk->DestroyImageView(vkctx->hwctx->act_dev, fs->flow_view[i], vkctx->hwctx->alloc);
1552 if (fs->flow_sint_view[i])
1553 vk->DestroyImageView(vkctx->hwctx->act_dev, fs->flow_sint_view[i], vkctx->hwctx->alloc);
1554 ff_vk_image_free(vkctx, &fs->flow_img[i], &fs->flow_mem[i]);
1555 }
1556 }
1557
1558 ff_vk_shader_free(vkctx, &s->grayscale);
1559 ff_vk_shader_free(vkctx, &s->interpolate);
1560
1561 if (s->sampler)
1562 vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, vkctx->hwctx->alloc);
1563 if (s->flow_sampler)
1564 vk->DestroySampler(vkctx->hwctx->act_dev, s->flow_sampler, vkctx->hwctx->alloc);
1565
1566 ff_vk_uninit(vkctx);
1567
1568 av_frame_free(&s->f0);
1569 av_frame_free(&s->f1);
1570}
1571
1573 {
1574 .name = "default",
1575 .type = AVMEDIA_TYPE_VIDEO,
1576 .config_props = config_input,
1577 },
1578};
1579
1581 {
1582 .name = "default",
1583 .type = AVMEDIA_TYPE_VIDEO,
1584 .config_props = config_output,
1585 },
1586};
1587
1589 .p.name = "fruc_vulkan",
1590 .p.description = NULL_IF_CONFIG_SMALL("Frame rate up-conversion using the Vulkan NV optical flow extension"),
1591 .p.priv_class = &fruc_vulkan_class,
1592 .p.flags = AVFILTER_FLAG_HWDEVICE,
1593 .priv_size = sizeof(FRUCVulkanContext),
1594 .init = init,
1595 .uninit = uninit,
1599 .activate = activate,
1600 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
1601};
static int config_input(AVFilterLink *inlink)
static const char *const format[]
Definition af_aiir.c:444
const FFFilter ff_vf_fruc_vulkan
static FILE * out
static AVFormatContext * ctx
int32_t
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition avassert.h:58
int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts)
Test and acknowledge the change of status on the link.
Definition avfilter.c:1467
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe)
Take a frame from the link's FIFO and update the link's stats.
Definition avfilter.c:1520
@ VARS_NB
Definition boxblur.c:43
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define bit(string, value)
Definition cbs_mpeg2.c:56
#define s(width, name)
Definition cbs_vp9.c:198
#define fs(width, name, subs,...)
Definition cbs_vp9.c:200
#define FLAGS
Definition cmdutils.c:598
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
static void comp(unsigned char *dst, ptrdiff_t dst_stride, unsigned char *src, ptrdiff_t src_stride, int add)
Definition eamad.c:79
int av_expr_parse_and_eval(double *d, const char *s, const char *const *const_names, const double *const_values, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), void *opaque, int log_offset, void *log_ctx)
Parse and evaluate an expression.
Definition eval.c:839
simple arithmetic expression evaluator
const char * usage
#define AV_NUM_DATA_POINTERS
Definition frame.h:473
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
#define AVFILTER_FLAG_HWDEVICE
The filter can create hardware frames using AVFilterContext.hw_device_ctx.
Definition avfilter.h:187
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition error.h:52
#define AVERROR(e)
Definition error.h:45
#define AV_FRAME_FLAG_INTERLACED
A flag to mark frames whose content is interlaced.
Definition frame.h:695
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition rational.c:35
AVRational av_d2q(double d, int max)
Convert a double precision floating point number to a rational.
Definition rational.c:110
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition rational.h:104
static av_always_inline AVRational av_inv_q(AVRational q)
Invert a rational.
Definition rational.h:159
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
Rescale a 64-bit integer with rounding to nearest.
int64_t av_gcd(int64_t a, int64_t b)
Compute the greatest common divisor of two integer operands.
Definition mathematics.c:37
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition avutil.h:247
const VkFormat * av_vkfmt_from_pixfmt(enum AVPixelFormat p)
Returns the optimal per-plane Vulkan format for a given sw_format, one for each plane.
enum VkFormat VkFormat
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static int activate(AVBitStreamFilterContext *ctx)
static int config_output(AVBitStreamFilterLink *outlink)
const char * from
Definition jacosubdec.c:64
const char * to
Definition webvttdec.c:36
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FORWARD_WANTED(outlink, inlink)
Forward the frame_wanted_out flag from an output link to an input link.
Definition filters.h:694
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
static void ff_outlink_set_status(AVFilterLink *link, int status, int64_t pts)
Set the status field of a link from the source filter.
Definition filters.h:629
#define FFERROR_NOT_READY
Filters implementation helper functions and internal structures.
Definition filters.h:34
#define FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink)
Forward the status on an output link to an input link.
Definition filters.h:639
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
void ff_vk_shader_update_img_array(FFVulkanContext *s, FFVkExecContext *e, FFVulkanShader *shd, AVFrame *f, VkImageView *views, int set, int binding, VkImageLayout layout, VkSampler sampler)
Update a descriptor in a buffer with an image array.
Definition vulkan.c:2734
int ff_vk_shader_load(FFVulkanShader *shd, VkPipelineStageFlags stage, VkSpecializationInfo *spec, uint32_t wg_size[3], uint32_t required_subgroup_size)
Initialize a shader object.
Definition vulkan.c:2255
void ff_vk_shader_add_descriptor_set(FFVulkanContext *s, FFVulkanShader *shd, const FFVulkanDescriptorSetBinding *desc, int nb, int singular)
Add descriptor to a shader.
Definition vulkan.c:2565
void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool)
Definition vulkan.c:331
VkImageAspectFlags ff_vk_aspect_flag(AVFrame *f, int p)
Get the aspect flag for a plane from an image.
Definition vulkan.c:1671
int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf, FFVkExecPool *pool, int nb_contexts, int nb_queries, VkQueryType query_type, int query_64bit, const void *query_create_pnext)
Allocates/frees an execution pool.
Definition vulkan.c:395
void ff_vk_image_free(FFVulkanContext *s, VkImage *img, VkDeviceMemory *mem)
Free an image created by ff_vk_image_create(); all GPU use must have completed.
Definition vulkan.c:1186
void ff_vk_exec_wait(FFVulkanContext *s, FFVkExecContext *e)
Definition vulkan.c:645
int ff_vk_image_create(FFVulkanContext *s, VkImage *img, VkDeviceMemory *mem, int width, int height, VkFormat format, int nb_layers, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, void *create_pnext)
Memory/buffer/image allocation helpers.
Definition vulkan.c:1109
int ff_vk_shader_add_push_const(FFVulkanShader *shd, int offset, int size, VkShaderStageFlagBits stage)
Add/update push constants for execution.
Definition vulkan.c:1627
int ff_vk_shader_update_img(FFVulkanContext *s, FFVkExecContext *e, FFVulkanShader *shd, int set, int bind, int offs, VkImageView view, VkImageLayout layout, VkSampler sampler)
Sets an image descriptor for specified shader and binding.
Definition vulkan.c:2709
void ff_vk_uninit(FFVulkanContext *s)
Frees main context.
Definition vulkan.c:2830
const char * ff_vk_ret2str(VkResult res)
Converts Vulkan return values to strings.
Definition vulkan.c:42
const VkComponentMapping ff_comp_identity_map
Definition vulkan.c:34
void ff_vk_frame_barrier(FFVulkanContext *s, FFVkExecContext *e, AVFrame *pic, VkImageMemoryBarrier2 *bar, int *nb_bar, VkPipelineStageFlags2 src_stage, VkPipelineStageFlags2 dst_stage, VkAccessFlagBits2 new_access, VkImageLayout new_layout, uint32_t new_qf)
Definition vulkan.c:2212
int ff_vk_exec_start(FFVulkanContext *s, FFVkExecContext *e)
Start/submit/wait an execution.
Definition vulkan.c:660
int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e, VkImageView views[AV_NUM_DATA_POINTERS], AVFrame *f, enum FFVkShaderRepFormat rep_fmt)
Create an imageview and add it as a dependency to an execution.
Definition vulkan.c:2143
void ff_vk_shader_free(FFVulkanContext *s, FFVulkanShader *shd)
Free a shader.
Definition vulkan.c:2806
int ff_vk_shader_register_exec(FFVulkanContext *s, FFVkExecPool *pool, FFVulkanShader *shd)
Register a shader with an exec pool.
Definition vulkan.c:2599
int ff_vk_init_sampler(FFVulkanContext *s, VkSampler *sampler, int unnorm_coords, VkFilter filt)
Create a sampler.
Definition vulkan.c:1638
FFVkExecContext * ff_vk_exec_get(FFVulkanContext *s, FFVkExecPool *pool)
Retrieve an execution pool.
Definition vulkan.c:622
int ff_vk_exec_submit(FFVulkanContext *s, FFVkExecContext *e)
Definition vulkan.c:983
void ff_vk_exec_bind_shader(FFVulkanContext *s, FFVkExecContext *e, const FFVulkanShader *shd)
Bind a shader.
Definition vulkan.c:2783
AVVulkanDeviceQueueFamily * ff_vk_qf_find(FFVulkanContext *s, VkQueueFlagBits dev_family, VkVideoCodecOperationFlagBitsKHR vid_ops)
Chooses an appropriate QF.
Definition vulkan.c:316
void ff_vk_exec_add_dep_signal_sem(FFVulkanContext *s, FFVkExecContext *e, VkSemaphore sem, uint64_t val, VkPipelineStageFlagBits2 stage)
Definition vulkan.c:839
void ff_vk_exec_discard(FFVulkanContext *s, FFVkExecContext *e)
Definition vulkan.c:763
int ff_vk_shader_link(FFVulkanContext *s, FFVulkanShader *shd, const char *spirv, size_t spirv_len, const char *entrypoint)
Link a shader into an executable.
Definition vulkan.c:2459
int ff_vk_exec_add_dep_frame(FFVulkanContext *s, FFVkExecContext *e, AVFrame *f, VkPipelineStageFlagBits2 wait_stage, VkPipelineStageFlagBits2 signal_stage)
Definition vulkan.c:881
void ff_vk_shader_update_push_const(FFVulkanContext *s, FFVkExecContext *e, FFVulkanShader *shd, VkShaderStageFlagBits stage, int offset, size_t size, void *src)
Update push constant in a shader.
Definition vulkan.c:2773
void ff_vk_exec_add_dep_wait_sem(FFVulkanContext *s, FFVkExecContext *e, VkSemaphore sem, uint64_t val, VkPipelineStageFlagBits2 stage)
Definition vulkan.c:825
const char * desc
Definition libsvtav1.c:83
static const struct @257111027162314367033347246032313251342043035002 planes[]
uint8_t w
Definition llvidencdsp.c:39
static const uint16_t mask[17]
Definition lzw.c:38
#define FFMIN(a, b)
Definition macros.h:49
#define FFALIGN(x, a)
Definition macros.h:78
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
var_name
Definition noise.c:46
static const char *const var_names[]
Definition noise.c:30
AVOptions.
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3500
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_FLAG_RGB
The pixel format contains RGB-like data (as opposed to YUV/grayscale).
Definition pixdesc.h:136
#define AV_PIX_FMT_FLAG_FLOAT
The pixel format contains IEEE-754 floating point values.
Definition pixdesc.h:158
#define AV_PIX_FMT_FLAG_PLANAR
At least one pixel component is not in the first data plane.
Definition pixdesc.h:132
#define AV_PIX_FMT_FLAG_BAYER
The pixel format is following a Bayer pattern.
Definition pixdesc.h:152
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition pixfmt.h:379
#define FF_ARRAY_ELEMS(a)
An instance of a filter.
Definition avfilter.h:273
void * priv
private data for use by the filter
Definition avfilter.h:288
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition frame.h:574
int flags
Frame flags, a combination of AV_FRAME_FLAGS.
Definition frame.h:716
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
VkImage img[AV_NUM_DATA_POINTERS]
Vulkan images to which the memory is bound to.
Main Vulkan context, allocated as AVHWDeviceContext.hwctx.
VkPhysicalDevice phys_dev
Physical device.
const VkAllocationCallbacks * alloc
Custom memory allocator, else NULL.
VkDevice act_dev
Active device.
VkPhysicalDeviceFeatures2 device_features
This structure should be set to the set of features that present and enabled during device creation.
VkCommandBuffer buf
Definition vulkan.h:142
FFVulkanExtensions extensions
Definition vulkan.h:299
int output_height
Definition vulkan.h:351
VkPhysicalDeviceOpticalFlowPropertiesNV optical_flow_props
Definition vulkan.h:309
AVVulkanDeviceContext * hwctx
Definition vulkan.h:339
FFVulkanFunctions vkfn
Definition vulkan.h:298
enum AVPixelFormat output_format
Definition vulkan.h:352
VkDeviceMemory gray_mem[2]
VkImageView flow_view[2]
native (SFIXED5) view, bound to the OF session
VkImage gray_img[2]
grayscale inputs (INPUT, REFERENCE)
VkImage flow_img[2]
[0] forward, [1] backward
uint64_t interp_done
VkImageView flow_sint_view[2]
R16G16_SINT reinterpret view for sampling.
VkImageView gray_view[2]
VkOpticalFlowSessionNV session
VkDeviceMemory flow_mem[2]
VkSampler flow_sampler
nearest sampler for the flow vectors
FFVulkanShader interpolate
FFVulkanContext vkctx
char * requested_frame_rate
output fps as an expression
int opt_grid_size
requested grid in pixels (0 = finest)
int gray_planes
number of input planes the grayscale pass samples
int64_t pts1
current frame pts in dest_time_base
FRUCFlowSlot slots[FRUC_NB_SLOTS]
FFVkExecPool e_of
optical flow execution pool
int perf_level
VkOpticalFlowPerformanceLevelNV.
int flow_valid
flow computed for current (f0, f1) pair
VkSemaphore sem_flow
optical flow -> interpolation (compute)
VkSampler sampler
linear sampler for the video planes
FFVkExecPool e
compute execution pool
AVRational srce_time_base
timebase of source
AVFrame * f0
last frame
int64_t pts0
last frame pts in dest_time_base
AVVulkanDeviceQueueFamily * qf_of
optical flow queue family
AVRational dest_time_base
timebase of destination
VkSemaphore sem_gray
grayscale (compute) -> optical flow
VkFormat input_format
grayscale input format
float luma_weights[4][4]
RGB->Y weights for the grayscale pass.
AVVulkanDeviceQueueFamily * qf
compute queue family
uint64_t gen
source pair generation (sem_gray/sem_flow value)
int flush
1 if the filter is being flushed
int width
luma width
VkFormat flow_format
flow vector format
uint64_t interp_value
monotonic interpolation counter (sem_interp value)
AVFrame * f1
current frame
int64_t start_pts
pts of the first output frame
FFVulkanShader grayscale
int height
luma height
AVRational dest_frame_rate
output frames per second
int64_t delta
pts1 to pts0 delta
VkOpticalFlowGridSizeFlagsNV grid_bit
VkSemaphore sem_interp
interpolation reads -> next pair optical flow
int64_t n
output frame counter
float luma_weights[4][4]
per-plane RGB->Y weights (dotted with each plane's texel)
int32_t planes
number of input planes sampled per frame
float plane_size[4][2]
visible texel extent of each plane
float luma_weights[4][4]
per-plane RGB->Y weights, matching the grayscale pass
#define av_free(p)
#define ff_dlog(a,...)
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
static int64_t pts
int size
#define img
@ VAR_SOURCE_FPS
Definition vf_fps.c:57
static int packed_luma_channel(const AVPixFmtDescriptor *desc, VkFormat vkfmt)
const unsigned int ff_fruc_interpolate_comp_spv_len
static const AVOption fruc_vulkan_options[]
static int process_work_frame(AVFilterContext *ctx)
static void plane_wh(const AVPixFmtDescriptor *desc, int width, int height, int plane, uint32_t *w, uint32_t *h)
static int compute_flow(AVFilterContext *avctx)
const unsigned int ff_fruc_grayscale_comp_spv_len
static const AVFilterPad fruc_vulkan_outputs[]
static int config_input(AVFilterLink *inlink)
static int init_image_layouts(FRUCVulkanContext *s)
static int packed_rgb_channel(const AVPixFmtDescriptor *desc, VkFormat vkfmt, int comp)
static VkFormat pick_of_format(FRUCVulkanContext *s, VkOpticalFlowUsageFlagsNV usage, VkFormat preferred)
const unsigned char ff_fruc_grayscale_comp_spv_data[]
const unsigned char ff_fruc_interpolate_comp_spv_data[]
static int copy_frame(AVFilterContext *avctx, AVFrame *out, AVFrame *src)
static int create_of_image(FRUCVulkanContext *s, VkImage *img, VkDeviceMemory *mem, VkImageView *view, VkFormat format, int width, int height, VkOpticalFlowUsageFlagsNV of_usage, VkImageUsageFlags usage, VkImageCreateFlags create_flags)
static const AVFilterPad fruc_vulkan_inputs[]
static av_cold int check_sw_format(AVFilterContext *avctx, enum AVPixelFormat sw_format)
static int interpolate_frame(AVFilterContext *avctx, AVFrame *out, float t)
static int activate(AVFilterContext *ctx)
static av_cold void uninit(AVFilterContext *avctx)
static int passthrough_frame(AVFilterContext *ctx, AVFrame **work, AVFrame *src)
static av_cold int init_filter(AVFilterContext *avctx)
#define OFFSET(x)
static int config_output(AVFilterLink *outlink)
static void of_image_barrier(VkImageMemoryBarrier2 *bar, VkImage img, VkPipelineStageFlags2 src_stage, VkAccessFlags2 src_access, VkPipelineStageFlags2 dst_stage, VkAccessFlags2 dst_access)
static int qf_transfer_preserves(FFVulkanContext *vkctx, uint32_t from, uint32_t to)
#define FRUC_NB_SLOTS
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition video.c:89
static double c[64]
@ FF_VK_REP_FLOAT
Definition vulkan.h:441
#define FF_VK_DEFAULT_EXEC_CONTEXTS
Definition vulkan.h:121
#define RET(x)
Definition vulkan.h:37
static const void * ff_vk_find_struct(const void *chain, VkStructureType stype)
Definition vulkan.h:365
#define DUP_SAMPLER(x)
Definition vulkan.h:73
static int ff_vk_count_images(AVVkFrame *f)
Definition vulkan.h:356
int ff_vk_filter_config_input(AVFilterLink *inlink)
int ff_vk_filter_config_output(AVFilterLink *outlink)
int ff_vk_filter_init(AVFilterContext *avctx)
General lavfi IO functions.
#define FF_VK_EXT_OPTICAL_FLOW
#define FF_VK_EXT_MAINTENANCE_9