Ir al contenido
  1. Artículos/

Una queja sobre la gestión de texturas en GameMaker

Artículo Desarrollo de juegos GameMaker Rendimiento
PacoChan
Autor
PacoChan
Me encantan la informática y los videojuegos desde que era un crío, y dedico mi tiempo a desarrollarlos y portearlos a consolas.
Tabla de contenido

El problema #

Actualmente estoy porteando un juego indie a consolas, el cual está hecho con GameMaker. Y cada vez que me toca lidiar con este motor, una cosa que me saca de quicio es cómo gestiona las texturas. No se pueden comprimir en absoluto. Durante mucho tiempo, todas las texturas se guardaban en formato PNG con transparencia, ocupando bastante espacio en disco, en RAM y en la memoria de vídeo (VRAM). Es decir, PNG realmente aplica compresión sin pérdida (no mucha en según qué tipos de imágenes), pero los contenidos de un PNG siempre se han de descomprimir primero antes de poder usarse o visualizarse. Así que en la práctica, todo queda descomprimido en la VRAM de la GPU. En resumen, tienen un poco de compresión cuando están en disco, pero no tienen nada cuando están cargadas en memoria, que es lo que más importa.

Cuando hablo de compresión de texturas como toca, me refiero a usar formatos como DXT, ETC, PVRTC o ASTC. Estos son formatos de compresión de texturas diseñados para ser usados por la GPU sin necesidad de descomprimirlas, es decir, cuando se tiene una textura comprimida en uno de estos formatos, ésta se carga en la VRAM de la GPU, y la GPU será capaz de renderizar la textura tal cual sin necesidad de descomprimirla. Así que las texturas en VRAM ocupan menos espacio, tardan menos tiempo en cargar y no se pierde tiempo descomprimiéndolas, con lo que permitirá tener más texturas y de mayor resolución, lo cual puede mejorar el aspecto visual del juego. Y esto es lo que hace la gran mayoría de juegos y motores de juegos… excepto el GameMaker.

¿Nos podéis dejar elegir, por favor? #

Buscando acerca de este problema hace unos años, encontré un hilo en los foros de GameMaker donde la gente habla de la falta de opciones de compresión, y uno de los desarrolladores dijo básicamente que comprimir texturas no tenía sentido ya que se verían mal en juegos 2D. Hale, os quedáis sin compresión ¯\_(ツ)_/¯. Es cierto que en algunos casos como cuando se usa pixel art, comprimirlo fastidia mucho la calidad y no se recomienda para ese estilo. Pero, ¿qué tal si se permite que la gente elija? ¿Y han pensado que a lo mejor no todos los juegos son pixel art y los hay que tienen grandes ilustraciones 2D que se beneficiarían mucho de la ganancia de rendimiento a costa de una reducción de calidad apenas notable? Mucha gente lleva años quejándose de problemas de rendimiento y consumo de memoria en sus juegos.

¿Tan difícil es añadir otra opción y que la gente pueda elegir?

Imaginemos un juego como Hollow Knight (que por suerte no fue hecho en GameMaker). Es un juego precioso de estilo Metroidvania 2D, muy bueno por cierto. Imagina todos esos sprites, fondos y animaciones totalmente sin comprimir. Eso tendría una serie de consecuencias:

  • Haría que el juego ocupara muchos gigabytes más. Éste es el menor de los problemas dado que igualmente a día de hoy existen juegos que sobrepasan los 100GBs. Pero es algo innecesario.
  • Los tiempos de carga serían mucho más largos.
  • El juego crashearía en muchos sistemas ya que requeriría mucha más RAM y VRAM que sistemas antiguos podrían no tener.
  • Muchas más ralentizaciones y tirones causados por la carga de grandes texturas durante gameplay.

Así que se convertiría en un juego mucho más demandante de recursos y difícil de optimizar. El pixel art normalmente consiste en texturas muy pequeñas, con lo que tenerlas todas sin comprimir no supone ningún problema. Pero para el resto de juegos 2D… os puedo asegurar que muchos de ellos chupan mucha más VRAM que algunos juegos 3D de gran presupuesto.

Lo intentaron… #

Tras muchos años de que el motor no tuviera opciones de compresión, una día por fin anunciaron un nuevo formato. ¡BIEN! ¡HURRA! Se llama QOI. Las texturas son un poco más pequeñas y se descomprimen un poco más rápido que un PNG. Y eso está bien y tal… pero volvemos a tener exactamente el mismo problema. Tienen que descomprimirse antes de poder ser usadas… con lo que siguen consumiendo mucha memoria tras ser cargadas… con lo que en la práctica no ha cambiado nada…

También ofrecieron una variante de ese formato llamada BZ2+QOI, que viene a ser como meter una imagen dentro de un archivo Zip. Tiene mejor compresión, pero la descompresión es MUCHO más lenta. Tal vez eso no se note mucho en PC, pero en la Nintendo Switch… ay madre mía… ese formato es inusable. Es dolorosamente lento. Un par de juegos en los que he trabajado tardaban como diez segundos en cargar cada habitación, y luego durante gameplay era un festival de parones y ralentizaciones. Y BZ2+QOI es lo que viene activado por defecto. Así que lo primero que me toca hacer en cada juego es cambiar todas las texturas a QOI. Y de repente ya no más tiempos de carga ni ralentizaciones. Pero aun así persiste el problema de los crasheos debido a que no caben todas las texturas en VRAM…

La cosa va a peor #

Hablando de tiempos de carga, me he fijado que los juegos hechos con GameMaker tardan un poco en arrancar. Y en Switch es aún peor. ¡He trabajado en juegos que tardaban hasta 20 segundos en arrancar! De hecho, el motor permite establecer una imagen de carga durante el arranque para que el juego no se quede con la pantalla en negro durante tanto tiempo. Así que, ¿de qué va todo eso? Recientemente descubrí el motivo… TODO JUEGO HECHO CON GAMEMAKER CARGA TODAS LAS TEXTURAS, TODAS LAS TEXTURAS CONTENIDAS EN TODO EL JUEGO DE PRINCIPIO A FIN, EN MEMORIA DURANTE EL ARRANQUE Y SE QUEDAN AHÍ DURANTE TODO EL TIEMPO MIENTRAS JUEGAS AL JUEGO. CHUPANDO UN COJONAZO DE MEMORIA.

Lo que cualquier motor decente hace es lo siguiente. Imaginemos que queremos renderizar una caja de madera:

  • Primero se carga la textura de la caja desde el disco duro o SSD a la memoria RAM.
  • Desde RAM se procede a copiar la textura a la VRAM de la GPU.
  • Una vez la textura está en la VRAM, está lista para ser usada y renderizada.
  • La textura que se cargó en RAM puede ser borrada, dado que ya no se necesita. Tenerla en VRAM es todo lo que se necesita para renderizarla. No obstante hay excepciones. Por ejemplo, si quieres ir editando la textura en tiempo real, te interesa mantenerla en RAM para editarla ahí, y luego copiarla de nuevo a la VRAM tras cada edición para ser renderizada. Pero la mayoría de veces no necesitas mantener la textura en RAM.

Y aquí viene PainMaker con toda su jeta, y no solamente mantiene en RAM las texturas que se usan en el momento de forma innecesaria, es que las mantiene TODAS.

Y pensemos en la Switch, la cual tiene solo 4GB de memoria unificada, de los cuales 1GB es usado por el sistema operativo. Con lo que en la práctica, unos 3GB disponibles en total. Que tenga memoria unificada significa que esos 3GB se usan tanto como RAM como VRAM, en vez de ser memorias separadas. En esos miserables 3GB tienen que caber todas las texturas del juego, y luego duplicados de algunas de ella para ser renderizadas (junto con audio, buffers, código, etc). Imagino que en móviles habrá problemas similares.

Pero ha habido algunas mejoras #

¡Oh! Gracias a los cielos divinos… justamente el año pasado (2022, tras 23 años desde que el motor existe) han añadido la posibilidad de NO cargar todas las texturas en memoria 🎊🎉🎉🎉🎊. ¡Puedes cargar solo las que necesitas! Esa característica se llama “Dynamic Texture Pages”, y desde luego menuda diferencia. He conseguido reducir bastante los tiempos de arranque y ya no tengo que preocuparme de quedarme sin memoria. Pero conlleva cierto trabajo cuando el juego no ha sido pensado para usar esta característica.

Conclusión #

Así que al final, realmente han arreglado muchos de estos problemas. Muy recientemente, pero mejor tarde que nunca. Tuve que lidiar con ellos en proyectos anteriores y no era divertido. Pero para mi proyecto actual, en el que he tenido problemas de memoria y crasheos, descubrir lo de los “Dynamic Texture Pages” hace tan solo unos días ha ayudado bastante. Pero aún está el problema de que las texturas siguen sin comprimir en VRAM, lo cual es importante. Espero poder vivir el día en que implementen formatos de compresión como Dios manda y por fin sea como un motor de juegos normal.