I have been running a simple, many body (800) 2x2 pixel SpriteKit simulation using the PhysicsWorld under iOS11 / Swift4 - a rudimentary particle system.
It contains a simple radial gravity node, with 800 objects orbiting it with no collision or contact detection employed.
On my test hardware, an iPhone6S - I have been really impressed with attaining 60FPS and super smooth simulation - which opens the way for much more exploration.
The only problem I have been running up against is that the performance is really inconsistent.
If I switch the gravity node's field strength to a high value mid run (meaning objects move around a lot faster) - in about 25% of runs, the simulation can get 60FPS no problems, but on the other 3 out of 4 runs, the framerate drops to 3-4FPS
If I revert to a low field strength (slower movements) then it always gets 60FPS again.
I have profiled in XCode - and can see that SOMETIMES 3 or 4 threads are given to the app, giving 60FPS, but the other times only a single thread is being given to the task - meaning gnarly performance
I have experimented with Grand Central Dispatch, and thread priorities - to no avail - the only code that is running each frame is the PhysicsWorld anyway, nothing I'm doing frame to frame.
Any ideas out there how I can get consistent 60FPS - as clearly the hardware is capable of it - but just when it feels like it!
Additional note: if the device is plugged into the dev Mac / on charge / USB / with XCode running etc - the performance is more often good with all threads utilised. But trying the exact same app, say the next day, running standalone on the device (not attached to dev machine) - nearly always very poor performance - MAJOR HEADSCRATCH!
Performance issues regarding nodes are common when physics bodies are used, if you have used a physics body use the standardised bodies instead of the custom one. Hope this helps.
Related
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.
I have a scene with multiple light nodes. Normally my game runs fine at 60fps on a late 2016 Macbook Pro. (The game is for mac, not iOS). When a light node is added the frame rate drops, and once there are 4-5 nodes it's extremely slow and laggy. I'm creating them like this:
let light: SKLightNode = SKLightNode()
light.falloff = 4.5
addChild(light)
I know that the lighting effects need a lot of rendering power, but I'm surprised at how fast they cause issues.
Any ideas on how I can improve performance?
SKLightNodes are very performance intensive, especially on older devices.
For example if you use 2 LightNodes in 1 SKScene on an iPhone 5 the frame rate drops to like 20FPS and makes the game basically unplayable.
I recently made a game with 4 lights in a scene and everything was fine on a iPhone 7, but on an older device it was unusable.
So IMO you should not use more than 1 SKLightNode per Sprite/Scene, maybe 2 max otherwise performance will be very bad. I am not sure how the performance is on macOS but the way you described it using 4-5 lights is way too much. So there is not too much you can do to improve performance.
SKLightNode performance issues
The WWDC session video What's New in SpriteKit mentions that you might get less than 60 FPS if you have more than one light on the same sprite.
Hope this helps
I've finished developing a card game called Up and Down the River in SpriteKit. It is a fairly simple card game with a few animations such as the action of dealing and playing a card.
According to debugger tools, it is generally Very High energy utilization and the averages near 170 wakes per second. (Shown below)
What is typical for a SpriteKit game? Should a simple card game be using this much energy? If not, what should I be looking for in order to reduce the energy usage?
Note: This is being run on macOS, however the game is cross-platform (meaning iOS and macOS). I get similar results for running on an iOS device.
When SpriteKit is running it is constantly updating the screen (usually at 60 frames per second).
If you do not need this high speed you can reduce it to 30 or 20 or lower frames per second by setting preferredFramesPerSecond on the SKView, see https://developer.apple.com/reference/spritekit/skview
If your game is completely static while waiting for user input you can even set isPaused on the SKView to stop updates completely while you are waiting.
I am creating this sort of first person shooter game for the iPhone 6 Plus, but when I introduce any lights to the scene, the frame rate goes from an already barely acceptable 12fps to an absolutely unplayable 2fps. Also, introducing a particle system with more than ten particles in it takes the frame rate to 9fps. I have already made it so that it adds all the walls and doors to a map node, and then flattens it using flattenedClone and adds that. I am unsure what else I can do without switching to Metal. But I am also wondering about this because if SceneKit were so slow, why would it even exist?
Problem solved: get a developer licence!
I am building a cocos2d continuous running ios game, where a character is animated to run through a scene. The background, the grounds under the character's feet and a fence behind the running character move in the opposite direction to simulate forward motion. The character itself is animated to run in place. Occasionally additional sprites move by in the scene.
All my movement (except the in place animation of the character) happens in the update method.
I notice that all the moving parts stutter when additional sprites are moving past the scene. When there are no additional sprites, the stutter goes away.
I also notice that the stutter is almost imperceptible in the ipad 3 (ios 6.0.1), whereas it is most noticeable in the iphone 3gs (ios 6.0.1) and ipad 1 (ios 5.1.1), and moderately noticeable on the iphone 4 (ios 6.0.1).
I have no idea where to begin to address this - I tried removing the actual rendering of the additional sprites, and I also tried removing the fence behind the character - but neither change had any impact on the stutter.
Any suggestions?
Update: fixed it by running time profiler as #Fogmeister suggested below. Turns out I was doing disk i/o on every update to look up some state variables. I optimized that away and the stutter is gone on all devices. Thanks!
You need to identify what is taking up the time in the code and causing the stutter.
It seems like it is just an optimisation issue as you can see more stutter on older, slower devices and less on new more powerful devices.
Analyse the app using the Time Profiler instrument. This will tell you where the time is being spent and point you almost instantly to the function (if not the line) that is taking the time.
Once you've found that you can start optimising.
What is your code for the movement of the sprites? If you are updating the positions manually in the -(void) update:(ccTime) delta {} method make sure you are multiplying each movement by delta to account for any minute framerate fluctuations.