How can I speed up my animations to make my EarlGrey test suite run faster - xctest

I saw the below API in GREYConfiguration that says that EarlGrey by default truncates CALayer animations more than 10 seconds -
/**
* Configuration for setting max allowable animation duration (in seconds) for any CALayer based
* animation. Animations exceeding the specified time will have their duration truncated to value
* specified by this config.
*
* Accepted values: #c double (negative values shouldn't be used)
* Default value: 10.0
*/
GREY_EXTERN NSString *const kGREYConfigKeyCALayerMaxAnimationDuration;
I'm developing a small gaming app that has a number of such animations going on. I noticed that my tests take a long time to run when I have my animations enabled, which is required for my UI Tests. I know that I can change the animation speed for my entire app using UIApplication.sharedApplication.keyWindow.layer.speed. Is there any way that I can change it only for my EarlGrey tests?

Put your UIApplication.sharedApplication.keyWindow.layer.speed behind a conditional statement:
#if EARLGREY_ENV
UIApplication.sharedApplication.keyWindow.layer.speed = 100
#endif
The line of code will only execute when the app is being run through Earl Grey.
For more information around dealing with animations in Earl Grey, see How should I handle animations? under their FAQs.

Related

Increasing framerate from 60 to 90 in Cocos2D V3 no effect

In CCAppDelegate I set:
NSTimeInterval animationInterval = [(config[CCSetupAnimationInterval] ?: #(1.0/90.0)) doubleValue];
[director setAnimationInterval:animationInterval];
director.fixedUpdateInterval = [(config[CCSetupFixedUpdateInterval] ?: #(1.0/90.0)) doubleValue];
and in CCDirectoriOS - startAnimation I set:
int frameInterval = (int) floor(_animationInterval * 90.0f);
CCLOG(#"cocos2d: animation started with frame interval: %.2f", 90.0f/frameInterval);
But the FPS counter shows 59/60 fps max.
How do I increase the framerate?
It doesn't make sense to increase the frame rate above 60, because the screen refresh rate is 60Hz, so it would not make any difference on screen and it would be just waste of battery and CPU.
You could in theory increase it in cocos2d, however you would have to change some code, as cocos2d uses CADisplayLink as its inner timer, which is obviously linked to the display thus giving you max 60 fps. You would have to have your own refresh timer with interval 1.0/90.0. But you still wouldn't see the difference.
The real reason is that iOS devices have a fixed screen refresh rate of 60 Hz. You can not display more than 60 frames per second on iOS because the screen doesn't refresh faster than 60 Hz (= times per second). The same is true for just about every other mobile device.
Now on PCs it's rather common to turn vertical synchronisation off and have the GPU render as fast as it can, with all the screen tearing you can possibly stand. On mobile devices, VSync is always on and you can not turn it off, ever.
Mobile benchmarking software frequently uses so-called "offscreen render" tests where they render everything into a memory buffer to circumvent the 60 Hz limit and to come up with a theoretically achievable framerate, like 150 fps. Of course you won't see realtime updates of the test on the screen while the test is running.

Long-running animations in iOS (up to 1/2 hour)

So I've got this animated pie chart working now. It can indicate e.g. progress over time (similar to UIProgressView).
For legacy reasons I am still using it with a timer that fires approx. every second and increases progress. It should now be possible to get rid of this timer and set the overall duration of a pie animation e.g. to 1/2 hour instead of letting the timer fire 30 * 60 times and starting as many short incremental animations.
So my question is this: are there any good reasons that speak against using such long (say up to 1/2 hour long) animations in iOS? In the example of the pie chart no more than approx. 360 frames would be needed even over 1/2 hour.
There is a good reason against very long animations: memory.
CoreAnimation will create a presentationLayer for every frame (see for example your other question), and (at least up to iOS 7.1) it will allocate and initialize them in background the moment you add the animation to the layer.
The frame rate depends on the device, not on the magnitude of the change of the animated property; moreover, there doesn't seem to be a way to tweak CoreAnimation's frame rate on iOS (while on OSX NSAnimation has a frameRate property), so if you animate progress (but it would be the same with any property) and set a duration time of 30 minutes, you will end up with a lot of memory wasted.
Some numbers. I scheduled some CABasicAnimations with path progress on your DZRoundProgressLayer, and added some logging in -initWithLayer:. This revealed that on the simulator, roughly 50 shadow copies (frames) are needed per second of animation.
This means 90K shadow copies are going to be created for 30 minutes: for several seconds after the beginning of the animation, CoreAnimation was still allocating the first thousands of copies. Adding some data payload to the instance variables of DZRoundProgressLayer showed the memory usage raising by several MB in the first seconds (then some memory management took over the unconstrained allocations, presumably freeing the old copies).
Is it a bad idea? It's a waste of resources, memory and CPU, even if your layer occupies a few bytes in memory, considering that the change in the pie area per frame is too small to be noticed. Setting up a NSTimer or KVO doesn't require many lines of code, so it might be worth to change approach.

Animation iOS with sound file

I have some animation which needs to appear on screen at very specific timings, these are stored in SQLite database. What I am planning to do is use nstimer to keep time and pop the animation when the specific time is reached.
Im thinking of using an NSTimer to count for the duration of the sound file and animation then when certain points are reached pop an on screen animation. The problem is the set timing are like this 55.715000 seconds so are very accurate and these need to sync with an audio track that will be played with the animation.
Firstly is this even possible, secondly how can i compare such specific timings the problem i seem to be facing is the code can't run quick enough and the time jumps more than .001 of a second.
I have no knowledge of openGLES or Cocos2d and learning these is not really feasible for the time scales.
If your visuals need to be exactly in sync with the audio (for example a music app where animations have to appear on the beats) then you need to use the following approach. It works on very old iPhone hardware and there is basically nothing that can go wrong at runtime.
Pre-render each "frame" of your visuals so that each one is stored as a full screen image. This could be done on the desktop or you can run the render logic on the phone and then capture the output to a file as pixels.
Once each frame is saved as an array of pixels, play the audio via the standard AVAudioPlayer APIs on iOS. This API takes care of the audio playback and reports a time that you will use to determine which video frame to display.
Once audio is playing, get the "time" and divide it by your video framerate to determine which image from an array of N images to display. Get the image data and wrap it up in a CGImageRef/UIImage, this will blit the image data to the screen in a optimal way.
If you would like to see working source code for this approach, take a look at AVSync. This example code shows the implementation that I used in my own app called iPractice in the app store. It is very fast and can run at 30FPS even on the old iPhone 3G.
Regardless of how accurate your timings are, you aren't going to get the device to display more frequently than about 60Hz (16.7 ms per frame). As I see it, you have at least two options here:
1) Use a CADisplayLink callback to check the playback progress of the audio and trigger each animation as it becomes timely.
Display link timers are created in a similar way to regular NSTimers:
-(void)viewDidLoad
{
// ...
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(scheduleAnimations:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)scheduleAnimations:(CADisplayLink *)displayLink
{
// get playback time from player object
// iterate queue of timings
// if the timestamp of a timing is equal to or less than the playback time
// create its animation for immediate execution
// remove it from the queue
}
2) Create a collection of animations, setting their respective beginTime properties to the appropriate trigger time (or, if using implicit or block-based animations, use the delay parameter).
[CATransaction begin];
// iterate collection of timings
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"key"];
animation.startTime = /* time to trigger animation */
animation.removedOnCompletion = NO;
[layer addAnimation:animation forKey:nil];
[CATransaction commit];

iOS framerate - always 30fps with UIImage?

i want to create a frame-by-frame animation with an array of images.
There is to set the animationDuration, the standard value is 1.
Can i be sure that it is always 30fps on every iOS-device when i start the animation with startAnimating?
So i need exactly 15 images for a 1 second animation - is there a special calculation i can use when i have more or less than 15 images that have to be animated in exactly one second?
E.g. 60/15*(count number of images in array)
I don't think you can count on UIImageView animationImages to give you 30FPS everywhere and everytime.
Indeed, this is meant for very simple and "opportunistic" animations. If you google for it, you will find reports of that method hogging a device (in specific conditions). On the other hand, if your images are small, then chances are that it could work, but you get no guarantees (nor ways to enforce the FPS you need).
So i need exactly 15 images for a 1 second animation - is there a special calculation i can use when i have more or less than 15 images that have to be animated in exactly one second?
If I understand your question right, then you can try with:
duration = number_of_images / FPS; // e.g., 60 images / 30 FPS = 2 seconds
of course, if the device will then show properly the 60 images at 30 FPS is another story.

UIView animateWithDuration: slows down animation frame rate

I am using CADisplayLink to draw frames using the EAGLView method in a game at 60 times per second.
When I call UIView animateWithDuration: the framerate drops down to exactly half, from 60 to 30 fps for the duration of the animation. Once the animation is over, the fps rises instantly back up to 60.
I also tried using NSTimer animation method instead of CADisplayLink and still get the same result.
The same behavior happens when I press the volume buttons while the speaker icon is fading out, so it may be using animateWithDuration. As I would like to be able to handle the speaker icon smoothly in my app, this means I can't just rewrite my animation code to use a different method other than animateWithDuration, but need to find a solution that works with it.
I am aware that there is an option to slow down animations for debug on the simulator, however, I am experiencing this on the device and no such option is enabled. I also tried using various options for animateWithDuration such as the linear one and the user interaction, but none had an improvement.
I am also aware I can design an engine that can still work with a frame rate that varies widely. However, this is not an ideal solution to this problem, as high fps is desirable for games.
Has someone seen this problem or solved it before?
The solution to this is to do your own animation and blit during the CADisplayLink callback.
1) for the volume issue, put a small volume icon in the corner, or show it if the user takes some predefined touch action, and give them touch controls. With that input you can use AVAudioPlayer to vary the volume, and just avoid the system control altogether. you might even be able to determine the user has pressed the volume buttons, and pop some note saying do it your way. This gets you away from any animations happening by the system.
2) When you have an animation you want to do, well, create a series of images in code (either then or before hand), and every so many callbacks in the displayLink blit the image to the screen.
Here's an old thread that describes similar drops in frame rate. In that case, the cause of the problem was adding two or more semi-transparent sprites, but I'd guess that any time you try to composite several layers together you may be doing enough work to cut the frame rate, and animateWithDuration very likely does exactly that kind of thing.
Either use OpenGL or CoreAnimation. They are not compatible.
To test this remove any UIView animation, the frame rate will be what you expect. Add back UIView animation, it will drop to 30fps.
You said:
When I call UIView animateWithDuration: the framerate drops down to exactly half, from 60 to 30 fps for the duration of the animation. Once the animation is over, the fps rises instantly back up to 60
I dont know why your not accepting my answer, this is exactly what happens when you combine UIView animation with CA animation not using a UIView.