FFmpeg
ops.c
Go to the documentation of this file.
1 /**
2  * Copyright (C) 2026 Lynne
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/mem.h"
22 #include "libavutil/refstruct.h"
23 
24 #include "../graph.h"
25 #include "../ops_internal.h"
26 #include "../swscale_internal.h"
27 
28 #include "ops.h"
29 
30 #if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
31 #include "spvasm.h"
32 #endif
33 
34 static void ff_sws_vk_uninit(AVRefStructOpaque opaque, void *obj)
35 {
36  FFVulkanOpsCtx *s = obj;
37 
38  ff_vk_uninit(&s->vkctx);
39 }
40 
42 {
43  int err;
44  SwsInternal *c = sws_internal(sws);
45 
46  if (!c->hw_priv) {
47  c->hw_priv = av_refstruct_alloc_ext(sizeof(FFVulkanOpsCtx), 0, NULL,
49  if (!c->hw_priv)
50  return AVERROR(ENOMEM);
51  }
52 
53  FFVulkanOpsCtx *s = c->hw_priv;
54  if (s->vkctx.device_ref && s->vkctx.device_ref->data != dev_ref->data) {
55  /* Reinitialize with new context */
56  ff_vk_uninit(&s->vkctx);
57  } else if (s->vkctx.device_ref && s->vkctx.device_ref->data == dev_ref->data) {
58  return 0;
59  }
60 
61  err = ff_vk_init(&s->vkctx, sws, dev_ref, NULL);
62  if (err < 0)
63  return err;
64 
65  s->qf = ff_vk_qf_find(&s->vkctx, VK_QUEUE_COMPUTE_BIT, 0);
66  if (!s->qf) {
67  av_log(sws, AV_LOG_ERROR, "Device has no compute queues\n");
68  return AVERROR(ENOTSUP);
69  }
70 
71  return 0;
72 }
73 
75 {
76  SwsInternal *c = sws_internal(sws);
77  FFVulkanOpsCtx *s = c->hw_priv;
78  return s ? s->vkctx.device_ref : NULL;
79 }
80 
81 #define MAX_DITHER_BUFS 4
82 #define MAX_FILT_BUFS 4
83 #define MAX_DATA_BUFS (MAX_DITHER_BUFS + MAX_FILT_BUFS*4)
84 
85 typedef struct VulkanPriv {
94 } VulkanPriv;
95 
96 static void process(const SwsFrame *dst, const SwsFrame *src, int y, int h,
97  const SwsPass *pass)
98 {
99  VulkanPriv *p = (VulkanPriv *) pass->priv;
100  FFVkExecContext *ec = ff_vk_exec_get(&p->s->vkctx, &p->e);
101  FFVulkanFunctions *vk = &p->s->vkctx.vkfn;
102  ff_vk_exec_start(&p->s->vkctx, ec);
103 
104  AVFrame *src_f = (AVFrame *) src->avframe;
105  AVFrame *dst_f = (AVFrame *) dst->avframe;
106  ff_vk_exec_add_dep_frame(&p->s->vkctx, ec, src_f,
107  VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
108  VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
109  ff_vk_exec_add_dep_frame(&p->s->vkctx, ec, dst_f,
110  VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
111  VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT);
112 
113  VkImageView src_views[AV_NUM_DATA_POINTERS];
114  VkImageView dst_views[AV_NUM_DATA_POINTERS];
115  ff_vk_create_imageviews(&p->s->vkctx, ec, src_views, src_f, p->src_rep);
116  ff_vk_create_imageviews(&p->s->vkctx, ec, dst_views, dst_f, p->dst_rep);
117 
118  ff_vk_shader_update_img_array(&p->s->vkctx, ec, &p->shd, src_f, src_views,
119  0, 0, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
120  ff_vk_shader_update_img_array(&p->s->vkctx, ec, &p->shd, dst_f, dst_views,
121  0, 1, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
122 
123  int nb_img_bar = 0;
124  VkImageMemoryBarrier2 img_bar[8];
125  ff_vk_frame_barrier(&p->s->vkctx, ec, src_f, img_bar, &nb_img_bar,
126  VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
127  VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
128  VK_ACCESS_SHADER_READ_BIT,
129  VK_IMAGE_LAYOUT_GENERAL,
130  VK_QUEUE_FAMILY_IGNORED);
131  ff_vk_frame_barrier(&p->s->vkctx, ec, dst_f, img_bar, &nb_img_bar,
132  VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
133  VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
134  VK_ACCESS_SHADER_WRITE_BIT,
135  VK_IMAGE_LAYOUT_GENERAL,
136  VK_QUEUE_FAMILY_IGNORED);
137  vk->CmdPipelineBarrier2(ec->buf, &(VkDependencyInfo) {
138  .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
139  .pImageMemoryBarriers = img_bar,
140  .imageMemoryBarrierCount = nb_img_bar,
141  });
142 
143  if (p->interlaced) {
144  uint32_t field = pass->graph ? pass->graph->dst.field : 0;
145  ff_vk_shader_update_push_const(&p->s->vkctx, ec, &p->shd,
146  VK_SHADER_STAGE_COMPUTE_BIT,
147  0, sizeof(field), &field);
148  }
149 
150  ff_vk_exec_bind_shader(&p->s->vkctx, ec, &p->shd);
151 
152  vk->CmdDispatch(ec->buf,
153  FFALIGN(dst->width, p->shd.lg_size[0])/p->shd.lg_size[0],
154  FFALIGN(dst->height, p->shd.lg_size[1])/p->shd.lg_size[1],
155  1);
156 
157  ff_vk_exec_submit(&p->s->vkctx, ec);
158  ff_vk_exec_wait(&p->s->vkctx, ec);
159 }
160 
161 static void free_fn(void *priv)
162 {
163  VulkanPriv *p = priv;
164  ff_vk_exec_pool_free(&p->s->vkctx, &p->e);
165  ff_vk_shader_free(&p->s->vkctx, &p->shd);
166  for (int i = 0; i < p->nb_data_bufs; i++)
167  ff_vk_free_buf(&p->s->vkctx, &p->data_bufs[i]);
168  av_refstruct_unref(&p->s);
169  av_free(priv);
170 }
171 
173  const SwsFilterWeights *wd, FFVkBuffer *buf)
174 {
175  int err;
176 
177  /* Weights */
178  err = ff_vk_create_buf(&s->vkctx, buf,
179  wd->num_weights*sizeof(float) +
180  wd->dst_size*sizeof(int32_t), NULL, NULL,
181  VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
182  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
183  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
184  if (err < 0)
185  goto fail;
186 
187  float *weights_data;
188  err = ff_vk_map_buffer(&s->vkctx, buf,
189  (uint8_t **)&weights_data, 0);
190  if (err < 0)
191  goto fail;
192  for (int i = 0; i < wd->num_weights; i++)
193  weights_data[i] = (float) wd->weights[i] / SWS_FILTER_SCALE;
194 
195  memcpy(weights_data + wd->num_weights,
196  wd->offsets, wd->dst_size*sizeof(int32_t));
197 
198  ff_vk_unmap_buffer(&s->vkctx, buf, 1);
199 
200  return 0;
201 
202 fail:
203  ff_vk_free_buf(&p->s->vkctx, buf);
204  return 0;
205 }
206 
208  const SwsDitherOp *dd, FFVkBuffer *buf)
209 {
210  int err;
211 
212  int size = (1 << dd->size_log2);
213  err = ff_vk_create_buf(&s->vkctx, buf,
214  size*size*sizeof(float), NULL, NULL,
215  VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
216  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
217  VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
218  if (err < 0)
219  return err;
220 
221  float *dither_data;
222  err = ff_vk_map_buffer(&s->vkctx, buf, (uint8_t **)&dither_data, 0);
223  if (err < 0)
224  goto fail;
225 
226  for (int i = 0; i < size; i++) {
227  for (int j = 0; j < size; j++) {
228  const AVRational64 r = dd->matrix[i*size + j];
229  dither_data[i*size + j] = r.num/(float)r.den;
230  }
231  }
232 
233  ff_vk_unmap_buffer(&s->vkctx, buf, 1);
234 
235  return 0;
236 
237 fail:
238  ff_vk_free_buf(&p->s->vkctx, buf);
239  return err;
240 }
241 
242 static int create_bufs(FFVulkanOpsCtx *s, VulkanPriv *p, const SwsOpList *ops)
243 {
244  int err;
245  p->nb_data_bufs = 0;
246  for (int n = 0; n < ops->num_ops; n++) {
247  const SwsOp *op = &ops->ops[n];
248  if (op->op == SWS_OP_DITHER) {
249  av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
250  err = create_dither_buf(s, p, &op->dither,
251  &p->data_bufs[p->nb_data_bufs]);
252  if (err < 0)
253  goto fail;
254  p->nb_data_bufs++;
255  } else if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V) {
256  av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
257  err = create_filter_buf(s, p, op->filter.kernel,
258  &p->data_bufs[p->nb_data_bufs]);
259  if (err < 0)
260  goto fail;
261  p->nb_data_bufs++;
262  } else if ((op->op == SWS_OP_READ ||
263  op->op == SWS_OP_WRITE) && op->rw.filter.op) {
264  av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
265  err = create_filter_buf(s, p, op->rw.filter.kernel,
266  &p->data_bufs[p->nb_data_bufs]);
267  if (err < 0)
268  goto fail;
269  p->nb_data_bufs++;
270  }
271  }
272 
273  return 0;
274 
275 fail:
276  for (int i = 0; i < p->nb_data_bufs; i++)
277  ff_vk_free_buf(&p->s->vkctx, &p->data_bufs[i]);
278  return err;
279 }
280 
281 #if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
282 struct DitherData {
283  int size;
284  int arr_1d_id;
285  int arr_2d_id;
286  int struct_id;
287  int struct_ptr_id;
288  int id;
289  int mask_id;
290  int binding;
291 };
292 
293 struct FilterData {
295  int filter_size;
296  int dst_size;
297  int num_weights;
298 
299  int arr_w_in_id;
300  int arr_w_out_id;
301  int arr_o_id;
302  int struct_id;
303  int struct_ptr_id;
304 
305  int id; /* buffer ID */
306  int binding; /* descriptor idx in desc set 1 */
307 
308  int tap_const_base;
309 };
310 
311 typedef struct SPIRVIDs {
312  int in_vars[3 + MAX_DATA_BUFS + 1];
313 
314  int glfn;
315  int ep;
316 
317  /* Types */
318  int void_type;
319  int b_type;
320  int u32_type;
321  int i32_type;
322  int f32_type;
323  int void_fn_type;
324 
325  /* Define vector types */
326  int bvec2_type;
327  int u32vec2_type;
328  int i32vec2_type;
329 
330  int u32vec3_type;
331 
332  int u32vec4_type;
333  int f32vec4_type;
334  int f32mat4_type;
335 
336  /* Constants */
337  int u32_p;
338  int f32_p;
339  int f32_0;
340  int u32_cid[5];
341 
342  int const_ids[128];
343  int nb_const_ids;
344 
345  int linear_deco_off[16];
346  int linear_deco_ops[16];
347  int nb_linear_ops;
348 
349  struct DitherData dither[MAX_DITHER_BUFS];
350  int dither_ptr_elem_id;
351  int nb_dither_bufs;
352 
353  struct FilterData filt[MAX_FILT_BUFS];
354  int filt_o_ptr_id;
355  int nb_filter_bufs;
356 
357  int out_img_type;
358  int out_img_array_id;
359 
360  int in_img_type;
361  int in_img_array_id;
362 
363  /* Pointer types for images */
364  int u32vec3_tptr;
365  int out_img_tptr;
366  int out_img_sptr;
367 
368  int in_img_tptr;
369  int in_img_sptr;
370 
371  /* Interlaced handling */
372  int interlaced;
373  int push_const_struct_id;
374  int push_const_ptr_id;
375  int push_const_elem_ptr_id;
376  int push_const_var_id;
377  int field_i32;
378 } SPIRVIDs;
379 
380 /* Section 1: Function to define all shader header data, and decorations */
381 static void define_shader_header(SwsContext *sws, FFVulkanShader *shd,
382  const SwsOpList *ops, SPICtx *spi, SPIRVIDs *id)
383 {
384  spi_OpCapability(spi, SpvCapabilityShader); /* Shader type */
385 
386  /* Declare required capabilities */
387  spi_OpCapability(spi, SpvCapabilityInt16);
388  spi_OpCapability(spi, SpvCapabilityInt8);
389  spi_OpCapability(spi, SpvCapabilityImageQuery);
390  spi_OpCapability(spi, SpvCapabilityStorageImageReadWithoutFormat);
391  spi_OpCapability(spi, SpvCapabilityStorageImageWriteWithoutFormat);
392  spi_OpCapability(spi, SpvCapabilityStorageBuffer8BitAccess);
393  /* Import the GLSL set of functions (used for min/max) */
394  id->glfn = spi_OpExtInstImport(spi, "GLSL.std.450");
395 
396  /* Next section starts here */
397  spi_OpMemoryModel(spi, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
398 
399  /* Entrypoint */
400  id->ep = spi_OpEntryPoint(spi, SpvExecutionModelGLCompute, "main",
401  id->in_vars,
402  3 + id->nb_dither_bufs + id->nb_filter_bufs +
403  (id->interlaced ? 1 : 0));
404  spi_OpExecutionMode(spi, id->ep, SpvExecutionModeLocalSize,
405  shd->lg_size, 3);
406 
407  if (id->interlaced) {
408  spi_OpDecorate(spi, id->push_const_struct_id, SpvDecorationBlock);
409  spi_OpMemberDecorate(spi, id->push_const_struct_id, 0,
410  SpvDecorationOffset, 0);
411  }
412 
413  /* gl_GlobalInvocationID descriptor decorations */
414  spi_OpDecorate(spi, id->in_vars[0], SpvDecorationBuiltIn,
415  SpvBuiltInGlobalInvocationId);
416 
417  /* Input image descriptor decorations */
418  spi_OpDecorate(spi, id->in_vars[1], SpvDecorationNonWritable);
419  spi_OpDecorate(spi, id->in_vars[1], SpvDecorationDescriptorSet, 0);
420  spi_OpDecorate(spi, id->in_vars[1], SpvDecorationBinding, 0);
421 
422  /* Output image descriptor decorations */
423  spi_OpDecorate(spi, id->in_vars[2], SpvDecorationNonReadable);
424  spi_OpDecorate(spi, id->in_vars[2], SpvDecorationDescriptorSet, 0);
425  spi_OpDecorate(spi, id->in_vars[2], SpvDecorationBinding, 1);
426 
427  for (int i = 0; i < id->nb_dither_bufs; i++) {
428  spi_OpDecorate(spi, id->dither[i].arr_1d_id, SpvDecorationArrayStride,
429  sizeof(float));
430  spi_OpDecorate(spi, id->dither[i].arr_2d_id, SpvDecorationArrayStride,
431  id->dither[i].size*sizeof(float));
432  spi_OpDecorate(spi, id->dither[i].struct_id, SpvDecorationBlock);
433  spi_OpMemberDecorate(spi, id->dither[i].struct_id, 0, SpvDecorationOffset, 0);
434  spi_OpDecorate(spi, id->dither[i].id, SpvDecorationDescriptorSet, 1);
435  spi_OpDecorate(spi, id->dither[i].id, SpvDecorationBinding,
436  id->dither[i].binding);
437  }
438 
439  for (int i = 0; i < id->nb_filter_bufs; i++) {
440  struct FilterData *f = &id->filt[i];
441  spi_OpDecorate(spi, f->arr_w_in_id, SpvDecorationArrayStride,
442  sizeof(float));
443  spi_OpDecorate(spi, f->arr_w_out_id, SpvDecorationArrayStride,
444  f->filter_size*sizeof(float));
445  spi_OpDecorate(spi, f->arr_o_id, SpvDecorationArrayStride,
446  sizeof(int32_t));
447  spi_OpDecorate(spi, f->struct_id, SpvDecorationBlock);
448  spi_OpMemberDecorate(spi, f->struct_id, 0, SpvDecorationOffset, 0);
449  spi_OpMemberDecorate(spi, f->struct_id, 1, SpvDecorationOffset,
450  f->num_weights*sizeof(float));
451  spi_OpDecorate(spi, f->id, SpvDecorationDescriptorSet, 1);
452  spi_OpDecorate(spi, f->id, SpvDecorationBinding, f->binding);
453  }
454 
455  if (!(sws->flags & SWS_BITEXACT))
456  return;
457 
458  /* All linear arithmetic ops must be decorated with NoContraction */
459  for (int n = 0; n < ops->num_ops; n++) {
460  const SwsOp *op = &ops->ops[n];
461  if (op->op != SWS_OP_LINEAR)
462  continue;
463  av_assert0((id->nb_linear_ops + 1) <= FF_ARRAY_ELEMS(id->linear_deco_off));
464 
465  int nb_ops = 0;
466  for (int j = 0; j < 4; j++) {
467  nb_ops += !!op->lin.m[j][0].num;
468  nb_ops += op->lin.m[j][0].num && op->lin.m[j][4].num;
469  for (int i = 1; i < 4; i++) {
470  nb_ops += !!op->lin.m[j][i].num;
471  nb_ops += op->lin.m[j][i].num &&
472  (op->lin.m[j][0].num || op->lin.m[j][4].num);
473  }
474  }
475 
476  id->linear_deco_off[id->nb_linear_ops] = spi_reserve(spi, nb_ops*4*3);
477  id->linear_deco_ops[id->nb_linear_ops] = nb_ops;
478  id->nb_linear_ops++;
479  }
480 }
481 
482 /* Section 2: Define all types and constants */
483 static void define_shader_consts(SwsContext *sws, const SwsOpList *ops,
484  SPICtx *spi, SPIRVIDs *id)
485 {
486  /* Define scalar types */
487  id->void_type = spi_OpTypeVoid(spi);
488  id->b_type = spi_OpTypeBool(spi);
489  int u32_type =
490  id->u32_type = spi_OpTypeInt(spi, 32, 0);
491  id->i32_type = spi_OpTypeInt(spi, 32, 1);
492  int f32_type =
493  id->f32_type = spi_OpTypeFloat(spi, 32);
494  id->void_fn_type = spi_OpTypeFunction(spi, id->void_type, NULL, 0);
495 
496  /* Define vector types */
497  id->bvec2_type = spi_OpTypeVector(spi, id->b_type, 2);
498  id->u32vec2_type = spi_OpTypeVector(spi, u32_type, 2);
499  id->i32vec2_type = spi_OpTypeVector(spi, id->i32_type, 2);
500 
501  id->u32vec3_type = spi_OpTypeVector(spi, u32_type, 3);
502 
503  id->u32vec4_type = spi_OpTypeVector(spi, u32_type, 4);
504  id->f32vec4_type = spi_OpTypeVector(spi, f32_type, 4);
505  id->f32mat4_type = spi_OpTypeMatrix(spi, id->f32vec4_type, 4);
506 
507  /* Constants */
508  id->u32_p = spi_OpUndef(spi, u32_type);
509  id->f32_p = spi_OpUndef(spi, f32_type);
510  id->f32_0 = spi_OpConstantFloat(spi, f32_type, 0);
511  for (int i = 0; i < 5; i++)
512  id->u32_cid[i] = spi_OpConstantUInt(spi, u32_type, i);
513 
514  /* Operation constants */
515  id->nb_const_ids = 0;
516  for (int n = 0; n < ops->num_ops; n++) {
517  /* Make sure there's always enough space for the maximum number of
518  * constants a single operation needs (currently linear, 31 consts). */
519  av_assert0((id->nb_const_ids + 31) <= FF_ARRAY_ELEMS(id->const_ids));
520  const SwsOp *op = &ops->ops[n];
521  switch (op->op) {
522  case SWS_OP_CONVERT:
523  if (ff_sws_pixel_type_is_int(op->convert.to) && op->convert.expand) {
524  AVRational64 m = ff_sws_pixel_expand(op->type, op->convert.to);
525  int tmp = spi_OpConstantUInt(spi, id->u32_type, m.num);
526  tmp = spi_OpConstantComposite(spi, id->u32vec4_type,
527  tmp, tmp, tmp, tmp);
528  id->const_ids[id->nb_const_ids++] = tmp;
529  }
530  break;
531  case SWS_OP_CLEAR:
532  for (int i = 0; i < 4; i++) {
533  if (!SWS_COMP_TEST(op->clear.mask, i))
534  continue;
535  AVRational64 cv = op->clear.value[i];
536  if (op->type == SWS_PIXEL_F32) {
537  float q = (float)cv.num/cv.den;
538  id->const_ids[id->nb_const_ids++] =
539  spi_OpConstantFloat(spi, f32_type, q);
540  } else {
541  av_assert0(cv.den == 1);
542  id->const_ids[id->nb_const_ids++] =
543  spi_OpConstantUInt(spi, u32_type, cv.num);
544  }
545  }
546  break;
547  case SWS_OP_LSHIFT:
548  case SWS_OP_RSHIFT: {
549  int tmp = spi_OpConstantUInt(spi, u32_type, op->shift.amount);
550  tmp = spi_OpConstantComposite(spi, id->u32vec4_type,
551  tmp, tmp, tmp, tmp);
552  id->const_ids[id->nb_const_ids++] = tmp;
553  break;
554  }
555  case SWS_OP_SCALE: {
556  int tmp;
557  if (op->type == SWS_PIXEL_F32) {
558  float q = op->scale.factor.num/(float)op->scale.factor.den;
559  tmp = spi_OpConstantFloat(spi, f32_type, q);
560  tmp = spi_OpConstantComposite(spi, id->f32vec4_type,
561  tmp, tmp, tmp, tmp);
562  } else {
563  av_assert0(op->scale.factor.den == 1);
564  tmp = spi_OpConstantUInt(spi, u32_type, op->scale.factor.num);
565  tmp = spi_OpConstantComposite(spi, id->u32vec4_type,
566  tmp, tmp, tmp, tmp);
567  }
568  id->const_ids[id->nb_const_ids++] = tmp;
569  break;
570  }
571  case SWS_OP_MIN:
572  case SWS_OP_MAX:
573  for (int i = 0; i < 4; i++) {
574  int tmp;
575  AVRational64 cl = op->clamp.limit[i];
576  if (!op->clamp.limit[i].den) {
577  continue;
578  } else if (op->type == SWS_PIXEL_F32) {
579  float q = (float)cl.num/((float)cl.den);
580  tmp = spi_OpConstantFloat(spi, f32_type, q);
581  } else {
582  av_assert0(cl.den == 1);
583  tmp = spi_OpConstantUInt(spi, u32_type, cl.num);
584  }
585  id->const_ids[id->nb_const_ids++] = tmp;
586  }
587  break;
588  case SWS_OP_DITHER:
589  for (int i = 0; i < 4; i++) {
590  if (op->dither.y_offset[i] < 0)
591  continue;
592  int tmp = spi_OpConstantUInt(spi, u32_type, op->dither.y_offset[i]);
593  id->const_ids[id->nb_const_ids++] = tmp;
594  }
595  break;
596  case SWS_OP_LINEAR: {
597  int tmp;
598  float val;
599  for (int i = 0; i < 4; i++) {
600  for (int j = 0; j < 4; j++) {
601  int k = sws->flags & SWS_BITEXACT ? i : j;
602  int l = sws->flags & SWS_BITEXACT ? j : i;
603  val = op->lin.m[k][l].num/(float)op->lin.m[k][l].den;
604  id->const_ids[id->nb_const_ids++] =
605  spi_OpConstantFloat(spi, f32_type, val);
606  }
607  tmp = spi_OpConstantComposite(spi, id->f32vec4_type,
608  id->const_ids[id->nb_const_ids - 4],
609  id->const_ids[id->nb_const_ids - 3],
610  id->const_ids[id->nb_const_ids - 2],
611  id->const_ids[id->nb_const_ids - 1]);
612  id->const_ids[id->nb_const_ids++] = tmp;
613  }
614 
615  tmp = spi_OpConstantComposite(spi, id->f32mat4_type,
616  id->const_ids[id->nb_const_ids - 5*4 + 4],
617  id->const_ids[id->nb_const_ids - 5*3 + 4],
618  id->const_ids[id->nb_const_ids - 5*2 + 4],
619  id->const_ids[id->nb_const_ids - 5*1 + 4]);
620  id->const_ids[id->nb_const_ids++] = tmp;
621 
622  for (int i = 0; i < 4; i++) {
623  val = op->lin.m[i][4].num/(float)op->lin.m[i][4].den;
624  id->const_ids[id->nb_const_ids++] =
625  spi_OpConstantFloat(spi, f32_type, val);
626  }
627 
628  tmp = spi_OpConstantComposite(spi, id->f32vec4_type,
629  id->const_ids[id->nb_const_ids - 4],
630  id->const_ids[id->nb_const_ids - 3],
631  id->const_ids[id->nb_const_ids - 2],
632  id->const_ids[id->nb_const_ids - 1]);
633  id->const_ids[id->nb_const_ids++] = tmp;
634  break;
635  }
636  default:
637  break;
638  }
639  }
640 }
641 
642 /* Section 3: Define bindings */
643 static void define_shader_bindings(const SwsOpList *ops, SPICtx *spi, SPIRVIDs *id,
644  int in_img_count, int out_img_count)
645 {
646  id->dither_ptr_elem_id = spi_OpTypePointer(spi, SpvStorageClassUniform,
647  id->f32_type);
648 
649  struct DitherData *dither = id->dither;
650  for (int i = 0; i < id->nb_dither_bufs; i++) {
651  int size_id = spi_OpConstantUInt(spi, id->u32_type, dither[i].size);
652  dither[i].mask_id = spi_OpConstantUInt(spi, id->u32_type, dither[i].size - 1);
653  spi_OpTypeArray(spi, id->f32_type, dither[i].arr_1d_id, size_id);
654  spi_OpTypeArray(spi, dither[i].arr_1d_id, dither[i].arr_2d_id, size_id);
655  spi_OpTypeStruct(spi, dither[i].struct_id, dither[i].arr_2d_id);
656  dither[i].struct_ptr_id = spi_OpTypePointer(spi, SpvStorageClassUniform,
657  dither[i].struct_id);
658  dither[i].id = spi_OpVariable(spi, dither[i].id, dither[i].struct_ptr_id,
659  SpvStorageClassUniform, 0);
660  }
661 
662  /* Filter buffers: struct { float w[dst_size][filter_size]; int o[dst_size]; } */
663  id->filt_o_ptr_id = 0;
664  if (id->nb_filter_bufs)
665  id->filt_o_ptr_id = spi_OpTypePointer(spi, SpvStorageClassUniform,
666  id->i32_type);
667 
668  for (int i = 0; i < id->nb_filter_bufs; i++) {
669  struct FilterData *f = &id->filt[i];
670  int fs_id = spi_OpConstantUInt(spi, id->u32_type, f->filter_size);
671  int ds_id = spi_OpConstantUInt(spi, id->u32_type, f->dst_size);
672 
673  spi_OpTypeArray(spi, id->f32_type, f->arr_w_in_id, fs_id);
674  spi_OpTypeArray(spi, f->arr_w_in_id, f->arr_w_out_id, ds_id);
675  spi_OpTypeArray(spi, id->i32_type, f->arr_o_id, ds_id);
676  spi_OpTypeStruct(spi, f->struct_id, f->arr_w_out_id, f->arr_o_id);
677  f->struct_ptr_id = spi_OpTypePointer(spi, SpvStorageClassUniform,
678  f->struct_id);
679  f->id = spi_OpVariable(spi, f->id, f->struct_ptr_id,
680  SpvStorageClassUniform, 0);
681 
682  /* Signed tap-index constants 0..filter_size-1 (consecutive <id>s) */
683  f->tap_const_base = spi_OpConstantInt(spi, id->i32_type, 0);
684  for (int t = 1; t < f->filter_size; t++)
685  spi_OpConstantInt(spi, id->i32_type, t);
686  }
687 
688  const SwsOp *op_w = ff_sws_op_list_output(ops);
689  const SwsOp *op_r = ff_sws_op_list_input(ops);
690 
691  /* Define image types for descriptors */
692  id->out_img_type = spi_OpTypeImage(spi,
693  op_w->type == SWS_PIXEL_F32 ?
694  id->f32_type : id->u32_type,
695  2, 0, 0, 0, 2, SpvImageFormatUnknown);
696  id->out_img_array_id = spi_OpTypeArray(spi, id->out_img_type, spi_get_id(spi),
697  id->u32_cid[out_img_count]);
698 
699  id->in_img_type = 0;
700  id->in_img_array_id = 0;
701  if (op_r) {
702  /* If the formats match, we have to reuse the types due to SPIR-V not
703  * allowing redundant type defines */
704  int match = ((op_w->type == SWS_PIXEL_F32) ==
705  (op_r->type == SWS_PIXEL_F32));
706  id->in_img_type = match ? id->out_img_type :
707  spi_OpTypeImage(spi,
708  op_r->type == SWS_PIXEL_F32 ?
709  id->f32_type : id->u32_type,
710  2, 0, 0, 0, 2, SpvImageFormatUnknown);
711  id->in_img_array_id = spi_OpTypeArray(spi, id->in_img_type, spi_get_id(spi),
712  id->u32_cid[in_img_count]);
713  }
714 
715  /* Pointer types for images */
716  id->u32vec3_tptr = spi_OpTypePointer(spi, SpvStorageClassInput,
717  id->u32vec3_type);
718  id->out_img_tptr = spi_OpTypePointer(spi, SpvStorageClassUniformConstant,
719  id->out_img_array_id);
720  id->out_img_sptr = spi_OpTypePointer(spi, SpvStorageClassUniformConstant,
721  id->out_img_type);
722 
723  id->in_img_tptr = 0;
724  id->in_img_sptr = 0;
725  if (op_r) {
726  id->in_img_tptr= spi_OpTypePointer(spi, SpvStorageClassUniformConstant,
727  id->in_img_array_id);
728  id->in_img_sptr= spi_OpTypePointer(spi, SpvStorageClassUniformConstant,
729  id->in_img_type);
730  }
731 
732  /* Define inputs */
733  spi_OpVariable(spi, id->in_vars[0], id->u32vec3_tptr,
734  SpvStorageClassInput, 0);
735  if (op_r) {
736  spi_OpVariable(spi, id->in_vars[1], id->in_img_tptr,
737  SpvStorageClassUniformConstant, 0);
738  }
739  spi_OpVariable(spi, id->in_vars[2], id->out_img_tptr,
740  SpvStorageClassUniformConstant, 0);
741 
742  if (id->interlaced) {
743  spi_OpTypeStruct(spi, id->push_const_struct_id, id->u32_type);
744  id->push_const_ptr_id = spi_OpTypePointer(spi, SpvStorageClassPushConstant,
745  id->push_const_struct_id);
746  id->push_const_elem_ptr_id = spi_OpTypePointer(spi, SpvStorageClassPushConstant,
747  id->u32_type);
748  spi_OpVariable(spi, id->push_const_var_id, id->push_const_ptr_id,
749  SpvStorageClassPushConstant, 0);
750  }
751 }
752 
753 static int insert_vmat_linear(const SwsOp *op, SPICtx *spi, SPIRVIDs *id,
754  int data, int const_off)
755 {
756  data = spi_OpMatrixTimesVector(spi, id->f32vec4_type,
757  id->const_ids[const_off + 4*5],
758  data);
759  return spi_OpFAdd(spi, id->f32vec4_type,
760  id->const_ids[const_off + 4*5 + 1 + 4], data);
761 }
762 
763 static int insert_bitexact_linear(const SwsOp *op, SPICtx *spi, SPIRVIDs *id,
764  int data, int linear_ops_idx, int const_off)
765 {
766  int type_s = op->type == SWS_PIXEL_F32 ? id->f32_type : id->u32_type;
767  int type_v = op->type == SWS_PIXEL_F32 ? id->f32vec4_type : id->u32vec4_type;
768 
769  int tmp[4];
770  tmp[0] = spi_OpCompositeExtract(spi, type_s, data, 0);
771  tmp[1] = spi_OpCompositeExtract(spi, type_s, data, 1);
772  tmp[2] = spi_OpCompositeExtract(spi, type_s, data, 2);
773  tmp[3] = spi_OpCompositeExtract(spi, type_s, data, 3);
774 
775  int off = spi_reserve(spi, 0); /* Current offset */
776  spi->off = id->linear_deco_off[linear_ops_idx];
777  for (int i = 0; i < id->linear_deco_ops[linear_ops_idx]; i++)
778  spi_OpDecorate(spi, spi->id + i, SpvDecorationNoContraction);
779  spi->off = off;
780 
781  int res[4];
782  for (int j = 0; j < 4; j++) {
783  res[j] = op->type == SWS_PIXEL_F32 ? id->f32_0 : id->u32_cid[0];
784  if (op->lin.m[j][0].num)
785  res[j] = spi_OpFMul(spi, type_s, tmp[0],
786  id->const_ids[const_off + j*5 + 0]);
787 
788  if (op->lin.m[j][0].num && op->lin.m[j][4].num)
789  res[j] = spi_OpFAdd(spi, type_s,
790  id->const_ids[const_off + 4*5 + 1 + j], res[j]);
791  else if (op->lin.m[j][4].num)
792  res[j] = id->const_ids[const_off + 4*5 + 1 + j];
793 
794  for (int i = 1; i < 4; i++) {
795  if (!op->lin.m[j][i].num)
796  continue;
797 
798  int v = spi_OpFMul(spi, type_s, tmp[i],
799  id->const_ids[const_off + j*5 + i]);
800  if (op->lin.m[j][0].num || op->lin.m[j][4].num)
801  res[j] = spi_OpFAdd(spi, type_s, res[j], v);
802  else
803  res[j] = v;
804  }
805  }
806 
807  return spi_OpCompositeConstruct(spi, type_v,
808  res[0], res[1], res[2], res[3]);
809 }
810 
811 static int read_filtered(SPICtx *spi, SPIRVIDs *id, const SwsOpList *ops,
812  const SwsOp *op, const struct FilterData *f,
813  const int *in_img, int gid, int gi2)
814 {
815  const int is_h = f->filter == SWS_OP_FILTER_H;
816  const int src_interlaced = ops->src.interlaced;
817 
818  const int src_float = op->type == SWS_PIXEL_F32;
819  const int read_vtype = src_float ? id->f32vec4_type : id->u32vec4_type;
820 
821  /* Buffer array index along the filtered axis: pos.x (H) or pos.y (V) */
822  int axis = spi_OpCompositeExtract(spi, id->u32_type, gid, is_h ? 0 : 1);
823 
824  /* int o = filter_o[axis]; */
825  int o_ptr = spi_OpAccessChain(spi, id->filt_o_ptr_id, f->id,
826  id->u32_cid[1], axis);
827  int o = spi_OpLoad(spi, id->i32_type, o_ptr, SpvMemoryAccessMaskNone, 0);
828 
829  /* Signed pixel position, for the non-filtered coordinate axis */
830  int pos_x = spi_OpCompositeExtract(spi, id->i32_type, gi2, 0);
831  int pos_y = spi_OpCompositeExtract(spi, id->i32_type, gi2, 1);
832 
833  /* For interlaced horizontal filtering, the y coordinate of every tap is
834  * the (constant) destination y mapped into the source image. */
835  if (src_interlaced && is_h) {
836  pos_y = spi_OpShiftLeftLogical(spi, id->i32_type, pos_y, id->u32_cid[1]);
837  pos_y = spi_OpIAdd(spi, id->i32_type, pos_y, id->field_i32);
838  }
839 
840  /* Accumulators, initialized to zero */
841  int acc_s[4] = { id->f32_0, id->f32_0, id->f32_0, id->f32_0 };
842  int acc_v = id->f32_0;
843  if (op->rw.mode == SWS_RW_PACKED)
844  acc_v = spi_OpCompositeConstruct(spi, id->f32vec4_type,
845  id->f32_0, id->f32_0,
846  id->f32_0, id->f32_0);
847 
848  for (int t = 0; t < f->filter_size; t++) {
849  /* float w = filter_w[axis][t]; */
850  int w_ptr = spi_OpAccessChain(spi, id->dither_ptr_elem_id, f->id,
851  id->u32_cid[0], axis,
852  f->tap_const_base + t);
853  int w = spi_OpLoad(spi, id->f32_type, w_ptr,
854  SpvMemoryAccessMaskNone, 0);
855 
856  /* Source coordinate, filtered axis offset by the tap index */
857  int c = t ? spi_OpIAdd(spi, id->i32_type, o, f->tap_const_base + t) : o;
858  /* For interlaced vertical filtering, the per-tap source row is
859  * field-local; map it to the actual image row. */
860  if (src_interlaced && !is_h) {
861  c = spi_OpShiftLeftLogical(spi, id->i32_type, c, id->u32_cid[1]);
862  c = spi_OpIAdd(spi, id->i32_type, c, id->field_i32);
863  }
864  int coord = is_h ?
865  spi_OpCompositeConstruct(spi, id->i32vec2_type, c, pos_y) :
866  spi_OpCompositeConstruct(spi, id->i32vec2_type, pos_x, c);
867 
868  if (op->rw.mode == SWS_RW_PACKED) {
869  int px = spi_OpImageRead(spi, read_vtype,
870  in_img[ops->plane_src[0]], coord,
871  SpvImageOperandsMaskNone);
872  if (!src_float)
873  px = spi_OpConvertUToF(spi, id->f32vec4_type, px);
874  px = spi_OpVectorTimesScalar(spi, id->f32vec4_type, px, w);
875  acc_v = spi_OpFAdd(spi, id->f32vec4_type, acc_v, px);
876  } else {
877  for (int e = 0; e < op->rw.elems; e++) {
878  int px = spi_OpImageRead(spi, read_vtype,
879  in_img[ops->plane_src[e]], coord,
880  SpvImageOperandsMaskNone);
881  if (src_float) {
882  px = spi_OpCompositeExtract(spi, id->f32_type, px, 0);
883  } else {
884  px = spi_OpCompositeExtract(spi, id->u32_type, px, 0);
885  px = spi_OpConvertUToF(spi, id->f32_type, px);
886  }
887  px = spi_OpFMul(spi, id->f32_type, w, px);
888  acc_s[e] = spi_OpFAdd(spi, id->f32_type, acc_s[e], px);
889  }
890  }
891  }
892 
893  if (op->rw.mode == SWS_RW_PACKED)
894  return acc_v;
895  return spi_OpCompositeConstruct(spi, id->f32vec4_type,
896  acc_s[0], acc_s[1], acc_s[2], acc_s[3]);
897 }
898 
899 /* Plane indices refer to actual frame planes, so the image handle arrays
900  * have to cover the highest plane referenced, not just the plane count. */
901 static int rw_op_img_count(const SwsOp *op, const uint8_t *planes)
902 {
903  int count = 0;
904  for (int i = 0; i < ff_sws_rw_op_planes(op); i++)
905  count = FFMAX(count, planes[i] + 1);
906  return count;
907 }
908 
909 static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, FFVulkanOpsCtx *s,
910  const SwsOpList *ops, FFVulkanShader *shd)
911 {
912  uint8_t spvbuf[1024*16];
913  SPICtx spi_context = { 0 }, *spi = &spi_context;
914  SPIRVIDs spid_data = { 0 }, *id = &spid_data;
915  spi_init(spi, spvbuf, sizeof(spvbuf));
916 
917  id->interlaced = ops->src.interlaced || ops->dst.interlaced;
918  p->interlaced = id->interlaced;
919 
920  ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
921  (uint32_t []) { 32, 32, 1 }, 0);
922  shd->precompiled = 0;
923 
924  if (id->interlaced)
925  ff_vk_shader_add_push_const(shd, 0, sizeof(uint32_t),
926  VK_SHADER_STAGE_COMPUTE_BIT);
927 
928  /* Image ops, to determine types */
929  const SwsOp *op_w = ff_sws_op_list_output(ops);
930  int out_img_count = rw_op_img_count(op_w, ops->plane_dst);
931  p->dst_rep = op_w->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : FF_VK_REP_UINT;
932 
933  const SwsOp *op_r = ff_sws_op_list_input(ops);
934  int in_img_count = op_r ? rw_op_img_count(op_r, ops->plane_src) : 0;
935  if (op_r)
936  p->src_rep = op_r->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : FF_VK_REP_UINT;
937 
939  {
940  .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
941  .stages = VK_SHADER_STAGE_COMPUTE_BIT,
942  .elems = 4,
943  },
944  {
945  .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
946  .stages = VK_SHADER_STAGE_COMPUTE_BIT,
947  .elems = 4,
948  },
949  };
950  ff_vk_shader_add_descriptor_set(&s->vkctx, shd, desc_set, 2, 0);
951 
952  /* Create dither buffers */
953  int err = create_bufs(s, p, ops);
954  if (err < 0)
955  return err;
956 
957  /* Entrypoint inputs; gl_GlobalInvocationID, input and output images, dither */
958  id->in_vars[0] = spi_get_id(spi);
959  id->in_vars[1] = spi_get_id(spi);
960  id->in_vars[2] = spi_get_id(spi);
961 
962  /* Create dither and filter buffer descriptor set. Both are collected in
963  * op order, so the bindings match the buffer order from create_bufs().*/
964  id->nb_dither_bufs = 0;
965  id->nb_filter_bufs = 0;
966  int nb_data_bufs = 0;
967  for (int n = 0; n < ops->num_ops; n++) {
968  const SwsOp *op = &ops->ops[n];
969  int var_id = 0;
970 
971  if (op->op == SWS_OP_DITHER) {
972  if (id->nb_dither_bufs >= MAX_DITHER_BUFS)
973  return AVERROR(ENOTSUP);
974  struct DitherData *d = &id->dither[id->nb_dither_bufs++];
975  d->size = 1 << op->dither.size_log2;
976  d->arr_1d_id = spi_get_id(spi);
977  d->arr_2d_id = spi_get_id(spi);
978  d->struct_id = spi_get_id(spi);
979  d->id = spi_get_id(spi);
980  d->binding = nb_data_bufs;
981  var_id = d->id;
982  } else if (op->op == SWS_OP_READ && op->rw.filter.op) {
983  if (id->nb_filter_bufs >= MAX_FILT_BUFS)
984  return AVERROR(ENOTSUP);
985  const SwsFilterWeights *wd = op->rw.filter.kernel;
986  struct FilterData *f = &id->filt[id->nb_filter_bufs++];
987  f->filter = op->rw.filter.op;
988  f->filter_size = wd->filter_size;
989  f->dst_size = wd->dst_size;
990  f->num_weights = wd->num_weights;
991  f->arr_w_in_id = spi_get_id(spi);
992  f->arr_w_out_id = spi_get_id(spi);
993  f->arr_o_id = spi_get_id(spi);
994  f->struct_id = spi_get_id(spi);
995  f->id = spi_get_id(spi);
996  f->binding = nb_data_bufs;
997  var_id = f->id;
998  } else {
999  continue;
1000  }
1001 
1002  id->in_vars[3 + nb_data_bufs] = var_id;
1003  desc_set[nb_data_bufs++] = (FFVulkanDescriptorSetBinding) {
1004  .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1005  .stages = VK_SHADER_STAGE_COMPUTE_BIT,
1006  };
1007  }
1008  if (nb_data_bufs)
1009  ff_vk_shader_add_descriptor_set(&s->vkctx, shd, desc_set,
1010  nb_data_bufs, 1);
1011 
1012  if (id->interlaced) {
1013  id->push_const_struct_id = spi_get_id(spi);
1014  id->push_const_var_id = spi_get_id(spi);
1015  id->in_vars[3 + id->nb_dither_bufs + id->nb_filter_bufs] =
1016  id->push_const_var_id;
1017  }
1018 
1019  /* Define shader header sections */
1020  define_shader_header(sws, shd, ops, spi, id);
1021  define_shader_consts(sws, ops, spi, id);
1022  define_shader_bindings(ops, spi, id, in_img_count, out_img_count);
1023 
1024  /* Main function starts here */
1025  spi_OpFunction(spi, id->ep, id->void_type, 0, id->void_fn_type);
1026  spi_OpLabel(spi, spi_get_id(spi));
1027 
1028  /* Load input image handles */
1029  int in_img[4] = { 0 };
1030  for (int i = 0; i < in_img_count; i++) {
1031  /* Deref array and then the pointer */
1032  int img = spi_OpAccessChain(spi, id->in_img_sptr,
1033  id->in_vars[1], id->u32_cid[i]);
1034  in_img[i] = spi_OpLoad(spi, id->in_img_type, img,
1035  SpvMemoryAccessMaskNone, 0);
1036  }
1037 
1038  /* Load output image handles */
1039  int out_img[4] = { 0 };
1040  for (int i = 0; i < out_img_count; i++) {
1041  int img = spi_OpAccessChain(spi, id->out_img_sptr,
1042  id->in_vars[2], id->u32_cid[i]);
1043  out_img[i] = spi_OpLoad(spi, id->out_img_type, img,
1044  SpvMemoryAccessMaskNone, 0);
1045  }
1046 
1047  /* Load gl_GlobalInvocationID */
1048  int gid = spi_OpLoad(spi, id->u32vec3_type, id->in_vars[0],
1049  SpvMemoryAccessMaskNone, 0);
1050 
1051  /* ivec2(gl_GlobalInvocationID.xy) */
1052  gid = spi_OpVectorShuffle(spi, id->u32vec2_type, gid, gid, 0, 1);
1053  int gi2 = spi_OpBitcast(spi, id->i32vec2_type, gid);
1054 
1055  /* For interlaced sources/destinations the shader operates on field-local
1056  * coordinates, while images contain the full frame. Map the y axis to the
1057  * actual image row: image_y = field_y * 2 + field. */
1058  int dst_gid = gid, dst_gi2 = gi2;
1059  int src_gid = gid;
1060  if (id->interlaced) {
1061  int field_u32_ptr = spi_OpAccessChain(spi, id->push_const_elem_ptr_id,
1062  id->push_const_var_id,
1063  id->u32_cid[0]);
1064  int field_u32 = spi_OpLoad(spi, id->u32_type, field_u32_ptr,
1065  SpvMemoryAccessMaskNone, 0);
1066  id->field_i32 = spi_OpBitcast(spi, id->i32_type, field_u32);
1067 
1068  int img_y_i32 = spi_OpShiftLeftLogical(spi, id->i32_type,
1069  spi_OpCompositeExtract(spi, id->i32_type, gi2, 1),
1070  id->u32_cid[1]);
1071  img_y_i32 = spi_OpIAdd(spi, id->i32_type, img_y_i32, id->field_i32);
1072 
1073  int gi2_x = spi_OpCompositeExtract(spi, id->i32_type, gi2, 0);
1074  int mapped_gi2 = spi_OpCompositeConstruct(spi, id->i32vec2_type,
1075  gi2_x, img_y_i32);
1076  int mapped_gid = spi_OpBitcast(spi, id->u32vec2_type, mapped_gi2);
1077 
1078  if (ops->src.interlaced)
1079  src_gid = mapped_gid;
1080  if (ops->dst.interlaced) {
1081  dst_gid = mapped_gid;
1082  dst_gi2 = mapped_gi2;
1083  }
1084  }
1085 
1086  /* imageSize(out_img[0]); */
1087  int img1_s = spi_OpImageQuerySize(spi, id->i32vec2_type, out_img[0]);
1088  int scmp = spi_OpSGreaterThanEqual(spi, id->bvec2_type, dst_gi2, img1_s);
1089  scmp = spi_OpAny(spi, id->b_type, scmp);
1090 
1091  /* if (out of bounds) return */
1092  int quit_label = spi_get_id(spi), merge_label = spi_get_id(spi);
1093  spi_OpSelectionMerge(spi, merge_label, SpvSelectionControlMaskNone);
1094  spi_OpBranchConditional(spi, scmp, quit_label, merge_label, 0);
1095 
1096  spi_OpLabel(spi, quit_label);
1097  spi_OpReturn(spi); /* Quit if out of bounds here */
1098  spi_OpLabel(spi, merge_label);
1099 
1100  /* Initialize main data state */
1101  int data;
1102  if (ops->ops[0].type == SWS_PIXEL_F32)
1103  data = spi_OpCompositeConstruct(spi, id->f32vec4_type,
1104  id->f32_p, id->f32_p,
1105  id->f32_p, id->f32_p);
1106  else
1107  data = spi_OpCompositeConstruct(spi, id->u32vec4_type,
1108  id->u32_p, id->u32_p,
1109  id->u32_p, id->u32_p);
1110 
1111  /* Keep track of which constant/buffer to use */
1112  int nb_const_ids = 0;
1113  int nb_dither_bufs = 0;
1114  int nb_linear_ops = 0;
1115  int nb_filter_used = 0;
1116 
1117  /* Operations */
1118  for (int n = 0; n < ops->num_ops; n++) {
1119  const SwsOp *op = &ops->ops[n];
1120  SwsPixelType cur_type = op->op == SWS_OP_CONVERT ?
1121  op->convert.to : op->type;
1122  int type_v = cur_type == SWS_PIXEL_F32 ?
1123  id->f32vec4_type : id->u32vec4_type;
1124  int type_s = cur_type == SWS_PIXEL_F32 ?
1125  id->f32_type : id->u32_type;
1126  int uid = cur_type == SWS_PIXEL_F32 ?
1127  id->f32_p : id->u32_p;
1128 
1129  switch (op->op) {
1130  case SWS_OP_READ:
1131  if (op->rw.frac) {
1132  return AVERROR(ENOTSUP);
1133  } else if (op->rw.filter.op) {
1134  av_assert0(op->rw.mode != SWS_RW_PALETTE);
1135  data = read_filtered(spi, id, ops, op,
1136  &id->filt[nb_filter_used++],
1137  in_img, gid, gi2);
1138  } else if (op->rw.mode == SWS_RW_PACKED) {
1139  data = spi_OpImageRead(spi, type_v, in_img[ops->plane_src[0]],
1140  src_gid, SpvImageOperandsMaskNone);
1141  } else if (op->rw.mode == SWS_RW_PLANAR) {
1142  int tmp[4] = { uid, uid, uid, uid };
1143  for (int i = 0; i < op->rw.elems; i++) {
1144  tmp[i] = spi_OpImageRead(spi, type_v,
1145  in_img[ops->plane_src[i]], src_gid,
1146  SpvImageOperandsMaskNone);
1147  tmp[i] = spi_OpCompositeExtract(spi, type_s, tmp[i], 0);
1148  }
1149  data = spi_OpCompositeConstruct(spi, type_v,
1150  tmp[0], tmp[1], tmp[2], tmp[3]);
1151  } else {
1152  return AVERROR(ENOTSUP);
1153  }
1154  break;
1155  case SWS_OP_WRITE:
1156  if (op->rw.frac || op->rw.filter.op) {
1157  return AVERROR(ENOTSUP);
1158  } else if (op->rw.mode == SWS_RW_PACKED) {
1159  spi_OpImageWrite(spi, out_img[ops->plane_dst[0]], dst_gid, data,
1160  SpvImageOperandsMaskNone);
1161  } else {
1162  for (int i = 0; i < op->rw.elems; i++) {
1163  int tmp = spi_OpCompositeExtract(spi, type_s, data, i);
1164  tmp = spi_OpCompositeConstruct(spi, type_v, tmp, tmp, tmp, tmp);
1165  spi_OpImageWrite(spi, out_img[ops->plane_dst[i]], dst_gid, tmp,
1166  SpvImageOperandsMaskNone);
1167  }
1168  }
1169  break;
1170  case SWS_OP_CLEAR:
1171  for (int i = 0; i < 4; i++) {
1172  if (!SWS_COMP_TEST(op->clear.mask, i))
1173  continue;
1174  data = spi_OpCompositeInsert(spi, type_v,
1175  id->const_ids[nb_const_ids++],
1176  data, i);
1177  }
1178  break;
1179  case SWS_OP_SWIZZLE:
1180  data = spi_OpVectorShuffle(spi, type_v, data, data,
1181  op->swizzle.in[0],
1182  op->swizzle.in[1],
1183  op->swizzle.in[2],
1184  op->swizzle.in[3]);
1185  break;
1186  case SWS_OP_CONVERT:
1187  if (ff_sws_pixel_type_is_int(cur_type) && op->convert.expand)
1188  data = spi_OpIMul(spi, type_v, data, id->const_ids[nb_const_ids++]);
1189  else if (op->type == SWS_PIXEL_F32 && type_s == id->u32_type)
1190  data = spi_OpConvertFToU(spi, type_v, data);
1191  else if (op->type != SWS_PIXEL_F32 && type_s == id->f32_type)
1192  data = spi_OpConvertUToF(spi, type_v, data);
1193  break;
1194  case SWS_OP_LSHIFT:
1195  data = spi_OpShiftLeftLogical(spi, type_v, data,
1196  id->const_ids[nb_const_ids++]);
1197  break;
1198  case SWS_OP_RSHIFT:
1199  data = spi_OpShiftRightLogical(spi, type_v, data,
1200  id->const_ids[nb_const_ids++]);
1201  break;
1202  case SWS_OP_SCALE:
1203  if (op->type == SWS_PIXEL_F32)
1204  data = spi_OpFMul(spi, type_v, data,
1205  id->const_ids[nb_const_ids++]);
1206  else
1207  data = spi_OpIMul(spi, type_v, data,
1208  id->const_ids[nb_const_ids++]);
1209  break;
1210  case SWS_OP_MIN:
1211  case SWS_OP_MAX: {
1212  int t = op->type == SWS_PIXEL_F32 ?
1213  op->op == SWS_OP_MIN ? GLSLstd450FMin : GLSLstd450FMax :
1214  op->op == SWS_OP_MIN ? GLSLstd450UMin : GLSLstd450UMax;
1215  for (int i = 0; i < 4; i++) {
1216  if (!op->clamp.limit[i].den)
1217  continue;
1218  int tmp = spi_OpCompositeExtract(spi, type_s, data, i);
1219  tmp = spi_OpExtInst(spi, type_s, id->glfn, t,
1220  tmp, id->const_ids[nb_const_ids++]);
1221  data = spi_OpCompositeInsert(spi, type_v, tmp, data, i);
1222  }
1223  break;
1224  }
1225  case SWS_OP_DITHER: {
1226  int did = nb_dither_bufs++;
1227  int x_id = spi_OpCompositeExtract(spi, id->u32_type, gid, 0);
1228  int y_pos = spi_OpCompositeExtract(spi, id->u32_type, gid, 1);
1229  x_id = spi_OpBitwiseAnd(spi, id->u32_type, x_id,
1230  id->dither[did].mask_id);
1231  for (int i = 0; i < 4; i++) {
1232  if (op->dither.y_offset[i] < 0)
1233  continue;
1234 
1235  int y_id = spi_OpIAdd(spi, id->u32_type, y_pos,
1236  id->const_ids[nb_const_ids++]);
1237  y_id = spi_OpBitwiseAnd(spi, id->u32_type, y_id,
1238  id->dither[did].mask_id);
1239 
1240  int ptr = spi_OpAccessChain(spi, id->dither_ptr_elem_id,
1241  id->dither[did].id, id->u32_cid[0],
1242  y_id, x_id);
1243  int val = spi_OpLoad(spi, id->f32_type, ptr,
1244  SpvMemoryAccessMaskNone, 0);
1245 
1246  int tmp = spi_OpCompositeExtract(spi, type_s, data, i);
1247  tmp = spi_OpFAdd(spi, type_s, tmp, val);
1248  data = spi_OpCompositeInsert(spi, type_v, tmp, data, i);
1249  }
1250  break;
1251  }
1252  case SWS_OP_LINEAR: {
1253  if (sws->flags & SWS_BITEXACT)
1254  data = insert_bitexact_linear(op, spi, id, data, nb_linear_ops, nb_const_ids);
1255  else
1256  data = insert_vmat_linear(op, spi, id, data, nb_const_ids);
1257  nb_linear_ops++;
1258  nb_const_ids += 5*5 + 1;
1259  break;
1260  }
1261  case SWS_OP_UNPACK:
1262  if (ops->src.format == AV_PIX_FMT_X2BGR10)
1263  data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 2, 1, 0);
1264  else
1265  data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 0, 1, 2);
1266  break;
1267  case SWS_OP_PACK:
1268  if (ops->dst.format == AV_PIX_FMT_X2BGR10)
1269  data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 2, 1, 0);
1270  else
1271  data = spi_OpVectorShuffle(spi, type_v, data, data, 1, 2, 3, 0);
1272  break;
1273  default:
1274  return AVERROR(ENOTSUP);
1275  }
1276  }
1277 
1278  /* Return and finalize */
1279  spi_OpReturn(spi);
1280  spi_OpFunctionEnd(spi);
1281 
1282  int len = spi_end(spi);
1283  if (len < 0)
1284  return AVERROR_INVALIDDATA;
1285 
1286  return ff_vk_shader_link(&s->vkctx, shd, spvbuf, len, "main");
1287 }
1288 #endif
1289 
1290 static int compile(SwsContext *sws, const SwsOpList *ops, SwsCompiledOp *out)
1291 {
1292  int err;
1293  SwsInternal *c = sws_internal(sws);
1294  FFVulkanOpsCtx *s = c->hw_priv;
1295  if (!s)
1296  return AVERROR(ENOTSUP);
1297 
1298  VulkanPriv *p = av_mallocz(sizeof(*p));
1299  if (!p)
1300  return AVERROR(ENOMEM);
1301  p->s = av_refstruct_ref(c->hw_priv);
1302 
1303  err = ff_vk_exec_pool_init(&s->vkctx, s->qf, &p->e, 1,
1304  0, 0, 0, NULL);
1305  if (err < 0)
1306  goto fail;
1307 
1308  err = AVERROR(ENOTSUP);
1309 #if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
1310  err = add_ops_spirv(sws, p, s, ops, &p->shd);
1311 #endif
1312  if (err < 0)
1313  goto fail;
1314 
1315  err = ff_vk_shader_register_exec(&s->vkctx, &p->e, &p->shd);
1316  if (err < 0)
1317  goto fail;
1318 
1319  for (int i = 0; i < p->nb_data_bufs; i++)
1320  ff_vk_shader_update_desc_buffer(&s->vkctx, &p->e.contexts[0], &p->shd,
1321  1, i, 0, &p->data_bufs[i],
1322  0, VK_WHOLE_SIZE, VK_FORMAT_UNDEFINED);
1323 
1324  *out = (SwsCompiledOp) {
1325  .opaque = true,
1326  .func_opaque = process,
1327  .priv = p,
1328  .free = free_fn,
1329  };
1330 
1331  return 0;
1332 
1333 fail:
1334  free_fn(p);
1335  return err;
1336 }
1337 
1338 #if HAVE_SPIRV_HEADERS_SPIRV_H || HAVE_SPIRV_UNIFIED1_SPIRV_H
1339 static int compile_spirv(SwsContext *sws, const SwsOpList *ops,
1340  SwsCompiledOp *out)
1341 {
1342  return compile(sws, ops, out);
1343 }
1344 
1345 const SwsOpBackend backend_spirv = {
1346  .name = "spirv",
1347  .flags = SWS_BACKEND_SPIRV,
1348  .compile = compile_spirv,
1349  .hw_format = AV_PIX_FMT_VULKAN,
1350 };
1351 #endif
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
ff_vk_create_buf
int ff_vk_create_buf(FFVulkanContext *s, FFVkBuffer *buf, size_t size, void *pNext, void *alloc_pNext, VkBufferUsageFlags usage, VkMemoryPropertyFlagBits flags)
Definition: vulkan.c:1050
spi_OpExecutionMode
static void spi_OpExecutionMode(SPICtx *spi, int entry_point_id, SpvExecutionMode mode, int *s, int nb_s)
Definition: spvasm.h:405
ff_sws_rw_op_planes
int ff_sws_rw_op_planes(const SwsOp *op)
Return the number of planes involved in a read/write operation.
Definition: ops.c:132
VulkanPriv::e
FFVkExecPool e
Definition: ops.c:87
VulkanPriv::data_bufs
FFVkBuffer data_bufs[MAX_DATA_BUFS]
Definition: ops.c:89
FFVulkanOpsCtx
Copyright (C) 2026 Lynne.
Definition: ops.h:27
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:42
SwsPass
Represents a single filter pass in the scaling graph.
Definition: graph.h:75
spi_OpConstantInt
static int spi_OpConstantInt(SPICtx *spi, int type_id, int val)
Definition: spvasm.h:584
compile
static int compile(SwsContext *sws, const SwsOpList *ops, SwsCompiledOp *out)
Definition: ops.c:1290
VulkanPriv::src_rep
enum FFVkShaderRepFormat src_rep
Definition: ops.c:91
r
const char * r
Definition: vf_curves.c:127
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SwsFilterWeights::filter_size
int filter_size
The number of source texels to convolve over for each row.
Definition: filters.h:89
uid
UID uid
Definition: mxfenc.c:2488
spi_end
static int spi_end(SPICtx *spi)
Definition: spvasm.h:100
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:47
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:45
spi_OpVariable
static int spi_OpVariable(SPICtx *spi, int var_id, int ptr_type_id, SpvStorageClass storage_class, int initializer_id)
Definition: spvasm.h:537
SWS_RW_PLANAR
@ SWS_RW_PLANAR
Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, depending on the underlying interp...
Definition: ops.h:100
ff_vk_shader_free
void ff_vk_shader_free(FFVulkanContext *s, FFVulkanShader *shd)
Free a shader.
Definition: vulkan.c:2680
out
static FILE * out
Definition: movenc.c:55
create_filter_buf
static int create_filter_buf(FFVulkanOpsCtx *s, VulkanPriv *p, const SwsFilterWeights *wd, FFVkBuffer *buf)
Definition: ops.c:172
MAX_DITHER_BUFS
#define MAX_DITHER_BUFS
Definition: ops.c:81
SwsFormat::interlaced
int interlaced
Definition: format.h:79
spi_OpTypeFunction
static int spi_OpTypeFunction(SPICtx *spi, int return_type_id, const int *args, int nb_args)
Definition: spvasm.h:498
ff_sws_op_list_input
const SwsOp * ff_sws_op_list_input(const SwsOpList *ops)
Returns the input operation for a given op list, or NULL if there is none (e.g.
Definition: ops.c:662
AVBufferRef::data
uint8_t * data
The data buffer.
Definition: buffer.h:90
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:51
ff_vk_exec_pool_init
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:357
AVRefStructOpaque
RefStruct is an API for creating reference-counted objects with minimal overhead.
Definition: refstruct.h:58
SWS_BACKEND_SPIRV
@ SWS_BACKEND_SPIRV
Vulkan SPIR-V backend.
Definition: swscale.h:120
spi_OpConstantUInt
static int spi_OpConstantUInt(SPICtx *spi, int type_id, uint32_t val)
Definition: spvasm.h:565
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:472
spi_OpFunctionEnd
static void spi_OpFunctionEnd(SPICtx *spi)
Definition: spvasm.h:532
ff_vk_map_buffer
static int ff_vk_map_buffer(FFVulkanContext *s, FFVkBuffer *buf, uint8_t **mem, int invalidate)
Definition: vulkan.h:566
ff_sws_pixel_type_is_int
static av_const bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: uops.h:62
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:59
SwsFilterWeights
Represents a computed filter kernel.
Definition: filters.h:85
SWS_BITEXACT
@ SWS_BITEXACT
Definition: swscale.h:178
data
const char data[16]
Definition: mxf.c:149
spi_OpDecorate
#define spi_OpDecorate(spi, target, deco,...)
Definition: spvasm.h:356
SwsFilterWeights::offsets
int * offsets
The computed source pixel positions for each row of the filter.
Definition: filters.h:105
SwsContext::flags
unsigned flags
Bitmask of SWS_*.
Definition: swscale.h:240
filter
void(* filter)(uint8_t *src, int stride, int qscale)
Definition: h263dsp.c:29
VulkanPriv::s
FFVulkanOpsCtx * s
Definition: ops.c:86
ff_vk_init
int ff_vk_init(FFVulkanContext *s, void *log_parent, AVBufferRef *device_ref, AVBufferRef *frames_ref)
Initializes the AVClass, in case this context is not used as the main user's context.
Definition: vulkan.c:2716
ff_vk_exec_get
FFVkExecContext * ff_vk_exec_get(FFVulkanContext *s, FFVkExecPool *pool)
Retrieve an execution pool.
Definition: vulkan.c:568
ff_vk_uninit
void ff_vk_uninit(FFVulkanContext *s)
Frees main context.
Definition: vulkan.c:2704
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
spi_OpAccessChain
#define spi_OpAccessChain(spi, res_type, ptr_id,...)
Definition: spvasm.h:311
spi_OpCompositeExtract
#define spi_OpCompositeExtract(spi, res_type, src,...)
Definition: spvasm.h:319
ff_vk_exec_bind_shader
void ff_vk_exec_bind_shader(FFVulkanContext *s, FFVkExecContext *e, const FFVulkanShader *shd)
Bind a shader.
Definition: vulkan.c:2657
SwsOpBackend::name
const char * name
Definition: ops_dispatch.h:135
AV_PIX_FMT_VULKAN
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition: pixfmt.h:379
ff_vk_exec_add_dep_frame
int ff_vk_exec_add_dep_frame(FFVulkanContext *s, FFVkExecContext *e, AVFrame *f, VkPipelineStageFlagBits2 wait_stage, VkPipelineStageFlagBits2 signal_stage)
Definition: vulkan.c:800
FFVkShaderRepFormat
FFVkShaderRepFormat
Returns the format to use for images in shaders.
Definition: vulkan.h:410
SwsOpList::plane_dst
uint8_t plane_dst[4]
Definition: ops.h:273
create_bufs
static int create_bufs(FFVulkanOpsCtx *s, VulkanPriv *p, const SwsOpList *ops)
Definition: ops.c:242
SWS_COMP_TEST
#define SWS_COMP_TEST(mask, X)
Definition: uops.h:97
SwsOpList::num_ops
int num_ops
Definition: ops.h:267
SwsDitherOp
Definition: ops.h:178
ff_vk_shader_update_img_array
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:2608
ff_vk_frame_barrier
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:2093
SPICtx
Definition: spvasm.h:52
ff_vk_shader_register_exec
int ff_vk_shader_register_exec(FFVulkanContext *s, FFVkExecPool *pool, FFVulkanShader *shd)
Register a shader with an exec pool.
Definition: vulkan.c:2473
create_dither_buf
static int create_dither_buf(FFVulkanOpsCtx *s, VulkanPriv *p, const SwsDitherOp *dd, FFVkBuffer *buf)
Definition: ops.c:207
val
static double val(void *priv, double ch)
Definition: aeval.c:77
spi_OpTypeBool
static int spi_OpTypeBool(SPICtx *spi)
Definition: spvasm.h:430
spi_OpConstantComposite
#define spi_OpConstantComposite(spi, res_type, src,...)
Definition: spvasm.h:307
refstruct.h
spvasm.h
SWS_RW_PACKED
@ SWS_RW_PACKED
Definition: ops.h:101
FFVulkanDescriptorSetBinding::type
VkDescriptorType type
Definition: vulkan.h:80
SwsFrame
Represents a view into a single field of frame data.
Definition: format.h:224
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:55
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
spi_reserve
static int spi_reserve(SPICtx *spi, int len)
Definition: spvasm.h:108
SwsPass::priv
void * priv
Definition: graph.h:111
float
float
Definition: af_crystalizer.c:122
MAX_DATA_BUFS
#define MAX_DATA_BUFS
Definition: ops.c:83
ff_sws_vk_init
int ff_sws_vk_init(SwsContext *sws, AVBufferRef *dev_ref)
Definition: ops.c:41
planes
static const struct @604 planes[]
dither
static const uint16_t dither[8][8]
Definition: vf_gradfun.c:46
spi_init
static void spi_init(SPICtx *spi, uint8_t *spv_buf, int buf_len)
Definition: spvasm.h:86
spi_OpFunction
static void spi_OpFunction(SPICtx *spi, int fn_id, int result_type_id, SpvFunctionControlMask function_control, int function_type_id)
Definition: spvasm.h:509
ff_sws_pixel_expand
static AVRational64 ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
Definition: ops_internal.h:31
op
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition: anm.c:76
ops.h
spi_OpMemoryModel
static void spi_OpMemoryModel(SPICtx *spi, SpvAddressingModel addressing_model, SpvMemoryModel memory_model)
Definition: spvasm.h:125
ff_vk_exec_wait
void ff_vk_exec_wait(FFVulkanContext *s, FFVkExecContext *e)
Definition: vulkan.c:573
MAX_FILT_BUFS
#define MAX_FILT_BUFS
Definition: ops.c:82
spi_OpLabel
static int spi_OpLabel(SPICtx *spi, int label_id)
Definition: spvasm.h:520
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
FF_VK_REP_FLOAT
@ FF_VK_REP_FLOAT
Definition: vulkan.h:414
av_refstruct_alloc_ext
static void * av_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, void(*free_cb)(AVRefStructOpaque opaque, void *obj))
A wrapper around av_refstruct_alloc_ext_c() for the common case of a non-const qualified opaque.
Definition: refstruct.h:94
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:53
SPICtx::id
int id
Definition: spvasm.h:59
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:58
field
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this field
Definition: writing_filters.txt:78
ff_sws_op_list_output
const SwsOp * ff_sws_op_list_output(const SwsOpList *ops)
Returns the output operation for a given op list, or NULL if there is none.
Definition: ops.c:671
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:62
SPICtx::off
int off
Definition: spvasm.h:55
ff_vk_exec_pool_free
void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool)
Definition: vulkan.c:299
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
SwsOpBackend
Definition: ops_dispatch.h:134
spi_OpUndef
static int spi_OpUndef(SPICtx *spi, int type_id)
Definition: spvasm.h:415
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:46
spi_OpBranchConditional
static void spi_OpBranchConditional(SPICtx *spi, int cond_id, int true_label, int false_label, uint32_t branch_weights)
Definition: spvasm.h:642
fail
#define fail
Definition: test.h:478
NULL
#define NULL
Definition: coverity.c:32
spi_OpExtInst
#define spi_OpExtInst(spi, res_type, instr_id, set_id,...)
Definition: spvasm.h:348
SwsFilterWeights::dst_size
int dst_size
Definition: filters.h:111
spi_OpTypeImage
static int spi_OpTypeImage(SPICtx *spi, int sampled_type_id, SpvDim dim, int depth, int arrayed, int ms, int sampled, SpvImageFormat image_format)
Definition: spvasm.h:453
ff_vk_shader_link
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:2333
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:63
SwsOpType
SwsOpType
Copyright (C) 2025 Niklas Haas.
Definition: ops.h:35
SwsPass::graph
const SwsGraph * graph
Definition: graph.h:76
spi_OpEntryPoint
static int spi_OpEntryPoint(SPICtx *spi, SpvExecutionModel execution_model, const char *name, const int *args, int nb_args)
Definition: spvasm.h:372
SwsDitherOp::matrix
AVRational64 * matrix
Definition: ops.h:179
spi_OpTypeStruct
#define spi_OpTypeStruct(spi, id,...)
Definition: spvasm.h:352
SwsPixelType
SwsPixelType
Definition: uops.h:38
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
spi_OpSelectionMerge
static void spi_OpSelectionMerge(SPICtx *spi, int merge_block, SpvSelectionControlMask selection_control)
Definition: spvasm.h:634
AV_PIX_FMT_X2BGR10
#define AV_PIX_FMT_X2BGR10
Definition: pixfmt.h:620
f
f
Definition: af_crystalizer.c:122
ff_vk_shader_update_push_const
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:2647
FFVulkanDescriptorSetBinding
Definition: vulkan.h:78
SwsDitherOp::size_log2
int size_log2
Definition: ops.h:181
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
SwsOp::type
SwsPixelType type
Definition: ops.h:212
size
int size
Definition: twinvq_data.h:10344
AV_NUM_DATA_POINTERS
#define AV_NUM_DATA_POINTERS
Definition: frame.h:473
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:48
VulkanPriv::dst_rep
enum FFVkShaderRepFormat dst_rep
Definition: ops.c:92
SwsOpList::src
SwsFormat src
Definition: ops.h:270
FFVulkanShader
Definition: vulkan.h:191
AVRational64
64-bit Rational number (pair of numerator and denominator).
Definition: rational64.h:52
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
av_refstruct_ref
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
img
#define img
Definition: vf_colormatrix.c:114
spi_OpTypeArray
static int spi_OpTypeArray(SPICtx *spi, int element_type_id, int id, int length_id)
Definition: spvasm.h:470
FFVkExecContext
Definition: vulkan.h:111
spi_OpExtInstImport
static int spi_OpExtInstImport(SPICtx *spi, const char *name)
Definition: spvasm.h:397
ff_vk_shader_update_desc_buffer
int ff_vk_shader_update_desc_buffer(FFVulkanContext *s, FFVkExecContext *e, FFVulkanShader *shd, int set, int bind, int elem, FFVkBuffer *buf, VkDeviceSize offset, VkDeviceSize len, VkFormat fmt)
Update a descriptor in a buffer with a buffer.
Definition: vulkan.c:2621
av_refstruct_unref
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition: refstruct.c:120
interlaced
uint8_t interlaced
Definition: mxfenc.c:2336
SwsGraph::dst
SwsFormat dst
Definition: graph.h:145
SwsFormat::format
enum AVPixelFormat format
Definition: format.h:81
ff_vk_exec_start
int ff_vk_exec_start(FFVulkanContext *s, FFVkExecContext *e)
Start/submit/wait an execution.
Definition: vulkan.c:580
spi_get_id
static int spi_get_id(SPICtx *spi)
Definition: spvasm.h:133
FF_VK_REP_UINT
@ FF_VK_REP_UINT
Definition: vulkan.h:418
process
static void process(const SwsFrame *dst, const SwsFrame *src, int y, int h, const SwsPass *pass)
Definition: ops.c:96
SwsOpList::ops
SwsOp * ops
Definition: ops.h:266
VulkanPriv
Definition: ops.c:85
s
uint8_t s
Definition: llvidencdsp.c:39
ff_vk_unmap_buffer
static int ff_vk_unmap_buffer(FFVulkanContext *s, FFVkBuffer *buf, int flush)
Definition: vulkan.h:573
spi_OpCompositeConstruct
#define spi_OpCompositeConstruct(spi, res_type, src,...)
Definition: spvasm.h:315
spi_OpImageWrite
static void spi_OpImageWrite(SPICtx *spi, int img_id, int pos_id, int src_id, SpvImageOperandsMask image_operands)
Definition: spvasm.h:669
len
int len
Definition: vorbis_enc_data.h:426
filt
static const int8_t filt[NUMTAPS *2]
Definition: af_earwax.c:40
SwsOp
Definition: ops.h:210
spi_OpVectorShuffle
#define spi_OpVectorShuffle(spi, res_type, src1, src2,...)
Definition: spvasm.h:364
ff_vk_free_buf
void ff_vk_free_buf(FFVulkanContext *s, FFVkBuffer *buf)
Definition: vulkan.c:1264
AVRational64::den
int64_t den
Denominator.
Definition: rational64.h:54
ff_vk_shader_add_descriptor_set
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:2439
SwsInternal
Definition: swscale_internal.h:337
ff_vk_create_imageviews
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:2010
spi_OpTypeVoid
static int spi_OpTypeVoid(SPICtx *spi)
Definition: spvasm.h:423
SwsOpList::dst
SwsFormat dst
Definition: ops.h:270
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:54
SwsCompiledOp
Definition: ops_dispatch.h:101
FFVkExecPool
Definition: vulkan.h:253
VulkanPriv::interlaced
int interlaced
Definition: ops.c:93
ff_vk_shader_add_push_const
int ff_vk_shader_add_push_const(FFVulkanShader *shd, int offset, int size, VkShaderStageFlagBits stage)
Add/update push constants for execution.
Definition: vulkan.c:1509
SWS_RW_PALETTE
@ SWS_RW_PALETTE
Definition: ops.h:102
ff_vk_qf_find
AVVulkanDeviceQueueFamily * ff_vk_qf_find(FFVulkanContext *s, VkQueueFlagBits dev_family, VkVideoCodecOperationFlagBitsKHR vid_ops)
Chooses an appropriate QF.
Definition: vulkan.c:286
id
enum AVCodecID id
Definition: dts2pts.c:607
SwsFormat::field
int field
Definition: format.h:80
spi_OpReturn
static void spi_OpReturn(SPICtx *spi)
Definition: spvasm.h:527
spi_OpImageRead
static int spi_OpImageRead(SPICtx *spi, int result_type_id, int img_id, int pos_id, SpvImageOperandsMask image_operands)
Definition: spvasm.h:656
FFVulkanShader::precompiled
int precompiled
Definition: vulkan.h:196
spi_OpMemberDecorate
#define spi_OpMemberDecorate(spi, type, target, deco,...)
Definition: spvasm.h:360
spi_OpCapability
static void spi_OpCapability(SPICtx *spi, SpvCapability capability)
Definition: spvasm.h:119
FFVulkanShader::lg_size
uint32_t lg_size[3]
Definition: vulkan.h:200
px
#define px
Definition: uops_tmpl.c:54
spi_OpConstantFloat
static int spi_OpConstantFloat(SPICtx *spi, int type_id, float val)
Definition: spvasm.h:596
SwsFilterWeights::num_weights
size_t num_weights
Definition: filters.h:98
VulkanPriv::shd
FFVulkanShader shd
Definition: ops.c:88
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
free_fn
static void free_fn(void *priv)
Definition: ops.c:161
mem.h
spi_OpLoad
static int spi_OpLoad(SPICtx *spi, int result_type_id, int ptr_id, SpvMemoryAccessMask memory_access, int align)
Definition: spvasm.h:608
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
VulkanPriv::nb_data_bufs
int nb_data_bufs
Definition: ops.c:90
spi_OpTypePointer
static int spi_OpTypePointer(SPICtx *spi, SpvStorageClass storage_class, int type_id)
Definition: spvasm.h:488
spi_OpCompositeInsert
#define spi_OpCompositeInsert(spi, res_type, src1, src2,...)
Definition: spvasm.h:368
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: uops.h:43
w
uint8_t w
Definition: llvidencdsp.c:39
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
FFALIGN
#define FFALIGN(x, a)
Definition: macros.h:78
AVRational64::num
int64_t num
Numerator.
Definition: rational64.h:53
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:52
FFVkBuffer
Definition: vulkan.h:91
ff_sws_vk_uninit
static void ff_sws_vk_uninit(AVRefStructOpaque opaque, void *obj)
Copyright (C) 2026 Lynne.
Definition: ops.c:34
int32_t
int32_t
Definition: audioconvert.c:56
ff_vk_exec_submit
int ff_vk_exec_submit(FFVulkanContext *s, FFVkExecContext *e)
Definition: vulkan.c:925
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
sws_internal
static SwsInternal * sws_internal(const SwsContext *sws)
Definition: swscale_internal.h:79
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:61
h
h
Definition: vp9dsp_template.c:2070
SwsOpList::plane_src
uint8_t plane_src[4]
Definition: ops.h:273
ff_sws_vk_device_ref
AVBufferRef * ff_sws_vk_device_ref(SwsContext *sws)
Returns the Vulkan device reference associated with sws, or NULL if Vulkan has not been initialized f...
Definition: ops.c:74
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
SwsContext
Main external API structure.
Definition: swscale.h:227
SwsFilterWeights::weights
int * weights
The computed look-up table (LUT).
Definition: filters.h:97
FFVulkanFunctions
Definition: vulkan_functions.h:275
SWS_FILTER_SCALE
@ SWS_FILTER_SCALE
14-bit coefficients are picked to fit comfortably within int16_t for efficient SIMD processing (e....
Definition: filters.h:40
ff_vk_shader_load
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:2136
src
#define src
Definition: vp8dsp.c:248