- 1 UE4 Editor
- 2 WinMain-GuardedMain
- EnginePreInit
- FEngineLoop::PreInitPreStartupScreen
- FEngineLoop::PreInitPostStartupScreen
- EditorInit-EngineInit
- EngineTick
- EditorExit
- EngineExit
UE4的官方文档非常强大,根据文档下载虚幻引擎源代码一步步操作,使用release branch下载源码并编译成功后,下面开始虚幻引擎之路。根据源码编译出的UE4在Engine\Binaries\Win64中,双击UE4Editor.exe即可使用。
UE4 Editor
UE4引擎是使用C++语言开发的,伟大的《C语言》、《C++》、《Windows程序设计》告诉我们,win32程序通常是从main函数或者WinMain函数开始执行的。(排除全局变量构造等情况,只按常规套路出牌。) 那么UE4Editor.exe是不是也是从这里开始执行的呢?
在Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp中有WinMain函数。
从以上的函数实现可以看出,对于UE4的WinMain函数,主要执行的工作是设置windows环境,创建命名Mutex(关于Mutex,参考检查程序是否是第一个实例),根据参数不同使用不同形式调用GuardedMain函数。在GuardedMain函数退出之后,结束引擎的循环,并释放命名Mutex,退出程序。
从上面可以看出,在WinMain函数中,主要工作是根据不同的参数,采用合适的形式调用GuardedMain函数,实际上对引擎的调用和其余工作都是在GuardedMain函数中完成的。
由于UE4对windows的头文件进行了极大的裁剪,并且为了保持对出错之后生成dump文件或Crash报告的能力的支持,由于在WinMain函数中不具备这个能力,所以将大部分工作转移到GuardedMain函数中执行。
从GuardedMain函数开始游戏循环
GuardedMain的代码流程是很清晰的,加上Epic的注释,很容易理解整体框架。
首先,由于UE4对windows的头文件进行了大量精简,在进入GuardedMain时还有一些运行环境初始化的工作没做,因此,先调用FCoreDelegates::GetPreMainInitDelegate().Broadcast();完成初始化工作。
然后,申明了一个内部类的局部变量,这里主要利用内部类局部变量析构的时候会自动执行析构函数,从而确保EngineExit();始终会得到执行。
接下来,主要工作是执行int32 ErrorLevel = EnginePreInit( CmdLine ); 也就是进行引擎的预初始化工作。
之后,根据当前运行的程序是不是Editor,决定调用ErrorLevel = EditorInit(GEngineLoop);或ErrorLevel = EngineInit();,当本次运行的不是Editor的时候会执行EngineInit。经过代码跟踪,在EditorInit内部会调用EngineInit,也就是说Editor除了有游戏运行的相关资源要初始化之外还有很多其他的工作需要完成。
再接下来程序就进入游戏循环了EngineTick();。
当程序从游戏循环中退出的时候,如果是编辑器,需要执行EditorExit。再之后就是return退出函数了。
等等,按照对称原则,前面执行了EngineInit,后面应该有一个对应的Exit才对啊,难道不是Editor就不用Exit了么?原来,EngineExit在前面讲到的局部变量析构的时候会被自动调用,很对称。(Patrick:那么问题来了,为什么要用这种局部变量析构的方式,直接在最后调用Exit不好么?我猜测原因是如果抛出异常了,依然能对函数内的局部对象进行析构。)
UE4的D3D设备
UE4作为一款3D游戏引擎,当然也离不开显卡的支持。目前,主流的3D API主要包括DirectX、OpenGL、Vulkan三种。其中,OpenGL和Vulkan是支持跨平台的,而DirectX只能在windows平台上使用。UE4在windows平台上可以选择使用DX12或DX11等directX设备作为渲染设备。而D3D设备在使用之前是必须先创建的。
那么,今天让我们一起来探寻windows下使用DirectX作为渲染API时是在什么时候创建的D3D设备。
在上一篇中,我们知道UE4启动之后经过了PreInit,init,tick,exit四个过程。那么,我们首先从PreInit开始寻找。
EnginePreInit函数实现在Engine\Source\Runtime\Launch\Private中进行实现,实现过程只是调用在Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp中实现的FEngineLoop::PreInit函数,由此可见,重点在FEngineLoop::PreInit函数。
我们查看FEngineLoop::PreInit的源代码,发现该函数是一个很长的函数,从934行一直到2401行,总行数1468行。在这个长长的函数中,我们需要寻找我们关心的内容。
在2584行,调用了RHIInit函数。(Patrick:代码有变化,但是这个函数还在)这里,出现了RHI这个名词。RHI是Render Hardware Interface(渲染硬件接口)的意思。由于UE4的跨平台的需求,不同平台上需要使用不同的3D API来进行渲染。为了对上层程序员隐藏底层渲染的实现,UE4提出了RHI的概念,将底层渲染相关工作全部通过RHI来进行抽象,使上层程序员不用关系底层到底是用的是什么3D API,只需要关心实现的效果即可。
在RHIInit中,判断了GDynamicRHI指针的有效性,从变量命名来看,这是一个全部的DynamicRHI指针,极有可能是指向渲染设备的。因此,本指针内容的创建极有可能包含3D设备的创建过程。函数中进行了如下调用GDynamicRHI = PlatformCreateDynamicRHI();,可见,这是在创建GDynamicRHI的内容,咱们需要继续深入。
在PlatformCreateDynamicRHI中,经过一系列对平台所支持的API的判断以及本程序所选择的API的判断之后,最终来到了以下调用:DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
创建了一个DynamicRHI对象并将指针返回给了GDynamicRHI指针。可见,我们需要重点关注CreateRHI接口。此时,DynamicRHIModule是一个windows D3D11 的RHI Module。因此,我们可以继续向下。
在Engine\Source\Runtime\Windows\D3D11RHI\Private\Windows\WindowsD3D11Device.cpp中,定义了该module的CreateRHI接口。GD3D11RHI = new FD3D11DynamicRHI(DXGIFactory1,ChosenAdapter.MaxSupportedFeatureLevel,ChosenAdapter.AdapterIndex,ChosenDescription);
这里貌似就要达到目标了,可当我们进入FD3D11DynamicRHI的构造函数的时候,在里面却并没有发现熟悉的创建D3D11设备的过程。但,在FD3D11DynamicRHI类中,我们发现了一下两个成员函数InitD3DDevice和Init
其中Init函数内部调用了InitD3DDevice函数。InitD3DDevice的实现如下:
从中可以看出,UE4中D3D11设备的创建时在InitD3DDevice函数中进行的(Patrick:D3D11CreateDevice),那是在什么时候调用的这个函数呢?
前面我们已经发现Init函数会调用它,那么FD3D11DynamicRHI::Init是在什么时候被调用呢?
我们回到RHIInit函数中,发现通过PlatformCreateDynamicRHI函数创建了FD3D11DynamicRHI对象之后会紧接着就进行该RHI对象的Init操作,从而调用InitD3DDevice函数完成第一个D3D11设备的创建。
至此,D3D11设备就被创建出来并用于相关资源的初始化工作,待渲染进行开始之后就可以用于渲染。
RHI
RHI: Render hardware interface 渲染硬件层接口, 本人理解RHI是一套硬件无关,平台无关的图形渲染API.
它是如何做到与平台无关,与硬件无关的呢?
每个RHI接口都对应着多个图形API的实现版本. 对于每个RHI接口,其实都有针对DX11,DX12,OpenGL等版本的实现。对于不同平台,引擎初始化的时候就已经确定要用哪一套图形API了。之后,调用的RHI其实就是对应初始化时候确定用的那套图形API实现的版本.
比如RHIDrawIndexedPrimitive接口,对于DX,OPENGL,其实都实现了RHIDrawIndexedPrimitive接口。
当引擎初始化的时候,判断是在windows平台上的,并决定使用DX11。之后,当调用RHIDrawIndexedPrimitive接口时,其实调用的是DX11版本的RHIDrawIndexedPrimitive。
对于RHI使用者而已,不需要关心是调用了哪套图形API,反正能正确运行,从而造成跨平台的假象;而从开发角度而言,RHI并不是平台无关的,它需要开发人员呕心沥血地开发和维护,以保证RHI在不同平台下运行结果都一样。
DynamicRHI.h里 FDynamicRHI, IRHIComputeContext两个虚基类里定义了所有的RHI接口。
实现RHI接口的子类有:
- 1. class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContext
- 2. class FD3D12DynamicRHI : public FDynamicRHI
- 3. class OPENGLDRV_API FOpenGLDynamicRHI : public FDynamicRHI, public IRHICommandContext
-
4. class FVulkanDynamicRHI : public FDynamicRHI
新一代跨平台,充分利用多核多线程的图形API Vulkan
-
5. class FMetalDynamicRHI : public FDynamicRHI, public FMetalRHICommandContext
苹果系统的图形API Metal
下面开始原创了
UE4的Android OpenGL ES设备
嗯,前面都是抄的,下面开始自己发挥了。
笔者对DX11不熟悉,就看老本行的Android OpenGL ES了。还是从Engine\Source\Runtime\RHI\Private\DynamicRHI.cpp的GDynamicRHI = PlatformCreateDynamicRHI();开始,从这里就开始根据平台不同,产生了分支,上面的抄录是使用Engine\Source\Runtime\RHI\Private\Windows\WindowsDynamicRHI.cpp分支。笔者下面准备走Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp分支。
在PlatformCreateDynamicRHI函数中,可以看到,也会有两个分支供选择,VulkanRHI和OpenGLDrv。这里我们还是选择OpenGLDrv,下面将使用OpenGLDrv的module,调用函数创建RHI,DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);。具体函数实现是在Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp,在这里可以看到真正的RHI被创建出来了return new FOpenGLDynamicRHI();
先看FOpenGLDynamicRHI的构造函数
Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp
首先,会先根据FAndroidGPUInfo::Get().GLVersion.Split(TEXT("OpenGL ES "), nullptr, &FullVersionString);判断是否支持OpenGL ES3.1
然后,根据GConfig->GetBool(TEXT("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings"), TEXT("bBuildForES31"), bBuildForES31, GEngineIni);判断是否开启使用OpenGL ES3.1
如果,支持且开启OpenGL ES3.1,会先判断是否支持ES3.2,然后先清理环境FAndroidAppEntry::ReleaseEGL();,再进行初始化AndroidEGL::GetInstance()->Init(AndroidEGL::AV_OpenGLES, FAndroidOpenGL::GLMajorVerion, FAndroidOpenGL::GLMinorVersion, false);。
ReleaseEGL的时候,先得到AndroidEGL单例AndroidEGL* EGL = AndroidEGL::GetInstance();,具体实现是在Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidEGL.cpp中Singleton = new AndroidEGL();其中包含了各种context、surface、display、window、尺寸等信息
然后,会判断如果EGL已经被初始化了(Patrick:这里有个小问题,因为c++的bool变量初始值是不确定的,这里希望是false,如果初始化成了true,后面init就出问题了),则通过:
- glDeleteFramebuffers和glDeleteRenderbuffers执行DestroyBackBuffer操作。
- eglMakeCurrent(PImplData->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);执行ResetDisplay操作。
- eglDestroyContext(PImplData->eglDisplay, PImplData->SharedContext/RenderingContext/SingleThreadedContext.eglContext);执行DestroyContext操作。
- eglDestroySurface(PImplData->eglDisplay, PImplData->eglSurface/auxSurface);执行DestroySurface操作。
- eglTerminate(PImplData->eglDisplay);执行TerminateEGL操作。
AndroidEGL::GetInstance()->Init的时候,会先InitEGL,然后再InitContexts。其中,InitEGL会执行下面操作
- PImplData->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLBoolean result = eglInitialize(PImplData->eglDisplay, 0 , 0);
- FString Extensions = ANSI_TO_TCHAR( eglQueryString( PImplData->eglDisplay, EGL_EXTENSIONS));获取支持的EGL extension
- bSupportsKHRCreateContext = Extensions.Contains(TEXT("EGL_KHR_create_context"));EGL_KHR_create_context
这个extension主要是为了使得EGL支持ES3的,因为使用ES3的话需要创建一个ES3相关的context。UE4中如果发现这个extension不支持的话,则直接创建一个ES2的context。具体的说,它做了如下功能:
- eglCreateContext的第四个参数attrib_list是key-value对,在egl1.4的时候,key只能是EGL_CONTEXT_CLIENT_VERSION ,value也只有1或者2,用于指定创建ES1或者ES2的context。但是,现在key可以是EGL_CONTEXT_MAJOR_VERSION_KHR(EGL_CONTEXT_CLIENT_VERSION的别名,默认为1,backwards compatible兼容老版本)、EGL_CONTEXT_MINOR_VERSION_KHR(默认为0)、EGL_CONTEXT_FLAGS_KHR、EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR(OpenGL)、EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR。
- key EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR(OpenGL 是GL_ARB_robustness extension中的一个属性) 对应 value EGL_NO_RESET_NOTIFICATION_KHR、EGL_LOSE_CONTEXT_ON_RESET_KHR
- key EGL_CONTEXT_FLAGS_KHR(默认为0) 对应 value EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR(会创建一个用于debug的context,用于检测、logger(会影响性能),针对这个context更细节的要求还没有,所以具体如何工作要看各个厂商的实现。如果这一位被设置了,GL中的GL_KHR_debug也同时会被开启。如果在老的EGL driver中开启了这个功能,会报错,虽然这个与前面提到的backwards compatible兼容老版本冲突,但是khronos会认为这个没问题,UE4中暂时应该没有分支能走到这个功能)、EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR(OpenGL,Forward-compatible,针对OpenGL 3.0及以后版本,如果开启这一位,则可以不再兼容标记deprecated的API)、EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR(OpenGL,会创建一个支持robust buffer access的context,所以,这个context必须支持GL_ARB_robustness这个extension,或者对应一个支持该函数的OpenGL版本)
- key EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR(OpenGL) 对应 value EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR、EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR
- eglChooseConfig的第二个参数是提供一个config模板,也是key-value对,其中有个key是EGL_RENDERABLE_TYPE,在egl1.4的时候,其对应的value只能是EGL_OPENGL_BIT对应OpenGL 1/2,EGL_OPENGL_ES_BIT对应OpenGL ES1,EGL_OPENGL_ES2_BIT对应OpenGL ES2,EGL_OPENVG_BIT对应OpenVG 1。但是,现在value可以是EGL_OPENGL_ES3_BIT_KHR
- bSupportsKHRSurfacelessContext = Extensions.Contains(TEXT("EGL_KHR_surfaceless_context"));EGL_KHR_surfaceless_context
如果应用程序只需要渲染到FBO,则完全不需要搞一个EGL Surface。具体的实现是在调用eglMakeCurrent指定context的时候,read/write surface设置为EGL_NO_SURFACE。
针对OpenGL ES来说,只有支持GL_OES_surfaceless_context,才有可能支持这个extension。
- bSupportsKHRNoErrorContext = Extensions.Contains(TEXT("EGL_KHR_create_context_no_error"));
与GL_KHR_no_error 共同使用,当UE4 UE_BUILD_SHIPPING 开启的时候,会使用这个功能
- 当GL出错的时候,不再输出错误信息,并且得到未知结果(通过glGetError会得到NO_ERROR或者OUT_OF_MEMORY的结果,OUT_OF_MEMORY是唯一幸存的报错信息)。好处是更加快速和节能。
- 使用方式是:通过eglCreateContext创建context的时候,第四个参数的key为EGL_CONTEXT_OPENGL_NO_ERROR_KHR,value默认为EGL_FALSE
- 当使用share contexts的时候,share的若干个context这个属性一定要一样。
- 当有debug或者robustness context的时候,value不能为EGL_TRUE。
- 如果当前设备不支持这个mode,但是依然按照这个config去创建的话,不会导致创建context失败。
- 针对CheckFramebufferStatus等API还是能得到想要的结果。
- result = eglBindAPI(EGL_OPENGL_ES_API);
- eglChooseConfig(PImplData->eglDisplay, Attributes, NULL, 0, &PImplData->eglNumConfigs);获取该display支持的config中,与提供的config匹配的共多少个
这里我一般是通过ret = eglGetConfigs(egl_display, NULL, 0, &count_configs)获取。而且这里第三个参数是0,按说这里应该是一个很大值才对,具体参见eglChooseConfig,UE4提供的config为:
- EGL_RED_SIZE : 5
- EGL_GREEN_SIZE : 6
- EGL_BLUE_SIZE : 5
- EGL_ALPHA_SIZE : 0
- EGL_DEPTH_SIZE : 16
- EGL_STENCIL_SIZE : 8
- EGL_SAMPLE_BUFFERS : 0
- EGL_SAMPLES : 0
- EGL_RENDERABLE_TYPE : EGL_OPENGL_ES2_BIT
- EGL_SURFACE_TYPE : EGL_WINDOW_BIT | EGL_PBUFFER_BIT
- EGL_CONFIG_CAVEAT : EGL_NONE
- result = eglChooseConfig(PImplData->eglDisplay, Attributes, EGLConfigList, NumConfigs, &PImplData->eglNumConfigs);获取display支持的所有与提供config匹配的config
- eglGetConfigAttrib(PImplData->eglDisplay, EGLConfigList[i], EGL_RED_SIZE, &ResultValue); r = ResultValue;逐一获取所有config的具体数值,并进行排序(优先级从高到低:sampleBuffers、sampleSamples、redSize、greenSize、blueSize、depthSize、bNonLinearDepth最好为1、stencilSize、alphaSize)。和提供config完全一样,最好。
其他属性都比较熟悉,唯有EGL_DEPTH_ENCODING_NV需要参考EGL_NV_depth_nonlinear,
- 由于perspective除法的原因,传统的整数Zbuffer,如果位数很少,在远平面附近的物件ZTest可能测试不准确(即使远平面和近平面的比例为100:1也可能出现问题)。这个扩展就是为OpenGL增加non-linear Zbuffer,可以提高实际使用范围,比如16位depthbuffer可以提高16倍,当远近平面之间的比例无法被严格控制的时候,大大提高ZTest的质量。
- key DEPTH_COMPONENT对应value DEPTH_COMPONENT16_NONLINEAR_NV
- eglChooseConfig、eglCreatePbufferSurface、eglGetConfigAttrib多了一个key EGL_DEPTH_ENCODING_NV,对应value EGL_DEPTH_ENCODING_NONE_NV、EGL_DEPTH_ENCODING_NONLINEAR_NV、EGL_DONT_CARE(默认值)
InitEGL之后则调用InitContexts,具体实现如下:
- PImplData->SharedContext.eglContext = eglCreateContext(PImplData->eglDisplay, PImplData->eglConfigParam, EGL_NO_CONTEXT , ContextAttributes);
- PImplData->RenderingContext.eglContext = eglCreateContext(PImplData->eglDisplay, PImplData->eglConfigParam, PImplData->SharedContext.eglContext , ContextAttributes);
- PImplData->SingleThreadedContext.eglContext = eglCreateContext(PImplData->eglDisplay, PImplData->eglConfigParam, EGL_NO_CONTEXT , ContextAttributes);
这里的ContextAttributes,根据是否支持bSupportsKHRCreateContext,也就是ES3。如果不支持,则走ES2,只有一个key EGL_CONTEXT_CLIENT_VERSION 对应 value 2。如果支持,EGL_CONTEXT_MAJOR_VERSION_KHR对应MajorVersion(理论上是3),EGL_CONTEXT_MINOR_VERSION_KHR对应MinorVersion,UE_BUILD_SHIPPING的时候EGL_CONTEXT_OPENGL_NO_ERROR_KHR对应EGL_TRUE,bDebug的时候(目前没这种情况)EGL_CONTEXT_FLAGS_KHR对应EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR。
到此,AndroidEGL::GetInstance()->Init就完全结束了。
不支持或者不开启OpenGL ES3.1的设备,UE4就不支持了。
Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp
FPlatformOpenGLDevice* Device = new FPlatformOpenGLDevice();这是一个struct,且构造函数是空的。(Patrick:这里顺便回忆一下C++中struct和class的区别:1.默认访问权限不同,struct是public,class是private。2.在继承关系,struct默认是public的,而class是private。3.class可用于定义模板参数,但是strcut不用于定义模板。4.struct能用{}赋值,比如:A a = {'p', 7, 451.154}; //定义时赋初值,在struct时没问题,在class时出错。但是,如果struct中加入一个构造函数(或虚函数),会使strcut更体现出一种对象的特性,使得{}操作不再有效。加入一个普通的成员函数,{}依旧可用。因为可以将普通的函数理解成对数据结构的一种算法,这并不打破它数据结构的特性。总结:当你觉得你要做的更像是一种数据结构集合的话,那么用struct。如果你要做的更像是一种对象的话,那么用class。)
然后执行Device->Init();,具体如下:
- FPlatformRHIFramePacer::Init(new FAndroidOpenGLFramePacer());
FAndroidOpenGLFramePacer是一个结构体,定义了一些swapbuffer的函数,以及帧率/间隔等对应的变量,无构造函数。
FPlatformRHIFramePacer,在Android中对应FAndroidPlatformRHIFramePacer,继承FGenericPlatformRHIFramePacer,定义在Engine\Source\Runtime\ApplicationCore\Public\Android\AndroidPlatformFramePacer.h
在Engine\Source\Runtime\ApplicationCore\Private\Android\AndroidPlatformFramePacer.cpp中可以看到FAndroidPlatformRHIFramePacer::Init函数实际上也就只是调用了FAndroidOpenGLFramePacer::Init函数
在Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGLFramePacer.cpp中可以看到FAndroidOpenGLFramePacer::Init核心是调用了SwappyGL_init(FAndroidApplication::GetJavaEnv(), FJavaWrapper::GameActivityThis);但是这个函数,在UE4源码中并没有看到实现
- bRunningUnderRenderDoc = glIsEnabled(GL_DEBUG_TOOL_EXT) != GL_FALSE;判断是否使用RenderDoc
- bool bCreateSurface = !AndroidThunkCpp_IsOculusMobileApplication();如果是Oculus则不需要Surface
- AndroidEGL::GetInstance()->InitSurface(false, bCreateSurface);创建Surface
FPlatformMisc::GetOverrideResolution(OverrideResX, OverrideResY)获取设备宽、高
ANativeWindow_setBuffersGeometry(PImplData->Window, Width, Height, PImplData->NativeVisualID);
CreateEGLSurface(PImplData->Window, bCreateWndSurface);详情如下:
- 如果bCreateWndSurface,PImplData->eglSurface = eglCreateWindowSurface(PImplData->eglDisplay, PImplData->eglConfigParam,InWindow, NULL);创建Surface
- eglSurfaceAttrib(PImplData->eglDisplay, PImplData->eglSurface, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);将Surface的EGL_TIMESTAMPS_ANDROID设置为true,这里涉及到extensionEGL_ANDROID_get_frame_timestamps,也就是当使用WindowSurface的时候,通过开启这个,可以记录下时间戳,进而计算出GPU pipeline的耗时。
- eglQuerySurface(PImplData->eglDisplay, PImplData->eglSurface, EGL_WIDTH, &PImplData->eglWidth) && eglQuerySurface(PImplData->eglDisplay, PImplData->eglSurface, EGL_HEIGHT, &PImplData->eglHeight)确保创建的surface宽高均大于0
- 如果bCreateWndSurface==false,PImplData->eglSurface = eglCreatePbufferSurface(PImplData->eglDisplay, PImplData->eglConfigParam, pbufferAttribs);
- PImplData->auxSurface = eglCreatePbufferSurface(PImplData->eglDisplay, PImplData->eglConfigParam, pbufferAttribs);创建一个off-screen的surface。
PImplData->SharedContext.eglSurface = PImplData->auxSurface;
PImplData->RenderingContext/SingleThreadedContext.eglSurface = PImplData->eglSurface;
- LoadEXT获取GL/EGL extension的函数
- glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)((void*)eglGetProcAddress("glGenVertexArrays"));GL_OES_vertex_array_object
- glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)((void*)eglGetProcAddress("glBindVertexArray"));GL_OES_vertex_array_object
VAO是OpenGL ES3.0 main spec的内容,在之前版本需要extensionGL_OES_vertex_array_object,主要目的是:如果按照ABA的顺序绘制AB这两种共3个物件,如果使用VBO,需要在绘制A的时候设置一遍A的VBO,绘制B的时候再设置一遍B的VBO,再绘制A的时候再设置一遍A的VBO。而如果使用VAO,则可以在最开始使用两个VAO记录A和B,然后在绘制的时候,先使用A的VAO绘制,然后B的VAO,然后再A的VAO,省去一些API调用。VAO在share context中不共享。参考文章VAO(vertex array object)和VBO(vertex buffer object)的详解
- eglGetSystemTimeNV_p = (PFNEGLGETSYSTEMTIMENVPROC)((void*)eglGetProcAddress("eglGetSystemTimeNV"));EGL_NV_system_time
从driver而非OS获取系统时间,eglGetSystemTimeNV() / eglGetSystemTimeFrequencyNV()可以获取到系统时间(以秒为单位)
- eglCreateSyncKHR_p = (PFNEGLCREATESYNCKHRPROC)((void*)eglGetProcAddress("eglCreateSyncKHR"));EGL_KHR_fence_sync
- eglDestroySyncKHR_p = (PFNEGLDESTROYSYNCKHRPROC)((void*)eglGetProcAddress("eglDestroySyncKHR"));EGL_KHR_fence_sync
- eglClientWaitSyncKHR_p = (PFNEGLCLIENTWAITSYNCKHRPROC)((void*)eglGetProcAddress("eglClientWaitSyncKHR"));EGL_KHR_fence_sync
- eglGetSyncAttribKHR_p = (PFNEGLGETSYNCATTRIBKHRPROC)((void*)eglGetProcAddress("eglGetSyncAttribKHR"));EGL_KHR_fence_sync
Fence是一种同步机制。GPU是异步的,也就是说当调用GL command返回时这条命令并不一定完毕了。仅仅是把这个命令放在本地的command buffer里。详细什么时候这条GL command被真正运行完毕CPU是不知道的,除非CPU使用glFinish()等待这些命令运行完。但是glFinish是阻塞的,导致CPU为了等GPU自己也不能工作了,会影响性能。假设用Fence的话就能够等GPU真的要用的时候再堵塞,而那之前CPU和GPU是能够并行工作的。eglCreateSyncKHR ()、eglDestroySyncKHR()用于产生和销毁同步对象。这个对象是往GL command队列中插入的一个特殊操作,当运行到它时,会发出信号指示队列前面的命令已所有运行完成。函数eglClientWaitSyncKHR()可让调用者堵塞,等待信号发生。参考文章Android系统中GraphicBuffer的同步-Fence同步机制
- eglPresentationTimeANDROID_p = (PFNeglPresentationTimeANDROID)((void*)eglGetProcAddress("eglPresentationTimeANDROID"));
指定将surface的当前颜色buffer呈现给viewer的时间,设置的时候不会导致颜色buffer呈现,而是在之后将颜色buffer发布到display或者pixmap的时候(Patrick:比如eglswapbuffer。),才使用该时间。作用:比如,view中声音和图像的同步,(Patrick:再比如framepacing?这样使得每帧之间时间间隔固定,比一帧短一帧长更平滑。UE4貌似也确实这么用了)
- eglGetNextFrameIdANDROID_p = (PFNeglGetNextFrameIdANDROID)((void*)eglGetProcAddress("eglGetNextFrameIdANDROID"));EGL_ANDROID_get_frame_timestamps
- eglGetCompositorTimingANDROID_p = (PFNeglGetCompositorTimingANDROID)((void*)eglGetProcAddress("eglGetCompositorTimingANDROID"));EGL_ANDROID_get_frame_timestamps
- eglGetFrameTimestampsANDROID_p = (PFNeglGetFrameTimestampsANDROID)((void*)eglGetProcAddress("eglGetFrameTimestampsANDROID"));EGL_ANDROID_get_frame_timestamps
- eglQueryTimestampSupportedANDROID_p = (PFNeglQueryTimestampSupportedANDROID)((void*)eglGetProcAddress("eglQueryTimestampSupportedANDROID"));EGL_ANDROID_get_frame_timestamps
- eglGetCompositorTimingSupportedANDROID_p = (PFNeglQueryTimestampSupportedANDROID)((void*)eglGetProcAddress("eglGetCompositorTimingSupportedANDROID"));EGL_ANDROID_get_frame_timestamps
- eglGetFrameTimestampsSupportedANDROID_p = (PFNeglQueryTimestampSupportedANDROID)((void*)eglGetProcAddress("eglGetFrameTimestampsSupportedANDROID"));EGL_ANDROID_get_frame_timestamps
- glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)((void*)eglGetProcAddress("glDebugMessageControlKHR"));GL_KHR_debug
- glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)((void*)eglGetProcAddress("glDebugMessageInsertKHR"));GL_KHR_debug
- glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)((void*)eglGetProcAddress("glDebugMessageCallbackKHR"));GL_KHR_debug
- glDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)((void*)eglGetProcAddress("glDebugMessageLogKHR"));GL_KHR_debug
- glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)((void*)eglGetProcAddress("glGetPointervKHR"));GL_KHR_debug
- glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)((void*)eglGetProcAddress("glPushDebugGroupKHR"));GL_KHR_debug
- glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)((void*)eglGetProcAddress("glPopDebugGroupKHR"));GL_KHR_debug
- glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)((void*)eglGetProcAddress("glObjectLabelKHR"));GL_KHR_debug
- glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)((void*)eglGetProcAddress("glGetObjectLabelKHR"));GL_KHR_debug
- glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)((void*)eglGetProcAddress("glObjectPtrLabelKHR"));GL_KHR_debug
- glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)((void*)eglGetProcAddress("glGetObjectPtrLabelKHR"));GL_KHR_debug
- glGetProgramBinary = (PFNGLGETPROGRAMBINARYOESPROC)((void*)eglGetProcAddress("glGetProgramBinaryOES"));GL_OES_get_program_binary
- glProgramBinary = (PFNGLPROGRAMBINARYOESPROC)((void*)eglGetProcAddress("glProgramBinaryOES"));GL_OES_get_program_binary
这个就厉害了,手游引擎现在应该都在做这个。原因是OpenGL ES的shader和DX有一个很重要的区别,DX可以离线将shader编程binary,放在游戏包体里,运行游戏时直接使用即可。但是手机GPU有众多厂商,每家芯片shader对应的binary都不同,必须要在目标手机上编译才行。过去的做法,只能是打开游戏的时候(比如在加载界面warmup),将所有的shader都编译binary,运行时直接使用(避免了运行时编译shader造成卡顿)。这样的话,缺点有两个:1.每次打开游戏的时候都多了编译shader这个步骤,导致进入游戏变慢,2.编译出来的binary要常驻内存,否则就要重新编译了。
有了programbinary之后,就可以在目标手机上,第一次打开的时候,将shader编译成binary,存在手机里,之后再打开游戏,直接从手机中加载即可,无需再做编译操作,也不需要常驻内存,使用的时候提前从手机中加载即可。具体使用方法在GL_OES_get_program_binary有例子。还有一个相关的extension GL_IMG_shader_binary
- PlatformRenderingContextSetup,设置Context、Surface和VAO,主要还是设置各种context对应的VAO
- SetCurrentRenderingContext
- 当前如果是GUseThreadedRendering,则确保eglMakeCurrent(PImplData->eglDisplay, PImplData->RenderingContext.eglSurface, PImplData->RenderingContext.eglSurface, PImplData->RenderingContext.eglContext)
- 反之,则确保eglMakeCurrent(PImplData->eglDisplay, PImplData->SingleThreadedContext.eglSurface, PImplData->SingleThreadedContext.eglSurface, PImplData->SingleThreadedContext.eglContext)
- 如果出现意外,比如eglGetCurrentContext()不为EGL_NO_CONTEXT,也不为上述context,则glFlush
- SetupCurrentContext
- 如果当前为GUseThreadedRendering,context eglGetCurrentContext()为PImplData->RenderingContext/SharedContext.eglContext,则获取RenderingContext/SharedContext的DefaultVertexArrayObject
- 否则,则获取SingleThreadedContext的DefaultVertexArrayObject
- 并通过glGenVertexArrays(1, DefaultVao);和glBindVertexArray(*DefaultVao);启用VAO
- InitDefaultGLContextState 获取并设置当前context的extension
- 通过glGetIntegerv(GL_NUM_EXTENSIONS, &ExtensionCount);获取当前context支持多少个extension,并通过glGetStringi(GL_EXTENSIONS, Index);逐一获取到
- glDisable(GL_DITHER);关闭dither(Patrick:注释上写的是UE4也不需要dither。而且Intel HD4000 <= 10.8.4开启dither的时候会在任何小于8bit的chanel进行dither,嗯,可以想到这样的话,深度就会出问题了。)
- glEnable(GL_FRAMEBUFFER_SRGB);开启GL_FRAMEBUFFER_SRGB以后,每次像素着色器运行后续帧缓冲,OpenGL将自动执行gamma校正,包括默认帧缓冲。需要注意,GL_FRAMEBUFFER_SRGB仅在OpenGL桌面以及OpenGLES 2.0的扩展GL_EXT_sRGB中可用。在OpenGLES 3.x上,只要帧缓冲区下面的纹理具有sRGB格式,在写入帧缓冲区时,颜色就会正确(自动)转换为sRGB。 。因此,当渲染为sRGB纹理时,您无需使用GL_FRAMEBUFFER_SRGB来启用sRGB转换。
- glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);这是OpenGL的extension,ES中并没有这个extension
- glEnable(GL_FRAMEBUFFER_FETCH_NONCOHERENT_QCOM);
framebufferfetch是一个很重要的特性,但是,它一直没有出现在OpenGL ES的main spec中,而是一直做为extension存在,虽然这个extension从es2.0时代就存在了。相关的extension共有如下几个:
- InitDebugContext
- PlatformSharedContextSetup,设置Context、Surface和VAO,主要还是设置各种context对应的VAO
- SetCurrentSharedContext
- 当前如果是GUseThreadedRendering,则确保eglMakeCurrent(PImplData->eglDisplay, PImplData->SharedContext.eglSurface, PImplData->SharedContext.eglSurface, PImplData->SharedContext.eglContext)
- 反之,则确保eglMakeCurrent(PImplData->eglDisplay, PImplData->SingleThreadedContext.eglSurface, PImplData->SingleThreadedContext.eglSurface, PImplData->SingleThreadedContext.eglContext)
- 如果出现意外,比如eglGetCurrentContext()不为EGL_NO_CONTEXT,也不为上述context,则glFlush
- SetupCurrentContext
- 如果当前为GUseThreadedRendering,context eglGetCurrentContext()为PImplData->RenderingContext/SharedContext.eglContext,则获取RenderingContext/SharedContext的DefaultVertexArrayObject
- 否则,则获取SingleThreadedContext的DefaultVertexArrayObject
- 并通过glGenVertexArrays(1, DefaultVao);和glBindVertexArray(*DefaultVao);启用VAO
- InitDefaultGLContextState 获取并设置当前context的extension
- InitDebugContext
- AndroidEGL::GetInstance()->InitBackBuffer();
- PImplData->ResolveFrameBuffer = 0;
- PImplData->OnScreenColorRenderBuffer = 0;
- PImplData->RenderingContext.ViewportFramebuffer =GetResolveFrameBuffer();
- PImplData->SharedContext.ViewportFramebuffer = GetResolveFrameBuffer();
- PImplData->SingleThreadedContext.ViewportFramebuffer = GetResolveFrameBuffer();
InitRHICapabilitiesForGL
Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp
- glGetString(GL_VENDOR) 获取vendor的信息。
- glGetString(GL_RENDERER) 获取硬件的信息。
- glGetString(GL_VERSION) 获取当前context的GL版本信息。
- glGetString(GL_SHADING_LANGUAGE_VERSION) 获取着色器语言的版本信息。
- FOpenGL4::ProcessExtensions
- bSupportsGPUMemoryInfo = ExtensionsString.Contains(TEXT("GL_NVX_gpu_memory_info"));
- bSupportsComputeShaders = ExtensionsString.Contains(TEXT("GL_ARB_compute_shader")) || (MajorVersion ==4 && MinorVersion >= 3) || (MajorVersion > 4);GL_ARB_compute_shader(OpenGL)
- bSupportsVertexAttribBinding = ExtensionsString.Contains(TEXT("GL_ARB_vertex_attrib_binding")) || (MajorVersion == 4 && MinorVersion >= 3) || (MajorVersion > 4);GL_ARB_vertex_attrib_binding(OpenGL)
- bSupportsTextureView = ExtensionsString.Contains(TEXT("GL_ARB_texture_view")) || (MajorVersion == 4 && MinorVersion >= 3) || (MajorVersion > 4);GL_ARB_texture_view(OpenGL)
- glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &MaxCombinedUAVUnits);
- glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &MaxPixelUAVUnits);
- glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &TextureBufferAlignment);
- glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &MaxComputeTextureImageUnits);
- glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_COMPONENTS, &MaxComputeUniformComponents);
- glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &MaxComputeUAVUnits);
- FOpenGL3::ProcessExtensions
- bSupportsTessellation = ExtensionsString.Contains(TEXT("GL_ARB_tessellation_shader")) || ((MajorVersion >= 4));GL_ARB_tessellation_shader(OpenGL)
- glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &MaxVertexUniformComponents);
- glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &MaxPixelUniformComponents);
- glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, &MaxGeometryUniformComponents);
- glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &MaxGeometryTextureImageUnits);
- glGetIntegerv(GL_MAX_VARYING_VECTORS, &MaxVaryingVectors);
- glGetIntegerv(GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, &MaxHullUniformComponents);
- glGetIntegerv(GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, &MaxDomainUniformComponents);
- glGetIntegerv(GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, &MaxHullTextureImageUnits);
- glGetIntegerv(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, &MaxDomainTextureImageUnits);
- glGetQueryiv(GL_TIMESTAMP, GL_QUERY_COUNTER_BITS, &TimestampQueryBits);
- FOpenGLBase::ProcessExtensions
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &MaxTextureImageUnits);
- glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &MaxVertexTextureImageUnits);
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &MaxCombinedTextureImageUnits);
- bSupportsASTC = ExtensionsString.Contains(TEXT("GL_KHR_texture_compression_astc_ldr"));GL_KHR_texture_compression_astc_ldr
- bSupportsCopyImage = ExtensionsString.Contains(TEXT("GL_ARB_copy_image"));GL_ARB_copy_image(OpenGL)
- bSupportsSeamlessCubemap = ExtensionsString.Contains(TEXT("GL_ARB_seamless_cube_map"));GL_ARB_seamless_cube_map(OpenGL)
- bSupportsTextureFilterAnisotropic = ExtensionsString.Contains(TEXT("GL_EXT_texture_filter_anisotropic"));GL_EXT_texture_filter_anisotropic
- bSupportsDrawBuffersBlend = ExtensionsString.Contains(TEXT("GL_ARB_draw_buffers_blend"));GL_ARB_draw_buffers_blend(OpenGL)
- FOpenGL3::ProcessExtensions
- GLuint FrameBuffer;
glGenFramebuffers(1, &FrameBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FrameBuffer);
GLuint VolumeTexture;
glGenTextures(1, &VolumeTexture);
glBindTexture(GL_TEXTURE_3D, VolumeTexture);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 256, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, VolumeTexture, 0);
bSupportsVolumeTextureRendering = (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glDeleteTextures(1, &VolumeTexture);
glDeleteFramebuffers(1, &FrameBuffer);由于没有相关extension,所以只能通过这种方式验证是否支持将3Dtexture(volume texture)作为FBO的RT。
- bSupportsSeparateShaderObjects = bUseSeparateShaderObjects && (ExtensionsString.Contains(TEXT("GL_ARB_separate_shader_objects")) || (MajorVersion == 4 && MinorVersion >= 4));GL_ARB_separate_shader_objects(OpenGL)
- bAndroidGLESCompatibilityMode = GetFeatureLevel() == ERHIFeatureLevel::ES3_1 && ExtensionsString.Contains(TEXT("GL_ARB_ES3_1_compatibility")) && FParse::Param(FCommandLine::Get(), TEXT("GLESCompat"));GL_ARB_ES3_1_compatibility(OpenGL)
- FOpenGL::InitDebugContext();
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &Value_GL_MAX_TEXTURE_SIZE);
- glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &Value_GL_MAX_TEXTURE_BUFFER_SIZE);GL_EXT_texture_buffer
- glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &Value_GL_MAX_CUBE_MAP_TEXTURE_SIZE);
- glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &Value_GL_MAX_ARRAY_TEXTURE_LAYERS);GL_NV_texture_array
- glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &Value_GL_MAX_3D_TEXTURE_SIZE);GL_OES_texture_3D
- glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &Value_GL_MAX_RENDERBUFFER_SIZE);
- glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &Value_GL_MAX_TEXTURE_IMAGE_UNITS);
- glGetIntegerv(GL_MAX_DRAW_BUFFERS, &Value_GL_MAX_DRAW_BUFFERS);
- glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &Value_GL_MAX_COLOR_ATTACHMENTS);
- glGetIntegerv(GL_MAX_SAMPLES, &Value_GL_MAX_SAMPLES);
- glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &Value_GL_MAX_COLOR_TEXTURE_SAMPLES);
- glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &Value_GL_MAX_DEPTH_TEXTURE_SAMPLES);
- glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &Value_GL_MAX_INTEGER_SAMPLES);
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &Value_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
- glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &Value_GL_MAX_VERTEX_ATTRIBS);
- glGetBooleanv(GL_STEREO, &Result);
- glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &Value_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);GL_EXT_texture_filter_anisotropic
- GMaxTextureMipCount = FMath::CeilLogTwo(Value_GL_MAX_TEXTURE_SIZE) + 1;
- GMaxTextureMipCount = FMath::Min(MAX_TEXTURE_MIP_COUNT, GMaxTextureMipCount);
- GSupportsParallelRenderingTasksWithSeparateRHIThread = false;
- GRHIThreadNeedsKicking = true;
- GRHISupportsExactOcclusionQueries = true;
- GSupportsRenderDepthTargetableShaderResources = true;
- GSupportsRenderTargetFormat_PF_G8 = true;
- GSupportsDepthBoundsTest = glDepthBoundsEXT != NULL(Patrick:网上看到这是OpenGL的extension,但是khronos上没找到,ES方面更是没听过。)
- GSupportsRenderTargetFormat_PF_FloatRGBA = true;
- GSupportsMultipleRenderTargets = true;
- GSupportsWideMRT = true;
- GSupportsTexture3D = true;
- GSupportsMobileMultiView = false;
- GSupportsImageExternal = false;
- GSupportsResourceView = true;
- GSupportsShaderFramebufferFetch = ExtensionsString.Contains(TEXT("GL_EXT_shader_framebuffer_fetch")) || ExtensionsString.Contains(TEXT("GL_NV_shader_framebuffer_fetch"))
|| ExtensionsString.Contains(TEXT("GL_ARM_shader_framebuffer_fetch ")); // has space at the end to exclude GL_ARM_shader_framebuffer_fetch_depth_stencil match;
- GSupportsShaderDepthStencilFetch = ExtensionsString.Contains(TEXT("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
- GHardwareHiddenSurfaceRemoval = true;
- GSupportsTimestampRenderQueries = TimestampQueryBits > 0;
- GRHISupportsBackBufferWithCustomDepthStencil = false;(Patrick:嗯,这个我也要画个问号)
- GRHISupportsLazyShaderCodeLoading = true;
- GRHISupportsTextureStreaming = bSupportsCopyImage;
- GVertexElementTypeSupport.SetSupported(VET_Half2, true);
- GVertexElementTypeSupport.SetSupported(VET_Half4, true);
- GVertexElementTypeSupport.SetSupported(VET_URGB10A2N, true);
- PF_A32B32G32R32F, FOpenGLTextureFormat(GL_RGBA32F, GL_RGBA32F, GL_RGBA, GL_FLOAT, false/*bCompressed*/, false/*bBGRA*/)
- PF_UYVY, FOpenGLTextureFormat( )
- PF_ShadowDepth, FOpenGLTextureFormat( GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false, false)
- PF_D24, FOpenGLTextureFormat( GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false, false)
- PF_A16B16G16R16, FOpenGLTextureFormat( GL_RGBA16F, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, false)
- PF_A1, FOpenGLTextureFormat( )
- PF_R16G16B16A16_UINT, FOpenGLTextureFormat( GL_RGBA16UI, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false, false)
- PF_R16G16B16A16_SINT, FOpenGLTextureFormat( GL_RGBA16I, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, false, false)
- PF_R32G32B32A32_UINT, FOpenGLTextureFormat( GL_RGBA32UI, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false, false)
- PF_R16G16B16A16_UNORM, FOpenGLTextureFormat( GL_RGBA16, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false, false)
- PF_R16G16B16A16_SNORM, FOpenGLTextureFormat( GL_RGBA16, GL_RGBA16, GL_RGBA, GL_SHORT, false, false)
- PF_R16G16_UINT, FOpenGLTextureFormat( GL_RG16UI, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, false, false)
- PF_R5G6B5_UNORM, FOpenGLTextureFormat( )
- PF_B8G8R8A8, FOpenGLTextureFormat(GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false, true)
- PF_R8G8B8A8, FOpenGLTextureFormat(GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false, false)
- PF_R8G8B8A8_UINT, FOpenGLTextureFormat(GL_RGBA8UI, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false, false)
- PF_G8, FOpenGLTextureFormat(GL_R8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, false, false)
- PF_A8, FOpenGLTextureFormat(GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, false, false)
- PF_R32_UINT, FOpenGLTextureFormat(GL_R32UI, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false, false)
- PF_R32_SINT, FOpenGLTextureFormat(GL_R32I, GL_R32I, GL_RED_INTEGER, GL_INT, false, false)
- PF_R16_UINT, FOpenGLTextureFormat(GL_R16UI, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false, false)
- PF_R16_SINT, FOpenGLTextureFormat(GL_R16I, GL_R16I, GL_RED_INTEGER, GL_SHORT, false, false)
- PF_R8_UINT, FOpenGLTextureFormat(GL_R8UI, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false, false)
- PF_R8G8, FOpenGLTextureFormat(GL_RG8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false, false)
- PF_FloatRGBA, FOpenGLTextureFormat(GL_RGBA16F, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_half_float、GL_EXT_color_buffer_float
- PF_FloatRGB, FOpenGLTextureFormat(GL_RGB16F, GL_RGB16F, GL_RGB, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_half_float、GL_EXT_color_buffer_float
- PF_G16, FOpenGLTextureFormat( GL_R16, GL_R16, GL_RED, GL_UNSIGNED_SHORT, false, false)GL_EXT_color_buffer_float
- PF_R32_FLOAT, FOpenGLTextureFormat( GL_R32F, GL_R32F, GL_RED, GL_FLOAT, false, false)GL_EXT_color_buffer_float
- PF_G16R16F, FOpenGLTextureFormat( GL_RG16F, GL_RG16F, GL_RG_EXT, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_float、GL_EXT_texture_rg
- PF_G16R16F_FILTER, FOpenGLTextureFormat( GL_RG16F, GL_RG16F, GL_RG_EXT, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_float、GL_EXT_texture_rg
- PF_G32R32F, FOpenGLTextureFormat( GL_RG32F, GL_RG32F, GL_RG_EXT, GL_FLOAT, false, false)GL_EXT_color_buffer_float、GL_EXT_texture_rg
- PF_A2B10G10R10, FOpenGLTextureFormat( GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false, false)GL_EXT_color_buffer_float、GL_EXT_texture_type_2_10_10_10_REV
- PF_R16F, FOpenGLTextureFormat( GL_R16F, GL_R16F, GL_RED, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_float
- PF_R16F_FILTER, FOpenGLTextureFormat( GL_R16F, GL_R16F, GL_RED, GL_HALF_FLOAT, false, false)GL_EXT_color_buffer_float
- PF_FloatR11G11B10, FOpenGLTextureFormat(GL_R11F_G11F_B10F, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false, false)(Patrick:OpenGL ES3.0的main spec中就支持这个format了,但是Engine\Source\Runtime\OpenGLDrv\Public\OpenGLES.h却返回false)
- PF_R8G8B8A8_SNORM, FOpenGLTextureFormat( GL_RGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false, false)
- PF_R32_UINT, FOpenGLTextureFormat( GL_R32UI, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false, false)
- PF_R32_SINT, FOpenGLTextureFormat( GL_R32I, GL_R32I, GL_RED_INTEGER, GL_INT, false, false)
- PF_R16_UINT, FOpenGLTextureFormat( GL_R16UI, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false, false)
- PF_R16_SINT, FOpenGLTextureFormat( GL_R16I, GL_R16I, GL_RED_INTEGER, GL_SHORT, false, false)
- PF_R32G32_UINT, FOpenGLTextureFormat( GL_RG32UI, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false, false)
- PF_DepthStencil, FOpenGLTextureFormat(GL_DEPTH_STENCIL_OES, GL_NONE, GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES, false, false)GL_OES_packed_depth_stencil
- PF_DXT1, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_DXT3, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_DXT5, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_ETC2_RGB, FOpenGLTextureFormat(GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2, GL_RGBA, GL_UNSIGNED_BYTE, true, false)(Patrick:Engine\Source\Runtime\OpenGLDrv\Public\OpenGL.h返回false,不知道,但是ES3.0肯定是支持ETC2,UE4也确实这么写的)
- PF_ETC2_RGBA, FOpenGLTextureFormat(GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_ETC2_R11_EAC, FOpenGLTextureFormat(GL_COMPRESSED_R11_EAC, GL_COMPRESSED_R11_EAC, GL_RED, GL_UNSIGNED_BYTE, true, false)
- PF_ETC2_RG11_EAC, FOpenGLTextureFormat(GL_COMPRESSED_RG11_EAC, GL_COMPRESSED_RG11_EAC, GL_RG, GL_UNSIGNED_BYTE, true, false)
- PF_ASTC_4x4, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false)GL_KHR_texture_compression_astc_ldr(Patrick:ES3.2的main spec也规定支持ASTC)
- PF_ASTC_6x6, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_ASTC_8x8, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_ASTC_10x10, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- PF_ASTC_12x12, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false)
- GRHINeedsUnatlasedCSMDepthsWorkaround = true;//Temporary fix for nvidia driver issue with non-power-of-two shadowmaps
回到Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp,GDynamicRHI(在这里就是FOpenGLDynamicRHI)被创建后,会调用GDynamicRHI的Init函数
FOpenGLProgramBinaryCache::Initialize
RegisterSharedShaderCodeDelegates
InitializeStateResources
- SharedContextState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
- RenderingContextState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
- PendingState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
RHICreateSamplerState
PointSamplerState = this->RHICreateSamplerState(PointSamplerStateParams);(Patrick:OpenGL ES3.0时代提出了sample object的概念,一个sample object可以对应多个texture,一个texture在不同时候也可以使用多个sample object。而在es2.0时代,每个texture都要有自己的sample属性)
- glGenSamplers(1, SamplerState->Resource);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); //Texture3D
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_LOD_BIAS, 0);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1);GL_EXT_texture_filter_anisotropic
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_COMPARE_MODE, GL_NONE);
- glSamplerParameteri(SamplerState->Resource, GL_TEXTURE_COMPARE_FUNC, GL_ALWAYS);
根据spec的描述,只有GL_DEPTH_COMPONENT 和GL_DEPTH_STENCIL才能使用GL_TEXTURE_COMPARE_MODE = GL_COMPARE_REF_TO_TEXTURE,之后,会根据GL_TEXTURE_COMPARE_FUNC设置的参数,将当前插值得到的结果,与当前绑定的texture采样得到的值相比较,并将结果存在r通道中。比如GL_ALWAYS:result=1.0
SupportsFastBufferData
先定义一些buffer,后面需要VBO和IBO的时候直接使用
- DynamicVertexBuffers.Init(CalcDynamicBufferSize(1));
- DynamicIndexBuffers.Init(CalcDynamicBufferSize(1));
FOpenGL::Flush
SetContext(RHIGetDefaultContext())
SetComputeContext(RHIGetDefaultAsyncComputeContext())
InitPreRHIResources
再次回到RHIInit,会发现主要内容已经结束了,下面就是判断一下,Editor必须运行在SM5环境下。到此RHIInit就结束了。
RenderUtilsInit
- GUseForwardShading -> GForwardShadingPlatformMask = ~0ull;
- DBufferVar -> GDBufferPlatformMask = ~0ull;
- BasePassVelocityCVar -> GBasePassVelocityPlatformMask = ~0ull;
- AnisotropicBRDFCVar -> GAnisotropicBRDFPlatformMask = ~0ull;
- SelectiveBasePassOutputsCVar -> GSelectiveBasePassOutputsPlatformMask = ~0ull;
- DistanceFieldsCVar -> GDistanceFieldsPlatformMask = ~0ull;
- RayTracingCVar -> GRayTracingPlaformMask = ~0ull;
ShaderCompile
- GShaderCompilerStats = new FShaderCompilerStats();
- GShaderCompilingManager = new FShaderCompilingManager();
- GDistanceFieldAsyncQueue = new FDistanceFieldAsyncQueue();
- InitializeShaderHashCache();
- InitializeShaderTypes
- CompileGlobalShaderMap
GetRendererModule
PostInitRHI
会调用Engine\Source\Runtime\RHI\Private\DynamicRHI.cpp的RHIPostInit函数,该函数与前面的RHIInit对应。
而RHIPostInit会调用GDynamicRHI->PostInit函数,由于前面的GDynamicRHI->Init对应,所以进入Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp查看
StartRenderingThread
Engine\Source\Runtime\RenderCore\Private\StartRenderingThread
SuspendTextureStreamingRenderTasks
FlushRenderingCommands
FRenderingThread
- GRenderingThreadRunnable = new FRenderingThread();
- GRenderingThread = FRunnableThread::Create(GRenderingThreadRunnable, *BuildRenderingThreadName(ThreadCount), 0, FPlatformAffinity::GetRenderingThreadPriority(), FPlatformAffinity::GetRenderingThreadMask(), FPlatformAffinity::GetRenderingThreadFlags());
- GRenderingThreadRunnable)->TaskGraphBoundSyncEvent->Wait();
- IConsoleManager::Get().RegisterThreadPropagation(GRenderingThread->GetThreadID(), &FConsoleRenderThreadPropagation::GetSingleton());
- FRenderCommandFence Fence;
- Fence.BeginFence();
- Fence.Wait();
- GRenderingThreadRunnableHeartbeat = new FRenderingThreadTickHeartbeat();
- GRenderingThreadHeartbeat = FRunnableThread::Create(GRenderingThreadRunnableHeartbeat, *FString::Printf(TEXT("RTHeartBeat %d"), ThreadCount), 16 * 1024, TPri_AboveNormal, FPlatformAffinity::GetRTHeartBeatMask());
ResumeTextureStreamingRenderTasks
FSlateRenderer
Slate为UE4的UI框架,相关联的主要有四个模块Slate,SlateCore,SlateRHIRenderer,UMG。Slate和SlateCore共同构筑了逻辑层部分,SlateRHIRenderer是渲染部分。UMG是在Slate和SlateCore上面的封装层,方便客户端调用,这也是为啥可以不费吹灰之力就能在unreal中用UserWidget开发一套UI的原因。在Unreal中UI就是铺在屏幕表面的面片,也是走顶点像素着色器,甚至还有后期,这和Unreal的SceneRendering的架构差不多。都是逻辑层负责组织资源,然后通知渲染层渲染。摘录自:虚幻4渲染编程(UI篇)【第一卷:Slate框架综述】
先进行初始化CurrentSlateApp.InitializeRenderer(SlateRendererSharedRef);
FSlateRHIRenderer::Initialize
Engine\Source\Runtime\SlateRHIRenderer\Private\SlateRHIRenderer.cpp
- LoadUsedTextures对于UI来说,shader一样,贴图一样,基本就可以合批了,所以我猜测这里先整理贴图。
- RenderingPolicy = MakeShareable(new FSlateRHIRenderingPolicy(SlateFontServices.ToSharedRef(), ResourceManager.ToSharedRef()));
- ElementBatcher = MakeUnique<.FSlateElementBatcher>(RenderingPolicy.ToSharedRef());
FEngineFontServices::Create
Engine\Source\Runtime\Engine\Private\UserInterface\EngineFontServices.cpp
Slate renderer准备好之后就开始准备字体Instance = new FEngineFontServices();
PostSplashScreen
IProjectManager::Get().LoadModulesForProject(ELoadingPhase::PostSplashScreen)
IPluginManager::Get().LoadModulesForEnabledPlugins(ELoadingPhase::PostSplashScreen)
对,UI也可以有后期。
PreInitPostStartupScreen
FShaderCodeLibrary::OpenLibrary(FApp::GetProjectName(), FPaths::ProjectContentDir());
FShaderPipelineCache::OpenPipelineFileCache(GMaxRHIShaderPlatform);
UMaterialInterface
UMaterialInterface::InitDefaultMaterials();
- Surface -> /Script/Engine.Engine.DefaultMaterialName
- Deferred Decal -> /Script/Engine.Engine.DefaultDeferredDecalMaterialName
- Light Function -> /Script/Engine.Engine.DefaultLightFunctionMaterialName
- Volume -> /Script/Engine.Engine.DefaultMaterialName @todo - get a real MD_Volume default material
- Post Process -> /Script/Engine.Engine.DefaultPostProcessMaterialName
- User Interface -> /Script/Engine.Engine.DefaultMaterialName
- Virtual Texture -> /Script/Engine.Engine.DefaultMaterialName
GEngine->Init
InitializeObjectReferences
FEngineLoop::Init
- GEngine = NewObject<.UEngine>(GetTransientPackage(), EngineClass);
- GEngine = GEditor = GUnrealEd = NewObject<.UUnrealEdEngine>(GetTransientPackage(), EngineClass);
- GEngine->Start();
- FViewport::SetGameRenderingEnabled(true, 3);
- FShaderPipelineCache::PauseBatching();
- FShaderPipelineCache::ResumeBatching();
FEngineLoop::Tick
TickRenderingTickables
- FTickableObjectRenderThread::RenderingThreadHighFrequencyTickableObjects->Tick
- FTickableObjectRenderThread::RenderingThreadTickableObjects->Tick
UpdateAllPrimitiveSceneInfos
GEngine->GetWorldContexts().World()->Scene->UpdateAllPrimitiveSceneInfos(RHICmdList)
这个看上去很重要,字面上看是更新当前场景所有的图元信息。FPrimitiveSceneInfo存在于渲染模块中,是基元组件状态,为渲染器模块私有,引擎看不到它,对应UPrimitiveComponent和 FPrimitiveSceneProxy。
UPrimitiveComponent是可渲染或进行物理交互的任意资源的基础类,也可以作为可视性剔除、投射阴影的粒度。与所有 UObjects 一样,游戏线程拥有所有变量和状态,渲染线程不应直接对其进行访问。
FPrimitiveSceneProxy是UPrimitiveComponent 的渲染器版本,为渲染线程映射 UPrimitiveComponent 状态。用于划分为子类以支持不同类型的基元(骨架、刚体、BSP 等),存在于引擎模块中,并在渲染通道中调用函数。实现某些非常重要的函数,如 GetViewRelevance(一帧开始的时候会调用 InitViews,进而调用该API返回 FPrimitiveViewRelevance)、DrawDynamicElements(在任意pass中调用dc渲染一个动态物件)、DrawStaticElements(当图元被attach到游戏线程的时候,将staticmesh进行submit到proxy上) 等。
总结一下,引擎模块:UPrimitiveComponent / FPrimitiveSceneProxy。渲染器模块:FPrimitiveSceneInfo
游戏线程:UPrimitiveComponent。渲染线程:FPrimitiveSceneProxy / FPrimitiveSceneInfo
- RemovedLocalPrimitiveSceneInfos(RemovedPrimitiveSceneInfos.Array()).Sort
- AddedLocalPrimitiveSceneInfos(AddedPrimitiveSceneInfos.Array()).Sort
- DeletedSceneInfos.Reserve(RemovedLocalPrimitiveSceneInfos.Num())
- AsyncCreateLightPrimitiveInteractionsTask = new FAsyncTask<.FAsyncCreateLightPrimitiveInteractionsTask>();
- AsyncCreateLightPrimitiveInteractionsTask->EnsureCompletion();
- AsyncCreateLightPrimitiveInteractionsTask->GetTask().Init(this);
- SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, nullptr);//这里具体实现是SceneNodes.Find(RemovedLocalPrimitiveSceneInfos)->SceneInfo = nullptr
- while (RemovedLocalPrimitiveSceneInfos.Num())处理RemovedLocalPrimitiveSceneInfos中所有元素
- StartIndex = RemovedLocalPrimitiveSceneInfos.Num() - 1;从TArray的最后一个元素开始
- TypeOffsetTable[BroadIndex].PrimitiveSceneProxyType == InsertProxyHash 找到TypeOffsetTable中的该元素,TypeOffsetTable中包含了所有的元素
- TArraySwapElements(Primitives, --TypeOffsetTable[TypeIndex].Offset, RemovedLocalPrimitiveSceneInfos[CheckIndex]->PackedIndex);将该元素在TArray Primitives、PrimitiveTransforms、PrimitiveSceneProxies、PrimitiveBounds、PrimitiveFlagsCompact、PrimitiveVisibilityIds、PrimitiveOcclusionFlags、PrimitiveComponentIds、PrimitiveVirtualTextureFlags、PrimitiveVirtualTextureLod、PrimitiveOcclusionBounds、PrimitivesNeedingStaticMeshUpdate中移动到最后
- AddPrimitiveToUpdateGPU(*this, SourceIndex);将SourceIndex和DestIndex加入Scene.GPUScene.PrimitivesToUpdate,也就是告诉场景这两个元素需要更新
- Primitives.Pop();将TArray Primitives、PrimitiveTransforms、PrimitiveSceneProxies、PrimitiveBounds、PrimitiveFlagsCompact、PrimitiveVisibilityIds、PrimitiveOcclusionFlags、PrimitiveComponentIds、PrimitiveVirtualTextureFlags、PrimitiveVirtualTextureLod、PrimitiveOcclusionBounds、PrimitivesNeedingStaticMeshUpdate进行pop,也就是删除
- PrimitiveSceneInfo->PackedIndex = INDEX_NONE;将该元素删除
- if (PrimitiveSceneInfo->Proxy->IsMovable()) VelocityData.RemoveFromScene(PrimitiveSceneInfo->PrimitiveComponentId);删除该元素关联的motion blur相关的信息
- PrimitiveSceneInfo->UnlinkAttachmentGroup();// Unlink the primitive from its shadow parent.
- PrimitiveSceneInfo->UnlinkLODParentComponent();// Unlink the LOD parent info if valid
- PrimitiveSceneInfo->FlushRuntimeVirtualTexture();// Flush virtual textures touched by primitive
- PrimitiveSceneInfo->RemoveFromScene(true);// Remove the primitive from the scene.
- AddPrimitiveToUpdateGPU(*this, PrimitiveIndex);// Update the primitive that was swapped to this index
- DistanceFieldSceneData.RemovePrimitive(PrimitiveSceneInfo);将该元素从DF中删除
- DeletedSceneInfos.Add(PrimitiveSceneInfo);将该元素加入DeletedSceneInfos
- RemovedLocalPrimitiveSceneInfos.RemoveAt(StartIndex, RemovedLocalPrimitiveSceneInfos.Num() - StartIndex);
- while (AddedLocalPrimitiveSceneInfos.Num())
- int StartIndex = AddedLocalPrimitiveSceneInfos.Num() - 1;从TArray的最后一个元素开始
- Primitives.Add(PrimitiveSceneInfo);将元素加入TArray Primitives、PrimitiveTransforms、PrimitiveSceneProxies、PrimitiveBounds、PrimitiveFlagsCompact、PrimitiveVisibilityIds、PrimitiveOcclusionFlags、PrimitiveComponentIds、PrimitiveVirtualTextureFlags、PrimitiveVirtualTextureLod、PrimitiveOcclusionBounds、PrimitivesNeedingStaticMeshUpdate
- PrimitiveSceneInfo->PackedIndex = PrimitiveSceneProxies.Num() - 1;
- AddPrimitiveToUpdateGPU(*this, SourceIndex);将SourceIndex加入Scene.GPUScene.PrimitivesToUpdate,也就是告诉场景这个元素需要更新
- TypeOffsetTable.Push(FTypeOffsetTableEntry(InsertProxyHash, TypeOffsetTable[BroadIndex - 1].Offset));如果元素不在TypeOffsetTable,则将其加入
- TArraySwapElements(Primitives, TypeOffsetTable[TypeIndex].Offset++, AddedLocalPrimitiveSceneInfos[AddIndex]->PackedIndex);将该元素在TArray Primitives、PrimitiveTransforms、PrimitiveSceneProxies、PrimitiveBounds、PrimitiveFlagsCompact、PrimitiveVisibilityIds、PrimitiveOcclusionFlags、PrimitiveComponentIds、PrimitiveVirtualTextureFlags、PrimitiveVirtualTextureLod、PrimitiveOcclusionBounds、PrimitivesNeedingStaticMeshUpdate中移动
- AddPrimitiveToUpdateGPU(*this, DestIndex);
- PrimitiveSceneInfo->LinkAttachmentGroup();// Add the primitive to its shadow parent's linked list of children.
- PrimitiveSceneInfo->LinkLODParentComponent();// Set lod Parent information if valid
- FPrimitiveSceneInfo::AddToScene(RHICmdList, this, TArrayView<.FPrimitiveSceneInfo*>(&AddedLocalPrimitiveSceneInfos[StartIndex], AddedLocalPrimitiveSceneInfos.Num() - StartIndex), true, !(CVarDoLazyStaticMeshUpdate.GetValueOnRenderThread()), bAsyncCreateLPIs);根据该元素是静态还是动态,加入场景
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- // Create an indirect lighting cache uniform buffer if we attaching a primitive that may require it, as it may be stored inside a cached mesh command.
- GetIndirectLightingCacheParameters(
Scene->GetFeatureLevel(),
Parameters,
nullptr,
nullptr,
FVector(0.0f, 0.0f, 0.0f),
0,
nullptr);
- SceneInfo->IndirectLightingCacheUniformBuffer = TUniformBufferRef<.FIndirectLightingCacheUniformParameters>::CreateUniformBufferImmediate(Parameters, UniformBuffer_MultiFrame, EUniformBufferValidation::None);
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- // If we are attaching a primitive that should be statically lit but has unbuilt lighting, // Allocate space in the indirect lighting cache so that it can be used for previewing indirect lighting
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- SceneInfo->NumLightmapDataEntries = SceneInfo->UpdateStaticLightingBuffer();
- SceneInfo->LightmapDataOffset = Scene->GPUScene.LightmapDataAllocator.Allocate(SceneInfo->NumLightmapDataEntries);
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- // Cache the nearest reflection proxy if needed
- SceneInfo->CacheReflectionCaptures();
- AddStaticMeshes(RHICmdList, Scene, SceneInfos, bAddToStaticDrawLists);
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- for (int32 MeshIndex = 0; MeshIndex < SceneInfo->StaticMeshes.Num(); MeshIndex++)
- FStaticMeshBatch& Mesh = SceneInfo->StaticMeshes[MeshIndex];
- FSparseArrayAllocationInfo SceneArrayAllocation = Scene->StaticMeshes.AddUninitialized();// Add the static mesh to the scene's static mesh list.
- Scene->StaticMeshes[SceneArrayAllocation.Index] = &Mesh;
- Mesh.Id = SceneArrayAllocation.Index;
- MeshRelevance.Id = SceneArrayAllocation.Index;
- Scene->StaticMeshBatchVisibility[Mesh.BatchVisibilityId] = true;
- CacheMeshDrawCommands(RHICmdList, Scene, SceneInfos);
PassMeshProcessor->AddMeshBatch(Mesh, BatchElementMask, SceneInfo->Proxy);
Process<.false>(MeshBatch, BatchElementMask, StaticMeshId, BlendMode, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode);
GetDepthPassShaders<.bPositionOnly, false>(
MaterialResource,
VertexFactory->GetType(),
FeatureLevel,
DepthPassShaders.HullShader,
DepthPassShaders.DomainShader,
DepthPassShaders.VertexShader,
DepthPassShaders.PixelShader,
ShaderPipeline
);
ParallelForTemplate(SceneInfos.Num(), [Scene, &SceneInfos](int32 Index)
{
SCOPED_NAMED_EVENT(FPrimitiveSceneInfo_AddStaticMeshes_DrawStaticElements, FColor::Magenta);
FPrimitiveSceneInfo* SceneInfo = SceneInfos[Index];
// Cache the primitive's static mesh elements.
FBatchingSPDI BatchingSPDI(SceneInfo);
BatchingSPDI.SetHitProxy(SceneInfo->DefaultDynamicHitProxy);
SceneInfo->Proxy->DrawStaticElements(&BatchingSPDI);
SceneInfo->StaticMeshes.Shrink();
SceneInfo->StaticMeshRelevances.Shrink();
check(SceneInfo->StaticMeshRelevances.Num() == SceneInfo->StaticMeshes.Num());
});
- for (int32 PassIndex = 0; PassIndex < EMeshPass::Num; PassIndex++)
- PassProcessorCreateFunction CreateFunction = FPassProcessorManager::GetCreateFunction(ShadingPath, PassType);
- FMeshPassProcessor* PassMeshProcessor = CreateFunction(Scene, nullptr, &CachedPassMeshDrawListContext);
- for (const FMeshInfoAndIndex& MeshAndInfo : MeshBatches)
- PassMeshProcessor->AddMeshBatch(Mesh, BatchElementMask, SceneInfo->Proxy);//将mesh加入PassMeshProcessor
Process(MeshBatch, BatchElementMask, StaticMeshId, PrimitiveSceneProxy, MaterialRenderProxy, Material, BlendMode, ShadingModels, LightmapPolicyType, MeshBatch.LCI);
TMeshProcessorShaders<
TMobileBasePassVSPolicyParamType<.FUniformLightMapPolicy>,
FBaseHS,
FBaseDS,
TMobileBasePassPSPolicyParamType<.FUniformLightMapPolicy>> BasePassShaders;
- MobileBasePass::GetShaders(
LightMapPolicyType,
NumMovablePointLights,
MaterialResource,
MeshBatch.VertexFactory->GetType(),
bEnableSkyLight,
BasePassShaders.VertexShader,
BasePassShaders.PixelShader);
- MobileBasePass::SetTranslucentRenderState(DrawRenderState, MaterialResource);
- DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<.false, CF_Equal>::GetRHI());
- StencilValue|= GET_STENCIL_BIT_MASK(RECEIVE_DECAL, ReceiveDecals);
- StencilValue|= GET_STENCIL_MOBILE_SM_MASK(ShadingModel);
- DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<
true, CF_DepthNearOrEqual,
true, CF_Always, SO_Keep, SO_Keep, SO_Replace,
false, CF_Always, SO_Keep, SO_Keep, SO_Keep,
// don't use masking as it has significant performance hit on Mali GPUs (T860MP2)
0x00, 0xff >::GetRHI());
- DrawRenderState.SetStencilRef(StencilValue); (Patrick:在这里设置shadingmodel相关的stencil)
- DrawRenderState.SetBlendState(TStaticBlendState<.CW_RGB, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
CW_RGBA, BO_Add, BF_One, BF_Zero, BO_Add, BF_One, BF_Zero,
true>::GetRHI());
- MobileBasePass::SetOpaqueRenderState(DrawRenderState, PrimitiveSceneProxy, MaterialResource, bEnableReceiveDecalOutput && IsMobileHDR(), bUsesDeferredShading);
- ShaderElementData.InitializeMeshMaterialData(ViewIfDynamicMeshCommand, PrimitiveSceneProxy, MeshBatch, StaticMeshId, false);
- BuildMeshDrawCommands(
MeshBatch,
BatchElementMask,
PrimitiveSceneProxy,
MaterialRenderProxy,
MaterialResource,
DrawRenderState,
BasePassShaders,
MeshFillMode,
MeshCullMode,
SortKey,
EMeshPassFeatures::Default,
ShaderElementData);
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- FPrimitiveSceneInfoCompact CompactPrimitiveSceneInfo(SceneInfo);// create potential storage for our compact info
- Scene->PrimitiveOctree.AddElement(CompactPrimitiveSceneInfo);// Add the primitive to the octree.
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- Scene->DynamicIndirectCasterPrimitives.Add(SceneInfo);
- Scene->PrimitiveSceneProxies[PackedIndex] = Proxy;
- Scene->PrimitiveTransforms[PackedIndex] = Proxy->GetLocalToWorld();
- Scene->PrimitiveFlagsCompact[PackedIndex] = FPrimitiveFlagsCompact(Proxy);
- Scene->PrimitiveOcclusionFlags[PackedIndex] = OcclusionFlags;
- Scene->PrimitiveOcclusionBounds[PackedIndex] = OcclusionBounds;
- Scene->PrimitiveComponentIds[PackedIndex] = SceneInfo->PrimitiveComponentId;
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- Scene->PrimitiveVirtualTextureFlags[SceneInfo->PackedIndex] = SceneInfo->RuntimeVirtualTextureFlags;// Store the runtime virtual texture flags.
- for (FPrimitiveSceneInfo* SceneInfo : SceneInfos)
- Scene->CreateLightPrimitiveInteractionsForPrimitive(SceneInfo, bAsyncCreateLPIs);
- PrimitiveSceneInfo->BeginDeferredUpdateStaticMeshes();静态物件的话需要执行这个
- VelocityData.UpdateTransform(PrimitiveSceneInfo, PrimitiveTransforms[PrimitiveIndex], PrimitiveTransforms[PrimitiveIndex]);ES3.1的话不支持这个
- AddPrimitiveToUpdateGPU(*this, PrimitiveIndex);
- DistanceFieldSceneData.AddPrimitive(PrimitiveSceneInfo);
- PrimitiveSceneInfo->FlushRuntimeVirtualTexture();// Flush virtual textures touched by primitive
- SceneLODHierarchy.UpdateNodeSceneInfo(PrimitiveSceneInfo->PrimitiveComponentId, PrimitiveSceneInfo);
- AddedLocalPrimitiveSceneInfos.RemoveAt(StartIndex, AddedLocalPrimitiveSceneInfos.Num() - StartIndex);
- const bool bUpdateStaticDrawLists = !UpdatedTransforms.Key->StaticElementsAlwaysUseProxyPrimitiveUniformBuffer();
- UpdatedSceneInfosWithStaticDrawListUpdate.Push(PrimitiveSceneInfo);
- UpdatedSceneInfosWithoutStaticDrawListUpdate.Push(PrimitiveSceneInfo);
- PrimitiveSceneInfo->FlushRuntimeVirtualTexture();
- PrimitiveSceneInfo->RemoveFromScene(bUpdateStaticDrawLists);// Remove the primitive from the scene at its old location// (note that the octree update relies on the bounds not being modified yet).
- VelocityData.UpdateTransform(PrimitiveSceneInfo, LocalToWorld, PrimitiveSceneProxy->GetLocalToWorld());
- PrimitiveSceneProxy->SetTransform(LocalToWorld, WorldBounds, LocalBounds, AttachmentRootPosition);// Update the primitive transform.
- PrimitiveSceneInfo->MarkIndirectLightingCacheBufferDirty();
- AddPrimitiveToUpdateGPU(*this, PrimitiveSceneInfo->PackedIndex);
- DistanceFieldSceneData.UpdatePrimitive(PrimitiveSceneInfo);
- FPrimitiveSceneInfo::AddToScene(RHICmdList, this, UpdatedSceneInfosWithStaticDrawListUpdate, true, true, bAsyncCreateLPIs);
- FPrimitiveSceneInfo::AddToScene(RHICmdList, this, UpdatedSceneInfosWithoutStaticDrawListUpdate, false, true, bAsyncCreateLPIs);
- PrimitiveSceneInfo->FlushRuntimeVirtualTexture();
- AsyncCreateLightPrimitiveInteractionsTask->StartBackgroundTask();
- VelocityData.OverridePreviousTransform(PrimitiveSceneInfo->PrimitiveComponentId, Transform.Value);
- for (const auto& Attachments : UpdatedAttachmentRoots)
- PrimitiveSceneInfo->UnlinkAttachmentGroup();
- PrimitiveSceneInfo->LightingAttachmentRoot = Attachments.Value;
- PrimitiveSceneInfo->LinkAttachmentGroup();
- for (const auto& CustomParams : UpdatedCustomPrimitiveParams)
- PrimitiveSceneProxy->CustomPrimitiveData = CustomParams.Value;
- PrimitiveSceneProxy->GetPrimitiveSceneInfo()->SetNeedsUniformBufferUpdate(true);
- DistanceFieldSceneData.UpdatePrimitive(DistanceFieldSceneDataUpdates);
- BeginCleanup(new DeferDeleteHitProxies(MoveTemp(PrimitiveSceneInfo->HitProxies)));
- UpdatedAttachmentRoots.Reset();
- UpdatedTransforms.Reset();
- UpdatedCustomPrimitiveParams.Reset();
- OverridenPreviousTransforms.Reset();
- DistanceFieldSceneDataUpdates.Reset();
- RemovedPrimitiveSceneInfos.Reset();
- AddedPrimitiveSceneInfos.Reset();
BeginFrameRenderThread
beginning of RHI frame
- GRHICommandList.LatchBypass();这里只判断是否是有额外的线程,关于渲染线程的解读,有一些文章说的挺好:渲染线程使用、观察RHICommand
- RHICmdList.BeginFrame();
GetContext().RHIBeginFrame();这里会根据不同平台走不同分支,我们关注Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp FOpenGLDynamicRHI::RHIBeginFrame
- RHIPrivateBeginFrame/** Called once per frame only from within an RHI. */
- GPUProfilingData.BeginFrame
- GetContextStateForCurrentContext().LastES2ColorRTResource = 0xFFFFFFFF;
- PendingState.DepthStencil = 0 ;
- FCoreDelegates::OnBeginFrameRT.Broadcast();
Scene->StartFrame()
GEngine->GetWorldContexts().World()->Scene->StartFrame();
EmitDynamicResolutionEvent
GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::BeginFrame)// When not in editor, we emit dynamic resolution's begin frame right after RHI's.
ResetDeferredUpdates
FDeferredUpdateResource::ResetNeedsUpdate();
FlushPendingDeleteRHIResources_RenderThread();
GEngine->Tick
main game engine tick (world, game objects, etc.)
GEngine->Tick(FApp::GetDeltaTime(), bIdleMode);在这里,会根据是否是Editor,走下面两个分支