Seite 1 von 2

FlipBuffers() ... ein Resourcenfresser ???

Verfasst: 09.10.2005 15:44
von PureLust
Hallo zusammen, ...

könnte es sein, dass FlipBuffers() beim Warten auf den Bildschirmsync nicht resourcenschonend wartet und die Kontrolle solange abgibt, sonden zu 100% die CPU auslastet ??? :shock:

Ist natürlich eine nicht besonders schöne Art und Weise ... /:->

Gibt es evtl. irgenwie eine andere Möglichkeit den Sync abzufangen (evtl. über Interrupts oder über 'nen Event)?

Oder gibt es vielleicht eine Möglichkeit die aktuelle Rasterzeile abzufragen?
So könnte man nach dem Zeichnen evtl. einen Delay() einbauen, der kurz vor dem Sync zu ende ist und anschließend einen FlipBuffers(1) machen.

Siehe CPU Belastung mit und ohne Delay() bei gleicher Framerate im Beispiel:

Code: Alles auswählen

InitSprite()
InitKeyboard()

StartTimer = ElapsedMilliseconds()-1
FrameCounter.l = 0
If OpenWindow(1,100,100,320,240,0,"jhg")
   If OpenWindowedScreen(WindowID(1),0,0,320, 240, 16,0,0)
      StartTimer = ElapsedMilliseconds()-1
      Repeat
         FrameCounter + 1
         StartDrawing(ScreenOutput()) 
         DrawText("   FPS : "+Str(FrameCounter *1000 / (ElapsedMilliseconds() - StartTimer))+"    ")
         StopDrawing() 
         ;Angenommen mann könnte die aktuelle Rasterzeile auslesen,
         ;so könnte man die Zeit bis zum nächsten Sync berechnen und durch
         ;einen entsprechenden Delay() auffüllen, ohne die Framerate zu beeinflussen.
         Delay(8)    ; ohne Delay() ist die CPU-Belastung durch FlipBuffers() = 100%
         FlipBuffers(1)
         ExamineKeyboard()
      Until KeyboardReleased(#PB_Key_Escape) 
   EndIf
EndIf
End
Thanks und Greetz, Luke.

Verfasst: 09.10.2005 15:55
von MLK
interessant dass du FlipBuffer() beschuldigst. es liegt aber wohl eher an der schleife an sich.

Code: Alles auswählen

Repeat
Until GetAsyncKeyState_(#VK_RETURN)
ein Delay(1 oder 3 oder ...) ist bei einer hauptschleife mit FlipBuffers() standard mit d.

Verfasst: 09.10.2005 16:40
von PureLust
Hi MLK, ...

an der Schleife lag as nicht, da KeyboardReleased() ja ohnehin nur nach jedem Sync ausgeführt wird, kann es auch nicht solche Auswirkungen haben.

Ich kreise das Problem deswegen bei FlipBuffers() ein, weil er ja bis zum nächsten Sync schlicht und ergreifend nur WARTEN soll, was er ja auch tut - dies jedoch anscheinend durch eine Dauerschleife macht und sich eben nicht unbedingt multitasking- und resourcenfreundlich verhält.

Irgendwie wird FlipBuffers() ja an die Sync-Informationen heran kommen und auswerten.
Leider scheint er danach die Wartezeit nun mal sinnlos in einer Dauerschleife zu verbringen.

Wenn man also ebenfalls an diese Infos herankommen könnte, so wäre es denkbar, einen Ersatz für FlipBuffers zu schreiben, bei der die Wartezeit halt eben nicht sinnlos 'verbraten' wird.

Und es macht AFAIK schon einen Unterschied ob man die Kontrolle an das OS für 1ms oder für 10ms abgibt. Somit wäre eine exakte Berechnung der optimalen Delay()-Zeit schon resourceschonender als pauschal ein Delay(1) zu machen.

In diesem Sinne, ... Greetze, Luke.

Verfasst: 09.10.2005 16:59
von glubschi90
Ich würde sagen, dass FlipBuffers()deshalb soviel CPU-Auslastung benötigt, weil der Befehl ja nicht nur dazu da ist, die Framerate zu regulieren, sondern auch den Backbuffer mit dem FrontBuffer und umgekehrt vertauscht.
Das braucht natürlich mehr Leistung, als nur eine Wartefunktion.

Verfasst: 09.10.2005 17:06
von Danilo
PureLust hat geschrieben:Ich kreise das Problem deswegen bei FlipBuffers() ein, weil er ja bis zum nächsten Sync schlicht und ergreifend nur WARTEN soll, was er ja auch tut - dies jedoch anscheinend durch eine Dauerschleife macht und sich eben nicht unbedingt multitasking- und resourcenfreundlich verhält.
FlipBuffers(1) ruft intern auch nur IDirectDrawSurface7::Flip()
mit dem Parameter DDFLIP_WAIT auf. Damit wird der Flip
synchronisiert und DirectX kehrt erst zurück wenn der Flip
durchgeführt wurde. Das heißt nicht PB wartet hier, sondern
DirectX.

Die Scanline kannste mit IDirectDraw7::GetScanLine() bekommen,
falls Du Dir da selbst was basteln willst.

Eine Überlegung wäre vielleicht:

Code: Alles auswählen

While IDirectDrawSurface7::GetFlipStatus(#DDGFS_CANFLIP) <> #DD_OK
  Delay(1)
Wend
IDirectDrawSurface7::Flip(...)
Oder:

Code: Alles auswählen

IDirectDrawSurface7::Flip(0, DDFLIP_DONOTWAIT)
While IDirectDrawSurface7::GetFlipStatus(#DDGFS_ISFLIPDONE) <> #DD_OK
  Delay(1)
Wend
Das loopt solange bis der Flip ausgeführt ist. Auf die Errors
wie DDERR_SURFACELOST usw. muß man dabei natürlich
noch checken, es zeigt aber das Prinzip wie es gehen könnte.

1 Millisekunde ist eine ziemlich lange Zeit. Aber oftmals wird
Delay() nicht so schnell zurück kommen, wenn es zu einem
anderen Prozess switcht der auch bissl Power zieht.
D.h. Du hättest dann evtl. einen Delay von vielleicht 5ms,
was einfach zu lange ist. Delay/Sleep gibt ja nur an das Du
mindestens diese Zeit warten willst, garantiert aber nicht das
es auch nur maximal soviel Zeit ist.

Verfasst: 09.10.2005 17:28
von PureLust
Hi Danilo, ...

vielen Dank, das ist genau was ich gesucht hatte !!! :allright:
Und auch die Problematik mit dem 'nicht rechtzeitig zurück kommenden' Delay() hast Du natürlich richtig gesehen.

Jetzt hab ich nur noch ein Problem:
Ich kann leider mit Deinen Beispielen, oder besser gesagt mit IDirectDrawSurface7::GetFlipStatus(#DDGFS_CANFLIP) nix anfangen.

GetFlipStatus() wird von der IDE nicht erkannt.
Fehlt mir irgend eine Lib oder benötige ich dazu einen Include???

BTW: Wollte mich nochmal für Deine PM vom Juli bedanken !!!
Wie Du siehst bin ich letztendlich doch bei Pure gelandet !!! ;)


ThanX & Gruß, Luke

Verfasst: 09.10.2005 19:12
von AndyX
btw: StartDrawing() etc und Plot() ohne Farbparameter ist auch n Speedbremser...

Verfasst: 09.10.2005 19:35
von Danilo
PureLust hat geschrieben:Jetzt hab ich nur noch ein Problem:
Ich kann leider mit Deinen Beispielen, oder besser gesagt mit IDirectDrawSurface7::GetFlipStatus(#DDGFS_CANFLIP) nix anfangen.

GetFlipStatus() wird von der IDE nicht erkannt.
Fehlt mir irgend eine Lib oder benötige ich dazu einen Include???
Das war nur Pseudo-Code für die DX-Interfaces.

Hab mal ein FlipBuffers_(sync) gemacht:

Code: Alles auswählen

;
; Danilo, 09.10.2005
;
Procedure FlipBuffers_(sync)
  #DDFLIP_WAIT       = $1
  #DDFLIP_NOVSYNC    = $8
  #DDFLIP_DONOTWAIT  = $20
  #DD_OK             = 0
  #DDGFS_ISFLIPDONE  = $00000002
  Shared FlipBuffers_Shared_DDS_Front.IDirectDrawSurface7
  !extrn _PB_DirectX_PrimaryBuffer
  !PUSH  dword [_PB_DirectX_PrimaryBuffer]
  !POP   dword [v_FlipBuffers_Shared_DDS_Front]
  
  If sync
     sync = #DDFLIP_WAIT
  Else
     sync = #DDFLIP_NOVSYNC
  EndIf
  
  hRet = FlipBuffers_Shared_DDS_Front\Flip(0, sync)
  ;If hRet = #DDERR_SURFACELOST
  ;   FlipBuffers_Shared_DDS_Front\Restore()
  ;EndIf

  If sync <> #DDFLIP_NOVSYNC
    Delay(1)
  EndIf

  While FlipBuffers_Shared_DDS_Front\GetFlipStatus(#DDGFS_ISFLIPDONE) <> #DD_OK
    Delay(1)
  Wend
  InvalidateRect_(ScreenID(),0,0)
  UpdateWindow_(ScreenID())  
EndProcedure


If InitSprite()=0 Or InitKeyboard()=0
  MessageRequester("ERROR","Cant init DirectX !",0):End
EndIf


If OpenWindow(1,100,100,320,240,#PB_Window_SystemMenu,"jhg")
  If OpenWindowedScreen(WindowID(1),0,0,320,240,1,0,0)
    If CreateSprite(1,32,32)=0
      End
    Else
      If StartDrawing(SpriteOutput(1))
        Box(0,0,32,32,RGB($00,$00,$FF))
        StopDrawing()
      EndIf
    EndIf
    StartTimer   = ElapsedMilliseconds()
    Repeat
      time = ElapsedMilliseconds()
      If time >= StartTimer+1000
        StartTimer   = time
        FPS          = FrameCounter
        FrameCounter = 0
      Else
        FrameCounter + 1
      EndIf

      ClearScreen(0,0,0)

      If StartDrawing(ScreenOutput())
        DrawingMode(1)
        FrontColor($FF,$FF,$00)
        DrawText(Str(FPS))
        Circle(160,120,x,$FFFFFF)
        x-1
        If x = -1: x = 120:EndIf
        StopDrawing()
      EndIf
      
      y+1
      DisplaySprite(1,160-16,y)
      If y>=240+32:y=-32:EndIf

      FlipBuffers_(1)
      ExamineKeyboard()
      If WindowEvent() = #PB_Event_CloseWindow
        quit = 1
      EndIf
    Until KeyboardReleased(#PB_Key_Escape) Or quit
  EndIf
EndIf
Bei FlipBuffers_(1) bekomme ich hier 64 FPS, bei FlipBuffers_(0)
sind es etwas über 400 FPS.
Beim PB-FlipBuffers(0) synchronisiert es bei mir hier trotzdem,
d.h. ich bekomme da auch nur ~60FPS und nicht 400 wie bei
meinem FlipBuffers_(0)

Mit Synchronisation ist die CPU-Auslastung hier 0%.

Irgendwie wundert mich das alles jetzt etwas. Ich weiß nicht
ob das PB-FlipBuffers noch irgend etwas anderes macht.
Wenn es so einfach ist wie in meinem Code, dann verstehe ich
jetzt auch nicht ganz wieso es bei PB soviel CPU-Auslastung hat.
Irgendwas ist da doch faul... hmm.

BTW: Wenn Du mit WindowedScreens arbeitest muß unbedingt
ein WindowEvent() mit in die Schleife, sonst sammeln sich die
Nachrichten in der Queue, bis es zum Überlauf kommt.

Verfasst: 09.10.2005 19:39
von Andre
Habe Fred mal gebeten, sich das hier beschriebene Problem näher anzusehen. 8)

Verfasst: 10.10.2005 01:24
von PureLust
@Danilo:
Danilo hat geschrieben:Bei FlipBuffers_(1) bekomme ich hier 64 FPS, bei FlipBuffers_(0)
sind es etwas über 400 FPS.
Beim PB-FlipBuffers(0) synchronisiert es bei mir hier trotzdem,
d.h. ich bekomme da auch nur ~60FPS und nicht 400 wie bei
meinem FlipBuffers_(0)
Hmmm ... das ist scheinbar wohl eine Eigenart von Windowed-Screens. :roll:

Bei Windowed-Screens syncronisiert mir PB-FlipBuffer() ebenfalls IMMER auf 60FPS bei 100% CPU-Last.
Bei FullScreens auf 60 bzw. garnicht (ca. 4900FPS) bei ebenfalls 100% CPU-Last.

Bei Deinem FlipBuffers_ erhalte ich bei mir im Windowed-Screen Modus 99FPS bei 0% CPU-Last bzw. ~2250FPS bei 100% CPU-Last.
Im FullScreen Modus erhalte ich hingegen die korrekten 60FPS bei ~40% bzw. ~4450FPS bei 100%.

Ich hatte mich natürlich gefragt, wo die 99FPS im Windowed-Screen Modus herkommen.
Nach einigem Rumprobieren habe ich dann festgestellt, dass ich durch das origianl PB-FlipBuffers(0) mit einem Delay(1) im FullScreen Modus die gleichen Werte erhalte (also 99FPS und 0% Auslastung).
Mit gleichen Parametern erhalte ich im Windowed-Screenmode jedoch die besagten 60FPS mit ~40% CPU Last - also die gleichen Werte wie bei Dir im FullScreen Mode.

Also alles in allem wohl ein recht undurchsichtiges bzw. verdrehtes Verhalten. /:->

PS: Hab ich Deine Routine richtig verstanden, dass sie im Grunde effektiv nichts weiter macht als ein Delay(1) einzufügen wenn (1) als Parameter übergeben wird (mal abgesehen davon, dass sie darauf warten würde, wenn der Flip noch nicht vollzogen ist: FlipBuffers_Shared_DDS_Front\GetFlipStatus(#DDGFS_ISFLIPDONE) <> #DD_OK)?