微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

unity学习笔记一

unity基础简介(一)

unity是如何跨平台的(附c++和c#编译过程)

前言

游戏引擎为了兼顾易用性和性能,往往使用一种高效率语言编写核心,使用另一种高级语言作为脚本语言,大部分游戏引擎的底层核心由c++编写,unreal使用“U++”作为脚本,unity使用c#作为脚本,当然,使用lua,javascript作为脚本也可以,笔者在学习unity时就疑心作为核心语言的c++和作为脚本语言的c#是如何在一起编译运行的,本文将大致描述关于unity的编译。

c++编译过程*

在聊unity的编译过程之前,我们先来回顾一下c++的编译过程,熟悉可跳过

直接上图

在这里插入图片描述

1.预处理(Preprocessing)

读取c源程序,对其中的伪指令(以# 开头的指令)和特殊符号进行处理。

伪指令主要包括以下四个方面:

  1. 宏定义指令,如# define Name TokenString,# undef等。

对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

  1. 条件编译指令,如# ifdef,# ifndef,# else,# elif,# endif等。

这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

  1. 文件包含指令,如# include “FileName” 或者# include < FileName> 等。

预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

  1. 特殊符号,预编译程序可以识别一些特殊的符号。

预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。

2.编译(Compilation),优化

编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码

优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码生成而进行的。

对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数
经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

3.汇编(Assemble)

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码

目标文件由段组成。通常一个目标文件中至少有两个段:

  1. 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

  2. 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

4.链接(Linking)

链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。

根据开发人员指定的同库函数链接方式的不同,链接处理可分为两种:

  1. 静态链接

在这链接方式下,函数代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数代码

  1. 动态链接

在此种方式下,函数代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码

这个过程大家估计已经烂熟于心,总体来说,c++只需要一次编译就可以生成可执行文件,然而由于各个平台的指令集不同,他们所使用的汇编也不同,c++文件要实现跨平台就要使用相应平台的编译器重新编译一次,虽然c++的编译很快,但无形增加了移植成本,想想Windows、Android、MacOS以及IOS,对应使用生成的库文件后缀分别为”.dll”、”.so”、”.bundle”、”.a”,若要跨平台就要将c++文件分别按四种标准编译分别编译。

c#编译过程*

c++++是吸收c++语言和Java等完全面向对象语言的优点开发出来的,它的编译过程如下

在这里插入图片描述

学过c#的人对这张图可以说是相当熟悉,具体来说,c#经历了两次编译,第一次是代码编写后由面向CLR的编译器编译为IL中间语言,在具体机器上运行时由CLR使用JIT编译器编译为机器码,CLR(公共语言运行时)就是c#跨平台的关键。

在这里插入图片描述

如图是IL语言代码,可以看到它类似于汇编语言对栈帧的操作,因此可以快速编译。

在这里插入图片描述

如图是第一次编译,理论上任何语言都可以通过特定的编译器通过词法和语法分析编译为托管模块供CLR使用,包括c++

在这里插入图片描述

如图是托管模块具体组成部分,其中最重要的就是元数据,它是面向对象思想在编译中的体现,元数据包含引用信息,因此可以脱离#include,C#可通过代码获取元数据之中的类信息,以支持反射,元数据同样支持GC跟踪对象间的引用和确保代码执行的类型安全,元数据总共有三种表,分别是定义表,引用表和清单表,如下图

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

清单表主要用于支持程序集的编译运行,程序集是CLR的操作对象,程序集是一个抽象概念,是一个或多个模块文件和资源文件组成的逻辑单元,有且只有一个后缀为.exe或.dll的主模块文件包含清单,使用程序集的原因是可重用类型的逻辑与物理表示可以分开,例如,对于程序集包含的多个类型,则常用类型与不常用类型可分开在不同文件中,对于不常用文件可以执行“增量”下载。程序集不止包含托管模块,而且包括资源和数据文件,这使得它是进行重用,版本控制和应用安全性设置的基本单元,CLR首先加载包含清单元数据表的文件,由此获得程序集中的所有文件名称,因此用户只需要知道含有清单的文件,不必关心具体文件划分。

在这里插入图片描述


在这里插入图片描述

在JIT执行阶段,一个方法第一次执行时,CLR会分配一个空间管理其中的引用,此时MscoreE.dll中的JITCompil函数会被调用,将被引用的方法编译为机器码,然后修改管理引用中的引用指向被编译好的代码,具体如下

在这里插入图片描述

在第二次使用方法时,无需再进行编译

在这里插入图片描述

由于JIT在运行时编译,这意味着每一次运行都需要编译一次,使用.Net Framework提供的NGen.exe工具可以在安装时就用JIT编译为本机代码

mono

了解了c#和c++的编译过程,我们也就知道了c#跨平台的原理,其实unity跨平台的原理与c#类似,只是核心变为了mono

在这里插入图片描述

.net是微软提出的标准,它是利用clr将其他语言进行跨平台的开发平台(正如前文所说,理论上任何语言都可以编译为clr支持的程序集),谁都可以实现它,微软官方使用.net core和.net framework实现,也同时包含了asp.net,富客户端等等库,然而由于微软的封闭性,.net framework只支持Windows,.net core目前也只在x86桌面平台,mono是早期Xamarin根据从微软泄露出的源码编写的另一种.net开源版本实现,因此可以支持跨平台部署,unity利用它支持脚本的跨平台,程序员所写的脚本会被编译为IL,之后与unity的dll文件一同交给运行平台的mono,其编译运行过程与前文的c#相似。

IL2cpp

在使用mono后,一些问题也随之而来,最大的问题就是一些平台(例如IOS,必须用编译器提前编译)不支持JIT,其次就是JIT性能问题,每次运行都需要编译,在加上mono在各平台移植维护的成本较大(毕竟缺少大公司长期支持)它也无法完成64未版本支持,这迫使unity找到另一种跨平台方法,就是IL2CPP。

在这里插入图片描述

这种方法实际就是用cpp代替了mono的功能,首先unity用il2cpp.exe将程序集编译为cpp源码,然后再拿到各个平台编译运行,虽然这么做让项目构建时间大幅提高,但也让运行时间大幅缩短,毕竟c++的性能有目共睹,而且各平台的c++编译器也会进行相应优化。

为了支持托管资源,项目在运行时仍会链接一个IL2CPP虚拟机,它支持GC,分配线程等功能

LLVM

目前unity推出了新的Burst编译器,号称比c++编译器还快,它是基于LLVM实现的。

在这里插入图片描述

可以看到,LLVM实际上也是利用中间语言(这里是LLVMIR)将不同语言编译为各平台代码的,只不过它不是JIT编译,而是完全AOT(预先编译),它可以直接编译IL,而不用使用mono的JIT编译器或转为cpp。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐