PipelineCache.hx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package h3d.impl;
  2. #if hl
  3. @:forward(setI32,setUI8,setUI16,getUI8,getUI16,getI32,setF32,getF32,sub)
  4. private abstract Bytes(hl.Bytes) from hl.Bytes to hl.Bytes {
  5. public function new(size) this = new hl.Bytes(size);
  6. public inline function compare( bytes : Bytes, size : Int ) {
  7. return this.compare(0, bytes, 0, size);
  8. }
  9. }
  10. #else
  11. @:forward(sub)
  12. private abstract Bytes(haxe.io.Bytes) from haxe.io.Bytes {
  13. public function new(size) {
  14. this = haxe.io.Bytes.alloc(size);
  15. }
  16. public inline function setI32(idx:Int,v:Int) {
  17. this.setInt32(idx, v);
  18. }
  19. public inline function setUI8(idx:Int,v:Int) {
  20. this.set(idx, v);
  21. }
  22. public inline function setUI16(idx:Int,v:Int) {
  23. this.setUInt16(idx, v);
  24. }
  25. public inline function getI32(idx:Int) {
  26. return this.getInt32(idx);
  27. }
  28. public inline function getUI8(idx:Int) {
  29. return this.get(idx);
  30. }
  31. public inline function getUI16(idx:Int) {
  32. return this.getUInt16(idx);
  33. }
  34. public function compare( bytes : Bytes, size : Int ) {
  35. var bytes : haxe.io.Bytes = cast bytes;
  36. for( i in 0...size ) {
  37. var d = this.get(i) - bytes.get(i);
  38. if( d != 0 ) return d;
  39. }
  40. return 0;
  41. }
  42. }
  43. #end
  44. @:generic class CachedPipeline<T> {
  45. public var bytes : Bytes;
  46. public var size : Int;
  47. public var pipeline : T;
  48. public function new() {
  49. }
  50. }
  51. @:forward(get,set)
  52. abstract PipelineCache<T>(Map<Int,#if hl hl.NativeArray #else Array #end<CachedPipeline<T>>>) {
  53. public function new() {
  54. this = new Map();
  55. }
  56. }
  57. class DepthProps {
  58. public var format : hxd.PixelFormat;
  59. public var bias : Single;
  60. public var slopeScaledBias : Single;
  61. public var clamp : Bool;
  62. public function new() {}
  63. }
  64. class PipelineBuilder {
  65. static inline var PSIGN_MATID = 0;
  66. static inline var PSIGN_COLOR_MASK = PSIGN_MATID + 4;
  67. static inline var PSIGN_DEPTH_BIAS = PSIGN_COLOR_MASK + 4;
  68. static inline var PSIGN_SLOPE_SCALED_DEPTH_BIAS = PSIGN_DEPTH_BIAS + 4;
  69. static inline var PSIGN_DEPTH_CLAMP = PSIGN_SLOPE_SCALED_DEPTH_BIAS + 4;
  70. static inline var PSIGN_STENCIL_MASK = PSIGN_DEPTH_CLAMP + 1;
  71. static inline var PSIGN_STENCIL_OPS = PSIGN_STENCIL_MASK + 2;
  72. static inline var PSIGN_RENDER_TARGETS = PSIGN_STENCIL_OPS + 4;
  73. static inline var PSIGN_DEPTH_TARGET_FORMAT = PSIGN_RENDER_TARGETS + 1;
  74. static inline var PSIGN_LAYOUT = PSIGN_DEPTH_TARGET_FORMAT + 4;
  75. static inline var MAX_BUFFERS = 8;
  76. static inline var SHIFT_PER_BUFFER = #if js 2 #else 1 #end;
  77. static inline var PSIGN_SIZE = PSIGN_LAYOUT + (MAX_BUFFERS << SHIFT_PER_BUFFER);
  78. public var needFlush : Bool;
  79. var signature = new Bytes(64);
  80. var tmpDepth = new DepthProps();
  81. var tmpPass = new h3d.mat.Pass("");
  82. var tmpStencil = new h3d.mat.Stencil();
  83. #if hl
  84. var adlerOut = new Bytes(4);
  85. #end
  86. public function new() {
  87. if( PSIGN_SIZE > 64 ) throw "assert";
  88. }
  89. static function getRTBits( tex : h3d.mat.Texture ) {
  90. inline function mk(channels,format) {
  91. return ((channels - 1) << 2) | (format + 1);
  92. }
  93. return switch( tex.format ) {
  94. case RGBA: mk(4,0);
  95. case R8: mk(1, 0);
  96. case RG8: mk(2, 0);
  97. case RGB8: mk(3, 0);
  98. case R16F: mk(1,1);
  99. case RG16F: mk(2,1);
  100. case RGB16F: mk(3,1);
  101. case RGBA16F: mk(4,1);
  102. case R32F: mk(1,2);
  103. case RG32F: mk(2,2);
  104. case RGB32F: mk(3,2);
  105. case RGBA32F: mk(4,2);
  106. case RG11B10UF: mk(2, 3);
  107. case RGB10A2: mk(3, 4);
  108. default: throw "Unsupported RT format "+tex.format;
  109. }
  110. }
  111. public inline function setShader( sh : hxsl.RuntimeShader ) {
  112. needFlush = sh.mode != Compute;
  113. }
  114. function setDepthProps( depth : h3d.mat.Texture ) {
  115. if( depth == null ) {
  116. signature.setI32(PSIGN_DEPTH_TARGET_FORMAT,0);
  117. signature.setI32(PSIGN_DEPTH_BIAS,0);
  118. signature.setF32(PSIGN_SLOPE_SCALED_DEPTH_BIAS,0);
  119. signature.setUI8(PSIGN_DEPTH_CLAMP,0);
  120. } else {
  121. signature.setI32(PSIGN_DEPTH_TARGET_FORMAT, depth.format.getIndex());
  122. signature.setI32(PSIGN_DEPTH_BIAS, Std.int(depth.depthBias));
  123. signature.setF32(PSIGN_SLOPE_SCALED_DEPTH_BIAS, depth.slopeScaledBias);
  124. signature.setUI8(PSIGN_DEPTH_CLAMP, depth.depthClamp ? 1 : 0);
  125. }
  126. }
  127. static function initFormats() {
  128. var fmt = [];
  129. for( f in ([Depth16,Depth24,Depth24Stencil8,Depth32] : Array<hxd.PixelFormat>) )
  130. fmt[f.getIndex()] = f;
  131. return fmt;
  132. }
  133. public function getDepthProps() {
  134. static var FORMATS = initFormats();
  135. var d = tmpDepth;
  136. d.format = FORMATS[signature.getI32(PSIGN_DEPTH_TARGET_FORMAT)];
  137. d.bias = signature.getI32(PSIGN_DEPTH_BIAS);
  138. d.clamp = signature.getUI8(PSIGN_DEPTH_CLAMP) != 0;
  139. d.slopeScaledBias = signature.getF32(PSIGN_SLOPE_SCALED_DEPTH_BIAS);
  140. return d;
  141. }
  142. public function setRenderTarget( tex : h3d.mat.Texture, depthEnabled : Bool ) {
  143. signature.setI32(PSIGN_RENDER_TARGETS, (tex == null ? 0 : getRTBits(tex)) | (depthEnabled ? 0x80000000 : 0));
  144. var depth = tex == null || !depthEnabled ? null : tex.depthBuffer;
  145. setDepthProps(depth);
  146. needFlush = true;
  147. }
  148. public function getDepthEnabled() {
  149. return signature.getI32(PSIGN_RENDER_TARGETS) & 0x80000000 != 0;
  150. }
  151. public function setDepth( depth : h3d.mat.Texture ) {
  152. signature.setI32(PSIGN_RENDER_TARGETS, 0x80000000);
  153. setDepthProps(depth);
  154. needFlush = true;
  155. }
  156. public function setRenderTargets( textures : Array<h3d.mat.Texture>, depthEnabled : Bool ) {
  157. var bits = 0;
  158. for( i => t in textures )
  159. bits |= getRTBits(t) << (i << 2);
  160. signature.setI32(PSIGN_RENDER_TARGETS, bits | (depthEnabled ? 0x80000000 : 0));
  161. var tex = textures[0];
  162. var depth = tex == null || !depthEnabled ? null : tex.depthBuffer;
  163. setDepthProps(depth);
  164. needFlush = true;
  165. }
  166. public function selectMaterial( pass : h3d.mat.Pass ) @:privateAccess {
  167. signature.setI32(PSIGN_MATID, pass.bits);
  168. signature.setUI8(PSIGN_COLOR_MASK, pass.colorMask);
  169. var st = pass.stencil;
  170. if( st != null ) {
  171. signature.setUI16(PSIGN_STENCIL_MASK, st.maskBits & 0xFFFF);
  172. signature.setI32(PSIGN_STENCIL_OPS, st.opBits);
  173. } else {
  174. signature.setUI16(PSIGN_STENCIL_MASK, 0);
  175. signature.setI32(PSIGN_STENCIL_OPS, 0);
  176. }
  177. needFlush = true;
  178. }
  179. public inline function setBuffer( i : Int, inf : hxd.BufferFormat.BufferMapping, stride : Int ) {
  180. if( inf.offset >= 256 ) throw "assert";
  181. signature.setUI16(PSIGN_LAYOUT + (i<<SHIFT_PER_BUFFER), (inf.offset << 1) | inf.precision.toInt());
  182. #if js
  183. signature.setUI16(PSIGN_LAYOUT + (i<<SHIFT_PER_BUFFER) + 2, stride);
  184. #end
  185. needFlush = true;
  186. }
  187. public function getCurrentPass() @:privateAccess {
  188. var pass = tmpPass;
  189. pass.loadBits(signature.getI32(PSIGN_MATID));
  190. pass.colorMask = signature.getUI8(PSIGN_COLOR_MASK);
  191. var mask = signature.getUI16(PSIGN_STENCIL_MASK);
  192. var ops = signature.getI32(PSIGN_STENCIL_OPS);
  193. if( ops == 0 )
  194. pass.stencil = null;
  195. else {
  196. pass.stencil = tmpStencil;
  197. pass.stencil.loadMaskBits(mask);
  198. pass.stencil.loadOpBits(ops);
  199. }
  200. return pass;
  201. }
  202. public function getBufferInput( i : Int ) {
  203. var b = signature.getUI16(PSIGN_LAYOUT + (i<<SHIFT_PER_BUFFER));
  204. return new hxd.BufferFormat.BufferMapping(i, (b >> 1) & ~3, @:privateAccess new hxd.BufferFormat.Precision(b & 7));
  205. }
  206. #if js
  207. public function getBufferStride( i : Int ) {
  208. return signature.getUI16(PSIGN_LAYOUT + (i << SHIFT_PER_BUFFER) + 2);
  209. }
  210. #end
  211. function hashSign( size : Int ) {
  212. #if hl
  213. adlerOut.setI32(0, 0);
  214. hl.Format.digest(adlerOut, signature, size, 3);
  215. return adlerOut.getI32(0);
  216. #else
  217. var tot = 0;
  218. for( i in 0...size>>2 )
  219. tot = (tot * 31 + signature.getI32(i<<2)) % 0x7FFFFFFF;
  220. switch( size & 3 ) {
  221. case 0:
  222. case 2: tot = (tot * 31 + signature.getUI16(size - 2)) % 0x7FFFFFFF;
  223. default: throw "assert";
  224. }
  225. return tot;
  226. #end
  227. }
  228. public function lookup<T>( cache : PipelineCache<T>, inputs : Int ) : CachedPipeline<T> {
  229. needFlush = false;
  230. var signatureSize = PSIGN_LAYOUT + (inputs << SHIFT_PER_BUFFER);
  231. var hash = hashSign(signatureSize);
  232. var pipes = cache.get(hash);
  233. if( pipes == null ) {
  234. pipes = #if hl new hl.NativeArray(1) #else [] #end;
  235. cache.set(hash, pipes);
  236. }
  237. var insert = -1;
  238. for( i in 0...pipes.length ) {
  239. var p = pipes[i];
  240. if( p == null ) {
  241. insert = i;
  242. break;
  243. }
  244. if( p.size == signatureSize && p.bytes.compare(signature, signatureSize) == 0 )
  245. return p;
  246. }
  247. if( insert < 0 ) {
  248. #if hl
  249. var pipes2 = new hl.NativeArray(pipes.length + 1);
  250. pipes2.blit(0, pipes, 0, insert);
  251. cache.set(hash, pipes2);
  252. pipes = pipes2;
  253. #else
  254. insert = pipes.length + 1;
  255. #end
  256. }
  257. var cp = new CachedPipeline<T>();
  258. cp.bytes = signature.sub(0, signatureSize);
  259. cp.size = signatureSize;
  260. pipes[insert] = cp;
  261. return cp;
  262. }
  263. }