2012-06-13 6 views
24

Actualmente estoy trabajando en un juego y deseo tener un menú principal con la imagen de fondo.¿La imagen de Graphics.DrawImage es demasiado lenta para imágenes más grandes?

Sin embargo, el método Graphics.DrawImage() me parece muy lento. He hecho algunas mediciones. Supongamos que MenuBackground es mi imagen de recursos con una resolución de 800 x 1200 píxeles. Lo dibujaré en otro mapa de bits de 800 x 1200 (primero renderizo todo en un mapa de bits de buffer, luego lo escalo y finalmente lo dibujo en la pantalla, así es como trato la posibilidad de resoluciones de múltiples jugadores. Pero no debería afectar de ninguna manera, mira el siguiente párrafo).

Así que hemos medido el siguiente código:

Stopwatch SW = new Stopwatch(); 
SW.Start(); 

// First let's render background image into original-sized bitmap: 

OriginalRenderGraphics.DrawImage(Properties.Resources.MenuBackground, 
    new Rectangle(0, 0, Globals.OriginalScreenWidth, Globals.OriginalScreenHeight)); 

SW.Stop(); 
System.Windows.Forms.MessageBox.Show(SW.ElapsedMilliseconds + " milliseconds"); 

El resultado es tranquila sorprendente para mí - la Stopwatch medidas algo entre 40 - 50 milliseconds. Y como la imagen de fondo no es lo único que se dibujará, el menú completo tarda más de 100 ms en mostrarse, lo que implica un retraso observable.

He intentado dibujarlo en el objeto Graphics dado por Paint event, pero el resultado fue 30 - 40 milliseconds - no ha cambiado mucho.

Entonces, ¿significa que Graphics.DrawImage() no se puede usar para dibujar imágenes más grandes? Si es así, ¿qué debo hacer para mejorar el rendimiento de mi juego?

+1

Si con "inutilizable" quiere decir "lento como un caracol en melaza", entonces sí. Como han dicho otros, dale una oportunidad a XNA. Manténgase en el renderizado de sprites 2D hasta que esté acostumbrado al framework XNA, luego avance hacia 3D si así lo desea. – CodeHxr

+1

Estoy votando eso solo por la analogía. :) – Ani

Respuesta

84

Sí, es demasiado lento.

Me encontré con este problema hace varios años mientras desarrollaba Paint.NET (desde el principio, de hecho, ¡y fue bastante frustrante!). El rendimiento de la representación fue abismal, ya que siempre fue proporcional al tamaño del mapa de bits y no al tamaño del área que se le dijo que volviera a dibujar. Es decir, la velocidad de fotogramas disminuyó a medida que el tamaño del mapa de bits subió, y la velocidad de fotogramas nunca aumentó ya que el tamaño del área no válida/redibujado disminuyó al implementar OnPaint() y llamar a Graphics.DrawImage(). Un pequeño mapa de bits, digamos 800x600, siempre funcionó bien, pero las imágenes más grandes (por ejemplo, 2400x1800) fueron muy lentas. (Puede suponer, para el párrafo anterior de todos modos, que no estaba sucediendo nada extra, como escalar con algún costoso filtro Bicúbico, lo que habría afectado negativamente el rendimiento).

Es posible forzar WinForms para usar GDI en lugar de GDI + y evite incluso la creación de un objeto Graphics entre bastidores, en cuyo punto puede superponer otro juego de herramientas de representación (p. Ej., Direct2D). Sin embargo, no es simple. Hago esto en Paint.NET, y puede ver lo que se requiere al usar algo como Reflector en la clase llamada GdiPaintControl en la DLL de SystemLayer, pero para lo que está haciendo lo consideraría como último recurso.

Sin embargo, el tamaño de mapa de bits que está utilizando (800x1200) debería funcionar correctamente en GDI + sin tener que recurrir a interoperabilidad avanzada, a menos que tenga como objetivo algo tan bajo como un Pentium II de 300MHz. Estos son algunos consejos que pueden ayudar a cabo:

  • Si está utilizando un mapa de bits opaco (sin alfa/transparencia) en la llamada a Graphics.DrawImage(), y sobre todo si se trata de un mapa de bits de 32 bits con un canal alfa (pero saber es opaco, o no le importa), luego configure Graphics.CompositingMode en CompositingMode.SourceCopy antes de llamar al DrawImage() (asegúrese de volver a establecer el valor original después, de lo contrario, las primitivas de dibujo normales se verán muy feas). Esto omite una gran cantidad de matemática de mezcla adicional por píxel.
  • Asegúrese de que Graphics.InterpolationMode no esté configurado como InterpolationMode.HighQualityBicubic. Usar NearestNeighbor será el más rápido, aunque si hay algún estiramiento puede que no se vea muy bien (a menos que se extienda exactamente 2x, 3x, 4x, etc.) Bilinear suele ser un buen compromiso. Nunca debe usar nada más que NearestNeighbor si el tamaño del mapa de bits coincide con el área en la que está dibujando, en píxeles.
  • Siempre dibuje en el objeto Graphics que se le ha asignado en OnPaint().
  • Siempre haga su dibujo en OnPaint. Si necesita volver a dibujar un área, llame al Invalidate(). Si necesita que el sorteo ocurra en este momento, llame al Update() después de Invalidate(). Este es un enfoque razonable ya que los mensajes WM_PAINT (que da como resultado una llamada al OnPaint()) son mensajes de "baja prioridad". Cualquier otro procesamiento por parte del administrador de ventanas se hará primero, y de esta manera usted podría terminar con muchos saltos y enganches de cuadros.
  • Usar un System.Windows.Forms.Timer como un temporizador de framerate/tick no funcionará muy bien. Estos se implementan utilizando el SetTimer de Win32 y dan como resultado mensajes WM_TIMER que dan como resultado el evento Timer.Tick que se genera y WM_TIMER es otro mensaje de baja prioridad que se envía solo cuando la cola de mensajes está vacía. Es mejor utilizar System.Threading.Timer y luego usar Control.Invoke() (¡para asegurarse de que está en el hilo correcto!) Y llamar al Control.Update().
  • En general, no use Control.CreateGraphics(). (corolario de 'dibujar siempre en OnPaint()' y 'utilizar siempre el Graphics que le dio OnPaint()')
  • Recomiendo no usar el controlador de eventos Paint. En su lugar, implemente OnPaint() en la clase que está escribiendo, que se derivará del Control. Derivado de otra clase, p. PictureBox o UserControl, no agregará ningún valor para usted o agregará gastos adicionales. (BTW PictureBox a menudo es mal interpretado. Probablemente nunca querrá usarlo.)

Espero que ayude.

+2

te mereces toneladas de upvotes.Para dibujar una grilla de fondo, ¿hay algún consejo? TextureBrush parece rápido, pero no funciona cuando tienes un escalado personalizado con un factor de zoom, ya que no coincide con – GorillaApe

+1

La mejor optimización de esta publicación para mí fue el modo de interpolación. Al cambiar 'Bilinear' por' NearestNeighbor' se hizo el dibujo 8 veces más rápido. – Pietu1998

+0

Años después todavía está ayudando a la gente con la pintura en winforms – Nathan

3

GDI + probablemente no sea la mejor opción para los juegos. DirectX/XNA u OpenGL deben ser preferidos ya que utilizan la aceleración de gráficos que sea posible y son muy rápidos.

3

GDI + no es un demonio de velocidad de ninguna manera. Cualquier manipulación de la imagen seria por lo general tiene que entrar en el lado natural de las cosas (llamadas PInvoke y/o la manipulación a través de un puntero obtenida llamando LockBits.)

¿Ha mirado en XNA/DirectX/OpenGL ?. Estos son marcos diseñados para el desarrollo de juegos y serán órdenes de magnitud más eficientes y flexibles que el uso de un marco de interfaz de usuario como WinForms o WPF. Todas las bibliotecas ofrecen enlaces C#.

Puede invocar en código nativo, utilizando funciones como BitBlt, pero también hay una sobrecarga asociada al cruce del límite del código administrado.

+0

Recomendaría OpenGL/DirectX también. XNA no es realmente la MEJOR opción per se por una variedad de razones. Facilidad de uso, sí, pero a la larga es una desventaja. – Ani

+0

@ananthonline: Tienes razón, merecen una mención. DirectX es superior a OpenGL si no le importa el soporte multiplataforma. ¿Puede ampliar su comentario * "a largo plazo [XNA es] un poco de handicap" *.Probablemente tengo algunos agujeros en mi conocimiento ya que no soy un desarrollador de juegos serio, solo un hobby mío. –

+0

XNA hace varias suposiciones para su uso. Una de las serias limitaciones que se me ocurren es que los rendertargets múltiples no pueden compartir un búfer de profundidad, lo que hace que los escenarios comunes, como hacer de forma eficiente el renderizado diferido, sean imposibles. Consulte aquí para obtener más información (http://forums.create.msdn.com/forums/p/64179/397431.aspx) – Ani

Cuestiones relacionadas