zoukankan      html  css  js  c++  java
  • unreal3启动流程总结

    一、启动代码所在工程为Launch(win32),可为所有同一codebase项目共享。

    但共享方式很不智能,是通过在源文件中添加大量#if/else条件编译宏来实现,即各项目在自己的工程中添加【程序运转流程】所需的各回调函数,然后在Launch工程的文件中添加这些函数的声明并调用。

    也就是说每增加一个新项目,LaunchEngineLoop.cpp里都要改很多地方,虽然都是机械定式化的,但仍然难免繁琐容易出错。

    二、最外层的WinMain函数,在Launch.cpp中,

    它首先设置好vc的crt的几个错误回调函数,然后就以保护方式进入真正的主函数,

    所谓主函数有两种可能,一是在编辑器模式下,程序中包含.net代码,所以运行ManagedGuardedMain,二是普通模式,不含托管代码,则运行GuardedMainWrapper。

    其实托管模式下的ManagedGuardedMain内部,也是要调到普通的GuardedMainWrapper,区别只是其内又添加了些异常捕获处理,用以获取.net调用栈以供调试。

    三、GuardedMainWrapper,顾名思义,它还是一个wrapper,封装了对真真正的主函数GuardedMain的调用。在这一层封装里,负责在发生异常时创建dmp文件。

    四、GuardedMain。这个就是真正的主体了,它本身不是特别复杂,但其调用过的函数和嵌套层次非常庞大(标*的阶段都属于可用条件编译关闭的阶段)

    1、创建一个局部对象做为卫兵,以防止本函数在意外退出(返回或异常)时,一定要调用EngineExit函数。

    2、设置dump文件的名字。

    3、*如果是以纯服务器模式运行,在命令行参数头部插入"SERVER"以做标识

    4、*如果支持STEAMWORKS(一种在线游戏平台),那么初始化它

    5、*如果支持OpenAutomate(nvidia开发的一套性能测试框架,win32下默认打开),那么初始化它

    6、EnginePreInit,非常重要,这里进入另一个大型函数:FEngineLoop::PreInit

    这里FEngineLoop是一个非常简单的类,就记录了一些关于时间、帧数的变量,但是有几个关键的流程函数:

    PreInit:就是这一步调用的函数,时机在整个流程最前(init初始化就已经有靠前的意思,它还带个pre预先,合起就是预初始,那当然是最前了)

    TickedInit:有声明无定义也无调用,可能是已经废弃的,与该类的一个内部类FTickedInitInfo或许是搭配使用结果一同废弃。看注释说这是给某些单线程平台使用的,现在似乎没有这么低端的平台了吧?

    Init:正式的初始化,也是个巨无霸,后面再详述。

    Exit:退出前调用,做各种清理工作。

    Tick:每一帧被调用的函数,渲染和消息处理都在这里了。

    其实启动这块大部份都是面向函数的过程式代码,这里却突然冒出一个面向对象的FEngineLoop类,煞是突兀,而且它也完全没有产生多个实例的意义,通篇仅有一个全局定义的对象FEngineLoop GEngineLoop;

    所以我觉得这个类封装得是毫无必要。

    7、继续说FEngineLoop::PreInit,此函数共1300行!!也就是说一屏50行的话,要滚26屏才看完。

    (1)给vc设置纯虚函数调用时的报错处理器。不得不说unreal3对vc的各种错误函数照顾得非常到位,但这一步似乎可以放到上面第二步中

    (2)记录(当前)主线程Id。因为渲染是在单独的线程里做,所以需要有所区分。

    (3)创建控制台变量管理器FConsoleManager,并注册内置的变量。

    (4)设置GFlushStreamingFunc函数指针指向FlushResourceStreaming,虽然现在还不知道“刷新流式资源”的意义,但就函数指针用法来看有点奇怪,如果说GFlushStreamingFunc可能被指向多个不同函数,那么在上层使用此函数指针来抽象相关行为才有必要,但搜索结果显示它除了初始值appNoop(一个空函数)外,就只被赋值为FlushResourceStreaming,而调用方和实现方其实都是unreal3引擎自身,如此包装中转有点故做神秘了。

    (5)设置游戏名字,存于全局变量GGameName(这些G开头的全局变量通常位于Core.h中)。这里就是第一条中提到的需各游戏项目用条件编译宏来定制的代码段之一了。

    (6)检查参数并设置GUseSeekFreePackageMap,用途暂不明,待理解后补上。

    (7)检查参数并设置GIsCookingForDemo,用途暂不明,待理解后补上。

    (8)检查参数是否启动编译器模式bHasEditorToken,也就是命令行参数第一个token为"EDITOR",此处用到FString的==操作符,其又调用stricmp,实现的是忽略大小的比较。

    (9)检查参数并设置GUseSeekFreeLoading,用途暂不明。猜意思seekfree应该是免寻址,那可能是所有资源的地址都在内存里了?

    (10)检查参数并设置GIsSeekFreePCServer、GIsSeekFreePCConsole,注释说明:PC Server和PC console都以最小化模式运行,不带编辑器,且只能加载烘焙后(cooked)的资源。

    (11)检查参数是否在运行【make工具】,也就是命令行第一个token为"MAKE"或"MAKECOMMANDLET",这是对脚本和资源进行打包处理的命令,此处只做标记,真正的处理在后面。

    (12)检查参数并设置GIsSimMobile,这看起来似乎是指在PC上模拟手机运行。

    (13)检查参数并设置GForceLogFlush,强制每次记log时都立即刷新,在FOutputDeviceFile::Serialize中有对其判断。

    (14)检查参数并设置GForceSoundRecook,强制重新烘焙声音资源,在UConformCommandlet::Main中有对其判断。

    (15)检查参数并设置GAlwaysBiasCompressionForSize,强制以最小体积模式做压缩,此变量影响一个非常基础通用的函数appCompressMemory,并不局限何种内容的数据。

    (16)检查参数并设置GShouldVerifyGCAssumptions,用途暂不明。

    (17)检查参数并设置GShouldTraceFaceFX、 GShouldTraceAnimationUsage,这应该都是性能统计相关。

    (18)这里开始出现一个奇怪的分支:如果在XBOX平台,那么从这里就直接调用appInit,去做“app初始化”了;而在其它平台,则在进入appInit之前,尚有大量工作要做,下面一一列出。

    (19)设置几个用于线程管理的全局对象GSynchronizeFactory、GThreadFactory、GThreadPool分别指向当前平台的对应实现,从XBOX/PS3到IPHONE/ANDROID/WIN不一而足。

    (20)创建线程池GThreadPool,在win上还有特别的GHiPriThreadPool。

    (21)在win上居然还有一段关于防火墙安装的代码,真是匪夷所思,跳过不看了

    (22)至此,调用appInit,进入另一层级的“初始化”阶段。420行!9屏!

      (a)设置全局对象GLogConsole、GError、GWarn,这些是由外层传入的参数来决定的,而这些实参也是一些依平台而定的全局对象,与(19)类似,各平台都定义了自己的LogErrorWarn渠道

      (b)设置全局对象GCallbackEvent、GCallbackQuery,这两形式同上,用途暂不明

      (c)如果要跟踪测试对象和类序列化操作性能,创建相应记录结构

      (d)设置GCmdLine,存储当前的命令行参数,比较奇特的是在非发行版中,还会检查环境变量"UE3-CmdLineArgs",其值将做为额外的命令行参数附加到前者中。

      (e)调用appSocketInit初始化网络层,设置GSocketSubsystem为当前平台实现并调用初始化函数,如在Windows上做例行的WSAStartup等操作。

      (f)设置全局对象GFileManager,也是外层传进的参数,与(a)类似

      (g)调用appPlatformPreInit,执行平台相关初始化操作,在Windows上主要是获取了屏幕大小等系统参数,另外还有个相当隐密的操作!即InitSHAHashes:从exe文件的资源节里提取一段嵌入密钥,也就是项目目录中的Build/Hashes.sha。这个东东具体是做什么用的现在还不清楚。

      (h)设置全局变量GSystemStartTime,记录当前时间,居然是个字符串变量,值如:

        + GSystemStartTime (19) "2016.03.24-18.46.17" FString

      (i)设置全局变量GEngineIni、GSystemSettingsIni、GGameIni、GInputIni、GUIIni等,以及另一组前缀带Default的变量,这些都是配置文件的路径,其中带Default前缀的是供修改的,然后在运行时被读入并覆盖引擎默认的值,得到的最终版本会写入不带Default前缀的那组文件中。

      (j)GFileManager初始化,需要在(i)之后,因为它要用到这些ini里的配置

      (k)重设MiniDumpFilenameW,因为现在有了更多信息可以更合理的保存它,如log目录

      (l)检查参数并设置GIsBuildMachine。这个看起来应该是给专门的打包机用的,因为根据搜索结果,只有在这个变量为TRUE时,才可以忽略float render target创建失败的错误,而一般打包机不会专门配很好的显卡,正好相符。

      (m)调用GLog->AddOutputDevice,根据情形添加一堆输出设备

      (n)设置全局对象GConfig,这是个ini总管理器FConfigCacheIni的实例,然后调用appCheckIniForOutdatedness,创建第(i)步中所有的ini实例

      (o)调用LoadConsoleVariablesFromINI,从ConfigConsoleVariables.ini中加载ConsoleVar,如没有就跳过

      (p)检查参数并设置各种全局变量:GWarn->TreatWarningsAsErrors、GIsUnattended、GIsSilent、GIsUTracing,都是琐碎标记

      (q)检查参数并设置GLightmassDebugMode、GLightmassStatsMode,静态全局光照的调试统计开关

      (r)显示控制台窗口,输出第一波log,用的是debugf( NAME_Init……

      (s)检查参数并设置cpu相依性

      (t)检查参数并设置GPrintLogTimes,控制写log时是否带上时间戳。

      (u)调用appDeleteOldLogs删除旧log

      (v)调用appPlatformInit,注意第(g)步,那里是PreInit,这里终于Init了。里面主要是初始随机数种子、获取系统内存状态。

      (w)再次调用appSocketInit,是对第一次调用的补充。

      (x)如果集成了STEAMWORKS,调用appSteamInit

      (y)调用GStatManager.Init(),用于对象状态统计跟踪

      (z)调用UObject::StaticInit(),这是非常重要的一步,用于UObject体系的初始化:

        1.在这里设置了大量有关UObject对象池、链等全局管理容器,

        2.调用AutoInitializeRegistrantsCore,这是Core模块的native class注册的地方

        3.调用ProcessRegistrants,处理所有之前创建的UObject的注册,因为在这之前UObject体系还未建立 ,正常的注册机制还不能用,所以都记录在列表里,延迟到此刻处理。

        4.一个关键变量UObject::GObjInitialized此刻被赋值为1

      (A)初始化USystem类(的默认对象),并加载配置,主要是设置好它身上的各种Path字段

      (B)接着new出一个USystem类的实例,存储在全局变量GSys上

      (C)通过GSys设置所有类型Log默认都压抑

      (D)通过UObject::SetLanguage(*appGetLanguageExt())设置语言

      (E)调用UploadHardwareSurveyIfNecessary,上传硬件调查?

      (F)设置全局对象GPackageFileCache,用途暂不明

      (G)初始化颜色表:GColorList.CreateColorMap

    (23)好了,终于从appInit中出来,回到GEngineLoop.PreInit 

    (24)如果是GUseSeekFreeLoading模式,从配置中读取全局变量GUseSubstanceInstallTimeCache

    (25)处理命令行参数中带"firstinstall"时的情形:暂不明白何时会带有这种参数 ?总之处理完就appRequestExit准备退出了

    (26)处理命令行参数中的"EATMEM",看起来似乎是预先消耗一定量的内存,具体目的暂不明白

    (27)创建全局对象GTaskPerfTracker、GTaskPerfMemDatabase,看注释说是用于全局任务性能跟踪

    (28)Windows上检查是否满足最小分辨率,并调用HandleGameExplorerIntegration处理game explorer集成问题

    (29)初始化系统配置表GSystemSettings.Initialize

    (30)从配置中获取一些重要的渲染参数:GUseTextureStreaming、GAllowScreenDoorFade、GAllowNvidiaStereo3d、GUseStreamingPause、GRenderMode

    (31)处理从远程桌面运行的情形

    (32)初始化RHI:Render Hardware Interface

    (33)将usf转化成二进制:GenerateBinaryShaderFiles,创建全局对象GShaderCompilingThreadManager

    (34)检查脚本是否有更新,对其进行编译

    (35)调用InitializeRegistrantsAndRegisterNames,注册所有模块的脚本类

    ……太多了,还没写到一半。。。未完待续

  • 相关阅读:
    万豪项目总结
    解决jquery animate({scrollTop$pos},500)与$(window).scroll方法冲突的问题
    一波水文来袭-让我们一起谈谈闭包【原创】
    JS模块化规范AMD之RequireJS
    JS模块化规范CMD之SeaJS
    邂逅Sass和Compass之Compass篇
    邂逅Sass和Compass之Sass篇
    idea 修改SVN账户信息
    idea 创建/加载 maven项目速度较慢
    gitlab新建分支,idea中无法找到
  • 原文地址:https://www.cnblogs.com/wellbye/p/5313097.html
Copyright © 2011-2022 走看看