Unreal Wizard banner

The Small Print

The Small Print

TLDR: Three things in UE 5.8 that cost frame time without anyone having asked for them. A stray Lighting Scenario checkbox on a streamed level made a 156 ms frame out of a 28 ms one. A landscape converted to Nanite was still costing around 4 ms of RHI thread for ray tracing. And the Epic anti-aliasing preset renders TSR's history at 200%. One checkbox and two lines of config between them.

The last post was a cost that nobody could see. These three are costs that somebody agreed to without reading the terms. None of them is a bug - the engine is doing exactly what it was told. It's just that nobody remembers telling it ;p

All of this is UE 5.8.4 with Lumen, hardware ray tracing, Nanite, virtual shadow maps, TSR and classic level streaming. Timings are at 1920x1080 on a desktop PC with an RTX 3080.


The Lighting Scenario Checkbox #

A scripted play-through, and one frame of around 155 ms at a point where a small level streams out. The level held two spot lights and two meshes. Hardly the sort of thing that should take a tenth of a second to hide.

The level was flagged as a Lighting Scenario - a checkbox in the Levels window (bIsLightingScenario). A lighting scenario is a level whose built lightmaps apply to other levels, so that you can swap between, say, a day and a night bake. This one had no built lighting at all, and neither did anything else, as everything was lit by Lumen. Two other lighting levels carried the flag too. Most likely it had been ticked because the level "is lighting".

Every time a level with that flag is made visible or hidden, the engine calls UWorld::PropagateLightingScenarioChange. That call:-

It does both whether or not the level has any built lighting to propagate.

In real-life terms: you change one lightbulb in the hallway, and the building manager has every tenant carry their furniture out to the street and back in again - in case the new bulb changed how the sofa looks. Nobody checks whether the bulb is any different from the old one.

The frame in which that level was hidden, two runs each:-

Flag set Flag cleared
Frame time 156, 155 ms 28, 25 ms

And where the slow frame went, from a trace:-

Time
Game thread inside level streaming ~102 ms
- tearing down render state ~32 ms (28 ms of it the flush)
- re-adding render state ~67 ms
Texture streaming update, same frame 27 ms (4 ms is normal)

The fix is to clear the flag on any level that isn't a real lighting scenario. With dynamic global illumination and no lightmaps, no level needs it.

One catch: bIsLightingScenario has no edit flags, so editor Python can't set it. Batch-fixing means the Levels window, or a few lines of editor C++.

What Epic could do: skip the global re-create when the scenario level has no built lighting data. I haven't written or tested an engine change for this.


The Landscape That's Still Being Ray Traced #

Converting a landscape to Nanite sounds like it should take the landscape out of the ray-tracing bill. It doesn't. With hardware ray tracing on, the landscape still contributes ray-tracing geometry after Nanite is enabled on it.

The cost is easy to miss, as it doesn't show up as an obvious GPU pass. It shows up as RHI thread time, in areas with a lot of landscape in range.

Test-configuration build, standing still, the same build with only r.RayTracing.Geometry.Landscape changed from 1 to 0:-

Frame time, 1 Frame time, 0
Outdoor area A 14.48 ms 13.37 ms
Outdoor area B 13.18 ms 11.31 ms

The RHI thread saving across the two areas was 3.8 to 4.3 ms.

With the setting built into the project (along with the TSR change below), the RHI thread went from 11.23 to 7.41 ms in area A and from 9.24 to 5.90 ms in area B, and those areas stopped being limited by the RHI thread.

The setting goes in DefaultEngine.ini:-

[/Script/Engine.RendererSettings]
r.RayTracing.Geometry.Landscape=0

What it costs: the landscape drops out of ray-traced effects. In our scenes the visible change was small - walls in one area lit slightly brighter and more evenly. Compare screenshots before you ship it.


The History Buffer at 200% #

The engine's BaseScalability.ini sets r.TSR.History.ScreenPercentage=200 at anti-aliasing quality 3 (Epic) and at Cinematic. Levels 0 to 2 use 100.

So a project that ships Epic as its default is paying for a history buffer at twice the resolution on each axis, whether or not anyone chose that.

With the value at 100, GPU time fell by 0.52 to 0.72 ms a frame at 1080p. That's out of a 10 to 14 ms frame.

To override it, in the project's DefaultScalability.ini:-

[AntiAliasingQuality@3]
r.TSR.History.ScreenPercentage=100

What it costs: a slightly softer image. A sharpness measure fell by 7 to 12% in the two most detailed areas. I saw no ghosting in motion screenshots, but I didn't check fast camera turns.


Both Settings Together #

Test-configuration build, with the landscape and TSR settings built into the project, against the same build without them:-

Before After
Opening area 10.50 ms 9.96 ms
Area 2 13.91 ms 13.19 ms
Area 3 13.27 ms 12.68 ms
Outdoor area A 14.83 ms 13.72 ms
Outdoor area B 12.54 ms 11.53 ms

Run-to-run spread was 0.01 to 0.34 ms.

Half a millisecond to a millisecond a frame isn't dramatic .. but it's two lines of config.


Three Shorter Ones #

Navigation data gathering. With DataGatheringMode=Instant on the navigation system and a dynamic navmesh, adding a streamed level's actors to the navigation octree cost 63 and 61 ms on the game thread at one level hand-over. With Lazy, the same updates took 1.4 and 1.0 ms. Navigation build totals were unchanged, and AI pathing showed no new warnings.

Unmipped, never-streamed effect flipbooks. A handful of large explosion flipbooks (up to 5000 px, HDR, no mips, Never Stream) accounted for most of a 1,110 MB non-streaming texture pool in one area, and for render thread frames of 81 to 89 ms when their level loaded. Capping them at 4096, compressing the HDR ones as BC6H and allowing streaming brought the pool down to 595 MB and those frames to 14 to 17 ms.

A movable sky light in each streamed lighting level. A movable sky light recaptures when its level becomes visible - about 75 ms on that frame. That's expected behaviour, but worth knowing before you split lighting across streamed levels. One sky light in an always-loaded level, adjusted with intensity and colour changes (which don't recapture), avoids it.


Check Your Own Project #

I've only checked 5.8.4, and the visual comparisons for the landscape and TSR settings were matched screenshots at five spots.


Takeaways #

Pinterest LinkedIn WhatsApp