[VC++'08] Nachrichtenschleife umgehen

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Brainsmith
Establishment
Beiträge: 109
Registriert: 04.09.2009, 13:52
Echter Name: Fabbo

[VC++'08] Nachrichtenschleife umgehen

Beitrag von Brainsmith »

Hallo allerseits,
ich lerne derzeitig, Computerspiele zu entwickeln. In einem von mir verwendeten Buch ("3D Spieleprogrammierung mit DirectX9 und C++" von David Scherfgen) steht, dass man für die Spieleprogrammierung stehts eine Nachrichtenschleife benutzen soll. Ein Bekannter hat mir erklärt, dass das Programm sonst die CPU gänzlich auslastet.
Dieser Bekannte nutzt jetzt aber Visual Basic und hat keine Nachrichtenschleife. Stattdessen sagte er, er benutze am Ende jedes Schleifendurchlaufs eine Abfrage an das System, ob denn noch etwas zu tun sei. Er gibt dem System also am Ende der Hauptschleife die Möglichkeit, andere Dinge zu tun.

Hier meine Frage(n):
1: Gibt es einen solchen Befehl auch in Visual C++?

Falls ja:
2: Besteht ein relevanter Performanceunterschied?
3: (Erklärt sich evtl aus der zweiten Frage) Was nutzt man am besten für Spieleentwicklung?
4: Gibt es eventuell auch eine ganz andere Lösung dafür?
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [VC++'08] Nachrichtenschleife umgehen

Beitrag von Krishty »

Hi,

Die Win-API (nicht Visual C++, das kompiliert dir bloß den Code) bietet PeekMessage() an um zu prüfen, ob eine Nachricht eingetroffen ist. Wenn man dem System signalisieren möchte, dass nichts Dringendes zu tun ist – ich glaube, das ist deine eigentliche Frage – sollte man SwitchToThread() aufrufen. Falls das Betriebssystem noch andere Dinge zu tun hat, erledigt es erst die, sonst wird dein Programm sofort fortgesetzt. Wenn du auf diese Weise die CPU ganz auslastest ist es egal, denn dann geschieht es mit dem Einverständnis des Betriebssystems. Willst du dein Programm drosseln, weil die Arbeit, die du ausführen würdest, sowieso umsonst wäre (bei 500 fps würde der Bildschirm >400 sowieso verwerfen), dann aktiviere in der Grafik-API deiner Wahl vertikale Synchronisation – dadurch übergibt die API die überflüssige Zeit an das Betriebssytem, bevor der Bildschirm für den nächsten Frame bereit ist. Als letzte Möglichkeit kannst du noch Sleep() benutzen, das ist aber meist die schlechteste Wahl.

Generell sieht es so aus, dass man die komplette Spiellogik in einer Schleife ausführt (while(!Beenden) oder for(;;) mit einem break, sobald der Befehl zum Beenden registriert wurde) und aus dieser heraus die Windows-Nachrichtenverarbeitung aufruft. Dabei sollte man PeekMessage() statt GetMessage() aufrufen, weil die Schleife bei letzterem so lange wartet, bis eine Nachricht eintrifft – das Spiel bliebe also bspw. stehen, wenn der User aufhörte, die Maus zu bewegen – und sowas ist unerwünscht, wenn der User z.B. auf etwas wartet. Am Ende der Schleife steht dann ein SwitchToThread(), und im nächsten Frame geht es von vorn los.

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Brainsmith
Establishment
Beiträge: 109
Registriert: 04.09.2009, 13:52
Echter Name: Fabbo

Re: [VC++'08] Nachrichtenschleife umgehen

Beitrag von Brainsmith »

Krishty hat geschrieben: Willst du dein Programm drosseln, weil die Arbeit, die du ausführen würdest, sowieso umsonst wäre (bei 500 fps würde der Bildschirm >400 sowieso verwerfen), dann aktiviere in der Grafik-API deiner Wahl vertikale Synchronisation – dadurch übergibt die API die überflüssige Zeit an das Betriebssytem, bevor der Bildschirm für den nächsten Frame bereit ist.
Danke für die schnelle Antwort. Ich denke, dass es am effektivsten wäre, die überflüssige Zeit an das Betriebssystem zu spenden ^^
Allerdings sehe ich da ein Problem: Angenommen, ich erreiche letztendlich gar nicht die maximale Framerate. Dann bekommt das Betriebssystem doch gar nicht mehr die Möglichkeit zu handeln, ist das richtig? Ist doch eigentlich wichtig, dass das Betriebssystem häufig Zeit zum handeln hat, oder? Meinetwegen, ich will Musik hören (im schlimmsten Falle Midi-Dateien, wo der Sound meines Wissens nach berechnet wird, also ständig irgendwas machen muss ^^). Das geht dann doch gar nicht, wenn ich das richtig verstanden habe.
Und falls ich mich für die vertikale Synchronisation entscheide... öh.. naja.. was muss ich dann wo wie einstellen? Bin mit vielen Begrifflichkeiten noch nicht so vertraut, sry.

Um noch einmal klarzustellen, was ich eigentlich meinte:
Ich will, dass das Programm auf 60 Fps gedrosselt wird und dass das Betriebssystem die Möglichkeit hat, Aktionen auszuführen. Auch im Falle von geringen Fps- Zahlen
Benutzeravatar
Krishty
Establishment
Beiträge: 8267
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [VC++'08] Nachrichtenschleife umgehen

Beitrag von Krishty »

Brainsmith hat geschrieben:Angenommen, ich erreiche letztendlich gar nicht die maximale Framerate. Dann bekommt das Betriebssystem doch gar nicht mehr die Möglichkeit zu handeln, ist das richtig?
Nein, wenn das OS Zeit braucht, holt es sich die. Dein Programm wird zig tausend Male pro Sekunde unterbrochen, damit das OS Hardware, Netzwerk, sich selbst (die ganzen Dienste, die im Hintergrund laufen), andere Prozesse mit höherer Priorität und, damit alles fair bleibt, auch andere Prozesse mit gleicher Priorität mit Rechenzeit versorgen kann. Das geschieht allerdings so transparent und in so kurzer Zeit, dass du es weder mitbekommst, noch von deinem Programm aus feststellen kannst, wann das geschah – sonst könnte eine simple Endlosschleife den kompletten Rechner lahmlegen. Ist einer der Grundsätze von Win32-Programmierung – Programme können nicht gegenseitig auf ihren Speicher zugreifen, wenn ein Programm endet werden alle seine offenen Ressourcen (Dateien, Speicher etc.) geschlossen, und Programme wissen nie genau, wann wer wieviel Rechenzeit abbekommen hat. Die Zeiten, in denen man da noch was falsch machen konnte, sind seit mehr als einer Dekade vorbei. Zurück zum Thema: SwitchToThread() signalisiert dem OS nur, dass auch mal mehr Prozesse oder Prozesse mit niedrigerer Priorität randürfen, dass also Überschuss besteht (und bekommt eine stärkere Bedeutung, wenn du mit Multi-Threading arbeitest).
Brainsmith hat geschrieben:Meinetwegen, ich will Musik hören (im schlimmsten Falle Midi-Dateien, wo der Sound meines Wissens nach berechnet wird, also ständig irgendwas machen muss ^^).
MP3 muss entpackt werden, unter’m Strich braucht man für alles gleich viel/wenig Leistung ;) Der Multimedia-Planer von Windows läuft mit hoher Priorität und reserviert beim OS ausreichend Bandbreite und Rechenzeit. Problematisch wird es erst, wenn du *wirklich* rechenaufwändige Dinge im Hintergrund tust, wie High-Definition-Videos zu gucken, oder wenn du deinem Programm die Priorität „Echtzeit“ zuweist (steht hier nur der Vollständigkeit halber, du wirst fast nie einen Grund haben, deine Programmpriorität zu ändern).
Brainsmith hat geschrieben:Und falls ich mich für die vertikale Synchronisation entscheide... öh.. naja.. was muss ich dann wo wie einstellen? Bin mit vielen Begrifflichkeiten noch nicht so vertraut, sry.
Kommt drauf an, welche Grafik-API du verwendest … falls du mit der Grafik noch garnicht angefangen hast, brauchst du es auch noch überhaupt nicht. Bei D3D9 kann man ein PRESENTATION_INTERVAL einstellen, bei D3D10 kann Present() sagen, wieviele VSyncs es warten soll.
Brainsmith hat geschrieben:Ich will, dass das Programm auf 60 Fps gedrosselt wird
Die effizienteste Frame-Rate hängt vor allem davon ab, wie hoch die Bildwiederholfrequenz des Monitors ist – ein Grund mehr, das von der Grafik-API machen zu lassen. Möchtest du das zu Testzwecken – und auch wirklich nur dafür – machen, probier Sleep(16); (16 ms sind ca 60 fps).
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten