Unity6 + UE5.4 PSO缓存实践记录

news2024/11/24 13:40:48

题图(取自COD冷战的着色器编译提示)

PSO(管线状态对象 Pipeline State Object)是伴随现代图形API(DirectX12、Vulkan、Metal)而出现的概念,它本质上是单次绘制时渲染管线所处的状态信息的集合(Shader、混合器状态、光栅器状态、图元拓扑信息等)。在D3D12中,PSO的描述信息由D3D12_GRAPHICS_PIPELINE_STATE_DESC结构体给出:

typedef struct D3D12_GRAPHICS_PIPELINE_STATE_DESC {
  ID3D12RootSignature                *pRootSignature;
  D3D12_SHADER_BYTECODE              VS;
  D3D12_SHADER_BYTECODE              PS;
  D3D12_SHADER_BYTECODE              DS;
  D3D12_SHADER_BYTECODE              HS;
  D3D12_SHADER_BYTECODE              GS;
  D3D12_STREAM_OUTPUT_DESC           StreamOutput;
  D3D12_BLEND_DESC                   BlendState;
  UINT                               SampleMask;
  D3D12_RASTERIZER_DESC              RasterizerState;
  D3D12_DEPTH_STENCIL_DESC           DepthStencilState;
  D3D12_INPUT_LAYOUT_DESC            InputLayout;
  D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue;
  D3D12_PRIMITIVE_TOPOLOGY_TYPE      PrimitiveTopologyType;
  UINT                               NumRenderTargets;
  DXGI_FORMAT                        RTVFormats[8];
  DXGI_FORMAT                        DSVFormat;
  DXGI_SAMPLE_DESC                   SampleDesc;
  UINT                               NodeMask;
  D3D12_CACHED_PIPELINE_STATE        CachedPSO;
  D3D12_PIPELINE_STATE_FLAGS         Flags;
} D3D12_GRAPHICS_PIPELINE_STATE_DESC;

Vulkan和Metal中,有和PSO相对应的概念,它们并不叫PSO,但是在提到现代图形API的图形管线状态时,我们通常都用PSO来指代


旧式图形API(如OpenGL、DirectX11等)中,管线状态可通过单独的API Call来在渲染过程中进行设置(比如glSetBlendState、glCompileShader等),这带来了几个潜在问题:

  • 在实际的DrawCall发出之前,用户可以随意改变管线的状态,所以图形驱动通常需要额外跟踪状态信息,并将实际的状态设置工作延迟到实际的DrawCall之前执行
  • 图形驱动需要在渲染过程中实时计算硬件状态,将API编码到硬件指令
  • 游玩时编译着色器会造成卡顿(虽然游戏引擎会提供一些预热功能来规避)
  • 这种工作流程本质上将管线状态设置工作耦合进渲染循环中,会干扰渲染效率

新式图形API通过引入PSO的概念,将管线状态视为一个集合对象,使用PSO之后的管线有以下优点

  • 管线状态不再可以在渲染过程中被松散设置
  • 一旦PSO对象被创建,其立刻处于可用的状态,且管线编译期信息不再可以被更改(如着色器、混合状态、颜色掩码等)
  • 着色器的编译相当于在PSO创建时就完成了
  • 因为PSO创建后的编译期相关信息不可以再更改,因而大部分硬件指令已经在此时被生成,驱动程序不需要再操心地跟踪管线状态
  • 渲染过程中,只需切换不同的PSO就可以改变管线状态,切换PSO的过程是相对较快的,这样可以将管线状态设置工作从渲染循环中剥离出来

概括来说,PSO设计理念之一,就是要将渲染过程中存在的实时状态计算(包括着色器编译),都显式地转移到一个统一的时间段内一气呵成地做完,将编译内容和渲染过程解耦,以为渲染过程让出更多的时间


但PSO的创建(编译)是一个非常非常耗时的操作,其时间开销已经不能和传统API的运行时着色器编译相提并论,即便是一些小Demo级的游戏里,如果尝试在游玩时创建关卡内所有物体的PSO,也可能导致长达数秒的停滞

现代游戏引擎为解决这个问题,通常会针对现代图形API平台增加PSO缓存支持,PSO缓存系统简单来说是帮助开发人员生成当前应用程序用到的所有PSO的列表,以便告诉用户机器在合适的阶段(比如加载期间,或由开发人员指定的其它时间)编译好这些PSO,这样能够避免游玩时创建所带来的停滞


这几年越来越多的PC平台游戏转向D3D12/Vulkan等新API,因此预先创建PSO成为了迫在眉睫的需要,大家可能发现这两年越来越多的游戏会显式的提示用户等待着色器编译(题图)

听起来是不是挺像Shader变体收集的,其实他们只有亿点点差别,如果你并不熟悉PSO,那你可以非常粗略的认为PSO缓存收集和编译就是新式图形API下的Shader变体收集和编译(但它们在底层的工作细节差别巨大)

本文主要内容

  1. 基于UE官方的游戏Demo来测试Unreal 5.4中的PSO缓存功能
  2. StackOBot,比较简单,有一个3D欢迎界面和一个关卡

  3. 古代山谷,相对更复杂,有两个自由移动关卡,带战斗和运行时生成的特效

  4. 彼时Unity6也已经推出了PSO缓存的功能,我们会利用新的URP模板场景来进行测试

UE PSO缓存实践

概览

Unreal 5.4中,PSO系统有两个组成部分:一个是PSO Precache,另一个是Bundle PSO Cache

PSO Precache(PSO预缓存系统):

是一种相对自动的预编译PSO的功能,相关逻辑在Component的**PostLoad()**函数内被调用。当用户第一次加载关卡的时候,Precache系统会尝试收集关卡内对象的网格和材质信息,并在后台线程上异步编译PSO

由于编译PSO的过程发生在关卡加载阶段,因此游玩时不会因为PSO编译而导致游戏卡顿(当然,理想情况如此);且编译的内容会被缓存下来,当用户第二次进入同一个关卡时,通过复用第一次的结果,加载速度会更快

Precache系统本质上依赖于底层缓存,编译好的数据只适用于当前软硬件环境。当图形驱动版本更换、图形硬件更换后,PSO需要被重新生成(这也就是你换了显卡或者更了驱动之后,游戏会提示你重新编译着色器的原因)

Precache系统相当于是分步进行的,因为它只会在加载时生成本关卡的PSO,这代表着玩家不需要在一上来就编译整个游戏要用到的所有PSO

另外,Precache的过程在PostLoad期间进行,但运行时生成的Actor并不会调用该函数,这意味着运行时由用户生成的新Actor的PSO可能无法被记录在案(比如一个只有在玩家触发时才出现的粒子特效,且该粒子特效并不是一开始就在关卡内的),这部分PSO会在游玩过程中编译,造成卡顿

该功能在5.3中首次出现,于5.4中默认开启,目前仅支持D3D12平台,移动端滚出克

Bundle PSO Cache(打包PSO缓存):

用于手动收集PSO,该系统早在UE4时就有了,开发人员需要手动跑游戏来收集PSO列表,并将收集到的文件包含到包体内发布给用户;当用户首次启动游戏时,Bundle PSO就开始编译

由于其流程依赖于手动采集,所以目前Epic也不是很青睐这种做法,官方是想在未来将PSO收集的过程完全自动化,以Precache系统完全替代Bundle PSO

但实际上目前最佳的实践仍是将这二者结合使用,因为PSO Precache总会有漏掉的地方,做不到100%的覆盖率

StackOBot项目简介

该项目刚启动时就进入一个3D场景+UI做成的欢迎界面:

因此实际上很多PSO编译的过程在刚进游戏就开始了,这样我们来不及启动Insight抓取数据

为了能更好的分析该3D欢迎界面的PSO编译情况,我又给它套娃了一层界面,让用户点两次启动才能进游戏

创建空关卡,并给一个UI,点击按钮后跳转到3D欢迎界面

1. StackOBot,关闭PSO缓存系统,清除上一次缓存

测试环境:13700F+RTX4070

第一次测试,我们使用下列命令关闭PSO预缓存系统,而且不会使用Bundle PSO

[/Script/Engine.RendererSettings]
r.PSOPrecaching=0

这样生成的包体内不含任何Bundle PSO信息,且加载关卡的时候也不会提前准备PSO,所有PSO都需要在用户游玩过程中遇到的时候生成

打包游戏,并使用 -clearPSODriverCache 启动参数来运行游戏,因为第一次编译PSO后的结果会被缓存下来,但我们是为了测试PSO缓存覆盖率,所以要保证每一次启动游戏时,上一次生成的二进制数据都要被清空,否则会干扰判断

1号尖峰:

RHICreateComputePipelineState(计算PSO):4.8s
RHICreateGraphicsPipelineState(图形PSO):1.7s

2号尖峰是一组连续尖峰的集合,其中最长的一个:

RHICreateComputePipelineState(计算PSO):1.0s
RHICreateGraphicsPipelineState(图形PSO):1.9s

游玩过程的小尖峰(出现尖峰时玩家有明显卡顿感):

RHICreateComputePipelineState(计算PSO):未触发
RHICreateGraphicsPipelineState(图形PSO):39.7ms -> 68.7ms -> 92.8ms

结论:

  • 运行时PSO创建开销很大,会造成明显的感知停顿

这还只是个小Demo场景,如果是更复杂的,卡顿会进一步放大导致几乎不能玩;另外,用户机器的CPU不一定比测试机好,所以他们的编译时间可能要更长

如果测试机的CPU核心数非常多,这可能会弱化PSO编译带来的停滞,此时可以使用 -corelimit=n (n是需要输入的核心数量)启动参数来运行游戏,可以更准确的复现目标机器编译PSO的耗时

2. StackOBot,关闭PSO缓存,不清除上一次缓存

这次我们不清除上一次的编译缓存,再来运行一下游戏

1号尖峰:

RHICreateComputePipelineState(计算PSO):414.7ms
RHICreateGraphicsPipelineState(图形PSO):162.7ms

2号尖峰:

RHICreateComputePipelineState(计算PSO):74.6ms
RHICreateGraphicsPipelineState(图形PSO):188.3ms

结论:

进游戏时PSO创建过程用时变短了,且游玩过程没有PSO编译尖峰,这受益于上一次运行时所保留下来的数据,此时所谓的PSO编译只是加载上一次的结果

注意,因为这一次的PSO创建依赖于上一次的缓存,假设上一次没走到某个特殊点A,那么这一次走到A点可能还是会触发编译尖峰,等到下次启动游戏时才会加载缓存

3. StackOBot,只启用PSO Precache,清除上一次缓存

首先启用PSO Precache系统

[/Script/Engine.RendererSettings]
r.PSOPrecaching=1

(可选)启用PSO Precache验证层,0表示关闭;1表示轻量级跟踪,对性能影响小;2表示详细跟踪;启用验证层可以在运行游戏时直观的看到PSO预缓存的命中情况

我们会先使用轻量级跟踪,后面会描述轻量级跟踪和详细跟踪的区别

[/Script/Engine.RendererSettings]
r.PSOPrecaching=1
r.PSOPrecache.Validation=1

1号尖峰:

RHICreateComputePipelineState(计算PSO):4.4s
RHICreateGraphicsPipelineState(图形PSO):3.6s

2号尖峰:

RHICreateComputePipelineState(计算PSO):1.2s
RHICreateGraphicsPipelineState(图形PSO):4.6s

对比第一次测试(第3小节)可知,本次2号尖峰(也就是正式进入游戏时)的PSO创建时长比之前高出许多,尤其是图形PSO部分

这代表关卡中要用到的大部分PSO都已经在关卡加载的时候被创建,因此游玩过程中的PSO尖峰就几乎不存在了


如果启用了PSO Precache验证层,那么跑游戏时可以通过Stat PSOPrecache命令来查看PSO缓存命中情况

图中显示有17个未追踪的PSO缓存,官方文档里给出未追踪的可能原因是

验证被禁用、全局材质、VertexFactory不受支持、MeshPassProcessor类型不受支持

这里我尝试将验证级别给到2,重新打包,其它参数不变,得到如下结果:

可以看到大部分PSO成功命中,少部分PSO在使用时还没来得及编译完毕(Too Late Count)

要注意的是该面板只统计“应该被Precache系统所捕获的PSO”,如果是Precache本身无法捕获到的内容(比如运行时生成的内容),是不会出现在这里的

4. StackOBot,只启用PSO Precache,清除上一次缓存,运行时生成Actor

先前提到过,Precache系统会在组件的PostLoad函数调用期间来编译PSO,而运行时生成的Actor不调用PostLoad,这会导致Precache无法准确收集到PSO

而且要额外注意两个问题:

  1. 只有当【运行时生成的新Actor所携带的PSO】与【当前加载的关卡内的任一物件的PSO】都不匹配的时候,才会触发运行时编译,否则它只是复用已编译的PSO
  1. Precache调试层不能捕获运行时Actor的PSO Miss事件,因为这种Actor本身就不归Precache管,所以不要觉得Precache Miss Count为0就是100% PSO覆盖

这里我做了一个简单测试,新建一个带Trigger的Actor,当玩家触发时会生成一个小蓝人,这个小蓝人的PSO信息从未在当前关卡出现过


Insight数据如下:

可以看到,触发Trigger的时候出现了81.9ms的图形PSO编译;另外,触发Trigger之前有一个很矮的小尖峰,我看了一下是20ms的计算PSO编译

但是第3小节测试的时候没发现有这个问题,所以也挺迷惑的,不知道是动了什么


不过,这些数据终究还是表明Precache系统做不到100%的PSO覆盖率,实际我们还是要结合Bundle PSO来使用,在接下来的古代山谷项目里,我们会尝试使用二者结合的工作流

5. 古代山谷项目,使用PSO Precache,清除上一次缓存

Insight数据如下:

1号尖峰是一系列尖峰的集合,只记录最高的数据:

RHICreateComputePipelineState(计算PSO):7.7s
RHICreateGraphicsPipelineState(图形PSO):5.5s

运行时PSO编译:

RHICreateComputePipelineState(计算PSO):未触发
RHICreateGraphicsPipelineState(图形PSO):32.8ms -> 15.2ms -> 296.7μs

从Insight数据看,我们确实触发了相当长时间的运行时PSO编译,但通过PSO Precache验证数据的结果来看,Miss Count始终保持为0,所以再次强调不要通过这个面板验证整体PSO的覆盖率

下面我们就尝试使用Bundle PSO Cache,来将这部分游玩时PSO编译内容给优化掉

7. 古代山谷项目,PSO Precache + Bundle PSO Cache,清除驱动缓存

① 配置设置:
  1. 在DefaultEngine.[ini](https://zhida.zhihu.com/search?q=ini&zhida_source=entity&is_preview=1)或者(Platform)Engine.ini中添加(实际项目更推荐后者,因为不同平台的PSO需要不同的收集):
[DevOptions.Shaders]
NeedsShaderStableKeys=true

[/Script/Engine.RendererSettings]
r.ShaderPipelineCache.Enabled=1
r.ShaderPipelineCache.ExcludePrecachePSO=1

其中,[r.ShaderPipelineCache.ExcludePrecachePSO=1](https://zhida.zhihu.com/search?q=r.ShaderPipelineCache.ExcludePrecachePSO%3D1&zhida_source=entity&is_preview=1) 用于确保Bundle PSO不会收集PreCache已经收集过的PSO

2. 在DefaultGame.ini中添加(注意该ini文件中可能已经存在该配置,记得先查找一下看看):

[/Script/UnrealEd.ProjectPackagingSettings]
bShareMaterialShaderCode=True
bSharedMaterialNativeLibraries=True 

3. 如果你从来没用过Bundle PSO Cache,请确保(Project)/Build/(Platform)/PipelineCaches文件夹是空的或根本不存在


② 跑游戏收集PSO:
  • 打包项目
  • 为打包出的程序添加-[logPSO](https://zhida.zhihu.com/search?q=logPSO&zhida_source=entity&is_preview=1) 启动参数,然后运行游戏
  • 尽可能覆盖所有位置

③ 转换PSO缓存:
  1. 跑完游戏后,你会发现游戏打包的目录中的(GameName)/Saved/CollectedPSOs里已经有了一个后缀为.rec.upipelinecache的文件,将该文件复制到一个自定义路径下(比如D:\PSOCache,你可以选择自己喜欢的,无所谓)
  2. 回到项目路径,找到(Project)/Saved/Cooked/(Platform)/(ProjectName)/Metadata/PipelineCaches文件夹,拷贝里面所有的.shk文件,复制到D:\PSOCache中(和.rec.upipelinecache一个目录)

有些时候,因项目平台设置的关系,.shk文件可能有PCD3D_SM5和PCD3D_SM6两种,实测如果把这两种SM的文件都放到D:\PSOCache下,会导致之后使用转换PSO缓存打包时报错,建议一次只复制一个SM级别的.shk

3. 在 D:\PSOCache 中新建一个txt,输入以下内容后改名为PSOCache.bat:

F:\Programme\Unreal\UE_5.4\Engine\Binaries\Win64\UnrealEditor-Cmd.exe -run=ShaderPipelineCacheTools expand D:\PSOCache\*.rec.upipelinecache D:\PSOCache\*.shk D:\PSOCache\20240819_AncientVallery_PCD3D_SM6.spc

  • 这里第一行是UnrealEditor-Cmd.exe的路径,这个是跟着你UE安装目录走的
  • 第二行注意要把 D:\PSOCache 改成你自己自定义的路径,如果你也是和我一样的路径,就无所谓
  • 第三行是最终生成的.spc文件名,命名随意

4. 运行该bat,你应该能得到一个.spc文件:


④ 使用转换后的PSO缓存再次打包:
  • 将③中生成的.spc文件复制到项目路径(Project)/Build/(Platform)/PipelineCaches中,再次打包
  • 注意一下打包过程中的log,看下有没有LogShaderPipelineCacheTools:开头的,有的话就正常


⑤ 运行游戏:
  1. 默认情况下,Bundle PSO会在游戏刚启动的时候就开始编译,你可以添加-Log启动参数,然后运行游戏看一下日志,如果有出现 LogRHI : FShaderPipelineCache::BeginNextPrecompileCacheTask(),则代表触发了编译过程

⑥ Insight数据:

1号尖峰:

RHICreateComputePipelineState(计算PSO):2.4s
RHICreateGraphicsPipelineState(图形PSO):11.1s

2号尖峰:

RHICreateComputePipelineState(计算PSO):1.8s
RHICreateGraphicsPipelineState(图形PSO):11.4s

游玩时PSO编译:未触发,但确实有尖峰,不过这就不在本文的范畴里了;由此可见我们成功通过Bundle PSO Cache弥补了Precache的不足

Unity6的PSO缓存

关于Unity6的PSO缓存,社区上有这么一个帖子:

https://discussions.unity.com/t/graphicsstatecollection-tracing-and-warmup-in-unity-6/951031​discussions.unity.com/t/graphicsstatecollection-tracing-and-warmup-in-unity-6/951031

简而言之:

  • Unity6中的PSO缓存类似UE的Bundle PSO Cache,需要手动收集,并在合适的时机预热
  • Unity可能会在未来版本中推出类似PSO Precache的功能(咕)
  • 传统的 ShaderVariantCollection.WarmUp 并不能适用于新图形API,因为该方法不能提供PSO所需的一些特殊信息,在新API下需要使用 GraphicsStateCollection
  • GraphicsStateCollection 目前在传统API下没有Fallback(这意味着在旧平台上要手动回退到_ShaderVariantCollection_.WarmUp?)

1. 新URP场景,关闭PSO缓存

我们使用新URP场景模板进行测试,该场景打包出来后是一个自动运行的Benchmark程序,用户无法以第一人称视角操作,如果你想自由移动,可以在打包的时候去掉第一个场景

该场景有三个展览室,进去会触发动画,同时也会触发PSO编译

在不使用任何预编译PSO的情况下,得到的Profiler数据如下,在进入展览室的时候,有一个142.43ms的尖峰,玩家卡顿感非常明显:

可以看到该帧的生成时间很长,若干个线程内都有PSO编译事件,我这里最猛的是2号线程,连续的几个PSO编译事件堆积起来可以有50多ms

2. 新URP场景,启用PSO缓存

① 创建脚本:

新建一个Mono脚本,编写代码,这里我写的是最基本的:

public class PSOCache : MonoBehaviour
{
    private GraphicsStateCollection _graphicsStateCollection;

    private string _cacheFilePath;

    private bool _hasValidCache = false;

    void Start()
    {
        _cacheFilePath = Application.dataPath + "/PSOCache.graphicsstate";

        _graphicsStateCollection = new GraphicsStateCollection();
        _hasValidCache = _graphicsStateCollection.LoadFromFile(_cacheFilePath);

        if (_hasValidCache)
        {
            _graphicsStateCollection.WarmUp();
        }
        else
        {
            _graphicsStateCollection.BeginTrace();
        }
    }

    private void OnDestroy()
    {
        if (!_hasValidCache)
        {
            _graphicsStateCollection.EndTrace();
            _graphicsStateCollection.SaveToFile(_cacheFilePath);
        }
    }
}

② 跑游戏收集PSO:

打包后,跑游戏,尽可能多的覆盖,这一步本质上和以前跑变体收集没什么区别,实际项目可以做成自动化的

结束游戏后,DataPath下会出现一个pso缓存文件:


③ 再次运行游戏查看Profiler数据:

虽然存在一些尖峰(最高的约28ms),但这些尖峰都不是PSO编译导致的,所以就不讨论了

如果你的PSO Cache命中良好,那么游玩过程中是不应该出现CreateGraphicsPipelineImpl事件的

补充说明

unity官方发的帖子,在跑GardenScene场景的时候,最多能干到276ms,Worker内的单个PSO编译事件能跑到84ms,但我本地测试没有这么恐怖,单个Worker内的单个PSO编译事件只占8ms左右(然后积少成多)

不知道是不是我的操作有问题,如果有dalao跑过相关流程请指教

Q & A

PSO的话题下常出现这样几个常见疑问:

Q:PSO能不能让厂商编译好,直接给用户,这样用户就不用等待编译了

A:固定硬件(Xbox,PS5,SteamDeck等)平台可以,PC不行,图形驱动和图形硬件都不一样,机器码都需要重编

Q:为什么以前的游戏没有着色器编译过程?

A:也有,只是没弹窗让你看到而已,现在这些游戏东西太多了,而且在新API下,如果不提前编,进游戏时实时创建PSO可比以前运行时编Shader开销大多了。所以目前的游戏都倾向于告(恐)诉(吓)用户最好编完再进,不然电脑会爆炸

附录

UE5.4 PSO预缓存源码调用链

这里简单描述一下,或许能帮助大家理一下思路

结构:[文件名] ~ [命名空间+函数名] [关键行数]

① RHI层:

Material.cpp ~ UMaterial::PrecachePSOs 2641 (注意PrecachePSOs函数在Component中也存在) ->

MaterialShared.cpp ~ FMaterial::CollectPSOs 2947 ->

PSOPrecache.cpp (Extern) ~ PrecacheMaterialPSOs ->

PSOPrecache.cpp ~ FMaterialPSORequestManager::PrecachePSOs 327 (如果先前已缓存过,则会于283行返回) ->

MaterialShared.cpp ~ FMaterialShaderMap::CollectPSOs 2744 ->

PSOPrecache.cpp (Extern) ~ PrecachePSOs 135 & 148 ->

PipelineStateCache.cpp ~ PipelineStateCache::PrecacheGraphicsPipelineState ->

PipelineStateCache.cpp ~ FPrecacheGraphicsPipelineCache::PrecacheGraphicsPipelineState 2775 ->

PipelineStateCache.cpp ~ TryAddNewState

1087行调用[CreateNewPSO](https://zhida.zhihu.com/search?q=CreateNewPSO&zhida_source=entity&is_preview=1)来初始化内存,但没有实际创建内容

1107行调用OnNewPipelineStateCreated来实际创建PSO ->

PipelineStateCache.cpp ~ FPrecacheGraphicsPipelineCache::OnNewPipelineStateCreated 2790 ->

PipelineStateCache.cpp ~ InternalCreateGraphicsPipelineState

如果是异步创建,则通过2524行的CompilePSO函数来创建

如果非异步创建,则直接调用2541行的RHICreateGraphicsPipelineState函数来创建 ->


② D3D12平台层:

D3D12State.cpp ~ FD3D12DynamicRHI::RHICreateGraphicsPipelineState ->

获取FD3D12PipelineStateCache的引用,并尝试在其中查找给定的PSO描述,如果找得到则直接返回

如果是新PSO则调用CreateAndAdd函数创建新PSO

D3D12RHIPrivate.h给出了D3D12平台下的具体行为


③ 传统API层(以D3D11为例):

D3D11平台无应用层PSO概念,因此没有重写RHICreateGraphicsPipelineState函数,D3D11RHIPrivate.h给出了D3D11平台下的具体行为

因为没有PSO概念,所以在Set Pipeline State的时候,D3D11走的是IRHICommandContextPSOFallback接口下的RHISetGraphicsPipelineState函数;该接口专门为不支持应用层PSO的图形API提供管线状态设置方法

Reference

  • Tomlooman关于UE PSO Cache的文章
  • 现代图形API的缺省指南-PSOs
  • UE5.4: PSO预缓存
  • UE Actor生命周期 - PostLoad函数
  • Unity 6 中的 GraphicsStateCollection 跟踪和预热

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2118394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

滁州皖东农商银行新任董事长未得官宣,年报涉嫌泄露股东隐私

撰稿|芋圆 近期,滁州皖东农商银行发布2024年半年报,其2024年上半年营业收入4.7亿元,同比增长72%,成功实现扭亏为盈;净利润1.37亿元,同比上涨10%,增速显著提升。但其利息净收入1.4亿元&#xff…

梨花声音研修院退费普通话学习初级方法

普通话作为中国的官方语言和国家通用语言,学习和掌握普通话不仅对中国人至关重要,对世界各地的学习者也具有重要意义。无论是为了工作、学术、旅游还是文化交流,掌握普通话都能带来巨大的好处。以下是一些行之有效的普通话学习方法&#xff0…

PMP–一、二、三模–分类–14.敏捷–技巧–敏捷项目章程

文章目录 技巧一模14.敏捷--项目章程--产品意愿--是指产品负责人对产品未来前景和方向的一个高度概括描述,它应符合公司或组织的战略目标。产品愿景声明发送给项目团队中的每个人,保证团队都理解并认可产品愿景。64、 [单选] 在一次会议上,产…

PHP体检信息管理系统-计算机毕业设计源码54850

目录 1 绪论 1.1 选题背景 1.2选题意义 1.3研究的主要内容 1.4论文结构与章节安排 2系统分析 2.1.1 技术可行性分析 2.1.2经济可行性分析 2.1.3操作可行性分析 2.2 系统流程分析 2.2.1 数据新增流程 2.2.2 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2…

线性代数|机器学习-P33卷积神经网络ImageNet和卷积规则

文章目录 1. ImageNet2. 卷积计算2.1 两个多项式卷积2.2 函数卷积2.3 循环卷积 3. 周期循环矩阵和非周期循环矩阵4. 循环卷积特征值4.1 卷积计算的分解4.2 运算量4.3 二维卷积公式 5. Kronecker Product 1. ImageNet ImageNet 的论文paper链接如下:详细请直接阅读相…

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"in…

奔驰 GLS450:后排电动遮阳帘升级,畅享私密与舒适

奔驰 GLS450&#xff1a;后排电动遮阳帘升级&#xff0c;畅享私密与舒适 奔驰 GLS450&#xff0c;作为豪华 SUV 的典范&#xff0c;一直致力于为乘客提供顶级的驾乘体验。此次升级后排电动遮阳帘&#xff0c;更是将舒适与私密提升到了新的高度。 升级后的电动遮阳帘&#xff…

第12章 第9节 Web应用测试(软件评测师)

1.web应用系统负载压力测试中&#xff0c;&#xff08;并发请求数&#xff09;不是衡量业务执行效率的指标。 【解析】并发请求数是考核系统能够承受的负载&#xff0c;交易执行吞吐量、交易执行相应时间、每秒点击率是衡量业务执行效率的指标 2.用户访问某web网站&#xff0…

你可能不知道的Activity启动的诡异现象探索

你可能不知道的Activity启动的诡异现象探索 这篇文章主要记录一下遇到的android启动过程中的一个有意思的探索过程&#xff0c;可能文章会比较长&#xff0c;相信我只要读下去一定会有所收获。这里说明一下&#xff0c;这篇文章肯定会涉及到activity的启动流程&#xff0c;很多…

JAVA社交新潮流同城组局搭子不愁系统小程序源码

社交新潮流——同城组局&#xff0c;搭子不愁系统 &#x1f389;【潮流前沿&#xff0c;社交新风向】&#x1f389; 在这个快节奏的城市生活中&#xff0c;你是否常常感到周末无聊&#xff0c;想找点乐子却苦于没有合适的搭子&#xff1f;别担心&#xff0c;今天我要给大家安…

【最经典的79个】软件测试面试题(内含答案)备战“金九银十”

001.软件的生命周期(prdctrm) 计划阶段(planning)-〉需求分析(requirement)-〉设计阶段(design)-〉编码(coding)->测试(testing)->运行与维护(running maintrnacne) 测试用例 用例编号 测试项目 测试标题 重要级别 预置条件 输入数据 执行步骤 预期结果 0002.问&…

RPG经典设计逻辑——DD系统简单拆解

RPG经典设计逻辑——D&D系统简单拆解 作为TGA2023年度游戏&#xff0c;身披数十项奖项及提名的《博德之门3》&#xff0c;近日又获得了2024雨果奖“最佳游戏或交互式作品”的奖项。“雨果奖”之名源于“科幻杂志之父”雨果・根斯巴克&#xff0c;是科幻及奇幻领域最负盛名…

Mysql高阶语句(2)

Mysql视图&#xff1a;优化操作与安全方案 概述&#xff1a; 视图&#xff08;View&#xff09;是数据库中的一种虚拟表&#xff0c;虽然不包含实际数据&#xff0c;但可以映射真实数据&#xff0c;像是数据库表的镜花水月或倒影。视图会动态地保存结果集的数据&#xff0c;使…

springboot邮件发送的方式?如何配置服务?

Springboot邮件发送的优化策略&#xff1f;Springboot发信技巧&#xff1f; Spring Boot作为一个流行的Java框架&#xff0c;提供了简单而强大的方式来实现邮件发送功能。AokSend将详细介绍Spring Boot邮件发送的方式&#xff0c;帮助开发者快速掌握这一技能。 SpringBoot邮件…

跟我一起学Python编程(五),变量的定义和使用

今天给大家介绍一下Python变量的定义和使用&#xff0c;我们要使用编程语言去做一件事情&#xff0c;一定要使用到变量。 什么是变量&#xff1f; 在Python中&#xff0c;变量可以用来存储各种类型的数据&#xff0c;如数字&#xff0c;文本&#xff08;称为字符串&#xff09…

云计算之大数据(上)

目录 一、Elasticsearch 1.1 产品组件 1.1.1 X-Pack 1.1.2 Beats数据采集中心 1.1.3 Logstash 1.1.4 Kibana 1.2 架构特性 1.2.1 性能 1.2.2 安全性 1.2.3 可用性 1.2.4 可扩展性 1.2.5 可维护性 1.2.6 国际化 1.3 综合检索分析 1.4 全观测 1.5 大数据检索加速…

遗传算法与深度学习实战(5)——遗传算法中常用遗传算子

遗传算法与深度学习实战&#xff08;5&#xff09;——遗传算法中常用遗传算子 0. 前言1. 常用选择算子1.1 轮盘选择 (roulette wheel selection)1.2 随机遍历抽样 (Stochastic universal sampling, SUS)1.3 排序选择 (rank-based selection)1.4 适应度缩放选择&#xff08;Fit…

无需更换摄像头,无需施工改造,降低智能化升级成本的智慧工业开源了

智慧工业视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型&#xff0c;并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络 谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络&#xff1a;谱图卷积网…

软件测试学习笔记丨接口测试用例设计

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32078 一、简介 接口测试在需求分析完成之后&#xff0c;即可设计对应的接口测试用例&#xff0c;然后根据用例进行接口测试。接口测试用例的设计&#xff0c;也需要用到黑盒测试用例的设计方…