I finally took the plunge and decided to really give the RTL a makeover. I was supposed to wait until Smart v1.1 with this, but releasing a product only to alter the RTL radically later would be unfair to our customers. Better to put in place a foundation that wont break the codebase later and that we can build on. So for the past 5 days I have edited every single method and class in the RTL’s foundation. But for what you say? Why fix what already works?
One of the hidden gems that my buddy Eric added to the compiler, is that we have direct access to the real javascript objects. You might have noticed that we used to keep a reference to the actual HTML Tag by use of TObject? In the older (and current) API all visual components have a property called tagRef. The problem was – this reference was both slow and dangerous. If you try to call any of it’s methods the app will crash, because it points to the real javascript handle and not our virtual method table.
Using a TObject reference also meant a greater speed penalty, since you had to typecast and test in a variety of ways before you used it. But now, all that is a thing of the past. The tagRef property has been completely removed from the RTL and has been replaced by a proper THandle. What is the fuzz about? Well, this property IS the actual javascript object. So, Eric in his infinite wizdom added support for magic stuff like:
[sourcecode language=”delphi”]
Self.Handle.style[‘backgroundColor’]:=clRed;
[/sourcecode]
What happens here is that you are accessing the javascript object directly, with nothing in between (!). As you can imagine, this gives us a tremendous advantage in speed over other solutions. It also means that the compiled code will be smaller, more efficient – and most importantly, easy to maintain. So now you can interact directly with the native DOM elements without an ASM section.
Speed injection
An example of just how much extra juice we managed to squeeze out of the browser with this rewrite, can be demonstrated by the “plasma” graphics demo we posted earlier (donated by a tester). Under the older RTL it rendered 120.000 pixels (manually plotted into the HTML5 graphics context via R,G,B,A) at roughly 4 frames per second on an iPhone 4S. That is extremely slow (but considering the amount of calls involved, im surprised we got 4 frames at all). After I ejected the use of tagRef and re-wrote parts of the w3graphics.pas unit, I now get 11 frames per second. Please remember that this extremely fast for a scripting engine, being able to draw 120.000 * 4 bytes eleven times a second. On my iMac it runs at a decent 80 FPS, which is even more impressive.
And this is just for starters. To keep the RTL backwards compatible I have retained the functions in w3system.pas (things like w3_setStyle, w3_setAttrib and so on), which means there are actually thousands of calls being made that are now redundant. It will take a lot of energy to get rid of them by Monday – but when I decide on something I never quit until it’s done.
Structure helpers
Following up on Delphi’s introduction of record functions, I have stripped the older functions for making and working with TRect, TRectF, TPoint and TPointF – and instead placed them directly in the record itself. This is much more uniform and leaves little doubt where a function should be (so new users dont have to wonder what unit function X resides in). Here is the interface for TRect as of writing. More and more functions will be added, but this is the “bare bones” i guess:
[sourcecode language=”delphi”]
TRect = Record
Left:Integer;
Top:Integer;
Right:Integer;
Bottom:Integer;
function Width:Integer;
function Height:Integer;
function toString:String;
function topLeft:TPoint;
function BottomRight:TPoint;
function Clip(Const RectToClip:TRect):TRect;
function Empty:Boolean;
function NullRect:TRect;
function Compare(Const aRect:TRect):Boolean;
function ContainsRow(Const aRow:Integer):Boolean;
function ContainsCol(Const aCol:Integer):Boolean;
function ContainsRect(Const aRect:TRect):Boolean;
function ContainsPos(Const aLeft,aTop:Integer):Boolean;
function ContainsPoint(Const apoint:TPoint):Boolean;
function Intersect(Const aRect:TRect;var intersection:TRect):Boolean;
function Expose(const aChildRect:TRect):TExposure;
function Make(Const aLeft,aTop,aWidth,aHeight:Integer):TRect;overload;
function Make(Const aWidth,aHeight:Integer):TRect;overload;
function MakeAbs(Const aLeft,aTop,aRight,aBottom:Integer):TRect;
End;
[/sourcecode]
FireFox support
The first version of Smart Mobile is designed exclusively for webkit, which means Safari mobile (iPad, iPod, iPhone), Android and equivalent desktop browsers. So little or no effort has gone into making Smart apps run on Opera or FireFox. But with some tweaks I now have a Sprite3d App running under FireFox as well 🙂
The bootstrap code (the section of code that initializes and starts your application) has been altered to include a browser check. So now it checks what system you are running and cache’s known style-prefixes and other information. The magic function that will make all the difference is called “w3_CSSPrefix”. This will help make your CSS calls “universal”. So where you would previously have written:
[sourcecode language=”delphi”]
w3_setStyle(handle,’webkitTranslate’,F3dState);
[/sourcecode]
You can now make it run on all browsers by changing it to:
[sourcecode language=”delphi”]
w3_setStyle(handle,w3_CSSPrefix(‘Translate’),F3dState);
[/sourcecode]
Hardware accelerated animations and events are a bit crappy on FireFox at the moment (in terms of commonalities), for instance their “animation end” event don’t fire as it’s supposed to, and it also deviates from the naming convention used elsewhere. So mozAnimationEnd wont do. I’ll have to come up with a solution for that. But full FireFox support wont happen until later. But you can prepare your app for universal support by using the w3_cssPrefix function.
API drivers
The only way to fully maximize the potential of all platforms is to create a common ground, but one that is rich enough to cater for a browser’s unique features. As such, I have decided to create a driver system to solve the browser compatibility problems once and for all. This is currently being prototyped but should make it into Smart v1.5 probably. In short you have a basic class with functions for getting the correct style names (and more) for the current browser. The driver will also contain functions to examine device capabilities (is camera supported, is database supported, is orientation supported, touch, sound formats, image formats — the whole multimedia spectrum). When your app starts, the browser will be recognized – and the appropriate driver created. The RTL will then reference the driver to get the correct properties to set.
As a bonus, anyone can write a new driver should a different browser appear on the marked. And should there be changes, you simply update the driver rather than every n’th class or method in the RTL.
The javascript punters are going to go “whaaat? how did they do that?”. Easy, object pascal 🙂