wpf performance optimization bitmap design-patterns

wpf - Patrones de optimización del rendimiento de mapa de bits



performance optimization (1)

No puedo encontrar una pregunta específica en su publicación, aparte de solicitar 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: El Hack. Combinando todo en una imagen

Evitaría esto si es 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á visualizando 1,000 imágenes en la pantalla a la vez, considere la pantalla inmobiliaria disponible. 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.

Además, considere no solo la sobrecarga del renderizado al redimensionar una imagen grande, sino también enviar 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: precarga de fondo

Esta es una técnica que no he escuchado, 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 excepto en el render 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: Contexto del dibujo

De acuerdo, todo lo que hace es permitirle poner en cola las llamadas de dibujo en modo retenido usando una sintaxis de estilo "OnPaint" que no se parece en nada a la antigua GDI 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: mapas de bits grabables

WriteableBitmaps es un subsistema poco utilizado y extraordinariamente poderoso 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. Sugiero revisar la divulgación del proyecto codeplex WriteableBitmapEx . He contribuido a esto una vez y veo 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 el Patrón 1 + 2 + 4.

Podría tener una cuadrícula de N controles de imagen en la pantalla en las ubicaciones establecidas en un control de cuadrícula. 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: mapa de bits en caché

Este es un intento de microsoft de hacer 1 + 2 + 4 como mencioné 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 técnica final le permite representar un UIElement en un mapa de bits, lo sabe, pero lo interesante es que puede realizar un generador de miniaturas de personas pobres (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á a 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!

Encontré varios patrones para optimizar el manejo de Bitmaps 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 puede agregar patrones , explicar cómo difieren , explicar si usan la CPU o la GPU , y enseñar cuándo utilizarlos y cómo combinarlos , ¡sería de gran ayuda!

Contexto: el escenario de "Cuadrícula" de imágenes:

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

Combine los pequeños insectores en un mapa de bits (¿cómo? ¿Contexto de dibujo?) Y utilícelos como fondo. Superponga esto con imágenes con contenido vacío que manejará los éxitos, menús de contexto, eventos, etc.

La ventaja es que solo estamos hablando de dos mapas de bits aquí: 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

Esto es una obviedad cuando sabes de antemano el tamaño de la imagen para cambiar el tamaño, y cuando estás preparado para perder detalles (color) para el rendimiento:

  1. Reduzca el tamaño del mapa de bits usando BitmapImage.DecodePixelWidth
  2. Reduzca la información de color utilizando FormatConvertedBitmap.DestinationFormat
  3. Establezca el ajuste del comportamiento de escala del control Image.Stretch to Stretch.None
  4. Establezca SetBitmapScalingMode para la imagen en LowQuality.
  5. Congelar al insector

Vea el código here .

Patrón 2: precarga de fondo

Este patrón es aplicable cuando cree que puede aprovechar el hecho de que el usuario esté mirando las imágenes en la pantalla y prepare con antelación las siguientes imágenes para visualizar. 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, de modo que el trabajo se descargue al trabajador en segundo plano.

Patrón 3: Contexto del dibujo

Esto fue sugerido por Sheldon Ziao de Microsoft Support 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: mapas de bits grabables

La documentación de MSDN here describe como un sistema dual de búfer: su subproceso de interfaz de usuario actualiza el búfer; 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, como en una película de video como 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 en 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

RenderTargetBitmap convierte un Visual en un mapa de bits. No estoy seguro de si es relevante aquí. Mira here .

Editar : Con respecto a la pregunta de Paul Hoenecke: He escrito que "Mi aplicación tiene que mostrar muchas interfaces de mapa de bits". No mencioné que necesito mostrar aproximadamente 800 imágenes al mismo tiempo .

Uno puede leer acerca de los problemas de rendimiento involucrados en mi SO. Pregunta sobre el rendimiento del mapa de bits de WPF y ¿Cómo puedo hacer que mostrar imágenes en WPF sea más "ágil"?

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

Editar : esta pregunta fue publicada en el foro de soporte de WPF , con algunas respuestas del personal de MS.