2012-02-16 13 views
35

Ho trovato diversi modelli per l'ottimizzazione della gestione di bitmap in WPF. Tuttavia, non capisco quando utilizzare ciascun modello. Poiché penso che questo sia un problema comune, ho riassunto ciò che ho capito e ciò che immagino e chiedo il tuo aiuto. Se è possibile aggiungere modelli, spiega come si differenziano, spiegare se usano la CPU o la GPU, e insegnare quando utilizzare ogni e come combinarli, sarebbe di grande aiuto !Pattern di ottimizzazione delle prestazioni bitmap

Contesto - Le immagini Scenario "Grid":

La mia applicazione deve visualizzare molte immagini bitmap. Le immagini vengono visualizzate sullo schermo in un'organizzazione simile a una griglia di righe e colonne (non necessariamente le classi Grid o UniformGrid, si pensi alla vista Album di Windows Media Player). Le immagini potrebbero spostarsi tra diverse celle della griglia. Alcune immagini su celle arbitrarie possono essere sostituite da altre. Le immagini dovrebbero essere selezionabili, dovrebbero fornire un menu contestuale, dovrebbero essere selezionabili, resistenti al trascinamento ecc. In altre parole, "combinare i piccoli buggers in una grande bitmap" non è applicabile, almeno non in modo ingenuo.

Modello 0: La Hack

Combino il piccolo maledice in una bitmap, e utilizzare questo come sfondo (come disegno contesto??). Sovrapponi questo con immagini con contenuti vuoti che gestiranno hit, menu contestuali, eventi, ecc.

Il vantaggio è che stiamo parlando solo di due bitmap qui: quello attualmente visualizzato e quello che dovrebbe sostituirlo. Questo dovrebbe essere molto veloce. Tuttavia, i miei anni di esperienza sollevano la bandiera rossa del pericolo. I tuoi commenti?

Motivo 1: ridurre la dimensione dell'immagine

Questo è un gioco da ragazzi quando si conosce in anticipo le dimensioni dell'immagine per ridimensionare, e quando si è disposti a perdere i dettagli (colore) per le prestazioni:

  1. Ridurre la dimensione del bitmap utilizzando BitmapImage.DecodePixelWidth
  2. Ridurre le informazioni sul colore utilizzando FormatConvertedBitmap.DestinationFormat
  3. impostare il comportamento di scala del controllo s etting Image.Stretch to Stretch.None
  4. Imposta il SetBitmapScalingMode per l'immagine su LowQuality.
  5. congelare il bastardo

vedi codice here.

Campione 2: Background pre-fetch

Questo modello è applicabile quando si pensa di poter usufruire degli utenti a guardare le immagini sullo schermo, e preparare in anticipo le prossime immagini da visualizzare. Gli svantaggi per il tuo progetto, oltre al sovraccarico della memoria, è che deve supportare l'obiettivo .Net Framework 4 e non solo il profilo del client, in modo che possa incorrere in un'installazione sul client. Tu stesso dovrai soffrire il dolore asincrono della programmazione.

In questo modello si crea esattamente il numero richiesto di controlli Immagine. Quando le bitmap devono essere aggiunte, spostate o eliminate, si modificano solo le risorse Bitmap dei controlli immagine. Un'attività di BackgroundWorker è responsabile della prelettura delle risorse Bitmap (possibilmente utilizzando il modello "Riduci dimensioni immagine" sopra) e di inserirle in MemoryCache.

Per far funzionare questo, è necessario impostare CacheOption di BitmapImage su OnLoad, in modo che il lavoro venga scaricato sull'operatore in background.

Motivo 3: Disegno Contesto

Questo è stato suggerito da Sheldon Ziao da Microsoft Support sul forum MSDN WPF here. Vedi pagina 494, Capitolo 15 "Grafica 2D" in Adam Nathan WPF 4 Unleashed per una descrizione di DrawingContext. Non posso dire di aver capito. Secondo la risposta here, suppongo che ciò migliorerebbe la gestione dei disegni Geometry, non delle bitmap. Successivamente, non penso che questo supporterà i requisiti di messa a fuoco e di eventi per le immagini (il mio male per non spiegare meglio i requisiti al forum) Inoltre, sono preoccupato dalla frase di riepilogo del libro: "Notare che l'uso di DrawingContext non cambia il fatto che stai operando all'interno di un sistema in modalità permanente. Il disegno specificato non si verifica immediatamente; i comandi sono mantenuti da WPF fino a quando non sono necessari. "Ciò significa che una volta che il nostro gestore pari non riusciamo a sfruttare il parallelismo come in" Sfondo pre-fetch ".

Schema 4: scrivibili bitmap

La documentazione MSDN here descrive come un sistema a doppio buffer: Your thread UI aggiorna il buffer; il thread di rendering di WPF lo sposta nella memoria video.

L'utilizzo previsto (vedere here) è per bitmap che cambiano molto come in un film video come display. Non ne sono sicuro, ma è possibile che ciò possa essere compromesso e combinato con il pattern Pre-prelettura in background e utilizzato nello scenario della griglia.

modello 5: bitmap memorizzata nella cache

Non molto informazioni sul MSDN (here). Nell'archivio del forum WPF (here) viene spiegato che "L'API BitmapCache è progettata per memorizzare nella cache il contenuto (durante il rendering in hardware) nella memoria video, il che significa che rimane residente sulla GPU. Questo ti fa risparmiare il costo di ripetere il rendering di quel contenuto quando lo si disegna sullo schermo. "Questa sembra una grande idea. Non sono sicuro, tuttavia, quali sono le insidie ​​e come usarlo.

Motivo 6: RenderTargetBitmap

Il RenderTargetBitmap converte un illustrato in bitmap. Non sono sicuro se è rilevante qui. Vedi here.

Modifica: Per quanto riguarda la domanda di Paul Hoenecke: Ho scritto che "La mia applicazione deve visualizzare molti tipi di bitmap". Ho omesso di menzionare che ho bisogno di visualizzare circa 800 immagini contemporaneamente.

Si può leggere sui problemi di prestazioni coinvolti a mie domande in modo WPF Bitmap performance e How can I make displaying images on WPF more “snappy”?

Ho modificato la descrizione del modello 1 per evidenziare il concetto che i controlli di immagine non vengono creati o eliminati (a meno che non vogliamo per visualizzare una griglia più grande o più piccola). Solo le Sorgenti sono impostate su BitmapSources differenti, nuove o nulle.

Edit: This question as posted on the WPF support forum, con alcune risposte da personale MS.

+1

Quante immagini stai anticipando? Ho creato un'app che mostra una griglia di migliaia di immagini prima. Abbiamo usato una casella di riepilogo in modalità virtuale; mentre l'utente scorre verso il basso, le immagini vengono caricate in un thread in background, congelate e impostate sulla Sorgente dell'immagine. Chiaramente, ci hai messo un sacco di pensiero in questo ... ma forse sapere di più su quello che vuoi ottenere sarebbe meglio. Ad esempio, quali problemi hai avuto che rendono l'ottimizzazione di una tale priorità? –

+0

@PaulHoenecke Ciao Paul, grazie per la tua risposta. Le raccolte virtuali ti aiutano quando vuoi visualizzare un piccolo numero di elementi fuori da una grande collezione. Qui, voglio visualizzare circa 1K articoli contemporaneamente. Inoltre, non sono sicuro che esista una griglia di virtualizzazione. Infine, il pattern 1 è essenzialmente virtualizzato - aggiungerò en edit. – Avi

+0

Avi, la tua domanda sembra inappropriata per il problema descritto. Potresti voler migliorare il rapporto di compressione per qualche motivo, ma poi devi sapere esattamente che tipo di immagini stai visualizzando. Oppure, dall'altra parte, potresti dare all'utente un'impressione di tutte le immagini disponibili, quindi potresti definire alcuni set e visualizzare miniature o simili. Ma entrambe le cose non hanno necessariamente a che fare l'una con l'altra. Forse vuoi qualcosa di simile a una metrica di similarità per definire insiemi di immagini simili? –

risposta

17

Non riesco a trovare una domanda specifica nel tuo post, oltre a chiedere commenti sugli approcci seguenti. Non pretenderò di sapere tutto quanto sopra ma ti dirò quello che so di aver lavorato per un po 'nello sviluppo di interfacce utente ad alte prestazioni usando WPF e Silverlight.

Pattern 0: The Hack. Combinare tutto in una sola immagine

Eviterei questo se possibile. Sembra che tu voglia mostrare un grande pannello di migliaia di piccole immagini. Ogni immagine è quindi una miniatura (poiché non è possibile visualizzare migliaia di immagini grandi contemporaneamente). Di conseguenza, difenderei il caching/ridimensionamento rispetto alla combinazione.

Motivo 1: ridurre la dimensione dell'immagine

Se si sta visualizzando 1.000 immagini sullo schermo in una sola volta, si consideri la schermata immobiliare disponibile. Il monitor medio è di 1280x1024 pixel o poco più di 1,3 MPixel. 1000 immagini suggeriscono di ottenere una dimensione massima di 1300 pixel per immagine o 36 * 36. Diciamo che le tue immagini hanno una dimensione di 32 * 32. Dovresti sicuramente creare una miniatura della dimensione dell'immagine da renderizzare sullo schermo, quindi al clic (o altra azione) mostrare l'immagine a dimensione intera.

Considerare inoltre non solo il sovraccarico di rendering del ridimensionamento di un'immagine grande, ma di inviare un'immagine grande alla GPU per ridimensionarla. Questi dati richiedono l'invio di larghezza di banda. Un'immagine grande può essere di diversi megabyte, mentre una miniatura della dimensione 32 * 32 potrebbe essere di alcuni kilobyte.

Se si richiede il dimensionamento dinamico, bene, ma è necessario sperimentare con la creazione di miniature multiple o generarle al volo.

Campione 2: Background pre-fetch

Questa è una tecnica che non ho sentito parlare, ma sembra plausibile. Qual è il sovraccarico nella tua applicazione? sta aggiornando la proprietà Image.Source o creando una nuova immagine, tessellando, eseguendo il layout e inviando le informazioni per renderlo alla GPU?

Tutte le operazioni precedenti si verificano sulla CPU ad eccezione del rendering finale. Riducendo il carico di lavoro sul lato CPU e aggiornando la fonte si potrebbe essere a qualcosa. Combina questo con WriteableBitmap come fonte e potresti migliorare ulteriormente le prestazioni (vedi sotto).

Motivo 3: Disegno Contesto

Ok, tutto questo non è consentono di fare la coda modalità trattenuta disegno chiamate utilizzando un "OnPaint" Sintassi stile che è niente come il vecchio GDI OnPaint.Nella mia esperienza OnRender non migliora le prestazioni, ma consente una flessibilità a grana fine su ciò che viene disegnato e quando. OnRender ti fornisce un contesto, che ha una funzione DrawImage, che consente di disegnare BitmapSource sulla pipeline di rendering senza la necessità di un controllo Image. Questo è buono in quanto rimuove alcuni overhead, tuttavia introduce problemi simili a quelli visti in Pattern0 (perderai il layout e dovrai calcolare la posizione di tutte le tue immagini). Se lo fai, potresti anche invocare Pattern 0, che ho sconsigliato.

Modello 4: scrivibili Bitmap

WriteableBitmaps sono un po 'sottosistema utilizzato e straordinariamente potente all'interno WPF. Li uso con grande efficacia per creare un componente per la creazione di grafici in grado di eseguire grandi quantità di dati in tempo reale. Vorrei suggerire di verificare il progetto di codeplex WriteableBitmapEx Disclosure, ho contribuito a questo una volta e vedendo se è possibile combinarlo con altri modelli. In particolare la funzione Blit che consente di scrivere una bitmap memorizzata nella cache su una sorgente bitmap su un'immagine.

Ad esempio, una buona tecnica potrebbe essere Motivo 1 + 2 + 4.

Si potrebbe avere una griglia di N controlli immagine sullo schermo in località impostate in un controllo griglia. Ognuno di questi è statico e non viene spostato fuori dalla vista, quindi non ci sono creazioni/eliminazioni in corso. Ora, in cima a tutto, ridimensiona l'immagine e scrivi su WriteableBitmap, impostato come proprietà Source su ogni immagine. Mentre scorri, ottieni le anteprime precedenti/precedenti e aggiorna i sorgenti usando WriteableBitmapEx.Blit. Pow! bontà di imaging virtualizzata, memorizzata nella cache, multi-thread.

Motivo 5: bitmap nella cache

Questo è un tentativo di Microsoft di fare 1 + 2 + 4 come discusso in precedenza. Quello che cerca di fare è dopo il layout (lato CPU), la tassellatura (lato CPU), l'invio di istruzioni di rendering in modalità conservata alla GPU (lato CPU) e rendering (lato GPU) memorizza nell'immagine raster l'elemento reso che viene riutilizzato usato nel prossimo passaggio di rendering. Sì, un fatto poco noto di WPF è che il meraviglioso motore GPU è terribilmente lento come gran parte del suo lavoro sulla CPU: P

Vorrei sperimentare con BitmapCache e vedere come si comporta. Ci sono avvertenze, e sono queste che quando aggiorni il tuo UIElement deve ricreare la cache in modo che gli elementi statici funzionino molto meglio di quelli dinamici. Inoltre non ho visto un miglioramento significativo delle prestazioni dall'uso di questo mentre le tecniche di stile WriteableBitmap possono dare un miglioramento dell'ordine di grandezza.

Modello 6: RenderTargetBitmap

Questa tecnica finale consente il rendering di un UIElement a una bitmap - si sa che - ma ciò che è interessante è questo possibile eseguire un generatore di miniature povero-mans (o si ridimensiona) . Ad esempio, imposta un'immagine con BitmapSource dell'immagine a dimensione intera. Ora imposta la dimensione del controllo Image su 32 * 32 e renderizza su bitmap. Ecco! Hai la tua miniatura BitmapSource da usare in combinazione con alcuni bitmap di scambio (Pattern 2) e/o scrivibili.

Ok, infine, volevo solo dire che il requisito che hai spinge WPF ai suoi limiti, tuttavia ci sono modi per farlo funzionare. Come ho detto, ho sistemi di compilazione che hanno reso migliaia o milioni di elementi sullo schermo in una sola volta usando la soluzione meravigliosa che è WriteableBitmap. Scendere lungo la rotta WPF standard si tradurrà in un inferno di prestazioni, quindi dovrai fare qualcosa di esotico per risolvere questo problema.

Come ho detto la mia raccomandazione è 1 + 2 + 4. Devi ridimensionare una miniatura, di questo non ho dubbi. L'idea di avere una griglia statica di controlli dell'immagine e l'aggiornamento delle fonti è molto buona. L'idea di usare WriteableBitmap (in particolare la funzione blit WriteableBitmapEx) per aggiornare le fonti è anche una cosa da esplorare.

Buona fortuna!

+0

Grazie per la risposta! Un'antica storia della Pasqua ebraica descrive quattro figli: saggio, cattivo, ingenuo e uno che non sa nemmeno quali domande porre. Sento che mi manca così tanto lo sfondo che non sono nemmeno in grado di porre le domande giuste: "tassellare"? Sei a conoscenza di qualsiasi libro o articolo che descrive la "pipeline" grafica (2D) in WPF? Quali sono le attività, cosa fa la CPU, cosa fa la GPU? – Avi

+1

Sì, sì! Vedi questo articolo: http://jeremiahmorrill.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/ Questa è roba piuttosto esoterica, non sentirti male. La maggior parte ti dirà "Just override OnRender" per le prestazioni, o "WPF usa la GPU", ma questa è la risposta sbagliata. Quando ti rendi conto che WPF fa gran parte del suo lato CPU di lavoro (zoppo !!), allora puoi iniziare a ottimizzare le prestazioni. –

+0

Passando attraverso la traccia che mi hai inviato, comincio ad avere la sensazione che WPF sia intrinsecamente lento e che si debba aspettare Direct2D o WinRT ... – Avi

Problemi correlati