FastPointer + Dynamic Mesh = World Explodes

Blitz3D Forums/Blitz3D Programming/FastPointer + Dynamic Mesh = World Explodes

ClayPigeon(Posted 2013) [#1]
I'm working on a Minecraftish game (actually, it's nothing like Minecraft, I'm just giving you an idea of the visual style) and the cube-based world is constantly updating. It is divided into 16x16x16-block sections called "chunks". Each chunk is just a type with a bank and a "needsUpdated" flag. When the system is cycling through all the types, if the needsUpdated flag is true, it rebuilds the mesh for that chunk.

My problem is this: It takes too long to build one chunk mesh per frame. So I tried using FastPointer and put the chunk updating into a thread. It works fine for a few seconds running a 60 FPS, but then MAVs on a RenderWorld line. I'm sure this has to do with calling RenderWorld while the thread is in the middle of building the mesh. Does anyone have any ideas for alternate approaches for multi-threading with a mesh being modified in real time? Thanks!


Yasha(Posted 2013) [#2]
All of your graphics resources need to be finalised and their threads paused while RenderWorld runs, yes. Rendering was not designed with threading in mind (there are almost no engines even for modern AAA games where it is, not least because shaders make it irrelevant), and there is no way to make it work while a mesh is being updated in another thread. So there's that to bear in mind.

First thing: are you just building one chunk at a time, and building a chunk doesn't fit into a single frame, or are you building many chunks and farming them out to different threads so that they can be built in parallel? (I'm not sure I see how threading actually helps the first scenario, since if there's only one task, nothing is getting parallelized..?)

Second: are chunks being updated for immediate rendering, or are they being handled in the background while out of sight? In other words do the requirements of your rendering system mean that a chunk needs to be updated within the span of a single frame? (again, this might mean that there's less to usefully parallelize.)

The most obvious thing that comes to mind for updating a mesh "safely" is to copy its vertex data into a bank, modify it directly in the bank, and copy it back to the mesh on the main thread when it's marked as complete using a block-copy operation (e.g. memcpy). This should be easy to work out if you're familiar with FastPointer, although it requires you to know the internal entity structure layout (there are some tables floating around somewhere... otherwise, give me a minute and I might be able to come up with something). This obviously is limited to things you can do by simply adjusting the properties of existing vertices (potentially do the same for tris) rather than creating anything new, which needs the proper creation functions.

One other problem you face with threading in B3D is that there is no safe way for threads to communicate. Best I can think of is to have your application open a port to itself and use network commands or something like that. Writing to global variables for another thread to read is not safe, as the whole point is that the threads can potentially access the same memory mid-write.


ClayPigeon(Posted 2013) [#3]
are you just building one chunk at a time, and building a chunk doesn't fit into a single frame, or are you building many chunks and farming them out to different threads so that they can be built in parallel?


I'm only updating one chunk per frame since I can't even update one in a single frame. It's the only way to keep a "reasonable" framerate.

are chunks being updated for immediate rendering, or are they being handled in the background while out of sight?


In a perfect world, the chunks need to update as soon as possible; the changes need to be visible instantaneously. Obviously, that's not going to happen. Would building the updated mesh into a separate model and hiding it work? As in, would using HideEntity on a mesh fix the MAV if it was still being processed when a RenderWorld came along?

copy its vertex data into a bank, modify it directly in the bank, and copy it back to the mesh


That wouldn't work because I'm not modifying the vertices on an update, I'm completely reconstructing the mesh. Anyway, I don't think that would speed anything up because I think the overhead is coming from the act of creating the vertices themselves, because there are so many.


Yasha(Posted 2013) [#4]
Would building the updated mesh into a separate model and hiding it work? As in, would using HideEntity on a mesh fix the MAV if it was still being processed when a RenderWorld came along?


I was thinking about suggesting that, but your Hide and Show commands would need to be synchronised with the rendering thread, or you'd have the same problem (if the Hide command is issued after the point during RenderWorld where the render list has been built, it will do nothing). Certainly worth a try if you can work out a way to do that. However, once you're syncing it with the main thread, you're effectively admitting that it won't be done in the same frame, and that the purpose of using a child thread is to skip a render cycle (or else... you'd be doing it on the main thread anyway).


That wouldn't work because I'm not modifying the vertices on an update, I'm completely reconstructing the mesh. Anyway, I don't think that would speed anything up because I think the overhead is coming from the act of creating the vertices themselves, because there are so many.


If this is because the number of verts can change, consider the option of creating every mesh with the maximum suitable number of vertices and simply crushing down the "unused" ones into a zero-by-zero point in one corner. Reusing any dynamically-allocated object is usually better than recreating it (when you create a new mesh and add vertices to it, there are a lot of vector.push_back operations that need to happen as the vertex buffer extends and the memory gets reallocated, among other things: this won't happen with a fixed-length vertex buffer).


Anyway if you do do the above, and it's not fast enough and you want to try the bank method, the pointer to the vertex buffer is found by peeking surface+28. Vertices start at 64-byte intervals and have the structure x#, y#, z#, nx#, ny#, nz#, argb%, u0#, v0#, u1#, v1# (the rest of the space is for bone data). To copy it to a bank you need to copy 64*number-of-verts; if you have a preallocated large vertex buffer you could also just copy the first few you want to use and modify and set the rest to 0 (by first memcpying from a second bank preloaded with all vertices crushed, and then memcpying the first N verts from the modified bank).


ClayPigeon(Posted 2013) [#5]
Well, I changed it to the system you suggested (put all the verts at 0,0,0 until needed and only modify coords) and the MAVs went away, but now there's all kinds of crazy artifacts, like seeing the triangles spazz around before they get placed, and sometimes things have holes in them. Also, I realized that the {0,0,0} vertices method defeats my LOD; all faces are visible as opposed to only the surface ones like I had it set up.