CacheFile.hx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. package hxsl;
  2. import hxsl.Ast.Tools;
  3. #if (sys || nodejs)
  4. private class NullShader extends hxsl.Shader {
  5. static var SRC = {
  6. var output : {
  7. var position : Vec4;
  8. var frag : Vec4;
  9. };
  10. function vertex() {
  11. output.position = vec4(0.);
  12. }
  13. function fragment() {
  14. output.frag = vec4(0.);
  15. }
  16. };
  17. }
  18. class CacheFile extends Cache {
  19. public static var FILENAME = "res/shaders.cache";
  20. var allowCompile : Bool;
  21. var recompileRT : Bool;
  22. var waitCount : Int = 0;
  23. var isLoading : Bool;
  24. var file : String;
  25. var sourceFile : String;
  26. // instances
  27. var shaders : Map<String,{ shader : SharedShader, version : String }> = new Map();
  28. var runtimeShaders : Array<RuntimeShader> = [];
  29. var linkers : Array<{ shader : Shader, vars : Array<hxsl.Output> }> = [];
  30. var batchers : Array<{ shader : SharedShader, rt : RuntimeShader, params : hxsl.Cache.BatchInstanceParams }> = [];
  31. // sources
  32. var compiledSources : Map<String,{ vertex : String, fragment : String }> = new Map();
  33. var allSources : Map<String,String> = new Map();
  34. public var allowSave = #if usesys false #else true #end;
  35. public function new( allowCompile, recompileRT = false ) {
  36. super();
  37. this.allowCompile = allowCompile;
  38. this.recompileRT = recompileRT || allowCompile;
  39. this.file = FILENAME;
  40. sourceFile = this.file + "." + getPlatformTag();
  41. load();
  42. }
  43. function getPlatformTag() {
  44. #if usesys
  45. return Sys.systemName().toLowerCase();
  46. #elseif hlsdl
  47. return "gl";
  48. #elseif (hldx && dx12)
  49. return "dx12";
  50. #elseif hldx
  51. return "dx";
  52. #elseif hlnx
  53. return "nx";
  54. #else
  55. return "unk";
  56. #end
  57. }
  58. override function getLinkShader( vars : Array<hxsl.Output> ) {
  59. var shader = super.getLinkShader(vars);
  60. for( l in linkers )
  61. if( l.shader == shader )
  62. return shader;
  63. linkers.push({ shader : shader, vars : vars.copy() });
  64. return shader;
  65. }
  66. override function createBatchShader( rt, shaders, params ) {
  67. var b = super.createBatchShader(rt, shaders, params);
  68. batchers.push({ rt : rt, shader : b.shader, params : params });
  69. return b;
  70. }
  71. static var HEX = "0123456789abcdef";
  72. function load(showProgress=false) {
  73. isLoading = true;
  74. var t0 = haxe.Timer.stamp();
  75. var wait = [];
  76. if( sys.FileSystem.exists(file) ) {
  77. loadShaders();
  78. if( sys.FileSystem.exists(sourceFile) )
  79. loadSources();
  80. else if( !allowCompile )
  81. throw "Missing " + sourceFile;
  82. if( allowCompile ) {
  83. // update missing shader sources (after platform switch)
  84. for( r in runtimeShaders )
  85. if( r.vertex.code == null || r.fragment.code == null )
  86. wait.push(r);
  87. }
  88. log(runtimeShaders.length+" shaders loaded in "+hxd.Math.fmt(haxe.Timer.stamp() - t0)+"s");
  89. } else if( !allowCompile )
  90. throw "Missing " + file;
  91. if( linkCache.linked == null ) {
  92. var rt = link(makeDefaultShader(), Default);
  93. linkCache.linked = rt;
  94. if( rt.vertex.code == null || rt.fragment.code == null ) {
  95. wait.push(rt);
  96. if( !allowCompile ) throw "Missing default shader code";
  97. }
  98. }
  99. if( wait.length > 0 ) {
  100. var fullCount = wait.length;
  101. waitCount += wait.length;
  102. #if hlmulti
  103. var t1 = haxe.Timer.stamp();
  104. for( r in wait ) {
  105. if (showProgress && (waitCount % 5 == 0 || waitCount <= 1)) {
  106. var progress = Std.int((1 - (waitCount / fullCount)) * 1000) / 10;
  107. Sys.print('$progress% \t(${fullCount - waitCount}/$fullCount) \r');
  108. }
  109. addNewShader(r);
  110. hxd.System.timeoutTick();
  111. }
  112. if (showProgress) {
  113. log("");
  114. var t = haxe.Timer.stamp() - t1;
  115. var m = hxd.Math.round(t / 60);
  116. var s = t - m * 60;
  117. log('$waitCount generated in ${m}m ${hxd.Math.fmt(s)}s');
  118. }
  119. #else
  120. haxe.Timer.delay(function() {
  121. for( r in wait ) {
  122. addNewShader(r);
  123. hxd.System.timeoutTick();
  124. }
  125. },1000); // wait until engine correctly initialized
  126. #end
  127. }
  128. isLoading = false;
  129. }
  130. function readString(f:haxe.io.Input) {
  131. var len = f.readByte();
  132. if( len == 0 ) return null;
  133. if( len == 0xFF ) len = f.readInt32();
  134. return f.readString(len - 1);
  135. }
  136. function resolveShader( name : String ) : hxsl.Shader {
  137. if ( StringTools.endsWith(name, ".shgraph") ) {
  138. #if hide
  139. var shgraph : hrt.shgraph.ShaderGraph = try cast hxd.res.Loader.currentInstance.load(name).toPrefab().load() catch( e : hxd.res.NotFound ) null;
  140. if (shgraph == null)
  141. return null;
  142. return shgraph.makeShaderInstance();
  143. #else
  144. return null;
  145. #end
  146. }
  147. var cl = Type.resolveClass(name);
  148. if( cl == null )
  149. return null;
  150. var shader : hxsl.Shader = Type.createEmptyInstance(cl);
  151. @:privateAccess shader.initialize();
  152. return shader;
  153. }
  154. function loadShaders() {
  155. var f = new haxe.io.BytesInput(sys.io.File.getBytes(file));
  156. var version = f.readInt32();
  157. function readString() {
  158. return this.readString(f);
  159. }
  160. function readStringOpt() {
  161. var len = f.readByte();
  162. if( len == '\n'.code ) return null;
  163. if( len == 0xFF ) len = f.readInt32();
  164. return f.readString(len - 1);
  165. }
  166. function unserialize() {
  167. return haxe.Unserializer.run(readString());
  168. }
  169. function readIntHex() {
  170. var len = f.readByte();
  171. if( len == 0 )
  172. return 0;
  173. var x = 0;
  174. for( i in 0...len ) {
  175. var c = f.readByte();
  176. if( c >= "0".code && c <= "9".code )
  177. c -= "0".code;
  178. else if( c >= "a".code && c <= "f".code )
  179. c -= "a".code - 10;
  180. else
  181. c -= "A".code - 10;
  182. x |= c << ((len - 1 - i) * 4);
  183. }
  184. return x;
  185. }
  186. function skip() {
  187. if( f.readByte() != "\n".code ) throw "assert";
  188. }
  189. linkers = [];
  190. var linkMap = new Map();
  191. while( true ) {
  192. skip();
  193. var name = readString();
  194. if( name == null ) break;
  195. var vars : Array<hxsl.Output> = unserialize();
  196. var shader = getLinkShader(vars);
  197. linkMap.set(name, shader);
  198. }
  199. batchers = [];
  200. var batchMap = new Map();
  201. var alreadySkipped = false;
  202. while( true ) {
  203. if ( !alreadySkipped ) skip();
  204. var name = readString();
  205. if( name == null ) break;
  206. var rt = readString();
  207. var str = readStringOpt();
  208. alreadySkipped = str == null;
  209. var params = null;
  210. if( str != null )
  211. params = new hxsl.Cache.BatchInstanceParams([for( s in str.split(";") ) { var v = s.split("="); { shader : v[0], params : v[1].split(",") }}]);
  212. batchMap.set(name, { rt : rt, params : params });
  213. }
  214. shaders = new Map();
  215. while( true ) {
  216. skip();
  217. var name = readString();
  218. if( name == null ) break;
  219. var version = readString();
  220. var shader = linkMap.get(name);
  221. if( shader == null ) {
  222. shader = resolveShader(name);
  223. if( shader == null ) {
  224. log("Missing shader " + name);
  225. continue;
  226. }
  227. }
  228. var shader = @:privateAccess shader.shader;
  229. if( getShaderVersion(shader) != version ) {
  230. Sys.println("Shader " + name+" version differs");
  231. continue;
  232. }
  233. shaders.set(name, { shader : shader, version : version });
  234. }
  235. // read runtime shaders specs
  236. var runtimes = [];
  237. while( true ) {
  238. skip();
  239. var specSign = readString();
  240. if( specSign == null )
  241. break;
  242. var missingShader = false;
  243. var inst = [for( i in 0...f.readByte() ) {
  244. var name = readString();
  245. var shader = shaders.get(name);
  246. var batch = null;
  247. if( shader == null ) {
  248. batch = batchMap.get(name);
  249. if( batch == null ) missingShader = true;
  250. }
  251. { shader : shader, batch : batch, bits : readIntHex(), index : f.readByte() };
  252. }];
  253. var sign = readString();
  254. if( missingShader ) continue;
  255. //log("Loading shader "+[for( i in inst ) (i.shader == null ? i.batch : i.shader.shader.data.name)+(i.bits == 0 ? "" : ":"+StringTools.hex(i.bits))].toString());
  256. runtimes.push({ signature : sign, specSign : specSign, inst: inst });
  257. }
  258. // recompile or load runtime shaders
  259. runtimeShaders = [];
  260. var rttMap = new Map<String,{ rt : RuntimeShader, shaders : hxsl.ShaderList }>();
  261. if( recompileRT ) {
  262. for( r in runtimes ) {
  263. var shaderList = null;
  264. var mode : RuntimeShader.LinkMode = Default;
  265. r.inst.reverse();
  266. for( i in r.inst ) {
  267. var s = Type.createEmptyInstance(hxsl.Shader);
  268. @:privateAccess {
  269. if( i.shader == null ) {
  270. var rt = rttMap.get(i.batch.rt);
  271. if( rt == null ) {
  272. r = null; // was modified
  273. break;
  274. }
  275. var sh = makeBatchShader(rt.rt, rt.shaders.next, i.batch.params);
  276. i.shader = { version : null, shader : sh.shader };
  277. mode = Batch;
  278. }
  279. s.constBits = i.bits;
  280. s.shader = i.shader.shader;
  281. s.instance = i.shader.shader.getInstance(i.bits);
  282. s.priority = i.index;
  283. }
  284. shaderList = new hxsl.ShaderList(s, shaderList);
  285. }
  286. if( r == null ) continue;
  287. //log("Recompile "+[for( s in shaderList ) shaderName(s)]);
  288. var rt = link(shaderList, mode); // will compile + update linkMap
  289. if( rt.spec.signature != r.specSign ) {
  290. var signParts = [for( i in rt.spec.instances ) i.shader.data.name+"_" + i.bits + "_" + i.index];
  291. throw "assert";
  292. }
  293. var rt2 = rttMap.get(r.specSign);
  294. if( rt2 != null ) throw "assert";
  295. runtimeShaders.push(rt);
  296. rttMap.set(r.specSign, { rt : rt, shaders : shaderList });
  297. }
  298. } else {
  299. var rtMap = new Map();
  300. for( r in runtimes )
  301. rtMap.set(r.specSign, r);
  302. while( true ) {
  303. skip();
  304. var sdata = readString();
  305. if( sdata == null ) break;
  306. var r : RuntimeShader = haxe.Unserializer.run(sdata);
  307. var spec = rtMap.get(r.signature);
  308. // shader was modified
  309. if( spec == null )
  310. continue;
  311. r.mode = Default;
  312. r.signature = spec.signature;
  313. var shaderList = null;
  314. spec.inst.reverse();
  315. for( i in spec.inst ) {
  316. var s = Type.createEmptyInstance(hxsl.Shader);
  317. @:privateAccess {
  318. if( i.shader == null ) {
  319. var rt = rttMap.get(i.batch.rt);
  320. if( rt == null ) {
  321. r = null; // was modified
  322. break;
  323. }
  324. var sh = makeBatchShader(rt.rt, rt.shaders.next, i.batch.params);
  325. i.shader = { version : null, shader : sh.shader };
  326. r.mode = Batch;
  327. }
  328. // pseudo instance
  329. var scache = i.shader.shader.instanceCache;
  330. var inst = scache.get(i.bits);
  331. if( inst == null ) {
  332. inst = new hxsl.SharedShader.ShaderInstance(i.shader.shader.data);
  333. scache.set(i.bits, inst);
  334. }
  335. s.constBits = i.bits;
  336. s.shader = i.shader.shader;
  337. s.instance = inst;
  338. s.priority = i.index;
  339. }
  340. shaderList = new hxsl.ShaderList(s, shaderList);
  341. }
  342. if( r == null ) continue;
  343. addToCache(r, shaderList);
  344. reviveRuntime(r);
  345. runtimeShaders.push(r);
  346. rttMap.set(spec.specSign, { rt : r, shaders : shaderList });
  347. }
  348. }
  349. }
  350. function addToCache( r : RuntimeShader, shaders : hxsl.ShaderList ) {
  351. var c = linkCache;
  352. for( s in shaders ) {
  353. var i = @:privateAccess s.instance;
  354. var cs = c.get(i.id);
  355. if( cs == null ) {
  356. cs = new Cache.SearchMap();
  357. c.set(i.id, cs);
  358. }
  359. c = cs;
  360. }
  361. c.linked = r;
  362. }
  363. function loadSources() {
  364. var f = new haxe.io.BytesInput(sys.io.File.getBytes(sourceFile));
  365. var version = f.readInt32();
  366. var runtimeMap = new Map();
  367. for( r in runtimeShaders )
  368. runtimeMap.set(r.signature, r);
  369. function readString() {
  370. return this.readString(f);
  371. }
  372. function skip() {
  373. if( f.readByte() != "\n".code ) throw "assert";
  374. }
  375. compiledSources = new Map();
  376. var allSigns = new Map();
  377. while( true ) {
  378. skip();
  379. var sign = readString();
  380. if( sign == null )
  381. break;
  382. var vertex = readString();
  383. var fragment = readString();
  384. if( compiledSources.exists(sign) ) throw "assert";
  385. if( !runtimeMap.exists(sign) )
  386. continue; // runtime shader was removed
  387. allSigns.set(vertex, true);
  388. allSigns.set(fragment, true);
  389. compiledSources.set(sign, { vertex : vertex, fragment : fragment });
  390. }
  391. allSources = new Map();
  392. var sourceMap = new Map();
  393. while( true ) {
  394. skip();
  395. var src = readString();
  396. if( src == null )
  397. break;
  398. var sign = getSourceSign(src);
  399. if( !allSigns.exists(sign) )
  400. continue;
  401. allSources.set(src, sign);
  402. sourceMap.set(sign, src);
  403. }
  404. // assign to runtime instances
  405. for( r in runtimeShaders ) {
  406. var s = compiledSources.get(r.signature);
  407. if( s == null ) {
  408. if( !recompileRT ) throw "Shader " + r.signature+" is missing source";
  409. continue;
  410. }
  411. r.vertex.code = sourceMap.get(s.vertex);
  412. r.fragment.code = sourceMap.get(s.fragment);
  413. if( r.vertex.code == null ) throw "Source " + r.signature + " is missing code " + s.vertex;
  414. if( r.fragment.code == null ) throw "Source " + r.signature + " is missing code " + s.fragment;
  415. }
  416. }
  417. function save() {
  418. if( !allowSave ) return;
  419. var out = new haxe.io.BytesOutput();
  420. out.writeInt32(1); // version
  421. function separator() {
  422. out.writeByte("\n".code);
  423. }
  424. function writeString(str:String,isOpt=false) {
  425. if( str == null ) {
  426. out.writeByte(0);
  427. return;
  428. }
  429. var bytes = haxe.io.Bytes.ofString(str);
  430. if( bytes.length < 254 || (isOpt && bytes.length == '\n'.code-1) )
  431. out.writeByte(bytes.length + 1);
  432. else {
  433. out.writeByte(0xFF);
  434. out.writeInt32(bytes.length + 1);
  435. }
  436. out.write(bytes);
  437. }
  438. function serialize(v:Dynamic) {
  439. var s = new haxe.Serializer();
  440. s.useCache = true;
  441. s.useEnumIndex = true;
  442. s.serialize(v);
  443. writeString(s.toString());
  444. }
  445. function writeIntHex( i : Int ) {
  446. if( i == 0 ) {
  447. out.writeByte(0);
  448. return;
  449. }
  450. // make it readable
  451. var str = StringTools.hex(i);
  452. out.writeByte(str.length);
  453. out.writeString(str);
  454. }
  455. // linkers
  456. linkers.sort(function(l1, l2) return @:privateAccess Reflect.compare(l1.shader.shader.data.name,l2.shader.shader.data.name));
  457. for( l in linkers ) {
  458. separator();
  459. writeString(@:privateAccess l.shader.shader.data.name);
  460. serialize(l.vars);
  461. }
  462. separator();
  463. writeString(null);
  464. // batchers
  465. batchers.sort(function(l1, l2) return @:privateAccess Reflect.compare(l1.shader.data.name,l2.shader.data.name));
  466. for( b in batchers ) {
  467. separator();
  468. writeString(@:privateAccess b.shader.data.name);
  469. writeString(@:privateAccess b.rt.spec.signature);
  470. if( b.params != null )
  471. writeString([for( s in @:privateAccess b.params.forcedPerInstance ) s.shader+"="+s.params.join(",")].join(";"), true);
  472. }
  473. separator();
  474. writeString(null);
  475. // shaders
  476. var shaders = [for( s in shaders ) s];
  477. shaders.sort(function(s1, s2) return Reflect.compare(s1.version, s2.version));
  478. for( s in shaders ) {
  479. separator();
  480. writeString(s.shader.data.name);
  481. writeString(s.version);
  482. }
  483. separator();
  484. writeString(null);
  485. // runtime shaders
  486. runtimeShaders.sort(sortBySpec);
  487. for( r in runtimeShaders ) {
  488. separator();
  489. writeString(r.spec.signature);
  490. if( r.spec.instances.length >= 255 ) throw "assert";
  491. out.writeByte(r.spec.instances.length);
  492. for( s in r.spec.instances ) {
  493. writeString(s.shader.data.name);
  494. writeIntHex(s.bits);
  495. out.writeByte(s.index);
  496. }
  497. writeString(r.signature);
  498. }
  499. separator();
  500. writeString(null);
  501. // save runtime shaders data
  502. for( r in runtimeShaders ) {
  503. separator();
  504. var r = cleanRuntime(r);
  505. var s = new haxe.Serializer();
  506. s.useCache = true;
  507. s.useEnumIndex = true;
  508. s.serialize(r);
  509. writeString(s.toString());
  510. }
  511. separator();
  512. writeString(null);
  513. try sys.FileSystem.createDirectory(new haxe.io.Path(file).dir) catch( e : Dynamic ) {};
  514. sys.io.File.saveBytes(file, out.getBytes());
  515. out = new haxe.io.BytesOutput();
  516. out.writeInt32(1); // version
  517. var sources = [for( k in compiledSources.keys() ) k];
  518. sources.sort(Reflect.compare);
  519. for( s in sources ) {
  520. separator();
  521. writeString(s);
  522. var src = compiledSources.get(s);
  523. writeString(src.vertex);
  524. writeString(src.fragment);
  525. }
  526. separator();
  527. writeString(null);
  528. var sources = [for( s in allSources.keys() ) s];
  529. sources.sort(Reflect.compare);
  530. for( s in sources ) {
  531. separator();
  532. writeString(s);
  533. }
  534. separator();
  535. writeString(null);
  536. sys.io.File.saveBytes(sourceFile, out.getBytes());
  537. }
  538. /**
  539. Returns a stripped down runtime shader that will not have enough data to be
  540. recompiled but will still allow to be used at runtime.
  541. **/
  542. function cleanRuntime( r : RuntimeShader ) {
  543. var rc = new RuntimeShader();
  544. @:privateAccess RuntimeShader.UID--; // unalloc id
  545. rc.id = 0;
  546. rc.signature = r.spec.signature; // store by spec, not by sign (dups)
  547. rc.vertex = cleanRuntimeData(r.vertex);
  548. rc.fragment = cleanRuntimeData(r.fragment);
  549. return rc;
  550. }
  551. function cleanRuntimeData(r:hxsl.RuntimeShader.RuntimeShaderData) {
  552. var rc = new hxsl.RuntimeShader.RuntimeShaderData();
  553. rc.kind = r.kind;
  554. rc.data = {
  555. name : null,
  556. vars : [],
  557. funs : null,
  558. };
  559. for( v in r.data.vars )
  560. if( v.kind == (r.kind == Vertex ? Input : Output) ) {
  561. rc.data.vars.push({
  562. id : v.id,
  563. name : v.name,
  564. kind : v.kind,
  565. type : v.type,
  566. });
  567. }
  568. rc.paramsSize = r.paramsSize;
  569. rc.globalsSize = r.globalsSize;
  570. rc.texturesCount = r.texturesCount;
  571. rc.bufferCount = r.bufferCount;
  572. if( r.params != null )
  573. rc.params = r.params.clone(true);
  574. if( r.globals != null )
  575. rc.globals = r.globals.clone(true);
  576. if( r.textures != null )
  577. rc.textures = r.textures.clone(true);
  578. if( r.buffers != null )
  579. rc.buffers = r.buffers.clone(true);
  580. return rc;
  581. }
  582. /**
  583. Puts things back after we load a cleaned up runtime shader
  584. **/
  585. function reviveRuntime( r : RuntimeShader ) {
  586. r.id = @:privateAccess hxsl.RuntimeShader.UID++;
  587. r.globals = new Map();
  588. reviveRuntimeData(r, r.vertex);
  589. reviveRuntimeData(r, r.fragment);
  590. }
  591. function reviveRuntimeData( r : RuntimeShader, rd : hxsl.RuntimeShader.RuntimeShaderData ) {
  592. function rvGlobal( g : RuntimeShader.AllocGlobal ) {
  593. if( g == null ) return;
  594. g.gid = Globals.allocID(g.path);
  595. rvGlobal(g.next);
  596. }
  597. function rvParam( a : RuntimeShader.AllocParam ) {
  598. if( a == null ) return;
  599. rvGlobal(a.perObjectGlobal);
  600. rvParam(a.next);
  601. }
  602. rvParam(rd.params);
  603. rvParam(rd.textures);
  604. rvParam(rd.buffers);
  605. rvGlobal(rd.globals);
  606. initGlobals(r, rd);
  607. }
  608. function sortBySpec( r1 : RuntimeShader, r2 : RuntimeShader ) {
  609. if( r1.mode != r2.mode )
  610. return r1.mode.getIndex() - r2.mode.getIndex();
  611. var minLen = hxd.Math.imin(r1.spec.instances.length, r2.spec.instances.length);
  612. for( i in 0...minLen ) {
  613. var i1 = r1.spec.instances[i];
  614. var i2 = r1.spec.instances[i];
  615. if( i1.shader != i2.shader )
  616. return Reflect.compare(i1.shader.data.name, i2.shader.data.name);
  617. }
  618. for( i in 0...minLen ) {
  619. var i1 = r1.spec.instances[i];
  620. var i2 = r1.spec.instances[i];
  621. if( i1.bits != i2.bits )
  622. return i1.bits - i2.bits;
  623. }
  624. return r1.spec.instances.length - r2.spec.instances.length;
  625. }
  626. function makeDefaultShader() {
  627. var link = getLinkShader([Value("output.frag")]);
  628. var def = new NullShader();
  629. def.updateConstants(null);
  630. return new hxsl.ShaderList(link, new hxsl.ShaderList(def));
  631. }
  632. function log( str : String ) {
  633. Sys.println(str);
  634. }
  635. function shaderName( s : hxsl.Shader ) @:privateAccess {
  636. var name = s.instance.shader.name;
  637. if( s.constBits != 0 )
  638. name += ":" + StringTools.hex(s.constBits);
  639. if( s.priority != 0 )
  640. name += "("+s.priority+")";
  641. if( s.constBits != 0 ) {
  642. var c = s.shader.consts;
  643. var consts = [];
  644. while( c != null ) {
  645. var bits = (s.constBits >> c.pos) & ((1 << c.bits) - 1);
  646. if( bits > 0 ) {
  647. switch( c.v.type ) {
  648. case TBool:
  649. consts.push(c.v.name);
  650. case TChannel(_):
  651. consts.push(c.v.name+"="+hxsl.Channel.createByIndex(bits&7)+"@"+(bits>>3));
  652. default:
  653. consts.push(c.v.name+"="+bits);
  654. }
  655. }
  656. c = c.next;
  657. }
  658. if( consts.length > 0 )
  659. name += consts.toString();
  660. }
  661. return name;
  662. }
  663. public dynamic function onMissingShader(shaders:hxsl.ShaderList) {
  664. log("Missing shader " + [for( s in shaders ) shaderName(s)]);
  665. return link(null, Default); // default fallback
  666. }
  667. public dynamic function onNewShader(r:RuntimeShader) {
  668. log("Compiled " + [for( i in r.spec.instances ) i.shader.data.name+(i.bits == 0 ? "" : ":" + StringTools.hex(i.bits))].join(" "));
  669. }
  670. override function compileRuntimeShader(shaders:hxsl.ShaderList, batchMode) {
  671. if( isLoading )
  672. return super.compileRuntimeShader(shaders, batchMode);
  673. if( allowCompile ) {
  674. // was not found in previous cache, let's compile and cache it
  675. var s = super.compileRuntimeShader(shaders, batchMode);
  676. onNewShader(s);
  677. waitCount++;
  678. haxe.Timer.delay(function() addNewShader(s), 0);
  679. return s;
  680. }
  681. return onMissingShader(shaders);
  682. }
  683. function getShaderVersion( s : SharedShader ) {
  684. return haxe.crypto.Md5.encode(Printer.shaderToString(s.data));
  685. }
  686. function addNewShader( s : RuntimeShader ) {
  687. if( runtimeShaders.indexOf(s) < 0 )
  688. runtimeShaders.push(s);
  689. if( allowSave ) addSource(s);
  690. for( i in s.spec.instances ) {
  691. var inst = shaders.get(i.shader.data.name);
  692. if( inst == null ) {
  693. if( s.mode == Batch && StringTools.startsWith(i.shader.data.name,"batchShader_") )
  694. continue;
  695. var version = getShaderVersion(i.shader);
  696. inst = { shader : i.shader, version : version };
  697. shaders.set(i.shader.data.name, inst);
  698. }
  699. }
  700. waitCount--;
  701. if( waitCount == 0 ) save();
  702. }
  703. function allocSource( s : String ) {
  704. var sign = allSources.get(s);
  705. if( sign == null ) {
  706. sign = getSourceSign(s);
  707. allSources.set(s, sign);
  708. }
  709. return sign;
  710. }
  711. function getSourceSign( s : String ) {
  712. return haxe.crypto.Md5.encode(s).substr(0,8);
  713. }
  714. function addSource( s : RuntimeShader ) {
  715. // true if already matches another combination
  716. if( !compiledSources.exists(s.signature) ) {
  717. // shader was selected by not compiled by driver, let's force-compile it by hand!
  718. if( s.vertex.code == null || s.fragment.code == null ) {
  719. var engine = h3d.Engine.getCurrent();
  720. if( engine == null ) engine = @:privateAccess new h3d.Engine();
  721. engine.driver.selectShader(s);
  722. }
  723. // same shader id is shared between multiple runtime shaders because they have the same signature
  724. // hopefully, the other shader will make it through addSource just a bit after
  725. if( s.vertex.code == null || s.fragment.code == null )
  726. return;
  727. compiledSources.set(s.signature, { vertex : allocSource(s.vertex.code), fragment : allocSource(s.fragment.code) });
  728. }
  729. }
  730. }
  731. #end