adding missing opcodes, fixing runtime array pointer windows
This commit is contained in:
@@ -258,10 +258,25 @@ fn readIntegralLaneAsI32(src: *const Value, lane_index: usize) RuntimeError!i32
|
||||
const lane_bits = try src.resolveLaneBitWidth();
|
||||
const sign = try src.resolveSign();
|
||||
return switch (lane_bits) {
|
||||
inline 8, 16, 32, 64 => |bits| if (sign == .signed)
|
||||
@intCast(try Value.readLane(.SInt, bits, src, lane_index))
|
||||
else
|
||||
@intCast(try Value.readLane(.UInt, bits, src, lane_index)),
|
||||
inline 8, 16, 32, 64 => |bits| blk: {
|
||||
if (sign == .signed) {
|
||||
const value = try Value.readLane(.SInt, bits, src, lane_index);
|
||||
if (bits > 32) {
|
||||
break :blk std.math.cast(i32, value) orelse if (value < 0)
|
||||
std.math.minInt(i32)
|
||||
else
|
||||
std.math.maxInt(i32);
|
||||
}
|
||||
break :blk @intCast(value);
|
||||
} else {
|
||||
const value = try Value.readLane(.UInt, bits, src, lane_index);
|
||||
const bits32: u32 = if (bits > 32)
|
||||
std.math.cast(u32, value) orelse std.math.maxInt(u32)
|
||||
else
|
||||
@intCast(value);
|
||||
break :blk @bitCast(bits32);
|
||||
}
|
||||
},
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ pub const ReflectionInfos = struct {
|
||||
|
||||
needs_derivatives: bool,
|
||||
has_control_barriers: bool,
|
||||
has_atomics: bool,
|
||||
early_fragment_tests: bool,
|
||||
};
|
||||
|
||||
options: ModuleOptions,
|
||||
|
||||
@@ -482,7 +482,6 @@ pub fn getMemberCounts(self: *const Self) usize {
|
||||
|
||||
pub inline fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
if (self.variant) |*variant| switch (variant.*) {
|
||||
.Variable => |*v| try v.value.flushPtr(allocator),
|
||||
.AccessChain => |*a| {
|
||||
if (!std.mem.allEqual(u8, std.mem.asBytes(&a.value), 0xaa))
|
||||
try a.value.flushPtr(allocator);
|
||||
|
||||
+369
-38
@@ -47,6 +47,11 @@ pub const SpecializationEntry = struct {
|
||||
size: usize,
|
||||
};
|
||||
|
||||
pub const WorkgroupMemory = struct {
|
||||
result: SpvWord,
|
||||
bytes: []u8,
|
||||
};
|
||||
|
||||
pub const Derivative = struct {
|
||||
dx: Value,
|
||||
dy: Value,
|
||||
@@ -118,14 +123,16 @@ function_stack: std.ArrayList(Function),
|
||||
|
||||
current_label: ?SpvWord,
|
||||
previous_label: ?SpvWord,
|
||||
active_entry_point: ?SpvWord,
|
||||
|
||||
specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8),
|
||||
derivatives: std.AutoHashMapUnmanaged(SpvWord, Derivative),
|
||||
phi_values: std.AutoHashMapUnmanaged(SpvWord, Value),
|
||||
|
||||
image_api: ImageAPI,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI) RuntimeError!Self {
|
||||
return .{
|
||||
var self: Self = .{
|
||||
.mod = module,
|
||||
.it = module.it,
|
||||
.results = blk: {
|
||||
@@ -150,10 +157,15 @@ pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI)
|
||||
.function_stack = .empty,
|
||||
.current_label = null,
|
||||
.previous_label = null,
|
||||
.active_entry_point = null,
|
||||
.specialization_constants = .empty,
|
||||
.derivatives = .empty,
|
||||
.phi_values = .empty,
|
||||
.image_api = image_api,
|
||||
};
|
||||
errdefer self.deinit(allocator);
|
||||
try self.refreshValueLayouts();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn initFrom(allocator: std.mem.Allocator, other: *const Self, image_api: ImageAPI) RuntimeError!Self {
|
||||
@@ -180,13 +192,16 @@ pub fn initFrom(allocator: std.mem.Allocator, other: *const Self, image_api: Ima
|
||||
.function_stack = .empty,
|
||||
.current_label = null,
|
||||
.previous_label = null,
|
||||
.active_entry_point = other.active_entry_point,
|
||||
.specialization_constants = .empty,
|
||||
.derivatives = .empty,
|
||||
.phi_values = .empty,
|
||||
.image_api = image_api,
|
||||
};
|
||||
errdefer self.deinit(allocator);
|
||||
|
||||
try self.copySpecializationConstantsFrom(allocator, other);
|
||||
try self.refreshValueLayouts();
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -207,6 +222,9 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
|
||||
entry.value_ptr.deinit(allocator);
|
||||
}
|
||||
self.derivatives.deinit(allocator);
|
||||
|
||||
self.clearPhiValues(allocator);
|
||||
self.phi_values.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn addSpecializationInfo(self: *Self, allocator: std.mem.Allocator, entry: SpecializationEntry, data: []const u8) RuntimeError!void {
|
||||
@@ -225,6 +243,169 @@ pub fn copySpecializationConstantsFrom(self: *Self, allocator: std.mem.Allocator
|
||||
}
|
||||
}
|
||||
|
||||
fn applyValueLayout(results: []Result, value: *Value, type_word: SpvWord) RuntimeError!void {
|
||||
const resolved_type_word = results[type_word].resolveTypeWordOrNull() orelse type_word;
|
||||
const type_data = (try results[resolved_type_word].getConstVariant()).Type;
|
||||
|
||||
switch (value.*) {
|
||||
.Structure => |*structure| switch (type_data) {
|
||||
.Structure => |type_structure| {
|
||||
@memcpy(@constCast(structure.offsets), type_structure.members_offsets);
|
||||
@memcpy(@constCast(structure.matrix_strides), type_structure.members_matrix_strides);
|
||||
@memcpy(@constCast(structure.row_major), type_structure.members_row_major);
|
||||
for (structure.values, type_structure.members_type_word, 0..) |*member_value, member_type_word, member_index| {
|
||||
if (member_value.* == .RuntimeArray) {
|
||||
member_value.RuntimeArray.matrix_stride = type_structure.members_matrix_strides[member_index];
|
||||
member_value.RuntimeArray.row_major = type_structure.members_row_major[member_index];
|
||||
}
|
||||
try applyValueLayout(results, member_value, member_type_word);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.Array => |array| switch (type_data) {
|
||||
.Array => |type_array| for (array.values) |*element| {
|
||||
try applyValueLayout(results, element, type_array.components_type_word);
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.Matrix => |columns| switch (type_data) {
|
||||
.Matrix => |type_matrix| for (columns) |*column| {
|
||||
try applyValueLayout(results, column, type_matrix.column_type_word);
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
.Vector => |elements| switch (type_data) {
|
||||
.Vector => |type_vector| for (elements) |*element| {
|
||||
try applyValueLayout(results, element, type_vector.components_type_word);
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn refreshValueLayouts(self: *Self) RuntimeError!void {
|
||||
for (self.results) |*result| {
|
||||
if (result.variant) |*variant| switch (variant.*) {
|
||||
.Variable => |*v| switch (v.storage_class) {
|
||||
.StorageBuffer,
|
||||
.Uniform,
|
||||
.PushConstant,
|
||||
.Workgroup,
|
||||
=> try applyValueLayout(self.results, &v.value, v.type_word),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn typePlainMemorySize(self: *const Self, type_word: SpvWord) RuntimeError!usize {
|
||||
const resolved_word = self.results[type_word].resolveTypeWordOrNull() orelse type_word;
|
||||
const target_type = (try self.results[resolved_word].getConstVariant()).Type;
|
||||
|
||||
return switch (target_type) {
|
||||
.Array => |a| blk: {
|
||||
const stride: usize = if (a.stride != 0)
|
||||
@intCast(a.stride)
|
||||
else
|
||||
try self.typePlainMemorySize(a.components_type_word);
|
||||
break :blk stride * @as(usize, @intCast(a.member_count));
|
||||
},
|
||||
.RuntimeArray => return RuntimeError.InvalidValueType,
|
||||
.Structure => |s| blk: {
|
||||
var size: usize = 0;
|
||||
for (s.members_type_word, 0..) |member_type_word, i| {
|
||||
const member_offset: usize = @intCast(s.members_offsets[i] orelse size);
|
||||
size = @max(size, member_offset + try self.typePlainMemorySize(member_type_word));
|
||||
}
|
||||
break :blk size;
|
||||
},
|
||||
else => target_type.getSize(self.results),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createWorkgroupMemory(self: *Self, allocator: std.mem.Allocator) RuntimeError![]WorkgroupMemory {
|
||||
var count: usize = 0;
|
||||
for (self.results) |result| {
|
||||
if (result.variant) |variant| switch (variant) {
|
||||
.Variable => |v| {
|
||||
if (v.storage_class == .Workgroup) count += 1;
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
const memories = allocator.alloc(WorkgroupMemory, count) catch return RuntimeError.OutOfMemory;
|
||||
errdefer allocator.free(memories);
|
||||
|
||||
var index: usize = 0;
|
||||
for (self.results, 0..) |result, result_id| {
|
||||
if (result.variant) |variant| switch (variant) {
|
||||
.Variable => |v| {
|
||||
if (v.storage_class == .Workgroup) {
|
||||
const size = try self.typePlainMemorySize(v.type_word);
|
||||
const bytes = allocator.alloc(u8, size) catch return RuntimeError.OutOfMemory;
|
||||
@memset(bytes, 0);
|
||||
memories[index] = .{
|
||||
.result = @intCast(result_id),
|
||||
.bytes = bytes,
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
return memories;
|
||||
}
|
||||
|
||||
pub fn destroyWorkgroupMemory(_: *Self, allocator: std.mem.Allocator, memories: []WorkgroupMemory) void {
|
||||
for (memories) |memory| allocator.free(memory.bytes);
|
||||
allocator.free(memories);
|
||||
}
|
||||
|
||||
pub fn bindWorkgroupMemory(self: *Self, memories: []const WorkgroupMemory) RuntimeError!void {
|
||||
for (memories) |memory| {
|
||||
_ = try (try self.results[memory.result].getValue()).write(memory.bytes);
|
||||
}
|
||||
}
|
||||
|
||||
fn clearPhiValues(self: *Self, allocator: std.mem.Allocator) void {
|
||||
var it = self.phi_values.iterator();
|
||||
while (it.next()) |entry| {
|
||||
entry.value_ptr.deinit(allocator);
|
||||
}
|
||||
self.phi_values.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
pub fn snapshotPhiValues(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
self.clearPhiValues(allocator);
|
||||
|
||||
for (self.results, 0..) |*result, result_id| {
|
||||
const value = switch (result.variant orelse continue) {
|
||||
.Constant => |*constant| &constant.value,
|
||||
.FunctionParameter => |*parameter| parameter.value_ptr orelse continue,
|
||||
else => continue,
|
||||
};
|
||||
if (std.meta.activeTag(value.*) == .Pointer) continue;
|
||||
const snapshot = try value.dupe(allocator);
|
||||
const gop = self.phi_values.getOrPut(allocator, @intCast(result_id)) catch {
|
||||
var tmp = snapshot;
|
||||
tmp.deinit(allocator);
|
||||
return RuntimeError.OutOfMemory;
|
||||
};
|
||||
if (gop.found_existing) gop.value_ptr.deinit(allocator);
|
||||
gop.value_ptr.* = snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPhiValueSnapshot(self: *Self, id: SpvWord) ?*const Value {
|
||||
return self.phi_values.getPtr(id);
|
||||
}
|
||||
|
||||
pub fn setDerivative(self: *Self, allocator: std.mem.Allocator, result: SpvWord, dx: *const Value, dy: *const Value) RuntimeError!void {
|
||||
const derivative: Derivative = .{
|
||||
.dx = try dx.dupe(allocator),
|
||||
@@ -283,33 +464,70 @@ pub fn copyDerivative(self: *Self, allocator: std.mem.Allocator, dst: SpvWord, s
|
||||
|
||||
pub fn applySpecializationLayout(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
self.reset();
|
||||
try self.applySpecializationConstants(allocator);
|
||||
try self.applySpecializationDependentLayout(allocator);
|
||||
}
|
||||
|
||||
pub fn applySpecializationInvocationLayout(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
self.reset();
|
||||
if (self.specialization_constants.count() != 0)
|
||||
try self.applySpecializationConstants(allocator);
|
||||
try self.applySpecializationDependentLayout(allocator);
|
||||
}
|
||||
|
||||
fn applySpecializationConstants(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
try self.pass(allocator, .initMany(&.{
|
||||
.SpecConstantTrue,
|
||||
.SpecConstantFalse,
|
||||
.SpecConstantComposite,
|
||||
.ConstantNull,
|
||||
.SpecConstant,
|
||||
.SpecConstantOp,
|
||||
}));
|
||||
}
|
||||
|
||||
fn applySpecializationDependentLayout(self: *Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
try self.pass(allocator, .initMany(&.{
|
||||
.TypeArray,
|
||||
.Variable,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
|
||||
if (self.active_entry_point) |entry_point| {
|
||||
if (entryPointNameMatches(self.mod.entry_points.items[entry_point].name, name))
|
||||
return entry_point;
|
||||
}
|
||||
|
||||
for (self.mod.entry_points.items, 0..) |entry_point, i| {
|
||||
if (blk: {
|
||||
// Not using std.mem.eql as entry point names may have longer size than their content
|
||||
for (0..@min(name.len, entry_point.name.len)) |j| {
|
||||
if (name[j] != entry_point.name[j])
|
||||
break :blk false;
|
||||
}
|
||||
if (entry_point.name.len != name.len and entry_point.name[name.len] != 0)
|
||||
break :blk false;
|
||||
break :blk true;
|
||||
}) return @intCast(i);
|
||||
if (entryPointNameMatches(entry_point.name, name)) return @intCast(i);
|
||||
}
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
pub fn getEntryPointByNameAndExecutionModel(self: *const Self, name: []const u8, execution_model: spv.SpvExecutionModel) RuntimeError!SpvWord {
|
||||
for (self.mod.entry_points.items, 0..) |entry_point, i| {
|
||||
if (entry_point.exec_model == execution_model and entryPointNameMatches(entry_point.name, name))
|
||||
return @intCast(i);
|
||||
}
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
fn entryPointNameMatches(entry_point_name: []const u8, name: []const u8) bool {
|
||||
// Not using std.mem.eql as entry point names may have longer size than their content.
|
||||
for (0..@min(name.len, entry_point_name.len)) |j| {
|
||||
if (name[j] != entry_point_name[j])
|
||||
return false;
|
||||
}
|
||||
return entry_point_name.len == name.len or entry_point_name[name.len] == 0;
|
||||
}
|
||||
|
||||
pub fn selectEntryPoint(self: *Self, entry_point_index: SpvWord) RuntimeError!void {
|
||||
if (entry_point_index >= self.mod.entry_points.items.len)
|
||||
return RuntimeError.InvalidEntryPoint;
|
||||
self.active_entry_point = entry_point_index;
|
||||
}
|
||||
|
||||
pub fn getResultByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
|
||||
for (self.results, 0..) |result, i| {
|
||||
if (result.name) |result_name| {
|
||||
@@ -332,17 +550,76 @@ pub inline fn getResultByLocation(self: *const Self, location: SpvWord, kind: Lo
|
||||
}
|
||||
|
||||
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 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 component < 4 and self.mod.output_locations[location][component] != 0) {
|
||||
return self.mod.output_locations[location][component];
|
||||
},
|
||||
const locations = switch (kind) {
|
||||
.input => &self.mod.input_locations,
|
||||
.output => &self.mod.output_locations,
|
||||
};
|
||||
if (location >= locations.len or component >= 4)
|
||||
return RuntimeError.NotFound;
|
||||
|
||||
const result = locations[location][component];
|
||||
if (result != 0 and self.resultIsInActiveInterface(result))
|
||||
return result;
|
||||
|
||||
if (self.active_entry_point) |entry_point_index| {
|
||||
const entry_point = self.mod.entry_points.items[entry_point_index];
|
||||
for (entry_point.globals) |global| {
|
||||
if (global >= self.results.len)
|
||||
continue;
|
||||
|
||||
const variant = self.results[global].variant orelse continue;
|
||||
const variable = switch (variant) {
|
||||
.Variable => |v| v,
|
||||
else => continue,
|
||||
};
|
||||
const storage_class_matches = switch (kind) {
|
||||
.input => variable.storage_class == .Input,
|
||||
.output => variable.storage_class == .Output,
|
||||
};
|
||||
if (!storage_class_matches)
|
||||
continue;
|
||||
|
||||
for (self.results[global].decorations.items) |decoration| {
|
||||
if (decoration.rtype == .Location and
|
||||
decoration.literal_1 == location and
|
||||
self.resultComponent(global) == component)
|
||||
{
|
||||
return global;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
|
||||
fn resultIsInActiveInterface(self: *const Self, result: SpvWord) bool {
|
||||
const entry_point_index = self.active_entry_point orelse return true;
|
||||
const global = self.resultGlobal(result) orelse return false;
|
||||
return std.mem.indexOfScalar(SpvWord, self.mod.entry_points.items[entry_point_index].globals, global) != null;
|
||||
}
|
||||
|
||||
fn resultGlobal(self: *const Self, result: SpvWord) ?SpvWord {
|
||||
if (result >= self.results.len)
|
||||
return null;
|
||||
|
||||
const variant = self.results[result].variant orelse return result;
|
||||
return switch (variant) {
|
||||
.AccessChain => |access_chain| access_chain.base,
|
||||
else => result,
|
||||
};
|
||||
}
|
||||
|
||||
fn resultComponent(self: *const Self, result: SpvWord) SpvWord {
|
||||
if (result >= self.results.len)
|
||||
return 0;
|
||||
|
||||
for (self.results[result].decorations.items) |decoration| {
|
||||
if (decoration.rtype == .Component)
|
||||
return decoration.literal_1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn getResultPrimitiveType(self: *const Self, result: SpvWord) RuntimeError!PrimitiveType {
|
||||
if (result >= self.results.len)
|
||||
return RuntimeError.OutOfBounds;
|
||||
@@ -354,6 +631,7 @@ pub fn getWorkgroupSize(self: *Self, allocator: std.mem.Allocator) RuntimeError!
|
||||
.SpecConstantTrue,
|
||||
.SpecConstantFalse,
|
||||
.SpecConstantComposite,
|
||||
.ConstantNull,
|
||||
.SpecConstant,
|
||||
.SpecConstantOp,
|
||||
}));
|
||||
@@ -410,13 +688,8 @@ pub fn beginEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_in
|
||||
return RuntimeError.InvalidEntryPoint;
|
||||
|
||||
// Spec constants pass
|
||||
try self.pass(allocator, .initMany(&.{
|
||||
.SpecConstantTrue,
|
||||
.SpecConstantFalse,
|
||||
.SpecConstantComposite,
|
||||
.SpecConstant,
|
||||
.SpecConstantOp,
|
||||
}));
|
||||
if (self.specialization_constants.count() != 0)
|
||||
try self.applySpecializationConstants(allocator);
|
||||
|
||||
{
|
||||
const entry_point_desc = &self.mod.entry_points.items[entry_point_index];
|
||||
@@ -681,20 +954,22 @@ 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] != 0 and
|
||||
self.results[self.mod.input_locations[location][0]].variant != null)
|
||||
{
|
||||
const result = self.mod.input_locations[location][0];
|
||||
if (self.getResultByLocationComponent(location, 0, .input)) |result| {
|
||||
if (self.results[result].variant == null)
|
||||
return RuntimeError.NotFound;
|
||||
|
||||
const value = try self.results[result].getConstValue();
|
||||
switch (value.*) {
|
||||
.Array => |arr| {
|
||||
if (arr.values.len == 0) return RuntimeError.OutOfBounds;
|
||||
return .{ .result = result, .array_element = 0 };
|
||||
return .{ .result = result };
|
||||
},
|
||||
.Matrix => return .{ .result = result, .matrix_column = 0 },
|
||||
else => return .{ .result = result },
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
RuntimeError.NotFound => {},
|
||||
else => return err,
|
||||
}
|
||||
|
||||
for (self.results, 0..) |*result, id| {
|
||||
@@ -736,11 +1011,10 @@ fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError
|
||||
while (base_location > 0) {
|
||||
base_location -= 1;
|
||||
|
||||
const result = if (base_location < self.mod.input_locations.len)
|
||||
self.mod.input_locations[base_location][0]
|
||||
else
|
||||
0;
|
||||
if (result == 0) continue;
|
||||
const result = self.getResultByLocationComponent(base_location, 0, .input) catch |err| switch (err) {
|
||||
RuntimeError.NotFound => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const location_offset: usize = @intCast(location - base_location);
|
||||
const value = try self.results[result].getConstValue();
|
||||
@@ -830,7 +1104,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| {
|
||||
if (self.getBuiltinResult(builtin)) |result| {
|
||||
try self.readResultValue(output, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
@@ -865,13 +1139,34 @@ pub fn writeInputLocation(self: *const Self, input: []const u8, location: SpvWor
|
||||
}
|
||||
|
||||
pub fn writeBuiltIn(self: *const Self, allocator: std.mem.Allocator, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
|
||||
if (self.mod.builtins.get(builtin)) |result| {
|
||||
if (self.getBuiltinResult(builtin)) |result| {
|
||||
try self.writeResultValue(allocator, input, result);
|
||||
} else {
|
||||
return RuntimeError.NotFound;
|
||||
}
|
||||
}
|
||||
|
||||
fn getBuiltinResult(self: *const Self, builtin: spv.SpvBuiltIn) ?SpvWord {
|
||||
if (self.mod.builtins.get(builtin)) |result| {
|
||||
if (self.resultIsInActiveInterface(result))
|
||||
return result;
|
||||
}
|
||||
|
||||
const entry_point_index = self.active_entry_point orelse return null;
|
||||
const entry_point = self.mod.entry_points.items[entry_point_index];
|
||||
for (entry_point.globals) |global| {
|
||||
if (global >= self.results.len)
|
||||
continue;
|
||||
|
||||
for (self.results[global].decorations.items) |decoration| {
|
||||
if (decoration.rtype == .BuiltIn and decoration.literal_1 == @intFromEnum(builtin))
|
||||
return global;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn flushDescriptorSets(self: *const Self, allocator: std.mem.Allocator) RuntimeError!void {
|
||||
for (self.results) |*result| {
|
||||
try result.flushPtr(allocator);
|
||||
@@ -902,12 +1197,48 @@ pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.S
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn hasResultOrMemberDecoration(self: *const Self, result: SpvWord, decoration: spv.SpvDecoration) bool {
|
||||
if (self.hasResultDecoration(result, decoration))
|
||||
return true;
|
||||
|
||||
if (result >= self.results.len)
|
||||
return false;
|
||||
|
||||
const type_word = switch ((self.results[result].variant orelse return false)) {
|
||||
.Variable => |variable| variable.type_word,
|
||||
else => return false,
|
||||
};
|
||||
const target_type_word = switch ((self.results[type_word].variant orelse return false)) {
|
||||
.Type => |t| switch (t) {
|
||||
.Pointer => |ptr| ptr.target,
|
||||
else => type_word,
|
||||
},
|
||||
else => return false,
|
||||
};
|
||||
const target_type = self.results[target_type_word].variant orelse return false;
|
||||
switch (target_type) {
|
||||
.Type => |t| switch (t) {
|
||||
.Structure => {
|
||||
for (self.results[target_type_word].decorations.items) |member_decoration| {
|
||||
if (member_decoration.rtype == decoration)
|
||||
return true;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void {
|
||||
var derivatives = self.derivatives.iterator();
|
||||
while (derivatives.next()) |entry| {
|
||||
entry.value_ptr.deinit(allocator);
|
||||
}
|
||||
self.derivatives.clearRetainingCapacity();
|
||||
self.clearPhiValues(allocator);
|
||||
|
||||
for (self.results) |*result| {
|
||||
if (result.variant) |*variant| {
|
||||
|
||||
+90
-33
@@ -264,19 +264,6 @@ pub const Value = union(Type) {
|
||||
break :blk self;
|
||||
},
|
||||
.Array => |a| blk: {
|
||||
// If an array is in externally visible storage we treat it as a runtime array
|
||||
if (is_externally_visible) {
|
||||
break :blk .{
|
||||
.RuntimeArray = .{
|
||||
.type_word = a.components_type_word,
|
||||
.stride = a.stride,
|
||||
.data = &.{},
|
||||
.matrix_stride = null,
|
||||
.row_major = false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self: Self = .{
|
||||
.Array = .{
|
||||
.stride = a.stride,
|
||||
@@ -389,6 +376,17 @@ pub const Value = union(Type) {
|
||||
};
|
||||
},
|
||||
},
|
||||
.Pointer => |p| .{
|
||||
.Pointer = .{
|
||||
.ptr = p.ptr,
|
||||
.image_texel = p.image_texel,
|
||||
.uniform_slice_window = p.uniform_slice_window,
|
||||
.uniform_backing_value = p.uniform_backing_value,
|
||||
.owns_uniform_backing_value = false,
|
||||
.matrix_stride = p.matrix_stride,
|
||||
.matrix_row_major = p.matrix_row_major,
|
||||
},
|
||||
},
|
||||
else => self.*,
|
||||
};
|
||||
}
|
||||
@@ -479,7 +477,7 @@ pub const Value = union(Type) {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |v| {
|
||||
_ = try v.read(output[offset..]);
|
||||
offset += arr.stride;
|
||||
offset += if (arr.stride != 0) arr.stride else try v.getPlainMemorySize();
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
@@ -488,10 +486,15 @@ pub const Value = union(Type) {
|
||||
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 member_output = if (v == .RuntimeArray and i + 1 < s.values.len and s.offsets[i + 1] != null) blk: {
|
||||
const next_offset: usize = @intCast(s.offsets[i + 1].?);
|
||||
if (next_offset < member_offset or next_offset > output.len) return RuntimeError.OutOfBounds;
|
||||
break :blk output[member_offset..next_offset];
|
||||
} else output[member_offset..];
|
||||
const read_size = if (s.matrix_strides[i]) |matrix_stride|
|
||||
try v.readWithMatrixLayout(output[member_offset..], matrix_stride, s.row_major[i])
|
||||
try v.readWithMatrixLayout(member_output, matrix_stride, s.row_major[i])
|
||||
else
|
||||
try v.read(output[member_offset..]);
|
||||
try v.read(member_output);
|
||||
end_offset = @max(end_offset, member_offset + read_size);
|
||||
}
|
||||
return end_offset;
|
||||
@@ -624,7 +627,7 @@ pub const Value = union(Type) {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |*value| {
|
||||
_ = try value.readWithMatrixLayout(output[offset..], matrix_stride, row_major);
|
||||
offset += arr.stride;
|
||||
offset += if (arr.stride != 0) arr.stride else try value.getPlainMemorySize();
|
||||
}
|
||||
break :blk offset;
|
||||
},
|
||||
@@ -671,7 +674,7 @@ pub const Value = union(Type) {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |*value| {
|
||||
_ = try value.writeWithMatrixLayout(input[offset..], matrix_stride, row_major);
|
||||
offset += arr.stride;
|
||||
offset += if (arr.stride != 0) arr.stride else try value.getPlainMemorySize();
|
||||
}
|
||||
break :blk offset;
|
||||
},
|
||||
@@ -771,7 +774,7 @@ pub const Value = union(Type) {
|
||||
var offset: usize = 0;
|
||||
for (arr.values) |*v| {
|
||||
_ = try v.write(input[offset..]);
|
||||
offset += arr.stride;
|
||||
offset += if (arr.stride != 0) arr.stride else try v.getPlainMemorySize();
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
@@ -780,17 +783,25 @@ pub const Value = union(Type) {
|
||||
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 member_input = if (v.* == .RuntimeArray and i + 1 < s.values.len and s.offsets[i + 1] != null) blk: {
|
||||
const next_offset: usize = @intCast(s.offsets[i + 1].?);
|
||||
if (next_offset < member_offset or next_offset > input.len) return RuntimeError.OutOfBounds;
|
||||
break :blk input[member_offset..next_offset];
|
||||
} else input[member_offset..];
|
||||
const write_size = if (s.matrix_strides[i]) |matrix_stride|
|
||||
try v.writeWithMatrixLayout(input[member_offset..], matrix_stride, s.row_major[i])
|
||||
try v.writeWithMatrixLayout(member_input, matrix_stride, s.row_major[i])
|
||||
else
|
||||
try v.write(input[member_offset..]);
|
||||
try v.write(member_input);
|
||||
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..]),
|
||||
.RuntimeArray => |*arr| {
|
||||
arr.data = @constCast(input[0..]);
|
||||
return input.len;
|
||||
},
|
||||
.Image => |*img| {
|
||||
if (input.len < @sizeOf(usize)) return RuntimeError.OutOfBounds;
|
||||
img.driver_image = @ptrFromInt(std.mem.bytesToValue(usize, input[0..@sizeOf(usize)]));
|
||||
@@ -840,7 +851,13 @@ pub const Value = union(Type) {
|
||||
}
|
||||
break :blk size;
|
||||
},
|
||||
.Array => |arr| arr.stride * arr.values.len,
|
||||
.Array => |arr| blk: {
|
||||
var size: usize = 0;
|
||||
for (arr.values) |v| {
|
||||
size += if (arr.stride != 0) arr.stride else try v.getPlainMemorySize();
|
||||
}
|
||||
break :blk size;
|
||||
},
|
||||
.Structure => |s| blk: {
|
||||
var size: usize = 0;
|
||||
for (s.values, 0..) |v, i| {
|
||||
@@ -959,14 +976,54 @@ pub const Value = union(Type) {
|
||||
}
|
||||
}
|
||||
|
||||
fn readScalarLane(comptime T: PrimitiveType, comptime bits: u32, v: *const Value) RuntimeError!getPrimitiveFieldType(T, bits) {
|
||||
const TT = getPrimitiveFieldType(T, bits);
|
||||
|
||||
return switch (v.*) {
|
||||
.Bool => |b| blk: {
|
||||
if (T != .Bool or bits != 8) return RuntimeError.InvalidSpirV;
|
||||
break :blk @as(TT, b);
|
||||
},
|
||||
.Int => |i| blk: {
|
||||
if (i.bit_count != bits) return RuntimeError.InvalidSpirV;
|
||||
break :blk switch (T) {
|
||||
.SInt => switch (bits) {
|
||||
8 => @as(TT, if (i.is_signed) i.value.sint8 else @bitCast(i.value.uint8)),
|
||||
16 => @as(TT, if (i.is_signed) i.value.sint16 else @bitCast(i.value.uint16)),
|
||||
32 => @as(TT, if (i.is_signed) i.value.sint32 else @bitCast(i.value.uint32)),
|
||||
64 => @as(TT, if (i.is_signed) i.value.sint64 else @bitCast(i.value.uint64)),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.UInt => switch (bits) {
|
||||
8 => @as(TT, if (i.is_signed) @bitCast(i.value.sint8) else i.value.uint8),
|
||||
16 => @as(TT, if (i.is_signed) @bitCast(i.value.sint16) else i.value.uint16),
|
||||
32 => @as(TT, if (i.is_signed) @bitCast(i.value.sint32) else i.value.uint32),
|
||||
64 => @as(TT, if (i.is_signed) @bitCast(i.value.sint64) else i.value.uint64),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
},
|
||||
.Float => |f| blk: {
|
||||
if (T != .Float or f.bit_count != bits) return RuntimeError.InvalidSpirV;
|
||||
break :blk switch (bits) {
|
||||
16 => @as(TT, f.value.float16),
|
||||
32 => @as(TT, f.value.float32),
|
||||
64 => @as(TT, f.value.float64),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
pub inline fn readLane(comptime T: PrimitiveType, comptime bits: u32, v: *const Value, lane_index: usize) RuntimeError!getPrimitiveFieldType(T, bits) {
|
||||
const TT = getPrimitiveFieldType(T, bits);
|
||||
|
||||
return switch (v.*) {
|
||||
.Int => (try getPrimitiveField(T, bits, @constCast(v))).*,
|
||||
.Float => (try getPrimitiveField(T, bits, @constCast(v))).*,
|
||||
.Bool, .Int, .Float => try readScalarLane(T, bits, v),
|
||||
|
||||
.Vector => |lanes| (try getPrimitiveField(T, bits, &lanes[lane_index])).*,
|
||||
.Vector => |lanes| try readScalarLane(T, bits, &lanes[lane_index]),
|
||||
|
||||
.Vector2f32 => |*vec| switch (lane_index) {
|
||||
inline 0...1 => |i| blk: {
|
||||
@@ -1001,7 +1058,7 @@ pub const Value = union(Type) {
|
||||
|
||||
.Vector2i32 => |*vec| switch (lane_index) {
|
||||
inline 0...1 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1011,7 +1068,7 @@ pub const Value = union(Type) {
|
||||
},
|
||||
.Vector3i32 => |*vec| switch (lane_index) {
|
||||
inline 0...2 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1021,7 +1078,7 @@ pub const Value = union(Type) {
|
||||
},
|
||||
.Vector4i32 => |*vec| switch (lane_index) {
|
||||
inline 0...3 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1032,7 +1089,7 @@ pub const Value = union(Type) {
|
||||
|
||||
.Vector2u32 => |*vec| switch (lane_index) {
|
||||
inline 0...1 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1042,7 +1099,7 @@ pub const Value = union(Type) {
|
||||
},
|
||||
.Vector3u32 => |*vec| switch (lane_index) {
|
||||
inline 0...2 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1052,7 +1109,7 @@ pub const Value = union(Type) {
|
||||
},
|
||||
.Vector4u32 => |*vec| switch (lane_index) {
|
||||
inline 0...3 => |i| blk: {
|
||||
if (bits == 32) {
|
||||
if (comptime (T == .SInt or T == .UInt) and bits == 32) {
|
||||
break :blk @as(TT, @bitCast(vec[i]));
|
||||
} else {
|
||||
return RuntimeError.InvalidSpirV;
|
||||
@@ -1185,7 +1242,7 @@ pub const Value = union(Type) {
|
||||
else => unreachable,
|
||||
},
|
||||
} },
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
.Bool => .{ .Bool = v },
|
||||
};
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
|
||||
+374
-136
@@ -114,21 +114,22 @@ pub const SetupDispatcher = block: {
|
||||
.AccessChain = setupAccessChain,
|
||||
.All = autoSetupConstant,
|
||||
.Any = autoSetupConstant,
|
||||
.AtomicAnd = autoSetupConstant,
|
||||
.AtomicCompareExchange = autoSetupConstant,
|
||||
.AtomicExchange = autoSetupConstant,
|
||||
.AtomicIAdd = autoSetupConstant,
|
||||
.AtomicIDecrement = autoSetupConstant,
|
||||
.AtomicIIncrement = autoSetupConstant,
|
||||
.AtomicISub = autoSetupConstant,
|
||||
.AtomicLoad = autoSetupConstant,
|
||||
.AtomicOr = autoSetupConstant,
|
||||
.AtomicSMax = autoSetupConstant,
|
||||
.AtomicSMin = autoSetupConstant,
|
||||
.AtomicStore = autoSetupConstant,
|
||||
.AtomicUMax = autoSetupConstant,
|
||||
.AtomicUMin = autoSetupConstant,
|
||||
.AtomicXor = autoSetupConstant,
|
||||
.ArrayLength = autoSetupConstant,
|
||||
.AtomicAnd = setupAtomic,
|
||||
.AtomicCompareExchange = setupAtomic,
|
||||
.AtomicExchange = setupAtomic,
|
||||
.AtomicIAdd = setupAtomic,
|
||||
.AtomicIDecrement = setupAtomic,
|
||||
.AtomicIIncrement = setupAtomic,
|
||||
.AtomicISub = setupAtomic,
|
||||
.AtomicLoad = setupAtomic,
|
||||
.AtomicOr = setupAtomic,
|
||||
.AtomicSMax = setupAtomic,
|
||||
.AtomicSMin = setupAtomic,
|
||||
.AtomicStore = setupAtomicStore,
|
||||
.AtomicUMax = setupAtomic,
|
||||
.AtomicUMin = setupAtomic,
|
||||
.AtomicXor = setupAtomic,
|
||||
.BitCount = autoSetupConstant,
|
||||
.BitFieldInsert = autoSetupConstant,
|
||||
.BitFieldSExtract = autoSetupConstant,
|
||||
@@ -144,14 +145,16 @@ pub const SetupDispatcher = block: {
|
||||
.Constant = opConstant,
|
||||
.ConstantComposite = opConstantComposite,
|
||||
.ConstantFalse = opConstantFalse,
|
||||
.ConstantNull = opConstantNull,
|
||||
.ConstantTrue = opConstantTrue,
|
||||
.ControlBarrier = opControlBarrier,
|
||||
.ControlBarrier = opControlBarrierSetup,
|
||||
.ConvertFToS = autoSetupConstant,
|
||||
.ConvertFToU = autoSetupConstant,
|
||||
.ConvertPtrToU = autoSetupConstant,
|
||||
.ConvertSToF = autoSetupConstant,
|
||||
.ConvertUToF = autoSetupConstant,
|
||||
.ConvertUToPtr = autoSetupConstant,
|
||||
.CopyObject = autoSetupConstant,
|
||||
.DPdx = opDerivativeSetup,
|
||||
.DPdxCoarse = opDerivativeSetup,
|
||||
.DPdxFine = opDerivativeSetup,
|
||||
@@ -321,6 +324,7 @@ pub fn initRuntimeDispatcher() void {
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.AtomicUMax)] = AtomicEngine(.MaxUnsigned).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.AtomicUMin)] = AtomicEngine(.MinUnsigned).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.AtomicXor)] = AtomicEngine(.Xor).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ArrayLength)] = opArrayLength;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.BitCount)] = BitEngine(.UInt, .BitCount).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.BitFieldInsert)] = BitEngine(.UInt, .BitFieldInsert).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.BitFieldSExtract)] = BitEngine(.SInt, .BitFieldSExtract).op;
|
||||
@@ -335,11 +339,13 @@ pub fn initRuntimeDispatcher() void {
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.CompositeConstruct)] = opCompositeConstruct;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.CompositeExtract)] = opCompositeExtract;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.CompositeInsert)] = opCompositeInsert;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConstantNull)] = opConstantNull;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ControlBarrier)] = opControlBarrier;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertFToS)] = ConversionEngine(.Float, .SInt).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertFToU)] = ConversionEngine(.Float, .UInt).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertSToF)] = ConversionEngine(.SInt, .Float).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertUToF)] = ConversionEngine(.UInt, .Float).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.CopyObject)] = opCopyObject;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.CopyMemory)] = opCopyMemory;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdx)] = DerivativeEngine(.x).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdxCoarse)] = DerivativeEngine(.x).op;
|
||||
@@ -580,15 +586,17 @@ fn BitEngine(comptime T: PrimitiveType, comptime Op: BitOp) type {
|
||||
const bits: u32 = info.int.bits;
|
||||
const U = std.meta.Int(.unsigned, bits);
|
||||
|
||||
if (count == 0) return base;
|
||||
if (count == 0 or offset >= bits) return base;
|
||||
|
||||
const actual_count: u64 = @min(count, @as(u64, bits) - offset);
|
||||
|
||||
const base_u: U = @bitCast(base);
|
||||
const insert_u: U = @bitCast(insert);
|
||||
|
||||
const field_mask: U = if (count == bits)
|
||||
const field_mask: U = if (actual_count == bits)
|
||||
~@as(U, 0)
|
||||
else
|
||||
(@as(U, 1) << @intCast(count)) - 1;
|
||||
(@as(U, 1) << @intCast(actual_count)) - 1;
|
||||
|
||||
const shift: std.math.Log2Int(U) = @truncate(offset);
|
||||
|
||||
@@ -604,23 +612,26 @@ fn BitEngine(comptime T: PrimitiveType, comptime Op: BitOp) type {
|
||||
|
||||
const bits: u32 = info.int.bits;
|
||||
|
||||
if (count == 0) return @as(TT, 0);
|
||||
if (count == 0 or offset >= bits) return @as(TT, 0);
|
||||
|
||||
const actual_count: u64 = @min(count, @as(u64, bits) - offset);
|
||||
|
||||
const U = std.meta.Int(.unsigned, bits);
|
||||
const base_u: U = @bitCast(base);
|
||||
const shift: std.math.Log2Int(U) = @truncate(offset);
|
||||
|
||||
const field: U = if (count == bits)
|
||||
const field: U = if (actual_count == bits)
|
||||
base_u
|
||||
else
|
||||
(base_u >> @intCast(offset)) &
|
||||
((@as(U, 1) << @intCast(count)) - 1);
|
||||
(base_u >> shift) &
|
||||
((@as(U, 1) << @intCast(actual_count)) - 1);
|
||||
|
||||
const result: U = if (!signed_result or count == bits) blk: {
|
||||
const result: U = if (!signed_result or actual_count == bits) blk: {
|
||||
break :blk field;
|
||||
} else blk: {
|
||||
const sign_bit: U = @as(U, 1) << @intCast(count - 1);
|
||||
const sign_bit: U = @as(U, 1) << @intCast(actual_count - 1);
|
||||
if ((field & sign_bit) != 0) {
|
||||
break :blk field | (~@as(U, 0) << @intCast(count));
|
||||
break :blk field | (~@as(U, 0) << @intCast(actual_count));
|
||||
}
|
||||
break :blk field;
|
||||
};
|
||||
@@ -647,34 +658,57 @@ fn BitEngine(comptime T: PrimitiveType, comptime Op: BitOp) type {
|
||||
.BitwiseAnd => op1 & op2,
|
||||
.BitwiseOr => op1 | op2,
|
||||
.BitwiseXor => op1 ^ op2,
|
||||
.ShiftLeft => op1 << @intCast(op2),
|
||||
.ShiftRight, .ShiftRightArithmetic => op1 >> @intCast(op2),
|
||||
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
inline fn operationTernary(comptime TT: type, op1: TT, op2: TT, op3: *const Value) RuntimeError!TT {
|
||||
inline fn operationShift(comptime TT: type, op1: TT, amount: u64) RuntimeError!TT {
|
||||
if (amount >= @bitSizeOf(TT)) return @as(TT, 0);
|
||||
const shift: std.math.Log2Int(TT) = @intCast(amount);
|
||||
return switch (Op) {
|
||||
.ShiftLeft => op1 << shift,
|
||||
.ShiftRight, .ShiftRightArithmetic => op1 >> shift,
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
inline fn operationTernary(comptime TT: type, op1: TT, op2: u64, op3: u64) RuntimeError!TT {
|
||||
return switch (Op) {
|
||||
.BitFieldSExtract => blk: {
|
||||
if (T != .SInt) return RuntimeError.InvalidSpirV;
|
||||
break :blk bitExtract(TT, true, op1, @intCast(op2), op3.Int.value.uint64);
|
||||
break :blk bitExtract(TT, true, op1, op2, op3);
|
||||
},
|
||||
.BitFieldUExtract => blk: {
|
||||
if (T != .UInt) return RuntimeError.InvalidSpirV;
|
||||
break :blk bitExtract(TT, false, op1, @intCast(op2), op3.Int.value.uint64);
|
||||
break :blk bitExtract(TT, false, op1, op2, op3);
|
||||
},
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
inline fn operationQuaternary(comptime TT: type, op1: TT, op2: TT, op3: *const Value, op4: *const Value) RuntimeError!TT {
|
||||
inline fn operationQuaternary(comptime TT: type, op1: TT, op2: TT, op3: u64, op4: u64) RuntimeError!TT {
|
||||
return switch (Op) {
|
||||
.BitFieldInsert => bitInsert(TT, op1, op2, op3.Int.value.uint64, op4.Int.value.uint64),
|
||||
.BitFieldInsert => bitInsert(TT, op1, op2, op3, op4),
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
fn readIntegerLaneAsU64(value: *const Value, lane_index: usize) RuntimeError!u64 {
|
||||
const lane_bits = try value.resolveLaneBitWidth();
|
||||
const sign = try value.resolveSign();
|
||||
return switch (lane_bits) {
|
||||
inline 8, 16, 32, 64 => |bits| blk: {
|
||||
if (sign == .signed) {
|
||||
const lane = try Value.readLane(.SInt, bits, value, lane_index);
|
||||
break :blk std.math.cast(u64, lane) orelse return RuntimeError.OutOfBounds;
|
||||
}
|
||||
break :blk @intCast(try Value.readLane(.UInt, bits, value, lane_index));
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
fn applyScalarBits(bit_count: SpvWord, dst: *Value, ops: [max_operator_count]?*const Value) RuntimeError!void {
|
||||
switch (bit_count) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
@@ -685,11 +719,25 @@ fn BitEngine(comptime T: PrimitiveType, comptime Op: BitOp) type {
|
||||
|
||||
if (comptime isUnaryOp()) break :blk try operationUnary(TT, a);
|
||||
|
||||
const b = try Value.readLane(T, bits, ops[1].?, 0);
|
||||
|
||||
if (comptime isBinaryOp()) break :blk try operationBinary(TT, a, b);
|
||||
if (comptime isTernaryOp()) break :blk try operationTernary(TT, a, b, ops[2].?);
|
||||
if (comptime isQuaternaryOp()) break :blk try operationQuaternary(TT, a, b, ops[2].?, ops[3].?);
|
||||
if (comptime isBinaryOp()) {
|
||||
if (comptime Op == .ShiftLeft or Op == .ShiftRight or Op == .ShiftRightArithmetic) {
|
||||
const amount = try readIntegerLaneAsU64(ops[1].?, 0);
|
||||
break :blk try operationShift(TT, a, amount);
|
||||
}
|
||||
const b = try Value.readLane(T, bits, ops[1].?, 0);
|
||||
break :blk try operationBinary(TT, a, b);
|
||||
}
|
||||
if (comptime isTernaryOp()) {
|
||||
const offset = try readIntegerLaneAsU64(ops[1].?, 0);
|
||||
const count = try readIntegerLaneAsU64(ops[2].?, 0);
|
||||
break :blk try operationTernary(TT, a, offset, count);
|
||||
}
|
||||
if (comptime isQuaternaryOp()) {
|
||||
const b = try Value.readLane(T, bits, ops[1].?, 0);
|
||||
const offset = try readIntegerLaneAsU64(ops[2].?, 0);
|
||||
const count = try readIntegerLaneAsU64(ops[3].?, 0);
|
||||
break :blk try operationQuaternary(TT, a, b, offset, count);
|
||||
}
|
||||
};
|
||||
|
||||
try Value.writeLane(T, bits, dst, 0, out);
|
||||
@@ -711,11 +759,25 @@ fn BitEngine(comptime T: PrimitiveType, comptime Op: BitOp) type {
|
||||
|
||||
if (comptime isUnaryOp()) break :blk try operationUnary(TT, a);
|
||||
|
||||
const b = try Value.readLane(T, bits, ops[1].?, if (ops[1].?.isVector()) i else 0);
|
||||
|
||||
if (comptime isBinaryOp()) break :blk try operationBinary(TT, a, b);
|
||||
if (comptime isTernaryOp()) break :blk try operationTernary(TT, a, b, ops[2].?);
|
||||
if (comptime isQuaternaryOp()) break :blk try operationQuaternary(TT, a, b, ops[2].?, ops[3].?);
|
||||
if (comptime isBinaryOp()) {
|
||||
if (comptime Op == .ShiftLeft or Op == .ShiftRight or Op == .ShiftRightArithmetic) {
|
||||
const amount = try readIntegerLaneAsU64(ops[1].?, if (ops[1].?.isVector()) i else 0);
|
||||
break :blk try operationShift(TT, a, amount);
|
||||
}
|
||||
const b = try Value.readLane(T, bits, ops[1].?, if (ops[1].?.isVector()) i else 0);
|
||||
break :blk try operationBinary(TT, a, b);
|
||||
}
|
||||
if (comptime isTernaryOp()) {
|
||||
const offset = try readIntegerLaneAsU64(ops[1].?, if (ops[1].?.isVector()) i else 0);
|
||||
const count = try readIntegerLaneAsU64(ops[2].?, if (ops[2].?.isVector()) i else 0);
|
||||
break :blk try operationTernary(TT, a, offset, count);
|
||||
}
|
||||
if (comptime isQuaternaryOp()) {
|
||||
const b = try Value.readLane(T, bits, ops[1].?, if (ops[1].?.isVector()) i else 0);
|
||||
const offset = try readIntegerLaneAsU64(ops[2].?, if (ops[2].?.isVector()) i else 0);
|
||||
const count = try readIntegerLaneAsU64(ops[3].?, if (ops[3].?.isVector()) i else 0);
|
||||
break :blk try operationQuaternary(TT, a, b, offset, count);
|
||||
}
|
||||
};
|
||||
|
||||
try Value.writeLane(T, bits, dst, i, out);
|
||||
@@ -809,18 +871,36 @@ fn CondEngine(comptime T: PrimitiveType, comptime Op: CondOp) type {
|
||||
}
|
||||
|
||||
fn applyScalarBits(bit_count: SpvWord, dst_bool: *Value, a_v: *const Value, b_v: ?*const Value) RuntimeError!void {
|
||||
if (comptime T == .Bool) {
|
||||
const a = switch (a_v.*) {
|
||||
.Bool => |value| value,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
if (comptime isUnaryOp()) {
|
||||
dst_bool.Bool = try operationUnary(bool, a);
|
||||
} else {
|
||||
const b_ptr = b_v orelse return RuntimeError.InvalidSpirV;
|
||||
const b = switch (b_ptr.*) {
|
||||
.Bool => |value| value,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
dst_bool.Bool = try operationBinary(bool, a, b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (bit_count) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (bits == 8 and T == .Float) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const TT = Value.getPrimitiveFieldType(T, bits);
|
||||
const a = (try Value.getPrimitiveField(T, bits, @constCast(a_v))).*;
|
||||
const a = try Value.readLane(T, bits, a_v, 0);
|
||||
|
||||
if (comptime isUnaryOp()) {
|
||||
dst_bool.Bool = try operationUnary(TT, a);
|
||||
} else {
|
||||
const b_ptr = b_v orelse return RuntimeError.InvalidSpirV;
|
||||
const b = (try Value.getPrimitiveField(T, bits, @constCast(b_ptr))).*;
|
||||
const b = try Value.readLane(T, bits, b_ptr, 0);
|
||||
dst_bool.Bool = try operationBinary(TT, a, b);
|
||||
}
|
||||
},
|
||||
@@ -912,57 +992,20 @@ fn CondEngine(comptime T: PrimitiveType, comptime Op: CondOp) type {
|
||||
.Bool => try applyScalarBits(lane_bits, dst, op1_value, op2_value),
|
||||
|
||||
.Vector => |dst_vec| {
|
||||
switch (op1_value.*) {
|
||||
.Vector => |op1_vec| for (dst_vec, op1_vec, 0..) |*d_lane, a_lane, i| {
|
||||
const b_ptr = laneRhsPtr(op2_value, i);
|
||||
try applyScalarBits(lane_bits, d_lane, &a_lane, b_ptr);
|
||||
switch (lane_bits) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (bits == 8 and T == .Float) return RuntimeError.InvalidSpirV;
|
||||
const TT = Value.getPrimitiveFieldType(T, bits);
|
||||
for (dst_vec, 0..) |*d_lane, i| {
|
||||
const a = try Value.readLane(T, bits, op1_value, i);
|
||||
d_lane.Bool = if (comptime isUnaryOp()) blk: {
|
||||
break :blk try operationUnary(TT, a);
|
||||
} else blk: {
|
||||
const b = try Value.readLane(T, bits, op2_value.?, i);
|
||||
break :blk try operationBinary(TT, a, b);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
.Vector4f32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(f32, 4, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(f32, 4, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector3f32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(f32, 3, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(f32, 3, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector2f32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(f32, 2, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(f32, 2, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector4i32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(i32, 4, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(i32, 4, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector3i32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(i32, 3, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(i32, 3, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector2i32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(i32, 2, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(i32, 2, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector4u32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(u32, 4, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(u32, 4, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector3u32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(u32, 3, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(u32, 3, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
.Vector2u32 => |*op1_vec| if (comptime isUnaryOp())
|
||||
try applyFixedVectorUnary(u32, 2, dst_vec, op1_vec)
|
||||
else
|
||||
try applyFixedVectorBinary(u32, 2, dst_vec, op1_vec, op2_value.?),
|
||||
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
},
|
||||
@@ -980,6 +1023,20 @@ fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: Primiti
|
||||
inline 8, 16, 32, 64 => |bits| blk: {
|
||||
if (bits == 8 and from_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
||||
const v = try Value.readLane(from_kind, bits, from, lane_index);
|
||||
if (comptime from_kind != .Float and to_kind != .Float) {
|
||||
const to_bits = @bitSizeOf(ToT);
|
||||
const FromUInt = std.meta.Int(.unsigned, bits);
|
||||
const ToUInt = std.meta.Int(.unsigned, to_bits);
|
||||
|
||||
const src_bits: FromUInt = @bitCast(v);
|
||||
const dst_bits: ToUInt = if (to_bits < bits)
|
||||
@truncate(src_bits)
|
||||
else if (from_kind == .SInt)
|
||||
@bitCast(@as(std.meta.Int(.signed, to_bits), @intCast(v)))
|
||||
else
|
||||
@intCast(src_bits);
|
||||
break :blk @bitCast(dst_bits);
|
||||
}
|
||||
break :blk std.math.lossyCast(ToT, v);
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
@@ -1001,11 +1058,9 @@ fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: Primiti
|
||||
const target_type = (try rt.results[try rt.it.next()].getVariant()).Type;
|
||||
const dst_value = try rt.results[try rt.it.next()].getValue();
|
||||
|
||||
const src_result = &rt.results[try rt.it.next()];
|
||||
const src_type_word = try src_result.getValueTypeWord();
|
||||
const src_value = try src_result.getValue();
|
||||
const src_value = try rt.results[try rt.it.next()].getValue();
|
||||
|
||||
const from_bits = try Result.resolveLaneBitWidth((try rt.results[src_type_word].getVariant()).Type, rt);
|
||||
const from_bits = try src_value.resolveLaneBitWidth();
|
||||
const to_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||
|
||||
const dst_lane_count = try dst_value.resolveLaneCount();
|
||||
@@ -1730,6 +1785,10 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
defer dy.deinit(allocator);
|
||||
|
||||
const result_type = (try rt.results[result_type_word].getVariant()).Type;
|
||||
if (!try typeHasFloatLanes(rt, result_type)) {
|
||||
rt.clearDerivative(allocator, result_id);
|
||||
return;
|
||||
}
|
||||
const lane_bits = try Result.resolveLaneBitWidth(result_type, rt);
|
||||
const lane_count = try Result.resolveLaneCount(result_type);
|
||||
|
||||
@@ -1749,6 +1808,19 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
try rt.setDerivative(allocator, result_id, &dx, &dy);
|
||||
}
|
||||
|
||||
fn typeHasFloatLanes(rt: *Runtime, target_type: Result.TypeData) RuntimeError!bool {
|
||||
return switch (target_type) {
|
||||
.Float,
|
||||
.Vector2f32,
|
||||
.Vector3f32,
|
||||
.Vector4f32,
|
||||
=> true,
|
||||
.Vector => |v| typeHasFloatLanes(rt, (try rt.results[v.components_type_word].getVariant()).Type),
|
||||
.Matrix => |m| typeHasFloatLanes(rt, (try rt.results[m.column_type_word].getVariant()).Type),
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn sampleImageExplicitLod(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: Runtime.ImageOffset) RuntimeError!void {
|
||||
switch (dst.*) {
|
||||
.Vector4f32,
|
||||
@@ -3078,6 +3150,44 @@ fn autoSetupConstant(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) Run
|
||||
_ = try setupConstant(allocator, rt);
|
||||
}
|
||||
|
||||
fn setupAtomic(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
rt.mod.reflection_infos.has_atomics = true;
|
||||
try autoSetupConstant(allocator, word_count, rt);
|
||||
}
|
||||
|
||||
fn setupAtomicStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
rt.mod.reflection_infos.has_atomics = true;
|
||||
}
|
||||
|
||||
fn opArrayLength(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
_ = try rt.it.next(); // result type
|
||||
const id = try rt.it.next();
|
||||
const structure = try rt.results[try rt.it.next()].getValue();
|
||||
const member_index = try rt.it.next();
|
||||
|
||||
const structure_value = switch (structure.*) {
|
||||
.Pointer => |p| switch (p.ptr) {
|
||||
.common => |value| value,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
},
|
||||
else => structure,
|
||||
};
|
||||
|
||||
const length: u32 = switch (structure_value.*) {
|
||||
.Structure => |s| blk: {
|
||||
if (member_index >= s.values.len) return RuntimeError.OutOfBounds;
|
||||
break :blk switch (s.values[member_index]) {
|
||||
.RuntimeArray => |arr| @as(u32, @intCast(arr.getLen())),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
};
|
||||
},
|
||||
.RuntimeArray => |arr| @intCast(arr.getLen()),
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
};
|
||||
|
||||
try Value.writeLane(.UInt, 32, try rt.results[id].getValue(), 0, length);
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -3264,26 +3374,53 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
||||
|
||||
switch (src.*) {
|
||||
.Vector, .Matrix => |src_slice| {
|
||||
const dst_slice = helpers.getDstSlice(dst);
|
||||
try helpers.copySlice(dst_slice.?, src_slice);
|
||||
if (dst.* == .RuntimeArray) {
|
||||
const size = try src.getPlainMemorySize();
|
||||
if (size > dst.RuntimeArray.data.len) return RuntimeError.OutOfBounds;
|
||||
_ = try src.read(dst.RuntimeArray.data[0..size]);
|
||||
return;
|
||||
}
|
||||
const dst_slice = helpers.getDstSlice(dst) orelse return RuntimeError.InvalidSpirV;
|
||||
try helpers.copySlice(dst_slice, src_slice);
|
||||
},
|
||||
.Array => |a| {
|
||||
const dst_slice = helpers.getDstSlice(dst);
|
||||
try helpers.copySlice(dst_slice.?, a.values);
|
||||
if (dst.* == .RuntimeArray) {
|
||||
const size = try src.getPlainMemorySize();
|
||||
if (size > dst.RuntimeArray.data.len) return RuntimeError.OutOfBounds;
|
||||
_ = try src.read(dst.RuntimeArray.data[0..size]);
|
||||
return;
|
||||
}
|
||||
const dst_slice = helpers.getDstSlice(dst) orelse return RuntimeError.InvalidSpirV;
|
||||
try helpers.copySlice(dst_slice, a.values);
|
||||
},
|
||||
.Structure => |s| {
|
||||
if (s.external_data) |src_data| {
|
||||
if (dst.* == .Structure) {
|
||||
if (dst.Structure.external_data) |dst_data| {
|
||||
if (src_data.len > dst_data.len) return RuntimeError.OutOfBounds;
|
||||
@memcpy(dst_data[0..src_data.len], src_data);
|
||||
_ = try dst.write(dst_data[0..src_data.len]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dst.* == .RuntimeArray) {
|
||||
const size = try src.getPlainMemorySize();
|
||||
if (size > dst.RuntimeArray.data.len) return RuntimeError.OutOfBounds;
|
||||
_ = try src.read(dst.RuntimeArray.data[0..size]);
|
||||
return;
|
||||
}
|
||||
if (dst.* == .Structure) {
|
||||
@memcpy(@constCast(dst.Structure.offsets), s.offsets);
|
||||
@memcpy(@constCast(dst.Structure.matrix_strides), s.matrix_strides);
|
||||
@memcpy(@constCast(dst.Structure.row_major), s.row_major);
|
||||
}
|
||||
const dst_slice = helpers.getDstSlice(dst);
|
||||
try helpers.copySlice(dst_slice.?, s.values);
|
||||
const dst_slice = helpers.getDstSlice(dst) orelse return RuntimeError.InvalidSpirV;
|
||||
try helpers.copySlice(dst_slice, s.values);
|
||||
},
|
||||
.Pointer => |ptr| switch (ptr.ptr) {
|
||||
.common => |src_val_ptr| {
|
||||
if (ptr.uniform_slice_window) |window| {
|
||||
try copyValue(dst, src_val_ptr);
|
||||
try helpers.readWindow(dst, window, ptr.matrix_stride, ptr.matrix_row_major);
|
||||
} else {
|
||||
try copyValue(dst, src_val_ptr);
|
||||
@@ -3312,6 +3449,11 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
||||
},
|
||||
},
|
||||
.RuntimeArray => |src_arr| switch (dst.*) {
|
||||
.Array => {
|
||||
const size = try dst.getPlainMemorySize();
|
||||
if (size > src_arr.data.len) return RuntimeError.OutOfBounds;
|
||||
_ = try dst.write(src_arr.data[0..size]);
|
||||
},
|
||||
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
|
||||
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
|
||||
.common => |dst_ptr_ptr| switch (dst_ptr_ptr.*) {
|
||||
@@ -3326,6 +3468,16 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn intValueToIndex(i: @TypeOf(@as(Value, undefined).Int)) RuntimeError!usize {
|
||||
return switch (i.bit_count) {
|
||||
8 => if (i.is_signed) std.math.cast(usize, i.value.sint8) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint8),
|
||||
16 => if (i.is_signed) std.math.cast(usize, i.value.sint16) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint16),
|
||||
32 => if (i.is_signed) std.math.cast(usize, i.value.sint32) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint32),
|
||||
64 => if (i.is_signed) std.math.cast(usize, i.value.sint64) orelse RuntimeError.OutOfBounds else std.math.cast(usize, i.value.uint64) orelse RuntimeError.OutOfBounds,
|
||||
else => RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const var_type = try rt.it.next();
|
||||
const id = try rt.it.next();
|
||||
@@ -3433,7 +3585,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
|
||||
}
|
||||
}
|
||||
|
||||
const component_index: usize = @intCast(i.value.uint32);
|
||||
const component_index = try intValueToIndex(i);
|
||||
|
||||
switch (value_ptr.*) {
|
||||
.Vector, .Matrix => |v| {
|
||||
@@ -3465,7 +3617,8 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
|
||||
}
|
||||
},
|
||||
.Array => |a| {
|
||||
if (component_index >= a.values.len) return RuntimeError.OutOfBounds;
|
||||
if (component_index >= a.values.len)
|
||||
return RuntimeError.OutOfBounds;
|
||||
uniform_slice_window = try helpers.advanceWindow(uniform_slice_window, component_index * a.stride);
|
||||
value_ptr = &a.values[component_index];
|
||||
switch (value_ptr.*) {
|
||||
@@ -3494,6 +3647,15 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
|
||||
value_ptr = &s.values[component_index];
|
||||
matrix_stride = s.matrix_strides[component_index];
|
||||
matrix_row_major = s.row_major[component_index];
|
||||
if (uniform_slice_window) |window| {
|
||||
if (value_ptr.* == .RuntimeArray) {
|
||||
value_ptr.RuntimeArray.data = window;
|
||||
if (matrix_stride) |stride| {
|
||||
value_ptr.RuntimeArray.matrix_stride = stride;
|
||||
value_ptr.RuntimeArray.row_major = matrix_row_major;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.RuntimeArray => |*arr| {
|
||||
if (component_index >= arr.getLen()) return RuntimeError.OutOfBounds;
|
||||
@@ -3703,8 +3865,9 @@ fn opBitcast(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErro
|
||||
_ = try to_value.write(bytes);
|
||||
}
|
||||
|
||||
fn opBranch(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
fn opBranch(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const id = try rt.it.next();
|
||||
try rt.snapshotPhiValues(allocator);
|
||||
rt.previous_label = rt.current_label;
|
||||
_ = rt.it.jumpToSourceLocation(switch ((try rt.results[id].getVariant()).*) {
|
||||
.Label => |l| l.source_location,
|
||||
@@ -3712,7 +3875,7 @@ fn opBranch(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
});
|
||||
}
|
||||
|
||||
fn opBranchConditional(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
fn opBranchConditional(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const cond_value = try rt.results[try rt.it.next()].getValue();
|
||||
const true_branch = switch ((try rt.results[try rt.it.next()].getVariant()).*) {
|
||||
.Label => |l| l.source_location,
|
||||
@@ -3722,6 +3885,7 @@ fn opBranchConditional(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeEr
|
||||
.Label => |l| l.source_location,
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
try rt.snapshotPhiValues(allocator);
|
||||
rt.previous_label = rt.current_label;
|
||||
if (cond_value.Bool) {
|
||||
_ = rt.it.jumpToSourceLocation(true_branch);
|
||||
@@ -3872,7 +4036,7 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.value = blk: {
|
||||
var composite = (try rt.results[composite_id].getVariant()).Constant.value;
|
||||
var composite = (try rt.results[composite_id].getValue()).*;
|
||||
for (0..index_count) |_| {
|
||||
const member_id = try rt.it.next();
|
||||
if (composite.getCompositeDataOrNull()) |v| {
|
||||
@@ -4049,6 +4213,36 @@ fn opConstantTrue(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) Runtim
|
||||
}
|
||||
}
|
||||
|
||||
fn zeroValue(value: *Value) RuntimeError!void {
|
||||
switch (value.*) {
|
||||
.Void => {},
|
||||
.Bool => |*b| b.* = false,
|
||||
.Int => |*i| i.value.uint64 = 0,
|
||||
.Float => |*f| f.value.float64 = 0,
|
||||
.Vector => |lanes| for (lanes) |*lane| try zeroValue(lane),
|
||||
.Vector4f32 => |*v| v.* = .{ 0, 0, 0, 0 },
|
||||
.Vector3f32 => |*v| v.* = .{ 0, 0, 0 },
|
||||
.Vector2f32 => |*v| v.* = .{ 0, 0 },
|
||||
.Vector4i32 => |*v| v.* = .{ 0, 0, 0, 0 },
|
||||
.Vector3i32 => |*v| v.* = .{ 0, 0, 0 },
|
||||
.Vector2i32 => |*v| v.* = .{ 0, 0 },
|
||||
.Vector4u32 => |*v| v.* = .{ 0, 0, 0, 0 },
|
||||
.Vector3u32 => |*v| v.* = .{ 0, 0, 0 },
|
||||
.Vector2u32 => |*v| v.* = .{ 0, 0 },
|
||||
.Matrix => |columns| for (columns) |*column| try zeroValue(column),
|
||||
.Array => |*a| for (a.values) |*element| try zeroValue(element),
|
||||
.RuntimeArray => |*arr| @memset(arr.data, 0),
|
||||
.Structure => |*s| for (s.values) |*field| try zeroValue(field),
|
||||
.Image, .Sampler, .SampledImage, .Pointer => {},
|
||||
.Function => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn opConstantNull(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const target = try setupConstant(allocator, rt);
|
||||
try zeroValue(&target.variant.?.Constant.value);
|
||||
}
|
||||
|
||||
fn opConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const target = try setupConstant(allocator, rt);
|
||||
switch (target.variant.?.Constant.value) {
|
||||
@@ -4937,6 +5131,20 @@ fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi
|
||||
try copyValue(try rt.results[target].getValue(), try rt.results[source].getValue());
|
||||
}
|
||||
|
||||
fn opCopyObject(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
_ = try rt.it.next(); // result type
|
||||
const target = try rt.it.next();
|
||||
const source = try rt.it.next();
|
||||
const target_value = try rt.results[target].getValue();
|
||||
const source_value = try rt.results[source].getValue();
|
||||
if (source_value.* == .Pointer) {
|
||||
target_value.* = source_value.*;
|
||||
target_value.Pointer.owns_uniform_backing_value = false;
|
||||
return;
|
||||
}
|
||||
try copyValue(target_value, source_value);
|
||||
}
|
||||
|
||||
fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const target = try rt.it.next();
|
||||
const decoration_type = try rt.it.nextAs(spv.SpvDecoration);
|
||||
@@ -5118,6 +5326,7 @@ fn opExecutionMode(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!
|
||||
.OutputLineStrip,
|
||||
.OutputTriangleStrip,
|
||||
=> rt.mod.reflection_infos.geometry_output = @intFromEnum(mode),
|
||||
.EarlyFragmentTests => rt.mod.reflection_infos.early_fragment_tests = true,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
@@ -5323,7 +5532,8 @@ fn opPhi(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim
|
||||
const parent_label_id = try rt.it.next();
|
||||
|
||||
if (parent_label_id == predecessor) {
|
||||
try copyValue(try rt.results[id].getValue(), try rt.results[value_id].getValue());
|
||||
const value = rt.getPhiValueSnapshot(value_id) orelse try rt.results[value_id].getValue();
|
||||
try copyValue(try rt.results[id].getValue(), value);
|
||||
try rt.copyDerivative(allocator, id, value_id);
|
||||
return;
|
||||
}
|
||||
@@ -5332,12 +5542,12 @@ fn opPhi(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim
|
||||
}
|
||||
|
||||
fn quantizeF32ToF16(value: f32) f32 {
|
||||
const rounded = @as(f32, @floatCast(@as(f16, @floatCast(value))));
|
||||
if (@abs(rounded) != 0.0 and @abs(rounded) < @as(f32, 0x1p-14)) {
|
||||
const sign = @as(u32, @bitCast(value)) & 0x80000000;
|
||||
return @bitCast(sign);
|
||||
const quantized = @as(f32, @floatCast(@as(f16, @floatCast(value))));
|
||||
if (quantized != 0.0 and @abs(quantized) < 0x1.0p-14) {
|
||||
const sign = @as(u32, @bitCast(quantized)) & 0x8000_0000;
|
||||
return @as(f32, @bitCast(sign));
|
||||
}
|
||||
return rounded;
|
||||
return quantized;
|
||||
}
|
||||
|
||||
fn quantizeToF16Value(target_type: Result.TypeData, rt: *Runtime, dst: *Value, src: *const Value) RuntimeError!void {
|
||||
@@ -5410,13 +5620,13 @@ fn opSelect(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const obj2_val = try rt.results[obj2].getValue();
|
||||
|
||||
if (target_val.getCompositeDataOrNull()) |*targets| {
|
||||
for (
|
||||
targets.*,
|
||||
cond_val.getCompositeDataOrNull().?,
|
||||
obj1_val.getCompositeDataOrNull().?,
|
||||
obj2_val.getCompositeDataOrNull().?,
|
||||
) |*t, c, o1, o2| {
|
||||
try copyValue(t, if (c.Bool) &o1 else &o2);
|
||||
for (targets.*, 0..) |*t, lane_index| {
|
||||
const condition = try readVectorLaneAsValue(cond_val, lane_index);
|
||||
if (condition != .Bool)
|
||||
return RuntimeError.InvalidValueType;
|
||||
|
||||
const selected = try readVectorLaneAsValue(if (condition.Bool) obj1_val else obj2_val, lane_index);
|
||||
try copyValue(t, &selected);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -5518,6 +5728,7 @@ fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
|
||||
var target = &rt.results[id];
|
||||
const components_type_word = try rt.it.next();
|
||||
const components_type_data = &((try rt.results[components_type_word].getVariant()).*).Type;
|
||||
const length_word = try rt.it.next();
|
||||
target.variant = .{
|
||||
.Type = .{
|
||||
.Array = .{
|
||||
@@ -5526,16 +5737,7 @@ fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
|
||||
.Type => |t| @as(Result.Type, t),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.member_count = switch ((try rt.results[try rt.it.next()].getValue()).*) {
|
||||
.Int => |i| if (!i.is_signed) @intCast(i.value.uint64) else switch (i.bit_count) {
|
||||
8 => @intCast(i.value.sint8),
|
||||
16 => @intCast(i.value.sint8),
|
||||
32 => @intCast(i.value.sint8),
|
||||
64 => @intCast(i.value.sint8),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
.member_count = try arrayMemberCount(rt, length_word),
|
||||
.stride = blk: {
|
||||
for (target.decorations.items) |decoration| {
|
||||
if (decoration.rtype == .ArrayStride)
|
||||
@@ -5548,6 +5750,31 @@ fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void
|
||||
};
|
||||
}
|
||||
|
||||
fn arrayMemberCount(rt: *Runtime, length_word: SpvWord) RuntimeError!SpvWord {
|
||||
for (rt.results[length_word].decorations.items) |decoration| {
|
||||
if (decoration.rtype != .SpecId)
|
||||
continue;
|
||||
|
||||
if (rt.specialization_constants.get(decoration.literal_1)) |data| {
|
||||
if (data.len < @sizeOf(u32))
|
||||
return RuntimeError.OutOfBounds;
|
||||
|
||||
return @intCast(std.mem.bytesToValue(u32, data[0..@sizeOf(u32)]));
|
||||
}
|
||||
}
|
||||
|
||||
return switch ((try rt.results[length_word].getValue()).*) {
|
||||
.Int => |i| if (!i.is_signed) @intCast(i.value.uint64) else switch (i.bit_count) {
|
||||
8 => @intCast(i.value.sint8),
|
||||
16 => @intCast(i.value.sint16),
|
||||
32 => @intCast(i.value.sint32),
|
||||
64 => @intCast(i.value.sint64),
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
};
|
||||
}
|
||||
|
||||
fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
const id = try rt.it.next();
|
||||
rt.results[id].variant = .{
|
||||
@@ -5838,20 +6065,31 @@ fn opVariable(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R
|
||||
.Uniform,
|
||||
.StorageBuffer,
|
||||
.PushConstant,
|
||||
.Workgroup,
|
||||
};
|
||||
|
||||
const is_externally_visible = std.mem.containsAtLeastScalar(spv.SpvStorageClass, &externally_visible_data_storages, 1, storage_class);
|
||||
const use_external_storage = is_externally_visible and (storage_class == .Workgroup or resolved_type != .Array);
|
||||
|
||||
if (target.variant) |*variant| {
|
||||
switch (variant.*) {
|
||||
.Variable => |*variable| variable.value.deinit(allocator),
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
target.variant = .{
|
||||
.Variable = .{
|
||||
.storage_class = storage_class,
|
||||
.type_word = resolved_word,
|
||||
.type = resolved_type,
|
||||
.value = try Value.init(allocator, rt.results, resolved_word, is_externally_visible and resolved_type != .Array),
|
||||
.value = try Value.init(allocator, rt.results, resolved_word, use_external_storage),
|
||||
},
|
||||
};
|
||||
|
||||
_ = initializer;
|
||||
if (initializer) |initializer_id| {
|
||||
try copyValue(try target.getValue(), try rt.results[initializer_id].getValue());
|
||||
}
|
||||
}
|
||||
|
||||
fn readDynamicVectorIndex(index_value: *const Value) RuntimeError!usize {
|
||||
|
||||
Reference in New Issue
Block a user