Gráficos Rosa, Pontuação Descontrolada, Crash de RX. Quatro Bugs em Um Sábado.
Quatro bugs em quatro subsistemas que passam todos nos seus testes unitários - esse é o modo de falha que lança demos quebradas. Pegá-lo é a disciplina que torna um exemplo algo que você de fato consegue rodar na RakuAI.
Existe um tipo específico de sábado que qualquer um que já lançou uma demo conhece. A demo funcionava no fim do fim de semana passado. Você a pegou neste sábado de manhã. Agora está quebrada de quatro jeitos diferentes e nenhum dos jeitos tem relação entre si.
Foi hoje. A demo era o jogo Shooter que lançamos como um dos exemplos canônicos. Os bugs estavam esperando como convidados de festa que ninguém convidou.
O que estava quebrado
A demo iniciava. Essa era a boa notícia. Depois disso:
Os gráficos estavam rosa. Qualquer um que já trabalhou em um renderizador em tempo real conhece o rosa-do-apocalipse. É a cor de placeholder de “shader faltando,” rosa choque, gritando para você que algo no pipeline de renderização falhou em encontrar o shader que esperava. Todo objeto gerado proceduralmente na demo estava sendo renderizado nessa cor. Naves. Asteroides. Partículas. Tudo rosa. Rosa por toda parte.
A pontuação descontrolou. Todo abate era registrado duas vezes. O contador de abates subia em pares. Vinte inimigos abatidos, “pontuação: quarenta.” Quarenta inimigos abatidos, “pontuação: oitenta.” No papel parece que o jogo estava sendo gentil, mas na prática o placar estava inflado e a lógica de recorde estava disparando em limiares completamente errados.
A inicialização de RX travou. A demo em uma tela plana iniciava limpa. No momento em que conectei um headset e mudei para o modo RX, saída forçada. Nenhum backtrace apareceu através do launcher. A thread do renderizador já tinha sumido antes de o launcher conseguir capturar um log.
A câmera não travava na nave. Câmera orbital padrão em terceira pessoa, deveria manter a nave em quadro. Na inicialização, a câmera surgia na origem do mundo e ficava lá. A nave estava visível através do vazio como um pontinho ao longe.
Quatro bugs. Zero sobreposição em subsistemas. O tipo de sábado de manhã que decide se você tem o fluxo de trabalho ou não.
O que causou cada um
Quero passar por cada um porque cada um ensina uma lição diferente sobre como uma base de código cresce e quebra em um fluxo de trabalho conduzido por agentes.
Os gráficos rosa
Causa raiz: handles de shader estavam sendo procurados por nome de string em toda chamada de renderização, e uma refatoração durante o feriado tinha movido a inicialização do cache de shader para um ponto diferente na sequência de boot. Os geradores procedurais agora estavam renderizando antes de o cache de shader ter aquecido. Estavam recebendo de volta o fallback de handle nulo, que o renderizador dutifully renderizava como rosa choque.
A correção foi criar um utilitário ShaderCache explícito que armazena em cache referências de shader resolvidas no primeiro uso e expõe uma API síncrona GetOrCreate que os geradores procedurais podem chamar sem se importar com a ordem de boot. Todo gerador procedural foi atualizado para usá-la. Rosa foi embora.
A lição: uma suposição de ordem de inicialização que vivia implicitamente na sequência de boot foi quebrada por uma refatoração que não sinalizou a suposição. Ordenação implícita é frágil. O novo cache torna a ordenação explícita e autocurativa.
A pontuação descontrolada
Causa raiz: o GameplayHUD estava contando abates no lado da UI, e o ScoreManager também estava contando abates no lado da simulação. Ambos estavam escutando o mesmo sinal de evento de abate. Ambos estavam somando em um campo de pontuação compartilhado. A pontuação era incrementada duas vezes por abate.
A correção foi escolher o dono canônico da pontuação. O ScoreManager é dono da verdade. O GameplayHUD lê do ScoreManager e renderiza. O incremento duplicado no HUD foi removido.
A lição: em um motor orientado a eventos, dois assinantes do mesmo evento vão ambos rodar. Se ambos escrevem no mesmo estado, o estado vai estar errado na proporção de quantos assinantes escreveram. A correção é desenhar uma linha clara sobre quem é dono de qual estado. Uma vez desenhada, o bug se torna impossível.
O crash de inicialização de RX
Causa raiz: o subsistema de RX estava tentando inicializar antes de o dispositivo gráfico subjacente estar pronto. Na inicialização em tela plana, a ordem por acaso funcionava porque o dispositivo gráfico sempre estava pronto quando o usuário pressionava “Iniciar.” Na inicialização em RX, a detecção de headset disparava o caminho de init de RX antes de o dispositivo gráfico ser confirmado como pronto. O subsistema de RX desreferenciou um handle de dispositivo nulo. Crash.
A correção foi um XRBootstrap que detecta RX no ponto mais cedo possível do boot, adia a inicialização real de RX até o dispositivo gráfico confirmar que está pronto, e desliga graciosamente se qualquer coisa na cadeia reporta uma falha. O crash agora se torna uma mensagem de erro limpa e o usuário recorre à tela plana.
A lição: crashes duros são o pior tipo de erro porque matam o logging que teria contado o que aconteceu. Detectar a falha mais cedo e reportá-la de forma limpa vale o custo de engenharia.
A câmera que não travava
Causa raiz: a câmera orbital tenta mirar na nave do jogador na inicialização. A nave é gerada pelo gerador procedural, assincronamente, alguns frames depois de a câmera inicializar. A câmera estava procurando pela nave no primeiríssimo frame, não encontrando nada, e depois nunca mais tentando de novo.
A correção foi dar à câmera orbital um mecanismo de nova tentativa. Ela procura pela nave por um número limitado de frames antes de desistir, e uma vez que trava, permanece travada. A câmera agora surge na origem brevemente, depois se encaixa na nave dentro do primeiro segundo de jogabilidade.
A lição: condições de corrida entre sistemas que inicializam em cronogramas ligeiramente diferentes vão te morder. A correção é uma pequena nova tentativa, limitada para não fazer loop para sempre, com um timeout explícito que produz um erro útil se a nova tentativa se esgotar.
O que aprendi sobre a demo Shooter especificamente
Três coisas, todas desconfortáveis.
Cada subsistema estava funcionando isoladamente. A integração estava quebrada. O renderizador funcionava. O sistema de pontuação funcionava. O subsistema de RX funcionava. A câmera funcionava. A demo Shooter como uma experiência integrada não funcionava. Esse é o tipo de falha que escapa de testes unitários toda vez, porque testes unitários por natureza exercitam coisas isoladamente.
A refatoração de feriado durante a pausa de final de dezembro foi a causa próxima de pelo menos dois desses bugs. A reorganização do cache de shader e a divisão de assinatura do evento de abate ambas entraram na janela do sprint de feriado. Ambas eram PRs corretos isoladamente. Ambas quebraram a demo de jeitos não óbvios. A lição é rodar as demos integradas como parte da CI, não só os testes unitários. Esse trabalho entrou nesta manhã como um PR separado.
O caminho de inicialização de RX precisava do seu próprio guardião de boot. Crashes duros são inaceitáveis porque matam o diagnóstico. Todo caminho de boot que toca hardware não trivial precisa de um guardião. Agora temos isso para RX. Não temos para áudio espacial nem para o link Wi-Fi 7. Adicionar os dois está na fila para os próximos dois sábados.
O que parceiros e construtores deveriam tirar disso
Se você está construindo algo que integra múltiplos subsistemas com dependências de ordem de inicialização, a lição é a mesma que a demo Shooter acabou de aprender. Torne a ordem de inicialização explícita. Torne os modos de falha graciosos. Rode a demo integrada na CI, não só os testes unitários.
Se você é um time independente pensando em adotar a Raku, a demo Shooter é um exemplo real que testamos contra todo lançamento. Se ela quebra, você vê ela quebrar. Esse é o tipo de sinal de disciplina pública que importa mais que uma lista de funcionalidades.
Se você é um lab de IA pensando em conectar seu modelo a uma experiência de RA, o caminho de inicialização de RX agora tem um guardião defensável. Seu modelo não vai ver um crash duro na entrada. Você vai receber relato de erro limpo se qualquer coisa na cadeia falhar. A infraestrutura está no lugar.
Quatro bugs. Um sábado. A demo compila limpa agora. Os convidados da festa foram escoltados para fora.
De volta a construir.
Lance em um runtime que roda as próprias demos
A RakuAI testa suas demos contra todo lançamento com CI de integração e guardiões graciosos de inicialização de RX. Esse é o sinal de disciplina pública que importa mais que uma lista de funcionalidades. Comece a construir sobre ele.