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\$
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\$
  • \$\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\$ – fridgemagnet Apr 24 '16 at 21:44
  • \$\begingroup\$ That's why openGL assigns a GLuint to every object you create... Store those IDs somewhere \$\endgroup\$ – rlam12 Apr 25 '16 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\$ – Stephane Hockenhull Apr 25 '16 at 1:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.