Skip to main content

Debugging & crash handling

FPS and memory overlay​

debug.FPSCounter (created in Main.setupGame() as Main.fpsVar) shows framerate and, optionally, RAM and peak RAM. Driven by:

PreferenceEffect
showFPSshow the counter at all
showRamUsage / showMaxRamUsageadd current / peak memory
debugInfoextra diagnostics

Memory numbers come from debug.Memory and utils.MemoryUtil; debug/mem/ contains a small C++ include (GetTotalMemory.hx + build.xml) for an accurate native figure.

Two optimizations to preserve: the PlayState check is cached (Main.isPlayState()) rather than re-running Std.isOfType each frame, and the text outline redraw is throttled.

Crash handling​

With CRASH_HANDLER enabled (the default), backend.CrashHandler.init() runs before the game is created and installs an OpenFL uncaught-error handler. On a crash it writes a log and shows states.ErrorState instead of killing the window.

The readable native stack traces come from defines set in project.hxp: openfl-enable-handle-error, HXCPP_CHECK_POINTER, HXCPP_STACK_LINE, HXCPP_STACK_TRACE, HXCPP_CATCH_SEGV.

ClientPrefs.peOGCrash switches to the plain Psych-style crash dialog for users who prefer it. CoolUtil.coolError(message, title) is the engine's own error popup, used for recoverable problems (for example opening the Note Colors menu with the feature off).

Hardware information in the crash log​

Every crash log (crash/FNF-Phoenix-Engine_<date>.log) ends with the hardware the game was running on, so a bug report carries it automatically:

GPU: NVIDIA GeForce RTX 3060
GPU Vendor: NVIDIA
GPU Driver: 535.104.05
Graphics API: OpenGL 4.6
CPU: Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz
CPU Vendor: Intel
OS: Windows 10.0.19045
Audio: OpenAL Soft | 1.23.1

backend.Native collects the values: the GPU, its vendor and the driver version come from the OpenGL context (GL.VENDOR, GL.RENDERER, GL.VERSION), the CPU and the operating system from the small native layer in source/backend/native/. The GPU sniffing is ported from the Psych Plus Engine — thanks to the Psych Plus Team for the original implementation.

Everything degrades gracefully: a target without a render context or without the native queries reports Unknown instead of failing, and the crash log is still written.

The same values are available to scripts through getSystemInfo(), getGPUName(), getGPUVendor(), getGPUDriverVersion(), getCPUName(), getCPUVendor(), getOSName(), getOSVersion(), getGraphicsApi() and getAudioApi() (Lua, Python and HScript), see the Lua and Python API references.

Script errors​

Lua, Python and HScript errors never take the game down:

  • shaders.ErrorHandledShader catches shader compile failures.
  • psychlua.DebugLuaText prints script errors and debugPrint(...) output on screen, through luaDebugGroup / pythonDebugGroup in PlayState.
  • Script hosts catch exceptions per call, so one broken hook does not stop the others.

Build metadata​

backend.HaxeCommit exposes the git branch/commit the build came from (printed by project.hxp's flair() at build time), which is what crash reports and the title screen can show. VERSION in project.hxp is the single source of the version string.

Discord Rich Presence​

backend.DiscordClient (flag DISCORD_ALLOWED, library hxdiscord_rpc) publishes the current state and song. It is toggled by ClientPrefs.discordRPC, scripts can change it with changePresence(...), and option menus set an rpcTitle. On Android the equivalent is routed through android.platform.AndroidDiscord.

Profiling and experimental builds​

DefineEffect
-DDEBUG_TRACY / FEATURE_DEBUG_TRACYTracy profiler instrumentation
-DULTRA_OPTIMIZEDexperimental maximum-speed configuration
-DULTRA_HTML5experimental web optimization
-debugFlixel/Haxe debug build, enables #if debug blocks

Tracing conventions​

Unconditional trace() calls were deliberately removed from hot paths. New ones belong inside #if debug:

#if debug
trace('loaded ${notes.length} notes');
#end

Screenshots​

backend.Screenshot plus the backend.SSPlugin Flixel plugin (added in Main.setupGame()) capture the window to disk without going through the OS.