Recomendações de desempenho para o Unity
Este artigo se baseia nas recomendações de desempenho para realidade misturada, mas se concentra em aprimoramentos específicos do Unity.
Recentemente, lançamos um aplicativo chamado Conceitos básicos de qualidade, que abrange problemas comuns de desempenho, design e ambiente e soluções para os aplicativos HoloLens 2. Esse aplicativo é uma ótima demonstração visual do conteúdo a seguir.
Usar configurações de projeto recomendadas do Unity
A primeira etapa mais importante ao otimizar o desempenho de aplicativos de realidade misturada no Unity é verificar se você está usando as configurações de ambiente recomendadas para o Unity. Esse artigo traz um conteúdo com algumas das configurações de cena mais importantes para a criação de aplicativos do Mixed Reality de alto desempenho. Algumas dessas configurações recomendadas também são realçadas abaixo.
Como criar um perfil com o Unity
O Unity fornece o Unity Profiler interno, que é um ótimo recurso para reunir informações de desempenho úteis para seu aplicativo específico. Embora seja possível executar o criador de perfil no editor, essas métricas não representam o ambiente de runtime verdadeiro. Portanto, os resultados dessa tarefa devem ser usados com cautela. Recomendamos que você crie o perfil do aplicativo remotamente durante a execução no dispositivo para obter os insights mais precisos e práticos.
O Unity fornece uma excelente documentação para:
- Como conectar o Unity Profiler aos aplicativos UWP remotamente
- Como diagnosticar problemas de desempenho com o Unity Profiler de maneira eficaz
Criação de perfil de GPU
Criador de perfil do Unity
Com o Unity Profiler conectado e após a adição do criador de perfil de GPU (confira Adicionar Criador de Perfil no canto superior direito), é possível ver quanto tempo está sendo gasto na CPU e na GPU, respectivamente, no meio do criador de perfil. Isso permite que o desenvolvedor obtenha uma aproximação rápida caso o aplicativo esteja limitado pela CPU ou pela GPU.
Observação
Para usar a criação de perfil de GPU, você precisa desabilitar Trabalhos gráficos em Configurações do player do Unity. Consulte o módulo Criador de perfil de uso de GPU do Unity para obter mais detalhes.
Depurador de quadros do Unity
Além disso, o Depurador de quadros do Unity também é uma ferramenta de insights muito avançada a ser usada. Ele fornecerá uma boa visão geral do que a GPU está fazendo em cada quadro. As coisas a receberem atenção são destinos de renderização adicionais e comandos de transferência de bits para copiar entre eles, pois eles são muito caros no HoloLens. O ideal é que nenhum destino de renderização fora da tela seja usado no HoloLens. Geralmente, eles são adicionados ao habilitar recursos de renderização caros (por exemplo, MSAA, HDR ou efeitos de tela inteira, como abrir a mão), que devem ser evitados.
Sobreposição da taxa de quadros do HoloLens
A página Desempenho do sistema do Portal de Dispositivos tem um bom resumo do desempenho da CPU e da GPU do dispositivo. Você pode habilitar Exibir contador de taxa de quadros no headset e Exibir grafo da taxa de quadros no headset. Essas opções habilitarão um grafo e um contador de FPS, respectivamente, que fornecerão comentários imediatos em qualquer aplicativo em execução no seu dispositivo.
PIX
O PIX também pode ser usado para criar o perfil de aplicativos do Unity. Também há instruções detalhadas sobre como usar e instalar o PIX para o HoloLens 2. Em um build de desenvolvimento, os mesmos escopos que você vê no Depurador de quadros do Unity também serão mostrados no PIX e podem ser inspecionados e perfilados com mais detalhes.
Observação
O Unity fornece a capacidade de modificar facilmente a resolução de destino de renderização de seu aplicativo em runtime por meio da propriedade XRSettings.renderViewportScale. A imagem final apresentada no dispositivo tem uma resolução fixa. A plataforma usará como amostra a saída de resolução mais baixa para criar uma imagem de resolução mais alta para renderização em visores.
UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;
Recomendações de desempenho da CPU
O conteúdo abaixo aborda mais práticas de desempenho aprofundadas, especialmente direcionadas ao desenvolvimento em C# e Unity.
Referências de cache
Recomendamos armazenar em cache as referências a todos os componentes relevantes e GameObjects na inicialização, porque chamadas de função que se repetem, como GetComponent<T>() e Camera.main, são mais caras em relação ao custo de memória necessário para armazenar um ponteiro. . O Camera.main usa apenas FindGameObjectsWithTag() abaixo dele, que pesquisa com alto custo o grafo de cena em busca de um objeto da câmera com a marca "MainCamera".
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
private Camera cam;
private CustomComponent comp;
void Start()
{
cam = Camera.main;
comp = GetComponent<CustomComponent>();
}
void Update()
{
// Good
this.transform.position = cam.transform.position + cam.transform.forward * 10.0f;
// Bad
this.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 10.0f;
// Good
comp.DoSomethingAwesome();
// Bad
GetComponent<CustomComponent>().DoSomethingAwesome();
}
}
Observação
Evite GetComponent(string)
Ao usar GetComponent(), há várias sobrecargas diferentes. É importante sempre usar as implementações baseadas em tipo e nunca a sobrecarga de pesquisa baseada em cadeia de caracteres. A pesquisa por cadeia de caracteres na cena é significativamente mais cara do que a pesquisa por tipo.
(Bom) Componente: GetComponent(tipo de tipo)
(Bom) T GetComponent<T>()
(Inadequado) Component GetComponent(string)>
Evitar operações caras
Evite o uso do LINQ
Embora o LINQ possa ser limpo e fácil de ler e gravar, ele geralmente requer mais computação e memória do que a escrita manual do algoritmo.
// Example Code using System.Linq; List<int> data = new List<int>(); data.Any(x => x > 10); var result = from x in data where x > 10 select x;
APIs comuns do Unity
Algumas APIs do Unity, embora úteis, podem ser caras para executar. A maioria deles envolve a pesquisa de todo o grafo de cena em busca de alguma lista correspondente de GameObjects. Em geral, essas operações podem ser evitadas por meio do cache de referências ou da implementação de um componente de gerenciador para os GameObjects, a fim de acompanhar as referências em runtime.
GameObject.SendMessage() GameObject.BroadcastMessage() UnityEngine.Object.Find() UnityEngine.Object.FindWithTag() UnityEngine.Object.FindObjectOfType() UnityEngine.Object.FindObjectsOfType() UnityEngine.Object.FindGameObjectsWithTag() UnityEngine.Object.FindGameObjectsWithTag()
Observação
SendMessage() e BroadcastMessage() devem ser eliminados a qualquer custo. Essas funções podem estar em uma ordem 1.000 vezes mais lenta do que as chamadas de função diretas.
Cuidado com a conversão boxing
A conversão boxing é um conceito fundamental do runtime e da linguagem C#. É o processo de encapsular variáveis de tipo de valor, como
char
,int
,bool
etc., em variáveis de tipo de referência. Quando é feita a conversão boxing de uma variável de tipo de valor, ela é encapsulada em umSystem.Object
que é armazenado no heap gerenciado. A memória é alocada e, por fim, quando descartada, precisa ser processada pelo coletor de lixo. Essas alocações e essas desalocações geram um custo de desempenho e, em muitos cenários, são desnecessárias ou podem ser substituídas com facilidade por uma alternativa menos cara.Para evitar a conversão boxing, verifique se as variáveis, as propriedades e os campos em que você armazena tipos numéricos e structs (incluindo
Nullable<T>
) são fortemente tipados como tipos específicos, comoint
,float?
ouMyStruct
, em vez de usar um objeto. Se colocar esses objetos em uma lista, lembre-se de usar uma lista fortemente tipada, comoList<int>
, em vez deList<object>
ouArrayList
.Exemplo de conversão boxing em C#
// boolean value type is boxed into object boxedMyVar on the heap bool myVar = true; object boxedMyVar = myVar;
Repetição de caminhos de código
Qualquer função de retorno de chamada do Unity repetida (ou seja, Update) executada muitas vezes por segundo e/ou quadro deve ser gravada com cuidado. Qualquer operação cara aqui terá um impacto enorme e consistente sobre o desempenho.
Funções de retorno de chamada vazio
Embora possa parecer não ter problema deixar o código abaixo em seu aplicativo, especialmente porque cada script do Unity é inicializado automaticamente com um método Update, esses retornos de chamada vazios podem ficar muito caros. O Unity opera em um limite de código não gerenciado/gerenciado, entre o código do UnityEngine e o código do aplicativo. A alternância de contexto nessa ponte é bem cara, mesmo que não haja nada a ser executado. Isso se torna especialmente problemático se o aplicativo tem centenas de GameObjects com componentes que tenham retornos de chamada repetitivos e vazios do Unity.
void Update() { }
Observação
Update() é a manifestação mais comum desse problema de desempenho, mas outros retornos de chamada repetidos do Unity, como os seguintes, podem ser igualmente ruins, se não piores: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), etc.
Operações para favorecer a execução uma vez por quadro
As APIs do Unity a seguir são operações comuns para muitos aplicativos holográficos. Embora nem sempre seja possível, os resultados dessas funções podem ser computados com muita frequência e são reutilizados no aplicativo para determinado quadro.
a) É uma boa prática ter uma classe Singleton dedicada ou um serviço para processar o foco Raycast na cena e, em seguida, reutilizar esse resultado em todos os outros componentes de cena em vez de fazer operações Raycast repetidas e idênticas por componente. Alguns aplicativos podem exigir raycasts de diferentes origens ou em diferentes LayerMasks.
UnityEngine.Physics.Raycast() UnityEngine.Physics.RaycastAll()
b) Evite operações GetComponent() em retornos de chamada repetitivos do Unity, como Update(), com o cache de referências em Start() ou Awake()
UnityEngine.Object.GetComponent()
c) É uma boa prática criar uma instância de todos os objetos na inicialização, se possível, e usar o pool de objetos para reciclar e reutilizar GameObjects em todo o runtime do aplicativo
UnityEngine.Object.Instantiate()
Evite interfaces e constructos virtuais
A invocação de chamadas de função por meio de interfaces em comparação com objetos diretos ou chamada de funções virtuais muitas vezes pode ser muito mais custosa do que o uso de constructos diretos ou chamadas de função diretas. Se a interface ou a função virtual for desnecessária, ela deverá ser removida. No entanto, o impacto no desempenho dessas abordagens geralmente vale a pena se o uso delas simplifica a colaboração de desenvolvimento, a legibilidade e a manutenção do código.
Em geral, a recomendação é não marcar campos e funções como virtuais, a menos que haja uma expectativa bem definida de que esse membro precise ser substituído. É preciso ser especialmente cuidadoso em relação aos caminhos de código de alta frequência que são chamados muitas vezes por quadro ou, até mesmo, uma vez por quadro, como um método
UpdateUI()
.Evite transmitir structs por valor
Ao contrário das classes, os structs são tipos de valor e, quando transmitidos diretamente para uma função, o conteúdo deles é copiado para uma instância recém-criada. Essa cópia adiciona custo de CPU, bem como memória adicional na pilha. Para structs pequenos, o efeito é geralmente muito mínimo e, portanto, aceitável. No entanto, para as funções invocadas repetidamente a cada quadro, bem como as funções que usam structs grandes, se possível, modifique a definição de função para transmiti-la por referência. Saiba mais aqui
Diversos
Física
r) Em geral, a maneira mais fácil de aprimorar a física é limitar o tempo gasto na física ou no número de iterações por segundo. Isso reduzirá a precisão da simulação. Confira TimeManager no Unity
b) Os tipos de colisores no Unity têm características de desempenho amplamente diferentes. A ordem abaixo lista os colisores com melhor desempenho até os colisores com pior desempenho, da esquerda para a direita. É importante evitar os colisores de malha, que são consideravelmente mais caros do que os colisores primitivos.
Esfera < Cápsula < Caixa <<< Malha (convexa) < Malha (não convexa)
Confira Melhores práticas de física do Unity para obter mais informações
Animações
Desabilite as animações ociosas desabilitando o componente Animator (a desabilitação do objeto de jogo não terá o mesmo efeito). Evite padrões de design em que um animador fica em um loop configurando um valor para o mesmo item. Há uma sobrecarga considerável nessa técnica, sem nenhum efeito no aplicativo. Saiba mais aqui.
Algoritmos complexos
Se o aplicativo estiver usando algoritmos complexos, como cinemática inversa, localização de caminho etc., procure encontrar uma abordagem mais simples ou ajustar as configurações relevantes para o desempenho dele
Recomendações do desempenho de CPU para GPU
Em geral, o desempenho de CPU para GPU se resume às chamadas de desenho enviadas à placa gráfica. Para aprimorar o desempenho, as chamadas de desenho precisam ser estrategicamente a) reduzidas ou b) reestruturadas para resultados ideais. Como as próprias chamadas de desenho fazem uso intensivo de recursos, a redução delas diminuirá o trabalho geral necessário. Além disso, as alterações de estado entre chamadas de desenho exigem etapas caras de validação e conversão no driver gráfico. Portanto, a reestruturação das chamadas de desenho do aplicativo usadas para limitar as alterações de estado (ou seja, materiais diferentes etc.) pode aumentar o desempenho.
O Unity conta com um ótimo artigo que fornece uma visão geral e se aprofunda no envio em lote de chamadas de desenho para a plataforma.
Renderização com uma instância de passagem única
A renderização com uma instância de passagem única no Unity permite que as chamadas de desenho para cada olho sejam reduzidas a uma chamada de desenho com instância. Devido à coerência de cache entre duas chamadas de desenho, também há alguma melhoria de desempenho na GPU.
Como habilitar esse recurso no seu projeto do Unity
- Abra as Configurações do OpenXR (acesse Editar>Configurações do Projeto>Gerenciamento de Plug-ins do XR>OpenXR).
- Selecione Instância de Passagem Única no menu suspenso Modo de Renderização.
Leia os artigos a seguir do Unity para obter detalhes dessa abordagem de renderização.
- Como maximizar o desempenho de RA e VR com a renderização avançada de estéreo
- Instância de passagem única
Observação
Um problema comum na renderização com uma instância de passagem única ocorre se os desenvolvedores já têm sombreadores personalizados existentes não escritos para a criação de instância. Depois de habilitar esse recurso, os desenvolvedores poderão perceber que alguns GameObjects são renderizados apenas em um olho. Isso ocorre porque os sombreadores personalizados associados não têm as propriedades apropriadas para a criação de instância.
Envio em lote estático
O Unity pode enviar em lote muitos objetos estáticos para reduzir chamadas de desenho para a GPU. O envio em lote estático funciona para a maioria dos objetos do renderizador no Unity que 1) compartilha o material e 2) é marcada como Estático (selecione um objeto no Unity e clique na caixa de seleção no canto superior direito do inspetor). Os GameObjects marcados como Estáticos não podem ser movidos durante todo o runtime do aplicativo. Assim, o lote estático pode ser difícil de aproveitar no HoloLens, onde praticamente todos os objetos precisam ser colocados, movidos, dimensionados, etc. Para fones de ouvido imersivos, o lote estático pode reduzir drasticamente as chamadas de desenho e, assim, melhorar o desempenho.
Leia Envio em lote estático em Envio em lote de chamadas de desenho no Unity para obter mais detalhes.
Envio em lote dinâmico
Como é problemático marcar objetos como Estáticos para o desenvolvimento no HoloLens, o envio em lote dinâmico pode ser uma ótima ferramenta para compensar esse recurso. Isso também pode ser útil em headsets imersivos. No entanto, o envio em lote dinâmico no Unity pode ser difícil de ser habilitado, porque os GameObjects precisam a) compartilhar o material e b) atender a uma lista longa de outros critérios.
Leia Envio em lote dinâmico em Envio em lote de chamadas de desenho no Unity para obter a lista completa. Normalmente, os GameObjects se tornam inválidos para serem enviados em lote dinamicamente, porque os dados de malha associados não podem ter mais de 300 vértices.
Outras técnicas
O envio em lote só pode ocorrer se vários GameObjects podem compartilhar o material. Normalmente, isso será bloqueado pela necessidade de os GameObjects terem uma textura exclusiva para o respectivo material. É comum combinar as texturas em uma textura grande, um método conhecido como atlas de textura.
Além disso, é preferível combinar malhas em um só GameObject sempre que possível e razoável. Cada renderizador do Unity terá as chamadas de desenho associadas em vez de enviar uma malha combinada em um renderizador.
Observação
A modificação das propriedades de Renderer.material em runtime criará uma cópia do material e, portanto, poderá interromper o envio em lote. Use Renderer.sharedMaterial para modificar as propriedades de material compartilhadas em GameObjects.
Recomendações de desempenho da GPU
Saiba mais sobre como otimizar a renderização de gráficos no Unity
Largura de banda e taxas de preenchimento
Ao renderizar um quadro na GPU, um aplicativo é limitado pela largura de banda de memória ou taxa de preenchimento.
- A largura de banda da memória é a taxa de leituras e gravações que a GPU pode fazer a partir da memória
- No Unity, altere Qualidade da Textura em Editar>Configurações do Projeto>Configurações de Qualidade.
- Taxa de preenchimento refere-se aos pixels que podem ser desenhados por segundo pela GPU.
- No Unity, use a propriedade XRSettings.renderViewportScale.
Otimizar o compartilhamento de buffer de profundidade
Recomendamos que você habilite o Compartilhamento de buffer de profundidade para otimizar a estabilidade do holograma. Ao habilitar a reprojeção de fase tardia baseada em profundidade com essa configuração, recomendamos selecionar o formato de profundidade de 16 bits em vez do formato de profundidade de 24 bits. Os buffers de profundidade de 16 bits reduzem drasticamente a largura de banda (e, portanto, a energia) associada ao tráfego do buffer de profundidade. Isso pode ser um grande aprimoramento na redução de energia e na melhoria do desempenho. No entanto, há dois resultados negativos possíveis com o uso do formato de profundidade de 16 bits.
Luta z
A fidelidade de intervalo de profundidade reduzida possibilita a ocorrência de z-fighting mais com 16 bits do que com 24 bits. Para evitar esses artefatos, modifique os planos de clipes próximos/distantes da câmera do Unity para considerar a precisão mais baixa. Para aplicativos baseados no HoloLens, um plano de recorte distante de 50 m em vez dos 1.000 m padrão do Unity geralmente pode eliminar qualquer z-fighting.
Buffer de estêncil desabilitado
Quando o Unity cria uma textura de renderização com profundidade de 16 bits, nenhum buffer de estêncil é criado. A seleção do formato de profundidade de 24 bits, conforme descrito na documentação do Unity, criará um buffer z de 24 bits e um buffer de estêncil de 8 bits – se 32 bits for aplicável em um dispositivo (por exemplo, o HoloLens), o que geralmente é o caso.
Evitar efeitos de tela inteira
As técnicas que operam na tela inteira podem ser bastante custosas, já que a ordem de magnitude é de milhões de operações a cada quadro. É recomendável evitar efeitos de pós-processamento como suavização, gestos de abrir a mão, entre outros.
Configurações de iluminação ideais
A Iluminação Global em Tempo Real do Unity pode gerar resultados visuais excepcionais, mas envolve cálculos de iluminação bastante caros. Recomendamos desabilitar a Iluminação Global em tempo real em cada arquivo de cena do Unity em Janela>Renderização>Configurações de Iluminação> desmarque Iluminação Global em Tempo Real.
Além disso, é recomendável desabilitar toda a conversão de sombra, pois ela também adiciona passagens de GPU custosas a uma cena do Unity. As sombras podem ser desabilitadas conforme a luz, mas também podem ser controladas de maneira holística por meio das configurações de Qualidade.
Editar>Configurações do Projeto e selecione a categoria Qualidade> selecione Baixa Qualidade para a plataforma UWP. Também é possível definir apenas a propriedade Shadows como Desabilitar Sombras.
É recomendável usar a iluminação integrada com seus modelos no Unity.
Reduzir a contagem de polígonos
A contagem de polígonos é reduzida por
- Remoção de objetos de uma cena
- Eliminação de ativos, o que reduz o número de polígonos de uma determinada malha
- Implementação de um sistema de LOD (Nível de Detalhe) no aplicativo que renderiza objetos distantes com a versão de polígono mais baixo da mesma geometria
Noções básicas sobre os sombreadores do Unity
Uma aproximação fácil para comparar os sombreadores em desempenho é identificar o número médio de operações que cada um executa em runtime. Isso pode ser feito com facilidade no Unity.
Selecione o ativo do sombreador ou um material e, no canto superior direito da janela do inspetor, selecione o ícone de engrenagem seguido de "Selecionar Sombreador"
Com o ativo do sombreador selecionado, clique no botão "Compilar e mostrar código" na janela do inspetor
Após a compilação, procure a seção de estatísticas nos resultados com o número de operações diferentes para o vértice e o sombreador de pixel (observação: os sombreadores de pixel costumam ser chamados de sombreadores de fragmento)
Otimizar sombreadores de pixel
Observando os resultados da estatística compilada usando o método acima, em média, o sombreador de fragmento geralmente executará mais operações do que o sombreador de vértice. O sombreador de fragmento, também conhecido como sombreador de pixel, é executado por pixel na saída da tela, ao passo que o sombreador de vértice só é executado por vértice de todas as malhas que estão sendo desenhadas na tela.
Portanto, os sombreadores de fragmento têm mais instruções do que os sombreadores de vértice devido a todos os cálculos de iluminação e quase sempre são executados em um conjunto de dados maior. Por exemplo, se a saída da tela for uma imagem de 2.000 por 2.000, o sombreador de fragmento poderá ser executado 2.000 * 2.000 = 4.000.000 vezes. Se a renderização de dois olhos estiver sendo feita, esse número dobrará, pois há duas telas. Se um aplicativo de realidade misturada tiver várias passagens, efeitos de pós-processamento de tela inteira ou estiver renderizando várias malhas no mesmo pixel, esse número aumentará drasticamente.
Portanto, a redução do número de operações no sombreador de fragmento pode, em geral, proporcionar ganhos de desempenho muito maiores em otimizações no sombreador de vértice.
Alternativas do sombreador padrão do Unity
Em vez de usar uma PBR (renderização baseada em física) ou outro sombreador de alta qualidade, examine a utilização de um sombreador mais barato e com melhor desempenho. O Mixed Reality Toolkit fornece o sombreador padrão do MRTK que foi otimizado para projetos de realidade misturada.
O Unity também fornece um sombreador apagado, com vértice iluminado, difuso e outras opções de sombreador simplificadas que são mais rápidas em comparação com o sombreador padrão do Unity. Confira Uso e desempenho de sombreadores internos para obter informações mais detalhadas.
Pré-carregamento de sombreador
Use o Pré-carregamento de sombreador e outros truques para otimizar o tempo de carregamento do sombreador. Em particular, o pré-carregamento de sombreador significa que você não verá nenhum problema devido à compilação do sombreador em runtime.
Limitar a sobreposição
No Unity, é possível exibir sobreposições para a cena alternando o menu do modo de desenho no canto superior esquerdo da Exibição de cena e selecionando Sobreposição.
Em geral, a sobreposição pode ser atenuada com a remoção de objetos antecipadamente ao envio para a GPU. O Unity fornece detalhes sobre como implementar a Remoção de Oclusão para o mecanismo.
Recomendações de memória
As operações de alocação e desalocação de memória excessiva podem ter efeitos adversos no aplicativo holográfico, resultando em desempenho inconsistente, quadros congelados e outros comportamentos prejudiciais. É especialmente importante entender as considerações de memória no desenvolvimento no Unity, pois o gerenciamento de memória é controlado pelo coletor de lixo.
Coleta de lixo
Os aplicativos holográficos perderão o tempo de computação do processamento para o GC (coletor de lixo) quando o GC for ativado para analisar objetos que não estão mais no escopo durante a execução, e a memória deles precisará ser liberada, de modo que possa ser disponibilizada para reutilização. As alocações e as desalocações constantes geralmente exigirão que o coletor de lixo seja executado com mais frequência, prejudicando o desempenho e a experiência do usuário.
O Unity forneceu uma página excelente que explica em detalhes como o coletor de lixo funciona e dicas para escrever um código mais eficiente em relação ao gerenciamento de memória.
Uma das práticas mais comuns que leva ao excesso de coleta de lixo não é armazenar em cache referências a componentes e classes no desenvolvimento no Unity. Todas as referências devem ser capturadas durante Start() ou Awake() e reutilizadas em funções posteriores, como Update() ou LateUpdate().
Outras dicas rápidas:
- Use a classe C# StringBuilder para criar dinamicamente cadeias de caracteres complexas em runtime
- Remova as chamadas a Debug.Log() quando não for mais necessário, pois elas ainda são executadas em todas as versões de build de um aplicativo
- Se o aplicativo holográfico geralmente exige muita memória, considere a possibilidade de chamar System.GC.Collect() durante o carregamento de fases, como ao apresentar uma tela de carregamento ou de transição
Pool de objetos
O pool de objetos é uma técnica popular para reduzir o custo de alocações e desalocações contínuas de objetos. Isso é feito pela alocação de um grande pool de objetos idênticos e pela reutilização das instâncias disponíveis inativas desse pool em vez da criação e da destruição constantes de objetos ao longo do tempo. Os pools de objetos são ótimos para componentes reutilizados que têm um tempo de vida variável durante um aplicativo.
Desempenho da inicialização
Considere iniciar seu aplicativo com uma cena menor e, em seguida, usar SceneManager.LoadSceneAsync para carregar o restante da cena. Isso permite que o aplicativo chegue a um estado interativo o mais rápido possível. Pode haver um grande pico de CPU enquanto a nova cena está sendo ativada e qualquer conteúdo renderizado pode tremer ou apresentar algum problema. Um modo de resolver isso é definir a propriedade AsyncOperation.allowSceneActivation como "false" na cena que está sendo carregada, aguardar a cena ser carregada, limpar a tela para preto e, em seguida, defini-la novamente como "true" para concluir a ativação da cena.
Lembre-se de que, durante o carregamento da cena de inicialização, a tela inicial holográfica será exibida para o usuário.