I am trying to render a scene using VBOs and VAOs, and I would like to update this scene with new data. The issue is that I do not know in advance how many vertices I will be rendering, so I cannot allocate memory nicely.
Now, I have already tried the "just allocate the maximum amount of memory you will ever use" approach, and I'm not sure it is the best one (my VBO could potentially be over 3 GB large). So my idea was to use 2 VBOs instead, and update one while the other gets drawn using glBufferData
.
Because I do not want the program to freeze while I update the buffer, I wanted to try multiprocessing to have the "draw" and "update" processes running simultaneously. However, when I call the function supposed to draw and update at the same time, it freezes anyway and displays a blank screen while it is updating the second buffer. When I stop updating the second buffer, it works normally.
Code for buffer creation and drawing/updating:
def __init__(self, coords_list, layer_list, model_list):
self.fgbuffer, self.bgbuffer = 0, 0
self.fgarray, self.bgarray = 0, 0
now = time.time()
self.render_list = gen_render_list(coords_list)
self.render_vbo, self.render_vao = glGenBuffers(1), glGenVertexArrays(1)
self.render_vbo_2, self.render_vao_2 = glGenBuffers(1), glGenVertexArrays(1)
glBindVertexArray(self.render_vao)
glBindBuffer(GL_ARRAY_BUFFER, self.render_vbo)
glBufferData(GL_ARRAY_BUFFER, np.array(self.render_list, dtype='float32'), GL_STREAM_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(20))
glEnableVertexAttribArray(2)
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(32))
glEnableVertexAttribArray(3)
glBindVertexArray(self.render_vao_2)
glBindBuffer(GL_ARRAY_BUFFER, self.render_vbo_2)
glBufferData(GL_ARRAY_BUFFER, np.array(self.render_list, dtype='float32'), GL_STREAM_DRAW)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(0))
glEnableVertexAttribArray(0)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(12))
glEnableVertexAttribArray(1)
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(20))
glEnableVertexAttribArray(2)
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, 36, ctypes.c_void_p(32))
glEnableVertexAttribArray(3)
self.fgbuffer, self.fgarray = self.render_vbo, self.render_vao
self.bgbuffer, self.bgarray = self.render_vbo_2, self.render_vao_2
def update_bgbuffer(self):
glBindVertexArray(self.bgarray)
glBindBuffer(GL_ARRAY_BUFFER, self.bgbuffer)
glBufferData(GL_ARRAY_BUFFER, np.array(self.render_list, dtype='float32'), GL_STREAM_DRAW)
def draw_buffer(self, program, texture):
program.use()
glBindTexture(GL_TEXTURE_2D_ARRAY, texture)
glBindVertexArray(self.fgarray)
glDrawArrays(GL_TRIANGLES, 0, len(self.render_list) * 6)
def draw_and_update(self, program, texture):
draw = multiprocessing.Process(target = self.draw_buffer, args = (program, texture))
update = multiprocessing.Process(target = self.update_bgbuffer, args = ())
draw.start()
update.start()
I assume there is a better way to create two identical buffers, but that is not really what my problem is about.
Render loop:
while not window.check_if_closed():
window.refresh(0)
glActiveTexture(GL_TEXTURE0)
shader_program_scene.use()
glEnable(GL_DEPTH_TEST)
if glfw.get_key(window.window, glfw.KEY_U) == glfw.PRESS:
world_render.draw_and_update(shader_program_scene, all_textures)
else:
world_render.draw_buffer(shader_program_scene, all_textures)
glBindVertexArray(0)
I had thought that sending data over to the GPU in a separate thread would eliminate the problem of having to wait for the transfer to be finished to be able to draw, but that apparently is not the case.
I would very much appreciate it if someone could explain to me how I am supposed to use two buffers simultaneously, if it is possible. Thanks in advance!
The OpenGL Context is thread local. A context cannot be current in multiple threads. You have to "switch" the context. A context has to be made current in a thread.
See also OpenGL and multithreading respectively Multithreaded Rendering on OpenGL.
glBufferData
creates and initializes a buffer object's data store (think about it as like as memory allocation and copy of the data to the memory). If you just want to update the buffer data, then you have to use glBufferSubData
. glBufferSubData
uses an existing buffer and copies data to the buffer.
See also What is the proper way to modify OpenGL vertex buffer?