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)
struct Entity {
float x, y, z;
float rotY;
bool visible;
};
std::vector<Entity> entities;
Dados ficam misturados, pouco cache-friendly.
SoA (Structure of Arrays)
struct TransformComponent {
std::vector<float> posX, posY, posZ;
std::vector<float> rotY;
};
struct RenderComponent {
std::vector<bool> visible;
};
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:
posX: [x1, x2, x3, x4...]
posY: [y1, y2, y3, y4...]
posZ: [z1, z2, z3, z4...]
rotY: [r1, r2, r3, r4...]
visible: [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
, DrawModel
DrawMeshInstanced
)Adicionei um HUD e logging em CSV para capturar métricas em tempo real:
Exemplo da função de logging:
void LogToCSV(float frameTime, int fps, int cubes, int drawCalls) {
static std::ofstream log("performance_log.csv", std::ios::app);
static bool initialized = false;
if (!initialized) {
log << "FrameTimeMs,FPS,CubesDrawn,DrawCalls\n";
initialized = true;
}
log << std::fixed << std::setprecision(4)
<< frameTime << "," << fps << "," << cubes << "," << drawCalls << "\n";
}
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. 😃