Memory Spike After Loading (Nanite + Primitive + DistanceFields)

We’re seeing a pretty hefty spike of memory usage right after a level finishes loading due to upload buffers. According to memory insights using the “Decline” view over the first second of gameplay I’m seeing this:

67108864  D3D12AllocatorUnused  Nanite.PageUploadBuffer
67108864  Nanite  Nanite.PageUploadBuffer
33554432  DistanceFields  DistanceFields.BrickUploadDataBuffer
33554432  D3D12AllocatorUnused  PrimitiveUploadBuffer
33554432  GPUScene  PrimitiveUploadBuffer
33554432  D3D12AllocatorUnused  DistanceFields.BrickUploadDataBuffer
8388608  DistanceFields  DistanceFields.DFObjectDataUploadBuffer
8388608  Nanite  Nanite.StreamingManager.HierarchyUpload

Which equates to about 270mb of buffers that get allocated and then are freed within a second. I’m not sure if the “unused” versions are double counting this memory if it’s actually allocating twice

Is there a way to control the size of these buffers?

Is there also a way to do all of this uploading during the loading phase rather than right after gameplay starts? Having some way to start the uploads and check if they’re complete would be enough.

Steps to Reproduce

Hi,

Thanks for reaching out.

>> Is there a way to control the size of these buffers?

There are a few console variables which can influence the size of the upload buffers or how they are uploaded:

r.DistanceFields.TextureUploadLimitKBytes (Default 8192) Max KB of distance field texture data to upload per frame from streaming requests.

r.GPUScene.MaxPooledUploadBufferSize (Default 256000) Maximum size of GPU Scene upload buffer size to pool.

r.Nanite.Streaming.DynamicPageUploadBuffer (Default 0) Set Dynamic flag on the page upload buffer. This can eliminate a buffer copy on some platforms, but potentially also make the transcode shader slower. Enable this setting may reduce the memory spike.

There are also a few more general upload CVars available which control the maximum allocation size ([this [Content removed] contains a bit more information on their usage):

d3d12.UploadHeap.BigBlock.MaxAllocationSize 67108864 Maximum allocation size on the big block allocator for upload memory

d3d12.UploadHeap.BigBlock.PoolSize 8388608 Pool size for the upload memory big block allocator

d3d12.PoolAllocator.ReadOnlyTextureMaxAllocationSize 67108864 Maximum size of a single allocation in the VRAM ReadOnly Texture pool allocator (default 64MB)

The following cases also contain some relevant bits:

[Content removed] [Content removed]

[Content removed]

>> Is there also a way to do all of this uploading during the loading phase rather than right after gameplay starts? Having some way to start the uploads and check if they’re complete would be enough.

After discussing this with my colleagues, it doesn’t look like there is a specific CVar or other built-in flag that would signal completion of these buffer uploads, however you may try to call FlushAsyncLoading(), which should block until tall pending package are completed.

Hopefully that helps. Please let me know if you have any further questions.

Thanks,

Sam