Heavy pixelation with paths drawn to Sprite.graphics on OSX


Having frozen our project’s various library versions for quite some time in the name of stability, I recently upgraded all our libraries to latest, which moved us from OpenFL 2.2.4 to 3.1.0 and Lime 2.0.6 to 2.4.4.

Beyond a couple of trivial fixes to get things compiling again, there are a couple of serious runtime issues with the OSX target (unsurprisingly fine in Flash, but I haven’t tried anywhere else), one of which is the massive pixelation of shapes (which we draw directly to Sprites via Graphics) when they get scaled up. We use these heavily in our vector-based graphics pipeline so this is a big issue for us.

I’d assume that this is because it’s caching these vector shapes as bitmaps but not redrawing when they get resized (which occurs via manipulation of the .transform.matrix property, and is often applied to a parent object rather than directly to the sprite itself). It doesn’t appear that forcing the cacheAsBitmap flag to false makes any difference to this behaviour.

Any ideas as to fixes / workarounds for this issue? I’ve tried compiling with -Dlegacy, but unfortunately that just leads to a silent crash at startup.

Using -Dlegacy should be similar to using OpenFL 2.2, it’s strange you get a silent crash, is there a difference in using the Neko target versus a desktop C++ target?

You’re right – we’re using Cairo to improve the shape rendering considerably in the newer rendering, but it does not (by design) scale up because I want to be very careful not to kill performance by doing software rendering repeatedly. I’m not sure of the best solution to this – perhaps cacheAsBitmap should be the answer to forcing a software render despite the scale, I’m not sure.

Anything else going on?

I’d like to talk more about strategy for the shapes in the new version, but also we should find why -Dlegacy is not working for you

Hi Joshua,

Thanks for the speedy reply.

I managed to track down the -Dlegacy issue via a good old fashioned binary search through our startup code. Turns out the issue was that in flash/openFL mode our engine relied on an initial stage RESIZE event to initialise some stuff, which was required for it to be in a “ready” state. What was happening was that the first ENTER_FRAME which drives our update loop was occurring before this, which in turn triggered Something Bad. Fixing was a simple matter of adding an isReady check around the update function (which really should have been there anyway), although presumably this is new behaviour since it worked in our previous version of OpenFL.

As you your quandary re: Cairo and graphics performance, my take would be that the current behaviour may be acceptable if cacheAsBitmap on the Sprite is true (even if this deviates from Flash behaviour, it seems like a sensible optimisation). However, I don’t believe it should be the default since it sort of defeats the typical goal of using vector graphics otherwise.

I’d imagine there are some nice optimisations you can make though, for example, if the resolved world transform in the scene graph for a particular Sprite has a smaller scale than it previously did, then probably no need to redraw (unless it’s a massive change, in which case you might do so in the name of freeing up memory). Similarly, when scaling up there could be an acceptable tolerance before the cache is deemed dirty and requires a redraw.

The other side of the argument, of course, is to keep the flash behaviour in its entirely and if people want caches which never redraw then they should implement them themselves by manually drawing to a bitmap and using that. I’m sure there are a world of tutorials out there covering that sort of optimisation for AS3 developers, so makes the transitionary learning curve easier. It all depends how committed you are to maintaining Flash behaviour, foibles and all (for better or for worse, I’m certainly no fan of the Flash API generally!).

Yeah, it’s tricky

Flash is always software, so redrawing is not an issue, but triggering software instead of hardware is a huge “misstep” that causes people to hamstring performance, maybe without realizing.

It’s simple to kill performance with a redraw – just tween the size. cacheAsBitmap (or cacheAsBitmapMatrix) seems like the way to toggle behavior (if we have more than one) but I can’t decide what is best for default, what is the alternative

It certainly is tricky.

From my point of view, I’d say the reason people use Graphics would largely be to get resolution independence and therefore making the fixed cached mode default is counterintuitive. I’d also argue that since OpenFL is, for the most part, a recreation of the Flash graphics API, then it makes sense to maintain the original behaviour wherever possible since it allows OpenFL users (particularly beginners) to easily learn from the huge volume of AS3 documentation / tutorials etc. out there. This is particularly important since there is a relative paucity of OpenFL specific material.

But I do, of course, fully take your point about the disproportionately large hit that redrawing takes vs. the Flash software renderer and ultimately this is a decision that needs to be driven by pragmatism more than any slavish adherence to the Flash of yore. However, personally, I think that this is a difference that perhaps users should be alerted to in OpenFL migration documents for AS3 developers, rather than trying to compensate for them.

Looking at the docs, it seems that the current behaviour in the new renderer is the equivalent of cacheAsBitmap always being true, and then any changes to transform.matrix actually behaving as changing the cacheAsBitmapMatrix property would in Flash.

It’s your call of course, but personally I’d say that changing scaleX/scaleY or the transform.matrix property should result in a redraw (albeit, as I noted previously, there are some optimisations that could reduce the amount of redrawing you do, even then). Then changing cacheAsBitmapMatrix transforms without redrawing. I’d also say that since it appears you are actually caching Graphics surfaces whatever the case, it might be that the cacheAsBitmap property is true by default and (perhaps) stays true even if you try to set it to false when running in this renderer.