Issue Checklist
What is your suggestion, and why should it be implemented?
Being a player of FNF, I always wondered one thing, how can this game be that slow ?
One day I wanted to play it on my old Core i5, and it was basically unplayable, couldn't do anything.
Plus with all the disclamers I see in mods (like Mario madness or Friday Night Dustin') about "how the game may be slow on low end devices" I was just like "Wait. So this game is heavier than GTA 4 (really, GTA 4 is smooth on my PC but for some reason Marion madness plays at 20 FPS) ? Make me laugh I will give you a coin".
Yeah Yeah, you may say that is something about mods and stuff, but as I said on my previous computer, FNF was basically unplayable, and I just don't see why.
So I decided to sneak a peak into the project to see how to make it faster, because I'm very angry.
I won't talk about everything I found weird in one issue so, I will go progressively as issues will be resolved (by me or anyone else).
So I will start with the easiest one.
Dead code
So skimming through the code base, I decided to focus on rendering because I think it's the only part that can really make a game slow. Won't talk about every shock I had but will start will this, there is a huge tons of unused code and redundancies.
Framebuffer.hx
Their purpose from what I saw is to be able to combine multiple objects into one texture to be able to process them (shader or anything else. But a quick grep in the code shows this:
> git grep "createFrameBuffer"
source/funkin/graphics/framebuffer/FrameBufferManager.hx: public function createFrameBuffer(name:String, bgColor:FlxColor):BitmapData
So basically this is totally unused anywhere. So my assumptions way be that only mods uses them (maybe) or it's just dead code.
Same with maskTexture in Stage.hx.
SpriteCopy.hx
By extension since only frame buffers uses this, it's also unused:
> git grep "SpriteCopy"
source/funkin/graphics/framebuffer/FrameBuffer.hx: final spriteCopies:Array<SpriteCopy> = [];
source/funkin/graphics/framebuffer/FrameBuffer.hx: public function addSpriteCopy(spriteCopy:SpriteCopy):Void
source/funkin/graphics/framebuffer/FrameBufferManager.hx: frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color));
source/funkin/graphics/framebuffer/SpriteCopy.hx:class SpriteCopy
Those don't really have an impact on performances but's always worth noting.
NoteSprites HSV
This snippet is from NoteSprites.hx, lines 262 to 267
// The hsvShader should only be applied when it's necessary.
// Otherwise, it should be turned off to keep note batching.
this.shader = null;
this.hsvShader.hue = 1.0;
this.hsvShader.saturation = 1.0;
this.hsvShader.value = 1.0;
So every notes even thoughs thes mostly don't use the HSVShader still allocate one. This is just waste and could be easily fixed with shared shaders or memoizations (every time you want to apply HSV to a sprite, you query it from a resource manager and apply directly instead of this)
FunkinGroups.hx
This is by far the weirder part of the code base. Since there already exist FlxTypedGroup
> git grep "FunkinGroup"
CHANGELOG.md:- Added a new FunkinGroup class to make groups of sprites easier to modify. ([4f31600](https://github.com/FunkinCrew/Funkin/commit/4f316003af9a6ca46c77bf4971059e20543cbd7b)) - by @MightyTheArmiddilo in [#6758](https://github.com/FunkinCrew/Funkin/pull/6758)
source/funkin/group/FunkinGroup.hx: * A FunkinGroup of FlxSprites.
source/funkin/group/FunkinGroup.hx:typedef FunkinSpriteGroup = FunkinGroup<FlxSprite>;
source/funkin/group/FunkinGroup.hx:class FunkinGroup<T:FlxSprite> extends FlxSprite
source/funkin/group/FunkinGroup.hx: * The children of this FunkinGroup.
source/funkin/group/FunkinGroup.hx: * The size of this FunkinGroup. Read only.
source/funkin/group/FunkinGroup.hx: * The length of this FunkinGroup. Read only.
source/funkin/group/FunkinGroup.hx: * The max size of this FunkinGroup. 0 and below is infinite.
source/funkin/group/FunkinGroup.hx: * The width of all the FunkinGroup's children's displays put together.
source/funkin/group/FunkinGroup.hx: * The height of all the FunkinGroup's children's displays put together.
source/funkin/group/FunkinGroup.hx: * If this is false, the FunkinGroup will update children normally. Otherwise,
source/funkin/group/FunkinGroup.hx: * Should this FunkinGroup treat itself more like one image (in scale terms).
source/funkin/group/FunkinGroup.hx: * Should this FunkinGroup treat itself more like one image (in angle terms).
source/funkin/group/FunkinGroup.hx: * Sets this FunkinGroup's `origin` to the center of its complete graphic.
source/funkin/group/FunkinGroup.hx: * Constructor for FunkinGroup.
source/funkin/group/FunkinGroup.hx: * @param preciseScale Whether to treat the FunkinGroup like one image (with scale).
source/funkin/group/FunkinGroup.hx: * @param preciseAngle Whether to treat the FunkinGroup like one image (with angle).
source/funkin/group/FunkinGroup.hx: * Adds a child to this FunkinGroup. Will also return said child for convenience.
source/funkin/group/FunkinGroup.hx: * @param child The child that the caller wants this FunkinGroup to add.
source/funkin/group/FunkinGroup.hx: * Makes a child right in this FunkinGroup. Will also return said child for convenience.
source/funkin/group/FunkinGroup.hx: * Adds a child to this FunkinGroup at a given index. Will also return said child for convenience.
source/funkin/group/FunkinGroup.hx: * @param child The child that the caller wants this FunkinGroup to add.
source/funkin/group/FunkinGroup.hx: * Moves select children from another FunkinGroup into this one. Only works if both FunkinGroups contain the same type.
source/funkin/group/FunkinGroup.hx: public function move(grp:FunkinGroup<T>, children:Array<T>):Void
source/funkin/group/FunkinGroup.hx: * Sorts the children of this FunkinGroup. Returns the sorted children
source/funkin/group/FunkinGroup.hx: * Get's the first alive child under this FunkinGroup. Returns null if it can't
source/funkin/group/FunkinGroup.hx: * Get's the first dead child under this FunkinGroup. Returns null if it can't
source/funkin/group/FunkinGroup.hx: * Counts the amount of alive children in this FunkinGroup.
source/funkin/group/FunkinGroup.hx: * Counts the amount of dead children in this FunkinGroup.
source/funkin/group/FunkinGroup.hx: * Gets the index of the first null child under this FunkinGroup.
source/funkin/group/FunkinGroup.hx: * Gets a random child from this FunkinGroup.
source/funkin/group/FunkinGroup.hx: override public function clone():FunkinGroup<T>
source/funkin/group/FunkinGroup.hx: var group = new FunkinGroup<T>(x, y, maxSize);
source/funkin/group/FunkinGroup.hx: // Unavailable functions that won't work with `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: throw "This function is not supported in FunkinGroup";
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: throw "This function is not supported in FunkinGroup";
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: throw "This function is not supported in FunkinGroup";
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/FunkinGroup.hx: * This functionality isn't supported in `FunkinGroup`.
source/funkin/group/ScriptedFunkinGroup.hx: * A script that can be tied to a FunkinGroup.
source/funkin/group/ScriptedFunkinGroup.hx: * Create a scripted class that extends FunkinGroup to use this.
source/funkin/group/ScriptedFunkinGroup.hx:class ScriptedFunkinGroup extends funkin.group.FunkinGroup<Dynamic> implements polymod.hscript.HScriptedClass {}
source/funkin/group/ScriptedFunkinGroup.hx:class ScriptedFunkinSpriteGroup extends funkin.group.FunkinGroup.FunkinSpriteGroup implements polymod.hscript.HScriptedClass {}
This is also totaly unused. And also it counterintuitive as it override methods to make them throw, which is a direct violation of the L in the SOLID
So functionnalities like parent children relationships maybe legit but except that this is mostly useless, just use FlxTypedGroup
Weird comment in NoteSprites.hx
/**
* This is true if the note is later than 10 frames within the strumline,
* and thus can't be hit by the player.
* It will be destroyed after it moves offscreen.
* Managed by PlayState.
*/
public var hasMissed:Bool;
/**
* This is true if the note is earlier than 10 frames within the strumline.
* and thus can't be hit by the player.
* Managed by PlayState.
*/
public var tooEarly:Bool;
/**
* This is true if the note is within 10 frames of the strumline,
* and thus may be hit by the player.
* Managed by PlayState.
*/
public var mayHit:Bool;
These comments are misleading as a simple grep shows:
> git grep "HIT_WINDOW_MS"
source/funkin/play/PlayState.hx: var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
source/funkin/play/notes/Strumline.hx: var hitWindowStart:Float = conductorInUse.songPosition - Constants.HIT_WINDOW_MS;
source/funkin/play/notes/Strumline.hx: var renderWindowEnd:Float = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + (renderDistanceMs / magicNumberIGuess);
source/funkin/util/Constants.hx: public static final HIT_WINDOW_MS:Float = 160.0;
source/funkin/util/GRhythmUtil.hx: start: note.strumTime - Constants.HIT_WINDOW_MS,
source/funkin/util/GRhythmUtil.hx: end: note.strumTime + Constants.HIT_WINDOW_MS
All codes now use HIT_WINDOW_MS. Those misleading comments almost drove me crazy.
Issue Checklist
What is your suggestion, and why should it be implemented?
Being a player of FNF, I always wondered one thing, how can this game be that slow ?
One day I wanted to play it on my old Core i5, and it was basically unplayable, couldn't do anything.
Plus with all the disclamers I see in mods (like Mario madness or Friday Night Dustin') about "how the game may be slow on low end devices" I was just like "Wait. So this game is heavier than GTA 4 (really, GTA 4 is smooth on my PC but for some reason Marion madness plays at 20 FPS) ? Make me laugh I will give you a coin".
Yeah Yeah, you may say that is something about mods and stuff, but as I said on my previous computer, FNF was basically unplayable, and I just don't see why.
So I decided to sneak a peak into the project to see how to make it faster, because I'm very angry.
I won't talk about everything I found weird in one issue so, I will go progressively as issues will be resolved (by me or anyone else).
So I will start with the easiest one.
Dead code
So skimming through the code base, I decided to focus on rendering because I think it's the only part that can really make a game slow. Won't talk about every shock I had but will start will this, there is a huge tons of unused code and redundancies.
Framebuffer.hx
Their purpose from what I saw is to be able to combine multiple objects into one texture to be able to process them (shader or anything else. But a quick grep in the code shows this:
So basically this is totally unused anywhere. So my assumptions way be that only mods uses them (maybe) or it's just dead code.
Same with
maskTexturein Stage.hx.SpriteCopy.hx
By extension since only frame buffers uses this, it's also unused:
Those don't really have an impact on performances but's always worth noting.
NoteSprites HSV
This snippet is from NoteSprites.hx, lines 262 to 267
So every notes even thoughs thes mostly don't use the HSVShader still allocate one. This is just waste and could be easily fixed with shared shaders or memoizations (every time you want to apply HSV to a sprite, you query it from a resource manager and apply directly instead of this)
FunkinGroups.hx
This is by far the weirder part of the code base. Since there already exist
FlxTypedGroupThis is also totaly unused. And also it counterintuitive as it override methods to make them throw, which is a direct violation of the L in the SOLID
So functionnalities like parent children relationships maybe legit but except that this is mostly useless, just use FlxTypedGroup
Weird comment in NoteSprites.hx
These comments are misleading as a simple grep shows:
All codes now use
HIT_WINDOW_MS. Those misleading comments almost drove me crazy.