Process/Thread Priority Findings

BlitzMax Forums/BlitzMax Programming/Process/Thread Priority Findings

Grey Alien(Posted 2006) [#1]
OK, I've done quite a bit of research and testing into process/thread priorities in Windows. Basically some people's PCs don't run Max games too well when they have background tasks eating up CPU time (they get "catchup" jerks when using delta timing/fixed rate logic).

So anyway, you can boost the Process Priority Class and/or the Thread Priority. However, if you boost them too much the combined value can make Windows unstable (according to the docs) and there is a table on MSDN showing you what the combined values are.

Anyway, I'm going to boost the full-screen mode to a "safe" level as a user who reported jerks has now said it's basically fine. I'll make another demo at some point soon for wider testing to see if anyone notices anything wierd which could be related to boosting the process and thread to Above Normal.

My question is, should I boost windowed mode too? I was thinking of boosting it the same amount for smooth windowed games, or should I assume that the user knows that windows mode won't be as good as full-screen for action games? I'm not sure the user *will* know this to be honest. I could put it on the options page, but then they'll never find it and activate it (imho). Anyway, I've only boosted it a tiny bit, which seems to be good for fighting back against other "hoggy" programs, and Windows still works totally fine on my PC.

And no my framework is not event driven, it's loop driven and the windowed mode loop has Delay(1) in it. Oh and windowed mode doesn't use MaxGUI it's just Graphics with some API calls.


ImaginaryHuman(Posted 2006) [#2]
I would think that the Delay(1) would be serving much the same purpose that increasing the priority would achieve?


Grey Alien(Posted 2006) [#3]
how do you figure that. If anything Delay(1) would mean the game loses *more* CPU time. Anyway, boosting the priority worked in tests (i.e. removed jerks) even with Delay(1) in the main loop.


Robert Cummings(Posted 2006) [#4]
UT2004 does not boost priority at all yet is unaffected by stutter. Sounds like you are on the WRONG track.


Grey Alien(Posted 2006) [#5]
blah blah, you said that before ;-) It's hard to tell with 3D games anyway. But it might not be me on the wrong track but something in the way BlitzMax handles it's threads or graphics drawing or something. Anyway, the guy who had stutters said they went away when he tried my priority boost test. But yeah, maybe I'm suppose to do it all as wait even/MaxGUI etc instead and that will solve it. However, it's not going to solve it for games where VSync is 0 and they try to render as fast as possible is it?


DStastny(Posted 2006) [#6]
I am pretty sure I know what the problem is but since I am unsure of how your framework is constructed its hard for me to guess.

But the standard BMAX keyboard input routines will cause that bouncy behavior you are seeing. If using event style coding it should allievate the problem.

If you look at the source behind the Keyboard input routines it excessivly process the windows message loop. Your program is yielding to OS to often which is problably source of your problems.

For example:
SuperStrict
Local F1:Int=False
Local F2:Int=False
Graphics 640,480
While Not KeyHit(KEY_ESCAPE)
	Cls
	If KeyHit(KEY_F1) F1= Not F1
	If KeyHit(KEY_F2) F2= Not F2
	If F1 DrawText "F1 is on",0,0
	If F2 DrawText "F2 is on",0,10
	Flip
Wend


In this simple example there is 3 calls to KeyHit if you look at source to KeyHit
Function KeyHit( key )
	If autoPoll PollSystem
	Local n=keyHits[key]
	keyHits[key]=0
	Return n
End Function


Notice how it cals PollSystem that yields to the windows message loop 3 times. That is probably cause of your studdering. Your looking in wrong place with messing with priorities.

If you use the newer event style programming it will make your problems go away.

With windows your program wont release its timeslice until you call PeekMessage. Notice how while true wend behaves and eats 100% of your CPU.

Only way I would see to fix it is to somehow hack the PollInput Module to expose autopoll and set it to false and then call Pollsystem once per frame. it will update all the state variables for your input.

Hope this helps you find your timing problem but I suspect is systemic to BMAXs attempt to remove the concept of event based model but unfortunatly that is what windows is.

Doug Stastny


Robert Cummings(Posted 2006) [#7]
Budman is correct. I spoke with a some pretty top programmers and they said the same thing - windows messaging and stuff like that can often really hit a program hard.


Ross C(Posted 2006) [#8]
About the delay command. As i understand, you are giving other apps a "window" of CPU time, at the moment you call it, so you can specify when they get this time.


Grey Alien(Posted 2006) [#9]
Budman: very interesting thanks. I've been suspicious it's something like that. I've even got written "Implement event-based timing to see if it's smoother?" on my to do list!

OEJ: OK, OK, I'll look into it I spose ;-) could screw up the framework a bit though, sigh.

Ross C: I'm calling delay only in windowed mode and right after the Flip command. Some people still get jerks though so it doesn't help. Admittedly it's not many people now at all.


DStastny(Posted 2006) [#10]
Try this

Rem
  Replacement driver for polled input hooks flip command process the windows messages for input
end rem

SuperStrict
Function FlipHookEvent:Object( id:Int ,data:Object,context:Object )
	TExtendedWin32SystemDriver(context).FixPoll() 
	Return data
End Function


Type TExtendedWin32SystemDriver Extends TWin32SystemDriver
	Method Poll()
	End Method
	Method FixPoll()
		super.Poll()
	End Method
	Method New()
		AddHook FlipHook,FlipHookEvent,Self	
	End Method
	Method Delete()
		RemoveHook FlipHook,FlipHookEvent
	End Method	
End Type


BRL.System.Driver=New TExtendedWin32SystemDriver 
' Add above to initialize your framework

' simple test program. Now only yields once per frame
Local F1:Int=False
Local F2:Int=False
Graphics 640,480
Flip
While Not KeyHit(KEY_ESCAPE)
	Cls
	If KeyHit(KEY_F1) F1= Not F1
	If KeyHit(KEY_F2) F2= Not F2
	If F1 DrawText "F1 is on",0,0
	If F2 DrawText "F2 is on",0,10
	Flip
Wend



Doug Stastny


Hotcakes(Posted 2006) [#11]
Higher thread priority is a good option, if it's left as an option. If you force it, I'll get mad. =]


Grey Alien(Posted 2006) [#12]
Budman: Thanks for that, very generous. However my framework customer tried that and it didn't improve the situation whereas boosting the priority does (the problem is all caused by a Yahoo weather widget, which, when closed, resolves the problem). Your theory sounded good but didn't work in practice :-( However, I might add your code anyway just because is cool :-D Thanks.

Toby: Well I'm only boosting the priority a tiny bit to a safe level so it won't cause any problems. As it's part of the framework, my framework users can choose if they want to apply it to their games or not. I may well add it to mine by default, sorry ;-p but you'll never notice except the game is moreorless guaranteed to run smoothly no matter what else the system is doing. You might choose to multi-task a game with some MPEG compression going on in the background or something, but most people won't, that's for sure. Most people have PCs filled with stupid applications and spyware consuming all the resources and I want the game to be able to run well on these systems.

Question about event-based polling: OK, fine I can react to events like keys, mouse, window events; I know this stuff well from my Delphi code. Thing is, say I have constantly moving enemies or scrolling, how can I do that with event-based coding. Do I have to set up a timer and use that? My logic runs at 200FPS, so I'd have to set up a 5ms timer and then every time it goes off, check that 5ms has truely passed and use delta time to move stuff an appropriate amount. If the timer doesn't go off due to a background task hogging resources or something, and say 10ms has passed, will the timer event occur twice in a row quickly (which is why I need to measure time between them) or just once? I'm a bit of noob with event-based in Max and scrolling etc, any advice is welcome, thanks :-)


DStastny(Posted 2006) [#13]
Ah another Delphi programmer do a bit of that myself. Real job :).

Interesting the Yahoo widget is such a CPU hog.


In delphi you hook the application idle process to do your looping. In bmax you can code that easy enough.

Bah timers you should calculate that yourself and run full speed just like your are doing now.

SuperStrict
Global F1:Int=False
Global F2:Int=False
Global F1X :Float=0
Global F2X :Float=0
Global LastTime:Int
Const PIXELSPERSEC:Float=10




Graphics 640,480
Function ProcessInput:Int()
	While PeekEvent()
		PollEvent()
	  	Select EventID()
		Case EVENT_KEYDOWN
	  		If EventData()=KEY_F1 F1= Not F1
			If EventData()=KEY_F2 F2= Not F2		
			If EventData()=KEY_ESCAPE Return False
		Case EVENT_APPTERMINATE
		  Return False
		End Select
	Wend 
	Return True
End Function

  
LastTime=MilliSecs()
While ProcessInput()
	Cls
	' move the text if on at 10 pixels per sec
	Local dt:Int
	dt=MilliSecs()-LastTime
	' if change in time since last frame
	If dt>0
		LastTime=MilliSecs()
		Local dx:Float = dt * PIXELSPERSEC/1000
		F1X:+dx
		F2X:+dx
		If F1X>640 F1X=0
		If F2X>640 F1X=0
	End If
	If F1 DrawText "F1 is on",F1X,0
	If F2 DrawText "F2 is on",F2X,10
	Flip
Wend


Hope that helps give you some ideas. Obviously you can incoporate this into a class framework. I have done a simple one that is named TApplication funny enough :)

Doug Stastny


Grey Alien(Posted 2006) [#14]
Budman: Delphi is my day job too, since 1996! Although I'm doing much more games programming these days in Blitz.

Anyway thanks very much for the code snippet, I get how that works, and didn't want to user Timers anyway. The thing is that isn't the app still consuming 100% CPU time because when PeekEvent is false, the function exits and the main while loop still goes round and round at full speed. This may defeat the point of letting other threads get their little time slice. Although I haven't tested it on this other person's PC yet (it will take a bit of integrating).

In fact I tested it full-screen and in windowed mode and checked out the CPU use in Task Manager (it was 50% in full-screen mode (I have a HT P4) and 20% in windowed mode), then I altered the code so that the main While loop says While not KeyDown(ESCAPE) instead (i.e. no even-based polling) and the CPU use is the same, it doesn't get worse. So is the event-based stuff giving the OS any more time? I'm not sure it is...

When I originally mentioned event-based games, I was imagining the use of WaitEvent so that when no events are happening the game is completely idle and the OS has all the CPU time it wants. However, I knew that to keep scrolling going an event would need to be generated fairly regularly and a timer might do that, or even a hook to VSync. So I'm still a bit


Grey Alien(Posted 2006) [#15]
Budman: more info: I tried changing ProcessInput to this:

Function ProcessInput:Int()
	  	Select WaitEvent()
		Case EVENT_KEYDOWN
	  		If EventData()=KEY_F1 F1= Not F1
			If EventData()=KEY_F2 F2= Not F2		
			If EventData()=KEY_ESCAPE Return False
		Case EVENT_APPTERMINATE
		  Return False
		End Select
	Return True
End Function


and I stuck CreateTimer(200) before the main loop.

This works too, it scrolls etc. However in full-screen mode, if I bring up task manager, CPU use drops to 0! This must mean the timer isn't being activated. In windowed mode though, the CPU use fluctuates a lot more but I wouldn't say it was really any less than your code using PeekEvent, hmm...

I wonder what BMax does internally with WaitEvent ... is it looping or does Windows handle the waiting? In theory the WaitEvent with a fast timer for the scrolling etc should be the best option as it leaves max time for the CPU. Although I'm still not clear what happens if the timer missed a few ticks due to lots going on, I guess it would queue several events, which is why using a delta time is important.


DStastny(Posted 2006) [#16]
Hi Grey, Delphi for me since Delphi 2 for Win32. Prior to that C++, turbo pascal...way way back. Now I am Director of R&D for development company so I get to play with different technologies, and make real money .Net and such for buisness apps.

BMAX is my side fun.

WaitEvent waits on system mutex for event to window. Not good idea for games. The Timer if I remember posts a high resolution event and wakes up the mutex so its should still be running. However no drawing happens since the DirectX context is disabled so all the drawing NOOPS so the loop just spins faster and spends more time in WaitMessage.

As for PeekEvent

Yes its still consuming 100% thats not a bad thing. People have this idea thats bad no its not. its bad when your program doesnt yield. By calling PeekMessage your are yielding to the OS. This is where the task switch will then then give any other process with same priority its slice of the CPU pie. You dont need the delay command for windows apps to try and yield. calling Peekmessage yields.

ie. If you have two programs using 100% like this they will each get 50% as they will usually swap at the PeekMessage. I say usually as the OS is preemtive so a high priority process like a network driver will still preemt the execution. For example the task manager. The UI gets klunky when you dont yield but the OS is still chugging away.

Since potential for yield is at PeekEvent which invokes PollSystem by minimizing the number of calls to BMAX commands that call Pollsystem ensures that your app is getting enough CPU.

I have been playing with DirectX forever and never had the problem on my machine you describe. Although I dont load a lot of crap on my machine, ie. widgets Thing is I do run stuff like database servers and such in background and they usually dont seem to interfere, unless a major query kicks in. But even then my frame rate drops but my program doesnt pause.

If you could demonstrate a stripped down program without your framework that demonstrates the problem, we might be able to track down the cause. It might be bad messagepump under the covers or bad handling of a message by BMAX. I have visually looked at them and they look ok.

Does problem exists with DirectX or the OpenGL driver?

FYI this widgets and virus scanners are notorius problems with systems from a support perspective as they can,
1. Cause unexpected slowdowns
2. Monitor your webactivty
3. Cause system instablity

Mabye your client needs to run spybot and see if he has anything nefarious on his machine.

Have you had more than one person report this? If I install this Yahoo thing will I duplicate problem. I have hyperthread PIV so I doubt it.

You might want to mess with priority although I dont think I have ever seen that as solution to timing problems games usually say in documentation shut that crap off. TDUMP some of your favorite games. Do they import SetPriorityClass


Doug Stastny


Grey Alien(Posted 2006) [#17]
Hi Budman: Yeah I started with Delphi 2 as well, even did a bit for Win3.1, but we soon ditched that and got into Win95. I made this product and managed (not owned, sigh) the company for a few years: http://www.merlio.com For years before my Delphi job I was bedroom coder (Spec, C64, BBC, Amiga) in Basic, assembly, Blitz Basic 2 etc, then C++ and ASM on PCs (in DOS. I started using BlitzPlus for fun in 2004 but quickly realised I was bored with business software (despite the good money) and want to get back to the fun of making games. However, I soon discovered than finishing a game 100% is damn hard work too :-) Anyway onto the issue at hand.

Yes its still consuming 100% thats not a bad thing. People have this idea thats bad no its not. its bad when your program doesnt yield. By calling PeekMessage your are yielding to the OS. This is where the task switch will then then give any other process with same priority its slice of the CPU pie. You dont need the delay command for windows apps to try and yield. calling Peekmessage yields.
OK right, thanks for clearing that up, I know you sorta tried to say it before but I get it now.

Basically my framework runs fine on *almost* all PCs, but just a couple have some jerks in any smooth moving objects. This occurs because the timing code detects a large gap has occured in time (say >50ms i.e. more than one frame) and it catches up (due to delta time). If I don't catch up you get slow downs instead which is rubbish. Anyway I know it's not my code but outside interference from the OS or other programs (like this widget). The person with the widget reports my framework is 100% fine when it's not running so it's obvious just adding some extra load. Also his PC isn't mega fast. The point is the many end users may have lots of utils and spyware running and my games need to run fine on their PCs. This person also reports that other Blitz3D and BlitzPlus games run fine making me think that something in Max (or the way I'm using it) is yielding too much to other processes and you may have discovered this (thank ;-))

Also I haven't installed the Yahoo thing either (I don't want to lol), but I really should for testing to see if the same happens on my PC.

As for if AAA titles boost the thread I don't know. I was wondering if any of them are able to just totally cut all the OS threads off. I know in the old days you used to turn off all the system interrupts that you didn't need so you game was "happy".

I'll ditch the idea of using Waitevent and try some peekevent code and send him a copy and see what happens. Watch this space.

Thanks again.


Grey Alien(Posted 2006) [#18]
Hmm, now I'm wondering how to implement this properly. I have a TMouse object that can updated with the correct mouse button settings and coords so that it can be used throughout the code until the next change. The problem is with keys, not all my keys are read in the same place because each screen is a separate object so there can't be a global keyboard routine (actually there IS a global one as well that handles debug and engine keys). Each screen checks various KeyHit and KeyDown calls. Maybe I'm going to have to make a global routine that calls PollEvent and stores the result of keypresses in my own array of Key Status and then read this array on each of my game screens? How does that sound?

Am I correct in assuming that if PeekEvent and PollEvent is used, KeyHit and KeyDown won't return any non-zero values because they've been processed by PollEvent?


DStastny(Posted 2006) [#19]
If you want to turn off BlitzMaxs Interal management of input DisablePolledInput() will shut it down so the regular BMAX commands wont work.

You might want to consider copying PolledInput.bmx and implmenting your own. BMX would work great if you could just disable the autopoll variable but its private and there are no methods that access it or use it but its checked.

Doug Stastny


Grey Alien(Posted 2006) [#20]
I see, thanks. I'll look into that. Did you get my idea about having an array of keys so that I basically manually fill it and check it later in the code?


DStastny(Posted 2006) [#21]
I'm sorry missed that.

Thats a great idea just look at the polled input module its how it works. You could work that into your framework.

Doug Stasty


Grey Alien(Posted 2006) [#22]
cool, I'll try it today.


Grey Alien(Posted 2006) [#23]
OK I replaced all my input code with PeekEvent and PolledEvent and switched off PolledInput (and removed Delay(1) in windowed mode). It was fine on mine BUT it was not fine on the target test machine, sigh. I really had high hopes for this. I think the best solution so far was the boosting thread priority.


DStastny(Posted 2006) [#24]
Can we see some specs Speed, OS, Graphics Card, from this target machine? Maybe we are looking at the problem from wrong perspective.

The thing is you say Blitz3d apps are fine, but not BMAX.

I think you need to create the smallest program that demonstrates the problem. Something without your framework involved.

If its simple enough we can then create same program in Blitz2d or Blitz3d and see if problem persists. If it does its the machine. And from there you just tell people to disable 3rd party widgets.

If not it should be simple to figure what is different. There really isnt much to BMAX windows processing vs. Blitz except for sound subsystem and automatic Garbage collection.

Your event based input solution is still a much better solution so dont worry about the work you did being wasted.

Another thing to consider. Does your test App use the Framework command to remove BMAX subsystems that are not currently in use? Maybe your picking up something else during compile time thats interfering.

Doug Stastny


Grey Alien(Posted 2006) [#25]
Target machine is: Pentium M 1.5Ghz (it's equivalent to Pentium 2.0Ghz) with 768MB of RAM and an ATI 9200 with 64MB.

I think you need to create the smallest program that demonstrates the problem. Something without your framework involved.
I had considered this but I'm already behind with my project, I may have to just move on for now and come back to this at a later date.

And from there you just tell people to disable 3rd party widgets
And there lies a problem. How do you tell average portal game player to do that and be sure they do it. Most likely they will just play my game and this "uh this is jerky" and play another one without the same problems ...

Your event based input solution is still a much better solution so dont worry about the work you did being wasted.
Why is it a better solution? Simply because it works in harmony with windows more? Actually at the moment there are a few more things I need to code like detecting app terminate, and if the mouse is on thye titlebar etc. So I can either invest time on completing it and bug testing it OR revert to the old stable code.

Does your test App use the Framework command
No it doesn't. This could be worth considering, but I wonder what is being picked up that could cause a problem? MaxGUI or something? I probably should try to install the widget thing...


DStastny(Posted 2006) [#26]
Target machine is: Pentium M 1.5Ghz (it's equivalent to Pentium 2.0Ghz) with 768MB of RAM and an ATI 9200 with 64MB


That machine spec shouldnt cause any problems.

And there lies a problem. How do you tell average portal game player to do that and be sure they do it. Most likely they will just play my game and this "uh this is jerky" and play another one without the same problems ...



The problem you have reported is one machine. Have you had this reported on multiple machines then you have something easier to track down.

Event based is better because yes it works better with conjunction of the OS. There are couple of issues with the DX7 driver caused by the default lack of event based processing. Not to get real technical but the flip mechanism in conjunction with BeginScene and EndScene then yeilding to OS can cause some undesirible behavior with some drivers. It can be coded around but its messy.

EVENT_APPTERMINATE will tell you when application is terminated.

You can use polledinput but you will yield to OS more often and as such have slower performance.

As for Framework off there is lots of code in BMAX that gets autoinitialized. Might be something with the network code. Since the Yahoo widget is checking the network maybe there is some conflict there.

I can understand how frustrating tracking down pblems like this can be but unless you have a concrete simple example the problem can be anywhere.

Im just trying to help as you want to change the priority but really thats not the solution. Finding the real cause of the problem would be better.

Maybe you just need to start collating some of these machines with issues.

Doug Stastny


Grey Alien(Posted 2006) [#27]
Budman: thanks for the input again. I fixed most of the event-based issues last night and have a few more tweaks to make, then I'll run with it as it does sound better. It's interesting to hear about the DirectX 7 issues, perhaps this is where the problem lies. I may well investigate the use of Framework too...Plus if I get time I may try to make a mini sample app to test the problem. Anyway thanks.