Wie löse ich das Python OpenGL Memory Leakage Problem?

Sep 28 2020

Ich versuche, einige Traingles zu zeichnen und einige Texte auf dem Bildschirm zu rendern. Aber ich habe beobachtet, dass der Speicher (RAM) nur für 6 Draw-Aufrufe allmählich zunimmt. Ich habe 8 GB RAM. Wenn ich das Programm starte, steigt die Speichernutzung innerhalb von 1 Minute von 4,2 auf 6. Hier ist der vollständige Code.

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GL import shaders

from shader import *

import glfw
import freetype
import glm

import numpy as np
from PIL import Image
import math

class CharacterSlot:
    def __init__(self, texture, glyph):
        self.texture = texture
        self.textureSize = (glyph.bitmap.width, glyph.bitmap.rows)

        if isinstance(glyph, freetype.GlyphSlot):
            self.bearing = (glyph.bitmap_left, glyph.bitmap_top)
            self.advance = glyph.advance.x
        elif isinstance(glyph, freetype.BitmapGlyph):
            self.bearing = (glyph.left, glyph.top)
            self.advance = None
        else:
            raise RuntimeError('unknown glyph type')

def _get_rendering_buffer(xpos, ypos, w, h, zfix=0.0):
    return np.asarray([
        xpos,     ypos + h, 0, 0,
        xpos,     ypos,     0, 1,
        xpos + w, ypos,     1, 1,
        xpos,     ypos + h, 0, 0,
        xpos + w, ypos,     1, 1,
        xpos + w, ypos + h, 1, 0
    ], np.float32)

def init_chars(shaderProgram,window_height,window_width,font_size=24,fontfile = "Vera.ttf"):
    glUseProgram(shaderProgram)
    
    #get projection
    shader_projection = glGetUniformLocation(shaderProgram, "projection")
    W = window_width
    H = window_height
    projection = glm.ortho(-W/2, W/2, -H/2, H/2)
    glUniformMatrix4fv(shader_projection, 1, GL_FALSE, glm.value_ptr(projection))
    
    #disable byte-alignment restriction
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)

    face = freetype.Face(fontfile)
    face.set_char_size(font_size*64 )

    #load first 128 characters of ASCII set
    Characters = dict()
    for i in range(0,128):
        face.load_char(chr(i))
        glyph = face.glyph
        
        #generate texture
        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, glyph.bitmap.width, glyph.bitmap.rows, 0,
                     GL_RED, GL_UNSIGNED_BYTE, glyph.bitmap.buffer)

        #texture options
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

        #now store character for later use
        Characters[chr(i)] = CharacterSlot(texture,glyph)

    glBindTexture(GL_TEXTURE_2D, 0)    
    glUseProgram(0)
    
    return Characters
    
def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
    r,g,b = color
    
    glUseProgram(shaderProgram)

    #configure VAO/VBO for texture quads
    VAO = glGenVertexArrays(1)
    glBindVertexArray(VAO)
    
    VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_STATIC_DRAW)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)
    
    glUniform3f(glGetUniformLocation(shaderProgram, "textColor"),r/255,g/255,b/255) 
    glActiveTexture(GL_TEXTURE0)
    glBindVertexArray(VAO)
    
    for c in text:
        ch = Characters[c]
        w, h = ch.textureSize
        w = w*scale
        h = h*scale
        vertices = _get_rendering_buffer(x,y,w,h)

        #render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.texture)
        #update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        #render quad
        glDrawArrays(GL_TRIANGLES, 0, 6)
        #now advance cursors for next glyph (note that advance is number of 1/64 pixels)
        x += (ch.advance>>6)*scale

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0)

    #UNBIND and DELETE VAO/VBO
    glBindVertexArray(0)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glDeleteBuffers(1, id(VBO))
    glDeleteBuffers(1, id(VAO))
    

def triangle(shaderProgram,window,x=0,y=0):
    vertices = [-0.5, -0.5, 0.0,
                 0.5, -0.5, 0.0,
                 0.0, 0.5, 0.0]
    vertices = np.array(vertices, dtype=np.float32)

    VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
    glEnableVertexAttribArray(0)

    #use shader program
    glUseProgram(shaderProgram)

    #accessing ourColor variable from shaderProgram
    vertexColorLoc = glGetUniformLocation(shaderProgram, "ourColor")
    glUniform4f(vertexColorLoc, 255, 28/255.0, 20/255.0, 0.7);

    #transform matrix
    transform = glm.mat4(1)
    transform = glm.translate(transform,glm.vec3(x,y,0))
    MVP = glGetUniformLocation(shaderProgram, "MVP")
    glUniformMatrix4fv(MVP, 1, GL_FALSE, glm.value_ptr(transform))

    #drawing trangle 
    glLineWidth(3)
    glDrawArrays(GL_TRIANGLES, 0, 3)
    
    glUseProgram(0)

    #UNBIND and DELETE VAO/VBO
    glBindVertexArray(0)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glDeleteBuffers(1, id(VBO))
   

def main():
    glfw.init()
    window = glfw.create_window(640, 640,"EXAMPLE PROGRAM",None,None)    
    glfw.make_context_current(window)
    
    #initliaze shader programs
    shaderProgram = get_shaderProgram()
    text_shaderProgram = get_text_shaderProgram()

    #load characters and VAO/VBO for text rendering
    Characters = init_chars(text_shaderProgram,640,640)
    #window loop
    while not glfw.window_should_close(window):
        glfw.poll_events()

        #screen
        glClearColor(0, 0, 0, 1)
        glClear(GL_COLOR_BUFFER_BIT)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        #draw functions
        
        render_text(window,text_shaderProgram,"TRIANGLE",-50,-200,1,Characters)
        render_text(window,text_shaderProgram,"A",0,180,1,Characters)
        render_text(window,text_shaderProgram,"B",-160,-180,1,Characters)
        render_text(window,text_shaderProgram,"C",150,-180,1,Characters)
        
        triangle(shaderProgram,window)
        triangle(shaderProgram,window,x=0.5,y=0.5)
        #swap buffers 
        glfw.swap_buffers(window)
        glfw.swap_interval(1)
        
    glfw.terminate()
    
if __name__ == '__main__':
    main()

Das Shader-Programm ist hier . Aber ich denke, die Probleme liegen im Pufferobjekt. Ich habe versucht, VAO / VBO zu lösen und Puffer durch folgenden Code zu löschen. Aber ich sehe keine Veränderung.

    #UNBIND and DELETE VAO/VBO
    glBindVertexArray(0)
    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glDeleteBuffers(1, id(VBO))
    glDeleteBuffers(1, id(VAO))

Hier ist das verwandte Problem, bei dem die akzeptierte Antwort darauf hindeutet, dass glGenBuffers einen Speicherverlust verursacht. Die alternative Funktion glCreateBuffers ist in pyopengl nicht verfügbar. Wie kann ich dieses Problem lösen?

Antworten

1 Rabbid76 Sep 28 2020 at 02:21

Ich kann keinen Grund sehe das Vertex - Array - Objekt für neu zu erstellen und Array - Puffer jedes Mal , wenn Objekte render_textbzw. triangleaufgerufen wird. Die Scheitelpunktspezifikation und die Anzahl der Scheitelpunkte ändern sich nicht, daher würde es ausreichen, den Inhalt des Puffers zu aktualisieren.

Erstellen Sie das Vertex-Array-Objekt und das Array-Pufferobjekt einmal bei der Initialisierung:

def init_buffers():
    global text_VAO, text_VBO, triangle_VAO, triangle_VBO

    text_VAO = glGenVertexArrays(1)
    glBindVertexArray(text_VAO)
    
    text_VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
    glBufferData(GL_ARRAY_BUFFER, 6 * 4 * 4, None, GL_STATIC_DRAW)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)

    vertices = [-0.5, -0.5, 0.0,
                 0.5, -0.5, 0.0,
                 0.0, 0.5, 0.0]
    vertices = np.array(vertices, dtype=np.float32)

    triangle_VAO = glGenVertexArrays(1)
    glBindVertexArray(triangle_VAO)

    triangle_VBO = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, triangle_VBO)
    glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
    glEnableVertexAttribArray(0)

Verwenden Sie dann in den Funktionen render_textund triangle:

def render_text(window,shaderProgram,text,x,y,scale,Characters,color=(170,250,255)):
    # [...]

    glBindVertexArray(text_VAO)
    
    for c in text:
        # [...]

        glBindBuffer(GL_ARRAY_BUFFER, text_VBO)
        glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.nbytes, vertices)

    # glDeleteBuffers(1, id(VBO)) <--- DELETE
    # glDeleteBuffers(1, id(VAO)) <--- DELETE

def triangle(shaderProgram,window,x=0,y=0):
    
    glBindVertexArray(triangle_VAO)

    # [...]

    # glDeleteBuffers(1, id(VBO)) <--- DELETE

Invoke init_buffersvor der Anwendung Schleife:

def main():
    # [...]

    init_buffers()
    
    while not glfw.window_should_close(window):
        # [...]