Desempenho do kit de cena com teste de cubo
Ao aprender a programação de gráficos 3D para jogos, decidi começar de maneira simples, usando a API 3D do Scene Kit. Meu primeiro objetivo no jogo foi criar uma imitação muito simplificada do MineCraft. Um jogo de apenas cubos - quão difícil pode ser.
Abaixo está um loop que escrevi para colocar uma carona de 100 x 100 cubos (10.000) e o desempenho do FPS foi péssimo (~ 20 FPS). Meu objetivo inicial de jogo é demais para o Scene Kit ou existe uma maneira melhor de abordar isso?
Li outros tópicos no StackExchange, mas não acho que eles respondam à minha pergunta. Converter os blocos de superfície expostos em uma única malha não funcionará, pois a SCNGeometry é imutável.
func createBoxArray(scene : SCNScene, lengthCount: Int, depthCount: Int) {
let startX : CGFloat = -(CGFloat(lengthCount) * CUBE_SIZE) + (CGFloat(lengthCount) * CUBE_MARGIN) / 2.0
let startY : CGFloat = 0.0
let startZ : CGFloat = -(CGFloat(lengthCount) * CUBE_SIZE) + (CGFloat(lengthCount) * CUBE_MARGIN) / 2.0
var currentZ : CGFloat = startZ
for z in 0 ..< depthCount {
currentZ += CUBE_SIZE + CUBE_MARGIN
var currentX = startX
for x in 0 ..< lengthCount {
currentX += CUBE_SIZE + CUBE_MARGIN
createBox(scene, x: currentX, y: startY, z: currentZ)
}
}
}
func createBox(scene : SCNScene, x: CGFloat, y: CGFloat, z: CGFloat) {
var box = SCNBox(width: CUBE_SIZE, height: CUBE_SIZE, length: CUBE_SIZE, chamferRadius: 0.0)
box.firstMaterial?.diffuse.contents = NSColor.purpleColor()
var boxNode = SCNNode(geometry: box)
boxNode.position = SCNVector3Make(x, y, z)
scene.rootNode.addChildNode(boxNode)
}
ATUALIZAÇÃO 12-30-2014: Modifiquei o código para que o SCNBoxNode seja criado uma vez e cada caixa adicional na matriz de 100 x 100 seja criada via:
var newBoxNode = firstBoxNode.clone()
newBoxNode.position = SCNVector3Make(x, y, z)
Essa alteração parece ter aumentado o FPS para ~ 30fps. As outras estatísticas são as seguintes (das estatísticas exibidas no SCNView):
10K (presumo que sejam chamadas de empate?) 120K (presumo que sejam faces) 360K (Supondo que essa seja a contagem de vértices)
A maior parte do loop de execução está na renderização (acho que estou estimando 98%). O tempo total do loop é 26,7ms (ai). Estou executando em um Mac Pro final de 2013 (6 núcleos com GPU Dual D500).
Dado que um jogo no estilo MineCraft tem um cenário que muda constantemente com base nas ações dos jogadores, não vejo como otimizar isso dentro dos limites do Scene Kit. Uma grande decepção, porque eu realmente gosto da estrutura. Eu adoraria ouvir as idéias de alguém sobre como resolver esse problema - sem isso, sou forçado a usar o OpenGL.
ATUALIZAÇÃO 12-30-2014 @ 14:00 ET: Estou vendo uma melhoria significativa de desempenho ao usar flattenedClone (). O FPS é agora um sólido 60fps, mesmo com mais caixas e DUAS chamadas de desenho. No entanto, acomodar um ambiente dinâmico (como o MineCraft suporta) ainda está se mostrando problemático - veja abaixo.
Como a matriz mudaria a composição ao longo do tempo, adicionei um manipulador keyDown para adicionar uma matriz de caixas ainda maior à existente e cronometrei a diferença entre adicionar a matriz de caixas, resultando em muito mais chamadas do que adicionar como um flattenedClone. Aqui está o que eu encontrei:
No keyDown, adiciono outra matriz de caixas de 120 x 120 (14.400 caixas)
// This took .0070333 milliseconds
scene?.rootNode.addChildNode(boxArrayNode)
// This took .02896785 milliseconds
scene?.rootNode.addChildNode(boxArrayNode.flattenedClone())
Chamar flattenedClone () novamente é 4x mais lento que adicionar a matriz.
Isso resulta em duas chamadas de desenho com faces de 293K e vértices de 878K. Ainda estou brincando com isso e atualizarei se encontrar algo novo. Resumindo, com meus testes adicionais ainda sinto que as restrições geométricas imutáveis do Scene Kit significam que não posso alavancar a estrutura.