Seeking Advice on Optimizing Game Performance with Flax Engine

Hey everyone,

I have recently started working on a small indie game using Flax, and I am really loving the engine so far. However, I have noticed that my game’s performance starts to dip when I add more complex assets and animations. However I have configured feature using Generative AI. I am still relatively new to optimizing games, so I’m hoping to get some advice from the community.

What are some best practices or tips you all recommend for optimizing performance in Flax? I’ve tried reducing poly count and tweaking some LODs, but it still feels a bit sluggish at times. Are there any specific tools or settings in Flax that I should be paying more attention to? I have done a bit of research and found these articles How can I speed up the engine? Generative AI Interview Questions useful but would love some guidance from the experts here.

Any help would be much appreciated!

Thanks in advance :slight_smile:

Smith

Have you used a profiler to figure out what is causing performance hiccups?

Just an FYI, rendering has been improved for the future 1.9 update.

Here are some tips that will apply to any real time rendering engine. Flax is no exception even though it is very fast. No TLDR…take your time. It is an easy read.

  • Obviously, use the profiler to see what is taking the large clumps of time. The issue with the profiler is that if you design/build improperly it will not magically fix that. You know where to look but what is the solution? To make better use of the profiler you need to do some better foundational things…I explain more below.

  • Arrays. Try to never have your arrays larger than 256 -512(List, various 2D array tables…etc). There is a reason for this but it is too deep for this conversation. It has to do with how fast CS and CPUs index the arrays. People always forget this and have arrays that are far too large. You are using AI which needs a lot of lookup tables. You can go higher but with special considerations and not hitting the array too often.

  • Meshes. You should never have a single mesh over 65535 vertices. Some engines handle this by splitting the mesh then using a header mesh to combine them back together. Yes, even UE5 with Nanite does this but it is not obvious, Unity is obvious, Flax is also not obvious (you can tell though). The issue with this is the vertex arrays are Int16 not Int32 (maximum UINT is 65536). They have never moved to Int32 because of array performance. Similar issue as the arrays above but this has a special rendering handler to blast through the vertex array. These split meshes have all sorts of performance issues with materials and rendering them. If you need a collection of meshes to form a whole make sure each piece is 65535 or less then assemble them in a prefab. You can even share material instances and textures since the meshes are UV based. Do the performance enhancement in Blender.

  • This next part is very important in building for performance in the first place. You will not need to do much optimization later because you can test as you go easily. The method described below will save you countless hours in solving where your design is having issues. Once you get used to doing the below (it actually speeds up programming by factors!) your optimization phase will be look at it once then move on. Once it works it works. If you need to return to it for some reason, it is easy to find and modify and test.

  • For your coding you must have a scheduler/handler in your scene. I call mine oArea. It handles time, events, sun rotation, weather, all sorts of things, and more importantly handles performance by activating Bool Events on specific intervals. The lowest interval is 25ms (meaning you should do nothing faster than 25ms unless it is very specific). Usually 50ms (20x per second) is plenty fast. Everything else in the scene/level that has a script finds this oArea and ‘listens’ to the state change of the bools. Inside their script are logic sections that are based upon that bool is true to process that logic/function. Simple and effective optimization.

To not leave you hanging to solve this on your own…an example of a global logic based event timer. I will show you the visual script in UE4/5 for easy understanding.

  • This is easy to adapt to CS/Godot/Python/C++…etc.
  • Scene/Level handler script. This will be a decently large script since it does a lot of things eventually.
  • Set the Delta Time to a variable each frame/tick…in Flax
public override void OnUpdate()

Note: Flax has both Scaled and Unscaled frame time deltas. The default Scaled Delta is 1.0 but can be changed. This is handy but make sure to note this somewhere if using it or it will cause you all sorts of headaches when using the delta time and it not equaling what you think is should. :grin:

UnscaledDeltaTime 

  • Add this Delta Variable to a accumulation variable…say 100ms. f100ms. Notice in UE4/5 we can actually call a special/custom system event. Any scripted object in the scene/level can subscribe to this event. Flax has events but they mean something different. You will simply have to Find your handler and script at Start, then set the Bool you need to an internal variable to the object script.

  • Each scripted object needs this.
    image

Find oArea then set as an Object Reference in the script (so you don’t have to keep using Find. Just do it once at Start)

What this is saying is: if oArea 100ms_Event = True then set b100ms_Event to True.
(in a CS script these would be organized to the top portion of the script)

If b100ms_Event = True then do something.
(Typically the best way to do this is add Functions at the bottom then call them from above. You don’t have to but as the script grows it becomes much easier to manage, test, and add/remove things later.

As example of a Function:
This part here is in the upper ‘Logic’ portion of the script. Notice how clean this Function call is. All of the code is embedded in GetNewFrameTime(fFloat)

fNewFrameTime = GetNewFrameTime(fFrameTime);

This part here is the Function in the bottom portion of the script. FYI the purpose of this example is to provide time compression to the in game time…example 1 Real Time Hour equals 4 in Game Hours.

// Gets the new frame time based upon a Seconds Compression factor.
    // fFrameTime = Time.deltaTime (Unity) UnscaledDeltaTime (Flax)
    // Basically Compression Adds Frame Time Faster
    public float GetNewFrameTime (float fFrameTime)
    {
        float fVal = 0.0f;

        // Seconds Compression Factor
        fSecondsCompressionFactor = (float)nSecondsCompressionFactor;

        // New Frame Time Based upon Compression        
        fVal = fSecondsCompressionFactor * fFrameTime;

        return fVal;
    }