sinisterchipmunk / jax

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

OPEN DISCUSSION: event-driven material assignment? #84

Open sinisterchipmunk opened 11 years ago

sinisterchipmunk commented 11 years ago

This is only a thought and I have no clue what the actual implementation would look like.

I have never been happy with the way Jax assigns uniform variables and attributes to the shaders. It is pretty easy for a developer (and we've come a long way toward making it as simple as possible), but there are significant performance penalties coming from the current approach.

Currently, all variables are assigned to each shader in use, every frame. This results in a predictably huge amount of time wasted on redundant assignments. Since Jax.Material#setVariables is called so many times, there is also significant CPU waste in the form of recalculating intermediate variables on the JavaScript side.

(Note: I haven't done any profiling. These are just my observations when looking at the way the code works.)

I'd like to try to remove as much of this overhead as possible. My proposal is to make changes event-based, so that they are only assigned when some dependent attribute is modified.

We'd leave the Jax.Material#setVariables method in place as it exists today. This way, legacy code would not be broken, and variables could still be recalculated every frame if needed.

This proposal comes in the form of an additional hook method (here called register), which would become the new "best practice" for shader variable assignment.

It could look something like this:

class Jax.Material.Layer.Position extends Jax.Material.Layer
  register: (context, mesh, model, vars) ->
    model.camera.on 'change', ->
      vars['MODEL_MATRIX'] = model.camera.getTransformationMatrix()
    # ...

Fringe benefits include less internal reliance on the matrix stack, and consequently less matrix concatenation on the CPU. We'd let the dependent matrices be calculated on the GPU, which is pretty much always faster than on the CPU, and I would be willing to bet that since the matrix calculations would usually be based on uniforms, they'd be very well optimized.

The vars object would be a collection of variables with the same API as today, but would not function as we know them today. Today, an assignment to the vars object is essentially global to the shader. The above model wouldn't quite work, otherwise a change based on any mesh using this material, would make the same change for all meshes using this material -- not what we want. So instead, vars would have to be managed, at a minimum, per-mesh: an assignment to the vars of one mesh would not alter the assignment to vars of another, because there would be multiple separate internally-managed vars objects. Thus, there would be a multitude of event listeners, each operating on various different meshes as materials are added to them.

We could also implement an unregister method for unhooking events when materials are removed. This would give the developer greater control, but I'd also be in fan of somehow being aware of which event listeners were registered by which materials on which meshes; and then Jax could just unregister all listeners when a material is removed from a mesh. This would nearly always be the expected result, after all.

Goutte commented 11 years ago

This looks great ! (except the first line) I can't really be constructive, not knowing much about how jax and others handle GLSL, but I see the point of event-based assigning.

What event do you think of ? I could only come up with :

Model Camera

Context Active Camera

sinisterchipmunk commented 11 years ago

For this to work, I think there need to be plenty of hooks for event listeners throughout Jax. Fortunately, a lot of these are already in place, though they may need some tweaking. We also would need to make triggering custom events much easier to do, so people can trigger changes based on things we can't foresee. I'm thinking of something like jQuery's trigger(evtName[, evtData]) method available to all Jax objects.

Some built-in events off the top of my head:

I'm sure there are many more to be considered, but that's the list of what strikes me as immediately obvious.