Principais problemas para títulos do Windows
O grupo Relações de Desenvolvedores de Tecnologias de Jogos e Gráficos do Microsoft Windows executa a análise de desempenho para muitos jogos do Windows a cada ano. Durante essas sessões, temos experiência prática para vincular aos comentários e consultas do desenvolvedor que recebemos diariamente. Ocasionalmente, ajudamos a rastrear uma falha misteriosa ou outro problema em um título, o que nos fornece mais informações sobre os problemas que os desenvolvedores estão encontrando.
Este artigo destaca muitos dos problemas comuns que vimos em jogos de pc de geração atual.
- de desempenho doCPU-Limited
- de gerenciamento de lote insatisfatório
- de cópia excessiva de memória
- uso excessivo de de envio de desenho dinâmico
- alta sobrecarga no de processamento de arquivos
- de instalação lenta e frustrante
- falta de consideração da memória física
- Over-Reliance na conversão de taxa de amostra de áudio Real-Time
- fragmentação de de memória virtual
- manipulação do do Word de controle de Floating-Point
- instalação opcional do de Runtime do DirectX
- uso excessivo de de sincronização de thread
- uso de RDTSC
Desempenho do CPU-Limited
A grande maioria dos jogos é limitada pelo desempenho da CPU em sistemas com GPUs (unidades de processamento de gráficos) de alto desempenho. Às vezes, isso ocorre devido ao mau uso do envio em lote para envios de desenho, mas, mais normalmente, isso ocorre devido a outros sistemas de jogos que consomem uma grande parte dos ciclos de CPU disponíveis. Nos poucos casos em que vimos a GPU como a limitação, a causa são taxas de preenchimento muito altas ou demanda de sombreador de pixel, em configurações de alta resolução ou baixo desempenho de sombreador de vértice por uma placa de vídeo.
Como a maioria dos títulos é limitada por CPU, as maiores vitórias de desempenho vêm da otimização de sistemas de jogos com uso intensivo de CPU. Normalmente, os sistemas de IA ou física e a lógica de detecção de colisão associada são os principais consumidores de ciclos de CPU em aplicativos Microsoft Direct3D bem comportados. Qualquer trabalho para melhorar esses sistemas pode melhorar o desempenho geral do jogo.
Gerenciamento de lote ruim
Alcançar um bom paralelismo com a GPU requer que os lotes de desenho contenham geometria suficiente – e os sombreadores têm a complexidade certa – para manter a placa de vídeo ocupada, enquanto não usam tantos lotes que o buffer de comando está inundado. No hardware de geração atual, recomendamos aproximadamente 300 ou menos envios em lotes por quadro (menos em CPUs de menor desempenho) para impedir que o processamento do buffer de comando do driver se torne um gargalo de desempenho. Algumas outras chamadas de estado de API e combinações de driver podem resultar em processamento de CPU caro (compilação de driver de sombreadores, por exemplo), portanto, recomendamos a análise de desempenho de rotina.
Cópia excessiva de memória
Durante o desenvolvimento da maioria dos títulos de pc, os desenvolvedores usam estruturas de dados convenientes e cadeias de caracteres para gerenciamento de conteúdo. O trabalho de CPU necessário para comparação de cadeia de caracteres, cópia e outras manipulações geralmente tem uma sobrecarga mensurável, especialmente ao levar em conta os acertos de desempenho associados ao subsistema de cache e memória. Planos devem ser feitos ao desenvolver esses sistemas para remover ou minimizar a dependência do processamento de cadeia de caracteres quando o produto entra nas fases primárias de teste e lançamento.
Uso excessivo de envio de desenho dinâmico
O hardware de vídeo moderno tem um bom desempenho ao lidar com dados estáticos. Adaptadores high-end geralmente têm uma grande quantidade de memória de vídeo, mas essa memória não pode ser efetivamente utilizada por dados dinâmicos.
Embora padrões de uso razoavelmente eficientes de buffers de vértice dinâmicos/buffers de índice possam ser implementados para conteúdo dinâmico, muitos títulos superutilizam esse idioma para o que de outra forma é conteúdo estático. Geralmente, vemos isso com árvores de particionamento de espaço binário (BSP) e sistemas baseados em portal que armazenam geometria em uma estrutura de dados que não é mapeada para o hardware e deve ser processada em buffers para cada quadro. Colocar o máximo de conteúdo possível em recursos estáticos pode reduzir consideravelmente a sobrecarga de largura de banda da transferência de dados para a placa de vídeo, fazer melhor uso da VRAM a bordo e reduzir a sobrecarga de CPU/cache envolvida no processamento desse conteúdo.
Alta sobrecarga no processamento de arquivos
Os jogos de computador têm uma reputação por longos tempos de carregamento, especialmente quando comparados com títulos de console com requisitos estritos de tempo de carregamento. Nossa análise da maneira como muitos títulos usam o subsistema de arquivos revela alguns problemas comuns.
A sobrecarga de abrir um arquivo geralmente é muito maior do que os desenvolvedores esperam. Com scanners de vírus sob demanda em uso generalizado e a funcionalidade adicional do NTFS, abrir um arquivo é uma operação bastante cara. Abrir muitos arquivos ao mesmo tempo ou abrir e fechar o mesmo arquivo repetidamente é, portanto, um método ruim de lidar com o gerenciamento de arquivos. Alguns jogos tentaram reduzir esse custo de desempenho testando a existência de um arquivo antes de abri-lo. A realidade é que o teste para a existência de um arquivo no NTFS requer a abertura do arquivo, portanto, o teste antes de abrir resulta no pagamento do custo duas vezes.
Jogos que permitem modificações de complemento, ou mods, ou que ainda incluem scaffolding de desenvolvimento para verificar se há arquivos de dados de substituição, podem ter atrasos significativos no carregamento do jogo devido à verificação desses arquivos, mesmo quando esses arquivos não estão presentes. Recomendamos que os jogos verifiquem apenas esses arquivos quando executados com um comutador de linha de comando especial ou outro indicador de modo, para que apenas os usuários que fazem uso dessa funcionalidade realmente paguem o custo de desempenho dessas verificações (geralmente extensas).
O desempenho adicional pode ser obtido do sistema de arquivos pelo seguinte:
- Uso apropriado das dicas do sistema de arquivos FILE_FLAG_RANDOM_ACCESS e FILE_FLAG_SEQUENTIAL_SCAN
- Dimensionamento de buffers para evitar uma grande quantidade de chamadas para as APIs de leitura/gravação do sistema operacional
- Acessando arquivos de forma assíncrona
- Carregando threads em segundo plano
Também é altamente recomendável converter dados offline (em tempo de build ou instalação) em vez de depender da conversão quando o jogo for executado pela primeira vez, pois isso impõe um imposto de desempenho significativo para cada usuário.
Instalação lenta e frustrante
Outro problema comum que vimos são tempos de instalação muito longos necessários para muitos jogos de pc modernos. Os instaladores solicitam ao usuário muitas vezes, às vezes simplesmente para informar ao usuário, por exemplo, "Você não precisa do DirectX instalado". Geralmente, esses instaladores ofensivos exigem que o usuário selecione Próximo ou OK muitas vezes antes da instalação do jogo realmente começar. Depois de começar, vimos alguns títulos levarem uma hora ou mais antes que o usuário tenha a oportunidade de jogar o jogo. Sentimos fortemente que a primeira hora de experiência de jogo não deve ser a instalação.
Recomendamos várias abordagens para lidar com a instalação. Primeiro, mantenha os prompts simples e no mínimo. Em segundo lugar, arquitete os dados do jogo de modo que alguns ou todos os arquivos de dados possam ser usados diretamente do disco de distribuição sempre que possível – unidades de DVD modernas têm largura de banda muito alta. Em terceiro lugar, considere implementar a instalação sob demanda em seus títulos para reduzir ou eliminar o processo de instalação e permitir que os usuários entrem no jogo o mais rápido possível. (Para obter mais informações sobre a instalação sob demanda, consulte Install-on-Demand for Games.)
Para obter mais recomendações sobre a instalação do jogo, consulte Simplificando a instalação do jogo.
Falta de consideração da memória física
Devido à grande variabilidade do hardware de pc no mercado, os títulos normalmente usam testes de configuração ad hoc para selecionar as configurações padrão para o nível de detalhes gráficos. Alguns dos títulos que vimos estão usando o tamanho da memória de vídeo nesses testes, mas não conseguindo correlacionar isso com o tamanho da memória física. Para lidar com situações de dispositivo perdido, a maior parte da memória de vídeo (VRAM local no cartão e a abertura de memória AGP não local) deve ser apoiada pela memória física, seja por meio do uso de recursos gerenciados ou estruturas de dados personalizadas. Algumas placas de vídeo high-end têm tamanhos de VRAM que rivalizam com o tamanho das memórias de CPU de baixo nível. Em situações em que o sistema tem memória física limitada em comparação com a placa de vídeo, a maior parte dessa VRAM não pode ser utilizada efetivamente e as configurações de detalhes inferiores devem ser configuradas.
Over-Reliance na conversão de taxa de amostra de áudio Real-Time
Outra fonte comum de queima de ciclo de CPU que vimos ocorre quando o sistema de áudio é necessário para converter a taxa de reprodução durante a combinação no buffer de hardware. Com drivers WDM (Modelo de Driver do Windows), o formato de buffer de hardware não está sob controle de aplicativo direto, porque é um recurso no nível do kernel; Em vez disso, o formato é selecionado com base no formato de alta qualidade de todas as fontes e nas funcionalidades do hardware. Por padrão, o Windows XP usa uma conversão de taxa de exemplo de alta qualidade para esse processo e, se a maioria dos exemplos de áudio exigir uma conversão de taxa, uma parte significativa dos ciclos de CPU será consumida.
É recomendável criar todos os buffers do DirectSound com a mesma taxa de exemplo. Se você usar as funções de do Microsoft Win32 waveOut, deverá usar uma taxa de exemplo consistente com elas também. Com drivers WDM, todos os buffers serão misturados pelo kernel e, se você usar uma taxa de amostragem mais alta em alguns deles, as taxas de exemplo de todo o restante serão convertidas para corresponder. Observe que isso implica usar a mesma taxa de reprodução para todos os seus exemplos de áudio, incluindo quaisquer buffers de descompactação de áudio de streaming. Definir a taxa de buffer primária não tem efeito, a menos que você esteja direcionando o Windows 98 ou o Windows Millennium Edition.
Nota
No Windows Vista e versões posteriores do sistema operacional, o DirectSound e o waveOut usam o da API de Sessão de Áudio do Windows (WASAPI) para todas as saídas de áudio.
Fragmentação da memória virtual
Vimos uma série de problemas recentes relacionados ao limite de 32 bits no espaço de memória do processo. Embora 2 GB de espaço de endereço virtual para processos de modo de usuário tenha sido mais do que adequado historicamente, o aumento do uso de arquivos grandes mapeados em memória, alocadores de memória personalizados e o aumento do tamanho da VRAM (que deve ser mapeado para o espaço do processo) começaram a causar situações em que as alocações de espaço de memória virtual estão falhando. Algumas DLLs não Microsoft estão usando locais de início fixo no meio do espaço de endereço virtual, o que está causando fragmentação que resulta em alocações com falha.
Esses problemas geralmente aparecem quando o jogo usa um esquema de alocação de memória personalizado que tenta alocar uma grande parte contínua do espaço de memória virtual. Nossa recomendação é gravar alocadores de modo que eles solicitem partes mais razoavelmente dimensionadas do espaço de endereço virtual, conforme necessário. Por exemplo, solicitando 64 ou 256 MB por vez, mas não 1 GB. No entanto, deve-se tomar cuidado para não causar mais fragmentação. O advento de sistemas operacionais e hardware de 64 bits ajudará muito esses problemas no futuro, mas é necessário ter cuidado com os sistemas de 32 bits de geração atual.
Manipulação do Word de controle de Floating-Point
Como um auxílio de depuração, alguns desenvolvedores têm habilitado exceções na FPU (unidade de ponto flutuante) por meio de manipulações da palavra de controle de ponto flutuante. Fazer isso é altamente problemático e provavelmente fará com que o processo falhe. Assim como a convenção de chamada exige que o registro ebx seja preservado, a maioria do sistema pressupõe que a FPU está em um estado padrão, dará resultados razoáveis e não gerará exceções. Drivers e outros componentes do sistema geralmente computam os resultados com base na suposição de que os valores de erro padrão aparecerão nos registros para condições incorretas, mas se as exceções estiverem habilitadas, eles ficarão sem tratamento e resultarão em falhas.
O Direct3D definirá a unidade de ponto flutuante como de precisão única, arredondada para mais próxima como parte da inicialização do thread de chamada, a menos que o sinalizador de D3DCREATE_FPU_PRESERVE seja usado, nesse caso, a palavra de controle de ponto flutuante não é intocada. Como a palavra de controle é uma configuração por thread, garantir que todos os threads de aplicativo sejam definidos como modo de precisão única pode otimizar o desempenho. Lembre-se de que chamar _control87 não é válido para codificação nativa x64, que usa SSE exclusivamente e é extremamente caro na arquitetura baseada em PowerPC da CPU do Xbox 360.
Nota
Se você modificar a palavra de controle, use _controlfp_s e esteja ciente de que para plataformas x64 você não pode alterar a precisão do ponto flutuante por meio da palavra de controle.
Em qualquer biblioteca em que precisamos ter regras de arredondamento diferentes ou outro comportamento , como lidar com sombreadores de vértice de software ou compilação, salvamos e restauramos a palavra de controle. Se um jogo precisar usar exceções de ARREDONDAMENTO ou FPU não padrão, ele deverá salvar e restaurar a palavra de controle de ponto flutuante e você deve ter certeza de que ele não chama nenhum código externo que não tenha sido provado estar seguro contra esses problemas, incluindo APIs do sistema.
Instalação opcional do Runtime do DirectX
Vários jogos perguntam ao usuário se o DirectX deve ser instalado. Isso poderá causar problemas se o usuário assumir que o sistema tem a mais recente redistribuível do DirectX instalada e ignorar a instalação e, posteriormente, a instalação continuar com êxito. Se o jogo exigir uma versão específica do D3DX ou outra funcionalidade atualizada que não foi instalada, o jogo não funcionará e o usuário ficará muito frustrado.
É altamente recomendável que o instalador do jogo instale silenciosamente o redistribuível DirectX no qual o jogo foi criado. O processo de instalação do DirectX foi projetado para que ele verifique se algo precisa ser atualizado e retorna rapidamente se não o fizer. Portanto, não é necessário perguntar ao usuário sobre como instalar o DirectX.
Uma instalação silenciosa do DirectX pode ser feita executando este comando do pacote de instalação: dxsetup.exe /silent
Além disso, o tamanho real da pasta redistribuível pode ser configurado para incluir apenas os arquivos de gabinete (.cab) que são realmente necessários para o uso e as plataformas de destino do jogo.
Uso excessivo de sincronização de thread
Ao criar perfis de jogos, os principais hotspots geralmente são encontrados relacionados à inserção e à saída de seções críticas. Com a prevalência de CPUs de vários núcleos, o uso de multithreading em jogos aumentou drasticamente e muitas implementações dependem do uso intenso da sincronização de threads. O tempo de CPU para fazer uma seção crítica mesmo sem qualquer contenção é bastante significativo e todas as outras formas de sincronização de thread são ainda mais caras. Portanto, deve-se tomar cuidado para minimizar o uso desses primitivos.
Uma fonte comum de sincronização excessiva em jogos é o uso de D3DCREATE_MULTITHREADED. Esse sinalizador, ao tornar o thread direct3D seguro para renderização de vários threads, adota uma abordagem muito conservadora, resultando em alta sobrecarga de sincronização. Os jogos devem evitar esse sinalizador. Em vez disso, arquitete o mecanismo para que toda a comunicação com o Direct3D seja de um único thread e qualquer comunicação entre threads seja tratada diretamente. Para obter mais informações sobre como criar jogos com vários threads, consulte o artigo Codificação para vários núcleos no Xbox 360 e no Microsoft Windows.
Uso de RDTSC
Não é recomendável usar a instrução x86 RDTSC. RDTSC falha ao calcular corretamente o tempo em alguns esquemas de gerenciamento de energia que alteram a frequência da CPU dinamicamente e em muitas CPUs de vários núcleos para as quais o contador de ciclo não é sincronizado entre núcleos. Em vez disso, os jogos devem usar a APIQueryPerformanceCounter. Para obter mais informações sobre problemas com RDTSC e implementando o tempo de alta resolução com queryPerformanceCounter, consulte o artigo Tempo de Jogo e Processadores Multicore.