Engine.hx 11 KB

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