Summary: I observed some sorts of flickering effects when I am dynamically updating mesh data shared from storage buffers using functions like setIndexBuffer, setVerticesBuffer with a compute shader. I think it is a problem of setIndexBuffer.
I am planning to use Babylon.js to implement something cool, but I encountered some weird flickering effects that I cannot find any relevant answers from the Internet. I am going to present some details below.
Details:
What does the demo do? Basically, it performs a marching tetrahedra algorithm (a bit like the well-known marching cubes algorithm, but it changes the primitive from cubes to tetrahedra). By varying the variable var time = 0.0 (see line 44), we can dynamically obtain balls with different radii.
Problem: However, as we can see (the video below is played at a slow speed), some flickering holes (in the middle of the video) are presented which is unexpected, and I guess that it is because a wrong index buffer is used. However, I have updated the index buffer inside the function registerBeforeRender using setIndexBuffer in line 179.
How the code work? We build a compute shader that produces face_out where three consecutive numbers define a point coordinate, and nine numbers define an output triangle; and num_out is the output number of triangles. Therefore, we may simply set the vertex buffer to be exactly shared with the storage buffer buffer_faceOut, and set the index buffer to a range [0, max_num_tet * 2 * 3), where max_num_tet * 2 is the maximal number of triangles possibly obtained.
Previous Attempts: It has already been tested that copying buffers from cpu to gpu works well without flickers, but it is relatively slow. So I decided to directly set mesh’s data buffers from outputs of a compute shader to avoid data transfers.
My Guess: I have tried various solutions but cannot resolve the issue. My best guess is that it is related to some sort of synchronization problem (it might be wrong), e.g., the index buffer of the current flickering rendering frame is not synchronized with the setIndexBuffer statement (see line 179). Moreover, by utilizing a large delay with setTimeout, the issue is not addressed, so I suppose that the setIndexBuffer function will not take effect immediately until the next rendering is done, so that’s why a wrong index buffer is used (an index buffer of previous timestamp) and causes some holes on the sphere.
Beacuse you are leaving “trash” faces behind. You have to set the position kind vertex buffer as well not just the index buffer. face_out in the shader.
@roland Hi, yeah, I have set the vertex buffer, so the mesh can read its vertex buffer that is shared with a storage buffer buffer_faceOut, see around line 106,
mesh.setVerticesBuffer(new BABYLON.VertexBuffer(
engine,
buffer_faceOut.getBuffer(),
"position",
false, // whether the data is updatable
false, // whether to postpone creating the internal WebGL buffer (optional)
3, // the stride (optional)
));
So ideally, when buffer_faceOut is produced from the compute shader, its content has already been set properly, so the mesh can read its vertex data.
Besides, you are right, there are some trash data after num_f * 9 (nine is because a triangle takes up 9 numbers), but if we use index buffer and set the total vertex count properly, it should not read those trash data I think. Because if you pause the animation,
you will find that there are no more holes, and it seems like these holes only appear when it transitions to another mesh; and I also observe that when the mesh become stable, the problem does not exist.
@roland You are right, in the code, the vertex buffer is updated for each frame. In fact, the vertex buffer is shared with a storage buffer, and the storage buffer is written with some dynamically computed data after a compute shader is executed. So the mesh vertex data is updated for each rendering. Note that I called dispatchWhenReady within registerBeforeRender so that the mesh data gets updated before rendering.
@roland Hi, given that you also mentioned the “trash data” issue, I tried to fill trash space with zeros to see if it can solve the issue, and it turns out that it does not change anything, the flicker still exists.
This is because we cannot put setIndexBuffer inside the asynchronous function (i.e., the then() function), and setIndexBuffer must be put alongside the setVerticesBuffer function. In other words, we must precalculate the value of num_f and call setIndexBuffer immediately, it can be after or before the setVerticesBuffer, but not within the asynchronous function.
I think the reason is that if we put setIndexBuffer within an asynchronous function, the registerBeforeRender function will finish and go to next frame rendering, and for the next frame, it might accidentally read in the updated vertex data (computed from the compute shader), but the index buffer is not yet updated since it is in another asynchronous function, which causes the weird rendering problem.
Moreover, if the dispatch function and the read function of storage buffer can be synchronous with the CPU, I think the problem could be solved as well (the solution is much more easier), but I couldn’t find any APIs to make these function calls synchronous.