Drawsee WebGPU支持
4.0版缺省启用WebGPU模式。如果想强制使用gl,需设参数{gpu:false}。另外全局设置Setting.Gpu也可控制
var earthViewer = new Drawsee.EarthViewer("earthDiv");
earthViewer.init().then(function () {
// 如果浏览器支持WebGPU则则自动启用,否则启用gl
});
WebGPU渲染模式下,底部状态栏会显示绿色WebGPU字样
注意:如果不是localhost,其他域名发布WebGPU网页,需要使用https协议
硬件配置:64位 Win10; CPU Intel 四核i5 16G内存;显卡 GeForce GT 1030 , 2G显存;刚支持Dx12的入门级配置
之前设想是通过RenderBundle打包方式,降低CPU使用,后来发现并这一点没有那么重要,因为大量CPU是用于图形解压之类,js主线程只是一小部分。 WebGPU带来的更大变化,是显卡使用率大幅度降低,达到4-6倍之多,相当于变相提高了渲染能力
技术讨论:为什么大量使用RenderBundle之后,显卡速度能有这么大的变化呢?初步分析,支持DirectX 12的显卡可能有一系列优化技术, 首先因为Bundle顶点数据是不变的,就可以存放在更快的二级缓存,然后VertexShader等的处理结果,可以被重复利用,甚至可以利用预先计算的包围盒,提前做遮挡剔除等, 当这些因素都起作用,就能解释4-6倍的速度差异。旋转等交互操作,仅仅改变View Matrix,完全有优化的算法
对硬件内存的分配,测试了Chrome的后续版本,包括Canary 117等,仍然是消极释放
WebGPU分配显存时,只要不超过实际显存的数量,就不会另外分配主机内存,超过时则开始在主机分配;而gl则是全部分配主机内存, 渲染时再拷贝入显存。由此可见,只要控制较小的工作集,WebGPU实际很省主机内存,可以给js程序留出更大配额
比如实际有2G显存,8G闲置内存;你用WebGPU分配2G纹理,还剩8G内存;但用gl分配2G纹理,就只剩6G内存了
Drawsee 4.0根据上述特点采取自主显存管理,主要原则:根据典型硬件设置触发点,比如1G,达到这个值之后,就开始积极回收资源; 资源一经分配,则不释放;任何需要的资源,都需要向资源管理中心申请并绑定;可以短时间锁定一些资源等等;
技术讨论:其实考虑一下,发现WebGPU的这种消极释放有一定的道理。因为它的任务是提供低层的硬件控制能力, 确保分配给js的是物理显存,拥有极高的性能;WebGPU尽量少做在主机内存和显存之间Copy的事情,因为那个是低效率的。 反观虽然gl可以透明管理大的虚拟显存,使用很简单,但是无法得到极高的性能
WebGIS平台,其实更能根据倾斜摄影这些海量数据的特点,利用WebGPU,采用类似于C++内存管理的稍微复杂的方法,合理分配需要的显存, 这样才能获得极致的性能提升。这里需要行业逻辑作为支撑,泛泛的gl虚拟内存管理机制办不到。各个行业都可以根据自己数据的特点设计管理算法
注:以下说数据量级是指一屏显示中需要用到的典型数据量。GIS海量数据自然是这无数倍
数据量级: 三角形数 200k     纹理块数 200
正射影像+三维地形绘制的三维地球是最简单的,无论是地形网格数据,还是影像数据,都是比较小的数据量,虽然WebGPU版本也是快, 但是由于gl版已经很快了,轻松的刷新到60fps,所以性能提升感觉不明显
数据量级: 三角形数 2000k     纹理块数 2000 较大块
倾斜摄影的数据密度是最大的,比三维地球提升了10倍,其中绝大部分是纹理数据,需要根据树状索引结构做针对性缓存管理, 合理使用主机和显卡内存配额,根据访问时间戳做调度
4.0暂时去掉了倾斜摄影的光影效果,从而可以更大压缩顶点内存,使用简单高效的shader,充分发挥WebGPU的优势。 因为最近绘制的Bundle在显存中,高速切换绘制不影响操作流畅性,所以能支持平滑漫游,同时保持高清晰度纹理。
另外在WebGPU模式下,给js主线程更大的内存额度,从而使更多的数据块保持在内存中,所以尽管总体上和gl版使用差不多的主机内存, 最近浏览的内容,绝大部分都一直能保持高清显示,而gl版,则只能经常切换回低分辨率,重新从磁盘缓存加载
倾斜摄影完全达到工程级应用水准,可以与动态植被等一起使用
具体的测试,通过Windows任务管理器就可以观察,参考官网主页倾斜摄影部分的描述
数据量级: 三角形数 2000k     静态纹理 1000 可用LOD缩减     动态纹理 2048*2048若干
解决了海量数据的调度管理之后,优化Bim时发现新问题,复杂wgsl的加载,动态阴影纹理
Wgsl加载 当加载光影场景时,启动很慢,使用Profiler检查出有大量时间段行为奇怪,从任务管理器上看, CPU闲置,GPU也闲置,然而Profiler里面标明GPU占满,画面也完全卡住。1000行的wgsl,就要卡1秒,这完全不能用正常的程序 加载编译来解释,因为浏览器加载js可以达到20万行/秒,glsl的编译也是5万行/秒的水平
尝试简化 在city演示的夜景模式,用到约50个材质,gl完全没有时间延迟,然而即使优化到5个wgsl,也要等待几秒。于是后来猜测, 应该是Chrome故意放慢了几百倍,比如可以防止恶意程序自动生成和执行wgsl,浏览器毕竟安全是第一位的
最新的Chrome115版,果然坐实了这个猜测,如下图,从白天切换到夜晚时,加载新材质,调试器出现了一种新的时间段Experience, 就是实验时间段,足足8秒的大停顿
鉴于Chrome目前设置的这个障碍,光影渲染能够做的优化受到比较大的影响。更多的测试还表明,如果使用大量的动态纹理,比如阴影纹理, 系统似乎也在有意减慢运行速度,或者在做安全检查,不论怎样简化Wgsl,甚至仅仅只是引用阴影纹理,不向里面绘制内容,还是会比gl版慢几倍, 其他的优化效果都被抵消掉了。所以阳光系统和暗光系统暂时也处于实验状态
117版已经放开了上述限制:阳光系统渲染比gl版提升5倍,暗光系统也有2倍提升,请耐心等待Chrome。 材质切换还稍慢,是babylon自动编译glsl到wgsl的原因,Drawsee将会持续探索优化方法
createTextures每次将分配4000块512*512的纹理,大约占用3G内存;deleteTextures则将所有分配的对象销毁
WebGPU 程序很简单,通过device创建纹理并保存在数组中。 WebGPU测试页面
for (let i = 0; i < 4000; i++) {
let texture = device.createTexture({
size: [512, 512, 1],
format: 'rgba8unorm',
usage: GPUTextureUsage.COPY_DST
});
textures.push(texture);
}
销毁对象
for (let i = 0; i < textures.length; i++)
textures[i].destroy();
textures = [];
进入测试页面,打开任务管理器观察内存。点击创建,2G显存立刻全部使用,同时Chrome进程增加 1G内存使用;然后点击销毁,显存和内存都没有减少,已经内部回收了?但是再次点击创建,发现不是那么回事, 内存使用又增加2.5G,然后点击销毁,内存使用依然不减少;如此创建销毁几次,内存一直飙升,很快浏览器就会崩溃。
这个测试结果很奇怪,如果创建的对象不允许释放,提供那个destroy方法有什么用?
WebGL 作为对比,创建和删除同样数量的纹理。 WebGL测试页面
for (let i = 0; i < 4000; i++) {
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageSource);
gl.bindTexture(gl.TEXTURE_2D, null);
textures.push(texture);
}
销毁对象
for (let i = 0; i < textures.length; i++)
gl.deleteTexture(textures[i]);
textures = [];
WebGL的内存管理就很直白,创建时立刻增加3G,删除立刻减少3G,不分配显存,反反复复创建删除没有任何问题