2014-12-16 19 views
5

Sto provando a scorrere facilmente alcune immagini attraverso una finestra usando DirectX11 tramite SharpDX in un'applicazione WPF.Animazione a scatti quando si scorre l'immagine in WPF usando SharpDX

Un po 'di background:

Le immagini sono rendimenti di segnale, variando dal tempo, che sono stati caricati da un file e caricato in D3D come una matrice consistenza, e l'immagine stessa è resa come una serie di texture quad (una serie di rettangoli adiacenti in una linea orizzontale molto lunga). Il viewport è impostato in modo che i bordi sinistro e destro rappresentino gli offset temporali e il segnale viene visualizzato per il periodo di tempo che intercorre tra questi tempi. L'implementazione di DirectX è molto semplice; i vertici quadrupli vengono generati una volta e un buffer per fotogramma molto piccolo contiene una semplice trasformazione della vista del mondo 2D che aggiorna la scala e la traduzione in base all'intervallo/zoom visibile corrente ecc. Per quanto ne so, l'implementazione D3D è non fa parte del problema che sto avendo - è davvero un set up molto semplice, e sembra essere estremamente veloce (come dovrebbe), e anche se ho qualche roba più elaborata (streaming di trame dal disco come richiesto), io hanno disattivato tutti questi e sono in esecuzione con molto semplici (piccoli) texture, mentre cerco di risolvere il problema che sto avendo ..

il problema:

scorrimento dell'immagine non è liscia, quando l'intervallo di tempo visibile è animato. L'immagine "tremola" per la maggior parte del tempo, e francamente sembra orribile (quando non è tremolante, sembra fantastico).

Il setup:

DirectX è reso ad un D3DImage, con un po 'di lavori in corso dietro le quinte per far funzionare DX11 - questo codice è tratto dal https://sharpdxwpf.codeplex.com/

Ci sono molteplici D3DImages (fino a un totale di 4), disposti in una griglia (ho provato con due, entrambi contengono segnali per lo stesso periodo di tempo e sono animati insieme).

Questi D3DImages vengono disegnati da un DrawingVisual (che è ospitato da un FrameworkElement personalizzato) in cui vengono utilizzati come origine di ImageBrush. L'oggetto frameworkelement attiva un rendering come richiesto e il disegno visivo gestisce la chiamata di rendering D3D e disegna un rettangolo per riempire il controllo utilizzando il pennello D3DImage.

Il valore dell'intervallo di tempo viene animato utilizzando una DoubleAnimation WPF. Le immagini attualmente visualizzate sono associate a questo valore tramite INotifyPropertyChanged e attivano un rendering (tramite InvalidateVisual) ad ogni modifica.

DrawingVisual rendere il codice, innescato dal cambiamento di valore "Posizione" (inizio intervallo di tempo visibile):

// update scene per-frame buffer first, with world-view transform 
using (DrawingContext dc = RenderOpen() 
{ 
    _scene.Renderer.Render(_draw_args); 
    _image.Invalidate(); 

    dc.DrawRectangle(_brush, null, new Rect(viewer.RenderSize)); 
} 

quello che ho provato:

Ho provato un certo numero di cose (e ricercata molto) per provare a determinare se il problema è dovuto a tempi di richiesta di rendering nervosi, o se il problema è ulteriormente nel processo di rendering.

  1. Aggancio in CompositionTarget.Rendering Innanzitutto guidare l'aggiornamento del valore di posizione tramite CompositionTarget.Rendering, e lasciando il resto del processo così com'è (cioèelementi reagiscono al cambiamento di questo valore):

(manco a dirlo, questo è molto codice "test"):

Stopwatch rsw; 
long last_time = 0; 
void play() 
{ 
    rsw = new Stopwatch(); 
    last_time = 0; 
    rsw.Start(); 
    CompositionTarget.Rendering += rendering; 
} 

void rendering(object sender, EventArgs e) 
{ 
    double update_distance = rsw.ElapsedMilliseconds - last_time; 
    Position += 10 * update_distance; 
    last_time = rsw.ElapsedMilliseconds; 
} 

Risultati - peggio del semplice doppio animazione.

  1. Utilizzo di DispatcherTimer. Simile a sopra, utilizzando un timer in combinazione con un cronometro per giudicare un po 'meglio il tempo trascorso. Peggi ancora risultati, con l'interessante nota a margine che l'utilizzo della CPU è sceso dal 7% circa (mezzo core) allo 0,7%.

Il tentativo originale, insieme alle varianti 1 & 2, tenta di aggiornare un singolo valore che attiva il rendering delle parti interessate. Interessante, ho registrato le differenze di tempo tra le richieste di rendering non appena hanno raggiunto la visuale del disegno - sebbene DisptacherTimer abbia effettivamente dato i risultati più coerenti (sia l'animazione WPF che CompositionTarget hanno abbandonato il fotogramma dispari), anche DisptacherTimer ha dato l'animazione più nervosa.

  1. Aggiornamento dell'immagine, ma non invalidazione dell'immagine. L'aggiornamento dell'origine di ImageBrush aggiorna l'immagine visualizzata, senza dover eseguire nuovamente il rendering di DrawingVisual. Questo metodo ha prodotto risultati molto nervosi.

  2. CompositionTarget.Rendering nell'elemento quadro stesso. Questo ha prodotto i migliori risultati del lotto, ma non è ancora perfetto. Il jitter accadrebbe, quindi si dissiperebbe, solo per ritornare di nuovo. In questo approccio, l'elemento framework che tiene il DV si aggancia a CompositionTarget.Rendering e la query visiva alla posizione corrente, che viene animata in modo indipendente. Potrei quasi vivere con questo approccio.

  3. Cercando l'attesa fino alla scena D3D è reso, prima di invalidare l'immagine (nessun miglioramento visibile):

    _scene.Renderer.Render (_draw_args);
    _scene.Renderer.Device.ImmediateContext.End (q);

    while (! (_ Scena.Renderer.Device.ImmediateContext.IsDataDisponibile (q))) Thread.Yield();

    _image.Invalidate();

Osservazioni:

  1. Io davvero non credo che questo è un problema di prestazioni in quanto tale. La mia macchina dev ha una buona scheda grafica, 8 core i7 ecc. E questa è una semplice operazione di rendering.
  2. Questo sembra davvero una sorta di problema di sincronizzazione tra D3D e il rendering di WPF, ma non ho idea di come iniziare a esaminare questo.
  3. Se ho due immagini che si animano in parallelo, il jitter è molto più pronunciato sul primo dei due (di solito).
  4. Se ridimensiono attivamente la finestra durante l'animazione, l'animazione è perfettamente fluida (nonostante il fatto che sia stato fatto molto lavoro extra, dato che il contesto D3D viene ridimensionato costantemente).

EDIT

Ho preso le cose per quanto possibile per cercare di isolare il problema, e la questione sembra essere fondamentale per il modo in cui viene aggiornato D3DImage & resi da WPF.

Ho modificato l'esempio WPFHost nella soluzione SharpDX Samples in modo che il semplice trigangle visualizzato sia animato sullo schermo. Questo esempio è ospitato in una DX10ImageSource resa da un DPFCanvas su CompositionTarget.Rendering.

Questo esempio non potrebbe essere più semplice e si presenta come "vicino al metallo", come si può ottenere durante il rendering di un D3DImage in WPF. Un singolo triangolo, tradotto attraverso lo schermo da un valore calcolato dalla differenza di tempo tra i rendering. La balbuzie rimane, va e vieni, come se fosse una sorta di problema di sincronizzazione. Mi sconcerta, ma sostanzialmente rende SharpDX inutilizzabile all'interno di WPF per qualsiasi tipo di animazioni fluide, il che è estremamente deludente.

Se qualcuno è interessato a riprodurre questo problema, i campioni SharpDX sono disponibili qui: https://github.com/sharpdx/SharpDX-Samples

ho fatto le seguenti semplici modifiche al l'esempio WPFHost:

void IScene.Render() 
{ 
... 

     EffectMatrixVariable wv = this.SimpleEffect.GetVariableBySemantic("WorldView").AsMatrix(); 
     wv.SetMatrix(this.WorldViewMatrix); 

... 
} 

void IScene.Update(TimeSpan sceneTime) 
{ 
    float x = (float)sceneTime.Milliseconds * 0.001f - 0.5f; 
    WorldViewMatrix = Matrix.Translation(x, 0, 0); 
} 

e Shader Simple.fx:

float4x4 WorldViewTransform : WorldView; 

PS_IN VS(VS_IN input) 
{ 
    PS_IN output = (PS_IN)0; 

    output.pos = mul(input.pos, WorldViewTransform); 
    output.col = input.col * Overlay; 

    return output; 
} 

risposta

0

D3DImage è fondamentalmente rotto.

Se non è necessario sovrapporre elementi XAML a D3D o non è necessario ridimensionare troppo frequentemente la superficie D3D, preferisce ospitare un HWND/WinForm nelle app WPF e renderizzarle direttamente utilizzando il rendering normale ciclo continuo. Se desideri maggiori dettagli sui motivi, puoi controllare questo issue.

+1

Ouch - questa non è la risposta che speravo! (ma grazie comunque). Sembra così, se non è nemmeno possibile animare un singolo triangolo senza problemi e non ci sono soluzioni alternative note. In realtà sto aggiornando una app WPF abbastanza consistente per usare SharpDX e perdendo tutto il overlay le capacità sarebbero seriamente limitanti - indagherò su quanto plausibile sia quella rotta, oppure potremmo o dover vivere con la balbuzie o tornare ad un approccio DrawingVisual (che in realtà era più agevole, ma limitato in altri modi). – Matt