Last active
August 29, 2015 14:09
-
-
Save jwmerrill/5a4c789d17380966a420 to your computer and use it in GitHub Desktop.
Modification of elm's runtime to allow drawing more consistently at 60 fps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9345,9351c9345,9374 | |
- var previousDrawId = 0; | |
- function domUpdate(newScene) { | |
- previousDrawId = ElmRuntime.draw(previousDrawId, function(_) { | |
- Render.update(elm.node.firstChild, savedScene, newScene); | |
- if (elm.Native.Window) elm.Native.Window.values.resizeIfNeeded(); | |
- savedScene = newScene; | |
- }); | |
+ | |
+ // domUpdate is called whenever the main Signal changes. On domUpdate, | |
+ // | |
+ // 1. schedule a draw using requestAnimationFrame if there is no draw already scheduled | |
+ // 2. replace the scene to be drawn with newScene | |
+ | |
+ var drawScheduled = false; | |
+ var newScene = currentScene; | |
+ | |
+ function domUpdate(_newScene) { | |
+ newScene = _newScene; | |
+ if (!drawScheduled) scheduleDraw(); | |
+ } | |
+ | |
+ function scheduleDraw() { | |
+ drawScheduled = true; | |
+ if (typeof requestAnimationFrame === 'undefined') { | |
+ draw(); | |
+ } else { | |
+ requestAnimationFrame(draw); | |
+ } | |
+ } | |
+ | |
+ function draw() { | |
+ drawScheduled = false; | |
+ Render.update(elm.node.firstChild, savedScene, newScene); | |
+ if (elm.Native.Window) elm.Native.Window.values.resizeIfNeeded(); | |
+ // Needed in case domUpdates are consistently called before draw in each frame. | |
+ if (savedScene !== newScene) scheduleDraw(); | |
+ savedScene = newScene; | |
9352a9376 | |
+ | |
9397,9423d9420 | |
- // define function for drawing efficiently | |
- // | |
- // draw : RequestID -> (() -> ()) -> RequestID | |
- // | |
- // Takes a "RequestID" allowing you to cancel old requests if possible. | |
- // Returns a "RequestID" so you can refer to past requests. | |
- // | |
- var vendors = ['ms', 'moz', 'webkit', 'o']; | |
- var win = typeof window !== 'undefined' ? window : {}; | |
- for (var i = 0; i < vendors.length && !win.requestAnimationFrame; ++i) { | |
- win.requestAnimationFrame = win[vendors[i]+'RequestAnimationFrame']; | |
- win.cancelAnimationFrame = win[vendors[i]+'CancelAnimationFrame'] || | |
- win[vendors[i]+'CancelRequestAnimationFrame']; | |
- } | |
- | |
- if (win.requestAnimationFrame && win.cancelAnimationFrame) { | |
- ElmRuntime.draw = function(previousRequestID, callback) { | |
- win.cancelAnimationFrame(previousRequestID); | |
- return win.requestAnimationFrame(callback); | |
- }; | |
- } else { | |
- ElmRuntime.draw = function(previousRequestID, callback) { | |
- callback(); | |
- return previousRequestID; | |
- }; | |
- } | |
- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The main problem with the existing runtime draw scheduler is that it cancels existing scheduled draws whenever a new draw is scheduled. This would be fine, except for the behavior when a new draw is scheduled during the "same tick" that an existing draw was scheduled to run. In this case, the existing draw is cancelled, and the new draw waits for the next frame. If another new draw is scheduled in that frame, it cancels the existing draw, and if this pattern continues on every frame, no frame is ever drawn.
In practice, this only happens intermittently when using
fps
, but it is easy to make it happen always using Monitor.Another non-ideal thing about the existing runtime is that it creates a new callback closure for every domUpdate, which probably increases GC pressure.
The modified implementation stores the new scene to be drawn in the existing closure, and it never uses
cancelAnimationFrame
. I also dropped the vendor prefixes onrequestAnimationFrame
because it appears that no recent browser uses them.The full modified file is available here.
I'd like to turn this into a proper pull request once I can figure out how to rebuild Elm's runtime after modifying
core.js
.