Source code
Revision control
Copy as Markdown
Other Tools
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TF too small buffers</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">#version 300 es
in float in_value1;
in float in_value2;
out float out_value1;
out float out_value2;
void main() {
out_value1 = in_value1 * 2.;
out_value2 = in_value2 * 2.;
}
</script>
<script id="fshader" type="x-shader/x-fragment">#version 300 es
precision mediump float;
out vec4 dummy;
void main() {
dummy = vec4(0.);
}
</script>
<script>
"use strict";
description("Transform feedback into buffers that are too small should produce errors.");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, null, 2);
if (!gl) {
testFailed("WebGL context does not exist");
} else {
testPassed("WebGL context exists");
}
const progInterleaved = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
["out_value1", "out_value2"], gl.INTERLEAVED_ATTRIBS,
["in_value1", "in_value2"]);
const progSeparate = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"],
["out_value1", "out_value2"], gl.SEPARATE_ATTRIBS,
["in_value1", "in_value2"]);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "program compilation");
// Attrib 1 contains 4 vertices. Attrib 2 contains 4 instance indices.
const vertexBuffer0 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), gl.STATIC_DRAW);
const vertexBuffer1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(1, 1);
let tfBuffer0 = gl.createBuffer();
let tfBuffer1 = gl.createBuffer();
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup");
const sizeOfFloat = 4;
let cases = [
{ name: "drawArrays",
drawFunction: ()=>gl.drawArrays(gl.POINTS, 0, 4),
result: [[2, 4, 6, 8], [2, 2, 2, 2]]},
{ name: "drawArraysInstanced one instance",
drawFunction: ()=>gl.drawArraysInstanced(gl.POINTS, 0, 4, 1),
result: [[2, 4, 6, 8], [2, 2, 2, 2]]},
{ name: "drawArraysInstanced four instances",
drawFunction: ()=>gl.drawArraysInstanced(gl.POINTS, 0, 1, 4),
result: [[2, 2, 2, 2], [2, 4, 6, 8]]},
];
for (let {name, drawFunction, result} of cases) {
debug("<h1>" + name + "</h1>")
let interleavedResult = [];
for (let i = 0; i < result[0].length; i++) {
interleavedResult.push(result[0][i], result[1][i]);
}
let doTransformFeedback = (drawFunction, error) => {
gl.beginTransformFeedback(gl.POINTS);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before draw");
drawFunction();
wtu.glErrorShouldBe(gl, error, "draw");
gl.endTransformFeedback();
}
gl.useProgram(progInterleaved);
debug("<h3>interleaved - Baseline success case</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.NO_ERROR);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult);
debug("<h3>interleaved - Buffer too small</h3>")
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat-1, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
[0, 0, 0, 0, 0, 0, 0]);
debug("<h3>interleaved - Multiple draws success case</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat*2, gl.STREAM_READ);
doTransformFeedback(()=>{drawFunction(); drawFunction()}, gl.NO_ERROR);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult.concat(interleavedResult))
debug("<h3>interleaved - Too small for multiple draws</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat*2-1, gl.STREAM_READ);
doTransformFeedback(()=>{drawFunction(); drawFunction()}, gl.INVALID_OPERATION);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, interleavedResult.concat([0, 0, 0, 0, 0, 0, 0]))
debug("<h3>interleaved - bindBufferRange too small</h3>")
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0, 0, 7*sizeOfFloat);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
[0, 0, 0, 0, 0, 0, 0, 0]);
debug("<h3>interleaved - bindBufferRange larger than buffer</h3>")
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 8*sizeOfFloat-1, gl.STREAM_READ);
gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0, 0, 8*sizeOfFloat);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER,
[0, 0, 0, 0, 0, 0, 0]);
gl.useProgram(progSeparate);
debug("<h3>separate - Baseline success case</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.NO_ERROR);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0]);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1]);
debug("<h3>separate - Buffer too small</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat-1, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
debug("<h3>separate - multiple draws success case</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
doTransformFeedback(()=>{drawFunction(); drawFunction();}, gl.NO_ERROR);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0].concat(result[0]));
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1].concat(result[1]));
debug("<h3>separate - Too small for multiple draws</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2, gl.STREAM_READ);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat*2-1, gl.STREAM_READ);
doTransformFeedback(()=>{drawFunction(); drawFunction();}, gl.INVALID_OPERATION);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[0].concat([0, 0, 0, 0]));
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, result[1].concat([0, 0, 0]));
debug("<h3>separate - bindBufferRange too small</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1, 0, 3*sizeOfFloat);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
debug("<h3>separate - bindBufferRange larger than buffer</h3>")
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 1, tfBuffer1, 0, 4*sizeOfFloat);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*sizeOfFloat-1, gl.STREAM_READ);
doTransformFeedback(drawFunction, gl.INVALID_OPERATION);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer0);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0, 0]);
gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1);
wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [0, 0, 0]);
}
debug("<h1>integer overflow</h1>")
gl.useProgram(progInterleaved);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer0);
gl.bufferData(gl.ARRAY_BUFFER, (1<<16)*sizeOfFloat, gl.STREAM_READ);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer1);
gl.bufferData(gl.ARRAY_BUFFER, (1<<16)*sizeOfFloat, gl.STREAM_READ);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer0);
gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, (1<<16)*sizeOfFloat*2, gl.STREAM_READ);
gl.beginTransformFeedback(gl.POINTS);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before draw");
// If count and primcount are stored in 32-bit signed integers and then
// multiplied to calculate the number of transform feedback vertices, the
// calculation will overflow to 0.
gl.drawArraysInstanced(gl.POINTS, 0, 1<<16, 1<<16);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "integer overflow and/or buffer too small");
gl.endTransformFeedback();
finishTest();
</script>
</body>
</html>