2
\$\begingroup\$

I've been playing around with VBOs as of late (mostly because my game necessitates it), and I've found the need to add new vertices to a VBO, but not necessarily at the end. The game in question is a Terraria port, and when a player updates the world, the VBO needs to be updated. Up until now, I've been using glBufferData() to update the array, but it's time I switch to glBufferSubdata() or glMapBuffer() I'd like to be able to update a certain "chunk" of the world when a block in it is modified, but this could potentially result in adding more or less vertices than are already in the appropriate memory section. If a player places a "block," more memory would need to be allocated, resulting in any proceeding vertices being shifted over x indices. My only thought would be to allocate enough memory for each chunk to be completely filled with blocks, but I haven't yet found a way to do that. Would anyone be able to point me in the right direction?

EDIT: To clarify, pushing new VBO data to the graphics card (which is what causes the lag spike) takes 11-12ms the first couple of times, and 1-4 every time after. This also raises the question: why does the graphics card seemingly need to be "broken in?" In other words, why is there such a huge delay at first?

\$\endgroup\$
3
  • \$\begingroup\$ The typical strategy for this is to just rebuild the VBO with the new vertices included. This avoids the problem of needing to grow or shrink the VBO in place. \$\endgroup\$
    – House
    Commented Sep 3, 2013 at 0:54
  • \$\begingroup\$ @Byte56 That's what I'm currently doing, but I'd like to use a more efficient method, as it causes a small but noticeable lag spike whenever it's rebound. \$\endgroup\$
    – caseif
    Commented Sep 3, 2013 at 1:14
  • 1
    \$\begingroup\$ I don't know if this is applicable here, but some DX9 era hardware absolutely hated it when you allocated vertex buffers dynamically. You were better off finding ways to reuse old vertex buffers (even if it meant wasting storage space), than creating and destroying vertex buffers. This behavior was vendor specific too, some vendors had very little performance impact for allocating/freeing vertex buffers and others had huge penalties. This had nothing to do with the actual bus bandwidth required to send the data to the GPU mind you, and everything to do with run-time allocation patterns. \$\endgroup\$ Commented Sep 3, 2013 at 4:14

1 Answer 1

2
\$\begingroup\$

You can gain efficiency by doing two things:

  1. Localizing updates. Only update a small part of the buffer to add or remove objects. Let's assume each object takes up a constant amount of space, say 96 bytes. To delete a object (tile), we issue a 96 byte upload via glBufferSubData to replace the deleted object with the data for the last object in the buffer. Then we make sure we render one less object of geometry. The effect is that the last object is still rendered once, but now in the dead space at the end of the buffer we have a second copy. To add and object, we update the buffer with another 96 bytes at the end, and render one more object's worth of geometry. If we run out of room, we allocate a new VBO and stick it in there. Thus each "chunk" can have multiple VBO's, if it is really full. Nothing has to be sorted in your VBO's, even if it is in client memory. You just need to keep track of which index each tile went into in order to know where to put each thing. In any case, uploading a Terraria's game worth of changes should not be a problem at all. This technique required 96*n bytes to be transferred per addition/deletion.

  2. Delaying updates. Issuing something and having it happen the next frame is a good way to get lots of performance. Rather than deleting the object from the VBO, rendering, and then swapping buffers, consider rendering, deleting, and then swapping. This way the graphics card gets to use the old data to render the frame, and can transfer the data over the bus while you are waiting to vsync. You'll lag by one frame this way, but it is the most efficient way to transfer data to the graphics card. If you want to get really clever, then you can issue two draw calls which render everything before and after the removed block, which doesn't touch the buffer, then remove it for the next frame. On adds, keep around a small VBO which you can push one object to and render without doing an allocation. The key is to not try to modify a buffer which the graphics card may be still using. There are many ways to accomplish this, and many ways to fail to do so in subtle ways.

\$\endgroup\$

You must log in to answer this question.

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