diff --git a/build.zig b/build.zig index ae03483..b9ed751 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { .name = "spirv_interpreter", .root_module = mod, .linkage = .dynamic, - .use_llvm = true, + //.use_llvm = true, }); const lib_install = b.addInstallArtifact(lib, .{}); diff --git a/example/shader.nzsl b/example/shader.nzsl index 94ac674..0a888e4 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -9,7 +9,9 @@ struct FragOut [entry(frag)] fn main() -> FragOut { + let ratio = vec4[f32](2.0, 2.0, 8.0, 0.25); + let output: FragOut; - output.color = vec4[f32](4.0, 3.0, 2.0, 1.0); + output.color = vec4[f32](4.0, 3.0, 2.0, 1.0) * ratio; return output; } diff --git a/example/shader.spv b/example/shader.spv index ee9794c..c074b40 100644 Binary files a/example/shader.spv and b/example/shader.spv differ diff --git a/example/shader.spvasm b/example/shader.spvasm index 2e81960..f746f7e 100644 --- a/example/shader.spvasm +++ b/example/shader.spvasm @@ -1,7 +1,7 @@ ; SPIR-V ; Version: 1.0 ; Generator: SirLynix Nazara ShaderLang Compiler; 4226 -; Bound: 23 +; Bound: 29 ; Schema: 0 OpCapability Shader OpMemoryModel Logical GLSL450 @@ -21,23 +21,30 @@ %v4float = OpTypeVector %float 4 %_ptr_Output_v4float = OpTypePointer Output %v4float %FragOut = OpTypeStruct %v4float + %float_2 = OpConstant %float 2 + %float_8 = OpConstant %float 8 + %float_0_25 = OpConstant %float 0.25 +%_ptr_Function_v4float = OpTypePointer Function %v4float %_ptr_Function_FragOut = OpTypePointer Function %FragOut %int = OpTypeInt 32 1 %int_0 = OpConstant %int 0 %float_4 = OpConstant %float 4 %float_3 = OpConstant %float 3 - %float_2 = OpConstant %float 2 %float_1 = OpConstant %float 1 -%_ptr_Function_v4float = OpTypePointer Function %v4float %color = OpVariable %_ptr_Output_v4float Output %main = OpFunction %void None %2 - %16 = OpLabel - %17 = OpVariable %_ptr_Function_FragOut Function - %18 = OpCompositeConstruct %v4float %float_4 %float_3 %float_2 %float_1 - %19 = OpAccessChain %_ptr_Function_v4float %17 %int_0 - OpStore %19 %18 - %21 = OpLoad %FragOut %17 - %22 = OpCompositeExtract %v4float %21 0 - OpStore %color %22 + %19 = OpLabel + %20 = OpVariable %_ptr_Function_v4float Function + %21 = OpVariable %_ptr_Function_FragOut Function + %22 = OpCompositeConstruct %v4float %float_2 %float_2 %float_8 %float_0_25 + OpStore %20 %22 + %23 = OpCompositeConstruct %v4float %float_4 %float_3 %float_2 %float_1 + %24 = OpLoad %v4float %20 + %25 = OpFMul %v4float %23 %24 + %26 = OpAccessChain %_ptr_Function_v4float %21 %int_0 + OpStore %26 %25 + %27 = OpLoad %FragOut %21 + %28 = OpCompositeExtract %v4float %27 0 + OpStore %color %28 OpReturn OpFunctionEnd diff --git a/src/Module.zig b/src/Module.zig index a36ef22..1e7f3df 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -160,7 +160,12 @@ pub fn init(allocator: std.mem.Allocator, source: []const SpvWord) ModuleError!S entry_points, }); - //@import("pretty").print(allocator, self.results, .{ .tab_size = 4, .max_depth = 0 }) catch return ModuleError.OutOfMemory; + //@import("pretty").print(allocator, self.results, .{ + // .tab_size = 4, + // .max_depth = 0, + // .struct_max_len = 0, + // .array_max_len = 0, + //}) catch return ModuleError.OutOfMemory; } return self; diff --git a/src/Result.zig b/src/Result.zig index cd36c09..5805c84 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -293,6 +293,14 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { self.decorations.deinit(allocator); } +pub fn getValue(self: *Self) RuntimeError!*Value { + return switch (self.variant orelse return RuntimeError.InvalidSpirV) { + .Variable => |*v| &v.value, + .Constant => |*v| v, + else => RuntimeError.InvalidSpirV, + }; +} + /// Performs a deep copy pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self { return .{ @@ -330,7 +338,7 @@ pub fn dupe(self: *const Self, allocator: std.mem.Allocator) RuntimeError!Self { }, }, }, - else => {}, + else => break :blk .{ .Type = t }, }, .Variable => |v| break :blk .{ .Variable = .{ diff --git a/src/opcodes.zig b/src/opcodes.zig index 1924569..604b20d 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -19,7 +19,7 @@ pub const SetupDispatcher = block: { @setEvalBranchQuota(65535); break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ .Capability = opCapability, - .CompositeConstruct = opCompositeConstructSetup, + .CompositeConstruct = autoSetupConstant, .Constant = opConstant, .Decorate = opDecorate, .EntryPoint = opEntryPoint, @@ -27,7 +27,7 @@ pub const SetupDispatcher = block: { .Function = opFunction, .FunctionEnd = opFunctionEnd, .Label = opLabel, - .Load = opLoadSetup, + .Load = autoSetupConstant, .MemberDecorate = opDecorateMember, .MemberName = opMemberName, .MemoryModel = opMemoryModel, @@ -44,6 +44,7 @@ pub const SetupDispatcher = block: { .TypeVector = opTypeVector, .TypeVoid = opTypeVoid, .Variable = opVariable, + .FMul = autoSetupConstant, }); }; @@ -56,6 +57,7 @@ pub const RuntimeDispatcher = block: { .Load = opLoad, .Return = opReturn, .Store = opStore, + .FMul = opFMul, }); }; @@ -272,7 +274,7 @@ fn opTypeVector(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi .Type = .{ .Vector = .{ .components_type_word = components_type_word, - .components_type = switch (rt.mod.results[components_type_word].variant.?) { + .components_type = switch (rt.mod.results[components_type_word].variant orelse return RuntimeError.InvalidSpirV) { .Type => |t| @as(Result.Type, t), else => return RuntimeError.InvalidSpirV, }, @@ -289,7 +291,7 @@ fn opTypeMatrix(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!voi .Type = .{ .Matrix = .{ .column_type_word = column_type_word, - .column_type = switch (rt.mod.results[column_type_word].variant.?) { + .column_type = switch (rt.mod.results[column_type_word].variant orelse return RuntimeError.InvalidSpirV) { .Type => |t| @as(Result.Type, t), else => return RuntimeError.InvalidSpirV, }, @@ -459,10 +461,6 @@ fn opLabel(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { }; } -fn opCompositeConstructSetup(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - _ = try setupConstant(allocator, rt); -} - fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.it.skip(); const id = try rt.it.next(); @@ -501,16 +499,7 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim const base_id = try rt.it.next(); const base = &rt.results[base_id]; - var value_ptr = blk: { - if (base.variant) |variant| { - switch (variant) { - .Variable => |v| break :blk &v.value, - .Constant => |v| break :blk &v, - else => {}, - } - } - return RuntimeError.InvalidSpirV; - }; + var value_ptr = try base.getValue(); const index_count = word_count - 3; for (0..index_count) |_| { @@ -570,10 +559,6 @@ fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { ); } -fn opLoadSetup(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { - _ = try setupConstant(allocator, rt); -} - fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.it.skip(); const id = try rt.it.next(); @@ -605,6 +590,48 @@ fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { } } +fn opFMul(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const res_type = try rt.it.next(); + const id = try rt.it.next(); + const op1 = try rt.it.next(); + const op2 = try rt.it.next(); + + const target_type = (rt.results[res_type].variant orelse return RuntimeError.InvalidSpirV).Type; + + const target = &rt.results[id]; + const value = try target.getValue(); + + const op1_target = &rt.results[op1]; + const op1_value = try op1_target.getValue(); + + const op2_target = &rt.results[op2]; + const op2_value = try op2_target.getValue(); + + const float_size = sw: switch (target_type) { + .Vector => |v| continue :sw (rt.results[v.components_type_word].variant orelse return RuntimeError.InvalidSpirV).Type, + .Float => |f| f.bit_length, + else => return RuntimeError.InvalidSpirV, + }; + + switch (value.*) { + .Float => switch (float_size) { + 16 => value.Float.float16 = op1_value.Float.float16 * op2_value.Float.float16, + 32 => value.Float.float32 = op1_value.Float.float32 * op2_value.Float.float32, + 64 => value.Float.float64 = op1_value.Float.float64 * op2_value.Float.float64, + else => return RuntimeError.InvalidSpirV, + }, + .Vector => |vec| for (vec, op1_value.Vector, op2_value.Vector) |*val, op1_v, op2_v| { + switch (float_size) { + 16 => val.Float.float16 = op1_v.Float.float16 * op2_v.Float.float16, + 32 => val.Float.float32 = op1_v.Float.float32 * op2_v.Float.float32, + 64 => val.Float.float64 = op1_v.Float.float64 * op2_v.Float.float64, + else => return RuntimeError.InvalidSpirV, + } + }, + else => return RuntimeError.InvalidSpirV, + } +} + fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Result { const res_type = try rt.it.next(); const id = try rt.it.next(); @@ -619,6 +646,10 @@ fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Resul return target; } +fn autoSetupConstant(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + _ = try setupConstant(allocator, rt); +} + fn readString(allocator: std.mem.Allocator, it: *WordIterator) RuntimeError![]const u8 { var str: std.ArrayList(u8) = .empty; while (it.nextOrNull()) |word| {