下面的源码以Android平台为例来分析UE4的启动过程(若要了解Windows和Linux平台的话,分析过程大体相似);这里涉及到的源码主要文件如下(文件路径为引擎源码的相对路径):
- Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp
- Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp
- Engine\Source\Runtime\RHI\Private\DynamicRHI.cpp
- Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp
- Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp
- Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp
- Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidEGL.cpp
- Engine\Source\Runtime\Core\Private\Android\AndroidPlatformMisc.cpp
- Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGLPrivate.h
- 首先,程序的入口Main函数在Engine\Source\Runtime\Launch\Private\Android\LaunchAndroid.cpp文件中(AndroidMain函数):
- AndroidMain函数中,会调用GEngineLoop.PreInit来初始化:
- GEngineLoop.PreInit最后会调用到Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp文件中的PreInit函数,如下图:
- FEngineLoop类的PreInit函数中会调用PreInitPreStartupScreen函数来初始化:
- FEngineLoop::PreInitPreStartupScreen这个函数实现比较复杂,前面会创建UE4中的一些线程等,我们关心的渲染相关的初始化在下面这里的RHIInit函数里面:
- RHIInit函数的实现在Engine\Source\Runtime\RHI\Private\DynamicRHI.cpp文件中,其主要通过GDynamicRHI对象的构建和Init来初始化渲染流程:
- 我们先看看PlatformCreateDynamicRHI()的实现,Android平台会执行Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp文件的PlatformCreateDynamicRHI函数: 该函数会调用DynamicRHIModule->CreateRHI来创建DynamicRHI对象:
- CreateRHI函数会调用到对应平台的文件(Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp): 这里可以看到主要是new了一个FOpenGLDynamicRHI对象,该类的构造函数实现就在同文件的CreateRHI函数实现的下面紧挨着;
- 我们先看FOpenGLDynamicRHI类构造函数中的PlatformInitOpenGL()函数的实现,该函数的实现在Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp文件中:
- 注意到这个函数里面的FAndroidGPUInfo::Get()调用,这个单例调用会构造FAndroidGPUInfo单例对象,看看FAndroidGPUInfo构造函数的实现(实现在Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGLPrivate.h文件中): 到这里,基本就快看到熟悉的EGL窗口的初始化过程了;
- 先看看FAndroidAppEntry::PlatformInit()函数实现,这个函数的实现会调用到AndroidOpenGL.cpp文件中的Init: 接着看看这里调用的AndroidEGL对象的Init函数实现;
- 跳转到Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidEGL.cpp文件的Init函数的实现: 这里基本上就是熟悉的EGLContext的创建,NativeWindow的绑定了;在这里可以看到UE4创建了3个EGLContext:SharedContext、RenderingContext、SingleThreadedContext;其中SharedContext与RenderingContext是共享的(用于多线程渲染),可以共享纹理、Shader等资源;
- 接着回到AndroidOpenGLPrivate.h文件中FAndroidGPUInfo的构造函数,会调用EGL->InitSurface()初始化EGLSurface;这里会创建两个EGLSurface:PImplData->eglSurface、PImplData->auxSurface;其中,PImplData->auxSurface会绑定到PImplData->SharedContext,PImplData->eglSurface会同时绑定到SingleThreadedContext和RenderingContext;
- 接着FAndroidGPUInfo的构造函数中调用EGL->SetCurrentSharedContext(),将SharedContext切换为当前线程的渲染上下文;
- 最后回到FOpenGLDynamicRHI构造函数中,再调用PlatformCreateOpenGLDevice()初始化EGLContext后(初始化时会根据是否使用多线程渲染来选择初始化的Context对象),最后仍然会将当先线程的上下文切换成SharedContext。
最后,需要注意,渲染引擎要怎么使用这三个EGLContext,会通过ini配置来确认是否用多线程渲染;Engine\Source\Runtime\Core\Private\Android\AndroidPlatformMisc.cpp文件中的FAndroidMisc::UseRenderThread()函数的返回值来确认是否需要使用多线程渲染;若使用多线程渲染,则AndroidEGL类中用到的GUseThreadedRendering变量将会置为true。若是多线程渲染,则共享Contex为SharedContext,Render Context为RenderingContext;否则共享Context和Render Context都是SingleThreadedContext;