2011-04-28 13 views
18

Estoy midiendo el tiempo entre fotogramas en una simple animación WPF. Perforator dice que la aplicación funciona a ~ 60 fps, por lo que esperaba que el tiempo entre fotogramas fuera ~ 16,6 m con poca desviación.¿Por qué la velocidad de fotogramas en WPF es irregular y no está limitada a la actualización del monitor?

public MainWindow() 
    { 
    ... 
     CompositionTarget.Rendering += Rendering; 
    } 

    List<long> FrameDurations = new List<long>(); 
    private long PreviousFrameTime = 0; 
    private void Rendering(object o, EventArgs args) 
    { 
     FrameDurations.Add(DateTime.Now.Ticks - PreviousFrameTime); 
     PreviousFrameTime = DateTime.Now.Ticks; 
    } 

Dos cosas me sorprendieron:

  • tiempo entre cuadros es bastante irregular
  • tiempo entre tramas es ~ 8 ms. Esperaba que la frecuencia de actualización del monitor estableciera un límite inferior de tiempo entre fotogramas (es decir, 60Hz = 16.6ms entre cada fotograma, y ​​cualquier cosa más rápida no tiene sentido).

DefaultFrameRate

Y - Tiempo entre tramas en las garrapatas (10.000 registros = 1ms)
X - Frame recuento

posibles factores de confusión

  • temporizador inexactitud
  • Si CompositionTarget .Rendering en realidad no se correlaciona con el dibujo de un solo cuadro

El proyecto que estoy usando: SimpleWindow.zip

=== Editar

Markus señaló que podría estar utilizando RenderingEventArgs.RenderingTime.Ticks en lugar de DateTime.Now.Ticks. Repetí la carrera y obtuve resultados muy diferentes. La única diferencia es el tiempo método:

DateTime.Now.Ticks

DefaultFrameRate

RenderingEventArgs.RenderingTime.Ticks

enter image description here

datos de RenderingEventArgs producido datos mucho más cerca el esperado 16.6ms/frame, y es consistente.

  • No estoy seguro de por qué DateTime.Now y RenderingEventArgs producirían datos tan diferentes.
  • Suponiendo que RenderingEventArgs está produciendo los tiempos correctos, todavía es un poco desconcertante que esos tiempos no sean los 16.6ms esperados.

Si la pantalla se actualiza cada 16.6ms y WPF se actualiza cada 14.9ms, podemos esperar una condición de carrera que provoque la rotura. Es decir, aproximadamente cada décimo marco WPF intentará escribir su imagen mientras la pantalla intenta leer la imagen.

+0

La clase cronómetro es el camino a seguir para evitar temporizador inexactitud –

+0

esto 'podría' ser útiles : http://rhnatiuk.wordpress.com/2008/12/21/wpf-video-playback-problems/ - No estoy seguro de cuán relevante es este problema, pero creo que todavía existe en la actualidad. –

+3

@Tristan @Martin ¡no es necesario medir el tiempo! puede [lanzar EventArgs a RenderingEventArgs para obtener el RenderTime] (http://msdn.microsoft.com/en-us/library/system.windows.media.compositiontarget.rendering (VS.95) .aspx) –

Respuesta

19

me planteó esta cuestión con el equipo de WPF y aquí es un resumen de la respuesta que me dieron:

El cálculo de la tasa de fotogramas del hilo de interfaz de usuario es difícil. WPF desacopla el subproceso UI del subproceso. El hilo de interfaz de usuario hará que:

  • Cada vez que algo se marca como sucio y que drene hacia abajo para Render prioridad. Esto puede ocurrir más a menudo que la frecuencia de actualización.

  • Si una animación está pendiente (o si alguien enganchado caso CompositionTarget.Rendering) que renderizaremos en el hilo de interfaz de usuario después de cada presente desde el hilo de procesamiento. Esto implica avanzar el árbol de sincronización tan animaciones calculan sus nuevos valores.

Debido a esto, el evento CompositionTarget.Rendering puede ser levantado varias veces por “marco”. Presentamos el pretendido “marco temporal” en los RenderingEventArgs y aplicaciones sólo debe hacer “por trama” funciona cuando el tiempo cambia reportados marco.

Tenga en cuenta que el hilo de interfaz de usuario está haciendo muchas cosas , por lo que no es confiable para asume el controlador de eventos CompositionTarget.Rendering funciona a una cadencia fiable. El modelo que usamos (desacoplando los dos hilos) significa que el subproceso UI puede estar un poco atrás, ya que está calculando animaciones para un tiempo de fotograma futuro.

Gracias especiales a Dwayne Necesito explicarme esto.

+5

Impresionante. ¿Dónde puedo aprender más cosas como esta? Hay un buen libro? He estado teniendo problemas para encontrar información de WPF 'bajo el capó'. Uno de los problemas es que, dado que WPF es tan fácil (en relación con GDI +, etc.), hay muchas personas escribiendo sobre ello que no comprenden realmente lo que están haciendo, pero aún escriben al respecto. Parece que hay bastante información de mala calidad para filtrar. – Tristan

2

WPF no está diseñado para ser un sistema de reproducción de velocidad de cuadro constante. WPF hace una pantalla cuando los elementos en la pantalla están marcados como cambiados.El sistema de renderización se ejecuta como un bucle de mensaje, por lo que no hay forma de garantizar que se renderice un cuadro en intervalos específicos.

+0

Tu respuesta tiene sentido, y es consistente con todo lo que estoy viendo. ¿Puede proporcionar datos o medios para respaldar su afirmación? (Además, gracias por su tiempo, lo aprecio) – Tristan

+1

Puede consultar este video en la arquitectura de WPF en Channel 9 de uno de los diseñadores de WPF (Hablan mucho de Avalon, era el nombre en clave de WPF antes de su release) [link] (http://channel9.msdn.com/Shows/Going+Deep/Greg-Schechter-Windows-Presentation-FoundationWPF-Architecture) – Vicro

+1

También encontré información sobre [MSDN] (http: // msdn .microsoft.com/en-us/library/ms748373.aspx # visual_rendering_behavior) sobre el comportamiento de representación. – Vicro

8

Primera - 'Christopher Bennage's-respuesta tiene una buena explicación y proporciona una pista para una solución:

'Sólo hacer ‘por trama’ de trabajo cuando el tiempo de trama reportado cambios'

Ésta es una un poco difícil, ya que los RenderingEventArgs están ocultos como un EventArgs normal y se debe realizar un reparto.

Para hacer esto un poco más facil poco, una solución conveniente se puede encontrar en "CLUNKERS CÓDIGO de Evan" http://evanl.wordpress.com/2009/12/06/efficient-optimal-per-frame-eventing-in-wpf/

Tomé su código y modifiqué un poco. Ahora acaba de tomar mi cortado con tijeras, agregue la clase a su proyecto, utilice CompositionTargetEx eran CompositionTarget que utilizó y que está bien :)

public static class CompositionTargetEx { 
    private static TimeSpan _last = TimeSpan.Zero; 
    private static event EventHandler<RenderingEventArgs> _FrameUpdating; 
    public static event EventHandler<RenderingEventArgs> Rendering { 
     add { 
      if (_FrameUpdating == null)     
       CompositionTarget.Rendering += CompositionTarget_Rendering; 
      _FrameUpdating += value; 
     } 
     remove { 
      _FrameUpdating -= value; 
      if (_FrameUpdating == null)     
       CompositionTarget.Rendering -= CompositionTarget_Rendering; 
     } 
    } 
    static void CompositionTarget_Rendering(object sender, EventArgs e) { 
     RenderingEventArgs args = (RenderingEventArgs)e; 
     if (args.RenderingTime == _last) 
      return; 
     _last = args.RenderingTime; _FrameUpdating(sender, args); 
    } 
} 
Cuestiones relacionadas