2012-02-16 12 views
35

Encontré varios patrones para optimizar el manejo de mapas de bits en WPF. Sin embargo, no entiendo cuándo usar cada patrón. Como creo que este es un problema común, resumí lo que entiendo y lo que supongo y le pido su ayuda. Si se puede añadir patrones , explicar cómo se diferencian, explique si utilizan la CPU o la GPU , y enseñar cuándo utilizar cada y cómo combinarlos, que sería una gran ayuda !Patrones de optimización del rendimiento de mapa de bits

Contexto - Las imágenes "cuadrícula" Escenario:

Mi aplicación tiene que mostrar muchas imágenes de mapa de bits. Las imágenes se muestran en la pantalla en una organización cuadriculada de filas y columnas (no necesariamente las clases Grid o UniformGrid, piense en la vista Album de Window Media Player). Las imágenes pueden moverse entre diferentes celdas de la grilla. Algunas imágenes en celdas arbitrarias pueden ser reemplazadas por otras. Se debe poder hacer clic en las imágenes, proporcionar un menú contextual, seleccionarlas, arrastrarlas, etc. En otras palabras, "combinar los pequeños insectores en un gran mapa de bits" no es aplicable, al menos no ingenuamente.

Patrón 0: El Hack

Combino los pequeños bichos en un mapa de bits, y usar esto como el fondo (cómo dibujar contexto??). Superponga esto con imágenes con contenido vacío que manejará los hits, menús contextuales, eventos, etc.

La ventaja es que aquí solo hablamos de dos mapas de bits: el que se muestra actualmente y el que debería reemplazarlo. Esto debería ser realmente rápido. Sin embargo, mis años de experiencia levantan la bandera roja de peligro. ¿Tus comentarios?

Patrón 1: Reducir el tamaño de la imagen

Ésta es una obviedad cuando se sabe de antemano el tamaño de la imagen para cambiar el tamaño a, y cuando estás dispuesto a perder detalles (color) para la prestación del servicio:

  1. Reducir el tamaño de mapa de bits utilizando BitmapImage.DecodePixelWidth
  2. Reducir la información de color usando FormatConvertedBitmap.DestinationFormat
  3. Establecer comportamiento de la escala del control de s etting Image.Stretch to Stretch.None
  4. Establezca SetBitmapScalingMode para la imagen en LowQuality.
  5. congelar el cabrón

Ver código here.

Patrón 2: Antecedentes de solicitud previa

Este patrón es aplicable cuando se piensa que puede tomar ventaja de que el usuario mirando las imágenes en la pantalla, y se prepara con anticipación las siguientes imágenes para mostrar. Las desventajas de su proyecto, además de la sobrecarga de memoria, es que tiene que admitir el objetivo de .Net Framework 4 y no solo el perfil del cliente, por lo que puede incurrir en una instalación en el cliente. Usted mismo tendrá que sufrir el dolor de la programación asincrónica.

En este patrón, crea exactamente el número requerido de controles de imagen. Cuando se deben agregar, mover o eliminar mapas de bits, solo se modifican los 'BitmapSource (s)' de los controles de imagen. Una tarea de BackgroundWorker es responsable de la precarga de los BitmapSource (s) (posiblemente usando el patrón "Reducir el tamaño de imagen" arriba) y de insertarlos en MemoryCache.

Para que esto funcione, debe establecer CacheOption de BitmapImage en OnLoad, para que el trabajo se descargue al trabajador en segundo plano.

Patrón 3: Dibujo Contexto

Esto fue sugerido por Sheldon Ziao de soporte técnico de Microsoft en el foro de MSDN WPF here. Consulte la página 494, Capítulo 15 "Gráficos 2D" en Adam Nathan's WPF 4 Unleashed para obtener una descripción de DrawingContext. No puedo decir que lo entiendo De acuerdo con la respuesta here, supongo que esto mejoraría el manejo de los dibujos de Geometría, no mapas de bits. A continuación, no creo que esto respalde los requisitos de enfoque y eventos para las imágenes (mi mal por no explicar mejor los requisitos en el foro). Además, estoy preocupado por la oración de resumen del libro: "Tenga en cuenta que el uso de DrawingContext no cambia el hecho de que está operando dentro de un sistema de modo retenido. El dibujo especificado no ocurre inmediatamente; los comandos son persistentes por WPF hasta que sean necesarios. "Esto significa que una vez que nuestro manejador par es re no podemos aprovechar el paralelismo como en" Anteproducción de fondo ".

Patrón 4: puede ser escrito mapas de bits

La documentación de MSDN here lo describe como un sistema de doble tampón: Su hilo de interfaz de usuario se actualiza la memoria intermedia; el hilo de renderizado de WPF mueve esto a la memoria de video.

El uso previsto (ver here) es para mapas de bits que cambian mucho en una película de video como la pantalla. No estoy seguro, pero es posible que se haya pirateado y se haya combinado con el patrón Pre-fetch de fondo y se haya utilizado en el escenario de la cuadrícula.

Patrón 5: mapa de bits en caché

No hay mucha información sobre el MSDN (here). En el archivo del foro WPF (here) se explica que "La API BitmapCache está diseñada para almacenar en caché su contenido (cuando se procesa en hardware) en la memoria de video, lo que significa que permanece residente en su GPU. Esto le ahorra el costo de volver a procesar ese contenido cuando lo dibuja en la pantalla. "Esto parece una gran idea. No estoy seguro, sin embargo, cuáles son las trampas y cómo usarlo.

Patrón 6: RenderTargetBitmap

El RenderTargetBitmap convierte un Visual a un mapa de bits. No estoy seguro de si es relevante aquí. Ver here.

Editar: En relación con la pregunta de Paul Hoenecke: He escrito que "Mi aplicación tiene que mostrar muchos mapas de bits". No mencioné que necesito mostrar aproximadamente 800 imágenes al mismo tiempo.

Uno puede leer acerca de los problemas de rendimiento que participan en mis SO preguntas WPF Bitmap performance y How can I make displaying images on WPF more “snappy”?

He modificado la descripción del patrón 1 para resaltar el concepto de que los controles de imagen no se crean o se eliminan (a menos que queramos para mostrar una grilla más grande o más pequeña). Solo sus orígenes están configurados en BitmapSources diferentes, nuevos o nulos.

Editar: This question as posted on the WPF support forum, con algunas respuestas del personal de MS.

+1

¿Cuántas imágenes anticipas? He hecho una aplicación que muestra una grilla de miles de imágenes antes. Usamos un cuadro de lista de modo virtual; a medida que el usuario se desplaza hacia abajo, las imágenes se cargan en una secuencia de fondo, se congelan y se establecen en la fuente de la imagen. Claramente, ha pensado mucho en esto ... pero, tal vez sea mejor saber más sobre exactamente lo que quiere lograr. Por ejemplo, ¿qué problemas ha tenido que optimizan esa prioridad? –

+0

@PaulHoenecke Hola, Paul, gracias por tu respuesta. Las colecciones virtuales lo ayudan cuando quiere mostrar una pequeña cantidad de elementos de una gran colección. Aquí, quiero mostrar aproximadamente 1K elementos al mismo tiempo. Además, no estoy seguro de que exista una grilla de virtualización. Por último, el patrón 1 esencialmente se está virtualizando: agregaré en edit. – Avi

+0

Avi, su pregunta parece inapropiada para el problema descrito. Es posible que desee mejorar la relación de compresión por alguna razón, pero luego debe saber exactamente qué tipo de imágenes está mostrando. O, por otro lado, es posible que desee darle al usuario una impresión de todas las imágenes disponibles, luego puede definir algunos conjuntos y visualizar miniaturas o similares. Pero ambas cosas no necesariamente tienen que ver entre sí. ¿Tal vez desee algo así como una métrica de similitud para definir conjuntos de imágenes similares? –

Respuesta

17

No puedo encontrar una pregunta específica en su publicación, solo solicito comentarios sobre los enfoques a continuación. No pretenderé saber todo lo anterior, pero le diré lo que sé que he trabajado durante un tiempo desarrollando interfaces de usuario de alto rendimiento con WPF y Silverlight.

Patrón 0: The Hack. Combinando todo en una imagen

Evitaría esto si fuera posible. Parece que desea mostrar un panel envolvente grande con miles de imágenes pequeñas. Cada imagen es, por lo tanto, una miniatura (ya que no se pueden mostrar miles de imágenes grandes a la vez). Como resultado, recomendaría almacenar en caché/cambiar el tamaño de la combinación.

Patrón 1: Reducir el tamaño de la imagen

Si está mostrando 1.000 imágenes en pantalla a la vez, considere la pantalla de bienes raíces disponibles. El monitor promedio es de 1280x1024 píxeles, o algo más de 1.3MPixels. 1000 imágenes sugieren que obtendrá un tamaño máximo de 1300 píxeles por imagen, o 36 * 36. Digamos que tus imágenes tienen un tamaño de 32 * 32. Definitivamente debe crear una miniatura del tamaño de esa imagen para representarla en la pantalla, luego al hacer clic (u otra acción) mostrar la imagen de tamaño completo.

Considere también no solo la sobrecarga de procesamiento para cambiar el tamaño de una imagen grande, sino también el envío de una imagen grande a la GPU para cambiar el tamaño. Esa información requiere ancho de banda para enviar. Una imagen grande puede ser de varios megabytes, mientras que una miniatura de tamaño 32 * 32 podría ser de algunos kilobytes.

Si necesita un tamaño dinámico, está bien, pero tendrá que experimentar con la creación de varias miniaturas o generarlas sobre la marcha.

Patrón 2: Antecedentes de solicitud previa

Ésta es una técnica que no he oído hablar, sin embargo, parece plausible. ¿Cuál es la sobrecarga en tu aplicación? ¿Está actualizando la propiedad Image.Source o creando una nueva Imagen, tessellating, realizando Layout y enviando la información para renderizarla a la GPU?

Todo lo anterior ocurre en la CPU a excepción de la representación final. Al reducir la sobrecarga en el lado de la CPU y actualizar la fuente, es posible que se encuentre con algo. Combine esto con WriteableBitmap como fuente y podrá obtener una mejora en el rendimiento (ver más abajo).

Patrón 3: Dibujo Contexto

Ok, todo esto hace es permitir poner en cola hasta el modo de dibujo conservado llamadas utilizando una sintaxis estilo "OnPaint", que en nada a la edad GDI es OnPaint.En mi experiencia, OnRender no mejora el rendimiento, pero permite una flexibilidad de grano fino sobre lo que se dibuja y cuándo. OnRender le proporciona un contexto, que tiene una función DrawImage, que permite que un BitmapSource sea dibujado a la tubería de renderizado sin la necesidad de un control de imagen. Esto es bueno, ya que elimina algunos gastos generales, sin embargo, presenta problemas similares a los que se ven en Pattern0 (perderá el diseño y tendrá que calcular la posición de todas sus imágenes). Si haces esto, también podrías invocar el Patrón 0, al cual desaconsejé.

Patrón 4: puede ser escrito mapas de bits

WriteableBitmaps son un poco subsistema utilizado y extraordinariamente poderosa dentro de WPF. Los uso con gran efecto para crear un componente de gráficos capaz de generar grandes cantidades de datos en tiempo real. Yo sugeriría revisar el proyecto del codesplex WriteableBitmapEx Divulgación, he contribuido a esto una vez y ver si puede combinarlo con otros patrones. Específicamente, la función Blit que le permite escribir un mapa de bits en caché a un origen de mapa de bits en una imagen.

Por ejemplo, una buena técnica podría ser Patrón 1 + 2 + 4.

Usted podría tener una rejilla de controles Imagen N en la pantalla en localizaciones situado en un control de la red. Cada uno de estos es estático y no se desplaza fuera de la vista por lo que no hay creaciones/eliminaciones. Ahora, además de esto, cambia el tamaño de tu imagen y escribe en un WriteableBitmap que se establece como la propiedad Source en cada imagen. A medida que se desplaza, obtenga las miniaturas siguientes/anteriores y actualice las fuentes con WriteableBitmapEx.Blit. Pow! bondad de imágenes virtualizadas, en caché y con múltiples subprocesos.

Patrón 5: Bitmap cach

Este es un intento por Microsoft para hacer 1 + 2 + 4 como he discutido anteriormente. Lo que intenta hacer es después del diseño (lado de la CPU), teselado (lado de la CPU), enviar instrucciones de renderización de modo retenido a la GPU (lado de la CPU) y renderizado (lado GPU) almacena en caché una imagen ráster del elemento renderizado que es utilizado en el siguiente pase de representación. Sí, un hecho poco conocido sobre WPF es que el maravilloso motor con GPU es terriblemente lento ya que hace la mayor parte de su trabajo en la CPU: P

Experimentaría con BitmapCache y vería cómo funciona. Hay advertencias, y es que cuando actualiza su UIElement tiene que volver a crear la caché para que los elementos estáticos funcionen mucho mejor que los dinámicos. Además, no he visto una mejora significativa en el rendimiento al usar esto, mientras que las técnicas de estilo WriteableBitmap pueden dar una mejora de orden de magnitud.

Patrón 6: RenderTargetBitmap

Esta última técnica permite representar UIElement a un mapa de bits - usted sabe que - pero lo que es interesante es que esto puede llevar a cabo un generador de miniaturas de mala Mans (o cambiar el tamaño) . Por ejemplo, establezca una Imagen con BitmapSource de su imagen de tamaño completo. Ahora configure el tamaño del control de Imagen en 32 * 32 y renderice en mapa de bits. Voila! Tiene su miniatura de BitmapSource para usar junto con algunos intercambios (Patrón 2) y/o bitmaps grabables.

Ok, solo quería decir que el requisito que tiene empujará WPF a sus límites, sin embargo, hay formas de hacerlo funcionar. Como dije, tengo sistemas de compilación que representan miles o millones de elementos en la pantalla a la vez utilizando la maravillosa solución que es WriteableBitmap. Bajar la ruta estándar de WPF resultará en un infierno de rendimiento, por lo que tendrás que hacer algo exótico para resolverlo.

Como dije, mi recomendación es 1 + 2 + 4. Debes cambiar el tamaño de una miniatura, de eso no tengo dudas. La idea de tener una grilla estática de controles de Imagen y actualizar las fuentes es muy buena. La idea de usar WriteableBitmap (específicamente la función blit WriteableBitmapEx) para actualizar las fuentes también es una que vale la pena explorar.

Buena suerte!

+0

¡Gracias por tu respuesta! Una antigua historia judía de la Pascua describe a cuatro hijos: sabio, malvado, ingenuo y que ni siquiera sabe qué preguntas hacer. Siento que carezco de antecedentes. Ni siquiera soy capaz de hacer las preguntas correctas: ¿"tessellating"? ¿Conoce algún libro o artículo que describa los "gráficos" de gráficos (2D) en WPF? ¿Cuáles son las actividades, qué hace la CPU, qué hace la GPU? – Avi

+1

Sí, lo hago! Ver este artículo: http://jeremiahmorrill.com/2011/02/14/a-critical-deep-dive-into-the-wpf-rendering-system/ Esto es bastante esotérico, no te sientas mal. La mayoría le dirá "Anular OnRender" para el rendimiento, o "WPF usa la GPU", pero esa es la respuesta incorrecta. Cuando se da cuenta de que WPF hace la mayor parte de su trabajo en la CPU (¡cojo!), Entonces puede comenzar a optimizar el rendimiento. –

+0

Al seguir el camino que me enviaste, empiezo a tener la sensación de que WPF es inherentemente lento y que uno debe esperar por Direct2D o WinRT ... – Avi

Cuestiones relacionadas