Detect Vertical Blank

BlitzMax Forums/OpenGL Module/Detect Vertical Blank

USNavyFish(Posted 2008) [#1]
Could anyone point me to a method for detecting an imminent hardware refresh?

For some reason, I can't get FlipHook to work. Do you need a Max2d context? I'm using OGL in a MaxGUI Canvas.


In case you're wondering why I need it, I plan on doing the following:

Detect Vertical Blank
Flip current buffer
Draw updated graphics (to backbuffer)
While drawing, set "IS_DRAWING" = True
DO NOT FLIP once done drawing. Set "IS_DRAWING" = False
Upon next Vertical Blank, check status of "IS_DRAWING"
If False, flip and repeat the whole process

If True, DO NOT FLIP. Allow the draw function to continue drawing
Wait for the next Vertical Blank and check again.


The idea is that, with a 60hz refresh rate, the system will attempt to draw once every 1/60th of a second. A buffer will already be 'ready' as soon as the system detects a vertical blank, and thus will be able to dump that completed buffer to the screen with no tearing. If the draw function hasn't finished drawing the buffer, it skips the flip until the next 1/60th of a second cycle. In this case of just one flip 'skip', the true flip rate would be 30 flips per second for that short period of time.

This is probably how "Flip 1" works, but for whatever reason Flip 1 locks up my app and hogs CPU.


Thanks for your assistance in advance.


ImaginaryHuman(Posted 2008) [#2]
Flip 1

detects the vertical blank and then flips the backbuffer.


USNavyFish(Posted 2008) [#3]
Yes, but as I said in my post...

This is probably how "Flip 1" works, but for whatever reason Flip 1 locks up my app and hogs CPU.


Besides, I would like more precise control over my buffer flips.

There has to be a way to detect the vertical blank. What about fliphook? I couldn't get that to work but perhaps was doing it wrong. Has anyone used it?


ImaginaryHuman(Posted 2008) [#4]
It would be nice to have a WaitVBL command that just waits for the blank and then returns, but alas we don't got one. At the moment you have to flip at the same time as the vblank wait, and like you say it wastes cpu time.

You could use a timer to estimate when the vblank is about to occur and trigger a flip?


USNavyFish(Posted 2008) [#5]
Unfortunately that still causes tearing. Sure, I can flip 60 or 30 or 15 times per second (assuming 60 hz), but there is no guarantee that my custom 'flip timer' will fire in sync with the VBL. The phase shift between the two leads to tearing.


ImaginaryHuman(Posted 2008) [#6]
Unless every several frames you do a Flip 1 and resynchronize the flips with your timed flips. Not ideal I know. It would be really nice to have a WaitVBL command - maybe we can rip some code out from Flip()? or does it access the o/s to do it?


USNavyFish(Posted 2008) [#7]
Well, the flip() code is stored in BRL.mod -> graphics.mod, under line 341.

The code is as follows:

Function Flip( sync=-1 )
	RunHooks FlipHook,Null
	If sync<>-1
		_driver.Flip sync
		Return
	EndIf
	If _graphics<>_exGraphics Or Not _softSync
		Local sync=False
		If _gDepth sync=True
		_driver.Flip sync
		Return
	EndIf
	_syncTime:+_syncPeriod
	_syncAccum:+_syncFrac
	If _syncAccum>=_syncRate
		_syncAccum:-_syncRate
		_syncTime:+1
	EndIf
	Local dt=_syncTime-MilliSecs()
	If dt>0
		Delay dt
	Else
		_syncTime:-dt
	EndIf
	_driver.Flip False
End Function



I'll take a closer look and try to figure out what's going on here.


ImaginaryHuman(Posted 2008) [#8]
I think that the line:
_driver.Flip sync
is the one which is calling a routine in the driver, which is probably platform specific, to do a Flip 0 or a Flip 1. The rest of the code seems to handle the Flip -1 condition. We'd have to look at the driver's Flip routine to see which parts of it are coded in BlitzMax and whether there is something there we can pull out to do just a vbl wait without flipping, otherwise it might involve dipping into C code or something.


USNavyFish(Posted 2008) [#9]
Are you sure about that?

_driver is a TGraphicsObject, whose method "Flip" is the one we're looking at above.

It seems like there are several recursive calls to the Flip function from within itself.


My guess is that the various globals, namely:

Global _softSync,_syncRate,_syncPeriod,_syncFrac,_syncAccum,_syncTime


Are somewhere registered with the platform-specific drivers, and an internal buffer swap is called by those drivers when conditions pertaining to the globals are met. Perhaps when _syncFrac = 0.

Gah, where's Mark when you need him?


ImaginaryHuman(Posted 2008) [#10]
See this below code, for example, in Objective C format from the .m Mac file, as part of the GLGraphics module (part of the GL driver):



And this was referenced in the GLGraphics.bmx file, so I'm sure the DX driver has its own code for flipping too. That's why I said you probably have to dip into C code to handle the verticle blank issue. I don't entirely follow what the above code is doing, either. It seems like it's using a GL graphics context and setting Sync to be a field within that type, either 1 or 0, and presumably the flushBuffer is actually doing the flip - presumably the vertical blank part of it is built into the o/s's implementation of OpenGL. You'd probably end up having to look at the o/s API itself to see if it provides a vblank wait that isn't tied to a flip.


ImaginaryHuman(Posted 2008) [#11]
I saw this on an Apple page: "OpenGL blocks drawing to the display while waiting for the next vertical retrace. Applications that attempt to draw to the screen during this waiting period waste time that could be spent performing other drawing operations or saving battery life and minimizing fan operation." "The swap interval can be set only to 0 or 1. If the swap interval is set to 1, the buffers are swapped only during the vertical retrace. After you set up synchronization, call the function CGLFlushDrawable to copy the back buffer to the front buffer during the vertical retrace of the display."

which says to me that with GL you don't really get to detect the vblank yourself, it's part of the call to flip the display because when you're using OpenGL you have to use the GL flip function provided by the o/s.

I don't quite get what you plan to do. Lets say you spend half a frame rendering the next frame and you have several milliseconds of time spare, what else do you plan to do in that time? Further processing while somehow the hook just magically occurs when the flip is ready? You still have to poll for events to trigger the hook function. Are you hoping for a `test if the vertical blank happened yet` poll command, which returns with a true/false rather than waiting? How about you use Flip 1 to sync to vbl, then you know that you have about 15 milliseconds until the next vbl, so after you finish preparing the next frame you check the time and if the time hasn't arrive to do the next flip (approximately just before) you keep doing processing. Then you do a Flip 1 - it'll waste a little bit of time but not all of it.


USNavyFish(Posted 2008) [#12]
My program is entirely hook-based. As a result, I need a logical way of calling the screen's redraw function, since I do not want to draw 'whenever possible,' as this would interrupt the other timed functions.

The ideal case is to be able to hook into the VBL so I can call my redraw function exactly once per refresh.

I'm going to put this one on the back-burner and simply call my redraw function less frequently. If anyone has used the "fliphook" function with success, I'd love to hear about it.


ImaginaryHuman(Posted 2008) [#13]
The flip hook is supposed to work. If it's not working, is it bugged?


USNavyFish(Posted 2008) [#14]
Perhaps I'm doing it wrong.. my assumption was that I simply Addhook(Fliphook,MyFlipHookFunc) like I would with any EmitEventHook, and then it will simply call MyFlipHookFunc once every flip. hmf


tonyg(Posted 2008) [#15]
It would help to have a small example which shows the problem.