Perguntas e respostas estendidas: Otimizando a memória e o tamanho da compilação com Addressables

PATRICK DEVARNEY / UNITY TECHNOLOGIESContributor
Apr 28, 2023|17 Min
Perguntas e respostas estendidas: Otimizando a memória e o tamanho da compilação com Addressables
Esta página da Web foi automaticamente traduzida para sua conveniência. Não podemos garantir a precisão ou a confiabilidade do conteúdo traduzido. Se tiver dúvidas sobre a precisão do conteúdo traduzido, consulte a versão oficial em inglês da página da Web.

Em fevereiro, como parte da minha função como consultor sênior de desenvolvimento de software na Unity Accelerate Solutions, liderei um webinar técnico sobre o Addressables Asset System. Durante a sessão ao vivo, demonstrei várias ferramentas de criação de perfil que você pode usar para otimizar a memória de tempo de execução e o tamanho da compilação de um projeto. O webinar terminou com uma sessão de perguntas e respostas, e nossa equipe recebeu mais perguntas do que tivemos tempo de responder.

A seguir, uma extensão da sessão de perguntas e respostas de encerramento, para que possamos responder mais perguntas.

P: O sistema Addressables é necessário para jogos leves – como jogos casuais, de arcade ou de quebra-cabeça – se eu não tiver problemas de memória?
A: Talvez não, mas é bom ter em mente que o sistema Addressables não melhora apenas o desempenho da memória. Ter a capacidade de escolher quando carregar o conteúdo pode melhorar os tempos de carregamento. Criar conteúdo no Addressables permite que você tenha compilações iterativas que não demoram tanto. Por exemplo, se você fizer uma pequena alteração no script, talvez não precise reconstruir todos os seus pacotes.

P: Os ativos carregados são liberados quando a cena muda?
A: Potencialmente. Ativos carregados de Addressables que estão prontos para serem liberados porque têm uma contagem de referência zero podem ser descarregados da memória durante uma transição de cena. Ao fazer a transição de cenas de forma não aditiva, chame Resources.UnloadUnusedAssets(). Isso consome muita CPU, mas permite que você descarregue parcialmente os AssetBundles.

P: O pool de objetos e os Addressables funcionam bem juntos?
A: Sim. Você pode carregar seu objeto uma vez do Addressables e então instanciar várias cópias dele para criar seu pool. Quando terminar de usar o pool, destrua todos os objetos e libere o AsyncOperationHandle que foi usado para carregar o ativo.

P: Os grupos e pacotes são carregados na memória de uma só vez?
A: Grupos endereçáveis são um conceito exclusivo do Editor. Em tempo de execução, você lida apenas com pacotes. Os pacotes são carregados na memória somente quando são necessários e somente o conteúdo desejado é carregado.

Exemplo: Você tem um pacote com 10 personagens. Você pede ao Addressables para carregar três caracteres. Os metadados do pacote e os três personagens serão carregados.

P: Se eu quiser liberar um ativo, preciso manter o AsyncOperationHandle ou o AssetReference?
A: Recomendamos manter o identificador e utilizá-lo, pois você é responsável por liberar o conteúdo quando terminar de usá-lo.

Por exemplo, os membros da nossa equipe geralmente seguem o caminho do handle para evitar chamar Instantiate/Release diretamente no AssetReference.

P: Quais são as desvantagens de muitos pacotes pequenos?
A: Esta documentação lista diversas desvantagens de muitos pacotes.

P: Quando um ativo em um pacote é necessário, qual a sobrecarga dos outros ativos no mesmo pacote? Se for um pacote remoto, ele deve ser baixado, mas não há realmente nenhuma sobrecarga de memória devido a ativos não utilizados no pacote?
A: Correto, um pacote remoto será totalmente baixado antes que você possa usá-lo.

Ativos descarregados em um pacote de ativos carregados têm sobrecarga mínima em tempo de execução. Sempre que você carrega ativos de um pacote, você precisa carregar os metadados do pacote. Parte desses metadados inclui um índice que lista todos os ativos no pacote. Mais ativos em um pacote equivalem a metadados maiores.

Você pode visualizar essa sobrecarga de memória fazendo uma captura com o Unity Memory Profiler. Na aba “All Of Memory”, há uma lista de todos os objetos “SerializedFile” na memória, um para cada pacote. Esses objetos são os metadados dos seus pacotes.

Saiba mais sobre esses metadados em nossa documentação.

P: Ao trabalhar em um ambiente de mundo aberto, quais estratégias de agrupamento posso usar para descarregar ativos individuais sem descarregar metade de um pacote e depender de Resources.UnloadUnusedAssets() para limpá-lo, sem a sobrecarga de ter cada ativo em seu próprio pacote?
A: O principal a lembrar é que o conteúdo deve ser agrupado se você pretende descarregá-lo ao mesmo tempo. Se o seu mundo de jogo tiver conteúdo “estático”, como árvores e pedras para um determinado bioma que não serão movidas pelo jogador, esse conteúdo deve ser agrupado. Qualquer conteúdo “dinâmico”, como itens que o jogador pode pegar, deve ser agrupado separadamente.

Esta postagem do blog e o repositório GitHub vinculado abordam a divisão de pacotes para um jogo de mundo aberto. Ele também apresenta uma maneira de desduplicar pacotes para reduzir a sobrecarga de memória de cada pacote. Os estágios 4 e 5 são particularmente relevantes para mundos abertos.

P: Quando devo deixar “AssetBundle CRC” habilitado?
A: A prática recomendada é habilitar isso, excluindo AssetBundles armazenados em cache para grupos remotos e desabilitando para grupos locais. A verificação serve apenas para garantir que os dados não foram corrompidos durante o download. Quase não há razão para fazer a verificação de AssetBundles locais.

P: Quando não vale a pena usar Addressables devido a problemas de desempenho da CPU ao carregar e descarregar ativos?
A: O sistema Addressables tem um impacto positivo no desempenho de carregamento da CPU, pois não precisa carregar todo o conteúdo antecipadamente.

Se você não usar Addressables ao carregar uma cena, terá que carregar todo o conteúdo e referências. Se você mover o conteúdo para Addressables, poderá escolher quando carregar qual conteúdo.

Por exemplo, digamos que você tenha um Gerente de Inventário em uma cena que tem uma referência a 1.000 itens de inventário. Se você não usar Addressables, terá que carregar cada malha, textura, clipe de áudio, etc., para todos esses itens de inventário. Se você esperar para carregar este conteúdo, o carregamento da cena será mais rápido.

P: Todas as dependências de um ativo endereçável também precisam ser endereçáveis ou isso só é necessário se forem compartilhadas?
A: Dependências não precisam ser marcadas como endereçáveis. As dependências serão inseridas no Addressables durante o processo de compilação, se necessário.

Por exemplo, se você fizer com que um player seja pré-fabricado como endereçável, não precisará marcar manualmente a malha, as texturas ou o áudio do player como endereçáveis também. Quando o pacote for criado, todas as dependências que ainda não existem no Addressables serão automaticamente incluídas no pacote prefabricado do player.

P: Se eu esquecer de liberar um recurso e mudar as cenas, o que acontece com esse recurso?
A: A troca de cenas não interage inerentemente mal com os controles. Mas se você carregar um ativo e esquecer de liberar seu identificador, o ativo persistirá na memória.

O Addressables tem um sistema interno de contagem de referências. Os identificadores são a forma como interagimos com este sistema. Carregar um ativo incrementa a contagem de referência, e liberar diminui a contagem de referência.

Os criadores são responsáveis por manter essa contagem de referências atualizada. O ativo permanecerá na memória enquanto a contagem de referências for maior que um.

P: Relacionado ao exemplo do webinar, suponha que eu esteja criando um jogo de mundo aberto. O chefe está presente em algum lugar no mundo aberto. Quando o jogador vai até o chefe, como uso os Endereçáveis aqui? Devo enviar o comando para carregar a espada de forma assíncrona, por meio de um gatilho, a uma certa distância do inimigo ou algo mais?
A: Pode ser uma linha tênue escolher quando carregar e descarregar conteúdo. Você quer ter certeza de que o chefe estará pronto quando o jogador precisar vê-lo, mas talvez não queira carregá-lo muito cedo, quando o jogador ainda consegue se virar e evitar o chefe.

O lado bom é que você pode iterar quando carregar e descarregar o conteúdo – não é preciso otimizá-lo perfeitamente na primeira tentativa.

Para começar, sugerimos carregar todo o conteúdo de uma “zona” específica quando o jogador se aproxima (por exemplo, o jogador se aproxima de uma entrada de masmorra, o que faz com que tudo dentro da masmorra seja carregado). Se isso causar pressão desnecessária na memória, você pode adicionar carregamento e descarregamento mais detalhados.

Se a espada não estiver carregando rápido o suficiente, considere mover o gatilho de carregamento para começar mais cedo, melhorando o tempo de carregamento dos ativos da espada usando o módulo de CPU do Unity Profiler para ver o que está sendo carregado ou usando Addressables de forma síncrona para garantir que o carregamento seja concluído.

Esta documentação inclui mais detalhes e um trecho de código para Addressables síncronos.

P: Se eu carregar um item endereçável quando uma cena começar, preciso ter uma tela de carregamento para ele?
A: O carregamento do Addressables normalmente é feito de forma assíncrona, como com Addressables.LoadAssetAsync().

Pode haver algum conteúdo que você não deseja carregar antes de sair da tela de carregamento. Você pode coletar esses AsyncOperationHandles e esperar que os necessários sejam concluídos antes de sair.

P: Qual é a pegada de memória dos metadados endereçáveis em tempo de execução (antes de carregar qualquer um dos seus dados)?
A: Durante a inicialização do Addressables, o arquivo de catálogo é carregado para que o Addressables saiba como mapear rótulos e endereços para ativos no disco ou em locais remotos. Um catálogo maior equivale a uma maior sobrecarga de memória em tempo de execução.

O tamanho do catálogo pode ser reduzido eliminando dados desnecessários, como não incluir rótulos ou GUIDs em grupos que não precisam deles, ou reduzindo o tamanho dos dados existentes. Por exemplo, definindo o Modo de Nomenclatura de Ativos Internos de um grupo como GUID em vez de nome de arquivo ou caminho completo (que pode ser mais longo). Você pode visualizar o tamanho da memória de tempo de execução do catálogo no Unity Memory Profiler.

P: O que o Unity Editor faz no tempo que gasta criando Addressables?
A: Um log de relatório de compilação é gerado na pasta /Library. Este log mostra cada etapa do processo de construção. Para adicionar detalhes adicionais ao log, siga este caminho para selecionar “Usar log de compilação detalhado”: Habilitando Editar > Preferências > Pipeline de compilação programável > Usar log de compilação detalhado.

Confira os visuais e a documentação sobre como visualizar o log.

P: Resources.Load() também tem um problema de duplicação?
A: Sim. Pode ser útil pensar no conteúdo dos Addressables e no conteúdo dos Resources como “mundos” diferentes. Se você tiver uma textura em /Resources, uma cópia dessa textura será incluída no arquivo Resources. Se os pacotes em Addressables dependerem dessa textura, cada pacote incluirá uma cópia implícita dela. Você acaba com várias cópias da textura no disco e potencialmente várias cópias na memória.

Para evitar essa duplicação, mova a textura para fora de /Resources e adicione-a a um grupo Addressables.

P: Você tem problemas semelhantes de tamanho em disco que são resolvidos removendo pacotes duplicados quando você não usa o Addressables?
A: Sim. No webinar e nos slides, mostramos como a desduplicação das duas cenas de corrida aquática reduziu significativamente o tamanho da construção.

P: Como posso evitar duplicatas de variantes de shader?
A: Os shaders podem ser desduplicados no mesmo processo que qualquer outro ativo – declare-os explicitamente em um grupo.

Se um ativo for declarado explicitamente em um grupo de Endereçáveis que será incluído na sua compilação, esse ativo não será duplicado em vários pacotes.

Especificamente para shaders, é prática comum que projetos usem um grupo “Shaders compartilhados” para conter shaders que você espera precisar na memória durante a vida útil do seu aplicativo e que são compartilhados entre muitos ativos.

P: Duas cenas do Unity que compartilham o mesmo prefab duplicam o tamanho da compilação?
A: Isso depende se o prefab do qual as cenas dependem foi explicitamente incluído em Addressables e se as cenas estão no mesmo pacote ou em pacotes diferentes.

Veja a explicação visual de como a duplicação ocorre nos slides do webinar e nesta postagem do blog no Estágio 4.

O importante é lembrar que todo conteúdo incluído em um pacote precisa ter acesso a todas as suas dependências. Se você colocar uma cena em um pacote, todas as suas dependências precisam ser:

  • Incluído explicitamente em algum lugar em Addressables
  • Incluído implicitamente no mesmo pacote

P: É possível comparar duplicatas em determinados grupos para evitar que todos os recursos do jogo sejam agrupados em um grupo isolado?
A: Sim. Você pode executar a regra de desduplicação integrada e então classificar os ativos na janela Grupos de Endereçáveis em agrupamentos melhores.

Ou uma abordagem mais escalável é escrever suas próprias Addressables AnalyzeRules, que aparecerão na janela Analyze. As regras integradas são entregues como C# no pacote Addressables e podem servir como uma linha de base.

Por exemplo, você pode querer encontrar todas as duplicatas em todos os seus grupos que começam com “Character-”. Quaisquer duplicatas implícitas podem ser colocadas em um grupo “Shared-Character”.

P: Você vai abordar compilações remotas e caminhos locais?
A: Não abordamos caminhos remotos e locais, que são chamados de “Perfis Endereçáveis” no webinar. No entanto, descrevemos o que são Perfis Endereçáveis e como usá-los nesta documentação.

P: Como o Addressables funciona com o Cloud Content Delivery (CCD)?
A: A integração do CCD é discutida nesta documentação.

P: Você pode dar dicas sobre as melhores práticas para implementar variações de Addressables de baixa e alta resolução?
A: Você pode encontrar um exemplo no Addressables Sample no GitHub.

P: E se o conteúdo do pacote estiver criptografado? O UnityDataTool também descriptografa o conteúdo?
A: Não. Os dados precisarão ser descriptografados antes que o UnityDataTool possa analisar o conteúdo.

P: É um caso de uso compatível criar pacotes de um projeto Unity e carregá-los em tempo de execução de um aplicativo criado de um projeto diferente?
A: Sim. Isso é resolvido usando vários catálogos ao mesmo tempo.

P: Existem desvantagens em usar InstantiateAsync ou situações em que é melhor usar LoadAsync + Instantiate manual?
A: É recomendável usar Addressables.LoadAssetAsync() e chamar Object.Instantiate(). Addressables.InstantiateAsync() tem um custo de desempenho maior.

P: Tenho muitos ScriptableObjects com pelo menos 1–2 sprites referenciados como variáveis. Se eu quiser mudar os sprites para Addressables, tenho que mudar as referências para Addressables uma por uma, ou existe algum truque para fazer isso?
A: Um script do Editor é provavelmente a melhor maneira de converter essas referências.

Você pode adicionar os campos AssetReference ao seu ScriptableObject (e manter temporariamente os campos Sprite). Em seguida, você pode escrever um script do Editor que itera pelos seus ScriptableObjects, procura o ativo Sprite em Addressables para encontrar o AddressableAssetEntry associado e armazena o endereço ou cria uma AssetReference para ser armazenada no ScriptableObject.

Por fim, você pode remover as referências diretas do Sprite e trocar qualquer código relacionado para usar o AssetReference.

P: Posso usar endereçáveis para jogos WebGL? Se sim, há algo específico a ser observado?
A: Sim, e sim. Duas coisas a serem observadas: Primeiro, o WebGL não suporta threading, então não use Tasks. Segundo, o cache funciona de forma diferente no WebGL – já vimos problemas com o cache de AssetBundles remotos antes.

P: Se eu usar Shader.Find(“ShaderName”), isso vem da compilação ou dos Addressables?
A: Elas vêm da compilação do Unity Player, não do Addressables. Shader.Find() não retorna resultados de AssetBundles.

P: Como posso organizar a janela Grupos Endereçáveis quando tenho muitos grupos com nomes semelhantes?
A: Para organizar a interface do usuário dos grupos de endereçamento, você pode habilitar a Hierarquia de grupos com traços. Isso agrupará grupos com nomes semelhantes. Por exemplo, “Personagem-pessoa” e “Personagem-pessoa2” aparecerão na IU sob o agrupamento “Personagem”.

Isso não afeta como os pacotes são criados. Esta é apenas uma mudança organizacional na interface do usuário.

Compartilhe seu feedback conosco no fórum Addressables . Não deixe de ficar atento aos novos blogs técnicos de outros desenvolvedores do Unity como parte do processo contínuo Tecnologia da série Trenches.