Optimizando un juego XNA 2D

56

¿Tiene sentido implementar la lógica para omitir la representación de objetos fuera de la ventana gráfica o no debería importarme y dejar que Framework lo haga?

smnbss
fuente

Respuestas:

79

El sacrificio es una optimización del rendimiento. Por lo tanto, no tiene sentido hacerlo solo por el simple hecho de hacerlo. Tienes que tener una razón.


La GPU (no el Marco XNA) elimina triángulos y píxeles a una velocidad increíblemente rápida. Cada triángulo que envíe debe transformarse (a través de su sombreador de vértices). Luego se eliminan los que aterrizan fuera de la pantalla. Luego llena los triángulos restantes, eliminando píxeles que están fuera de la pantalla. Los píxeles restantes se dibujan en el búfer posterior (a través de su sombreador de píxeles).

(Cuando digo "entonces", en realidad hace todo esto en una tubería paralela masiva).

Por lo tanto, es muy raro e inusual que tenga que seleccionar triángulos individuales. Para alcanzar los límites de vértice, debes dibujar una cantidad absurdamente grande de triángulos. Para alcanzar los límites de velocidad de relleno, búsqueda de texturas o sombreado de píxeles, generalmente debe tener una alta complejidad de profundidad (en cuyo caso la eliminación de ventana gráfica / frustum no ayudará).


Por lo tanto, generalmente hay poco o ningún costo para tener geometría fuera de la pantalla.

El costo, particularmente en el contexto del dibujo de "objetos" (generalmente objetos 3D), es en realidad enviar esos objetos a la GPU en primer lugar . Envíe demasiados objetos y alcanzará su límite de lote (obtendrá unos pocos miles * lotes por cuadro).

Recomiendo leer esta respuesta y esta plataforma de diapositivas vinculada para obtener una descripción detallada de los lotes.

Debido a esto, si implementa el sacrificio de frustum, puede reducir el número de lotes que envía a la GPU. Si tiene un lote limitado, esto puede llevarlo por debajo del límite.


Ahora, su pregunta es sobre 2D XNA, por lo que presumiblemente está utilizando SpriteBatch. Esto es un poco diferente.

No es un error que se llame "Sprite Batch ". Lo que está haciendo es tomar los sprites que dibujas y hacer lo mejor posible para enviar esos sprites a la GPU en la menor cantidad de lotes posible al agruparlos.

Pero SpriteBatch se verá obligado a iniciar un nuevo lote si:

  • Dibuja más sprites de los que pueden caber en un solo lote (2048 sprites en XNA 4)
  • Cambia la textura (es por eso que hay una opción de ordenar por textura)

Por lo tanto, el sacrificio es una optimización adecuada si se encuentra con la primera. Si está enviando una cantidad tan grande de sprites que termina con demasiados lotes (probablemente también esté utilizando ancho de banda, pero estoy bastante seguro de que primero alcanzará el límite de lote). En general, esto solo sucederá si tienes un mundo realmente enorme, por lo que, en este caso, puedes salirte con la tuya con un sacrificio muy simple, rápido pero impreciso.

Ahora: si está dibujando con suficientes intercambios de textura para superar el límite de lote, y muchos de ellos están realmente fuera de la pantalla y eliminarlos lo colocaría por debajo del límite de lote. Entonces sí, el sacrificio es una optimización que funcionará.

Sin embargo , una mejor optimización a seguir en este caso es usar atlas de textura (también conocido como: hojas de sprites). Esto le permite reducir el número de intercambios de texturas y, por lo tanto, lotes, independientemente de lo que esté en la pantalla o no. (Esta es la razón principal por la que puede especificar un rectángulo de origen para sus sprites).


(Como siempre: este es un consejo sobre la optimización del rendimiento. Por lo tanto, debe medir y comprender el rendimiento de su propio juego y los límites que está alcanzando, antes de gastar esfuerzo en agregar optimizaciones).

Andrew Russell
fuente
55
+1 Por mencionar que no hay que sacrificar solo por el simple hecho de hacerlo, sino cuando es necesario para el rendimiento.
Will Marcouiller
12
+1 por decirme finalmente que las hojas de sprites tienen una razón para existir aparte de la conveniencia del programador / diseñador
Bill
3
+1 para una gran explicación, aprendí mucho de tu respuesta.
Jesse Emond
1
El enlace al pdf "Lote, Lote, Lote" está roto. Aquí hay uno actualizado: ce.u-sys.org/Veranstaltungen/…
Marton
13

De acuerdo con una publicación en los foros de MSDN, el marco XNA no elimina el sacrificio:

El framework XNA no hará ningún sacrificio por ti, no podría ser porque no tiene idea de dónde terminará realmente algo. Todas las transformaciones se realizan en la GPU y podrían ser un sombreador personalizado.

http://xboxforums.create.msdn.com/forums/p/22763/121897.aspx

Esta publicación continúa diciendo que si tienes muchos objetos en tu escena, valdría la pena rodar los tuyos.

Nate
fuente
66
Especialmente si tienes un mapa de desplazamiento (dijiste que era 2D, por lo que podría ser relevante). Es un desperdicio si dibujas grandes secciones que no son necesarias. No es muy difícil y es posible que veas algún tipo de mejora.
Michael Coleman
1

He estado desarrollando motores de terreno de vóxeles procesales durante unos años en XNA. En la mayoría de los cuadros, los motores están transformando literalmente cientos de miles de quads. En mi perfil, descubrí que el frustum y la eliminación de oclusiones aumentan el rendimiento.

El perfil XNA HiDef (no Reach) tiene una clase OcclusionQuery que se utiliza para realizar el sacrificio de oclusión. La eliminación de oclusión elimina esos quads de una ventana gráfica que otros quads ocultan a la vista.

Algo más que debe considerar es encabezar tanto la generación como el teselado por separado.

Entonces, sí, cuando se acerque a una gran cantidad de cuadrículas en cada cuadro que se está transformando, debe pensar detenidamente en cómo puede aplicar las técnicas de selección y enhebrado para mantener la velocidad de cuadros, reduciendo el tráfico a los sombreadores de gpu.

Jason Coombes
fuente