timeSetEvent and graphics

Windows specific questions.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

timeSetEvent and graphics

Post by deltarho[1859] »

Having suggested timeSetEvent at the end of the 'Extremely slow console/Graphics program' thread I wondered how it would fair in other graphics code.

I don't know why I considered this because graphics is not one of my comfort zones. Fortunately, one of dodicat's comfort zones is graphics so I unashamedly 'pinched' his code from here.

When I had finished, the CPU load of the two approaches was pretty much the same; about 0.75% I was hoping to see my code with a smaller load. I decided that my code was not worth posting.

However, I could't get a very high frame rate from dodicat's code unless I reduced the System clock's timer interval. I did this by using my 'TimerInterval (Win10)'.

The reason for posting is that I had 'TimerInterval (Win10)' running when testing my code. If the resolution, second parameter of timeSetEvent, was set to less than 16 then the System clock's interval was set to the same value.

At MSDN the resolution is described as
Resolution of the timer event, in milliseconds. The resolution increases with smaller values; a resolution of 0 indicates periodic events should occur with the greatest possible accuracy. To reduce system overhead, however, you should use the maximum value appropriate for your application.
No mention of the System clock.

At the PowerBASIC forum very nearly everyone uses a resolution of zero. There are some where the resolution is not zero and greater than 15.625ms. I have used resolutions greater than 15.625ms where the delay was large; 60000 for example. If the resolution is linked to the System clock then using a resolution in excess of 15.625ms will not not make a blind bit of difference because 15.625ms is as good as it can get.

With a resolution of zero I expected an interval of 0.5ms because my system will accommodate that. That did not happen - an interval of 1ms was used.

Some of you may have known that the resolution was linked to the System clock but it was news to me - we are after all using a multimedia timer. With speeds less than 16ms, but keeping the resolution at 16ms, the graphics certainly speed up and the System clock's interval is not changed. However, the graphics is a bit flaky and on some occasions the application stopped responding. To avoid that I introduced

Code: Select all

If speed < 16 Then resolution = speed\2 
The default resolution is set to 16 and will now get reduced to speed\2 if speed < 16.

With 'speed = 1' then the resolution will be set at zero but we will get 1ms. Sometimes the screen locks up - we need a better resolution. So, speed is limited to 2ms. With a speed of 2 and resolution of 1 the graphics move pretty much as the fastest speed I can get from dodicat's code. The CPU load for both codes is now at about 1.25%

Somebody else must know that the resolution is linked to the System clock. It took a while but I found an article at Code Project which had a link to an old blog which said "timeSetEvent will call timeBeginPeriod to set the timer resolution to the resolution specified in the call to timeSetEvent." However, the blogger did not mention where he got that from. The blog mentioned that timeSetEvent was obsolete - the blog was written 12 years ago. Oh, dear. <laugh>

So, there we have it, further evidence of the Windows APIs 'dumbing down' on details.<smile>

So, does timeSetEvent have role to play in graphics code? Well, since I am not a graphics guy I am not qualified to make a judgement and really should not have stuck my nose in. I am going to hazard a guess and say no. If any graphics members reckon that it is a bad idea I should tell you that I already have a towel ready to throw into the ring. It was an interesting exercise and I learned something about timeSetEvent; even though it was by accident. I wonder how many of those at PowerBASIC, if any, realise that by setting a resolution of zero the System clock's timer interval is changed to 1ms.

For what it is worth here is the code.

Code: Select all

#include once "windows.bi"
#Include Once "win/mmsystem.bi"
 
Dim As Uint speed, resolution
Dim TimerID As Uint_Ptr
 
Sub DisplayProc( uTimerID As Uint, uMsg As Uint, dwUser As Dword Ptr, dw1 As Dword_Ptr, dw2 As Dword_Ptr )
Static x As Long = 10
Static y As Long = 10
Static dx As Long = 1
Static dy As Long = 1
  x += dx : y += dy
  If x<10 Or x>630 Then dx = -dx
  If y<10 Or y>470 Then dy = -dy
  Screenlock
  Cls
  Circle(x,y),10 'draw the ball
  Screenunlock
End Sub
 
Screen 18
speed = 16 ' Reduce to speed up
If speed < 2 Then speed = 2
resolution = 16
If speed < 16 Then resolution = speed\2
 
TimerID = timeSetEvent( speed, resolution, Cast( LPTIMECALLBACK, @DisplayProc ), 0, TIME_PERIODIC )
 
Sleep
 
timeKillEvent TimerID
 
Sleep
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: timeSetEvent and graphics

Post by dodicat »

If I set your speed to 1 I get about 500 fps.
Here is the framecounter I use:

Code: Select all

 Function framecounter() As Integer
    var t2=timer
    Static As Double t3,frames,answer
    frames=frames+1
    If (t2-t3)>=1 Then
        t3=t2
        answer=frames
        frames=0
    End If
    Return answer
End Function 
But I get much flickering.(Try draw string (20,20), "" &framecounter after cls in your code.
Maybe screenlock/screenunlock is unsuitable with the api threading as it is with fb threading?
Here is a freebasic threading but using screenset and flip.

Code: Select all

declare function settimer       alias "timeBeginPeriod"(as Ulong=1) as long
declare function freetimer      alias "timeEndPeriod"  (as Ulong=1) as long
dim shared as long MySpeed

Function Regulate(Byval MyFps As long,Byref fps As long) As long
    Static As Double timervalue,_lastsleeptime,t3,frames
    frames+=1
    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    _lastsleeptime=sleeptime
    timervalue=Timer
    Return sleeptime
End Function

Sub DisplayProc(p as any ptr)
screenset 1,0
Static x As Long = 10
Static y As Long = 10
Static dx As Long = 1
Static dy As Long = 1
static as long fps,sleeptime,z=1
while z
  if inkey=chr(27) then z=0
  sleeptime=regulate(MySpeed,fps)
  x += dx : y += dy
  If x<10 Or x>630 Then dx = -dx
  If y<10 Or y>470 Then dy = -dy
  Cls
  Circle(x,y),10 'draw the ball
  draw string(20,20),"Framerate = " &fps
  draw string(200,200),"Sleep " &sleeptime &" , 1"
  flip
  settimer
  sleep sleeptime,1
  freetimer
  wend
End Sub


screen 18,,2

Dim  thread As Any Ptr


MySpeed=400  '<   set framerate


thread = ThreadCreate(@displayproc,0)
sleep
ThreadWait (thread)




 
Which avoids flickering (here anyway)
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

dodicat wrote:But I get much flickering.
Same here. I hadn't noticed that with just the ball flying around.

With the second code using 'MySpeed=1000' I am topping out at about 750 fps without any apparent flicker and sleeptime with its back up against the wall at 1.

Thanks for that.

Another idea bites the dust. <smile>

Anyway, at least if I use timeSetEvent for non-graphic work, which I have done in the past, I now know more about the second parameter, uResolution, than MSDN tells us.

PS

Code: Select all

Declare Function settimer Alias "timeBeginPeriod"(As Ulong=1) As Long
I could not believe what I was looking at.

Checked the manual out for Alias and there was the answer in the Example.

That is totally mind boggling. <Wow>
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

Yours truly wrote:That is totally mind boggling. <Wow>
It was!

Just tried in other code and got a failure.

Stripped it down to just this.

Code: Select all

Declare Function settimer Alias "timeBeginPeriod"(As Ulong=1) As Long
Declare Function freetimer Alias "timeEndPeriod"(As Ulong=1) As Long

settimer
freetimer
Sleep
and am getting

Code: Select all

undefined reference to 'timeBeginPeriod@4'
undefined reference to 'timeEndPeriod@4'
I have seen that flaming @4 before.

The only difference in usage is that settimer and freetimer are in a primary thread of execution in the above.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: timeSetEvent and graphics

Post by fxm »

If you have none graphic keyword in your code (neither #include "fbgfx.bi"), try to add at head:
#inclib "winmm"
Last edited by fxm on Oct 20, 2017 22:03, edited 1 time in total.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: timeSetEvent and graphics

Post by dodicat »

If you get that pulling functions, be a little more specific

Code: Select all


declare function settimer  lib "winmm" alias "timeBeginPeriod"(as Ulong=1) as long
declare function freetimer lib "winmm" alias "timeEndPeriod"  (as Ulong=1) as long

settimer
freetimer


Sleep
 
Normally you can pull out functions one by one by alias only.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: timeSetEvent and graphics

Post by dodicat »

Sorry fxm, didn't see your post.
Anyway, with this you can open your task manager and see the cpu usage with different framerates.

Code: Select all


declare function settimer       alias "timeBeginPeriod"(as Ulong=1) as long
declare function freetimer      alias "timeEndPeriod"  (as Ulong=1) as long

dim shared as long MySpeed

Function Regulate(Byval MyFps As long,Byref fps As long) As long
    Static As Double timervalue,_lastsleeptime,t3,frames
    frames+=1
    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    _lastsleeptime=sleeptime
    timervalue=Timer
    Return sleeptime
End Function

Sub DisplayProc(byval p as any ptr)
screenset 1,0
Static x As Long = 10
Static y As Long = 10
Static dx As Long = 1
Static dy As Long = 1
static as long fps,sleeptime
while 1
  sleeptime=regulate(MySpeed,fps)
  x += dx : y += dy
  If x<10 Or x>=630 Then dx = -dx
  If y<10 Or y>=470 Then dy = -dy
 line(0,0)-(640,480),0,bf
 line(0,0)-(640,480),15,b
  Circle(x,y),10 'draw the ball
  draw string(20,20),"Framerate = " &fps
  draw string(200,200),"Sleep " &sleeptime &" , 1"
  flip
  settimer
  sleep sleeptime,1
  freetimer
  wend
End Sub

sub inputsub
do
screenset 1,0
locate 34
print spc(41);"    "
locate 34
input "Enter a new framerate or <return> to end ",MySpeed
print spc(18);"    " 
locate 35
print "Framerate set at ";myspeed
if Myspeed=0 then end
loop
end sub


screen 19,,2
Dim  thread As Any Ptr

MySpeed=400  '<   set initial framerate

thread = ThreadCreate(@displayproc,0)

inputsub

ThreadWait (thread)

sleep




 
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

I had been to mmsystem.bi to check out timeBeginPeriod but did not check out the library referred to. Woe is me!

Thank you, both.
dodicat wrote:you can open your task manager
I am quoting CPU loads in the opening post.

I prefer Process Hacker - it knocks spots off Task Manager.

Your code reminds me of Heisenberg's Uncertainty Principle one aspect of which is just looking at something disturbs what we are looking at. The CPU load of your latest code has increased the CPU load. <smile>

Nice code all the same. <wink>
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

and, ii this case, we could use the Windows dll and be done with it.

Code: Select all

Declare Function settimer  Lib "winmm.dll" Alias "timeBeginPeriod"(as Ulong=1) as Long
Declare Function freetimer Lib "winmm.dll" Alias "timeEndPeriod"(as Ulong=1) as Long
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: timeSetEvent and graphics

Post by dodicat »

I notice that in Win32API.inc (Power basic) , you have exactly that (except dword as long).
So in powerbasic you can pull out these functions also. (Am I right?)
It is such a large file, it is interesting to browse.
Heisenberg might notice that I am not only looking, but painting a rectangle and drawing more dos fonts running task manager and accessing two procedures.
So it's a bit heavy handed for delicate quantum measurements.

Is there a function or method of finding the cpu usage via the api?
I am looking around but no luck as yet.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

dodicat wrote:So in powerbasic you can pull out these functions also. (Am I right?)
PowerBASIC is a different kettle of fish in this respect. Here is a simple example using timeBeginPeriod.

Code: Select all

#Compile exe "test.exe"
#Dim All
#Include "Win32API.inc"
#Include "C:\PBWin\Timers\MultipleTimersQPC.inc"
 
Function PBMain
 
  InitializeTimers
 
  timeBeginPeriod(1)
  StartTimer(0)
  Sleep 1
  StopTimer(0)
  Print sTimeTaken(0,3,0)
  Waitkey$
 
End Function
PB's Timer is linked to the System clock so I use my own high resolution timing via MultipleTimersQPC.inc.

As you can see, with Windows APIs there is no 'pulling' - I just type in the API - job done. There are some functions which will not come in via Win32API.inc in which case we would need to refer to the appropriate inc file. Instead of PBs include files we can use José Roca's header files.

The above compiles to about 20KB.
Heisenberg might notice
I have some bad news for you - he died in 1976.
Is there a function or method of finding the cpu usage via the api?
I have not checked this out but I think CPU Usage is buried in it somewhere: NtQuerySystemInformation

Flaming heck, that is some API - put the kettle on!

With regard a per process then I would have to do some digging on that one.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

@dodicat

Have you heard this one: "You can ask a mathematician to show you how to do something but don't ask him to do it"

No. Never mind.
CPU usage is CPU time divided by real time. The GetThreadTimes and GetProcessTimes functions give you that information.
Chances are that is what Task Manager and Process Hacker are doing.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

@dodicat

I am closing in.

Use PerformanceCounter to get CPU Usage
The code sample demonstrates how to use the PerformanceCounter to track the CPU usage of the system or a certain process.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: timeSetEvent and graphics

Post by deltarho[1859] »

@dodicat

Performance Monitor can examine a process.

Go to the Performance Monitor. Right-click on the graph and select "Add Counters".

In the "Available counters" list, open the "Process" section by clicking on the down arrow next to it. Select "% Processor Time" (and any other counter you want).

In the "Instances of selected object" list, select the process you want to track. Then click on "Add >>". Click on OK once you have what you need.

This is what I got with your FB threaded code.

Image

I wasn't quick enough with my screen grabber and, as you can see, at the left the next time slice was starting. <old age> There is the facility to save an image - perhaps I should have used that.

The first part of the graph is with your code running at 400fps. At the arrow I dialed in 800fps and got about 750fps. It is quite clear that the processor time has increased with 750fps.

I have two monitors so I was able to 'play' with your app on my primary monitor and watch the results on my secondary monitor.

I am on Win10 but the above will work on Win7 as well.

Nice one Microsoft. <smile>
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: timeSetEvent and graphics

Post by dodicat »

Deltarho[1859]
I had a bash at getting the cpu usage into the threading thing.
It's not too bad here, but probably only here.

Code: Select all


type DataType as longint
declare function  GetSystemTimes alias "GetSystemTimes"(as DataType ptr,as DataType ptr,as DataType ptr) as long
declare function settimer       alias "timeBeginPeriod"(as Ulong=1) as long
declare function freetimer      alias "timeEndPeriod"  (as Ulong=1) as long
shell "echo %NUMBER_OF_PROCESSORS% >tmpcores.txt"
Function loadfile(file as string) as String
   var  f=freefile
    Open file For Binary Access Read As #f
    Dim As String text
    If Lof(1) > 0 Then
      text = String(Lof(f), 0)
      Get #f, , text
    End If
    Close #f
    return text
end Function
dim shared as long cores
cores=valint(loadfile("tmpcores.txt"))
kill "tmpcores.txt"

dim shared as long MySpeed

function cpu_usage() byref as long
static as DataType last_idleTime
static as DataType last_kernelTime
static as DataType last_userTime
static as long x
static as long cpu
static as long lastcpu
dim as DataType kerneltime,usertime,idletime
if GetSystemTimes(@idleTime,@kernelTime,@userTime ) then
var usr =(usertime)-(last_usertime)
var kerl =(kerneltime)-(last_kerneltime)
var idl =(idletime)-(last_idletime)
var sys = kerl + usr
dim as long i=int( (sys-idl)*100 /sys )
 if i>0 then x = i 
 cpu=((x/cores)+lastcpu)/3
 cpu_usage= cpu
end if
last_usertime=usertime
last_kernelTime=kerneltime
last_idleTime=idletime
lastcpu=cpu
end function

Function Regulate(Byval MyFps As long,Byref fps As long) As long
    Static As Double timervalue,_lastsleeptime,t3,frames
    frames+=1
    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0
    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000
    If sleeptime<1 Then sleeptime=1
    _lastsleeptime=sleeptime
    timervalue=Timer
    Return sleeptime
End Function

Sub DisplayProc(byval p as any ptr)
screenset 1,0
Static x As Long = 10
Static y As Long = 10
Static dx As Long = 1
Static dy As Long = 1
static as long fps,sleeptime
while 1
  sleeptime=regulate(MySpeed,fps)
  x += dx : y += dy
  If x<10 Or x>=630 Then dx = -dx
  If y<10 Or y>=470 Then dy = -dy
 line(0,0)-(640,480),0,bf
 line(0,0)-(640,480),15,b
  Circle(x,y),10 'draw the ball
  draw string(20,20),"Framerate = " &fps
   draw string(20,50),"CPU = " &cpu_usage & "%"
  draw string(200,200),"Sleep " &sleeptime &" , 1"
  flip
  settimer
  sleep sleeptime,1
  freetimer
  wend
End Sub

sub inputsub
do
screenset 1,0
locate 34
print spc(41);"    "
locate 34
input "Enter a new framerate or <return> to end ",MySpeed
print spc(18);"    " 
locate 35
print "Framerate set at ";myspeed
if Myspeed=0 then end
loop
end sub


screen 19,,2
Dim  thread As Any Ptr

MySpeed=400  '<   set initial framerate

thread = ThreadCreate(@displayproc,0)

inputsub

ThreadWait (thread)

sleep




  
Post Reply