sinisterchipmunk / jax

Framework for creating rich WebGL-enabled applications using JavaScript and Ruby
http://jaxgl.com
MIT License
96 stars 16 forks source link

Omit unused portions of Jax from build #34

Closed sinisterchipmunk closed 12 years ago

sinisterchipmunk commented 12 years ago

Since Jax uses Ruby and Rails on the server and/or precompile side, it has one major advantage that AFAIK no other WebGL framework has: optimizability. And yet, we aren't yet using it.

Case in point: the Utah Teapot. It's one of those meshes that is great for testing and makes a pretty good welcome-to-Jax app, but I don't see it really making an appearance in many production apps. But there it is, consuming a whopping 15% of the precompiled Jax application, even when gzipped!

We need a way to optimize out these potentially-useless code segments. There are a few ways to do this, but none of them strikes me as particularly novel. I'm open to further suggestions. Here's what I'm able to come up with so far:

  1. Start explicitly #= requireing individual files. This sucks, because not only does it break all existing applications, it forces the developer to have a fairly meaningful understanding of how Jax is organized internally (which changes a lot from release to release, anyway).
  2. Add a #= reject directive so that users can explicitly reject certain files. This sucks, because it forces the developer to learn "one more thing" before they can deploy to production. Jax is about doing what you want, without you necessarily having to tell it how.
  3. Add a line to config/application.rb something like config.jax_exclusions = ['jax/builtin/meshes/teapot.js']. This sucks for reasons 1 and 2 combined, but it strikes me as the happy medium between the two.
  4. Discern which source files are being used by scanning the developer's files for stuff like new Jax.Mesh.Teapot, and adapt the Jax sources to match. This sucks because a brute force regexp search on all javascript files strikes me as all of: brittle, un-DRY, and slow (though with Rails' new caching mechanisms and precompiling, speed may be less of an issue). This would likely involve some ERB magic in jax/application.js so that the necessary source files could be prepended prior to the Sprockets preprocessors doing their thing. Even with my consternations, this is the approach I'm currently leaning toward. Please, give me a better solution!
sinisterchipmunk commented 12 years ago

One more idea I just thought of. Certain files (namely the built-in meshes) could be replaced with almost-empty dummies. The dummies would construct empty meshes that don't really get rendered. They'd also use ajax to load the real versions of themselves at runtime. Once the ajax call completes, the dummy is replaced by the real version, and any already-instantiated dummies have their empty meshes replaced by the real mesh. This defers loading of the meshes until runtime, and removes unused meshes (I'm looking at you, Teapot) from the load at all.

Unfortunately, this is pretty exclusive to Mesh objects and doesn't intuitively translate into more of the framework.

sinisterchipmunk commented 12 years ago

Another idea, which is now my favorite so far.

User files (files in app/assets) all get precompiled as part of application.js as they are today. Jax files get precompiled into multiple segments by category, separate from application.js. For instance, all Jax files related to triangle intersection would be compiled into a single file such as jax/intersections.js. The goal is to find an ideal balance between number of files and organization of code.

Then, relevant Jax APIs are modified to accept a callback method. Jax internally performs AJAX requests to pull down the necessary components as needed during runtime. When the Jax module has been loaded, the callback is invoked. For backwards compatibility, synchronous XHR can be used, but these invocations would log deprecation warnings since synchronous XHR hurts performance.

This change would deprecate a lot of the current APIs, but it would not completely break them so I think it would be an acceptable addition to v2.1 instead of requiring a bump to major.

I think it's a good compromise because although the same code base does get precompiled into the Rails assets (e.g. the Utah Teapot data is still in there), it doesn't actually need to be loaded unless needed at runtime, thus saving on bandwidth and keeping the Jax runtime fairly lightweight.

With this scheme, developers could also use Sprockets require directives to eager load Jax modules they know they'll need; this would improve the first few frames of runtime.

sinisterchipmunk commented 12 years ago

49 is about to happen, because it will make the Jax load process much easier to understand and (if necessary) debug. In the process, I'll split apart the core components and add //= require directives for them to the default generated jax/application.js file. This way if a dev doesn't want a part of Jax to load, it's real easy: just take out the corresponding directive.

Bam! Done. No ajax, no regexps, no muss, no fuss. Just nice, clean code.

sinisterchipmunk commented 12 years ago

Complete, see d4182c1bee279dc094c87b4bcd254133ee0d1d5a.