libgdx/libgdx · error · GdxRuntimeException

Cannot change attributes while VBO is bound

Error message

Cannot change attributes while VBO is bound

What it means

setBuffer() swaps the underlying ByteBuffer and VertexAttributes of an InstanceBufferObject. Doing this while the buffer is bound to GL state (isBound == true, set by bind() and cleared by unbind()) would desynchronize the CPU-side view from what the shader attribute pointers were set up against, so the method fail-fasts. This mirrors the same guard in VertexBufferObject.

Source

Thrown at backends/gdx-backends-gwt/src/com/badlogic/gdx/backends/gwt/emu/com/badlogic/gdx/graphics/glutils/InstanceBufferObject.java:96

	@Deprecated
	public FloatBuffer getBuffer () {
		isDirty = true;
		return buffer;
	}

	@Override
	public FloatBuffer getBuffer (boolean forWriting) {
		isDirty |= forWriting;
		return buffer;
	}

	/** Low level method to reset the buffer and attributes to the specified values. Use with care!
	 *
	 * @param data
	 * @param ownsBuffer
	 * @param value */
	protected void setBuffer (Buffer data, boolean ownsBuffer, VertexAttributes value) {
		if (isBound) throw new GdxRuntimeException("Cannot change attributes while VBO is bound");
		attributes = value;
		if (data instanceof ByteBuffer)
			byteBuffer = (ByteBuffer)data;
		else
			throw new GdxRuntimeException("Only ByteBuffer is currently supported");
		this.ownsBuffer = ownsBuffer;

		final int l = byteBuffer.limit();
		byteBuffer.limit(byteBuffer.capacity());
		buffer = byteBuffer.asFloatBuffer();
		byteBuffer.limit(l);
		buffer.limit(l / 4);
	}

	private void bufferChanged () {
		if (isBound) {
			Gdx.gl20.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(), null, usage);
			Gdx.gl20.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);

View on GitHub (pinned to 97f4086187)

Solutions

  1. Always pair bind()/unbind(): call unbind() (or ensure the drawing API you used ends with it) before calling setBuffer().
  2. Restructure so attribute-layout changes happen at frame boundaries, not during rendering.
  3. If an exception can interrupt a draw, catch it and unbind() in a finally block so isBound resets.

Example fix

// before
instanceBuffer.bind(shader);
instanceBuffer.setBuffer(newData, true, newAttributes); // throws: isBound
instanceBuffer.unbind();

// after
instanceBuffer.unbind();
instanceBuffer.setBuffer(newData, true, newAttributes);
instanceBuffer.bind(shader);
Defensive patterns

Strategy: validation

Validate before calling

// Call setBuffer only when unbound; enforce with your own wrapper:
void reconfigure(InstanceBufferObject ibo, ByteBuffer data, VertexAttributes attrs) {
    ibo.unbind(); // no-op safe when not bound in newer versions; else track state
    ibo.setBuffer(data, true, attrs);
}

Prevention

When it happens

Trigger: Calling setBuffer(...) between bind(shader) and unbind(); reconfiguring attributes from inside a render callback while the instance buffer is bound; a reassign method invoked during a batched draw loop before unbind() ran.

Common situations: Hot-swapping instance attributes (e.g. particle systems changing attribute layout) without unbinding first; refactoring a Mesh-style class so that buffer resets happen mid-draw; forgetting unbind() after an exception interrupted a draw sequence, leaving isBound latched true.

Related errors


AI-assisted analysis of libgdx/libgdx@97f4086187 (2026-08-14). Data as JSON: /api/errors/a18aca425986df0a. Report an issue: GitHub.