2014-10-02 85 views
14

Sto disegnando un'animazione utilizzando GDI a doppio buffer su una finestra, su un sistema in cui è abilitata la composizione DWM e visualizzando sullo schermo chiaramente visibile tearing. C'è un modo per impedirlo?E 'possibile evitare di strappare artefatti durante il disegno usando GDI su una finestra con composizione DWM?

dettagli

L'animazione prende la stessa immagine, e lo sposta da destra a sinistra sullo schermo; il numero di pixel è determinato dalla differenza tra l'ora corrente e l'ora di inizio dell'animazione e il tempo di fine, per ottenere una frazione completa che viene applicata all'intera larghezza della finestra, utilizzando timeGetTime con uno 1ms resolution. L'animazione disegna in un loop senza elaborare i messaggi dell'applicazione; chiama il metodo (libreria VCL) Repaint che internamente invalida e quindi chiama UpdateWindow per la finestra in questione, chiamando direttamente nella procedura di messaggio con WM_PAINT. L'implementazione VCL del gestore vernice utilizza BeginBufferedPaint. La pittura è essa stessa a doppio buffer.

Lo scopo di questo è di avere il frame rate più alto possibile per ottenere un'animazione uniforme sullo schermo. (Il disegno utilizza doppio buffering per rimuovere lo sfarfallio e per garantire un'intera immagine o telaio è sullo schermo in qualsiasi momento. E 'invalida e aggiornamenti direttamente chiamando nella gestione di segnalazione, senza fare altro elaborazione dei messaggi. La pittura è implementato utilizzando tecniche moderne (es. BeginBufferedPaint) per composizione Aero.) All'interno di questo, la pittura viene eseguita in un paio di chiamate BitBlt (una per il lato sinistro dell'animazione, cioè cosa si sta spostando fuori dallo schermo e una per il lato destro dell'animazione, cioè cosa si sta muovendo sullo schermo.)

Durante la visione dell'animazione, è chiaramente visibile tearing. Si verifica su Windows Vista, 7 e 8.1 su più sistemi con diverse schede grafiche.

Il mio approccio per gestire questo è stato quello di ridurre la velocità con cui sta disegnando, o di provare ad aspettare VSync prima di dipingere di nuovo. Questo potrebbe essere l'approccio sbagliato, quindi la risposta a questa domanda potrebbe essere "Fai qualcos'altro completamente: X". Se è così, grande :)

(Quello che mi piacerebbe davvero è un modo per chiedere la DWM di comporre/usare i frame solo completamente dipinte per questa finestra specifica.)

Ho provato i seguenti approcci , nessuno dei quali rimuove tutte le lacrime visibili. Quindi la domanda è, È possibile evitare la rottura quando si usa la composizione DWM e, in caso affermativo, come?

Approcci cercato:

  • Ottenere la frequenza di aggiornamento del monitor tramite GetDeviceCaps(Application.MainForm.Handle, VREFRESH); dormire per 1/frequenza di aggiornamento millisecondi. Leggermente migliorato sulla pittura il più velocemente possibile, ma può essere un pio desiderio. Percentuale di animazione percettivamente leggermente inferiore. (Tweaks: normale Sleep e una ad alta risoluzione di spin-wait utilizzando timeGetTime.)

  • Utilizzando DwmSetPresentParameters per cercare di limitare l'aggiornamento alla stessa velocità con cui il codice disegna. (Variazioni: lotti di buffer (cBuffer = 8) (nessun effetto visibile); specificando un tasso fonte di monitor di frequenza di aggiornamento/1 e pelo utilizzando il codice di cui sopra (la stessa solo cercando l'approccio sonno); specificando un aggiornamento per frame di 1, 10, ecc. (Nessun effetto visibile); modifica della copertura del fotogramma sorgente (nessun effetto visibile.)

  • Utilizzando DwmGetCompositionTimingInfo in una varietà di modi:

      • Mentre cFramesPending> 0, rotazione;
      • Get cFrame (struttura composta) ed effetti mentre questo numero non cambia;
      • Get cFrameDisplayed e effetti mentre questo non cambia;
      • Calcolo di un tempo per dormire a aggiungendo qpcVBlank + qpcRefreshPeriod, e poi mentre QueryPerformanceCounter restituisce un tempo inferiore a questo, rotazione
  • Tutti questi approcci sono stati anche variato dalla pittura , quindi gira/dorme prima di dipingere di nuovo; o il contrario: dormire e poi dipingere.

Pochi sembrano avere alcun effetto visibile e che effetto v'è è difficile da qualificare e può essere solo il risultato di un frame rate inferiore. Nessuno impedisce la lacerazione, ovvero nessuno fa in modo che il DWM componga la finestra con una copia "intera" del contenuto della DC della finestra.

Consigli apprezzato :)

+1

GDI è un'animazione vecchia e croccante e non ottimizzata. Forse prova Direct2D? –

+1

@JonathanPotter Buon punto. Uno dei limiti a cui sono sottoposto non è l'utilizzo di DX di alcun tipo - una ragione è il numero di piattaforme e la mancanza di requisiti, il codice finale deve essere eseguito. (Non tutti sono moderni Windows.) Forse il codice DX helper solo quando esiste Aero ...? Tuttavia, dovrebbe passare senza problemi da e verso GDI/DX senza problemi visivi, poiché attiva/disattiva l'animazione. È possibile? –

+0

Forse potresti scrivere una classe helper di disegno che usi DX se disponibile e ricade altrimenti su GDI. Sarebbe piuttosto raro trovare un sistema che non supporta Direct3D anche se non D2D. –

risposta

2

Dal momento che si sta utilizzando BitBlt, assicurarsi che i DIB sono 4-byte/pixel. Con 3 byte/pixel, GDI è orribilmente lento mentre DWM è in esecuzione, che potrebbe essere la fonte del tuo strappo. Un altro problema BitBlt in cui mi sono imbattuto, se il DIB è leggermente più grande, rispetto alla chiamata a BitBlt, richiede un tempo inaspettatamente lungo. Se si divide una chiamata in chiamate più piccole di quelle che si disegnano solo una parte dei dati, potrebbe essere d'aiuto. Entrambi questi elementi mi hanno aiutato nel mio caso, solo perché lo stesso BitBlt era in esecuzione troppo lentamente, causando in tal modo artefatti video.

+0

In caso di aggiunta: non dimenticare di aggiornare solo dirty rect (GetUpdateRect) al posto dell'intera finestra ... – Jurlie