Mees Dekker
Introduction
Performance has always been a tricky topic within game development, especially in VR games as it can cause motion sickness. Because I’ve always been interessted in VR and beautiful looking scenes I thought this was the perfect opportunity to find the sweet spot between performance and visuals.
But before that, a little explenation is needed to know the context about my project. Me and a friend are creating our own VR game based on the anime Sword Art Online. We are trying to keep everything as close to the anime as possible so that also means replicating the environments. We wanted to create a level that is similar to this

basically a big grass field with trees, bushes etc. Although with so many objects in a scene…. you can guess it. It nuked our FPS and were only able to get around 5-10 FPS which of course made it impossible to play.
So, my project will be about improving the performance of my VR project. I found that anything less then 72 frames per second is likely to cause disorientation/motion sickness, so that would be the absolute minimum goal. But ideally you want to shoot for 90 frames per second as most sources state it is the optimal for VR experiences. Of course you can always go further but my starting point will be reaching at least 72 frames per second.
Contents
- Introduction
- Research performance techniques
- LOD(Level of detail)
- Unity Terrain
- ASW and Render pipeline
- SRP Batcher VS GPU instancing
- Conclusion
- Sources
Research performance techniques
After looking at multiple sources from Unity and VR professionals for VR optimisation, there were some techniques that kept comming back:
- LOD(Level of detail)
- Use objects with less vertices
- Texture compression
- Mipmapping
- Fog
- Light baking
- Occlusion culling
- Single-pass rendering
- Static batching
- GPU instancing
- Trilinear filtering
I researched all of them but I’m not going to implement all of these techniques into my project and chose the ones that will have the most impact for my perticular case. When I started my research, the scene looked like this

We can see that rendering costs a lot of time and we are having a lot of draw calls every frame. We can use that as a starting point to improve performance!
LOD(Level of detail)
Looking at the profiler above we can see that rendering costs a lot time. In which LOD’s are a great way to improve the performance. LOD stands for Level Of Detail and is an optimisation technique in which you have multiple versions of an object with fewer poly counts. Depending on how far the camera is to the object you show a different poly count version. Where you want the highest poly count version of your object to be shown when you are close to it, as it wouldn’t really make sense to render a high poly object if you are far away from it.
Step one for me would be to update the assets so that they would have LOD versions. Luckily my friend already had an asset pack we could use that not only had LOD versions of all its assets, but the assets were also lower poly in general! So step two is to create a scene with all the new assets.

If we look at the scene now, we can actually walk around pretty smoothly! I played a little with the global value of the LODs (which determines the distance used when a lower poly version is rendered) and thought this was a pretty acceptable setting.
Unity Terrain
When we look at the profiler again we can still see that rendering takes up most of the time every frame, more specificaly the Semaphore.WaitForSignal. On first glance I had no idea what this was but after some researching I found that it’s possibly because my Graphics pipeline is waiting for something else to finish.

Right now all the tree’s, grass, stones, flowers etc in the scene are separate game objects, but another idea to create levels might be to use the Unity Terrain component. Unity uses optimizations such as billboarding for distant Trees to maintain good rendering performance. This means that you can have dense forests with thousands of Trees, and still keep an acceptable frame rate. (Unity 2022). Which will be very helpful in improving the perfomance.
When deleting all the grass objects in the scene and using the terrain component to place the exact same amount. We actually get a very similar fps rate.

So then I got curious. Right now there are about 17k grass objects in the scene but what will happen if I place a 100k of them?

To my surprise the fps was about the same as when I had 17k grass objects in my scene. After seeing this I deleted all the objects beside details such as stones, lily pads etc. And replaced them with the terrain editor. Beside replacing the objects I also added more of them to the scene as the 100k vs 17k objects test from above didn’t really have any perfomance drops. So if we look at the scene with (almost) all the objects being placed with the terrain component we still get a similar fps rate, but now we also have option to add way more of them if we want to.

ASW and Render pipeline
When I wanted to continue working on my project at home I saw that I suddenly got these weird spikes in performance
And upon looking further into it I realised that again it was Semaphore.WaitForSignal causing it. So I looked back at my previous research on what exactly this is. The problem was that people did have the same problem but they all had different answers. Going from the CPU outperforming the GPU, It being a bug in some Unity versions, having too many draw calls or it being the volumentrics in my scene.
After trying all the things listed above, Checking on other pc’s with different GPU’s, changing the Unity version, lowering the object count in my scene(resulting in fewer draw calls), changing the volumetrics/lighting settings in my scene and more. I finally got my answer: I still had no idea what was going on. I didn’t matter what I changed, my fps kept getting stuck at 45 with Semaphore.WaitForSignal being the cause.
But then I finally stumbled across a forum where somebody mentioned ASW meaning Asynchronous Space Warp. And this answer really saved me. ASW is a projection technique that will boost the frame rate and is integrated in the XRI tool I use for VR within Unity. It basically locks the framerate to 45 when it even slightly goes under 90 because then you could still get smooth frames. It’s a similar principle to having v-sync enabled on a monitor. So even if my cpu/gpu could push further then 90 it would still be capped at 90 because it’s the same as the refreshrate of the VR headset I’m using (HTC Vive) and doesn’t need to push further. So the spikes in the profiler wasn’t some render thing but the CPU waiting on purpose so that the fps would not go above 45.
I was still messing around with the volumetrics/lighting settings right before discovering ASW, reading that it can get very costly. One thing I didn’t try yet was changing the entire Render Pipeline Asset instead of tweaking the one we have now. But I didn’t really wan’t to do that as I liked how it looked already. But after changing the Render Pipeline Asset just to see what would happen, the fps wasn’t capped at 45 but at 90! Meaning that the Asset we used wasn’t as performant or VR ready as the one I changed it to.
SRP Batcher VS GPU instancing
Now with a (almost) steady 90 fps on our hands what would be the next step? More terrain and foliage!! In the end I want a big level so now it was time to crank up the object count. When I added more terrain and added some foliage to it my fps also got back to 45 again due to ASW. Meaning that I couldn’t reach fps in the 90s anymore.
One performance technique that is used often with rendering a lot of the same objects is GPU Instancing. Now with seeing promising things about it during my research at the very start of this project and classmates saying I should use it. I decided to dive right into it. After looking at Unity’s website and multiple forums on how to implement it I still couldnt get it to work. My drawcalls(batches) just wouldn’t get saved no matter what I did.

After adjusting my search question to GPU instancing with URP specificaly I came across the SRP Batcher. The SRP Batcher is a rendering loop that speeds up CPU rendering in Scenes with Materials that use the same Shader variant. The SRP Batcher was intended as a replacement for GPU instancing as its more versatile and more perfomant, in general usage. None of my batches were saved because URP prioritizes the SRP Batcher over GPU instancing making it so that it doesn’t matter if you enabled instancing, it will still use the batcher. Luckily to turn it off you simply had to check off a box in the Render Pipeline Asset and hooray! Now my batches are getting saved!

But then the last question: Is GPU instancing better than the SRP Batcher? I found that when you have a large number of the same mesh and material on screen then GPU Instancing makes sense and should perform better. And when you have a large number of different materials but few shader variants then SRP Batching is the best for performance overall. SRP Batcher might be the best overall but for my case specifically I want to render a lot of the same mesh and material. So I chose to stick with GPU instancing.
Conclusion
I got a lot of my questions answered about performance in VR. There were more unknown things that I had to discover then I would’ve thought which is all the more interesting ofcourse!
My goal was to reach at least 72 fps. Now that I know ASW is in play exactly 72 fps wouldn’t be a thing so it’s either 45 or 90. Both 45 and 90 are smooth due to it being capped but it’s better to go for 90 ofcourse. With the status of my current level where I have over 350k objects consisting of tree’s, bushes, flowers, grass, stones etc I do get 90 fps but not consistently.
In general I’m very happy with my results and I know a lot more when it comes to performance which allows me to continue building on this project with the knowledge I build up researching this.
Sources
- Unity (n.d.). Optimizing your VR/AR experiences. Unity. https://learn.unity.com/tutorial/optimizing-your-vr-ar-experiences#5fe2ad02edbc2a08ce47222b
- XR Bootcamp (n.d.). Unity optimization tips. https://xrbootcamp.com/unity-optimization-tips/
- YouTube (2023). Title of the video. YouTube. https://www.youtube.com/watch?v=1JRAPSStZ1w
- The Game Dev Guru (n.d.). Occlusion culling tutorial. https://thegamedev.guru/unity-performance/occlusion-culling-tutorial/
- The MaxScriptGuy (n.d.). The single pass vs multi-pass rendering in Unity for VR. Medium. https://themaxscriptguy.medium.com/the-single-pass-vs-multi-pass-rendering-in-unity-for-vr-4962ac30ceb9
- Unity (n.d.). How to maximize AR and VR performance with advanced stereo rendering. Unity Technologies Blog. https://blog.unity.com/technology/how-to-maximize-ar-and-vr-performance-with-advanced-stereo-rendering
- Unity (n.d.). Terrain Trees. Unity Manual. https://docs.unity3d.com/Manual/terrain-Trees.html
- YouTube (Year). Title of the video. YouTube. https://www.youtube.com/watch?v=IrYPkSIvpIw
- XR Bootcamp (n.d.). Increasing FPS in Unity VR games: Course review. https://xrbootcamp.com/increasing-fps-in-unity-vr-games-course-review/#:~:text=72%20frames%20per%20second%20is,for%20comfortable%20immersive%20digital%20experiences.
- Unity (n.d.). Semaphore.WaitForSignal causing performance issues. Unity Forum. https://forum.unity.com/threads/semaphore-waitforsignal-causing-performance-issues.824580/
- Unity (n.d.). XR SDK HDRP Semaphore.Dot.WaitForSignal usage spikes when using XR. Unity Issue Tracker. https://issuetracker.unity3d.com/issues/xr-xr-sdk-hdrp-semaphore-dot-waitforsignal-usage-spikes-when-using-xr
- Unity (n.d.). SRP VR performance is significantly worst using single-pass instanced compared to multi-pass rendering. Unity Issue Tracker. https://issuetracker.unity3d.com/issues/srp-vr-performance-is-significantly-worst-using-single-pass-instanced-compared-to-multi-pass-rendering
- Unity (n.d.). Profiler: The following GFX.WaitForRenderThread accounts for more than 50% of CPU processing. Unity Forum. https://forum.unity.com/threads/profiler-the-following-gfx-waitforrenderthread-accounts-for-more-than-50-of-cpu-processing.965483/
- AtMeta Community Forums (n.d.). VR WaitForGPU killing performance in Unity3D: Very difficult to. https://communityforums.atmeta.com/t5/PCVR-Development/VR-WaitForGPU-killing-performance-in-Unity3D-Very-difficult-to/td-p/631196
- Unity (n.d.). Confused about performance of SRP batching vs GPU instancing. Unity Forum. https://forum.unity.com/threads/confused-about-performance-of-srp-batching-vs-gpu-instancing.949185/
- The Ghost Howls (2021, December 12). Application Spacewarp Unity: How to review. https://skarredghost.com/2021/12/12/application-spacewarp-unity-how-to-review/
