Du bist nicht angemeldet.

[VB.NET] VSync

Lieber Besucher, herzlich willkommen bei: Visual-Basic-Forum VB-Paradise.de. Falls dies dein erster Besuch auf dieser Seite ist, lies bitte die Hilfe durch. Dort wird dir die Bedienung dieser Seite näher erläutert. Darüber hinaus solltest du dich registrieren, um alle Funktionen dieser Seite nutzen zu können. Benutze das Registrierungsformular, um dich zu registrieren oder informiere dich ausführlich über den Registrierungsvorgang. Falls du dich bereits zu einem früheren Zeitpunkt registriert hast, kannst du dich hier anmelden.

FreakJNS

Benutzter Registrierter

  • »FreakJNS« ist männlich

Beiträge: 1 249

Dabei seit: 19. März 2011

Hilfreich-Bewertungen: 297

  • Private Nachricht senden

21

Samstag, 24. März 2012, 13:45

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

EDIT:

Zitat

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:

Zitat

Da habe ich ~6 Frames pro Sekunde

Dieses Ergebnis kannst du nicht mit meiner Methode vergleichen....

Zitat

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!

Zitat

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« (24. März 2012, 14:30)


~blaze~

stating the obvious

  • »~blaze~« ist männlich

Beiträge: 3 151

Dabei seit: 2. August 2007

Wohnort: München

Hilfreich-Bewertungen: 604

  • Private Nachricht senden

22

Samstag, 24. März 2012, 14:56

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~

  • »Krissel095« ist männlich

Beiträge: 95

Dabei seit: 3. Juni 2008

Wohnort: Berlin

Hilfreich-Bewertungen: 60

  • Private Nachricht senden

23

Samstag, 24. März 2012, 15:20

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
3
4
5
6
7
8
9
10
11
12
13
2 GameTime Variablen: renderTime, tickTime
Solange aktiv:	
        loopStart = aktuelle Zeit

	tickTime aktualisieren
	Game.Tick aufrufen

	renderTime aktualisieren
	Wenn der FrameLimiter aktiv ist und weniger Zeit verstrichen ist, als die Zielframerate, die erreicht werden soll:
		Game.Render aufrufen

	Solange loopStart - aktuelle Zeit < Zielframerate
		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
2
3
4
5
6
7
e.Graphics.CompositingMode = CompositingMode.SourceCopy
e.Graphics.CompositingQuality = CompositingQuality.HighSpeed
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor
e.Graphics.SmoothingMode = SmoothingMode.HighSpeed
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed

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

  • »AliveDevil« ist der Autor dieses Themas

Beiträge: 1 471

Hilfreich-Bewertungen: 382

  • Private Nachricht senden

24

Samstag, 24. März 2012, 20:52

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

Visual Basic Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Dim renderer As BufferedGraphics
Sub Form_Shown()
    renderer = BufferedGraphicsManager.Current.Allocate(Me.CreateGraphics(), Me.ClipRectangle)
    renderThread = New Threading.Thread(AddressOf render)
    renderThread.Start()
End Sub
Sub Form_Paint()
    halt.Reset() ' ManualResetEvent
    Try
        renderer.Render()
    Catch
    End Try
    halt.Set()
    Threading.Thread.Sleep(10)
    Me.Invalidate()
End Sub
Sub render()
    Do
        halt.Wait()
        Try
            Dim g = renderer.Graphics()
            ' Hier zeichen Code
        halt.Wait()
    Loop
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)
Fragen zur Programmierung? Das Forum!
Hast du grad Lambda gesagt? User.Any( u => u.Username.Equals(this.Username) ) ? User.Single( u => u.Username.Equals(this.Username)).Execute( e => e.Signature.Edit(SignatureEdit.NonStatic | SignatureEdit.WIP) ) : throw new UserNotFoundException();

  • »Krissel095« ist männlich

Beiträge: 95

Dabei seit: 3. Juni 2008

Wohnort: Berlin

Hilfreich-Bewertungen: 60

  • Private Nachricht senden

25

Samstag, 24. März 2012, 21:00

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

  • »AliveDevil« ist der Autor dieses Themas

Beiträge: 1 471

Hilfreich-Bewertungen: 382

  • Private Nachricht senden

26

Samstag, 24. März 2012, 21:05

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:

Visual Basic Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Public Interface In1000Interface
#Region "Propertys"
	ReadOnly Property Version As Version
	ReadOnly Property Name As String
	ReadOnly Property Creater As String
	ReadOnly Property Image As Image
	ReadOnly Property Description As String
	ReadOnly Property PluginType As EPluginType
	Property IdentityNumber As Guid
#End Region
#Region "Enums"
	Enum EPluginType
		Game = 0
	End Enum
#End Region
#Region "Öffentliche Methoden"
    Sub init(ByVal instance As MainForm.Connection, ByVal guid As Guid)
	Sub Load()
	Sub UnLoad()
    Sub MainEvent(ByVal Action As n1000.Game.Action, ByVal ParamArray params() As Object)
#End Region
#Region "Private Methoden"
#End Region
#Region "Private Funktionen"
#End Region
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.
Fragen zur Programmierung? Das Forum!
Hast du grad Lambda gesagt? User.Any( u => u.Username.Equals(this.Username) ) ? User.Single( u => u.Username.Equals(this.Username)).Execute( e => e.Signature.Edit(SignatureEdit.NonStatic | SignatureEdit.WIP) ) : throw new UserNotFoundException();

  • »Krissel095« ist männlich

Beiträge: 95

Dabei seit: 3. Juni 2008

Wohnort: Berlin

Hilfreich-Bewertungen: 60

  • Private Nachricht senden

27

Samstag, 24. März 2012, 21:07

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

  • »AliveDevil« ist der Autor dieses Themas

Beiträge: 1 471

Hilfreich-Bewertungen: 382

  • Private Nachricht senden

28

Samstag, 24. März 2012, 21:11

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.
Fragen zur Programmierung? Das Forum!
Hast du grad Lambda gesagt? User.Any( u => u.Username.Equals(this.Username) ) ? User.Single( u => u.Username.Equals(this.Username)).Execute( e => e.Signature.Edit(SignatureEdit.NonStatic | SignatureEdit.WIP) ) : throw new UserNotFoundException();

  • »Krissel095« ist männlich

Beiträge: 95

Dabei seit: 3. Juni 2008

Wohnort: Berlin

Hilfreich-Bewertungen: 60

  • Private Nachricht senden

29

Samstag, 24. März 2012, 21:48

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

Social Bookmarks