From e620d8f759d34bd706e011a629e91a9d91887873 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Mon, 10 Apr 2023 14:40:01 +0330 Subject: [PATCH] dusk: basic binary expression errors --- libs/dusk/src/Ast.zig | 5 - libs/dusk/src/AstGen.zig | 305 +++++++++++++++++++++----------------- libs/dusk/src/IR.zig | 159 +++++++++++++++++++- libs/dusk/src/Parser.zig | 12 +- libs/dusk/test/boids.wgsl | 3 +- 5 files changed, 336 insertions(+), 148 deletions(-) diff --git a/libs/dusk/src/Ast.zig b/libs/dusk/src/Ast.zig index 0a9ab9f9..9a31a35e 100644 --- a/libs/dusk/src/Ast.zig +++ b/libs/dusk/src/Ast.zig @@ -362,11 +362,6 @@ pub const Node = struct { /// RHS : -- depth_texture_type, - /// TOK : ident - /// LHS : -- - /// RHS : -- - user_type, - // ####### Attribute ####### // TOK : attr diff --git a/libs/dusk/src/AstGen.zig b/libs/dusk/src/AstGen.zig index 8db1419b..2333ce99 100644 --- a/libs/dusk/src/AstGen.zig +++ b/libs/dusk/src/AstGen.zig @@ -7,7 +7,7 @@ const AstGen = @This(); allocator: std.mem.Allocator, tree: *const Ast, -instructions: std.ArrayListUnmanaged(IR.Inst) = .{}, +instructions: IR.Inst.List = .{}, refs: std.ArrayListUnmanaged(IR.Inst.Ref) = .{}, strings: std.ArrayListUnmanaged(u8) = .{}, scratch: std.ArrayListUnmanaged(IR.Inst.Ref) = .{}, @@ -52,6 +52,21 @@ pub fn genTranslationUnit(self: *AstGen) !u32 { return try self.addRefList(self.scratch.items[scratch_top..]); } +pub fn genDecl(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { + const ref = scope.decls.get(node).?; + if (ref != .none) return ref; + + const decl = switch (self.tree.nodeTag(node)) { + .global_variable => try self.genGlobalVariable(scope, node), + .type_alias => try self.genTypeAlias(scope, node), + .struct_decl => try self.genStruct(scope, node), + else => return error.AnalysisFail, // TODO: make this unreachable + }; + scope.decls.putAssumeCapacity(node, decl); + return decl; +} + +/// adds `decls` to scope and checks for re-declarations pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void { std.debug.assert(scope.decls.count() == 0); @@ -69,15 +84,15 @@ pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void { // ); // } - var name_iter = scope.decls.keyIterator(); - while (name_iter.next()) |node| { - if (std.mem.eql(u8, self.declNameLoc(node.*).?.slice(self.tree.source), name)) { + var iter = scope.decls.keyIterator(); + while (iter.next()) |node| { + if (std.mem.eql(u8, name, self.declNameLoc(node.*).?.slice(self.tree.source))) { try self.errors.add( loc, "redeclaration of '{s}'", .{name}, try self.errors.createNote( - self.declNameLoc(node.*).?, + self.declNameLoc(node.*), "other declaration here", .{}, ), @@ -90,41 +105,42 @@ pub fn scanDecls(self: *AstGen, scope: *Scope, decls: []const Ast.Index) !void { } } -pub fn genDecl(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { - const ref = scope.decls.get(node).?; - if (ref != .none) return ref; - - const decl = switch (self.tree.nodeTag(node)) { - .global_variable => try self.genGlobalVariable(scope, node), - .type_alias => try self.genTypeAlias(scope, node), - .struct_decl => try self.genStruct(scope, node), - else => return error.AnalysisFail, // TODO: else prong should not ever be trigerred - }; - scope.decls.putAssumeCapacity(node, decl); - return decl; -} - -pub fn declRef(self: *AstGen, scope: *Scope, loc: Token.Loc) !IR.Inst.Ref { +/// takes token location and returns the first declaration +/// in current and parent scopes +pub fn declRef(self: *AstGen, scope: *Scope, loc: Token.Loc) error{ OutOfMemory, AnalysisFail }!IR.Inst.Ref { const name = loc.slice(self.tree.source); - var s = scope; while (true) { - var name_iter = s.decls.keyIterator(); - while (name_iter.next()) |node| { - if (std.mem.eql(u8, self.declNameLoc(node.*).?.slice(self.tree.source), name)) { + var node_iter = s.decls.keyIterator(); + while (node_iter.next()) |node| { + if (std.mem.eql(u8, name, self.declNameLoc(node.*).?.slice(self.tree.source))) { return self.genDecl(scope, node.*); } } - s = scope.parent orelse break; + s = scope.parent orelse { + try self.errors.add( + loc, + "use of undeclared identifier '{s}'", + .{name}, + null, + ); + return error.AnalysisFail; + }; } +} - try self.errors.add( - loc, - "use of undeclared identifier '{s}'", - .{name}, - null, - ); - return error.AnalysisFail; +/// returns declaration type or if type is unkown, returns value +pub fn valueOrType(inst: IR.Inst) IR.Inst.Ref { + switch (inst.tag) { + .global_variable_decl => { + const decl_type = inst.data.global_variable_decl.type; + if (decl_type == .none) { + return inst.data.global_variable_decl.expr; + } + return decl_type; + }, + else => unreachable, + } } pub fn genTypeAlias(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { @@ -286,47 +302,103 @@ pub fn genStruct(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { } pub fn genExpr(self: *AstGen, scope: *Scope, node: Ast.Index) !IR.Inst.Ref { - const tag = self.tree.nodeTag(node); - const lhs = self.tree.nodeLHS(node); - const rhs = self.tree.nodeRHS(node); + const node_loc = self.tree.tokenLoc(self.tree.nodeToken(node)); + const node_tag = self.tree.nodeTag(node); + const node_lhs = self.tree.nodeLHS(node); + const node_rhs = self.tree.nodeRHS(node); + const node_lhs_loc = self.tree.tokenLoc(self.tree.nodeToken(node_lhs)); + const node_rhs_loc = self.tree.tokenLoc(self.tree.nodeToken(node_rhs)); - switch (tag) { + switch (node_tag) { .bool_true => return .true_literal, - .bool_false => return .true_literal, + .bool_false => return .false_literal, else => {}, } const inst_index = try self.reserveInst(); - const inst: IR.Inst = switch (tag) { + const inst: IR.Inst = switch (node_tag) { .number_literal => .{ .tag = .integer_literal, .data = .{ .integer_literal = 1 } }, - .not => .{ .tag = .not, .data = .{ .ref = try self.genExpr(scope, lhs) } }, - .negate => .{ .tag = .negate, .data = .{ .ref = try self.genExpr(scope, lhs) } }, - .deref => .{ .tag = .deref, .data = .{ .ref = try self.genExpr(scope, lhs) } }, - .addr_of => .{ .tag = .addr_of, .data = .{ .ref = try self.genExpr(scope, lhs) } }, - .mul => .{ .tag = .mul, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .div => .{ .tag = .div, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .mod => .{ .tag = .mod, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .add => .{ .tag = .add, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .sub => .{ .tag = .sub, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .shift_left => .{ .tag = .shift_left, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .shift_right => .{ .tag = .shift_right, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .binary_and => .{ .tag = .binary_and, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .binary_or => .{ .tag = .binary_or, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .binary_xor => .{ .tag = .binary_xor, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .circuit_and => .{ .tag = .circuit_and, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .circuit_or => .{ .tag = .circuit_or, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .equal => .{ .tag = .equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .not_equal => .{ .tag = .not_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .less => .{ .tag = .less, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .less_equal => .{ .tag = .less_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .greater => .{ .tag = .greater, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .greater_equal => .{ .tag = .greater_equal, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .index_access => .{ .tag = .index, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .component_access => .{ .tag = .member_access, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genExpr(scope, rhs) } } }, - .bitcast => .{ .tag = .bitcast, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, lhs), .rhs = try self.genType(scope, rhs) } } }, + .not => .{ .tag = .not, .data = .{ .ref = try self.genExpr(scope, node_lhs) } }, + .negate => .{ .tag = .negate, .data = .{ .ref = try self.genExpr(scope, node_lhs) } }, + .deref => .{ .tag = .deref, .data = .{ .ref = try self.genExpr(scope, node_lhs) } }, + .addr_of => .{ .tag = .addr_of, .data = .{ .ref = try self.genExpr(scope, node_lhs) } }, + + .mul, + .div, + .mod, + .add, + .sub, + .shift_left, + .shift_right, + .binary_and, + .binary_or, + .binary_xor, + .circuit_and, + .circuit_or, + .equal, + .not_equal, + .less, + .less_equal, + .greater, + .greater_equal, + => blk: { + const lhs = try self.genExpr(scope, node_lhs); + const rhs = try self.genExpr(scope, node_rhs); + const inst_tag: IR.Inst.Tag = switch (node_tag) { + .mul => .mul, + .div => .div, + .mod => .mod, + .add => .add, + .sub => .sub, + .shift_left => .shift_left, + .shift_right => .shift_right, + .binary_and => .binary_and, + .binary_or => .binary_or, + .binary_xor => .binary_xor, + .circuit_and => .circuit_and, + .circuit_or => .circuit_or, + .equal => .equal, + .not_equal => .not_equal, + .less => .less, + .less_equal => .less_equal, + .greater => .greater, + .greater_equal => .greater_equal, + else => unreachable, + }; + + if (try self.isIntegerResulting(lhs, false)) { + if (try self.isIntegerResulting(rhs, false)) { + break :blk .{ + .tag = inst_tag, + .data = .{ .binary = .{ .lhs = lhs, .rhs = rhs } }, + }; + } + } + + try self.errors.add( + node_loc, + "incompatible types: '{s}' and '{s}'", + .{ node_lhs_loc.slice(self.tree.source), node_rhs_loc.slice(self.tree.source) }, + try self.errors.createNote( + null, + "{} and {}", + .{ self.instructions.items[lhs.toIndex().?].tag, self.instructions.items[rhs.toIndex().?].tag }, + ), + ); + return error.AnalysisFail; + }, + + .index_access => .{ .tag = .index, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genExpr(scope, node_rhs) } } }, + .component_access => .{ .tag = .member_access, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genExpr(scope, node_rhs) } } }, + .bitcast => .{ .tag = .bitcast, .data = .{ .binary = .{ .lhs = try self.genExpr(scope, node_lhs), .rhs = try self.genType(scope, node_rhs) } } }, .ident_expr => .{ - .tag = .ident, - .data = .{ .name = try self.addString(self.tree.tokenLoc(self.tree.nodeToken(node)).slice(self.tree.source)) }, + .tag = .var_ref, + .data = .{ + .var_ref = .{ + .name = try self.addString(self.tree.tokenLoc(self.tree.nodeToken(node)).slice(self.tree.source)), + .variable = try self.declRef(scope, node_loc), + }, + }, }, else => { std.debug.print("WTF REALLY\n", .{}); @@ -364,76 +436,6 @@ pub fn addInst(self: *AstGen, inst: IR.Inst) error{OutOfMemory}!IR.Inst.Index { return @intCast(IR.Inst.Index, self.instructions.items.len - 1); } -// // pub fn expression(self: *AstGen, node: Ast.Index) !?IR.Expression { -// // const lhs = self.tree.nodeLHS(node); -// // const rhs = self.tree.nodeRHS(node); -// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node)); -// // return switch (self.tree.nodeTag(node)) { -// // .mul => { -// // const lir = try self.expression(lhs) orelse return null; -// // const rir = try self.expression(rhs) orelse return null; - -// // const is_valid_op = -// // (lir == .number and rir == .number) or -// // ((lir == .construct and lir.construct == .vector) and rir == .number) or -// // (lir == .number and (rir == .construct and rir.construct == .vector)) or -// // ((lir == .construct and lir.construct == .vector) and (rir == .construct and rir.construct == .vector)) or -// // ((lir == .construct and lir.construct == .matrix) and (rir == .construct and rir.construct == .matrix)); -// // if (!is_valid_op) { -// // try self.errors.add( -// // loc, -// // "invalid operation with '{s}' and '{s}'", -// // .{ @tagName(std.meta.activeTag(lir)), @tagName(std.meta.activeTag(rir)) }, -// // null, -// // ); -// // return null; -// // } -// // }, -// // // .div, -// // // .mod, -// // // .add, -// // // .sub, -// // // .shift_left, -// // // .shift_right, -// // // .binary_and, -// // // .binary_or, -// // // .binary_xor, -// // // .circuit_and, -// // // .circuit_or, -// // .number_literal => .{ .literal = try self.create(.{ .number = try self.create(try self.numberLiteral(node) orelse return null) }) }, -// // .bool_literal => .{ .literal = try self.create(.{ .bool = self.boolLiteral(node) }) }, -// // else => return null, // TODO -// // }; -// // } - -// // pub fn numberLiteral(self: *AstGen, node: Ast.Index) !?IR.NumberLiteral { -// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node)); -// // const str = loc.slice(self.tree.source); - -// // if (std.mem.startsWith(u8, str, "0") and -// // !std.mem.endsWith(u8, str, "i") and -// // !std.mem.endsWith(u8, str, "u") and -// // !std.mem.endsWith(u8, str, "f") and -// // !std.mem.endsWith(u8, str, "h")) -// // { -// // try self.errors.add( -// // loc, -// // "number literal cannot have leading 0", -// // .{str}, -// // null, -// // ); -// // return null; -// // } - -// // return null; -// // } - -// // pub fn boolLiteral(self: *AstGen, node: Ast.Index) bool { -// // const loc = self.tree.tokenLoc(self.tree.nodeToken(node)); -// // const str = loc.slice(self.tree.source); -// // return str[0] == 't'; -// // } - pub fn genType(self: *AstGen, scope: *Scope, node: Ast.Index) error{ AnalysisFail, OutOfMemory }!IR.Inst.Ref { return switch (self.tree.nodeTag(node)) { .bool_type => try self.genBoolType(node), @@ -442,7 +444,7 @@ pub fn genType(self: *AstGen, scope: *Scope, node: Ast.Index) error{ AnalysisFai .matrix_type => try self.genMatrixType(scope, node), .atomic_type => try self.genAtomicType(scope, node), .array_type => try self.genArrayType(scope, node), - .user_type => { + .ident_expr => { const node_loc = self.tree.tokenLoc(self.tree.nodeToken(node)); const decl_ref = try self.declRef(scope, node_loc); switch (decl_ref) { @@ -1084,3 +1086,34 @@ pub fn declNameLoc(self: *AstGen, node: Ast.Index) ?Token.Loc { }; return self.tree.tokenLoc(token); } + +pub fn isIntegerResulting(self: *AstGen, ref: IR.Inst.Ref, is_decl: bool) !bool { + if (is_decl and ref.isNumberType()) { + return true; + } + + if (ref.isNumberLiteral(self.instructions) or + ref.is(self.instructions, &.{ + .mul, + .div, + .mod, + .add, + .sub, + .shift_left, + .shift_right, + .binary_and, + .binary_or, + .binary_xor, + })) { + return true; + } else if (ref.is(self.instructions, &.{.var_ref})) { + const inst = self.instructions.items[ref.toIndex().?]; + const var_inst = self.instructions.items[inst.data.var_ref.variable.toIndex().?]; + return self.isIntegerResulting(valueOrType(var_inst), true); + } else if (ref.is(self.instructions, &.{.deref})) { + const inst = self.instructions.items[ref.toIndex().?]; + return self.isIntegerResulting(inst.data.ref, true); + } + + return false; +} diff --git a/libs/dusk/src/IR.zig b/libs/dusk/src/IR.zig index 40848d97..aea717ee 100644 --- a/libs/dusk/src/IR.zig +++ b/libs/dusk/src/IR.zig @@ -56,6 +56,7 @@ pub const Inst = struct { tag: Tag, data: Data, + pub const List = std.ArrayListUnmanaged(Inst); pub const Index = u32; const ref_start_index = @typeInfo(Ref).Enum.fields.len; @@ -88,6 +89,149 @@ pub const Inst = struct { return null; } } + + pub fn is(self: Ref, list: List, comptime expected: []const Inst.Tag) bool { + inline for (expected) |e| { + if (list.items[self.toIndex().?].tag == e) return true; + } + return false; + } + + pub fn isType(self: Ref, list: List) bool { + return switch (self) { + .none, + .true_literal, + .false_literal, + => false, + .bool_type, + .i32_type, + .u32_type, + .f32_type, + .f16_type, + .sampler_type, + .comparison_sampler_type, + .external_sampled_texture_type, + => true, + _ => switch (list.items[self.toIndex().?].tag) { + .struct_decl, + .vector_type, + .matrix_type, + .atomic_type, + .array_type, + .ptr_type, + .sampled_texture_type, + .multisampled_texture_type, + .storage_texture_type, + .depth_texture_type, + => true, + else => false, + }, + }; + } + + pub fn isNumberType(self: Ref) bool { + return switch (self) { + .i32_type, + .u32_type, + .f32_type, + .f16_type, + => true, + else => false, + }; + } + + pub fn isLiteral(self: Ref, list: List) bool { + return switch (self) { + .true_literal, + .false_literal, + => true, + .none, + .bool_type, + .i32_type, + .u32_type, + .f32_type, + .f16_type, + .sampler_type, + .comparison_sampler_type, + .external_sampled_texture_type, + => false, + _ => switch (list.items[self.toIndex().?].tag) { + .integer_literal, + .float_literal, + => true, + else => false, + }, + }; + } + + pub fn isBoolLiteral(self: Ref) bool { + return switch (self) { + .true_literal, + .false_literal, + => true, + else => false, + }; + } + + pub fn isNumberLiteral(self: Ref, list: List) bool { + const i = self.toIndex() orelse return false; + return switch (list.items[i].tag) { + .integer_literal, + .float_literal, + => true, + else => false, + }; + } + + pub fn isExpr(self: Ref, list: List) bool { + const i = self.toIndex() orelse return false; + return switch (list.items[i].tag) { + .index, + .member_access, + .bitcast, + .ident, + => true, + else => self.isBinaryExpr() or self.isUnaryExpr(), + }; + } + + pub fn isBinaryExpr(self: Ref, list: List) bool { + const i = self.toIndex() orelse return false; + return switch (list.items[i].tag) { + .mul, + .div, + .mod, + .add, + .sub, + .shift_left, + .shift_right, + .binary_and, + .binary_or, + .binary_xor, + .circuit_and, + .circuit_or, + .equal, + .not_equal, + .less, + .less_equal, + .greater, + .greater_equal, + => true, + else => false, + }; + } + + pub fn isUnaryExpr(self: Ref, list: List) bool { + const i = self.toIndex() orelse return false; + return switch (list.items[i].tag) { + .not, + .negate, + .deref, + .addr_of, + => true, + else => false, + }; + } }; pub const Tag = enum(u6) { @@ -187,8 +331,8 @@ pub const Inst = struct { /// data is binary (lhs is expr, rhs is type) bitcast, - /// data is name - ident, + /// data is var_ref + var_ref, pub fn isDecl(self: Tag) bool { return switch (self) { @@ -200,8 +344,7 @@ pub const Inst = struct { pub const Data = union { ref: Ref, - /// index to null-terminated string in `strings` - name: u32, + var_ref: VarRef, global_variable_decl: GlobalVariableDecl, struct_decl: StructDecl, struct_member: StructMember, @@ -231,10 +374,16 @@ pub const Inst = struct { member_access: MemberAccess, }; + pub const VarRef = struct { + /// index to null-terminated string in `strings` + name: u32, + variable: Ref, + }; + pub const GlobalVariableDecl = struct { /// index to null-terminated string in `strings` name: u32, - type: Ref, + type: Ref = .none, addr_space: AddressSpace, access_mode: AccessMode, /// length of attributes diff --git a/libs/dusk/src/Parser.zig b/libs/dusk/src/Parser.zig index 89d75365..ed1ae69a 100644 --- a/libs/dusk/src/Parser.zig +++ b/libs/dusk/src/Parser.zig @@ -305,6 +305,16 @@ pub fn globalVarDecl(p: *Parser, attrs: ?Ast.Index) !?Ast.Index { }; } + if (initializer == Ast.null_index and var_type == Ast.null_index) { + try p.errors.add( + p.getToken(.loc, var_token), + "initializer expression is required while type is unknown", + .{}, + null, + ); + return error.Parsing; + } + const extra = try p.addExtra(Ast.Node.GlobalVarDecl{ .attrs = attrs orelse Ast.null_index, .name = name_token, @@ -1042,7 +1052,7 @@ pub fn expectTypeSpecifier(p: *Parser) error{ OutOfMemory, Parsing }!Ast.Index { pub fn typeSpecifier(p: *Parser) !?Ast.Index { if (p.peekToken(.tag, 0) == .ident) { const main_token = p.advanceToken(); - return try p.addNode(.{ .tag = .user_type, .main_token = main_token }); + return try p.addNode(.{ .tag = .ident_expr, .main_token = main_token }); } return p.typeSpecifierWithoutIdent(); } diff --git a/libs/dusk/test/boids.wgsl b/libs/dusk/test/boids.wgsl index 4ef52f84..5b42d0a7 100644 --- a/libs/dusk/test/boids.wgsl +++ b/libs/dusk/test/boids.wgsl @@ -19,7 +19,8 @@ struct SimParams { rule3Scale : f32, } -@group(0) @binding(0) var params : SimParams; +var v0 = 5; +@group(0) @binding(0) var params : SimParams = *v0 * 1 + 2; @group(0) @binding(1) var particlesSrc : Particles; @group(0) @binding(2) var particlesDst : Particles;