I have been wondering if I could update an attribute buffer. I tried here,
but after 2 seconds when the resize attempt happens, you’ll see this in console:
Error: THREE.WebGLAttributes: The size of the buffer attribute's array buffer does not match the original size. Resizing buffer attributes is not supported.
Is this not supported due to technical limitations with webgl, or is it just not implemented in Three.js yet? If the latter, is it open for pull request?
Based on some reading here,
It seems that buffers can be re-bound to attribute locations, so it seems that buffers can be swapped. If so, then bufferAttribute.array = new Float32Array(); bufferAttribute.needsUpdate = true could correspond to swapping to a new buffer. Is this doable?
The reason I am wondering is because it seems that then this would make it possible to share an attribute buffer across multiple materials without destroying all buffers associated with a geometry (i.e. without having to use geometry.dispose()).
Same question found on StackOverflow:
Similar question for plain WebGL:
It says there:
No, unfortunately, WebGL lacks such abilities. If you want to resize a buffer, you’ll lose it’s content, there’s no way around that.
Wouldn’t it be possible then, assuming we re-write the data after resizing (because it was zeroed out)? The OP asks about not having to re-write the, which may not possible, which is why the answer is “no” in that case.
Ok, after doing some research it seems this is doable:
// Create new buffer
const newBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, newBuffer);
gl.bufferData(gl.ARRAY_BUFFER, newSize, gl.STATIC_DRAW);
// Copy data from old to new buffer
gl.bindBuffer(gl.COPY_READ_BUFFER, oldBuffer);
gl.bindBuffer(gl.COPY_WRITE_BUFFER, newBuffer);
gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, oldSize);
// Delete old buffer
gl.deleteBuffer(oldBuffer);
If we can put this mechanism into WebGLRenderer, would it be worth it from a maintenance perspective?
I can say that from an end user perspective it will be an improvement to usability because. For example imagine you make a geometry, and want to share it with multiple meshes, or maybe you give a geometry reference to 3rd parties and cannot make them change the reference. It will get messy to have to keep track of the meshes to replace the geometry references, or to have to tell users of the geometry how to replace the references. Instead, geometry attributes could be resized dynamically (with the understood cost of copying data from old buffer to new buffer), without any downstream geometry references having to be replaced.
Hmm, it seems that geometry.dispose() should at least be enough to work around the limitation, but it doesn’t seem to work.
In the following pen, I dispose() the geometry before resizing the attribute, which eliminates the runtime error, but it still renders the same number of points instead of more.
Have I missed something, or is this a bug with geometry.dispose()?
@Mugen87 Wdyt? If its a bug, then a better PR to make first would be to fix dispose().
Ah! I forgot about that. So after doing both: call dispose(), and update count manually, here’s the working demo:
I’m guessing this isn’t much faster than making new geometry objects (saving on the cost of making the new JS BufferGeometry objects), but definitely makes it more ideal for maintaining downstream references to geometries.
I’m also guessing this is faster than, based on some preliminary research, growing and updating a texture (aiming to share data across materials, either with an attribute, or with a texture).
Ok, so the idea for per-attribute replacement. Is it worth a PR for improved performance and more intuitive update code?