Skip to content

Add support transform feedback separate buffer mode#9109

Open
AlexAPPi wants to merge 18 commits into
playcanvas:mainfrom
AlexAPPi:alex-tf-separate-slots
Open

Add support transform feedback separate buffer mode#9109
AlexAPPi wants to merge 18 commits into
playcanvas:mainfrom
AlexAPPi:alex-tf-separate-slots

Conversation

@AlexAPPi

Copy link
Copy Markdown
Contributor

Description

Adding separate buffer mode for transform feedback would expand the engine’s capabilities and make GPU pipelines more flexible. Interleaved mode is great for compact data packing, but separate provides cleaner separation of concerns and works better when different outputs should be stored and consumed independently.

Key benefits:

  • More flexible data architecture. Positions, velocities, normals, and auxiliary attributes can be stored separately.
  • Better bandwidth usage. Later passes can read only the buffers they need instead of an entire interleaved block.
  • Better fit for simulations. Particle systems, trails, crowds, skinning, and similar GPU workflows map naturally to separate layouts.
  • Easier data reuse. Individual buffers can be plugged into different rendering or compute stages more easily.
  • Clearer API. Developers can explicitly control where each output goes.
  • Simpler debugging. Issues in one data stream are isolated instead of being mixed into one packed buffer.

This change adds a new extension to the existing API:

    /**
     * Sets the output vertex buffer. It will be written to by a shader with transform feedback
     * varyings.
     *
     * @param {VertexBuffer} tf - The output vertex buffer.
     * @param {number} [slot] - The buffer slot index.
     * @ignore
     */
    setTransformFeedbackBuffer(tf, slot = 0): void;

Fixes #9031

Checklist

  • I have read the contributing guidelines
  • My code follows the project's coding standards
  • This PR focuses on a single change

Comment thread src/platform/graphics/shader.js Outdated
@mvaligursky

Copy link
Copy Markdown
Contributor

It'd be great to add an example. It can be a hidden test examples in test folder as well, that's just tests the functionality without having to look particularly good.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for transform feedback “separate” buffer mode and multi-slot transform feedback buffer binding on the WebGL backend, extending the existing transform feedback plumbing to better match WebGL2’s indexed TF buffer bindings (Issue #9031).

Changes:

  • Added feedbackVaryingsMode shader definition option to choose between INTERLEAVED_ATTRIBS and SEPARATE_ATTRIBS when configuring transform feedback varyings.
  • Extended WebglGraphicsDevice#setTransformFeedbackBuffer to support a buffer slot and updated draw-time TF buffer binding/unbinding to iterate slots.
  • Updated JSDoc to document the new shader option and added a runtime warning in the TransformFeedback helper when used with separate mode.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/platform/graphics/webgl/webgl-shader.js Selects TF varying buffer mode (interleaved vs separate) during program link.
src/platform/graphics/webgl/webgl-graphics-device.js Adds multi-slot TF buffer tracking and binds/unbinds all active TF slots around draws.
src/platform/graphics/transform-feedback.js Warns when the TransformFeedback helper is used with separate varyings mode.
src/platform/graphics/shader.js Documents new definition.feedbackVaryingsMode shader option.
src/platform/graphics/shader-definition-utils.js Plumbs feedbackVaryingsMode through shader definition creation and documents it.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/platform/graphics/webgl/webgl-shader.js Outdated
Comment thread src/platform/graphics/webgl/webgl-graphics-device.js Outdated
Comment on lines +2439 to +2463
const gl = this.gl;
const buffers = this.transformFeedbackBuffers;

let len = buffers.length;

// Let's expand the array up to the slot index.
if (len <= slot) {
for (let i = len; i <= slot; i++) {
buffers.push(null);
}
len = slot + 1;
}

buffers[slot] = tf;

// Let's take the last slot available in the array.
let numSlots = 0;
for (let i = len - 1; i >= 0; i--) {
if (buffers[i]) {
numSlots = i + 1;
break;
}
}

this.transformFeedbackNumSlots = numSlots;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old API works with slot 0, so there shouldn't be any issues; the added loop, conversely, might mask the error, making it impossible for the user to debug it.

Comment thread src/platform/graphics/webgl/webgl-graphics-device.js Outdated
Comment thread src/platform/graphics/transform-feedback.js Outdated
@AlexAPPi
AlexAPPi requested a review from mvaligursky July 23, 2026 16:48
@AlexAPPi

Copy link
Copy Markdown
Contributor Author

It'd be great to add an example. It can be a hidden test examples in test folder as well, that's just tests the functionality without having to look particularly good.

It looks like there is no WebGL2 mock implemented yet—I just noticed this. I created my own version, but perhaps it would be better to submit it as a separate PR?

Comment thread src/platform/graphics/constants.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

src/platform/graphics/webgl/webgl-graphics-device.js:2057

  • Debug.assert(bufId, ...) treats any falsy value as invalid. While WebGLBuffer objects are truthy, this can still produce false assertions (and a vague message) with mocks or alternative implementations. Also, accessing buf.impl without optional chaining can throw if a non-VertexBuffer is accidentally stored.
                        const buf = tfb[i];
                        const bufId = buf?.impl.bufferId ?? null;
                        Debug.assert(bufId, 'Transform feedback buffer slot value is null.');
                        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, i, bufId);

Comment thread test/platform/graphics/webgl/transform-feedback.test.mjs Outdated
Comment thread test/platform/graphics/webgl/transform-feedback.test.mjs Outdated
Comment thread test/canvas-webgl-mock.js
Comment thread src/platform/graphics/webgl/webgl-graphics-device.js
@AlexAPPi
AlexAPPi requested a review from mvaligursky July 24, 2026 11:05
@mvaligursky

Copy link
Copy Markdown
Contributor

As mentioned initially, I would really like to see either engine example for public, or engine example in test folder for internal testing. Unit test does not proof this actually executes on WebGL.

@AlexAPPi

Copy link
Copy Markdown
Contributor Author

As mentioned initially, I would really like to see either engine example for public, or engine example in test folder for internal testing. Unit test does not proof this actually executes on WebGL.

This example wore me out a bit—I couldn't think of anything simpler than a series. I hope it turned out to be clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for multiple transform feedback buffer slots

3 participants