Prerequisites
Game Version
Bug Description
The alpha drain for in-game UI messages runs once per render frame, while the amount it subtracts is derived from the logic frame age. Messages therefore disappear sooner as the render frame rate rises.
amount = REAL_TO_INT( ((currLogicFrame - m_uiMessages[ i ].timestamp) * 0.01f) ); // InGameUI.cpp:1898
if( a - amount < 0 )
a = 0;
else
a -= amount;
timestamp is a logic frame (InGameUI.cpp:2429), and the loop sits in InGameUI::update(), which is called from GameClient::update() (GameClient.cpp:734) on the render path.
Because REAL_TO_INT truncates, amount stays 0 until the message is 100 logic frames old, so the initial hold is the same at any frame rate. The sensitivity is entirely in the drain phase:
| render FPS (logic 30) |
message fully faded |
| 30 |
9.2 s (measured 8.85 s) |
| 60 |
7.1 s |
| 120 |
5.5 s |
The 30 FPS row is confirmed in game. The 60 and 120 rows are simulated from the code, not measured.
Note that alpha is stored as an UnsignedByte and amount is an Int, so simply scaling amount by the frame pacer ratio would truncate to zero at high frame rates. A fix needs either fractional state or gating on the logic step.
Reproduction Steps
- Start a network game (logic/render decoupling is always active there), or lower the logic time scale in skirmish, with the render FPS limit at 30 and the logic rate at 30.
- Save the game to post the "Game Saved" message, and time how long it stays visible.
- Repeat with the render FPS limit at 120 and the logic rate still 30 — the message fades noticeably sooner.
Record both the render FPS and the logic rate for each run.
Additional Context
These figures assume the message timeout is effectively zero, which is what the shipped data gives today. There is a separate defect in how MessageDelayMS is converted to frames; if that is corrected, this behaviour changes with it.
Prerequisites
Game Version
Bug Description
The alpha drain for in-game UI messages runs once per render frame, while the amount it subtracts is derived from the logic frame age. Messages therefore disappear sooner as the render frame rate rises.
timestampis a logic frame (InGameUI.cpp:2429), and the loop sits inInGameUI::update(), which is called fromGameClient::update()(GameClient.cpp:734) on the render path.Because
REAL_TO_INTtruncates,amountstays 0 until the message is 100 logic frames old, so the initial hold is the same at any frame rate. The sensitivity is entirely in the drain phase:The 30 FPS row is confirmed in game. The 60 and 120 rows are simulated from the code, not measured.
Note that alpha is stored as an
UnsignedByteandamountis anInt, so simply scalingamountby the frame pacer ratio would truncate to zero at high frame rates. A fix needs either fractional state or gating on the logic step.Reproduction Steps
Record both the render FPS and the logic rate for each run.
Additional Context
These figures assume the message timeout is effectively zero, which is what the shipped data gives today. There is a separate defect in how
MessageDelayMSis converted to frames; if that is corrected, this behaviour changes with it.