[当我使用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:缓冲区绑定了变换反馈。
var fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0)
,似乎干扰了转换反馈。 checkSync函数似乎可以正常工作。我的问题是1)我在哪里出错了? 2)这是一种可以通过一些调整而适用于我的用例的技术,还是我需要尝试完全不同的方法?
[当我使用getBufferSubData从Chrome的转换缓冲区中将顶点数据读取到Float32Array时,我收到警告“性能警告:已读回READ-USAGE缓冲区,而没有等待隔离栅。...