Starling 2.0 Beta

Daniel Sperl on February 29, 2016

Seven months ago, I set myself the goal of bringing Starling to the next level. To completely rethink its rendering architecture; to make it more extendable; to clean up all the tiny little compromises that had built up since its first release, and make every single class something to be proud of.

I can tell you, it was quite a trip. More than once, I found myself at a dead end, thinking I'd never be able to pull this off. But I knew what I had signed up for, and every time I managed to finish another small part of the puzzle, I became more confident that it would work out in the end, until finally, all parts fell into place.

The new version is miles ahead of what Starling has ever been, and it lays the foundations of even greater things. As you can imagine, it's a huge relief and joy for me to finally be able to write this blog post, introducing: Starling 2.0!

Completely new Rendering Architecture

In the past, the basic building blocks of all rendering were the Quad and Image classes. That was somewhat limiting, since you're not always interested in rectangular objects. Thus, I inserted a new base class into that inheritance chain: the Mesh.

As its name suggests, a mesh describes an arbitrary shape; anything that can be built up with triangles can be displayed by a mesh. Quad and Image are now both inheriting from this class, but you could easily create, say, a Triangle or Circle and still get full batching.

Defining the shape of an object is one part of rendering; another is how it's going to appear on the screen. Do you map a texture onto it? Are the colors modified in any way? What about lighting? Multi-texturing? In previous versions, you were pretty much stuck with the basics in that respect. Not any more: now, the new MeshStyle class takes care of that.

Each mesh now has a style property. Per default, that points to the standard MeshStyle implementation, which supports textured and colored vertices. However, you can assign arbitrary styles, too; the class was designed to be extended.

Let's look at a sample. We start with a standard image and then attach a LightStyle to it, which is part of the new Dynamic Lighting Extension.

var image:Image = new Image(assets.getTexture("hero"));
trace(image.style); // -> MeshStyle
addChild(image);

var style:LightStyle = new LightStyle(assets.getTexture("hero_normalmap"));
image.style = style;

This style uses a normal map (created with Andreas Löw's SpriteIlluminator) to add realistic lighting effects to your meshes.

With custom styles, the possibilities are nearly endless; and since styles fully support batching, there is no performance penalty at all for using them. In fact, you can even let Starling use a custom style for all meshes per default! Want to write a style that supports batching across multiple textures? Yes you can!

Behind the Scenes

If you want to create your own styles, you can follow this new tutorial in the Starling Wiki. Along the way, you'll notice that the new version not only introduces the Mesh and MeshStyle classes, but a whole new infrastructure for Stage3D rendering.

  • The Program class wraps fragment and vertex shaders together in a single object that automatically survives a context loss.
  • The Painter replaces the old RenderSupport class, simplifying a lot of common rendering tasks and providing a stack of RenderStates.
  • The VertexData class now not only uses a ByteArray to store all its data efficiently, but can be configured to hold arbitrary formats.
  • The IndexData accompanies VertexData now, so you can handle indices just as easily as vertices.
  • The Effect class (as well as its subclasses FilterEffect and MeshEffect) encapsulates all steps of a Stage3D drawing operation.

For advanced Starling users, all of those new building blocks greatly simplify custom rendering code, removing a lot of boiler-plate code and preventing problems before they arise.

Render Cache

One thing that always bugged me in the past was the high CPU load in scenes with a big number of static objects. There must be a way to automatically optimize static scenes, I thought, but I never quite came up with a solution for this problem. Until now!

If an object and its parents do not change in a given frame, they are now rendered directly from the new render cache. This means much less work for the CPU: no iterating over children, no transformations of vertices, just a single ByteArray copy operation. That's especially great for Feathers applications: while your users look at a static screen, the CPU load is now minimal, enhancing battery life significantly.

But even in games where there's constantly a lot going on, there are almost always some static objects on the screen. Having the render cache for those parts of the display tree frees up valuable CPU time for everything else. And even in worst-case scenarios (everything is moving), the overhead of the cache is reasonably small (no more than 10% compared to Starling 1.7).

Here's how this looks like in Scout. What you see below is the CPU usage of a mostly static scene, rendered with Starling 1.7 (top) and Starling 2.0 (bottom).

Think of this as Starling's contribution to fight climate change. ;-)

New Filter API

Here's another API I was never fully satisfied with: fragment filters. They did their job, but some parts of them felt more complicated than necessary. That's why I completely rewrote them, as well. The new API has several advantages:

  • It automatically makes use of the render cache: if your object doesn't move, the filter won't need to be processed again, but will use the output from the previous frame.
  • It's much easier to create your own filters. I already wrote a tutorial that gets you started.
  • DropShadow and Glow are now separate classes.
  • You can easily combine several filters on one display object via the new FilterChain.

Here's the filter chain in action:

var hueFilter:ColorMatrixFilter = new ColorMatrixFilter();
hueFilter.adjustHue(1);
var dropShadowFilter:DropShadowFilter = new DropShadowFilter();

var image:Image = new Image(assets.getTexture("starling"));
image.filter = new FilterChain(hueFilter, dropShadowFilter);
addChild(image);

Convenience Features

While I was refactoring all the code that had been created over the last couple of years, I also took the time to revisit several other topics that I'd been wanting to address for a long time.

  • The TextField class is now accompanied by the new TextFormat. The setup is quite similar to classic Flash — but without the pain of constantly having to re-assign the format for changes to show up. In Starling, when you change the TextField's format, it's going to be updated right away!
  • The TextField class also includes a new wordWrap property.
  • The Image class contains two new properties: scale9Grid and tileGrid. For Feathers-users, this will sound familiar: they replace Scale3Image, Scale9Image and TiledImage.
  • All Display Objects can now be scaled uniformly with a single property: scale.
  • My personal favorite: the new pixelSnapping property, which is enabled per default, makes sure that all objects are rendered as sharp and crisp as possible. No more casting to int to avoid blurriness!
  • You can now attach "frame actions" to MovieClips, i.e. code to execute at certain frames.
  • The Pool provides simple object-pooling for standard classes like Point, Rectangle and Matrix.
  • The juggler now supports uint handles for IAnimatable removal, which prevents nasty pooling bugs.

How to Upgrade

As you probably guessed from what I wrote above, this update is much more extensive than any previous release — it's called "2.0" for a reason! This also means, however, that upgrading a project to Starling 2.0 is not as straight-forward as before, depending on your code (custom rendering?) and external dependencies.

To help you with that, I wrote a migration guide that helps you decide if you should upgrade, and if so, what you need to change. The most important information in advance: the upcoming Feathers 3.0 already works great with Starling 2! (A huge thanks to Josh Tynjala!)

If it turns out that you'd rather stick with the old version for the time being, I have a small gift for you, as well: as of today, I'm also releasing Starling 1.8, fixing several bugs and issues that popped up since the last release. Henceforth, I'll support this release via its own branch on GitHub, while all 2.x development will happen on the master branch.

Now Get Started!

Finally, I want to thank all of you for the continuing support for Starling. The new version is a gift to the community; it wouldn't have happened if Starling weren't as popular as it is today. While it was a lot of hard work to build it, it was also one of the most rewarding journeys of my life as a developer, and I loved every minute of it.

Please, everyone: download the new version, play around with it and let me know any issues you run into! I want to get rid of that "beta" flag as soon as possible. I already have so many ideas for Starling 2.1, I want to get this done with! :-)

I'd also appreciate any comments below. Let me know what you think of the new version, and tell us if the upgrade went smoothly for you. Thanks a lot in advance!

— UPDATE (March 4th) —

A quick heads-up: several developers have reported performance issues with the new version. I'm currently looking into it and hope to get this fixed in a second beta. Sorry for the troubles!