WebGPU绘图出现'a valid external instance reference no longer exists'警告排查
WebGPU绘制线条报错"a valid external instance reference no longer exists"的定位与解决
错误定位
- 渲染纹理内存泄漏:每次
render执行都会新建MSAA纹理,但从未销毁。GPU资源耗尽后就会触发外部实例引用失效的警告。 - Buffer扩容逻辑错误:
addPoint里的判断条件points.length % pointsPerBuffer * 2 === 0有运算符优先级问题,实际应该判断点数(points.length/2)是否达到pointsPerBuffer。错误的条件会导致扩容时机混乱,甚至在GPU还在使用旧buffer时就销毁它。 - Buffer销毁时机不安全:直接调用
pop.destroy()销毁旧buffer,但GPU命令是异步执行的,之前提交的渲染命令可能还在引用这个buffer,提前销毁会引发资源引用失效的警告。
解决办法
1. 修复纹理资源泄漏
每次渲染完成后,等待GPU命令执行完毕再销毁临时纹理:
function render () { const texture = device.createTexture( { size: [ canvas.width, canvas.height ], format: presentationFormat, sampleCount: sampleCount, usage: GPUTextureUsage.RENDER_ATTACHMENT } ); renderPassDescriptor.colorAttachments[ 0 ].view = texture.createView(); const canvasTexture = context.getCurrentTexture(); renderPassDescriptor.colorAttachments[ 0 ].resolveTarget = canvasTexture.createView(); const encoder = device.createCommandEncoder(); const pass = encoder.beginRenderPass( renderPassDescriptor ); pass.setPipeline( pipeline ); buffers.forEach( ( buffer, i ) => { pass.setVertexBuffer( 0, buffer ); pass.draw( pointsList[ i ].length / 2 ); } ); pass.end(); const commandBuffer = encoder.finish(); device.queue.submit( [ commandBuffer ] ); // 等待命令执行完毕后销毁纹理 device.queue.onSubmittedWorkDone().then(() => { texture.destroy(); }); requestAnimationFrame( render ); }
2. 修正Buffer扩容逻辑
把扩容判断改成正确的点数校验,并且改成新增buffer而非扩容旧buffer(更安全):
const addPoint = ( x: number, y: number ) => { let buffer = buffers[ buffers.length - 1 ]; let points = pointsList[ pointsList.length - 1 ]; // 当前buffer点数达到上限,新建buffer if (points.length / 2 >= pointsPerBuffer) { createBuffer(buffers); createPoints(pointsList); buffer = buffers[ buffers.length - 1 ]; points = pointsList[ pointsList.length - 1 ]; } const offset = points.length / 2; device.queue.writeBuffer( buffer, offset * 2 * 4, new Float32Array( [ x, y ] ) ); points.push( x, y ); ++numPoints; };
3. 安全销毁Buffer(若必须扩容旧buffer)
如果一定要保留扩容逻辑,必须等待所有GPU命令完成后再销毁旧buffer:
// 原扩容代码修改 const pop = buffers.pop(); // 等待已提交的GPU命令全部执行完毕再销毁 device.queue.onSubmittedWorkDone().then(() => { pop.destroy(); });
内容的提问来源于stack exchange,提问作者user25460123
相关产品推荐
相关产品推荐

