Separare la logica / l'aggiornamento dal codice di rendering / disegno in un singolo thread usando sleep


9

Ho letto che la velocità degli oggetti di gioco non dovrebbe essere ostacolata da FPS ma dovrebbe essere basata sul tempo. Come posso separare il codice di aggiornamento / disegno per massimizzare le prestazioni senza limitare la velocità di disegno e fornire una velocità di aggiornamento della logica costante in base al tempo?

Il mio attuale pseudo codice è il seguente

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

Il problema è che il codice di disegno ostacola le prestazioni della frequenza update (). E consuma il 100% della CPU perché se viene gettato il sonno, getta via entrambe le funzioni di disegno / logica.

Sto anche usando SDL e non sembra avere un'opzione vsync. Ho anche sentito parlare dei termini a tempo fisso e variabile, tuttavia non sono sicuro di come si possa fare con sleep ()


1
Non è necessario sprecare il 100% della potenza della CPU solo per l'attesa, mettere uno sleep (0) alla fine dei cicli while ticksElapsed () <100. Il sistema operativo tornerà immediatamente al thread se non ci sono altri thread che vuole correre. Ma non sprecare più il 100% della potenza della CPU.
Maik Semder,

Tuttavia, la soluzione migliore per una tale configurazione a 1 thread è usare vsync, se non puoi vsync, quindi chiama sleep (0) in un ciclo fino a raggiungere la frequenza dei fotogrammi target, quindi aggiorna e disegna
Maik Semder

Risposte:


3

Nel tuo frammento di codice sembra che tu stia cercando di eseguire il gioco in modalità passo a tempo fisso aspettando se il disegno e l'aggiornamento impiegano meno di 15 ms (60 fps). Questo è possibile e hai indovinato che non è possibile farlo utilizzando una chiamata di sonno perché non sai esattamente per quanto tempo dormirai. Il circuito di attesa è la buona soluzione.

Tuttavia, considera il caso in cui l'aggiornamento e il disegno superino i 15 ms, ora il disegno del gioco e l'aggiornamento rallentano. Ora puoi fare entrambe le cose: rilevare questo stato e rilasciare i frame (salta il disegno e vai direttamente all'aggiornamento fino a quando non sei di nuovo sincronizzato), tuttavia se il computer è solo per rallentare non raggiungerà mai.

Un'altra soluzione è rendere indipendente la logica di aggiornamento a tempo fisso. Non hai bisogno di un thread separato per questo, devi solo rispecificare quanto velocemente dovrebbero muoversi le cose. Invece di 5 pixel per tick, è necessario utilizzare 50 pixel al secondo. Per ottenere ciò è necessario un timer di alta precisione e tutta la logica di aggiornamento dovrebbe essere in grado di accedere al timer per vedere quanto tempo è trascorso dall'ultimo aggiornamento.

Fondamentalmente si passa da:

void UpdatePlayer()
 player.x += 10;

Per

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;

Quindi il mio motore consumerà sempre il 100% e non posso farci nulla?
Oskenso Kashi,

1
Consumerà tutti i cicli che lo scheduler gli consente di ottenere, ma non è un problema dato che non puoi davvero fare molte altre cose mentre stai giocando :).
Roy T.

@Oskenso Tuttavia, è un problema se usi più di 1 thread, quindi il thread principale non permetterà agli altri di correre il più possibile, sprecando molta potenza computazionale nel ciclo while, dovresti davvero considerare un sonno
Maik Semder,

@Maik Semder: hai una soluzione per cui il sonno (x) non è preciso? Dopo che è trascorso l'intervallo di sospensione, il thread è pronto per essere eseguito. Ma un thread pronto non è garantito per essere eseguito immediatamente. Dipende dallo scheduler. Quando usi due thread ci sono altre soluzioni, per questo vedi questo eccellente articolo: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.

1
@Roy sleep (0) è la soluzione. Restituisce immediatamente se non vi sono altri thread che vogliono essere eseguiti ( Sleep WinAPI ) e danno ad altri thread la possibilità di essere eseguito. Se l'altro thread non darà la possibilità al thread principale di essere eseguito in cambio, allora hai un problema di threading, ma bloccare tutto il resto non chiamando sleep in primo luogo lo rende ancora peggio e difficilmente è una soluzione. La chiave è chiamare sleep (0) e testare il tempo trascorso fino a quando non raggiungi la frequenza dei fotogrammi fissa target, in modo da non sprecare il 100% della CPU solo per l'attesa.
Maik Semder,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.