
Quem já tentou renderizar milhares de objetos em tempo real sabe: a performance despenca rápido.
O culpado? Muitas vezes não é a GPU, e sim a quantidade absurda de draw calls que a CPU precisa enviar.
Neste post, vou mostrar como usei ECS + SoA + instancing com Raylib para melhorar drasticamente a performance ao renderizar 125.000 cubos.
Renderizar cada cubo com DrawModel parece simples, mas cada chamada gera um overhead na CPU:
125.000 cubos = 125.000 draw calls = CPU engasgada.
A forma como guardamos dados impacta diretamente o desempenho.
AoS (Array of Structs)
1struct Entity { 2 float x, y, z; 3 float rotY; 4 bool visible; 5}; 6std::vector<Entity> entities;
Dados ficam misturados, pouco cache-friendly.
SoA (Structure of Arrays)
1struct TransformComponent { 2 std::vector<float> posX, posY, posZ; 3 std::vector<float> rotY; 4}; 5 6struct RenderComponent { 7 std::vector<bool> visible; 8};
Dados ficam contíguos na memória → melhor para cache, SIMD e processamento em batch.
Esse layout é essencial para performance em engines modernas.
Na memória, os dados seriam armazenados da seguinte maneira:
1posX: [x1, x2, x3, x4...] 2posY: [y1, y2, y3, y4...] 3posZ: [z1, z2, z3, z4...] 4rotY: [r1, r2, r3, r4...] 5visible: [v1, v2, v3, v4...]
Implementei um mini ECS onde cada entidade é definida por:
TransformComponent (posição, rotação)RenderComponent (visibilidade)Isso permite manipular milhares de entidades de forma eficiente, sem precisar criar structs gigantes por objeto.
DrawModel por cubo)Translate, Rotate, DrawModelDrawMeshInstanced)Adicionei um HUD e logging em CSV para capturar métricas em tempo real:
Exemplo da função de logging:
1void LogToCSV(float frameTime, int fps, int cubes, int drawCalls) { 2 static std::ofstream log("performance_log.csv", std::ios::app); 3 static bool initialized = false; 4 5 if (!initialized) { 6 log << "FrameTimeMs,FPS,CubesDrawn,DrawCalls\n"; 7 initialized = true; 8 } 9 10 log << std::fixed << std::setprecision(4) 11 << frameTime << "," << fps << "," << cubes << "," << drawCalls << "\n"; 12}
Esses dados podem ser exportados depois para gerar gráficos de comparação.
🚀 Próximos passos: aplicar SSE/AVX para otimizar a atualização das matrizes em batch.
Link para o código: https://github.com/WLSF/raylib_tests
Já testou instancing ou ECS em seus projetos?
Quais ganhos percebeu?
Me marca no Twitter(@frantz_willian) para trocarmos ideia. 😃