diff --git a/src/Runtime.zig b/src/Runtime.zig index ad7ee16..bd13488 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -94,6 +94,16 @@ pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI) const results = allocator.dupe(Result, module.results) catch return RuntimeError.OutOfMemory; for (results, module.results) |*new_result, result| { new_result.* = result.dupe(allocator) catch return RuntimeError.OutOfMemory; + if (new_result.variant) |*variant| { + switch (variant.*) { + .AccessChain => |*access_chain| { + allocator.free(access_chain.indexes); + access_chain.value.deinit(allocator); + new_result.variant = null; + }, + else => {}, + } + } } break :blk results; }, @@ -192,13 +202,13 @@ pub fn dumpResultsTable(self: *Self, allocator: std.mem.Allocator, writer: *std. } /// Calls an entry point, `entry_point_index` being the index of the entry point ordered by declaration in the bytecode -pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!void { +pub inline fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!void { _ = try self.beginEntryPoint(allocator, entry_point_index); } pub fn beginEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_index: SpvWord) RuntimeError!EntryPointStatus { self.reset(); - if (entry_point_index > self.mod.entry_points.items.len) + if (entry_point_index >= self.mod.entry_points.items.len) return RuntimeError.InvalidEntryPoint; // Spec constants pass @@ -307,9 +317,18 @@ fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeErro .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)), + .f32_ptr => |value_ptr| { + if (output.len < @sizeOf(f32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, output[0..@sizeOf(f32)], std.mem.asBytes(value_ptr)); + }, + .i32_ptr => |value_ptr| { + if (output.len < @sizeOf(i32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, output[0..@sizeOf(i32)], std.mem.asBytes(value_ptr)); + }, + .u32_ptr => |value_ptr| { + if (output.len < @sizeOf(u32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, output[0..@sizeOf(u32)], std.mem.asBytes(value_ptr)); + }, }, else => _ = try a.value.read(output), }, @@ -324,9 +343,18 @@ fn writeResultValue(self: *const Self, input: []const u8, result: SpvWord) Runti .AccessChain => |*a| switch (a.value) { .Pointer => |ptr| switch (ptr.ptr) { .common => |value_ptr| _ = try value_ptr.write(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)]), + .f32_ptr => |value_ptr| { + if (input.len < @sizeOf(f32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]); + }, + .i32_ptr => |value_ptr| { + if (input.len < @sizeOf(i32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]); + }, + .u32_ptr => |value_ptr| { + if (input.len < @sizeOf(u32)) return RuntimeError.OutOfBounds; + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]); + }, }, else => _ = try a.value.write(input), }, @@ -337,6 +365,69 @@ fn writeResultValue(self: *const Self, input: []const u8, result: SpvWord) Runti } } +const InputLocationTarget = struct { + result: SpvWord, + matrix_column: ?usize = null, +}; + +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]; + const value = try self.results[result].getConstValue(); + switch (value.*) { + .Matrix => return .{ .result = result, .matrix_column = 0 }, + else => return .{ .result = result }, + } + } + + var base_location = location; + while (base_location > 0) { + base_location -= 1; + + const result = if (base_location < self.mod.input_locations.len) + self.mod.input_locations[base_location] + else + 0; + if (result == 0) continue; + + const location_offset: usize = @intCast(location - base_location); + const value = try self.results[result].getConstValue(); + switch (value.*) { + .Matrix => |columns| { + if (location_offset < columns.len) { + return .{ + .result = result, + .matrix_column = location_offset, + }; + } + }, + else => {}, + } + } + + return RuntimeError.NotFound; +} + +fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) RuntimeError!*@import("Value.zig").Value { + const value = switch ((try self.results[target.result].getVariant()).*) { + .Variable => |*v| &v.value, + .AccessChain => |*a| &a.value, + else => return RuntimeError.InvalidSpirV, + }; + + if (target.matrix_column) |column| { + switch (value.*) { + .Matrix => |columns| { + if (column >= columns.len) return RuntimeError.OutOfBounds; + return &columns[column]; + }, + else => return RuntimeError.InvalidValueType, + } + } + + return value; +} + 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); @@ -356,11 +447,28 @@ 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; } } +pub fn getInputLocationMemorySize(self: *const Self, location: SpvWord) RuntimeError!usize { + const target = try self.resolveInputLocationTarget(location); + return (try self.getInputLocationTargetValue(target)).getPlainMemorySize(); +} + +pub fn writeInputLocation(self: *const Self, input: []const u8, location: SpvWord) RuntimeError!void { + const target = try self.resolveInputLocationTarget(location); + const value = try self.getInputLocationTargetValue(target); + _ = try value.write(input); + value.clearExternalData(); +} + pub fn writeBuiltIn(self: *const Self, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void { if (self.mod.builtins.get(builtin)) |result| { try self.writeResultValue(input, result); @@ -388,8 +496,40 @@ pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.S return false; } +pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void { + for (self.results) |*result| { + if (result.variant) |*variant| { + switch (variant.*) { + .AccessChain => |*access_chain| { + access_chain.value.flushPtr(allocator) catch {}; + }, + else => {}, + } + } + } + + for (self.results) |*result| { + if (result.variant) |*variant| { + switch (variant.*) { + .AccessChain => |*access_chain| { + access_chain.value.deinit(allocator); + allocator.free(access_chain.indexes); + result.variant = null; + }, + .FunctionParameter => |*parameter| { + parameter.value_ptr = null; + }, + else => {}, + } + } + } + + self.reset(); +} + fn reset(self: *Self) void { self.function_stack.clearRetainingCapacity(); + self.current_parameter_index = 0; self.current_function = null; self.current_label = null; self.previous_label = null; diff --git a/src/Value.zig b/src/Value.zig index 9a691d6..dbe1ccf 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -80,18 +80,22 @@ pub const Value = union(Type) { data: []u8, pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!*Value { + const offset = try self.getCheckedOffsetOfIndex(index); const value = allocator.create(Value) catch return RuntimeError.OutOfMemory; errdefer allocator.destroy(value); value.* = try Value.init(allocator, results, self.type_word, false); - _ = try value.write(self.data[self.getOffsetOfIndex(index)..]); + errdefer value.deinit(allocator); + _ = try value.write(self.data[offset .. offset + self.stride]); return value; } pub inline fn createLocalValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!Value { + const offset = try self.getCheckedOffsetOfIndex(index); var value = try Value.init(allocator, results, self.type_word, false); - _ = try value.write(self.data[self.getOffsetOfIndex(index)..]); + errdefer value.deinit(allocator); + _ = try value.write(self.data[offset .. offset + self.stride]); return value; } @@ -99,7 +103,15 @@ pub const Value = union(Type) { return self.stride * index; } + pub inline fn getCheckedOffsetOfIndex(self: *const @This(), index: usize) RuntimeError!usize { + const offset = self.getOffsetOfIndex(index); + if (self.stride == 0 or offset > self.data.len or self.stride > self.data.len - offset) + return RuntimeError.OutOfBounds; + return offset; + } + pub inline fn getLen(self: *const @This()) usize { + if (self.stride == 0) return 0; return @divTrunc(self.data.len, self.stride); } }, @@ -145,6 +157,7 @@ pub const Value = union(Type) { /// array element. This may differ from ptr.common when the pointer is /// to a child/member of that materialized value. uniform_backing_value: ?*Self = null, + owns_uniform_backing_value: bool = false, }, pub inline fn getCompositeDataOrNull(self: *const Self) ?[]Self { @@ -330,40 +343,54 @@ pub const Value = union(Type) { const vecRoutine = struct { inline fn routine(comptime T: type, vec: T, out: []u8) RuntimeError!usize { const size = @typeInfo(T).vector.len * 4; - if (size >= out.len) { - const range = @typeInfo(T).vector.len; - inline for (0..range) |i| { - const start = i * 4; - const end = (i + 1) * 4; - if (start >= out.len or end > out.len) return i * 4; - @memcpy(out[start..end], std.mem.asBytes(&vec[i])); - } - } - std.mem.bytesAsValue(T, out[0..]).* = vec; + if (out.len < size) return RuntimeError.OutOfBounds; + std.mem.bytesAsValue(T, out[0..size]).* = vec; return size; } }.routine; switch (self.*) { .Bool => |b| { + if (output.len < 1) return RuntimeError.OutOfBounds; output[0] = if (b == true) 1 else 0; return 1; }, .Int => |i| { switch (i.bit_count) { - 8 => output[0] = @bitCast(i.value.uint8), - 16 => @memcpy(output[0..2], std.mem.asBytes(&i.value.uint16)), - 32 => @memcpy(output[0..4], std.mem.asBytes(&i.value.uint32)), - 64 => @memcpy(output[0..8], std.mem.asBytes(&i.value.uint64)), + 8 => { + if (output.len < 1) return RuntimeError.OutOfBounds; + output[0] = @bitCast(i.value.uint8); + }, + 16 => { + if (output.len < 2) return RuntimeError.OutOfBounds; + @memcpy(output[0..2], std.mem.asBytes(&i.value.uint16)); + }, + 32 => { + if (output.len < 4) return RuntimeError.OutOfBounds; + @memcpy(output[0..4], std.mem.asBytes(&i.value.uint32)); + }, + 64 => { + if (output.len < 8) return RuntimeError.OutOfBounds; + @memcpy(output[0..8], std.mem.asBytes(&i.value.uint64)); + }, else => return RuntimeError.InvalidValueType, } return @divExact(i.bit_count, 8); }, .Float => |f| { switch (f.bit_count) { - 16 => @memcpy(output[0..2], std.mem.asBytes(&f.value.float16)), - 32 => @memcpy(output[0..4], std.mem.asBytes(&f.value.float32)), - 64 => @memcpy(output[0..8], std.mem.asBytes(&f.value.float64)), + 16 => { + if (output.len < 2) return RuntimeError.OutOfBounds; + @memcpy(output[0..2], std.mem.asBytes(&f.value.float16)); + }, + 32 => { + if (output.len < 4) return RuntimeError.OutOfBounds; + @memcpy(output[0..4], std.mem.asBytes(&f.value.float32)); + }, + 64 => { + if (output.len < 8) return RuntimeError.OutOfBounds; + @memcpy(output[0..8], std.mem.asBytes(&f.value.float64)); + }, else => return RuntimeError.InvalidValueType, } return @divExact(f.bit_count, 8); @@ -400,6 +427,7 @@ pub const Value = union(Type) { var end_offset: usize = 0; for (s.values, 0..) |v, i| { const member_offset: usize = @intCast(s.offsets[i] orelse end_offset); + if (member_offset > output.len) return RuntimeError.OutOfBounds; const read_size = try v.read(output[member_offset..]); end_offset = @max(end_offset, member_offset + read_size); } @@ -415,40 +443,54 @@ pub const Value = union(Type) { const vecRoutine = struct { inline fn routine(comptime T: type, vec: *T, in: []const u8) RuntimeError!usize { const size = @typeInfo(T).vector.len * 4; - if (size >= in.len) { - const range = @typeInfo(T).vector.len; - inline for (0..range) |i| { - const start = i * 4; - const end = (i + 1) * 4; - if (start >= in.len or end > in.len) return i * 4; - @memcpy(std.mem.asBytes(&vec[i]), in[start..end]); - } - } - vec.* = std.mem.bytesToValue(T, in[0..]); + if (in.len < size) return RuntimeError.OutOfBounds; + vec.* = std.mem.bytesToValue(T, in[0..size]); return size; } }.routine; switch (self.*) { .Bool => |*b| { + if (input.len < 1) return RuntimeError.OutOfBounds; b.* = if (input[0] != 0) true else false; return 1; }, .Int => |*i| { switch (i.bit_count) { - 8 => i.value.uint8 = @bitCast(input[0]), - 16 => @memcpy(std.mem.asBytes(&i.value.uint16), input[0..2]), - 32 => @memcpy(std.mem.asBytes(&i.value.uint32), input[0..4]), - 64 => @memcpy(std.mem.asBytes(&i.value.uint64), input[0..8]), + 8 => { + if (input.len < 1) return RuntimeError.OutOfBounds; + i.value.uint8 = @bitCast(input[0]); + }, + 16 => { + if (input.len < 2) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&i.value.uint16), input[0..2]); + }, + 32 => { + if (input.len < 4) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&i.value.uint32), input[0..4]); + }, + 64 => { + if (input.len < 8) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&i.value.uint64), input[0..8]); + }, else => return RuntimeError.InvalidValueType, } return @divExact(i.bit_count, 8); }, .Float => |*f| { switch (f.bit_count) { - 16 => @memcpy(std.mem.asBytes(&f.value.float16), input[0..2]), - 32 => @memcpy(std.mem.asBytes(&f.value.float32), input[0..4]), - 64 => @memcpy(std.mem.asBytes(&f.value.float64), input[0..8]), + 16 => { + if (input.len < 2) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&f.value.float16), input[0..2]); + }, + 32 => { + if (input.len < 4) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&f.value.float32), input[0..4]); + }, + 64 => { + if (input.len < 8) return RuntimeError.OutOfBounds; + @memcpy(std.mem.asBytes(&f.value.float64), input[0..8]); + }, else => return RuntimeError.InvalidValueType, } return @divExact(f.bit_count, 8); @@ -466,7 +508,25 @@ pub const Value = union(Type) { .Vector3u32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input), .Vector2u32 => |*vec| return vecRoutine(@TypeOf(vec.*), vec, input), - .Vector, .Matrix => |*values| { + .Vector => |*values| { + var offset: usize = 0; + for (values.*) |*v| { + offset += try v.write(input[offset..]); + } + return offset; + }, + .Matrix => |*values| { + const matrix_stride = 16; + if (values.len != 0) { + const column_size = try values.*[0].getPlainMemorySize(); + if (column_size < matrix_stride and input.len >= (values.len - 1) * matrix_stride + column_size) { + for (values.*, 0..) |*v, column| { + _ = try v.write(input[column * matrix_stride ..]); + } + return (values.len - 1) * matrix_stride + column_size; + } + } + var offset: usize = 0; for (values.*) |*v| { offset += try v.write(input[offset..]); @@ -485,24 +545,49 @@ pub const Value = union(Type) { var end_offset: usize = 0; for (s.values, 0..) |*v, i| { const member_offset: usize = @intCast(s.offsets[i] orelse end_offset); + if (member_offset > input.len) return RuntimeError.OutOfBounds; const write_size = try v.write(input[member_offset..]); end_offset = @max(end_offset, member_offset + write_size); } + if (end_offset > input.len) return RuntimeError.OutOfBounds; s.external_data = @constCast(input[0..end_offset]); return end_offset; }, .RuntimeArray => |*arr| arr.data = @constCast(input[0..]), - .Image => |*img| img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])), - .Sampler => |*sampler| sampler.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])), + .Image => |*img| { + if (input.len < @sizeOf(usize)) return RuntimeError.OutOfBounds; + img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..@sizeOf(usize)])); + }, + .Sampler => |*sampler| { + if (input.len < @sizeOf(usize)) return RuntimeError.OutOfBounds; + sampler.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[0..@sizeOf(usize)])); + }, .SampledImage => |*img| { - img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..])); - img.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[@sizeOf(usize)..])); + if (input.len < @sizeOf(usize) * 2) return RuntimeError.OutOfBounds; + img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..@sizeOf(usize)])); + img.driver_sampler = @ptrFromInt(std.mem.bytesToValue(usize, input[@sizeOf(usize)..][0..@sizeOf(usize)])); }, else => return RuntimeError.InvalidValueType, } return 0; } + pub fn clearExternalData(self: *Self) void { + switch (self.*) { + .Vector, .Matrix => |values| { + for (values) |*value| value.clearExternalData(); + }, + .Array => |*arr| { + for (arr.values) |*value| value.clearExternalData(); + }, + .Structure => |*s| { + s.external_data = null; + for (s.values) |*value| value.clearExternalData(); + }, + else => {}, + } + } + pub fn getPlainMemorySize(self: *const Self) RuntimeError!usize { return switch (self.*) { .Bool => 1, @@ -568,6 +653,7 @@ pub const Value = union(Type) { } pub fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void { + _ = allocator; switch (self.*) { .Pointer => |*p| { if (p.uniform_slice_window) |window| { @@ -589,20 +675,8 @@ pub const Value = union(Type) { }, } - if (p.uniform_backing_value) |backing| { - backing.deinit(allocator); - allocator.destroy(backing); - p.uniform_backing_value = null; - } - p.uniform_slice_window = null; } - - if (p.uniform_backing_value) |backing| { - backing.deinit(allocator); - allocator.destroy(backing); - p.uniform_backing_value = null; - } }, else => {}, } @@ -623,6 +697,16 @@ pub const Value = union(Type) { allocator.free(s.values); allocator.free(s.offsets); }, + .Pointer => |*p| { + if (p.owns_uniform_backing_value) { + if (p.uniform_backing_value) |backing| { + backing.deinit(allocator); + allocator.destroy(backing); + } + } + p.uniform_backing_value = null; + p.owns_uniform_backing_value = false; + }, else => {}, } } diff --git a/src/opcodes.zig b/src/opcodes.zig index 5e90d87..25b1798 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -1625,6 +1625,7 @@ fn opImageTexelPointer(allocator: std.mem.Allocator, word_count: SpvWord, rt: *R if (rt.results[result_id].variant) |*variant| { switch (variant.*) { .AccessChain => |*a| { + try a.value.flushPtr(allocator); allocator.free(a.indexes); a.value.deinit(allocator); }, @@ -1648,6 +1649,7 @@ fn opImageTexelPointer(allocator: std.mem.Allocator, word_count: SpvWord, rt: *R .z = z, }, .uniform_backing_value = backing, + .owns_uniform_backing_value = true, } }, }, }; @@ -1816,7 +1818,12 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic: fn applySIMDVectorf32(comptime N: usize, d: *@Vector(N, f32), l: *const Value, r: *const Value) RuntimeError!void { switch (Op) { .MatrixTimesVector => inline for (0..N) |i| { - d[i] = @reduce(.Add, l.Matrix[i].getVectorSpecialization(N, f32) * r.getVectorSpecialization(N, f32)); + const l_vec = l.Matrix[i].getVectorSpecialization(N, f32); + const r_vec = r.getVectorSpecialization(N, f32); + d[i] = 0; + inline for (0..N) |j| { + d[i] += l_vec[j] * r_vec[j]; + } }, else => try applyDirectSIMDVectorf32(N, d, l.getVectorSpecialization(N, f32), r), } @@ -2048,6 +2055,7 @@ fn setupAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runt if (rt.results[id].variant) |*variant| { switch (variant.*) { .AccessChain => |*a| { + try a.value.flushPtr(allocator); allocator.free(a.indexes); a.value.deinit(allocator); }, @@ -2285,11 +2293,13 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime var uniform_slice_window: ?[]u8 = null; var uniform_backing_value: ?*Value = null; + var owns_uniform_backing_value = false; if (std.meta.activeTag(value_ptr.*) == .Pointer) { const ptr = value_ptr.Pointer; uniform_slice_window = ptr.uniform_slice_window; uniform_backing_value = ptr.uniform_backing_value; + owns_uniform_backing_value = false; switch (ptr.ptr) { .common => |common| value_ptr = common, else => return RuntimeError.InvalidSpirV, @@ -2312,6 +2322,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime const ptr = value_ptr.Pointer; uniform_slice_window = ptr.uniform_slice_window; uniform_backing_value = ptr.uniform_backing_value; + owns_uniform_backing_value = false; switch (ptr.ptr) { .common => |common| value_ptr = common, else => return RuntimeError.InvalidSpirV, @@ -2365,13 +2376,14 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime allocator.destroy(backing); } - if (uniform_backing_value) |old_backing| { + if (owns_uniform_backing_value) if (uniform_backing_value) |old_backing| { old_backing.deinit(allocator); allocator.destroy(old_backing); - } + }; value_ptr = backing; uniform_backing_value = backing; + owns_uniform_backing_value = true; uniform_slice_window = arr.data[element_offset .. element_offset + arr.stride]; }, .Vector4f32 => |*v| switch (component_index) { @@ -2379,6 +2391,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .f32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(f32), @sizeOf(f32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2387,6 +2400,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .f32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(f32), @sizeOf(f32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2395,6 +2409,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .f32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(f32), @sizeOf(f32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2403,6 +2418,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .i32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(i32), @sizeOf(i32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2411,6 +2427,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .i32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(i32), @sizeOf(i32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2419,6 +2436,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .i32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(i32), @sizeOf(i32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2427,6 +2445,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .u32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(u32), @sizeOf(u32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2435,6 +2454,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .u32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(u32), @sizeOf(u32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2443,6 +2463,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .u32_ptr = &v[idx] }, .uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, idx * @sizeOf(u32), @sizeOf(u32)), .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, } }, else => return RuntimeError.InvalidSpirV, }, @@ -2457,6 +2478,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime .ptr = .{ .common = value_ptr }, .uniform_slice_window = uniform_slice_window, .uniform_backing_value = uniform_backing_value, + .owns_uniform_backing_value = owns_uniform_backing_value, }, }; }, @@ -2795,7 +2817,8 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run return; } - var elem_value = try arr.createLocalValueFromIndex(alloc, results, elem_offset); + var elem_value = try arr.createLocalValueFromIndex(alloc, results, index); + defer elem_value.deinit(alloc); try insertAt(alloc, results, &elem_value, object_value, indices[1..]); _ = try elem_value.read(arr.data[elem_offset..]); },