x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Sub level streaming FPS drops

Hi,

Currently in our project we are using sub level and other asset streaming to reduce the memory usage and initial loading times but whenever streaming is occurring, the FPS drops considerable and causes freezes of up to 2 seconds.

Is streaming not happening on a separate thread? Is there a way to make it more smooth?

Thanks,

Franco

Product Version: Not Selected
Tags:
more ▼

asked Sep 12 '18 at 01:51 PM in Using UE4

avatar image

Answers.Archive STAFF
1.9k 193 302 675

avatar image Answers.Archive STAFF Sep 12 '18 at 01:51 PM

Hi Jon,

I'd want to reopen this thread because we have been testing out a lot of different parameters and no matter what we do we still have large streaming hitches.

There is one section in particular where we would like to be able to play a full screen movie (in PS4) while a couple of sublevels load in the background. The parameters I modify temporarily so I can control the streaming during the movie are as follows:

 GAsyncLoadingTimeLimit = 0.5f
 GAsyncLoadingUseFullTimeLimit = 0
 GPriorityAsyncLoadingExtraTime = 1.0f
 GLevelStreamingActorsUpdateTimeLimit = 0.5f
 GLevelStreamingUnregisterComponentsTimeLimit = 0.05f
 GLevelStreamingComponentsRegistrationGranularity = 1
 GLevelStreamingComponentsUnregistrationGranularity = 1

I am attaching part of the logs from when I activated DUMPHITCHES right before starting the movie: [1]: /storage/attachments/253474-moviehitches.txt

Is there something I'm not understanding about streaming? Is there anything else I could try out? Someone suggested I could try bPlayersOnly in the UWorld but that will pause the streaming which defeats the purpose of what we are trying to do.

Thanks,

Franco

moviehitches.txt (74.8 kB)
(comments are locked)
10|2000 characters needed characters left

2 answers: sort voted first

Franco,

There are two parts to level streaming, in terms of loading. The first is reading the assets from the disk, and the second is creating the necessary UObjects for the level.

UE4 requires that UObjects and derived classes be created on the Game Thread (there are a number of reasons for this). The main parts of Async Loading that happens in a separate thread is actually reading data from disk.

As long as you have the Async Loading Thread enabled, I/O work will be handled in a separate background thread. However, there's still some amount of work that needs to happen on the GameThread (like creating UObjects). This is in AsyncLoading.cpp, and can be changed as you normally would CVars (including through INI):

 static int32 GAsyncLoadingThreadEnabled;
 static FAutoConsoleVariableRef CVarAsyncLoadingThreadEnabledg(
     TEXT("s.AsyncLoadingThreadEnabled"),
     GAsyncLoadingThreadEnabled,
     TEXT("Placeholder console variable, currently not used in runtime."),
     ECVF_Default
     );

There's also a few settings in CoreSettings.cpp:

 float GAsyncLoadingTimeLimit = 5.0f;
 int32 GAsyncLoadingUseFullTimeLimit = 1;
 float GPriorityAsyncLoadingExtraTime = 20.0f;
 
 static FAutoConsoleVariableRef CVarAsyncLoadingTimeLimit(
     TEXT("s.AsyncLoadingTimeLimit"),
     GAsyncLoadingTimeLimit,
     TEXT("Maximum amount of time to spend doing asynchronous loading (ms per frame)."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarAsyncLoadingUseFullTimeLimit(
     TEXT("s.AsyncLoadingUseFullTimeLimit"),
     GAsyncLoadingUseFullTimeLimit,
     TEXT("Whether to use the entire time limit even if blocked on I/O."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarPriorityAsyncLoadingExtraTime(
     TEXT("s.PriorityAsyncLoadingExtraTime"),
     GPriorityAsyncLoadingExtraTime,
     TEXT("Additional time to spend asynchronous loading during a high priority load."),
     ECVF_Default
     );

Second is actually creating the level + objects in game. This does not happen on a separate thread. So, the way level streaming deals with this is by splitting up the Creation and Registration of objects across multiple frames. There are a number of options for this. These options are also CVars that can be set, and there is also a section under property settings which makes editing them easier. These are defined in CoreSettings.cpp:

 int32 GUseBackgroundLevelStreaming = 1;
 float GAsyncLoadingTimeLimit = 5.0f;
 int32 GAsyncLoadingUseFullTimeLimit = 1;
 float GPriorityAsyncLoadingExtraTime = 20.0f;
 float GLevelStreamingActorsUpdateTimeLimit = 5.0f;
 float GLevelStreamingUnregisterComponentsTimeLimit = 1.0f;
 int32 GLevelStreamingComponentsRegistrationGranularity = 10;
 int32 GLevelStreamingComponentsUnregistrationGranularity = 5;
 
 static FAutoConsoleVariableRef CVarUseBackgroundLevelStreaming(
     TEXT("s.UseBackgroundLevelStreaming"),
     GUseBackgroundLevelStreaming,
     TEXT("Whether to allow background level streaming."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarAsyncLoadingTimeLimit(
     TEXT("s.AsyncLoadingTimeLimit"),
     GAsyncLoadingTimeLimit,
     TEXT("Maximum amount of time to spend doing asynchronous loading (ms per frame)."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarAsyncLoadingUseFullTimeLimit(
     TEXT("s.AsyncLoadingUseFullTimeLimit"),
     GAsyncLoadingUseFullTimeLimit,
     TEXT("Whether to use the entire time limit even if blocked on I/O."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarPriorityAsyncLoadingExtraTime(
     TEXT("s.PriorityAsyncLoadingExtraTime"),
     GPriorityAsyncLoadingExtraTime,
     TEXT("Additional time to spend asynchronous loading during a high priority load."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarLevelStreamingActorsUpdateTimeLimit(
     TEXT("s.LevelStreamingActorsUpdateTimeLimit"),
     GLevelStreamingActorsUpdateTimeLimit,
     TEXT("Maximum allowed time to spend for actor registration steps during level streaming (ms per frame)."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarLevelStreamingUnregisterComponentsTimeLimit(
     TEXT("s.UnregisterComponentsTimeLimit"),
     GLevelStreamingUnregisterComponentsTimeLimit,
     TEXT("Maximum allowed time to spend for actor unregistration steps during level streaming (ms per frame). If this is zero then we don't timeslice"),
     ECVF_Default
 );
 
 static FAutoConsoleVariableRef CVarLevelStreamingComponentsRegistrationGranularity(
     TEXT("s.LevelStreamingComponentsRegistrationGranularity"),
     GLevelStreamingComponentsRegistrationGranularity,
     TEXT("Batching granularity used to register actor components during level streaming."),
     ECVF_Default
     );
 
 static FAutoConsoleVariableRef CVarLevelStreamingComponentsUnregistrationGranularity(
     TEXT("s.LevelStreamingComponentsUnregistrationGranularity"),
     GLevelStreamingComponentsUnregistrationGranularity,
     TEXT("Batching granularity used to unregister actor components during level unstreaming."),
     ECVF_Default
     );

One thing to point out is that any of the "time" values mentioned above are in milliseconds (I've seen a few licensees assume it was seconds).

Basically, there are a number of different settings associated with level streaming. There's definitely no "one size fits all" adjustment of these settings, so it's up to individual projects to figure out what values work well for them.

One thing you can do to start is enable PERF_TRACK_DETAILED_ASYNC_STATS (which is defined in AsyncLoading.h). This will print out detailed information on how much time each frame was spent on handling Streaming Steps when a world is loaded. From there, you can figure out what you need to adjust in order to get a better framerate. You want to make sure to disable this when not testing, as there are other pieces of code that also use this can it could bloat your logs a bit.

Of course, you'll have to make tradeoffs and find a good balance between total loading time and framerate.

Thanks, Jon

more ▼

answered Sep 12 '18 at 01:51 PM

avatar image

Answers.Archive STAFF
1.9k 193 302 675

avatar image Answers.Archive STAFF Sep 12 '18 at 01:51 PM +

Hi Jon, thanks for the answer. I see that for GAsyncLoadingThreadEnabled the comment is "Placeholder console variable, currently not used in runtime." and if I search the entire code base for this variable, it appears nowhere else. Does this mean that this is not implemented yet and all aspects of streaming happens in the game thread?

avatar image Answers.Archive STAFF Sep 12 '18 at 01:52 PM

Franco,

The variable is used, but admittedly it may not be the most obvious and the comments are outdated.

If you search for "s.AsyncLoadingThreadEnabled" (which is the CVars name), you'll see that it is referenced in 2 places outside it's definition.

First, UStreamingSettings::AsyncLoadingThreadEnabled is set up as a Console Variable property, so it should reflect that value. However, this isn't directly used in the engine, but rather it's used so the value can be set via the Settings Editor from within the Editor (I believe it's under Project Settings).

Second, inside AsyncLoadingThread::IsMultiThreaded when THREADSAFE_UOBJECTS is enabled (In ObjectMacros.h, you'll see this is enabled by default). You'll see that when IsMultiThreaded returns true, the constructor for FAsyncLoadingThread will create a new thread:

 if (FAsyncLoadingThread::IsMultithreaded())
 {
     Thread = FRunnableThread::Create(this, TEXT("FAsyncLoadingThread"), 0, TPri_Normal);
 }
 else
 {
     Thread = nullptr;
     Init();
 }


Thanks, Jon

avatar image Answers.Archive STAFF Sep 12 '18 at 01:52 PM

I'm still having trouble activating the loading thread. In DefaultEngine.ini I have:

 [/Script/Engine.StreamingSettings]
 s.EventDrivenLoaderEnabled=False
 s.AsyncLoadingThreadEnabled=True

And if I put a breakpoint in UStreamingSettings::PostInitProperties() then UStreamingSettings::AsyncLoadingThreadEnabled is true. But when I get to FAsyncLoadingThread::FAsyncLoadingThread(), FAsyncLoadingThread::IsMultithreaded() returns false and Thread becomes nullptr.

avatar image Answers.Archive STAFF Sep 12 '18 at 01:52 PM

Franco,

Something I should have asked earlier, how are you testing this? Are you built in Development or Shipping? Running PIE, Standalone, or a packaged build?

If you're using a Packaged build, are you running on a Console or a Desktop?

Here is the full implementation of the IsMultithreaded check:

 /** True if multithreaded async loading should be used. */
 static FORCEINLINE bool IsMultithreaded()
 {
     static struct FAsyncLoadingThreadEnabled
     {
         bool Value;
         FORCENOINLINE FAsyncLoadingThreadEnabled()
         {
 #if THREADSAFE_UOBJECTS
             if (FPlatformProperties::RequiresCookedData())
             {
                 check(GConfig);
                 bool bConfigValue = true;
                 GConfig->GetBool(TEXT("/Script/Engine.StreamingSettings"), TEXT("s.AsyncLoadingThreadEnabled"), bConfigValue, GEngineIni);
                 bool bCommandLineNoAsyncThread = FParse::Param(FCommandLine::Get(), TEXT("NoAsyncLoadingThread"));
                 bool bCommandLineAsyncThread = FParse::Param(FCommandLine::Get(), TEXT("AsyncLoadingThread"));
                 Value = bCommandLineAsyncThread || (bConfigValue && FApp::ShouldUseThreadingForPerformance() && !bCommandLineNoAsyncThread);
             }
             else
 #endif
             {
                 Value = false;
             }
         }
     } AsyncLoadingThreadEnabled;
     return AsyncLoadingThreadEnabled.Value;
 }


As you can see, it's not solely dependent on the CVar. It also checks whether or not the platform requires cooked data (which is generally true, unless you're running a non-cooked build on a desktop), whether or not you've explicitly disabled the thread (via command line), and also FApp::ShouldUseThreadingForPerformance (which could return false for a number of reasons, so I'd suggest taking a look at its implementation).

I'd also suggest you take a look at the Performance and Profiling docs if you haven't already. These can give you more insight into exactly what's causing the hitch:

https://docs.unrealengine.com/latest/INT/Engine/Performance/

Something I'll point out is that if you're running an uncooked build (e.g., standalone etc.) then it's possible loading is also causing you to compile blueprints on the fly.

It's also worth noting that any hard references to assets (either directly in the level, or in classes / objects the level has references to (and so on down the chain)) that those will need to be resolved before the level will be considered loaded. To that end, it's possible if you haven't been careful that loading certain levels could inadvertently be loading more content then you'd expect.

Thanks, Jon N.

avatar image Answers.Archive STAFF Sep 12 '18 at 01:52 PM

Yeah I was using PIE to test this. Thanks Jon, this clears up everything.

Franco

(comments are locked)
10|2000 characters needed characters left

Hi Jon,

I'd want to reopen this thread because we have been testing out a lot of different parameters and no matter what we do we still have large streaming hitches.

There is one section in particular where we would like to be able to play a full screen movie (in PS4) while a couple of sublevels load in the background. The parameters I modify temporarily so I can control the streaming during the movie are as follows:

 GAsyncLoadingTimeLimit = 0.5f
 GAsyncLoadingUseFullTimeLimit = 0
 GPriorityAsyncLoadingExtraTime = 1.0f
 GLevelStreamingActorsUpdateTimeLimit = 0.5f
 GLevelStreamingUnregisterComponentsTimeLimit = 0.05f
 GLevelStreamingComponentsRegistrationGranularity = 1
 GLevelStreamingComponentsUnregistrationGranularity = 1

I am attaching part of the logs from when I activated DUMPHITCHES right before starting the movie: [1]: /storage/attachments/253475-moviehitches.txt

Is there something I'm not understanding about streaming? Is there anything else I could try out? Someone suggested I could try bPlayersOnly in the UWorld but that will pause the streaming which defeats the purpose of what we are trying to do.

Thanks,

Franco

moviehitches.txt (74.8 kB)
more ▼

answered Sep 12 '18 at 01:51 PM

avatar image

Answers.Archive STAFF
1.9k 193 302 675

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question