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]
