adding decoration members propagation
Build / build (push) Successful in 1m40s
Test / build (push) Successful in 7m45s

This commit is contained in:
2026-04-27 15:42:29 +02:00
parent 5b7380eea0
commit 9cdb683f3f
4 changed files with 220 additions and 33 deletions
+108 -20
View File
@@ -166,6 +166,106 @@ fn pass(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
}
}
fn resolveConstantWord(self: *const Self, id: SpvWord) ?SpvWord {
if (id >= self.results.len) return null;
const variant = self.results[id].variant orelse return null;
return switch (variant) {
.Constant => |c| switch (c.value) {
.Int => |i| i.value.uint32,
else => null,
},
else => null,
};
}
fn findAccessChainToMember(self: *const Self, base_id: SpvWord, member_index: SpvWord) ?SpvWord {
for (self.results, 0..) |result, id| {
const variant = result.variant orelse continue;
switch (variant) {
.AccessChain => |a| {
if (a.base != base_id or a.indexes.len == 0) continue;
const first_index = self.resolveConstantWord(a.indexes[0]) orelse continue;
if (first_index == member_index) return @intCast(id);
},
else => {},
}
}
return null;
}
fn applyInterfaceDecoration(
self: *Self,
storage_class: spv.SpvStorageClass,
decoration: Result.Decoration,
id: SpvWord,
) ModuleError!void {
switch (storage_class) {
.Input => switch (decoration.rtype) {
.BuiltIn => self.builtins.put(
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
id,
),
.Location => self.input_locations[decoration.literal_1] = id,
else => {},
},
.Output => switch (decoration.rtype) {
.BuiltIn => self.builtins.put(
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
id,
),
.Location => self.output_locations[decoration.literal_1] = id,
else => {},
},
else => {},
}
}
fn applyStructMemberInterfaceDecorations(
self: *Self,
storage_class: spv.SpvStorageClass,
type_word: SpvWord,
id: SpvWord,
) ModuleError!void {
switch (storage_class) {
.Input, .Output => {},
else => return,
}
const type_result = &self.results[type_word];
const target_type_word = if (type_result.variant) |variant| switch (variant) {
.Type => |t| switch (t) {
.Pointer => |ptr| ptr.target,
else => type_word,
},
else => type_word,
} else type_word;
const target_result = &self.results[target_type_word];
if (target_result.variant) |variant| {
switch (variant) {
.Type => |t| switch (t) {
.Structure => {
for (target_result.decorations.items) |decoration| {
switch (decoration.rtype) {
.BuiltIn, .Location => {
const member_id = self.findAccessChainToMember(id, decoration.index) orelse continue;
try self.applyInterfaceDecoration(storage_class, decoration, member_id);
},
else => {},
}
}
},
else => {},
},
else => {},
}
}
}
fn applyDecorations(self: *Self) ModuleError!void {
for (self.results, 0..) |result, id| {
if (result.variant == null)
@@ -177,27 +277,9 @@ fn applyDecorations(self: *Self) ModuleError!void {
for (result.decorations.items) |decoration| {
switch (result.variant.?) {
.Variable => |v| {
try self.applyInterfaceDecoration(v.storage_class, decoration, @intCast(id));
switch (v.storage_class) {
.Input => {
switch (decoration.rtype) {
.BuiltIn => self.builtins.put(
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
@intCast(id),
),
.Location => self.input_locations[decoration.literal_1] = @intCast(id),
else => {},
}
},
.Output => {
switch (decoration.rtype) {
.BuiltIn => self.builtins.put(
std.enums.fromInt(spv.SpvBuiltIn, decoration.literal_1) orelse return ModuleError.InvalidSpirV,
@intCast(id),
),
.Location => self.output_locations[decoration.literal_1] = @intCast(id),
else => {},
}
},
.StorageBuffer, .Uniform, .UniformConstant => {
switch (decoration.rtype) {
.Binding => binding = decoration.literal_1,
@@ -221,6 +303,12 @@ fn applyDecorations(self: *Self) ModuleError!void {
else => {},
}
}
switch (result.variant.?) {
.Variable => |v| try self.applyStructMemberInterfaceDecorations(v.storage_class, v.type_word, @intCast(id)),
else => {},
}
if (set != null and binding != null) {
self.bindings[set.?][binding.?] = @intCast(id);
}
+14 -1
View File
@@ -201,6 +201,8 @@ pub const VariantData = union(Variant) {
},
AccessChain: struct {
target: SpvWord,
base: SpvWord,
indexes: []SpvWord,
value: Value,
},
FunctionParameter: struct {
@@ -247,7 +249,10 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
},
.Constant => |*c| c.value.deinit(allocator),
.Variable => |*v| v.value.deinit(allocator),
.AccessChain => |*a| a.value.deinit(allocator),
.AccessChain => |*a| {
allocator.free(a.indexes);
a.value.deinit(allocator);
},
.Function => |f| allocator.free(f.params),
else => {},
}
@@ -363,6 +368,14 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self {
.params = allocator.dupe(SpvWord, f.params) catch return RuntimeError.OutOfMemory,
},
},
.AccessChain => |a| break :blk .{
.AccessChain = .{
.target = a.target,
.base = a.base,
.indexes = allocator.dupe(SpvWord, a.indexes) catch return RuntimeError.OutOfMemory,
.value = try a.value.dupe(allocator),
},
},
else => break :blk variant,
}
}
+41 -4
View File
@@ -242,9 +242,46 @@ pub fn writeDescriptorSet(self: *const Self, input: []const u8, set: SpvWord, bi
}
}
fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
const variant = self.results[result].variant orelse return RuntimeError.InvalidSpirV;
switch (variant) {
.Variable => |v| _ = try v.value.read(output),
.AccessChain => |a| switch (a.value) {
.Pointer => |ptr| switch (ptr.ptr) {
.common => |value_ptr| _ = try value_ptr.read(output),
.f32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(f32)], std.mem.asBytes(value_ptr)),
.i32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(i32)], std.mem.asBytes(value_ptr)),
.u32_ptr => |value_ptr| std.mem.copyForwards(u8, output[0..@sizeOf(u32)], std.mem.asBytes(value_ptr)),
},
else => _ = try a.value.read(output),
},
else => return RuntimeError.InvalidSpirV,
}
}
fn writeResultValue(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void {
if (self.results[result].variant) |*variant| {
switch (variant.*) {
.Variable => |*v| _ = try v.value.writeConst(input),
.AccessChain => |*a| switch (a.value) {
.Pointer => |ptr| switch (ptr.ptr) {
.common => |value_ptr| _ = try value_ptr.writeConst(input),
.f32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]),
.i32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]),
.u32_ptr => |value_ptr| std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]),
},
else => _ = try a.value.writeConst(input),
},
else => return RuntimeError.InvalidSpirV,
}
} else {
return RuntimeError.InvalidSpirV;
}
}
pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
if (std.mem.indexOfScalar(SpvWord, &self.mod.output_locations, result)) |_| {
_ = try self.results[result].variant.?.Variable.value.read(output);
try self.readResultValue(output, result);
} else {
return RuntimeError.NotFound;
}
@@ -252,7 +289,7 @@ pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError
pub fn readBuiltIn(self: *const Self, output: []u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
if (self.mod.builtins.get(builtin)) |result| {
_ = try self.results[result].variant.?.Variable.value.read(output);
try self.readResultValue(output, result);
} else {
return RuntimeError.NotFound;
}
@@ -260,7 +297,7 @@ 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.results[result].variant.?.Variable.value.writeConst(input);
try self.writeResultValue(input, result);
} else {
return RuntimeError.NotFound;
}
@@ -268,7 +305,7 @@ pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) Runtime
pub fn writeBuiltIn(self: *const Self, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
if (self.mod.builtins.get(builtin)) |result| {
_ = try self.results[result].variant.?.Variable.value.writeConst(input);
try self.writeResultValue(input, result);
} else {
return RuntimeError.NotFound;
}
+57 -8
View File
@@ -88,6 +88,7 @@ pub const SetupDispatcher = block: {
.AtomicUMax = autoSetupConstant,
.AtomicUMin = autoSetupConstant,
.AtomicXor = autoSetupConstant,
.AccessChain = setupAccessChain,
.BitCount = autoSetupConstant,
.BitFieldInsert = autoSetupConstant,
.BitFieldSExtract = autoSetupConstant,
@@ -145,6 +146,7 @@ pub const SetupDispatcher = block: {
.IAddCarry = autoSetupConstant,
.IEqual = autoSetupConstant,
.ImageRead = autoSetupConstant,
.InBoundsAccessChain = setupAccessChain,
.IMul = autoSetupConstant,
.INotEqual = autoSetupConstant,
.ISub = autoSetupConstant,
@@ -1119,6 +1121,39 @@ fn autoSetupConstant(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) Run
_ = try setupConstant(allocator, rt);
}
fn setupAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const var_type = try rt.it.next();
const id = try rt.it.next();
const base_id = try rt.it.next();
const index_count: usize = @intCast(word_count - 3);
const indexes = allocator.alloc(SpvWord, index_count) catch return RuntimeError.OutOfMemory;
errdefer allocator.free(indexes);
for (indexes) |*index| {
index.* = try rt.it.next();
}
if (rt.results[id].variant) |*variant| {
switch (variant.*) {
.AccessChain => |*a| {
allocator.free(a.indexes);
a.value.deinit(allocator);
},
else => {},
}
}
rt.results[id].variant = .{
.AccessChain = .{
.target = var_type,
.base = base_id,
.indexes = indexes,
.value = try Value.init(allocator, rt.results, var_type, false),
},
};
}
fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
const helpers = struct {
inline fn copySlice(dst_slice: []Value, src_slice: []const Value) RuntimeError!void {
@@ -1269,25 +1304,39 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const index_count = word_count - 3;
const index_count: usize = @intCast(word_count - 3);
if (rt.results[id].variant) |*variant| {
switch (variant.*) {
.AccessChain => |*a| try a.value.flushPtr(allocator),
else => {},
const indexes, const free_responsability = blk: {
if (rt.results[id].variant) |*variant| {
switch (variant.*) {
.AccessChain => |*a| {
if (a.indexes.len != index_count)
return RuntimeError.InvalidSpirV;
try a.value.flushPtr(allocator);
a.value.deinit(allocator);
break :blk .{ a.indexes, false };
},
else => {},
}
}
}
break :blk .{ allocator.alloc(SpvWord, index_count) catch return RuntimeError.OutOfMemory, true };
};
errdefer if (free_responsability) allocator.free(indexes);
rt.results[id].variant = .{
.AccessChain = .{
.target = var_type,
.base = base_id,
.indexes = indexes,
.value = blk: {
var is_owner_of_uniform_slice = false;
var uniform_slice_window: ?[]u8 = null;
for (0..index_count) |index| {
const is_last = (index == index_count - 1);
const member = &rt.results[try rt.it.next()];
const index_id = try rt.it.next();
indexes[index] = index_id;
const member = &rt.results[index_id];
const member_value = switch ((try member.getVariant()).*) {
.Constant => |c| &c.value,
.Variable => |v| &v.value,
@@ -1488,7 +1537,7 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
const res_type = try rt.it.next();
const id = try rt.it.next();
const composite_id = try rt.it.next();
const index_count = word_count - 3;
const index_count: usize = @intCast(word_count - 3);
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();