<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>阿基de一亩三分地啦</title>
        <link>https://www.morningheart.com/</link>
        <description>独游道路刚开始呢</description>
        <lastBuildDate>Fri, 27 Mar 2026 00:29:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2026, Dongfangliu</copyright>
        <item>
            <title><![CDATA[UE4 材质翻译HLSL流程简析]]></title>
            <link>https://www.morningheart.com/article/8ccf931b-b287-45e0-ad0a-288e37536571</link>
            <guid>https://www.morningheart.com/article/8ccf931b-b287-45e0-ad0a-288e37536571</guid>
            <pubDate>Fri, 25 Aug 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-8ccf931bb28745e0ad0a288e37536571"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-3d3635ff34ee42b78e843e1b7de9e45c" data-id="3d3635ff34ee42b78e843e1b7de9e45c"><span><div id="3d3635ff34ee42b78e843e1b7de9e45c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3d3635ff34ee42b78e843e1b7de9e45c" title="大体流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">大体流程</span></span></h3><div class="notion-text notion-block-91c7d1aec8d040858df002ccf03fabf4">从<code class="notion-inline-code">FHLSLMaterialTranslator::Translate()</code> 出发，我们看一下流程概况，开头即讲述了由于normal可能以节点<code class="notion-inline-code"><em>PixelNormalWS</em></code><em> </em>的形式被其他property的计算用到,因此先得到normal的计算代码。</div><div class="notion-text notion-block-65f632f0ec2742859e5baf678fae664c">在此之前，本身材质编译是在Pixel层面去做，有一些操作希望放到Vertex层面做，那这一部分也需要先计算出来，同理还有一些自定义的表达式（<code class="notion-inline-code">CustomExpression</code>)。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-9b3abcc4f8fd49c3a25d800fd8b55595"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F0f6365be-d871-41a1-85b1-ea5041a970d7%2FUntitled.png?table=block&amp;id=9b3abcc4-f8fd-49c3-a25d-800fd8b55595&amp;t=9b3abcc4-f8fd-49c3-a25d-800fd8b55595&amp;width=730.7750244140625&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-cf19de294e984501ba808f44ec0faecf">接下来，可以看到每个材质属性（<code class="notion-inline-code">Material Property</code>）均是通过一个<code class="notion-inline-code">Material-&gt;CompilePropertyAndSetMaterialProperty(MP_XX,this)</code> 的入口生成各自的代码，其中细节略后再提。</div><div class="notion-text notion-block-0227187801e34ab9aa571226bbfefb60">但此处并不是真正生成代码的地方，这里仅仅是将该材质属性相关联的代码段（<code class="notion-inline-code">FShaderCodeChunk</code>）生成，并没有做拼接。</div><div class="notion-text notion-block-b29d2a01c12f416d8cd0020b044d7606">代码段的拼接核心在<code class="notion-inline-code">GetFixedParameterCode</code> 中，</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-f25b06bec45e42d8bc2da9d94237bab1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F55ac8f91-b762-4cb4-9c57-32785dd942fa%2FUntitled.png?table=block&amp;id=f25b06be-c45e-42d8-bc2d-a9d94237bab1&amp;t=f25b06be-c45e-42d8-bc2d-a9d94237bab1&amp;width=730.7875366210938&amp;cache=v2" alt="拼接已生成的HLSL，仍然是先生成normal的部分，再生成其他属性的部分。（HLSLMaterialTranslator.cpp）" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">拼接已生成的HLSL，仍然是先生成normal的部分，再生成其他属性的部分。（HLSLMaterialTranslator.cpp）</figcaption></div></figure><div class="notion-text notion-block-213ad1b0a87d4924ad4fa0b90a823269">再往后看，则没有更多稀奇的代码了，不在本文的关注范畴内。打开<code class="notion-inline-code">GetFixedParameterCode</code> 一看，更加不稀奇，主要使用了<code class="notion-inline-code">GetDefinitions()</code> 来获得所有代码段拼接的结果。。。一看，好家伙，就全从头到尾直接连起来是吧</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-01a28281478041d4884219bf1496b460"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fe4061354-3b8c-4798-9d90-40412c55038c%2FUntitled.png?table=block&amp;id=01a28281-4780-41d4-8842-19bf1496b460&amp;t=01a28281-4780-41d4-8842-19bf1496b460&amp;width=730.7875366210938&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-99bf4b5170594c3b818b8e0520a85bc5">当然，这么做是对的，正确的原因来自于它的核心翻译规则：递归翻译。</div><div class="notion-text notion-block-e493781164444634be1702e12d8f95a0">在这里，其实已经有很多的疑问了：</div><ol start="1" class="notion-list notion-list-numbered notion-block-fe4e0e7ea34c46edaf2cf272205cde6c" style="list-style-type:decimal"><li>频繁出现的<code class="notion-inline-code">FShaderCodeChunk</code>说是代码段，但具体倒底是啥，怎么起作用的</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-dce2b84f27094d1fbf2e203a1a248fd0" style="list-style-type:decimal"><li>这些<code class="notion-inline-code">ShaderdXXX</code>的变量是干啥用的,就说<code class="notion-inline-code">SharedPropertyCodeChunks</code>好了</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-4d4798e597f24607bf371ea0fdf3b92f" style="list-style-type:decimal"><li>递归翻译是怎么具体体现的？为什么直接线性拼接是对的？</li></ol><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-5616d1376a614b2cb3496ffaecb1996e" data-id="5616d1376a614b2cb3496ffaecb1996e"><span><div id="5616d1376a614b2cb3496ffaecb1996e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5616d1376a614b2cb3496ffaecb1996e" title="FShaderCodeChunk的构成"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">FShaderCodeChunk的构成</span></span></h3><div class="notion-text notion-block-dc71351f95f843bf84ada4f0a15e48be">感恩UE在此处仍然留有一定的注释。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-ea6f4336f75f41c7b609337a2bdb7321"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F89d8c91a-d6a3-4773-ad7f-cb574b7c02a6%2FUntitled.png?table=block&amp;id=ea6f4336-f75f-41c7-b609-337a2bdb7321&amp;t=ea6f4336-f75f-41c7-b609-337a2bdb7321&amp;width=730.7750244140625&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-79ef0769211f4e3e8cf857c1544610ca"><li><code class="notion-inline-code">Hash</code>不必多说，其计算来自于<code class="notion-inline-code">const uint64 Hash = CityHash64((char*)FormattedCode, Result * sizeof(TCHAR));</code> <code class="notion-inline-code">FormattedCode</code>其实就是<code class="notion-inline-code">Definition</code> 。</li></ul><ul class="notion-list notion-list-disc notion-block-44bfc7eef9604462b2f607019a90c7d1"><li><code class="notion-inline-code">SymbolName</code>是一个变量名字，以“<code class="notion-inline-code">Local_N</code>”的形式由<code class="notion-inline-code">FHLSLMaterialTranslator::CreateSymbolName</code> 创建</li></ul><ul class="notion-list notion-list-disc notion-block-fc46eaaf8d8e4810afe772abb734f902"><li><code class="notion-inline-code">Definition</code> 可以被总结为两种情况：</li><ul class="notion-list notion-list-disc notion-block-fc46eaaf8d8e4810afe772abb734f902"><ol start="1" class="notion-list notion-list-numbered notion-block-2200d2a9663d497d946477c83af9450e" style="list-style-type:decimal"><li><code class="notion-inline-code">UniformExpression</code>或者<code class="notion-inline-code">bInline</code>：</li><ol class="notion-list notion-list-numbered notion-block-2200d2a9663d497d946477c83af9450e" style="list-style-type:lower-alpha"><li>常数 比如<code class="notion-inline-code"> 0.1，0.5，Material3(0,0,0)</code></li><li>UniformExpression， 通常是由于我们在材质上设置的非布尔参数构成，翻译后会变成<code class="notion-inline-code">MaterialScalar(Vector)Expressions[i].x/y/z/w</code></li><li>bInline, 比如<code class="notion-inline-code">Local_0.rgb,false,true</code>这类并不需要赋值和计算的</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-b28ba2fbbf2d4602b4244cb9c4228f60" style="list-style-type:decimal"><li>非Constant以及非UniformExpression：</li><ol class="notion-list notion-list-numbered notion-block-b28ba2fbbf2d4602b4244cb9c4228f60" style="list-style-type:lower-alpha"><li><code class="notion-inline-code">Type Local_N = Func(Local_0,Local_1,...,Local_{N-1})</code></li></ol></ol></ul></ul><ul class="notion-list notion-list-disc notion-block-b8fc3262c4ac4446805dc2a732824a81"><li><code class="notion-inline-code">Type</code> 是这个CodeChunk的结果的数据类型，一般通过<code class="notion-inline-code">FHLSLMaterialTranslator::GetArithmeticResultType</code> 进行运算得到</li></ul><div class="notion-text notion-block-51ffc41e8cc74744a751658909dc4c1d">总结，<code class="notion-inline-code">FShaderCodeChunk</code>可以是一段简单的变量\常量的读取截断，也可以是一个完整的变量声明+赋值，根据代码内容有对应的Hash。</div><div class="notion-blank notion-block-f19a9e36c0624fd386b1707b7348c7f8"> </div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-5a0bdfd08e2c48f486a154b5bb25d5cf" data-id="5a0bdfd08e2c48f486a154b5bb25d5cf"><span><div id="5a0bdfd08e2c48f486a154b5bb25d5cf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5a0bdfd08e2c48f486a154b5bb25d5cf" title="从CompilePropertyAndSetMaterialProperty 开始的翻译细节"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">从<code class="notion-inline-code">CompilePropertyAndSetMaterialProperty</code> 开始的翻译细节</span></span></h3><div class="notion-text notion-block-dff1feb24237481084bdedf13fd27a18">映入眼帘的是</div><div class="notion-text notion-block-55bd424a6e7f44e5a31a7c47a5de8ea5">进去<code class="notion-inline-code">FHLSLMaterialTranslator::SetMaterialProperty</code>一看，主要是配置了翻译时当前是在哪个<code class="notion-inline-code">ShaderFrequency</code>(SF_Vertex?SF_Pixel?SF_Compute?):</div><div class="notion-text notion-block-0b09900a1f214be4989691e4bb1eeaf2">这里把将符合<code class="notion-inline-code">InShaderFrequency</code> 的<code class="notion-inline-code">CodeChunks</code>作为当前编译时用到<code class="notion-inline-code">CodeChunks</code> ，可见这个<code class="notion-inline-code"><b><em>Shared</em></b></code><em><b> </b></em>其实是在<code class="notion-inline-code">ShaderFrequency</code> 上Share翻译出来的CodeChunk。</div><div class="notion-blank notion-block-b488dc44eb5b44f69fc0dae25b5e5afb"> </div><div class="notion-text notion-block-b8244e10853645699013ca2d3f802141">返回来继续看，主要是调用了<code class="notion-inline-code"> MaterialInterface-&gt;CompileProperty</code> ，后续会跳转到有</div><div class="notion-text notion-block-e4ca2d3bca814c75bf098b0fb5403391"><code class="notion-inline-code">FHLSLMaterialTranslator::CallExpression(FMaterialExpressionKey ExpressionKey,FMaterialCompiler* Compiler)</code></div><div class="notion-text notion-block-b0ef137c8e364f30b12b024f38035749">作为编译某个材质节点的切入口。</div><div class="notion-blank notion-block-30a2936dc01a459da12bc6084e61bd89"> </div><div class="notion-text notion-block-5be835d34c5048129c849cfb93d7a7b2">此处微微一品~（虽然我确实品了老长一会）可以知道通过<code class="notion-inline-code">FunctionStacks[ShaderFrequency]-&gt;</code><code class="notion-inline-code">ExpressionCodeMap</code> 存储已经翻译过的Expression结果（无论失败与否），避免重复翻译同一Expression, Expresssion即<code class="notion-inline-code">UMaterialExpression</code>是材质蓝图里面的一个个节点。</div><div class="notion-text notion-block-e05a76ca9d71416180b18075957874cd">核心的切入点在这一段：</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-47a47db0ce0c43429b9c010af44255e7" data-id="47a47db0ce0c43429b9c010af44255e7"><span><div id="47a47db0ce0c43429b9c010af44255e7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#47a47db0ce0c43429b9c010af44255e7" title="递归翻译"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">递归翻译</span></span></h4><div class="notion-text notion-block-ec9f06bd6de44cc3865948e4c8b9c1bb">此时您切入Compile()可以发现来到了<code class="notion-inline-code">MaterialExpressions.cpp</code>,总而言之，基本都是<code class="notion-inline-code">Compile</code>内部会调用输入节点的<code class="notion-inline-code">Compile</code>，这也就是所谓的递归翻译。可以脑补出一个流程，水流从材质属性往深深的材质蓝图向下流去，由深入浅的返回时一条条翻译出来，由此，语句之间的依赖关系自动解决，因此最后直接线性拼接即可得到正确的翻译结果。</div><div class="notion-blank notion-block-3e866bea570b401b8ae38a352efb371b"> </div><div class="notion-text notion-block-486261f013314156be483c412516468b">此时，还想补充一下一些Compile中的细节，<code class="notion-inline-code">FShaderCodeChunk</code> 和<code class="notion-inline-code">FShaderCodeChunk::Definition</code>是如何生成的。</div><div class="notion-blank notion-block-685e0f4d777547d7bfb7190c373ef089"> </div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-7140be3555f44ea799f7a1c7de5ef0af" data-id="7140be3555f44ea799f7a1c7de5ef0af"><span><div id="7140be3555f44ea799f7a1c7de5ef0af" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7140be3555f44ea799f7a1c7de5ef0af" title="FShaderCodeChunk 的生成"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><code class="notion-inline-code">FShaderCodeChunk</code> 的生成</span></span></h3><div class="notion-blank notion-block-9117243981f74664a49784de99086f97"> </div><div class="notion-text notion-block-191efb6ca1c646099898c5c7c0c7f885">我们可以简单从一个<code class="notion-inline-code">UMaterialExpressionAdd::Compile</code>::Compile进行解读</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-935ce07345d24037b7da3380841fb3ba"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fde78974a-fcec-4a66-9e91-12ec04283d7b%2FUntitled.png?table=block&amp;id=935ce073-45d2-4037-b7da-3380841fb3ba&amp;t=935ce073-45d2-4037-b7da-3380841fb3ba&amp;width=730.7625122070312&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-b02ec1c2392747019f165d47e598a1e2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fb1dd4667-8832-40ae-9d9e-afc1abdf5c35%2FUntitled.png?table=block&amp;id=b02ec1c2-3927-4701-9f16-5d47e598a1e2&amp;t=b02ec1c2-3927-4701-9f16-5d47e598a1e2&amp;width=730.7875366210938&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-9b2343d54a984d78a9cd44746b4b370a">注意两个函数<code class="notion-inline-code">AddCodeChunkWithHash</code> 和<code class="notion-inline-code">GetParameterCode</code></div><div class="notion-text notion-block-7c614bc14a2745fb9e86727362702ced"><code class="notion-inline-code">GetParameterCode</code> 的作用是返回此处用到的CodeChunk的符号，我愿统称为变量名，与前文的Definition基本是一个含义。</div><div class="notion-text notion-block-f9433828a99b437aabd97f891373cbe2"><code class="notion-inline-code">AddCodeChunkWithHash</code> 内部调用了<code class="notion-inline-code">AddCodeChunkInner</code> 生成codechunk并塞入到当前工作的<code class="notion-inline-code">CurrentScopeChunks</code>中：</div><div class="notion-text notion-block-2080ac6a28ee486abfc6c526401db962">返回新生成的CodeChunk在数组中的位置，也是这个原因，大多数时候看到的Chunk[]数组里面存的都是int32，而不是FShaderCodeChunk这样的数据。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Texture Streaming中的LOD计算路径]]></title>
            <link>https://www.morningheart.com/article/2ba761d8-2498-438f-9a4c-7a9b066329db</link>
            <guid>https://www.morningheart.com/article/2ba761d8-2498-438f-9a4c-7a9b066329db</guid>
            <pubDate>Fri, 28 Jul 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-2ba761d82498438f9a4c7a9b066329db"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-4fd89b053cad406f9218f2681e9631eb" data-id="4fd89b053cad406f9218f2681e9631eb"><span><div id="4fd89b053cad406f9218f2681e9631eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4fd89b053cad406f9218f2681e9631eb" title="引子"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">引子</span></span></h3><div class="notion-text notion-block-159f3e9b44df44299a35579b7c444067"><code class="notion-inline-code">UTextureLODSettings::CalculateLODBias(const UTexture* Texture, bool bIncCinematicMips) const </code></div><div class="notion-text notion-block-ce64a49aefaa49aaadaf45817f8bcbfc"> 这个函数的作用是根据group设置的最大最小lodsize，以及当前贴图的最大尺寸，计算出两者差值，得到bias</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-d5e854887d66475b8a3a910027151650" data-id="d5e854887d66475b8a3a910027151650"><span><div id="d5e854887d66475b8a3a910027151650" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d5e854887d66475b8a3a910027151650" title="代码"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">代码</span></span></h3><div class="notion-text notion-block-0fb3f3d22643403a8f5c23f89cf4e119">这个streaming最高的mip计算</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-c2eaa7f8e3084fffb00a226ad49783e6"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fb196b4f1-77fb-415f-87d2-dddddcf39c4c%2FUntitled.png?table=block&amp;id=c2eaa7f8-e308-4fff-b00a-226ad49783e6&amp;t=c2eaa7f8-e308-4fff-b00a-226ad49783e6&amp;width=730.7875366210938&amp;cache=v2" alt="正常来说这里的ResouceLODBias是0，因为AssetLODBias也是约等于CachedLODBias" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">正常来说这里的ResouceLODBias是0，因为AssetLODBias也是约等于CachedLODBias</figcaption></div></figure><div class="notion-text notion-block-dcbaf3212b0b4606b2276df3c4ad7d97">我们看一下<code class="notion-inline-code">CachedCombinedLODBias</code>的计算，平平无奇：</div><div class="notion-text notion-block-eb21a9801e3f4d6e8bfa927fc98f1df9">关注<code class="notion-inline-code">FStreamableRenderResourceState::AssetLODBias</code>的计算：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7d1ac4df470c431eb463961a9fac50c8"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:576px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F0b7e8108-526d-40b6-8ec5-7d062b556c03%2FUntitled.png?table=block&amp;id=7d1ac4df-470c-431e-b463-961a9fac50c8&amp;t=7d1ac4df-470c-431e-b463-961a9fac50c8&amp;width=576&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-6f157c24d4db44d0ba14a9c1063b9734"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:624px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F7b70ad15-00a7-4c8d-b717-5b26eba445ff%2FUntitled.png?table=block&amp;id=6f157c24-d4db-44d0-ba14-a9c1063b9734&amp;t=6f157c24-d4db-44d0-ba14-a9c1063b9734&amp;width=624&amp;cache=v2" alt="之所以说约等于的原因，在于AssetLODBias消除了NumCinematicMipLevels的影响（UTexture::GetResourcePostInitState）" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">之所以说约等于的原因，在于AssetLODBias消除了NumCinematicMipLevels的影响（<code class="notion-inline-code">UTexture::GetResourcePostInitState</code>）</figcaption></div></figure><div class="notion-text notion-block-38d46ff215b34b9293ecffaf702af644"><code class="notion-inline-code">AssetLODBias = AssetMipIdxForResourceFirstMip = GetCachedLODBias()-NumCinematicMipLevels</code></div><div class="notion-text notion-block-e2a50e1c66064916be76d6ef370c248c">紧接着知道了实际上Streaming时的最大LOD数目，<code class="notion-inline-code">MaxNumLODs=NumMips-AssetLODBias </code> ，合情合理。</div><div class="notion-text notion-block-22e161f97ab5460d8471a6c65a1e9ba3">接下来算运行streaming时允许的最大最小mip数目时，就考虑到了这个局部变量LODBias，即这节开头的引子</div><a style="width:100%" href="StreamingTexture.cpp"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-549d79bb05ab43c1a685fbd14e06f9d4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fc5a6fb5a-12db-415b-802f-877b51ccd90d%2FUntitled.png?table=block&amp;id=549d79bb-05ab-43c1-a685-fbd14e06f9d4&amp;t=549d79bb-05ab-43c1-a685-fbd14e06f9d4&amp;width=730.7875366210938&amp;cache=v2" alt="StreamingTexture.cpp" loading="lazy" decoding="async"/></div></figure></a><div class="notion-text notion-block-d47bc98d604d4e9ca2c4f1d260da7929">最终，Streaming时计算需要的mips时需要考虑<code class="notion-inline-code">MaxAllowedNumMips</code> 和<code class="notion-inline-code">MinAllowedNumMips</code> 。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-ecfd5d9e6cbd4d13a849d06cf8bb21ff"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:345px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fda31bcc3-2ed7-4a38-bb43-2c68088f8270%2FUntitled.png?table=block&amp;id=ecfd5d9e-6cbd-4d13-a849-d06cf8bb21ff&amp;t=ecfd5d9e-6cbd-4d13-a849-d06cf8bb21ff&amp;width=345&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-9a2f7b99640a42eab4b46995227bc07b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:384px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F024ae790-3c84-4771-8190-599379453416%2FUntitled.png?table=block&amp;id=9a2f7b99-640a-42ea-b4b4-6995227bc07b&amp;t=9a2f7b99-640a-42ea-b4b4-6995227bc07b&amp;width=384&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-d374013ddd7448c79d8e539dfc73bdc0" data-id="d374013ddd7448c79d8e539dfc73bdc0"><span><div id="d374013ddd7448c79d8e539dfc73bdc0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d374013ddd7448c79d8e539dfc73bdc0" title="总结"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">总结</span></span></h3><div class="notion-text notion-block-b3ca3fc673074a54b32ff82a011e221e">总而言之，<code class="notion-inline-code">FStreamingRenderAsset::MaxAllowedNumMips</code> 以及<code class="notion-inline-code">FStreamingRenderAsset::MinAllowedNumMips</code> 决定了可以StremIn/Out的贴图Mip范围。这两个值实际上受到了<code class="notion-inline-code">FStreamableRenderResourceState::MaxNumLODs</code> 的直接影响，而这个<code class="notion-inline-code">MaxNumLODs</code>本身即是减去LODBias之后的值。</div><div class="notion-text notion-block-5607d4f4f7db4ce390f4fdc674776d9d">在切画质(切换<code class="notion-inline-code">ActiveDeviceProfile</code>)时并不会更新每个texture的<code class="notion-inline-code">CachedCombinedLODBias</code> ,因此导致<code class="notion-inline-code">MaxNumLODs</code>以及<code class="notion-inline-code">MaxAllowedNumMips</code>实际上并不会有效更新，进而streaming的结果保持原有值。</div><div class="notion-blank notion-block-fbb0325633c741c9a35bd5c9ca24a409"> </div><div class="notion-text notion-block-8f8a681f66084bd5a3ddcf6cbd99d9b2">在切画质时，我组项目能看到贴图发生肉眼可见的原因，主要是独门秘技<code class="notion-inline-code"><em><span class="notion-inline-underscore">InGameLODBias</span></em></code>在其中发挥作用，它会在计算<code class="notion-inline-code">MinAllowedNumMips</code> 和<code class="notion-inline-code">MaxAllowedNumMips</code> 时参与作用。目前该变量的设定是对战场景中贴图的最小LODBias（实际LODBias至少大于等于<code class="notion-inline-code"><em><span class="notion-inline-underscore">InGameLODBias</span></em></code>）。</div><div class="notion-blank notion-block-76cb1977e5524e6f8bcdb41fcbd0e03f"> </div><div class="notion-text notion-block-cc51e2056c934e1689eaf67098a45399">这意味着当<code class="notion-inline-code"><em><span class="notion-inline-underscore">InGameLODBias</span></em></code>对所有画质为0时，贴图的画质只取决于它被创建时的画质，而不受后续画质切换改变，如果真是这个现状，我的评价是非常恐怖。得确认确认</div><div class="notion-blank notion-block-6459ac06f0c648d6a3ab1ad5b559c4cb"> </div><div class="notion-text notion-block-9a69b7c3eae143e1966559609f9a2a70">得再品品，看看哪里是不是能在切画质时搞点mip的计算数据更新，更新CachedLODBias,更新<code class="notion-inline-code">FStreamableRenderResourceState</code> 。</div><div class="notion-blank notion-block-91b9b46e9143429197256da5a2e0010b"> </div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-27e2950c3c714f5abca8cd7b5ae222eb" data-id="27e2950c3c714f5abca8cd7b5ae222eb"><span><div id="27e2950c3c714f5abca8cd7b5ae222eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#27e2950c3c714f5abca8cd7b5ae222eb" title="一种可能的修复路径"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">一种可能的修复路径</span></span></h3><div class="notion-text notion-block-c22cbd0cb79e448eb95c4e3adf755e5e">这是目前我自己看下来觉得最安全的修复位置和修复方法，由于系统的复杂性，还是不踩屎坑了</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8ce0c23601034a15abf7c48b97913bc3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F4bc45be4-c0b2-4e8b-92ea-4c49cac5db3d%2FUntitled.png?table=block&amp;id=8ce0c236-0103-4a15-abf7-c48b97913bc3&amp;t=8ce0c236-0103-4a15-abf7-c48b97913bc3&amp;width=730.7875366210938&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-b28480a091c849b9a41c2b6dd557c427"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UE4 PSO的载入和保存规则]]></title>
            <link>https://www.morningheart.com/article/9b6f3652-0083-42b1-a010-f9035b1bca3a</link>
            <guid>https://www.morningheart.com/article/9b6f3652-0083-42b1-a010-f9035b1bca3a</guid>
            <pubDate>Fri, 05 Nov 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-9b6f3652008342b1a010f9035b1bca3a"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-row"><a target="_blank" rel="noopener noreferrer" class="notion-bookmark notion-block-8cfc9760f28847b7bbf3835d4bed7ecf" href="https://imzlp.com/posts/24336/"><div><div class="notion-bookmark-title">UE项目优化：PSO Caching</div><div class="notion-bookmark-description">UE中具有PSO Caching机制，全称Pipeline State Object Caching，用于预先记录和构建出运行时所使用的材质依赖的Shader信息，当项目首次使用这些Shader时，该列表可以加速Shader的加载/编译过程。PSO Caching会把渲染状态、顶点声明、Primitive类型、RenderTarget像素格式等数据保存到文件中，提升Shader的加载效率。本篇文章主要介绍PSO Caching的启用及构建流程，并会分析PSO Cache在引擎中的加载流程以及实现热更PSO方式、错误处理等，PSO Caching的原理有时间再进行详细分析。 PSO Caching的官方文档： PSO Caching 。 PSO Cache构建流程概览： PSO Caching的部署和使用大致分为以下几个步骤： 为项目开启PSO Cahche和ShaderStableKeys，打包后可以从Metadata/PipelineCaches目录下获得ShaderStableInfo*.scl.csv 添加logPSO参数启动游戏，用于在运行时记录PSO数据（*.rec.upipelinecache） 通过ShaderStableInfo*.scl.csv和*.rec.upipelinecache生成*.stablepc.csv 再次执行Cook，通过*.stablepc.csv生成upipelinecache文件，打至包内； 启动游戏，引擎自动加载*.stable.upipelinecache，编译Shader时使用PSO Caching 本篇文章的内容顺序也遵循着几个步骤。 首先需要为项目开启 ShaderStableKeys ，在执行Cook时生成稳定的ShaderKey，作为记录Shader的凭据。 在 DefaultEngine.ini （或平台相关如AndroidEngine.ini）中添加以下值： 添加之后再执行打包（Cook），会创建以下目录： 并且会在该目录下生成两个文件（分别对应项目、引擎）： Cook过程中会有以下log，表明生成了这两个文件： 可以使用它们通过-run=ShaderPipelineCacheTools这个Commandlet来生成*.stablepc.csv 。 *.scl.csv 文件的内容： 启动游戏时加入-logPSO参数或者在 DefaultEngine.ini 中加入以下配置： 也可以在 Devices Profile中设置： 这两个参数在以下代码中使用： 运行游戏时会有log: 并会在 Saved/CoolectedPSOs 中创建以下文件： 使用以下commandlet： 以上命令会在引擎的Binaries/Win64下生成 Client_GLSL_ES3_1_ANDROID.stablepc.csv文件，注意一定要匹配{PROJECTNAME}_{SHADER_FORMART_NAME}.stablepc.csv 这个命名规则。 Android的命名为：Client_GLSL_ES3_1_ANDROID.stablepc.csvIOS的命名为：Client_SF_METAL.stablepc.csv 生成时具有以下Log： 最终PSO所需要的所有文件： 我把测试工程生成的文件备份了一份： PSOCache.7z ，可以查看每个文件中的内容。 把生成的*stablepc.csv放到 Build/Android/PipelineCaches目录下，注意 Build/PLATFORM这个Platform是编译平台，不是Cook的资源平台，Android的包就是 Android而不是 Android_ASTC等。之后重新打包即可。 引擎在Cook时通过 stavlepc.csv 创建PipelineCache的代码： 实际使用 stablepc.csv的地方就是用它来执行 ShaderPipelineCacheTools 这个commandlet生成upipelinecache文件并打至包内。 ShaderPipelineCacheToolsCommandlet的执行命令为： 生成*.stable.upipelinecache文件的包内路径为 Content\PipelineCaches\Android ： 因为它是位于Content下并会打包进pak的文件，我们也可以对其进行热更。 当安装了包含 upipelinecache 的包，在运行时就会有以下log： 与 ShaderCode类似，引擎在启动时也是会自动加载PSO Cache的，在 FEngineLoop中通过调用 ...</div><div class="notion-bookmark-link"><div class="notion-bookmark-link-icon"><img src="https://www.notion.so/image/https%3A%2F%2Fimzlp.com%2Fimages%2Ffavicon-16x16-tilt-brush.png?table=block&amp;id=8cfc9760-f288-47b7-bbf3-835d4bed7ecf&amp;t=8cfc9760-f288-47b7-bbf3-835d4bed7ecf" alt="UE项目优化：PSO Caching" loading="lazy" decoding="async"/></div><div class="notion-bookmark-link-text">https://imzlp.com/posts/24336/</div></div></div><div class="notion-bookmark-image"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fimg.imzlp.com%2Fimgs%2Fzlp%2Fblog%2Fposts%2F24336%2FPSO_Caching_Digramh.webp?table=block&amp;id=8cfc9760-f288-47b7-bbf3-835d4bed7ecf&amp;t=8cfc9760-f288-47b7-bbf3-835d4bed7ecf" alt="UE项目优化：PSO Caching" loading="lazy" decoding="async"/></div></a></div><div class="notion-blank notion-block-cf2a7719b0f94ca78aa49ce6a973c769"> </div><div class="notion-file notion-block-707f7330799d41dea56a247b33e355e7"><a target="_blank" rel="noopener noreferrer" class="notion-file-link" href="https://file.notion.so/f/f/5faa5465-0058-4b82-84ec-2bbb3e79dc5a/4d1b9680-63c1-47b4-b66f-36dab285ce92/PSO%E6%80%BB%E7%BB%93.txt?table=block&amp;id=707f7330-799d-41de-a56a-247b33e355e7&amp;spaceId=5faa5465-0058-4b82-84ec-2bbb3e79dc5a&amp;expirationTimestamp=1774598400000&amp;signature=u4j6RMAnJgvmxL0cpRJ0-ZUebNp457sZRcWGB_93AKw"><svg class="notion-file-icon" viewBox="0 0 30 30"><path d="M22,8v12c0,3.866-3.134,7-7,7s-7-3.134-7-7V8c0-2.762,2.238-5,5-5s5,2.238,5,5v12c0,1.657-1.343,3-3,3s-3-1.343-3-3V8h-2v12c0,2.762,2.238,5,5,5s5-2.238,5-5V8c0-3.866-3.134-7-7-7S6,4.134,6,8v12c0,4.971,4.029,9,9,9s9-4.029,9-9V8H22z"></path></svg><div class="notion-file-info"><div class="notion-file-title">PSO总结.txt</div><div class="notion-file-size">2.0KB</div></div></a></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-637940fd64f14e7b8c9daf3eb18628ef" data-id="637940fd64f14e7b8c9daf3eb18628ef"><span><div id="637940fd64f14e7b8c9daf3eb18628ef" class="notion-header-anchor"></div><a class="notion-hash-link" href="#637940fd64f14e7b8c9daf3eb18628ef" title="载入"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">载入</span></span></h3><div class="notion-text notion-block-1670db3dc3324c16bf6ec463af5b03fb">载入是在<code class="notion-inline-code">FEngineLoop::PreInitPostStartupScreen</code>中发生<code class="notion-inline-code">OpenPipelineFileCache</code></div><div class="notion-text notion-block-6c71edd9865043eebda75783ca4acfa1">逻辑是先从 <code class="notion-inline-code">\Content\PipelineCaches\Windows</code>载入<code class="notion-inline-code">\Content\PipelineCaches\Windows\XXX_PCD3D_SM5.stable.upipelinecache</code> </div><div class="notion-text notion-block-89b32bdd0d994160b4252f6ce8723ed0">接着，检测是否需要<code class="notion-inline-code">ShouldLoadUserCache()</code> ，这一步说的UserCache就是<code class="notion-inline-code">\Saved\xxx_PCD3D_SM5.upipelinecache</code>  。判断条件是，是否能够开启<code class="notion-inline-code">r.ShaderPipelineCache.LogPSO</code> ,以及<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code> 这两个flag。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-67c6db81b4544df18bd9f10188247687"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F778675e8-a591-47d4-aa66-ea178d9dd9c3%2FUntitled.png?table=block&amp;id=67c6db81-b454-4df1-8bd9-f10188247687&amp;t=67c6db81-b454-4df1-8bd9-f10188247687&amp;width=1649&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-512590f2d2164fb5ab87440d66f93609"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fafc46dbb-d1a9-4ba4-bd88-b831bcffa0fc%2FUntitled.png?table=block&amp;id=512590f2-d216-4fb5-ab87-440d66f93609&amp;t=512590f2-d216-4fb5-ab87-440d66f93609&amp;width=1726&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-d163f44c719248ab934e86222cb82d36">SaveUserCache这个flag默认在mac的shipping版本是开的。对于这两个Flag,在Windows的Shipping版本也有强制开启。</div><a style="width:100%" href="WindowsRHI.cpp"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1627f40bad82416fac230f82a7473422"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F5686063a-316a-4557-acc3-830639965ef8%2FUntitled.png?table=block&amp;id=1627f40b-ad82-416f-ac23-0f82a7473422&amp;t=1627f40b-ad82-416f-ac23-0f82a7473422&amp;width=1858&amp;cache=v2" alt="WindowsRHI.cpp" loading="lazy" decoding="async"/></div></figure></a><div class="notion-text notion-block-e753e9206b0a42c081b32909c11a4c34"><em>但我个人觉得还是</em><em><code class="notion-inline-code">PSOFileCacheSaveUserCacheCVar.Set(1); </code></em><em> 这种方式比较容易不产生怀疑吧。。</em></div><div class="notion-text notion-block-603e48e9d06847348ead4d82e8504b3b">经过检测，这一调用的源头<code class="notion-inline-code">RHIInit()</code>发生在<code class="notion-inline-code">FEngineLoop::PreInitPreStartupScreen()</code>第2592行，而<code class="notion-inline-code">PreInitPostStartupScreen()</code> 发生在<code class="notion-inline-code">FEngineLoop::PreInit</code>3648行,在<code class="notion-inline-code">PreInitPreStartupScreen()</code> 之后。说明变量赋值在变量调用之前。</div><div class="notion-text notion-block-746c511d694246878c913c43669661a9">所以在shipping版本中，它100%是<span class="notion-yellow_background"><code class="notion-inline-code">_PCD3D_SM5.stable.upipelinecache</code></span><span class="notion-yellow_background"> +</span><code class="notion-inline-code">\Saved\xxx_PCD3D_SM5.upipelinecache</code><span class="notion-yellow_background"> 组合</span>的结果。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-fff978690975456fab88ba0c55883460" data-id="fff978690975456fab88ba0c55883460"><span><div id="fff978690975456fab88ba0c55883460" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fff978690975456fab88ba0c55883460" title="退出及保存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">退出及保存</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-f222ed56fe374725b0948bc1e2432e93"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fb4c6d1d7-1d96-4536-b894-c7539faa1cba%2FUntitled.png?table=block&amp;id=f222ed56-fe37-4725-b094-8bc1e2432e93&amp;t=f222ed56-fe37-4725-b094-8bc1e2432e93&amp;width=1797&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-61168706563e492fa54b26460dddecf0">保存在<code class="notion-inline-code">\Saved\xxx_PCD3D_SM5.upipelinecache</code> 。</div><div class="notion-text notion-block-aa98437a77444fea876658d8f1dc25f4">可以看到调用<code class="notion-inline-code">FShaderPipelineCache::Close()</code>时会保存：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1d9ee454609e4a3396e5389bc1a549b3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F74cdc67c-7ed2-4eb0-bf73-b9a23ae82f08%2FUntitled.png?table=block&amp;id=1d9ee454-609e-4a33-96e5-389bc1a549b3&amp;t=1d9ee454-609e-4a33-96e5-389bc1a549b3&amp;width=1079&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-85c0163276684f08a1f6686b5cb19b70">在析构时保存：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e329866b93ea47ef9003ceef6910b99d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F56919962-8b3e-4fa9-8726-8a8d6a0beb47%2FUntitled.png?table=block&amp;id=e329866b-93ea-47ef-9003-ceef6910b99d&amp;t=e329866b-93ea-47ef-9003-ceef6910b99d&amp;width=1275&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-c13a803a23844f25baf8ca26a133e94f">除此之外，运行时也会自动保存：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e9ae7ca084ce4f8b837047c51a894542"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ff43e9b75-5434-4dd3-8bba-1e58958105b1%2FUntitled.png?table=block&amp;id=e9ae7ca0-84ce-4f8b-8370-47c51a894542&amp;t=e9ae7ca0-84ce-4f8b-8370-47c51a894542&amp;width=2115&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-8747ebe3647d4d97912694d2b600d692">同样需要开启<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code> 这个flag,当然它默认在shipping中是打开的。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-f4fb9bc372194af7bedb9374ac66f2c7" data-id="f4fb9bc372194af7bedb9374ac66f2c7"><span><div id="f4fb9bc372194af7bedb9374ac66f2c7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f4fb9bc372194af7bedb9374ac66f2c7" title="结论"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">结论</span></span></h3><div class="notion-text notion-block-2025eb4ab6284dc8ac574aab58032d8c">结论是，载入时，一定会载入<code class="notion-inline-code">\Content\PipelineCaches\Windows\XXX_PCD3D_SM5.stable.upipelinecache</code> 如果存在的话。</div><div class="notion-text notion-block-f6277599d15f402f972fdd46bf9f13d0">如果开了<code class="notion-inline-code">r.ShaderPipelineCache.LogPSO</code>和<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code>，则还会载入本地缓存的<code class="notion-inline-code">\Saved\xxx_PCD3D_SM5.upipelinecache</code> 。</div><div class="notion-text notion-block-eb124916adb94f519247a5ee66e66e9a">关于保存，如果开启了<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code> ，会尝试去保存，是否能保存还取决于 <code class="notion-inline-code">r.ShaderPipelineCache.Enabled</code> <code class="notion-inline-code">r.ShaderPipelineCache.LogPSO</code> ，结果会保存在<code class="notion-inline-code">\Saved\XXX_PCD3D_SM5.upipelinecache</code> 。触发保存的时机分别在<code class="notion-inline-code">FShaderPipelineCache::Tick()</code><span style="padding:0.5em"></span><code class="notion-inline-code">FShaderPipelineCache::Close()</code> ,<code class="notion-inline-code">FShaderPipelineCache::~FShaderPipelineCache()</code> 以及<code class="notion-inline-code">PipelineStateCacheOnAppDeactivate()</code>。</div><div class="notion-blank notion-block-fa0254bada8e4437be829fb42b1034a3"> </div><div class="notion-text notion-block-ea0b17c52b0a4ff6af7cac6a4fc152f8">总结而言，默认是一定会载入<code class="notion-inline-code">XXX_PCD3D_SM5.stable.upipelinecache</code> ，运行时产生的新PSO会保存在<code class="notion-inline-code">\Saved\XXX_PCD3D_SM5.upipelinecache</code> ，但条件是开启了 <code class="notion-inline-code">r.ShaderPipelineCache.Enabled</code>，<code class="notion-inline-code">r.ShaderPipelineCache.LogPSO</code> 以及<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code> 。</div><div class="notion-text notion-block-73c1d4dff8de4cc2ae5fe9ef0b55f853">我认为UE预期shipping版本开启这项保存PSO的功能，同时开发者应该尽量收集更多到PSO到<code class="notion-inline-code">stable.upipelinecache</code> 中，玩家再帮忙收集一部分做到本地缓存更新<code class="notion-inline-code">\Saved\XXX_PCD3D_SM5.upipelinecache</code>，就会越来越不卡顿。除此之外，也可以把<code class="notion-inline-code">XXX_PCD3D_SM5.upipelinecache</code>发给开发者进行合并。</div><div class="notion-blank notion-block-2e5b1d0052a742258824d35c5915536a"> </div><div class="notion-text notion-block-89266c4773b34cb0ad31968cf817efb1">那么目前这些功能在Shipping版本可以看到代码里是开的。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-5b627ec3e9a644588af3ce27e41c051e" data-id="5b627ec3e9a644588af3ce27e41c051e"><span><div id="5b627ec3e9a644588af3ce27e41c051e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#5b627ec3e9a644588af3ce27e41c051e" title="验证"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">验证</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-78a0665565094ca2a0bf848dbdce0e38" data-id="78a0665565094ca2a0bf848dbdce0e38"><span><div id="78a0665565094ca2a0bf848dbdce0e38" class="notion-header-anchor"></div><a class="notion-hash-link" href="#78a0665565094ca2a0bf848dbdce0e38" title="Test Build"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Test Build</span></span></h4><div class="notion-text notion-block-bbb5cb7376fa42b3bf47c0dee7fceac0">在<b>Test Build</b>中默认<code class="notion-inline-code">r.ShaderPipelineCache.LogPSO</code> 和<code class="notion-inline-code">r.ShaderPipelineCache.SaveUserCache</code>  没开，导致在运行时无法存储/生成<code class="notion-inline-code">\Saved\XXX_PCD3D_SM5.upipelinecache</code> 。</div><div class="notion-text notion-block-b9b1b31b8aca44269f8bfb7e2a9bfed6">所以的content里的文件都被加密打包成<code class="notion-inline-code">.pak</code> 文件，<code class="notion-inline-code">XXX_PCD3D_SM5.stable.upipelinecache</code> 同样在其中，能被成功读入(可从本地log验证）。</div><div class="notion-text notion-block-5a7638060de24e4faba44bcaee0b7c49">单纯增加<code class="notion-inline-code">-logpso</code>并不会触发保存。</div><div class="notion-text notion-block-02a2efd4c768492c8ee2814b75b32273">一个可行的方案是增加如下命令行参数：</div><div class="notion-text notion-block-d7edea8ce9a64079b1b544fd1881aa17">可以在登陆界面通过console检查到对应变量的开启。</div><div class="notion-text notion-block-2a826cd189464b30bc0e4941ec72892d">然而这一命令却无法触发打开游戏时本地cache的加载，并没有预期之中的<code class="notion-inline-code">LogRHI: Opened FPipelineCacheFile: ../../../xxx/Saved/XXX_PCD3D_SM5.upipelinecache</code> 。</div><div class="notion-text notion-block-31e34060c02d42239c2b93a5a0a8dadf">这是因为，由于UE_SHIPPING=0导致两个flag被赋值为0（<code class="notion-inline-code">&gt;FEngineLoop::PreInitPreStartupScreen()</code>），紧接着发生了OpenPipelineCache()(<code class="notion-inline-code">PreInitPostStartupScreen()</code>)，此时自然无法读取，推测后面才发生了命令行的flag赋值。</div><div class="notion-text notion-block-9e9f6d74a18648a38ec7ac983f182eb2">结论是，<b>Test Build</b><span class="notion-inline-underscore">在代码不做改动的情况下，可以通过增加命令行参数收集PSO，但无法正常载入本地PSO缓存</span>。</div><div class="notion-blank notion-block-e74b1b0ee6eb4efc9f51418879e84165"> </div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-7f13ec3729b14bb1972876a1023b5ebc" data-id="7f13ec3729b14bb1972876a1023b5ebc"><span><div id="7f13ec3729b14bb1972876a1023b5ebc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7f13ec3729b14bb1972876a1023b5ebc" title="Shipping Build"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Shipping Build</span></span></h4><div class="notion-text notion-block-db150481b94149d49d7c0ee2b3d4f75f">Jenkins上的太慢，本地先build了一个只有登录界面的shipping.</div><div class="notion-text notion-block-7b9af1b9edfb4b67996e9148d217ec45"><b>第一次</b>打开客户端，经检查，默认开启两个flag.</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-c4ee083246574edd96e54a5648499ddf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:537px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F1e394169-18ed-4ade-bc0e-b0110a4b5118%2FUntitled.png?table=block&amp;id=c4ee0832-4657-4edd-96e5-4a5648499ddf&amp;t=c4ee0832-4657-4edd-96e5-4a5648499ddf&amp;width=537&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-d7075138aa7e4f6788436a2fd4899b59">检查log,没有发现类似<code class="notion-inline-code">LogRHI: Opened FPipelineCacheFile: ../../../xxx/Saved/XXX_PCD3D_SM5.upipelinecache</code> 的信息。</div><div class="notion-text notion-block-54aeb82a28464a35b6698893004944c2">符合预期，因为全新版本并没有这个本地缓存文件，会生成。</div><div class="notion-text notion-block-d883a2bd43874cccb7318273291c265f">同时在Saved 文件夹中可发现有新创建的<code class="notion-inline-code">xxx_PCD3D_SM5.upipelinecache</code> 。</div><div class="notion-text notion-block-018d87e281fe44e0ba385ba92d1bc2b9"><b>第二次</b>打开客户端，发现读取本地缓存的Log:</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-485f287caa994630b7273eda1be38225"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fff223c6d-6854-4339-b2bd-4284d41b60c0%2FUntitled.png?table=block&amp;id=485f287c-aa99-4630-b727-3eda1be38225&amp;t=485f287c-aa99-4630-b727-3eda1be38225&amp;width=2192&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-4e9427c7947b4e5bb631ee7d9bc6bff1">符合预期。</div><div class="notion-text notion-block-95d6f4270fc8443aa5fb9ba52b818342">虽然有console可以用很诡异，但我很确定编译时选的是shipping.</div><div class="notion-blank notion-block-f87e56cc39b94d119d0f0d68bae4376c"> </div><div class="notion-text notion-block-f38e86b440b44a2180bdebfb6941c7cf">接下来直接看看服务器做出的版本吧。。</div><div class="notion-text notion-block-ae749c7b235b40e18e425325c0fd0c4b"><b>第一次</b>打开客户端</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-686226d08af94f00a5124394c38f2ee2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F1496af85-ccd3-4170-a5fb-a609667ef4a3%2FUntitled.png?table=block&amp;id=686226d0-8af9-4f00-a512-4394c38f2ee2&amp;t=686226d0-8af9-4f00-a512-4394c38f2ee2&amp;width=2198&amp;cache=v2" alt="试图打开本地缓存，间接说明了LogPSO和SaveUserCache是开启的。" loading="lazy" decoding="async"/><figcaption class="notion-asset-caption">试图打开本地缓存，间接说明了LogPSO和SaveUserCache是开启的。</figcaption></div></figure><div class="notion-text notion-block-2219a34ca9e043e1b2bfa0ddf571a42b">也新创建了本地缓存</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-08a0addb96794ae58fd651a5b0634d78"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F1ad3d7a6-5fa8-479d-a040-5ce3bc61c21d%2FUntitled.png?table=block&amp;id=08a0addb-9679-4ae5-8fd6-51a5b0634d78&amp;t=08a0addb-9679-4ae5-8fd6-51a5b0634d78&amp;width=1022&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-0f087416b4fb4d14b453e565ae4296cf">在Haven里逛该之后，准备退出了，看一下目前PSO的情况：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-5557c435f438453ab56cb4a64679f84c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fcd1aac2c-e85d-4b87-8abb-d0ee33cf5654%2FUntitled.png?table=block&amp;id=5557c435-f438-453a-b56c-b4a64679f84c&amp;t=5557c435-f438-453a-b56c-b4a64679f84c&amp;width=1035&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-c789d87b45894d25aa48c43d2ad0f7ed"><b>第二次</b>打开客户端，发现读取本地缓存的Log:</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-de2f59198f3844aea25898571abcff22"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fc35385e4-21a5-4461-a47f-956a60702bab%2FUntitled.png?table=block&amp;id=de2f5919-8f38-44ae-a258-98571abcff22&amp;t=de2f5919-8f38-44ae-a258-98571abcff22&amp;width=2381&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1bd64333ceec4ad69f6ca02dfcff5211">在登录界面看PSO的情况：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-f6cda9d278534b2d8bf409a5d2fd0b1a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F53e65fe8-096a-4180-b9b7-35c85b2dfb9e%2FUntitled.png?table=block&amp;id=f6cda9d2-7853-4b2d-8bf4-09a5d2fd0b1a&amp;t=f6cda9d2-7853-4b2d-8bf4-09a5d2fd0b1a&amp;width=1038&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2e639d15bf534c6585661c960a320942">数目对的上，说明读取成功，没有新增的PSO。</div><div class="notion-text notion-block-09627df48fee44e6ad5ff0b33d865dbc">接下来去haven逛该看看涨不涨</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-6011effdc1dc447ba8e18a564ab665de"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fabc7c16b-af36-4c18-ae5a-cf912f45f1b0%2FUntitled.png?table=block&amp;id=6011effd-c1dc-447b-a8e1-8a564ab665de&amp;t=6011effd-c1dc-447b-a8e1-8a564ab665de&amp;width=1073&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-d0224a286b2b43b994580ab275c74956">只有很微小的两个，主要是因为登陆时碰到了7日礼包界面，真的服咯，再开一遍。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-75fc10e253e649649a9ad9ba6645a7bd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Ff336196c-b648-49b1-b70c-26a3d7c8d16b%2FUntitled.png?table=block&amp;id=75fc10e2-53e6-4964-9a9a-d9ba6645a7bd&amp;t=75fc10e2-53e6-4964-9a9a-d9ba6645a7bd&amp;width=1088&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-d7e9ac4728934da9bd75f26142e2869f">OK，没有新增，证明Shipping版本的PSO载入和保存机制如上所言。注意编版本时一定要选shipping,虽然出来的也有命令行，但Test的PSO是没法读入本地缓存的。</div><div class="notion-blank notion-block-64efdf9db3864807a687c4ee023be262"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[UE4中通过代码创建贴图]]></title>
            <link>https://www.morningheart.com/article/d128b11e-74c5-4003-979a-5bde9f5aa10d</link>
            <guid>https://www.morningheart.com/article/d128b11e-74c5-4003-979a-5bde9f5aa10d</guid>
            <pubDate>Fri, 15 Sep 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-d128b11e74c54003979a5bde9f5aa10d"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-blank notion-block-4bca7f755cd148009095f76df0911d88"> </div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>