VSync

  • VB.NET

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von Krissel095.

    Dann musst du deine zeichenroutine (die zu langsam ist) optimieren oder eine alternative finden. Schnelleres zeichnen ist per gdi nicht möglich...

    EDIT:

    ein Threading.Thread.Sleep(10) eingebaut, damit die FPS ungefähr bei 60 liegen

    Ausführungszeit Zeichenroutine = (1000 - 60*10) / 60 = 6,6ms

    In deiner Threaded-Variante wird also ein backbuffer (bitmap) bezeichnet und dieser dann ausgegeben. das erklärt das hier:
    Da habe ich ~6 Frames pro Sekunde

    Dieses Ergebnis kannst du nicht mit meiner Methode vergleichen....
    Das ist genauso schlimm wie die Threaded-Variante


    ..du siehst nur, dass das zeichnen des backbufferst unperformant ist. Zeichne NICHT auf einen backbuffer. Die Zeichenroutine soll direkt auf das graphics-objekt zeichnen und nicht auf ieine bitmap!
    Nach Adam Riese braucht deine Grahpics-Zeichenmethode ca. 7ms. Um 60 FPS konstant zu halten sind da nohc knapp 10ms Luft nach oben. Um nicht mehr als 60FPS zu erreichen wird durch meine Klasse das neuzeichnen verzögert. Braucht die Zeichenmehtode mal länger wird das ganze automatisch angepasst um eine konstante Framerate zu erhalten
    Da der Backbuffer hier extrem viele "ms frisst": weglassen!

    Dabei habe ich leider keinen Ansatz, wie ich es anstellen könnte die FPS gescheit konstant zu halten.

    Da habe ich dir ja den quellcode gegeben, nur den backbuffer vergessen (bedenke, dass alle 1/60Sekunde neugezeichnet wird, da braucht man keinen backbuffer)^^

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „FreakJNS“ ()

    Hi
    erstelle doch einfach einen Puffer mit BufferedGraphicsManager.Current.Allocate oder eigenem Kontext statt Current. Anschließend zeichnest du die neu zu zeichnenden (nicht alle) Elemente in den BufferedGraphics (z.B. wenn sie ihre Position ändern). Ich würde für die zu zeichnenden Elemente übrigens ein Interface verwenden, das halt eine Sub Render(ByVal surface As Graphics, ByVal clipBounds As Rectangle) und eine Property Bounds As Rectangle unterstützt und entsprechende Aktionen durchführt. Beim invalidieren von Bereichen kannst du ja ein Rechteck-Array angeben, das die neuzuzeichnenden Bereiche angibt.
    Auch wenn mich dafür alle schlagen: den Puffer kannst du auf CreateGraphics erzeugen und außerhalb der Paint-Methode behalten. Wenn gewünscht, kannst du den Puffer auch beim Vergrößern der Form neu erzeugen. Beim Auslösen des Paint-Events kannst du dann einfach den Puffer in e.Graphics rendern. Rendern würde ich in einem separaten Thread und die Daten eben bei Bedarf erst kopieren. So ist auch VSync gewährleistet.
    Zum zeitgesteuerten Wiederholen kannst du einfach eine StopWatch mit Sleep verwenden, da threadübergreifende Vorgänge stattfinden, wäre evtl. ein AutoResetEvent auch gut und halt Mointor zum Kontrollieren der Zugriffe. Schau dir das einfach mal an und überleg dir, wie du das in Kombination mit BufferedGraphics benutzen kannst.

    Evtl. interessiert dich das hier auch:
    [VB 2008] Scheduled Action - Aktionen zeitgesteuert aufrufen (wartet auf die vorhergehende Aktion)

    Gruß
    ~blaze~
    Falls es dich interessiert, wie das ganze (rein theoretisch) in Dots abläuft:

    In Dots läuft parallel zum GUI Thread ein weiterer GameLoop Thread, der sich innerhalb der GameLoop-Klasse befindet. Ich verwende außerdem eine eigene GameTime-Klasse, die die Properties ElapsedGameTime und TotalGameTime besitzt. Der GameLoop läuft so ab (Pseudocode):

    Quellcode

    1. 2 GameTime Variablen: renderTime, tickTime
    2. Solange aktiv:
    3. loopStart = aktuelle Zeit
    4. tickTime aktualisieren
    5. Game.Tick aufrufen
    6. renderTime aktualisieren
    7. Wenn der FrameLimiter aktiv ist und weniger Zeit verstrichen ist, als die Zielframerate, die erreicht werden soll:
    8. Game.Render aufrufen
    9. Solange loopStart - aktuelle Zeit < Zielframerate
    10. nichts tun



    Das Spiel wird dabei in eine Bitmap gerendert und das Paint-Event wartet auf die Fertigstellung des Bitmap-Rendervorgangs. Ist der Vorgang abgeschlossen, wird die Bitmap per Fast-Copy auf das GameSurface (in dem Fall ein UserControl, das DoubleBuffering aktiviert hat und unter anderem Maus und Tastatur an Wrapper-Klassen weiterleitet).

    Quellcode

    1. e.Graphics.CompositingMode = CompositingMode.SourceCopy
    2. e.Graphics.CompositingQuality = CompositingQuality.HighSpeed
    3. e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
    4. e.Graphics.SmoothingMode = SmoothingMode.HighSpeed
    5. e.Graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed
    6. e.Graphics.DrawImageUnscaled(Me.backBuffer, 0, 0, Me.backBuffer.Width, Me.backBuffer.Height)



    Aus Performancegründen wurde der Frame Limiter in der aktuellen Version entfernt, da einfach die maximale Anzahl an Frames, die erreicht werden können zum rendern verwendet werden. Ein weiterer Tipp: Das wichtigste ist der Einbezug einer Zeit-Variable, in meinem Fall GameTime. Du solltest deine Objekte immer in Abhängigkeit der verstrichenen Zeit bewegen, da sich sonst bei FrameRate-Einbrüchen oder hohen FrameRates deine Objekte entweder langsamer, oder schneller bewegen.

    Ich hoffe das hat dir geholfen ;)

    MfG
    Okay..das mit den BufferedGraphics habe ich soweit...nur leider bekomme ich sehr viele InvalidOperation-Exceptions...
    fragt mich nicht warum :P

    VB.NET-Quellcode

    1. Dim renderer As BufferedGraphics
    2. Sub Form_Shown()
    3. renderer = BufferedGraphicsManager.Current.Allocate(Me.CreateGraphics(), Me.ClipRectangle)
    4. renderThread = New Threading.Thread(AddressOf render)
    5. renderThread.Start()
    6. End Sub
    7. Sub Form_Paint()
    8. halt.Reset() ' ManualResetEvent
    9. Try
    10. renderer.Render()
    11. Catch
    12. End Try
    13. halt.Set()
    14. Threading.Thread.Sleep(10)
    15. Me.Invalidate()
    16. End Sub
    17. Sub render()
    18. Do
    19. halt.Wait()
    20. Try
    21. Dim g = renderer.Graphics()
    22. ' Hier zeichen Code
    23. halt.Wait()
    24. Loop
    25. End Sub

    Nun..das flackert son bisschen..wahrsch. wegen Framedrops beim Paint-Loop.
    Könnte mir das jemand etwas näher bringen? (Styles sind OptimizedDoubleBuffer)
    Hast du meinen Beitrag dazu nicht gelesen? Thread.Sleep ist so ziemlich das schlechteste, was man für einen Gameloop verwenden kann. Die Sleep funktioniert garantiert nämlich nicht zu 100%, dass dein Thread nach exakt der angegebenen Zeit wieder "aufwacht". Google mal nach Busy Waiting, das dürfte eher was für dich sein. Außerdem solltest du keinen Festbetrag warten, sondern in Abhängigkeit der benötigten Renderzeit die Restzeit warten, oder eben eine weitere While-Schleife zum warten verwenden.

    Ein Gameloop besteht zu dem auch nicht nur aus einem Render-Aufruf, du solltest deine Logik seperat implementieren und dieser oberste Priorität verschaffen, damit dein Spiel auch bei Einbruch der Grafik in eine festen Geschwindigkeit läuft.

    MfG
    lulz.
    Ich hab ühaupt keinen GameLoop :P
    Das ist der Drawing-Teil.
    Somit: fail?
    Ich handle alles mit Events, sodass die Spiele mit dem Hauptprogramm kommunizieren können.
    Also...Maus-Move: RaiseEvent
    KeyDown: RaiseEvent
    Programm schließen? RaiseEvent!
    Was das Plugin damit dann macht, interessiert mich nicht.
    Die Interfaces bestehen daraus:

    VB.NET-Quellcode

    1. Public Interface In1000Interface
    2. #Region "Propertys"
    3. ReadOnly Property Version As Version
    4. ReadOnly Property Name As String
    5. ReadOnly Property Creater As String
    6. ReadOnly Property Image As Image
    7. ReadOnly Property Description As String
    8. ReadOnly Property PluginType As EPluginType
    9. Property IdentityNumber As Guid
    10. #End Region
    11. #Region "Enums"
    12. Enum EPluginType
    13. Game = 0
    14. End Enum
    15. #End Region
    16. #Region "Öffentliche Methoden"
    17. Sub init(ByVal instance As MainForm.Connection, ByVal guid As Guid)
    18. Sub Load()
    19. Sub UnLoad()
    20. Sub MainEvent(ByVal Action As n1000.Game.Action, ByVal ParamArray params() As Object)
    21. #End Region
    22. #Region "Private Methoden"
    23. #End Region
    24. #Region "Private Funktionen"
    25. #End Region
    26. End Interface

    Dazu habe ich dann noch ne Com-Klasse geschrieben, die die ganze Kommunikation wegen Hooks und so regelt..und die Welt is iO.
    Ah, alles klar. Dann habe ich wohl deine eigentlich Absichten falsch interpretiert. Worum handelt es sich denn genau bei der Anwendung, die du momentan programmierst? Sprich welchen Zweck hat eine konstante Framerate in deinem GUI?

    MfG
    Die Anwendung soll eine Art Minispieleverwaltung werden. Also alle Spiele in einen Ordner und dann soll man auswählen, welches Spiel man gerne hätte (such im Spiele-Forum mal nach n1000 und such den letzten Beitrag).
    Dabei ist meine Anwendung im Hintergrund die komplette Grafikengine. Also das Hauptprogramm ruft die Draw-Routine mit dem aktuellen Graphics-Objekt und überlässt dem Spiel das rendern.
    Wenn nun das Game ansich sehr anspruchsvoll ist und lange zum rendern braucht, bricht die Framerate ein, was ziemlich ungünstig ist.
    Stattdessen soll lieber das Bild stehen bleiben und die Anwendung trotzdem mit 60 FPS laufen.
    Wobei ich das theoretisch auch später implementieren kann. Derzeit liegts auch eher an den Funktionen. Trotzdem wäre es ganz gut, zu wissen, wie ich das hinbekomme.
    Wenn deine Anwendung die Engine für die Spiele, die über sie laufen sein soll, brauchst du wohl oder übel auch nen Gameloop. Was nützt dir ein Spiel, das keine Logik implentieren kann?

    MfG