From 37da19ed43fdd28f0c7b2087a81afeb433f81a19 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Fri, 23 Jan 2026 03:23:44 +0100 Subject: [PATCH] adding base extension support --- example/main.zig | 14 ++-- example/shader.nzsl | 8 +-- example/shader.spv | Bin 1360 -> 1344 bytes example/shader.spv.txt | 135 +++++++++++++++++++-------------------- src/Module.zig | 1 + src/Result.zig | 6 +- src/Runtime.zig | 1 + src/ext/GLSL_std_450.zig | 0 src/opcodes.zig | 28 +++++++- 9 files changed, 112 insertions(+), 81 deletions(-) create mode 100644 src/ext/GLSL_std_450.zig diff --git a/example/main.zig b/example/main.zig index 88723e6..0cc17dd 100644 --- a/example/main.zig +++ b/example/main.zig @@ -94,15 +94,15 @@ const Runner = struct { rt: spv.Runtime, fn run(self: *Self, y: usize, pixel_map: [*]u32) void { - const entry = self.rt.getEntryPointByName("main") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); - const color = self.rt.getResultByName("color") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); - const time = self.rt.getResultByName("time") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); - const pos = self.rt.getResultByName("pos") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); - const res = self.rt.getResultByName("res") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); - var output: [4]f32 = undefined; - var rt = self.rt; // Copy to avoid pointer access of `self` at runtime. Okay as Runtime contains only pointers and trivially copyable fields + const entry = rt.getEntryPointByName("main") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); + const color = rt.getResultByName("color") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); + const time = rt.getResultByName("time") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); + const pos = rt.getResultByName("pos") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); + const res = rt.getResultByName("res") catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); + var output: [4]f32 = undefined; + for (0..screen_width) |x| { rt.writeInput(f32, &.{@as(f32, @floatFromInt(std.time.milliTimestamp()))}, time) catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); rt.writeInput(f32, &.{ @floatFromInt(screen_width), @floatFromInt(screen_height) }, res) catch |err| std.debug.panic("Catch error {s}", .{@errorName(err)}); diff --git a/example/shader.nzsl b/example/shader.nzsl index e918355..064ce5e 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -3,8 +3,8 @@ module; struct FragIn { - [location(0)] time: u32, - [location(1)] dim: vec2[f32], + [location(0)] time: f32, + [location(1)] res: vec2[f32], [location(2)] pos: vec2[f32], } @@ -18,8 +18,8 @@ fn main(input: FragIn) -> FragOut { let output: FragOut; output.color = vec4[f32]( - input.pos.x / input.dim.x, - input.pos.y / input.dim.y, + input.pos.x / input.res.x, + input.pos.y / input.res.y, 1.0, 1.0 ); diff --git a/example/shader.spv b/example/shader.spv index f2a83eefbcdbfbc43530356e02a01f11284659d0..599620a2d64b3698d2839dde4f6c19e601c6bc0c 100644 GIT binary patch literal 1344 zcmZ9MOK(#_424b7gqBB}zIld}00jz#vOov~g$>XZuwh@J2t}d|O3EIw;wQ5~;`{C$ zqpObOjL-4-*q*scyEQx;aVCECw&O|EYdKml5sT5T2>qp4h&lF4*wNwm*gyw6k1b+r zSda6@5yWegA^DoTPJA9sXXDB7^TFQko^%Jc$NqMD`1Js_E{xc9GCmr)cgq>;Pe-%= zxCQ<`nN=F=_-cIkaZ+iBU0){OCR29n*k@{X=*ynf)3^83a)xGJbJll7r_ULeSRJwb ziPaL@pIE&-|DI|&-w@uFocVI|4NeU?b3B)GW$tQuqb+9etQs5mZT3NBb97Mia_gXO z#n;?U!M>Gi0JeVpb`hIk-_Kn2jh|z;j(p}{D4g+&_~tj`OJMW+Wq$RHYvwGoo8z3; zTw(W&y&GRGSiVohd(bzw!EUac{g}6jMf}`1{s_;w=U;%;^jmNSvGe$bP3#*cwzhAm z?o%@5{U%(_^oQTe>}uJ|70h~K=ek<(8*;tdYYlE6Rk8gT_wKHjxOZw?Y=0T|TT;_I zH7>T_jNib|_)B`I?>IvK6E&P;3magr%)5zio^>;Ch%cXcxA5g$zl+vweD#c_)*bxQ zJ#53tr`BD3Iqx&`?%|*A;Xa&v=I!9inU_60z*o=MM{eJ@eu&wdF~0}RI(`qkSiaLM eZba;P=k_~#T;kSL%RehQm*@Q+p5WF-a)HrJYDX1^ySgF zKYh9M?N496y#J1BIbRRllbHEp=WCc8Vx5BJT$#I2-e{E>yer2dHJj)P@l^P}nK5zW z?Fl}`HjD3jdAx5A)Ax&5BFi_dkY!>Q(fZc&?j^L`;>mTnV8&;$<*LV>$NX;DhkY4W z%(;R#$6Z--0c}m^Fn$$nj(8W#y=WU-Mw=^UKjvLSB7W@|e~e|^`#bn@`foUczVrB| zb>BBl-`c*Z^`DU`?myyjraydF&~n+!4a9o-&ULf+ugP`Kw`$lPtort2+}-)_nBz{3 z>)T((Z(|#Gr^fZ|H{%s{#&_w#9j_wdpQzz}?jT~G%!BF9S{|B+ZbnzVd2fYkDV*mgE diff --git a/example/shader.spv.txt b/example/shader.spv.txt index ae71392..fe4fbe6 100644 --- a/example/shader.spv.txt +++ b/example/shader.spv.txt @@ -1,86 +1,85 @@ Version 1.0 Generator: 2560130 -Bound: 51 +Bound: 50 Schema: 0 OpCapability Capability(Shader) OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) - OpEntryPoint ExecutionModel(Fragment) %25 "main" %5 %12 %15 %21 - OpExecutionMode %25 ExecutionMode(OriginUpperLeft) + OpEntryPoint ExecutionModel(Fragment) %24 "main" %5 %11 %14 %20 + OpExecutionMode %24 ExecutionMode(OriginUpperLeft) OpSource SourceLanguage(NZSL) 4198400 OpSourceExtension "Version: 1.1" - OpName %17 "FragIn" - OpMemberName %17 0 "time" - OpMemberName %17 1 "dim" - OpMemberName %17 2 "pos" - OpName %22 "FragOut" - OpMemberName %22 0 "color" + OpName %16 "FragIn" + OpMemberName %16 0 "time" + OpMemberName %16 1 "res" + OpMemberName %16 2 "pos" + OpName %21 "FragOut" + OpMemberName %21 0 "color" OpName %5 "time" - OpName %12 "dim" - OpName %15 "pos" - OpName %21 "color" - OpName %25 "main" + OpName %11 "res" + OpName %14 "pos" + OpName %20 "color" + OpName %24 "main" OpDecorate %5 Decoration(Location) 0 - OpDecorate %12 Decoration(Location) 1 - OpDecorate %15 Decoration(Location) 2 - OpDecorate %21 Decoration(Location) 0 - OpMemberDecorate %17 0 Decoration(Offset) 0 - OpMemberDecorate %17 1 Decoration(Offset) 8 - OpMemberDecorate %17 2 Decoration(Offset) 16 - OpMemberDecorate %22 0 Decoration(Offset) 0 + OpDecorate %11 Decoration(Location) 1 + OpDecorate %14 Decoration(Location) 2 + OpDecorate %20 Decoration(Location) 0 + OpMemberDecorate %16 0 Decoration(Offset) 0 + OpMemberDecorate %16 1 Decoration(Offset) 8 + OpMemberDecorate %16 2 Decoration(Offset) 16 + OpMemberDecorate %21 0 Decoration(Offset) 0 %1 = OpTypeVoid %2 = OpTypeFunction %1 - %3 = OpTypeInt 32 0 + %3 = OpTypeFloat 32 %4 = OpTypePointer StorageClass(Input) %3 %6 = OpTypeInt 32 1 %7 = OpConstant %6 i32(0) %8 = OpTypePointer StorageClass(Function) %3 - %9 = OpTypeFloat 32 -%10 = OpTypeVector %9 2 -%11 = OpTypePointer StorageClass(Input) %10 -%13 = OpConstant %6 i32(1) -%14 = OpTypePointer StorageClass(Function) %10 -%16 = OpConstant %6 i32(2) -%17 = OpTypeStruct %3 %10 %10 -%18 = OpTypePointer StorageClass(Function) %17 -%19 = OpTypeVector %9 4 -%20 = OpTypePointer StorageClass(Output) %19 -%22 = OpTypeStruct %19 -%23 = OpTypePointer StorageClass(Function) %22 -%24 = OpConstant %9 f32(1) -%48 = OpTypePointer StorageClass(Function) %19 + %9 = OpTypeVector %3 2 +%10 = OpTypePointer StorageClass(Input) %9 +%12 = OpConstant %6 i32(1) +%13 = OpTypePointer StorageClass(Function) %9 +%15 = OpConstant %6 i32(2) +%16 = OpTypeStruct %3 %9 %9 +%17 = OpTypePointer StorageClass(Function) %16 +%18 = OpTypeVector %3 4 +%19 = OpTypePointer StorageClass(Output) %18 +%21 = OpTypeStruct %18 +%22 = OpTypePointer StorageClass(Function) %21 +%23 = OpConstant %3 f32(1) +%47 = OpTypePointer StorageClass(Function) %18 %5 = OpVariable %4 StorageClass(Input) -%12 = OpVariable %11 StorageClass(Input) -%15 = OpVariable %11 StorageClass(Input) -%21 = OpVariable %20 StorageClass(Output) -%25 = OpFunction %1 FunctionControl(0) %2 -%26 = OpLabel -%27 = OpVariable %23 StorageClass(Function) -%28 = OpVariable %18 StorageClass(Function) -%29 = OpAccessChain %8 %28 %7 - OpCopyMemory %29 %5 -%30 = OpAccessChain %14 %28 %13 - OpCopyMemory %30 %12 -%31 = OpAccessChain %14 %28 %16 - OpCopyMemory %31 %15 -%32 = OpAccessChain %14 %28 %16 -%33 = OpLoad %10 %32 -%34 = OpCompositeExtract %9 %33 0 -%35 = OpAccessChain %14 %28 %13 -%36 = OpLoad %10 %35 -%37 = OpCompositeExtract %9 %36 0 -%38 = OpFDiv %9 %34 %37 -%39 = OpAccessChain %14 %28 %16 -%40 = OpLoad %10 %39 -%41 = OpCompositeExtract %9 %40 1 -%42 = OpAccessChain %14 %28 %13 -%43 = OpLoad %10 %42 -%44 = OpCompositeExtract %9 %43 1 -%45 = OpFDiv %9 %41 %44 -%46 = OpCompositeConstruct %19 %38 %45 %24 %24 -%47 = OpAccessChain %48 %27 %7 - OpStore %47 %46 -%49 = OpLoad %22 %27 -%50 = OpCompositeExtract %19 %49 0 - OpStore %21 %50 +%11 = OpVariable %10 StorageClass(Input) +%14 = OpVariable %10 StorageClass(Input) +%20 = OpVariable %19 StorageClass(Output) +%24 = OpFunction %1 FunctionControl(0) %2 +%25 = OpLabel +%26 = OpVariable %22 StorageClass(Function) +%27 = OpVariable %17 StorageClass(Function) +%28 = OpAccessChain %8 %27 %7 + OpCopyMemory %28 %5 +%29 = OpAccessChain %13 %27 %12 + OpCopyMemory %29 %11 +%30 = OpAccessChain %13 %27 %15 + OpCopyMemory %30 %14 +%31 = OpAccessChain %13 %27 %15 +%32 = OpLoad %9 %31 +%33 = OpCompositeExtract %3 %32 0 +%34 = OpAccessChain %13 %27 %12 +%35 = OpLoad %9 %34 +%36 = OpCompositeExtract %3 %35 0 +%37 = OpFDiv %3 %33 %36 +%38 = OpAccessChain %13 %27 %15 +%39 = OpLoad %9 %38 +%40 = OpCompositeExtract %3 %39 1 +%41 = OpAccessChain %13 %27 %12 +%42 = OpLoad %9 %41 +%43 = OpCompositeExtract %3 %42 1 +%44 = OpFDiv %3 %40 %43 +%45 = OpCompositeConstruct %18 %37 %44 %23 %23 +%46 = OpAccessChain %47 %26 %7 + OpStore %46 %45 +%48 = OpLoad %21 %26 +%49 = OpCompositeExtract %18 %48 0 + OpStore %20 %49 OpReturn OpFunctionEnd diff --git a/src/Module.zig b/src/Module.zig index 999db9c..9e1b301 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -41,6 +41,7 @@ const ModuleError = error{ InvalidSpirV, InvalidMagic, UnsupportedEndianness, + UnsupportedExtension, OutOfMemory, }; diff --git a/src/Result.zig b/src/Result.zig index 4cbbce0..c7859ea 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -1,5 +1,6 @@ const std = @import("std"); const spv = @import("spv.zig"); +const op = @import("opcodes.zig"); const RuntimeError = @import("Runtime.zig").RuntimeError; @@ -228,7 +229,10 @@ pub const Value = union(Type) { pub const VariantData = union(Variant) { String: []const u8, - Extension: struct {}, + Extension: struct { + /// Should not be allocated but rather a pointer to a static array + dispatcher: []op.OpCodeExtFunc, + }, Type: union(Type) { Void: struct {}, Bool: struct {}, diff --git a/src/Runtime.zig b/src/Runtime.zig index 0f12f5a..0440c63 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -26,6 +26,7 @@ pub const RuntimeError = error{ ToDo, Unreachable, UnsupportedSpirV, + UnsupportedExtension, }; pub const Function = struct { diff --git a/src/ext/GLSL_std_450.zig b/src/ext/GLSL_std_450.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/opcodes.zig b/src/opcodes.zig index 14942e0..fb7e097 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -13,7 +13,6 @@ const SpvByte = spv.SpvByte; const SpvWord = spv.SpvWord; const SpvBool = spv.SpvBool; -// OpExtInstImport // OpExtInst Sin // OpExtInst Cos // OpExtInst Length @@ -71,6 +70,7 @@ const BitOp = enum { }; pub const OpCodeFunc = *const fn (std.mem.Allocator, SpvWord, *Runtime) RuntimeError!void; +pub const OpCodeExtFunc = *const fn (std.mem.Allocator, SpvWord, SpvWord, SpvWord, *Runtime) RuntimeError!void; pub const SetupDispatcher = block: { @setEvalBranchQuota(65535); @@ -174,6 +174,8 @@ pub const SetupDispatcher = block: { .Variable = opVariable, .VectorTimesMatrix = autoSetupConstant, .VectorTimesScalar = autoSetupConstant, + .ExtInst = autoSetupConstant, + .ExtInstImport = opExtInstImport, }); }; @@ -1104,6 +1106,30 @@ fn opExecutionMode(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError! } } +fn opExtInst(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const target_type = try rt.it.next(); + const id = try rt.it.next(); + const set = try rt.it.next(); + const inst = try rt.it.next(); + + switch (try rt.results[set].getVariant()) { + .Extension => |ext| if (ext.dispatcher[inst]) |pfn| { + try pfn(allocator, target_type, id, word_count, rt); + }, + else => return RuntimeError.InvalidSpirV, + } +} + +fn opExtInstImport(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + rt.mod.results[id].name = try readStringN(allocator, &rt.it, word_count - 1); + rt.mod.results[id].variant = .{ + .Extension = .{ + .dispatcher = undefined, + }, + }; +} + fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const return_type = try rt.it.next(); const id = try rt.it.next();