团结引擎——DotNet Wasm方案

参考:团结引擎 DotNet WebAssembly(Wasm) 介绍

一、当前编译流程

  1. 通过IL2CPP将C#转成C/C++;
  2. 通过Emscripen将C/C++转成WebAssembly;

二、 当前存在问题

  • IL2CPP在处理类似泛型、反射结构时,由于缺少运行时信息,必须全量生成泛型模板代码,引起Wasm的运存进一步膨胀

三、一些解决方案

1. 基于IL2CPP的的分包处理和代码裁剪

  • 优势
    • 可减少单个Wasm文件的体积
  • 劣势
    • 分包和裁剪都依赖运行时信息,需要引入的其他信息具有不确定性和复杂性

2. 基于.Net 8的Blazor方案

用户的C#代码以Blazor的方式做运行时解释执行代码,引擎的代码保持Wasm的实现;

  • 优势
    • 将用户的C#代码与Wasm分离,极大减少Wasm文件的体积,显著减轻运行内存的压力
  • 劣势
    • 解析执行在运行时处理,会增加额外的CPU使用和延迟
    • 解析执行的代码,无法做代码优化(如删除冗余代码、重排指令顺序、内联函数等)
    • 解析执行需要做类型检查,也会增加CPU开销

四、DotNet Wasm方案

DotNet Wasm 方案以 .NET8 为基础,依赖于 Emscripten 工具链构建 WebAssembly,并且使用裁剪优化后的 mono 作为 .Net 运行时,充分利用引擎原本对 mono 的支持,使得用户几乎可以无感地接入使用。

五、DotNet Wasm整体流程

1. IL2CPP与DotNet Wasm的编译流程图

请添加图片描述

2. 构建流程

①DLL Compile and Strip

Compile C# to IL

使用 Roslyn 将用户的 C# 脚本编译为 IL,以 dll 文件形式参与后续构建流程;

输入:

  • 用户 C# 脚本(包含 Package 和自定义 .asmdef)

输出:

  • dll 文件(IL)
Strip Managed Code

使用 UnityLinker 扫描项目用到的 Dll 并作可选的代码剔除,使得生成的 Dll 更小;

输入:

  • 上一步编译出的 dll 文件
  • Unity Module 中的 dll 文件
  • Plugin 中的 dll 文件
  • .NET BCL

输出:

  • ManagedStripped dlls
  • UnityLinkerToEditorData.json
Generate icall and Register Unity Modules

根据 UnityLinker 的裁剪的结果,生成引擎部分的 Native 注册类,参与后续构建。注意因为项目区别此处生成的类数量也会有差异,对 Wasm 体积产生影响;

输入:

  • UnityLinkerToEditorData.json
  • Unity Modules

输出:

  • UnityClassRegistration.cpp
  • UnityICallRegistration.cpp

②.NET8 MSBuild

Scan for PInvoke and icall

扫描所有的ManagedStripped dll 文件,在 .NET BCL、引擎 Native 模块的函数生成wrapper function table,并生成两个头文件记录;

输入:

  • 所有的 ManagedStripped dll 文件

输出:

  • wrapper function table
  • pinvoke-table.h
  • icall-table.h
Compile Native Files

输入:

  • 上一步的wrapper function table
  • Generate icall and Register Unity Modules时生成的引擎Native注册类
  • Plugins目录中的C/CPP文件

输出:

  • Native Objects
Link and Compile

此步骤为生成 wasm 和 js framework 的核心步骤;

输入:

  • 上一步的Native Objects、Unity的静态链接依赖、.NET运行时依赖(对应产物dotnet.native.wasm)
  • Unity JS Framework、 .NET8 Runtime JS、浏览器基础功能包括 IndexedDB,OpenGL API,Audio,Sensor 等 JS 库(对应产物dotnet.native.js)
  • Dotnet MSBuild(对应产物dotnet.runtime.js)

输出:

  • dotnet.native.wasm(等同于IL2CPP的Webgl.wasm)
  • dotnet.native.js(等同于IL2CPP的Webgl.framework.js)
  • dotnet.runtime.js(等同于IL2CPP的Webgl.framework.js)
Convert dll to WebCIL

将一些DLL转化为WebCIL的wasm格式,便于运行时的JIT进行解释执行;

输入:

  • 用户代码程序集、引擎代码程序集以及 .NET 基础类库 (BCL)

输出:

  • WebCIL类型的wasm

3. 构建产物

请添加图片描述

4. 加载流程

请添加图片描述

①Load First Page

浏览器:

  1. 下载 index.html 和 loader.js
  2. 随后渲染 HTML 页面,执行 loader.js 中的获取 data 文件和 dotnet.js 文件的逻辑,等待下载完成后进行初始化或解析

WX Game:

  1. 下载loader.js
  2. 执行loader.js来下载data并初始化dotnet.js(webgl.wasm.framework.unityweb.js)

②Fetch data & dotnet.js

loader.js 会分别下载 data 文件和 dotnet.js 文件,下载 dotnet.js 之后会马上执行初始化函数,等待初始化结束之后才会执行 callMain 入口函数;

③Fetch blazor.boot.json

dotnet.js 初始化过程中,首先会加载 blazor.boot.json,其中包含了项目中依赖的文件清单与 Hash 值,根据此文件内容来确定加载文件的名称以及是否加载缓存

加载的文件:

  • dotnet.native.wasm文件:wasm 代码运行的核心文件
  • dotnet.native.xxxx.js 和 dotnet.runtime.xxxx.js:负责初始化 JS Module
  • WebCILs:从 dll 转化而来

④Async Download Resources

dotnet.js 初始化获取具体的文件清单后,会异步下载上述所有类型的文件;

其中:

  • dotnet.native.xxxx.js 和 dotnet.runtime.xxxx.js 不会从缓存加载;
  • 其它文件则会根据 hash 值进行判断,如果 hash 值发生变化或者本地缓存不存在才重新下载并且缓存文件,否则直接从缓存中加载;

⑤Initialize mono .NET runtime

dotnet.js 初始化在资源下载完成之后,会调用 dotnet.native.xxxx.js 和 dotnet.runtime.xxxx.js 相关函数初始化 JS Module;

  • dotnet.native.xxxx.js:包含Unity JS Framwrok、User Plugin JS 和 Browser Base Library
  • dotnet.runtime.xxxx.js:包含了 .NET Runtime JS

⑥Initialize Assemblies(WebCILs)

在 WebCIL 下载或者从缓存加载完成之后,dotnet.js 会把 WebCIL (以及其它可能存在的 symbol 和 pdb) 从 ArrayBuffer 转换为 Unit8Array,并且将其复制进 heap,最后 exports 出去留待运行时按需加载。

⑦ Instantiate dotnet.native.wasm

dotnet.native.wasm 作为核心的 wasm 文件,会在 .NET JS Runtime 初始化完成之后调用 WebAssembly.Instantiate 来进行实例化

⑧Export Engine Instance

此时引擎的 Instance 准备完毕,export 以供 loader.js 调用。

⑨Start Game

在上面所有的步骤都完成之后,回到 loader.js 会执行 Wasm 入口函数 callMain,正式进入游戏的启动流程。

⑩Load Assemblies (WebCILs)

在游戏启动后会根据需求加载此前已经在 heap 的 WebCIL,调用相关函数以完成游戏的加载和运行。

六、新旧两种Wasm方案性能情况

1. 性能对比

测试环境:

  • Code Optimization:Runtime Speed
  • 测试环境为 MacBook 32G M1 Pro
  • 使用 Instruments - Activity Monitor 记录运行时内存和 CPU 使用率
  • 使用 Chrome DevTool Performace 和 Memory 工具记录 Frame Time 和 Wasm Heap 大小
  • WebGL 检测 WebContent 基于 WebKit miniBrowser,iOS 检测 WebContent 基于 iOS17.2.1
  • 测试多轮,分别取采样区间中的最小值/中位数/最大值计入表中请添加图片描述

2. 构建时间对比

请添加图片描述

七、总结

官方总结:

  • 相比 IL2CPP,DotNet Wasm 方案对 Wasm Heap 基本没有影响
  • 相比 IL2CPP,DotNet Wasm 方案的 Frame Time 有轻微的增加,但在浏览器普遍的 60FPS 刷新频率的条件下并不会产生帧率差异
  • 相比 IL2CPP,DotNet Wasm 方案可以获得显著内存收益。并且随着用户脚本复杂度的提高,由于脚本不进入 Wasm 编译链路,相对 IL2CPP 的内存收益会越发明显
  • 相比 IL2CPP,DotNet Wasm 方案并不对 CPU 带来额外负担,并且部分测试用例中 CPU 负载与波动表现优于 IL2CPP
  • 相比 IL2CPP,DotNet Wasm 方案构建时间相对 IL2CPP 大幅降低,这可以让开发者快速进行开发迭代
  • WebCLI的形式将用户代码剥离出来,且不再合并构建,并在Wasm初始化后各自独立加载,天然支持代码热更新

部分新特性:

  • 除了GC Boehm,还支持mono的分代GC Sgen(Simple Generation GC),后者会将GC分散到帧中,在频繁小内存的分配释放场景中帧率更高且波动更小
  • 通过统计解释执行时命中函数的频率,将热点代码动态生成为 Wasm Module,让热点部分进入 Wasm 从而进一步提升执行效率;此外,也可以自己选择部分 DLL 参与 AOT 编译从而直接进入 Wasm,牺牲部分运存换取性能提升(源自 DotNet 8 的新概念,基于JIT)
  • 对于用户的脚本代码,可以在浏览器中直接调试 C#,Native C/C++ 以及 JavaScript,这种开箱即用的调试体验可以极大提升开发效率

个人总结:

  • 新方案摒弃了IL2CPP的方案,改用23年新推出的.NET Blazor方案
  • 新方案使用时间换空间,CPU耗时会增加,而Wasm运存会降低;
  • 新方案将用户代码、部分引擎Manager代码从构建中剥离出来,作为单独Wasm包存在,一定程度上便于热更新;但是这个Wasm包需要通过服务器下载来加载到运存中,暂时不清楚是放在自己的服务器还是官方引擎的服务器中(像微信小游戏的插件Wasm包就必须上传到微信的服务器并通过校验)
  • 新方案中的用户代码在调试模式下,能够在浏览器上直接调试,可能存在安全风险
  • 新方案的编译产物和现有IL2CPP的产物有很大差异,暂时不清楚微信小游戏SDK是否有对应的适配版本

八、看法与疑问

1. 看法

  1. 原有通过Emscripten编译生成的Wasm并在运行时加载执行的流程,类似于AOT;
  2. 而新增的Dotnet Wasm方案更像将部分代码以JIT形式执行,其他部分仍然保留AOT形式;

2. 存疑

Q:产物都是WASM格式文件,浏览器时如何识别并处理哪些可直接执行,哪些需要JIT解释执行?

Q:DotNet Wasm和IL2CPP的产物不同,WX Game SDK是否有对应的适配版本?

Q:该方案需要联网下载部分文件(如dotnet.native.xxxx.js 和 dotnet.runtime.xxxx.js),无网络连接的情况下如何处理?

Q:用户的脚本代码可以在浏览器直接调试,是否存在一定的安全风险?

九、知识延伸

1. Roslyn

微软开源的.NET编译器,支持将C#编译成中间代码IL

2. Unity Linker

特点:

  • 用于剥离托管代码
  • 基于Mono IL Linker的定制版本

执行流程:

  1. 分析项目中的所有程序集,首先标记顶级、根类型、方法、属性、字段等;
  2. 分析已标记为要进行识别的根,并标记这些根所依赖的托管代码;
  3. 完成此静态分析后,所有剩余的未标记代码都无法通过应用程序代码中的任何执行路径来访问,并将从程序集中删除;

3. Blazor WebAssembly

特点:

  • Blazor应用、其依赖项及.Net运行时并行下载到浏览器;
  • 应用将在浏览器线程中直接执行
  • .NET运行时包含.NET中间语言IL解释器,并支持JIT运行时

4. WebCIL

特点:

  • 是一种适用于 .NET 程序集的 Web 友好打包格式,旨在支持在限制性网络环境中使用 Blazor WebAssembly;
  • 文件格式为标准的.wasm的WebAssembly文件;
  • 可以将DLL 信息封装为符合 Wasm Binary Format 的容器格式;
  • 运行时会将 Payload 复制到 Wasm 内存中;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/413897.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot的计算机知识竞赛网站(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的计算机知识竞赛网站(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系结…

11 PLL IP核

PLL IP 核简介 锁相环(PLL)作为一种反馈控制电路,其特点是利用外部输入的参考信号来控制环路内部震荡信号的频率和相位。因为锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。锁相环在工作…

六、防御保护---防火墙内容安全篇

六、防御保护---防火墙内容安全篇 一、IAE(Intelligent Awareness Engine)引擎二、深度检测技术(DFI和DPI)2.1 DPI -- 深度包检测技术2.1.1 基于“特征字”的检测技术2.1.2 基于应用网关的检测技术2.1.3 基于行为模式的检测技术 2.2 DFI -- 深…

MySQL的事务与隔离级别

1. 什么是事务? 数据库中的事务是指对数据库执行一批操作,而这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。这个时候就需要用到事务。 最经典的例子就是转账,你要给朋友小白转 1000 块钱&…

【Java 基础】Java 数组、方法极致精讲

《Java 零基础入门到精通》专栏持续更新中。通过本专栏你将学习到 Java 从入门到进阶再到实战的全套完整内容,所有内容均将集中于此专栏。无论是初学者还是有经验的开发人员,都可从本专栏获益。 订阅专栏后添加我微信或者进交流群,进群可找我领取 前端/Java/大数据/Python/低…

华为手动ipv6-to-ipv4隧道

中间r2的两个接口配置两个地址就行了,其它什么都不用配置 两边出接口R1和R3手动隧道建立:先把IPV4打通,并配置默认路由 再起隧道接口上进行配置,再配置带隧道的默认路由 PC上和上联接口网关只有IPV6地址 最终两个PC可以ping通 …

vue3的echarts从后端获取数据,用于绘制图表

场景需求:后端采用flask通过pymysql从数据库获取数据,并返回给前端。前端vue3利用axios获取数据并运用到echarts绘制图表。 第一步,vue中引入echarts 首先vue下载echarts npm install echarts 然后在main.js文件写如下代码 import {create…

Python入门到精通(九)——Python数据可视化

Python数据可视化 一、JSON数据格式 1、定义 2、python数据和JSON数据转换 二、pyecharts 三、折线图 四、地图 五、动态柱状图 一、JSON数据格式 1、定义 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据JSON本质上是一个带有特定格式的字符…

网络初识(概念入门)

目录 1.局域网VS广域网 1.1局域网 1.2广域网 2.五元组 2.1 IP和端口 2.1.1 IP 2.1.2端口号 2.2协议 3.协议分层 4. TCP/IP五层模型 5.封装和分用 5.1封装 5.2分用 1.局域网VS广域网 1.1局域网 简单介绍:指在某一特定区域内由多台计算机组成的互联网组…

Vue概念详解【目录】

本专栏简介: 这个专栏是关于 Vue2 和 Vue3 各种概念的大集合!它深入挖掘原理,分析各种优势和劣势,适配各种应用场景,部分内容还列出了代码示例,以清晰地讲述原理。在这里,你将全面了解 Vue2 和…

基于华为atlas的分类模型实战

分类模型选用基于imagenet训练的MobileNetV3模型,分类类别为1000类。 pytorch模型导出为onnx: 修改mobilenetv3.py中网络结构,模型选用MobileNetV3_Small模型,网络输出节点增加softmax层,将原始的return self.linear4…

测试开发(6)软件测试教程——自动化测试selenium(自动化测试介绍、如何实施、Selenium介绍 、Selenium相关的API)

接上次博客:测试开发(5)测试分类标准 :按测试对像划分、按是否查看代码划分、按开发阶段划分、按测试实施组织、按是否运行划分、按是否手工划分、按测试地域划分-CSDN博客 目录​​​​​​​ 什么是自动化测试 自动化测试介绍…

【Git教程】(五)分支 —— 并行式开发,分支相关操作(创建、切换、删除)~

Git教程 分支 1️⃣ 并行式开发2️⃣ 修复旧版本中的 bug3️⃣ 分支4️⃣ 当前活跃分支5️⃣ 重置分支指针6️⃣ 删除分支7️⃣ 清理提交对象🌾 总结 对于版本提交为什么不能依次进行,以便形成一条直线型的提交历史记录,我们认为有 以下两个…

ARM系列 -- 虚拟化(四)

今天来看看虚拟中断。 在一个非虚拟化的系统中,操作系统可以直接访问GIC的寄存器,并且处理GIC的物理中断接口(physical interrupt interface)。 但是在一个虚拟化的系统中,不是这样。Guest OS并不知道它运行在虚拟系…

LiveGBS流媒体平台GB/T28181功能-查看国标设备下通道会话列表直播|回放|对讲|播放|录像|级联UDP|TCP|H264|H265会话

LiveGBS流媒体平台GB/T28181功能-查看直播|回放|对讲|播放|录像|级联UDP|TCP|H264|H265会话 1、会话列表2、会话类型3、搭建GB28181视频直播平台 1、会话列表 LiveGBS-> 国标设备-》点击在线状态 点击会话列表 2、会话类型 下拉会话类型可以看到 直播会话、回放会话、下载…

uniapp:使用DCloud的uni-push推送消息通知(在线模式)java实现

uniapp:使用DCloud的uni-push推送消息通知(在线模式)java实现 1.背景 今天开发app的时候遇到一个需求: 业务在出发特定条件的时候向对应的客户端推送消息通知。 为什么选择在线模式,因为我们使用的是德邦类似的手持终端&#xf…

Qt6.8 GRPC功能使用(2)标准 Qt实现客户端

简介 基于之前的文章所说, Qt6.7之后才开始支持客户端、服务端、及双向流,恰好电脑需要重装,看到Qt6.8版本就直接安装了,内容也是使用Qt6.8的版本进行编译的 客户端实现步骤 1. 安装Qt6.8, 包含GRPC功能模块 Qt 6.8安装目录下包…

在Ubuntu22.04 LTS上搭建Kubernetes集群

文章目录 准备工作系统准备软件包准备主机和IP地址准备 安装步骤安装前准备关闭防火墙设置服务器时区关闭 swap 分区关闭SELinux配置hosts配置文件转发 IPv4 并让 iptables 看到桥接流量 安装容器运行时安装Kubernetes配置并安装apt包初始化集群 安装calico网络插件部署应用 本…

配置MMDetection的solov2攻略整理

目录 一、MMDetection 特性 常见用法 二、ubuntu20.04配置solov2 三、Windows11配置solov2 一、MMDetection MMDetection是一个用于目标检测的开源框架,由OpenMMLab开发和维护。它提供了丰富的预训练模型和模块,可以用于各种目标检测任务&#xff…

Chiplet技术与汽车芯片(一)

目录 1.摩尔定律放缓 2.Chiplet的优势 2.1 提升芯片良率、降本增效 2.2 设计灵活,降低设计成本 2.3 标准实行,构建生态 3.Chiplet如何上车 22年8月左右,Chiplet概念突然在二级市场火了起来,封测四小龙华天、长电、通富微电、…
最新文章