PR #22609 opened by Lynne
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22609
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22609.patch

swscale gets runtime-defined assembly once again!

This commit splits the Vulkan backend into two, SPIR-V and GLSL,
enabling falling back onto the GLSL implementation if an instruction
is unavailable, or simply for testing.

Sponsored-by: Sovereign Tech Fund


>From e0e2647260a70a1ad64afe705352092fed72e8a7 Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Tue, 24 Mar 2026 23:14:52 +0100
Subject: [PATCH] swscale/vulkan: add a native SPIR-V assembler backend

swscale gets runtime-defined assembly once again!

This commit splits the Vulkan backend into two, SPIR-V and GLSL,
enabling falling back onto the GLSL implementation if an instruction
is unavailable, or simply for testing.

Sponsored-by: Sovereign Tech Fund
---
 libswscale/ops.c           |  10 +-
 libswscale/vulkan/ops.c    | 373 +++++++++++++++++++++++-
 libswscale/vulkan/spvasm.h | 572 +++++++++++++++++++++++++++++++++++++
 3 files changed, 946 insertions(+), 9 deletions(-)
 create mode 100644 libswscale/vulkan/spvasm.h

diff --git a/libswscale/ops.c b/libswscale/ops.c
index ece49e299f..90e6f6080f 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -31,7 +31,10 @@
 extern const SwsOpBackend backend_c;
 extern const SwsOpBackend backend_murder;
 extern const SwsOpBackend backend_x86;
-extern const SwsOpBackend backend_vulkan;
+extern const SwsOpBackend backend_spirv;
+#if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
+extern const SwsOpBackend backend_glsl;
+#endif
 
 const SwsOpBackend * const ff_sws_op_backends[] = {
     &backend_murder,
@@ -40,7 +43,10 @@ const SwsOpBackend * const ff_sws_op_backends[] = {
 #endif
     &backend_c,
 #if CONFIG_VULKAN
-    &backend_vulkan,
+    &backend_spirv,
+#endif
+#if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
+    &backend_glsl,
 #endif
     NULL
 };
diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 7a6c53a5a5..8e4e60cdc1 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -25,6 +25,7 @@
 #include "../swscale_internal.h"
 
 #include "ops.h"
+#include "spvasm.h"
 
 static void ff_sws_vk_uninit(AVRefStructOpaque opaque, void *obj)
 {
@@ -157,6 +158,342 @@ static void free_fn(void *priv)
     av_free(priv);
 }
 
+#if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
+#include <spirv-tools/libspirv.h>
+
+static int spirv_validate_and_disassemble(const uint8_t *spirv, size_t 
spirv_len)
+{
+    if (spirv == NULL || spirv_len == 0 || spirv_len % 4 != 0) {
+        av_log(NULL, AV_LOG_ERROR, "Invalid SPIR-V length\n");
+        return AVERROR(EINVAL);
+    }
+
+    spv_context ctx = spvContextCreate(SPV_ENV_VULKAN_1_3);
+    if (!ctx) {
+        av_log(NULL, AV_LOG_ERROR, "Failed to create SPIRV-Tools context\n");
+        return AVERROR_EXTERNAL;
+    }
+
+    spv_diagnostic diag = NULL;
+    spv_result_t res;
+
+    /* validate */
+    res = spvValidateBinary(ctx, (const uint32_t*)spirv, spirv_len / 4, &diag);
+    if (res != SPV_SUCCESS) {
+        av_log(NULL, AV_LOG_ERROR, "SPIR-V validation failed:\n");
+        if (diag) {
+            spvDiagnosticPrint(diag);
+            spvDiagnosticDestroy(diag);
+        }
+        spvContextDestroy(ctx);
+        return AVERROR_INVALIDDATA;
+    } else {
+        spvDiagnosticDestroy(diag);
+        spv_text disasm;
+        res = spvBinaryToText(ctx, (const uint32_t*)spirv, spirv_len / 4,
+                              SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
+                              SPV_BINARY_TO_TEXT_OPTION_INDENT |
+                              SPV_BINARY_TO_TEXT_OPTION_COLOR,
+                              &disasm, NULL);
+        if (res == SPV_SUCCESS && disasm) {
+            av_log(NULL, AV_LOG_ERROR, "SPIR-V diassembly:\n");
+            printf("%s\n", disasm->str);
+            spvTextDestroy(disasm);
+        } else {
+            av_log(NULL, AV_LOG_ERROR, "SPIR-V disassembly failed\n");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    spvContextDestroy(ctx);
+    return 0;
+}
+#endif
+
+static int add_ops_spirv(VulkanPriv *p, FFVulkanOpsCtx *s,
+                         SwsOpList *ops, FFVulkanShader *shd)
+{
+    /* Interlaced formats are not currently supported */
+    if (ops->src.interlaced || ops->dst.interlaced)
+        return AVERROR(ENOTSUP);
+
+    ff_vk_shader_load(shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL,
+                      (int []) { 32, 32, 1 }, 0);
+
+    uint8_t spvbuf[1024*32];
+    SPICtx spi;
+    spi_init(&spi, spvbuf, sizeof(spvbuf));
+
+    /* Declare required capabilities */
+    spi_OpCapability(&spi, SpvCapabilityShader);
+    spi_OpCapability(&spi, SpvCapabilityInt16);
+    spi_OpCapability(&spi, SpvCapabilityInt8);
+    spi_OpCapability(&spi, SpvCapabilityImageQuery);
+    spi_OpCapability(&spi, SpvCapabilityStorageImageReadWithoutFormat);
+    spi_OpCapability(&spi, SpvCapabilityStorageImageWriteWithoutFormat);
+    spi_OpMemoryModel(&spi, SpvAddressingModelLogical, SpvMemoryModelGLSL450);
+
+    /* Inputs: gl_GlobalInvocationID, output images, input images */
+    int in_vars[] = { spi_get_id(&spi), spi_get_id(&spi), spi_get_id(&spi) };
+
+    int ep = spi_OpEntryPoint(&spi, SpvExecutionModelGLCompute, "main",
+                              in_vars, FF_ARRAY_ELEMS(in_vars));
+    spi_OpExecutionMode(&spi, ep, SpvExecutionModeLocalSize,
+                        shd->lg_size, 3);
+
+    /* gl_GlobalInvocationID */
+    spi_OpDecorate(&spi, in_vars[0], SpvDecorationBuiltIn,
+                   (int []) { SpvBuiltInGlobalInvocationId }, 1);
+
+    /* Image ops, to determine types */
+    const SwsOp *op_r = ff_sws_op_list_input(ops);
+    const SwsOp *op_w = ff_sws_op_list_output(ops);
+    int out_img_count = op_w->rw.packed ? 1 : op_w->rw.elems;
+    int in_img_count = op_r ? op_r->rw.packed ? 1 : op_r->rw.elems : 0;
+
+    /* Output image */
+    spi_OpDecorate(&spi, in_vars[1], SpvDecorationNonReadable, NULL, 0);
+    spi_OpDecorate(&spi, in_vars[1], SpvDecorationDescriptorSet,
+                   (int []) { 0 }, 1);
+    spi_OpDecorate(&spi, in_vars[1], SpvDecorationBinding,
+                   (int []) { 1 }, 1);
+
+    const FFVulkanDescriptorSetBinding desc_set[2] = {
+        {
+            .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems = out_img_count,
+        },
+        {
+            .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+            .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+            .elems = in_img_count,
+        },
+    };
+    ff_vk_shader_add_descriptor_set(&s->vkctx, shd, desc_set, 1 + !!op_r, 0, 
0);
+
+    if (op_r) {
+        /* Input image */
+        spi_OpDecorate(&spi, in_vars[2], SpvDecorationNonWritable, NULL, 0);
+        spi_OpDecorate(&spi, in_vars[2], SpvDecorationDescriptorSet,
+                       (int []) { 0 }, 1);
+        spi_OpDecorate(&spi, in_vars[2], SpvDecorationBinding,
+                       (int []) { 0 }, 1);
+    }
+
+    /* Scalar types */
+    int void_type    = spi_OpTypeVoid(&spi);
+    int b_type       = spi_OpTypeBool(&spi);
+    int u32_type     = spi_OpTypeInt(&spi, 32, 0);
+    int i32_type     = spi_OpTypeInt(&spi, 32, 1);
+    int f32_type     = spi_OpTypeFloat(&spi, 32);
+    int void_fn_type = spi_OpTypeFunction(&spi, void_type, NULL, NULL, 0);
+
+    /* Vector types */
+    int bvec2_type   = spi_OpTypeVector(&spi, b_type,   2);
+    int u32vec2_type = spi_OpTypeVector(&spi, u32_type, 2);
+    int i32vec2_type = spi_OpTypeVector(&spi, i32_type, 2);
+
+    int u32vec3_type = spi_OpTypeVector(&spi, u32_type, 3);
+
+    int u32vec4_type = spi_OpTypeVector(&spi, u32_type, 4);
+    int f32vec4_type = spi_OpTypeVector(&spi, f32_type, 4);
+
+    /* Constants */
+    int u32_p = spi_OpUndef(&spi, u32_type);
+    int f32_p = spi_OpUndef(&spi, f32_type);
+    int u32_0 = spi_OpConstantUInt(&spi, u32_type, 0);
+    int u32_1 = spi_OpConstantUInt(&spi, u32_type, 1);
+    int u32_2 = spi_OpConstantUInt(&spi, u32_type, 2);
+    int u32_3 = spi_OpConstantUInt(&spi, u32_type, 3);
+    int u32_cid[4] = { u32_0, u32_1, u32_2, u32_3 };
+
+    /* Conversion scaling constants */
+    int nb_scc = 0;
+    int conv_scale_id[16];
+
+    for (int n = 0; n < ops->num_ops; n++) {
+        const SwsOp *op = &ops->ops[n];
+        switch (op->op) {
+        case SWS_OP_CONVERT:
+            if (ff_sws_pixel_type_is_int(op->convert.to) && 
op->convert.expand) {
+                int m = ff_sws_pixel_expand(op->type, op->convert.to).num;
+                int tmp = spi_OpConstantUInt(&spi, u32_type, m);
+                tmp = spi_OpConstantComposite(&spi, u32vec4_type,
+                                              (int []) { tmp, tmp, tmp, tmp }, 
4);
+                conv_scale_id[nb_scc++] = tmp;
+            }
+            break;
+        }
+    }
+
+    nb_scc = 0;
+
+    int out_img_type;
+    out_img_type = spi_OpTypeImage(&spi,
+                                   op_w->type == SWS_PIXEL_F32 ? f32_type :
+                                   u32_type,
+                                   2, 0, 0, 0, 2, SpvImageFormatUnknown);
+    int in_img_type = 0;
+    if (op_r) {
+        in_img_type = ((op_w->type == SWS_PIXEL_F32) ==
+                       (op_r->type == SWS_PIXEL_F32)) ? out_img_type :
+                      spi_OpTypeImage(&spi,
+                                      op_r->type == SWS_PIXEL_F32 ? f32_type :
+                                      u32_type,
+                                      2, 0, 0, 0, 2, SpvImageFormatUnknown);
+    }
+
+    /* Image types for descriptors */
+    int out_img_array_id = spi_OpTypeArray(&spi, out_img_type, 
u32_cid[out_img_count]);
+
+    int in_img_array_id = 0;
+    if (op_r)
+        in_img_array_id = spi_OpTypeArray(&spi, in_img_type, 
u32_cid[in_img_count]);
+
+    /* Pointer types for images */
+    int u32vec3_tptr = spi_OpTypePointer(&spi, SpvStorageClassInput, 
u32vec3_type);
+    int out_img_tptr = spi_OpTypePointer(&spi, SpvStorageClassUniformConstant,
+                                        out_img_array_id);
+    int out_img_sptr = spi_OpTypePointer(&spi, SpvStorageClassUniformConstant,
+                                         out_img_type);
+
+    int in_img_tptr = 0, in_img_sptr = 0;
+    if (op_r) {
+        in_img_tptr  = spi_OpTypePointer(&spi, SpvStorageClassUniformConstant,
+                                         in_img_array_id);
+        in_img_sptr  = spi_OpTypePointer(&spi, SpvStorageClassUniformConstant,
+                                         in_img_type);
+    }
+
+    /* Define inputs */
+    spi_OpVariable(&spi, in_vars[0], u32vec3_tptr, SpvStorageClassInput, 0);
+    spi_OpVariable(&spi, in_vars[1], out_img_tptr, 
SpvStorageClassUniformConstant, 0);
+    if (op_r)
+        spi_OpVariable(&spi, in_vars[2], in_img_tptr, 
SpvStorageClassUniformConstant, 0);
+
+    /* Main function */
+    spi_OpFunction(&spi, ep, void_type, 0, void_fn_type);
+    spi_OpLabel(&spi, spi_get_id(&spi));
+
+    /* Load output image handles */
+    int out_img[4];
+    for (int i = 0; i < out_img_count; i++) {
+        int img = spi_OpAccessChain(&spi, out_img_sptr, in_vars[1],
+                                    (int []) { u32_cid[i] }, 1);
+        out_img[i] = spi_OpLoad(&spi, out_img_type, img,
+                                SpvMemoryAccessMaskNone, 0);
+    }
+
+    /* Load input image handles */
+    int in_img[4] = { 0 };
+    for (int i = 0; i < in_img_count; i++) {
+        /* Deref array and then the pointer */
+        int img = spi_OpAccessChain(&spi, in_img_sptr, in_vars[2],
+                                    (int []) { u32_cid[i] }, 1);
+        in_img[i] = spi_OpLoad(&spi, in_img_type, img,
+                               SpvMemoryAccessMaskNone, 0);
+    }
+
+    /* Check if gid is within the image size */
+    int gid = spi_OpLoad(&spi, u32vec3_type, in_vars[0], 
SpvMemoryAccessMaskNone, 0);
+    int gi2 = spi_OpVectorShuffle(&spi, u32vec2_type, gid, gid, (int []) { 0, 
1 }, 2);
+    gi2 = spi_OpBitcast(&spi, i32vec2_type, gi2);
+
+    int img1_s = spi_OpImageQuerySize(&spi, i32vec2_type, out_img[0]);
+    int scmp = spi_OpSGreaterThanEqual(&spi, bvec2_type, gi2, img1_s);
+    scmp = spi_OpAny(&spi, b_type, scmp);
+
+    int quit_label = spi_get_id(&spi), merge_label = spi_get_id(&spi);
+    spi_OpSelectionMerge(&spi, merge_label, SpvSelectionControlMaskNone);
+    spi_OpBranchConditional(&spi, scmp, quit_label, merge_label, 0);
+
+    spi_OpLabel(&spi, quit_label);
+    spi_OpReturn(&spi); /* Quit if out of bounds to avoid wasting time */
+    spi_OpLabel(&spi, merge_label);
+
+    int data;
+    for (int n = 0; n < ops->num_ops; n++) {
+        const SwsOp *op = &ops->ops[n];
+        SwsPixelType cur_type = op->op == SWS_OP_CONVERT ? op->convert.to :
+                                op->type;
+        int type_v = cur_type == SWS_PIXEL_F32 ? f32vec4_type : u32vec4_type;
+        int type_s = cur_type == SWS_PIXEL_F32 ? f32_type : u32_type;
+
+        switch (op->op) {
+        case SWS_OP_READ:
+            if (op->rw.frac) {
+                return AVERROR(ENOTSUP);
+            } else if (op->rw.packed) {
+                data = spi_OpImageRead(&spi, type_v, in_img[0], gid,
+                                       SpvImageOperandsMaskNone);
+                data = spi_OpVectorShuffle(&spi, type_v, data, data,
+                                           (int []) { ops->order_src.in[0],
+                                                      ops->order_src.in[1],
+                                                      ops->order_src.in[2],
+                                                      ops->order_src.in[3] }, 
4);
+            } else {
+                int zid = cur_type == SWS_PIXEL_F32 ? f32_p : u32_p;
+                int tmp[4] = { zid, zid, zid, zid };
+                for (int i = 0; i < op->rw.elems; i++) {
+                    tmp[i] = spi_OpImageRead(&spi, type_v,
+                                             in_img[ops->order_src.in[i]], gid,
+                                             SpvImageOperandsMaskNone);
+                    tmp[i] = spi_OpCompositeExtract(&spi, type_s, tmp[i],
+                                                    (int []) { 0 }, 1);
+                }
+                data = spi_OpCompositeConstruct(&spi, type_v, tmp, 4);
+            }
+            break;
+        case SWS_OP_WRITE:
+            if (op->rw.frac) {
+                return AVERROR(ENOTSUP);
+            } else if (op->rw.packed) {
+                spi_OpVectorShuffle(&spi, type_v, data, data,
+                                    (int []) { ops->order_dst.in[0],
+                                               ops->order_dst.in[1],
+                                               ops->order_dst.in[2],
+                                               ops->order_dst.in[3] }, 4);
+                spi_OpImageWrite(&spi, in_img[0], gid, data,
+                                 SpvImageOperandsMaskNone);
+            } else {
+                for (int i = 0; i < op->rw.elems; i++) {
+                    int tmp = spi_OpCompositeExtract(&spi, type_s, data,
+                                                     &i, 1);
+                    int zid = cur_type == SWS_PIXEL_F32 ? f32_p : u32_p;
+                    int vtmp[4] = { tmp, zid, zid, zid };
+                    spi_OpCompositeConstruct(&spi, type_v, vtmp, 4);
+                    spi_OpImageWrite(&spi, in_img[ops->order_dst.in[i]], gid, 
data,
+                                     SpvImageOperandsMaskNone);
+                }
+            }
+            break;
+        case SWS_OP_CONVERT:
+            if (ff_sws_pixel_type_is_int(cur_type) && op->convert.expand)
+                data = spi_OpIMul(&spi, type_v, data, conv_scale_id[nb_scc++]);
+            else if (op->type == SWS_PIXEL_F32 && type_s == u32_type)
+                data = spi_OpConvertFToU(&spi, type_v, data);
+            else if (op->type != SWS_PIXEL_F32 && type_s == f32_type)
+                data = spi_OpConvertUToF(&spi, type_v, data);
+            break;
+        default:
+            return AVERROR(ENOTSUP);
+        }
+    }
+
+    spi_OpReturn(&spi);
+    spi_OpFunctionEnd(&spi);
+
+    spi_end(&spi);
+
+#if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
+    spirv_validate_and_disassemble(spvbuf, bytestream2_tell_p(&spi.pb));
+#endif
+
+    return ff_vk_shader_link(&s->vkctx, shd, spvbuf, 
bytestream2_tell_p(&spi.pb),
+                             "main");
+}
+
 #if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
 static void add_desc_read_write(FFVulkanDescriptorSetBinding *out_desc,
                                 enum FFVkShaderRepFormat *out_rep,
@@ -385,7 +722,7 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
 }
 #endif
 
-static int compile(SwsContext *sws, SwsOpList *ops, SwsCompiledOp *out)
+static int compile(SwsContext *sws, SwsOpList *ops, SwsCompiledOp *out, int 
glsl)
 {
     int err;
     SwsInternal *c = sws_internal(sws);
@@ -397,15 +734,19 @@ static int compile(SwsContext *sws, SwsOpList *ops, 
SwsCompiledOp *out)
         .s = s,
     };
 
+    if (glsl) {
 #if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
-    {
         err = add_ops_glsl(&p, s, ops, &p.shd);
         if (err < 0)
             return err;
-    }
 #else
-    return AVERROR(ENOTSUP);
+        return AVERROR(ENOTSUP);
 #endif
+    } else {
+        err = add_ops_spirv(&p, s, ops, &p.shd);
+        if (err < 0)
+            return err;
+    }
 
     err = ff_vk_shader_register_exec(&s->vkctx, &s->e, &p.shd);
     if (err < 0)
@@ -420,8 +761,26 @@ static int compile(SwsContext *sws, SwsOpList *ops, 
SwsCompiledOp *out)
     return 0;
 }
 
-const SwsOpBackend backend_vulkan = {
-    .name      = "vulkan",
-    .compile   = compile,
+static int compile_spirv(SwsContext *sws, SwsOpList *ops, SwsCompiledOp *out)
+{
+    return compile(sws, ops, out, 0);
+}
+
+const SwsOpBackend backend_spirv = {
+    .name      = "spirv",
+    .compile   = compile_spirv,
     .hw_format = AV_PIX_FMT_VULKAN,
 };
+
+#if CONFIG_LIBSHADERC || CONFIG_LIBGLSLANG
+static int compile_glsl(SwsContext *sws, SwsOpList *ops, SwsCompiledOp *out)
+{
+    return compile(sws, ops, out, 1);
+}
+
+const SwsOpBackend backend_glsl = {
+    .name      = "vulkan",
+    .compile   = compile_glsl,
+    .hw_format = AV_PIX_FMT_VULKAN,
+};
+#endif
diff --git a/libswscale/vulkan/spvasm.h b/libswscale/vulkan/spvasm.h
new file mode 100644
index 0000000000..4234e4723c
--- /dev/null
+++ b/libswscale/vulkan/spvasm.h
@@ -0,0 +1,572 @@
+/**
+ * Copyright (C) 2026 Lynne
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWSCALE_VULKAN_SPVASM_H
+#define SWSCALE_VULKAN_SPVASM_H
+
+#include <stdint.h>
+#include "libavcodec/bytestream.h"
+#include <spirv/unified1/spirv.h>
+#include <spirv/unified1/GLSL.std.450.h>
+
+typedef struct SPICtx {
+    PutByteContext pb;
+    int bool_type_id;
+    int id;
+} SPICtx;
+
+static inline void spi_init(SPICtx *spi, uint8_t *spv_buf, int buf_len)
+{
+    spi->id = 1;
+    bytestream2_init_writer(&spi->pb, spv_buf, buf_len);
+    bytestream2_put_le32(&spi->pb, SpvMagicNumber);
+    bytestream2_put_le32(&spi->pb, (1 << 16) | (6 << 8)); /* version */
+    bytestream2_put_le32(&spi->pb, 0); /* generator */
+    bytestream2_put_le32(&spi->pb, 0); /* last bound ID + 1, rewritten */
+    bytestream2_put_le32(&spi->pb, 0); /* schema */
+}
+
+static inline void spi_end(SPICtx *spi)
+{
+    AV_WL32(spi->pb.buffer_start + 3*4, spi->id);
+}
+
+static inline void spi_OpCapability(SPICtx *spi, SpvCapability capability)
+{
+    bytestream2_put_le32(&spi->pb, (2 << 16) | 17);
+    bytestream2_put_le32(&spi->pb, capability);
+}
+
+static inline void spi_OpMemoryModel(SPICtx *spi, SpvAddressingModel 
addressing_model,
+                                     SpvMemoryModel memory_model)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 14);
+    bytestream2_put_le32(&spi->pb, addressing_model);
+    bytestream2_put_le32(&spi->pb, memory_model);
+}
+
+static int spi_get_id(SPICtx *spi)
+{
+    return spi->id++;
+}
+
+static int spi_strl(const char *str)
+{
+    return FFALIGN(strlen(str) + 1, 4) >> 2;
+}
+
+static void spi_put_str(SPICtx *spi, const char *str)
+{
+    bytestream2_put_buffer(&spi->pb, str, strlen(str) + 1);
+    int padding = (4 - (bytestream2_tell_p(&spi->pb) & 3)) & 3;
+    for (int i = 0; i < padding; i++)
+        bytestream2_put_byte(&spi->pb, 0);
+}
+
+static inline int spi_OpEntryPoint(SPICtx *spi, SpvExecutionModel 
execution_model,
+                                   const char *name, int *ids, int nb_ids)
+{
+    bytestream2_put_le32(&spi->pb, ((3 + nb_ids + spi_strl(name)) << 16) | 15);
+    bytestream2_put_le32(&spi->pb, execution_model);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    spi_put_str(spi, name);
+    for (int i = 0; i < nb_ids; i++)
+        bytestream2_put_le32(&spi->pb, ids[i]);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpExtInstImport(SPICtx *spi, const char *name)
+{
+    bytestream2_put_le32(&spi->pb, (2 + spi_strl(name) << 16) | 11);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    spi_put_str(spi, name);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpExecutionMode(SPICtx *spi, int entry_point_id,
+                                       SpvExecutionMode mode, int *s, int nb_s)
+{
+    bytestream2_put_le32(&spi->pb, (3 + nb_s << 16) | 16);
+    bytestream2_put_le32(&spi->pb, entry_point_id);
+    bytestream2_put_le32(&spi->pb, mode);
+    for (int i = 0; i < nb_s; i++)
+        bytestream2_put_le32(&spi->pb, s[i]);
+}
+
+static inline void spi_OpDecorate(SPICtx *spi, int target_id,
+                                  SpvDecoration decoration,
+                                  const int *op, int nb_op)
+{
+    bytestream2_put_le32(&spi->pb, (3 + nb_op << 16) | 71);
+    bytestream2_put_le32(&spi->pb, target_id);
+    bytestream2_put_le32(&spi->pb, decoration);
+    for (int i = 0; i < nb_op; i++)
+        bytestream2_put_le32(&spi->pb, op[i]);
+}
+
+static inline int spi_OpUndef(SPICtx *spi, int type_id)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 1);
+    bytestream2_put_le32(&spi->pb, type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeVoid(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (2 << 16) | 19);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeBool(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (2 << 16) | 20);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    spi->bool_type_id = spi->id;
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeInt(SPICtx *spi, int width, int signedness)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 21);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, width);
+    bytestream2_put_le32(&spi->pb, signedness);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeFloat(SPICtx *spi, int width)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 22);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, width);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeFloatEnc(SPICtx *spi, int width,
+                                     SpvFPEncoding floating_point_encoding)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 22);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, width);
+    bytestream2_put_le32(&spi->pb, floating_point_encoding);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeVector(SPICtx *spi, int comp_type_id, int 
comp_count)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 23);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, comp_type_id);
+    bytestream2_put_le32(&spi->pb, comp_count);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeMatrix(SPICtx *spi, int col_type_id, int col_count)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 24);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, col_type_id);
+    bytestream2_put_le32(&spi->pb, col_count);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeImage(SPICtx *spi, int sampled_type_id, SpvDim dim,
+                                  int depth, int arrayed, int ms, int sampled,
+                                  SpvImageFormat image_format)
+{
+    bytestream2_put_le32(&spi->pb, (9 << 16) | 25);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, sampled_type_id);
+    bytestream2_put_le32(&spi->pb, dim - 1);
+    bytestream2_put_le32(&spi->pb, depth);
+    bytestream2_put_le32(&spi->pb, arrayed);
+    bytestream2_put_le32(&spi->pb, ms);
+    bytestream2_put_le32(&spi->pb, sampled);
+    bytestream2_put_le32(&spi->pb, image_format);
+    /* TODO: if implementing kernel mode, write an access qualifier here */
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeArray(SPICtx *spi, int element_type_id, int 
length_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 28);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, element_type_id);
+    bytestream2_put_le32(&spi->pb, length_id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeRuntimeArray(SPICtx *spi, int element_type_id)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 29);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, element_type_id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypePointer(SPICtx *spi, SpvStorageClass storage_class,
+                                    int type_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 32);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, storage_class);
+    bytestream2_put_le32(&spi->pb, type_id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpTypeFunction(SPICtx *spi, int return_type_id,
+                                     const int *arg_id, const int *arg_type_id,
+                                     int nb_args)
+{
+    bytestream2_put_le32(&spi->pb, (3 + nb_args << 16) | 33);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, return_type_id);
+    for (int i = 0; i < nb_args; i++)
+        bytestream2_put_le32(&spi->pb, arg_id[i]);
+    for (int i = 0; i < nb_args; i++)
+        bytestream2_put_le32(&spi->pb, arg_type_id[i]);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpFunction(SPICtx *spi, int fn_id, int result_type_id,
+                                  SpvFunctionControlMask function_control,
+                                  int function_type_id)
+{
+    bytestream2_put_le32(&spi->pb, (5 << 16) | 54);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, fn_id);
+    bytestream2_put_le32(&spi->pb, function_control);
+    bytestream2_put_le32(&spi->pb, function_type_id);
+}
+
+static inline int spi_OpLabel(SPICtx *spi, int label_id)
+{
+    bytestream2_put_le32(&spi->pb, (2 << 16) | 248);
+    bytestream2_put_le32(&spi->pb, label_id);
+    return label_id;
+}
+
+static inline void spi_OpReturn(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (1 << 16) | 253);
+}
+
+static inline int spi_OpReturnValue(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (2 << 16) | 254);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpFunctionEnd(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (1 << 16) | 56);
+}
+
+static inline int spi_OpVariable(SPICtx *spi, int var_id, int ptr_type_id,
+                                 SpvStorageClass storage_class, int 
initializer_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 + !!initializer_id << 16) | 59);
+    bytestream2_put_le32(&spi->pb, ptr_type_id);
+    bytestream2_put_le32(&spi->pb, var_id);
+    bytestream2_put_le32(&spi->pb, storage_class);
+    if (initializer_id)
+        bytestream2_put_le32(&spi->pb, initializer_id);
+    return var_id;
+}
+
+static inline int spi_OpConstantTrue(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 41);
+    bytestream2_put_le32(&spi->pb, spi->bool_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpConstantFalse(SPICtx *spi)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 42);
+    bytestream2_put_le32(&spi->pb, spi->bool_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpConstantUInt(SPICtx *spi, int type_id, uint32_t val)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 43);
+    bytestream2_put_le32(&spi->pb, type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, val);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpConstantUInt64(SPICtx *spi, int type_id, uint64_t val)
+{
+    bytestream2_put_le32(&spi->pb, (5 << 16) | 43);
+    bytestream2_put_le32(&spi->pb, type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, val & UINT32_MAX);
+    bytestream2_put_le32(&spi->pb, val >> 32);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpConstantInt(SPICtx *spi, int type_id, int val)
+{
+    uint32_t vu = (union { int i; uint32_t u; }){val}.u;
+    return spi_OpConstantUInt(spi, type_id, vu);
+}
+
+static inline int spi_OpConstantInt64(SPICtx *spi, int type_id, int64_t val)
+{
+    uint64_t vu = (union { int64_t i; uint64_t u; }){val}.u;
+    return spi_OpConstantUInt64(spi, type_id, vu);
+}
+
+static inline int spi_OpConstantFloat(SPICtx *spi, int type_id, float val)
+{
+    uint32_t vu = (union { double f; uint32_t u; }){val}.u;
+    return spi_OpConstantUInt(spi, type_id, vu);
+}
+
+static inline int spi_OpConstantDouble(SPICtx *spi, int type_id, double val)
+{
+    uint64_t vu = (union { double d; uint64_t u; }){val}.u;
+    return spi_OpConstantUInt64(spi, type_id, vu);
+}
+
+static inline int spi_OpAccessChain(SPICtx *spi, int result_type_id, int 
ptr_id,
+                                    const int *idx_id, int nb_idx)
+{
+    bytestream2_put_le32(&spi->pb, (4 + nb_idx << 16) | 65);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, ptr_id);
+    for (int i = 0; i < nb_idx; i++)
+        bytestream2_put_le32(&spi->pb, idx_id[i]);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpExtInst(SPICtx *spi, int result_type_id, int instr_idx,
+                                int set_id, const int *opr_id, int nb_opr)
+{
+    bytestream2_put_le32(&spi->pb, (5 + nb_opr << 16) | 12);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, set_id);
+    bytestream2_put_le32(&spi->pb, instr_idx);
+    for (int i = 0; i < nb_opr; i++)
+        bytestream2_put_le32(&spi->pb, opr_id[i]);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpLoad(SPICtx *spi, int result_type_id, int ptr_id,
+                             SpvMemoryAccessMask memory_access, int align)
+{
+    int is_aligned = !!(memory_access & SpvMemoryAccessAlignedMask);
+    bytestream2_put_le32(&spi->pb, (5 + is_aligned << 16) | 61);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, ptr_id);
+    bytestream2_put_le32(&spi->pb, memory_access);
+    if (is_aligned)
+        bytestream2_put_le32(&spi->pb, align);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpStore(SPICtx *spi, int ptr_id, int obj_id,
+                               SpvMemoryAccessMask memory_access, int align)
+{
+    int is_aligned = !!(memory_access & SpvMemoryAccessAlignedMask);
+    bytestream2_put_le32(&spi->pb, (4 + is_aligned << 16) | 62);
+    bytestream2_put_le32(&spi->pb, ptr_id);
+    bytestream2_put_le32(&spi->pb, obj_id);
+    bytestream2_put_le32(&spi->pb, memory_access);
+    if (is_aligned)
+        bytestream2_put_le32(&spi->pb, align);
+}
+
+static inline int spi_OpVectorShuffle(SPICtx *spi, int result_type_id,
+                                      int vec1_id, int vec2_id,
+                                      int *comp, int nb_comp)
+{
+    bytestream2_put_le32(&spi->pb, (5 + nb_comp << 16) | 79);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, vec1_id);
+    bytestream2_put_le32(&spi->pb, vec2_id);
+    for (int i = 0; i < nb_comp; i++)
+        bytestream2_put_le32(&spi->pb, comp[i]);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpBitcast(SPICtx *spi, int result_type_id, int src_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 124);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, src_id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_OpImageQuerySize(SPICtx *spi, int result_type_id,
+                                       int img_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 104);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, img_id);
+    return spi_get_id(spi);
+}
+
+static inline int spi_op_1in(SPICtx *spi, int result_type_id,
+                             int src_id, const int id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | id);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, src_id);
+    return spi_get_id(spi);
+}
+#define FN1IN(name, code)                                                   \
+static inline int spi_ ## name(SPICtx *spi, int result_type_id, int src_id) \
+{                                                                           \
+    return spi_op_1in(spi, result_type_id, src_id, code);                   \
+}
+FN1IN(OpConvertFToU, 109)
+FN1IN(OpConvertFToS, 110)
+FN1IN(OpConvertSToF, 111)
+FN1IN(OpConvertUToF, 112)
+
+static inline int spi_op_2in(SPICtx *spi, int result_type_id,
+                             int src1_id, int src2_id, const int id)
+{
+    bytestream2_put_le32(&spi->pb, (5 << 16) | id);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, src1_id);
+    bytestream2_put_le32(&spi->pb, src2_id);
+    return spi_get_id(spi);
+}
+
+#define FN2IN(name, code)                                           \
+static inline int spi_ ## name(SPICtx *spi, int result_type_id,     \
+                               int src1_id, int src2_id)            \
+{                                                                   \
+    return spi_op_2in(spi, result_type_id, src1_id, src2_id, code); \
+}
+FN2IN(OpIMul, 132)
+FN2IN(OpIEqual, 170)
+FN2IN(OpINotEqual, 171)
+FN2IN(OpUGreaterThan, 172)
+FN2IN(OpSGreaterThan, 173)
+FN2IN(OpUGreaterThanEqual, 174)
+FN2IN(OpSGreaterThanEqual, 175)
+FN2IN(OpULessThan, 176)
+FN2IN(OpSLessThan, 177)
+FN2IN(OpULessThanEqual, 178)
+FN2IN(OpSLessThanEqual, 179)
+
+static inline int spi_OpAny(SPICtx *spi, int result_type_id, int vec_id)
+{
+    bytestream2_put_le32(&spi->pb, (4 << 16) | 154);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, vec_id);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpSelectionMerge(SPICtx *spi, int merge_block,
+                                        SpvSelectionControlMask 
selection_control)
+{
+    bytestream2_put_le32(&spi->pb, (3 << 16) | 247);
+    bytestream2_put_le32(&spi->pb, merge_block);
+    bytestream2_put_le32(&spi->pb, selection_control);
+}
+
+static inline void spi_OpBranchConditional(SPICtx *spi, int cond_id,
+                                           int true_label, int false_label,
+                                           uint32_t branch_weights)
+{
+    bytestream2_put_le32(&spi->pb, (4 + 2*(!!branch_weights) << 16) | 250);
+    bytestream2_put_le32(&spi->pb, cond_id);
+    bytestream2_put_le32(&spi->pb, true_label);
+    bytestream2_put_le32(&spi->pb, false_label);
+    if (branch_weights) {
+        bytestream2_put_le32(&spi->pb, branch_weights >> 16);
+        bytestream2_put_le32(&spi->pb, false_label & UINT16_MAX);
+    }
+}
+
+static inline int spi_OpImageRead(SPICtx *spi, int result_type_id, int img_id,
+                                  int pos_id, SpvImageOperandsMask 
image_operands)
+{
+    bytestream2_put_le32(&spi->pb, (6 << 16) | 98);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, img_id);
+    bytestream2_put_le32(&spi->pb, pos_id);
+    bytestream2_put_le32(&spi->pb, image_operands);
+    return spi_get_id(spi);
+}
+
+static inline void spi_OpImageWrite(SPICtx *spi, int img_id, int pos_id,
+                                    int src_id, SpvImageOperandsMask 
image_operands)
+{
+    bytestream2_put_le32(&spi->pb, (5 << 16) | 99);
+    bytestream2_put_le32(&spi->pb, img_id);
+    bytestream2_put_le32(&spi->pb, pos_id);
+    bytestream2_put_le32(&spi->pb, src_id);
+    bytestream2_put_le32(&spi->pb, image_operands);
+}
+
+static inline int spi_op_1out(SPICtx *spi, int result_type_id,
+                              const int *src_id, int nb_src, int id)
+{
+    bytestream2_put_le32(&spi->pb, (3 + nb_src << 16) | id);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    for (int i = 0; i < nb_src; i++)
+        bytestream2_put_le32(&spi->pb, src_id[i]);
+    return spi_get_id(spi);
+}
+#define FN1OUT(name, code)                                         \
+static inline int spi_ ## name(SPICtx *spi, int result_type_id,    \
+                               const int *src_id, int nb_src)      \
+{                                                                  \
+    return spi_op_1out(spi, result_type_id, src_id, nb_src, code); \
+}
+FN1OUT(OpCompositeConstruct, 80)
+FN1OUT(OpConstantComposite, 44)
+
+static inline int spi_OpCompositeExtract(SPICtx *spi, int result_type_id,
+                                         int src_id, const int *idx_id, int 
nb_idx)
+{
+    bytestream2_put_le32(&spi->pb, (4 + nb_idx << 16) | 81);
+    bytestream2_put_le32(&spi->pb, result_type_id);
+    bytestream2_put_le32(&spi->pb, spi->id);
+    bytestream2_put_le32(&spi->pb, src_id);
+    for (int i = 0; i < nb_idx; i++)
+        bytestream2_put_le32(&spi->pb, idx_id[i]);
+    return spi_get_id(spi);
+}
+
+#endif /* SWSCALE_VULKAN_SPVASM_H */
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to