Boxer Engine Updated

@Steveblue Can you describe where/why the bottle necks happens in worker vs non-worker?

I think the latency of the messaging back and forth between window and worker is hindering performance. The latency may only be ms but it has to be below 16ms to fully operate at 60fps. Perhaps my method of running requestAnimationFrame on the window is flawed and writing a polyfill for requestAnimationFrame on the Worker is the answer, but it is my understanding after doing some research that none of the existing polyfills perform better than 30fps. Right now boxer runs 60fps on the window, on every frame a message is sent to the worker to update, while at the same time the worker may be messaging the window to update properties tied to DOMComponent. Possibly making all messaging unidirectional will solve some performance issues.

@joe Have you made any progress trying to use a Worker? What have you found?

I havenā€™t gotten anything rendering with a worker version yet, but what Iā€™ve found so far is that itā€™s hard to make and API (around workers) that is as easy to use without knowing workers are in play. Maybe Iā€™m starting at too high of a level to work down from?

I have some idea of what Iā€™m gonna change about my current design though. At first I started with something similar to yours, where the whole scene is in a single worker. Then, when the user does new Node on the UI side, that automatically creates a reciprocal Node instance inside of the worker. Itā€™s the same class, with one instance on both sides, but some methods are UI-side methods, and some are worker-side methods, while some are hybrid and can be called on either side. Iā€™m not sure of keeping this structure though. My goal is to provide a simple UI-side API, but in making it simple, the implementation is a lot more involved. One of my goals is also to prevent the user from hurting themselves (or their app) with the API, which makes things perhaps less flexible, but easier for a beginner to start using and easier for us to guarantee some level of performance without users worrying about it.

When a user simply writes new Scene, behind the scenes that gets a ref to an Engine singleton, associates the new Scene to the Engine (Engine is part of the private API), creates a scene worker if one doesnā€™t exist yet, and finally a reciprocal creates Scene object in the scene worker. That all happens without the user knowing. The user only has to do scene.add(new Node) to begin to build a scene graph, which behind the scenes causes a reciprocal worker Node to be instantiated in the scene worker.

Iā€™m thinking about splitting the scene worker into separate workers, where on is merely a communication gateway, between the UI and other workers. There will be a worker whoā€™s sole job is to calculate world transforms from the scene graph, and separate workers for calculating (possible groups) of calculations that map to local transforms on Nodes. Iā€™m not exactly sure what thatā€™ll look like yet though. Maybe youā€™ll get some ideas from reading this. :smiley:

After reading some articles (I forgot where they are :[ ), Iā€™m not sure exposing direct classes (Node/Scene/etc) as part of the public API is the best thing to do. I think it might be better to expose methods or functions for creating the pieces that the user needs, then that way (behind the scenes) it can be easier to separate certain logic from certain classes (for example, remove the creation of the Engine and SceneWorker singletons from the Scene class, and do those in a function that create the Scene instead:

let scene = Engine.createScene()
scene.add(Engine.createNode())

or

let scene = Scene.create()
scene.add(Node.create())

The variable and method names are subject to change, but the idea is that we expose public methods/functions for creating the pieces we need, while internally they could be implemented with classes. The benefit is that people that use the public API donā€™t have to worry about how to extend a class in order to build more things. Instead, users can make their own classes that use these creator/factory methods/functions, and the behavior of our class instances can then be guaranteed more easily. Of course, thereā€™s nothing stopping a user from importing the Node class directly, but that doesnā€™t need to be part of the public API (ā€œprivateā€ API could be toggled with a button in the UI and show a warning that this isnā€™t the recommended way of using our library and is subject to change).

In the app that Iā€™m currently working on (not using workers yet), Iā€™m making UI components that use Node and Scene instances but never extend them; the UI components only ever contain instances of the engineā€™s classes and do as needed with them. Iā€™m trying it out to see what itā€™s like not to extend anything from the library.

Some of the concepts of not extending are from guidelines Iā€™ve read about React where some suggest to always use composition of components instead of component extension (except for the mandatory extending of React.Component to initially create components). Iā€™ve found that, indeed, code becomes easier to reason because then extensions and modifications of the behavior of base classes does not become cognitive load that the developer has to keep in mind.

Iā€™ve been using async/await with CSP using async-csp in my current project (look at some of the Channel methods like Channel#pipe which might seem similar to methods like Famous 0.3 EventHandler#pipe). Iā€™m wondering if any of these async/await+csp techniques might be handy in coordinating workers and other things in the engineā€¦

So, thatā€™s where Iā€™m at thought wise. Iā€™ve gotta get something rendering though, then Iā€™ll have more ideas. :]