html页面会出现浏览器崩溃,大规模WebGL应用引发浏览器崩溃的几种情况及解决办法...

我们使用Javascript写WebApp的时候,一般都不会考虑对象的生命周期,不太关注内存“泄露”的问题,依赖JS引擎的垃圾回收机制就可以运行的很好,基本上很少会出现浏览器崩溃的情况。但在Web端显示大规模三维模型的时候,内存/显存就是一个非常紧张的资源,需要综合考虑模型数据组织、任务调度、资源管理、浏览器兼容等方方面面的问题。本文针对我们在开发基于WebGL的三维应用中遇到到几种导致浏览器崩溃的情况进行分析并提供解决方案。

异步请求过多

浏览器对并发异步请求是有限制的。如果程序不做处理,“同时”发送几百个请求就可能导致浏览器崩溃。

解决办法:

使用请求队列:把需要多次请求得到的数据重新组织,在一次或几次请求完成。如果非得需要很多次(几百上千的情况)请求,可以把请求排队,用多个异步请求队列加载数据。

并发异步请求资源死锁

若一个资源被多个异步请求同时请求的时候就可能导致浏览器死锁,死锁的结果就是浏览器崩溃。默认浏览器都是启用cache的,而浏览器在从cache中读取数据的时候会加锁。

解决办法:

在组织异步请求队列的时候,把相同的资源放在相同的队列中,而不是放在不同的队列中。

GPU进程崩溃

Chrome是多进程架构,每个Tab都会启用单独的进程来处理页面。但所有的进程都会公用一个GPU进程。那么问题来了,如果开启多个WebGL应用页面,每个页面占用一定的GPU资源,GPU进程的内存加起来总的就会轻轻松松超过1.5G,结果就是GPU进程崩溃。使用64位Chrome情况会稍有改善,但依赖本机的GPU显存大小。在实际中用WebGL显示大模型会轻轻松松的撑爆GPU进程。

解决办法:

优化Mesh数据

Mesh简化

Mesh共享

使用对象池

限制同时绘制对象的数量

JS对象过多导致崩溃

在上图中的浏览器管理器中可以看到多个内存:内存、GPU内存、Javascript内存。其中Javascript内存是JS对象占用的内存,JS垃圾回收的是这部分内存。

Javascript代码和垃圾回收运行在同一个线程的环境,当垃圾回收的时候,js代码不会执行。如果js对象过多,(占用内存过多),垃圾回收的过程也会变得漫长。所以Chrome简单粗暴的限制了Javascript内存的占用,在x64下最大~1.4G。

解决办法:

优化数据结构

增加数据动态管理的机制。

JS代码运行超时

如果JS运行时间过长,超出一定的时间,浏览器就弹个对话框,让用户选择是否结束。相同的代码在chrome中没有问题,而在firefox中就可能无响应。

解决方法:

把耗时的算法设计成分步执行,使用requestAnimationFrame模拟多线程。

总结

相对桌面应用程序开发,浏览器是一个资源受限的环境:JS执行效率,内存,线程等。对于大规模的Web应用,我们经常需要在性能和资源上做平衡。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在three.js中,大场景模型下显存占用太高的问题,可以通过以下几种方法来解决: 1. 优化模型 可以通过优化模型来减小模型的大小,从而减小显存的占用。具体的优化方法包括:合并几何体、删除不必要的网格、压缩纹理等等。下面是一个示例代码: ``` // 合并几何体 var geometry = new THREE.Geometry(); scene.traverse(function(child) { if (child instanceof THREE.Mesh) { child.updateMatrix(); geometry.merge(child.geometry, child.matrix); } }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // 删除不必要的网格 var mesh = new THREE.Mesh(geometry, material); mesh.castShadow = true; mesh.receiveShadow = true; mesh.frustumCulled = false; scene.add(mesh); scene.traverse(function(child) { if (child instanceof THREE.Mesh) { if (child !== mesh) { scene.remove(child); } } }); // 压缩纹理 var texture = new THREE.TextureLoader().load('texture.jpg'); texture.minFilter = THREE.LinearMipMapLinearFilter; texture.anisotropy = renderer.getMaxAnisotropy(); ``` 2. 分段加载模型 可以将大场景模型分成多个小模型,然后在需要的时候动态加载。具体的方法包括:使用LOD(层次细节对象)、使用Octree(八叉树)、使用Frustum Culling(锥体剔除)等等。下面是一个示例代码: ``` // 使用LOD var lod = new THREE.LOD(); lod.addLevel(mesh1, 100); lod.addLevel(mesh2, 200); lod.addLevel(mesh3, 300); scene.add(lod); // 使用Octree var octree = new THREE.Octree(); scene.traverse(function(child) { if (child instanceof THREE.Mesh) { octree.add(child); } }); var objects = octree.search(camera.position, 1000); // 使用Frustum Culling var frustum = new THREE.Frustum(); frustum.setFromMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); scene.traverse(function(child) { if (child instanceof THREE.Mesh) { if (frustum.intersectsObject(child)) { child.visible = true; } else { child.visible = false; } } }); ``` 3. 减小渲染范围 可以通过减小渲染范围来减小显存的占用。具体的方法包括:使用裁剪平面、使用遮挡剔除等等。下面是一个示例代码: ``` // 使用裁剪平面 var plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), -10); var clipPlane = new THREE.ClipPlane(plane, camera); renderer.clippingPlanes = [clipPlane]; renderer.localClippingEnabled = true; // 使用遮挡剔除 var occluder = new THREE.Mesh(geometry, material); occluder.position.set(0, 0, 0); occluder.scale.set(10, 10, 10); occluder.castShadow = false; occluder.receiveShadow = false; occluder.material.color.setHex(0x000000); occluder.material.opacity = 0.5; occluder.renderOrder = 0; scene.add(occluder); var occluderScene = new THREE.Scene(); occluderScene.add(camera); occluderScene.add(occluder); var renderer2 = new THREE.WebGLRenderer(); renderer2.setSize(width, height); renderer2.autoClear = false; renderer2.domElement.style.position = 'absolute'; renderer2.domElement.style.top = '0px'; renderer2.domElement.style.left = '0px'; document.body.appendChild(renderer2.domElement); renderer2.render(occluderScene, camera); renderer2.domElement.style.display = 'none'; renderer.setOpaqueSort(false); renderer.domElement.addEventListener('mousedown', function(event) { var x = event.clientX; var y = event.clientY; var rect = event.target.getBoundingClientRect(); x = ((x - rect.left) / rect.width) * 2 - 1; y = -((y - rect.top) / rect.height) * 2 + 1; var mouseVector = new THREE.Vector3(x, y, 0.5); mouseVector.unproject(camera); var raycaster = new THREE.Raycaster(camera.position, mouseVector.sub(camera.position).normalize()); var intersects = raycaster.intersectObjects(scene.children, true); if (intersects.length > 0) { var object = intersects[0].object; renderer2.render(occluderScene, camera); var isOccluded = renderer2.domElement.getContext('webgl').isPointInPath(x, y); if (!isOccluded) { // do something } } }); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值