Cache.hx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. package hxsl;
  2. using hxsl.Ast;
  3. import hxsl.RuntimeShader;
  4. class BatchInstanceParams {
  5. var forcedPerInstance : Array<{ shader : String, params : Array<String> }>;
  6. var cachedSignature : String;
  7. public function new( forcedPerInstance ) {
  8. this.forcedPerInstance = forcedPerInstance;
  9. }
  10. public function getSignature() {
  11. if( cachedSignature == null ) {
  12. for( fp in forcedPerInstance )
  13. fp.params.sort(Reflect.compare);
  14. cachedSignature = haxe.crypto.Md5.encode([for( s in forcedPerInstance ) s.shader+"="+s.params.join(",")].join(";")).substr(0,8);
  15. }
  16. return cachedSignature;
  17. }
  18. }
  19. class SearchMap {
  20. public var linked : RuntimeShader;
  21. var nexts : Array<SearchMap> = [];
  22. var minId = 0;
  23. public function new() { }
  24. public function set(id: Int, s: SearchMap) {
  25. if(minId == 0) {
  26. minId = id;
  27. nexts = [s];
  28. return;
  29. }
  30. var offset = id - minId;
  31. if(offset < 0) {
  32. var n = [];
  33. for(i in 0...nexts.length)
  34. n[i - offset] = nexts[i]; // shift indices
  35. nexts = n;
  36. minId += offset;
  37. offset = 0;
  38. }
  39. nexts[offset] = s;
  40. }
  41. inline public function get(id: Int) {
  42. return nexts[id - minId];
  43. }
  44. }
  45. class Cache {
  46. var linkCache : SearchMap;
  47. var linkShaders : Map<String, Shader>;
  48. var batchShaders : Map<RuntimeShader, { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int }>;
  49. var byID : Map<String, RuntimeShader>;
  50. var batchShadersParams : Map<String, Map<RuntimeShader, { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int }>>;
  51. function new() {
  52. linkCache = new SearchMap();
  53. linkShaders = new Map();
  54. batchShaders = new Map();
  55. batchShadersParams = new Map();
  56. byID = new Map();
  57. }
  58. /**
  59. Creates a shader that generate the output requested.
  60. **/
  61. public function getLinkShader( vars : Array<Output> ) {
  62. var key = [for( v in vars ) Std.string(v)].join(",");
  63. var shader = linkShaders.get(key);
  64. if( shader != null )
  65. return shader;
  66. var s = new hxsl.SharedShader("");
  67. var id = haxe.crypto.Md5.encode(key).substr(0, 8);
  68. s.data = {
  69. name : "shaderLinker_"+id,
  70. vars : [],
  71. funs : [],
  72. };
  73. var pos = null;
  74. var outVars = new Map<String,TVar>();
  75. var outputCount = 0;
  76. var tvec4 = TVec(4, VFloat);
  77. function makeVec( g, size, args : Array<Output>, makeOutExpr : Output -> Int -> TExpr ) {
  78. var out = [];
  79. var rem = size;
  80. for( i in 0...args.length ) {
  81. var e = makeOutExpr(args[args.length - 1 - i], rem - (args.length - 1 - i));
  82. rem -= Tools.size(e.t);
  83. out.unshift(e);
  84. }
  85. return { e : TCall({ e : TGlobal(g), t : TVoid, p : pos }, out), t : TVec(size,VFloat), p : pos };
  86. }
  87. function makeVar( name : String, t, parent : TVar ) {
  88. var path = parent == null ? name : parent.getName() + "." + name;
  89. var v = outVars.get(path);
  90. if( v != null )
  91. return v;
  92. v = {
  93. id : Tools.allocVarId(),
  94. name : name,
  95. type : t,
  96. kind : Var,
  97. parent : parent,
  98. };
  99. if( parent == null )
  100. s.data.vars.push(v);
  101. else {
  102. switch( parent.type ) {
  103. case TStruct(vl): vl.push(v);
  104. default: throw "assert";
  105. }
  106. }
  107. outVars.set(path, v);
  108. return v;
  109. }
  110. function makeOutExpr( v : Output, rem : Int ) : TExpr {
  111. switch( v ) {
  112. case Const(v):
  113. return { e : TConst(CFloat(v)), t : TFloat, p : pos };
  114. case Vec2(args):
  115. return makeVec(Vec2, 2, args, makeOutExpr);
  116. case Vec3(args):
  117. return makeVec(Vec3, 3, args, makeOutExpr);
  118. case Vec4(args):
  119. return makeVec(Vec4, 4, args, makeOutExpr);
  120. case Value(vname,size):
  121. var v = outVars.get(vname);
  122. if( v != null )
  123. return { e : TVar(v), t : v.type, p : pos };
  124. var path = vname.split(".");
  125. var parent : TVar = null;
  126. while( path.length > 1 )
  127. parent = makeVar(path.shift(), TStruct([]), parent);
  128. if( size != null )
  129. rem = size;
  130. v = makeVar(path.shift(), rem == 1 ? TFloat : TVec(rem, VFloat), parent);
  131. return { e : TVar(v), t : v.type, p : pos };
  132. case PackNormal(v):
  133. return { e : TCall({ e : TGlobal(PackNormal), t : TVoid, p : pos }, [makeOutExpr(v,3)]), t : tvec4, p : pos };
  134. case PackFloat(v):
  135. return { e : TCall({ e : TGlobal(Pack), t : TVoid, p : pos }, [makeOutExpr(v, 1)]), t : tvec4, p : pos };
  136. case Swiz(v, comps):
  137. return { e : TSwiz(makeOutExpr(v,4), comps), t : TVec(comps.length, VFloat), p : pos };
  138. }
  139. }
  140. function makeOutput( v : Output ) : TExpr {
  141. var ov : TVar = {
  142. id : Tools.allocVarId(),
  143. type : tvec4,
  144. name : "OUTPUT" + (outputCount++),
  145. kind : Output,
  146. };
  147. s.data.vars.push(ov);
  148. return { e : TBinop(OpAssign,{ e : TVar(ov), t : tvec4, p : pos }, makeOutExpr(v,4)), t : TVoid, p : pos };
  149. }
  150. function defineFun( kind : FunctionKind, vars : Array<Output> ) {
  151. var fv : TVar = {
  152. id : Tools.allocVarId(),
  153. type : TFun([]),
  154. name : ("" + kind).toLowerCase(),
  155. kind : Function,
  156. };
  157. var f : TFunction = {
  158. kind : kind,
  159. ref : fv,
  160. args : [],
  161. ret : TVoid,
  162. expr : { e : TBlock([for( v in vars ) makeOutput(v)]), p : pos, t : TVoid },
  163. };
  164. s.data.funs.push(f);
  165. }
  166. defineFun(Vertex, [Value("output.position")]);
  167. defineFun(Fragment, vars);
  168. shader = std.Type.createEmptyInstance(Shader);
  169. @:privateAccess shader.shader = s;
  170. linkShaders.set(key, shader);
  171. @:privateAccess shader.updateConstantsFinal(null);
  172. return shader;
  173. }
  174. @:noDebug
  175. public function link( shaders : hxsl.ShaderList, mode : LinkMode ) {
  176. var c = linkCache;
  177. for( s in shaders ) {
  178. var i = @:privateAccess s.instance;
  179. var cs = c.get(i.id);
  180. if( cs == null ) {
  181. cs = new SearchMap();
  182. c.set(i.id, cs);
  183. }
  184. c = cs;
  185. }
  186. if( c.linked == null )
  187. c.linked = compileRuntimeShader(shaders, mode);
  188. return c.linked;
  189. }
  190. function compileRuntimeShader( shaders : hxsl.ShaderList, mode : LinkMode ) {
  191. var shaderDatas = [];
  192. var index = 0;
  193. for( s in shaders ) {
  194. var i = @:privateAccess s.instance;
  195. shaderDatas.push( { inst : i, p : s.priority, index : index++ } );
  196. }
  197. shaderDatas.reverse(); // default is reverse order
  198. /*
  199. Our shader list is supposedly already sorted. However some shaders
  200. with high priority might be prepended at later stage (eg: Base2D)
  201. So let's sort again just in case.
  202. */
  203. haxe.ds.ArraySort.sort(shaderDatas, function(s1, s2) return s2.p - s1.p);
  204. #if debug
  205. for( s in shaderDatas ) Printer.check(s.inst.shader);
  206. #end
  207. #if shader_debug_while
  208. for( s in shaders ) {
  209. function checkRec( e : TExpr ) {
  210. switch( e.e ) {
  211. case TWhile(cond,_,_):
  212. var name = @:privateAccess s.shader.data.name;
  213. haxe.Log.trace("FOUND SHADER while( "+Printer.toString(cond)+")", { fileName : name, methodName : "", lineNumber : 0, className : name });
  214. default:
  215. }
  216. e.iter(checkRec);
  217. }
  218. for( f in @:privateAccess s.instance.shader.funs )
  219. checkRec(f.expr);
  220. }
  221. #end
  222. #if shader_debug_dump
  223. var shaderId = @:privateAccess RuntimeShader.UID;
  224. #if ( js && !sys && !hxnodejs )
  225. if( shaderId == 0 ) js.Syntax.code("window.shaders_debug_dump = [];");
  226. js.Syntax.code("window.shaders_debug_dump[{0}] = '';", shaderId);
  227. var dbg: { writeString: String->Void, close:Void->Void } = {
  228. writeString: (str: String) -> { js.Syntax.code("window.shaders_debug_dump[{0}] += {1}", shaderId, str); },
  229. close: () -> {}
  230. };
  231. #else
  232. if( shaderId == 0 ) try sys.FileSystem.createDirectory("shaders") catch( e : Dynamic ) {};
  233. var dbg = sys.io.File.write("shaders/"+shaderId+"_dump.c");
  234. #end
  235. var oldTrace = haxe.Log.trace;
  236. haxe.Log.trace = function(msg,?pos) dbg.writeString(haxe.Log.formatOutput(msg,pos)+"\n");
  237. if( dbg != null ) {
  238. dbg.writeString("----- DATAS ----\n\n");
  239. for( s in shaderDatas ) {
  240. dbg.writeString("\t\t**** " + s.inst.shader.name + (s.p == 0 ? "" : " P="+s.p)+ " *****\n");
  241. dbg.writeString(Printer.shaderToString(s.inst.shader,Debug.VAR_IDS)+"\n\n");
  242. }
  243. }
  244. //TRACE = shaderId == 0;
  245. #end
  246. var linker = new hxsl.Linker(mode);
  247. var s = try linker.link([for( s in shaderDatas ) s.inst.shader]) catch( e : Error ) {
  248. var shaders = [for( s in shaderDatas ) Printer.shaderToString(s.inst.shader)];
  249. e.msg += "\n\nin\n\n" + shaders.join("\n-----\n");
  250. throw e;
  251. }
  252. if( mode == Batch ) {
  253. function checkRec( v : TVar ) {
  254. if( v.qualifiers != null && v.qualifiers.indexOf(PerObject) >= 0 ) {
  255. if( v.qualifiers.length == 1 ) v.qualifiers = null else {
  256. v.qualifiers = v.qualifiers.copy();
  257. v.qualifiers.remove(PerObject);
  258. }
  259. if( v.kind != Var ) v.kind = Local;
  260. }
  261. switch( v.type ) {
  262. case TStruct(vl):
  263. for( v in vl )
  264. checkRec(v);
  265. default:
  266. }
  267. }
  268. for( v in s.vars )
  269. checkRec(v);
  270. }
  271. #if shader_debug_dump
  272. if( dbg != null ) {
  273. dbg.writeString("----- LINK ----\n\n");
  274. dbg.writeString(Printer.shaderToString(s,Debug.VAR_IDS)+"\n\n");
  275. }
  276. #end
  277. #if debug
  278. Printer.check(s,[for( s in shaderDatas ) s.inst.shader]);
  279. #end
  280. var prev = s;
  281. var splitter = new hxsl.Splitter();
  282. var sl = try splitter.split(s, mode == Batch ) catch( e : Error ) { e.msg += "\n\nin\n\n"+Printer.shaderToString(s); throw e; };
  283. // params tracking
  284. var paramVars = new Map();
  285. for( v in linker.allVars )
  286. if( v.v.kind == Param ) {
  287. switch( v.v.type ) {
  288. case TStruct(_): continue;
  289. default:
  290. }
  291. var inf = shaderDatas[v.instanceIndex];
  292. var nv = @:privateAccess splitter.varMap.get(v.v);
  293. paramVars.set(nv == null ? v.id : nv.id, { instance : inf.index, index : inf.inst.params.get(v.merged[0].id) } );
  294. }
  295. #if shader_debug_dump
  296. if( dbg != null ) {
  297. dbg.writeString("----- SPLIT ----\n\n");
  298. for( s in sl )
  299. dbg.writeString(Printer.shaderToString(s, Debug.VAR_IDS) + "\n\n");
  300. }
  301. #end
  302. #if debug
  303. for( s in sl )
  304. Printer.check(s,[prev]);
  305. #end
  306. var prev = sl;
  307. var sl = new hxsl.Dce().dce(sl);
  308. #if shader_debug_dump
  309. if( dbg != null ) {
  310. dbg.writeString("----- DCE ----\n\n");
  311. for( s in sl )
  312. dbg.writeString(Printer.shaderToString(s, Debug.VAR_IDS) + "\n\n");
  313. }
  314. #end
  315. #if debug
  316. for( i => s in sl )
  317. Printer.check(s,[prev[i]]);
  318. #end
  319. var r = buildRuntimeShader(sl, paramVars);
  320. r.mode = mode;
  321. #if shader_debug_dump
  322. if( dbg != null ) {
  323. dbg.writeString("----- FLATTEN ----\n\n");
  324. for( s in r.getShaders() )
  325. dbg.writeString(Printer.shaderToString(s.data, Debug.VAR_IDS) + "\n\n");
  326. }
  327. #end
  328. r.spec = { instances : @:privateAccess [for( s in shaders ) new ShaderInstanceDesc(s.shader, s.constBits)], signature : null };
  329. for( i in 0...shaderDatas.length ) {
  330. var s = shaderDatas[shaderDatas.length - 1 - i];
  331. r.spec.instances[s.index].index = i;
  332. }
  333. var signParts = [for( i in r.spec.instances ) i.shader.data.name+"_" + i.bits + "_" + i.index];
  334. r.spec.signature = haxe.crypto.Md5.encode(signParts.join(":"));
  335. r.signature = haxe.crypto.Md5.encode([for( s in r.getShaders() ) Printer.shaderToString(s.data)].join(""));
  336. var r2 = byID.get(r.signature);
  337. if( r2 != null )
  338. r.id = r2.id; // same id but different variable mapping
  339. else
  340. byID.set(r.signature, r);
  341. #if shader_debug_dump
  342. dbg.writeString("---- OUTPUT -----\n\n");
  343. dbg.writeString(h3d.Engine.getCurrent().driver.getNativeShaderCode(r)+"\n\n");
  344. if( dbg != null ) dbg.close();
  345. haxe.Log.trace = oldTrace;
  346. #end
  347. return r;
  348. }
  349. function buildRuntimeShader( shaders : Array<ShaderData>, paramVars ) {
  350. var r = new RuntimeShader();
  351. r.globals = new Map();
  352. for( s in shaders ) {
  353. var kind = switch( s.name ) {
  354. case "vertex": Vertex;
  355. case "fragment": Fragment;
  356. case "main": Main;
  357. default: throw "assert";
  358. }
  359. var fl = flattenShader(s, kind, paramVars);
  360. fl.kind = kind;
  361. switch( kind ) {
  362. case Vertex:
  363. r.vertex = fl;
  364. case Fragment:
  365. r.fragment = fl;
  366. case Main:
  367. r.compute = fl;
  368. default:
  369. throw "assert";
  370. }
  371. initGlobals(r, fl);
  372. #if debug
  373. Printer.check(fl.data,[s]);
  374. #end
  375. }
  376. return r;
  377. }
  378. function initGlobals( r : RuntimeShader, s : RuntimeShaderData ) {
  379. var p = s.globals;
  380. while( p != null ) {
  381. r.globals.set(p.gid, true);
  382. p = p.next;
  383. }
  384. var p = s.params;
  385. while( p != null ) {
  386. if( p.perObjectGlobal != null )
  387. r.globals.set(p.perObjectGlobal.gid, true);
  388. p = p.next;
  389. }
  390. }
  391. function getPath( v : TVar ) {
  392. if( v.parent == null )
  393. return v.name;
  394. return getPath(v.parent) + "." + v.name;
  395. }
  396. function flattenShader( s : ShaderData, kind : FunctionKind, params : Map<Int,{ instance:Int, index:Int }> ) {
  397. var flat = new Flatten();
  398. var c = new RuntimeShaderData();
  399. var data = flat.flatten(s, kind);
  400. #if (hl && heaps_compact_mem)
  401. data = hl.Api.compact(data, null, 0, null);
  402. #end
  403. var textures = [];
  404. var buffers = [];
  405. c.texturesCount = 0;
  406. for( g in flat.allocData.keys() ) {
  407. var alloc = flat.allocData.get(g);
  408. switch( g.kind ) {
  409. case Param:
  410. var out = [];
  411. var count = 0;
  412. for( a in alloc ) {
  413. if( a.v == null ) continue; // padding
  414. var p = params.get(a.v.id);
  415. if( p == null ) {
  416. var ap = new AllocParam(a.v.name, a.pos, -1, -1, a.v.type);
  417. ap.perObjectGlobal = new AllocGlobal( -1, getPath(a.v), a.v.type);
  418. out.push(ap);
  419. count++;
  420. continue;
  421. }
  422. var ap = new AllocParam(a.v.name, a.pos, p.instance, p.index, a.v.type);
  423. switch( a.v.type ) {
  424. case TArray(t,_) if( t.isTexture() ):
  425. // hack to mark array of texture, see ShaderManager.fillParams
  426. ap.pos = -a.size;
  427. count += a.size;
  428. default:
  429. count++;
  430. }
  431. out.push(ap);
  432. }
  433. for( i in 0...out.length - 1 )
  434. out[i].next = out[i + 1];
  435. switch( g.type ) {
  436. case TArray(t, _) if( t.isTexture() ):
  437. textures.push({ t : t, all : out });
  438. c.texturesCount += count;
  439. case TArray(TVec(4, VFloat), SConst(size)):
  440. c.params = out[0];
  441. c.paramsSize = size;
  442. case TArray(TBuffer(_, _, kind), _):
  443. for( outBuf in out )
  444. if ( outBuf != null )
  445. buffers.push(outBuf);
  446. default: throw "assert";
  447. }
  448. case Global:
  449. var out = [for( a in alloc ) if( a.v != null ) new AllocGlobal(a.pos, getPath(a.v), a.v.type)];
  450. for( i in 0...out.length - 1 )
  451. out[i].next = out[i + 1];
  452. switch( g.type ) {
  453. case TArray(TVec(4, VFloat),SConst(size)):
  454. c.globals = out[0];
  455. c.globalsSize = size;
  456. default:
  457. throw "assert";
  458. }
  459. default: throw "assert";
  460. }
  461. }
  462. if( textures.length > 0 ) {
  463. // relink in order based on type
  464. textures.sort(function(t1,t2) {
  465. return switch ( [t1.t, t2.t] ) {
  466. case [TSampler(t1, a1), TSampler(t2, a2)]:
  467. if ( a1 != a2 )
  468. a1 ? 1 : -1;
  469. else
  470. t1.getIndex() - t2.getIndex();
  471. case [TRWTexture(t1, a1, _), TRWTexture(t2, a2, _)]:
  472. if ( a1 != a2 )
  473. a1 ? 1 : -1;
  474. else
  475. t1.getIndex() - t2.getIndex();
  476. default :
  477. t1.t.getIndex() - t2.t.getIndex();
  478. }
  479. });
  480. c.textures = textures[0].all[0];
  481. for( i in 1...textures.length ) {
  482. var prevAll = textures[i-1].all;
  483. var prev = prevAll[prevAll.length - 1];
  484. prev.next = textures[i].all[0];
  485. }
  486. }
  487. if ( buffers.length > 0 ) {
  488. buffers.sort(function(b1, b2) {
  489. return switch ( [b1.type, b2.type] ) {
  490. case [TBuffer(_, _, k1), TBuffer(_, _, k2)]:
  491. k1.getIndex() - k2.getIndex();
  492. default:
  493. b1.type.getIndex() - b2.type.getIndex();
  494. }
  495. });
  496. }
  497. var p = null;
  498. for ( b in buffers ) {
  499. if ( c.buffers == null ) {
  500. c.buffers = b;
  501. p = c.buffers;
  502. } else {
  503. p.next = b;
  504. p = p.next;
  505. }
  506. }
  507. c.bufferCount = buffers.length;
  508. if( c.globals == null )
  509. c.globalsSize = 0;
  510. if( c.params == null )
  511. c.paramsSize = 0;
  512. c.data = data;
  513. return c;
  514. }
  515. public function makeBatchShader( rt : RuntimeShader, shaders, params : BatchInstanceParams ) : BatchShader {
  516. var batchMap;
  517. if( params == null )
  518. batchMap = batchShaders;
  519. else {
  520. batchMap = batchShadersParams.get(params.getSignature());
  521. if( batchMap == null ) {
  522. batchMap = new Map();
  523. batchShadersParams.set(params.getSignature(),batchMap);
  524. }
  525. }
  526. var sh = batchMap.get(rt); // don't use rt.id to avoid collisions on identical signatures
  527. if( sh == null ) {
  528. sh = createBatchShader(rt, shaders, params);
  529. batchMap.set(rt, sh);
  530. }
  531. var shader = std.Type.createEmptyInstance(BatchShader);
  532. @:privateAccess shader.shader = sh.shader;
  533. shader.params = sh.params;
  534. shader.paramsSize = sh.size;
  535. return shader;
  536. }
  537. function isPerInstance(v:TVar) {
  538. if( v.qualifiers == null )
  539. return false;
  540. for( q in v.qualifiers )
  541. if( q.match(PerInstance(_) | PerObject) )
  542. return true;
  543. return false;
  544. }
  545. function createBatchShader( rt : RuntimeShader, shaders : hxsl.ShaderList, params : BatchInstanceParams ) : { shader : SharedShader, params : RuntimeShader.AllocParam, size : Int } {
  546. var s = new hxsl.SharedShader("");
  547. var id = (params == null ? rt.spec.signature : haxe.crypto.Md5.encode(rt.spec.signature + params.getSignature())).substr(0, 8);
  548. function declVar( name, t, kind ) : TVar {
  549. return {
  550. id : Tools.allocVarId(),
  551. type : t,
  552. name : name,
  553. kind : kind,
  554. };
  555. }
  556. var instancedParams = [];
  557. if( params != null ) {
  558. var forcedPerInstance = @:privateAccess params.forcedPerInstance;
  559. var instanceIndex = 1;
  560. var forcedIndex = forcedPerInstance.length - 1;
  561. var s = shaders;
  562. while( s != null && forcedIndex >= 0 ) {
  563. if( @:privateAccess s.s.shader.data.name == forcedPerInstance[forcedIndex].shader ) {
  564. instancedParams[instanceIndex] = forcedPerInstance[forcedIndex].params;
  565. forcedIndex--;
  566. }
  567. instanceIndex++;
  568. s = s.next;
  569. }
  570. }
  571. var pos = null;
  572. var hasOffset = declVar("Batch_HasOffset",TBool,Param);
  573. var inputOffset = declVar("Batch_Start",TFloat,Input);
  574. hasOffset.qualifiers = [Const()];
  575. inputOffset.qualifiers = [PerInstance(1)];
  576. var useStorage = declVar("Batch_UseStorage",TBool,Param);
  577. var vcount = declVar("Batch_Count",TInt,Param);
  578. var vuniformBuffer = declVar("Batch_Buffer",TBuffer(TVec(4,VFloat),SVar(vcount),Uniform),Param);
  579. var vstorageBuffer = declVar("Batch_StorageBuffer",TBuffer(TVec(4,VFloat),SConst(0),RW),Param);
  580. var voffset = declVar("Batch_Offset", TInt, Local);
  581. var euniformBuffer = { e : TVar(vuniformBuffer), p : pos, t : vuniformBuffer.type };
  582. var estorageBuffer = { e : TVar(vstorageBuffer), p : pos, t : vstorageBuffer.type };
  583. var eoffset = { e : TVar(voffset), p : pos, t : voffset.type };
  584. var tvec4 = TVec(4,VFloat);
  585. var countBits = 16;
  586. vcount.qualifiers = [Const(1 << countBits)];
  587. useStorage.qualifiers = [Const()];
  588. s.data = {
  589. name : "batchShader_"+id,
  590. vars : [vcount,hasOffset,useStorage,vuniformBuffer,vstorageBuffer,voffset,inputOffset],
  591. funs : [],
  592. };
  593. function getVarRec( v : TVar, name, kind ) {
  594. if( v.kind == kind && v.name == name )
  595. return v;
  596. switch( v.type ) {
  597. case TStruct(vl):
  598. for( v in vl ) {
  599. var v = getVarRec(v, name, kind);
  600. if( v != null ) return v;
  601. }
  602. default:
  603. }
  604. return null;
  605. }
  606. function getVar( p : RuntimeShader.AllocParam ) {
  607. var s = shaders;
  608. if( p.perObjectGlobal != null ) {
  609. var path = p.perObjectGlobal.path.split(".");
  610. while( s != null ) {
  611. for( v in @:privateAccess s.s.shader.data.vars ) {
  612. if( v.name != path[0] ) continue;
  613. var v = getVarRec(v, p.name, Global);
  614. if( v != null ) return v;
  615. }
  616. s = s.next;
  617. }
  618. } else {
  619. var i = p.instance - 1;
  620. while( i > 0 ) {
  621. i--;
  622. s = s.next;
  623. }
  624. var name = p.name;
  625. while( true ) {
  626. for( v in @:privateAccess s.s.shader.data.vars ) {
  627. var v = getVarRec(v, name, Param);
  628. if( v != null ) return v;
  629. }
  630. var cc = name.charCodeAt(name.length - 1);
  631. if( cc >= '0'.code && cc <= '9'.code ) name = name.substr(0,-1) else break;
  632. }
  633. }
  634. throw "Var not found "+p.name;
  635. }
  636. var params = null;
  637. var used = [];
  638. var added = [];
  639. function addParam(p:RuntimeShader.AllocParam) {
  640. var pid = p.perObjectGlobal != null ? -p.perObjectGlobal.gid : p.instance * 1024 + p.index;
  641. if( added.indexOf(pid) >= 0 )
  642. return;
  643. added.push(pid);
  644. var size = switch( p.type ) {
  645. case TMat4: 4 * 4;
  646. case TVec(n,VFloat): n;
  647. case TFloat: 1;
  648. default: throw "Unsupported batch var type "+p.type;
  649. }
  650. var index;
  651. if( size >= 4 ) {
  652. index = used.length << 2;
  653. for( i in 0...size>>2 )
  654. used.push(15);
  655. } else if( size == 1 ) {
  656. var best = -1;
  657. for( i in 0...used.length )
  658. if( used[i] != 15 && (best < 0 || used[best] < used[i]) )
  659. best = i;
  660. if( best < 0 ) {
  661. best = used.length;
  662. used.push(0);
  663. }
  664. index = best << 2;
  665. for( k in 0...4 ) {
  666. var bit = 3 - k;
  667. if( used[best] & (1 << bit) == 0 ) {
  668. used[best] |= 1 << bit;
  669. index += bit;
  670. break;
  671. }
  672. }
  673. } else {
  674. var k = size == 2 ? 3 : 7;
  675. var best = -1;
  676. for( i in 0...used.length )
  677. if( used[i] & k == 0 ) {
  678. used[i] |= k;
  679. best = i;
  680. break;
  681. }
  682. if( best < 0 ) {
  683. best = used.length;
  684. used.push(k);
  685. }
  686. index = best << 2;
  687. }
  688. var p2 = new AllocParam(p.name, index, p.instance, p.index, p.type);
  689. p2.perObjectGlobal = p.perObjectGlobal;
  690. p2.next = params;
  691. params = p2;
  692. }
  693. inline function isPerInstance(p:RuntimeShader.AllocParam,v:TVar) {
  694. var params = instancedParams[p.instance];
  695. if( params != null && params.indexOf(v.name) >= 0 )
  696. return true;
  697. if( this.isPerInstance(v) )
  698. return true;
  699. return false;
  700. }
  701. var p = rt.vertex.params;
  702. while( p != null ) {
  703. var v = getVar(p);
  704. if( isPerInstance(p, v) )
  705. addParam(p);
  706. p = p.next;
  707. }
  708. var p = rt.fragment.params;
  709. while( p != null ) {
  710. var v = getVar(p);
  711. if( isPerInstance(p, v) )
  712. addParam(p);
  713. p = p.next;
  714. }
  715. var parentVars = new Map();
  716. var swiz = [[X],[Y],[Z],[W]];
  717. function readOffset( ebuffer, index : Int ) : TExpr {
  718. return { e : TArray(ebuffer,{ e : TBinop(OpAdd,eoffset,{ e : TConst(CInt(index)), t : TInt, p : pos }), t : TInt, p : pos }), t : tvec4, p : pos };
  719. }
  720. function declareLocalVar( v : AllocParam ) {
  721. var vreal : TVar = declVar(v.name, v.type, Local);
  722. if( v.perObjectGlobal != null ) {
  723. var path = v.perObjectGlobal.path.split(".");
  724. path.pop();
  725. var cur = vreal;
  726. while( path.length > 0 ) {
  727. var key = path.join(".");
  728. var name = path.pop();
  729. var vp = parentVars.get(path);
  730. if( vp == null ) {
  731. vp = declVar(name,TStruct([]),Local);
  732. parentVars.set(path,vp);
  733. }
  734. switch( vp.type ) {
  735. case TStruct(vl): vl.push(cur);
  736. default:
  737. }
  738. cur.parent = vp;
  739. cur = vp;
  740. }
  741. }
  742. s.data.vars.push(vreal);
  743. return vreal;
  744. }
  745. function extractVar( vreal, ebuffer, v : AllocParam ) {
  746. var index = (v.pos>>2);
  747. var extract = switch( v.type ) {
  748. case TMat4:
  749. { p : pos, t : v.type, e : TCall({ e : TGlobal(Mat4), t : TVoid, p : pos },[
  750. readOffset(ebuffer, index),
  751. readOffset(ebuffer, index + 1),
  752. readOffset(ebuffer, index + 2),
  753. readOffset(ebuffer, index + 3),
  754. ]) };
  755. case TVec(4,VFloat):
  756. readOffset(ebuffer, index);
  757. case TVec(3,VFloat):
  758. { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),v.pos&3 == 0 ? [X,Y,Z] : [Y,Z,W]) };
  759. case TVec(2,VFloat):
  760. var swiz = switch( v.pos & 3 ) {
  761. case 0: [X,Y];
  762. case 1: [Y,Z];
  763. default: [Z,W];
  764. }
  765. { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),swiz) };
  766. case TFloat:
  767. { p : pos, t : v.type, e : TSwiz(readOffset(ebuffer, index),swiz[v.pos&3]) }
  768. default:
  769. throw "assert";
  770. }
  771. return { p : pos, e : TBinop(OpAssign, { e : TVar(vreal), p : pos, t : v.type }, extract), t : TVoid };
  772. }
  773. var exprsUniform = [];
  774. var exprsStorage = [];
  775. var stride = used.length;
  776. var p = params;
  777. while( p != null ) {
  778. var vreal = declareLocalVar(p);
  779. exprsUniform.push(extractVar(vreal, euniformBuffer, p));
  780. exprsStorage.push(extractVar(vreal, estorageBuffer, p));
  781. p = p.next;
  782. }
  783. var inits = [];
  784. inits.push({
  785. p : pos,
  786. e : TBinop(OpAssign, eoffset, { e : TGlobal(InstanceID), t : TInt, p : pos }),
  787. t : TVoid,
  788. });
  789. // when Batch_hasOffset is set to true, have InstanceID somewhat emulate DrawID
  790. inits.push({
  791. p : pos,
  792. e : TIf({ e : TVar(hasOffset), t : TBool, p : pos },{
  793. p : pos,
  794. e : TBinop(OpAssignOp(OpAdd), eoffset, { e : TCall({ e : TGlobal(ToInt), t : TVoid, p : pos },[{ p : pos, t : TFloat, e : TVar(inputOffset) }]), t : TInt, p : pos }),
  795. t : TVoid,
  796. }, null),
  797. t : TVoid,
  798. });
  799. inits.push({
  800. p : pos,
  801. t : TInt,
  802. e : TBinop(OpAssignOp(OpMult),eoffset,{ e : TConst(CInt(stride)), t : TInt, p : pos }),
  803. });
  804. inits.push({
  805. p : pos,
  806. e : TIf({ e : TVar(useStorage), t : TBool, p : pos },{
  807. p : pos,
  808. e : TBlock(exprsStorage),
  809. t : TVoid,
  810. }, {
  811. p : pos,
  812. e : TBlock(exprsUniform),
  813. t : TVoid,
  814. }),
  815. t : TVoid,
  816. });
  817. var fv : TVar = declVar("init",TFun([]), Function);
  818. var f : TFunction = {
  819. kind : Init,
  820. ref : fv,
  821. args : [],
  822. ret : TVoid,
  823. expr : { e : TBlock(inits), p : pos, t : TVoid },
  824. };
  825. s.data.funs.push(f);
  826. s.consts = new SharedShader.ShaderConst(vcount,2,countBits+1);
  827. s.consts.globalId = 0;
  828. s.consts.next = new SharedShader.ShaderConst(hasOffset,0,1);
  829. s.consts.next.globalId = 0;
  830. s.consts.next.next = new SharedShader.ShaderConst(useStorage,1,1);
  831. s.consts.next.next.globalId = 0;
  832. return { shader : s, params : params, size : stride };
  833. }
  834. static var INST : Cache;
  835. public static function get() : Cache {
  836. var c = INST;
  837. if( c == null )
  838. INST = c = new Cache();
  839. return c;
  840. }
  841. public static function set(c) {
  842. INST = c;
  843. }
  844. public static function clear() {
  845. INST = null;
  846. }
  847. }