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