使用 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的一个帮手。它创建的数据将在您的应用程序需要使用该数据的任何情况下使用。它不知道您是否正在跨帧缓冲区共享附件(常见),因此它无法为您管理资源。由你决定。
我正在使用 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的一个帮手。它创建的数据将在您的应用程序需要使用该数据的任何情况下使用。它不知道您是否正在跨帧缓冲区共享附件(常见),因此它无法为您管理资源。由你决定。