Wednesday, July 7, 2010

Why REST/HTTP based Client-Side Scripting for Snowglobe

Over the last few years I've written various methods to integrate or decouple parts of the snowglobe viewer to add scriptable features. C++ is good for a lower-level optimization, yet there is so much already written in C++ for the viewer that doesn't need to be in C++. There is also the need to separate tasks being done in the main loop of the viewer that doesn't need to be done in the main loop. Eventually, after several models and rewrites, the REST/HTTP based API has helped overcome many obstacles that other usual methods fail.

I'll get to the Client-Side Scripting features in later, yet we have to dig deep to understand some concerns before the desired features to understand why choose REST/HTTP.

The REST/HTTP/XML based API automatically deals with one major concern and that is byte size and byte order. I've tried GObjects, P/Invoke, and few other methods. We could complain about trashed code that works until you understand how high I set the bar for desired functionality. Byte size and byte order seems to be the major gotcha even for those that claim otherwise. I still see those that want language agnostic and platform agnostic interfaces yet choose something that isn't so agnostic about byte order. They may hate XML until they understand how it guarantees machine-readable data structures. Some still tend to think that those data structures must be transferred in XML. Let's make this easy, here is a default REST/HTTP/XML interface that guarantees its agnostic ability. From there, add other paths that maybe stream more native or shared formats.

The next concern on the table to put the renderer into a shared library. I have done this, once. There was much that needed to be manually updated source wise for every update to the upline repository. The only way to end this time consumption to manually update it was to minimize any customization to the viewer source. This meant being able to have the user interface totally separate from the scene renderer. The tricky part to keep in mind is the ability to swith to fullscreen where the window overlays aren't being controlled by the desktop anymore. I've already tested new code to do such overlays and set it aside until the REST interface is more complete. Being able to create the shared library again should be easier to maintain now with the minimization done. The touched code went down from over 150 files down to about 10.  The shared library of LL code should help to further embed the renderer instead of extra source changes to load an arbitrary custom library from the viewer now. What I did in the past was to have C# load LL code as a shared library and control its main loop. A few GObjects can do well here for this shared library initialization and loop control.

Some are concerned that REST/HTTP is too slow for method calls. That really depends on design of each call. One shouldn't expect to do thousands of object updates every frame by separate individual methods calls when one could more optimally setup a shared memory area and burst that updates at blit speed. For now, I've implemented a simple burst mode where if two or more calls on the REST/HTTP queue or about to be sent individually that the code is smart enough to combine them into a single call for multiple queries. That was needed now because the current implementation of the main render loop only addresses the network every frame. That network poll per frame is a major bottleneck. The combined queuries easily overcomes that.

Asynchronous interaction between different objects in code become more and more desired. The main loop of the renderer is based on synchronous optimization to avoid locks. As the components are separate out there is the question of data states when queried. If data is accessed at the wrong time in the main loop then it could be in the wrong state. Direct hooks worked fine, yet only if one hooks into the loop at proper time to access the data. This kind of documentation is not obvious in code no matter how well written in such synchronous style on such large source code base. The REST interface allows asynchronous calls in and out and dispatches as needed. The asynchronousity allows a more modular design in code. Now several threads can simple use the REST interface to manage these kinds of tasks instead. In a common sychronous design there tends to be custom code at every point of the way to test for proper states. A generic REST design (even without HTTP) doesn't suffer such state conflicts as typical SOAP design.

I could surely rant about a few more areas. The above I tend to often repeat in one way or another.