Engine.hx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. package h3d;
  2. import h3d.mat.Data;
  3. private class TargetTmp {
  4. public var t : h3d.mat.Texture;
  5. public var textures : Array<h3d.mat.Texture>;
  6. public var next : TargetTmp;
  7. public var layer : Int;
  8. public var mipLevel : Int;
  9. public var depthBinding : DepthBinding;
  10. public function new(t, n, l, m, db) {
  11. this.t = t;
  12. this.next = n;
  13. this.layer = l;
  14. this.mipLevel = m;
  15. this.depthBinding = db;
  16. }
  17. }
  18. enum DepthBinding {
  19. ReadWrite;
  20. ReadOnly;
  21. DepthOnly;
  22. NotBound;
  23. }
  24. class Engine {
  25. #if multidriver
  26. static var ID = 0;
  27. public var id(default, null) : Int;
  28. #end
  29. public var driver(default,null) : h3d.impl.Driver;
  30. public var mem(default,null) : h3d.impl.MemoryManager;
  31. public var hardware(default, null) : Bool;
  32. public var width(default, null) : Int;
  33. public var height(default, null) : Int;
  34. public var debug(default, set) : Bool;
  35. public var drawTriangles(default, null) : Int;
  36. public var drawCalls(default, null) : Int;
  37. public var dispatches(default, null) : Int;
  38. public var shaderSwitches(default, null) : Int;
  39. public var backgroundColor : Null<Int> = 0xFF000000;
  40. public var autoResize : Bool;
  41. public var fullScreen(default, set) : Bool;
  42. public var fps(get, never) : Float;
  43. var realFps : Float;
  44. var lastTime : Float;
  45. var antiAlias : Int;
  46. var tmpVector = new h3d.Vector4();
  47. var window : hxd.Window;
  48. var targetTmp : TargetTmp;
  49. var targetStack : TargetTmp;
  50. var currentTargetTex : h3d.mat.Texture;
  51. var currentTargetLayer : Int;
  52. var currentTargetMip : Int;
  53. var currentDepthBinding : DepthBinding;
  54. var needFlushTarget : Bool;
  55. var nullTexture : h3d.mat.Texture;
  56. var textureColorCache = new Map<Int,h3d.mat.Texture>();
  57. var inRender = false;
  58. public var ready(default,null) = false;
  59. @:allow(hxd.res) var resCache = new Map<{},Dynamic>();
  60. public static var SOFTWARE_DRIVER = false;
  61. public static var ANTIALIASING = 0;
  62. @:access(hxd.Window)
  63. function new() {
  64. #if multidriver
  65. this.id = ID;
  66. ID++;
  67. #end
  68. this.hardware = !SOFTWARE_DRIVER;
  69. this.antiAlias = ANTIALIASING;
  70. this.autoResize = true;
  71. fullScreen = !hxd.System.getValue(IsWindowed);
  72. window = hxd.Window.getInstance();
  73. realFps = hxd.System.getDefaultFrameRate();
  74. lastTime = haxe.Timer.stamp();
  75. window.addResizeEvent(onWindowResize);
  76. setCurrent();
  77. #if macro
  78. driver = new h3d.impl.NullDriver();
  79. #elseif (js || hlsdl || usegl)
  80. #if (hlsdl && heaps_vulkan)
  81. if( hxd.Window.USE_VULKAN )
  82. driver = new h3d.impl.VulkanDriver();
  83. else
  84. #end
  85. #if js
  86. driver = js.Browser.supported ? new h3d.impl.GlDriver(antiAlias) : new h3d.impl.NullDriver();
  87. #else
  88. driver = new h3d.impl.GlDriver(antiAlias);
  89. #end
  90. #elseif (hldx && dx12)
  91. driver = new h3d.impl.DX12Driver();
  92. #elseif hldx
  93. driver = new h3d.impl.DirectXDriver();
  94. #elseif usesys
  95. driver = new haxe.GraphicsDriver(antiAlias);
  96. #else
  97. #if sys Sys.println #else trace #end("No output driver available." #if hl + " Compile with -lib hlsdl or -lib hldx" #end);
  98. #end
  99. }
  100. static var CURRENT : Engine = null;
  101. public function setDriver(d) {
  102. driver = d;
  103. if( mem != null ) mem.driver = d;
  104. }
  105. public static inline function getCurrent() {
  106. return CURRENT;
  107. }
  108. public inline function setCurrent() {
  109. CURRENT = this;
  110. window.setCurrent();
  111. }
  112. public function init() {
  113. driver.init(onCreate, !hardware);
  114. }
  115. public function driverName(details=false) {
  116. return driver.getDriverName(details);
  117. }
  118. public function selectShader( shader : hxsl.RuntimeShader ) {
  119. flushTarget();
  120. if( driver.selectShader(shader) )
  121. shaderSwitches++;
  122. }
  123. public function selectMaterial( pass : h3d.mat.Pass ) {
  124. driver.selectMaterial(pass);
  125. }
  126. public function uploadInstanceShaderBuffers(buffers) {
  127. driver.flushShaderBuffers();
  128. driver.uploadShaderBuffers(buffers, Params);
  129. driver.uploadShaderBuffers(buffers, Textures);
  130. driver.uploadShaderBuffers(buffers, Buffers);
  131. }
  132. public function uploadShaderBuffers(buffers, which) {
  133. driver.uploadShaderBuffers(buffers, which);
  134. }
  135. function selectBuffer( buf : Buffer ) {
  136. if( buf.isDisposed() )
  137. return false;
  138. flushTarget();
  139. driver.selectBuffer(buf);
  140. return true;
  141. }
  142. public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
  143. return renderBuffer(b, mem.getTriIndexes(b.vertices), 3, start, max);
  144. }
  145. public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
  146. return renderBuffer(b, mem.getQuadIndexes(b.vertices), 2, start, max);
  147. }
  148. // we use preallocated indexes so all the triangles are stored inside our buffers
  149. function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
  150. if( indexes.isDisposed() )
  151. return;
  152. var ntri = Std.int(b.vertices / vertPerTri);
  153. if( drawTri < 0 )
  154. drawTri = ntri - startTri;
  155. if( startTri < 0 || drawTri < 0 || startTri + drawTri > ntri )
  156. throw "Invalid vertices count";
  157. if( drawTri > 0 && selectBuffer(b) ) {
  158. // *3 because it's the position in indexes which are always by 3
  159. driver.draw(indexes, startTri * 3, drawTri);
  160. drawTriangles += drawTri;
  161. drawCalls++;
  162. }
  163. }
  164. // we use custom indexes, so the number of triangles is the number of indexes/3
  165. public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
  166. if( indexes.isDisposed() )
  167. return;
  168. var maxTri = Std.int(indexes.count / 3);
  169. if( drawTri < 0 ) drawTri = maxTri - startTri;
  170. if( drawTri > 0 && selectBuffer(b) ) {
  171. // *3 because it's the position in indexes which are always by 3
  172. driver.draw(indexes, startTri * 3, drawTri);
  173. drawTriangles += drawTri;
  174. drawCalls++;
  175. }
  176. }
  177. public function renderMultiBuffers( format : hxd.BufferFormat.MultiFormat, buffers : Array<Buffer>, indexes : Indexes, startTri = 0, drawTri = -1 ) {
  178. var maxTri = Std.int(indexes.count / 3);
  179. if( maxTri <= 0 ) return;
  180. flushTarget();
  181. driver.selectMultiBuffers(format, buffers);
  182. if( indexes.isDisposed() )
  183. return;
  184. if( drawTri < 0 ) drawTri = maxTri - startTri;
  185. if( drawTri > 0 ) {
  186. // render
  187. driver.draw(indexes, startTri * 3, drawTri);
  188. drawTriangles += drawTri;
  189. drawCalls++;
  190. }
  191. }
  192. public function renderInstanced( indexes : Indexes, commands : h3d.impl.InstanceBuffer ) {
  193. if( indexes.isDisposed() )
  194. return;
  195. if( commands.commandCount > 0 ) {
  196. driver.drawInstanced(indexes, commands);
  197. drawTriangles += commands.triCount;
  198. drawCalls++;
  199. }
  200. }
  201. function set_debug(d) {
  202. debug = d;
  203. driver.setDebug(debug);
  204. return d;
  205. }
  206. function onCreate( disposed ) {
  207. setCurrent();
  208. if( autoResize ) {
  209. width = window.width;
  210. height = window.height;
  211. }
  212. if( disposed ) {
  213. hxd.impl.Allocator.get().onContextLost();
  214. mem.onContextLost();
  215. } else {
  216. mem = new h3d.impl.MemoryManager(driver);
  217. mem.init();
  218. nullTexture = new h3d.mat.Texture(0, 0, [NoAlloc]);
  219. }
  220. hardware = driver.hasFeature(HardwareAccelerated);
  221. set_debug(debug);
  222. set_fullScreen(fullScreen);
  223. resize(width, height);
  224. if( disposed )
  225. onContextLost();
  226. else
  227. onReady();
  228. ready = true;
  229. }
  230. public dynamic function onContextLost() {
  231. }
  232. public dynamic function onReady() {
  233. }
  234. function onWindowResize() {
  235. if( autoResize && !driver.isDisposed() ) {
  236. var w = window.width, h = window.height;
  237. if( w != width || h != height )
  238. resize(w, h);
  239. onResized();
  240. }
  241. }
  242. function set_fullScreen(v) {
  243. fullScreen = v;
  244. if( mem != null && hxd.System.getValue(IsWindowed) ) {
  245. window.displayMode = v ? Borderless : Windowed;
  246. }
  247. return v;
  248. }
  249. public dynamic function onResized() {
  250. }
  251. public function resize(width, height) {
  252. // minimum 32x32 size
  253. if( width < 32 ) width = 32;
  254. if( height < 32 ) height = 32;
  255. this.width = width;
  256. this.height = height;
  257. if( !driver.isDisposed() ) driver.resize(width, height);
  258. }
  259. public function begin() {
  260. if( driver.isDisposed() )
  261. return false;
  262. // init
  263. inRender = true;
  264. drawTriangles = 0;
  265. shaderSwitches = 0;
  266. drawCalls = 0;
  267. dispatches = 0;
  268. targetStack = null;
  269. needFlushTarget = currentTargetTex != null;
  270. #if (usesys && !macro)
  271. haxe.System.beginFrame();
  272. #end
  273. driver.begin(hxd.Timer.frameCount);
  274. if( backgroundColor != null ) clear(backgroundColor, 1, 0);
  275. return true;
  276. }
  277. public function hasFeature(f) {
  278. return driver.hasFeature(f);
  279. }
  280. public function end() {
  281. inRender = false;
  282. driver.end();
  283. }
  284. public function getCurrentTarget() {
  285. return targetStack == null ? null : targetStack.t == nullTexture ? targetStack.textures[0] : targetStack.t;
  286. }
  287. public function pushTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0, depthBinding = ReadWrite ) {
  288. var c = targetTmp;
  289. if( c == null )
  290. c = new TargetTmp(tex, targetStack, layer, mipLevel, depthBinding);
  291. else {
  292. targetTmp = c.next;
  293. c.t = tex;
  294. c.next = targetStack;
  295. c.mipLevel = mipLevel;
  296. c.layer = layer;
  297. c.depthBinding = depthBinding;
  298. }
  299. targetStack = c;
  300. updateNeedFlush();
  301. }
  302. function updateNeedFlush() {
  303. var t = targetStack;
  304. if( t == null )
  305. needFlushTarget = currentTargetTex != null;
  306. else
  307. needFlushTarget = currentTargetTex != t.t || currentTargetLayer != t.layer || currentTargetMip != t.mipLevel || t.textures != null || currentDepthBinding != t.depthBinding;
  308. }
  309. public function pushTargets( textures : Array<h3d.mat.Texture>, depthBinding = ReadWrite ) {
  310. pushTarget(nullTexture, depthBinding);
  311. targetStack.textures = textures;
  312. needFlushTarget = true;
  313. }
  314. public function pushDepth( depthBuffer : h3d.mat.Texture ) {
  315. pushTarget(depthBuffer, DepthOnly);
  316. }
  317. public function popTarget() {
  318. var c = targetStack;
  319. if( c == null )
  320. throw "popTarget() with no matching pushTarget()";
  321. targetStack = c.next;
  322. updateNeedFlush();
  323. // recycle
  324. c.t = null;
  325. c.textures = null;
  326. c.next = targetTmp;
  327. targetTmp = c;
  328. }
  329. inline function flushTarget() {
  330. if( needFlushTarget ) doFlushTarget();
  331. }
  332. function doFlushTarget() {
  333. var t = targetStack;
  334. if( t == null ) {
  335. driver.setRenderTarget(null);
  336. currentTargetTex = null;
  337. } else {
  338. if ( t.depthBinding == DepthOnly )
  339. driver.setDepth(t.t);
  340. else if( t.textures != null )
  341. driver.setRenderTargets(t.textures, t.depthBinding);
  342. else
  343. driver.setRenderTarget(t.t, t.layer, t.mipLevel, t.depthBinding);
  344. currentTargetTex = t.t;
  345. currentTargetLayer = t.layer;
  346. currentTargetMip = t.mipLevel;
  347. currentDepthBinding = t.depthBinding;
  348. }
  349. needFlushTarget = false;
  350. }
  351. public function clearF( color : h3d.Vector4, ?depth : Float, ?stencil : Int ) {
  352. flushTarget();
  353. driver.clear(color, depth, stencil);
  354. }
  355. public function clear( ?color : Int, ?depth : Float, ?stencil : Int ) {
  356. if( color != null )
  357. tmpVector.setColor(color);
  358. flushTarget();
  359. driver.clear(color == null ? null : tmpVector, depth, stencil);
  360. }
  361. /**
  362. * Sets up a scissored zone to eliminate pixels outside the given range.
  363. * Call with no parameters to reset to full viewport.
  364. */
  365. public function setRenderZone( x = 0, y = 0, width = -1, height = -1 ) : Void {
  366. flushTarget();
  367. driver.setRenderZone(x, y, width, height);
  368. }
  369. public function render( obj : { function render( engine : Engine ) : Void; } ) {
  370. if( !begin() ) return false;
  371. obj.render(this);
  372. end();
  373. var delta = haxe.Timer.stamp() - lastTime;
  374. lastTime += delta;
  375. if( delta > 0 ) {
  376. var curFps = 1. / delta;
  377. if( curFps > realFps * 2 ) curFps = realFps * 2 else if( curFps < realFps * 0.5 ) curFps = realFps * 0.5;
  378. var f = delta / .5;
  379. if( f > 0.3 ) f = 0.3;
  380. realFps = realFps * (1 - f) + curFps * f; // smooth a bit the fps
  381. }
  382. return true;
  383. }
  384. public function setDepthClamp( enabled : Bool ) {
  385. driver.setDepthClamp(enabled);
  386. }
  387. public function setDepthBias( depthBias : Float, slopeScaledBias : Float ) {
  388. driver.setDepthBias( depthBias, slopeScaledBias );
  389. }
  390. public function dispose() {
  391. driver.dispose();
  392. window.removeResizeEvent(onWindowResize);
  393. if ( mem != null )
  394. mem.dispose();
  395. #if multidriver
  396. for ( r in resCache ) {
  397. var resource = Std.downcast(r, hxd.res.Resource);
  398. if ( resource != null ) {
  399. resource.entry.unwatch(id);
  400. }
  401. }
  402. #end
  403. }
  404. function get_fps() {
  405. return Math.ceil(realFps * 100) / 100;
  406. }
  407. }