Mejora de rendimiento: La invalidación global en VALORANT
¡Hola!
Soy Aaron Cheney, ingeniero de software del equipo de rendimiento de
VALORANT. El rendimiento es un elemento clave a la hora de proteger
la integridad competitiva de nuestro juego, y el equipo se
responsabiliza de monitorizar, mantener y mejorar el rendimiento
tanto del servidor como del cliente.
Nos
hace mucha ilusión poder compartir noticias sobre una característica
que lleva varios meses en desarrollo: la
invalidación global.
En seguida os explicaremos los detalles, pero, antes de nada, vamos a
echar un vistazo al impacto que ha tenido esta característica en el
rendimiento del cliente desde la versión 4.03.
Os
adelantamos que ha sido bastante increíble.
La
invalidación global ha traído mejorías para gran parte de nuestros
jugadores. De hecho,
es el mayor aumento de rendimiento del cliente desde el lanzamiento.
Las
métricas prometen mucho (y estamos contentísimos con los
resultados), pero es importante entender qué significa todo esto.
Trabajamos con grandes cantidades de datos complejos; organizarlos,
filtrarlos y manipularlos nos ayuda a entender mejor la experiencia
de los jugadores. Para haceros una idea general, hay que tener todo
esto en mente:
- El cuadro compara métricas "por versión" y "FPS medios"; cuanto más altas sean las cifras, mejor.
- Cada línea representa una configuración de hardware frecuente entre nuestros jugadores (es decir, la combinación de una tarjeta gráfica concreta con una CPU determinada). Al analizar los datos, estas configuraciones son el elemento que más acertadamente predice el rendimiento del equipo. Los equipos que tengan el mismo procesador y tarjeta gráfica se agrupan en estos cuadros.
- Obtenemos todos los datos de dos fuentes distintas: las partidas normales y las competitivas. Como estos son los modos de juego más populares, invertimos gran parte de nuestro esfuerzo en averiguar cómo mejorar el rendimiento en áreas relacionadas con ellos.
- Hemos excluido las partidas que no tienen 10 jugadores. Así, los casos más extremos no desplazarán los datos (el rendimiento es más alto cuantos menos jugadores haya en la partida).
RESUMEN DE LA INVALIDACIÓN GLOBAL
La
invalidación global supone una mejora de hasta un 15 %
para clientes vinculados al procesador (equipos de gama media y alta,
por lo general). Conseguir ese porcentaje ha supuesto el esfuerzo y
la colaboración de varios equipos durante muchos meses. El proceso
que desarrollamos para identificar áreas del juego en las que
podíamos optimizar ha dado sus frutos, y gestionar los riesgos del
proceso nos ha permitido proporcionar una experiencia más estable a
los jugadores.
¿A quién beneficia esto?: Ajustando las expectativas
Según
los datos del servidor normal, la invalidación
global ha supuesto una mejora de hasta un 15 %
para configuraciones del cliente vinculadas al procesador (equipos
de gama media y alta, por lo general).
Aunque
la tendencia a mejorar resulta evidente en la suma de todos los
datos, no representa la experiencia inmediata de juego. Tampoco
garantiza que todos los equipos con el mismo hardware vayan a obtener
los mismos resultados.
Eso
quiere decir que el rendimiento básico de VALORANT en equipos
vinculados al procesador ha aumentado en términos generales, pero
los resultados concretos de vuestro ordenador dependen de una serie
de factores individuales.
ENTENDER LA INVALIDACIÓN GLOBAL
Antes
de resumiros en qué consiste la invalidación global, tenemos que
explicaros unas cuantas cosas sobre elementos de la IU en Unreal
Engine.
Widgets y estructuras de árbol
Los
elementos de la IU (popularmente denominados widgets
por su nombre en inglés) están compuestos de piezas más pequeñas
que tienen estructura de árbol. Esta estructura se parece bastante a
la del sistema de archivos y carpetas de vuestro ordenador. Un widget
puede tener cualquier cantidad de "hijos" (al igual que una
carpeta puede contener cualquier cantidad de archivos).
Estas piezas se van combinando para crear widgets complejos. El contador de munición, por ejemplo, está integrado por varias piezas, y el árbol correspondiente es algo más o menos así:
Si se junta todo, el contador de munición acaba con esta pinta:
(Los recuadros rojos están enfatizados para que resulte más claro; en la práctica, muchos bordes se solaparían).
Cuando
se cambia un widget
(o varios) del árbol, es posible que afecte a otros. Por ejemplo, si
uno se desplaza a otro punto de la pantalla, todos los widgets
que estén debajo también tendrán que recalcular su posición.
Estos cambios los gestiona un sistema que llamamos "invalidación",
que es sobre lo que vamos a hablar en la siguiente sección.
La invalidación
La
invalidación es el mecanismo que Unreal Engine emplea para indicar
cuándo un widget
concreto ha cambiado y necesita actualizarse.
Hay
varios motivos por los que hay que invalidar los widgets:
la animación, el color, la opacidad, el tamaño, el orden, el texto,
las imágenes y muchas otras propiedades pueden ir cambiando en
respuesta a cosas que suceden en la partida. Cuando se dan estos
cambios, el widget
queda invalidado, lo que indica al sistema que debe actualizarlo.
Para
complicar este proceso todavía más, hay varios tipos de
invalidación. Estos son algunos ejemplos:
- Distribución: cuando lo que varía es el tamaño del widget (consume muchos recursos).
- Aspecto: cuando el aspecto del widget cambia, pero el tamaño permanece igual.
- Orden de hijos: cuando cambia el orden de los widgets dentro del ��rbol (implica distribución, así que también consume muchos recursos).
- Visibilidad: cuando el widget pasa de ser visible a invisible y viceversa (también implica distribución, así que consume muchos recursos).
Estos
tipos de invalidación se utilizan para indicar qué operaciones son
necesarias para aplicar el cambio pertinente.
Cuando
un widget
depende de otro, la cosa se complica aún más. Los widgets
se organizan en jerarquías, y su distribución depende de una serie
de factores. Invalidar un único widget
puede
requerir invalidar otros tantos para aplicar correctamente los
cambios. Por ejemplo, si hay varios widgets
organizados en una distribución vertical (como sucede con la lista
de amigos en el panel social) y el orden cambia (por ejemplo, cuando
se conecta un amigo), todos los widgets
de la distribución tienen que actualizarse.
Este
sistema tiene varios objetivos:
- Invalidar la menor cantidad de widgets posible. Esto reduce la cantidad que tenemos que actualizar para aplicar los cambios correctamente.
- Solo invalidar widgets cuando resulte necesario. Invalidar un widget cuando no hace falta supone una pérdida de ciclos de CPU que no podemos permitirnos.
- Cuando no se invalida un widget, almacenar el resultado para cargarlo rápidamente en cada fotograma. Si nada cambia, ahorramos ciclos de CPU.
Y
con esto ya sabéis suficiente sobre el tema para entender cómo se
actualizan los widgets
y qué tipos de cosas dan lugar a invalidaciones. Ahora vamos a ver
cómo han aplicado los desarrolladores toda esta teoría.
Cuadros de invalidación
Unreal
Engine cuenta con un componente, los cuadros de invalidación, que
permite agrupar varios widgets.
Si están en un mismo cuadro de invalidación, los widgets
no podrán precargarse, marcarse o trazarse. En su lugar, el
resultado se almacena en un búfer de vértice.
Cuando
se invalida uno de los widgets
del cuadro, los datos almacenados se descartan y el widget
se actualiza y se traza de nuevo. Es cierto que refrescar la memoria
para un único fotograma consume bastantes recursos, pero la
amortización resultante compensa a la larga.
Los
cuadros de invalidación son un elemento clave del rendimiento de la
IU de VALORANT, y lo fueron especialmente en los preparativos para el
lanzamiento.
Sin embargo, los cuadros también tienen su coste:
- Es necesario que los desarrolladores entiendan qué widgets funcionarán bien si se agrupan juntos en un cuadro de invalidación. Si un widget necesita actualizarse con regularidad, no es buen candidato.
- Colocar widgets en un cuadro de invalidación es algo que los desarrolladores deben hacer manualmente. Hacerlo con todos los widgets del juego no es factible, así que el equipo también debe saber priorizar los widgets que merezca la pena agrupar en un cuadro.
Si
queréis saber más sobre los cuadros de invalidación, echadle un
vistazo a la documentación
de Epic.
¡Ahora
ya tenemos suficiente contexto para hablar sobre la invalidación
global!
Llega la invalidación global
A
estas alturas, seguro que estáis pensando: "¿Y por qué no
ponéis todos los elementos de la IU en un cuadro de invalidación
global?". Pues eso es precisamente para lo que sirve la
invalidación global (más o menos).
La
invalidación global pretende mejorar el rendimiento de la IU en todo
el juego a la vez que reduce la cantidad de trabajo manual necesario
para colocar los widgets
en cuadros de invalidación individuales.
Combina lo mejor de ambos mundos.
Sin
embargo, al menos en la versión UE4.25 (la versión de Unreal Engine
que usamos para VALORANT), la invalidación global no es compatible
con todo tipo de widgets.
Las versiones posteriores han implementado mejoras, pero VALORANT no
puede incorporarlas inmediatamente. Además, no sabíamos muy bien
cuánto aumentaría realmente el rendimiento y la velocidad de
VALORANT con la invalidación global.
Aquí
es donde comienza nuestra labor.
¿POR QUÉ DECIDIMOS EMBARCARNOS EN ESTE PROYECTO?
A
finales de julio de 2021, el equipo decidió realizar pruebas
internas del sistema de invalidación global. Hicimos unos cuantos
cambios menores para arreglar un par de errores y completar la prueba
con éxito. Sin embargo, sabíamos que iríamos encontrando más y
más errores durante el proceso... Y vaya que si lo hicimos.
Cuando
acabamos, teníamos más de 20 bugs,
y eran solo los más evidentes. Teníamos claro que probablemente se
nos hubieran colado otros más discretos y problemáticos, y también
nos habían quedado un par de situaciones poco frecuentes por
testear.
Pero...
¿qué hay de los resultados de la invalidación global? Pues fueron
maravillosos.
Tras
analizar los datos de esa primera prueba, concluimos que la IU
funcionaba cerca de un 35 % más rápido.
(Nota: la IU solo es una parte del coste de cada fotograma).
Pero
todavía nos quedaban un montón de preguntas:
- ¿Cuánto tiempo necesitábamos para arreglar todos los errores?
- ¿Qué equipos debían ser los responsables del trabajo?
- ¿Deberíamos priorizar este proyecto frente a otros que tuviéramos planeados? Para poder cumplir con un ritmo de lanzamientos regular, solemos fijar nuestros calendarios con meses de antelación. El trabajo que surge sobre la marcha, por muy emocionante que nos resulte (como esta vez), resulta difícil de encajar.
- ¿Y si solucionar los errores reduce la mejora de rendimiento? Corregir todos los errores detectados requeriría muchos cambios en el código, y cada uno tenía el potencial de aumentar el consumo de la IU.
- ¿Cuándo deberíamos abordar este proyecto? Como sabíamos que la invalidación global estaba incorporándose activamente a las versiones más recientes de Unreal Engine, teníamos que plantearnos cuándo queríamos empezar a integrar los cambios de Epic.
Al
final, decidimos que el proyecto merecía la pena por varios motivos.
Integraciones de Unreal Engine y consideraciones de calendario
Aunque
las versiones 4.26 y 4.27 de Unreal Engine han realizado avances
significativos en términos de invalidación global, el calendario de
integración de VALORANT va con algo de retraso. No incorporamos
inmediatamente los cambios más recientes para evitar riesgos y
garantizar estabilidad a nuestros jugadores.
Nuestro
calendario indicaba que íbamos a seguir usando la versión 4.25 de
Unreal Engine durante muchos meses más, y eso significaba que los
jugadores tardarían bastante más de un año en ver estas mejoras de
rendimiento. No nos hacía mucha gracia la idea, la verdad.
Si
queréis más detalles sobre cómo nos planteamos el proceso de
incorporar mejoras de Unreal Engine a VALORANT, echad un vistazo a
este
hilo
de Twitter del jefe de tecnología de VALORANT, Marcus Reid.
Mejoras de rendimiento registradas
La
invalidación global representaba algo único en términos de ajustes
de rendimiento: valores
medidos.
Durante la prueba interna, pudimos medir la mejora potencial de
rendimiento y el camino hasta obtener esos valores fue bastante
intuitivo.
Los
ajustes de rendimiento son algo complicado. Hay que manejarse en
centímetros, no kilómetros; los cambios graduales ayudan a mejorar
el rendimiento a largo plazo, y no solemos encontrarnos cambios
concretos que puedan ofrecernos mejoras de dos dígitos. Esta
optimización nos resultaba demasiado tentadora como para ignorarla.
Incluso
teniendo en cuenta la posibilidad de que perdiéramos parte de los
resultados por las correcciones de errores, la invalidación global
era la mejor oportunidad que teníamos de ofrecer una mejora
significativa en un plazo razonable a nuestros jugadores.
¿CÓMO LO HEMOS CONSEGUIDO?
A
pesar de que los experimentos iniciales comenzaron a finales de julio
de 2021, no empezamos a concentrarnos del todo en estabilizar la
nueva característica hasta finales de septiembre.
Integración selectiva de los cambios de Epic
Integrar
por completo la 4.26 y la 4.27 no era factible, pero sabíamos que
Epic había estado trabajando activamente en la invalidación global,
así que decidimos indagar entre los miles de cambios para
identificar cuáles podían ayudarnos a implementar la invalidación
global sin perder estabilidad.
Integrar
solo una parte de los cambios no fue tarea sencilla. Era crucial que
modificáramos la cantidad mínima posible de características clave
del motor, porque así mantendríamos la estabilidad a la par que
incorporábamos los cambios de Epic que nos interesaban. Para evitar
que esto afectara a otros desarrolladores, todo el trabajo lo
realizamos en una rama separada del equipo principal de VALORANT.
Tras
integrar una selección de los cambios de Epic a nuestro motor, nos
pasamos varias semanas más arreglando todos los errores que pudimos
mientras nos preparábamos para introducir la invalidación global a
la rama principal del juego. Durante el proceso,
creamos un interruptor que nos permitía deshabilitar rápidamente la
característica
si algo salía muy mal.
Después
de corregir un montón de errores e incorporar muchos cambios de las
versiones 4.26 y 4.27 de Unreal Engine, incorporamos la rama de
trabajo aislada a la principal.
Denominadores comunes entre errores
Aunque
muchos de los errores de la invalidación global se manifestaron de
formas diferentes, muchas veces la causa era la misma. Esos errores
fueron los que priorizamos arreglar, porque solucionábamos varios
problemas con un solo cambio. Por ejemplo, hubo un cambio que arregló
más de 10 bugs
en distintos aspectos del juego. Analizar detalladamente la raíz del
problema nos llevó a desarrollar soluciones contundentes que iban
mejorando la estabilidad de la invalidación global.
Identificar y arreglar errores, hacer pruebas y repetir
Los
meses siguientes consistieron en ir activando la invalidación global
para una prueba, identificar una serie de errores, desactivarla de
nuevo y solucionar estos errores, una y otra vez.
En
cada ciclo encontrábamos menos errores. Seguimos haciendo esto hasta
que el flujo constante de bugs
se convirtió más bien en un goteo ocasional que acabó por
detenerse del todo.
A
finales de noviembre de 2021, todos los problemas graves estaban
resueltos y la invalidación global funcionaba con relativa
estabilidad.
Errores destacados
- Astra provoca cuelgues en el juego: hubo un momento en el que todos los jugadores de Astra experimentaban cuelgues al cargar la partida. Arreglamos este error tras integrar los cambios de Epic.
- Cuelgue por herencia múltiple: en C++, la herencia múltiple es un asunto complicado. Sin meternos mucho en el meollo, digamos que el orden de los destructores de una clase determinada no se ejecutó correctamente, lo que provocó un cuelgue. Lo solucionamos implementando un par de líneas de código que cambiaban el orden de la herencia. Si queréis saber más sobre la herencia múltiple, echadle un vistazo a esta página.
- Sonido del chat infinito: en los menús, la barra del chat reproduce un sonido al pasar el ratón por encima. Para desgracia de todos nosotros, dimos con un error que hacía que este sonido se reprodujera varias veces por segundo. Para arreglarlo, tuvimos que entender cómo reaccionan los widgets a las acciones del ratón varias veces por fotograma.
Pruebas de metodología
Una
característica de la invalidación global que nos hizo avanzar con
pies de plomo (y hacer pruebas muy detalladas) es que afecta
a todos los aspectos del juego.
Literalmente.
¿La
lista de amigos? Síp. ¿El botón para entrar en cola? También. ¿El
menú de ajustes? Vaya que sí. ¿Mi puntería? Bueno, eso...
La
cosa es que hay elementos de la IU en todas las partes del juego, y
muchas veces contienen información de importancia crítica.
Cargarnos
uno de esos elementos era una opción inaceptable.
Para
evitarlo, nuestro departamento de control de calidad elaboró un
calendario de pruebas con varias estrategias. Así, fuimos
confirmando con mayor certeza que la invalidación funcionaba como
queríamos.
Pruebas verticales
Una
"trayectoria vertical" en VALORANT representa la ruta
principal que los jugadores suelen tomar desde que abren el cliente
hasta que interactúan con la pantalla de fin de partida, pasando por
ponerse en cola y jugar la partida en sí. Al centrarse en los
elementos críticos del juego, el equipo de calidad pudo probar los
elementos que más se utilizaban de todo el juego e identificar
problemas rápidamente.
Pruebas destructivas
Las
pruebas destructivas continúan la labor de las verticales. Este tipo
de pruebas están pensadas para identificar problemas menos
frecuentes, que suelen necesitar que se modifiquen factores externos
(el ping de la red, los FPS, el funcionamiento al usar Alt + TAB,
etc.). Equipados con un repertorio de herramientas internas, el
equipo de control de calidad se pasó varias semanas con las pruebas
destructivas.
Pruebas de excepciones
Hay
muchos aspectos de VALORANT que solo experimentan un pequeño
porcentaje de los jugadores. Algunos de ellos solo se experimentan
una vez (por ejemplo, la experiencia para nuevos jugadores). Que
estas zonas del juego estén menos transitadas no quiere decir que
sean menos importantes. Identificar y hacer pruebas para todos los
casos excepcionales nos ayudó a dar con errores ocultos.
Pruebas en el entorno de la beta pública (PBE)
La
PBE fue un paso muy importante para la invalidación global.
- Era la primera vez que los jugadores podían probar esta característica. Eso quería decir que podíamos empezar a probarla en "condiciones reales".
- El entorno de la beta pública reúne una amplia variedad de especificaciones de hardware. El hecho de que cubra las gamas baja, media y alta es algo intencionado, y realizar pruebas con tanta variedad de dispositivos nos permitió confirmar que la invalidación global tendría buenos resultados para nuestros jugadores en general.
Tras
las pruebas del fin de semana del 22 y el 23 de enero de 2022,
estábamos seguros de que la invalidación global no interfería con
la integridad del juego y que las mejoras de rendimiento coincidían
con nuestras estimaciones.
El lanzamiento de la invalidación global
Tras
lanzar la invalidación global con la versión 4.03, seguimos muy de
cerca los informes de errores de los jugadores. También nos
mantuvimos muy alerta a los datos de rendimiento para confirmar que
nuestras proyecciones encajaban con los resultados. Al final, la
invalidación global ha sido un éxito rotundo entre los jugadores, y
esperamos que disfrutéis de los tiempos de respuesta mejorados.
Ahora
que esta característica ya está incorporada, el equipo de
rendimiento pasará a buscar la próxima gran mejora. Hasta entonces,
¡nos vemos en el campo de batalla!