HaxeFlixel / flixel-addons

Additional classes for HaxeFlixel
170 stars 136 forks source link

Terrible FlxSliceSprite performance on native #404

Open Geokureli opened 1 year ago

Geokureli commented 1 year ago

I've only tested on mac, but adding the following to the FlxCamera demo brings the fps from 60-62 down to 20-23

import flixel.math.FlxRect;
import flixel.addons.display.FlxSliceSprite;

@:forward
abstract BorderSprite(FlxSliceSprite) from FlxSliceSprite to FlxSliceSprite
{
    inline public function new (x = 0.0, y = 0.0, width:Float, height:Float)
    {
        this = new FlxSliceSprite("assets/Border.png", new FlxRect(15, 15, 20, 20), width, height);
        this.x = x;
        this.y = y;
        #if debug
        this.ignoreDrawDebug = true;
        #end
    }
}

Edit: upon further investigation, the above code struggles because when width and height are 1280 by 960, this is using 25,480 vertices to draw this. By adding the following lines, the vertex count goes down to 72!

this.stretchBottom = true;
this.stretchTop = true;
this.stretchLeft = true;
this.stretchRight = true;
this.stretchCenter = true;

This still doesn't explain why the performance issue was only worse when FlxG.drawDebug is enabled, though. but the above change eliminates the performance issue, entirely. Perhaps drawDebug is a red herring, and the usual slowdown from drawDebug made a more noticeable impact when combined with the slowdown caused by the inefficient slice sprite vertices.

FlxStrip is capable of rendering a repeating image across, for instance:

final strip = new FlxStrip(20, 20, "assets/images/haxe.png");
strip.loadGraphic("assets/images/haxe.png", true, 100, 100);
strip.vertices = DrawData.ofArray([0.0, 0, 160, 0.0, 160, 80, 0, 80]);
strip.uvtData = DrawData.ofArray([0.0, 0, 2, 0, 2, 1, 0, 1]);
strip.indices = DrawData.ofArray([0, 1, 2, 0, 2, 3]);
strip.repeat = true;
add(strip);

results in the following:

Screenshot 2023-08-30 at 11 18 00 AM

However this may not be applicable to FlxSliceSprite, because it's not repeating the entire texture, but only a middle portion.

Whenever I make sliceSprites I typically make the middle sections as small as possible, as I expect them to be stretched, by default and i want image sizes to be as small as possible. I do think think it makes sense to make these stretch flags true, by default, given that seemingly insignificant factors like image size and rect size can have exponential impacts on the performance and memory of slice sprites

SeiferTim commented 12 months ago

HashLink seems to have a huge issue with large flixel.addons.display.FlxSliceSprite If I have a small one, it's fine. (< 1/4 of the screen) if I have a larger one (1/2 the screen or larger) the FPS drops to single digits while it's visible.

I'm making a small side-project game and I wanted to have a pop-up menu use a SliceSprite as a background. I tried it as a FlxSubState with the SliceSprite about ~60% of the screen size (320x120 px) and uses a tween to alpha it in and out of visibility when openSubState was called. As soon as alpha > 0, HL started to REALLY chug and although it should have taken .5 seconds for the tween, it took more than 10.

Thinking it was just the alpha that was causing this, I switched to a regular FlxState and just made my SliceSprite the whole screen size , and then used FlxG.camera.fade when the state was switched. Again - as soon as the state was switched, FPS dropped to ~4 and it took way longer for the fade to finish.

I swapped the SliceSprite out for a FlxSprite using a png the size of the whole screen and the frames are back to 60. I was going to make an issue about it and saw this already here.

I have not tested this with this new project - yet - but I have another project with lots of FlxSliceSprites that runs fine on Windows target, but I have not been able to use HL for ever because of the FPS - although I never narrowed down what the issue was.

Geokureli commented 12 months ago

@SeiferTim did you notice this section:

Edit: upon further investigation, the above code struggles because when width and height are 1280 by 960, this is using 25,480 vertices to draw this. By adding the following lines, the vertex count goes down to 72!

this.stretchBottom = true;
this.stretchTop = true;
this.stretchLeft = true;
this.stretchRight = true;
this.stretchCenter = true;

you might get better performance on HL with these fields set to true, especially if you're using small source images. This is due to the fact that the default values will tile the image, in my case, using 36000% more vertices

SeiferTim commented 11 months ago

@SeiferTim did you notice this section:

Edit: upon further investigation, the above code struggles because when width and height are 1280 by 960, this is using 25,480 vertices to draw this. By adding the following lines, the vertex count goes down to 72!

this.stretchBottom = true;
this.stretchTop = true;
this.stretchLeft = true;
this.stretchRight = true;
this.stretchCenter = true;

you might get better performance on HL with these fields set to true, especially if you're using small source images. This is due to the fact that the default values will tile the image, in my case, using 36000% more vertices

Well, this did not seem to make a difference. Also, the sizes we're talking about are pretty small. My screen size is 320x180 and just having a Frame that is 1/2 the screen size is killing framerate. ???

Geokureli commented 11 months ago

The performance is only bad on HL? does making the size smaller improve performance at all? is it possible you're setting the width or height every frame, even if it's setting it to the same values used previously?

SeiferTim commented 11 months ago

The performance is only bad on HL? does making the size smaller improve performance at all? is it possible you're setting the width or height every frame, even if it's setting it to the same values used previously?

HTML5 and Windows run at 60fps This is a tiny game. 320x180px I have 3 places where I am using frames: The title screen has a small one that is about 1/5 the size of the screen and it's fine. I am using one on my pause screen which is a little larger than the title screen one and it's fine. On my HiScore screen I am using one that is about 1/2 the size of the screen and when that one is being drawn (it starts at alpha=0 - as soon as I start tweening) the frames hit single digits the entire time the frame is on the screen until I change states.

The HiScore screen has a few flxsprites, and a few FlxBitmapTexts nothing too crazy. Simply removing the Frame fixes the framerate. so it's definitely that.

The frame is being initialized in the FlxState.create and other than the alpha is not being touched again.

I'm using this class:

class Frame extends FlxSliceSprite
{
    public function new(Width:Float, Height:Float):Void
    {
        super("assets/images/ui_frame.png", FlxRect.get(15, 15, 4, 4), Width, Height);
        stretchBottom = stretchTop = stretchLeft = stretchRight = true;
    }
}

I'd be happy to show you code or anything in Discord if you want. This isn't a major issue - I'm only using HL for debugging and the game is done and works fine on the other targets. BUT it would be nice to not have to worry about this in the future.

Geokureli commented 11 months ago

Sounds like its not anything you're doing wrong, just wanted to check. HL is new and maybe openfl's hl backend is wonky, and requires a deep dive 🤷‍♂️