Quanto tempo impiega OpenGL ad aggiornare effettivamente lo schermo?


9

Ho una semplice app di test OpenGL in C che disegna cose diverse in risposta all'input chiave. (Mesa 8.0.4, provato con Mesa-EGL e con GLFW, Ubuntu 12.04LTS su un PC con NVIDIA GTX650). I sorteggi sono abbastanza semplici / veloci (tipo di roba a triangolo rotante). Il mio codice di prova non limita deliberatamente il framerate in alcun modo, sembra solo questo:

while (true)
{
    draw();
    swap_buffers();
}

L'ho cronometrato con molta attenzione e trovo che il tempo che intercorre tra una eglSwapBuffers()(o glfwSwapBuffers, stessa cosa) chiamata successiva è di circa 16,6 millisecondi. Il tempo che intercorre tra una chiamata e eglSwapBuffers()l'altra prima di quella successiva è solo un po 'meno, anche se ciò che viene disegnato è molto semplice. Il tempo impiegato dalla chiamata ai buffer di swap è inferiore a 1 ms.

Tuttavia, il tempo dall'app che cambia ciò che sta disegnando in risposta al tasto preme sulla modifica effettivamente visualizzata sullo schermo è> 150 ms (vale circa 8-9 frame). Questo viene misurato con una registrazione della fotocamera dello schermo e della tastiera a 60 fps.

Pertanto, le domande:

  1. Dove sono memorizzati i sorteggi tra una chiamata per scambiare i buffer e mostrarli sullo schermo? Perché il ritardo? Sembra che l'app stia disegnando sempre più fotogrammi davanti allo schermo.

  2. Cosa può fare un'applicazione OpenGL per provocare un disegno immediato sullo schermo? (ovvero: nessun buffering, basta bloccare fino al completamento dell'estrazione; Non ho bisogno di un throughput elevato, ho bisogno di una bassa latenza)

  3. Cosa può fare un'applicazione per fare in modo che l'estrazione immediata di cui sopra avvenga il più velocemente possibile?

  4. Come può un'applicazione sapere cosa è effettivamente sullo schermo in questo momento? (Oppure, quanto tempo / quanti frame è il ritardo di buffering corrente?)


Puoi separare il tempo impiegato dalla tastiera per notificare la tua applicazione al tempo necessario per renderlo effettivamente?
ThorinII,

@ThorinII: punto giusto. Probabilmente posso impostare qualcosa di strano usando un LED su una porta parallela ecc. Per ottenere un'indicazione di quando l'app riceve effettivamente la pressione del tasto. Tuttavia, è solo fgetc (stdin), quanto può essere lento? : /
Alex I

Avevo l'impressione che glFlush fosse usato per provocare un flush immediato di tutti i comandi.
Programmdude

1
@AlexI controllo questo gamedev.stackexchange.com/questions/66543/… potrebbe aiutarti
concept3d

1
@ concept3d: Sì, in pratica ha risposto, ho pensato che ti sarebbe piaciuto qualche rappresentante extra :) A quanto pare, però, devo aspettare un giorno per premiarlo.
Alex I,

Risposte:


6

Qualsiasi funzione API di disegno chiamata dalla CPU verrà inviata al buffer dell'anello di comando GPU per essere eseguita successivamente dalla GPU. Ciò significa che le funzioni OpenGL sono per lo più funzioni non bloccanti. Quindi la CPU e la GPU lavoreranno in parallelo.

La cosa più importante da notare è che l'applicazione può essere associata a CPU o GPU. una volta chiamato glFinish, la CPU dovrebbe attendere che la GPU completi i suoi comandi di disegno, se la GPU impiega più tempo e può causare l'arresto della CPU, allora le tue applicazioni sono legate alla GPU. Se la GPU termina il disegno dei comandi e la CPU impiega troppo tempo a glFinish, l'applicazione è associata alla CPU.

E nota che c'è una differenza tra glFlushe glFinish.

glFlush: indica che tutti i comandi che sono stati precedentemente inviati al GL devono essere completati a tempo finito.

glFinish: forza il completamento di tutti i precedenti comandi GL. Finish non ritorna fino a quando tutti gli effetti dei comandi precedentemente emessi sullo stato del client e del server GL e sul framebuffer non saranno completamente realizzati. "

glXSwapBuffers esegue un glFlush implicito prima che ritorni. I comandi OpenGL successivi possono essere emessi immediatamente dopo aver chiamato glXSwapBuffers, ma non vengono eseguiti fino al completamento dello scambio del buffer.

Molto probabilmente il tempo di frame effettivo sarà determinato da quale delle due CPU / GPU impiega più tempo per completare il suo lavoro.


Questo è molto utile Alcune possibili correzioni: opengl.org/sdk/docs/man2/xhtml/glXSwapBuffers.xml "glXSwapBuffers esegue un glFlush implicito prima che ritorni" sembra che in realtà non esegua un glFinish, quindi il buffer dei comandi può avere parecchi roba in esso quando ritorna il buffer di scambio.
Alex I,

... Inoltre, penso che il punto più interessante sia che la mia semplicissima applicazione di test non è né CPU né GPU limitate - né ha molto lavoro da fare qui - ma qualcos'altro, in qualche modo finisce con entrambi i framerate bassi (esattamente come il monitor frequenza di aggiornamento ??) e latenza elevata (a causa del buffer dei comandi, in realtà hai spiegato abbastanza bene quella parte).
Alex I,

@AlexI both low framerate (exactly same as monitor refresh rate??no a meno che tu non stia usando esplicitamente VSync.
concept3d

@AlexI Voglio anche sottolineare che il frame time è diverso dall'FPS, usa il frame time per profilare la tua applicazione perché è lineare. FPS d'altra parte è una misurazione scarsa che non è lineare.
concept3d

Non sto usando esplicitamente vsync. Non sto facendo nulla per cambiare le impostazioni di defauls vsync, non so nemmeno dove cambiare quelle in OpenGL. Ho appena ottenuto esattamente 60 fps (fino a una percentuale).
Alex I,

4

OpenGL non aggiorna mai lo schermo, tecnicamente.

Esiste un'API di sistema per finestre separata da GL (ad es. GLX, WGL, CGL, EGL) che esegue questa operazione. Gli swap di buffer che utilizzano queste API generalmente invocano implicitamente glFlush (...)ma in alcune implementazioni (ad esempio il rasterizzatore GDI su Windows) esegue un completo glFinish (...):

 

    * Sul lato sinistro, è il percorso ICD (hardware) attraverso SwapBuffers (...). Sul lato destro, il percorso GDI (software).

Se hai VSYNC abilitato e doppio buffering, qualsiasi comando che modifichi il back buffer prima che si verifichi uno swap in sospeso deve arrestarsi fino allo swap. C'è una profondità limitata nella coda dei comandi, quindi questo comando bloccato alla fine causerà un ingorgo nella pipeline. Ciò potrebbe significare che invece di bloccare SwapBuffers (...)l'applicazione in realtà blocca alcuni comandi GL non correlati fino a quando VBLANK non gira. Ciò che si riduce davvero a quanti back buffer hai nella tua catena di swap.

Finché tutti i buffer posteriori sono pieni di frame finiti ma devono essere spostati in avanti, i buffer di swap causeranno implicitamente il blocco. Purtroppo, non esiste alcun modo per controllare esplicitamente il numero di back buffer utilizzati dalla maggior parte delle API del sistema di finestre GL (a parte 0 con buffer singolo o 1 con buffer doppio). Il driver è libero di utilizzare 2 back buffer se lo desidera (triplo buffering), ma non è possibile richiederlo a livello di applicazione usando qualcosa come GLX o WGL.


Andon: buone informazioni, grazie. Penso che ciò che sto vedendo sia in parte un doppio buffering, ma: "tentare di modificare il back buffer prima che si verifichi lo swap deve bloccare" - perché? sembra che tutto possa essere inviato al buffer dei comandi, incluso lo scambio :)
Alex I

msgstr "controlla esplicitamente il numero di back buffer utilizzati dalla maggior parte delle API del sistema di finestre GL (a parte 0 con buffer singolo o 1 con buffer doppio)" - come si controlla un buffer singolo / doppio?
Alex I,

@AlexI: ho chiarito la lingua riguardo al motivo per cui il comando può portare al blocco. In altre API esiste un concetto noto come render-ahead, che è effettivamente la profondità della coda comandi. Per quanto riguarda il controllo del buffer singolo / doppio, è controllato dal formato pixel selezionato quando si crea il contesto di rendering. In WGL, è possibile selezionare il buffer singolo o doppio, ma se si seleziona il doppio buffer, il driver potrebbe effettivamente creare 2 back buffer (quindi il doppio buffering diventa triple buffering) se l'utente imposta il proprio driver per farlo.
Andon M. Coleman,

In realtà non si desidera eseguire il rendering di troppi frame in anticipo perché, sebbene ciò riduca il blocco, aumenta anche la latenza. Il modo migliore per ridurre al minimo la latenza è in realtà invocare glFinish (...)immediatamente dopo lo scambio di buffer. Questo cancellerà la coda dei comandi, ma significa anche che la GPU e la CPU saranno sincronizzate (il che non è un bene se si desidera che la GPU funzioni sempre).
Andon M. Coleman,

1

Presumo che tu abbia familiarità con questo esperimento ?

In sostanza John Carmack stava facendo qualcosa di simile, registrando lo schermo e i pixel di temporizzazione inviati allo schermo. Scoprì che buona parte della latenza proveniva dallo schermo. Altri fattori sono stati il ​​ritardo di input dalla tastiera, i driver video e naturalmente l'esecuzione del programma stesso.

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.