团结引擎——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://xiahunao.cn/news/2814363.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Redis7

摘录 https://github.com/Romantic-Lei/Learning-in-practice/blob/master/Redis/ 官网地址: 英文:Redis 中文:CRUG网站 redis中文文档 安装包:https://redis.io/download/,选择redis7.0版本即可 Redis在线测试地址(不用下载也…

(全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF

研究生英语读写教程基础级教师用书PDF 研究生英语读写教程提高级教师用书PDF pdf下载(完整版下载) (1)研究生英语读写教程基础级教师用书PDF (2)研究生英语读写教程基提高级教师用书PDF

WPF的DataGrid自动生成中文列头

直接将一个对象集合绑定到DataGrid上面,设置自动生成列AutoGenerateColumns"True",DataGrid会自动根据对象类的属性生成对应的列 示例类对象: public class DataModel{public int Id { get; set; }public string Name { get; set;…

vue -- watermark水印添加方法

前言 项目生成公司水印是很普遍的需求,下面是vue项目生产水印的方法。话不多说,复制粘贴就可以马上解决你的需求。 步骤1 创建watermark.js文件。目录结构 /** 水印添加方法 */let setWatermark (str1, str2) > {let id 1.23452384164.1234124…

FL Studio 21 Mac汉化免费版 附安装教程

FL Studio 21 Mac是Mac系统中的一款水果音乐编辑软件,提供多种插件,包括采样器、合成器和效果器,可编辑不同风格的音乐作品,Pattern/Song双模式,可兼容第三方插件和音效包,为您的创意插上翅膀。FL Studio 2…

AI-数学-高中-27-复数相关定义及运算

原作者视频:【复数】【一数辞典】2复数的四则运算_哔哩哔哩_bilibili 复数的标准形式: 注意:把i当成x看,其他完全平方公式一样。 混合运算规则:实部跟实部运算,虚部跟虚部运算,i^2-1; 注意&…

Java最新面试宝典 SpringMVC面试题)

Java最新面试宝典 SpringMVC面试题 前言1、什么是SpringMVC?2、SpringMVC 的优点?3、Spring MVC配置步骤?4、SpringMVC工作原理了解吗?5、Spring MVC 核心组件的功能?6、B/S 系统标准的三层架构是什么?7、C…

DETR(1):论文详解

文章目录 1. DETR 模型结构2.损失函数2.1 预测结果和GT 的匹配2.2 训练的loss计算3.实验3.1 大物体表现效果好3.2 Transformer Encoder 和Decoder的作用3.3 object query4. 伪代码5. 结论

Jetpack Compose 1.7中的新修饰符receiveContent

在 Jetpack Compose 1.7.0 中提供了一个新的修饰符,Modifier.receiveContent,通过该修饰符可以支持在输入框中输入输入法软键盘中自带的图片表情等,非常方便。 示例代码如下: class MyActivity: ComponentActivity() { overrid…

[源码分析]webrtc音频流从接收到播放的关键流程

如图所示,音频流从接收到播放的核心关键流程和执行线程。 neteq中对音频流的RTP数据包进行了处理和解码操作。同时数据出现了跨线程的投递。 后面有空补上核心对象之间的关系图。 后面将添加对neteq模块的细节分析。

影像仪激光扫描功能,无缝连接2D/3D混合测量

在现代工业生产领域,影像仪用于质量控制和产品检测,是一个不可或缺的工具。它通过高精度的成像和图像处理技术,可以及时发现产品的缺陷和异常,以保证产品质量的稳定性和一致性。 影像仪的重要性及其面临的挑战 在工业生产方面&a…

【QT+QGIS跨平台编译】之五十五:【QGIS_CORE跨平台编译】—【qgsmeshcalcparser.cpp生成】

文章目录 一、Bison二、生成来源三、构建过程一、Bison GNU Bison 是一个通用的解析器生成器,它可以将注释的无上下文语法转换为使用 LALR (1) 解析表的确定性 LR 或广义 LR (GLR) 解析器。Bison 还可以生成 IELR (1) 或规范 LR (1) 解析表。一旦您熟练使用 Bison,您可以使用…

mac打不开xxx软件, 因为apple 无法检查其是否包含恶意

1. 安全性与隐私下面的允许来源列表,有些版本中的‘任何来源’选项被隐藏了,有些从浏览器下载的软件需要勾选这个选项才能安装 打开‘任何来源’选项 sudo spctl --master-disable 关闭‘任何来源’选项 sudo spctl --master-enable

RK3568平台 RTC时间框架

一.RTC时间框架概述 RTC(Real Time Clock)是一种用于计时的模块,可以是再soc内部,也可以是外部模块。对于soc内部的RTC,只需要读取寄存器即可,对于外部模块的RTC,一般需要使用到I2C接口进行读取…

MS90C031LVDS 四通道总线驱动器,可兼容替代DS90C031

产品简述 MS90C031 是一款低功耗、高数据传输率的四通道 CMOS 差分 LVDS 信号总线驱动芯片,其支持的数据接收率超过 155.5Mbps (77.7MHz) 。 MS90C031 将 TTL/CMOS 输入信号,转换成低压 (350mV) 的差分输出信 号。芯片驱动器还支持三态输出功…

【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范

【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范 大家好 我是寸铁👊 总结了一篇测试API查询信息无法返回数据库信息与api、rpc文件编写规范的文章✨ 喜欢的小伙伴可以点点关注 💝 问题背景 大家好,我是寸铁&#xff01…

【王道数据结构】【chapter7查找】【P285t5】

线性表中各节点的检索概率不等时,可用如下策略提高顺序检索的效率;若找到指定的结点,则将该结点和其前驱结点(若存在)交换,使得经常被访问的结点尽量位于表的前端。试设计在顺序结构和链式结构的线性表盘上…

麒麟银河操作系统V10部署ffmpeg

麒麟银河操作系统V10部署ffmpeg 部署ffmpeg用来处理视频的各种操作 想使用ffmpeg,要先安装nasm,yasm,x264之后,否则会报错 nkvers 查看麒麟操作系统版本 cat /proc/version #查看linux版本信息 uname -a #查看linux版本和内核…

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

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

【初中生讲机器学习】12. 似然函数和极大似然估计:原理、应用与代码实现

创建时间:2024-02-23 最后编辑时间:2024-02-24 作者:Geeker_LStar 你好呀~这里是 Geeker_LStar 的人工智能学习专栏,很高兴遇见你~ 我是 Geeker_LStar,一名初三学生,热爱计算机和数学,我们一起加…