So I’ve been working on a mobile-based platformer for a few years now. I’ve spent more than a year optimizing the game for mobile as much as I could. Almost every single one of my meshes are using Mobile/Diffuse shader. When I test the performance of my game (on my phone) with the profiler, the frame rate stays at 60 fps for the most part. The CPU time fluctuates between 16 ms and 17 ms. The rendering statistics are also excellent, with draw calls fluctuating between 18 and 25, triangles fluctuating between 1.0k and 2.0k and vertices fluctuating between 2.0k and 3.1k. Below is a screenshot of the Rendering profiler when the game is running smoothly. I believe that for a mobile device, these should be very good numbers.
Under normal scenarios (when the performance is smooth), Render.OpaqueGeometry takes between 1 ms and 2 ms of CPU time. However, as you can see in the screenshot above, Render.OpaqueGeometry is taking 5.36 ms of CPU time and the total CPU time has jumped to 24.16 ms.
Based on my observations, this spike shows up once or twice per minute. Sometimes, several minutes go by without any spikes. And in very rare occasions, the spike can show up 3 times in a single minute (although I don’t think that happened the last couple of times I tested it).
So why do you guys think this is happening despite the game being heavily optimized? I read in a post in Unity Answers that Render.OpaqueGeometry spikes are frequent in all games and it’s nothing to worry about. But do you think 2 spikes per minute is too frequent for a simple platformer?
Hello,
I can’t really tell much from the screenshots, as they are lacking some more context. What I can see is, that in the frames leading up to the spike, the UI category starts getting more expensive. That may not lead to this but just be another symptom, but that might merit further investigation. There is a “Canvas.RenderSubBatch” sample in your screenshot though so it could be UI related?
Besides that:
Does JobAlloc.Grow happen for all frames, some frames, all spike frames or just randomly without relation to the spikes?
How do other lines in the profiler coincide with these spikes or lead up to them? E.g. how doe the memory charts relate to it? Maybe something else is gradually filling up memory and then in this frame, a re-alloc has to be made?
How’s this look in the UI Profiler?
What else is happening simultaneously to this function on other threads? Could you check in timeline view and also check previous frames?
How do the surrounding frames, in particularly their Render Thread, relate to this frame? could be that the issue is in the frame before this one and just causes a run-in issue.
Also, please keep in mind that on mobile, the OS can just randomly decide to push youre app’s threads off-core for a bit, other system resources can be contested and that it could get throttled by the OS due to thermal/battery consumption.
I’m pretty sure it’s not UI related because a couple days back I tested the game after disabling all UI components and these spikes were still present.
One interesting thing I did notice though. I tested game visually for 10 minutes to look for stutters. Surprisingly, I haven’t noticed a single stutter in my game (except during the first 10 seconds or so when all the scripts are being initialized). Of course I’ll have to visually test it a couple more times to confirm that there really were no stutters. Do you have any idea why my game possibly didn’t stutter despite the profiler showing that there are a few spikes here and there? Because from my experience, my phone has a pretty crap GPU so it can’t handle a frame rate below 60 fps. (Note that this time I wasn’t running the game in Development Build)
I haven’t found a solution yet. I’ve been trying to fix this problem for over a year now and it’ getting quite frustrating. Here’s a little update to my problem.
I’ve optimized a lot of things in my game, including draw calls, vertices, triangles, quality settings, garbage collection, UI, textures, object pooling…you name it. When I profile the game, the game runs at 60 fps, as expected. But every 30 seconds or so, I see a spike that lasts for about 2-3 frames. These spikes are caused by Camera.Render (inside PostLateUpdate.FinishFrameRendering. I know that there are a lot of stuff inside Camera.Render like Render.OpaqueGeometry, Render.TransparentGeometry, Final Blit Pass, etc. But it’s not just Render.OpaqueGeometry that’s causing Camera.Render to spike (as I initially thought). It’s all of them. It’s funny because I don’t even have any transparent objects in my scene. Yet everything inside Camera.Render causes the performance to drops for about 2-3 frames. By following multiple UI optimization tutorials, I’ve even split my Canvases into multiple parts (depending on which UI should be static, which should be dynamic, etc). That didn’t solve it either. I even removed the Graphics Raycaster component from all canvases that don’t require input. These spikes are visible even if I disable all the canvases in my scene (so I know that the UI isn’t causing it).
Another thing causes spikes occasionally is WaitForTargetFPS (VSync), which obviously occurs because I’m using Application.TargetFrameRate to cap my frame rate to 60. I already turned off VSync btw.
I’m super confused. I know that as far as graphics go, I’ve optimized everything right. This leads me to hypothesize that the problem lies in the settings. There has to be some checkmark somewhere that I need to disable (or enable). Maybe it’s in the camera? That can’t be it because I’ve optimized that as well (I turned off HDR and MSAA and all that fancy stuff).
I thought that maybe if I switch to LWRP instead of the default render pipeline, the spikes will no longer occur. Nope. That didn’t fix it either. I even tried turning off 32-bit Display Buffer in Player Settings. That didn’t do it either.
Every optimization tips I’ve read have been useless. I honestly have no idea how other mobile developers who use Unity manage to achieve smooth rendering in their games without any frame rate drops (e.g. Temple Run).
Have you checked the CPU Usage Profiler’s Timeline view? Hierarchy view only shows the main thread and doesn’t highlight cross thread scheduling issues. You might have to check the previous frame if the render thread from that frame laps over into the current one and causes the current one to wait.
Otherwise if you have a completely static “benchmark” like test scenario where this happens reliabily in, it might be worth a bug report.
After testing the games multiple times these past few weeks, I no longer think this is an issue with Camera.Render. Rather, I think this is an issue with Vsync.
If you look at the screenshots again, you’ll see that it’s not just the rendering (green region) that spikes in the profiler. The Vsync (yellow region) actually drops in the exact same frame. This is something I noticed in every Camera.Render spike. Whenever Vsync drops, Camera.Render spikes. So I thought “Maybe the drop in Vsync is causing something else to spike, which in turn is causing Camera.Render to spike”.
And guess what. It turns out that whenever Vsync drops, Graphics.PresentAndSync increases. It usually uses 0% of the CPU time. But when VSync drops, Graphics.PresentAndSync takes up about 3%-4%. I think this is what is causing Camera.Render to spike.
I checked the contents of Graphics.PresentAndSync in the hierarchy view and the only thing inside was Device.Present.
Also please keep in mind that I turned off Vsync in the quality settings. The reason I still have Vsync in my game is that I’m capping the frame rate to 60.
Do you have any idea what might be causing this Graphics.PresentAndSync increase?
Based on what I’ve read on other threads, these are the things that could be causing Graphics.PresentAndSync to increase:
Flare Layer, Anti-aliasing enabled in Camera. (However, my camera doesn’t have Flare Layer and Anti-aliasing components, so this clearly isn’t causing the problem)
Images and texts having Outline, or Shadow or Position as UV1 components attached. (However, the UI in my game doesn’t have these components attached, so this isn’t causing the problem)
In Lighting settings, the shadow resolution is very high. (However, in my game I disabled shadows and my lighting settings doesn’t even have an option for changing shadow resolution. So shadows aren’t the problem)
Vsync being enabled (However in my game, I already set Vsync to Don’t Sync. So this isn’t causing the problem)
Having transparent objects in my scene. (However in my game, all transparent objects stay disabled. So this isn’t the problem.)
Using custom shaders. (I’m not using custom shaders. I’m only using LWRP/SimpleLit shader and Unlit/Texture shader. So custom shaders aren’t the problem.)
Having Auto Graphics API enabled in Player Settings (Now this is interesting. I actually do have Auto Graphics API enabled in Player Settings. However, according to the Unity docs, enabling this is supposed to lead to the best case scenario where the player build uses the most appropriate Graphics API.)
So as you can see, there isn’t any reason why Device.Present should increase in the profiler since I avoided all the stuff that’s supposed to cause it (except Auto Graphics API).
PresentAndSync just means the GPU is working on presenting and/or syncing. Those reasons you listed are just different things that might make your game GPU bound or cause it to VSync.
Now it looks like you have a frame where rendering somehow misses a VBlank and therefore spikes. The following frame then doesn’t wait for VSync because it’s rendering is done shortly before the next VBlank. That would also explain why you don’t see it on device because it basically catches itself after “stumbling” over this spike. All of this would be clearer to see in timeline view.