1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "render_node_util.h"
17 
18 #include <algorithm>
19 
20 #include <render/device/intf_gpu_resource_manager.h>
21 #include <render/device/intf_shader_manager.h>
22 #include <render/device/pipeline_state_desc.h>
23 #include <render/namespace.h>
24 #include <render/nodecontext/intf_node_context_descriptor_set_manager.h>
25 #include <render/nodecontext/intf_pipeline_descriptor_set_binder.h>
26 #include <render/nodecontext/intf_render_node.h>
27 #include <render/nodecontext/intf_render_node_context_manager.h>
28 #include <render/nodecontext/intf_render_node_graph_share_manager.h>
29 #include <render/nodecontext/intf_render_node_util.h>
30 
31 #include "datastore/render_data_store_post_process.h"
32 #include "device/gpu_resource_handle_util.h"
33 #include "nodecontext/pipeline_descriptor_set_binder.h"
34 #include "util/log.h"
35 
36 using namespace BASE_NS;
37 
38 RENDER_BEGIN_NAMESPACE()
39 namespace {
GetRoutedResource(const IRenderNodeGpuResourceManager & gpuResourceMgr,const IRenderNodeGraphShareManager & rngShareMgr,const RenderDataConstants::RenderDataFixedString & name,const RenderDataConstants::RenderDataFixedString & nodeName,const RenderNodeGraphResourceLocationType resourceLocation,const uint32_t resourceIndex,const RenderHandleType handleType)40 RenderHandle GetRoutedResource(const IRenderNodeGpuResourceManager& gpuResourceMgr,
41     const IRenderNodeGraphShareManager& rngShareMgr, const RenderDataConstants::RenderDataFixedString& name,
42     const RenderDataConstants::RenderDataFixedString& nodeName,
43     const RenderNodeGraphResourceLocationType resourceLocation, const uint32_t resourceIndex,
44     const RenderHandleType handleType)
45 {
46     RenderHandle handle;
47     switch (resourceLocation) {
48         case (RenderNodeGraphResourceLocationType::DEFAULT):
49             if (handleType == RenderHandleType::GPU_BUFFER) {
50                 handle = gpuResourceMgr.GetBufferHandle(name);
51             } else if (handleType == RenderHandleType::GPU_IMAGE) {
52                 handle = gpuResourceMgr.GetImageHandle(name);
53             } else {
54                 handle = gpuResourceMgr.GetSamplerHandle(name);
55             }
56             break;
57         case (RenderNodeGraphResourceLocationType::FROM_RENDER_GRAPH_INPUT):
58             handle = rngShareMgr.GetRenderNodeGraphInput(resourceIndex);
59             break;
60         case (RenderNodeGraphResourceLocationType::FROM_RENDER_GRAPH_OUTPUT):
61             handle = rngShareMgr.GetRenderNodeGraphOutput(resourceIndex);
62             break;
63         case (RenderNodeGraphResourceLocationType::FROM_PREVIOUS_RENDER_NODE_OUTPUT):
64             handle = rngShareMgr.GetRegisteredPrevRenderNodeOutput(resourceIndex);
65             break;
66         case (RenderNodeGraphResourceLocationType::FROM_NAMED_RENDER_NODE_OUTPUT):
67             if (!name.empty()) {
68                 handle = rngShareMgr.GetRegisteredRenderNodeOutput(nodeName, name);
69             } else {
70                 handle = rngShareMgr.GetRegisteredRenderNodeOutput(nodeName, resourceIndex);
71             }
72             break;
73         case (RenderNodeGraphResourceLocationType::FROM_PREVIOUS_RENDER_NODE_GRAPH_OUTPUT):
74             if (!name.empty()) {
75                 handle = rngShareMgr.GetNamedPrevRenderNodeGraphOutput(name);
76             } else {
77                 handle = rngShareMgr.GetPrevRenderNodeGraphOutput(resourceIndex);
78             }
79             break;
80         default:
81             break;
82     }
83 #if (RENDER_VALIDATION_ENABLED == 1)
84     if (!RenderHandleUtil::IsValid(handle)) {
85         PLUGIN_LOG_ONCE_W(string(nodeName + name),
86             "RENDER_VALIDATION: named render node GPU resource handle not found (name:%s) (type:%u)", name.c_str(),
87             static_cast<uint32_t>(handleType));
88     }
89 #endif
90     return handle;
91 }
92 
SetupRenderNodeResourceHandles(const IRenderNodeGpuResourceManager & gpuResourceMgr,const IRenderNodeGraphShareManager & rngShareMgr,const RenderNodeGraphInputs::InputResources & resources,RenderNodeHandles::InputResources & res)93 void SetupRenderNodeResourceHandles(const IRenderNodeGpuResourceManager& gpuResourceMgr,
94     const IRenderNodeGraphShareManager& rngShareMgr, const RenderNodeGraphInputs::InputResources& resources,
95     RenderNodeHandles::InputResources& res)
96 {
97     const auto setHandles = [](const IRenderNodeGpuResourceManager& gpuResourceMgr,
98                                 const IRenderNodeGraphShareManager& rngShareMgr, const RenderHandleType handleType,
99                                 const auto& input, auto& output) {
100         output.reserve(input.size());
101         for (const auto& ref : input) {
102             const RenderHandle handle = GetRoutedResource(gpuResourceMgr, rngShareMgr, ref.name, ref.nodeName,
103                 ref.resourceLocation, ref.resourceIndex, handleType);
104             output.push_back(RenderNodeResource { ref.set, ref.binding, handle, {}, ref.mip, ref.layer });
105         }
106     };
107     const auto setImageHandles = [](const IRenderNodeGpuResourceManager& gpuResourceMgr,
108                                      const IRenderNodeGraphShareManager& rngShareMgr,
109                                      vector<RenderNodeResource>& optionalSamplers, auto& input, auto& output) {
110         output.reserve(input.size());
111         for (const auto& ref : input) {
112             const RenderHandle handle = GetRoutedResource(gpuResourceMgr, rngShareMgr, ref.name, ref.nodeName,
113                 ref.resourceLocation, ref.resourceIndex, RenderHandleType::GPU_IMAGE);
114             RenderHandle secondHandle;
115             for (auto sampIter = optionalSamplers.begin(); sampIter != optionalSamplers.end();) {
116                 if ((sampIter->set == ref.set) && (sampIter->binding == ref.binding)) {
117                     secondHandle = sampIter->handle;
118                     optionalSamplers.erase(sampIter);
119                 } else {
120                     sampIter++;
121                 }
122             }
123             output.push_back(RenderNodeResource { ref.set, ref.binding, handle, secondHandle, ref.mip, ref.layer });
124         }
125     };
126 
127     setHandles(gpuResourceMgr, rngShareMgr, RenderHandleType::GPU_BUFFER, resources.buffers, res.buffers);
128     setHandles(gpuResourceMgr, rngShareMgr, RenderHandleType::GPU_SAMPLER, resources.samplers, res.samplers);
129     // loops through samplers for possible combined image sampler (this method is usually only called in
130     // RenderNodeInit())
131     // If found, moves the sampler handle from res.samplers to res.images.secondHandle
132     setImageHandles(gpuResourceMgr, rngShareMgr, res.samplers, resources.images, res.images);
133 
134     setHandles(gpuResourceMgr, rngShareMgr, RenderHandleType::GPU_BUFFER, resources.customInputBuffers,
135         res.customInputBuffers);
136     setHandles(gpuResourceMgr, rngShareMgr, RenderHandleType::GPU_BUFFER, resources.customOutputBuffers,
137         res.customOutputBuffers);
138 
139     vector<RenderNodeResource> sams;
140     setImageHandles(gpuResourceMgr, rngShareMgr, sams, resources.customInputImages, res.customInputImages);
141     setImageHandles(gpuResourceMgr, rngShareMgr, sams, resources.customOutputImages, res.customOutputImages);
142 }
143 } // namespace
144 
RenderNodeUtil(IRenderNodeContextManager & renderNodeContextMgr)145 RenderNodeUtil::RenderNodeUtil(IRenderNodeContextManager& renderNodeContextMgr)
146     : renderNodeContextMgr_(renderNodeContextMgr)
147 {}
148 
CreateInputRenderPass(const RenderNodeGraphInputs::InputRenderPass & renderPass) const149 RenderNodeHandles::InputRenderPass RenderNodeUtil::CreateInputRenderPass(
150     const RenderNodeGraphInputs::InputRenderPass& renderPass) const
151 {
152     RenderNodeHandles::InputRenderPass rp;
153     const auto& gpuResourceMgr = renderNodeContextMgr_.GetGpuResourceManager();
154     const auto& rngShareMgr = renderNodeContextMgr_.GetRenderNodeGraphShareManager();
155     if (!renderPass.attachments.empty()) {
156         rp.attachments.reserve(renderPass.attachments.size());
157         for (const auto& ref : renderPass.attachments) {
158             const RenderHandle handle = GetRoutedResource(gpuResourceMgr, rngShareMgr, ref.name, ref.nodeName,
159                 ref.resourceLocation, ref.resourceIndex, RenderHandleType::GPU_IMAGE);
160             rp.attachments.push_back(RenderNodeAttachment { handle, ref.loadOp, ref.storeOp, ref.stencilLoadOp,
161                 ref.stencilStoreOp, ref.clearValue, ref.mip, ref.layer });
162         }
163 
164         rp.subpassIndex = renderPass.subpassIndex;
165         rp.subpassCount = renderPass.subpassCount;
166         rp.subpassContents = renderPass.subpassContents;
167 
168         rp.depthAttachmentIndex = renderPass.depthAttachmentIndex;
169         rp.depthResolveAttachmentIndex = renderPass.depthResolveAttachmentIndex;
170         rp.inputAttachmentIndices = renderPass.inputAttachmentIndices;
171         rp.colorAttachmentIndices = renderPass.colorAttachmentIndices;
172         rp.resolveAttachmentIndices = renderPass.resolveAttachmentIndices;
173         rp.fragmentShadingRateAttachmentIndex = renderPass.fragmentShadingRateAttachmentIndex;
174         rp.depthResolveModeFlagBit = renderPass.depthResolveModeFlagBit;
175         rp.stencilResolveModeFlagBit = renderPass.stencilResolveModeFlagBit;
176         rp.shadingRateTexelSize = renderPass.shadingRateTexelSize;
177         rp.viewMask = renderPass.viewMask;
178     }
179 
180     return rp;
181 }
182 
CreateInputResources(const RenderNodeGraphInputs::InputResources & resources) const183 RenderNodeHandles::InputResources RenderNodeUtil::CreateInputResources(
184     const RenderNodeGraphInputs::InputResources& resources) const
185 {
186     RenderNodeHandles::InputResources res;
187     const auto& gpuResourceMgr = renderNodeContextMgr_.GetGpuResourceManager();
188     const auto& rngShareMgr = renderNodeContextMgr_.GetRenderNodeGraphShareManager();
189     SetupRenderNodeResourceHandles(gpuResourceMgr, rngShareMgr, resources, res);
190     return res;
191 }
192 
CreatePipelineLayout(const RenderHandle & shaderHandle) const193 PipelineLayout RenderNodeUtil::CreatePipelineLayout(const RenderHandle& shaderHandle) const
194 {
195     const auto& shaderMgr = renderNodeContextMgr_.GetShaderManager();
196     RenderHandle plHandle = shaderMgr.GetPipelineLayoutHandleByShaderHandle(shaderHandle);
197     if (RenderHandleUtil::IsValid(plHandle)) {
198         return shaderMgr.GetPipelineLayout(plHandle);
199     } else {
200         return shaderMgr.GetReflectionPipelineLayout(shaderHandle);
201     }
202 }
203 
CreatePipelineDescriptorSetBinder(const PipelineLayout & pipelineLayout) const204 IPipelineDescriptorSetBinder::Ptr RenderNodeUtil::CreatePipelineDescriptorSetBinder(
205     const PipelineLayout& pipelineLayout) const
206 {
207     auto& descriptorSetMgr = renderNodeContextMgr_.GetDescriptorSetManager();
208     return descriptorSetMgr.CreatePipelineDescriptorSetBinder(pipelineLayout);
209 }
210 
BindResourcesToBinder(const RenderNodeHandles::InputResources & resources,IPipelineDescriptorSetBinder & pipelineDescriptorSetBinder) const211 void RenderNodeUtil::BindResourcesToBinder(
212     const RenderNodeHandles::InputResources& resources, IPipelineDescriptorSetBinder& pipelineDescriptorSetBinder) const
213 {
214     pipelineDescriptorSetBinder.ClearBindings();
215     for (const auto& ref : resources.buffers) {
216         if (RenderHandleUtil::IsValid(ref.handle)) {
217             BindableBuffer bindable;
218             bindable.handle = ref.handle;
219             pipelineDescriptorSetBinder.BindBuffer(ref.set, ref.binding, bindable);
220         }
221     }
222     for (const auto& ref : resources.images) {
223         if (RenderHandleUtil::IsValid(ref.handle)) {
224             BindableImage bindable;
225             bindable.handle = ref.handle;
226             bindable.samplerHandle = ref.secondHandle; // might be combined image sampler if valid handle
227             bindable.mip = ref.mip;
228             bindable.layer = ref.layer;
229             pipelineDescriptorSetBinder.BindImage(ref.set, ref.binding, bindable);
230         }
231     }
232     for (const auto& ref : resources.samplers) {
233         if (RenderHandleUtil::IsValid(ref.handle)) {
234             BindableSampler bindable;
235             bindable.handle = ref.handle;
236             pipelineDescriptorSetBinder.BindSampler(ref.set, ref.binding, bindable);
237         }
238     }
239 }
240 
GetDescriptorCounts(const PipelineLayout & pipelineLayout) const241 DescriptorCounts RenderNodeUtil::GetDescriptorCounts(const PipelineLayout& pipelineLayout) const
242 {
243     DescriptorCounts dc;
244     for (uint32_t setIdx = 0; setIdx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++setIdx) {
245         const auto& setRef = pipelineLayout.descriptorSetLayouts[setIdx];
246         if (setRef.set == PipelineLayoutConstants::INVALID_INDEX) {
247             continue;
248         }
249         dc.counts.reserve(dc.counts.size() + setRef.bindings.size());
250         for (const auto& bindingRef : setRef.bindings) {
251             dc.counts.push_back(DescriptorCounts::TypedCount { bindingRef.descriptorType, bindingRef.descriptorCount });
252         }
253     }
254     return dc;
255 }
256 
GetDescriptorCounts(const array_view<DescriptorSetLayoutBinding> bindings) const257 DescriptorCounts RenderNodeUtil::GetDescriptorCounts(const array_view<DescriptorSetLayoutBinding> bindings) const
258 {
259     DescriptorCounts dc;
260     for (const auto& bindingRef : bindings) {
261         dc.counts.push_back(DescriptorCounts::TypedCount { bindingRef.descriptorType, bindingRef.descriptorCount });
262     }
263     return dc;
264 }
265 
CreateRenderPass(const RenderNodeHandles::InputRenderPass & renderPass) const266 RenderPass RenderNodeUtil::CreateRenderPass(const RenderNodeHandles::InputRenderPass& renderPass) const
267 {
268     RenderPass rp;
269     RenderPassDesc& rpDesc = rp.renderPassDesc;
270 
271     const uint32_t attachmentCount = static_cast<uint32_t>(renderPass.attachments.size());
272     PLUGIN_ASSERT(attachmentCount <= PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT);
273     uint32_t width = ~0u;
274     uint32_t height = ~0u;
275     if (attachmentCount > 0) {
276         rp.renderPassDesc.attachmentCount = attachmentCount;
277         for (size_t idx = 0; idx < renderPass.attachments.size(); ++idx) {
278             const auto& ref = renderPass.attachments[idx];
279             rpDesc.attachments[idx] = { ref.layer, ref.mip, ref.loadOp, ref.storeOp, ref.stencilLoadOp,
280                 ref.stencilStoreOp, ref.clearValue };
281             rpDesc.attachmentHandles[idx] = ref.handle;
282             if (idx == 0) { // optimization, width and height must match (will end in error later)
283                 const GpuImageDesc desc = renderNodeContextMgr_.GetGpuResourceManager().GetImageDescriptor(ref.handle);
284                 width = desc.width;
285                 height = desc.height;
286             }
287         }
288 
289         rpDesc.subpassContents = renderPass.subpassContents;
290         rpDesc.subpassCount = renderPass.subpassCount;
291         if (renderPass.subpassIndex >= renderPass.subpassCount) {
292             PLUGIN_LOG_E("Render pass subpass idx < count (%u < %u)", renderPass.subpassIndex, renderPass.subpassCount);
293         }
294         if (renderPass.subpassIndex < renderPass.subpassCount) {
295             rp.subpassStartIndex = renderPass.subpassIndex;
296             // update the one subpass described in render node graph
297             auto& spDesc = rp.subpassDesc;
298 
299             spDesc.inputAttachmentCount = static_cast<uint32_t>(renderPass.inputAttachmentIndices.size());
300             spDesc.colorAttachmentCount = static_cast<uint32_t>(renderPass.colorAttachmentIndices.size());
301             spDesc.resolveAttachmentCount = static_cast<uint32_t>(renderPass.resolveAttachmentIndices.size());
302 
303             spDesc.depthAttachmentCount = (renderPass.depthAttachmentIndex != ~0u) ? 1u : 0u;
304             spDesc.depthAttachmentIndex = (spDesc.depthAttachmentCount > 0) ? renderPass.depthAttachmentIndex : ~0u;
305             spDesc.depthResolveAttachmentCount = (renderPass.depthResolveAttachmentIndex != ~0u) ? 1u : 0u;
306             spDesc.depthResolveAttachmentIndex =
307                 (spDesc.depthResolveAttachmentCount > 0) ? renderPass.depthResolveAttachmentIndex : ~0u;
308             spDesc.depthResolveModeFlagBit = renderPass.depthResolveModeFlagBit;
309             spDesc.stencilResolveModeFlagBit = renderPass.stencilResolveModeFlagBit;
310 
311             spDesc.fragmentShadingRateAttachmentCount =
312                 (renderPass.fragmentShadingRateAttachmentIndex != ~0u) ? 1u : 0u;
313             spDesc.fragmentShadingRateAttachmentIndex =
314                 (spDesc.fragmentShadingRateAttachmentCount > 0) ? renderPass.fragmentShadingRateAttachmentIndex : ~0u;
315             spDesc.shadingRateTexelSize = renderPass.shadingRateTexelSize;
316             spDesc.viewMask = renderPass.viewMask;
317 
318             for (uint32_t idx = 0; idx < spDesc.inputAttachmentCount; ++idx) {
319                 spDesc.inputAttachmentIndices[idx] = renderPass.inputAttachmentIndices[idx];
320             }
321             for (uint32_t idx = 0; idx < spDesc.colorAttachmentCount; ++idx) {
322                 spDesc.colorAttachmentIndices[idx] = renderPass.colorAttachmentIndices[idx];
323             }
324             for (uint32_t idx = 0; idx < spDesc.resolveAttachmentCount; ++idx) {
325                 spDesc.resolveAttachmentIndices[idx] = renderPass.resolveAttachmentIndices[idx];
326             }
327         }
328     }
329     rpDesc.renderArea = { 0, 0, width, height };
330 
331     return rp;
332 }
333 
CreateDefaultViewport(const RenderPass & renderPass) const334 ViewportDesc RenderNodeUtil::CreateDefaultViewport(const RenderPass& renderPass) const
335 {
336     return ViewportDesc {
337         0.0f,
338         0.0f,
339         static_cast<float>(renderPass.renderPassDesc.renderArea.extentWidth),
340         static_cast<float>(renderPass.renderPassDesc.renderArea.extentHeight),
341         0.0f,
342         1.0f,
343     };
344 }
345 
CreateDefaultScissor(const RenderPass & renderPass) const346 ScissorDesc RenderNodeUtil::CreateDefaultScissor(const RenderPass& renderPass) const
347 {
348     return ScissorDesc {
349         0,
350         0,
351         renderPass.renderPassDesc.renderArea.extentWidth,
352         renderPass.renderPassDesc.renderArea.extentHeight,
353     };
354 }
355 
GetRenderPostProcessConfiguration(const PostProcessConfiguration & input) const356 RenderPostProcessConfiguration RenderNodeUtil::GetRenderPostProcessConfiguration(
357     const PostProcessConfiguration& input) const
358 {
359     RenderPostProcessConfiguration output;
360     std::fill(output.factors, output.factors + PostProcessConfiguration::INDEX_FACTOR_COUNT,
361         Math::Vec4 { 0.0f, 0.0f, 0.0f, 0.0f });
362 
363     output.flags = { input.enableFlags, 0, 0, 0 };
364     output.renderTimings = renderNodeContextMgr_.GetRenderNodeGraphData().renderingConfiguration.renderTimings;
365     output.factors[PostProcessConfiguration::INDEX_TONEMAP] = PostProcessConversionHelper::GetFactorTonemap(input);
366     output.factors[PostProcessConfiguration::INDEX_VIGNETTE] = PostProcessConversionHelper::GetFactorVignette(input);
367     output.factors[PostProcessConfiguration::INDEX_DITHER] = PostProcessConversionHelper::GetFactorDither(input);
368     output.factors[PostProcessConfiguration::INDEX_COLOR_CONVERSION] =
369         PostProcessConversionHelper::GetFactorColorConversion(input);
370     output.factors[PostProcessConfiguration::INDEX_COLOR_FRINGE] = PostProcessConversionHelper::GetFactorFringe(input);
371 
372     output.factors[PostProcessConfiguration::INDEX_BLUR] = PostProcessConversionHelper::GetFactorBlur(input);
373     output.factors[PostProcessConfiguration::INDEX_BLOOM] = PostProcessConversionHelper::GetFactorBloom(input);
374     output.factors[PostProcessConfiguration::INDEX_FXAA] = PostProcessConversionHelper::GetFactorFxaa(input);
375     output.factors[PostProcessConfiguration::INDEX_TAA] = PostProcessConversionHelper::GetFactorTaa(input);
376     output.factors[PostProcessConfiguration::INDEX_DOF] = PostProcessConversionHelper::GetFactorDof(input);
377     output.factors[PostProcessConfiguration::INDEX_MOTION_BLUR] =
378         PostProcessConversionHelper::GetFactorMotionBlur(input);
379 
380     BASE_NS::CloneData(output.userFactors, sizeof(output.userFactors), input.userFactors, sizeof(input.userFactors));
381 
382     return output;
383 }
384 
GetRenderPostProcessConfiguration(const IRenderDataStorePostProcess::GlobalFactors & globalFactors) const385 RenderPostProcessConfiguration RenderNodeUtil::GetRenderPostProcessConfiguration(
386     const IRenderDataStorePostProcess::GlobalFactors& globalFactors) const
387 {
388     RenderPostProcessConfiguration output;
389 
390     output.flags = { globalFactors.enableFlags, 0, 0, 0 };
391     output.renderTimings = renderNodeContextMgr_.GetRenderNodeGraphData().renderingConfiguration.renderTimings;
392 
393     BASE_NS::CloneData(output.factors, sizeof(output.factors), globalFactors.factors, sizeof(globalFactors.factors));
394     BASE_NS::CloneData(
395         output.userFactors, sizeof(output.userFactors), globalFactors.userFactors, sizeof(globalFactors.userFactors));
396 
397     return output;
398 }
399 
HasChangeableResources(const RenderNodeGraphInputs::InputRenderPass & renderPass) const400 bool RenderNodeUtil::HasChangeableResources(const RenderNodeGraphInputs::InputRenderPass& renderPass) const
401 {
402     bool changeable = false;
403     for (const auto& ref : renderPass.attachments) {
404         if (ref.resourceLocation != RenderNodeGraphResourceLocationType::DEFAULT) {
405             changeable = true;
406         }
407     }
408     return changeable;
409 }
410 
HasChangeableResources(const RenderNodeGraphInputs::InputResources & resources) const411 bool RenderNodeUtil::HasChangeableResources(const RenderNodeGraphInputs::InputResources& resources) const
412 {
413     auto HasChangingResources = [](const auto& vec) {
414         bool changeable = false;
415         for (const auto& ref : vec) {
416             if (ref.resourceLocation != RenderNodeGraphResourceLocationType::DEFAULT) {
417                 changeable = true;
418             }
419         }
420         return changeable;
421     };
422     bool changeable = false;
423     changeable = changeable || HasChangingResources(resources.buffers);
424     changeable = changeable || HasChangingResources(resources.images);
425     changeable = changeable || HasChangingResources(resources.samplers);
426     changeable = changeable || HasChangingResources(resources.customInputBuffers);
427     changeable = changeable || HasChangingResources(resources.customOutputBuffers);
428     changeable = changeable || HasChangingResources(resources.customInputImages);
429     changeable = changeable || HasChangingResources(resources.customOutputImages);
430     return changeable;
431 }
432 RENDER_END_NAMESPACE()
433