PipelineCache.hx 8.5 KB

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