How do I slow down the game loop in the game_loop package - dart

I am using the game_loop pub package to handle my eventloop. The problem is that it updates way too often. I don't need to update or redraw that often, and input key repetition is also too fast. I do not know much about eventloops or browser redraws, so I might think of it the wrong way, but is there a way to slow the loop down?

Run heavy tasks in the separate Isolate while keeping game loop as lightweight as possible. The game loop should be implemented with window.animationFrame. How do I drive an animation loop at 60fps with Dart? You should learn all about requestAnimationFrame - it's the key to the smooth animations.
And your game logic speed should not depend on the browser FPS(Frames Per Second) use scheduler instead Does Dart have a scheduler?

Try adding a timer and avoid adding an onUpdate or onRender handler to your GameLoop instance:
//timer fires 20 times per second, as an example
gameLoop.addTimer(render,0.05,periodic: true);
...
...
render(GameLoopTimer timer)
{
//draw or update code here
}

Related

SpriteKit, put a process on another core?

Imagine in your
class NattyScene: SKScene {
you have perhaps a custom Field for the nodes, or something else that happens every frame. Now imagine you have some calculation, a nice example might be center of gravity ...
var globalCOG: CGPoint
func updateCOG() {
.. say, get all the .position of all Spaceship ..
globalCOG = .. some point
}
It would make great sense to put that on another thread, assuming issues like
it's threadsafe / fast to read the .positions
this other putative thread on another core, knows about SpriteKit frames (so that you can calculate until giveup time in the usual manner, etc, perhaps you may prefer to skip frame or whatever - the usual panoply of threaded game programming)
you can threadsafe/blahblah write the COG global back to the rest of SpriteKit
What's the deal on this?
What really is the modern idiom for this in Swift4 / SpriteKit
How do you force it to go on another whole physical core?
Any ideas?
override func update(_ currentTime: TimeInterval) {
let x = safely get info from a thread on another physical core on the device
print("now that's cool")
}
on the other core ...
func loopy() {
let x = safely look at info on NattyScene
let globalCOG = calculation
}
Note, KOD has pointed to DispatchQueue, which is great - but is there any way to ensure it's really on another core?
Nice question, unfortunately I don't think this is possible for 2 main reasons.
Reason 1
You don't have this kind of low level access in iOS.
The OS is the one who decides which thread runs on which core.
It also has the capability of turning on and off the cores depending on several conditions beyond the scope of your app.
E.g. When in Grand Central Dispatch you write
DispatchQueue.global(qos: .background).async { }
You have no guarantee the closure will be executed on a different core.
Reason 2
The Game Run Loop of SpriteKit does execute a bunch of stuff
call update
evaluates actions
simulates physics
applies constraints
Your idea does imply the execution of the following steps within the call update phase
move on a "background" thread
perform some heavy computations
bring the result back to the main thread
But at this point you have no guarantee the Game Run Loop is still in the call update phase. It could be in the evaluates actions or it could even be working at the next frame.
On the other hand you have no more than 16 milliseconds to render each frame.
Possible Solution
Instead of targeting another CPU core you could take full advantage of the 64 bits allowed by the current core.
Look at this Q/A about SceneKit where are listed the benefits of SIMD.

How to make a function atomic in Swift?

I'm currently writing an iOS app in Swift, and I encountered the following problem: I have an object A. The problem is that while there is only one thread for the app (I didn't create separate threads), object A gets modified when
1) a certain NSTimer() triggers
2) a certain observeValueForKeyPath() triggers
3) a certain callback from Parse triggers.
From what I know, all the above three cases work kind of like a software interrupt. So as the code run, if NSTimer()/observeValueForKeyPath()/callback from Parse happens, current code gets interrupted and jumps to corresponding code. This is not a race condition (since just one thread), and I don't think something like this https://gist.github.com/Kaelten/7914a8128eca45f081b3 can solve this problem.
There is a specific function B called in all three cases to modify object A, so I'm thinking if I can make this function B atomic, then this problem is solved. Is there a way to do this?
You are making some incorrect assumptions. None of the things you mention interrupt the processor. 1 and 2 both operate synchronously. The timer won't fire or observeValueForKeyPath won't be called until your code finishes and your app services the event loop.
Atomic properties or other synchronization techniques are only meaningful for concurrent (multi-threaded) code. If memory serves, Atomic is only for properties, not other methods/functions.
I believe Parse uses completion blocks that are run on a background thread, in which case your #3 **is* using separate threads, even though you didn't realize that you were doing so. This is the only case in which you need to be worried about synchronization. In that case the simplest thing is to simply bracket your completion block code inside a call to dispatch_async(dispatch_get_main_queue()), which makes all the code in the dispatch_async closure run on the main, avoiding concurrency issues entirely.

Better way to run multiple HealthKit sample queries?

I have a scenario where I need to retrieve multiple sets of data from HealthKit -- body temperature, weight, and blood pressure. I need all 3 before I can continue processing because they're going to end up in a PDF.
My naive first approach is going to be run one, then in the HKSampleQuery's resultsHandler call the second, then in that resultsHandler call the third. That feels kind of -- I don't know -- it feels like I'm missing something.
Is there a better way or is the naive approach reasonable?
I ran into this same problem, and a much better approach for any kind of nested async call would be to use GCD's dispatch groups. These allow you to wait until multiple async tasks have completed.
Here's a link with an example: Using dispatch groups to wait for multiple web services
You're going to want to use GCD dispatch groups.
First, set up a global variable for the main thread
var GlobalMainQueue: dispatch_queue_t {
return dispatch_get_main_queue()
}
Next, create the dispatch group:
let queryGroup = dispatch_group_create()
Right before your queries execute, call:
dispatch_group_enter(queryGroup)
After your query executes, call:
dispatch_group_leave(queryGroup)
Then, handle your completion code:
dispatch_group_notify(queryGroup, GlobalMainQueue) {
// completion code here
}
You should try to run the queries in parallel for better performance. In the completion handler for each one, call a common function that notes a query has completed. In that common function, when you determine that all of the queries have finished then you can proceed to the next step.
One simple approach to tracking the completion of the queries in the common function is to use a counter, either counting up from zero to the number of queries, or down from the number of total queries to zero.
Since HealthKit query handlers are called on an anonymous background dispatch queue, make sure you synchronize access to your counter, either by protecting it with a lock or by modifying the counter on a serial dispatch queue that you control, such as the main queue.

What If the Updates Handler for CoreMotion Does Not Finish Fast Enough?

I am registering to receive updates from a CMMotionManager like so:
motionManager.startDeviceMotionUpdatesToQueue(deviceMotionQueue) {
[unowned self] (deviceMotion, error) -> Void in
// ... handle data ...
}
where deviceMotionQueue is an NSOperationQueue with the highest quality of service, i.e. the highest possible update rate:
self.deviceMotionQueue.qualityOfService = NSQualityOfService.UserInteractive
This means that I am getting updates often. Like really often. So I was wondering: what happens if I don't handle one update fast enough? If the update interval is shorter than the execution time of 'handle data'? Will the motion manager drop some information? Or will it queue up and after a while become run out of memory? Or is this not feasable at all?
It's hard to know what the internal CoreMotion implementation will do, and given that what it does is an "implementation detail", even if you could discern its current behavior, you wouldn't want to rely on that behavior moving forward.
I think the common solution to this is to do the minimum amount of work in the motion update handler, and then manage the work/rate-limiting/etc yourself. So, for instance, if you wanted to drop interstitial updates that arrived while you were processing the last update, you could have the update handler that you pass into CoreMotion do nothing but (safely) add a copy of deviceMotion to a mutable array, and then enqueue the "real" handler on a different queue. The real handler might then have a decision tree like:
if the array is empty, return immediately
otherwise (safely) take the last element, clear all elements from the array, and do the work based on the last element
This would have the effect of letting you take only the most recent reading, but also to have knowledge of how many updates were missed, and, if it's useful, what those missed updates were. Depending on your app, it might be useful to batch process the missed events as a group.
But the takeaway is this: if you want to be sure about how a system like this behaves, you have to manage it yourself.

Instruments blocks Background Thread?

EDIT:
Today I worked with my code a bit and gave background thread task some advantages over the main thread task. Essentially one thread is working with the even numbered items in an array and the other is working with the odd numbers. I gave the background thread the even number half, which will be either equal to or one less than the odd half. Also, I moved the while(!collisionDone) to a bit later in the code, as far back as I can to keep it thread safe. I put an NSLog in there to detect if the condition is ever false when it reaches that point, and it has not fired once. Furthermore, the instruments build now runs fine. That means the problem is stalling the main thread with the while loop. So that means my question now is:
How should I stall the main thread to wait until the background thread completes a task? Perhaps demonstrate how one uses NSLock to achieve that functionality?
Today I added multithreading in an attempt to reduce lag in my game. I am using an NSTimer to call my time loop function like this:
[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(time:) userInfo: nil repeats: true];
Then, in that function, I have:
//run collision using multithreading
collisionDone = false;
[self performSelectorInBackground:#selector(collideBackground:) withObject:objectToSend];
[self collideMain:objectToSend];
while (!collisionDone) {
//notdone
}
The idea is that I am running one set of collision checks on one thread and another set in another thread. The empty while loop is to ensure the background thread has completed before carrying on in the function. After this bit, the code goes on to update the view.
This all runs fine, normally, but when I went to Instruments to check on my FPS, I found that the game freezes, it appears that it is freezing somewhere in the first run of the time loop. The app ran fine in Instruments before the multithreading, which essentially worked the same, except simply using [self collideBackground:objectToSend] rather than sending it to another thread. It would appear that the background thread is not completing or not being run at all, so collisionDone will always be false, and therefore the app will wait in that endless loop until I kill it.
Also, I have tried replacing that line with [self collideBackground:arrayToSend];, and it starts working again. Another thing I tried is putting NSLog(#"called"); at the very start of viewWillAppear: and it seems to run exactly one more frame!?!?!?
I would like any ideas as to why exactly this is happening and how to fix it. I doubt it is unrelated to the multithreading as doing everything on a single thread fixes it.
Yes it is related to multithreading, and your poll on the main thread. You should put a callback into your background thread instead. And not blocking the main thread.
// is not good at all
collisionDone = false;
/* etc... that will make collision to true */
while (!collisionDone) {
}
make instead
[self computeSomethingOnBagroundWhithData: nil onCompletion:^{
// callBack stuff
}]
Edit:
while (!collisionDone)
is always true because when you are creating another thread, the instruction is stuck in "while" statement, and is not reevaluating each time "collisionDone" as you would expect, but evaluate it inside the while scope. By the way, there could be some optimizer stuff, and "collisionDone" could only be evaluated in local scope (but here I'm only guessing, since it's compiler dependent). You should reenter the method in order to make it work, or join the thread when you are done. Since you don't want complex code for this kind of stuff, block are exactly what you want. libDispatch are perfect for this.
So you can try "self.collisionDone" in order to try to get the genuine pointer. But there's lot of chance that won't change anything.
Or you can do your stuff in background and when you are done, send a signal / callback to your client code. You don't need to make polling like you did. This is really bad design (really trust me ;) )
It has been unclear what the best approach to solving this problem or what the actual cause of the problem (yes it is that while(!collisionDone) loop, but why?), but none of the proposed alternatives can do the job properly. Although other options have been suggested, NSThread and performSelectorInBackground: have been the only ways I have detected the functionality I am looking for. Furthermore, after testing with the multithreading part turned off, I realized that the iPhone 4s has a much, much better processor than the iPhone 4, and I can run the game at 60 FPS on the iPhone 4s and newer without multithreading (the iPhone 4 can only handle 30 FPS). Lastly, I intended for my multithreading to utilize the dual-core processor of the iPhone 4s, so half of the collision would be done in one core while the other core does the other half. I am not sure this is actually happening and I have no idea how to detect this.
My conclusion: Remove this attempt at multithreading from the app completely. It is unnecessary and the benefits are dubious.
#Fonix: The question you linked to shows exactly what I am trying to do, however, none of the solutions they presented seem to be working.
The final solution after talking to my programmer friend about it at school:
//threadLock is an NSLock declared in the .h
-(void)timeLoop {
[self performSelectorInBackground:#selector(collideBackground) withObject:nil];
[self collideMainAtStartingPoint:0];
[threadLock lock];
[threadLock unlock];
//update the view hierarchy and stuff like that
}
-(void)collideBack {
[threadLock lock];
[self collideMainAtStartingPoint:2];
[threadLock unlock];
}

Resources