2010-09-23 18 views

risposta

0

Non ho mai visto niente del genere. Normalmente si esegue il rendering di un frame il più velocemente possibile, con alcuni frame CPU o pre-elaborazione e il rendering successivo, quindi i flap di utilizzo vanno da 0 a 100%. Solo raramente gli FPS sono limitati a un numero massimo e solo in questo caso questo sarebbe un numero significativo.

2

No. È persino difficile definire rigorosamente in un ambiente così parallelo. Tuttavia puoi approssimarlo con l'estensione ARB_timer_query.

+1

potresti dare un esempio di utilizzo dell'estensione ARB_timer_query? – Newbie

4

Non proprio, ma è possibile ottenere contatori delle prestazioni diversi utilizzando le utilità del fornitore, per NVIDIA si dispone di NVPerfKit e NVPerfHUD. Altri venditori hanno utility simili.

1

Ho implementato un framework di misurazione del tempo di esecuzione della GPU basato su query con timer nella mia implementazione del thread di rendering OpenGL. Io condividere le parti di query timer di seguito:

Assumere

  • enqueue esegue una funzione sul thread di rendering
  • limiter.frame60 è solo uguale a 0, una volta ogni 60 fotogrammi

Codice:

struct TimerQuery 
{ 
    std::string description; 
    GLuint timer; 
}; 
typedef std::deque<TimerQuery> TimerQueryQueue; 

...

TimerQueryQueue timerQueryQueue; 

...

void GlfwThread::beginTimerQuery(std::string description) 
{ 
    if (limiter.frame60 != 0) 
     return; 

    enqueue([this](std::string const& description) { 
     GLuint id; 
     glGenQueries(1, &id); 
     timerQueryQueue.push_back({ description, id }); 
     glBeginQuery(GL_TIME_ELAPSED, id); 
    }, std::move(description)); 
} 

void GlfwThread::endTimerQuery() 
{ 
    if (limiter.frame60 != 0) 
     return; 

    enqueue([this]{ 
     glEndQuery(GL_TIME_ELAPSED); 
    }); 
} 


void GlfwThread::dumpTimerQueries() 
{ 
    while (!timerQueryQueue.empty()) 
    { 
     TimerQuery& next = timerQueryQueue.front(); 

     int isAvailable = GL_FALSE; 
     glGetQueryObjectiv(next.timer, 
          GL_QUERY_RESULT_AVAILABLE, 
          &isAvailable); 
     if (!isAvailable) 
      return; 

     GLuint64 ns; 
     glGetQueryObjectui64v(next.timer, GL_QUERY_RESULT, &ns); 

     DebugMessage("timer: ", 
        next.description, " ", 
        std::fixed, 
        std::setprecision(3), std::setw(8), 
        ns/1000.0, Stopwatch::microsecText); 

     glDeleteQueries(1, &next.timer); 

     timerQueryQueue.pop_front(); 
    } 
} 

Ecco alcuni esempio di output:

Framerate t=5.14 fps=59.94 fps_err=-0.00 aet=2850.67μs adt=13832.33μs alt=0.00μs cpu_usage=17% 
instanceCount=20301 parallel_μs=2809 
timer: text upload range 0.000μs 
timer: clear and bind 95.200μs 
timer: upload 1.056μs 
timer: draw setup 1.056μs 
timer: draw 281.568μs 
timer: draw cleanup 1.024μs 
timer: renderGlyphs 1.056μs 
Framerate t=6.14 fps=59.94 fps_err=0.00 aet=2984.55μs adt=13698.45μs alt=0.00μs cpu_usage=17% 
instanceCount=20361 parallel_μs=2731 
timer: text upload range 0.000μs 
timer: clear and bind 95.232μs 
timer: upload 1.056μs 
timer: draw setup 1.024μs 
timer: draw 277.536μs 
timer: draw cleanup 1.056μs 
timer: renderGlyphs 1.024μs 
Framerate t=7.14 fps=59.94 fps_err=-0.00 aet=3007.05μs adt=13675.95μs alt=0.00μs cpu_usage=18% 
instanceCount=20421 parallel_μs=2800 
timer: text upload range 0.000μs 
timer: clear and bind 95.232μs 
timer: upload 1.056μs 
timer: draw setup 1.056μs 
timer: draw 281.632μs 
timer: draw cleanup 1.024μs 
timer: renderGlyphs 1.056μs 

Questo mi permette di chiamare renderThread->beginTimerQuery("draw some text"); davanti ai miei opengl disegnano chiamate o qualsiasi altra cosa, e renderThread->endTimerQuery(); subito dopo, per misurare il tempo di esecuzione della GPU trascorso.

L'idea qui è che invia un comando alla coda di comando GPU proprio prima della sezione misurata, quindi glBeginQueryTIME_ELAPSED registra il valore di un contatore definito dall'implementazione. Il glEndQuery emette un comando GPU per memorizzare la differenza tra il conteggio corrente e quello memorizzato all'inizio della query TIME_ELAPSED. Tale risultato viene memorizzato dalla GPU nell'oggetto query ed è "disponibile" in un momento futuro asincrono. Il mio codice mantiene una coda di query temporizzate emesse e controlla una volta al secondo per le misurazioni completate. Il mio dumpTimerQueue continua a stampare le misurazioni finché la query del timer all'inizio della coda è ancora disponibile. Alla fine colpisce un timer che non è ancora disponibile e interrompe la stampa dei messaggi.

Ho aggiunto una funzione aggiuntiva che elimina 59 chiamate su 60 per le funzioni di misurazione, quindi misura solo una volta al secondo per tutta la strumentazione nel mio programma. Questo impedisce troppi spam e lo rende utilizzabile per eseguire lo stdout per lo sviluppo e impedisce troppe interferenze prestazionali causate dalle misurazioni. Questo è ciò che è la cosa limiter.frame60, frame60 è garantito per essere < 60. Si avvolge.

Mentre questo non risponde perfettamente alla domanda, è possibile dedurre l'utilizzo della GPU notando il tempo trascorso per tutte le chiamate di prelievo rispetto al tempo di orologio a muro trascorso. Se il frame era 16ms e la query del timer TIME_ELAPSED era 8ms, è possibile dedurre circa il 50% di utilizzo della GPU.

Un'altra nota: la misurazione viene misurata durante l'esecuzione della GPU, inserendo i comandi GPU nella coda GPU. Il threading non ha nulla a che fare con esso, se le operazioni all'interno di quelle enqueue fossero eseguite in un thread sarebbe equivalente.

Problemi correlati