A common traditional implementation for engines is to set a high residency priority for render targets, UAVs, transient buffers, large streaming pools, raytacing accel structures and buffers, etc.
Unreal appears to do this for most of the above except streaming heaps that I can tell (nanite heaps? texture heaps?), which it probably should if it doesn’t. However, there is a couple more modern approaches that Unreal should consider.
Most aren’t aware that Windows can and will evict video memory to nonlocal even if the app is under the given video memory budget. If large allocations are at the same priority as small allocations, then you can get a nondeterministic order of which heaps evict. This is a problem if some space is freed up because there may not be space to reinstate those allocations in local mem for large allocations due to fragmentation. Generally, it is better to evict smaller heaps before larger heaps both from the impact on the frame and handling fragmentation when the game hovers around high vram usage. A solution to this is to always set residency priority for all resources upon allocation, scaled with the allocation size. The enum for priority is not fixed, it can be an int so you can set resource priorities at a rather high resolution relative to each other for that fine grained control. This change can keep the game running perf stable in many situations where it is under even somewhat high vram pressure (and will also make perf instability from evictions more deterministic)
On top of this, some engines have experimented with running a re-prioritization pass if the game hits a certain threshold based on usage/size. So the most recently touched heaps would reprioritize higher than older heaps. This can be fairly cpu intensive however as you might imagine, so amortizing/incrementing it over several frames is often necessary. This can also really save the game from completely barfing under high vram pressure, or at least mitigating the impact to a smaller timeframe (a hitch over a sustained period of performance degradation).
I will likely be implementing at least the size scaling for our project locally but won’t have any code for that for a while (I’ve implemented it in previous engines so I know at least one reasonable implementation), but wanted to put this out there in case Epic is already considering improving this.
Thank you for providing this feedback! We’re planning to take a closer look at residency soon including how we can share parts of the solution between D3D12 and Vulkan. - appreciate these recommendations and lessons learned.
Also, you mentioned things(render targets, UAVs, transient buffers, large streaming pools, raytacing accel structures and buffers) should have a high residency priority,
But what things are left that can have a lower residency priority? It seems everything should be set as high residency priority then.
Glad to hear it’s on the books. I can definitely stress that vram eviction is one of, if not the most common cause of significant perf degradation in games on end users machines, especially as users have more background processes like youtube videos and other apps eating video mem. Windows assigns budgets based on the overall vram usage across all apps and there is no way to give your game’s mem higher priority (other than the SetResidencyPriority call on individual allocations). That along with almost no metric gathering for nonlocal memory % makes this issue fly under the radar for most dev teams and they only see it reflected in poor user sentiment after release. I definitely encourage Epic to take it very seriously.
Oh yeah, not even setting SetResidencyPriority on the render targets and buffers that Unreal does currently set it for would cause the game to barf for sure. That is no surprise there.