From ee5a400010b3a5852606b2f8593bd5c745bf0a26 Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Fri, 5 Jun 2026 01:21:43 +0200 Subject: [PATCH] adding component locations --- src/GLSL_std_450/opcodes.zig | 2 + src/Module.zig | 16 ++++-- src/Runtime.zig | 52 ++++++++++++-------- src/WordIterator.zig | 1 + src/opcodes.zig | 95 +++++++++++++++++++++++++++++++++--- 5 files changed, 134 insertions(+), 32 deletions(-) diff --git a/src/GLSL_std_450/opcodes.zig b/src/GLSL_std_450/opcodes.zig index ba50baf..38b4219 100644 --- a/src/GLSL_std_450/opcodes.zig +++ b/src/GLSL_std_450/opcodes.zig @@ -86,6 +86,7 @@ pub fn initRuntimeDispatcher() void { runtime_dispatcher[@intFromEnum(ext.GLSLOp.FAbs)] = MathEngine(.Float, .FAbs).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FClamp)] = MathEngine(.Float, .FClamp).opTripleOperators; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FMax)] = MathEngine(.Float, .FMax).opDoubleOperators; + runtime_dispatcher[@intFromEnum(ext.GLSLOp.FMin)] = MathEngine(.Float, .FMin).opDoubleOperators; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FSign)] = MathEngine(.Float, .FSign).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Floor)] = MathEngine(.Float, .Floor).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Length)] = opLength; @@ -203,6 +204,7 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp) type { fn operation(comptime TT: type, l: TT, r: TT) RuntimeError!TT { return switch (Op) { .FMax => @max(l, r), + .FMin => @min(l, r), else => RuntimeError.InvalidSpirV, }; } diff --git a/src/Module.zig b/src/Module.zig index 8884912..b4e4175 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -75,8 +75,8 @@ geometry_output_count: SpvWord, geometry_input: SpvWord, geometry_output: SpvWord, -input_locations: [lib.SPIRV_MAX_INPUT_LOCATIONS]SpvWord, -output_locations: [lib.SPIRV_MAX_OUTPUT_LOCATIONS]SpvWord, +input_locations: [lib.SPIRV_MAX_INPUT_LOCATIONS][4]SpvWord, +output_locations: [lib.SPIRV_MAX_OUTPUT_LOCATIONS][4]SpvWord, bindings: std.ArrayList(BindingEntry), builtins: std.EnumMap(spv.SpvBuiltIn, SpvWord), @@ -203,6 +203,14 @@ fn findAccessChainToMember(self: *const Self, base_id: SpvWord, member_index: Sp return null; } +fn resultComponent(self: *const Self, id: SpvWord) SpvWord { + for (self.results[id].decorations.items) |decoration| { + if (decoration.rtype == .Component) + return decoration.literal_1; + } + return 0; +} + fn applyInterfaceDecoration(self: *Self, storage_class: spv.SpvStorageClass, decoration: Result.Decoration, id: SpvWord) ModuleError!void { switch (storage_class) { .Input => switch (decoration.rtype) { @@ -210,7 +218,7 @@ fn applyInterfaceDecoration(self: *Self, storage_class: spv.SpvStorageClass, dec std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV, id, ), - .Location => self.input_locations[decoration.literal_1] = id, + .Location => self.input_locations[decoration.literal_1][self.resultComponent(id)] = id, else => {}, }, .Output => switch (decoration.rtype) { @@ -218,7 +226,7 @@ fn applyInterfaceDecoration(self: *Self, storage_class: spv.SpvStorageClass, dec std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV, id, ), - .Location => self.output_locations[decoration.literal_1] = id, + .Location => self.output_locations[decoration.literal_1][self.resultComponent(id)] = id, else => {}, }, else => {}, diff --git a/src/Runtime.zig b/src/Runtime.zig index bd13488..2dbcc80 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -177,13 +177,19 @@ pub fn getResultByName(self: *const Self, name: []const u8) RuntimeError!SpvWord return RuntimeError.NotFound; } -pub fn getResultByLocation(self: *const Self, location: SpvWord, kind: enum { input, output }) RuntimeError!SpvWord { +pub const LocationKind = enum { input, output }; + +pub inline fn getResultByLocation(self: *const Self, location: SpvWord, kind: LocationKind) RuntimeError!SpvWord { + return self.getResultByLocationComponent(location, 0, kind); +} + +pub fn getResultByLocationComponent(self: *const Self, location: SpvWord, component: SpvWord, kind: LocationKind) RuntimeError!SpvWord { switch (kind) { - .input => if (location < self.mod.input_locations.len and self.mod.input_locations[location] != 0) { - return self.mod.input_locations[location]; + .input => if (location < self.mod.input_locations.len and component < 4 and self.mod.input_locations[location][component] != 0) { + return self.mod.input_locations[location][component]; }, - .output => if (location < self.mod.output_locations.len and self.mod.output_locations[location] != 0) { - return self.mod.output_locations[location]; + .output => if (location < self.mod.output_locations.len and component < 4 and self.mod.output_locations[location][component] != 0) { + return self.mod.output_locations[location][component]; }, } return RuntimeError.NotFound; @@ -371,8 +377,8 @@ const InputLocationTarget = struct { }; fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError!InputLocationTarget { - if (location < self.mod.input_locations.len and self.mod.input_locations[location] != 0) { - const result = self.mod.input_locations[location]; + if (location < self.mod.input_locations.len and self.mod.input_locations[location][0] != 0) { + const result = self.mod.input_locations[location][0]; const value = try self.results[result].getConstValue(); switch (value.*) { .Matrix => return .{ .result = result, .matrix_column = 0 }, @@ -385,7 +391,7 @@ fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError base_location -= 1; const result = if (base_location < self.mod.input_locations.len) - self.mod.input_locations[base_location] + self.mod.input_locations[base_location][0] else 0; if (result == 0) continue; @@ -429,11 +435,13 @@ fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) R } pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void { - if (std.mem.indexOfScalar(SpvWord, &self.mod.output_locations, result)) |_| { - try self.readResultValue(output, result); - } else { - return RuntimeError.NotFound; + for (&self.mod.output_locations) |*location| { + if (std.mem.indexOfScalar(SpvWord, location, result)) |_| { + try self.readResultValue(output, result); + return; + } } + return RuntimeError.NotFound; } pub fn readBuiltIn(self: *const Self, output: []u8, builtin: spv.SpvBuiltIn) RuntimeError!void { @@ -445,16 +453,18 @@ pub fn readBuiltIn(self: *const Self, output: []u8, builtin: spv.SpvBuiltIn) Run } pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void { - if (std.mem.indexOfScalar(SpvWord, &self.mod.input_locations, result)) |_| { - try self.writeResultValue(input, result); - if (self.results[result].variant) |*variant| switch (variant.*) { - .Variable => |*v| v.value.clearExternalData(), - .AccessChain => |*a| a.value.clearExternalData(), - else => {}, - }; - } else { - return RuntimeError.NotFound; + for (&self.mod.input_locations) |*location| { + if (std.mem.indexOfScalar(SpvWord, location, result)) |_| { + try self.writeResultValue(input, result); + if (self.results[result].variant) |*variant| switch (variant.*) { + .Variable => |*v| v.value.clearExternalData(), + .AccessChain => |*a| a.value.clearExternalData(), + else => {}, + }; + return; + } } + return RuntimeError.NotFound; } pub fn getInputLocationMemorySize(self: *const Self, location: SpvWord) RuntimeError!usize { diff --git a/src/WordIterator.zig b/src/WordIterator.zig index d509228..94b38e2 100644 --- a/src/WordIterator.zig +++ b/src/WordIterator.zig @@ -56,6 +56,7 @@ pub inline fn nextAs(self: *Self, comptime E: type) RuntimeError!E { if (self.next_force_skip) |skip_index| { if (self.index == skip_index) { _ = self.skip(); + self.next_force_skip = null; } } return self.nextAsOrNull(E) orelse return RuntimeError.InvalidSpirV; diff --git a/src/opcodes.zig b/src/opcodes.zig index 25b1798..72b9810 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -224,6 +224,11 @@ pub const SetupDispatcher = block: { .ShiftLeftLogical = autoSetupConstant, .ShiftRightArithmetic = autoSetupConstant, .ShiftRightLogical = autoSetupConstant, + .SpecConstant = opSpecConstant, + .SpecConstantComposite = opConstantComposite, + .SpecConstantFalse = opSpecConstantFalse, + .SpecConstantOp = opSpecConstantOp, + .SpecConstantTrue = opSpecConstantTrue, .SourceExtension = opSourceExtension, .TypeArray = opTypeArray, .TypeBool = opTypeBool, @@ -378,6 +383,7 @@ pub fn initRuntimeDispatcher() void { runtime_dispatcher[@intFromEnum(spv.SpvOp.SpecConstantOp)] = opSpecConstantOp; runtime_dispatcher[@intFromEnum(spv.SpvOp.SpecConstantTrue)] = opSpecConstantTrue; runtime_dispatcher[@intFromEnum(spv.SpvOp.Store)] = opStore; + runtime_dispatcher[@intFromEnum(spv.SpvOp.Switch)] = opSwitch; runtime_dispatcher[@intFromEnum(spv.SpvOp.UConvert)] = ConversionEngine(.UInt, .UInt).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UDiv)] = MathEngine(.UInt, .Div, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UGreaterThan)] = CondEngine(.UInt, .Greater).op; @@ -3220,20 +3226,95 @@ fn opSpecConstantFalse(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) R } } +fn opSwitch(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { + if (word_count < 2) + return RuntimeError.InvalidSpirV; + + const selector = try rt.results[try rt.it.next()].getValue(); + const default_target = try rt.it.next(); + + const SelectorData = struct { + value: u64, + literal_width: SpvWord, + }; + const selector_data: SelectorData = switch (selector.*) { + .Int => |i| switch (i.bit_count) { + 8 => .{ .value = @as(u64, i.value.uint8), .literal_width = @as(SpvWord, 1) }, + 16 => .{ .value = @as(u64, i.value.uint16), .literal_width = @as(SpvWord, 1) }, + 32 => .{ .value = @as(u64, i.value.uint32), .literal_width = @as(SpvWord, 1) }, + 64 => .{ .value = i.value.uint64, .literal_width = @as(SpvWord, 2) }, + else => return RuntimeError.InvalidSpirV, + }, + else => return RuntimeError.InvalidValueType, + }; + const selector_value = selector_data.value; + const literal_width = selector_data.literal_width; + + var target = default_target; + var remaining = word_count - 2; + while (remaining != 0) { + if (remaining < literal_width + 1) + return RuntimeError.InvalidSpirV; + + const literal = if (literal_width == 2) blk: { + const low = @as(u64, try rt.it.next()); + const high = @as(u64, try rt.it.next()); + break :blk (high << 32) | low; + } else try rt.it.next(); + const literal_target = try rt.it.next(); + + if (literal == selector_value) + target = literal_target; + + remaining -= literal_width + 1; + } + + rt.previous_label = rt.current_label; + _ = rt.it.jumpToSourceLocation(switch ((try rt.results[target].getVariant()).*) { + .Label => |l| l.source_location, + else => return RuntimeError.InvalidSpirV, + }); +} + fn opSpecConstantOp(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { if (word_count < 3) return RuntimeError.InvalidSpirV; - const start_location = rt.it.emitSourceLocation(); - - _ = try setupConstant(allocator, rt); + const target = try setupConstant(allocator, rt); const inner_op = try rt.it.nextAs(spv.SpvOp); - _ = rt.it.goToSourceLocation(start_location); - rt.it.forceSkipIndex(2); + const target_value = try target.getValue(); + switch (target_value.*) { + .Int => |*dst| { + if (word_count != 5) + return RuntimeError.UnsupportedSpirV; - const pfn = runtime_dispatcher[@intFromEnum(inner_op)] orelse return RuntimeError.UnsupportedSpirV; - try pfn(allocator, word_count - 1, rt); + const lhs = (try rt.results[try rt.it.next()].getValue()).Int; + const rhs = (try rt.results[try rt.it.next()].getValue()).Int; + const lhs_u = lhs.value.uint64; + const rhs_u = rhs.value.uint64; + + dst.value.uint64 = switch (inner_op) { + .IAdd => @addWithOverflow(lhs_u, rhs_u)[0], + .ISub => @subWithOverflow(lhs_u, rhs_u)[0], + .IMul => @mulWithOverflow(lhs_u, rhs_u)[0], + .UDiv => if (rhs_u != 0) @divTrunc(lhs_u, rhs_u) else return RuntimeError.DivisionByZero, + .UMod => if (rhs_u != 0) @mod(lhs_u, rhs_u) else return RuntimeError.DivisionByZero, + .SDiv => switch (dst.bit_count) { + 32 => @as(u32, @bitCast(@divTrunc(lhs.value.sint32, rhs.value.sint32))), + 64 => @bitCast(@divTrunc(lhs.value.sint64, rhs.value.sint64)), + else => return RuntimeError.UnsupportedSpirV, + }, + .SMod => switch (dst.bit_count) { + 32 => @as(u32, @bitCast(@mod(lhs.value.sint32, rhs.value.sint32))), + 64 => @bitCast(@mod(lhs.value.sint64, rhs.value.sint64)), + else => return RuntimeError.UnsupportedSpirV, + }, + else => return RuntimeError.UnsupportedSpirV, + }; + }, + else => return RuntimeError.UnsupportedSpirV, + } } fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {