Desenvolvimento de JogosDesign 6 Game EngineEstratégiaIsométricosLabirintosPlatformersProgramaçãoProjectosShoot 'Em UpShooter HorizontalTopTop 10 Game EngineTop Game EngineUm Dia - 3DV-ScrollingVários SimuladoresVideo Jogos

2025-01-20 – Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo…

Pois é, reduzi o uso da memória do meu Game Engine em perto de 90%, e ainda consegui manter ou aumentar ligeiramente a velocidade do mesmo (mesmos FPS), sem contar que preparei o meu Game Engine também para uso em Multi-Threading, sendo que esta operação levou semanas de trabalho árduo que afectou centenas de ficheiros de código.

Como? Explico abaixo.

Mas quanto ao baixar 90% de memória e subir até a velocidade, é espectacular pois por norma acabamos por gastar mais memória se queremos ganhar velocidade, ou perder velocidade se queremos baixar o uso da memória, e quando dei formação cheguei a usar como exemplo um caso em que poderia usar um int em vez de bool, para aumentar a velocidade de um programa, mas aumentando o consumo de memória, ou usar bool para reduzir o mesmo mas baixando a velocidade.

Como fiz isto? Vou dar uma dica; espreitem as linhas de código no meu CodeBlocks:

2025-01-20 – Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo…

Acima podem ver que eu não uso um std::unordered_map mas sim um “ThreadSafeUnorderedMap”, bem como substituí um std::vector por um “ThreadSafeVector”, e até podem ver no exemplo acima um “ThreadSafeBidimensionalArray” ao invés de usar arrays normais do C++.

O que fiz foi simples: Entre várias outras coisas (claro), eu substituí containers de C++, por containers feitos por mim, mais leves, customizados ao meu gosto, Thread-Safe, mais rápidos, etc, e esses containers começados por “ThreadSafe” que uso acima, não os encontrarão no C++ pois fui eu que os criei de acordo com as necessidades do meu engine.

Pois se queremos criar um Game Engine do zero, não podemos usar containers normais fornecidos pelo C++.

Eu usava em grande parte porque o meu objectivo nos últimos anos não era optimizar ao máximo o Game Engine, mas sim o suficiente (daí em escrever muito “optimizado qb”), porque queria estar focado na diversão de criar o engine, ver coisas a funcionar, trabalhar com Matemática e Física, como balançar numa corda, etc.

Mas mais cedo ou mais tarde eu tinha de começar a tratar desta parte do engine, mais low-level, de tirar mais proveito do hardware.

Vejam como usava perto de 2 GB para funcionar há uns meses:

2024-10-05 - Vista das TaskRunners no meu Sistema de Gestão de Threads finalizado no meu Game Engine em C++...
2024-10-05 – Vista das TaskRunners no meu Sistema de Gestão de Threads finalizado no meu Game Engine em C++…

E vejam como usa 90% menos agora:

2025-01-20 - Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo...
2025-01-20 – Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo…

Ele arranca com apenas 120 MB, o que seria uma redução de 95%, mas na realidade é 90% porque à medida que vou usando recursos do jogo, ele sobe para os 200 MB.

Não criei apenas os meus próprios containers, mas também os meus próprios algoritmos, iteradores, funções variadas, etc, para substituir os correspondentes da STL do C++.

Por estranho que pareça, até simplifiquei o código em algumas coisas, vejamos um exemplo abaixo:

Velho: this->gameObjects.erase(std::remove(this->gameObjects.begin(),this->gameObjects.end(),obj),this->gameObjects.end());

Novo: this->gameObjects.erase(obj);

Mais legível, certo?

Claro que isto dito assim, parece um paraíso, parece facílimo, parece tudo de bom.

Mas deu muito trabalho, tive de alterar centenas de ficheiros, vezes e vezes em repetido, pois por cada container alterado, que me forçou a alterar centenas de ficheiros, tive de voltar a alterar mais umas dezenas ou centenas com outro container, etc, e isto semanas a fio.

No vídeo acima conseguem ver vários executáveis de teste, para verem que por vezes aumentava a velocidade (chegando aos 550 FPS num executável acima), depois tentava cortar o uso da memória, até achar um ponto ideal em que consumia menos 90% de memória mas mantendo ou até ganhando ligeiramente alguma velocidade.

Este engine estaria pronto para rodar como está em máquinas de 2010, e até em máquinas de 2005 se reduzir um pouco os FPS talvez ao invés dos 60 FPS actuais, e talvez resolução (pois uso 1280×1024 agora).

Em máquinas de 2000 também poderia funcionar, mas aí forçar-me-ia a adaptações mais radicais pois os CPUs eram muito mais fracos.

Mas lembrem-se de que me estou a dar ao luxo de puxar muito pela máquina, senão vejamos:

  • A máquina é um portátil HP dos de 500/600€ comprado em 2021, com um I5 que só tem 8 threads e talvez 4 ou 6 cores (fiz um upgrade de 8 GB para 24 GB porque dá jeito na compilação dos resources do jogo);
  • Estes resultados têm em conta que uso 1280×1024 de resolução desenhada no ecrã;
  • Ainda por cima em RGBA de 16 milhões de cores, ou seja, cada píxel são 4 bytes, ou seja, 5 MB por cada ecrã desenhado, e com transparências de várias layers, etc;
  • Um nível de testes com 200 tiles de largura (32 píxeis por 32 píxeis cada), e 95 de altura, ou seja, 19.000 tiles, em que cada tile tem 32*32 píxeis, dando 19 MB só da parte gráfica do background do nível;
  • O nível de testes tem mais de 100 inimigos no ecrã;
  • Além de umas 5000 gotas de chuva (objectos);
  • E milhares de bolhas de ar na água, entre muitas outras coisas no ecrã;
  • Tudo, inclusivé os inimigos, desenhados em real-time, mesmo fora do ecrã visível (daí poderem ver no mapa do jogo os inimigos fora do ecrã visível a mexerem-se);
  • Uso vários MP3 e WAV embebidos no executável que ocupam a sua memória também;
  • Fogo e outros mecanismos desenhados em real-time (não são sprites);
  • Gotas de chuva desenhadas em real-time, bem como neve, etc (não são sprites);
  • Armas laser desenhadas em real-time, em que cada píxel das mesmas que cruzem um píxel de um inimigo lhes tira tipo 0,20% da energia em real-time;
  • Ou seja, colisões mesmo a nível de píxel em real-time;
  • 60 FPS (apesar de chegar aos 500 FPS usando apenas 16% do CPU disponível, podendo ir aos 3000 FPS neste CPU velho);
  • Etc!

Se eu reduzir apenas a resolução de 1280×1024 para 320×200, só aí reduziria a memória usada no desenho do ecrã em 20 vezes, e poderia calcular só o que é visível no ecrã, reduzir inimigos, reduzir gotas de chuva e neve e bolhas de água, meter sprites em vez de fogo gerado por algoritmos em real-time, etc!

Por isso metê-lo a funcionar em máquinas de 2005 seria fácil com poucas configurações.

Meter em máquinas de 2000 já requeriria cortar algumas coisas, mas funcionaria.

E se o quisesse adaptar para máquinas de MS-DOS dos anos 90, teria de alterar muita coisa como as paletes, etc, mas seria possível, meter tudo isto a funcionar no MS-DOS em máquinas antigas.

Mas dispenso, pois o meu objectivo é usar o Game Engine para criar os meus jogos em máquinas actuais, com o poder todo que me permitem usar.

E eu poderia reduzir um pouco mais, mas não compensaria o esforço pois os 120 MB a 200 MB devem-se ao nível enorme de testes que tenho no meu platformer com 1001 coisas geradas em tempo real.

Mas penso que o meu engine já está mais do que optimizado, e no exemplo abaixo podemos ver o meu remake do Master of Orion a ocupar só uns 66 MB, e isso deve-se aos MP3, às texturas, ao próprio engine, e 1001 coisas que estão activas para o Platformer e que nem lhe fariam falta (posso reduzir):

2025-01-20 - O meu nível de testes de Master of Orion a ocupar 66 MB por ter poucos recursos usados...
2025-01-20 – O meu nível de testes de Master of Orion a ocupar 66 MB por ter poucos recursos usados…

O outro acima chega aos 200 MB porque já tem muitos inimigos, um nível gigante, muitos algoritmos a gerar Fìsica em tempo real, etc.

Por isso reduzir mais de momento não vale a pena, seria trabalho a mais para pouco ganho, e para máquinas actuais com 16 ou 32 GB de memória, ocupar 0,2 GB é mais do que suficiente para jogos complexos. 🙂

Bem, agora vou andar ocupado com a mudança para o Porto (estou em fase de mudanças o que me atrasou também esta operação de engenharia de software mais complexa), mas um dia quando voltar a programar, vai ser para as partes divertidas, pois em termos de memória e CPU o meu Game Engine já está bastante optimizado. 🙂

Pelo menos já me sinto melhor ao não ver o meu Game Engine ser mostrado como algo que ocupa 2 GB de memória por estar a usar ferramentas STL do C++, ao invés de criar as minhas próprias, agora já me sinto melhor ao exibi-lo!

Mais notícias um dia.

Hasta!

2025-01-20.

Partilhado no mesmo dia no meu LinkedIn, em:

(Link a colocar mais tarde).

Próximo post sobre o meu Game Engine:

(A colocar um dia).

Post anterior, também sobre o meu Game Engine (o Pai-Natal que atirava bombas, a preparar para o Batman the Movie remake):

2025-01-09 – Testes de Balística com Granadas e um Pai Natal, no meu Game Engine em C++…
2025-01-20 - Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo...
2025-01-20 – Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo…
2025-01-20 - Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo...
2025-01-20 – Optimização do uso de memória (-90%) do meu Game Engine em C++ (Criação dos meus próprios containers), e adaptando-o para Multi-Threading em simultâneo…

Leave a Reply

Your email address will not be published. Required fields are marked *

RSS
Follow by Email
LinkedIn
LinkedIn
Share
URL has been copied successfully!