Smooth animation / sprite movement

Advanced game related topics
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

Well, if anybody gets something to work properly, please post it in here. Until then I'm stuck with FlipBuffers(#PB_Screen_WaitSynchronization) and the corresponding high CPU load...

Remember: I'm trying to become CPU and refresh rate independent with limited CPU load...

Of course, by the time we've figured out a solution, the PBdevs have (hopefully) released a version that does do it properly...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
idle
Always Here
Always Here
Posts: 6088
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

This will get you the scan line, without any drop in performance, if you utilize a high performance timer you can then use the scan line to time your refresh.

Code: Select all


IDD.IDirectDraw7                 

If OpenLibrary(0,"ddraw.dll")
  If CallFunction(0,"DirectDrawCreateEx",0,@IDD.IDirectDraw7,?IID_IDirectDraw7,0) <> 0
    MessageRequester("Warning:","Couldn't init DirectDraw",0)
    End
  Else 
    bopen = #True 
  EndIf
Else
  MessageRequester("Warning:","Couldn't init DirectDraw",0)
  End
EndIf 

;****************** the business

If bopen 
   line.w 
   ret = IDD\GetScanLine(@line)
EndIf

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

DataSection
  IID_IDirectDraw7:
    Data.l $15e65ec0
    Data.w $3b9c, $11d2
    Data.b $b9, $2f, $00, $60, $97, $97, $ea, $5b
EndDataSection  


;can call any of these I expect, like the above 

;   STDMETHOD(Compact)(THIS) PURE;
;    STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE;
;    STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE;
;    STDMETHOD(CreateSurface)(THIS_  LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE;
;    STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE;
;    STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE;
;    STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE;
;    STDMETHOD(FlipToGDISurface)(THIS) PURE;
;    STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE;
;    STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE;
;    STDMETHOD(GetFourCCCodes)(THIS_  LPDWORD, LPDWORD ) PURE;
;    STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE;
;    STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE;
;    STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE;
;    STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE;
;    STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE;
;    STDMETHOD(RestoreDisplayMode)(THIS) PURE;
;    STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE;
;    STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD) PURE;
;    STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE;
User avatar
Demivec
Addict
Addict
Posts: 4282
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

idle wrote:This will get you the scan line, without any drop in performance, if you utilize a high performance timer you can then use the scan line to time your refresh.
@idle: I reached the same conclusion and I just finished working on the code to do this (in 3 days). If you would have posted this sooner it would have saved me some sleep. :P
User avatar
idle
Always Here
Always Here
Posts: 6088
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Post by idle »

Demivec wrote:
idle wrote:This will get you the scan line, without any drop in performance, if you utilize a high performance timer you can then use the scan line to time your refresh.
@idle: I reached the same conclusion and I just finished working on the code to do this (in 3 days). If you would have posted this sooner it would have saved me some sleep. :P
Sorry I hadn't had the time to look into it until this morning, at least you got it sorted. :)
User avatar
Demivec
Addict
Addict
Posts: 4282
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

A little bit later today, after I've slept, I post a demo that includes a method that uses DirectDraw with the GetScanLine, then computes the display frequency, then uses this information for a lazy, but precise timer to FlipBuffers() in the demo that Kaeru posted. Whew!

I have all of the above actually working well. Since there is an element of research to this, I still have to work out the effects, both positive and negative, of a minor aspect of the timing adjustments.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

Now we're talking! Gonna' have a good look at this next week, see what I can cook up...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Demivec
Addict
Addict
Posts: 4282
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

blueznl wrote:Now we're talking! Gonna' have a good look at this next week, see what I can cook up...
@blueznl: Well, this should get you started...but let me digress for a moment. I noticed that SetFrameRate() also eats up the CPU time and by using the method demonstrated in the following code, it is also unnecessary.

I realized after the former comment I posted that all the work I was going through to determine the display frequency wasn't needed (though I suceeded in doing it anyway). bluznl you had found the method for me by using ExamineDesktops() and DesktopFrequency(). Again, if I had only found those sooner. :P

So what I will end up demonstrating is only a lazy, but precise timer to use with FlipBuffers(#PB_Screen_WaitSynchronization). The timing method was given in the link that Kaeru had posted and no doubt it has been shown elsewhere as well. It uses API functions to change the timing resolution of Delay() as well as needing a high performance timer. If need be you may be able to do away with the high performance timer. The method gives away program time while it waits for the next screen refresh. When the time gets close it then sits and waits until it is refreshed. The result is it shares time with other programs, doesn't hog the CPU (good for laptop batteries too) and avoids tears and screwy sprite animations.

I decided to post the demonstration with your code instead. I didn't mess with the code too much, though I did add a few things. When you run it you can push "+" to add more sprites up to the maximum of 1000 or push "-" to take sprites away. With 1000 sprites it uses only about 38% of the CPU and with 10 it uses 6%.

When you test it tell me if you can let your ship sit there on the screen with trying to dodge something. There's no collision detection, so no worries. :P

Here it is:

Code: Select all

;most of game code is blueznl's
;Demivec (added screen timing method) and the code marked with ;---start ;---stop

Enumeration
  #w_main
  ;
  #f_exit
  #f_none
  ;
  #i_arrow                           ; image used for player
  #i_rock1                           ; image used for rock1
  #i_rock2                           ; image used for rock2
  #i_rock3                           ; image used for rock3  
  ;
EndEnumeration
;
Global i_arrow_h.l
;
Structure spr
  alive.l                            ; set to #false to disable this sprite
  sprite2d_nr.l                      ; 2d sprite used
  sprite3d_nr.l                      ; 3d sprite used
  a.f                                ; object orientation
  Rotation.f                         ; rotation speed in degrees / second, does not affect vector
  Z.f                                ; object size factor (1 = original size)
  x.f                                ; x-coordinate
  y.f                                ; y-coordinate
  w.l                                ; width in pixels
  h.l                                ; height in pixels
  v_angle.f                          ; vector angle in degrees
  v_speed.f                          ; speed in pixels per second
  v_mode.l                           ; #true to use vector, #false to use x / y components
  v_x.f                              ; horizontal speed in pixels per second
  v_y.f                              ; same vertical
EndStructure

;--start (deals with timing method)
Global highPerfTimerFreq.q ;holds frequency of the high performance timer
Define prevEndOfFrame.q, timer.q
Define ticksToWait.q, ticksPassed.q, i.l, gapTicks.q
Define.l framerate, timeGap

QueryPerformanceFrequency_(@highPerfTimerFreq.q)
If highPerfTimerFreq = 0
  MessageRequester("Warning:","High performance timer not available",0)
  End
EndIf 

;this will enable finer resolution of time when using Delay(), must use timeEndPeriod_(1) before program ends
timeBeginPeriod_(1)

gapTicks = (highPerfTimerFreq * 10) / 1000 ;this is 10 ms to shorten the wait time by, just to be safe

QueryPerformanceCounter_(@timer)
prevEndOfFrame = timer
;--stop
;
InitSprite()
InitSprite3D()
InitKeyboard()
;
ExamineDesktops()
framerate = DesktopFrequency(0)
;--start (deals with timing method)
ticksToWait = highPerfTimerFreq / framerate - gapTicks
;--stop
framelength.f = 1 / framerate        ; time per frame in seconds
; Debug framerate
;
screen_w.l = 1280                    ; screen width
screen_h.l = 1024                    ; screen height
screen_z.l = 32                      ; color depth
OpenScreen(screen_w,screen_h,screen_z,"SpriteEngine")
;SetFrameRate(framerate)
;
; sorry, no luck with windowed screens yet...
;
;   screen_w.l = 800
;   screen_h.l = 600
;   screen_z.l = 32
;   w_main_h.l = OpenWindow(#w_main,10,10,screen_w,screen_h,"SpriteEngine",#PB_Window_ScreenCentered)
;   OpenWindowedScreen(w_main_h,0,0,screen_w,screen_h,0,0,0)
;   SetFrameRate(framerate)
;
Sprite3DQuality(0)
;
; player sprite
;
#spr_player = 0
;
i_arrow_h = CreateImage(#i_arrow,32,32,32)
StartDrawing(ImageOutput(#i_arrow))
  Box(0,0,31,31,RGB(0,0,0))
  FrontColor(RGB(0,255,0))
  LineXY(2,30,16,2)
  LineXY(16,2,30,30)
  LineXY(30,30,16,8)
  LineXY(16,8,2,30)
StopDrawing()
;
CreateSprite(#spr_player,32,32,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(#spr_player))
  DrawImage(i_arrow_h,0,0)
StopDrawing()
CreateSprite3D(#spr_player,#spr_player)
;
;--start
; rock sprites
;
#spr_rock1 = 1
i_rock1_h = CreateImage(#i_rock1,128,128,32)
StartDrawing(ImageOutput(#i_rock1))
  Box(0,0,127,127,RGB(0,0,0))
  FrontColor(RGB(255,255,255))
  LineXY(14,38,14,73):LineXY(15,39,15,73):LineXY(16,40,16,73)
  LineXY(14,73,40,112):LineXY(15,73,40,111):LineXY(16,73,40,110):LineXY(17,73,41,110)
  LineXY(40,112,78,98):LineXY(40,111,78,97):LineXY(40,110,78,96)
  LineXY(78,98,90,111):LineXY(78,97,90,110):LineXY(78,96,90,109):LineXY(79,96,91,109)
  LineXY(90,111,112,86):LineXY(90,110,111,86):LineXY(90,109,110,86):LineXY(89,109,109,86)
  LineXY(112,86,79,63):LineXY(111,86,78,63):LineXY(110,86,78,62):LineXY(108,86,76,62)
  LineXY(79,63,112,50):LineXY(78,63,111,50):LineXY(78,62,110,50):LineXY(78,61,110,49)
  LineXY(112,50,112,39):LineXY(111,50,111,39):LineXY(110,50,110,39)
  LineXY(112,39,77,14):LineXY(111,39,77,15):LineXY(110,39,77,16)
  LineXY(77,14,39,14):LineXY(77,15,40,15):LineXY(77,16,41,16)
  LineXY(39,14,51,38):LineXY(40,15,52,39):LineXY(41,16,53,40)
  LineXY(51,38,14,38):LineXY(52,39,15,39):LineXY(53,40,16,40)
StopDrawing()
;
CreateSprite(#spr_rock1,128,128,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(#spr_rock1))
  DrawImage(i_rock1_h,0,0)
StopDrawing()
;
#spr_rock2 = 2
i_rock2_h = CreateImage(#i_rock2,128,128,32)
StartDrawing(ImageOutput(#i_rock2))
  Box(0,0,127,127,RGB(0,0,0))
  FrontColor(RGB(255,255,255))
  LineXY(14,41,14,88):LineXY(15,41,15,88):LineXY(16,40,16,88)
  LineXY(14,88,36,110):LineXY(15,88,36,109):LineXY(16,88,36,108)
  LineXY(36,110,78,110):LineXY(36,109,78,109):LineXY(37,108,79,108)
  LineXY(78,110,110,89):LineXY(78,109,109,89):LineXY(79,108,109,88):LineXY(79,107,108,88)
  LineXY(110,89,98,66):LineXY(109,89,97,66):LineXY(109,88,97,65):LineXY(107,87,97,66)
  LineXY(98,66,112,41):LineXY(97,66,111,41):LineXY(97,65,111,40)
  LineXY(112,41,87,18):LineXY(111,41,87,19):LineXY(111,40,88,19):LineXY(109,40,87,20):LineXY(108,40,86,20)
  LineXY(87,18,64,40):LineXY(87,19,64,41):LineXY(88,19,65,41):LineXY(89,19,64,43)
  LineXY(64,40,38,18):LineXY(64,41,38,19):LineXY(65,41,39,19):LineXY(64,42,38,20):LineXY(63,42,37,20)
  LineXY(38,18,14,41):LineXY(38,19,15,41):LineXY(39,19,17,40)
StopDrawing()
CreateSprite(#spr_rock2,128,128,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(#spr_rock2))
  DrawImage(i_rock2_h,0,0)
StopDrawing()

#spr_rock3 = 3
i_rock3_h = CreateImage(#i_rock3,128,128,32)
StartDrawing(ImageOutput(#i_rock3))
  Box(0,0,127,127,RGB(0,0,0))
  FrontColor(RGB(255,255,255))
  LineXY(14,41,25,67):LineXY(15,41,26,67):LineXY(16,41,27,67)
  LineXY(25,67,14,89):LineXY(26,67,15,89):LineXY(27,67,16,89)
  LineXY(14,89,39,110):LineXY(15,89,39,109):LineXY(16,89,39,108):LineXY(17,89,40,108)
  LineXY(39,110,52,100):LineXY(39,109,52,99):LineXY(39,108,52,98)
  LineXY(52,100,87,110):LineXY(52,99,87,109):LineXY(52,98,87,108)
  LineXY(87,110,110,79):LineXY(87,109,109,79):LineXY(87,108,108,79):LineXY(87,107,107,79)
  LineXY(110,79,88,54):LineXY(109,79,87,54):LineXY(108,79,86,54)
  LineXY(88,54,112,40):LineXY(87,54,111,40):LineXY(86,54,110,40):LineXY(85,53,109,40)
  LineXY(112,40,88,18):LineXY(111,40,88,19):LineXY(110,40,88,20):LineXY(110,41,88,21)
  LineXY(88,18,64,31):LineXY(88,19,64,32):LineXY(88,20,64,33)
  LineXY(64,31,39,18):LineXY(64,32,39,19):LineXY(64,33,39,20)
  LineXY(39,18,14,41):LineXY(39,19,15,41):LineXY(39,20,16,41)
StopDrawing()
CreateSprite(#spr_rock3,128,128,#PB_Sprite_Texture)
StartDrawing(SpriteOutput(#spr_rock3))
  DrawImage(i_rock3_h,0,0)
StopDrawing()
;--stop
;
#spr_max = 1000
Dim spr.spr(#spr_max)
;
With spr(0)
  \alive = #True
  \sprite2d_nr = #spr_player
  \sprite3d_nr = #spr_player
  \a = 0
  \Rotation = 0
  \Z = 1
  \x = screen_w / 2
  \y = screen_h / 2
  \w = SpriteWidth( \sprite2d_nr)
  \h = SpriteHeight( \sprite2d_nr)
  \v_angle = 0
  \v_speed = 0
  \v_mode.l = #False
  \v_x.f = 0
  \v_y.f = 0
EndWith
;--start
Procedure setRock(index.l,sprite2d.l,Size.f = 1.0)
  Protected sprite3d.l
  Shared spr()
  
  sprite3d = CreateSprite3D(#PB_Any,sprite2d)
  With spr(index)
    \alive = #True
    \sprite2d_nr = sprite2d
    \sprite3d_nr = sprite3d
    \a = 0
    \Rotation = -0.45 / Size  + 0.225 * Random(1)
    If Random(1): \Rotation = -\Rotation: EndIf 
    \Z = 1
    \x = (3 * screen_w / 10) - Random(screen_w / 5) + (Random(1) * screen_w * 0.6)
    \y = (3 * screen_h / 10) - Random(screen_h / 5) + (Random(1) * screen_h * 0.6)
    \w = SpriteWidth( \sprite2d_nr) * Size
    \h = SpriteHeight( \sprite2d_nr) * Size
    \v_angle = Random(360)
    \v_speed = 75 + Random(25) / Size
    \v_mode.l = #True
    \v_x.f = 0
    \v_y.f = 0
  EndWith
EndProcedure

setRock(1,#spr_rock1)
setRock(2,#spr_rock2)
setRock(3,#spr_rock3)
setRock(4,#spr_rock1,0.5)
setRock(5,#spr_rock3,0.5)
setRock(6,#spr_rock1,0.25)
setRock(7,#spr_rock2,0.25)
setRock(8,#spr_rock3,0.25)
setRock(9,#spr_rock1,0.25)
;--stop
;
spr_number.l = 10                     ; number of sprites to process
;
action = #f_none
Repeat
  ;
  ; calculate movement etc.
  ;
  n = 0
  While n < spr_number
    With spr(n)
      If \alive
        \a = \a + \Rotation
        ZoomSprite3D( \sprite3d_nr, \w, \h)
        RotateSprite3D( \sprite3d_nr , \a , 0)
        If \v_mode
          \v_x = \v_speed * Sin( \v_angle / 180 * #PI )
          \v_y = 0 - \v_speed * Cos( \v_angle / 180 * #PI )
        EndIf
        \x = \x + \v_x * framelength
        \y = \y + \v_y * framelength
        ;
        ; wrap outside screen borders
        ;
        If \x > screen_w + \w
          \x = 0 - \w
        ElseIf \x < 0 - \w
          \x = screen_w + \w
        EndIf
        If \y > screen_h + \h
          \y = 0 - \h
        ElseIf \y < 0 - \h
          \y = screen_h + \h
        EndIf
        ;
      EndIf
    EndWith
    n = n+1
  Wend
  ;
  ; display all active sprites
  ;
  ClearScreen(0)
  Start3D()
    n = 0
    While n < spr_number
      With spr(n)
        If \alive
          DisplaySprite3D( \sprite3d_nr , \x , \y , 255)
        EndIf
      EndWith
      n = n+1
    Wend
  Stop3D()
  ;
  ;FlipBuffers(#PB_Screen_WaitSynchronization)
  ;--start (deals with timing method)
  While 1
    QueryPerformanceCounter_(@timer);
    ticksPassed = timer - prevEndOfFrame
    timeGap = ticksToWait - ticksPassed
    
    If timer < prevEndOfFrame Or  timeGap <= 0: Break: EndIf 
    
    If (timeGap > highPerfTimerFreq * 1 / 1000)
      Delay(1)
    Else                        
      For i = 0 To 9
        Delay(0)  ; causes thread to give up its timeslice
      Next 
    EndIf 
  Wend
  
  ; ** the rest will wait FlipBuffers
  FlipBuffers(#PB_Screen_WaitSynchronization)
  prevEndOfFrame = timer
  ;--stop
  
  ;
  ExamineKeyboard()
  If KeyboardPushed(#PB_Key_Escape)
    action = #f_exit
  EndIf
 
  If KeyboardPushed(#PB_Key_Add)
    If spr_number < #spr_max
      sprite.l = Random(2)
      Select sprite
        Case 0
          sprite = #spr_rock1
        Case 1
          sprite = #spr_rock2
        Case 2
          sprite = #spr_rock3
      EndSelect
      
      Select Random(6)
        Case 0
          setRock(spr_number,sprite)
        Case 1,2
          setRock(spr_number,sprite,0.5)
        Default
          setRock(spr_number,sprite,0.25)
      EndSelect
      spr_number + 1     
    EndIf
  EndIf 
  
  If KeyboardPushed(#PB_Key_Subtract)
    If spr_number > 2
      spr_number - 1
      FreeSprite3D(spr(spr_number)\sprite3d_nr)
      spr(spr_number)\alive = 0
    EndIf 
  EndIf 
  
  With spr(0)
    ;
    ; move the player object using A / W / S / D / UP / DN / LFT / RGHT
    ;
    If KeyboardPushed(#PB_Key_Left)
      \Rotation = \Rotation - 10 * framelength         ; with inertia
      ; \a = \a - 300 * framelength                    ; without inertia
    ElseIf KeyboardPushed(#PB_Key_Right)
      \Rotation = \Rotation + 10 * framelength
      ; \a = \a + 300 * framelength
    ElseIf KeyboardPushed(#PB_Key_Up)
      \v_x = \v_x + 500 * Sin( \a / 180 * #PI ) * framelength
      \v_y = \v_y - 500 * Cos( \a / 180 * #PI ) * framelength
    ElseIf KeyboardPushed(#PB_Key_Down)
      \v_x = \v_x - 100 * Sin( \a / 180 * #PI ) * framelength
      \v_y = \v_y + 100 * Cos( \a / 180 * #PI ) * framelength
    ElseIf KeyboardPushed(#PB_Key_A)
      ; \x = \x - 100 * framelength                    ; without inertia
      \v_x = \v_x - 100 * framelength                  ; with inertia
    ElseIf KeyboardPushed(#PB_Key_D)
      ; \x = \x + 100 * framelength
      \v_x = \v_x + 100 * framelength
    ElseIf KeyboardPushed(#PB_Key_W)
      ; \y = \y - 100 * framelength
      \v_y = \v_y - 100 * framelength
    ElseIf KeyboardPushed(#PB_Key_S)
      ; \y = \y + 100 * framelength
      \v_y = \v_y + 100 * framelength
    EndIf
  EndWith
  ;
Until action = #f_exit
CloseScreen() 
;--start (deals with timing method)
timeEndPeriod_(1)
;--stop
@Edit: updated some program comments
Last edited by Demivec on Mon Sep 01, 2008 6:23 am, edited 1 time in total.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6172
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

Hmmm. Interesting. One of my CPU cores still moves up to 25% as if it is fully loaded. Did you test it on a single core or multi core CPU?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB - upgrade incoming...)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Demivec
Addict
Addict
Posts: 4282
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

blueznl wrote:Hmmm. Interesting. One of my CPU cores still moves up to 25% as if it is fully loaded. Did you test it on a single core or multi core CPU?
Alas, I only have a single core for testing. If you are using multi core, what are the other CPU's doing? I'm just curious because I really don't have any answers, just observations. I couldn't get the animation to work perfectly without waiting for 10 ms for a vertical blank via the FlipBuffers(). If that period could be shortened it will save even more CPU. I couldn't determine a way to calculate that amount of time (10 ms) and had to do it empirically.

I noticed that if I am running other active programs, especially ones that hog CPU for drawing purposes like, well, drawing programs, it will keep the CPU use higher even when they are not being actively used.
Post Reply