Page 1 of 2

Realtime visual benchmark (VBL & Rasterlines)

Posted: Tue Mar 24, 2009 4:57 pm
by djes
Here's a little benchmark using VBL sync and raster lines.

You'll see on the screen a visual fx (circles going from left to right, like a starfield), and lines. These lines are showing visually how long is during parts of our code, using the display as a time basis.

The space between two lines represents time taken by our code.

What this program does is to synchronize code on the (virtual) screen beam's position. It starts the benchmark after the ClearScreen() and draws the first line. Then it computes and draws the FX, and then draws the second line. Then it draws the technical details like FPS and information text, and after that the third line. So you visually see how much time each part has taken, by the distance between two lines. More computing, more sprites, and the second line will go down. The distance between second and third will not vary much, as it's always the same thing computed from frame to frame.

If it's going to the bottom, you'll encounter what is called a "frame drop" : the computing needs more than one frame , so can't be synchronized with the display... The next frame will not contain updated drawing from the double buffer, as it's not finished. It gives an ugly visual slowdown that is the enemy of realtime artists like demomakers ! :evil:

If the code took less than 16 ms on 60Hz display, it means we have 60 imgs/secs and the best fluidity! :D The distance between the lines is then less than a full height, the buffer swap is always synchronized with display.

(note to demomakers, next job to do : overscan ;) )

[Edit 1]Here's a little explanation (please forgive my bad english and shortcuts to simplify explanations).

It's an old demomaker tip to have a clue on how much time code is taking.

The old CRT cathodic screen had a phosphorous surface, a material that emits light when receiving electrons. To display pixels, an electron beam, that covers the whole screen, was going from left to right, and from top to bottom. For a 60 imgs/sec display, it was doing it 60 times/sec (easy, no?). It's what you see in your graphic adapter display parameters (60 Hz).

On most of ancient computers (even on PC), it was relatively easy to sync the CPU to the display. So, we changed the screen background color to see visually how long was our code. It was far easier to optimise code this way than stopping code and looking numbers, and faster than drawing numbers on the screen.

For example, on my good old Amiga 1200, the shortest CPU instruction (the minimum distance between two colour changes) was approx. 8 hires pixels long. Amiga was special, as lot of parts of the hardware was synced with the display : part of the RAM (Chip-RAM), and a dedicated coprocessor (the copper) was able to alter hardware registers based on beam's position. It was terrific.

Note on the double buffer
On a double buffered screen, we're drawing on a back buffer, and the system waits for the VBL* beam position to swap the displayed buffer and the back buffer. If the drawing operations are not finished before the VBL, the swap can't occur, and there's a frame drop : the same frame is displayed two times.

*VBL : Vertical Blank, when the beam has to come back from the lower end to the beginning of the screen.

[Edit 2] Changed title to be more adequate to english users. (original title originated from a french thread)
[Edit 3] Cleanup and 4.60 fix
[Edit 4] Cleanup and 5.51 (x64) fix
[Edit 5] x64 and x86 fix
[Edit 6] Integrated kvitaliy FPS counter, and updated explanations.

Code: Select all

;*****************************************************************************
;* RasterLine and vertical blank (VBL) benchmark
;* Realtime visual benchmark. Press space to add "stars". The time is given
;* by the space between blue and green line.
;* By djes (djes@free.fr) 03/24/2009
;* Thanks to Stefan Moebius for VBL sync examples
;* Note : I do not check functions return values
;* 02/28/2012 : PB 4.60 fix
;* 02/07/2017 : PB 5.51 (x64 & x86) fix
;*****************************************************************************

Structure D3DRaster_STATUS
  InVBlank.l
  ScanLine.l
EndStructure

Structure Pos
  x.l
  y.l
  Speed.l
EndStructure

;*****************************************************************************

Define.D3DRaster_STATUS Raster
Define.IDirect3DDevice9 D3DDeviceInterface

Global NewList Stars.Pos()

;*****************************************************************************
; Waits for the vblank to start - takes all the cpu
; Attend le début de la synchro vbl
Procedure VBLWait()
  Shared D3DDeviceInterface, Raster, Exit
  Repeat
    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Escape)
      Exit=#True
    EndIf
    D3DDeviceInterface\GetRasterStatus(0, @Raster)
  Until Raster\InVBlank=#True Or Exit
EndProcedure

;*****************************************************************************
; Waits for the vblank to finish - takes all the cpu
; Attend la fin de la synchro vbl
Procedure VBLEndWait()
  Shared D3DDeviceInterface, Raster, Exit
  Repeat
    ExamineKeyboard()
    If KeyboardPushed(#PB_Key_Escape)
      Exit=#True
    EndIf
    D3DDeviceInterface\GetRasterStatus(0, @Raster)
  Until Raster\InVBlank=#False Or Exit
EndProcedure

;*****************************************************************************
; Looks for the raster beam position and draws there a coloured line
; Récupère la position du Raster et y affiche une ligne de la couleur indiquée
Procedure RasterLine(Color.l)
  Shared Raster, D3DDeviceInterface
  StartDrawing(ScreenOutput())
  D3DDeviceInterface\GetRasterStatus(0, @Raster)
  LineXY(0, Raster\ScanLine, 1023, Raster\ScanLine, Color)
  StopDrawing()
EndProcedure

;*****************************************************************************
Procedure CreateSprites()
  CreateSprite(0, 64, 64)
  StartDrawing(SpriteOutput(0))
  For y = -31 To 31
    For x = -31 To 31
      i.d = Sqr(Pow(x/32, 2)+Pow(y/32, 2))
      If i < 1 
        Plot(32 + x, 32 + y, RGB(255 - 255 * i, 64 - 64 * i, 128 - 64 * i))  
      EndIf
    Next x
  Next y
  ;Circle(32, 32, 31, RGB($FF, $FF, $FF))
  StopDrawing()
EndProcedure

;*****************************************************************************
;Opens screen and get the device - Ouvre l'écran et récupère le device
Procedure InitDisplay()
  Shared D3DDeviceInterface
  OpenScreen(1024, 768, 32, "", #PB_Screen_NoSynchronization, 60)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !extrn PB_Screen_Direct3DDevice
    !MOV dword EAX, [PB_Screen_Direct3DDevice]
    !MOV dword [v_D3DDeviceInterface],EAX
  CompilerElseIf #PB_Compiler_Processor = #PB_Processor_x86
    !extrn _PB_Screen_Direct3DDevice
    !MOV dword EAX, [_PB_Screen_Direct3DDevice]
    !MOV dword [v_D3DDeviceInterface],EAX
  CompilerElse
    MessageRequester("Alert", "Unsupported processor")
    End
  CompilerEndIf
EndProcedure

;*****************************************************************************
; Checks if the user has switched (and that we have lost focus) (ALT-TAB)
; Vérifie si l'utilisateur n'a pas changé d'appli (en nous faisant donc perdre le focus) (ALT-TAB)
Procedure FullScreenCheck()
  ; We're lowering our priority - on baisse notre priorité
  SetPriorityClass_(  GetCurrentProcess_(), #NORMAL_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
  ReleaseMouse(1)
  CloseScreen()
  ; Opens a visible window with a taskbar button - Ouvre une fenêtre visible, avec un bouton dans la barre des tâches
  OpenWindow(0, 0, 0, 256, 256, "RasterLines", #PB_Window_Minimize)
  ; Wait til our window is coming back to foreground - Attend que notre fenêtre repasse au premier plan
  Repeat
    ; Now our events are to be processed with WaitWindowEvent, else IsScreenActive() will never say that our window has the focus again
    ; it should be written in the doc !
    ; Maintenant les événements sont à gérer avec WaitWindowEvent, sinon IsScreenActive() ne dit jamais que notre fenêtre a à nouveau le focus
    ; il faudrait écrire ça dans la doc !!!
    Event = WaitWindowEvent()   
  Until Event = #PB_Event_ActivateWindow 
  CloseWindow(0)  
  ReleaseMouse(0)
  ; Better recreate the screen - il vaut mieux recréer l'écran
  InitDisplay()
  ; And the sprites too (have to!) - et les sprites aussi (indispensable)
  CreateSprites()  
  ; Gives to the system some time to rest - laisse un peu le temps au système de récupérer
  ;Delay(2000)  
  ; We're waiting for the synchro a new time - On réattend la synchro
  VBLWait()
  SetPriorityClass_(  GetCurrentProcess_(), #HIGH_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_ABOVE_NORMAL)     ;warning : keyboard lock
EndProcedure

Procedure NewStar()
  AddElement(Stars())
  Stars()\x = Random(1024 + 128) - 64
  Stars()\y = Random(768 + 128) - 64
  Stars()\Speed = Random(16) + 1
EndProcedure

;*****************************************************************************
;-                                START
;*****************************************************************************

InitSprite()
InitKeyboard()
InitMouse()

InitDisplay()
CreateSprites()

For i = 1 To 5
  NewStar()
Next i

Exit=#False

; We're giving max priority to our process, as it should not be interrupted by other tasks
; On donne une priorité maximale à notre processus
SetPriorityClass_(  GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock

;*****************************************************************************
;-                                MAIN LOOP
;*****************************************************************************

Repeat
  
  ; Waits for the sync - attends la synchro
  VBLWait()
  
  ; Flips buffers without the PB's sync - flippe le buffer sans utiliser la synchro PB
  FlipBuffers()
  
  ; During VBL, lowers a bit our priority to give some time to the OS (especially to handle the keyboard)
  ; Pendant la VBL, baisse un peu notre priorité pour donner un peu de temps au système (pour gérer le clavier surtout)
  SetPriorityClass_(  GetCurrentProcess_(), #HIGH_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_NORMAL)
  
  ExamineKeyboard()
  ExamineMouse()
  
  If KeyboardPushed(#PB_Key_Escape)
    Exit = #True
  EndIf
  
  If KeyboardPushed(#PB_Key_Space)
    NewStar()
  EndIf
  
  If Not IsScreenActive()
    FullScreenCheck()   
  EndIf
  
  ; We're waiting for the VBL end (could be better !!!)
  ; On attend la fin de la VBL (à améliorer) pour être sûr de ne pas sauter une frame
  VBLEndWait()
  
  ;we're giving max priority to our process - on donne une priorité maximale à notre processus
  SetPriorityClass_(  GetCurrentProcess_(), #REALTIME_PRIORITY_CLASS)
  SetThreadPriority_( GetCurrentThread_() , #THREAD_PRIORITY_TIME_CRITICAL)     ;warning : keyboard lock
  
  ClearScreen(0)
  
  ;- BENCHMARK START
  
  ; Draws the first raster line (base of our benchmark)
  RasterLine($FF0000)
  
  ; Computes something (here an FX)
  ForEach Stars()
    Stars()\x + Stars()\Speed
    If Stars()\x > 1023
      Stars()\x - 1088
    EndIf
  Next
  ForEach Stars()
    DisplayTransparentSprite(0, Stars()\x, Stars()\y, Stars()\Speed << 4)
  Next
  
  ; Draws the second RasterLine to see (visually) how much time our fx is taking
  ; the less space between the two lines, the fastest our effect
  ; Affiche une RasterLine pour visualiser combien de temps prend notre effet
  RasterLine($FF00)
  
  ; Technical infos drawing
  StartDrawing(ScreenOutput())
  ; FPS 
  Now = ElapsedMilliseconds()
  If (Now-Ticks) >= 1000
    Ticks = Now
    FramePerSec.s = "FPS: " + Str( FrameCounter )
    FrameCounter = 0
  EndIf
  FrameCounter + 1
  DrawText(2,2, FramePerSec )
  
  DrawingMode(#PB_2DDrawing_Transparent)
  Text.s = "Press SPACE to add balls ; ESC to quit"
  DrawText(512 - TextWidth(Text)/2, 300, Text, #Blue)
  Text.s = "Stars Nb : " + Str(ListSize(Stars()))
  DrawText(512 - TextWidth(Text)/2, 320, Text, #Red)
  StopDrawing()
  
  ;Draws another raster line to see how much time was taken by this drawing
  RasterLine($FF)
  
  ;- BENCHMARK END 
  
Until Exit

CloseScreen()

End

Posted: Wed Mar 25, 2009 12:55 pm
by Psychophanta
Good one djes.
And useful explanation for a lot of users here :)

Posted: Wed Mar 25, 2009 6:36 pm
by djes
Psychophanta wrote:Good one djes.
And useful explanation for a lot of users here :)
Thank you (I know, in fact ;)), but it seems that this thread is not so successful... So bad.

Posted: Wed Mar 25, 2009 7:29 pm
by Psychophanta
Bahhh, these modern programmers just only like windows and controls, they don't know about the good things :wink:

Posted: Thu Mar 26, 2009 9:48 pm
by blueznl
I simply haven't had time yet to look at this, too busy with work and kids at the moment...

But I'm going to try as soon as I have time, got some interesting ideas ;-)

Posted: Mon Mar 30, 2009 3:22 pm
by djes
Update : code and title

Posted: Sat Apr 11, 2009 2:45 am
by djes
Here's another example using plot. Press left mouse button to see frame rate.

http://djes.free.fr/purebasic/eye.exe

Posted: Thu May 28, 2009 12:46 pm
by Kaeru Gaman
found this topic only by the reference you put there:
http://www.purebasic.fr/english/viewtop ... 379#287379

never thought "virtual electron beam sync benchmark" would be interesting concerning game timing...

I got to see how much and when I find the time to dive into this...

cheers!
OldFrog

Posted: Thu May 28, 2009 1:30 pm
by djes
Yep :lol: I've changed 3 times the subject name...

Re: Vertical blank - electron beam benchmark (VBL & Rasterli

Posted: Tue Feb 28, 2012 10:30 am
by djes
Edit : 4.60 fix and code cleanup

Re: Vertical blank - electron beam benchmark (VBL & Rasterli

Posted: Mon Mar 05, 2012 12:15 am
by Psychophanta
Excellent! Thanks

Re: Vertical blank - electron beam benchmark (VBL & Rasterli

Posted: Mon Mar 05, 2012 7:09 pm
by Kwai chang caine
Cool!!!! when i add numerous white ball, i have a splendid depth effect, nearly 3D :shock:
Thanks for sharing 8)

Re: Vertical blank - electron beam benchmark (VBL & Rasterli

Posted: Thu Jan 26, 2017 4:39 pm
by djes
Updated code for 5.42. Contains examples of Alt-Tab / lost focus / full screen mode exiting and restoring, realtime priority handling, oldschool realtime screen synchronisation, Purebasic intensive benchmarking.

Re: Realtime visual benchmark (VBL & Rasterlines)

Posted: Fri Jan 27, 2017 9:40 am
by Mesa
I've got a polink error on an external unsolved !extrn PB_Screen_Direct3DDevice, line 81.


I use Windows XP32 and PB 5.51 x86.

M.

Re: Realtime visual benchmark (VBL & Rasterlines)

Posted: Fri Jan 27, 2017 12:28 pm
by djes
Yes, it only works with x64 version, it seems that x86 files (sprite and screen subsystems) doesn't include the same things !