Melhoria de Desempenho: Anulação Global do VALORANT
Olá! Sou Aaron Cheney, engenheiro de software da equipe de desempenho do VALORANT. Desempenho é um componente essencial para manter a integridade competitiva do jogo, e nossa equipe é responsável por monitorar, realizar manutenções e aumentar o desempenho tanto do servidor quanto do cliente.
Estamos muito contentes por poder compartilhar novidades sobre uma funcionalidade que estamos desenvolvendo há alguns meses: a Anulação Global (ou "Global Invalidation" em inglês). Daremos mais detalhes em breve, mas primeiro precisamos mencionar como isso impactou o desempenho do cliente desde a Atualização 4.03.
Já vou adiantando: o resultado foi incrível.
A Anulação Global trouxe melhorias consideráveis para grande parte da nossa base de jogadores. Na verdade, esse foi o maior aumento de desempenho do cliente desde o lançamento.
Sim, os gráficos são empolgantes, e estamos muito felizes com os resultados, mas é importante entender exatamente o que eles significam! Trabalhamos com conjuntos de dados grandes e complexos, e ao organizar, filtrar e controlar esses dados, conseguimos compreender melhor a experiência de quem joga. Para entender o conjunto da obra, é necessário saber que:
- O gráfico mostra "Atualização" e "FPS médio"; nesse caso: quanto mais altos os números, melhor.
- Cada linha representa uma configuração de hardware comum de se ver (combinação de CPU + GPU) na nossa base de jogadores. Ao analisar os dados de desempenho, essa combinação é considerada o indicador mais importante para verificar o desempenho esperado. Nesses gráficos, máquinas com a mesma combinação de CPU + GPU são agregadas no mesmo lugar.
- Os dados são retirados de duas filas: Sem Classificação e Competitiva. Como esses modos são os mais populares do VALORANT, nos esforçamos muito para entender e melhorar o desempenho deles.
- Também excluímos partidas que não contam com exatos 10 jogadores. Isso faz com que casos atípicos não distorçam os nossos dados (afinal, partidas com menos jogadores têm desempenho melhor).
RESUMO DA ANULAÇÃO GLOBAL
A Anulação Global melhora em até 15% os clientes que dependem de CPU (no geral, máquinas de configuração média/alta). Chegar a esses valores foi um esforço que envolveu várias equipes e levou muitos meses para ser concluído. Valeu a pena realizar o processo de identificação de áreas do jogo prontas para serem otimizadas, e controlar os riscos durante o processo nos ajudou a garantir uma experiência estável para toda a comunidade.
Quem se beneficia com isso e ajuste de expectativas
Com base nos dados do ambiente de jogo, a Anulação Global melhorou os clientes que dependem da CPU em até 15% (máquinas de configuração média/alta).
Apesar de a evolução crescente ser notória no resultado total, ela não representa a mecânica de jogo durante as partidas. Além disso, ela também não garante que todas as máquinas que contam com hardwares idênticos terão os mesmos resultados.
Resumindo: o desempenho-base do VALORANT em máquinas dependentes de CPU melhorou, mas a melhoria exata de cada máquina dependerá de vários outros fatores individuais de cada pessoa.
ENTENDENDO A ANULAÇÃO GLOBAL
Antes de dar uma visão geral da Anulação Global, precisamos explicar primeiro alguns elementos de interface do Unreal Engine.
Widgets e estruturas ramificadas
Os elementos de interface (conhecidos como Widgets) são criados a partir de blocos menores usando estruturas ramificadas. Essas estruturas são parecidas com o sistema de arquivos do computador. Um Widget pode ter um número qualquer de filhos (assim como uma pasta pode ter um número qualquer de arquivos).
Os blocos podem ser combinados para criar Widgets complexos. Por exemplo, nosso contador de munição é feito de várias partes, resultando aproximadamente na seguinte estrutura ramificada:
Juntando tudo isso, os blocos da contagem de munição ficam assim:
(As linhas verdes acima foram aumentadas para melhor visualização. Na prática, várias linhas acabam se sobrepondo.)
Sempre que um ou vários Widgets são alterados dentro da estrutura ramificada, eles podem afetar vários outros Widgets. Por exemplo, se um Widget for movido para uma nova posição na tela, todos os outros Widgets abaixo dele também precisam ter suas posições recalculadas. Mudanças como esta são gerenciadas por um sistema chamado "Anulação", nosso próximo tópico.
Anulação
Esse é o mecanismo que o Unreal Engine usa para indicar quando um certo Widget foi alterado e precisa ser atualizado.
Um Widget pode precisar ser anulado por vários motivos: animação, cor, opacidade, tamanho, ordenamento, texto, imagens e várias outras propriedades podem ser alteradas em resposta a algo que acontece no jogo. Quando mudanças assim acontecem, o Widget é "anulado", sinalizando que uma atualização é necessária.
Complicando o processo um pouco mais, um Widget pode ter vários tipos de anulação. Alguns deles são:
- Layout – quando o tamanho de um Widget é alterado (muito custoso).
- Tinta – quando a aparência de um Widget é alterada, mas o tamanho não.
- Ordem de filhos – quando a ordem dos Widgets foi alterada dentro de uma estrutura (que também mexe com o layout, ou seja, muito custoso).
- Visibilidade – quando a visibilidade do Widget é alterada, ficando invisível ou visível (que também mexe com o layout, ou seja, muito custoso).
Essas anulações são usadas para representar os tipos de operações que precisam ser executadas para que o Widget seja desenvolvido corretamente.
O processo todo fica ainda mais complicado quando um Widget depende de outro e, como eles são organizados em hierarquias, o layout acaba dependendo de vários fatores. Anular um único Widget pode exigir a anulação de vários outros Widgets relacionados para dar certo. Por exemplo, se vários Widgets estiverem organizados em um layout vertical (ex.: o Painel Social com a lista de amigos) e a ordem deles for alterada (ex.: alguém da lista ficou online), todos os Widgets dentro desse layout precisam ser atualizados.
Sistemas assim contam com vários objetivos:
- Anular o mínimo de Widgets possível. Isso reduz o número de Widgets que precisam ser atualizados para que o sistema funcione corretamente.
- Anular um Widget apenas quando for necessário. Anular um Widget de maneira incorreta desperdiça preciosos ciclos da CPU.
- Quando um Widget não for anulado, é preciso armazenar o resultado para que cada quadro seja processado rapidamente. Se nada muda, vários ciclos de CPU podem ser economizados.
Bem, isso é suficiente para entender como a atualização de Widgets funciona e quais acontecimentos podem causar uma anulação. Agora, vamos falar sobre como os desenvolvedores colocaram isso em prática.
Caixas de Anulação
O Unreal Engine providencia um componente para agrupar vários Widgets chamado de "Caixa de Anulação". Todos os Widgets dentro de uma única Caixa de Anulação são impedidos de ser processados, marcados ou pintados previamente. Em vez disso, o resultado é armazenado em um "vertex buffer".
Sempre que um dos Widgets dentro da Caixa de Anulação é anulado, os dados armazenados são descartados, e o Widget é atualizado e pintado novamente. Mesmo que a atualização do cache seja custosa para um único quadro, o resultado amortizado é muito melhor a longo prazo.
As Caixas de Anulação são uma parte essencial do desempenho de interface do VALORANT, e tiveram um papel bem importante quando estávamos trabalhando no lançamento. Entretanto, esse processo não é totalmente livre de regras:
- Os desenvolvedores precisam entender quais Widgets são bons para agrupar dentro de uma Caixa de Anulação. Widgets atualizados frequentemente não são adequados!
- Colocar Widgets dentro de uma Caixa de Anulação requer trabalho manual do desenvolvedor. Isso não é viável para todos os Widgets no jogo, logo, os desenvolvedores precisam entender quais deles valem a pena colocar em uma Caixa de Anulação.
Para ler mais sobre Caixas de Anulação, basta conferir a documentação da Epic.
Agora que temos contexto suficiente, vamos falar sobre a Anulação Global!
Apresentando: Anulação Global
Depois de aprender tudo isso, vocês devem estar perguntando: "Por que eles não colocam todos os elementos de interface em uma Caixa de Anulação global?". Bem, isso é precisamente o que a Anulação Global faz (de certa forma).
O objetivo da Anulação Global é melhorar significativamente o desempenho da interface do jogo e, ao mesmo tempo, ajudar os desenvolvedores com uma redução no trabalho manual necessário para colocar Widgets em Caixas de Anulação individuais. É a melhor situação para todo mundo.
Porém, no UE4.25 (versão do Unreal Engine usada pelo VALORANT), a Anulação Global ainda não é totalmente compatível com todos os tipos de Widgets. As versões posteriores já trouxeram melhorias, mas o VALORANT não conseguiu utilizar essas vantagens imediatamente. Além disso, ainda não sabíamos que o VALORANT ficaria tão rápido com a Anulação Global.
Depois de ver como seriam os resultados, foi aí que nosso trabalho começou.
POR QUE DECIDIMOS REALIZAR ESSE PROJETO?
No final de julho de 2021, a equipe decidiu realizar um teste interno com a Anulação Global. Poucas correções foram implementadas para o teste ser concluído com sucesso. Sabíamos que alguns bugs apareceriam durante o processo... e eles apareceram.
Até o fim do teste, encontramos aproximadamente 20 bugs (só os mais óbvios!). Era como se esses bugs mais sutis e traiçoeiros estivessem esperando para serem encontrados, sem falar dos casos mais extremos que não foram testados.
Mesmo depois de tudo isso, a Anulação Global apresentou melhorias de desempenho? Com certeza.
Analisando os dados gerados em um único teste, determinamos que a interface estava aproximadamente 35% mais rápida. (Atenção: a interface representa apenas parte do custo de um único quadro).
Porém, várias perguntas ainda ficaram no ar:
- Quanto tempo vai levar para corrigir todos os bugs?
- O trabalho ficaria nas mãos de qual/quais equipe(s)?
- Isso tem maior prioridade do que outros projetos já planejados? Para cumprir os prazos de lançamento de conteúdo, nossos cronogramas normalmente estão seis meses à frente do jogo atual — sendo assim, projetos emergenciais, mesmo que empolgantes assim, dificilmente conseguem ser encaixados.
- Corrigir os bugs reduziria a importância das melhorias de desempenho? Corrigir todos os bugs exigiria várias mudanças de código, e cada uma delas poderia fazer a interface ficar mais custosa.
- Quando deveríamos trabalhar nesse projeto? Sabendo que a Anulação Global estava sendo desenvolvida em versões posteriores do Unreal Engine, precisávamos considerar esse cronograma na hora de integrar as mudanças da Epic.
No fim, concluímos que o projeto valia todo esse esforço por alguns motivos:
Considerações envolvendo integrações e cronogramas do Unreal Engine
Apesar de os motores gráficos Unreal Engine 4.26 e 4.27 terem realizado progresso significativo na Anulação Global, o VALORANT atua em um cronograma de integração tardio. Não trabalhamos com as versões mais atuais a fim de gerenciar riscos e de garantir a estabilidade do jogo para nossa comunidade.
Porém, como nosso cronograma apontava que permaneceríamos na versão 4.25 por vários meses, demoraria mais de um ano para os jogadores se beneficiariam com essas melhorias. E isso, para nós, não parecia ser uma ideia muito agradável.
Para mais detalhes sobre como o VALORANT trabalha com as atualizações do Unreal Engine, confiram esses tweets (em inglês) do Líder Técnico do VALORANT, Marcus Reid.
Melhorias de desempenho conhecidas
A Anulação Global traz algo único quando o assunto é desempenho: valor mensurável. Durante nosso teste interino, nós medimos as prováveis melhorias que teríamos e concluímos que o trajeto para implementá-las estava (quase) bem definido.
Sabe como é, é complicado tocar projetos que envolvem desempenho. Neles, até os menores detalhes são importantes. Ao aplicar mudanças adicionais, você ajuda a melhorar o desempenho do jogo ao longo do tempo, mas é raro encontrar uma única mudança capaz melhorar as coisas na casa dos dois dígitos. Essa otimização era boa demais para ficar apenas no papel.
Mesmo depois de considerar os ganhos reduzidos por conta das correções de bugs, a Anulação Global continuava sendo nossa melhor chance de implementar melhorias consideráveis para a comunidade dentro de um período razoável.
COMO REALIZAMOS ESSE PROJETO?
Apesar de o primeiro teste com a Anulação Global ter começado no final de julho de 2021, o trabalho de verdade para estabilizar a funcionalidade começou mesmo só no final de setembro.
Integrando mudanças da Epic de maneira seletiva
Integrar totalmente os motores gráficos Unreal Engine 4.26 e 4.27 estava fora de cogitação, mas como sabíamos que o pessoal da Epic estava trabalhando na Anulação Global, decidimos analisar as milhares de mudanças feitas por eles para identificar quais delas nos permitiriam chegar a uma versão mais estável e totalmente funcional.
Implementar uma integração parcial dessas mudanças foi um trabalho bem desafiador. Era importante modificar a menor quantidade possível de funcionalidades centrais para manter a estabilidade e, ao mesmo tempo, implementar as mudanças certas fornecidas pela Epic. Todo esse projeto foi feito em um ramo separado do ramo principal do VALORANT para evitar que outros desenvolvedores fossem impactados por isso.
Depois de integrarmos as mudanças da Epic no nosso sistema de forma seletiva, passamos várias semanas corrigindo a maior quantidade possível de bugs enquanto nos preparávamos para levar a Anulação Global para o ramo principal do VALORANT. Durante o processo, criamos um botão para ligar/desligar a funcionalidade (caso alguma coisa catastrófica acontecesse).
Com vários bugs corrigidos e várias mudanças das versões 4.26 e 4.27 implementadas, fundimos o ramo isolado de volta no ramo principal.
Encontrando semelhanças entre os bugs
Apesar da manifestação variada dos bugs da Anulação Global, a causa de todas as questões normalmente podia ser rastreada e associada a um único problema. Era mais importante corrigir esses bugs, já que isso eliminaria vários outros problemas com uma única mudança. Por exemplo, uma única alteração que realizamos corrigiu mais de dez bugs espalhados pelo jogo! Uma análise cuidadosa da raiz do problema nos levou a soluções concretas que melhoraram a estabilidade da Anulação Global, assim como nossa confiança nela.
Identificar bugs, corrigir bugs, testar e repetir
As semanas e meses seguintes se resumiram a um ciclo: acionar a Anulação Global antes dos testes, identificar vários bugs, desligar a Anulação Global depois dos testes e corrigir os bugs.
A cada ciclo que se passava, menos bugs eram encontrados. Continuamos fazendo isso até que o grande fluxo de bugs fosse reduzido aos poucos até acabar.
Até o fim de novembro de 2021, todos os problemas maiores foram resolvidos e a Anulação Global estava, em grande parte, estável.
Bugs de destaque
- Astra travando o jogo – Em certo ponto, sempre que alguém resolvia jogar com a Astra, o jogo travava no carregamento da partida. Corrigimos isso integrando as mudanças da Epic.
- Travamento de herança múltipla – Esse é um assunto complicado na programação C++. Sem entrar em detalhes, digamos que a ordem dos destruidores em uma certa classe não estava sendo executada corretamente, causando travamentos. Com uma troca de duas linhas de código para alterar a ordem de herança, o problema foi corrigido. Simples assim. Para ler mais sobre herança múltipla, basta conferir esta página (em inglês).
- Chat de áudio infinito – Nos menus, a barra de chat reproduz um som sempre que o mouse passa por cima dela. Um bug fazia esse som ser reproduzido várias vezes por segundo. Uma chatice pra todo mundo! Corrigir esse bug envolvia entender o ritmo em que os Widgets recebiam informações do mouse.
Testando metodologias
Um elemento da Anulação Global que exigiu mais cautela (algo necessário para concluir nossos testes) foi o fato de que ela afetava todos os aspectos do jogo. Literalmente.
Lista de amigos? Com certeza. O botão para entrar na fila? Também. O botão de configurações? Sem dúvidas. Minha porcentagem de headshots? Claro que sim...
Enfim, existem vários elementos de interface espalhados pelo jogo que passam informações importantes para os jogadores. Quebrar apenas um desses elementos seria algo inaceitável.
Para evitar isso, nosso departamento de Controle de Qualidade criou um plano de testes com várias estratégias para garantir o funcionamento correto da Anulação Global.
Teste de corte vertical
Um corte vertical no VALORANT representa o trajeto principal que alguém geralmente percorre: executar o cliente, entrar na fila de uma partida, jogar uma partida na íntegra e interagir com a tela de fim de partida. Ao se concentrar nos elementos mais importantes do jogo, a equipe de Controle de Qualidade conseguiu realizar testes nos elementos mais usados de forma rápida e identificar os problemas com antecedência.
Teste destrutivo
Quando o teste de corte vertical termina, o "teste destrutivo" começa. Esse tipo de teste tem como objetivo identificar problemas inesperados, geralmente através da modificação de fatores externos (como ping da rede, taxa de quadros, Alt + Tab etc.). Armada com um conjunto de ferramentas internas, a equipe de Controle de Qualidade realizou testes destrutivos ao longo de várias semanas.
Teste de casos extremos
Várias partes do VALORANT são exploradas apenas por um pequeno percentual da comunidade. Já outras só são curtidas uma vez (ex.: Experiência de Novo Jogador). Mas, só porque essas partes do jogo não são muito acessadas, não quer dizer que são menos importantes. Identificar e testar todos os nossos casos extremos ajuda muito na hora de encontrar erros ocultos.
Teste no PBE (Public Beta Environment ou Ambiente Público de Testes)
O PBE foi um grande marco para a Anulação Global:
- Foi a primeira vez que o público externo pôde testar a funcionalidade. Em outras palavras, a Anulação Global pôde ser testada nas condições do "mundo real".
- O PBE representa uma variedade de hardwares, cobrindo configurações baixas e altas, e testar a funcionalidade com uma gama tão variada de máquinas nos ajudou a ter mais confiança na Anulação Global com uma base mais ampla.
Depois do teste no PBE durante o fim de semana dos dias 22 e 23 de janeiro de 2022, tivemos certeza de que a Anulação Global não ia interferir com a integridade do jogo e que as melhorias de desempenho estavam de acordo com o esperado.
Lançamento da Anulação Global
Depois do lançamento da Anulação Global na Atualização 4.03, monitoramos cautelosamente os relatórios de bugs enviados pela comunidade. Também ficamos de olho nos dados de desempenho para garantir que as estimativas estavam de acordo com os resultados. No final, o projeto foi um grande sucesso! E esperamos que vocês continuem aproveitando a taxa de quadros melhorada.
Com esse lançamento finalizado, a equipe de desempenho voltará a trabalhar em outras melhorias. Até a próxima, e bons abates!