Main-Loop Ja oder Nein?



  • Hallo!

    Ich bin nur ein einfacher Noob der von Spieleprogrammierung an und für sich keine Ahnung hat. Jetzt habe ich aber mit einem Freund der gerade ein Pong-Spiel in C++ programmiert hat eine, ich will mal sagen, etwas phiolosophische Debatte am Laufen.

    Ich habe mir ein paar Quelltexte angeguckt, und er verwendet einen so genannten "Main-Loop". Meine Intention dabei ist aber, dass der Rechner doch dadurch viel zu stark ausgelastet wird, da er ja quasi "permanent" Vergleiche (Bedingung der Schleife) macht, oder gar Funktionen aufruft, oder oder, je nach dem wie die Schleife aufgebaut ist. Zum Beispiel so:

    while ( m_bQuit )
    {
      // render
    }
    

    In m_bQuit steht im "normalen Spielablauf" also die ganze Zeit true drinne und die Schleife ist damit eine (fast) Endlosschleife. Meiner Ansicht nach wärs doch viel Sinnvoller, einen Thread zu benutzen, und wenn ich Beispielsweise 60 FPS haben möchte, den Thread beispielsweise nach jedem Schleifendurchlauf an die fünfzehn ms schlafen zu legen.

    Wie siehts damit aus? Wie macht man sowas normalerweise, und warum lastet sone Endlosschleife dann nicht den Rechner aus, wenn sie es angeblich nicht tut? Warum sind Threads unsinnig?

    Ich bedanke mich schonmal für alle Antworten im Voraus, danke! 🙂



  • Meiner Ansicht nach wärs doch viel Sinnvoller, einen Thread zu benutzen, und wenn ich Beispielsweise 60 FPS haben möchte, den Thread beispielsweise nach jedem Schleifendurchlauf an die fünfzehn ms schlafen zu legen.

    Dein Programm hat immer mindestens einen Thread, d.h. es reicht auch, wenn irgendwo in der Schleife ein Sleep steht. Dafür brauchst du keinen zweiten Thread.

    und warum lastet sone Endlosschleife dann nicht den Rechner aus, wenn sie es angeblich nicht tut? Warum sind Threads unsinnig?
    

    Bitte nochmal...? Wenn in der Schleife kein Sleep steht, dann kannst du dich so ziemlich darauf verlassen, dass einer deiner CPU-Kerne vollständig ausgelastet ist. Und wieso sollen Threads unsinning sein? In diesem Fall ist ein weiterer Thread unsinnig, falls der die ganze Arbeit macht und der Hauptthread danach nur noch Däumchen dreht.



  • Die FPS-Rate stutzt man nie händisch runter. Wenn du Auto fährst, trittst du ja auch nicht jede Sekunde einmal kurz Vollgas und dann wieder Leerlauf 😉 Das kommt nicht so gut. Gleichmäßige Frameraten kann man z.B. über V-Sync aktivieren. Eventuell solltest du auch überlegen Logik-Ticks von Frame-Ticks zu trennen, da man die Logik im Spiel meistens nicht alle 5 ms durchrechnen muss bzw. kann.

    Gruß
    Don06



  • Gleichzeitig will man aber nicht bei jedem Dödelkram seinen Rechner auf 100% Last haben. 😉



  • Hallo!

    Erstmal Danke für eure Antworten.

    Ich habe hier ein Beispiel aus einem Tutorial von NeHe, wo wir aber akkut kein Sleep finden können.

    while(!done)									// Loop That Runs While done=FALSE
    	{
    		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	// Is There A Message Waiting?
    		{
    			if (msg.message==WM_QUIT)				// Have We Received A Quit Message?
    			{
    				done=TRUE;							// If So done=TRUE
    			}
    			else									// If Not, Deal With Window Messages
    			{
    				TranslateMessage(&msg);				// Translate The Message
    				DispatchMessage(&msg);				// Dispatch The Message
    			}
    		}
    		else										// If There Are No Messages
    		{
    			// Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
    			if ((active && !DrawGLScene()) || keys[VK_ESCAPE])	// Active?  Was There A Quit Received?
    			{
    				done=TRUE;							// ESC or DrawGLScene Signalled A Quit
    			}
    			else									// Not Time To Quit, Update Screen
    			{
    				SwapBuffers(hDC);					// Swap Buffers (Double Buffering)
    			}
    
    			if (keys[VK_F1])						// Is F1 Being Pressed?
    			{
    				keys[VK_F1]=FALSE;					// If So Make Key FALSE
    				KillGLWindow();						// Kill Our Current Window
    				fullscreen=!fullscreen;				// Toggle Fullscreen / Windowed Mode
    				// Recreate Our OpenGL Window
    				if (!CreateGLWindow("NeHe's Solid Object Tutorial",640,480,16,fullscreen))
    				{
    					return 0;						// Quit If Window Was Not Created
    				}
    			}
    		}
    	}
    

    while(!done) // Loop That Runs While done=FALSE
    {
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
    {
    if (msg.message==WM_QUIT) // Have We Received A Quit Message?
    {
    done=TRUE; // If So done=TRUE
    }
    else // If Not, Deal With Window Messages
    {
    TranslateMessage(&msg); // Translate The Message
    DispatchMessage(&msg); // Dispatch The Message
    }
    }
    else // If There Are No Messages
    {
    // Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
    if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Active? Was There A Quit Received?
    {
    done=TRUE; // ESC or DrawGLScene Signalled A Quit
    }
    else // Not Time To Quit, Update Screen
    {
    SwapBuffers(hDC); // Swap Buffers (Double Buffering)
    }

    if (keys[VK_F1]) // Is F1 Being Pressed?
    {
    keys[VK_F1]=FALSE; // If So Make Key FALSE
    KillGLWindow(); // Kill Our Current Window
    fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
    // Recreate Our OpenGL Window
    if (!CreateGLWindow("NeHe's Solid Object Tutorial",640,480,16,fullscreen))
    {
    return 0; // Quit If Window Was Not Created
    }
    }
    }
    }[/cpp]
    Das ist quasi die Main-Loop aus dem Tutorial, aber wie gesagt kein Sleep. Ist das tut jetzt so schlecht, oder handlet er das sleep irgendwie in einer der Funktionen ab? Oder oder? 😕



  • Sorry sollte eigentlich nicht doppelt rein 😞



  • Windows Anwendungen gehen automatisch in den "Idle"-Modus, wenn sie nichts zu tun haben. Das möchte man in einer OpenGL-Anwendung aber gerade nicht. Deshalb wird hier auch PeekMessage und nicht GetMessage verwendet (GetMessage legt den Thread schlafen bis die nächste Nachricht kommt). Sleep hat außerdem eine schlechte Auflösung (so um die 16 ms, aber nie genau). In einem modernen 3D-Spiel kann man es sich gar nicht erlauben in jedem Loop-Durchlauf 16 ms zu verschwenden.

    BTW: 100% Auslastung hat bei meinem System noch kein einzelner Prozess geschafft und das ist schon 3 Jahre alt.

    Gruß
    Don06



  • Don06 schrieb:

    BTW: 100% Auslastung hat bei meinem System noch kein einzelner Prozess geschafft und das ist schon 3 Jahre alt.

    Jeder Prozess, der mit einer solchen Schleife arbeitet (also z.B. fast jedes Spiel) dürfte einen Kern annähernd zu 100% auslasten. Wenn deine Auslastung weit darunter liegt, dann wird entweder doch irgendwo gewartet, es laufen andere Prozesse CPU-hungrige Prozesse oder der Prozess hat nur einen derartigen Killerthread, du aber mehrere Prozessoren/Kerne.



  • @Nanyunki: Bei mir geht dann ein Kern so auf 80% - 90% der andere so auf 10%-20%, so dass ich insgesamt nur selten über 50% (gesamt) bei einem Prozess habe.

    Hier mal die CPU Auslastung bei folgendem Programm:

    int main()
    {
        while (true)
            ;
    }
    

    CPU Usage

    EDIT: Achja, die 10-20% Prozent kommen nicht von anderen Prozessen. Hintergrund-Prozesse verbrauchen bei mir max. 2%.

    Gruß
    Don06



  • @Don06: 50% heisst ein Kern ist auf 100%. Dass dabei ein Kern mit 80% und einer mit 20% angezeigt wird kommt nur daher weil Windows das Programm nicht immer auf dem selben Kern ausführt, sondern ab und zu den Kern wechselt. Warum das passiert ist erstmal egal, wichtig ist: ein Kern wird komplett ausgelastet durch die Schleife.

    Zurück zum Thema: normalerweise macht man es doch so dass man der Grafik-API anschafft beim Flippen auf den vsync zu warten, wodurch man die Framerate ganz "automatisch" (=ohne Sleep) auf die des Monitors reduziert (also bei TFTs normalerweise auf 60 Hz).


Anmelden zum Antworten