团结引擎——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.hqwc.cn/news/496192.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LACP——链路聚合控制协议

LACP——链路聚合控制协议 什么是LACP? LACP(Link Aggregation Control Protocol,链路聚合控制协议)是一种基于IEEE802.3ad标准的实现链路动态聚合与解聚合的协议,它是链路聚合中常用的一种协议。 链路聚合组中启用了…

【QML】去掉MessageDialog右上角的问号

1. 默认显示效果 2. 修改后效果 3. 修改方法 main.cpp中添加QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpi…

桥接模式(Bridge Pattern) C++

上一节&#xff1a;适配器模式&#xff08;Adapter Pattern&#xff09; C 文章目录 0.理论1.组件2.使用场景 1.实践 0.理论 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它的核心思想是将抽象部分与其实现部分分离&#xff0c;使它们可…

Linux系统——Nginx拓展

目录 一、重写功能——rewrite 1.if 1.1 if 2. return 2.1状态码301和302的区别 301 302 3. set 4. break 5. rewrite 5.1 rewrite flag使用 5.2 flag说明 5.3举例 5.3.1访问 bj 跳转 beijing 5.3.2举例——break 5.3.3 http 转 https 5.3.4 break 与 last …

windows系统使用Vscode在WSL调试golang本地进程

背景&#xff1a; windows10企业版 vscodegolang1.20 wsl编译运行。 vscode 使用本地wsl进行进程attach操作&#xff0c;发现&#xff1a;Access is denied. 本地进程启动&#xff0c;vscode调试进程。windows-Linux控制台: Starting: C:\Users\book\go\bin\dlv.exe dap --l…

微服务之qiankun主项目+子项目搭建

主项目使用history&#xff0c;子项目使用hash模式 1. 下载安装"qiankun": "^2.10.13"2. 手动调用qiankun,使用vue脚手架搭建的项目1. 主项目配置&#xff08;我使用的是手动调用乾坤&#xff0c;在指定页面显示内容&#xff09;1. 要使用的页面中引入乾坤…

【计算机】本科考研还是就业?

其实现在很多计算机专业的学生考研&#xff0c;也是无奈的选择 技术发展日新月异&#xff0c;而在本科阶段&#xff0c;大家学着落后的技术&#xff0c;出来找工作自然会碰壁。而且现在用人单位的门槛越来越高&#xff0c;学历默认研究生起步&#xff0c;面试一般都是三轮起步…

idea生成WebServices接口

文章目录 idea生成WebServices接口1.创建接口2.生成wsdl文件3.在soapUI中&#xff0c;生成6个文件4.将生成的文件拷贝到工程中5.在service-config中注册服务 idea生成WebServices接口 1.创建接口 新建一个webServices工程&#xff0c;按照接口规范生成接口、请求类、响应类。…

【Pytorch深度学习开发实践学习】Pytorch实现LeNet神经网络(1)

1.model.py import torch.nn as nn import torch.nn.functional as F引入pytorch的两个模块 关于这两个模块的作用&#xff0c;可以参考下面 Pytorch官方文档 torch.nn包含了构成计算图的基本模块 torch,nn.function包括了计算图中的各种主要函数&#xff0c;包括&#…

如何让网页APP化 渐进式Web应用(PWA)

前言 大家上网应该发现有的网页说可以安装对应应用&#xff0c;结果这个应用好像就是个web&#xff0c;不像是应用&#xff0c;因为这里采用了PWA相关技术。 PWA&#xff0c;全称为渐进式Web应用&#xff08;Progressive Web Apps&#xff09;&#xff0c;是一种可以提供类似…

Nginx重写功能和反向代理

目录 一、重写功能rewrite 1. ngx_http_rewrite_module模块指令 1.1 if 指令 1.2 return 指令 1.3 set 指令 1.4 break 指令 2. rewrite 指令 3. 防盗链 3.1 实现盗链 3.2 实现防盗链 4. 实用网址 二、反向代理 1. 概述 2. 相关概念 3. 反向代理模块 4. 参数配…

读《Shape-Guided: Shape-Guided Dual-Memory Learning for 3D Anomaly Detection》

Chu Y M, Chieh L, Hsieh T I, et al. Shape-Guided Dual-Memory Learning for 3D Anomaly Detection[J]. 2023.&#xff08;为毛paperwithcode上面曾经的榜一引用却只有1&#xff09; 摘要 专家学习 无监督 第一个专家&#xff1a;局部几何&#xff0c;距离建模 第二个专家&…