On my mobile device my fps drop to below 60 but the profiler (connected to the device, debug build, deep prof.) shows quite some time is spent in Vsync. From what I know this should not be an issue since that just means it’s waiting for the frame sync and is already done. Yet I can clearly see the low fps on device (debug and release builds) and also my fps counter shows sub 60 fps.
Also the Rendering shows some very odd results. Triangles are reported as ~28.7k where it should be 700. For comparison: if “stats” is turned on in the editor game view it usually is around 700 tris. The whole scene may have 29k, though it’s never shown all at once.
Here is a screenshot of the profiler with vsync display turned off and on. I am stumped as to how to find out why the fps are getting low. Especially since, if shown over time, the only real change is the VSync increasing.
Could you please share some screenshots of the Timeline view with the main thread and render threads expanded? This kind of issue is usually down to the timings and interplay between these two and the GPU. So if you can get GPU Profiling working with it (adding the GPU Profiler Module and changing the settings according to what the Profiler says is keeping it from getting GPU data), that should give more insights into what’s going on.
Also, are you using the build in RP or URP? Which Unity version? (It clearly is 2019.3 or higher but that’s still a broad range).
Also please note the documentation on Application.targetFrameRate, as it calls out that mobile devices will always wait for a vBlank. A vBlank will occur at the refresh rate of you phone’s screen which looks to be ~50hz. So hitting 60 FPS on that phone would be impossible.
Oh, thanks for the quick reply.
Sorry I did not add the Unity version, it is Unity 2019.4.1f1.
I am using URP 7.3.1., Target “Android”, Vulkan, GLES 3.0+, Il2CPP, .Net Standard 2.0m test device: Nokia 7 Plus, Android 10.
Thanks for the reminder concerning the frame rate. My target frame rate is set to 60 since I assumed 60 Hz or less on the device. From my understanding I expect to either see full fps (60/50), or half fps (30/25) or below on the device screen and nothing in between. I’ll change that to match the actual supported Hz but I don’t think it’s an issue here, right? The “fps” I mentioned in my first post are just calculated as averages for me to make a more informed decision.
Sadly the GPU Usage profiler module only shows a yellow exclamation mark (Vulkan not supported). I gathered from the docs that it may not be supported (Unity - Manual: GPU Usage Profiler module).
Quote “…changing the settings according to what the Profiler says is keeping it from getting GPU data …”
I am not sure how to do that, where does it show that info?
I have attached the threads, hope it’s useful. Also I have noticed some odd “spike frame” with very high VSync (threads also attached). Maybe it’s just a fluke, never seen it before.
yeah, to get GPU profiling data you’d have to go to the player settings and under “Other” change the graphics API to use OpenGL instead of vulkan (and double check that graphics jobs are turned off in the same pane, and maybe enable it for the normal usecase. Also the spike might not occur outside or vulkan and performance might be too different to derive too much info this way). But unless you want to figure out what that spike is I don’t thinks that’s going to help much. I’d just log out the refresh rate to confirm it is 50 and if it is, you’re already running as fast as you can.
If you use targetFrameRate instead of QualitySettings.vSyncCount, ensuring that you set a target appropriate to the refresh rate is advisable though.
As to that spike: the VSync spike looks to be something choking up the GPU for a bit. Maybe it needed to compute some new shaders, or was suspended to background/ tabbed out?
Also that very first spike is down to garbage collection. Your ~7KB of GC.Alloc per frame could be something to investigate (using the Profiler’s Call Stacks feature) and improve upon and since you are VSync bound anyways, you might want to switch on incremental GC to smoothen those GC spikes out by performing the collection during vSync time.
Sorry for my late reply. Thank you for all the good advice.
Chaging to OpenGL only did not really work for me. The message in the GPU profiler changed from “Vulcan is not supported” to “This device is not supported”. I will try tomorrow with another device. ADB gave up on me today, had to use debbugging over wifi. “Multithreaded Rendering” is still turned on, will try to turn it off too. I’ve also started plugging the memory leaks and set the framerate to the reported refresh rate at start.
Quote: “I’d just log out the refresh rate to confirm it is 50”
Are your referring to “Screen.currentResolution.refreshRate”? I alwas thought this is just a wrapper to get some infos from the hardware / OS. Thought it will stay the same throughout the lifetime of the game. Will have to check that, thanks.
Yes that’s afaik correct. Just googling for the device names it’s hard to find the refresh rate as most specs pages don’t display it, so logging it out is one of the easier ways to get that number.
That’s not the relevant setting, Use Graphics Jobs is the one that needs to be off.
I should have mentioned that “Graphics Jobs” has never been on in the first place. I just thought maybe “Multithreaded Rendering” also has some impact on whether or not GPU Profiling will work, that’s why I brought it up. Sorry for the missinformation there.
Just to be sure this time, here are the full render settings which I use at the moment.
Update1: tried it with “Multithreaded Rendering” off and also tested an older phone (Moto G5, Android 8.1), no luck there. Is there a list with compatible devices or maybe I miss something obvious?
Update2: Just noticed that the VSync I saw yesterday is now gone. Am I imagining things? Now the profiler is perfectly in sync with what I see on device (profiler shows times just above the 16 MS budget, no vSync). The only difference I can see is that I am connecting the profiler over WiFi now instead of cable (ADB) multithreaded rendering is off. It looks like the vSync is now part of “Rendering”.
Update3: Okay, I have tested “Multithreaded Rendering” vs “No Multithreaded Rendering” now and it seems when Multithreaded is on then vSync will show up, if it is off, then it will be part of Rendering, makes sense now. Therefore my conclusion is to turn multithreaded rendering off for profiling builds.
I think I will shelve the issue, since I can now properly profile why I am above budget.
VSync is happening, whether Multithreading Rendering is on or off. Without Multithreaded Rendering, all rendering happens on the main thread and so does the call to Present. Not sure why it isn’t categorized properly as vSync, maybe an oversight, maybe because it can’t properly know what part of Present is VSync and what part is not.
Turning off Multithreading Rendering means your main thread will need to do more work and it’ll change the performance of what you want to measure too much to me useful to find issues when it is turned on.
I believe the only reason to turn off Graphics Jobs in 2019.3+ would be to enable GPU profiling. Next to the Graphics API used, the only other setting affecting the availability of GPU profiling would be the Enable Frame Timing Stats checkbox there if under GLES.
So you should:
turn on Multithreading Rendering & Graphics Jobs
see if Vulkan or OpenGL give you better performance
be happy to see VSync timings in your profile.
Because seeing the VSync while hitting the refresh rate of the screen means you are well within the capacities of the device and the device has some time to cool off between frames, leading to longer battery life, less hot phones, longer (or indefinite) play times until your device will get thermal throttled by the OS (which would mean your performance would suffer).
And also, seeing VSync in the Profiler allows you to see just how much time you have to spare for GPU performance. Lastly: incremental GC will use the profiler markers for vSync to know how much time it has to collect garbage each frame, so this can save you precious main thread time and get rid of the GC spikes!
And yes, not all devices and device driver versions can be GPU profiled and I have no list or set of criteria for this, we are aware though and want to expand the availability of GPU Profiling eventually. For now, you might have to look at what GPU manufacturer produced the chips in your phone and see if you can use their own GPU Profiling tools, or RenderDoc. But you don’t appear to be having any GPU performance issues anyways on this device.
Ah okay, thanks for the intel on mutithreaded rendering.
In retrospect, that’s what started this whole profiling adventure for me. I saw vSync in the profiler but clearly the game fps felt (and still feel) like dropping to 30 on the device if I go to a certain area in my level. I am still not quite sure what to make of it. For now I will just trust the profiler and if it says I am below budget, then I will “be happy to see VSync timings in your my profile.”
At lot of how the profiler works has become clearer for me in this process, thank you.
Side note: it would be very nice to be able to fetch the vSync time via script. A lot of questions regarding “real fps” are floating around but all solutions I found simply measure if fps are below or at the target fps, not how much you are above it. Would be handy intel if one tests without a profiler at hand (playtesting). But I guess this is not the thread to open that box.
Meh, why not :
You should be able to use the Recorder API to fetch the timings for PlayerLoop for main thread timing and then subtract the times for WaitForTargetFPS. From 2020.2 on I’d advise on using the more advanced and flexible ProfilerRecorder API instead.