为了防止getBufferSubData图形管道停顿而在转换反馈上进行回调

问题描述 投票:0回答:1

[当我使用getBufferSubData从Chrome的转换缓冲区中将顶点数据读取到Float32Array时,我收到警告“ 性能警告:没有在栅栏上等待就回读了READ-usage缓冲区。这导致图形管线停顿。] >”。我的理解是,一旦调用getBufferSubData(可能是在着色器完成之前),GPU就会尝试将顶点数据写回到CPU。我认为,如果可以防止这种情况,我也许可以加快我的应用程序的速度,我认为最好的方法是使用回调。为了澄清,返回的数据是正确的;我希望加快我的应用程序的速度并更好地了解发生了什么。

我已尝试使用fenceSync实现回调,类似于this answer。这应该在执行getBufferSubData之前检查GPU是否已完成执行当前命令(包括转换反馈)。这是我的代码。

(function () {
    'use strict';

    const createRandomF32Array = (arrSize) => {
        return Float32Array.from({length: arrSize}, () => Math.floor(Math.random() * 1000));
    };

    const createGlContext = () => {
        const canvas = document.createElement("canvas");
        const gl = canvas.getContext("webgl2");
        canvas.id = 'webgl_canvas';
        document.body.appendChild(canvas);
        if (gl === null) {
            alert("Unable to initialize WebGL. Your browser or machine may not support it.");
            return;
          }
        return gl;
    };

    // creates a single set of linked shaders containing a vertex and a fragment shader
    class shaderProgram {
        constructor(gl, rawVertex, rawFragment, transformFeedbackAttribs=false) {
            this.gl = gl;
            const compiledVertex = this.compileShader(gl.VERTEX_SHADER, rawVertex);
            const compiledFragment = this.compileShader(gl.FRAGMENT_SHADER, rawFragment);
            this.program = this.createProgram(compiledVertex, compiledFragment, transformFeedbackAttribs);
            this.attributeLocations = {};
            this.uniformLocations = {};
        }
        // run on init
        compileShader(shaderType, shaderSource) {
            const gl = this.gl;
            var shader = gl.createShader(shaderType);
            gl.shaderSource(shader, shaderSource);
            gl.compileShader(shader);
            var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
            if (success) {
              return shader;
            }
            console.log(gl.getShaderInfoLog(shader));
            gl.deleteShader(shader);
          }
        // run on init
        createProgram = (rawVertex, rawFragment, transformFeedbackAttribs) => {
            const gl = this.gl;
            var program = gl.createProgram();
            gl.attachShader(program, rawVertex);
            gl.attachShader(program, rawFragment);

            if (!(transformFeedbackAttribs === false)) {
                gl.transformFeedbackVaryings(program, [transformFeedbackAttribs], gl.INTERLEAVED_ATTRIBS);
            }
            gl.linkProgram(program);
            var success = gl.getProgramParameter(program, gl.LINK_STATUS);
            if (success) {
              return program;
            }
            console.log(gl.getProgramInfoLog(program));
            gl.deleteProgram(program);
        }

        logAttributeLocations = (attributeName) => {
            const gl = this.gl;
            const attributeLocation = gl.getAttribLocation(this.program, attributeName);
            if (!(attributeName in this.attributeLocations)) {
                this.attributeLocations[attributeName] = attributeLocation;
            }
            return attributeLocation;
        }

        logUniformLocations = (uniformName) => {
            const gl = this.gl;
            const uniformLocation = gl.getUniformLocation(this.program, uniformName);
            if (!(uniformName in this.uniformLocations)) {
                this.uniformLocations[uniformName] = uniformLocation;
            }
            return uniformLocation;
        }

        activate = () => {
            const gl = this.gl;
            gl.useProgram(this.program);
        }

        deactivate = () => {
            const gl = this.gl;
            gl.useProgram(0);
        }

    }

    // the aim of this class is to build a buffer to be sent to the gpu
    class renderObject {
        constructor(gl) {
            this.gl = gl;
            this.vao = this.gl.createVertexArray();
            this.buffers = {};
        }

        addDataToShaderAttribute = (dataset, dataDimension, attributeLocation) => {
            const gl = this.gl;
            var attributeVboNumber = this.addDataToBuffer(dataset);
            gl.bindVertexArray(this.vao);
            gl.enableVertexAttribArray(attributeLocation);
            gl.vertexAttribPointer(attributeLocation, dataDimension, gl.FLOAT, false, 0, 0);
            return attributeVboNumber;
        }

        prepareDataForShaderUniform = (dataset) => {
            const gl = this.gl;
            var uniformVboNumber = this.addDataToBuffer(dataset);
            return uniformVboNumber;
        }

        addDataToBuffer = (dataset) => {
            const gl = this.gl;
            var vertexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, dataset, gl.STATIC_DRAW);
            var bufferNumber = Object.keys(this.buffers).length;
            this.buffers[bufferNumber] = vertexBuffer;
            return bufferNumber;
        }

        draw = (drawType, offset, dataLength) => {
            const gl = this.gl;
            gl.drawArrays(drawType, offset, dataLength);
        }

        calculateAndRetreive = (drawType, offset, dataLength) => {
            const gl = this.gl;
            var transformBuffer = gl.createBuffer();
            var emptyDataArray = new Float32Array(dataLength);
            gl.enable(gl.RASTERIZER_DISCARD);

            gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, transformBuffer);
            gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, emptyDataArray, gl.STATIC_READ);
            var bufferNumber = Object.keys(this.buffers).length;
            this.buffers[bufferNumber] = transformBuffer;
        
            gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, transformBuffer);
            gl.beginTransformFeedback(gl.POINTS);
            gl.drawArrays(gl.POINTS, offset, dataLength);
            gl.endTransformFeedback();
            var arrBuffer = emptyDataArray;
            gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer);
            this.callbackOnSync(this.returnBufferData, emptyDataArray);
        }

        callbackOnSync = (callback, param) => {
            const gl = this.gl;

            var fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
            gl.flush();
            setTimeout(checkSync);

            function checkSync() {
                console.log(fence);
                const status = gl.clientWaitSync(fence, 0, 0);
                console.log(status);
                if (status == gl.CONDITION_SATISFIED) {
                    gl.deleteSync(fence);
                    return callback(param);
                } else {
                    return(setTimeout(checkSync));
                }
            }
        }

        returnBufferData = (arrBuffer) => {
            const gl = this.gl;

            gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer);
            console.log(arrBuffer);
            return arrBuffer;
        }

    }

    var testVertex = "#version 300 es\r\n\r\nin float a_position;\r\nout float o_position;\r\n\r\nvoid main() {\r\n    o_position = float(a_position + 5.0);\r\n}";

    var testFragment = "#version 300 es\r\nprecision mediump float;\r\n\r\nout vec4 o_FragColor;\r\n\r\nvoid main() {\r\n  o_FragColor = vec4(0.0);\r\n}";

    const gl = createGlContext();
    var positions = createRandomF32Array(1000);

    var t0 = performance.now();

    var testShader = new shaderProgram(gl, testVertex, testFragment, "o_position");
    var aPositionAttribute = testShader.logAttributeLocations("a_position");
    var uResolutionUniform = testShader.logUniformLocations("u_resolution");

    var pointsBuffer = new renderObject(gl);
    var dataBuffer = pointsBuffer.addDataToShaderAttribute(positions, 1, aPositionAttribute);

    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);

    testShader.activate();
    var output = pointsBuffer.calculateAndRetreive(gl.TRIANGLES, 0, positions.length, testShader);

    var t1 = performance.now();
    console.log("GPU function took " + (t1 - t0) + " milliseconds.");

    console.log(output);

}());
<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <head>
        <title>Rollup Example</title>
    </head>

    <body>
    </body>

    <script src="../build/bundle.min.js"></script>
</html>

这会发出警告“ GL_INVALID_OPERATION:缓冲区绑定了变换反馈。

”,并且返回数组中的每个值均为0。引起问题的行似乎是:
var fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0)

,似乎干扰了转换反馈。 checkSync函数似乎可以正常工作。我的问题是1)我在哪里出错了? 2)这是一种可以通过一些调整而适用于我的用例的技术,还是我需要尝试完全不同的方法?

[当我使用getBufferSubData从Chrome的转换缓冲区中将顶点数据读取到Float32Array时,我收到警告“性能警告:已读回READ-USAGE缓冲区,而没有等待隔离栅。...

webgl webgl2
1个回答
0
投票
所以我认为这可能是a bug in Chrome。您的代码在Mac Chrome上有效,但在Windows Chrome上失败。
© www.soinside.com 2019 - 2024. All rights reserved.