11import { Engine } from "../Engine" ;
2+ import { IPlatformTransformFeedbackPrimitive } from "../renderingHardwareInterface" ;
23import { ShaderProgram } from "../shader/ShaderProgram" ;
34import { Buffer } from "./Buffer" ;
45import { BufferBindFlag } from "./enums/BufferBindFlag" ;
56import { BufferUsage } from "./enums/BufferUsage" ;
7+ import { MeshTopology } from "./enums/MeshTopology" ;
68import { TransformFeedback } from "./TransformFeedback" ;
79import { VertexBufferBinding } from "./VertexBufferBinding" ;
810import { VertexElement } from "./VertexElement" ;
9- import { MeshTopology } from "./enums/MeshTopology" ;
1011
1112/**
1213 * @internal
13- * A primitive that manages ping-pong buffers and VAOs for Transform Feedback rendering.
14- * Handles buffer creation, VAO setup, TF draw with bindBufferRange, and ping-pong swap.
14+ * Transform Feedback primitive that manages buffers, draw calls, and state for GPU-side data capture.
1515 */
1616export class TransformFeedbackPrimitive {
17+ /** @internal */
18+ _platformPrimitive : IPlatformTransformFeedbackPrimitive ;
19+
1720 private _engine : Engine ;
21+ private _transformFeedback : TransformFeedback ;
1822 private _readBuffer : Buffer ;
1923 private _writeBuffer : Buffer ;
20- private _transformFeedback : TransformFeedback ;
21-
22- // VAO pair for current program (one per ping-pong direction)
23- private _vaoA : WebGLVertexArrayObject ;
24- private _vaoB : WebGLVertexArrayObject ;
25- private _useA = true ;
26- private _lastProgramId = - 1 ;
27-
2824 private _renderBufferBinding : VertexBufferBinding ;
2925 private _byteStride : number ;
30- private _vertexCount = 0 ;
31- private _initialized = false ;
26+ private _readIsFirst = true ;
3227
28+ /**
29+ * Buffer binding for the render pass (points to the latest TF output).
30+ */
3331 get currentRenderBufferBinding ( ) : VertexBufferBinding {
3432 return this . _renderBufferBinding ;
3533 }
3634
35+ /**
36+ * The current read buffer (TF input / render source).
37+ */
3738 get readBuffer ( ) : Buffer {
3839 return this . _readBuffer ;
3940 }
4041
42+ /**
43+ * The current write buffer (TF output target).
44+ */
4145 get writeBuffer ( ) : Buffer {
4246 return this . _writeBuffer ;
4347 }
4448
45- get initialized ( ) : boolean {
46- return this . _initialized ;
47- }
48-
49+ /**
50+ * @param engine - Engine instance
51+ * @param byteStride - Bytes per vertex in the TF buffer
52+ */
4953 constructor ( engine : Engine , byteStride : number ) {
5054 this . _engine = engine ;
5155 this . _byteStride = byteStride ;
5256 this . _transformFeedback = new TransformFeedback ( engine ) ;
5357 this . _transformFeedback . isGCIgnored = true ;
58+ this . _platformPrimitive = engine . _hardwareRenderer . createPlatformTransformFeedbackPrimitive ( ) ;
5459 }
5560
5661 /**
57- * Resize ping-pong buffers for the given vertex count.
62+ * Resize buffers.
63+ * @param vertexCount - Number of vertices to allocate
5864 */
5965 resize ( vertexCount : number ) : void {
60- if ( vertexCount === this . _vertexCount && this . _initialized ) return ;
61-
62- const engine = this . _engine ;
63- const byteLength = this . _byteStride * vertexCount ;
64-
6566 this . _readBuffer ?. destroy ( ) ;
6667 this . _writeBuffer ?. destroy ( ) ;
6768
68- const readBuffer = new Buffer ( engine , BufferBindFlag . VertexBuffer , byteLength , BufferUsage . Dynamic , false ) ;
69+ const byteLength = this . _byteStride * vertexCount ;
70+ const readBuffer = new Buffer ( this . _engine , BufferBindFlag . VertexBuffer , byteLength , BufferUsage . Dynamic , false ) ;
6971 readBuffer . isGCIgnored = true ;
70- const writeBuffer = new Buffer ( engine , BufferBindFlag . VertexBuffer , byteLength , BufferUsage . Dynamic , false ) ;
72+ const writeBuffer = new Buffer ( this . _engine , BufferBindFlag . VertexBuffer , byteLength , BufferUsage . Dynamic , false ) ;
7173 writeBuffer . isGCIgnored = true ;
7274
73- const floatStride = this . _byteStride / 4 ;
74- const zeroData = new Float32Array ( vertexCount * floatStride ) ;
75- readBuffer . setData ( zeroData ) ;
76- writeBuffer . setData ( zeroData ) ;
77-
7875 this . _readBuffer = readBuffer ;
7976 this . _writeBuffer = writeBuffer ;
80- this . _renderBufferBinding = new VertexBufferBinding ( this . _readBuffer , this . _byteStride ) ;
81- this . _vertexCount = vertexCount ;
82- this . _initialized = true ;
83- // Force VAO rebuild on next updateVAOs
84- this . _lastProgramId = - 1 ;
77+ this . _renderBufferBinding = new VertexBufferBinding ( readBuffer , this . _byteStride ) ;
8578 }
8679
8780 /**
88- * Ensure VAOs are up-to-date. Automatically rebuilds when program or buffers change.
81+ * Prepare for drawing. Updates attribute bindings if needed and binds state.
82+ * @param program - The shader program
83+ * @param feedbackElements - Vertex elements for the feedback buffer
84+ * @param inputBinding - Input buffer binding (e.g., instance data)
85+ * @param inputElements - Vertex elements for the input buffer
8986 */
90- updateVAOs (
87+ beginDraw (
9188 program : ShaderProgram ,
92- tfElements : VertexElement [ ] ,
93- extraBindings : { binding : VertexBufferBinding ; elements : VertexElement [ ] } [ ]
89+ feedbackElements : VertexElement [ ] ,
90+ inputBinding : VertexBufferBinding ,
91+ inputElements : VertexElement [ ]
9492 ) : void {
95- if ( program . id === this . _lastProgramId ) return ;
96-
97- const gl = this . _engine . _hardwareRenderer . gl as WebGL2RenderingContext ;
98-
99- this . _deleteVAOs ( gl ) ;
100-
101- this . _vaoA = this . _createVAO ( gl , program , this . _readBuffer , tfElements , extraBindings ) ;
102- this . _vaoB = this . _createVAO ( gl , program , this . _writeBuffer , tfElements , extraBindings ) ;
103- this . _lastProgramId = program . id ;
104-
105- gl . bindVertexArray ( null ) ;
93+ this . _platformPrimitive . update (
94+ program ,
95+ this . _readBuffer ,
96+ this . _writeBuffer ,
97+ this . _byteStride ,
98+ feedbackElements ,
99+ inputBinding ,
100+ inputElements
101+ ) ;
102+ this . _platformPrimitive . bind ( this . _readIsFirst ) ;
106103 }
107104
108105 /**
109- * Bind the current VAO for TF drawing.
106+ * Execute a single TF draw call for a vertex range.
107+ * @param mode - Primitive topology
108+ * @param first - First vertex index
109+ * @param count - Number of vertices to process
110110 */
111- bindVAO ( ) : void {
112- const gl = this . _engine . _hardwareRenderer . gl as WebGL2RenderingContext ;
113- gl . bindVertexArray ( this . _useA ? this . _vaoA : this . _vaoB ) ;
111+ draw ( mode : MeshTopology , first : number , count : number ) : void {
112+ const transformFeedback = this . _transformFeedback ;
113+ transformFeedback . bind ( ) ;
114+ transformFeedback . bindBufferRange ( 0 , this . _writeBuffer , first * this . _byteStride , count * this . _byteStride ) ;
115+ transformFeedback . begin ( mode ) ;
116+ this . _platformPrimitive . draw ( mode , first , count ) ;
117+ transformFeedback . end ( ) ;
118+ transformFeedback . unbindBuffer ( 0 ) ;
119+ transformFeedback . unbind ( ) ;
114120 }
115121
116122 /**
117- * Unbind VAO after TF drawing .
123+ * Finish drawing and unbind state .
118124 */
119- unbindVAO ( ) : void {
120- const gl = this . _engine . _hardwareRenderer . gl as WebGL2RenderingContext ;
121- gl . bindVertexArray ( null ) ;
122- }
123-
124- /**
125- * Execute a TF draw call for a range of vertices.
126- */
127- draw ( rhi : any , mode : MeshTopology , first : number , count : number ) : void {
128- const byteOffset = first * this . _byteStride ;
129- const byteSize = count * this . _byteStride ;
130- this . _transformFeedback . bind ( ) ;
131- this . _transformFeedback . bindBufferRange ( 0 , this . _writeBuffer , byteOffset , byteSize ) ;
132- this . _transformFeedback . begin ( mode ) ;
133- rhi . drawArrays ( mode , first , count ) ;
134- this . _transformFeedback . end ( ) ;
135- // Unbind TF buffer from TRANSFORM_FEEDBACK_BUFFER target to avoid
136- // conflicts when the same buffer is bound as ARRAY_BUFFER in render pass.
137- this . _transformFeedback . unbindBuffer ( 0 ) ;
138- this . _transformFeedback . unbind ( ) ;
125+ endDraw ( ) : void {
126+ this . _platformPrimitive . unbind ( ) ;
139127 }
140128
141129 /**
@@ -145,66 +133,14 @@ export class TransformFeedbackPrimitive {
145133 const temp = this . _readBuffer ;
146134 this . _readBuffer = this . _writeBuffer ;
147135 this . _writeBuffer = temp ;
148- this . _useA = ! this . _useA ;
136+ this . _readIsFirst = ! this . _readIsFirst ;
149137 this . _renderBufferBinding = new VertexBufferBinding ( this . _readBuffer , this . _byteStride ) ;
150138 }
151139
152140 destroy ( ) : void {
153- const gl = this . _engine . _hardwareRenderer . gl as WebGL2RenderingContext ;
154- this . _deleteVAOs ( gl ) ;
141+ this . _platformPrimitive ?. destroy ( ) ;
155142 this . _readBuffer ?. destroy ( ) ;
156143 this . _writeBuffer ?. destroy ( ) ;
157144 this . _transformFeedback ?. destroy ( ) ;
158145 }
159-
160- private _deleteVAOs ( gl : WebGL2RenderingContext ) : void {
161- if ( this . _vaoA ) {
162- gl . deleteVertexArray ( this . _vaoA ) ;
163- this . _vaoA = null ;
164- }
165- if ( this . _vaoB ) {
166- gl . deleteVertexArray ( this . _vaoB ) ;
167- this . _vaoB = null ;
168- }
169- }
170-
171- private _createVAO (
172- gl : WebGL2RenderingContext ,
173- program : ShaderProgram ,
174- tfReadBuffer : Buffer ,
175- tfElements : VertexElement [ ] ,
176- extraBindings : { binding : VertexBufferBinding ; elements : VertexElement [ ] } [ ]
177- ) : WebGLVertexArrayObject {
178- const vao = gl . createVertexArray ( ) ;
179- gl . bindVertexArray ( vao ) ;
180-
181- const attribs = program . attributeLocation ;
182-
183- tfReadBuffer . bind ( ) ;
184- this . _bindElements ( gl , attribs , tfElements , this . _byteStride ) ;
185-
186- for ( const { binding, elements } of extraBindings ) {
187- binding . buffer . bind ( ) ;
188- this . _bindElements ( gl , attribs , elements , binding . stride ) ;
189- }
190-
191- gl . bindBuffer ( gl . ARRAY_BUFFER , null ) ;
192- return vao ;
193- }
194-
195- private _bindElements (
196- gl : WebGL2RenderingContext ,
197- attribs : Record < string , number > ,
198- elements : VertexElement [ ] ,
199- stride : number
200- ) : void {
201- for ( const element of elements ) {
202- const loc = attribs [ element . attribute ] ;
203- if ( loc !== undefined && loc !== - 1 ) {
204- const info = element . _formatMetaInfo ;
205- gl . enableVertexAttribArray ( loc ) ;
206- gl . vertexAttribPointer ( loc , info . size , info . type , info . normalized , stride , element . offset ) ;
207- }
208- }
209- }
210146}
0 commit comments