WebGL 内存泄漏
Posted
技术标签:
【中文标题】WebGL 内存泄漏【英文标题】:WebGL Memory Leak 【发布时间】:2012-12-24 09:54:45 【问题描述】:我知道有很多关于内存泄漏的话题,但我尝试了解决方案,但仍然不起作用。
我正在处理这个example
所以我有
materialPano=new THREE.MeshFaceMaterial( materials );
materialPano.needsUpdate=true;
mesh = new THREE.Mesh( new THREE.CubeGeometry( 400, 400, 400, 7, 7, 7 ), materialPano );
我正在添加一些功能以在单击按钮时更改纹理。 问题是之前的纹理没有被删除,并且每个新纹理使用的内存都会增加。
所以当我点击按钮时,它会执行一个函数:
materials = [loadTexture(myNewTexture1 ), loadTexture( myNewTexture2),loadTexture( myNewTexture3 ),loadTexture( myNewTexture4 ),loadTexture( myNewTexture5), loadTexture( myNewTexture6)];
myNewTexureK
是新的图像文件,随着按钮的变化而变化。我更新了网格材质。
materialPano.materials=materials;
mesh.material=materialPano;
问题是我不知道如何删除以前的纹理。我尝试了很多类似的东西:
for(var k=0;k<materials.length;k++)
materials[k].deallocate();
scene.remove(materials[k]);
renderer.deallocateTexture(materials[k]);
renderer.deallocateMaterial(materials[k]);
renderer.deallocateObject(materials[k]);
delete materials[k];
materials[k]=null;
renderer.deallocateMaterial(materials);
renderer.deallocateObject(materials);
delete materials;
materials=null;
我在这里做materials=[loadTexture(newTexture,...)];
我改变了loadTexture
函数:
function loadTexture( path )
var texture = new THREE.Texture( texture_placeholder );
var material = new THREE.MeshBasicMaterial( map: texture, overdraw: true );
var image = new Image();
image.onload = function ()
texture.needsUpdate = true;
material.map.image = this;
render();
;
image.src = path;
texture.deallocate();//new line
renderer.deallocateTexture( texture );//new line
return material;
但它不会删除任何东西! 并且没有对example做任何修改,我注意到刷新页面时,内存也增加了,所以示例中存在内存泄漏?
有没有办法真正删除纹理以避免内存泄漏?
非常感谢!
没有人有解决办法吗? :( 我编辑消息以尝试更准确。 我有:
var mesh;
function loadTexture( path )
var texture = new THREE.Texture( texture_placeholder );
var material = new THREE.MeshBasicMaterial( map: texture, overdraw: true );
var image = new Image();
image.onload = function ()
texture.needsUpdate = true;
material.map.image = this;
render();
;
image.src = path;
texture.deallocate();
renderer.deallocateTexture( texture );
return material;
function init()
//some initializations, create scene, webgl renderer, ...
var materiales = [
loadTexture( '1.jpg' ),
loadTexture( '2.jpg'),
loadTexture( '3.jpg' ),
loadTexture( '4.jpg' ),
loadTexture( '5.jpg'),
loadTexture( '6.jpg' )
];
mesh = new THREE.Mesh( new THREE.CubeGeometry( 400, 400, 400, 7, 7, 7 ),new THREE.MeshFaceMaterial( materiales ) );
scene.add( mesh );
当我点击一个按钮时,我会这样做:
updateTexture()
var materiales = [
loadTexture( 'new1.jpg' ),
loadTexture( 'new2.jpg'),
loadTexture( 'new3.jpg' ),
loadTexture( 'new4.jpg' ),
loadTexture( 'new5.jpg'),
loadTexture( 'new6.jpg' )
];
mesh.material=new THREE.MeshFaceMaterial( materiales );
问题是每次点击时内存都会增加。只有那个代码,这是正常的,没有什么会删除以前的mesh.material。但我尝试了很多类似的东西:
mesh.deallocate(mesh.material);
mesh.geometry.deallocate(mesh.material);
scene.deallocate(mesh.material);
renderer.deallocateMaterial(mesh.material);
renderer.deallocateTexture(mesh.material);
renderer.deallocateObject(mesh.material);
scene.remove(mesh.material);
而且内存还在增加。我确切地说我在 Firefox v 17.0.1 上工作。泄漏也出现在 Chrome 上。
【问题讨论】:
尝试保留mesh.material
原样,然后替换纹理:mesh.material.materials[ i ].map = texture; texture.needsUpdate = true;
【参考方案1】:
如果您使用 r51 - r53,请尝试添加 texture.deallocate()
【讨论】:
api 同时更改为 .dispose() ......见github.com/mrdoob/three.js/issues/2760#ref-commit-0fd5a5c【参考方案2】:这是我对http://mrdoob.github.com/three.js/examples/webgl_panorama_equirectangular.html所做的修改
我添加了来自http://mrdoob.github.com/three.js/examples/canvas_geometry_panorama.html 的loadTexture 函数,执行changeTexture() 函数的按钮。
<!DOCTYPE html>
<head>
<title>three.js webgl - equirectangular panorama demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body
background-color: #000000;
margin: 0px;
overflow: hidden;
#info
position: absolute;
top: 0px; width: 100%;
color: #ffffff;
padding: 5px;
font-family:Monospace;
font-size:13px;
font-weight: bold;
text-align:center;
a
color: #ffffff;
</style>
</head>
<body>
<div id="container"></div>
<div id="info"><a href="http://threejs.org" target="_blank">three.js webgl</a> - equirectangular panorama demo. photo by <a href="http://www.flickr.com/photos/jonragnarsson/2294472375/" target="_blank">Jón Ragnarsson</a>.<input id="changeTexture" type="button" value="Memory leak test" onclick="changeTexture();"> </div>
<script src="./build/three.min.js"></script>
<script>
var idTexture='id1';
var camera, scene, renderer;
var fov = 70,
texture_placeholder,
isUserInteracting = false,
onMouseDownMouseX = 0, onMouseDownMouseY = 0,
lon = 0, onMouseDownLon = 0,
lat = 0, onMouseDownLat = 0,
phi = 0, theta = 0;
var materialsT, materialPano;
init();
animate();
function init()
var container, mesh;
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
//put here your 6 textures, one per face. Of course, you can use the same texture for all the faces
materialsT = [
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg' ),
loadTexture(idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg' )
];
materialPano=new THREE.MeshFaceMaterial( materialsT );
mesh = new THREE.Mesh( new THREE.CubeGeometry( 256, 256, 256, 7, 7, 7 ),materialPano );
mesh.scale.x = -1;
scene.add( mesh );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'mousewheel', onDocumentMouseWheel, false );
document.addEventListener( 'DOMMouseScroll', onDocumentMouseWheel, false);
window.addEventListener( 'resize', onWindowResize, false );
function loadTexture( path )
var texture = new THREE.Texture( texture_placeholder );
var material = new THREE.MeshBasicMaterial( map: texture, overdraw: true );
var image = new Image();
image.onload = function ()
texture.needsUpdate = true;
material.map.image = this;
render();
;
image.src = path;
texture.deallocate();
renderer.deallocateTexture( texture );
return material;
function changeTexture()
idCourant=(idTexture=='id1')?'id2':'id1';
//you can use the same texture, there is still the memory leak
materialsT = [
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg'),
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg' ),
loadTexture( idTexture+'.jpg'),
loadTexture( idTexture+'.jpg' )
];
materialPano.materials=materialsT;//without this line, I don't have memory leak but I can't change the texture. So it means that loadTexture doesn't have memory leak since the memory doesn't increase when I click on the button with only materialsT=[loadTexture(...),...] in the changeTexture() function.
//I try with the following (put before materialPano.materials=materialsT; of course) to deallocate materialPano but it doesn't do anything
/*
renderer.deallocateTexture(materialPano.materials);
renderer.deallocateMaterial(materialPano.materials);
renderer.deallocateObject(materialPano.materials);
renderer.deallocateTexture(materialPano);
renderer.deallocateMaterial(materialPano);
renderer.deallocateObject(materialPano);
//*/
function onWindowResize()
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
function onDocumentMouseDown( event )
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
function onDocumentMouseMove( event )
if ( isUserInteracting )
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
function onDocumentMouseUp( event )
isUserInteracting = false;
function onDocumentMouseWheel( event )
// WebKit
if ( event.wheelDeltaY )
fov -= event.wheelDeltaY * 0.05;
// Opera / Explorer 9
else if ( event.wheelDelta )
fov -= event.wheelDelta * 0.05;
// Firefox
else if ( event.detail )
fov += event.detail * 1.0;
camera.projectionMatrix.makePerspective( fov, window.innerWidth / window.innerHeight, 1, 1100 );
render();
function animate()
requestAnimationFrame( animate );
render();
function render()
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = ( 90 - lat ) * Math.PI / 180;
theta = lon * Math.PI / 180;
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
renderer.render( scene, camera );
</script>
</body>
我不知道如何以及在哪里使用 mesh.material.materials[ i ].map = texture;纹理.needsUpdate = true;正如你所说的那样。
谢谢!
【讨论】:
嗨!抱歉,久等了。你可以在这里测试代码:jodyj.com/test 有canvas版本和webgl版本。只需单击按钮即可查看内存增加(例如在进程窗口中)。对于画布版本,内存泄漏出现在 Firefox 和 Chrome 而不是 IE 上。对于 webgl 版本(不是 IE),Firefox 和 Chrome 存在内存泄漏。我对代码进行了注释,并指出了哪一行造成了泄漏(materialPano.materials=materialsT; )。我在网格、材质上使用 deallocate 函数进行了测试,但它没有改变任何东西。谢谢!【参考方案3】:抱歉让您久等了。 你可以在这里测试代码:http://jodyj.com/test/ 有canvas版本和webgl版本。 只需单击按钮即可查看内存增加(例如在进程窗口中)。
对于画布版本,内存泄漏出现在 Firefox 和 Chrome 而不是 IE 上。 对于 webgl 版本(不是 IE),Firefox 和 Chrome 存在内存泄漏。 我对代码进行了注释,并指出了哪一行造成了泄漏(materialPano.materials=materialsT; )。 我在网格、材质上使用 deallocate 函数进行了测试,但它并没有改变任何东西。
谢谢!
【讨论】:
【参考方案4】:解决了 webgl 版本的问题。
添加:
for(var k=0;k<materialsT.length;k++)
renderer.deallocateTexture(materialsT[k].map);
renderer.deallocateTexture(materialsT[k]);
materialsT[k].deallocate();
materialsT[k].map.deallocate();
在 materialT=[ loadTexture(...), ...]. 和之前的 changeTexture 函数中
texture.deallocate();
renderer.deallocateTexture( texture );
在返回之前的 loadTexture 函数中。
但是,画布版本仍然存在泄漏。
【讨论】:
以上是关于WebGL 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章