Scene Kit Performance con prueba de cubo
Al aprender la programación de gráficos en 3D para juegos, decidí comenzar de manera simple usando la API de Scene Kit 3D. Mi primer objetivo de juego era construir una imitación muy simplificada de MineCraft. Un juego de cubos: lo difícil que puede ser.
A continuación se muestra un bucle que escribí para colocar un paseo de 100 x 100 cubos (10,000) y el rendimiento de FPS fue abismal (~ 20 FPS). ¿Mi objetivo de juego inicial es demasiado para Scene Kit o hay una mejor manera de abordarlo?
He leído otros temas en StackExchange pero no creo que respondan mi pregunta. La conversión de los bloques de superficie expuestos a una sola malla no funcionará ya que SCNGeometry es inmutable.
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)
}
ACTUALIZACIÓN 30-12-2014: Modifiqué el código para que SCNBoxNode se cree una vez y luego cada cuadro adicional en la matriz de 100 x 100 se cree a través de:
var newBoxNode = firstBoxNode.clone()
newBoxNode.position = SCNVector3Make(x, y, z)
Este cambio parece haber aumentado el FPS a ~ 30 fps. Las otras estadísticas son las siguientes (de las estadísticas que se muestran en SCNView):
10K (¿Supongo que esto es dibujar llamadas?) 120K (Supongo que esto es caras) 360K (Suponiendo que este es el conteo de vértices)
La mayor parte del ciclo de ejecución está en Renderizado (supongo que estima el 98%). El tiempo de bucle total es 26.7 ms (ouch). Estoy ejecutando una Mac Pro a finales de 2013 (6 núcleos con GPU Dual D500).
Dado que un juego de estilo MineCraft tiene un panorama que cambia constantemente según las acciones de los jugadores, no veo cómo puedo optimizar esto dentro de los límites de Scene Kit. Una gran decepción ya que realmente me gusta el marco. Me encantaría escuchar las ideas de alguien sobre cómo puedo abordar este problema; sin eso, me veo obligado a utilizar OpenGL.
ACTUALIZACIÓN 12-30-2014 @ 2:00 pm ET: Estoy viendo una mejora significativa en el rendimiento al usar flattenedClone (). El FPS ahora es un sólido 60 fps incluso con más cuadros y DOS llamadas de dibujo. Sin embargo, acomodar un entorno dinámico (como admite MineCraft) sigue resultando problemático, ver a continuación.
Dado que la matriz cambiaría la composición con el tiempo, agregué un controlador keyDown para agregar una matriz de caja aún más grande a la existente y sincronicé la diferencia entre agregar la matriz de cajas dando como resultado muchas más llamadas versus agregar como un clon aplanado. Esto es lo que encontré:
En keyDown agrego otra matriz de 120 x 120 cajas (14,400 cajas)
// This took .0070333 milliseconds
scene?.rootNode.addChildNode(boxArrayNode)
// This took .02896785 milliseconds
scene?.rootNode.addChildNode(boxArrayNode.flattenedClone())
Llamar nuevamente a flattenedClone () es 4 veces más lento que agregar la matriz.
Esto da como resultado dos llamadas de dibujo que tienen 293K caras y 878K vértices. Todavía estoy jugando con esto y lo actualizaré si encuentro algo nuevo. En pocas palabras, con mis pruebas adicionales todavía siento que las restricciones geométricas inmutables de Scene Kit significan que no puedo aprovechar el marco.