温(mian)馨(ze)提(sheng)示(ming):
- 此文是个人学习过程记录,内容质量差
- 今天刚好学习UE一个月,本文是作为前两篇的笔记补全计划^ ^
- 本来一开始只是看看PSO背后的代码逻辑,没想到本来一两篇笔记就能搞好的,我写了好几篇
- 经过对PSO预编译流程的梳理,从一开始不知道怎么读代码到能捋清一个模块确实收获很多,但看到源码后也深感自己的不足,自己远没有能总结出像样的笔记的能力,所以暂时以后也不更新了^^
- 立个flag,我要当图形学和游戏引擎糕手!明年九月我要去拯救世界!
初始化PSO
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)启动屏幕的预处理的前处理
void FShaderPipelineCache::Initialize(EShaderPlatform Platform)在这里初始化ShaderPipelineCache并且根据平台创建FShaderPipelineCache
void FPipelineFileCache::Initialize(uint32 InGameVersion)在这里初始化PipelineFileCache
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)启动屏幕的预处理的后处理
bool FShaderPipelineCache::OpenPipelineFileCache(EShaderPlatform Platform)在这里面调用了一图流靠近右上角的open函数
bool FShaderPipelineCache::Open(FString const& Name, EShaderPlatform Platform)除了调用FPipelineFileCache::OpenPipelineFileCache以外,还创建了LocalPreFetchedTasks来为后续做准备
bool FPipelineFileCache::OpenPipelineFileCache(FString const& Name, EShaderPlatform Platform, FGuid& OutGameFileGuid)就是在这里面创建了TOC
对编译任务做准备
void FShaderPipelineCache::Tick( float DeltaTime )这一步是每帧都在做的,对准备编译任务来说,核心部分是以下代码:
if (ReadyForNextBatch() && (OrderedCompileTasks.Num() || FetchTasks.Num()))
{
uint32 Num = 0;
if (BatchSize > static_cast<uint32>(FetchTasks.Num()))
{
Num = BatchSize - FetchTasks.Num();
}
Num = FMath::Min(Num, (uint32)OrderedCompileTasks.Num());
if (FetchTasks.Num() < (int32)Num)
{
TDoubleLinkedList<FPipelineCacheFileFormatPSORead*> NewBatch;
Num -= FetchTasks.Num();
for (auto Iterator = OrderedCompileTasks.CreateIterator(); Iterator && Num > 0; ++Iterator)
{
bool bHasShaders = true;
for (FSHAHash const& Hash : Iterator->Shaders)
{
bHasShaders &= FShaderCodeLibrary::ContainsShaderCode(Hash);
}
if (bHasShaders)
{
FPipelineCacheFileFormatPSORead* Entry = new FPipelineCacheFileFormatPSORead;
Entry->Hash = Iterator->Hash;
Entry->Ar = new FShaderPipelineCacheArchive;
// Add to both new batch and fetch lists
NewBatch.AddTail(Entry);
FetchTasks.AddTail(Entry);
Iterator.RemoveCurrent();
FPlatformAtomics::InterlockedIncrement(&TotalActiveTasks);
FPlatformAtomics::InterlockedDecrement(&TotalWaitingTasks);
--Num;
}
}
FPipelineFileCache::FetchPSODescriptors(NewBatch);
}
if (static_cast<uint32>(FetchTasks.Num()) > BatchSize)
{
UE_LOG(LogRHI, Warning, TEXT("FShaderPipelineCache: Attempting to pre-compile more jobs (%d) than the batch size (%d)"), FetchTasks.Num(), BatchSize);
}
PreparePipelineBatch(FetchTasks);
}
主要干了四个事情:判断和添加读取的数量,预取ShaderCodeLibrary,取得相应PSO的描述符,准备管线Batch
void FPipelineFileCache::FetchPSODescriptors(TDoubleLinkedList<FPipelineCacheFileFormatPSORead*>& Batch) 从这里获取之前准备好的TOC的数据
void FShaderPipelineCache::PreparePipelineBatch(TDoubleLinkedList<FPipelineCacheFileFormatPSORead*>& PipelineBatch) 在这里面通过FShaderCodeLibrary::PreloadShader函数来预加载PSO的shader,如果加载成功就把异步读取的任务加入到ReadTasks中
预编译PSO
void FShaderPipelineCache::Tick( float DeltaTime ),对这一步来说,核心是以下代码:
if (ReadyForPrecompile())
{
SCOPE_SECONDS_ACCUMULATOR(STAT_PreCompileTotalTime);
SCOPE_CYCLE_COUNTER(STAT_PreCompileTime);
uint32 Start = FPlatformTime::Cycles();
PrecompilePipelineBatch();
uint32 End = FPlatformTime::Cycles();
if (BatchTime > 0.0f)
{
float ElapsedMs = FPlatformTime::ToMilliseconds(End - Start);
if (ElapsedMs < BatchTime)
{
BatchSize++;
}
else if (ElapsedMs > BatchTime)
{
if (BatchSize > 1)
{
BatchSize--;
}
else
{
UE_LOG(LogRHI, Warning, TEXT("FShaderPipelineCache: Cannot reduce BatchSize below 1 to meet target of %f ms, elapsed time was %f ms)"), BatchTime, ElapsedMs);
}
}
}
}
主要干了两个事情,第一个是调用PrecompilePipelineBatch,第二个是动态调节BatchSize的大小
void FShaderPipelineCache::PrecompilePipelineBatch() 这个函数是判断了CompileTasks和BatchSize的最小值后调用可以用上的最多的CompileTasks来通过Precompile函数进行预编译
bool FShaderPipelineCache::Precompile(FRHICommandListImmediate& RHICmdList, EShaderPlatform Platform, FPipelineCacheFileFormatPSO const& PSO) 这里就是进行了预编译,核心代码如下:
if(FPipelineCacheFileFormatPSO::DescriptorType::Graphics == PSO.Type)
{
FGraphicsPipelineStateInitializer GraphicsInitializer;
FRHIVertexDeclaration* VertexDesc = PipelineStateCache::GetOrCreateVertexDeclaration(PSO.GraphicsDesc.VertexDescriptor);
GraphicsInitializer.BoundShaderState.VertexDeclarationRHI = VertexDesc;
FVertexShaderRHIRef VertexShader;
if (PSO.GraphicsDesc.VertexShader != FSHAHash())
{
VertexShader = FShaderCodeLibrary::CreateVertexShader(Platform, PSO.GraphicsDesc.VertexShader);
GraphicsInitializer.BoundShaderState.VertexShaderRHI = VertexShader;
}
#if PLATFORM_SUPPORTS_TESSELLATION_SHADERS
FHullShaderRHIRef HullShader;
if (PSO.GraphicsDesc.HullShader != FSHAHash())
{
HullShader = FShaderCodeLibrary::CreateHullShader(Platform, PSO.GraphicsDesc.HullShader);
GraphicsInitializer.BoundShaderState.HullShaderRHI = HullShader;
}
FDomainShaderRHIRef DomainShader;
if (PSO.GraphicsDesc.DomainShader != FSHAHash())
{
DomainShader = FShaderCodeLibrary::CreateDomainShader(Platform, PSO.GraphicsDesc.DomainShader);
GraphicsInitializer.BoundShaderState.DomainShaderRHI = DomainShader;
}
#endif
FPixelShaderRHIRef FragmentShader;
if (PSO.GraphicsDesc.FragmentShader != FSHAHash())
{
FragmentShader = FShaderCodeLibrary::CreatePixelShader(Platform, PSO.GraphicsDesc.FragmentShader);
GraphicsInitializer.BoundShaderState.PixelShaderRHI = FragmentShader;
}
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
FGeometryShaderRHIRef GeometryShader;
if (PSO.GraphicsDesc.GeometryShader != FSHAHash())
{
GeometryShader = FShaderCodeLibrary::CreateGeometryShader(Platform, PSO.GraphicsDesc.GeometryShader);
GraphicsInitializer.BoundShaderState.GeometryShaderRHI = GeometryShader;
}
#endif
auto BlendState = GetOrCreateBlendState(PSO.GraphicsDesc.BlendState);
GraphicsInitializer.BlendState = BlendState;
auto RasterState = GetOrCreateRasterizerState(PSO.GraphicsDesc.RasterizerState);
GraphicsInitializer.RasterizerState = RasterState;
auto DepthState = GetOrCreateDepthStencilState(PSO.GraphicsDesc.DepthStencilState);
GraphicsInitializer.DepthStencilState = DepthState;
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; ++i)
{
GraphicsInitializer.RenderTargetFormats[i] = PSO.GraphicsDesc.RenderTargetFormats[i];
GraphicsInitializer.RenderTargetFlags[i] = PSO.GraphicsDesc.RenderTargetFlags[i];
}
GraphicsInitializer.RenderTargetsEnabled = PSO.GraphicsDesc.RenderTargetsActive;
GraphicsInitializer.NumSamples = PSO.GraphicsDesc.MSAASamples;
GraphicsInitializer.SubpassHint = (ESubpassHint)PSO.GraphicsDesc.SubpassHint;
GraphicsInitializer.SubpassIndex = PSO.GraphicsDesc.SubpassIndex;
GraphicsInitializer.DepthStencilTargetFormat = PSO.GraphicsDesc.DepthStencilFormat;
GraphicsInitializer.DepthStencilTargetFlag = PSO.GraphicsDesc.DepthStencilFlags;
GraphicsInitializer.DepthTargetLoadAction = PSO.GraphicsDesc.DepthLoad;
GraphicsInitializer.StencilTargetLoadAction = PSO.GraphicsDesc.StencilLoad;
GraphicsInitializer.DepthTargetStoreAction = PSO.GraphicsDesc.DepthStore;
GraphicsInitializer.StencilTargetStoreAction = PSO.GraphicsDesc.StencilStore;
GraphicsInitializer.PrimitiveType = PSO.GraphicsDesc.PrimitiveType;
GraphicsInitializer.bFromPSOFileCache = true;
// This indicates we do not want a fatal error if this compilation fails
// (ie, if this entry in the file cache is bad)
GraphicsInitializer.bFromPSOFileCache = 1;
// Use SetGraphicsPipelineState to call down into PipelineStateCache and also handle the fallback case used by OpenGL.
SetGraphicsPipelineState(RHICmdList, GraphicsInitializer, EApplyRendertargetOption::DoNothing, false);
bOk = true;
}
这里先是创建了FGraphicsPipelineStateInitializer GraphicsInitializer,然后分别设置其BoundShaderState、BlendState、RasterizerState、DepthStencilState等等很多东西,不过我之前看的是OpenGL的,由于OpenGL没有PSO的概念,虽然PSO也收集了,但是只会用到BoundShaderState。收集完后会调用SetGraphicsPipelineState函数
void SetGraphicsPipelineState(FRHICommandList& RHICmdList, const FGraphicsPipelineStateInitializer& Initializer, EApplyRendertargetOption ApplyFlags, bool bApplyAdditionalState) 这个函数主要干两个事情,第一个是调用GetAndOrCreateGraphicsPipelineState函数来创建真正的PSO,然后调用SetGraphicsPipelineState设置管线状态
FGraphicsPipelineState* PipelineStateCache::GetAndOrCreateGraphicsPipelineState(FRHICommandList& RHICmdList, const FGraphicsPipelineStateInitializer& OriginalInitializer, EApplyRendertargetOption ApplyFlags) 从这个函数里面去取得或者创建PSO,重点部分如下:
if (DoAsyncCompile)
{
OutCachedState->CompletionEvent = TGraphTask<FCompilePipelineStateTask>::CreateTask().ConstructAndDispatchWhenReady(OutCachedState, *Initializer);
RHICmdList.AddDispatchPrerequisite(OutCachedState->CompletionEvent);
}
else
{
OutCachedState->RHIPipeline = RHICreateGraphicsPipelineState(*Initializer);
if(!OutCachedState->RHIPipeline)
{
HandlePipelineCreationFailure(*Initializer);
}
}
如果使用异步编译的话,启动一个异步任务来编译渲染管线状态,并确保在这个状态编译完成之前,任何依赖于它的渲染命令都不会被执行。不使用的话就通过RHICreateGraphicsPipelineState来执行,这就和之前的笔记PSO函数调用对接上了^^
FORCEINLINE_DEBUGGABLE void SetGraphicsPipelineState(class FGraphicsPipelineState* GraphicsPipelineState, const FBoundShaderStateInput& ShaderInput, bool bApplyAdditionalState)这个函数首先通过ExecuteSetGraphicsPipelineState来获得RHIGraphicsPipelineState,接着通过RHISetGraphicsPipelineState来调用RHISetGraphicsPipelineState,紧接着里面会调用RHISetBoundShaderState,这也和之前笔记的PSO函数调用对接上了^^
mesh使用PSO的流程
首先要了解meshdrawingpipeline
MeshDrawingPipeline
网格体渲染从"FPrimitiveSceneProxy"开始,这是游戏线程的"UPrimitiveComponent"渲染线程表示。"FPrimitiveSceneProxy"负责通过对"GetDynamicMeshElements"和"DrawStaticElements"的回调将FMeshBatch提交给渲染器。
"FMeshBatch"包含了通道确定最终着色器绑定和渲染状态所需的所有内容。
下一步是将"FMeshBatch"转换为一个特定于网格体通道的"FMeshDrawCommand"。"FMeshDrawCommand"是"FMeshBatch"和RHI之间的接口。它是一个完全无状态的绘制描述,存储了RHI需要知道的关于网格体绘制的所有信息。"FMeshDrawCommand"是由一个特定于网格体通道的"FMeshPassProcessor"根据"FMeshBatch"创建的。
最后,"SubmitMeshDrawCommands"用于将"FMeshDrawCommand"转换为RHICommandList上设置的一系列RHI命令。
我们的重点是MeshDrawingPipeline中是怎么和PSO扯上关系的,所以从BuildMeshDrawCommands这步开始
BuildMeshDrawCommands
因为每个"FMeshPassProcessor"必须通过"BuildMeshDrawCommands()“来调用通道着色器的"GetShaderBindings()”,所以通过"ShaderElementData"参数到"BuildMeshDrawCommands()"来将任意数据从"FMeshPassProcessor"传递到"GetShaderBindings()"调用。
template<typename PassShadersType, typename ShaderElementDataType>
void FMeshPassProcessor::BuildMeshDrawCommands(
const FMeshBatch& RESTRICT MeshBatch,
uint64 BatchElementMask,
const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy,
const FMaterialRenderProxy& RESTRICT MaterialRenderProxy,
const FMaterial& RESTRICT MaterialResource,
const FMeshPassProcessorRenderState& RESTRICT DrawRenderState,
PassShadersType PassShaders,
ERasterizerFillMode MeshFillMode,
ERasterizerCullMode MeshCullMode,
FMeshDrawCommandSortKey SortKey,
EMeshPassFeatures MeshPassFeatures,
const ShaderElementDataType& ShaderElementData)
{
const FVertexFactory* RESTRICT VertexFactory = MeshBatch.VertexFactory;
const FPrimitiveSceneInfo* RESTRICT PrimitiveSceneInfo = PrimitiveSceneProxy ? PrimitiveSceneProxy->GetPrimitiveSceneInfo() : nullptr;
FMeshDrawCommand SharedMeshDrawCommand;
SharedMeshDrawCommand.SetStencilRef(DrawRenderState.GetStencilRef());
FGraphicsMinimalPipelineStateInitializer PipelineState;
PipelineState.PrimitiveType = (EPrimitiveType)MeshBatch.Type;
PipelineState.ImmutableSamplerState = MaterialRenderProxy.ImmutableSamplerState;
EVertexInputStreamType InputStreamType = EVertexInputStreamType::Default;
if ((MeshPassFeatures & EMeshPassFeatures::PositionOnly) != EMeshPassFeatures::Default) InputStreamType = EVertexInputStreamType::PositionOnly;
if ((MeshPassFeatures & EMeshPassFeatures::PositionAndNormalOnly) != EMeshPassFeatures::Default) InputStreamType = EVertexInputStreamType::PositionAndNormalOnly;
check(VertexFactory && VertexFactory->IsInitialized());
FRHIVertexDeclaration* VertexDeclaration = VertexFactory->GetDeclaration(InputStreamType);
check(!VertexFactory->NeedsDeclaration() || VertexDeclaration);
SharedMeshDrawCommand.SetShaders(VertexDeclaration, PassShaders.GetUntypedShaders(), PipelineState);
PipelineState.RasterizerState = GetStaticRasterizerState<true>(MeshFillMode, MeshCullMode);
check(DrawRenderState.GetDepthStencilState());
check(DrawRenderState.GetBlendState());
PipelineState.BlendState = DrawRenderState.GetBlendState();
PipelineState.DepthStencilState = DrawRenderState.GetDepthStencilState();
PipelineState.DrawShadingRate = GetShadingRateFromMaterial(MaterialResource.GetShadingRate());
check(VertexFactory && VertexFactory->IsInitialized());
VertexFactory->GetStreams(FeatureLevel, InputStreamType, SharedMeshDrawCommand.VertexStreams);
SharedMeshDrawCommand.PrimitiveIdStreamIndex = VertexFactory->GetPrimitiveIdStreamIndex(InputStreamType);
int32 DataOffset = 0;
if (PassShaders.VertexShader.IsValid())
{
FMeshDrawSingleShaderBindings ShaderBindings = SharedMeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Vertex, DataOffset);
PassShaders.VertexShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, ShaderElementData, ShaderBindings);
}
if (PassShaders.HullShader.IsValid() & PassShaders.DomainShader.IsValid())
{
FMeshDrawSingleShaderBindings HullShaderBindings = SharedMeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Hull, DataOffset);
FMeshDrawSingleShaderBindings DomainShaderBindings = SharedMeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Domain, DataOffset);
PassShaders.HullShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, ShaderElementData, HullShaderBindings);
PassShaders.DomainShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, ShaderElementData, DomainShaderBindings);
}
if (PassShaders.PixelShader.IsValid())
{
FMeshDrawSingleShaderBindings ShaderBindings = SharedMeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Pixel, DataOffset);
PassShaders.PixelShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, ShaderElementData, ShaderBindings);
}
if (PassShaders.GeometryShader.IsValid())
{
FMeshDrawSingleShaderBindings ShaderBindings = SharedMeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Geometry, DataOffset);
PassShaders.GeometryShader->GetShaderBindings(Scene, FeatureLevel, PrimitiveSceneProxy, MaterialRenderProxy, MaterialResource, DrawRenderState, ShaderElementData, ShaderBindings);
}
SharedMeshDrawCommand.SetDebugData(PrimitiveSceneProxy, &MaterialResource, &MaterialRenderProxy, PassShaders.GetUntypedShaders(), VertexFactory);
const int32 NumElements = MeshBatch.Elements.Num();
for (int32 BatchElementIndex = 0; BatchElementIndex < NumElements; BatchElementIndex++)
{
if ((1ull << BatchElementIndex) & BatchElementMask)
{
const FMeshBatchElement& BatchElement = MeshBatch.Elements[BatchElementIndex];
FMeshDrawCommand& MeshDrawCommand = DrawListContext->AddCommand(SharedMeshDrawCommand, NumElements);
DataOffset = 0;
if (PassShaders.VertexShader.IsValid())
{
FMeshDrawSingleShaderBindings VertexShaderBindings = MeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Vertex, DataOffset);
FMeshMaterialShader::GetElementShaderBindings(PassShaders.VertexShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, InputStreamType, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, VertexShaderBindings, MeshDrawCommand.VertexStreams);
}
if (PassShaders.HullShader.IsValid() & PassShaders.DomainShader.IsValid())
{
FMeshDrawSingleShaderBindings HullShaderBindings = MeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Hull, DataOffset);
FMeshDrawSingleShaderBindings DomainShaderBindings = MeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Domain, DataOffset);
FMeshMaterialShader::GetElementShaderBindings(PassShaders.HullShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, EVertexInputStreamType::Default, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, HullShaderBindings, MeshDrawCommand.VertexStreams);
FMeshMaterialShader::GetElementShaderBindings(PassShaders.DomainShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, EVertexInputStreamType::Default, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, DomainShaderBindings, MeshDrawCommand.VertexStreams);
}
if (PassShaders.PixelShader.IsValid())
{
FMeshDrawSingleShaderBindings PixelShaderBindings = MeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Pixel, DataOffset);
FMeshMaterialShader::GetElementShaderBindings(PassShaders.PixelShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, EVertexInputStreamType::Default, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, PixelShaderBindings, MeshDrawCommand.VertexStreams);
}
if (PassShaders.GeometryShader.IsValid())
{
FMeshDrawSingleShaderBindings GeometryShaderBindings = MeshDrawCommand.ShaderBindings.GetSingleShaderBindings(SF_Geometry, DataOffset);
FMeshMaterialShader::GetElementShaderBindings(PassShaders.GeometryShader, Scene, ViewIfDynamicMeshCommand, VertexFactory, EVertexInputStreamType::Default, FeatureLevel, PrimitiveSceneProxy, MeshBatch, BatchElement, ShaderElementData, GeometryShaderBindings, MeshDrawCommand.VertexStreams);
}
int32 DrawPrimitiveId;
int32 ScenePrimitiveId;
GetDrawCommandPrimitiveId(PrimitiveSceneInfo, BatchElement, DrawPrimitiveId, ScenePrimitiveId);
FMeshProcessorShaders ShadersForDebugging = PassShaders.GetUntypedShaders();
DrawListContext->FinalizeCommand(MeshBatch, BatchElementIndex, DrawPrimitiveId, ScenePrimitiveId, MeshFillMode, MeshCullMode, SortKey, PipelineState, &ShadersForDebugging, MeshDrawCommand);
}
}
}
在代码创建了中FGraphicsMinimalPipelineStateInitializer PipelineState这个类的源码注释中提到:
- 不包含渲染目标状态的管线状态
- 适用于在绘制之间渲染目标状态不会改变的网格绘制。
- 注意:该类的大小会影响网格绘制遍历性能。
创建这个轻量的PipelineStateInitializer后调用了SetShaders函数并对其BoundShaderState赋值,至此如果是调用的PSOcache,那么应该是在这里计入缓存的管线id
SubmitMeshDrawCommands
在这个函数里面调用的是SubmitMeshDrawCommandsRange,此函数代码如下:
void SubmitMeshDrawCommandsRange(
const FMeshCommandOneFrameArray& VisibleMeshDrawCommands,
const FGraphicsMinimalPipelineStateSet& GraphicsMinimalPipelineStateSet,
FRHIVertexBuffer* PrimitiveIdsBuffer,
int32 BasePrimitiveIdsOffset,
bool bDynamicInstancing,
int32 StartIndex,
int32 NumMeshDrawCommands,
uint32 InstanceFactor,
FRHICommandList& RHICmdList)
{
FMeshDrawCommandStateCache StateCache;
INC_DWORD_STAT_BY(STAT_MeshDrawCalls, NumMeshDrawCommands);
for (int32 DrawCommandIndex = StartIndex; DrawCommandIndex < StartIndex + NumMeshDrawCommands; DrawCommandIndex++)
{
SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, MeshEvent, GEmitMeshDrawEvent != 0, TEXT("Mesh Draw"));
const FVisibleMeshDrawCommand& VisibleMeshDrawCommand = VisibleMeshDrawCommands[DrawCommandIndex];
const int32 PrimitiveIdBufferOffset = BasePrimitiveIdsOffset + (bDynamicInstancing ? VisibleMeshDrawCommand.PrimitiveIdBufferOffset : DrawCommandIndex) * sizeof(int32);
checkSlow(!bDynamicInstancing || VisibleMeshDrawCommand.PrimitiveIdBufferOffset >= 0);
FMeshDrawCommand::SubmitDraw(*VisibleMeshDrawCommand.MeshDrawCommand, GraphicsMinimalPipelineStateSet, PrimitiveIdsBuffer, PrimitiveIdBufferOffset, InstanceFactor, RHICmdList, StateCache);
}
}
这个函数传入了FGraphicsMinimalPipelineStateInitializer 的参数给FMeshDrawCommand::SubmitDraw用,这个函数通过之前的MeshDrawCommand的CachedPipelineId来获取GraphicsMinimalPipelineStateSet,然后通过AsGraphicsPipelineStateInitializer函数来把这个轻量的管线状态变为FGraphicsPipelineStateInitializer,接下来会调用SetGraphicsPipelineState函数,到这里就和之前的预编译流程的后面部分成功对接^^