• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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         rp.subpassFlags = renderPass.subpassFlags;
168 
169         rp.depthAttachmentIndex = renderPass.depthAttachmentIndex;
170         rp.depthResolveAttachmentIndex = renderPass.depthResolveAttachmentIndex;
171         rp.inputAttachmentIndices = renderPass.inputAttachmentIndices;
172         rp.colorAttachmentIndices = renderPass.colorAttachmentIndices;
173         rp.resolveAttachmentIndices = renderPass.resolveAttachmentIndices;
174         rp.fragmentShadingRateAttachmentIndex = renderPass.fragmentShadingRateAttachmentIndex;
175         rp.depthResolveModeFlags = renderPass.depthResolveModeFlags;
176         rp.stencilResolveModeFlags = renderPass.stencilResolveModeFlags;
177         rp.shadingRateTexelSize = renderPass.shadingRateTexelSize;
178         rp.viewMask = renderPass.viewMask;
179     }
180 
181     return rp;
182 }
183 
CreateInputResources(const RenderNodeGraphInputs::InputResources & resources) const184 RenderNodeHandles::InputResources RenderNodeUtil::CreateInputResources(
185     const RenderNodeGraphInputs::InputResources& resources) const
186 {
187     RenderNodeHandles::InputResources res;
188     const auto& gpuResourceMgr = renderNodeContextMgr_.GetGpuResourceManager();
189     const auto& rngShareMgr = renderNodeContextMgr_.GetRenderNodeGraphShareManager();
190     SetupRenderNodeResourceHandles(gpuResourceMgr, rngShareMgr, resources, res);
191     return res;
192 }
193 
CreatePipelineLayout(const RenderHandle & shaderHandle) const194 PipelineLayout RenderNodeUtil::CreatePipelineLayout(const RenderHandle& shaderHandle) const
195 {
196     const auto& shaderMgr = renderNodeContextMgr_.GetShaderManager();
197     RenderHandle plHandle = shaderMgr.GetPipelineLayoutHandleByShaderHandle(shaderHandle);
198     if (RenderHandleUtil::IsValid(plHandle)) {
199         return shaderMgr.GetPipelineLayout(plHandle);
200     } else {
201         return shaderMgr.GetReflectionPipelineLayout(shaderHandle);
202     }
203 }
204 
CreatePipelineDescriptorSetBinder(const PipelineLayout & pipelineLayout) const205 IPipelineDescriptorSetBinder::Ptr RenderNodeUtil::CreatePipelineDescriptorSetBinder(
206     const PipelineLayout& pipelineLayout) const
207 {
208     auto& descriptorSetMgr = renderNodeContextMgr_.GetDescriptorSetManager();
209     return descriptorSetMgr.CreatePipelineDescriptorSetBinder(pipelineLayout);
210 }
211 
BindResourcesToBinder(const RenderNodeHandles::InputResources & resources,IPipelineDescriptorSetBinder & pipelineDescriptorSetBinder) const212 void RenderNodeUtil::BindResourcesToBinder(
213     const RenderNodeHandles::InputResources& resources, IPipelineDescriptorSetBinder& pipelineDescriptorSetBinder) const
214 {
215     pipelineDescriptorSetBinder.ClearBindings();
216     for (const auto& ref : resources.buffers) {
217         if (RenderHandleUtil::IsValid(ref.handle)) {
218             BindableBuffer bindable;
219             bindable.handle = ref.handle;
220             pipelineDescriptorSetBinder.BindBuffer(ref.set, ref.binding, bindable);
221         }
222     }
223     for (const auto& ref : resources.images) {
224         if (RenderHandleUtil::IsValid(ref.handle)) {
225             BindableImage bindable;
226             bindable.handle = ref.handle;
227             bindable.samplerHandle = ref.secondHandle; // might be combined image sampler if valid handle
228             bindable.mip = ref.mip;
229             bindable.layer = ref.layer;
230             pipelineDescriptorSetBinder.BindImage(ref.set, ref.binding, bindable);
231         }
232     }
233     for (const auto& ref : resources.samplers) {
234         if (RenderHandleUtil::IsValid(ref.handle)) {
235             BindableSampler bindable;
236             bindable.handle = ref.handle;
237             pipelineDescriptorSetBinder.BindSampler(ref.set, ref.binding, bindable);
238         }
239     }
240 }
241 
GetDescriptorCounts(const PipelineLayout & pipelineLayout) const242 DescriptorCounts RenderNodeUtil::GetDescriptorCounts(const PipelineLayout& pipelineLayout) const
243 {
244     DescriptorCounts dc;
245     for (uint32_t setIdx = 0; setIdx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++setIdx) {
246         const auto& setRef = pipelineLayout.descriptorSetLayouts[setIdx];
247         if (setRef.set == PipelineLayoutConstants::INVALID_INDEX) {
248             continue;
249         }
250         dc.counts.reserve(dc.counts.size() + setRef.bindings.size());
251         for (const auto& bindingRef : setRef.bindings) {
252             dc.counts.push_back(DescriptorCounts::TypedCount { bindingRef.descriptorType, bindingRef.descriptorCount });
253         }
254     }
255     return dc;
256 }
257 
GetDescriptorCounts(const array_view<DescriptorSetLayoutBinding> bindings) const258 DescriptorCounts RenderNodeUtil::GetDescriptorCounts(const array_view<DescriptorSetLayoutBinding> bindings) const
259 {
260     DescriptorCounts dc;
261     for (const auto& bindingRef : bindings) {
262         dc.counts.push_back(DescriptorCounts::TypedCount { bindingRef.descriptorType, bindingRef.descriptorCount });
263     }
264     return dc;
265 }
266 
CreateRenderPass(const RenderNodeHandles::InputRenderPass & renderPass) const267 RenderPass RenderNodeUtil::CreateRenderPass(const RenderNodeHandles::InputRenderPass& renderPass) const
268 {
269     RenderPass rp;
270     RenderPassDesc& rpDesc = rp.renderPassDesc;
271 
272     const uint32_t attachmentCount = (uint32_t)renderPass.attachments.size();
273     PLUGIN_ASSERT(attachmentCount <= PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT);
274     uint32_t width = ~0u;
275     uint32_t height = ~0u;
276     if (attachmentCount > 0) {
277         rp.renderPassDesc.attachmentCount = attachmentCount;
278         for (size_t idx = 0; idx < renderPass.attachments.size(); ++idx) {
279             const auto& ref = renderPass.attachments[idx];
280             rpDesc.attachments[idx] = { ref.layer, ref.mip, ref.loadOp, ref.storeOp, ref.stencilLoadOp,
281                 ref.stencilStoreOp, ref.clearValue };
282             rpDesc.attachmentHandles[idx] = ref.handle;
283             if (idx == 0) { // optimization, width and height must match (will end in error later)
284                 const GpuImageDesc desc = renderNodeContextMgr_.GetGpuResourceManager().GetImageDescriptor(ref.handle);
285                 width = desc.width;
286                 height = desc.height;
287             }
288         }
289 
290         rpDesc.subpassContents = renderPass.subpassContents;
291         rpDesc.subpassCount = renderPass.subpassCount;
292         if (renderPass.subpassIndex >= renderPass.subpassCount) {
293             PLUGIN_LOG_E("Render pass subpass idx < count (%u < %u)", renderPass.subpassIndex, renderPass.subpassCount);
294         }
295         if (renderPass.subpassIndex < renderPass.subpassCount) {
296             rp.subpassStartIndex = renderPass.subpassIndex;
297             // update the one subpass described in render node graph
298             auto& spDesc = rp.subpassDesc;
299 
300             spDesc.inputAttachmentCount = (uint32_t)renderPass.inputAttachmentIndices.size();
301             spDesc.colorAttachmentCount = (uint32_t)renderPass.colorAttachmentIndices.size();
302             spDesc.resolveAttachmentCount = (uint32_t)renderPass.resolveAttachmentIndices.size();
303 
304             spDesc.depthAttachmentCount = (renderPass.depthAttachmentIndex != ~0u) ? 1u : 0u;
305             spDesc.depthAttachmentIndex = (spDesc.depthAttachmentCount > 0) ? renderPass.depthAttachmentIndex : ~0u;
306             spDesc.depthResolveAttachmentCount = (renderPass.depthResolveAttachmentIndex != ~0u) ? 1u : 0u;
307             spDesc.depthResolveAttachmentIndex =
308                 (spDesc.depthResolveAttachmentCount > 0) ? renderPass.depthResolveAttachmentIndex : ~0u;
309             spDesc.depthResolveModeFlags = renderPass.depthResolveModeFlags;
310             spDesc.stencilResolveModeFlags = renderPass.stencilResolveModeFlags;
311 
312             spDesc.fragmentShadingRateAttachmentCount =
313                 (renderPass.fragmentShadingRateAttachmentIndex != ~0u) ? 1u : 0u;
314             spDesc.fragmentShadingRateAttachmentIndex =
315                 (spDesc.fragmentShadingRateAttachmentCount > 0) ? renderPass.fragmentShadingRateAttachmentIndex : ~0u;
316             spDesc.shadingRateTexelSize = renderPass.shadingRateTexelSize;
317             spDesc.viewMask = renderPass.viewMask;
318 
319             spDesc.subpassFlags = renderPass.subpassFlags;
320 
321             for (uint32_t idx = 0; idx < spDesc.inputAttachmentCount; ++idx) {
322                 spDesc.inputAttachmentIndices[idx] = renderPass.inputAttachmentIndices[idx];
323             }
324             for (uint32_t idx = 0; idx < spDesc.colorAttachmentCount; ++idx) {
325                 spDesc.colorAttachmentIndices[idx] = renderPass.colorAttachmentIndices[idx];
326             }
327             for (uint32_t idx = 0; idx < spDesc.resolveAttachmentCount; ++idx) {
328                 spDesc.resolveAttachmentIndices[idx] = renderPass.resolveAttachmentIndices[idx];
329             }
330         }
331     }
332     rpDesc.renderArea = { 0, 0, width, height };
333 
334     return rp;
335 }
336 
CreateDefaultViewport(const RenderPass & renderPass) const337 ViewportDesc RenderNodeUtil::CreateDefaultViewport(const RenderPass& renderPass) const
338 {
339     return ViewportDesc {
340         0.0f,
341         0.0f,
342         static_cast<float>(renderPass.renderPassDesc.renderArea.extentWidth),
343         static_cast<float>(renderPass.renderPassDesc.renderArea.extentHeight),
344         0.0f,
345         1.0f,
346     };
347 }
348 
CreateDefaultScissor(const RenderPass & renderPass) const349 ScissorDesc RenderNodeUtil::CreateDefaultScissor(const RenderPass& renderPass) const
350 {
351     return ScissorDesc {
352         0,
353         0,
354         renderPass.renderPassDesc.renderArea.extentWidth,
355         renderPass.renderPassDesc.renderArea.extentHeight,
356     };
357 }
358 
GetRenderPostProcessConfiguration(const PostProcessConfiguration & input) const359 RenderPostProcessConfiguration RenderNodeUtil::GetRenderPostProcessConfiguration(
360     const PostProcessConfiguration& input) const
361 {
362     RenderPostProcessConfiguration output;
363     std::fill(output.factors, output.factors + PostProcessConfiguration::INDEX_FACTOR_COUNT,
364         Math::Vec4 { 0.0f, 0.0f, 0.0f, 0.0f });
365 
366     output.flags = { input.enableFlags, 0, 0, 0 };
367     output.renderTimings = renderNodeContextMgr_.GetRenderNodeGraphData().renderingConfiguration.renderTimings;
368     output.factors[PostProcessConfiguration::INDEX_TONEMAP] = PostProcessConversionHelper::GetFactorTonemap(input);
369     output.factors[PostProcessConfiguration::INDEX_VIGNETTE] = PostProcessConversionHelper::GetFactorVignette(input);
370     output.factors[PostProcessConfiguration::INDEX_DITHER] = PostProcessConversionHelper::GetFactorDither(input);
371     output.factors[PostProcessConfiguration::INDEX_COLOR_CONVERSION] =
372         PostProcessConversionHelper::GetFactorColorConversion(input);
373     output.factors[PostProcessConfiguration::INDEX_COLOR_FRINGE] = PostProcessConversionHelper::GetFactorFringe(input);
374 
375     output.factors[PostProcessConfiguration::INDEX_BLUR] = PostProcessConversionHelper::GetFactorBlur(input);
376     output.factors[PostProcessConfiguration::INDEX_BLOOM] = PostProcessConversionHelper::GetFactorBloom(input);
377     output.factors[PostProcessConfiguration::INDEX_FXAA] = PostProcessConversionHelper::GetFactorFxaa(input);
378     output.factors[PostProcessConfiguration::INDEX_TAA] = PostProcessConversionHelper::GetFactorTaa(input);
379     output.factors[PostProcessConfiguration::INDEX_DOF] = PostProcessConversionHelper::GetFactorDof(input);
380     output.factors[PostProcessConfiguration::INDEX_MOTION_BLUR] =
381         PostProcessConversionHelper::GetFactorMotionBlur(input);
382 
383     BASE_NS::CloneData(output.userFactors, sizeof(output.userFactors), input.userFactors, sizeof(input.userFactors));
384 
385     return output;
386 }
387 
GetRenderPostProcessConfiguration(const IRenderDataStorePostProcess::GlobalFactors & globalFactors) const388 RenderPostProcessConfiguration RenderNodeUtil::GetRenderPostProcessConfiguration(
389     const IRenderDataStorePostProcess::GlobalFactors& globalFactors) const
390 {
391     RenderPostProcessConfiguration output;
392 
393     output.flags = { globalFactors.enableFlags, 0, 0, 0 };
394     output.renderTimings = renderNodeContextMgr_.GetRenderNodeGraphData().renderingConfiguration.renderTimings;
395 
396     BASE_NS::CloneData(output.factors, sizeof(output.factors), globalFactors.factors, sizeof(globalFactors.factors));
397     BASE_NS::CloneData(
398         output.userFactors, sizeof(output.userFactors), globalFactors.userFactors, sizeof(globalFactors.userFactors));
399 
400     return output;
401 }
402 
HasChangeableResources(const RenderNodeGraphInputs::InputRenderPass & renderPass) const403 bool RenderNodeUtil::HasChangeableResources(const RenderNodeGraphInputs::InputRenderPass& renderPass) const
404 {
405     bool changeable = false;
406     for (const auto& ref : renderPass.attachments) {
407         if (ref.resourceLocation != RenderNodeGraphResourceLocationType::DEFAULT) {
408             changeable = true;
409         }
410     }
411     return changeable;
412 }
413 
HasChangeableResources(const RenderNodeGraphInputs::InputResources & resources) const414 bool RenderNodeUtil::HasChangeableResources(const RenderNodeGraphInputs::InputResources& resources) const
415 {
416     auto HasChangingResources = [](const auto& vec) {
417         bool changeable = false;
418         for (const auto& ref : vec) {
419             if (ref.resourceLocation != RenderNodeGraphResourceLocationType::DEFAULT) {
420                 changeable = true;
421             }
422         }
423         return changeable;
424     };
425     bool changeable = false;
426     changeable = changeable || HasChangingResources(resources.buffers);
427     changeable = changeable || HasChangingResources(resources.images);
428     changeable = changeable || HasChangingResources(resources.samplers);
429     changeable = changeable || HasChangingResources(resources.customInputBuffers);
430     changeable = changeable || HasChangingResources(resources.customOutputBuffers);
431     changeable = changeable || HasChangingResources(resources.customInputImages);
432     changeable = changeable || HasChangingResources(resources.customOutputImages);
433     return changeable;
434 }
435 RENDER_END_NAMESPACE()
436