Actuate scaling animation jerky with SVG's

Hi,

I hope that you all stay safe in this corona virus dark times.

I have a project that requires to use a good number of SVG’s in a board (Sprite or bitmap), and I want to make a zoom in and a zoom out effect using actuate. I’ve manage to do that fairly easy, but when I have more than 20 SVG’s item on the board, the animation starts to look a little jerky, if I use more than 50 SVG’s then it’s not an animation any more.

This happens to the HTML5 and NEKO targets (not tried the others), but not with the FLASH target as expected. I was expecting this to happen, because I know that SVG performance degrades when the number is high, so I’ve tried using BitmapData and the performance was good even with hundreds of items, but this project require that the items look good when scaling, and they do not look good with Bitmap, even if I rescale them before putting them in the bitmap, using the smoothing options didnt help, I’ve tried the allow-high-dpi=“true”, everything I could find to make the bitmap look reasonable, and noting worked.

But one thing that is strange is when I use the HTML5 target, with the SVG (not the bitmap), the animation when scaling look jerky and slow, but if I use the browser develop tools -> “responsive design mode”, I can resize the canvas all I want and the scaling is fast with no degradation of the items. I can use 500 SVG’s and the actuate takes a minute or so to draw to the final scale, but if I rescale using the “responsive design mode” handle, it scale almost instantly.

Can someone explain to me what is in play, behind the scenes, when the browser fires an “Event.RESIZE” and openfl auto-resize the stage? How is the scaling done?

I’ve used the matrix to scale and the result is the same, if I understand what is happening when the stage is auto-resized, perhaps I can optimize the zoom in and zoom out effect.

Here it is the code for anyone that wants to try:

import openfl.display.Sprite; import format.SVG; import openfl.Assets; import motion.Actuate; import motion.easing.Expo; import openfl.events.MouseEvent;
class Main extends Sprite 
{
	public var board:Sprite = new Sprite(); // board
	
	public function new() 
	{
		super();
		addEventListener(MouseEvent.MOUSE_WHEEL, wheelEventHandler);
		
		board.graphics.lineStyle(2, 0xCCCCCC);
		board.graphics.beginFill(0xFFFFFF);
		board.graphics.drawRect(0,0,700,400);
		board.graphics.endFill();
		addChild(board);
		
		for (i in 1 ... 100) { // number of SVG's on the board
			var item:Sprite = new Sprite(); // each item on the board
			var itemSVG:SVG = new SVG(Assets.getText("img/openfl.svg"));
			itemSVG.render(item.graphics);
			item.width = item.height = 50;
			board.addChild (item);
			item.x = 650 * Math.random();  item.y = 350 * Math.random();
		}	
	}
	private function wheelEventHandler(event:MouseEvent):Void {
		switch (event.type) {
			case MouseEvent.MOUSE_WHEEL: {
				if (event.delta > 0) { // wheel scroll up, scales up the board by 20%
					Actuate.tween (board, 1, {scaleX: board.scaleX * 1.2, scaleY: board.scaleY * 1.2}).ease (Expo.easeInOut);
				} else { // wheel scroll down, scales down the board by 20%
					Actuate.tween (board, 1, {scaleX: board.scaleX * 0.8, scaleY: board.scaleY * 0.8}).ease (Expo.easeInOut);
				}
			}
		}
	}
}

Sorry Joshua, probably you are the only one that could answer this question, what is using, the openfl html5 target, for the stage auto-resize? Because that is much better performant than normal stage scaling.

I’ve been inspecting the code for dealing with the resize event for an HTML5Window and the resize behaviour of the stage, if the backend is HTML5, and for what I could interpreted, the stage is normally resized by the height and width properties, so the performance degradation that I’m having is probably the actuate fault. So, the best option for my case is to try other tween libraries, or can actuate be tweaked?

Hi, I don’t have a solution but just want to point out it’s most probably not Actuate at fault here : all it does is changing some properties values, it doesn’t handle rendering.

Maybe try to render all SVGs on a single sprite, if possible ?

Also there might be something like .cacheAsBitmap happening ? Something like that would easily kill performance.

I barely touched the html5 target so maybe a stupid question, but have you checked if it was using webGL or canvas ?

Hi, thank you for taking some time to reply.

I’m only suspecting and not stating that it’s entirely Actuate fault. I don’t understand why the stage auto scaling in response to the browser resize event is super fast, with no visual degradation, and for what I can see it’s only changing the width and height of the stage that has all the Sprites in it.

The app will have many SVG’s at the same time, the user can add or delete SVG’s in the process and they have to be independent of one another as they can be positioned at any time in any place the user wants. I don’t know how can I achieve that using only one sprite, and not complicate things too much.

The app is something like Inkscape, in the sense that we can load SVG’s, move them around, zoom in and out, rotate, etc. My app is not intended to be a vector editor, is something else, not even close to that, but for this behaviour is just like the zoom in/out of Inkscape.

I recall trying to fiddle with that, but when I activate the cacheAsBitmap, a strange behaviour happened when moving one or more Sprites or when I use the Actuate, the stage did not render properly. Perhaps I didn’t use it correctly. Using the example code above, could you say how can I activate (or deactivate, if it is by default) the cacheAsBitmap?

Yes, I use trace(stage.window.context.type); and it’s returning webgl.

I’ve made a short video demonstrating what I was telling in the first post. The video is small, only 3Mb, so I’ve uploaded to WeTransfer:

Instead of adding 100 Sprites of the OpenFL SVG logo to the board Sprite, I’ve added 1000 Sprites. In the video does not show the initial rendering of the Sprites, but it took on my machine, between 15 to 20 seconds. Between animation rendering of the 2 Actuate animations of zoom in, it took more or less, 20 seconds each. When I activate the “responsive design mode” of Firefox, the rescaling, is almost instantaneous, as you could see in the video. I made the same test with 5000 Sprites, it took a while to do the initial render, but in responsive mode, the scaling is also almost instantaneous, and whether I’m upscaling or downscaling, there is no visible degradation on the scaled Sprites.

So I cannot understand the reason for the upscaling taking so long for actuate, and what is happening behind the scenes when the stage is rescaled by an outside resize event, to get such a great performance? If someone can see the video, and explain to me what can it be?

Hi! I can’t understand WHY your stage is resizing while Firefox resize it’s window. Do you have static (not a “0”) width and height in project.xml file? Are you sure that the STAGE is scaling? Please, check stageWidth and stageHeight values, is it changes? If is’t not, then it’s just a RASTER browser canvas scaling, there is no any VECTOR redraws, so quality must be not SO good and sharp. See the screenshots, first is original

, second is VECTOR scaling , third is just browser canvas RASTER scaling Still not so bad, smooth, but there is no vector features. When you scale a sprite, it MUST fairly redraws ALL the vectors (lines, circles, fills…) and it just cannot be fast! Try to add small sprite (10x10) and then scale it with scaleX and scaleY=100. Then set small stage size (!) and try scale this small sprite just by browser resizing, maybe then you will see the difference? If you still see sharp vector graphics, then It would be very interesting for me to figure out how this can be possible.!

P.S. Real vector graphics (such as SVG) are very slow and not suitable for animation. Many benchmarks (crystalmark for windows, motionmark for browsers) use it for testing, like “look, your computer can draw 1000 single vector shapes (filled circles, rectangles…) 10 timer per second, great!”. Also, if you go to microsoft Word and add 50-100 clipart pictures (which is vector) on one page, it draws and scales veeery slowly. The only way to animate vector graphics is cheating - rasterize it, scale with animation, and then draw real vector graphics once. In any case, to make the application work comfortably, there should be few vector graphics, not hundreeds of images…

Yeah, that was my initial thought, that this is only possible if the browser scales a canvas raster, but when I saw that it scales the stage with little degradation, I wonder if there’s something else at play. Your right, I scaled the vector to a small size and reduced the size of the stage and then the raster scaling was evident when I stretch the canvas up, my initial confusion was that the scaling was so smooth compared with the openfl bitmap image scaling that make me think that probably it was scaling the sprite of the stage and not a canvas raster. Thank you for clearing this out.

I understand this and I was kinda expecting that I cannot use more than a few dozens of vector graphics, but when I was stress testing the performance of the vectors on HTML5 and changed the size of the browser, to try a mobile perspective, that I came across this.

The thing is that I have already have a similar app to what I’m trying to do, with almost 10 years, made in AS using vector graphics, and the performance is quite good, especially in more modern machines, with modern gpus. But once more I was not expecting to have the same performance out of html5, so I started with the approach that you are mentioning above, that is using bitmap images with the size of the max zoom that I allow for the app, and then let the scaling do it’s thing. I don’t even need to swap the scaled bitmap for the vector, if the final result is smooth enough (considering that the equivalent original image is always bigger that the scale used, meaning scale < 1). The final result need to be saved to a image of the entire board, so behind the scenes, before saving, I construct the board using the vectors in the place of the scaled images, to have the best quality in the final image. It all seemed good, until I start to test the scaling of the Bitmap objects and the quality was not good enough for this purpose, nothing compare to the raster browser canvas scaling (this is source of my confusion).

Perhaps, if I show my findings you could direct and help me to improve the quality of Bitmap scaling. In this next image is the comparison of the scaled OpenFL SVG (vector) and the scaled Bitmap PNG at 50% and less, in the browsers, flash and windows neko.


You can see that as we scale down the bitmap, the worse the quality of the scaled result, with the exception of the flash target (expected), This was made using .smoothing = true and allow-high-dpi=true. How can I improve this?

PS: I placed Chrome in there, because it was the worst of them all even the vector part (is not in the picture), but I think that the smoothing is disabled somehow, and need to be activated, I didn’t have the time to inspect this…

PS2: for better visualization, here is a zoomed imaged of the smaller scales:

The problem of scaling is relevant for me too, especially because I using only bitmap fonts in my games :frowning: And yep, chrome is the worst. Now i’m using only 0.5-2 scaling, it’s still look acceptable for me, so I gave up so far… :frowning: I’m not sure, but i think the problem is how browsers handles OpenGL textures scaling using linear texture filtering, but I can’t imagine how to intervene in this

Ok, i got it :slight_smile: Now it looks fine (downscaling) in WebGL and that is cool for me :slight_smile: But i think it’s necessary to make some changes in OpenFL to allow some textures (bitmaps) to generate mipmaps for downscaling. It takes some additional VRAM and it’s not cool to apply this to every bitmap, so it’s better to send it as parameter, like smoothing. But when bitmap is downscaled, OpenGL also would render it faster than without this modification.
The problems is:

  1. I’m using OpenFL 8.9.1, so there can be a big difference in render code with new versions.
  2. I will never compile to Flash, so i just add a new parameter to Bitmap (mipfiltering) and don’t care about incompatibility with Flash.
  3. There will be problems when switching to a new version of openfl. Because of changes like this, i still using 8.9.1 and it is veeery hard for me to update it…

If you are ready to go for it anyway, i can tell you what to do :slight_smile:

Yeessss, it work’s and look’s awesome!!! This will be perfect for my project (and for yours too, as it seems).

I’m using 8.9.6, so I can test this with te last version.

I don´t need to compile to Flash either.

I can make the test for you, no problem there.

Yes and yes, I’m ready, if this work, it will be a life-saver, and I don’t have to change to other language/framework. Please direct me to what I have to do for achieving this.

edit—
And it work’s flawless in Chrome too, simply awesome!!

Maybe consider using Starling ? It already has mipmaps

I know that, if one wants, can adapt something to do everything, but isn’t Starling more directed to game development? I’m developing an application. I’m not used to game frameworks, and yes I know that game frameworks aren’t hard to pick up, but do I need to use it? In the sense that I have to change my mindset to use entities and others things. (please correct me If I’m wrong, because I might be mistakenly confusion Starling with other frameworks, like HaxeFlixel…)

Starling is directed at providing hardware-accelerated graphics while keeping things similar to the flash API (DisplayObject, DisplayObjectContainer, addChild() etc). It’s not specifically made for game development, nothing comparable to HaxeFlixel.

On the flash platform over the last years Starling has been used a lot for mobile apps, along with the Feathers UI lib (which is now coming for regular OpenFL). My last mobile app is as3/AIR with Starling/Feathers.

Once hardware-accelerated graphics are really mature in OpenFL I think Starling will kinda disappear, or only be used when converting as3 projects. But for the time being it still has its uses :slight_smile:

I think it might make sense to use it and not mess with a special version of OpenFL in your case, the implications are not huge : some startup code, using Image instead of Bitmap and instanciating textures from bitmaps using Texture.fromBitmap() should be pretty much all you need for this.

Interesting, I did mistaken Starling for a game framework. I’m looking at the Starling github and it seems to serve my purposes.

I can see that this is on top of OpenFL, but the commit was about an year ago, is this being abandoned?
Edit — The last commit was about 2 months ago, I was seeing the Starling haxelib at the same time, and it is the v2.5.1 that was released a year ago.

My main target in the near future is HTML5, is Starling working well with this target?

So, all that I’ve done so far in OpenFL remains the same, only the Bitmap objects have to replaced with Starling Image objects (simplistic speaking), no major rework as to be done, right?

That’s good idea, you may try starling. It is not game framework, I even avoid this in my games because of some reasons - it’s additional excess layer with tons of code (which functional is pointless for OpenFL in my case), and also it conflicts with my main 3D game layer (which is based on Away3D modification).
But for some projects it is still usefull and should work without issues.
You can try this to avoid OpenFL intervention (I had to make so many modifications, so there is no way back for me). And if it still doesn’t look good for you - we can try to apply my patch :slight_smile: The problem is that i look to 8.9.6 and there are some difference with 8.9.1 in the places that I changed…

I’m using a version I took from Github right now, not sure when but it works with lastest OpenFL. It doesn’t need many updates unless changes in openFL require it, it’s a pretty mature lib on flash platform maybe 10 years old or something like that.

I don’t target html5 so far, I did a few tests and it seemed to work just fine. I expect it to work fine in your case as well since it’s pretty simple.

No major rework implied yes : you need some startup code for starling, then your entry point should be a starling.display.Sprite.
From your example code “board” would be a starling.display.Sprite too then every Item would be a starling.display.Image displaying a starling.textures.Texture that you can create from Bitmap or BitmapData.

When using Starling you don’t have Sprite.graphics though, there’s an extension for that but I don’t use it and I don’t know if it has been ported to Haxe. Also Starling uses a different “Stage” which displays behind non-Starling DisplayObjects. You can access both Starling’s and regular stage through Starling.current.stage and Starling.current.nativeStage.

Also if you want the best performance/lowest GPU usage the best is to have all your bitmaps in a single one, using a software like TexturePacker ( https://www.codeandweb.com/texturepacker ). This will result in a single big texture, it’s very easy to use with Starling’s AssetManager, that will allow GPU to draw every image in a single draw call.

something like :

var assetManager:AssetManager = new AssetManager();
var atlas:TextureAtlas = new TextureAtlas(Texture.fromBitmapData(Assets.getBitmapData("path/to/your/atlas.png")), Xml.parse(Assets.getText("path/to/your/atlas.xml")));
assetManager.addTextureAtlas("myAtlas", atlas);

and then to access textures (sub textures) :
var img:Image = new Image(assetManager.getTexture("logo1"))

Yeah, if your patch need an extensive OpenFL modification, I’ll try Starling first, and if doesn’t look good I’ll get back to you, if you don’t mind?

Good to know, I’ll try it for sure. I didn’t tried it sooner, because I was with the wrong impression that it was a game framework.

If the midmapping does make the downscalling look good, I will not use the SVG’s directly, I’ll use pre-prepared images, and when is time to save the stage to an image, I’ll construct a hidden OpenFL stage with the SVG’s in it for having the best graphical fidelity, that is, in the case that the Starling Image quality don’t reach the same quality of the SVG’s, otherwise, I might not need the SVG’s at all (perhaps only if I allow, latter on, the user to load is personal SVG’s).

As I don´t develop games, I don´t use packing all the bitmaps into a single image, so can I do that in a normal image editor?

I suppose that it requires more than the image packing, like some information of where are the location of the assets, am I right?

Is there something like the TexturePacker, but opensource or free that I could try before committing to something that requires payment?

You could do your own texture atlas by hand yes : it’s just a matter of putting all images in a single one and then create the xml, here is one of mine :

<TextureAtlas imagePath="hexagones_128.png">
    <SubTexture name="hex_action_range_128" x="111" y="258" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_action_range_long_128" x="112" y="2" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_action_range_max_128" x="112" y="2" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_action_range_min_128" x="112" y="2" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_action_range_short_128" x="112" y="2" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_active_entity_128" x="2" y="2" width="108" height="126" frameX="-1" frameY="-1" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_area_defense_128" x="112" y="116" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_area_effect_128" x="209" y="230" width="96" height="112" frameX="-7" frameY="-8" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_available_target_128" x="108" y="384" width="102" height="120" frameX="-4" frameY="-4" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_base_128" x="2" y="384" width="104" height="122" frameX="-3" frameY="-3" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_movable_128" x="112" y="230" width="21" height="26" frameX="-45" frameY="-51" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_movable_selected_128" x="2" y="258" width="107" height="124" frameX="-2" frameY="-2" frameWidth="111" frameHeight="128"/>
    <SubTexture name="hex_target_selected_128" x="2" y="130" width="108" height="126" frameX="-1" frameY="-1" frameWidth="111" frameHeight="128"/>
</TextureAtlas> 

There should be open source / free alternatives, since I bought a TP licence quite a while ago I never looked for one. I think I used some code to generate one dynamically during execution but that was quite a while ago, probably as3, if I find it I’ll post it here.

What TexturePacker really brings is UI + lots of options that you won’t need in this case.

Did a quick search and found this one : https://www.cyotek.com/cyotek-spriter

Thanks, when I get some results with Starling, I’ll post here a solution or a workaround for the problem I presented in the beginning of this thread.