2 Copyright (c) 2019 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
10 <meta charset=
"utf-8">
11 <title>Switching transform feedback objects
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"></script>
17 <div id=
"description"></div>
18 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
19 <div id=
"console"></div>
20 <script id=
"vshader" type=
"x-shader/x-vertex">#version
300 es
26 out_value1 = in_value *
2.;
27 out_value2 = in_value *
4.;
30 <script id=
"fshader" type=
"x-shader/x-fragment">#version
300 es
31 precision mediump float;
39 description("Tests switching transform feedback objects.");
41 debug("<h3>Setup</h3>")
43 var wtu
= WebGLTestUtils
;
44 var canvas
= document
.getElementById("canvas");
45 var gl
= wtu
.create3DContext(canvas
, null, 2);
47 testFailed("WebGL context does not exist");
51 const prog_interleaved
= wtu
.setupTransformFeedbackProgram(gl
, ["vshader", "fshader"],
52 ["out_value1", "out_value2"], gl
.INTERLEAVED_ATTRIBS
,
54 const prog_no_varyings
= wtu
.setupTransformFeedbackProgram(gl
, ["vshader", "fshader"],
55 [], gl
.INTERLEAVED_ATTRIBS
,
57 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "shader compilation");
58 const vertexBuffer
= createBuffer(gl
, new Float32Array([1, 2, 3, 4]));
59 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexBuffer
);
60 gl
.enableVertexAttribArray(0);
61 gl
.vertexAttribPointer(0, 1, gl
.FLOAT
, false, 0, 0);
62 gl
.useProgram(prog_interleaved
);
63 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "vertex buffer and program setup");
65 const tf1
= gl
.createTransformFeedback();
66 const tf2
= gl
.createTransformFeedback();
67 const tfBuffer1
= createBuffer(gl
, new Float32Array([0, 0]));
68 const tfBuffer2
= createBuffer(gl
, new Float32Array([0, 0]));
69 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf1
);
70 gl
.bindBufferBase(gl
.TRANSFORM_FEEDBACK_BUFFER
, 0, tfBuffer1
);
71 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
72 gl
.bindBufferBase(gl
.TRANSFORM_FEEDBACK_BUFFER
, 0, tfBuffer2
);
73 const expected_tf_output
= [2, 4];
74 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "TF object setup");
76 debug("<h3>Baseline transform feedback success case</h3>");
78 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf1
);
79 gl
.beginTransformFeedback(gl
.POINTS
);
80 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "begin TF");
81 gl
.drawArrays(gl
.POINTS
, 0, 1);
82 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "draw");
83 gl
.endTransformFeedback();
84 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "end TF");
86 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer1
);
87 wtu
.checkFloatBuffer(gl
, gl
.TRANSFORM_FEEDBACK_BUFFER
, expected_tf_output
);
89 debug("<h3>Generic binding is not changed when switching TF object</h3>");
91 // According to the GL ES spec historically, TRANSFORM_FEEDBACK_BUFFER_BINDING is listed as part
92 // of the transform feedback object state. However, many drivers treat it as global context state
93 // and not part of the tranform feedback object, which means that it does not change when
94 // bindTransformFeedback is called. Khronos has resolved to change the spec to specify the latter
95 // behavior: https://gitlab.khronos.org/opengl/API/issues/66 (Khronos private link). This tests
96 // for the new behavior.
98 // Set each buffer to contain its buffer number. We use this to check which
99 // buffer is *really* bound at the driver level by reading the buffer contents.
100 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, null);
101 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer1
);
102 gl
.bufferData(gl
.TRANSFORM_FEEDBACK_BUFFER
, new Float32Array([1]), gl
.STREAM_READ
);
103 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer2
);
104 gl
.bufferData(gl
.TRANSFORM_FEEDBACK_BUFFER
, new Float32Array([2]), gl
.STREAM_READ
);
105 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "bufferData");
107 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf1
);
108 checkParameter(gl
.TRANSFORM_FEEDBACK_BUFFER_BINDING
, tfBuffer2
);
109 checkIndexedParameter(gl
.TRANSFORM_FEEDBACK_BUFFER_BINDING
, 0, tfBuffer1
);
110 wtu
.checkFloatBuffer(gl
, gl
.TRANSFORM_FEEDBACK_BUFFER
, [2]);
111 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "readback");
113 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, null);
114 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
115 checkParameter(gl
.TRANSFORM_FEEDBACK_BUFFER_BINDING
, null);
116 checkIndexedParameter(gl
.TRANSFORM_FEEDBACK_BUFFER_BINDING
, 0, tfBuffer2
);
118 debug("<h3>Error switching TF object while TF is enabled</h3>");
120 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf1
);
121 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer1
);
122 gl
.bufferData(gl
.TRANSFORM_FEEDBACK_BUFFER
, new Float32Array([0, 0]), gl
.STREAM_READ
);
123 gl
.beginTransformFeedback(gl
.POINTS
);
124 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "begin");
125 checkParameter(gl
.TRANSFORM_FEEDBACK_BINDING
, tf1
);
127 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
128 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "bind while unpaused");
130 // Check that nothing actually changed and rendering still works
131 checkParameter(gl
.TRANSFORM_FEEDBACK_BINDING
, tf1
);
132 gl
.drawArrays(gl
.POINTS
, 0, 1);
133 gl
.endTransformFeedback();
134 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "transform feedback should complete successfully");
135 wtu
.checkFloatBuffer(gl
, gl
.TRANSFORM_FEEDBACK_BUFFER
, expected_tf_output
);
138 debug("<h3>Successfully switching TF object while TF is paused</h3>");
140 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer1
);
141 gl
.bufferData(gl
.TRANSFORM_FEEDBACK_BUFFER
, new Float32Array([0, 0]), gl
.STREAM_READ
);
142 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer2
);
143 gl
.bufferData(gl
.TRANSFORM_FEEDBACK_BUFFER
, new Float32Array([0, 0]), gl
.STREAM_READ
);
145 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
146 gl
.beginTransformFeedback(gl
.POINTS
);
147 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "begin on tf2");
148 checkParameter(gl
.TRANSFORM_FEEDBACK_BINDING
, tf2
);
150 gl
.pauseTransformFeedback();
151 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf1
);
152 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "bind while paused");
153 gl
.beginTransformFeedback(gl
.POINTS
);
154 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "begin on tf1");
155 checkParameter(gl
.TRANSFORM_FEEDBACK_BINDING
, tf1
);
156 gl
.drawArrays(gl
.POINTS
, 0, 1);
157 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "draw should succeed");
158 gl
.endTransformFeedback();
159 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "end on tf1");
160 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer1
);
161 wtu
.checkFloatBuffer(gl
, gl
.TRANSFORM_FEEDBACK_BUFFER
, expected_tf_output
);
163 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
164 gl
.endTransformFeedback();
165 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "end on tf2");
167 debug("<h3>Misc. invalid operations</h3>")
169 gl
.endTransformFeedback();
170 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "endTransformFeedback before begin");
171 gl
.pauseTransformFeedback();
172 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "pauseTransformFeedback when not active");
173 gl
.resumeTransformFeedback();
174 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "pauseTransformFeedback when not active");
176 gl
.beginTransformFeedback(gl
.POINTS
);
177 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "transform feedback should begin successfully");
178 gl
.drawArrays(gl
.TRIANGLE_STRIP
, 0, 1);
179 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "wrong primitive mode");
180 gl
.useProgram(prog_no_varyings
);
181 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "switch program while active");
182 gl
.resumeTransformFeedback();
183 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "resumeTransformFeedback when not paused");
184 gl
.bindTransformFeedback(gl
.TRANSFORM_FEEDBACK
, tf2
);
185 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "bindTransformFeedback when active");
186 gl
.bindBuffer(gl
.TRANSFORM_FEEDBACK_BUFFER
, tfBuffer2
);
187 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "bindBuffer(TRANSFORM_FEEDBACK_BUFFER) when active");
188 gl
.bindBufferBase(gl
.TRANSFORM_FEEDBACK_BUFFER
, 0, tfBuffer2
);
189 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "bindBufferBase(TRANSFORM_FEEDBACK_BUFFER) when active");
191 gl
.pauseTransformFeedback();
192 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "pause");
193 gl
.pauseTransformFeedback();
194 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "already paused");
195 gl
.endTransformFeedback();
196 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "end while paused");
201 function createBuffer(gl
, dataOrSize
) {
202 const buf
= gl
.createBuffer();
203 gl
.bindBuffer(gl
.ARRAY_BUFFER
, buf
);
204 gl
.bufferData(gl
.ARRAY_BUFFER
, dataOrSize
, gl
.STATIC_DRAW
);
205 gl
.bindBuffer(gl
.ARRAY_BUFFER
, null);
209 function checkParameter(param
, expected
) {
210 const value
= gl
.getParameter(param
);
211 if (value
!= expected
) {
212 testFailed(wtu
.glEnumToString(gl
, param
) + " was " + value
+ ", but expected " + expected
);
214 testPassed(wtu
.glEnumToString(gl
, param
) + " was " + value
+ ", matching expected " + expected
);
218 function checkIndexedParameter(param
, index
, expected
) {
219 const value
= gl
.getIndexedParameter(param
, index
);
220 if (value
!= expected
) {
221 testFailed(wtu
.glEnumToString(gl
, param
) + "[" + index
+ "] was " + value
+ ", but expected " + expected
);
223 testPassed(wtu
.glEnumToString(gl
, param
) + "[" + index
+ "] was " + value
+ ", matching expected " + expected
);