使用 twgl 和 framebuffers 会导致 GPU 中的内存泄漏并导致浏览器崩溃(附加的 plunkr)

Using twgl and framebuffers results in memory leak in GPU and crashes the browser(plunkr attached)

我正在使用 twgl.js 库在 webgl 中编写直方图应用程序。 我已经成功实施了。但是它很容易使浏览器崩溃。

我在这里添加了 plunkr @ https://plnkr.co/edit/hK9YXyT0Cj9BEUowYiKVSubH?p=info

大部分渲染部分在renderer.js。

请从本地机器浏览 jpeg 并在启用 GPU 内存后使用 SHIFT + ESC 监控 chrome GPU 内存。 加载图像后,它将计算图像中该区域的直方图并自行缩放 out/in,每次缩放都会增加内存。

问题是它只用一张图片就让 GPU 崩溃了。 为了尽快解决问题,我添加了一个 setInterval,它每 100 毫秒调用一次以重新渲染图像并计算直方图。

代码:

`

var prepareHistogram = function (img) {
//arrays.texcoord = [0.2, 0.2, 1.0, 0.2, 0.2, 1.0, 1.0, 1.0];
gl.arrays = arrays;
gl.arrays.position.data = [-1, -1, 1, -1, -1, 1, 1, 1];
gl.arrays.position.numComponents = 2;
gl.arrays.texcoord.numComponents = 2;
//gl.arrays.texcoord = [0.2, 0.2, 1.0, 0.2, 0.2, 1.0, 1.0, 1.0];

quadBufferInfo = twgl.createBufferInfoFromArrays(gl, gl.arrays);
//quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
quadBufferInfo.indices = null;

var newFbi = twgl.createFramebufferInfo(gl);
twgl.bindFramebufferInfo(gl, newFbi);

gl.useProgram(newProgramInfo.program);
twgl.setBuffersAndAttributes(gl, newProgramInfo, quadBufferInfo);
twgl.setUniforms(newProgramInfo, {
    u_texture: texture,
    u_resolution: [img.width, img.height]
});
twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);
 /* twgl.bindFramebufferInfo(gl, null);
twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);


return;   */


numIds = img.width * img.height;
pixelIds = dummyPixelIds.subarray(0, numIds);
var pixelIdBufferInfo = twgl.createBufferInfoFromArrays(gl, {
    pixelId: {
    size: 2,
    data: pixelIds,
    numComponents : 1
    }
});

// make a 256x1 RGBA floating point texture and attach to a framebuffer
var sumFbi = twgl.createFramebufferInfo(gl, [{
    type: gl.FLOAT,
    min: gl.NEAREST,
    mag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
}, ], 256, 1);

if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) {
    alert("can't render to floating point texture");
}

// Render sum of each color

// we're going to render a gl.POINT for each pixel in the source image
// That point will be positioned based on the color of the source image
// we're just going to render vec4(1,1,1,1). This blend function will
// mean each time we render to a specific point that point will get
// incremented by 1.
gl.blendFunc(gl.ONE, gl.ONE);
gl.enable(gl.BLEND);
gl.useProgram(histProgramInfo.program);

twgl.setBuffersAndAttributes(gl, histProgramInfo, pixelIdBufferInfo);
twgl.bindFramebufferInfo(gl, sumFbi);

// render each channel separately since we can only position each POINT
// for one channel at a time.
gl.colorMask(true, true, true, false);
twgl.setUniforms(histProgramInfo, {  
    u_texture: newFbi.attachments[0],  
    u_resolution: [img.width, img.height]
});
twgl.drawBufferInfo(gl, gl.POINTS, pixelIdBufferInfo);

gl.colorMask(true, true, true, true);
gl.blendFunc(gl.ONE, gl.ZERO);
gl.disable(gl.BLEND);

// render-compute min
// We're rendering are 256x1 pixel sum texture to a single 1x1 pixel texture

// make a 229x1 pixel RGBA, FLOAT texture attached to a framebuffer
var maxFbi = twgl.createFramebufferInfo(gl, [{
    type: gl.FLOAT,
    min: gl.NEAREST,
    mag: gl.NEAREST,
    wrap: gl.CLAMP_TO_EDGE,
}, ], 229, 1);

twgl.bindFramebufferInfo(gl, maxFbi);

gl.useProgram(maxProgramInfo.program);
twgl.setBuffersAndAttributes(gl, maxProgramInfo, quadBufferInfo);
twgl.setUniforms(maxProgramInfo, {
    u_texture: sumFbi.attachments[0],
    pixelCount : pixelCount
});
twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);
// render histogram.
//twgl.bindFramebufferInfo(gl, null);
var newFbi2 = twgl.createFramebufferInfo(gl);
twgl.bindFramebufferInfo(gl, newFbi2);

gl.useProgram(showProgramInfo.program);
twgl.setBuffersAndAttributes(gl, showProgramInfo, quadBufferInfo);
twgl.setUniforms(showProgramInfo, {
    u_resolution: [img.width, img.height],
    u_res: [img.width, img.height],
    u_maxTexture: maxFbi.attachments[0],
    inputImage : newFbi.attachments[0]
});
twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);

twgl.bindFramebufferInfo(gl, null);

gl.useProgram(showProgramInfo2.program);

twgl.setUniforms(showProgramInfo2, {
    u_texture: newFbi2.attachments[0],
    u_resolution:  [gl.canvas.width, gl.canvas.height]
});


//arrays.texcoord.numComponents = 2;
//arrays.position.data = [ar.x1, ar.y1, ar.x2, ar.y1, ar.x1, ar.y2, ar.x2, ar.y2];
//gl.arrays = arrays;
//quadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);

//gl.arrays = arrays;

gl.arrays.position.data = [ar.x1, ar.y1, ar.x2, ar.y1, ar.x1, ar.y2, ar.x2, ar.y2];
//gl.arrays.texcoord = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
/* gl.arrays.texcoord = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
gl.arrays.texcoord.numComponents = 2;
gl.arrays.position.numComponents = 2; */

quadBufferInfo = twgl.createBufferInfoFromArrays(gl, gl.arrays);

quadBufferInfo.indices = null;
twgl.setBuffersAndAttributes(gl, showProgramInfo2, quadBufferInfo);
twgl.drawBufferInfo(gl, gl.TRIANGLES, quadBufferInfo);

}
`

代码通过调用 twgl.createFramebufferInfo 在每个时间间隔分配多个大纹理,因此代码最终 运行 内存不足。

如果可能,您应该在初始化时分配您的纹理。换句话说,在初始时调用 twgl.createFrameBufferInfo,如果您以后需要它们的大小不同,请调用 twgl.resizeFramebufferInfo 来更改它们的大小。

否则由你来释放它们

function freeFramebufferInfoResources(gl, fbi) {
  for (const attachment of fbi.attachments) {
    if (attachment instanceof WebGLTexture) {
      gl.deleteTexture(attachment);
    } else {
      gl.deleteRenderbuffer(attachment);
    }
  }
  gl.deleteFramebuffer(fbi.framebuffer);
}

至于为什么twgl不包含这个函数是因为twgl只是WebGL的一个帮手。它创建的数据将在您的应用程序需要使用该数据的任何情况下使用。它不知道您是否正在跨帧缓冲区共享附件(常见),因此它无法为您管理资源。由你决定。