Recomendações de desempenho para o Unity

Este artigo baseia-se nas recomendações de desempenho para a realidade mista, mas foca-se em melhorias específicas do Unity.

Lançámos recentemente uma aplicação denominada Quality Fundamentals que abrange problemas comuns de desempenho, design e ambiente e soluções para aplicações HoloLens 2. Esta aplicação é uma excelente demonstração visual para os conteúdos que se seguem.

O primeiro passo mais importante para otimizar o desempenho de aplicações de realidade mista no Unity é ter a certeza de que está a utilizar as definições de ambiente recomendadas para o Unity. Este artigo contém conteúdo com algumas das configurações de cenário mais importantes para a criação de aplicações de Mixed Reality de desempenho. Algumas destas definições recomendadas também estão realçadas abaixo.

Como criar perfis com o Unity

O Unity fornece o Unity Profiler incorporado, que é um excelente recurso para recolher informações de desempenho valiosas para a sua aplicação específica. Embora possa executar o gerador de perfis no editor, estas métricas não representam o verdadeiro ambiente de runtime, pelo que os resultados devem ser utilizados com cautela. Recomendamos que crie um perfil remoto da sua aplicação durante a execução no dispositivo para obter as informações mais precisas e acionáveis.

O Unity fornece uma excelente documentação para:

  1. Como ligar o gerador de perfis do Unity a aplicações UWP remotamente
  2. Como diagnosticar eficazmente problemas de desempenho com o Unity Profiler

Criação de perfis de GPU

Gerador de perfis do Unity

Com o Unity Profiler ligado e depois de adicionar o gerador de perfis de GPU (veja Adicionar Profiler no canto superior direito), pode-se ver quanto tempo está a ser gasto na CPU & GPU, respetivamente, no meio do profiler. Isto permite ao programador obter uma aproximação rápida se a sua aplicação estiver vinculada à CPU ou à GPU.

Unity CPU vs GPU

Nota

Para utilizar a criação de perfis GPU, tem de desativar as Tarefas Gráficas nas Definições do Leitor do Unity. Veja o módulo GpU Usage Profiler do Unity para obter mais detalhes.

Depurador de molduras do Unity

O Depurador de Molduras do Unity também é uma ferramenta poderosa e perspicaz a utilizar. Irá dar-lhe uma boa descrição geral do que a GPU está a fazer em cada frame. Os aspetos a ter em conta são destinos de composição adicionais e comandos blit para copiar entre eles, uma vez que estes são muito caros no HoloLens. Idealmente, não devem ser utilizados destinos de composição fora do ecrã no HoloLens. Normalmente, estas funcionalidades são adicionadas ao ativar funcionalidades de composição dispendiosas (por exemplo, MSAA, HDR ou efeitos de ecrã inteiro, como bloom), que devem ser evitadas.

Sobreposição da taxa de fotogramas do HoloLens

A página Desempenho do Sistema do Portal do Dispositivo tem um bom resumo do desempenho da CPU e da GPU do dispositivo. Pode ativar o contador de taxas de fotogramas de visualização nos auscultadores e gráfico de taxa de fotogramas de visualização nos auscultadores. Estas opções irão ativar um contador FPS e um gráfico, respetivamente, que lhe darão feedback imediato em qualquer aplicação em execução no seu dispositivo.

PIX

O PIX também pode ser utilizado para criar perfis de aplicações do Unity. Também existem instruções detalhadas sobre como utilizar e instalar o PIX para HoloLens 2. Numa compilação de desenvolvimento, os mesmos âmbitos que vê no Frame Debugger do Unity também serão apresentados no PIX e podem ser inspecionados e criados perfis mais detalhadamente.

Nota

O Unity fornece a capacidade de modificar facilmente a resolução de destino de composição da sua aplicação no runtime através da propriedade XRSettings.renderViewportScale . A imagem final apresentada no dispositivo tem uma resolução fixa. A plataforma irá provar a saída de resolução inferior para criar uma imagem de resolução mais alta para composição em ecrãs.

UnityEngine.XR.XRSettings.renderViewportScale = 0.7f;

Recomendações de desempenho da CPU

O conteúdo abaixo abrange práticas de desempenho mais aprofundadas, especialmente direcionadas para o desenvolvimento do Unity & C#.

Referências de cache

Recomendamos a colocação em cache de referências a todos os componentes relevantes e GameObjects na inicialização, uma vez que as chamadas de função de repetição, como GetComponent<T>() e Camera.main , são mais caras em relação ao custo de memória para armazenar um ponteiro. . Camera.main apenas utiliza FindGameObjectsWithTag() por baixo, que procura caro um objeto de câmara no gráfico de cenas com a etiqueta "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();
    }
}

Nota

Evitar GetComponent(cadeia)
Ao utilizar GetComponent(), existem várias sobrecargas diferentes. É importante utilizar sempre as implementações baseadas em Tipos e nunca a sobrecarga de pesquisa baseada em cadeias. Procurar por cadeia na sua cena é significativamente mais dispendioso do que procurar por Tipo.
(Bom) Componente GetComponent(Tipo)
(Bom) T GetComponent<T>()
(Incorreto) Componente GetComponent(cadeia)>

Evitar operações dispendiosas

  1. Evitar a utilização do LINQ

    Embora o LINQ possa ser limpo e fácil de ler e escrever, geralmente requer mais computação e memória do que se escreveu o algoritmo manualmente.

    // 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;
    
  2. APIs comuns do Unity

    Algumas APIs do Unity, embora úteis, podem ser dispendiosas de executar. A maioria destas opções envolve procurar em todo o gráfico de cenas uma lista correspondente de GameObjects. Geralmente, estas operações podem ser evitadas ao colocar referências em cache ou implementar um componente de gestor para os GameObjects controlarem as referências no runtime.

        GameObject.SendMessage()
        GameObject.BroadcastMessage()
        UnityEngine.Object.Find()
        UnityEngine.Object.FindWithTag()
        UnityEngine.Object.FindObjectOfType()
        UnityEngine.Object.FindObjectsOfType()
        UnityEngine.Object.FindGameObjectsWithTag()
        UnityEngine.Object.FindGameObjectsWithTag()
    

Nota

SendMessage() e BroadcastMessage() devem ser eliminados a todo o custo. Estas funções podem estar na ordem 1000x mais lenta do que as chamadas de função diretas.

  1. Cuidado com o boxe

    O boxe é um conceito fundamental da linguagem C# e do runtime. É o processo de encapsular variáveis com tipo de valor, como char, int, bool, etc. em variáveis com tipo de referência. Quando uma variável com tipo de valor é "boxed", é encapsulada num System.Object, que é armazenado na área de dados gerida. A memória é alocada e, eventualmente, quando eliminada tem de ser processada pelo recoletor de lixo. Estas alocações e desalocação incorrem num custo de desempenho e, em muitos cenários, são desnecessárias ou podem ser facilmente substituídas por uma alternativa menos dispendiosa.

    Para evitar o boxe, certifique-se de que as variáveis, campos e propriedades nas quais armazena tipos numéricos e estruturas (incluindo Nullable<T>) são fortemente digitadas como tipos específicos, como int, float? ou MyStruct, em vez de utilizar o objeto. Se colocar estes objetos numa lista, certifique-se de que utiliza uma lista fortemente digitada, como List<int> em vez de List<object> ou ArrayList.

    Exemplo de boxe em C#

    // boolean value type is boxed into object boxedMyVar on the heap
    bool myVar = true;
    object boxedMyVar = myVar;
    

Repetir caminhos de código

Todas as funções de chamada de retorno do Unity repetidas (ou seja, Atualização) executadas muitas vezes por segundo e/ou frame devem ser escritas cuidadosamente. Quaisquer operações dispendiosas aqui terão um impacto enorme e consistente no desempenho.

  1. Funções de chamada de retorno vazias

    Embora o código abaixo possa parecer inocente de sair na sua aplicação, especialmente porque cada script do Unity inicializa automaticamente com um método Update, estas chamadas de retorno vazias podem tornar-se dispendiosas. O Unity funciona entre um limite de código não gerido e gerido, entre o código UnityEngine e o código da aplicação. O contexto que passa por esta ponte é bastante caro, mesmo que não haja nada para executar. Isto torna-se especialmente problemático se a sua aplicação tiver 100 de GameObjects com componentes com chamadas de retorno do Unity repetidas vazias.

    void Update()
    {
    }
    

Nota

Update() é a manifestação mais comum deste problema de desempenho, mas outras chamadas de retorno repetidas do Unity, como as seguintes podem ser igualmente más, se não piores: FixedUpdate(), LateUpdate(), OnPostRender", OnPreRender(), OnRenderImage(), etc.

  1. Operações que favorecem a execução uma vez por frame

    As seguintes APIs do Unity são operações comuns para muitas Aplicações Holográficas. Embora nem sempre seja possível, os resultados destas funções podem geralmente ser calculados uma vez e os resultados reutilizados na aplicação para uma determinada moldura.

    a) É uma boa prática ter uma classe ou serviço Singleton dedicado para lidar com o seu olhar Raycast para a cena e, em seguida, reutilizar este resultado em todos os outros componentes de cena, em vez de fazer operações raycast repetidas e idênticas por cada componente. Algumas aplicações podem necessitar de raycasts de origens diferentes ou de diferentes LayerMasks.

        UnityEngine.Physics.Raycast()
        UnityEngine.Physics.RaycastAll()
    

    b) Evite operações GetComponent() em chamadas de retorno repetidas do Unity, como Update() por colocação em cache de referências em Iniciar() ou Acordado()

        UnityEngine.Object.GetComponent()
    

    c) É uma boa prática instanciar todos os objetos, se possível, na inicialização e utilizar o agrupamento de objetos para reciclar e reutilizar GameObjects ao longo do runtime da sua aplicação

        UnityEngine.Object.Instantiate()
    
  2. Evitar interfaces e construções virtuais

    Invocar chamadas de funções através de interfaces vs. objetos diretos ou chamar funções virtuais pode, muitas vezes, ser muito mais caro do que utilizar construções diretas ou chamadas de funções diretas. Se a função ou interface virtual for desnecessária, deve ser removida. No entanto, o desempenho atingido por estas abordagens vale a pena a compensação se utilizá-las simplificar a colaboração de desenvolvimento, a legibilidade do código e a manutenção do código.

    Geralmente, a recomendação é não marcar campos e funções como virtuais, a menos que exista uma clara expectativa de que este membro tenha de ser substituído. Deve-se ter especial cuidado em torno de caminhos de código de alta frequência que são chamados muitas vezes por fotograma ou mesmo uma vez por moldura, como um UpdateUI() método.

  3. Evitar a passagem de estruturas por valor

    Ao contrário das classes, as estruturas são tipos de valor e, quando transmitidas diretamente para uma função, os respetivos conteúdos são copiados para uma instância criada recentemente. Esta cópia adiciona o custo da CPU, bem como memória adicional na pilha. Para pequenas estruturas, o efeito é mínimo e, portanto, aceitável. No entanto, para funções invocadas repetidamente cada frame, bem como funções que tomam grandes estruturas, se possível, modifique a definição da função para passar por referência. Saiba mais aqui

Diversos

  1. Física

    a) Geralmente, a forma mais fácil de melhorar a física é limitar a quantidade de tempo gasto em Física ou o número de iterações por segundo. Isto reduzirá a precisão da simulação. Ver TimeManager no Unity

    b) Os tipos de colisores no Unity têm características de desempenho muito diferentes. A ordem abaixo lista os colisores mais eficazes para os colisores com menos desempenho da esquerda para a direita. É importante evitar os Colisores de Malha, que são substancialmente mais caros do que os colisores primitivos.

    Sphere < Capsule < Box <<< Mesh (Convex) < Mesh (não Convex)

    Consulte As Melhores Práticas físicas do Unity para obter mais informações

  2. Animações

    Desative as animações inativas ao desativar o componente Animator (desativar o objeto de jogo não terá o mesmo efeito). Evite padrões de estrutura em que um animador se senta num ciclo ao definir um valor para a mesma coisa. Existe uma sobrecarga considerável para esta técnica, sem qualquer efeito na aplicação. Saiba mais aqui.

  3. Algoritmos complexos

    Se a sua aplicação estiver a utilizar algoritmos complexos, como kinemática inversa, localização de caminhos, etc., procure encontrar uma abordagem mais simples ou ajustar as definições relevantes para o seu desempenho

Recomendações de desempenho da CPU para GPU

Geralmente, o desempenho da CPU para GPU resume-se às chamadas de sorteio submetidas para a placa gráfica. Para melhorar o desempenho, as chamadas de desenho têm de ser estrategicamente reduzidas ou b) reestruturadas para obter resultados ideais. Uma vez que as chamadas de desenho são intensivas em termos de recursos, reduzi-las reduzirá o trabalho geral necessário. Além disso, as alterações de estado entre chamadas de desenho requerem validação dispendiosa e passos de tradução no controlador gráfico e, por conseguinte, a reestruturação das chamadas de desenho da sua aplicação para limitar as alterações de estado (ou seja, materiais diferentes, etc.) pode aumentar o desempenho.

O Unity tem um excelente artigo que fornece uma descrição geral e analisa as chamadas de desenho em lotes para a sua plataforma.

Composição de instâncias de passagem única

A Composição de Instâncias de Passagem Única no Unity permite que as chamadas de desenho para cada olho sejam reduzidas para uma chamada de desenho instância. Devido à coerência da cache entre duas chamadas de sorteio, também existem algumas melhorias de desempenho na GPU.

Para ativar esta funcionalidade no projeto do Unity

  1. Abra As Definições openXR (aceda a Editar>Definições> do ProjetoXR Gestão de Plug-ins>OpenXR).
  2. Selecione Single Pass Instanced (Instâncias de Passagem Única ) no menu pendente Modo de Composição .

Leia os seguintes artigos do Unity para obter detalhes sobre esta abordagem de composição.

Nota

Um problema comum com a Composição de Instâncias de Passagem Única ocorre se os programadores já tiverem sombreados personalizados existentes não escritos para instancing. Depois de ativar esta funcionalidade, os programadores podem reparar que alguns GameObjects são compostos apenas num olho. Isto deve-se ao facto de os sombreados personalizados associados não terem as propriedades adequadas para o instancing.

Veja Composição Estéreo de Passagem Única para HoloLens do Unity para saber como resolver este problema

Criação de batches estáticos

O Unity é capaz de criar lotes de muitos objetos estáticos para reduzir as chamadas de desenho para a GPU. O Batching Estático funciona para a maioria dos objetos de Composição no Unity que 1) partilham o mesmo material e 2) estão todos marcados como Estáticos (Selecione um objeto no Unity e selecione a caixa de verificação no canto superior direito do inspetor). Os GameObjects marcados como Estáticos não podem ser movidos ao longo do runtime da sua aplicação. Assim, a criação de batches estáticos pode ser difícil de tirar partido do HoloLens, onde praticamente todos os objetos precisam de ser colocados, movidos, dimensionados, etc. Para auscultadores envolventes, a criação de lotes estáticos pode reduzir drasticamente as chamadas de desenho e, assim, melhorar o desempenho.

Leia Batching Estático emDesenhar Batching de Chamadas no Unity para obter mais detalhes.

Criação de batches dinâmicos

Uma vez que é problemático marcar objetos como Estáticos para o desenvolvimento do HoloLens, a criação de batches dinâmicos pode ser uma ótima ferramenta para compensar esta falta de funcionalidade. Também pode ser útil em headsets envolventes. No entanto, a criação de batches dinâmicos no Unity pode ser difícil de ativar porque os GameObjects têm de a) partilhar o mesmo Material e b) cumprir uma longa lista de outros critérios.

Leia Batching Dinâmico emDesenhar Batching de Chamadas no Unity para obter a lista completa. Normalmente, os GameObjects tornam-se inválidos para serem colocados em lotes dinamicamente, uma vez que os dados de malha associados não podem ter mais de 300 vértices.

Outras técnicas

A criação de batches só pode ocorrer se vários GameObjects conseguirem partilhar o mesmo material. Normalmente, isto será bloqueado pela necessidade de os GameObjects terem uma textura única para o respetivo Material. É comum combinar Texturas numa textura grande, um método conhecido como Atlasing de Textura.

Além disso, é preferível combinar malhas num GameObject sempre que possível e razoável. Cada Compositor no Unity terá as chamadas de desenho associadas em vez de submeter uma malha combinada num Renderer.

Nota

Modificar as propriedades de Renderer.material no runtime irá criar uma cópia do Material e, assim, potencialmente interromper a criação de batches. Utilize Renderer.sharedMaterial para modificar propriedades de material partilhado em GameObjects.

Recomendações de desempenho da GPU

Saiba mais sobre como otimizar a composição de gráficos no Unity

Taxas de largura de banda e preenchimento

Ao compor uma moldura na GPU, uma aplicação está vinculada por largura de banda de memória ou taxa de preenchimento.

  • A largura de banda de memória é a taxa de leituras e escritas que a GPU pode fazer a partir da memória
    • No Unity, altere a Qualidade da Textura em Editar>Definições de Qualidadedas Definições> de Projeto.
  • A taxa de preenchimento refere-se aos pixéis que podem ser desenhados por segundo pela GPU.

Otimizar a partilha da memória intermédia de profundidade

Recomendamos que ative a partilha da memória intermédia de profundidade para otimizar a estabilidade do holograma. Ao ativar a reprojeção tardia baseada em profundidade com esta definição, recomendamos que selecione o formato de profundidade de 16 bits em vez do formato de profundidade de 24 bits . As memórias intermédias de profundidade de 16 bits reduzirão drasticamente a largura de banda (e, portanto, a potência) associada ao tráfego de memória intermédia de profundidade. Isto pode ser uma grande melhoria tanto na redução de energia como no desempenho. No entanto, existem dois resultados negativos possíveis com o formato de profundidade de 16 bits.

Luta de Z

A fidelidade do intervalo de profundidade reduzida torna o z-fighting mais provável de ocorrer com 16 bits do que 24 bits. Para evitar estes artefactos, modifique os planos de recorte próximos/distantes da câmara do Unity para ter em conta a precisão mais baixa. Para aplicações baseadas no HoloLens, um plano de recorte distante de 50 m em vez dos 1000 m predefinidos do Unity pode geralmente eliminar qualquer z-fighting.

Memória Intermédia de Stencil Desativada

Quando o Unity cria uma Textura de Composição com profundidade de 16 bits, não é criada nenhuma memória intermédia de stencil. Selecionar o formato de profundidade de 24 bits, conforme descrito na documentação do Unity, irá criar uma memória intermédia z de 24 bits e uma memória intermédia de stencil de 8 bits (se a versão de 32 bits for aplicável num dispositivo (por exemplo, o HoloLens), o que é geralmente o caso).

Evitar efeitos de ecrã inteiro

As técnicas que operam em ecrã inteiro podem ser dispendiosas, uma vez que a sua ordem de magnitude é de milhões de operações em cada frame. Recomenda-se evitar efeitos pós-processamento, como antialiasing, flor e muito mais.

Definições de iluminação ideais

A Iluminação Global em tempo real no Unity pode fornecer resultados visuais excecionais, mas envolve cálculos de iluminação dispendiosos. Recomendamos que desative a Iluminação Global em tempo real para cada ficheiro de cena do Unityatravés das Definições de Iluminação deComposição> de Janelas> Desmarque > a Iluminação Global em Tempo real.

Além disso, é recomendado desativar todas as conversões de sombras, uma vez que também adicionam passes GPU caros para uma cena do Unity. As sombras podem ser desativadas por luz, mas também podem ser controladas de forma holística através das definições de Qualidade.

Editar>Definições do Projeto e, em seguida, selecione a categoria >Qualidade Selecionar Baixa Qualidade para a Plataforma UWP. Também pode definir a propriedade Sombras como Desativar Sombras.

Recomendamos que utilize iluminação assada com os seus modelos no Unity.

Reduzir a contagem de poli

A contagem de polígonos é reduzida por qualquer um dos

  1. Remover objetos de uma cena
  2. Dizimação de recursos, que reduz o número de polígonos para uma determinada malha
  3. Implementar um Sistema de Nível de Detalhe (LOD) na sua aplicação, que compõe objetos distantes com uma versão de polígono inferior da mesma geometria

Compreender os sombreados no Unity

Uma aproximação fácil para comparar sombreados no desempenho é identificar o número médio de operações que cada uma executa no runtime. Isto pode ser feito facilmente no Unity.

  1. Selecione o elemento de sombreado ou selecione um material e, em seguida, no canto superior direito da janela do inspetor, selecione o ícone de engrenagem seguido de "Selecionar Sombreado"

    Selecionar sombreado no Unity

  2. Com o recurso shader selecionado, selecione o botão "Compilar e mostrar código" na janela inspetor

    Compilar Código shader no Unity

  3. Após a compilação, procure a secção estatísticas nos resultados com o número de operações diferentes para o vértice e o sombreado de píxeis (Nota: os sombreados de píxeis também são frequentemente denominados sombreados de fragmentos)

    Operações de Sombreado Padrão do Unity

Otimizar sombreados de píxeis

Ao observar os resultados da estatística compilada com o método acima, o sombreado de fragmento executa geralmente mais operações do que o sombreado de vértice, em média. O sombreado de fragmento, também conhecido como sombreado de píxeis, é executado por pixel na saída do ecrã, enquanto o sombreado de vértice é executado apenas por vértice de todas as malhas que estão a ser desenhadas para o ecrã.

Assim, não só os sombreados de fragmentos têm mais instruções do que sombreados de vértice devido a todos os cálculos de iluminação, como os sombreados de fragmentos são quase sempre executados num conjunto de dados maior. Por exemplo, se a saída do ecrã for uma imagem de 2k por 2k, o sombreado de fragmento pode ser executado 2000*2 000 = 4000 000 vezes. Se compor dois olhos, este número duplica uma vez que existem dois ecrãs. Se uma aplicação de realidade mista tiver vários passes, efeitos pós-processamento em ecrã inteiro ou compor múltiplas malhas para o mesmo píxel, este número aumentará drasticamente.

Por conseguinte, a redução do número de operações no sombreado de fragmentos pode, geralmente, proporcionar ganhos de desempenho muito maiores em termos de otimizações no sombreado de vértice.

Alternativas de sombreado do Unity Standard

Em vez de utilizar uma composição baseada fisicamente (PBR) ou outro tom de alta qualidade, veja utilizar um tom mais eficaz e mais barato. O Mixed Reality Toolkit fornece o sombreado padrão do MRTK que foi otimizado para projetos de realidade mista.

O Unity também fornece opções de sombreado não iluminadas, iluminadas por vértice, difusas e outras opções simplificadas de sombreado que são mais rápidas em comparação com o sombreado Do Unity Standard. Veja Utilização e Desempenho de Sombreados Incorporados para obter informações mais detalhadas.

Pré-carregamento do Shader

Utilize o pré-carregamento do Shader e outros truques para otimizar o tempo de carregamento do sombreado. Em particular, o pré-carregamento do shader significa que não verá problemas devido à compilação do shader do runtime.

Limite de desmaiado

No Unity, pode-se apresentar a substituição da cena ao alternar o menu do modo de desenho no canto superior esquerdo da vista Cena e ao selecionar Anular a utilização.

Geralmente, a eliminação excedida pode ser mitigada ao abater objetos com antecedência antes de serem enviados para a GPU. O Unity fornece detalhes sobre a implementação do Abate de Oclusão para o seu motor.

Recomendações de memória

A alocação excessiva de memória & operações de desalocação podem ter efeitos adversos na sua aplicação holográfica, resultando num desempenho inconsistente, frames congelados e outro comportamento prejudicial. É especialmente importante compreender as considerações de memória ao desenvolver no Unity, uma vez que a gestão da memória é controlada pelo recoletor de lixo.

Libertação da memória

As aplicações holográficas perderão o tempo de processamento de computação para o recoletor de memória (GC) quando o GC for ativado para analisar objetos que já não estão no âmbito durante a execução e a respetiva memória tem de ser libertada, para que possa ser disponibilizada para reutilização. Geralmente, as alocações constantes e desalocação exigirão que o recoletor de lixo seja executado com mais frequência, o que prejudica o desempenho e a experiência do utilizador.

O Unity forneceu uma excelente página que explica detalhadamente como funciona o recoletor de lixo e sugestões para escrever código mais eficiente no que diz respeito à gestão da memória.

Uma das práticas mais comuns que leva à libertação excessiva da memória não é colocar em cache referências a componentes e classes no desenvolvimento do Unity. Quaisquer referências devem ser capturadas durante Start() ou Awake() e reutilizadas em funções posteriores, como Update() ou LateUpdate().

Outras sugestões rápidas:

  • Utilizar a classe C# StringBuilder para criar dinamicamente cadeias complexas no runtime
  • Remova as chamadas para Debug.Log() quando já não forem necessárias, uma vez que ainda são executadas em todas as versões de compilação de uma aplicação
  • Se a sua aplicação holográfica necessitar geralmente de muita memória, considere chamar System.GC.Collect() durante as fases de carregamento, como quando apresentar um ecrã de carregamento ou transição

Agrupamento de objetos

O agrupamento de objetos é uma técnica popular para reduzir o custo da alocação contínua de objetos e desalocações. Isto é feito ao alocar um grande conjunto de objetos idênticos e reutilizar instâncias inativas e disponíveis deste conjunto em vez de gerar e destruir objetos constantemente ao longo do tempo. Os conjuntos de objetos são ótimos para componentes reutilizáveis com duração variável durante uma aplicação.

Desempenho de arranque

Considere iniciar a sua aplicação com uma cena mais pequena e, em seguida, utilizar SceneManager.LoadSceneAsync para carregar o resto da cena. Isto permite que a sua aplicação obtenha um estado interativo o mais rápido possível. Pode existir um pico de CPU grande enquanto a nova cena está a ser ativada e que qualquer conteúdo composto pode gaguejar ou provocar problemas. Uma forma de contornar esta situação é definir a propriedade AsyncOperation.allowSceneActivation como "falso" no local que está a ser carregado, aguardar que a cena seja carregada, limpar o ecrã para preto e, em seguida, defini-lo novamente como "verdadeiro" para concluir a ativação da cena.

Lembre-se de que, enquanto a cena de arranque estiver a ser carregada, o ecrã inicial holográfico será apresentado ao utilizador.

Ver também