- 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);这里最重要,生成MeshBatcher,PassMeshProcessor的生成在下面li中(FMobileBasePassMeshProcessor::AddMeshBatch、FBasePassMeshProcessor::AddMeshBatch)
Process<.false>(MeshBatch, BatchElementMask, StaticMeshId, BlendMode, PrimitiveSceneProxy, *EffectiveMaterialRenderProxy, *EffectiveMaterial, MeshFillMode, MeshCullMode);生成shader,设置GL状态机,生成MeshDrawCommands(FMobileBasePassMeshProcessor::Process、FBasePassMeshProcessor::Process)
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
- 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,走下面两个分支
- UGameEngine::Tick
- CleanupGameViewport();// Clean up the game viewports that have been closed.
- GameViewport->SetDropDetail(DeltaSeconds);// Decide whether to drop high detail because of frame rate.这个厉害了,可以根据帧率设置LOD,如果帧率低了,可以丢掉一些细节和使用更低的LOD
- GetWorld()->bDropDetail = FrameRate < FMath::Clamp(GEngine->MinDesiredFrameRate, 1.f, 100.f) && !(FApp::IsBenchmarking() || FApp::UseFixedTimeStep() || GEngine->bUseFixedFrameRate);
- GetWorld()->bAggressiveLOD = FrameRate < FMath::Clamp(GEngine->MinDesiredFrameRate - 5.f, 1.f, 100.f) && !(FApp::IsBenchmarking() || FApp::UseFixedTimeStep() || GEngine->bUseFixedFrameRate);
- StaticTick(DeltaSeconds, !!GAsyncLoadingUseFullTimeLimit, GAsyncLoadingTimeLimit / 1000.f);// Update subsystems.// This assumes that UObject::StaticTick only calls ProcessAsyncLoading.// Spend a bit of time (pre)loading packages - currently 5 ms.
- FEngineAnalytics::Tick(DeltaSeconds);
- FStudioAnalytics::Tick(DeltaSeconds);空的
- ChaosModule->DispatchGlobalCommands();
- TickWorldTravel(WorldList[WorldIdx], DeltaSeconds);// Tick all travel and Pending NetGames (Seamless, server, client)
- if (!bIdleMode) WorldList[WorldIdx].World()->Tick( LEVELTICK_All, DeltaSeconds );/*Update the level after a variable amount of time, DeltaSeconds, has passed.All child actors are ticked after their owners have been ticked.*/
- if (Context.World()->AreAlwaysLoadedLevelsLoaded()) // Only update reflection captures in game once all 'always loaded' levels have been loaded// This won't work with actual level streaming though
- USkyLightComponent::UpdateSkyCaptureContents(Context.World());// Update sky light first because it's considered direct lighting, sky diffuse will be visible in reflection capture indirect specular
- UReflectionCaptureComponent::UpdateReflectionCaptureContents(Context.World());
- Context.OwningGameInstance->GetFirstGamePlayer()->Exec( GamePlayer->GetWorld(), *(FString("CAUSEEVENT ") + Context.LastURL.GetOption(TEXT("causeevent="),NULL)), *GLog );// Issue cause event after first tick to provide a chance for the game to spawn the player and such.
- UpdateTransitionType(Context.World());// Update the transition screen.
- BlockTillLevelStreamingCompleted(Context.World());// Block on async loading if requested.
- if( GIsServer == true ) Context.World()->UpdateLevelStreaming();
- ConditionalCommitMapChange(Context);
- FTickableGameObject::TickObjects(nullptr, LEVELTICK_All, false, DeltaSeconds);
- MediaModule->TickPostEngine();
- GameViewport->Tick(DeltaSeconds);
- RedrawViewports// Render everything.这里就是我们渲染关注的重点
- GameViewport->LayoutPlayers();
- GameViewport->Viewport->Draw(bShouldPresent);
到这里,就和另外一个分支UUnrealEdEngine::Tick汇合了。大家都是调用Engine\Source\Runtime\Engine\Private\UnrealClient.cpp的FViewport::Draw函数
- EnqueueBeginRenderFrame(bShouldPresent);
- AdvanceFrameRenderPrerequisite();//Gather the frame render prerequisites and make sure all render commands are at least queued
- Viewport->BeginRenderFrame(RHICmdList);/** Starts a new rendering frame. Called from the rendering thread. */实际并没有走到,所以先跳过
- UWorld* ViewportWorld = ViewportClient->GetWorld();
- FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->FeatureLevel.GetValue() : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
- Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
- Canvas.SetAllowSwitchVerticalAxis(true);// Make sure the Canvas is not rendered upside down
- ViewportClient->Draw(this, &Canvas);
到这里又出现了分支,对应上面两个分支。分别是Engine\Source\Runtime\Engine\Private\GameViewportClient.cpp中的UGameViewportClient::Draw和Engine\Source\Editor\UnrealEd\Private\EditorViewportClient.cpp中的FEditorViewportClient::Draw。我们这里看前者
- GEngine->ViewExtensions->GatherActiveExtensions(InViewport)->SetupViewFamily(ViewFamily);看上去基本都是一些AR之类的扩展
- ViewFamily.ViewMode = VMI_Lit
- FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, nullptr, eSSP_FULL);
- CalcSceneViewInitOptions(ViewInitOptions, Viewport, ViewDrawer, eSSP_FULL)
- GetProjectionData(Viewport, StereoPass, /*inout*/ ViewInitOptions)
- ViewInitOptions.IsValidViewRectangle()
- ViewInitOptions.OverlayColor = PlayerController->PlayerCameraManager->FadeColor;
- ViewInitOptions.OverlayColor.A = FMath::Clamp(PlayerController->PlayerCameraManager->FadeAmount, 0.0f, 1.0f);
- ViewInitOptions.ColorScale = FLinearColor( PlayerController->PlayerCameraManager->ColorScale.X, PlayerController->PlayerCameraManager->ColorScale.Y, PlayerController->PlayerCameraManager->ColorScale.Z );
- ViewInitOptions.bInCameraCut = PlayerController->PlayerCameraManager->bGameCameraCutThisFrame;
- ViewInitOptions.SceneViewStateInterface = ViewStates[ViewIndex].GetReference();
- ViewInitOptions.ViewActor = PlayerController->GetViewTarget();
- ViewInitOptions.PlayerIndex = GetControllerId();
- ViewInitOptions.ViewElementDrawer = ViewDrawer;
- ViewInitOptions.BackgroundColor = FLinearColor::Black;
- ViewInitOptions.LODDistanceFactor = PlayerController->LocalPlayerCachedLODDistanceFactor;
- ViewInitOptions.StereoPass = StereoPass;
- ViewInitOptions.WorldToMetersScale = PlayerController->GetWorldSettings()->WorldToMeters;
- ViewInitOptions.CursorPos = Viewport->HasMouseCapture() ? FIntPoint(-1, -1) : FIntPoint(Viewport->GetMouseX(), Viewport->GetMouseY());
- ViewInitOptions.OriginOffsetThisFrame = PlayerController->GetWorld()->OriginOffsetThisFrame;
- GetViewPoint(ViewInfo, StereoPass);// Get the viewpoint...technically doing this twice// but it makes GetProjectionData better
- ViewInitOptions.bUseFieldOfViewForLOD = ViewInfo.bUseFieldOfViewForLOD;
- ViewInitOptions.FOV = ViewInfo.FOV;
- ViewInitOptions.DesiredFOV = ViewInfo.DesiredFOV;
- ViewInitOptions.ViewFamily = ViewFamily;
- PlayerController->BuildHiddenComponentList(OutViewLocation, /*out*/ ViewInitOptions.HiddenPrimitives);
- ViewFamily->Views.Add(View);
- View->StartFinalPostprocessSettings(OutViewLocation);设置后效参数,先使用FPostProcessSettings进行初始化参数,后通过postprocessvolume和传入的OutViewLocation设置,具体实现类似Unity PPSv2,具体实现是在Engine\Source\Runtime\Engine\Private\World.cpp的UWorld::AddPostProcessingSettings函数中
- View->OverridePostProcessSettings( (*CameraAnimPPSettings)[PPIdx], (*CameraAnimPPBlendWeights)[PPIdx]);将刚才设置的PPSvolume的最终结果设置到view上。具体实现是在Engine\Source\Runtime\Engine\Private\SceneView.cpp的FSceneView::OverridePostProcessSettings函数中
- PlayerController->PlayerCameraManager->UpdatePhotographyPostProcessing(View->FinalPostProcessSettings);
- View->EndFinalPostprocessSettings(ViewInitOptions);设置后效参数,具体实现在Engine\Source\Runtime\Engine\Private\SceneView.cpp的FSceneView::EndFinalPostprocessSettings函数中。// will be deprecated soon, use the new asset LightPropagationVolumeBlendable instead
- ViewFamily->ViewExtensions[ViewExt]->SetupView(*ViewFamily, *View);
- Views.Add(View);
- View->CurrentBufferVisualizationMode = CurrentBufferVisualizationMode;//None
- View->CameraConstrainedViewRect = View->UnscaledViewRect;
- View->SetupRayTracedRendering();
- FinalizeViews(&ViewFamily, PlayerViewMap);
- MyWorld->UpdateLevelStreaming();
- ViewFamily.SecondaryViewFraction = GetDPIDerivedResolutionFraction();//1.0
- ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(ViewFamily, GlobalResolutionFraction, AllowPostProcessSettingsScreenPercentage));//1.0
- ViewFamily.bIsHDR = GetWindow().IsValid() ? GetWindow().Get()->GetIsHDR() : false;//false
- GetRendererModule().BeginRenderingViewFamily(SceneCanvas,&ViewFamily);
上面提到的分支,在这里又走到了一起,核心函数还是调用Engine\Source\Runtime\Renderer\Private\SceneRendering.cpp的FRendererModule::BeginRenderingViewFamily函数
- ViewFamily->Scene->GetRenderScene()->GetWorld()->SendAllEndOfFrameUpdates();//guarantee that all render proxies are up to date before kicking off a BeginRenderViewFamily.
- ViewFamily->ViewExtensions[ViewExt]->BeginRenderViewFamily(*ViewFamily);
- World->SetMapNeedsLightingFullyRebuilt(Scene->NumUncachedStaticLightingInteractions, Scene->NumUnbuiltReflectionCaptures);发布版本中这里是空的// Set the world's "needs full lighting rebuild" flag if the scene has any uncached static lighting interactions.// Note: reading NumUncachedStaticLightingInteractions on the game thread here which is written to by the rendering thread// This is reliable because the RT uses interlocked mechanisms to update it//Sets bMapNeedsLightingFullyRebuild to the specified value. Marks the worldsettings package dirty if the value changed.
- FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, Canvas->GetHitProxyConsumer());这里就是最重要的,选择使用什么渲染管线了。
EShadingPath ShadingPath = InViewFamily->Scene->GetShadingPath();这里提供了两条默认渲染管线
- SceneRenderer = new FDeferredShadingSceneRenderer(InViewFamily, HitProxyConsumer);
Engine\Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp
根据选择开ZPrepass(DDM_None、DDM_NonMaskedOnly、DDM_AllOccluders、DDM_AllOpaque、DDM_MaskedOnly)。设置ZPrepass是否Movable。关闭bClusteredShadingLightsInLightGrid。默认情况下bDitheredLODTransitionsUseStencil为false
- SceneRenderer = new FMobileSceneRenderer(InViewFamily, HitProxyConsumer);
Engine\Source\Runtime\Renderer\Private\MobileShadingRenderer.cpp
bModulatedShadowsInUse = false, bShouldRenderCustomDepth = false;
- 这里两个渲染管线都继承自FSceneRenderer,所以需要看一下FSceneRenderer的构造函数
构造函数中定义了:Scene、ViewFamily、MeshCollector(ES3_1)、RayTracingCollector(ES3_1)、bUsedPrecomputedVisibility(false)、InstancedStereoWidth(0)、RootMark(null)、FamilySize(0,0)
ViewInfo->Drawer->Draw(ViewInfo,&ViewElementPDI);
ViewFamily.SetScreenPercentageInterface(InViewFamily->ScreenPercentageInterface->Fork_GameThread(ViewFamily));
ViewInfo.CustomVisibilityQuery = GCustomCullingImpl->CreateQuery(ViewInfo);// launch custom visibility queries for views
- USceneCaptureComponent::UpdateDeferredCaptures(Scene);这里看上去是更新类似reflectionprobe对应的东西
SceneCapturesToUpdateMap->UpdateSceneCaptureContents(Scene);// Only update the scene captures associated with the current scene.// Updating others not associated with the scene would cause invalid data to be rendered into the target
- ViewExtensionPreRender_RenderThread(RHICmdList, SceneRenderer);// We need to execute the pre-render view extensions before we do any view dependent work.// Anything between here and FDrawSceneCommand will add to HMD view latency
- SceneRenderer->Scene->UpdatePlanarReflectionContents(SceneRenderer->Scene->PlanarReflections_GameThread[ReflectionIndex], *SceneRenderer);这里实现了planerreflection
- FVector2D DesiredPlanarReflectionTextureSizeFloat = FVector2D(FSceneRenderer::GetDesiredInternalBufferSize(MainSceneRenderer.ViewFamily).X, FSceneRenderer::GetDesiredInternalBufferSize(MainSceneRenderer.ViewFamily).Y) * FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);获取绘制planerreflection所需RT的尺寸
- RenderTarget->ReleaseResource();如果已有RT尺寸不符,则将已有RT删除
- ReleaseRHI();
- ReleaseDynamicRHI();
- GetResourceList()[ListIndex] = nullptr;
- GetFreeIndicesList();.Add(ListIndex);
- ListIndex = INDEX_NONE;
- CaptureComponent->RenderTarget = new FPlanarReflectionRenderTarget(DesiredPlanarReflectionTextureSize);
FPlanarReflectionRenderTarget继承自FTexture和FRenderTarget,FTexture继承自FRenderResource。
构造函数中,初始化了:Size(DesiredPlanarReflectionTextureSize), TextureRHI(NULL),SamplerStateRHI(NULL),DeferredPassSamplerStateRHI(NULL),LastRenderTime(-FLT_MAX),bGreyScaleFormat(false),bIgnoreGammaConversions(false),bSRGB(false),ListIndex(INDEX_NONE),FeatureLevel(ERHIFeatureLevel::Num)
- RenderTarget->InitResource();
- LocalListIndex = ResourceList.Add(this);将刚创建的RT加入TArray。
- InitDynamicRHI
具体实现见Engine\Source\Runtime\Engine\Public\PlanarReflectionSceneProxy.h的FPlanarReflectionRenderTarget::InitDynamicRHI函数
- SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer);创建sample object
这里会根据平台使用不同的分支,我们选择Engine\Source\Runtime\OpenGLDrv\Private\OpenGLState.cpp中的FOpenGLDynamicRHI::RHICreateSamplerState函数,这个在前面RHICreateSamplerState已经说过了,总之就是创建一个具备某些属性的sample object,这里就不重复了。
- RHICreateTargetableShaderResource2D( GetSizeX(),GetSizeY(),PF_FloatRGBA,1/*NumMips*/,0/*Flags*/,TexCreate_RenderTargetable,false/*bForceSeparateTargetAndShaderResource*/,{ FClearValueBinding(FLinearColor::Black) },RenderTargetTextureRHI,Texture2DRHI,1/*NumSamples*/,false/*bForceSharedTargetAndShaderResource*/);这里就是会根据不同的平台真正的创建texture,比如OpenGL的实现在Engine\Source\Runtime\OpenGLDrv\Private\OpenGLTexture.cpp的FOpenGLDynamicRHI::InitializeGLTexture函数中直接调用OpenGL ES API创建texture。
OutTargetableTexture = OutShaderResourceTexture = RHICreateTexture2D(SizeX, SizeY, PF_FloatRGBA/*Format*/, 1/*NumMips*/, 1/*NumSamples*/, 0/*Flags*/ | TargetableTextureFlags | TexCreate_ShaderResource, CreateInfo);// Create a single texture that has both TargetableTextureFlags and TexCreate_ShaderResource set.
- InitRHI这个函数貌似为空
- FPlatformMisc::MemoryBarrier(); // there are some multithreaded reads of ListIndex
- TArray<.FSceneCaptureViewInfo> SceneCaptureViewInfo 计算PlanerReflection的VP矩阵
- FPlane MirrorPlane = FPlane(CaptureComponent->GetComponentTransform().ToMatrixWithScale().TransformPosition(FVector::ZeroVector), CaptureComponent->GetComponentTransform().ToMatrixWithScale().TransformVector(FVector(0, 0, 1)));先将Plane的矩阵镜像
- NewView.ViewLocation = (MirrorPlane * MainSceneRenderer.Views.ViewMatrices.GetViewMatrix()).InverseTransformPosition(FVector::ZeroVector)根据Plane将view镜像
- ViewRotationMatrix = (MirrorPlane * MainSceneRenderer.Views.ViewMatrices.GetViewMatrix()).RemoveTranslation();根据Plane将view镜像
- BuildProjectionMatrix(MainSceneRenderer.Views.UnscaledViewRect.Size(), ECameraProjectionMode::Perspective, FMath::Atan(1.0f / MainSceneRenderer.Views.ViewMatrices.GetProjectionMatrix().M[0][0]) + FMath::DegreesToRadians(CaptureComponent->ExtraFOV), 1.0f, GNearClippingPlane, ProjectionMatrix);获取根据新的FOV算出来的projection矩阵
- NewView.ProjectionMatrix = ProjectionMatrix;
- SceneCaptureViewInfo.Add(NewView)将新的VP矩阵加入TArray
- FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(CaptureComponent->RenderTarget,this,CaptureComponent->ShowFlags).SetResolveScene(false).SetRealtimeUpdate(true));创建一个viewfamily,指定RT。
SetResolveScene/** Setting to if true then results of scene rendering are copied/resolved to the RenderTarget. */
SetRealtimeUpdate/** Set whether the view family is updated in real-time. */
- ViewFamily.SecondaryViewFraction = MainSceneRenderer.ViewFamily.SecondaryViewFraction;// Uses the exact same secondary view fraction on the planar reflection as the main viewport.
- SetupViewFamilyForSceneCapture( ViewFamily,CaptureComponent,SceneCaptureViewInfo,CaptureComponent->MaxViewDistanceOverride,/* bCaptureSceneColor = */ true, /* bIsPlanarReflection = */ true,&PostProcessSettings,/*PostProcessBlendWeight*/1.0f,/*ViewActor =*/ nullptr);
- ViewFamily.EngineShowFlags.ScreenPercentage = MainSceneRenderer.ViewFamily.EngineShowFlags.ScreenPercentage;// Fork main renderer's screen percentage interface to have exactly same settings.
- ViewFamily.SetScreenPercentageInterface(FSceneRenderer::ForkScreenPercentageInterface(MainSceneRenderer.ViewFamily.GetScreenPercentageInterface(), ViewFamily));
- FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(&ViewFamily, nullptr);创建绘制PlaneReflection所需要的渲染管线,这个API前面也提到了CreateSceneRenderer
- SceneRenderer->ViewFamily.EngineShowFlags.ScreenPercentage = MainSceneRenderer.ViewFamily.EngineShowFlags.ScreenPercentage;// Disable screen percentage on planar reflection renderer if main one has screen percentage disabled.
- SceneRenderer->Views[ViewIndex].GlobalClippingPlane = MirrorPlane;/** Global clipping plane being applied to the scene, or all 0's if disabled. This is used when rendering the planar reflection pass. */(Patrick:这个有意思了,除了nearplane和farplane,planerreflection还有另外一个plane,这个相当于culling的时候多了一个遮挡板么?)
- SceneRenderer->Views[ViewIndex].bAllowTemporalJitter = false;// Jitter can't be removed completely due to the clipping plane// Also, this prevents the prefilter pass, which reads from jittered depth, from having to do special handling of it's depth-dependent input
- SceneRenderer->Views[ViewIndex].bRenderSceneTwoSided = CaptureComponent->bRenderSceneTwoSided;/** Whether to force two sided rendering for this view. */
- CaptureComponent->ProjectionWithExtraFOV[ViewIndex] = SceneCaptureViewInfo[ViewIndex].ProjectionMatrix;
- SceneRenderer->Views[ViewIndex].FinalPostProcessSettings.ScreenPercentage = MainSceneRenderer.Views[ViewIndex].FinalPostProcessSettings.ScreenPercentage;// Plumb down the main view's screen percentage to the planar reflection.
- CaptureComponent->SceneProxy->ProjectionWithExtraFOV[ViewIndex] = SceneCaptureViewInfo[ViewIndex].ProjectionMatrix;
- UpdatePlanarReflectionContents_RenderThread(RHICmdList, MainSceneRenderer, SceneRenderer, CaptureComponent->SceneProxy, CaptureComponent->RenderTarget, CaptureComponent->RenderTarget, MirrorPlane, CaptureComponent->GetOwner() ? CaptureComponent->GetOwner()->GetFName() : NAME_None, FResolveParams(), true);
- if (MirrorPlane.PlaneDot(MainSceneRenderer->Views.ViewMatrices.GetViewOrigin()) > 0) && (MainSceneRenderer->Views.ViewFrustum.IntersectBox(SceneProxy->WorldBounds.GetCenter(), SceneProxy->WorldBounds.GetExtent()))判断用于镜面反射的镜面是否在视野中
- FDeferredUpdateResource::UpdateResources(RHICmdList);这个API前面也提到了FDeferredUpdateResource::UpdateResources// update any resources that needed a deferred update
- SceneRenderer->Views.UpdatePlanarReflectionViewMatrix(MainSceneRenderer->Views, MirrorMatrix(MirrorPlane));// Reflection view late update
- SceneRenderer->Render(RHICmdList);这就是真正的执行管线渲染了。
前面也提到了,UE4默认支持两种渲染管线Engine\Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp和Engine\Source\Runtime\Renderer\Private\MobileShadingRenderer.cpp
- FMobileSceneRenderer::Render
- Scene->UpdateAllPrimitiveSceneInfos这个API前面也提到了UpdateAllPrimitiveSceneInfos
- PrepareViewRectsForRendering
- if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags))大气散射需要compute shader的支持(Switch不支持CS)// Requires SM5 or ES3_1 (GL/Vulkan) for compute shaders and volume textures support.
- PrepareSunLightProxy(*Scene->GetSkyAtmosphereSceneInfo(), NUM_ATMOSPHERE_LIGHTS, *Scene->AtmosphereLights[LightIndex]);这里的算法是来自寒霜引擎// See explanation in https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf page 26
*Scene->AtmosphereLights[LightIndex].Proxy->SetAtmosphereRelatedProperties( CVarSkyAtmosphereTransmittanceLUTLightPerPixelTransmittance.GetValueOnAnyThread() > 0 ? FLinearColor::White : SkyAtmosphereProxy.GetAtmosphereSetup().GetTransmittanceAtGroundLevel(AtmosphereLightDirection) / SkyAtmosphereProxy.GetTransmittanceAtZenith(), GetLightDiskLuminance(*Scene->AtmosphereLights[LightIndex], *Scene->AtmosphereLights[LightIndex].Proxy->GetColor() / SkyAtmosphereProxy.GetTransmittanceAtZenith()));
假如前面的if不支持SkyAtmosphere,则 AtmosphereLights[LightIndex]->Proxy->SetAtmosphereRelatedProperties(FLinearColor::White, GetLightDiskLuminance(*AtmosphereLights[LightIndex], AtmosphereLights->Proxy->GetColor()));
- WaitOcclusionTests
- const bool bGammaSpace = !IsMobileHDR();在UE4 mobile中,如果不是HDR,则走gamma空间。
- const bool bRenderToSceneColor = bStereoRenderingAndHMD || bRequiresUpscale || FSceneRenderer::ShouldCompositeEditorPrimitives(View) || View.bIsSceneCapture || View.bIsReflectionCapture;根据需要绘制到FBO上。// ES2 requires that the back buffer and depth match dimensions.// For the most part this is not the case when using scene captures. Thus scene captures always render to scene color target.
- const bool bRequiresTranslucencyPass = RequiresTranslucencyPass(RHICmdList, View);为了避免swap RT,尽量将更多的事情放在一个pass中,然后绘制某些半透明的时候需要depth,比如软粒子、延迟贴花。所以在这里会判断是否支持FB,在我们的SRP中知道基本上除了部分特别低端的手机Adreno 510以下,对FB的支持不够完善,其余都还好。// Whether we need to render translucency in a separate render pass// On mobile it's better to render as much as possible in a single pass
- // All iOS support frame_buffer_fetch
- // Vulkan uses subpasses for depth fetch
- if (IsAndroidOpenGLESPlatform(ShaderPlatform) && (GSupportsShaderFramebufferFetch || GSupportsShaderDepthStencilFetch))// Some Androids support frame_buffer_fetch
- if (View.bIsPlanarReflection || View.bIsSceneCapture)// Always render reflection capture in single pass
- if (!IsMobileHDR())// Always render LDR in single pass
- if (bMobileMSAA && !IsSimulatedPlatform(ShaderPlatform))// MSAA depth can't be sampled or resolved, unless we are on PC (no vulkan)
- const bool bForceDepthResolve = CVarMobileForceDepthResolve.GetValueOnRenderThread() == 1;// Whether we need to store depth for post-processing// On PowerVR we see flickering of shadows and depths not updating correctly if targets are discarded.
- const bool bSeparateTranslucencyActive = IsMobileSeparateTranslucencyActive(View);
- bool bKeepDepthContent = bForceDepthResolve || bRequiresTranslucencyPass || (bRenderToSceneColor && (bSeparateTranslucencyActive || View.bIsReflectionCapture || (View.bIsSceneCapture && (ViewFamily.SceneCaptureSource == ESceneCaptureSource::SCS_SceneColorHDR || ViewFamily.SceneCaptureSource == ESceneCaptureSource::SCS_SceneColorSceneDepth))));
- bool bSubmitOffscreenRendering = !bGammaSpace || bRenderToSceneColor;// Whether to submit cmdbuffer with offscreen rendering before doing post-processing
- GSystemTextures.InitializeTextures(RHICmdList, ViewFeatureLevel);// Initialize global system textures (pass-through if already initialized).
- FSceneRenderTargets::Get(RHICmdList).Allocate(RHICmdList, this);在这里前面已经创建好了texture,这里会再确认一下,如果不合适会重新创建// Allocate the maximum scene render target space for the current view family.
- if (UseVirtualTexturing(ViewFeatureLevel)) FVirtualTextureSystem::Get().AllocateResources(RHICmdList, ViewFeatureLevel); FVirtualTextureSystem::Get().CallPendingCallbacks();// AllocateResources needs to be called before RHIBeginScene
- GRenderTargetPool.TransitionTargetsWritable(RHICmdList);//make sure all the targets we're going to use will be safely writable.
- InitViews(RHICmdList);到了又一个重要步骤了,culling,get可见物件list。在这里,除了culling,还有做各个pass的准备工作,相当于Unity SRP中pass的setup// Find the visible primitives.
- PreVisibilityFrameSetup(RHICmdList);
详细信息在:\Engine\Source\Runtime\Renderer\Private\SceneVisibility.cpp的FSceneRenderer::PreVisibilityFrameSetup函数
- RHICmdList.BeginScene();// Notify the RHI we are beginning to render a scene.
- if (DoLazyStaticMeshUpdate) FPrimitiveSceneInfo::UpdateStaticMeshes(RHICmdList, Scene, SceneInfos, false);
SceneInfo->RemoveCachedMeshDrawCommands(); CacheMeshDrawCommands(RHICmdList, Scene, SceneInfos);
- RunGPUSkinCacheTransition(RHICmdList, Scene, EGPUSkinCacheTransition::FrameSetup);
- if (IsHairStrandsEnable(Scene->GetShaderPlatform()) && Views.Num() > 0) RunHairStrandsInterpolation(RHICmdList, WorldType, Scene->GetGPUSkinCache(), ShaderDrawData, ShaderMap, EHairStrandsInterpolationType::SimulationStrands, nullptr);
- if (Scene->FXSystem && Views.IsValidIndex(0)) Scene->FXSystem->PreInitViews(RHICmdList, Views[0].AllowGPUParticleUpdate() && !ViewFamily.EngineShowFlags.HitProxies); // Notify the FX system that the scene is about to perform visibility checks.
- if (ViewFamily.EngineShowFlags.LightInfluences) LightInfluencesPDI.DrawLine(PrimitiveSceneInfo->Proxy->GetBounds().Origin, LightSceneInfo->Proxy->GetLightToWorld().GetOrigin(), LineColor, SDPG_World);// Draw lines to lights affecting this mesh if its selected.
- // Setup motion blur parameters (also check for camera movement thresholds)还设置了TAA相关的参数
- View.DitherFadeOutUniformBuffer = FDitherUniformBufferRef::CreateUniformBufferImmediate(View.GetTemporalLODTransition(), UniformBuffer_SingleFrame);// Setup global dither fade in and fade out uniform buffers.
- ComputeViewVisibility(RHICmdList, BasePassDepthStencilAccess, ViewCommandsPerView, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer);这个应该就是真的在计算culling了
- UpdateReflectionSceneData(Scene);
- Scene->ConditionalMarkStaticMeshElementsForUpdate();
Primitives[PrimitiveIndex]->BeginDeferredUpdateStaticMeshes();
- FPrimitiveSceneInfo::UpdateStaticMeshes(RHICmdList, Scene, UpdatedSceneInfos);
- View.PrimitiveVisibilityMap.Init(false,Scene->Primitives.Num());// Allocate the view's visibility maps.
- View.DynamicMeshEndIndices.SetNumUninitialized(Scene->Primitives.Num());// we don't initialized as we overwrite the whole array (in GatherDynamicMeshElements)
- View.PrimitiveDefinitelyUnoccludedMap.Init(false,Scene->Primitives.Num());
- View.PotentiallyFadingPrimitiveMap.Init(false,Scene->Primitives.Num());
- View.PrimitiveFadeUniformBuffers.AddZeroed(Scene->Primitives.Num());
- View.PrimitiveFadeUniformBufferMap.Init(false, Scene->Primitives.Num());
- View.StaticMeshVisibilityMap.Init(false,Scene->StaticMeshes.GetMaxIndex());
- View.StaticMeshFadeOutDitheredLODMap.Init(false,Scene->StaticMeshes.GetMaxIndex());
- View.StaticMeshFadeInDitheredLODMap.Init(false,Scene->StaticMeshes.GetMaxIndex());
- View.StaticMeshBatchVisibility.AddZeroed(Scene->StaticMeshBatchVisibility.GetMaxIndex());
- View.PrimitivesLODMask.Init(FLODMask(), Scene->Primitives.Num());
- View.PrimitivesCustomData.Init(nullptr, Scene->Primitives.Num());
- View.PrimitiveCustomDataMemStack.Reserve(WillExecuteInParallel ? FMath::CeilToInt(((float)View.PrimitiveVisibilityMap.Num() / (float)FRelevancePrimSet<.int32>::MaxInputPrims)) + 1 : 1);// We must reserve to prevent realloc otherwise it will cause memory leak if we Execute In Parallel
- View.AllocateCustomDataMemStack();
- View.VisibleLightInfos.Empty(Scene->Lights.GetMaxIndex());
- View.DirtyIndirectLightingCacheBufferPrimitives.Reserve(Scene->Primitives.Num());// The dirty list allocation must take into account the max possible size because when GILCUpdatePrimTaskEnabled is true,// the indirect lighting cache will be update on by threaded job, which can not do reallocs on the buffer (since it uses the SceneRenderingAllocator).
- View.PrimitiveViewRelevanceMap.Empty(Scene->Primitives.Num());
- View.PrimitiveViewRelevanceMap.AddZeroed(Scene->Primitives.Num());
- View.PrecomputedVisibilityData = ViewState->GetPrecomputedVisibilityData(View, Scene);这个看上去是在预计算可见性数据//Returns an array of visibility data for the given view position, or NULL if none exists. The data bits are indexed by VisibilityId of each primitive in the scene.This method decompresses data if necessary and caches it based on the bucket and chunk index in the view state.
- HLODTree.UpdateVisibilityStates(View);更新HLOD的状态,包含渐变、消失等
- FrustumCull<.true, true, true>(Scene, View) template<.bool UseCustomCulling, bool bAlsoUseSphereTest, bool bUseFastIntersect>
- UpdatePrimitiveFading(Scene, View);
- View.PrimitiveVisibilityMap.AccessCorrespondingBit(View.HiddenPrimitives) = false;// If any primitives are explicitly hidden, remove them now.
- View.PrimitiveVisibilityMap.AccessCorrespondingBit(View.ShowOnlyPrimitives) = false;// If the view has any show only primitives, hide everything else
- OcclusionCull(RHICmdList, Scene, View, DynamicVertexBuffer);看字面意思是在做OC,还包括了HZ//Cull occluded primitives in the view.
- FPrimitiveSceneInfo::UpdateStaticMeshes(RHICmdList, Scene, AddedSceneInfos);
- ComputeAndMarkRelevanceForViewParallel(RHICmdList, Scene, View, ViewCommands, ViewBit, HasDynamicMeshElementsMasks, HasDynamicEditorMeshElementsMasks, HasViewCustomDataMasks);
ParallelFor(Packets.Num(),
[&Packets](int32 Index)
{
Packets[Index]->AnyThreadTask();
},
!WillExecuteInParallel
);
ComputeRelevance();
在D:\Trunk\EngineSource\Engine\Source\Runtime\Engine\Private\Materials\MaterialInterface.cpp中根据材质blendmode,bEnableMobileSeparateTranslucency(DisplayName = "Mobile Separate Translucency"),以及bEnableSeparateTranslucency(DisplayName = "Render After DOF")属性确定bSeparateTranslucency、bSeparateTranslucencyModulate、bNormalTranslucency确定TranslucentPrimCount中的ETranslucencyPass
MarkRelevant();
- ViewState->FrozenPrimitives.Add(Scene->PrimitiveComponentIds[BitIt.GetIndex()]);// if we are freezing the scene, then remember the primitives that are rendered.
- GatherDynamicMeshElements(Views, Scene, ViewFamily, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer, HasDynamicMeshElementsMasks, HasDynamicEditorMeshElementsMasks, HasViewCustomDataMasks, MeshCollector);// Gather FMeshBatches from scene proxies
- SetupMeshPass(View, BasePassDepthStencilAccess, ViewCommands);//创建MeshPassProcessor
Pass.DispatchPassSetup(
Scene,
View,
PassType,
BasePassDepthStencilAccess,
MeshPassProcessor,
View.DynamicMeshElements,
&View.DynamicMeshElementsPassRelevance,
View.NumVisibleDynamicMeshElements[PassType],
ViewCommands.DynamicMeshCommandBuildRequests[PassType],
ViewCommands.NumDynamicMeshCommandBuildRequestElements[PassType],
ViewCommands.MeshCommands[PassIndex]);设置TaskContext,生成DynamicMeshDrawCommands
- InitSkyAtmosphereForViews(RHICmdList);看上去就是创建一些RT
- InitSkyAtmosphereForScene(RHICmdList, Scene);
FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo();
EPixelFormat TextureLUTFormat = GetSkyLutTextureFormat(Scene->GetFeatureLevel());
EPixelFormat TextureLUTSmallFormat = GetSkyLutSmallTextureFormat();
- Desc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(TransmittanceLutWidth, TransmittanceLutHeight), TranstmittanceLUTUseSmallFormat ? TextureLUTSmallFormat : TextureLUTFormat, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_UAV, false);这个API前面也提到了Create2DDesc
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, TransmittanceLutTexture, TEXT("TransmittanceLutTexture"), true, ERenderTargetTransience::Transient);这个API前面也提到了FindFreeElement
- Desc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(MultiScatteredLuminanceLutWidth, MultiScatteredLuminanceLutHeight), TextureLUTFormat, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_UAV, false);
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, MultiScatteredLuminanceLutTexture, TEXT("MultiScatteredLuminanceLutTexture"), true, ERenderTargetTransience::Transient);
- Desc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(1, 1), TextureLUTFormat, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_UAV, false);
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, DistantSkyLightLutTexture, TEXT("DistantSkyLightLutTexture"), true, ERenderTargetTransience::Transient);
- InitSkyAtmosphereForView(RHICmdList, Scene, View);
const FSkyAtmosphereRenderSceneInfo& SkyInfo = *Scene->GetSkyAtmosphereSceneInfo();
EPixelFormat TextureLUTFormat = GetSkyLutTextureFormat(Scene->GetFeatureLevel());
EPixelFormat TextureLUTSmallFormat = GetSkyLutSmallTextureFormat();
EPixelFormat TextureAerialLUTFormat = (CVarSkyAtmosphereLUT32.GetValueOnAnyThread() != 0) ? PF_A32B32G32R32F : PF_FloatRGBA;
- FPooledRenderTargetDesc SkyAtmosphereViewLutTextureDesc = FPooledRenderTargetDesc::Create2DDesc( FIntPoint(SkyViewLutWidth, SkyViewLutHeight), TextureLUTFormat, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_UAV, false);
- FPooledRenderTargetDesc SkyAtmosphereCameraAerialPerspectiveVolumeDesc = FPooledRenderTargetDesc::CreateVolumeDesc( CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeScreenResolution, CameraAerialPerspectiveVolumeDepthResolution, TextureAerialLUTFormat, FClearValueBinding::None, TexCreate_HideInVisualizeTexture, TexCreate_ShaderResource | TexCreate_UAV, false);
- GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereViewLutTextureDesc, View.SkyAtmosphereViewLutTexture, TEXT("View.SkyAtmosphereViewLutTexture"), true, ERenderTargetTransience::Transient);
- GRenderTargetPool.FindFreeElement(RHICmdList, SkyAtmosphereCameraAerialPerspectiveVolumeDesc, View.SkyAtmosphereCameraAerialPerspectiveVolume, TEXT("View.SkyAtmosphereCameraAerialPerspectiveVolume"), true, ERenderTargetTransience::Transient);
- PostVisibilityFrameSetup(ILCTaskData);
- ((FSceneViewState*)View.State)->TrimHistoryRenderTargets(Scene);
- // Clear the mobile light shaft data.(Patrick:mobile居然把lightshaft关闭了,这个应该是可以保留的)
- Scene->IndirectLightingCache.StartUpdateCachePrimitivesTask(Scene, *this, true, OutILCTaskData);
- DrawBox( &LightPDI, LightToWorld, FVector( 0.0f, LightParameters.SourceRadius, LightParameters.SourceLength ), ColoredMeshInstance, SDPG_World );// Draw shapes for reflection captures
- InitFogConstants
- if (bDynamicShadows && !IsSimpleForwardShadingEnabled(ShaderPlatform)) InitDynamicShadows(RHICmdList); 为渲染shadow做准备工作// Setup dynamic shadows./** Finds the visible dynamic shadows for each view. */
具体实现在Engine\Source\Runtime\Renderer\Private\ShadowSetupMobile.cpp的FMobileSceneRenderer::InitDynamicShadows函数中
- /** Finds the visible dynamic shadows for each view. */
- void FMobileSceneRenderer::InitDynamicShadows(FRHICommandListImmediate& RHICmdList)
- static auto* MyCVarMobileEnableStaticAndCSMShadowReceivers = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableStaticAndCSMShadowReceivers"));//可以在配置文件Config\DefaultEngine.ini中看到默认值
- const bool bMobileEnableStaticAndCSMShadowReceivers = MyCVarMobileEnableStaticAndCSMShadowReceivers->GetValueOnRenderThread() == 1;
- const bool bCombinedStaticAndCSMEnabled = MyCVarMobileEnableStaticAndCSMShadowReceivers->GetValueOnRenderThread()!=0;
- static auto* CVarMobileEnableMovableLightCSMShaderCulling = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.EnableMovableLightCSMShaderCulling"));//可以在配置文件Config\DefaultEngine.ini中看到默认值
- const bool bMobileEnableMovableLightCSMShaderCulling = CVarMobileEnableMovableLightCSMShaderCulling->GetValueOnRenderThread() == 1;
- for (FLightSceneInfo* MobileDirectionalLightSceneInfo : Scene->MobileDirectionalLights)// initialize CSMVisibilityInfo for each eligible light.
- bool bLightHasCombinedStaticAndCSMEnabled = bCombinedStaticAndCSMEnabled && LightSceneProxy->UseCSMForDynamicObjects();///UseCSMForDynamicObjects()始终为false/** Whether this light should create CSM for dynamic objects only (mobile renderer) */
- bool bMovableLightUsingCSM = bMobileEnableMovableLightCSMShaderCulling && LightSceneProxy->IsMovable() && MobileDirectionalLightSceneInfo->ShouldRenderViewIndependentWholeSceneShadows();//ShouldRenderViewIndependentWholeSceneShadows()/** Encapsulates all View-Independent reasons to render ViewIndependentWholeSceneShadows for this light */
- bool bShouldRenderLight = ShouldRenderLightViewIndependent();//当灯光不是黑色,且并非静态光或者没有参与预计算的时候,才返回True//return !Proxy->GetColor().IsAlmostBlack() && (!Proxy->HasStaticLighting() || !IsPrecomputedLightingValid());//Only render lights with dynamic lighting or unbuilt static lights
- bool bCastDynamicShadow = Proxy->CastsDynamicShadow();
- const bool bCreateShadowToPreviewStaticLight = Proxy->HasStaticShadowing() && bCastDynamicShadow && !IsPrecomputedLightingValid();// Also create a whole scene shadow for lights with precomputed shadows that are unbuilt
- return bShouldRenderShadow = bShouldRenderLight && bCastDynamicShadow && (!Proxy->HasStaticLighting() || bCreateShadowToPreviewStaticLight);
- if (bLightHasCombinedStaticAndCSMEnabled || bMovableLightUsingCSM)
- Views.VisibleLightInfos[MobileDirectionalLightSceneInfo->Id].MobileCSMSubjectPrimitives.InitShadowSubjectPrimitives(PrimitiveCount);//MobileCSMSubjectPrimitives:/** List of CSM shadow casters. Used by mobile renderer for culling primitives receiving static + CSM shadows */InitShadowSubjectPrimitives:/** Used to initialize the ShadowSubjectPrimitivesEncountered bit array * to prevent shadow primitives being added more than once. */
- FSceneRenderer::InitDynamicShadows(RHICmdList, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer);
- const bool bMobile = FeatureLevel < ERHIFeatureLevel::SM5;
- bool bStaticSceneOnly = false; bStaticSceneOnly = bStaticSceneOnly || Views.bStaticSceneOnly;
- const bool bProjectEnablePointLightShadows = Scene->ReadOnlyCVARCache.bEnablePointLightShadows;
- uint32 NumPointShadowCachesUpdatedThisFrame = 0;
- uint32 NumSpotShadowCachesUpdatedThisFrame = 0;
- const FLightOcclusionType OcclusionType = GetLightOcclusionType(LightSceneInfoCompact);//获取lightShadow的类型:Shadowmap/Raytraced
- bIsVisibleInAnyView = LightSceneInfo->ShouldRenderLight(Views[ViewIndex]);//查看灯光是否可见 View frustums are only checked when lights have visible primitives or have modulated shadows,// so we don't need to check for that again here
- static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting"));
- const bool bAllowStaticLighting = (!AllowStaticLightingVar || AllowStaticLightingVar->GetValueOnRenderThread() != 0);
- const bool bPointLightShadow = LightSceneInfoCompact.LightType == LightType_Point || LightSceneInfoCompact.LightType == LightType_Rect;//灯光的类型有:LightType_Directional、LightType_Point、LightType_Spot、LightType_Rect
- const bool bShouldCreateShadowForMovableLight = LightSceneInfoCompact.bCastDynamicShadow && (!LightSceneInfo->Proxy->HasStaticShadowing() || !bAllowStaticLighting);// Only create whole scene shadows for lights that don't precompute shadowing (movable lights)
- const bool bCreateShadowForMovableLight = bShouldCreateShadowForMovableLight && (!bPointLightShadow || bProjectEnablePointLightShadows);
- const bool bShouldCreateShadowToPreviewStaticLight = LightSceneInfo->Proxy->HasStaticShadowing() && LightSceneInfoCompact.bCastStaticShadow && !LightSceneInfo->IsPrecomputedLightingValid();// Also create a whole scene shadow for lights with precomputed shadows that are unbuilt
- const bool bCreateShadowToPreviewStaticLight = bShouldCreateShadowToPreviewStaticLight && (!bPointLightShadow || bProjectEnablePointLightShadows);
- const bool bShouldCreateShadowForOverflowStaticShadowing = LightSceneInfo->Proxy->HasStaticShadowing() && !LightSceneInfo->Proxy->HasStaticLighting() && LightSceneInfoCompact.bCastStaticShadow && LightSceneInfo->IsPrecomputedLightingValid() && LightSceneInfo->Proxy->GetShadowMapChannel() == INDEX_NONE;// Create a whole scene shadow for lights that want static shadowing but didn't get assigned to a valid shadowmap channel due to overlap
- const bool bCreateShadowForOverflowStaticShadowing = bShouldCreateShadowForOverflowStaticShadowing && (!bPointLightShadow || bProjectEnablePointLightShadows);//如果不支持点光源阴影的话,这里应该always false
- if (bCreateShadowForMovableLight || bCreateShadowToPreviewStaticLight || bCreateShadowForOverflowStaticShadowing) CreateWholeSceneProjectedShadow(LightSceneInfo, NumPointShadowCachesUpdatedThisFrame, NumSpotShadowCachesUpdatedThisFrame);// Try to create a whole scene projected shadow./** Creates a projected shadow for all primitives affected by a light. If the light doesn't support whole-scene shadows, it returns false. * @param LightSceneInfo - The light to create a shadow for. * @return true if a whole scene shadow was created */
- if (LightSceneInfo->Proxy->GetWholeSceneProjectedShadowInitializer(ViewFamily, ProjectedShadowInitializers) && (!bMobilePlatform || LightSceneInfo->Proxy->IsMovable()))
- const uint32 ShadowBorder = (ProjectedShadowInitializers[0].bOnePassPointLightShadow && !bMobilePlatform) ? 0 : SHADOW_BORDER;
- const uint32 EffectiveDoubleShadowBorder = ShadowBorder * 2;
- const uint32 MinShadowResolution = FMath::Max<.int32>(0, CVarMinShadowResolution.GetValueOnRenderThread());
- const int32 MaxShadowResolutionSetting = GetCachedScalabilityCVars().MaxShadowResolution;
- const FIntPoint ShadowBufferResolution = SceneContext_ConstantsOnly.GetShadowDepthTextureResolution();
- const uint32 MaxShadowResolution = FMath::Min(MaxShadowResolutionSetting, ShadowBufferResolution.X) - EffectiveDoubleShadowBorder;
- const uint32 MaxShadowResolutionY = FMath::Min(MaxShadowResolutionSetting, ShadowBufferResolution.Y) - EffectiveDoubleShadowBorder;
- const uint32 ShadowFadeResolution = FMath::Max<.int32>(0, CVarShadowFadeResolution.GetValueOnRenderThread());
- const float ScreenRadius = LightSceneInfo->Proxy->GetEffectiveScreenRadius(View.ShadowViewMatrices);
- switch (LightSceneInfo->Proxy->GetLightType()) case LightType_Directional:UnclampedResolution = ScreenRadius * CVarShadowTexelsPerPixel.GetValueOnRenderThread();
- const float FadeAlpha = CalculateShadowFadeAlpha(UnclampedResolution, ShadowFadeResolution, MinShadowResolution) * LightSceneInfo->Proxy->GetShadowAmount();// Compute FadeAlpha before ShadowResolutionScale contribution (artists want to modify the softness of the shadow, not change the fade ranges)//这个厉害了,貌似是边界过度,这个之前没做
- FadeAlphas.Add(FadeAlpha);
- ClampedResolution = UnclampedResolution * ShadowResolutionScale;
- MaxDesiredResolution = FMath::Max( MaxDesiredResolution, FMath::Max<.float>( ClampedResolution, FMath::Min<.float>(MinShadowResolution, ShadowBufferResolution.X - EffectiveDoubleShadowBorder) ) );
- bStaticSceneOnly = bStaticSceneOnly || View.bStaticSceneOnly;
- bAnyViewIsSceneCapture = bAnyViewIsSceneCapture || View.bIsSceneCapture;
- if (MaxFadeAlpha > 1.0f / 256.0f)
- if (ProjectedShadowInitializer.bOnePassPointLightShadow && !IsMobilePlatform(ShaderPlatform)) SizeX = SizeY = SceneContext_ConstantsOnly.GetCubeShadowDepthZResolution(SceneContext_ConstantsOnly.GetCubeShadowDepthZIndex(MaxDesiredResolution));// Round to a resolution that is supported for one pass point light shadows// CTG Begin: Double Dong, we don't round for mobile platform这里不太理解?
- ComputeWholeSceneShadowCacheModes( LightSceneInfo, ProjectedShadowInitializer.bOnePassPointLightShadow, ViewFamily.CurrentRealTime, MaxDesiredResolution, FIntPoint(MaxShadowResolution, MaxShadowResolutionY), Scene, //* Below are in-out or out parameters. They can change*/ ProjectedShadowInitializer, ShadowMapSize, InOutNumPointShadowCachesUpdatedThisFrame, InOutNumSpotShadowCachesUpdatedThisFrame, NumShadowMaps, CacheMode);
- uint32* NumCachesUpdatedThisFrame = nullptr;
- uint32 MaxCacheUpdatesAllowed = 0;
- if (GCacheWholeSceneShadows && (!bCubeShadowMap || RHISupportsGeometryShaders(GShaderPlatformForFeatureLevel[Scene->GetFeatureLevel()]) || RHISupportsVertexShaderLayer(GShaderPlatformForFeatureLevel[Scene->GetFeatureLevel()])) && ((LightSceneInfo->Proxy->GetLightType() != LightType_Spot && LightSceneInfo->Proxy->GetLightType() != LightType_Point) || !IsMobilePlatform(GShaderPlatformForFeatureLevel[Scene->GetFeatureLevel()])))//移动设备只支持方向光
- if (Scene->GetCachedWholeSceneShadowMapsSize() < GWholeSceneShadowCacheMb * 1024 * 1024) OutNumShadowMaps = 2; OutCacheModes[0] = SDCM_StaticPrimitivesOnly; OutCacheModes[1] = SDCM_MovablePrimitivesOnly; ++* NumCachesUpdatedThisFrame; Scene->CachedShadowMaps.Add(LightSceneInfo->Id, FCachedShadowMapData(InOutProjectedShadowInitializer, RealTime));
- if (OutNumShadowMaps > 0) int32 NumOcclusionQueryableShadows += IsShadowCacheModeOcclusionQueryable(OutCacheModes[i]);
- SizeX = ShadowMapSize.X - ShadowBorder * 2;
- SizeY = ShadowMapSize.Y - ShadowBorder * 2;
- for (int32 CacheModeIndex = 0; CacheModeIndex < NumShadowMaps; CacheModeIndex++)
- ProjectedShadowInfo->SetupWholeSceneProjection( LightSceneInfo, NULL, ProjectedShadowInitializer, SizeX, SizeY, ShadowBorder, false // no RSM );
- if (bDirectionalLight)
- PreShadowTranslation = -WorldToFace.InverseFast().TransformPosition(TransformedPosition - FVector(SnapX, SnapY, 0.0f));
- UserBiasForStaticShadow = Initializer.StaticShadowMapBias[LevelForStaticShadow];
- UserSlopeBiasForStaticShadow = Initializer.StaticShadowSlopeBias[LevelForStaticShadow];
- if (CascadeSettings.ShadowSplitIndex >= 0 && bDirectionalLight)
- ShadowBounds = InLightSceneInfo->Proxy->GetShadowSplitBounds( *InDependentView, bRayTracedDistanceField ? INDEX_NONE : CascadeSettings.ShadowSplitIndex, InLightSceneInfo->IsPrecomputedLightingValid(), 0);
- GetViewFrustumBounds(CasterFrustum, WorldToFace * FShadowProjectionMatrix(Initializer.MinLightW, MaxSubjectZ, Initializer.WAxis), true);
- FViewElementPDI ShadowPDI(InDependentView, nullptr, &InDependentView->DynamicPrimitiveShaderData);
- GetViewFrustumBounds(ReceiverFrustum, ReceiverMatrix, true);//投影和接收矩阵还不同
- UpdateShaderDepthBias();
- if (IsWholeSceneDirectionalShadow())
- DepthBias = CVarCSMShadowDepthBias.GetValueOnRenderThread() / (MaxSubjectZ - MinSubjectZ);// the z range is adjusted to we need to adjust here as well
- const float WorldSpaceTexelScale = ShadowBounds.W / ResolutionX;
- DepthBias = FMath::Lerp(DepthBias, DepthBias * WorldSpaceTexelScale, CascadeSettings.CascadeBiasDistribution);
- DepthBias *= LightSceneInfo->Proxy->GetUserShadowBias();
- SlopeScaleDepthBias = CVarCSMShadowSlopeScaleDepthBias.GetValueOnRenderThread();
- SlopeScaleDepthBias *= LightSceneInfo->Proxy->GetUserShadowSlopeBias();
- ShaderDepthBias = FMath::Max(DepthBias, 0.0f);
- ShaderSlopeDepthBias = FMath::Max(DepthBias * SlopeScaleDepthBias, 0.0f);
- ShaderMaxSlopeDepthBias = CVarShadowMaxSlopeScaleDepthBias.GetValueOnRenderThread();
- ProjectedShadowInfo->CacheMode = CacheMode[CacheModeIndex];
- ProjectedShadowInfo->FadeAlphas = FadeAlphas;
- if (ProjectedShadowInitializer.bOnePassPointLightShadow) for (int32 FaceIndex = 0; FaceIndex < 6; FaceIndex++) ProjectedShadowInfo->OnePassShadowViewMatrices.Add(WorldToLightMatrix); ProjectedShadowInfo->OnePassShadowViewProjectionMatrices.Add(ShadowViewProjectionMatrix); GetViewFrustumBounds(ProjectedShadowInfo->OnePassShadowFrustums[FaceIndex], ShadowViewProjectionMatrix, false); ProjectedShadowInfo->CasterFrustum.Init();
- if (!ProjectedShadowInfo->bRayTracedDistanceField)
- if (CacheMode[CacheModeIndex] != SDCM_StaticPrimitivesOnly) BuildLightViewFrustumConvexHulls(LightOrigin, Views, LightViewFrustumConvexHulls);//CSM的第二层使用的是SDCM_MovablePrimitivesOnly
- ProjectedShadowInfo->AddSubjectPrimitive(Interaction->GetPrimitiveSceneInfo(), &Views, FeatureLevel, false);//这里不太懂?
- Scene->CachedShadowMaps.FindChecked(ProjectedShadowInfo->GetLightSceneInfo().Id).bCachedShadowMapHasPrimitives = bHasStaticPrimitives;//这里不太懂?
- VisibleLightInfo.AllProjectedShadows.Add(ProjectedShadowInfo);//这里不太懂?
- if ((!LightSceneInfo->Proxy->HasStaticLighting() && LightSceneInfoCompact.bCastDynamicShadow) || bCreateShadowToPreviewStaticLight) AddViewDependentWholeSceneShadowsForView(ViewDependentWholeSceneShadows, ViewDependentWholeSceneShadowsThatNeedCulling, VisibleLightInfo, *LightSceneInfo);//修正Stereo模式下的Fade、Bias等参数,问题是这里为什么也有修改? Allow movable and stationary lights to create CSM, or static lights that are unbuilt
- if (!bMobile || (LightSceneInfo->Proxy->CastsModulatedShadows() && !LightSceneInfo->Proxy->UseCSMForDynamicObjects())) SetupInteractionShadows(RHICmdList, Interaction, VisibleLightInfo, bStaticSceneOnly, ViewDependentWholeSceneShadows, PreShadows);
- CreatePerObjectProjectedShadow(RHICmdList, Interaction, bCreateTranslucentObjectShadow, bCreateInsetObjectShadow || bCreateObjectShadowForStationaryLight, ViewDependentWholeSceneShadows, PreShadows);//看上去是人物自阴影,或者影中影?这里也有修改。。
- InitProjectedShadowVisibility(RHICmdList);// Calculate visibility of the projected shadows.其中会根据ViewFamily.EngineShowFlags.ShadowFrustums,通过DrawFrustumWireframe绘制阴影区域。
- UpdatePreshadowCache(FSceneRenderTargets::Get(RHICmdList));// Clear old preshadows and attempt to add new ones to the cache/** Removes stale shadows and attempts to add new preshadows to the cache. */
- GatherShadowPrimitives(PreShadows, ViewDependentWholeSceneShadowsThatNeedCulling, bStaticSceneOnly);// Gathers the list of primitives used to draw various shadow types
- AllocateShadowDepthTargets(RHICmdList);
- for (TSparseArray<.FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
- for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo.AllProjectedShadows.Num(); ShadowIndex++)//VisibleLightInfo.AllProjectedShadows中包含了N层cache(CSM)、SetupInteractionShadows加入的自阴影,AddViewDependentWholeSceneShadowsForView加入的Stereo阴影
- if (bShadowIsVisible)
- if (ProjectedShadowInfo->bWholeSceneShadow) INC_DWORD_STAT(STAT_WholeSceneShadows);
- if (ProjectedShadowInfo->CacheMode == SDCM_MovablePrimitivesOnly) INC_DWORD_STAT(STAT_CachedWholeSceneShadows);
- bool bNeedsProjection = ProjectedShadowInfo->CacheMode != SDCM_StaticPrimitivesOnly && (FeatureLevel >= ERHIFeatureLevel::SM5 || ProjectedShadowInfo->bPerObjectOpaqueShadow);// Mobile rendering only projects opaque per object shadows.
- const bool bNeedsShadowmapSetup = !ProjectedShadowInfo->bCapsuleShadow && !ProjectedShadowInfo->bRayTracedDistanceField;
- if (bNeedsShadowmapSetup)
- if (ProjectedShadowInfo->bDirectionalLight && ProjectedShadowInfo->bWholeSceneShadow) WholeSceneDirectionalShadows.Add(ProjectedShadowInfo);
- VisibleLightInfo.ShadowsToProject.Sort(FCompareFProjectedShadowInfoBySplitIndex()); // Sort cascades, this is needed for blending between cascades to work
- VisibleLightInfo.RSMsToProject.Sort(FCompareFProjectedShadowInfoBySplitIndex());
- AllocateCSMDepthTargets(RHICmdList, WholeSceneDirectionalShadows);//创建RT WholeSceneShadowmap和WholeSceneShadowmap1
- if (CachedPreShadows.Num() > 0)//没有走这里,所以没看
- if (!Scene->PreShadowCacheDepthZ) GRenderTargetPool.FindFreeElement(RHICmdList, Desc, Scene->PreShadowCacheDepthZ, TEXT("PreShadowCacheDepthZ"), true, ERenderTargetTransience::NonTransient);
- SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget = Scene->PreShadowCacheDepthZ;
- for (int32 ShadowIndex = 0; ShadowIndex < CachedPreShadows.Num(); ShadowIndex++)
- ProjectedShadowInfo->SetupShadowDepthView(RHICmdList, this); // Note: adding preshadows whose depths are cached so that GatherDynamicMeshElements// will still happen, which is necessary for preshadow receiver stenciling
- AllocateRSMDepthTargets(RHICmdList, RSMShadows);//没有走这里,所以没看
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ShadowMapAtlas.RenderTargets.ColorTargets[0], TEXT("RSMNormal"), true, ERenderTargetTransience::NonTransient);
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ShadowMapAtlas.RenderTargets.ColorTargets[1], TEXT("RSMDiffuse"), true, ERenderTargetTransience::NonTransient);
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ShadowMapAtlas.RenderTargets.DepthTarget, TEXT("RSMDepth"), true, ERenderTargetTransience::NonTransient);
- AllocateCachedSpotlightShadowDepthTargets(RHICmdList, CachedSpotlightShadows);//没有走这里,所以没看
- for (int32 ShadowIndex = 0; ShadowIndex < CachedSpotlightShadows.Num(); ShadowIndex++)
- GRenderTargetPool.FindFreeElement(RHICmdList, ShadowMapDesc2D, ShadowMap.RenderTargets.DepthTarget, TEXT("CachedShadowDepthMap"), true, ERenderTargetTransience::NonTransient);
- AllocatePerObjectShadowDepthTargets(RHICmdList, Shadows);//和bOnePassPointLightShadow关联,有走到这里
- SceneContext.AllocateMobileShadowAtlas(RHICmdList); -> GRenderTargetPool.FindFreeElement(RHICmdList, Desc, MobileShadowAtlas, TEXT("ShadowDepthAtlas"));// Create a texture to store the resolved light attenuation values, and a render-targetable surface to hold the unresolved light attenuation values.
- ProjectedShadowInfo->RenderTargets.CopyReferencesFromRenderTargets(ShadowMapAtlas.RenderTargets);
- ProjectedShadowInfo->SetupShadowDepthView(RHICmdList, this);
- ShadowMapAtlas.Shadows.Add(ProjectedShadowInfo);
- AllocateTranslucentShadowDepthTargets(RHICmdList, TranslucentShadows);//没有走这里,所以没看
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ShadowMapAtlas.RenderTargets.ColorTargets[SurfaceIndex], GetTranslucencyShadowTransmissionName(SurfaceIndex), true, ERenderTargetTransience::NonTransient);//"TranslucencyShadowTransmission%d"
- AllocateOnePassPointLightDepthTargets(RHICmdList, WholeScenePointShadows);//没有走这里,所以没看
- if (FeatureLevel >= ERHIFeatureLevel::SM5)
- GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ShadowMapCubemap.RenderTargets.DepthTarget, TEXT("CubeShadowDepthZ"), true, ERenderTargetTransience::NonTransient);
- for (int32 TranslucentShadowIndex = 0; TranslucentShadowIndex < TranslucentShadows.Num(); ++TranslucentShadowIndex)//没有走这里,所以没看
- SetupTranslucentSelfShadowUniformParameters(ShadowInfo, Parameters);
- RHIUpdateUniformBuffer(*UniformBufferPtr, &Parameters);
- for (TMap<.int32, FCachedShadowMapData>::TIterator CachedShadowMapIt(Scene->CachedShadowMaps); CachedShadowMapIt; ++CachedShadowMapIt)
- if (ShadowMapData.ShadowMap.IsValid() && ViewFamily.CurrentRealTime - ShadowMapData.LastUsedTime > 2.0f) ShadowMapData.ShadowMap.Release();// Remove cache entries that haven't been used in a while//cache的一段时间不用,会删除,这个现在应该没用,可以看下
- GatherShadowDynamicMeshElements(DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer);// Generate mesh element arrays from shadow primitive arrays//绘制阴影的时候,整理要绘制的东西
- for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num(); AtlasIndex++)
- for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.RSMAtlases.Num(); AtlasIndex++)
- for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapCubemaps.Num(); AtlasIndex++)
- for (int32 ShadowIndex = 0; ShadowIndex < SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num(); ShadowIndex++)
- for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases.Num(); AtlasIndex++)
- ProjectedShadowInfo->GatherDynamicMeshElements(*this, VisibleLightInfo, ReusedViewsArray, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer);
- PrepareViewVisibilityLists();// Prepare view's visibility lists.
- View.MobileCSMVisibilityInfo.MobilePrimitiveCSMReceiverVisibilityMap.Init(false, View.PrimitiveVisibilityMap.Num());// Init list of primitives that can receive Dynamic CSM.
- // Init static mesh visibility info for CSM drawlist
- MobileCSMVisibilityInfo.MobileCSMStaticMeshVisibilityMap.Init(false, View.StaticMeshVisibilityMap.Num());
- MobileCSMVisibilityInfo.MobileCSMStaticBatchVisibility.AddZeroed(View.StaticMeshBatchVisibility.Num());
- // Init static mesh visibility info for default drawlist that excludes meshes in CSM only drawlist.
- MobileCSMVisibilityInfo.MobileNonCSMStaticMeshVisibilityMap = View.StaticMeshVisibilityMap;
- MobileCSMVisibilityInfo.MobileNonCSMStaticBatchVisibility = View.StaticMeshBatchVisibility;
- for (FLightSceneInfo* MobileDirectionalLightSceneInfo : Scene->MobileDirectionalLights)
- bAlwaysUseCSM = bAlwaysUseCSM || (!bMobileEnableMovableLightCSMShaderCulling && LightSceneProxy->IsMovable() && MobileDirectionalLightSceneInfo->ShouldRenderViewIndependentWholeSceneShadows());//
- BuildCSMVisibilityState(MobileDirectionalLightSceneInfo);// Build visibility lists of CSM receivers and non-csm receivers.
- const uint32 CSMCullingMethod = CVarsCsmShaderCullingMethod.GetValueOnRenderThread() & 0xF;//1 - Light frustum, all primitives whose bounding box is within CSM receiving distance. (default)//Frustum内的,且在CSM影响范围内的物件会受到CSM的影响
- BuildSingleCascadeShadowInfo(View, VisibleLightInfos, LightSceneInfo, SingleCascadeInfo)//内部调用了SetupWholeSceneProjection计算矩阵
- if (ViewFamily.EngineShowFlags.ShadowFrustums) DrawFrustumWireframe(&ShadowFrustumPDI, (ViewMatrix * FPerspectiveMatrix(ActualFOV, AspectRatio, 1.0f, Near, Far)).Inverse(), FColor::Emerald, 0);
- if (MobileCSMSubjectPrimitives.GetShadowSubjectPrimitives().Num() != 0 || CSMCullingMethod == 0 || CSMCullingMethod == 1)
- GetViewFrustumBounds(ViewFrustum, View.ViewMatrices.GetViewProjectionMatrix(), true);
- auto IsShadowReceiver = [&ViewFrustum, &ShadowReceiverFrustum, &PreShadowTranslation](const FVector& PrimOrigin, const FVector& PrimExtent) { return ViewFrustum.IntersectBox(PrimOrigin, PrimExtent) && ShadowReceiverFrustum.IntersectBox(PrimOrigin + PreShadowTranslation, PrimExtent); };// Common receiver test functions.// Test receiver bounding box against view+shadow frustum only//这里厉害了,可以设置多种culling方式用于计算哪些物件在CSM内
- switch (CSMCullingMethod) case 1:auto IsShadowReceiverFrustumOnly = [&IsShadowReceiver](const FVector& PrimOrigin, const FVector& PrimExtent, float PrimRadius) { return IsShadowReceiver(PrimOrigin, PrimExtent); }; bStaticCSMReceiversFound = MobileDetermineStaticMeshesCSMVisibilityState(Scene, View, ProjectedShadowInfo, IsShadowReceiverFrustumOnly); break;
- for (FScenePrimitiveOctree::TConstIterator<.SceneRenderingAllocator> PrimitiveOctreeIt(Scene->PrimitiveOctree); PrimitiveOctreeIt.HasPendingNodes(); PrimitiveOctreeIt.Advance())// Find primitives that are in a shadow frustum in the octree.在八叉树中遍历得到哪些物件被culling通过
- if (PrimitiveOctreeNode.HasChild(ChildRef))
- const FOctreeNodeContext ChildContext = PrimitiveOctreeNodeContext.GetChildContext(ChildRef);
- if (IsReceiverFunc(FVector(ChildContext.Bounds.Center), FVector(ChildContext.Bounds.Extent), ChildContext.Bounds.Extent.Size3())) PrimitiveOctreeIt.PushChild(ChildRef);
- for (FScenePrimitiveOctree::ElementConstIt NodePrimitiveIt(PrimitiveOctreeNode.GetElementIt()); NodePrimitiveIt; ++NodePrimitiveIt) // Check all the primitives in this octree node.
- bFoundReceiver = MobileDetermineStaticMeshesCSMVisibilityStateInner(Scene, View, *NodePrimitiveIt, ProjectedShadowInfo, IsReceiverFunc) || bFoundReceiver; // gather the shadows for this one primitive//这里就是逐物件的计算,1.是否在CSM范围内,2.
- const FPrimitiveViewRelevance& Relevance = View.PrimitiveViewRelevanceMap[NodePrimitiveIt.PrimitiveSceneInfo->GetIndex()];
- const FBoxSphereBounds& PrimitiveBounds = NodePrimitiveIt.Bounds;
- bool bCanReceiveDynamicShadow = (Relevance.ShadingModelMask != (1 << MSM_Unlit)) && (Relevance.bOpaque || Relevance.bMasked) && IsReceiverFunc(PrimitiveBounds.Origin, PrimitiveBounds.BoxExtent, PrimitiveBounds.SphereRadius);
- if (bCanReceiveDynamicShadow)
- bFoundCSMReceiver = EnableStaticMeshCSMVisibilityState(LightProxy->IsMovable() && LightSceneInfo.ShouldRenderViewIndependentWholeSceneShadows(), PrimitiveSceneInfo, View.MobileCSMVisibilityInfo, View);//这里还是有点复杂,似懂非懂的
- if (MobileCSMVisibilityInfo.MobilePrimitiveCSMReceiverVisibilityMap[PrimitiveSceneInfo->GetIndex()]) return false;
- MobileCSMVisibilityInfo.MobilePrimitiveCSMReceiverVisibilityMap[PrimitiveSceneInfo->GetIndex()] = true;//感觉这是在算cache,如果已经为true,则直接返回flase,直到找到新的
- if (bMovableLight || CouldStaticMeshEverReceiveCSMFromStationaryLight(View.GetFeatureLevel(), PrimitiveSceneInfo, StaticMesh))//这里用到了AllowDistanceFieldShadows以及LightMapInteraction和ShadowMapInteraction
- if (PrimitiveSceneInfo->StaticMeshes[MeshIndex].MaterialRenderProxy->GetMaterial(View.GetFeatureLevel())->GetShadingModels().IsLit())
- MobileCSMVisibilityInfo.MobileCSMStaticMeshVisibilityMap[StaticMesh.Id] = MobileCSMVisibilityInfo.MobileNonCSMStaticMeshVisibilityMap[StaticMesh.Id];// CSM enabled list
- MobileCSMVisibilityInfo.MobileNonCSMStaticMeshVisibilityMap[StaticMesh.Id] = false;// CSM excluded list
- if (StaticMesh.bRequiresPerElementVisibility)
- MobileCSMVisibilityInfo.MobileCSMStaticBatchVisibility[StaticMesh.BatchVisibilityId] = MobileCSMVisibilityInfo.MobileNonCSMStaticBatchVisibility[StaticMesh.BatchVisibilityId];// CSM enabled list
- MobileCSMVisibilityInfo.MobileNonCSMStaticBatchVisibility[StaticMesh.BatchVisibilityId] = 0;// CSM excluded list
- bFoundReceiver = true;
- return bFoundReceiver || PrimitiveSceneInfo->StaticMeshes.Num() == 0;// Dynamic primitives do not have static meshes
- return bFoundCSMReceiver;
- MobileCSMVisibilityInfo.bMobileDynamicCSMInUse = bStaticCSMReceiversFound;
- View.MobileCSMVisibilityInfo.bAlwaysUseCSM = bAlwaysUseCSM;
- bModulatedShadowsInUse = VisibleLightInfo.ShadowsToProject.Num() > 0;
- PrepareViewVisibilityLists();// TODO: only do this when CSM + static is required.
- SetupMobileBasePassAfterShadowInit(BasePassDepthStencilAccess, ViewCommandsPerView);
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchPassSetup(
Scene,
View,
EMeshPass::BasePass,
BasePassDepthStencilAccess,
MeshPassProcessor,
View.DynamicMeshElements,
&View.DynamicMeshElementsPassRelevance,
View.NumVisibleDynamicMeshElements[EMeshPass::BasePass],
ViewCommands.DynamicMeshCommandBuildRequests[EMeshPass::BasePass],
ViewCommands.NumDynamicMeshCommandBuildRequestElements[EMeshPass::BasePass],
ViewCommands.MeshCommands[EMeshPass::BasePass],
BasePassCSMMeshPassProcessor,
&ViewCommands.MeshCommands[EMeshPass::MobileBasePassCSM]);
- Scene->IndirectLightingCache.FinalizeCacheUpdates(Scene, *this, ILCTaskData);// if we kicked off ILC update via task, wait and finalize.(indirect light cache间接光缓存)最主要的差别是VLM为逐像素插值(GPU),而ILC为逐物体(CPU)。像素级别插值带来的是更加细腻的光影过度,减少动态物体运动时间接光的突然变化;另外也为照亮Volumetric fog带来可能性
- Views[ViewIndex].ViewState->UpdatePreExposure(Views[ViewIndex]);// initialize per-view uniform buffer. Pass in shadow info as necessary.
具体实现在Engine\Source\Runtime\Renderer\Private\PostProcess\PostProcessEyeAdaptation.cpp的FSceneViewState::UpdatePreExposure函数中
- Views[ViewIndex].InitRHIResources();// Initialize the view's RHI resources.
具体实现是Engine\Source\Runtime\Renderer\Private\SceneRendering.cpp的FViewInfo::SetupUniformBufferParameters函数,设置各种Uniform参数,比如MVP、灯光、天光、噪声、DF、fog、PrecomputedVolumetricLightmap、各种sample object、TAA参数、TranslucencyLightingVolume、曝光、DOF、PreIntegratedBRDF等各种参数/** Creates the view's uniform buffers given a set of view transforms. */
- CreateDirectionalLightUniformBuffers(Views[ViewIndex]);// TODO: remove when old path is removed// Create the directional light uniform buffers
具体实现是Engine\Source\Runtime\Renderer\Private\MobileBasePassRendering.cpp的SetupMobileDirectionalLightUniformParameters函数,也就是设置一些参数
- UpdateGPUScene(RHICmdList, *Scene);(Patrick:有点像是GPU Driven pipeline,但是不太确定)
- UploadDynamicPrimitiveShaderDataForView(RHICmdList, *Scene, Views[ViewIndex]);(Patrick:有点像是GPU Driven pipeline,但是不太确定)
- Extension->BeginFrame(); Extension->PrepareView(&Views[ViewIndex]);// Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush
- // update buffers used in cached mesh path// in case there are multiple views, these buffers will be updated before rendering each view
- Scene->UniformBuffers.UpdateViewUniformBuffer(View, false);// We want to wait for the extension jobs only when the view is being actually rendered for the first time
- PersistentViewUniformBufferExtensions->BeginRenderView(&View, bShouldWaitForPersistentViewUniformBufferExtensionsJobs);// Let the implementation of each extension decide whether it can cache the result for CachedView
- ViewUniformBuffer.UpdateUniformBufferImmediate(*View.CachedViewUniformShaderParameters);// ViewUniformBuffer can be cached by mesh commands, so we need to update it every time we change current view.
- InstancedViewUniformBuffer.UpdateUniformBufferImmediate(reinterpret_cast<.FInstancedViewUniformShaderParameters&>(*View.CachedViewUniformShaderParameters));// If we don't render this pass in stereo we simply update the buffer with the same view uniform parameters.// The shader will detect this and it will not attempt to apply ISR while this view is being rendered.// TODO: It's more efficient to change the shader binding to point to ViewUniformBuffer instead of updating InstancedViewUniformBuffer.
- UpdateDepthPrepassUniformBuffer(RHICmdList, View);
SetupSceneTextureUniformParameters(SceneContext, View.FeatureLevel, ESceneTextureSetupMode::None, SceneTextureParameters);设置各种RT,包括:Color、depthstencil attachment,GBuffer,SSAO,Custom Depth / Stencil,EyeAdaptation,SceneColorCopyTexture
Scene->UniformBuffers.DepthPassUniformBuffer.UpdateUniformBufferImmediate(SceneTextureParameters);
- UpdateOpaqueBasePassUniformBuffer(RHICmdList, View);
SetupMobileBasePassUniformParameters(RHICmdList, View, false, Parameters);设置雾、planarReflection、EyeAdaptation、PreIntegratedGF的参数
SetupMobileSceneTextureUniformParameters(SceneContext, View.FeatureLevel, bTranslucentPass, View.bCustomDepthStencilValid, BasePassParameters.SceneTextures);设置各种RT,包括:SceneColorTexture、SceneDepthTexture、SceneAlphaCopyTexture、CustomDepthTexture、MobileCustomStencilTexture、VirtualTextureFeedbackUAV、EyeAdaptationBuffer(Patrick:这不就和上面重复了么)
Scene->UniformBuffers.MobileOpaqueBasePassUniformBuffer.UpdateUniformBufferImmediate(Parameters);
- UpdateTranslucentBasePassUniformBuffer(RHICmdList, View);
SetupMobileBasePassUniformParameters(RHICmdList, View, true, Parameters);又来了一遍,看来每次调用UpdateUniformBufferImmediate之前都要来一遍
Scene->UniformBuffers.MobileTranslucentBasePassUniformBuffer.UpdateUniformBufferImmediate(Parameters);
- UpdateDirectionalLightUniformBuffers(RHICmdList, View);
SetupMobileDirectionalLightUniformParameters(*Scene, View, VisibleLightInfos, ChannelIdx, bDynamicShadows, Params);设置光照的参数,这个其实在前面也调用过SetupMobileDirectionalLightUniformParameters
Scene->UniformBuffers.MobileDirectionalLightUniformBuffers[ChannelIdx + 1].UpdateUniformBufferImmediate(Params);
- SetupMobileDistortionPassUniformBuffer(RHICmdList, View, DistortionPassParameters);
SetupMobileSceneTextureUniformParameters(SceneRenderTargets, View.FeatureLevel, true, View.bCustomDepthStencilValid, DistortionPassParameters.SceneTextures);这个前面也刚调用过SetupMobileSceneTextureUniformParameters
SetupDistortionParams(DistortionPassParameters.DistortionParams, View);设置一些扰动相关的参数
- Scene->UniformBuffers.MobileDistortionPassUniformBuffer.UpdateUniformBufferImmediate(DistortionPassParameters);
- UpdateSkyReflectionUniformBuffer();
SetupMobileSkyReflectionUniformParameters(Scene->SkyLight, Parameters);设置天光方面的参数
Scene->UniformBuffers.MobileSkyReflectionUniformBuffer.UpdateUniformBufferImmediate(Parameters);
- UpdatePrimitiveIndirectLightingCacheBuffers();// Now that the indirect lighting cache is updated, we can update the uniform buffers.
- OnStartRender(RHICmdList);
- FVisualizeTexturePresent::OnStartRender(Views[0]);
- CompositionGraph_OnStartFrame();在发布版本中,这里是空的
- View.ViewState->OnStartRender(View, ViewFamily);
- SetupLightPropagationVolume(View, ViewFamily);
- ConditionallyAllocateSceneSoftwareOcclusion(View.GetFeatureLevel());
- GEngine->GetPreRenderDelegate().Broadcast();
- // Global dynamic buffers need to be committed before rendering. DynamicIndexBuffer.Commit(); DynamicVertexBuffer.Commit(); DynamicReadBuffer.Commit(); RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
- if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)) RenderSkyAtmosphereLookUpTables(RHICmdList);前面都是在准备环境,这里应该是第一次真正渲染了。虽然只是绘制一个LUT。// Generate the Sky/Atmosphere look up tables
- if (bUseVirtualTexturing) FVirtualTextureSystem::Get().Update(RHICmdList, ViewFeatureLevel, Scene);
- if (bDeferredShading)//延迟管线中,先计算ClusterBaseLighting用于reflectioncapture和light
- GatherAndSortLights(SortedLightSet);
- FRDGBuilder GraphBuilder(RHICmdList);
- ComputeLightGrid(GraphBuilder, (Views[0].NumBoxReflectionCaptures + Views[0].NumSphereReflectionCaptures > 0 || GMobileUseClusteredDeferredShading != 0), SortedLightSet);
- GraphBuilder.Execute();
- if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags)) RenderSkyAtmosphereLookUpTables(RHICmdList);前面都是在准备环境,这里应该是第一次真正渲染了。虽然只是绘制一个LUT。// Generate the Sky/Atmosphere look up tables
- Scene->FXSystem->PreRender(RHICmdList, NULL, !Views[0].bIsPlanarReflection);GPU粒子相关,需要做预处理// Notify the FX system that the scene is about to be rendered.
- Scene->FXSystem->GetGPUSortManager()->OnPreRender(RHICmdList);
- RenderShadowDepthMaps(RHICmdList);这里是真正的渲染shadowmap了。
- if (bShouldRenderCustomDepth) RenderCustomDepthPass(RHICmdList);// Custom depth// bShouldRenderCustomDepth has been initialized in InitViews on mobile platform
- if (SceneContext.BeginRenderingCustomDepth(RHICmdList, bPrimitives))
RHICmdList.BeginRenderPass(RPInfo, TEXT("BeginRenderCustomDepth"));
- RHICmdList.SetViewport(ViewRect.Min.X, ViewRect.Min.Y, 0.0f, ViewRect.Max.X, ViewRect.Max.Y, 1.0f);设置viewport
- Scene->UniformBuffers.CustomDepthViewUniformBuffer.UpdateUniformBufferImmediate(*View.CachedViewUniformShaderParameters);
- Scene->UniformBuffers.InstancedCustomDepthViewUniformBuffer.UpdateUniformBufferImmediate(reinterpret_cast<.FInstancedViewUniformShaderParameters&>(*View.CachedViewUniformShaderParameters));// If we don't render this pass in stereo we simply update the buffer with the same view uniform parameters.
- PersistentViewUniformBufferExtensions->BeginRenderView(&View);
- View.ParallelMeshDrawCommandPasses[EMeshPass::CustomDepth].DispatchDraw(nullptr, RHICmdList);这里也是真正的调用DC(一直调到RHI层)。
- SceneContext.FinishRenderingCustomDepth(RHICmdList);// resolve using the current ResolveParams
- RHICmdList.EndRenderPass();
- RHICmdList.CopyToResolveTarget(MobileCustomDepth->GetRenderTargetItem().TargetableTexture, MobileCustomDepth->GetRenderTargetItem().ShaderResourceTexture, FResolveParams(ResolveRect));
- RHICmdList.CopyToResolveTarget(MobileCustomStencil->GetRenderTargetItem().TargetableTexture, MobileCustomStencil->GetRenderTargetItem().ShaderResourceTexture, FResolveParams(ResolveRect));
- if (bDeferredShading) SceneColor = RenderDeferred(RHICmdList, ViewList, SortedLightSet);//延迟管线
- FRHITexture* ColorTargets[5] = {
SceneContext.GetSceneColorSurface(),
SceneContext.GetGBufferATexture().GetReference(),
SceneContext.GetGBufferBTexture().GetReference(),
SceneContext.GetGBufferCTexture().GetReference(),
SceneContext.SceneDepthAux->GetRenderTargetItem().ShaderResourceTexture.GetReference()
};
- ERenderTargetActions GBufferAction = bRequiresMultiPass ? ERenderTargetActions::Clear_Store : ERenderTargetActions::Clear_DontStore;// Whether RHI needs to store GBuffer to system memory and do shading in separate render-pass
- EDepthStencilTargetActions DepthAction = bKeepDepthContent ? EDepthStencilTargetActions::ClearDepthStencil_StoreDepthStencil : EDepthStencilTargetActions::ClearDepthStencil_DontStoreDepthStencil;
- ERenderTargetActions ColorTargetsAction[5] = {ERenderTargetActions::Clear_Store, GBufferAction, GBufferAction, GBufferAction, ERenderTargetActions::Clear_Store};
- for (int32 Index = 0; Index < UE_ARRAY_COUNT(ColorTargets); ++Index)
{
BasePassInfo.ColorRenderTargets[Index].RenderTarget = ColorTargets[Index];
BasePassInfo.ColorRenderTargets[Index].ResolveTarget = nullptr;
BasePassInfo.ColorRenderTargets[Index].ArraySlice = -1;
BasePassInfo.ColorRenderTargets[Index].MipIndex = 0;
BasePassInfo.ColorRenderTargets[Index].Action = ColorTargetsAction[Index];
}
- BasePassInfo.DepthStencilRenderTarget.DepthStencilTarget = SceneContext.GetSceneDepthSurface();
BasePassInfo.DepthStencilRenderTarget.ResolveTarget = nullptr;
BasePassInfo.DepthStencilRenderTarget.Action = DepthAction;
BasePassInfo.DepthStencilRenderTarget.ExclusiveDepthStencil = FExclusiveDepthStencil::DepthWrite_StencilWrite;
- BasePassInfo.SubpassHint = ESubpassHint::DeferredShadingSubpass;// Mobile defferred shading subpass// Render pass has depth reading subpass(DepthReadSubpass)
- BasePassInfo.NumOcclusionQueries = ComputeNumOcclusionQueriesToBatch();// Some RHIs require a hint that occlusion queries will be used in this render pass
- BasePassInfo.bOcclusionQueries = BasePassInfo.NumOcclusionQueries != 0;
- BasePassInfo.FoveationTexture = nullptr;// Some RHIs can use a texture to control the sampling and/or shading resolution of different areas
// (@todo: This implementation is specific to fixed foveated rendering, and will be updated or replaced as a more general pathway comes online)(Patrick:这个厉害了,这是VRS吧。)
- BasePassInfo.MultiViewCount = 0;// if this renderpass should be multiview, and if so how many views are required
- RHICmdList.BeginRenderPass(BasePassInfo, TEXT("BasePassRendering"));这个API在之前见过BeginRenderPass
- RenderPrePass(RHICmdList);这里应该就是ZPrepass
UE4的Zprepass也分很多种
- DDM_MaskedOnly// Draw a depth pass to avoid overdraw in the other passes.// Mobile only does MaskedOnly DepthPass for the moment
- Scene->UniformBuffers.UpdateViewUniformBuffer(View);
- SetupPrePassView(RHICmdList, View, this);设置viewport
- View.ParallelMeshDrawCommandPasses[EMeshPass::DepthPass].DispatchDraw(nullptr, RHICmdList);这里就是真正的调用DC了(一直调到RHI层)。
- RenderMobileBasePass(RHICmdList, ViewList);嗯,这里应该就是main pass了。
- UpdateOpaqueBasePassUniformBuffer(RHICmdList, View);这个在前面也调用过了UpdateOpaqueBasePassUniformBuffer
- UpdateDirectionalLightUniformBuffers(RHICmdList, View);这个在前面也调用过了UpdateDirectionalLightUniformBuffers
- RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1);设置viewport
- View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(nullptr, RHICmdList);这里就是真正的调用DC了(一直调到RHI层)。
- RenderMobileEditorPrimitives(RHICmdList, View, DrawRenderState);// editor primitives
- RenderOcclusion(RHICmdList);
- if (!bRequiresMultiPass)
- RHICmdList.NextSubpass();// SceneColor + GBuffer write, SceneDepth is read only
- RenderDecals(RHICmdList);
- RHICmdList.NextSubpass();// SceneColor write, SceneDepth is read only
- MobileDeferredShadingPass(RHICmdList, *Scene, *ViewList[0], SortedLightSet);
- RenderTranslucency(RHICmdList, ViewList);
- if (UseVirtualTexturing(ViewFeatureLevel) && !IsHlslccShaderPlatform(View.GetShaderPlatform())) SceneContext.BindVirtualTextureFeedbackUAV(SceneColorRenderPassInfo);// TODO: required only for DX11 ?
- ViewFamily.ViewExtensions[ViewExt]->PostRenderBasePass_RenderThread(RHICmdList, Views[ViewIndex]);
- if (!bGammaSpace) PreTonemapMSAA(RHICmdList);在MSAA之前先进行Tonemapping,否则高光部分压不下来,会出现高光锯齿// Pre-tonemap before MSAA resolve (iOS only) //bOnChipPreTonemapMSAA
- FGraphicsPipelineStateInitializer GraphicsPSOInit;设置PSO的参数
- GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
- GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); TShaderMapRef<.FScreenVS> VertexShader(ShaderMap);
- GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); TShaderMapRef<.FPreTonemapMSAA_ES2> PixelShader(ShaderMap);
- GraphicsPSOInit.PrimitiveType = PT_TriangleList;
- RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
- SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);根据PSO设置GPU状态机
- RHICmdList.SetViewport(0, 0, 0.0f, TargetSize.X, TargetSize.Y, 1.0f);设置viewport
- DrawRectangle( RHICmdList, 0, 0, TargetSize.X, TargetSize.Y, 0, 0, TargetSize.X, TargetSize.Y, TargetSize, TargetSize, VertexShader, EDRF_UseTriangleOptimization);看上去是通过绘制一个全屏面片的方式进行后处理
- if (Scene->FXSystem && Views.IsValidIndex(0)) Scene->FXSystem->PostRenderOpaque( RHICmdList, Views[0].ViewUniformBuffer, nullptr, nullptr, Views[0].AllowGPUParticleUpdate() );对应前面的FXSystem->PreRender,不过看函数中的内容基本一样
- Scene->FXSystem->GetGPUSortManager()->OnPostRenderOpaque(RHICmdList);对应前面的FXSystem->GetGPUSortManager()->OnPreRender
- RHICmdList.Transition(FRHITransitionInfo(SceneColor, ERHIAccess::Unknown, ERHIAccess::SRVMask));
- RenderAmbientOcclusion(RHICmdList, SceneContext.SceneDepthZ);
- if (bDeferredShading) SceneContext.AdjustGBufferRefCount(RHICmdList, -1);// Release the original reference on the scene render targets
- if (!View.bIsMobileMultiViewDirectEnabled) CopyMobileMultiViewSceneColor(RHICmdList);
- RHICmdList.DiscardRenderTargets(true, true, 0);OpenGL并不支持这个功能
- FRHIRenderPassInfo RPInfo(ViewFamily.RenderTarget->GetRenderTargetTexture(), ERenderTargetActions::Clear_Store);// Switching from the multi-view scene color render target array to side by side scene color
- TransitionRenderPassTargets(RHICmdList, RPInfo);
- RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyMobileMultiViewColor"));这个API在之前见过BeginRenderPass
- GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
- GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); TShaderMapRef<.FScreenVS> VertexShader(ShaderMap);
- GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); TShaderMapRef<.FCopyMobileMultiViewSceneColorPS> PixelShader(ShaderMap);
- SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);根据PSO设置GPU状态机
- RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Min.X + View.ViewRect.Width(), View.ViewRect.Min.Y + View.ViewRect.Height(), 1.0f);设置viewport
- DrawRectangle( RHICmdList, 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), 0, 0, View.ViewRect.Width(), View.ViewRect.Height(), TargetSize, TargetSize, VertexShader, EDRF_UseTriangleOptimization);看上去是通过绘制一个全屏面片的方式进行后处理
- RHICmdList.EndRenderPass();这个API在之前见过EndRenderPass
- if (bUseVirtualTexturing) SubmitVirtualTextureFeedbackBuffer(RHICmdList, SceneContext.VirtualTextureFeedback, Desc);
- FRDGBuilder GraphBuilder(RHICmdList);
- FRDGTextureRef ViewFamilyTexture = TryCreateViewFamilyTexture(GraphBuilder, ViewFamily);
- MobileSceneTexturesPerView[ViewIndex] = CreateMobileSceneTextureUniformBuffer(GraphBuilder, SetupMode);
- PostProcessingInputs.ViewFamilyTexture = ViewFamilyTexture;
- PostProcessingInputs.SceneTextures = MobileSceneTexturesPerView[ViewIndex];
- AddMobilePostProcessingPasses(GraphBuilder, Views[ViewIndex], PostProcessingInputs);
- if (PassSequence.IsEnabled(EPass::Distortion))
{
PassSequence.AcceptPass(EPass::Distortion);
FMobileDistortionAccumulateInputs DistortionAccumulateInputs;
DistortionAccumulateInputs.SceneColor = SceneColor;
FMobileDistortionAccumulateOutputs DistortionAccumulateOutputs = AddMobileDistortionAccumulatePass(GraphBuilder, View, DistortionAccumulateInputs);
FMobileDistortionMergeInputs DistortionMergeInputs;
DistortionMergeInputs.SceneColor = SceneColor;
DistortionMergeInputs.DistortionAccumulate = DistortionAccumulateOutputs.DistortionAccumulate;
SceneColor = AddMobileDistortionMergePass(GraphBuilder, View, DistortionMergeInputs);
}
- AddPostProcessMaterialPass(BL_BeforeTranslucency, false);
- if (PassSequence.IsEnabled(EPass::SunMask))
{
PassSequence.AcceptPass(EPass::SunMask);
bool bUseDepthTexture = SceneColor.Texture->Desc.Format == PF_FloatR11G11B10;
FMobileSunMaskInputs SunMaskInputs;
SunMaskInputs.bUseDepthTexture = bUseDepthTexture;
SunMaskInputs.bUseDof = bUseDof;
SunMaskInputs.bUseMetalMSAAHDRDecode = bMetalMSAAHDRDecode;
SunMaskInputs.bUseSun = bUseSun;
SunMaskInputs.SceneColor = SceneColor;
SunMaskInputs.SceneTextures = Inputs.SceneTextures;
// Convert depth to {circle of confusion, sun shaft intensity}
FMobileSunMaskOutputs SunMaskOutputs = AddMobileSunMaskPass(GraphBuilder, View, SunMaskInputs);
PostProcessSunShaftAndDof = SunMaskOutputs.SunMask;
if (!bUseDepthTexture)
{
SceneColor = SunMaskOutputs.SceneColor;
}
// The scene color will be decoded after sun mask pass and output to linear color space for following passes if sun shaft enabled
// set bMetalMSAAHDRDecode to false if sun shaft enabled
bMetalMSAAHDRDecode = (bMetalMSAAHDRDecode && !bUseSun);
//@todo Ronin sunmask pass isnt clipping to image only.
}
- if (PassSequence.IsEnabled(EPass::BloomSetup)) //bUseSun || bUseMobileDof || bUseBloom || bUseBasicEyeAdaptation || bUseHistogramEyeAdaptation
{
PassSequence.AcceptPass(EPass::BloomSetup);
bool bHasEyeAdaptationPass = (bUseBasicEyeAdaptation || bUseHistogramEyeAdaptation);
FMobileBloomSetupInputs BloomSetupInputs;
BloomSetupInputs.bUseBloom = bUseBloom;
BloomSetupInputs.bUseDof = bUseMobileDof;
BloomSetupInputs.bUseEyeAdaptation = bHasEyeAdaptationPass;
BloomSetupInputs.bUseMetalMSAAHDRDecode = bMetalMSAAHDRDecode;
BloomSetupInputs.bUseSun = bUseSun;
BloomSetupInputs.SceneColor = SceneColor;
BloomSetupInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
BloomSetupOutputs = AddMobileBloomSetupPass(GraphBuilder, View, EyeAdaptationParameters, BloomSetupInputs);
if (bHasEyeAdaptationPass && View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
{
GraphBuilder.QueueTextureExtraction(BloomSetupOutputs.EyeAdaptation.Texture, &View.ViewState->PrevFrameViewInfo.MobileBloomSetup_EyeAdaptation);
}
}
- if (PassSequence.IsEnabled(EPass::DepthOfField))
{
PassSequence.AcceptPass(EPass::DepthOfField);
if (bUseMobileDof)
{
// Near dilation circle of confusion size.
// Samples at 1/16 area, writes to 1/16 area.
FMobileDofNearInputs DofNearInputs;
DofNearInputs.BloomSetup_SunShaftAndDof = BloomSetupOutputs.SunShaftAndDof;
DofNearInputs.bUseSun = bUseSun;
FMobileDofNearOutputs DofNearOutputs = AddMobileDofNearPass(GraphBuilder, View, DofNearInputs);
// DOF downsample pass.
// Samples at full resolution, writes to 1/4 area.
FMobileDofDownInputs DofDownInputs;
DofDownInputs.bUseSun = bUseSun;
DofDownInputs.DofNear = DofNearOutputs.DofNear;
DofDownInputs.SceneColor = SceneColor;
DofDownInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
FMobileDofDownOutputs DofDownOutputs = AddMobileDofDownPass(GraphBuilder, View, DofDownInputs);
// DOF blur pass.
// Samples at 1/4 area, writes to 1/4 area.
FMobileDofBlurInputs DofBlurInputs;
DofBlurInputs.DofDown = DofDownOutputs.DofDown;
DofBlurInputs.DofNear = DofNearOutputs.DofNear;
FMobileDofBlurOutputs DofBlurOutputs = AddMobileDofBlurPass(GraphBuilder, View, DofBlurInputs);
DofOutput = DofBlurOutputs.DofBlur;
if (bUseTonemapperFilm)
{
FMobileIntegrateDofInputs IntegrateDofInputs;
IntegrateDofInputs.DofBlur = DofBlurOutputs.DofBlur;
IntegrateDofInputs.SceneColor = SceneColor;
IntegrateDofInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
SceneColor = AddMobileIntegrateDofPass(GraphBuilder, View, IntegrateDofInputs);
}
}
else
{
bool bDepthOfField = IsGaussianActive(View);
if (bDepthOfField)
{
float FarSize = View.FinalPostProcessSettings.DepthOfFieldFarBlurSize;
float NearSize = View.FinalPostProcessSettings.DepthOfFieldNearBlurSize;
const float MaxSize = CVarDepthOfFieldMaxSize.GetValueOnRenderThread();
FarSize = FMath::Min(FarSize, MaxSize);
NearSize = FMath::Min(NearSize, MaxSize);
const bool bFar = FarSize >= 0.01f;
const bool bNear = NearSize >= CVarDepthOfFieldNearBlurSizeThreshold.GetValueOnRenderThread();
const bool bCombinedNearFarPass = bFar && bNear;
if (bFar || bNear)
{
// AddGaussianDofBlurPass produces a blurred image from setup or potentially from taa result.
auto AddGaussianDofBlurPass = [&GraphBuilder, &View](FScreenPassTexture& DOFSetup, bool bFarPass, float KernelSizePercent)
{
const TCHAR* BlurDebugX = bFarPass ? TEXT("FarDOFBlurX") : TEXT("NearDOFBlurX");
const TCHAR* BlurDebugY = bFarPass ? TEXT("FarDOFBlurY") : TEXT("NearDOFBlurY");
FGaussianBlurInputs GaussianBlurInputs;
GaussianBlurInputs.NameX = BlurDebugX;
GaussianBlurInputs.NameY = BlurDebugY;
GaussianBlurInputs.Filter = DOFSetup;
GaussianBlurInputs.TintColor = FLinearColor::White;
GaussianBlurInputs.CrossCenterWeight = FVector2D::ZeroVector;
GaussianBlurInputs.KernelSizePercent = KernelSizePercent;
return AddGaussianBlurPass(GraphBuilder, View, GaussianBlurInputs);
};
FMobileDofSetupInputs DofSetupInputs;
DofSetupInputs.bFarBlur = bFar;
DofSetupInputs.bNearBlur = bNear;
DofSetupInputs.SceneColor = SceneColor;
DofSetupInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
FMobileDofSetupOutputs DofSetupOutputs = AddMobileDofSetupPass(GraphBuilder, View, DofSetupInputs);
FScreenPassTexture DofFarBlur, DofNearBlur;
if (bFar)
{
DofFarBlur = AddGaussianDofBlurPass(DofSetupOutputs.DofSetupFar, true, FarSize);
}
if (bNear)
{
DofNearBlur = AddGaussianDofBlurPass(DofSetupOutputs.DofSetupNear, false, NearSize);
}
FMobileDofRecombineInputs DofRecombineInputs;
DofRecombineInputs.bFarBlur = bFar;
DofRecombineInputs.bNearBlur = bNear;
DofRecombineInputs.DofFarBlur = DofFarBlur;
DofRecombineInputs.DofNearBlur = DofNearBlur;
DofRecombineInputs.SceneColor = SceneColor;
DofRecombineInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
SceneColor = AddMobileDofRecombinePass(GraphBuilder, View, DofRecombineInputs);
}
}
}
}
- if (PassSequence.IsEnabled(EPass::Bloom)) //bUseBloom || bUseSun
{
PassSequence.AcceptPass(EPass::Bloom);
auto AddBloomDownPass = [&GraphBuilder, &View](FScreenPassTexture& BloomDownSource, float BloomDownScale)
{
FMobileBloomDownInputs BloomDownInputs;
BloomDownInputs.BloomDownScale = BloomDownScale;
BloomDownInputs.BloomDownSource = BloomDownSource;
return AddMobileBloomDownPass(GraphBuilder, View, BloomDownInputs);
};
float BloomDownScale = 0.66f * 4.0f;
FScreenPassTexture PostProcessDownsample_Bloom[4];
for (int32 i = 0; i < 4; ++i)
{
PostProcessDownsample_Bloom[i] = AddBloomDownPass(i == 0 ? BloomSetupOutputs.Bloom : PostProcessDownsample_Bloom[i - 1], BloomDownScale);
}
const FFinalPostProcessSettings& Settings = View.FinalPostProcessSettings;
auto AddBloomUpPass = [&GraphBuilder, &View](FScreenPassTexture& BloomUpSourceA, FScreenPassTexture& BloomUpSourceB, float BloomSourceScale, const FVector4& TintA, const FVector4& TintB)
{
FMobileBloomUpInputs BloomUpInputs;
BloomUpInputs.BloomUpSourceA = BloomUpSourceA;
BloomUpInputs.BloomUpSourceB = BloomUpSourceB;
BloomUpInputs.ScaleAB = FVector2D(BloomSourceScale, BloomSourceScale);
BloomUpInputs.TintA = TintA;
BloomUpInputs.TintB = TintB;
return AddMobileBloomUpPass(GraphBuilder, View, BloomUpInputs);
};
float BloomUpScale = 0.66f * 2.0f;
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom4Tint.R, Settings.Bloom4Tint.G, Settings.Bloom4Tint.B, 0.0f);
FVector4 TintB = FVector4(Settings.Bloom5Tint.R, Settings.Bloom5Tint.G, Settings.Bloom5Tint.B, 0.0f);
TintA *= Settings.BloomIntensity;
TintB *= Settings.BloomIntensity;
BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[2], PostProcessDownsample_Bloom[3], BloomUpScale, TintA, TintB);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom3Tint.R, Settings.Bloom3Tint.G, Settings.Bloom3Tint.B, 0.0f);
TintA *= Settings.BloomIntensity;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[1], BloomUpOutputs, BloomUpScale, TintA, TintB);
}
// Upsample by 2
{
FVector4 TintA = FVector4(Settings.Bloom2Tint.R, Settings.Bloom2Tint.G, Settings.Bloom2Tint.B, 0.0f);
TintA *= Settings.BloomIntensity;
// Scaling Bloom2 by extra factor to match filter area difference between PC default and mobile.
TintA *= 0.5;
FVector4 TintB = FVector4(1.0f, 1.0f, 1.0f, 0.0f);
BloomUpOutputs = AddBloomUpPass(PostProcessDownsample_Bloom[0], BloomUpOutputs, BloomUpScale, TintA, TintB);
}
}
- if (PassSequence.IsEnabled(EPass::EyeAdaptation))
{
PassSequence.AcceptPass(EPass::EyeAdaptation);
FMobileEyeAdaptationSetupInputs EyeAdaptationSetupInputs;
EyeAdaptationSetupInputs.bUseBasicEyeAdaptation = bUseBasicEyeAdaptation;
EyeAdaptationSetupInputs.bUseHistogramEyeAdaptation = bUseHistogramEyeAdaptation;
EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation = FScreenPassTexture(TryRegisterExternalTexture(GraphBuilder, View.PrevViewInfo.MobileBloomSetup_EyeAdaptation));
if (!EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation.IsValid())
{
EyeAdaptationSetupInputs.BloomSetup_EyeAdaptation = BloomSetupOutputs.EyeAdaptation;
}
FMobileEyeAdaptationSetupOutputs EyeAdaptationSetupOutputs = AddMobileEyeAdaptationSetupPass(GraphBuilder, View, EyeAdaptationParameters, EyeAdaptationSetupInputs);
FMobileEyeAdaptationInputs EyeAdaptationInputs;
EyeAdaptationInputs.bUseBasicEyeAdaptation = bUseBasicEyeAdaptation;
EyeAdaptationInputs.bUseHistogramEyeAdaptation = bUseHistogramEyeAdaptation;
EyeAdaptationInputs.EyeAdaptationSetupSRV = EyeAdaptationSetupOutputs.EyeAdaptationSetupSRV;
AddMobileEyeAdaptationPass(GraphBuilder, View, EyeAdaptationParameters, EyeAdaptationInputs);
}
- if (PassSequence.IsEnabled(EPass::SunMerge))
{
PassSequence.AcceptPass(EPass::SunMerge);
FScreenPassTexture SunBlurOutputs;
if (bUseSun)
{
FMobileSunAlphaInputs SunAlphaInputs;
SunAlphaInputs.BloomSetup_SunShaftAndDof = BloomSetupOutputs.SunShaftAndDof;
SunAlphaInputs.bUseMobileDof = bUseMobileDof;
FScreenPassTexture SunAlphaOutputs = AddMobileSunAlphaPass(GraphBuilder, View, SunAlphaInputs);
FMobileSunBlurInputs SunBlurInputs;
SunBlurInputs.SunAlpha = SunAlphaOutputs;
SunBlurOutputs = AddMobileSunBlurPass(GraphBuilder, View, SunBlurInputs);
}
FMobileSunMergeInputs SunMergeInputs;
SunMergeInputs.BloomSetup_Bloom = BloomSetupOutputs.Bloom;
SunMergeInputs.BloomUp = BloomUpOutputs;
SunMergeInputs.SunBlur = SunBlurOutputs;
SunMergeInputs.bUseBloom = bUseBloom;
SunMergeInputs.bUseSun = bUseSun;
SunMergeInputs.bUseAa = bUseAa;
BloomOutput = AddMobileSunMergePass(GraphBuilder, View, SunMergeInputs);
if (bUseAa && View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
{
GraphBuilder.QueueTextureExtraction(BloomOutput.Texture, &View.ViewState->PrevFrameViewInfo.MobileAaBloomSunVignette);
}
// Mobile temporal AA requires a composite of two of these frames.
if (bUseAa)
{
FMobileSunAvgInputs SunAvgInputs;
SunAvgInputs.SunMerge = BloomOutput;
SunAvgInputs.LastFrameSunMerge = FScreenPassTexture(TryRegisterExternalTexture(GraphBuilder, View.PrevViewInfo.MobileAaBloomSunVignette));
if (!SunAvgInputs.LastFrameSunMerge.IsValid())
{
SunAvgInputs.LastFrameSunMerge = BloomOutput;
}
BloomOutput = AddMobileSunAvgPass(GraphBuilder, View, SunAvgInputs);
}
}
- if (PassSequence.IsEnabled(EPass::SeparateTranslucency))
{
PassSequence.AcceptPass(EPass::SeparateTranslucency);
FMobileSeparateTranslucencyInputs SeparateTranslucencyInputs;
SeparateTranslucencyInputs.SceneColor = SceneColor;
SeparateTranslucencyInputs.SceneDepth = SceneDepth;
AddMobileSeparateTranslucencyPass(GraphBuilder, View, SeparateTranslucencyInputs);
}
- AddPostProcessMaterialPass(BL_BeforeTonemapping, false);
- if (PassSequence.IsEnabled(EPass::Tonemap))
{
bool bHDRTonemapperOutput = false;
if (!BloomOutput.IsValid())
{
BloomOutput = BlackAlphaOneDummy;
}
if (bUseTonemapperFilm)
{
bool bDoGammaOnly = false;
FRDGTextureRef ColorGradingTexture = nullptr;
if (IStereoRendering::IsAPrimaryView(View))
{
ColorGradingTexture = AddCombineLUTPass(GraphBuilder, View);
}
// We can re-use the color grading texture from the primary view.
else if (View.GetTonemappingLUT())
{
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, View.GetTonemappingLUT());
}
else
{
const FViewInfo* PrimaryView = static_cast(View.Family->Views[0]);
ColorGradingTexture = TryRegisterExternalTexture(GraphBuilder, PrimaryView->GetTonemappingLUT());
}
FTonemapInputs TonemapperInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, TonemapperInputs.OverrideOutput);
// This is the view family render target.
if (TonemapperInputs.OverrideOutput.Texture)
{
FIntRect OutputViewRect;
if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::RawOutput)
{
OutputViewRect = View.ViewRect;
}
else
{
OutputViewRect = View.UnscaledViewRect;
}
ERenderTargetLoadAction OutputLoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
TonemapperInputs.OverrideOutput.ViewRect = OutputViewRect;
TonemapperInputs.OverrideOutput.LoadAction = OutputLoadAction;
}
TonemapperInputs.SceneColor = SceneColor;
TonemapperInputs.Bloom = BloomOutput;
TonemapperInputs.EyeAdaptationTexture = nullptr;
TonemapperInputs.ColorGradingTexture = ColorGradingTexture;
TonemapperInputs.bWriteAlphaChannel = View.AntiAliasingMethod == AAM_FXAA || IsPostProcessingWithAlphaChannelSupported() || bUseMobileDof;
TonemapperInputs.bFlipYAxis = RHINeedsToSwitchVerticalAxis(View.GetShaderPlatform()) && !PassSequence.IsEnabled(EPass::PostProcessMaterialAfterTonemapping);
TonemapperInputs.bOutputInHDR = bHDRTonemapperOutput;
TonemapperInputs.bGammaOnly = bDoGammaOnly;
TonemapperInputs.bMetalMSAAHDRDecode = bMetalMSAAHDRDecode;
TonemapperInputs.EyeAdaptationBuffer = bUseEyeAdaptation && View.GetLastEyeAdaptationBuffer(GraphBuilder.RHICmdList) ? View.GetLastEyeAdaptationBuffer(GraphBuilder.RHICmdList)->SRV : nullptr;
SceneColor = AddTonemapPass(GraphBuilder, View, TonemapperInputs);
}
else
{
FMobileTonemapperInputs TonemapperInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, TonemapperInputs.OverrideOutput);
if (TonemapperInputs.OverrideOutput.Texture)
{
FIntRect OutputViewRect;
if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::RawOutput)
{
OutputViewRect = View.ViewRect;
}
else
{
OutputViewRect = View.UnscaledViewRect;
}
ERenderTargetLoadAction OutputLoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
TonemapperInputs.OverrideOutput.ViewRect = OutputViewRect;
TonemapperInputs.OverrideOutput.LoadAction = OutputLoadAction;
}
TonemapperInputs.bFlipYAxis = RHINeedsToSwitchVerticalAxis(View.GetShaderPlatform()) && !PassSequence.IsEnabled(EPass::PostProcessMaterialAfterTonemapping);
TonemapperInputs.bMetalMSAAHDRDecode = bMetalMSAAHDRDecode;
TonemapperInputs.bOutputInHDR = bHDRTonemapperOutput;
TonemapperInputs.bSRGBAwareTarget = bSRGBAwareTarget;
TonemapperInputs.bUseEyeAdaptation = bUseEyeAdaptation;
TonemapperInputs.SceneColor = SceneColor;
TonemapperInputs.BloomOutput = BloomOutput;
TonemapperInputs.DofOutput = DofOutput;
TonemapperInputs.SunShaftAndDof = PostProcessSunShaftAndDof;
TonemapperInputs.EyeAdaptationBuffer = bUseEyeAdaptation && View.GetLastEyeAdaptationBuffer(GraphBuilder.RHICmdList) ? View.GetLastEyeAdaptationBuffer(GraphBuilder.RHICmdList)->SRV : nullptr;
SceneColor = AddMobileTonemapperPass(GraphBuilder, View, TonemapperInputs);
}
//The output color should been decoded to linear space after tone mapper apparently
bMetalMSAAHDRDecode = false;
}
- AddPostProcessMaterialPass(BL_AfterTonemapping, true);
- if (PassSequence.IsEnabled(EPass::TAA))
{
if (View.ViewState && !View.bStatePrevViewInfoIsReadOnly)
{
GraphBuilder.QueueTextureExtraction(SceneColor.Texture, &View.ViewState->PrevFrameViewInfo.MobileAaColor);
}
FMobileTAAInputs TAAInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::TAA, TAAInputs.OverrideOutput);
TAAInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
TAAInputs.SceneColor = SceneColor;
TAAInputs.LastFrameSceneColor = FScreenPassTexture(TryRegisterExternalTexture(GraphBuilder, View.PrevViewInfo.MobileAaColor));
if (!TAAInputs.LastFrameSceneColor.IsValid())
{
TAAInputs.LastFrameSceneColor = SceneColor;
}
SceneColor = AddMobileTAAPass(GraphBuilder, View, TAAInputs);
}
- if (PassSequence.IsEnabled(EPass::HighResolutionScreenshotMask))
{
FHighResolutionScreenshotMaskInputs HighResolutionScreenshotMaskInputs;
HighResolutionScreenshotMaskInputs.SceneColor = SceneColor;
PassSequence.AcceptOverrideIfLastPass(EPass::Tonemap, HighResolutionScreenshotMaskInputs.OverrideOutput);
HighResolutionScreenshotMaskInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
SceneColor = AddHighResolutionScreenshotMaskPass(GraphBuilder, View, HighResolutionScreenshotMaskInputs);
}
- if (PassSequence.IsEnabled(EPass::PrimaryUpscale) || (bShouldPrimaryUpscale && !PassSequence.IsLastPass(EPass::Tonemap)))
{
FUpscaleInputs PassInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::PrimaryUpscale, PassInputs.OverrideOutput);
PassInputs.Method = EUpscaleMethod::Bilinear;
PassInputs.Stage = EUpscaleStage::PrimaryToOutput;
PassInputs.SceneColor = SceneColor;
PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
SceneColor = AddUpscalePass(GraphBuilder, View, PassInputs);
}
- if (PassSequence.IsEnabled(EPass::HMDDistortion))
{
FHMDDistortionInputs PassInputs;
PassSequence.AcceptOverrideIfLastPass(EPass::HMDDistortion, PassInputs.OverrideOutput);
PassInputs.SceneColor = SceneColor;
PassInputs.OverrideOutput.LoadAction = View.IsFirstInFamily() ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
SceneColor = AddHMDDistortionPass(GraphBuilder, View, PassInputs);
}
- 没有Vignette
- GEngine->GetPostRenderDelegate().Broadcast();
- RenderFinish(GraphBuilder, ViewFamilyTexture);
- GraphBuilder.Execute();
- FDeferredShadingSceneRenderer::Render
- SceneRenderer->ViewFamily.DisplayInternalsData.Setup(World);发布版本中这里基本是空的// for r.DisplayInternals (allows for easy passing down data from main to render thread)
- RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
- FDeferredUpdateResource::UpdateResources(RHICmdList);这个API前面也提到了FDeferredUpdateResource::UpdateResources// update any resources that needed a deferred update
- SceneRenderer->Render(RHICmdList);这里就是前面刚看了一遍的真正的渲染管线SceneRenderer->Render
- SceneRenderer->Scene->DistanceFieldSceneData.PrimitiveModifiedBounds[SceneRenderer->Scene->DistanceFieldSceneData.PrimitiveModifiedBounds].Reset();// Only reset per-frame scene state once all views have processed their frame, including those in planar reflections
- PersistentViewUniformBufferExtensions->EndFrame();// Immediately issue EndFrame() for all extensions in case any of the outstanding tasks they issued getting out of this frame
- GRenderTargetPool.SetEventRecordingActive(false);// to not have event recording for some time during rendering (e.g. thumbnail rendering)
- FlushPendingDeleteRHIResources_RenderThread();//Helper function performing actual work in render thread.
- GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::EndDynamicResolutionRendering);// Beyond this point, only UI rendering independent from dynamc resolution.(Patrick:动态分辨率,不知道和Unity的动态分辨率一样么。)
- // Clear areas of the rendertarget (backbuffer) that aren't drawn over by the views.(Patrick:将backbuffer没有被绘制到的地方绘制成黑色,这个有意思)
- if (MyWorld->LineBatcher != nullptr) MyWorld->LineBatcher->Flush();// Remove temporary debug lines.
- if (MyWorld->ForegroundLineBatcher != nullptr) MyWorld->ForegroundLineBatcher->Flush();
- if (MyWorld->FXSystem) MyWorld->FXSystem->DrawDebug(SceneCanvas);// Draw FX debug information.
- if (FSlateApplication::Get().GetPlatformApplication()->IsAllowedToRender())// Render the UI.
- FVector CanvasOrigin(FMath::TruncToFloat(View->UnscaledViewRect.Min.X), FMath::TruncToInt(View->UnscaledViewRect.Min.Y), 0.f);
- CanvasObject->Init(View->UnscaledViewRect.Width(), View->UnscaledViewRect.Height(), View, SceneCanvas);// rendering to directly to viewport target
- SceneCanvas->PushAbsoluteTransform(FTranslationMatrix(CanvasOrigin));// Set the canvas transform for the player's view rectangle.
- CanvasObject->ApplySafeZoneTransform();
- if( PlayerController->MyHUD )这里是渲染HUD// Render the player's HUD.
PlayerController->MyHUD->SetCanvas(CanvasObject, DebugCanvasObject); PlayerController->MyHUD->PostRender();
- CanvasObject->PopSafeZoneTransform();
- SceneCanvas->PopTransform();
- SceneCanvas->Flush_GameThread();//ensure canvas has been flushed before rendering UI
- DrawnDelegate.Broadcast();
- PostRender(DebugCanvasObject);// Allow the viewport to render additional stuff
- MyWorld->GetPlayerControllerIterator()->GetPlayerViewPoint(PlayerCameraLocation, PlayerCameraRotation);// Grab the player camera location and orientation so we can pass that along to the stats drawing code.
- EndDrawDelegate.Broadcast();
- Canvas.Flush_GameThread();
- UGameViewportClient::OnViewportRendered().Broadcast(this);
- ViewportClient->ProcessScreenShots(this);
- SetRequiresVsync(bLockToVsync);// Slate doesn't present immediately. Tag the viewport as requiring vsync so that it happens.
- EnqueueEndRenderFrame(bLockToVsync, bShouldPresent);
- World->GetPlayerControllerIterator()->PlayerCameraManager->bGameCameraCutThisFrame = false;// Reset the camera cut flags if we are in a viewport that has a world
- // countdown the present delay, and then stop the movie at the end// this doesn't need to be on rendering thread as long as we have a long enough delay (2 or 3 frames), because// the rendering thread will never be more than one frame behind
- if(GCaptureCompositionNextFrame) GRenderingThreadSuspension.Reset(); GCaptureCompositionNextFrame = false;
- GetRendererModule().PostRenderAllViewports();// Some tasks can only be done once we finish all scenes/viewports
++GFrameNumber;// Increment FrameNumber before render the scene. Wrapping around is no problem.// This is the only spot we change GFrameNumber, other places can only read.
- IStreamingManager::Get().Tick( DeltaSeconds );// Update resource streaming after viewports have had a chance to update view information. Normal update.
- GEngine->GetAudioDeviceManager()->UpdateActiveAudioDevices(bIsAnyNonPreviewWorldUnpaused);// Update Audio. This needs to occur after rendering as the rendering code updates the listener position.
- GRenderingRealtimeClock.Tick(DeltaSeconds);看上去只是在更新时间// rendering thread commands// Tick the GRenderingRealtimeClock, unless it's paused
CurrentDeltaTime = DeltaTime; CurrentTime += DeltaTime;//Updates the timer.
- GRenderTargetPool.TickPoolElements();看上去是在整理RT
- FRDGBuilder::TickPoolElements();
具体实现见Engine\Source\Runtime\RenderCore\Private\RenderGraphResourcePool.cpp的FRenderGraphResourcePool::TickPoolElements函数
- UUnrealEdEngine::Tick
Super::Tick(DeltaSeconds, bIdleMode);
Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp
- CleanupGameViewport();// Clean up the game viewports that have been closed.
- bool bWasNonRealtimeViewportDraw = UpdateSingleViewportClient(GCurrentLevelEditingViewportClient, bAllowNonRealtimeViewports, bUpdateLinkedOrthoViewports);
GShaderCompilingManager
GShaderCompilingManager->ProcessAsyncResults(true, false);
// Process any asynchronous shader compile results that are ready, limit execution time
GDistanceFieldAsyncQueue
GDistanceFieldAsyncQueue->ProcessAsyncTasks();
RHITick
RHITick( FApp::GetDeltaTime() ); // Update RHI.// tick render hardware interface
这里也要根据不同平台走不同分支,Engine\Source\Runtime\OpenGLDrv\Private\OpenGLViewport.cpp中FOpenGLDynamicRHI::RHITick是空的,Engine\Source\Runtime\Apple\MetalRHI\Private\MetalViewport.cpp中也基本上是空的,Engine\Source\Runtime\VulkanRHI\Private\VulkanViewport.cpp中有不少内容。
EmitDynamicResolutionEvent
GEngine->EmitDynamicResolutionEvent(EDynamicResolutionStateEvent::EndFrame);// We emit dynamic resolution's end frame right before RHI's. GEngine is going to ignore it if no BeginFrame was done.
EndFrameRenderThread
- RHICmdList.EndFrame();
GetContext().RHIEndFrame();这里会根据不同平台走不同分支,我们关注Engine\Source\Runtime\OpenGLDrv\Private\OpenGLDevice.cpp FOpenGLDynamicRHI::RHIEndFrame
- GPUProfilingData.EndFrame
GDynamicRHI->RHIAdvanceFrameFence();
虽然并非全部原创,但还是希望转载请注明出处:电子设备中的画家|王烁 于 2020 年 9 月 8 日发表,原文链接(http://geekfaner.com/ue4/blog9_sourcecode3.html)