0

So I am simulating this smartphone app to Windows. It's a game that runs it's logic and draw methods at a 1/60 rate. In milliseconds this is 16.6667

I've implemented this game loop:

    private const double UPDATE_RATE = 1000d / 60d;

    private void GameLoop()
    {
        double startTime;
        while (GetStatus() != GameStatus.NotConnected)
        {
            startTime = Program.TimeInMillis;
            //Update Logic
            while (Program.TimeInMillis - startTime <= UPDATE_RATE)
            {
                //Thread.Yield(); it consumed CPU before adding this too, adding this had no effect
                Thread.Sleep(TimeSpan.FromTicks(1));//don't eat my cpu
            }
        }
        Debug.WriteLine("GameLoop shutdown");
    }

Program.TimeInMillis comes from a NanoStopwatch class, that return the time in millis in double. Which is useful because this gameloop has to be really accurate to make everything work.

As you probably know, the Thread.Sleep will consume a lot of CPU here. Probably because it requires int millis which will convert my TimeSpan to 0 milliseconds.

I recently came across this thing called WaitHandle, but everything I see uses int millis. I really need to make this accurate so I actually need double millis or microseconds/nanoseconds.

Is there anything in C# that allows me to Wait for x amount of microseconds. I don't mind creating something myself (like I did for the NanoStopwatch), but I need to be pointed in the right direction. Found a lot on the internet, but everything uses int millis and that's not accurate enough for me.

I ran some tests changing the Sleep to use something like: UPDATE_RATE - (Program.TimeInMillis - startTime) which isn't making my gameloop accurate.

I hope everything is clear enough, if you need more information just leave a comment.

tl;dr - Is there any way to let a thread wait for an amount of microseconds/nanoseconds in C#? My gameloop needs to be accurate and milliseconds isn't accurate enough for me.

Thanks in advance!

kevintjuh93
  • 1,010
  • 7
  • 22
  • 2
    use `Timers` rather than `Threads` – harishr Sep 29 '15 at 07:19
  • 2
    Why says `Thread.Sleep()` consumes a high amount of CPU? –  Sep 29 '15 at 07:19
  • @entre Are timers more accurate, I think I can override the `Timer` class to make it work with nano/micro-seconds – kevintjuh93 Sep 29 '15 at 07:20
  • Timers are non-blocking, regarding nano/micro-seconds read its docs, i am not sure about it – harishr Sep 29 '15 at 07:20
  • @Roy Because I need my loop to be really accurate. So I need to make it `Sleep`, but not for long so it won't run longer than `UPDATE_RATE`. `Thread.Sleep(0) consumes a lot of CPU and `Thread.Sleep(1)` uses less, but makes my gameloop not accurate enough. – kevintjuh93 Sep 29 '15 at 07:21
  • @entre I did read it, it's all milliseconds though. – kevintjuh93 Sep 29 '15 at 07:22
  • @KevinKal _"Thread.Sleep(0) consumes a lot of CPU and Thread.Sleep(1)` uses less"_ - That's incorrect. The values may cause your thread to suspend longer than the other but it does not mean Task Manager will spike out. One does not imply the other –  Sep 29 '15 at 07:23
  • @Roy I tested it and it does, else I wouldn't be asking this question. But if you can show me examples where it doesn't consumes a lot, I would be glad to see them. – kevintjuh93 Sep 29 '15 at 07:24
  • @KevinKal Sure. Add `Thread.Sleep(10000)` to a WinForms app. My app is suspended but at no point does Task Manager spike out due to _"a lot of CPU"_. –  Sep 29 '15 at 07:26
  • @Roy ofcourse it doesn't when sleeping for such a long time. But my timer needs to be accurate so it's in a `while loop`. Check my code. It calls `sleep` a lot, till it reached the `UPDATE_RATE`. That's accurate, but it consumes CPU. (Around 25%-30% CPU) – kevintjuh93 Sep 29 '15 at 07:28
  • possible duplicate of [Thread.Sleep or Thread.Yield](http://stackoverflow.com/questions/11480912/thread-sleep-or-thread-yield) –  Sep 29 '15 at 07:28
  • _[Thread.Yield will interrupt the current thread to allow other threads to do work. However, if they do not have any work to do, your thread will soon be rescheduled and will continue to poll, thus 100% utilization of 1 core. Given the choice between the two, Thread.Sleep is better suited for your task. However, I agree with the comment from @Bryan that a Threading.Timer makes for a more elegant solution](http://stackoverflow.com/questions/11480912/thread-sleep-or-thread-yield)_ –  Sep 29 '15 at 07:29
  • @Roy it happened BEFORE adding the `Thread.Yield` I will remove it as it has actually nothing to do with my question. And it's not a duplicate at all -_- – kevintjuh93 Sep 29 '15 at 07:30
  • How is it if you replace TimeSpan.FromTicks(1) with just 1 for the single millisecond? – Paul Hodgson Sep 29 '15 at 07:32
  • @PaulHodgson it's better CPU wise, but my gameloop becomes really unstable. As it's not accurate anymore. – kevintjuh93 Sep 29 '15 at 07:34
  • 5
    Generally a game loop runs _flat-out_ without any form of sleep/yield or timer. It is during each frame that the game determines _how much time_ has elapsed in order to determine deltas to move game objects by. That's how we do things in XNA and Unity3D. A game with reasonable level of scene detail won't require nanosecond accuracy so I would not bother with timers either –  Sep 29 '15 at 07:36
  • TImeSpan.FromTicks(1) gives you a really high resolution it's pretty much the same as saying don't sleep hence the crazy load on the CPU. As Roy says you are best to run flat out and modify the game logic to reflect on elapsed time. – Paul Hodgson Sep 29 '15 at 07:41
  • Yeah, but I need mine to `Hibernate/Sleep`. It has to be accurate because the logic contains `time/timers` that need to be correct. – kevintjuh93 Sep 29 '15 at 07:41
  • @PaulHodgson well I am coding `Clash of Clans` for Windows. And that's not how `Clash of Clans` work, I need it to be like this :| Btw I don't think it gives me a high resolution, I think it is converted to `Sleep(0)` as `Sleep` uses milliseconds (int32). – kevintjuh93 Sep 29 '15 at 07:42
  • 2
    most (all) games are written this way. – Paul Hodgson Sep 29 '15 at 07:45
  • Well believe me, `Clash of Clans`, isn't like that. It has methods like `SetHibernate`. And because it's written in `Java`, which allows the use of nanoseconds when `Sleeping` it's accurate enough. So is there ANY way in C# to do a `Sleep/Wait/Hibernate/` using nano/microseconds/ticks?? – kevintjuh93 Sep 29 '15 at 07:47
  • I just read something about `SpinWait` going to check it out. – kevintjuh93 Sep 29 '15 at 07:55
  • 2
    I think the problem is that you haven't sufficiently described what it is you are trying to do. Instead of fixating on the numerous wait functions, you should instead explain _why you need hibernate_ functionality in the first place. I mean, if you want to suspend your game, break out of the game loop. Job done. That will work regardless of your game loop design –  Sep 29 '15 at 08:02
  • Well it's because I am porting this game to C#. Looking at the decompiled code it hibernates to let it run at `1/60ms`. It is required for all of the gamelogic and also for the checksums that are sent to the server. I need to simulate everything as good as possible. – kevintjuh93 Sep 29 '15 at 08:14

1 Answers1

9

You need hardware support for this kind of accuracy. A signal that generates an interrupt and trigger code to get the job done. There are two obvious candidates for such a signal in Windows.

The first one is the VSYNC interrupt, a video device driver implementation detail. It occurs at the monitor refresh rate, usually 60 Hertz for LCD monitors. Just what you are asking for of course, no coincidence. Most programmers use DirectX to implement game graphics, you ask it to use the signal with the D3DPRESENT setting. Otherwise very unclear why you are not using it from the question, but the solution you should pursue.

The second one is the clock interrupt. That's the one you are complaining about. A big deal on Windows, that's the signal that wakes up the thread scheduler and gets code to run. It is directly responsible for the accuracy of Thread.Sleep(). Code that sleeps cannot start running again until the clock interrupt occurs and the scheduler puts the thread back into the active state. There's no other way to do it, the processor is physically turned off with the HLT instruction, consuming no power, and can only be woken up with an interrupt. By default, the clock interrupt ticks 64 times per second. Once every 15.625 millisecond. It tends to get tinkered with by drivers and whatnot, they often reprogram it to tick at 10 millisecond. Companies that give out free software and have a stake in making their own products look good make it as low as 1 millisecond.

Which is what you need to do as well if you can't tame DirectX for some reason. Pinvoke timeBeginPeriod(1) to get 1 msec accuracy. Technically you can go as low as 500 microseconds with NtSetTimerResolution() but that's a low as the dial will go. And do keep in mind that you can't sustain such rates consistently, thread scheduling quanta, garbage collection pauses, hard paging faults and obnoxious device drivers that run their code at realtime priority take much longer than that.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Well it's a lightweight (clientless) version of the game. That's why it has to be accurate but not cpu consuming. Can't test any of the posted things at the moment, but I will take a look at all of it. Also it doesn't matter that's not not always exactly `UPDATE_RATE`, but it must NOT be off by more than 0.5ms, which doesn't occur when using `Sleep(0), but does occur with anything else. My NanoStopwatch checks for `HighRes` to ensure that I can get a more accurate time. What do you suggest to make my gameloop execute every `1/60d` milliseconds. Consuming not much CPU and being accurate enough. – kevintjuh93 Sep 29 '15 at 08:08
  • @KevinKal I wonder what happens in the original game in java when the GC steps in? Or on devices not efficient enough to sustain the required framerate? – dureuill Sep 29 '15 at 08:20
  • @kevintjuh93 _"I will check the decompiled code again"_ - why are you decompiling their game anyway? _"[You agree that you will not, under any circumstances...Reverse engineer, decompile, disassemble, decipher or otherwise attempt to derive the source code for any underlying software or other intellectual property used to provide the Service or any Supercell game, or to obtain any information from the Service or any Supercell game using any method not expressly permitted by Supercell](http://supercell.com/en/terms-of-service/)"_ –  Sep 29 '15 at 08:59