Re: Super Precision Timer
Quote:
Originally Posted by
some1uk03
Any Ideas?
Use both.
Do you currently use Timer1_Timer event to show QueryPerformanceCounter/Frequency values?
You can use Merri or The Trick's Timer to show QueryPerformanceCounter/Frequency values.
cheers,
</wqw>
Re: Super Precision Timer
I was going through the SelfTimer everything is setup with LONG's whereas i'm dealing with 0.0013 precisions.
I've changed all LONG's to Currency but that has a major impact on speed. (not as fast as QueryPerformanceCounter).
I'll now try and implement QueryPerformanceCounter in to there.
Re: Super Precision Timer
Here's my implementation of a high-precision timer. As can be seen, I use Currency up until the last minute (when a precision timer is requested) and then I use Double. I've used it quite a bit and never had a problem with it.
I do like the idea though of combining it with the timer control (or the SetTimer API call) if we want a high precision timer on regular intervals. I know that the timer control's event will interrupt a form dragging operation, and I believe the SetTimer callback will as well.
Also, I'm not sure the use of Currency should have any major impact on speed. Currency is nothing but a 2's complement 8-byte integer with a decimal point four-from-the-right shoved into it when it's shown to the user. As such, I'd be shocked if Currency addition and subtraction wasn't done in CPU registers (which is faster than the FPU). In that little class I wrote, that final division is done as Doubles though (as VB6 converts Currency to Double for multiplication and division). But the way I've done it, there's no back-and-forth casting.
EDIT: Also, form dragging is weird. I don't believe it's done in the same thread as the application (<--- that's wrong, see next post), but it does (mostly) pause that application's thread. I don't believe the dragging is done in the same thread, or how could it be interrupted, and I certainly don't believe there's any equivalent to DoEvents in a Windows drag operation ... but maybe there is ... it would actually make some sense if there was, a loop in the thread would be paused but events would still be raised.
Re: Super Precision Timer
Now I'm convinced that's exactly what's happening ... when dragging a form by its title bar, the message pump is still monitored.
Here's some code I just wrote for a Form1 test (with a Timer1 on the form):
Code:
Option Explicit
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub Form_Load()
Timer1.Interval = 5000
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
Debug.Print "sleeping"
Sleep 2000
Debug.Print "awake"
End Sub
You'll notice that you can't drag the form during the Sleep cycle. That suggests it's all in the same thread.
EDIT: Ahhh, and you can't drag a form if/when the application has the thread. Just put a Sleep in Form_Activate to test this. You can't drag the form until that Sleep expires.
1 Attachment(s)
Re: Super Precision Timer
Ok I'm attaching a short demo of bother classes. Mine vs Merri's.
You'll see the execution speed difference.
Also try and drag the form around and you'll see that Merri's timer continues to execute whilst myTimer pauses.
So I need the execution time of myTimer and it not to pause on form drag.
Re: Super Precision Timer
You cannot "setup" a timer class by calling DoEvents in a VB6 loop. The freeze while dragging is the least problem you have with your current code.
Both other classes use Set/KillTimer API functions to hook an OS provided TimerProc notification.
If you want to "outsmooth" these you'll need your plan B implemented: "Maybe run the class on a separate thread?" When implemented in a separate thread then a loop and DoEvents/Sleep is ok.
Btw, both API based classes bring nothing better that the builtin Timer control in terms of smoothness with only benefit that because these are implemented in classes they do not need a form to site the Timer control on i.e. classes can be used "headlessly" from other classes which is handy (but ofthen not crucial).
cheers,
</wqw>
Re: Super Precision Timer
I'm not sure what the final objective is, but even putting it all in a separate thread may not accomplish what you want, Some1uk03. I mean, if you've got to get some perfectly timed events back into your main thread, I'm not sure that'll be possible. Sure, the events will be perfectly timed in the "worker" thread, but the messaging (with some event) to get them back into the main thread will have the same problems you're currently having. IMHO, it's not exactly timers you're struggling with, but rather the inconsistent monitoring of the message pump, and that's going to happen no matter what.
Now, if you do it all in some worker thread, then that might work ... some thread that's headless (with no forms to drag and nothing to stop it). Then, all you'll have to worry about is the consistency of the execution timeslices that Windows gives it to execute.
EDIT: You can partially solve the cpu/core timeslice issue by bumping up the thread priority. I wouldn't bump it up above "Above Normal" or bad things begin to happen, like your mouse not working and other things.
Re: Super Precision Timer
Some time ago I experimented with getting an accurate timer. You can only get as accurate as the CPU tick counter will permit, and it is a compromise. If it is set too low, it has a negative effect on other functions.
What you can do however is lower the setting when you start the timer and restore it after. Details can be found here:
https://docs.microsoft.com/en-us/win...imebeginperiod
Code:
Option Explicit
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Long) As Long
Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long
Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long
Private Function TimerEx() As Double
Dim cFreq As Currency
Dim cValue As Currency
QueryPerformanceFrequency cFreq
QueryPerformanceCounter cValue
TimerEx = cValue / cFreq
End Function
Private Sub cmdStart_Click()
Dim Start As Double
Static Delay As Long
Dim Result(4) As Long
Dim N%
Delay = Delay + CLng(Text2.Text)
Text1.Text = CStr(Delay) & " ms Delay" & vbCrLf
timeBeginPeriod 1
Start = TimerEx
Sleep Delay
Result(0) = CLng((TimerEx - Start) * 1000)
Sleep Delay
Result(1) = CLng((TimerEx - Start) * 1000)
Sleep Delay
Result(2) = CLng((TimerEx - Start) * 1000)
Sleep Delay
Result(3) = CLng((TimerEx - Start) * 1000)
Sleep Delay
Result(4) = CLng((TimerEx - Start) * 1000)
timeEndPeriod 1
For N% = 0 To 4
Text1.Text = Text1.Text & Result(N%) & vbCrLf
Next N%
End Sub
Re: Super Precision Timer
I just tried via another method with SubClassing.
Rather than checking for Window messages, that's where i perform the QueryPerformanceCounter checks but in comparison it's just as fast as the SetTimer API. Nothing beats the execution speed of Do/Loop
Re: Super Precision Timer
QueryPerformanceCounter+doevents
Re: Super Precision Timer
another way is to not use the form controlbox, and instead use a borderless form, where u create your own caption and buttons.
I do that in my projects and the latest game, and theres no worry about freezing up.
and Im using a loop + VerticalBlankEvent, so I have my own timer, following the monitor refresh rate.
so, the loop is controlling everything, mouse, buttons, rendering, caption etc.
that way u have fully control of everything.
and no doevents, I use PeekMessage to control mouse and keyboard, that way I dont need subclassing either.
so its a all-in-one approach with many benefits.
but sure, u need to create everything yourself, from labels to buttons.
Re: Super Precision Timer
Quote:
Originally Posted by
Elroy
I'm not sure what the final objective is, but....
Final objective is to have a constant Loop/Feedback/monitoring.
Quote:
Originally Posted by
xiaoyao
QueryPerformanceCounter+doevents
That's the current problem. Doevents causes a pause/hang/freeze on form drag.
-----------
My next test, was to move the class and turn in to an ActiveX.dll which I thought would run in it's own thread, but it looks like nothing has changed. How do we get this to run on it's own thread?
Re: Super Precision Timer
Quote:
Originally Posted by
some1uk03
Doevents causes a pause/hang/freeze on form drag.
Avoiding the built-in dragging-support of the system-rendered Form-Titlebar, is the only solution.
Even running your timing-trigger on a separate thread would not change that -
(not when you expect this thread to also cause "continuous visual updates" on your Main-Thread or Main-Form).
It's easy enough, to define a Form without a Titlebar
(which has your own titlebar-replacement in a PicBox or UserControl).
Olaf
Re: Super Precision Timer
Quote:
Originally Posted by
Schmidt
Avoiding the built-in dragging-support of the system-rendered Form-Titlebar, is the only solution.
Olaf
I'm currently already not using the Default system-rendered form-titlebar. Instead, I do my form dragging via:
Code:
Call ReleaseCapture
Call SendMessage(FrmhWnd, &H112, &HF012&, 0)
+ The issue is not just about dragging a form. Any form of interaction causes the same affect. Such a Menu or a button click etc....
Quote:
Originally Posted by
Schmidt
Even running your timing-trigger on a separate thread would not change that -
(not when you expect this thread to also cause "continuous visual updates" on your Main-Thread or Main-Form).
It would change it as Merris or The Tricks timer via SetTimer achieve that already? Their version does not interrupt the callback?
Re: Super Precision Timer
those api will not help. u need to create your own form-moving.
as I wrote (as u just ignore it)
you can use peekmessage and create such thing inside a loop.
will not halt/freeze anything.
and buttons/labels are very easy to create.
- PeekMessage
- Use uMsg.Message to get the window message (such as mouseup,down,keys,paint,doubleclick etc)
- u can use uMsg.hWnd to restrict a picturebox, if u want to make it easy for yourself for the titlebar
here is just check if mouse down in the right hwnd, if so, u can store it as "mouse-hold", and if u move the mouse, it will move the entire form, u can use MoveWindow API
here a list of messages (not everything works of course)
https://wiki.winehq.org/List_Of_Windows_Messages
Re: Super Precision Timer
First your own form moving code (not the WM_NCLBUTTONDOWN hack everyone and you are using) then you own custom menubar and sub-menus, can’t even imagine what next :))
The moment you yield control with DoEvent (it’s calling internally DispatchMessage which is calling other hwnds WndProc routines) this is equivalent to calling into foreign WndProc routines which might in turn spin Do/Loops like yours on their own and never yield back until user release mouse button so during this time your timer stalls.
These foreign WndProc routines do call DoEvent/DispatchMessage though in their modal loop so everything is redrawn correct and WM_TIMER notifications (originating from a worker thread) reach their TimerProc endpoints and that’s why the Set/KillTimer API based implementations continue ticking on menu or form drag.
Re: Super Precision Timer
If you are satisfied with not accurate but very tiny periods you could create a separate thread with an inifinite loop with QPC. Just use PostMessage to post notify to the main thread like that:
Code:
Private Function ThreadProc( _
ByRef tSet As tTimerSettings) As Long
Dim cFreq As Currency
Dim cCt1 As Currency
Dim cCt2 As Currency
Dim cTicks As Currency
QueryPerformanceFrequency cFreq
cTicks = cFreq * (tSet.lMicroSec / 1000000)
QueryPerformanceCounter cCt1
Do
QueryPerformanceCounter cCt2
If cCt2 - cCt1 >= cTicks Then
PostMessage tSet.hWnd, WM_USER, 0, ByVal 0&
cCt1 = cCt2 + ((cCt2 - cCt1) - cTicks)
End If
Loop While tSet.bEnable
End Function
It eats all the CPU time but for example i can maintain tiny (but "dirty" because Windows isn't RTOS) periods like 100 microseconds:
https://www.vbforums.com/images/ieimages/2022/01/2.png
Re: Super Precision Timer
The Trick: Any example project attached with that?
1 Attachment(s)
Re: Super Precision Timer
Quote:
Originally Posted by
some1uk03
The Trick: Any example project attached with that?
You should use precompiled binaries to debug in IDE so i created AxDll for that. Warm up your CPU :D
ADDED:
To use tiny intervals in single-processor machines you could use SendMessage but you need to little bit change exit logic to avoid deadlocks.
Re: Super Precision Timer
Can anyone (ahem, Olaf) comment on if the underlying mechanism of vbRichClient's code:
New_c.Timing True 'let's time the calculation
...
...
Caption = New_c.Timing
is equivalent to using QueryPerformanceCounter + QueryPerformanceFrequency?
Re: Super Precision Timer
The underlying mechanism uses QueryPerformanceCounter and QueryPerformanceFrequency.
(I'm assuming your use of "+" means "and" and not "add" of course!)
Re: Super Precision Timer
Thank you, good to know.
And your assumption is correct. I just copied the writing style of OP's first line...
Re: Super Precision Timer
Happy to help!
Quote:
Originally Posted by
SeabrookStan
And your assumption is correct. I just copied the writing style of OP's first line...
Ahh, gotcha :)
Re: Super Precision Timer
Finally had time to get back on this and got it working they way i like, except one thing.
I have a thing against TLB's, & external .dll's so decided to remove the TLB and implement all necessary API's. Also, it's no longer an ActiveX Dll, but within a standard .exe project.
I got it working in IDE by bypassing CreateThread and it all works perfectly fine.
However, when compiled to an .exe, it crashes as soon as it reaches QueryPerformanceFrequency.
I remember reading somewhere that there's issues when calling VBRuntime files in a new thread, however QueryPerformanceFrequency & QueryPerformanceCounter are from kernel32.
Any ideas on that?