ShaderCache.hx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package h3d.impl;
  2. enum abstract ShaderCacheMode(Int) from Int to Int {
  3. var Base64 = 0;
  4. var Binary = 1;
  5. }
  6. class ShaderCache {
  7. var file : String;
  8. var outputFile : String;
  9. var data : Map<String, haxe.io.Bytes>;
  10. var sources : Map<String, String>;
  11. var sourceFile : String;
  12. public var keepSource : Bool;
  13. var mode : ShaderCacheMode;
  14. inline static var VERSION_KEY_WORD = "VERSION";
  15. inline static var VERSION = 1;
  16. inline static var MODE_KEY_WORD = "MODE";
  17. public function new( file : String, ?outputFile : String, mode = Base64) {
  18. this.file = file;
  19. this.outputFile = outputFile ?? file;
  20. this.mode = mode;
  21. sourceFile = file + ".source";
  22. }
  23. public function disableSave() {
  24. outputFile = null;
  25. }
  26. public function initEmpty() {
  27. data = [];
  28. sources = [];
  29. }
  30. function load() {
  31. data = new Map();
  32. try loadFile(file) catch( e : Dynamic ) {};
  33. if( outputFile != file ) try loadFile(outputFile) catch( e : Dynamic ) {};
  34. if( keepSource ) try loadSources() catch( e : Dynamic ) {};
  35. }
  36. function loadFile( file : String ) {
  37. #if !sys
  38. throw "Cannot load shader cache with this platform";
  39. #else
  40. if( !sys.FileSystem.exists(file) )
  41. return;
  42. var cache = new haxe.io.BytesInput(sys.io.File.getBytes(file));
  43. var hasVersion = cache.readString(VERSION_KEY_WORD.length) == VERSION_KEY_WORD;
  44. var curPos = cache.position;
  45. if ( !hasVersion )
  46. cache.position = curPos = 0;
  47. var hasMode = cache.readString(MODE_KEY_WORD.length) == MODE_KEY_WORD;
  48. var mode = Base64;
  49. if ( hasMode )
  50. mode = cache.readInt32();
  51. else
  52. cache.position = curPos;
  53. switch( mode ) {
  54. case Base64: loadCache(cache);
  55. case Binary: loadBinaryCache(cache);
  56. }
  57. #end
  58. }
  59. #if sys
  60. function loadCache(cache : haxe.io.BytesInput) {
  61. while( cache.position < cache.length ) {
  62. var len = cache.readInt32();
  63. if( len < 0 || len > 4<<20 ) break;
  64. var key = cache.readString(len);
  65. if( key == "" ) break;
  66. var len = cache.readInt32();
  67. if( len < 0 || len > 4<<20 ) break;
  68. var str = cache.readString(len);
  69. data.set(key,haxe.crypto.Base64.decode(str));
  70. cache.readByte(); // newline
  71. }
  72. }
  73. function loadBinaryCache(cache : haxe.io.BytesInput) {
  74. while( cache.position < cache.length ) {
  75. var len = cache.readInt32();
  76. if( len < 0 || len > 4<<20 ) break;
  77. var key = cache.readString(len);
  78. if( key == "" ) break;
  79. var len = cache.readInt32();
  80. if( len < 0 || len > 4<<20 ) break;
  81. var buf = haxe.io.Bytes.alloc(len);
  82. cache.readBytes(buf, 0, len);
  83. data.set(key, buf);
  84. }
  85. }
  86. #end
  87. function loadSources() {
  88. #if !sys
  89. throw "Cannot load shader cache with this platform";
  90. #else
  91. sources = new Map();
  92. if( !sys.FileSystem.exists(sourceFile) )
  93. return;
  94. var cache = new haxe.io.BytesInput(sys.io.File.getBytes(sourceFile));
  95. while( cache.position < cache.length ) {
  96. var len = cache.readInt32();
  97. if( len < 0 || len > 4<<20 ) break;
  98. var key = cache.readString(len);
  99. if( key == "" ) break;
  100. var len = cache.readInt32();
  101. if( len < 0 || len > 4<<20 ) break;
  102. var str = cache.readString(len);
  103. sources.set(key, str);
  104. cache.readByte(); // newline
  105. cache.readByte(); // newline
  106. }
  107. #end
  108. }
  109. public function resolveShaderBinary( source : String, ?configurationKey = "" ) {
  110. if( data == null ) load();
  111. return data.get(configurationKey + haxe.crypto.Md5.encode(source));
  112. }
  113. public function saveCompiledShader( source : String, bytes : haxe.io.Bytes, ?configurationKey = "", ?saveToFile = true ) {
  114. if( outputFile == null )
  115. return;
  116. if( data == null ) load();
  117. var key = configurationKey + haxe.crypto.Md5.encode(source);
  118. if( data.get(key) == bytes && (!keepSource || sources.get(key) == source) )
  119. return;
  120. data.set(key, bytes);
  121. if( saveToFile )
  122. save();
  123. if( keepSource ) {
  124. sources.set(key, source);
  125. saveSources();
  126. }
  127. }
  128. function save() {
  129. var out = new haxe.io.BytesOutput();
  130. var keys = Lambda.array({ iterator : data.keys });
  131. keys.sort(Reflect.compare);
  132. out.writeString(VERSION_KEY_WORD);
  133. out.writeInt32(VERSION);
  134. out.writeString(MODE_KEY_WORD);
  135. out.writeInt32(mode);
  136. switch ( mode ) {
  137. case Base64: writeCache(keys, out);
  138. case Binary: writeBinaryCache(keys, out);
  139. }
  140. #if sys
  141. try sys.io.File.saveBytes(outputFile, out.getBytes()) catch( e : Dynamic ) {};
  142. #end
  143. }
  144. function writeCache(keys : Array<String>, out : haxe.io.BytesOutput) {
  145. for( key in keys ) {
  146. out.writeInt32(key.length);
  147. out.writeString(key);
  148. var b64 = haxe.crypto.Base64.encode(data.get(key));
  149. out.writeInt32(b64.length);
  150. out.writeString(b64);
  151. out.writeByte('\n'.code);
  152. }
  153. }
  154. function writeBinaryCache(keys : Array<String>, out : haxe.io.BytesOutput) {
  155. for( key in keys ) {
  156. out.writeInt32(key.length);
  157. out.writeString(key);
  158. var bytes = data.get(key);
  159. out.writeInt32(bytes.length);
  160. out.writeBytes(bytes, 0, bytes.length);
  161. }
  162. }
  163. function saveSources() {
  164. var out = new haxe.io.BytesOutput();
  165. var keys = Lambda.array({ iterator : sources.keys });
  166. keys.sort(Reflect.compare);
  167. for( key in keys ) {
  168. out.writeInt32(key.length);
  169. out.writeString(key);
  170. var src = sources.get(key);
  171. out.writeInt32(src.length);
  172. out.writeString(src);
  173. out.writeByte('\n'.code);
  174. out.writeByte('\n'.code);
  175. }
  176. #if sys
  177. try sys.io.File.saveBytes(sourceFile, out.getBytes()) catch( e : Dynamic ) {};
  178. #end
  179. }
  180. }