7 Tips to Optimizing for Virtual Reality with Unreal Engine 4


Developing for Virtual Reality (VR) is a new and exciting challenge. Developers are faced with the challenges of the human biology, which was not too apparent in traditional games. A few dropped frames here and there might be forgiven, but not for VR. A few frame drops can make the difference between an enjoyable VR experience or a sickness-inducing one.

My goal for this post is to go over 7 tips that you can do to help your VR experience run as smooth as possible.


Nativizing Blueprints

Nativizing Blueprints is a new feature added to Unreal Engine (UE) 4.15.x. This feature allows you to convert Blueprints (BP) into C++ before the engine packs everything into an executable. If you are not much of a coder then this is great news. Without having to do any coding, you can speed up your BPs by enabling this new feature. To find out how to enable and apply Nativizing Blueprints to your VR project, you can visit the UE documentations.

When you are nativizing blueprints, you have the options to make it inclusive or exclusive. Using inclusive nativizing would convert every compatible BP into C++. Using exclusive nativizing would require you to pick manually which BP objects to nativized. I would recommend you use exclusive nativizing because the nativizing process adds a lot to your executable file size. In addition, nativizing all your BPs can potentially lead to bugs that are a nightmare to trace down.

Nativizing Blueprints Example

In my post about C++ and BP, I have an example of a C++ actor and a BP actor doing the same computations. The example shows that C++ outperforms BP with the computations by a large margin. I will be building off the same example for demonstrating nativizing BP.

First, you want to enable Nativizing BP by going to Edit > Project Settings > Packaging > Blueprints. Then choose the inclusive or exclusive mode. For this example, I am using exclusive.

Nativizing BP enable settings

Go to the BP object you want to nativize (Block_BP for this example) and open up “Class Settings” and tick the Nativize option.

Enable Block_BP to be nativized before packaging

This is all you need to do to nativize a BP object. All there is left to do is package up the project and run the executable afterward to see the difference.

Non-nativized Block_BP running 10M computations

Nativized Block_BP running 10M computations

Notice how much faster the nativized BP object is compared to its non-nativized self.

Do Certain Things in C++

One area that C++ really shines is in computation. The overhead with C++ is much less than BP and it definitely shows when you are doing thousands of computations every frame.

With nativizing BP feature in UE4.15, writing code might be less relevant. For some BPs the nativizing feature can produce results that match actual C++ code performance. However, for BP that does not support nativizing, you will still need to write it in C++ manually. In addition, nativized C++ code is not friendly for humans to read.

// snippet of UE4 generated C++ from BP source
PRAGMA_DISABLE_DEPRECATION_WARNINGS
ABlock_BP_C__pf3363230609::ABlock_BP_C__pf3363230609(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	if(HasAnyFlags(RF_ClassDefaultObject) && (ABlock_BP_C__pf3363230609::StaticClass() == GetClass()))
	{
		ABlock_BP_C__pf3363230609::__CustomDynamicClassInitialization(CastChecked<UDynamicClass>(GetClass()));
	}
	
	bpv__Cube__pf = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Cube"));
	RootComponent = bpv__Cube__pf;
	bpv__Cube__pf->CreationMethod = EComponentCreationMethod::Native;
	bpv__Cube__pf->StaticMesh = CastChecked<UStaticMesh>(CastChecked<UDynamicClass>(ABlock_BP_C__pf3363230609::StaticClass())->UsedAssets[0], ECastCheckedType::NullAllowed);
	bpv__Cube__pf->OverrideMaterials = TArray<UMaterialInterface*> ();
	bpv__Cube__pf->OverrideMaterials.Reserve(1);
	bpv__Cube__pf->OverrideMaterials.Add(CastChecked<UMaterialInterface>(CastChecked<UDynamicClass>(ABlock_BP_C__pf3363230609::StaticClass())->UsedAssets[1], ECastCheckedType::NullAllowed));
	if(!bpv__Cube__pf->IsTemplate())
	{
		bpv__Cube__pf->BodyInstance.FixupData(bpv__Cube__pf);
	}
	bpv__period__pf = 2.000000f;
	bpv__time_elapsed__pf = 0.000000f;
	bpv__tick_count__pf = 0;
	PrimaryActorTick.bCanEverTick = true;
}

Instanced Stereo

Enabling instanced stereo feature in UE4 may help your performance in VR. Instanced stereo does not show much of an improvement for a simple scene with a low number of draw calls. However, when your scene is doing over thousands of draw calls, instanced stereo will help speed things up.

Instanced stereo will render the image for both eyes in parallel, which is faster than rendering one-by-one. The time gain then goes to processing more draw calls.

To enable instanced stereo, go to Edit > Project Settings > Rendering > VR > Instanced Stereo.

Disable Heavy Post-Processors

There are certain expensive post-processes that UE4 have enabled by default that is not ideal for VR. An example of such post-process is Ambient Occlusion. Other post-processes are an annoyance in VR and even disrupt and break immersion in the VR experience. An example is Lens Flares.

To disable post-processing go to Edit > Project Settings > Rendering. Note that older versions of UE4 have it as Default Postprocessing Settings while newer ones have it under Default Settings.

Avoid Dynamic Shadows and Lightings

Shadows and lightings can easily boggle down your performance. They are expensive to render and hogs up all the CPU cycles, leaving little to process VR related things such as movements and rotations.

Instead of using dynamic shadows, you can create a material that looks like shadows. Rendering a material is fast and inexpensive. Best of all, it looks just as good when you have the materials all setup.

Instead of using dynamic lighting, opt for static lighting. Whenever possible, you should bake as much lighting as you can. This will help keep the per-frame cost as low as possible.

Use Multiple Models

You can switch between a 2D and 3D model of objects in the VR scene. For example, when objects are far away from the user, use the 2D model. It is less expensive to render and the difference between 2D and 3D is not noticeable from afar. Once the user gets within a certain range, switch the object over to the more detailed 3D model.

Always Profile Performance

Your VR experience may be CPU and or GPU bound. It is difficult to tell with the complexity of game engines nowadays. Something to remember is that “onTick” function is GPU bound and timers are CPU bound.

CPU Profiling

A simple way to check is to run the console command, “stat unit”. This command shows basic information such as the time it takes between frames, game thread, drawing/rendering thread, and GPU processing time. To use this command you can bring up the console with the ` key or execute it as a console command at the start of your level Blueprint.

Invoking the stat unit command with Blueprint

Output of the stat unit command

For more advanced CPU profiling techniques, you can visit Bob Tellez’s post about CPU profiling in UE4.

GPU Profiling

In order to capture a single frame with GPU timings, you can use the command “profilegpu”. This command dumps accurate timings of the GPU. You can use the command in the editor console or in Blueprint.

Using profilegpu in Blueprint

Output from profilegpu command

A good place to learn about profiling your project is with the GPU profiling and Performance and Profiling documentations. Here is a great list of tips from developers part of FATED on optimizing VR experience.


I hope you found this helpful. If so, share it with others so they can benefit as well.

Is there something else you believe would be a great addition to the post? Do you have some tips to add? If so, feel free to leave a comment, send me a tweet or send me an email at steven@brightdevelopers.com. I will be glad to hear from you.

To stay in touch, you can follow me on twitter.


About Steven To

Steven To is a software developer that specializes in mobile development with a background in computer engineering. Beyond his passion for software development, he also has an interest in Virtual Reality, Augmented Reality, Artificial Intelligence, Personal Development, and Personal Finance. If he is not writing software, then he is out learning something new.