【UE源码分析】PSO cache函数调用过程分析

温(mian)馨(ze)提(sheng)示(ming):

  1. 此文内容质量极差,仅作为个人学习过程记录
  2. 作者刚学习UE一个月,文章只是作为笔记,自己都不一定能看得懂
  3. 这个文档是接上文有个nt试图从类开始阅读源码结果半天啥都没看懂(笑死),这次从函数调用开始分析,函数名后面的括号数字只是个人方便记忆做的函数调用记录,因为本人的脑子是容量为三的函数队列,看到第三个函数前面的全忘了
  4. 这篇文章分析的是PSO功能在每次tick的时候PSOcache干的事情,但是本人能(ji)力(du)有(lan)限(gou),很多没写出来哈哈哈,估计也没啥人看嘿嘿
  5. 文档写下来后发现这样自己的源码理解还是不清晰,很混乱,下次改用流程图来画出来

PSO函数调用过程分析

OpenGLDrv.h

RHISetGraphicsPipelineState(0)

万物之源^^,起于RHI,把FRHIGraphicsPipelineState* GraphicsState封装成FRHIGraphicsPipelineStateFallBack* FallbackGraphicsState

这个函数最核心的地方在这里,把RHICreateBoundShaderState_internal(2)的返回值作为参数传给RHISetBoundShaderState(1)调用

RHICreateBoundShaderState_internal(2)

这个函数首先通过FRHICommandListExecutor::GetImmediateCommandList()获取RHICommandList,后面的两个宏定义如下

实际的代码如下,首先定义了一个lambda表达式 GLCommand,它封装了 RHICreateBoundShaderState_OnThisThread(3) 函数的调用。然后,根据当前的执行环境,它要么直接执行这个lambda表达式,要么将其封装在一个 FRHICommandGLCommand 命令中并将其添加到 RHICmdList 中以在RHI线程上执行。最后返回lambda表达式的结果,无论是直接执行还是在RHI线程上执行。

auto GLCommand = [&]() {
    return RHICreateBoundShaderState_OnThisThread(VertexDeclarationRHI,
        VertexShaderRHI,
        HullShaderRHI,
        DomainShaderRHI,
        PixelShaderRHI,
        GeometryShaderRHI,
        FromPSOFileCache);
};

if (RHICmdList.Bypass() || !IsRunningRHIInSeparateThread() || IsInRHIThread())
{
    return GLCommand();
}
else
{
    FBoundShaderStateRHIRef ReturnValue = (FBoundShaderStateRHIRef)0;
    ALLOC_COMMAND_CL(RHICmdList, FRHICommandGLCommand)([&ReturnValue, GLCommand = MoveTemp(GLCommand)]() {
        ReturnValue = GLCommand();
    });
    RHITHREAD_GLTRACE_BLOCKING;
    RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
    return ReturnValue;
}

OpenGLCommands.cpp

RHISetBoundShaderState(1)

这个函数干了三件事,把传入的参数FRHIBoundShaderState* BoundShaderStateRHI转成FOpenGLBoundShaderState* BoundShaderState;把PendingState的BoundShaderState更新;把BoundShaderState放入BoundShaderStateHistory中

OpenGLShaders.cpp

RHICreateBoundShaderState_OnThisThread(3)

该函数把参数通过 lambda表达式给整合成FOpenGLLinkedProgramConfiguration Config的值。

接下来通过GetCachedBoundShaderState函数去cache找,并把BoundShaderState给存到FCachedBoundShaderStateLink* CachedBoundShaderStateLink中如果找到了,就直接通过CachedBoundShaderStateLink返回BoundShaderState。这过程首先调用了GetOpenGLProgramsCache函数来获取静态变量类FGLProgramCache ProgramsCache,调用Touch函数来更新LRU,使得LinkedProgram能保存在cache中,初始化后返回。

如果没找到

先到StaticLastReleasedPrograms数组中找之前释放的program,找到了就把这个prog放到LinkedProgram里面

这个数组的内容是由FOpenGLBoundShaderState的析构函数写入的,把LinkedProgram放在StaticLastReleasedPrograms里面

如果在上面的StaticLastReleasedPrograms里没有,通过find函数找到ProgramsCache,通过bUseLRUCache来选择在两个map中尝试取出LinkedProgram。然后对CachedProgram进行判断,如果没有程序,调用CheckSinglePendingGLProgramCreateRequest函数(4)把二进制数据变成程序后再次尝试通过find找到ProgramsCache。

到了这一步后如果有CachedProgram,就放入LinkedProgram并且后续进行配置如果上面几步都没能获得CachedProgram,先通过重置程序ID、标记着色器参数缓存为脏,以及清除已链接程序的引用,然后调用LinkProgram(4);接下来把LinkedProgram加入cache,如果正在构建缓存文件并使用LRU,则通过EvictFromLRU()来驱逐最后创建的着色器。

最后整个程序返回出一个新建的BoundShaderState,不过这边的VertexDeclaration最后也没用上,不知道这步是干什么的

CheckSinglePendingGLProgramCreateRequest(4)

判断CachePtr,如果cache里面有东西就通过ProgramKey来调用CheckSinglePendingGLProgramCreateRequest_internal函数检查

ProgramBinRefPtr从CachePtr的ProgramToBinaryMap来查找FGLProgramBinaryFileCacheEntry,找到后把ReadRequest传给LocalReadRequest,检查如果LocalReadRequest有效,则把ReadRequest置空,修改GLProgramState的状态为ProgramLoaded,然后调用CompleteLoadedGLProgramRequest_internal(5)来完成程序的创建,并且GLProgramState变为ProgramAvailable了。如果没有LocalReadRequest并且GLProgramState是ProgramLoaded,则从PendingGLProgramCreateRequests里面找,通过CompleteLoadedGLProgramRequest_internal(5)来创建程序。

CompleteLoadedGLProgramRequest_internal(5)

这个函数通过通过bProgramExists标记查看ProgramsCache中是否有数据。如果使用了LRU,并且程序不存在的话,通过AddAsEvicted函数把二进制数据存到GL program中。如果程序存在的话,说明二进制数据已经被使用了,清空它。如果没有使用LRU且程序不存在的话,通过CreateGLProgramFromBinary(6)函数来创建程序并且把程序id放在FGLProgramBinaryFileCacheEntry的GLProgramId中;创建FOpenGLLinkedProgram* NewLinkedProgram并放入ProgramsCache;最后把GLProgramState变为ProgramAvailable。

CreateGLProgramFromBinary(6)(6)

这个函数通过识别cvar CVarStoreCompressedBinaries在任何一个线程上的值,如果存储的是压缩数据则去CreateGLProgramFromCompressedBinary(7),不是压缩数据的话就去CreateGLProgramFromUncompressedBinary来把二进制数据变成程序

CreateGLProgramFromCompressedBinary(7)

这个函数把CompressedProgramBinary通过UncompressCompressedBinaryProgram解压缩后调用CreateGLProgramFromUncompressedBinary

这个函数会调用GenProgramPipelines和ProgramBinary把二进制数据变为程序最后通过VerifyLinkedProgram函数来验证是否成功

LinkProgram(4)

首先看FOpenGLProgramBinaryCache是否存在,通过UseCachedProgram_internal(5)来把Program和CachedProgramBinary设置好,接下来通过CompilePendingShaders(5)把各个shader给编译好。如果Program的值为0说明没有启用cache,则自己创建program并attach shader;经过这两步后对Program验证,通过CacheProgram(5)来存储该program。接下来把Program绑定到管线上,重新创建LinkedProgram,如果有曲面细分的话就加上。通过判断CVarLRUKeepProgramBinaryResident是否为1来把CachedProgramBinary加入。最后通过ConfigureStageStates函数对LinkedProgram进行配置

UseCachedProgram_internal(5)

首先通过ProgramBinRefPtr去获得FGLProgramBinaryFileCacheEntry FoundProgram,判断FoundProgram的state不是ProgramStored或者ProgramAvailable说明这个地方为非预期,但如果是ProgramAvailable说明可以直接赋值返回,否则说明这个时候还没有准备好。如果BinaryFileState是BuildingCacheFileWithMove,那就去PreviousBinaryCacheInfo里面找,如果找到了就把二进制数据给到CachedProgramBinaryOUT,再通过CreateGLProgramFromBinary(6)(6)将其变为程序,最后通过AddProgramBinaryDataToBinaryCache(6)来把程序存储起来

AddProgramBinaryDataToBinaryCache(6)(6)

这个函数对Ar进行写入操作,首先存入ProgramKey,然后存入ProgramBinarySize,再存入BinaryProgramData。然后创建NewIndexEntry并放入ProgramEntryContainer,最后由AddProgramFileEntryToMap(7)来把程序和程序的哈希值都储存起来

AddProgramFileEntryToMap(7)

这个函数首先把NewEntry加入到ProgramToBinaryMap里面然后根据每个shader的哈希值,如果不是全零,则加入到ShaderToProgramsMap里面

CompilePendingShaders(5)

把每个阶段的shader的resource收集起来,先通过UncompressShader把压缩的resource解压,然后通过CompileCurrentShader编译好的shader的代码

CacheProgram(5)

这个函数调用AppendGLProgramToBinaryCache来实现首先判断BinaryFileState是否为BuildingCacheFile或BuildingCacheFileWithMove,

是就通过AddUniqueGLProgramToBinaryCache函数,如果ProgramToBinaryMap没有这个key则通过AddProgramBinaryDataToBinaryCache(6)(6)加入

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值