SceneKit scenes lag when resuming app - ios

In my app, I have several simple scenes (a single 80 segment sphere with a 500px by 1000px texture, rotating once a minute) displaying at once. When I open the app, everything goes smoothly. I get constant 120fps with less than 50mb of memory usage and around 30% cpu usage.
However, if I minimize the app and come back to it a minute later, or just stop interacting with the app for a while, the scenes all lag terribly and get around 4 fps, despite Xcode reporting 30fps, normal memory usage, and super low (~3%) cpu usage.
I get this behavior when testing on a real iPhone 7 iOS 10.3.1, and I'm not sure if this behavior exists on other devices or the emulator.
Here is a sample project I pulled together to demonstrate this issue. (link here) Am I doing something wrong here? How can I make the scenes wake up and resume using as much cpu as they need to maintain good fps?

I won't probably answer the question you've asked directly, but can give you some points to think about.
I launched you demo app on my iPod 6-th gen (64-bit), iOS 10.3.1 and it lags from the very beginning up to about a minute with FPS 2-3. Then after some time it starts to spin smoothly. The same after going background-foreground. It can be explained with some caching of textures.
I resized one of the SCNView's so that it fits the screen, other views stayed behind. Set v4.showsStatistics = true
And here what I got
as you can see Metal flush takes about 18.3 ms for one frame and its only for one SCNView.
According to this answer on Stackoverflow
So, if my interpretation is correct, that would mean that "Metal
flush" measures the time the CPU spends waiting on video memory to
free up so it can push more data and request operations to the GPU.
So we might suspect that problem is in 4 different SCNViews working with GPU simultaneously.
Let's check it. Comparing to the 2-nd point, I've deleted 3 SCNViews behind and put 3 planets from those views to the front one. So that one SCNView has 4 planets at once. And here is the screenshot
and as you can see Metal flush takes up to 5 ms and its from the beginning and everything goes smoothly. Also you may notice that amount of triangles (top right icon) is four times as many as what we can see on the first screenshot.
To sum up, just try to combine all SCNNodes on one SCNView and possibly you'll get a speed up.

So, I finally figured out a partially functional solution, even though its not what I thought it would be.
The first thing I tried was to keep all the nodes in a single global scene as suggested by Sander's answer and set the delegate on one of the SCNViews as suggested in the second answer to this question. Maybe this used to work or it worked in a different context, but it didn't work for me.
How Sander ended up helping me was the use of the performance statistics, which I didn't know existed. I enabled them for one of my scenes, and something stood out to me about performance:
In the first few seconds of running, before the app gets dramatic frame drops, the performance display read 240fps. "Why was this?", I thought. Who would need 240 fps on a mobile phone with a 60hz display, especially when the SceneKit default is 60. Then it hit me: 60 * 4 = 240.
What I guess was happening is that each update in a single scene triggered a "metal flush", meaning that each scene was being flushed 240 times per second. I would guess that this fills the gpu buffer (or memory? I have no idea) slowly, and eventually SceneKit needs to start clearing it out, and 240 fps across 4 views is simply too much for it to keep up with. (which explains why it initially gets good performance before dropping completely.).
My solution (and this is why I said "partial solution"), was to set the preferedFramesPerSecond for each SceneView to 15, for a total of 60 (I can also get away with 30 on my phone, but I'm not sure if this holds up on weaker devices). Unfortunately 15fps is noticeably choppy, but way better than the terrible performance I was getting originally.
Maybe in the future Apple will enable unique refreshes per SceneView.
TL;DR: set preferredFramesPerSecond to sum to 60 over all of your SceneViews.

Related

Preloading assets in AR with SceneKit

In our AR app and appclip made with SceneKit, we experience very significant drops in framerate when we make our 3D content appear at different steps of the experience. We have conducted our tests on an iPhone X (iOS 15.2.1), on an iPhone 12 Pro (iOS 14), and on an iPad Pro 2020 (iPad OS 14.8.1).
For now, all of our 3D objects are in our Main Scene. Those which are supposed to appear at some point in the experience have their opacity set to 0.01 at the beginning and then fade in with a SCNAction (the reason why we tried setting their opacity to 0.01 at the start was to make sure that these objects are rendered from the start of the experience).
However, if the objects all have their opacity set to 1 from the start of the experience, we do not experience any fps drop.
It is worth noting that the fps drops only happen the first time the app is opened. If I close it and re-open it, then it unfolds without any freeze.
What would be the best way to load (or pre-load) these 3D elements to avoid these freezes?
I have been told the prepareObject:shouldAbortBlock: and the prepareObjects:withCompletionHandler methods could solve our problem.
In our case the SceneView is an ARCNView, so we can't seem to access the SCNSceneRenderer methods. We have only managed to implement the "Prepare" method to preload our assets:
https://developer.apple.com/documentation/scenekit/scnscenerenderer/1523375-prepare
It seems to be doing something because we get a significantly longer freeze at the start of the experience, however, we still experience the same fps drops during the experience when our objects appear for the first time.
Is there any documentation on how to use the prepareObject:shouldAbortBlock: and the prepareObjects:withCompletionHandler methods in an ARCNView?

Core Animation profiling: When an app is idle, should it be 0fps of 60fps?

I've been trying to optimize scrolling of my UICollectionView, and the Core Animation profiler has me puzzled...
When the app is idle (no scrolling or interaction in anyway) I'm averaging around 59-60 fps, but occasionally it will drop down to 7 or 12 fps:
Is this expected behavior? Because I'm not interacting with the app when this drop happens I don't visually see anything, but I'm curious if this is something I should be troubleshooting.
Other times when profiling core animation bottlenecks I've seen fps drop down to 0 fps when idle/not interacting with the app.
The app isn't crash or freezing, so is this some sort of bug in Instruments? (I'd expect consistently to be 0fps or close to 60fps when nothing is happening in the app).
Update:
Here's an example of the FPS graph after running the profiler a few minutes later (I'd tried turning on rasterization for a type of view, but then reverted back to not rasterizing, so although the project was rebuilt, the codebase is the same):
Here I'm getting between 32 and 55 fps when interacting with the app, and dropping down to 0 fps when idle.
From my subjective perspective I'm not noticing anything major between what I'm seeing between these two examples, but from Xcode's perspective I'm seeing two different stories.
Does anyone know what's happening here?

Interpreting downward spikes in Time Profiler

I'd appreciate some help on how I should interpret some results I get from Time Profiler and Activity Monitor. I couldn't find anything on this on the site, probably because the question is rather specific. However, I imagine I'm not the only one not sure what to read into the spikes they get on the Time Profiler.
I'm trying to figure out why my game is having regular hiccups on the iPhone 4. I'm trying to run it at 60 FPS, so I know it's tricky on such an old device, but I know some other games manage that fine. I'm using Unity, but this is a more general question about interpreting Instruments results. I don't have enough reputation to post images, and I can only post two links, so I can't post everything I'd like.
Here is what I get running my game on Time Profiler:
Screenshot of Time Profiler running my game
As far as I understand (but please correct me if I'm wrong), this graph is showing how much CPU my game uses during each sample the Time Profiler takes (I've set the samples to be taken once per millisecond). As you can see, there are frequent downward spikes in that graph, which (based on looking at the game itself as it plays) coincide with the hiccups in the game.
Additionally, the spikes are more common while I touch the device, especially if I move my finger on it continuously (which is what I did while playing our game above). (I couldn't make a comparable non-touching version because my game requires touching, but see below for a comparison.)
What confuses me here is that the spikes are downward: If my code was inefficient, doing too many calculations on some frames, I'd expect to see upward spikes, now downward. So here are the theories I've managed to come up with:
1) The downward spikes represent something else stealing CPU time (like, a background task, or the CPU's speed itself varying, or something). Because less time is available for my processing, I get hiccups, and it also shows as my app using less CPU.
2) My code is in fact inefficient, causing spikes every now and then. Because the processing takes isn't finished in one frame, it continues onto the next, but only needs a little extra time. That means that on that second frame, it uses less CPU, resulting in a downward spike. (It is my understanding that iOS frames are always equal legnth, say, 1/60 s, and so the third frame cannot start early even if we spent just a little extra time on the second.)
3) This is just a sampling problem, caused by the fact that the sampling frequency is 1ms while the frame length is about 16ms.
Both theories would make sense to me, and would also explain why our game has hiccups but some lighter games don't. 1) Lighter games would not suffer so badly from CPU stolen, because they don't need that much CPU to begin with. 2) Lighter games don't have as many spikes of their own.
However, some other tests seem to go against each of these theories:
1) If frames always get stolen like this, I'd expect similar spikes to appear on other games too. However, testing with another game (from the App Store, also using Unity), I don't get them (I had an image to show that but unfortunately I cannot post it).
Note: This game has lots of hiccups while running in the Time Profiler as well, so hiccups don't seem to always mean downward spikes.
2) To test the hypothesis that my app is simply spiking, I wrote a program (again in Unity) that wastes a consistent amount of milliseconds per frame (by running a loop until the specified time has passed according to the system clock). Here's what I get on Time Profiler when I make it waste 8ms per frame:
Screenshot of Time Profiler running my time waster app
As you can see, the downward spikes are still there, even though the app really shouldn't be able to cause spikes. (You can also see the effect of touching here, as I didn't touch it for the first half of the visible graph, and touched it continuously for the second.)
3) If this was due to unsync between the framerate and the sampling, I'd expect there to be a lot more oscillation there. Surely, my app would use 100% of the milliseconds until it's done with a frame, then drop to zero?
So I'm pretty confused about what to make of this. I'd appreciate any insight you can provide into this, and if you can tell me how to fix it, all the better!
Best regards,
Tommi Horttana
Have you tried unity's profiler? Does it show simillar results? Note that unity3d has two profilers on ios:
editor profiler - pro only (but there is a 30 day trial)
internal profiler - you have to enable it in xcode project's source
Look at http://docs.unity3d.com/Manual/MobileProfiling.html, maybe something will hint you.
If i had to guess, I'd check one of the most common source timing hickups - the mono garbage collector.
Try running it yourself in a set frequency (like every 250ms) and see if there is a difference in the pattern:
System.GC.Collect();

Cocos2dx 2.1.4 Game, Continuos FPS drop and never recovers

I am creating a game using cocos2dx 2.1.4. Its FPS drops continuously , and never recover.
Please find the details as follows
Background about the way I am doing things:-
Its game about scrolling down some shapes, each shape is made up of some square blocks.I have 7 kind of blocks. All loaded in a Sprite Sheet and using these blocks from this sprite sheet I create a shape.
A level file is consist of these shapes. I load two levels at the same time one onscreen and another off screen to make it seamless scrolling. For loading two levels at the same time I used two different CCSprite game batch nodes as :-
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("56blackglow.plist");
_gameBatchNode1 = CCSpriteBatchNode::create("56blackglow.png", 200);
_gameBatchNode1->retain();
this->addChild(_gameBatchNode1,kForeground);
_gameBatchNode2= CCSpriteBatchNode::create("56blackglow.png", 200);
_gameBatchNode2->retain();
this->addChild(_gameBatchNode2,kForeground);
The problem I am facing is that as I keep on playing the game frame rate drops continuously , from 60 fps till 10 fps and never recovers or might recover in near future , as I observed for 20 minutes but its too much time to wait.
My observations:-
1> I used Time profiler it shows maximum time is in draw() calls. Also if I play game very fast the peak of time increases in track, that should be fine as I am giving more work to do, but once a peak is attained it remains approximately at that height only, even if I leave the game Idle. Is it normal ? According to me it should have returned to its normal peak once the current work is done.
2> Some where I thought its happening because I used two batch nodes and removing its children on a user touch immediately might causing it slow but then after removing the children it should run normal. to give an idea is it ok to remove 10 children from batch node immediately ? some guys say its very slow process. Just to check if this causing problem , I did :-
Instead of removing them I just set visibility of the children to false.But still FPS drops and never recovers.
Please share your thoughts on this.
Though SpriteBatchNodes are generally quite good for drawing a lot of elements efficiently, I think there are best used for static/not-so-dynamic elements. In your case, if you have a lot of elements which go out of the screen but are still alive the draw() function will have to make some checks, thus hogging your performance (even if you set isVisible(false); explicitly, it still nedds to be checked).
In your case I think it would be better if you simply added new shapes outside of screen via some time-based function, and if they scroll outside of view simply remove them from scene, without using batchNodes.
Just found that with every touch, I am adding 8 new sprites to the layer, and its adding every time I touch . So with time I am giving more and more work to do. This is the problem
Actually I wanted to replace the sprite at 8 places with a touch, the way I was doing every time :-
_colorBottom1=CCSprite::createWithSpriteFrameName(png[0]);
this->addChild(_colorBottom1,kForeground);
_colorBottom1->setPosition(ccp((_colorPanelLeftPad)*_blockWidth,_blockWidth));
It was causing this sprite to be added with every touch.
but it should have been (Replace the texture instead of creating the Sprite again):-
CCSpriteFrame *frame1=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(png0);
_colorBottom1->setDisplayFrame(frame1);

Cocos2d 2.0 - 3 numbers on the bottom left?

I'm having a problem that I'm not sure how to solve.
In cocos2d 2.0 , the second number on the bottom left drops to a low number like 0.002 and causes lag in my game!!
The second number is the 'Frames Per Second's Milliseconds', or the amount of time it takes to go to the next frame. I got this info from a question similar to mine, here is a link to that question:
Cocos2d 2.0 - 3 numbers on the bottom left
The games FPS's Milliseconds usually runs at about 0.016 or 0.021 and there is no lag.
Shouldn't it run smoother at numbers as low as 0.002?
How can I stop this lag?
Is there anybody that knows enough about cocos2d to help me out?
When your app runs really, really slow (around 10 fps or less) the milliseconds display is no longer accurate and will display a very low number.
You need to find out what's causing the drop in framerate. If the number of draw calls is high (100+) then your problem is that you're rendering too much and/or inefficiently (use sprite batching).
If the number of draw calls is reasonably low (no more than 50) then your problem is not the rendering but your own code. Possibly some time consuming (inefficient?) algorithm or frequently loading/unloading objects and/or data (files), those are the most common cases.
How are you observing/measuring this 'lag' ?
If you are reporting this based on running on a simulator, please go check on a device. Simulator numbers are meaningless. Btw, the number does not CAUSE lag, the number is the result (measure) of resource consumption by the app , ie low FPS is caused by laggy software, not the reverse.

Resources