123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- package hxsl;
- using hxsl.Ast;
- class Serializer {
- var out : haxe.io.BytesBuffer;
- var input : haxe.io.BytesInput;
- var varMap : Map<Int, TVar>;
- var idMap : Map<Int,Int>;
- var typeIdMap : Map<Type,Int>;
- var types : Array<Type>;
- var uid = 1;
- var tid = 1;
- public function new() {
- }
- inline function writeArr<T>( arr : Array<T>, f : T -> Void ) {
- writeVarInt(arr.length);
- for( v in arr ) f(v);
- }
- inline function readArr<T>( f : Void -> T ) {
- return [for( i in 0...readVarInt() ) f()];
- }
- function readVarInt() {
- var b = input.readByte();
- if( b < 128 )
- return b;
- if( b == 0xFF )
- return input.readInt32();
- return ((b & 0x7F) << 8) | input.readByte();
- }
- function writeVarInt( id : Int ) {
- if( id < 128 )
- out.addByte(id);
- else {
- var n = id >> 8;
- if( n >= 127 ) {
- out.addByte(0xFF);
- out.addInt32(n);
- } else {
- out.addByte(n | 128);
- out.addByte(id & 0xFF);
- }
- }
- }
- function writeID( id : Int ) {
- var id2 = idMap.get(id);
- if( id2 == null ) {
- id2 = uid++;
- idMap.set(id,id2);
- }
- writeVarInt(id2);
- }
- inline function readID() {
- return readVarInt();
- }
- function writeTID( t : Type ) {
- var tid = typeIdMap.get(t);
- if( tid != null ) {
- writeVarInt(tid);
- return false;
- }
- tid = this.tid++;
- typeIdMap.set(t,tid);
- writeVarInt(tid);
- return true;
- }
- function writeType( t : Type ) {
- out.addByte(t.getIndex());
- switch (t) {
- case TVec(size, t):
- out.addByte(size | (t.getIndex() << 3));
- case TBytes(size):
- out.addInt32(size);
- case TStruct(vl):
- if( writeTID(t) )
- writeArr(vl,writeVar);
- case TFun(variants):
- // not serialized
- case TArray(t, size), TBuffer(t, size):
- writeType(t);
- switch (size) {
- case SConst(v): out.addByte(0); writeVarInt(v);
- case SVar(v): writeVar(v);
- }
- case TChannel(size):
- out.addByte(size);
- case TVoid, TInt, TBool, TFloat, TString, TMat2, TMat3, TMat4, TMat3x4, TSampler2D, TSampler2DArray, TSamplerCube:
- }
- }
- static var TVECS = new Map();
- function readType() : Type {
- return switch( input.readByte() ) {
- case 0: TVoid;
- case 1: TInt;
- case 2: TBool;
- case 3: TFloat;
- case 4: TString;
- case 5:
- var bits = input.readByte();
- var v = TVECS.get(bits);
- if( v == null ) {
- v = TVec(bits & 7, VecType.createByIndex(bits>>3));
- TVECS.set(bits, v);
- }
- v;
- case 6: TMat3;
- case 7: TMat4;
- case 8: TMat3x4;
- case 9: TBytes(input.readInt32());
- case 10: TSampler2D;
- case 11: TSampler2DArray;
- case 12: TSamplerCube;
- case 13:
- var id = readVarInt();
- var t = types[id];
- if( t != null ) return t;
- t = TStruct(readArr(readVar));
- types[id] = t;
- t;
- case 14:
- TFun(null);
- case 15:
- var t = readType();
- var v = readVar();
- TArray(t, v == null ? SConst(readVarInt()) : SVar(v));
- case 16:
- var t = readType();
- var v = readVar();
- TBuffer(t, v == null ? SConst(readVarInt()) : SVar(v));
- case 17:
- TChannel(input.readByte());
- case 18: TMat2;
- default:
- throw "assert";
- }
- }
- function writeString( s : String ) {
- var bytes = haxe.io.Bytes.ofString(s);
- writeVarInt(bytes.length);
- out.add(bytes);
- }
- function readString() {
- var len = readVarInt();
- var s = input.read(len).getString(0,len);
- return s;
- }
- function writeVar( v : TVar ) {
- if( v == null ) {
- out.addByte(0);
- return;
- }
- writeID(v.id);
- if( varMap.exists(v.id) ) return;
- varMap.set(v.id, v);
- writeString(v.name);
- writeType(v.type);
- out.addByte(v.kind.getIndex());
- writeVar(v.parent);
- if( v.qualifiers == null )
- out.addByte(0);
- else {
- out.addByte(v.qualifiers.length);
- for( q in v.qualifiers ) {
- out.addByte(q.getIndex());
- switch (q) {
- case Private, Nullable, PerObject, Shared, Ignore:
- case Const(max): out.addInt32(max == null ? 0 : max);
- case Name(n): writeString(n);
- case Precision(p): out.addByte(p.getIndex());
- case Range(min, max): out.addDouble(min); out.addDouble(max);
- case PerInstance(v): out.addInt32(v);
- case Doc(s): writeString(s);
- }
- }
- }
- }
- function writeFun( f : TFunction ) {
- out.addByte(f.kind.getIndex());
- writeVar(f.ref);
- writeArr(f.args, writeVar);
- writeType(f.ret);
- writeExpr(f.expr);
- }
- function writeConst( c : Const ) {
- out.addByte(c.getIndex());
- switch (c) {
- case CNull:
- case CBool(b): out.addByte(b?1:0);
- case CInt(v): out.addInt32(v);
- case CFloat(v): out.addDouble(v);
- case CString(v): writeString(v);
- }
- }
- function writeExpr( e : TExpr ) {
- if( e == null ) {
- out.addByte(0);
- return;
- }
- out.addByte(e.e.getIndex() + 1);
- switch (e.e) {
- case TConst(c):
- writeConst(c);
- case TVar(v):
- writeVar(v);
- case TGlobal(g):
- out.addByte(g.getIndex());
- case TParenthesis(e):
- writeExpr(e);
- case TBlock(el):
- writeArr(el, writeExpr);
- case TBinop(op, e1, e2):
- switch( op ) {
- case OpAssignOp(op):
- out.addByte(op.getIndex() | 128);
- default:
- out.addByte(op.getIndex());
- }
- writeExpr(e1);
- writeExpr(e2);
- case TUnop(op, e1):
- out.addByte(op.getIndex());
- writeExpr(e1);
- case TVarDecl(v, init):
- writeVar(v);
- writeExpr(init);
- case TCall(e, args):
- writeExpr(e);
- writeArr(args, writeExpr);
- case TSwiz(e, regs):
- writeExpr(e);
- if( regs.length == 0 ) throw "assert";
- var bits = regs.length - 1, k = 2;
- for( r in regs ) {
- bits |= r.getIndex() << k;
- k += 2;
- }
- out.addByte(bits & 0xFF);
- out.addByte(bits >> 8);
- case TIf(econd, eif, eelse):
- writeExpr(econd);
- writeExpr(eif);
- writeExpr(eelse);
- case TDiscard:
- case TReturn(e):
- writeExpr(e);
- case TFor(v, it, loop):
- writeVar(v);
- writeExpr(it);
- writeExpr(loop);
- case TContinue:
- case TBreak:
- case TArray(e, index):
- writeExpr(e);
- writeExpr(index);
- case TArrayDecl(el):
- writeArr(el, writeExpr);
- case TSwitch(e, cases, def):
- writeExpr(e);
- writeArr(cases, function(c) {
- writeArr(c.values, writeExpr);
- writeExpr(c.expr);
- });
- writeExpr(def);
- case TWhile(e, loop, normalWhile):
- writeExpr(e);
- writeExpr(loop);
- out.addByte(normalWhile ? 1 : 0);
- case TMeta(m, args, e):
- writeString(m);
- writeArr(args, writeConst);
- writeExpr(e);
- }
- writeType(e.t);
- // no position
- }
- function readConst() : Const {
- return switch( input.readByte() ) {
- case 0: CNull;
- case 1: CBool(input.readByte() != 0);
- case 2: CInt(input.readInt32());
- case 3: CFloat(input.readDouble());
- case 4: CString(readString());
- default: throw "assert";
- }
- }
- static var BOPS = {
- var ops = Binop.createAll();
- ops.insert(OpAssignOp(null).getIndex(),null);
- ops;
- };
- static var UNOPS = Unop.createAll();
- static var TGLOBALS = hxsl.TGlobal.createAll();
- static var TSWIZ = new Map();
- static var REGS = [X,Y,Z,W];
- function readExpr() : TExpr {
- var k = input.readByte();
- if( k-- == 0 )
- return null;
- var e : TExprDef = switch( k ) {
- case 0: TConst(readConst());
- case 1: TVar(readVar());
- case 2: TGlobal(TGLOBALS[input.readByte()]);
- case 3: TParenthesis(readExpr());
- case 4: TBlock(readArr(readExpr));
- case 5:
- var op = input.readByte();
- TBinop(op >= 128 ? OpAssignOp(BOPS[op&127]) : BOPS[op], readExpr(), readExpr());
- case 6: TUnop(UNOPS[input.readByte()], readExpr());
- case 7: TVarDecl(readVar(), readExpr());
- case 8: TCall(readExpr(), readArr(readExpr));
- case 9:
- var e = readExpr();
- var bits = input.readUInt16();
- var swiz = TSWIZ.get(bits);
- if( swiz == null ) {
- swiz = [for( i in 0...(bits&3)+1 ) REGS[(bits>>(i*2+2))&3]];
- TSWIZ.set(bits, swiz);
- }
- TSwiz(e, swiz);
- case 10: TIf(readExpr(), readExpr(), readExpr());
- case 11: TDiscard;
- case 12: TReturn(readExpr());
- case 13: TFor(readVar(), readExpr(), readExpr());
- case 14: TContinue;
- case 15: TBreak;
- case 16: TArray(readExpr(), readExpr());
- case 17: TArrayDecl(readArr(readExpr));
- case 18: TSwitch(readExpr(), readArr(function() {
- return {
- values : readArr(readExpr),
- expr : readExpr(),
- };
- }), readExpr());
- case 19: TWhile(readExpr(), readExpr(), input.readByte() != 0);
- case 20: TMeta(readString(), readArr(readConst), readExpr());
- default: throw "assert";
- }
- return {
- e : e,
- t : readType(),
- p : null,
- }
- }
- static var VKINDS = VarKind.createAll();
- static var PRECS = Prec.createAll();
- function readVar() : TVar {
- var id = readID();
- if( id == 0 )
- return null;
- var v = varMap.get(id);
- if( v != null ) return v;
- v = {
- id : Tools.allocVarId(),
- name : readString(),
- type : null,
- kind : null,
- }
- varMap.set(id, v);
- v.type = readType();
- v.kind = VKINDS[input.readByte()];
- v.parent = readVar();
- var nq = input.readByte();
- if( nq > 0 ) {
- v.qualifiers = [];
- for( i in 0...nq ) {
- var qid = input.readByte();
- var q = switch( qid ) {
- case 0: var n = input.readInt32(); Const(n == 0 ? null : n);
- case 1: Private;
- case 2: Nullable;
- case 3: PerObject;
- case 4: Name(readString());
- case 5: Shared;
- case 6: Precision(PRECS[input.readByte()]);
- case 7: Range(input.readDouble(), input.readDouble());
- case 8: Ignore;
- case 9: PerInstance(input.readInt32());
- case 10: Doc(readString());
- default: throw "assert";
- }
- v.qualifiers.push(q);
- }
- }
- return v;
- }
- static var FKIND = FunctionKind.createAll();
- function readFun() : TFunction {
- return {
- kind : FKIND[input.readByte()],
- ref : readVar(),
- args : readArr(readVar),
- ret : readType(),
- expr : readExpr(),
- };
- }
- static var SIGN = 0x8B741D; // will be encoded to HXSL
- public function unserialize( data : String ) : ShaderData {
- input = new haxe.io.BytesInput(haxe.crypto.Base64.decode(data,false));
- if( input.readByte() != (SIGN & 0xFF) || input.readByte() != (SIGN >> 8) & 0xFF || input.readByte() != (SIGN >> 16) & 0xFF )
- throw "Invalid HXSL data";
- varMap = new Map();
- types = [];
- return {
- name : readString(),
- vars : readArr(readVar),
- funs : readArr(readFun),
- };
- }
- public function serialize( s : ShaderData ) {
- varMap = new Map();
- idMap = new Map();
- typeIdMap = new Map();
- out = new haxe.io.BytesBuffer();
- out.addByte(SIGN & 0xFF);
- out.addByte((SIGN >> 8) & 0xFF);
- out.addByte((SIGN >> 16) & 0xFF);
- writeString(s.name);
- writeArr(s.vars, writeVar);
- writeArr(s.funs, writeFun);
- return haxe.crypto.Base64.encode(out.getBytes(),false);
- }
- public static function run( s : ShaderData ) {
- return new Serializer().serialize(s);
- }
- }
|