0
\$\begingroup\$

My game is set up so the the display controls the game loop, usual in Cocoa. The callback function calls run() which is this. The thing is, the thread running the callback is using 63% CPU and the RAM is constantly rising, slowly (memory leak?). I fixed it up a bit, before RAM used was 2.32 GB. The code in question is meant to draw one rotating emoji on the screen, which it does.

func run(){
    update()
    render()
}

Where render() calls draw() for all GameObjects is this:

func draw(graphic: Graphic2D) {

    programID = 0
    vaoID = 0
    vboID = 0
    eboID = 0
    vertexShader = 0
    fragmentShader = 0

    let vertices: [GLfloat] = [ -0.25, -0.25, graphic.colors[0].r, graphic.colors[0].g, graphic.colors[0].b, graphic.colors[0].a, 0.0, 0.0,
                                -0.25,  0.25, graphic.colors[1].r, graphic.colors[1].g, graphic.colors[1].b, graphic.colors[1].a, 0.0, 1.0,
                                 0.25,  0.25, graphic.colors[2].r, graphic.colors[2].g, graphic.colors[2].b, graphic.colors[2].a, 1.0, 1.0,
                                 0.25, -0.25, graphic.colors[3].r, graphic.colors[3].g, graphic.colors[3].b, graphic.colors[3].a, 1.0, 0.0 ]

    let indices: [GLuint] = [ 0, 1, 2,
                              2, 3, 0 ]

    let translationMat = GLKMatrix4Translate(GLKMatrix4Identity, graphic.position.x, graphic.position.y, 0)
    let rotationMat = GLKMatrix4RotateZ(GLKMatrix4Identity, graphic.rotation)
    finalTransform = GLKMatrix4Multiply(translationMat, rotationMat)

    let texURL = NSBundle.mainBundle().URLForResource(graphic.textures[0].pathName, withExtension: graphic.textures[0].fileType)

    let texInfo: GLKTextureInfo? = try? GLKTextureLoader.textureWithContentsOfURL(texURL!, options: [GLKTextureLoaderGenerateMipmaps : 1 , GLKTextureLoaderOriginBottomLeft : 1])



    var shaderSource: String

    glGenBuffers(1, &vboID)
    glGenBuffers(1, &eboID)

    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
    glTexParameteri(GLenum(GL_TEXTURE_2D), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)


    glGenVertexArrays(1, &vaoID)

    vertexShader = glCreateShader(GLenum(GL_VERTEX_SHADER))
    fragmentShader = glCreateShader(GLenum(GL_FRAGMENT_SHADER))

    shaderSource = graphic.shaders.vertexShader
    var vsptr = getShaderRef(shaderSource)
    glShaderSource(vertexShader, 1, &vsptr, nil)
    glCompileShader(vertexShader)

    var success: GLint = 0
    glGetShaderiv(vertexShader, GLbitfield(GL_COMPILE_STATUS), &success)
    if success <= 0 { Swift.print("ERROR O00:: vertex shader compile error")
                      Swift.print(shaderSource)}

    shaderSource = graphic.shaders.fragmentShader
    var fsptr = getShaderRef(shaderSource)
    glShaderSource(fragmentShader, 1, &fsptr, nil)
    glCompileShader(fragmentShader)

    glGetShaderiv(fragmentShader, GLbitfield(GL_COMPILE_STATUS), &success)
    if success <= 0 { Swift.print("ERROR O01:: fragment shader compile error")
                      Swift.print(shaderSource)}

    programID = glCreateProgram()

    glAttachShader(programID, vertexShader)
    glAttachShader(programID, fragmentShader)
    glLinkProgram(programID)

    glGetProgramiv(programID, GLbitfield(GL_LINK_STATUS), &success)
    if success <= 0 { Swift.print("ERROR O02:: program compile error") }

    glDetachShader(programID, vertexShader)
    glDetachShader(programID, fragmentShader)

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)


    glBindVertexArray(vaoID)

        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vboID)
        glBufferData(GLenum(GL_ARRAY_BUFFER), vertices.count * sizeof(GLfloat), vertices, GLenum(GL_STATIC_DRAW))

        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), eboID)
        glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), indices.count * sizeof(GLuint), indices, GLenum(GL_STATIC_DRAW))

        glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 32, UnsafePointer<GLuint>(bitPattern: 0))
        glEnableVertexAttribArray(0)

        glVertexAttribPointer(1, 4, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 32, UnsafePointer<GLuint>(bitPattern: 8))
        glEnableVertexAttribArray(1)

        glVertexAttribPointer(2, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 32, UnsafePointer<GLuint>(bitPattern: 24))
        glEnableVertexAttribArray(2)

    glBindVertexArray(0)


    glActiveTexture(GLenum(GL_TEXTURE0))
    if texInfo != nil {
        glBindTexture(texInfo!.target, texInfo!.name)
    }

    let transLocation = glGetUniformLocation(programID, "transform")

    glUseProgram(programID)


    glBindVertexArray(vaoID)


    glUniformMatrix4fv(transLocation, 1, 0, finalTransform.array)
    glDrawElements(GLenum(GL_TRIANGLES), 6, GLenum(GL_UNSIGNED_INT), UnsafePointer<Void>(bitPattern: 0))

    glBindVertexArray(0)

    if texInfo != nil {
        var name = texInfo?.name
        glBindTexture(texInfo!.target, 0)
        glDeleteTextures(1, &name!)
    }

    glDeleteVertexArrays(1, &vaoID)
    glDeleteBuffers(1, &vboID)
    glDeleteBuffers(1, &eboID)
    glDeleteProgram(programID)

}
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

The first issue is that you are re-creating and destroying the buffers, reloading the texture, and recompiling the shader on every single draw loop.

This is the source of the huge CPU usage.

As for the memory going up, there could be a memory leak in the scripting language you are using.

Not recreating everything on every loop will probably reduce the impact to practically nothing.

Create a LoadEverything() function that is called once when your program start.

Cheers,

\$\endgroup\$
3
  • \$\begingroup\$ Thank you, it appears that was the case. I removed the texture loader and the RAM usage was constant, it appears it was creating extra data from a separate thread not deallocated. I understand how to load the textures and shaders only once, but how do I go about using the buffers once? \$\endgroup\$ Commented Apr 24, 2016 at 21:44
  • \$\begingroup\$ That's why openGL assigns a GLuint to every object you create... Store those IDs somewhere \$\endgroup\$
    – rlam12
    Commented Apr 25, 2016 at 1:22
  • \$\begingroup\$ The part between glBindVertexArray(vaoID) and glBindVertexArray(0) that doesn't have the glDrawElements call is the part that setup the VertexArray, that goes into the LoadEverything() function with the glGenBuffers(1, &vboID) and glGenBuffers(1, &eboID). The glDeleteBuffers should not be called. At least until you don't need the buffers anymore (if you create a UnloadEverything() function for example). Once you get into level loading and unloading it gets more complicated but StackExchange isn't the place for this, plenty of good tutorials on-line for writing game engines. \$\endgroup\$ Commented Apr 25, 2016 at 1:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .