I'm wondering how drawing simple geometry with textures can eat up so much performance (below 60fps)? Even my good graphics card (GTX 960) can "only" draw up to 1000 sprites smoothly. The textures I'm using are all power of 2 textures and don't exceed a size of 512x512. I'm even filtering with GL_NEAREST
only.
The sprites themself are random generated in size. So there are no 1000 fullscreen quads, which would be no real use case.
I'm drawing my sprites batched, meaning I have one dynamic vertex buffer and a static index buffer. I update the vertex buffer every frame with glBufferSubData
once and then draw everything with ``glDrawElements`. I have about 5 different textures which I bind once per frame resulting in 5 draw calls. For rendering I'm using only one shader which is bound once the application starts.
So I have 5 texture bindings, 5 draw calls, and one vertex buffer update per frame which is not really that much.
Here is an example with one texture:
val shaderProgram = ShaderProgram("assets/default.vert", "assets/default.frag")
val texture = Texture("assets/logo.png")
val sprite = BufferSprite(texture)
val batch = BufferSpriteBatch()
val projView = Matrix4f().identity().ortho2D(0f, 640f, 0f, 480f)
fun setup() {
glEnable(GL_TEXTURE)
//glColorMask(true, true, true, true)
//glDepthMask(false)
glUseProgram(shaderProgram.program)
texture.bind()
batch.begin()
for(i in 1..1000)
batch.draw(sprite)
batch.update()
}
fun render() {
glClear(GL_COLOR_BUFFER_BIT)
stackPush().use { stack ->
val mat = stack.mallocFloat(16)
projView.get(mat)
val loc = glGetUniformLocation(shaderProgram.program, "u_projView")
glUniformMatrix4fv(loc, false, mat)
batch.flush()
}
}
The batch.draw()
method puts the sprites vertex data in a cpu side buffer and batch.update()
uploads everything to the gpu with glBufferSubData
.
And setting up the spritebatch looks as follows:
glBindBuffer(GL_ARRAY_BUFFER, tmpVbo)
glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW)
glEnableVertexAttribArray(0)
glEnableVertexAttribArray(1)
glEnableVertexAttribArray(2)
glVertexAttribPointer(0, 2, GL_FLOAT, false, 24 * sizeof(Float), 0)
glVertexAttribPointer(1, 4, GL_FLOAT, false, 24 * sizeof(Float), 2.toLong() * sizeof(Float))
glVertexAttribPointer(2, 2, GL_FLOAT, false, 24 * sizeof(Float), 6.toLong() * sizeof(Float))
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tmpEbo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
I was profiling my program first, but updating the vertex buffers and all the geometry takes about 10% of the total time per frame. But swapping the buffers takes up the rest of the 90% frame time.
So I'm asking, how can such big AAA games render their scenes with millions of vertices, if drawing pixels is such a time consuming task? I know that their is a lot of optimizations in the code, but still.