Prechádzať zdrojové kódy

Implement HXSL Syntax.code support (#1264)

Pavel Alexandrov 5 mesiacov pred
rodič
commit
56b65e3a5b
10 zmenil súbory, kde vykonal 185 pridanie a 2 odobranie
  1. 15 1
      hxsl/Ast.hx
  2. 24 0
      hxsl/Checker.hx
  3. 23 0
      hxsl/Dce.hx
  4. 2 0
      hxsl/Eval.hx
  5. 16 0
      hxsl/GlslOut.hx
  6. 16 0
      hxsl/HlslOut.hx
  7. 36 1
      hxsl/Linker.hx
  8. 14 0
      hxsl/Printer.hx
  9. 15 0
      hxsl/Serializer.hx
  10. 24 0
      hxsl/Splitter.hx

+ 15 - 1
hxsl/Ast.hx

@@ -181,6 +181,7 @@ enum TExprDef {
 	TWhile( e : TExpr, loop : TExpr, normalWhile : Bool );
 	TMeta( m : String, args : Array<Const>, e : TExpr );
 	TField( e : TExpr, name : String );
+	TSyntax(target : String, code : String, args : Array<SyntaxArg> ); // target = "code" should be treated as "insert regardless of target"
 }
 
 typedef TVar = {
@@ -315,6 +316,17 @@ enum TGlobal {
 	UnpackUnorm4x8;
 }
 
+enum SyntaxArgAccess {
+	Read;
+	Write;
+	ReadWrite;
+}
+
+typedef SyntaxArg = {
+	e: TExpr,
+	access: SyntaxArgAccess,
+}
+
 enum Component {
 	X;
 	Y;
@@ -540,7 +552,7 @@ class Tools {
 				if( hasSideEffect(p) )
 					return true;
 			return false;
-		case TVarDecl(_), TDiscard, TContinue, TBreak, TReturn(_):
+		case TVarDecl(_), TDiscard, TContinue, TBreak, TReturn(_), TSyntax(_, _, _):
 			return true;
 		case TSwitch(e, cases, def):
 			for( c in cases ) {
@@ -584,6 +596,7 @@ class Tools {
 		case TConst(_), TVar(_), TGlobal(_), TDiscard, TContinue, TBreak:
 		case TMeta(_, _, e): f(e);
 		case TField(e, _): f(e);
+		case TSyntax(_, _, args): for (arg in args) f(arg.e);
 		}
 	}
 
@@ -606,6 +619,7 @@ class Tools {
 		case TConst(_), TVar(_), TGlobal(_), TDiscard, TContinue, TBreak: e.e;
 		case TMeta(m, args, e): TMeta(m, args, f(e)); // don't map args
 		case TField(e, name): TField(f(e), name);
+		case TSyntax(target, code, args): TSyntax(target, code, [for (arg in args) ({ e : f(arg.e), access : arg.access })]);
 		}
 		return { e : ed, t : e.t, p : e.p };
 	}

+ 24 - 0
hxsl/Checker.hx

@@ -553,6 +553,30 @@ class Checker {
 				// not closure support
 				error("Global function must be called immediately", e.pos);
 			}
+		case ECall({ expr: EField({ expr: EIdent("Syntax") }, target) }, args):
+			if ( args.length == 0 )
+				error("Syntax." + target + " should have a string as first argument", e.pos);
+			var code = switch ( args[0].expr ) {
+				case EConst(CString(code)): code;
+				default: error("Syntax." + target + " should have a string as first argument", args[0].pos);
+			}
+			var sargs: Array<Ast.SyntaxArg> = [];
+			for ( i in 1...args.length ) {
+				var arg = args[i];
+				switch ( arg.expr ) {
+					case EMeta(flags = ("rw"|"r"|"w"), _, flaggedArg):
+						sargs.push({ e : typeExpr(flaggedArg, Value), access : switch( flags ) {
+								case "r": Read;
+								case "w": Write;
+								case "rw": ReadWrite;
+								default: throw "assert";
+							}
+						});
+					default:
+						error("Syntax." + target + " arguments should have an access meta of @r, @w or @rw", arg.pos);
+				}
+			}
+			return { e: TSyntax(target, code, sargs), t: TVoid, p: e.pos };
 		case ECall(e1, args):
 			function makeCall(e1) {
 				return switch( e1.t ) {

+ 23 - 0
hxsl/Dce.hx

@@ -266,6 +266,29 @@ class Dce {
 			check(val, writeTo, isAffected);
 			writeTo.pop();
 			isAffected.append(v,15);
+		case TSyntax(_, _, args):
+			for ( arg in args ) {
+				if ( arg.access != Read ) {
+					var tvars : Array<VarDeps> = [];
+					function findTVars( e : TExpr ) {
+						switch ( e.e ) {
+							case TVar(v): tvars.push(get(v));
+							default: e.iter(findTVars);
+						}
+					}
+					findTVars(arg.e);
+					for ( v in tvars ) {
+						writeTo.push(v, 15);
+						for ( arg2 in args ) {
+							if ( arg2.access != Write ) check(arg2.e, writeTo, isAffected);
+						}
+						writeTo.pop();
+						isAffected.append(v, 15);
+					}
+				} else {
+					check(arg.e, writeTo, isAffected);
+				}
+			}
 		default:
 			e.iter(check.bind(_, writeTo, isAffected));
 		}

+ 2 - 0
hxsl/Eval.hx

@@ -564,6 +564,8 @@ class Eval {
 			TMeta(name, args, e2);
 		case TField(e, name):
 			TField(evalExpr(e), name);
+		case TSyntax(target, code, args):
+			TSyntax(target, code, [for ( arg in args ) ({ e : evalExpr(arg.e), access : arg.access })]);
 		};
 		return { e : d, t : t, p : e.p }
 	}

+ 16 - 0
hxsl/GlslOut.hx

@@ -665,6 +665,22 @@ class GlslOut {
 			addExpr(val, tabs);
 			add(".");
 			add(name);
+		case TSyntax("code" | "glsl", code, args):
+			var pos = 0;
+			var argRegex = ~/{(\d+)}/g;
+			while ( argRegex.matchSub(code, pos) ) {
+				var matchPos = argRegex.matchedPos();
+				add(code.substring(pos, matchPos.pos));
+				var index = Std.parseInt(argRegex.matched(1));
+				// if (index >= args.length) throw "Attempting to use substitution index of " + index + ", which is out of bounds of argument list";
+				if ( index < args.length )
+					addValue(args[index].e, tabs);
+
+				pos = matchPos.pos + matchPos.len;
+			}
+			add(code.substr(pos));
+		case TSyntax(_, _, _):
+			// Do nothing: Code for other language
 		}
 	}
 

+ 16 - 0
hxsl/HlslOut.hx

@@ -729,6 +729,22 @@ class HlslOut {
 			addValue(e, tabs);
 			add(".");
 			add(f);
+		case TSyntax("code" | "hlsl", code, args):
+			var pos = 0;
+			var argRegex = ~/{(\d+)}/g;
+			while ( argRegex.matchSub(code, pos) ) {
+				var matchPos = argRegex.matchedPos();
+				add(code.substring(pos, matchPos.pos));
+				var index = Std.parseInt(argRegex.matched(1));
+				// if (index >= args.length) throw "Attempting to use substitution index of " + index + ", which is out of bounds of argument list";
+				if ( index < args.length )
+					addValue(args[index].e, tabs);
+
+				pos = matchPos.pos + matchPos.len;
+			}
+			add(code.substr(pos));
+		case TSyntax(_, _, _):
+			// Do nothing: Code for other language
 		}
 	}
 

+ 36 - 1
hxsl/Linker.hx

@@ -32,6 +32,7 @@ private class ShaderInfos {
 	public var onStack : Bool;
 	public var hasDiscard : Bool;
 	public var isCompute : Bool;
+	public var hasSyntax : Bool;
 	public var marked : Null<Bool>;
 	public function new(n, v) {
 		this.name = n;
@@ -235,11 +236,45 @@ class Linker {
 			locals.set(v.id, true);
 		case TFor(v, _, _):
 			locals.set(v.id, true);
+		case TSyntax(target, code, args):
+			var mappedArgs: Array<SyntaxArg> = [];
+			for ( arg in args ) {
+				var e = switch ( arg.access ) {
+					case Read:
+						mapExprVar(arg.e);
+					case Write:
+						var e = curShader != null ? mapSyntaxWrite(arg.e) : arg.e;
+						mapExprVar(e);
+					case ReadWrite:
+						// Make sure syntax writes are appended after reads.
+						var e = mapExprVar(arg.e);
+						if (curShader != null) e = mapSyntaxWrite(e);
+						e;
+				}
+				mappedArgs.push({ e: e, access: arg.access });
+			}
+			if ( curShader != null ) curShader.hasSyntax = true;
+			return { e : TSyntax(target, code, mappedArgs), t : e.t, p : e.p };
 		default:
 		}
 		return e.map(mapExprVar);
 	}
 
+	function mapSyntaxWrite( e : TExpr ) {
+		switch ( e.e ) {
+			case TVar(v):
+				var v = allocVar(v, e.p);
+				if( !curShader.writeMap.exists(v.id) ) {
+					debug(curShader.name + " syntax write " + v.path);
+					curShader.writeMap.set(v.id, v);
+					curShader.writeVars.push(v);
+				}
+				return { e : TVar(v.v), t : v.v.type, p : e.p };
+			default:
+				return e.map(mapSyntaxWrite);
+		}
+	}
+
 	function addShader( name : String, vertex : Null<Bool>, e : TExpr, p : Int ) {
 		var s = new ShaderInfos(name, vertex);
 		curShader = s;
@@ -419,7 +454,7 @@ class Linker {
 
 		// force shaders containing discard to be included
 		for( s in shaders )
-			if( s.hasDiscard || s.isCompute ) {
+			if( s.hasDiscard || s.isCompute || s.hasSyntax ) {
 				initDependencies(s);
 				entry.deps.set(s, true);
 			}

+ 14 - 0
hxsl/Printer.hx

@@ -304,6 +304,20 @@ class Printer {
 			addExpr(e, tabs);
 			add(".");
 			add(name);
+		case TSyntax(target, code, args):
+			add("Syntax.");
+			add(target);
+			add("(");
+			addConst(CString(code));
+			for ( arg in args ) {
+				switch ( arg.access ) {
+					case Read: add(", @r ");
+					case Write: add(", @w ");
+					case ReadWrite: add(", @rw ");
+				}
+				addExpr(arg.e, tabs);
+			}
+			add(")");
 		}
 
 	}

+ 15 - 0
hxsl/Serializer.hx

@@ -323,6 +323,13 @@ class Serializer {
 		case TField(e,name):
 			writeExpr(e);
 			writeString(name);
+		case TSyntax(target, code, args):
+			writeString(target);
+			writeString(code);
+			writeArr(args, (arg) -> {
+				writeExpr(arg.e);
+				out.addByte(arg.access.getIndex());
+			});
 		}
 		writeType(e.t);
 		// no position
@@ -391,6 +398,14 @@ class Serializer {
 		case 19: TWhile(readExpr(), readExpr(), input.readByte() != 0);
 		case 20: TMeta(readString(), readArr(readConst), readExpr());
 		case 21: TField(readExpr(), readString());
+		case 22: TSyntax(readString(), readString(), readArr(() -> {
+			return {
+				e: readExpr(),
+				access: Ast.SyntaxArgAccess.createByIndex(input.readByte()),
+				read: false,
+				write: false,
+			};
+		}));
 		default: throw "assert";
 		}
 		return {

+ 24 - 0
hxsl/Splitter.hx

@@ -329,6 +329,30 @@ class Splitter {
 			inf.local = true;
 			inf.write++;
 			checkExpr(loop);
+		case TSyntax(_, _, args):
+			var arg : Ast.SyntaxArg = null;
+			function checkSyntaxExpr( e : Ast.TExpr) {
+				switch ( e.e ) {
+					case TVar(v):
+						var inf = get(v);
+						// inf.read is decremented in order to keep accurate count of reads
+						// as it will be incremented by checkExpr afterwards
+						switch ( arg.access ) {
+							case Read: inf.read--;
+							case Write: inf.write++;
+							case ReadWrite:
+								inf.read--;
+								inf.write++;
+						}
+					default:
+						e.iter(checkSyntaxExpr);
+				}
+			}
+			for (i in 0...args.length) {
+				arg = args[i];
+				checkSyntaxExpr(arg.e);
+				checkExpr(arg.e);
+			}
 		default:
 			e.iter(checkExpr);
 		}