Realtime visual benchmark (VBL & Rasterlines)

Share your advanced PureBasic knowledge/code with the community.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Realtime visual benchmark (VBL & Rasterlines)

Post 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
Last edited by djes on Thu Feb 09, 2017 12:59 pm, edited 20 times in total.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Good one djes.
And useful explanation for a lot of users here :)
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post 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.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Bahhh, these modern programmers just only like windows and controls, they don't know about the good things :wink:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post 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 ;-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

Update : code and title
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

Here's another example using plot. Press left mouse button to see frame rate.

http://djes.free.fr/purebasic/eye.exe
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post 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
oh... and have a nice day.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Post by djes »

Yep :lol: I've changed 3 times the subject name...
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

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

Post by djes »

Edit : 4.60 fix and code cleanup
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

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

Post by Psychophanta »

Excellent! Thanks
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

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

Post by Kwai chang caine »

Cool!!!! when i add numerous white ball, i have a splendid depth effect, nearly 3D :shock:
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

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

Post 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.
Mesa
Enthusiast
Enthusiast
Posts: 433
Joined: Fri Feb 24, 2012 10:19 am

Re: Realtime visual benchmark (VBL & Rasterlines)

Post 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.
User avatar
djes
Addict
Addict
Posts: 1806
Joined: Sat Feb 19, 2005 2:46 pm
Location: Pas-de-Calais, France

Re: Realtime visual benchmark (VBL & Rasterlines)

Post by djes »

Yes, it only works with x64 version, it seems that x86 files (sprite and screen subsystems) doesn't include the same things !
Post Reply