cesium 源码分析 worker与webassmbly

        当前cesium的代码有有一部份是关于解压Draco压缩过的gltf/glb文件的过程,这个过程封装在了DracoLoader.js中,这个过程中使用到了worker+webassmbly提高执行效率,现在大致分解一下执行的过程,至于如何编写wasm文件以及如何执行可参考Emscripten官网,使用emsdk工具链处理。

      DracoLoader.js中调用线程的过程代码如下:

// 全局变量
DracoLoader._decoderTaskProcessor = undefined;
// draco解析环境准备就绪的状态
DracoLoader._taskProcessorReady = false;
// 派生Draco环境,派生类与元类参数大致相同
DracoLoader._getDecoderTaskProcessor = function () {
  // 如果全局单例没有创建就创建
  if (!defined(DracoLoader._decoderTaskProcessor)) {
    // 创建任务处理过程
    var processor = new TaskProcessor(
      "decodeDraco",          // Draco解析类
      DracoLoader._maxDecodingConcurrency
    );
    // 初始化webassmbly相关过程,只要是加载wasm文件
    processor
      .initWebAssemblyModule({
        modulePath: "ThirdParty/Workers/draco_wasm_wrapper.js",   //  wasm封装类(js封装wasm接口)
        wasmBinaryFile: "ThirdParty/draco_decoder.wasm",          //  wasm文件
        fallbackModulePath: "ThirdParty/Workers/draco_decoder.js",//  浏览器不支持wasm时的替代类
      })
      .then(function () {
        DracoLoader._taskProcessorReady = true;     // draco解析环境准备就绪的状态
      });
    DracoLoader._decoderTaskProcessor = processor;  // 保存为全局实例
  }

  return DracoLoader._decoderTaskProcessor;         // 返回全局实例
};

          可以看到将这个处理过程封装在了TaskProcessor.js(封装了worker)中,这其中使用了4个文件decodeDraco、draco_wasm_wrapper.js、draco_decoder.wasm、draco_decoder.js。

        decodeDraco文件,这个是接收主线程数据的类,也是线程种真正调用draco_wasm_wrapper接口的类;

        draco_wasm_wrapper.js文件用来封装wasm对js所开放的接口,如果想深入研究可以看看这个文件的源码并结合官网了解;

        draco_decoder.wasm文件就是wasm格式的二进制文件,这个文件很小(-Oz优化后),因为是二进制所以对调试不是很友好,通过C++编译时调试方式大致分为3种,在c++种添加printf控制台信息、通过一种叫做s语言的解析器翻译成s语言(类汇编)可以单步调试、还有一种使用google插件C/C++ devtools support (DWARF)工具调试,可以看c++源码也可以单步调试;

        draco_decoder.js文件,如果当前浏览器环境不支持webassembly时可以将其他语言编译为asm.js的javascript文件。

        下面看看主线承让处理wasm的过程,代码如下:

// 向worker中发送用于加载和使用wasm的配置信息,同时含有对不支持wasm不支持时如何处理的类
TaskProcessor.prototype.initWebAssemblyModule = function (webAssemblyOptions) {
  // 没有定义线程就创建
  if (!defined(this._worker)) {
    this._worker = createWorker(this);
  }

  // 
  var deferred = when.defer();
  var processor = this;
  var worker = this._worker;
  // 获取wasm加载时的相关配置信息
  getWebAssemblyLoaderConfig(this, webAssemblyOptions).then(function (
    // wasm的配置信息(js文件的绝对路径,wasm的二进制文件)
    wasmConfig
  ) {
    // 检查浏览器是否支持内存转移
    return when(canTransferArrayBuffer(), function (canTransferArrayBuffer) {
      var transferableObjects;
      // wasm的二进制文件
      var binary = wasmConfig.wasmBinary;
      // 如果内存可以转移,将wasm二进制文件的内存转移到线程中
      if (defined(binary) && canTransferArrayBuffer) {
        transferableObjects = [binary];
      }
      // 接收子线程的处理结果(线程中的requrejs引导文件加载完成)
      worker.onmessage = function (event) {
        // 重新设置子线程的消息接收函数(数据处理完成时回调)
        worker.onmessage = function (event) {
          // 线程任务完成时调用
          completeTask(processor, event.data);
        };
        // promise返回
        deferred.resolve(event.data);
      };

      // 向线程传送数据
      worker.postMessage(
        { webAssemblyConfig: wasmConfig },  // wasm配置信息
        transferableObjects   //转移的内存
      );
    });
  });

  return deferred;
};

可以看到wasm文件是在主线程中使用xhr下载的,然后将wasm的二进制文件传到子线程中去,线程相关的解析可以查看我之前的cesium源码解析 worker。

        下面看看decodeDraco.js这个类

// wasm模块加载完成后向主线程发送消息
function initWorker(dracoModule) {
  // 解析的接口
  draco = dracoModule;
  // 创建接收主线程数据并解析的函数
  self.onmessage = createTaskProcessorWorker(decode);
  // 通知出现成wasm解析函数准备就绪
  self.postMessage(true);
}

// 配置wasm接口
function decodeDraco(event) {
  var data = event.data;

  // Expect the first message to be to load a web assembly module
  // 主线程中传入的wasm配置信息
  var wasmConfig = data.webAssemblyConfig;
  if (defined(wasmConfig)) {
    // Require and compile WebAssembly module, or use fallback if not supported
    // 通过requirejs方式加载draco_wasm_wrapper.js
    return require([wasmConfig.modulePath], function (dracoModule) {
      // wasm文件存在
      if (defined(wasmConfig.wasmBinaryFile)) {
        // draco_wasm_wrapper.js文件下载完成
        if (!defined(dracoModule)) {
          // 如果draco_wasm_wrapper.js未定义使用默认的
          dracoModule = self.DracoDecoderModule;
        }

        //调用draco_wasm_wrapper.js中的函数,主要是将wasm相关的接口绑定到compiledModule上
        dracoModule(wasmConfig).then(function (compiledModule) {
          // 初始化线程的消息
          initWorker(compiledModule);
        });
      } else {
        initWorker(dracoModule());
      }
    });
  }

用来在线程中准备wasm接口的过程,接口准备就绪后向主线程发送通知之后就等着接收解析数据。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cesium 是一个基于 WebGL 的开源地球渲染引擎,主要用于在浏览器中呈现三维地理数据。它使用了一些复杂的渲染技术和算法来实现高性能和高质量的地球渲染效果。下面是对 Cesium 渲染源码的简要分析: 1. Shader:Cesium 使用了大量的着色器程序来实现不同的渲染效果,如光照、纹理映射、阴影等。着色器程序是在 GPU 上执行的,并且可以通过 GLSL(OpenGL Shading Language)语言进行编写。Cesium 的着色器程序通常是在运行时动态生成的,以适应不同的地图数据和渲染需求。 2. 地形渲染:Cesium 支持高度精细的地形渲染,它使用了基于切片的地形渲染技术。在渲染过程中,Cesium 会将地球表面分割成小块切片,并根据每个切片的高度数据和纹理信息生成相应的网格和纹理。这些切片可以根据需要进行加载和卸载,以实现地图数据的动态加载和显示。 3. 纹理映射:Cesium 使用了纹理映射技术来实现地球表面的贴图效果。它可以将不同类型的纹理(如卫星图像、地形纹理、气候数据等)映射到地球表面的不同部分,以实现真实的地貌效果。Cesium 通过加载和解析各种类型的地图数据,将其转换为纹理信息,并将其应用到地球表面的相应区域。 4. 光照和阴影:Cesium 支持实时的光照和阴影效果,以增强地球渲染的真实感。它使用了基于物理的光照模型,考虑了光源的位置、光照强度、表面材质等因素,并通过计算每个顶点和像素的光照值来实现逼真的光照效果。此外,Cesium 还支持动态阴影的生成,可以根据光源的位置和地形的形状计算出地球表面上的阴影效果。 5. 动态渲染:Cesium 支持动态渲染技术,可以在实时交互的情况下实现高性能的地球渲染效果。它使用了一些优化技术,如级联阴影映射、视锥剔除、LOD(Level of Detail)等,以减少渲染负载并提高渲染效率。此外,Cesium 还支持动态加载和卸载地图数据,以实现在不同的视角和缩放级别下的快速渲染和响应。 需要注意的是,Cesium源码非常庞大和复杂,涉及到多个模块和子系统。以上只是对其渲染部分的简要分析,实际的源码分析需要深入研究 Cesium 的代码库和文档,并对 WebGL 和图形渲染技术有一定的了解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值