Frame rate only as fast as the main monitor when showing game on secondary monitor using DPI aware

Post bugreports for the Windows version here
coco2
Enthusiast
Enthusiast
Posts: 353
Joined: Mon Nov 25, 2013 5:38 am
Location: Australia

Frame rate only as fast as the main monitor when showing game on secondary monitor using DPI aware

Post by coco2 »

When you have "Enable DPI Aware Executable" checked in Compiler Options, if your second monitor is a higher frequency than your main monitor, FlipBuffers() will only allow a frame rate of the slower monitor. To play a game (DPI aware) on another monitor with a higher frequency you need to set that monitor as the main. I'm not sure if this is a bug or a limitation of Windows.

Code to test this (run it with a main monitor that has a lower frequency than the second monitor, for example the main monitor is 60Hz and the secondary monitor is 144Hz. If you turn off DPI aware the secondary monitor will run at it's native frequency fine, but if you turn it on it will only run as fast as the main monitor's frequency):

Code: Select all

EnableExplicit

DeclareModule Fonts
  
  #Max_Fonts = 10
  #Font_Num_Characters = 95
  
  #Font_Num_Characters_Numbers = 10
  #Font_Num_Characters_Uppercase = 26
  #Font_Num_Characters_Lowercase = 26
  #Font_Num_Characters_Symbol = 33
  
  #Font_Characters_Select = 1 ; used if you just want to select a few
  #Font_Characters_Numbers = 2
  #Font_Characters_Uppercase = 4
  #Font_Characters_Lowercase = 8
  #Font_Characters_Symbol_1 = 16
  #Font_Characters_Symbol_2 = 32
  #Font_Characters_Symbol_3 = 64
  #Font_Characters_Symbol_4 = 128
  #Font_Characters_All = 256
  
  Structure Font_Character_Structure
    Sprite_ID.i
    Image_ID.i
    Width.i
    Height.i
    Loaded.i
  EndStructure
  
  Structure Font_Structure
    Name.s
    Filename.s
    Size.i
    Character.Font_Character_Structure[#Font_Num_Characters]
    Crop_Top_Ratio.d
    Crop_Bottom_Ratio.d
    Crop_Left_Ratio.d
    Crop_Right_Ratio.d    
    Colour.i
    Back_Colour.i
    Available.i ; 0 = select, 1 = numbers, 2 = uppercase, 4 = lowercase, 8 = symbol 1, 16 = symbol 2, 32 = symbol 3, 64 = symbol 4, 128 = all
    Select_Available.s
    Border.i
    Border_Colour.i
    Loaded.i
  EndStructure
  
  Dim *Fonts.Font_Structure(#Max_Fonts)
    
  Declare Initialise(Array *Fonts.Font_Structure(1))
  Declare.s GetChar(Char.i)
  Declare.i GetCharNum(Char.s)
  Declare.i GetNum(ASCII.i)
  Declare.i CreateFont(Array *Fonts.Font_Structure(1), Font_ID.i, Flags.i=0)
  Declare.i GetCharacterWidth(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s)
  Declare.i GetCharacterHeight(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s)
  Declare DisplayCharacterSprite(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s, x.i, y.i)
  Declare DisplayCharacterImage(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s, x.i, y.i)
  Declare DisplayStringSprite(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s, x.i, y.i, Spacing.i=0)
  Declare DisplayStringImage(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s, x.i, y.i, Spacing.i=0)
  Declare.i GetStringWidth(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s)
  Declare Shutdown(Array *Fonts.Font_Structure(1))
  
EndDeclareModule

Module Fonts
  
  Procedure Initialise(Array *Fonts.Font_Structure(1))
    Protected c.i
    For c = 0 To #Max_Fonts
      *Fonts(c) = AllocateStructure(Font_Structure)
    Next c
  EndProcedure
  
  Procedure.s GetChar(Char.i)
    Select Char
      Case 0 To 9: ProcedureReturn(Chr(48+Char))
      Case 10 To 35: ProcedureReturn(Chr(55+Char))
      Case 36 To 61: ProcedureReturn(Chr(61+Char))  
      Case 62 To 77: ProcedureReturn(Chr(Char-30))
      Case 78 To 84: ProcedureReturn(Chr(Char-20))
      Case 85 To 90: ProcedureReturn(Chr(6+Char))
      Case 91 To 94: ProcedureReturn(Chr(32+Char))
    EndSelect
  EndProcedure 
  
  Procedure.i GetCharNum(Char.s)
    Protected ASCII.i = Asc(Char)
    Select ASCII
      Case 48 To 57:ProcedureReturn(ASCII-48)
      Case 65 To 90:ProcedureReturn(ASCII-55)
      Case 97 To 122:ProcedureReturn(ASCII-61)
      Case 32 To 47:ProcedureReturn(ASCII+30)
      Case 58 To 64:ProcedureReturn(ASCII+20)
      Case 91 To 96:ProcedureReturn(ASCII-6)
      Case 123 To 126:ProcedureReturn(ASCII-32)
    EndSelect
  EndProcedure 
  
  Procedure.i GetNum(ASCII.i)
    Select ASCII
      Case 48 To 57:ProcedureReturn(ASCII-48)
      Case 65 To 90:ProcedureReturn(ASCII-55)
      Case 97 To 122:ProcedureReturn(ASCII-61)
      Case 32 To 47:ProcedureReturn(ASCII+30)
      Case 58 To 64:ProcedureReturn(ASCII+20)
      Case 91 To 96:ProcedureReturn(ASCII-6)
      Case 123 To 126:ProcedureReturn(ASCII-32)
    EndSelect
  EndProcedure    
  
  Procedure.i CheckCreateCharacter(Array *Fonts.Font_Structure(1), Font_ID.i, c.i)
    ; Checks whether to create the character based on the settings provided
    Protected.i Result = 0
    If *Fonts(Font_ID)\Available & #Font_Characters_Select
      If FindString(*Fonts(Font_ID)\Select_Available, GetChar(c)):Result = 1:EndIf
    EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Numbers And c>=0 And c<=9:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Uppercase And c>=10 And c<=35:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Lowercase And c>=36 And c<=61:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Symbol_1 And c>=62 And c<=77:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Symbol_2 And c>=78 And c<=84:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Symbol_3 And c>=85 And c<=90:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_Symbol_4 And c>=91 And c<=94:Result = 1:EndIf
    If *Fonts(Font_ID)\Available & #Font_Characters_All:Result = 1:EndIf
    ProcedureReturn Result
  EndProcedure  
  
  Procedure.i CreateFont(Array *Fonts.Font_Structure(1), Font_ID.i, Flags.i=0)
    Protected Char.s
    Protected c.i
    Protected Crop_Left.i
    Protected Crop_Right.i
    Protected Crop_Top.i
    Protected Crop_Bottom.i    
    If *Fonts(Font_ID)\Filename <> ""
      If FileSize(*Fonts(Font_ID)\Filename) = -1
        Debug "Font file does not exist: " + *Fonts(Font_ID)\Filename
        ProcedureReturn 0
      EndIf
      RegisterFontFile(*Fonts(Font_ID)\Filename)
    EndIf
    LoadFont(Font_ID, *Fonts(Font_ID)\Name, *Fonts(Font_ID)\Size, Flags)
    Restore Font_Characters_Numbers
    For c = 0 To #Font_Num_Characters-1
      Read.s Char
      If CheckCreateCharacter(*Fonts(), Font_ID, c)
        ; create temp image so that the font can be selected
        Temp_Image = CreateImage(#PB_Any, 1, 1, 32)
        StartDrawing(ImageOutput(Temp_Image))
        DrawingFont(FontID(Font_ID))
        *Fonts(Font_ID)\Character[c]\Width = TextWidth(Char)
        *Fonts(Font_ID)\Character[c]\Height = TextHeight(Char)
        StopDrawing()
        FreeImage(Temp_Image)
        If *Fonts(Font_ID)\Crop_Top_Ratio > 0  And *Fonts(Font_ID)\Crop_Top_Ratio < 1
          Crop_Top = *Fonts(Font_ID)\Character[c]\Height * *Fonts(Font_ID)\Crop_Top_Ratio
        EndIf
        If *Fonts(Font_ID)\Crop_Bottom_Ratio > 0  And *Fonts(Font_ID)\Crop_Bottom_Ratio < 1
          Crop_Bottom = *Fonts(Font_ID)\Character[c]\Height * *Fonts(Font_ID)\Crop_Bottom_Ratio
        EndIf
        If *Fonts(Font_ID)\Crop_Left_Ratio > 0  And *Fonts(Font_ID)\Crop_Left_Ratio < 1
          Crop_Left = *Fonts(Font_ID)\Character[c]\Width * *Fonts(Font_ID)\Crop_Left_Ratio
        EndIf
        If *Fonts(Font_ID)\Crop_Right_Ratio > 0 And *Fonts(Font_ID)\Crop_Right_Ratio < 1
          Crop_Right = *Fonts(Font_ID)\Character[c]\Width * *Fonts(Font_ID)\Crop_Right_Ratio
        EndIf
        *Fonts(Font_ID)\Character[c]\Width = *Fonts(Font_ID)\Character[c]\Width - (Crop_Left + Crop_Right)
        *Fonts(Font_ID)\Character[c]\Height = *Fonts(Font_ID)\Character[c]\Height - (Crop_Top + Crop_Bottom)        
        *Fonts(Font_ID)\Character[c]\Sprite_ID = CreateSprite(#PB_Any, *Fonts(Font_ID)\Character[c]\Width, *Fonts(Font_ID)\Character[c]\Height, #PB_Sprite_AlphaBlending)
        If Not *Fonts(Font_ID)\Character[c]\Sprite_ID
          Debug "CreateFont: sprite creation failed"
          Debug "It must be run after the following:"
          Debug "- InitSprite()"
          Debug "- OpenScreen() or OpenWindowedScreen()"
          ProcedureReturn 0
        EndIf
        If StartDrawing(SpriteOutput(*Fonts(Font_ID)\Character[c]\Sprite_ID))
          DrawingMode(#PB_2DDrawing_AllChannels)
          DrawingFont(FontID(Font_ID))
          DrawText(-Crop_Left, -Crop_Top, Char, *Fonts(Font_ID)\Colour, *Fonts(Font_ID)\Back_Colour)
          If *Fonts(Font_ID)\Border
            DrawingMode(#PB_2DDrawing_AllChannels | #PB_2DDrawing_Outlined)
            Box(0, 0, *Fonts(Font_ID)\Character[c]\Width, *Fonts(Font_ID)\Character[c]\Height, *Fonts(Font_ID)\Border_Colour)
          EndIf
          StopDrawing()
        EndIf  
        *Fonts(Font_ID)\Character[c]\Image_ID = CreateImage(#PB_Any, *Fonts(Font_ID)\Character[c]\Width, *Fonts(Font_ID)\Character[c]\Height, 32)          
        If StartDrawing(ImageOutput(*Fonts(Font_ID)\Character[c]\Image_ID))
          DrawingMode(#PB_2DDrawing_AllChannels)
          DrawingFont(FontID(Font_ID))
          DrawText(-Crop_Left, -Crop_Top, Char, *Fonts(Font_ID)\Colour, *Fonts(Font_ID)\Back_Colour)
          If *Fonts(Font_ID)\Border
            DrawingMode(#PB_2DDrawing_AllChannels | #PB_2DDrawing_Outlined)
            Box(0, 0, *Fonts(Font_ID)\Character[c]\Width, *Fonts(Font_ID)\Character[c]\Height, *Fonts(Font_ID)\Border_Colour)
          EndIf
          StopDrawing()          
        EndIf
        *Fonts(Font_ID)\Character[c]\Loaded = 1
      EndIf
      *Fonts(Font_ID)\Loaded = 1
    Next c
    ProcedureReturn 1
  EndProcedure
  
  Procedure DisplayCharacterSprite(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s, x.i, y.i)
    Protected ASCII.i = Asc(Char)
    Protected.i CharNum = GetCharNum(Char)
    If *Fonts(Font_ID)\Character[CharNum]\Loaded
      DisplayTransparentSprite(*Fonts(Font_ID)\Character[CharNum]\Sprite_ID, x, y)
    EndIf
  EndProcedure
  
  Procedure DisplayCharacterImage(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s, x.i, y.i)
    ; draws a character to the output as an image
    Protected.i ASCII = Asc(Char)
    Protected.i CharNum = GetCharNum(Char)
    If *Fonts(Font_ID)\Character[CharNum]\Loaded
      DrawAlphaImage(ImageID(*Fonts(Font_ID)\Character[CharNum]\Image_ID), x, y)
    EndIf
  EndProcedure  
  
  Procedure.i GetCharacterWidth(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s)
    Protected.i ASCII = Asc(Char)
    Protected.i Return_Width = 0
    Protected.i CharNum = GetCharNum(Char)
    If *Fonts(Font_ID)\Character[CharNum]\Loaded
      Return_Width = *Fonts(Font_ID)\Character[CharNum]\Width
    Else
      Return_Width = 0
    EndIf
    ProcedureReturn Return_Width
  EndProcedure
  
  Procedure.i GetCharacterHeight(Array *Fonts.Font_Structure(1), Font_ID.i, Char.s)
    Protected.i ASCII = Asc(Char)
    Protected.i CharNum = GetCharNum(Char)
    Protected.i Return_Height = 0
    If *Fonts(Font_ID)\Character[CharNum]\Loaded
      Return_Height = *Fonts(Font_ID)\Character[CharNum]\Height
    Else
      Return_Height = 0
    EndIf
    ProcedureReturn Return_Height
  EndProcedure    
  
  Procedure DisplayStringSprite(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s, x.i, y.i, Spacing.i=0)
    ; Displays the string on the screen using sprites
    ; Spacing is used to monospace the text
    Protected.i ASCII
    Protected.i Width
    Protected.i CharNum
    Protected.i i
    Structure Text_Array_Structure
      Index.u[0]
    EndStructure
    Protected *Text_Array.Text_Array_Structure = @Text
    For i = 0 To Len(Text)-1
      ASCII = *Text_Array\Index[i]
      CharNum = GetNum(ASCII)
      If *Fonts(Font_ID)\Character[CharNum]\Loaded
        DisplayTransparentSprite(*Fonts(Font_ID)\Character[CharNum]\Sprite_ID, x, y)
        Width = *Fonts(Font_ID)\Character[CharNum]\Width
      EndIf
      If Spacing > 0
        x = x + Spacing
      Else
        x = x + Width
      EndIf
    Next i
  EndProcedure
  
  Procedure DisplayStringImage(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s, x.i, y.i, Spacing.i=0)
    ; Displays the string to the output using images
    ; Spacing is used to monospace the text
    Protected.s Char
    Protected.i Width
    Protected.i CharNum
    For i = 1 To Len(Text)
      Char = Mid(Text, i, 1)
      CharNum = GetCharNum(Char)
      If *Fonts(Font_ID)\Character[CharNum]\Loaded
        DrawAlphaImage(ImageID(*Fonts(Font_ID)\Character[CharNum]\Image_ID), x, y)
        Width = *Fonts(Font_ID)\Character[CharNum]\Width
      EndIf
      If Spacing > 0
        x = x + Spacing
      Else
        x = x + Width
      EndIf
    Next i    
  EndProcedure 
  
  Procedure.i GetStringWidth(Array *Fonts.Font_Structure(1), Font_ID.i, Text.s)
    Protected.i Return_Width = 0
    Protected.s Char
    Protected.i CharNum
    Protected.i i
    Return_Width = 0
    For i = 1 To Len(Text)
      Char = Mid(Text, i, 1)
      CharNum = GetCharNum(Char)      
      If *Fonts(Font_ID)\Character[CharNum]\Loaded
        Return_Width = Return_Width + *Fonts(Font_ID)\Character[CharNum]\Width
      EndIf
    Next i
    ProcedureReturn Return_Width
  EndProcedure  
  
  Procedure Shutdown(Array *Fonts.Font_Structure(1))
    ; Note: the screen needs to be open for this to work
    Protected.i c1
    Protected.i c2
    For c1 = 0 To #Max_Fonts
      For c2 = 0 To #Font_Num_Characters-1
        If *Fonts(c1)\Character[c2]\Loaded
          FreeSprite(*Fonts(c1)\Character[c2]\Sprite_ID)
          FreeImage(*Fonts(c1)\Character[c2]\Image_ID)
        EndIf
      Next c2
      If *Fonts(c1)\Loaded:FreeFont(c1):EndIf
      FreeStructure(*Fonts(c1))
    Next c1
  EndProcedure
  
  DataSection
    Font_Characters_Numbers:
    Data.s "0","1","2","3","4","5","6","7","8","9" ; 0-9
    Font_Characters_Alphabetic_Uppercase:
    Data.s "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z" ; 10-35
    Font_Characters_Alphabetic_Lowercase:
    Data.s "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z" ; 36-61
    Font_Characters_Symbol_1:
    Data.s " ","!",#DQUOTE$,"#","$","%","&","'","(",")","*","+",",","-",".","/" ; 62-77
    Font_Characters_Symbol_2:
    Data.s ":",";","<","=",">","?","@" ; 78-84
    Font_Characters_Symbol_3:
    Data.s "[","\","]","^","_","`" ; 85-90
    Font_Characters_Symbol_4:
    Data.s "{","|","}","~" ; 91-94
  EndDataSection
  
EndModule

#Balls = 10
#Max_Monitors_Available = 32
#Max_FPS_Samples = 500; maximum number of FPS samples
Structure Monitor_Structure
  Frequency.i
  Width.i
  Height.i
  Name.s
  Combo_Item.i
  X.i
  Y.i
EndStructure
Structure Coords_Structure
  x.i
  y.i
  Direction_X.i
  Direction_Y.i
  Speed.d
EndStructure
InitSprite()
Fonts::Initialise(Fonts::*Fonts())
With Fonts::*Fonts(0)
  \Name = "Consolas"
  \Size = 72
  \Crop_Bottom_Ratio = 0.15
  \Crop_Top_Ratio = 0.13
  \Colour = RGBA(255, 255, 255, 255)
  \Back_Colour = RGBA(0, 0, 0, 0)
  \Available = Fonts::#Font_Characters_Numbers | Fonts::#Font_Characters_Select
  \Select_Available = " FPSrameTim"
  \Border = 0
  \Border_Colour = RGBA(255, 0, 0, 255)
EndWith
Dim Monitors.Monitor_Structure(#Max_Monitors_Available)
Dim Balls.Coords_Structure(#Balls)
Dim FPS_Sample.i(#Max_FPS_Samples) ; array for holding FPS samples in milliseconds
Define.i Sprite_Size = 200
Define.i Radius = Sprite_Size/2-1
Define.i Num_Monitors = ExamineDesktops()
Define.i Monitor = 0
Define.i c
For c = 0 To Num_Monitors-1
  Monitors(c)\Width = DesktopWidth(c)
  Monitors(c)\Height = DesktopHeight(c)
  Monitors(c)\Name = DesktopName(c)
  Monitors(c)\X = DesktopX(c)
  Monitors(c)\Y = DesktopY(c)
  Monitors(c)\Frequency = DesktopFrequency(c)
Next c
OpenWindow(0, 100, 100, 300, 200, "Select Monitor")
Define.i Select_Monitor_Text = TextGadget(#PB_Any, 50, 20, 200, 20, "Select a monitor:")
Define.i Combo_Box = ComboBoxGadget(#PB_Any, 50, 40, 200, 30)
For c = 0 To Num_Monitors-1
  Define.s Text = Str(c)+":"+Monitors(c)\Name+" - "+Monitors(c)\Width+" x "+Monitors(c)\Height
  Monitors(c)\Combo_Item = AddGadgetItem(Combo_Box, c, Text)
Next c
SetGadgetState(Combo_Box, 0)
Define.i Button_OK = ButtonGadget(#PB_Any, 50, 120, 80, 30, "OK")
Define.i Button_Cancel = ButtonGadget(#PB_Any, 170, 120, 80, 30, "Cancel")
Define.i Event
Define.i Quit
Define.i Gadget
Define.i Selected
Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_CloseWindow
      Quit = 1      
    Case #PB_Event_Gadget
      Gadget = EventGadget()
      Select Gadget
        Case Button_Cancel
          Quit = 1
        Case Button_OK
          Selected = 1
      EndSelect
  EndSelect
Until Event = #PB_Event_CloseWindow Or Quit Or Selected
If Quit
  End
EndIf
Monitor = GetGadgetState(Combo_Box)
OpenWindow(0, Monitors(Monitor)\X, Monitors(Monitor)\Y, Monitors(Monitor)\Width, Monitors(Monitor)\Height, "FPS Test", #PB_Window_Maximize | #PB_Window_BorderLess)
Define.i WindowWidth = WindowWidth(0)*DesktopResolutionX()
Define.i WindowHeight = WindowHeight(0)*DesktopResolutionY()
OpenWindowedScreen(WindowID(0), 0, 0, WindowWidth, WindowHeight, #False, 0, 0, #PB_Screen_WaitSynchronization)
Fonts::CreateFont(Fonts::*Fonts(), 0)
For c = 0 To #Balls-1
  CreateSprite(c, Sprite_Size, Sprite_Size, #PB_Sprite_AlphaBlending)
  If StartDrawing(SpriteOutput(c))
    DrawingMode(#PB_2DDrawing_AllChannels)
    Circle(Sprite_Size/2, Sprite_Size/2, Radius, RGBA(Random(255, 0), Random(255, 0), Random(255, 0), 150))
    StopDrawing()
    Balls(c)\x = Random(WindowWidth-Sprite_Size, 0)
    Balls(c)\y = Random(WindowHeight-Sprite_Size, 0)
    Balls(c)\Direction_X = Random(1, 0)
    Balls(c)\Direction_Y = Random(1, 0)
    Balls(c)\Speed = Random(5, 1)
  EndIf
Next c
InitKeyboard()
Define.i FPS_Samples = Monitors(Monitor)\Frequency
Define.q FPS_Begin_Time = ElapsedMilliseconds()
Define.q FPS_Last_Frame_Time
Define.i FPS_Average_Index
Define.d FPS_Average_Sum
Define.d FPS
Define.i Start_Time
Define.i FPS_Initialised
Define.q Frame_Time
Define.i Char_Height = Fonts::GetCharacterHeight(Fonts::*Fonts(), 0, "0")
Define.i Char_Spacing = 4
Repeat
  ClearScreen(#Black)
  Start_Time = ElapsedMilliseconds()
  FPS_Last_Frame_Time = ElapsedMilliseconds() - FPS_Begin_Time
  FPS_Begin_Time = ElapsedMilliseconds()
  FPS_Average_Sum = FPS_Average_Sum - FPS_Sample(FPS_Average_Index)
  FPS_Average_Sum = FPS_Average_Sum + FPS_Last_Frame_Time
  FPS_Sample(FPS_Average_Index) = FPS_Last_Frame_Time
  FPS_Average_Index = FPS_Average_Index + 1
  If FPS_Average_Index = FPS_Samples
    FPS_Average_Index = 0
    FPS_Initialised = 1
  EndIf
  If FPS_Initialised
    FPS = 1000 / (FPS_Average_Sum / FPS_Samples)
  EndIf    
  For c = 0 To #Balls-1
    DisplayTransparentSprite(c, Balls(c)\x, Balls(c)\y)
    If Balls(c)\Direction_X = 1
      Balls(c)\x = Balls(c)\x + Balls(c)\Speed
    Else
      Balls(c)\x = Balls(c)\x - Balls(c)\Speed
    EndIf
    If Balls(c)\Direction_Y = 1
      Balls(c)\y = Balls(c)\y + Balls(c)\Speed
    Else
      Balls(c)\y = Balls(c)\y - Balls(c)\Speed
    EndIf
    If Balls(c)\x > WindowWidth-Sprite_Size
      Balls(c)\Direction_X = 0
      Balls(c)\x = (WindowWidth-Sprite_Size) - (Balls(c)\x - (WindowWidth-Sprite_Size))
    EndIf
    If Balls(c)\x < 0
      Balls(c)\Direction_X = 1
      Balls(c)\x = 0 - Balls(c)\x
    EndIf
    If Balls(c)\y > WindowHeight-Sprite_Size
      Balls(c)\Direction_Y = 0
      Balls(c)\y = (WindowHeight-Sprite_Size) - (Balls(c)\y - (WindowHeight-Sprite_Size))
    EndIf
    If Balls(c)\y < 0
      Balls(c)\Direction_Y = 1
      Balls(c)\y = 0 - Balls(c)\y
    EndIf
  Next c
  Fonts::DisplayStringSprite(Fonts::*Fonts(), 0, "FPS:  "+FormatNumber(FPS, 0), 0, 0)
  Fonts::DisplayStringSprite(Fonts::*Fonts(), 0, "Frame:"+Str(FPS_Average_Index), 0, Char_Height + Char_Spacing)
  Fonts::DisplayStringSprite(Fonts::*Fonts(), 0, "Time: "+Str(Frame_Time), 0, 2*Char_Height + Char_Spacing)
  FlipBuffers()
  Frame_Time = ElapsedMilliseconds() - Start_Time
  Event = WindowEvent()
  ExamineKeyboard()
Until Event = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)
CloseScreen()