• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "node_context_pso_manager.h"
17 
18 #include <cstdint>
19 
20 #include <base/containers/vector.h>
21 #include <base/util/hash.h>
22 #include <render/namespace.h>
23 #include <render/nodecontext/intf_node_context_pso_manager.h>
24 
25 #include "device/device.h"
26 #include "device/gpu_program.h"
27 #include "device/gpu_program_util.h"
28 #include "device/gpu_resource_handle_util.h"
29 #include "device/gpu_resource_manager.h"
30 #include "device/pipeline_state_object.h"
31 #include "device/shader_manager.h"
32 #include "util/log.h"
33 
34 template<>
hash(const RENDER_NS::ShaderSpecializationConstantDataView & specialization)35 uint64_t BASE_NS::hash(const RENDER_NS::ShaderSpecializationConstantDataView& specialization)
36 {
37     uint64_t seed = BASE_NS::FNV_OFFSET_BASIS;
38     if ((!specialization.data.empty()) && (!specialization.constants.empty())) {
39         const size_t minSize = BASE_NS::Math::min(specialization.constants.size(), specialization.data.size());
40         for (size_t idx = 0; idx < minSize; ++idx) {
41             const auto& currConstant = specialization.constants[idx];
42             const auto constantSize = RENDER_NS::GpuProgramUtil::SpecializationByteSize(currConstant.type);
43             if ((currConstant.offset + constantSize) <= specialization.data.size_bytes()) {
44                 const uint8_t* data = (const uint8_t*)specialization.data.data() + currConstant.offset;
45                 const size_t bytes = sizeof(uint64_t) < constantSize ? sizeof(uint64_t) : constantSize;
46                 HashCombine(seed, array_view(data, bytes));
47             }
48 #if (RENDER_VALIDATION_ENABLED == 1)
49             else {
50                 PLUGIN_LOG_E("RENDER_VALIDATION: shader specialization issue with constant and data size mismatch");
51             }
52 #endif
53         }
54     }
55     return seed;
56 }
57 
58 using namespace BASE_NS;
59 
60 RENDER_BEGIN_NAMESPACE()
61 namespace {
HashComputeShader(const RenderHandle shaderHandle,const ShaderSpecializationConstantDataView & shaderSpecialization)62 uint64_t HashComputeShader(
63     const RenderHandle shaderHandle, const ShaderSpecializationConstantDataView& shaderSpecialization)
64 {
65     return Hash(shaderHandle.id, shaderSpecialization);
66 }
67 
HashGraphicsShader(const RenderHandle shaderHandle,const RenderHandle graphicsStateHandle,const array_view<const DynamicStateEnum> dynamicStates,const ShaderSpecializationConstantDataView & shaderSpecialization,const uint64_t customGraphicsStateHash)68 uint64_t HashGraphicsShader(const RenderHandle shaderHandle, const RenderHandle graphicsStateHandle,
69     const array_view<const DynamicStateEnum> dynamicStates,
70     const ShaderSpecializationConstantDataView& shaderSpecialization, const uint64_t customGraphicsStateHash)
71 {
72     uint64_t hash = 0;
73     for (const auto& ref : dynamicStates) {
74         HashCombine(hash, static_cast<uint64_t>(ref));
75     }
76     return Hash(hash, shaderHandle.id, graphicsStateHandle.id, shaderSpecialization, customGraphicsStateHash);
77 }
78 
79 #if (RENDER_VALIDATION_ENABLED == 1)
validateSSO(ShaderManager & shaderMgr,const RenderHandle shaderHandle,const VertexInputDeclarationDataWrapper & vidw)80 void validateSSO(
81     ShaderManager& shaderMgr, const RenderHandle shaderHandle, const VertexInputDeclarationDataWrapper& vidw)
82 {
83     const GpuShaderProgram* gsp = shaderMgr.GetGpuShaderProgram(shaderHandle);
84     if (gsp) {
85         const auto& reflection = gsp->GetReflection();
86         const bool hasBindings = (reflection.vertexInputDeclarationView.bindingDescriptions.size() > 0);
87         const bool vidHasBindings = (vidw.bindingDescriptions.size() > 0);
88         if (hasBindings != vidHasBindings) {
89             PLUGIN_LOG_E(
90                 "RENDER_VALIDATION: vertex input declaration pso (bindings: %u) mismatch with shader reflection "
91                 "(bindings: %u)",
92                 static_cast<uint32_t>(vidw.bindingDescriptions.size()),
93                 static_cast<uint32_t>(reflection.vertexInputDeclarationView.bindingDescriptions.size()));
94         }
95     }
96 }
97 #endif
98 } // namespace
99 
NodeContextPsoManager(Device & device,ShaderManager & shaderManager)100 NodeContextPsoManager::NodeContextPsoManager(Device& device, ShaderManager& shaderManager)
101     : device_ { device }, shaderMgr_ { shaderManager }
102 {}
103 
BeginBackendFrame()104 void NodeContextPsoManager::BeginBackendFrame()
105 {
106     // destroy pending
107     const uint64_t frameCount = device_.GetFrameCount();
108     constexpr uint64_t additionalFrameCount { 2u };
109     const auto minAge = device_.GetCommandBufferingCount() + additionalFrameCount;
110     const auto ageLimit = (frameCount < minAge) ? 0 : (frameCount - minAge);
111     {
112         auto& gpCache = computePipelineStateCache_;
113         for (auto iter = gpCache.pendingPsoDestroys.begin(); iter != gpCache.pendingPsoDestroys.end();) {
114             if (iter->frameIndex < ageLimit) {
115                 iter = gpCache.pendingPsoDestroys.erase(iter);
116             } else {
117                 ++iter;
118             }
119         }
120     }
121     {
122         auto& gpCache = graphicsPipelineStateCache_;
123         for (auto iter = gpCache.pendingPsoDestroys.begin(); iter != gpCache.pendingPsoDestroys.end();) {
124             if (iter->frameIndex < ageLimit) {
125                 iter = gpCache.pendingPsoDestroys.erase(iter);
126             } else {
127                 ++iter;
128             }
129         }
130     }
131 
132     ProcessReloadedShaders();
133 }
134 
ProcessReloadedShaders()135 void NodeContextPsoManager::ProcessReloadedShaders()
136 {
137     const uint64_t frameCount = device_.GetFrameCount();
138     // check for shader manager reloaded shaders -> re-create psos
139     const uint64_t shaderMgrReloadShaderIndex = shaderMgr_.GetLastReloadedShaderFrameIndex();
140     if ((shaderMgrReloadShaderIndex > lastReloadedShadersFrameIndex_) || (lastReloadedShadersFrameIndex_ == 0)) {
141         const auto reloadedShaders = shaderMgr_.GetReloadedShadersForBackend();
142         for (const auto& shaderRef : reloadedShaders) {
143             if (shaderRef.frameIndex > lastReloadedShadersFrameIndex_) {
144                 // find if using reloaded shader handles
145                 {
146                     auto& gpCache = computePipelineStateCache_;
147                     for (size_t idx = 0U; idx < gpCache.psoCreationData.size(); ++idx) {
148                         const auto& ref = gpCache.psoCreationData[idx];
149                         for (const auto& refHandle : shaderRef.shadersForBackend) {
150                             if (ref.shaderHandle.id == refHandle.id) {
151                                 // move pso and set as null
152                                 gpCache.pendingPsoDestroys.push_back(
153                                     { move(gpCache.pipelineStateObjects[idx]), frameCount });
154                                 gpCache.pipelineStateObjects[idx] = nullptr;
155                                 break;
156                             }
157                         }
158                     }
159                 }
160                 {
161                     auto& gpCache = graphicsPipelineStateCache_;
162                     auto& pso = gpCache.pipelineStateObjects;
163                     for (auto iter = pso.begin(); iter != pso.end();) {
164                         bool erase = false;
165                         for (const auto& refHandle : shaderRef.shadersForBackend) {
166                             if (iter->second.shaderHandle.id == refHandle.id) {
167                                 erase = true;
168                                 break;
169                             }
170                         }
171                         if (erase) {
172                             // move pso and erase
173                             gpCache.pendingPsoDestroys.push_back({ move(iter->second.pso), frameCount });
174                             iter = pso.erase(iter);
175                         } else {
176                             ++iter;
177                         }
178                     }
179                 }
180             }
181         }
182     }
183     // this frame has been processed
184     lastReloadedShadersFrameIndex_ = frameCount;
185 }
186 
GetComputePsoHandle(const RenderHandle shaderHandle,const PipelineLayout & pipelineLayout,const ShaderSpecializationConstantDataView & shaderSpecialization)187 RenderHandle NodeContextPsoManager::GetComputePsoHandle(const RenderHandle shaderHandle,
188     const PipelineLayout& pipelineLayout, const ShaderSpecializationConstantDataView& shaderSpecialization)
189 {
190     if (RenderHandleUtil::GetHandleType(shaderHandle) != RenderHandleType::COMPUTE_SHADER_STATE_OBJECT) {
191 #if (RENDER_VALIDATION_ENABLED == 1)
192         PLUGIN_LOG_E("RENDER_VALIDATION: invalid shader handle given to compute pso creation");
193 #endif
194         return {}; // early out
195     }
196     // if not matching pso -> deferred creation in render backend
197     RenderHandle psoHandle;
198 
199     auto& cache = computePipelineStateCache_;
200 
201     const uint64_t hash = HashComputeShader(shaderHandle, shaderSpecialization);
202     const auto iter = cache.hashToHandle.find(hash);
203     const bool needsNewPso = (iter == cache.hashToHandle.cend());
204     if (needsNewPso) {
205         PLUGIN_ASSERT(cache.psoCreationData.size() == cache.pipelineStateObjects.size());
206 
207         // reserve slot for new pso
208         const auto index = static_cast<uint32_t>(cache.psoCreationData.size());
209         cache.pipelineStateObjects.emplace_back(nullptr);
210         // add pipeline layout descriptor set mask to pso handle for fast evaluation
211         uint32_t descriptorSetBitmask = 0;
212         for (uint32_t idx = 0; idx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++idx) {
213             if (pipelineLayout.descriptorSetLayouts[idx].set != PipelineLayoutConstants::INVALID_INDEX) {
214                 descriptorSetBitmask |= (1 << idx);
215             }
216         }
217         psoHandle = RenderHandleUtil::CreateHandle(RenderHandleType::COMPUTE_PSO, index, 0, descriptorSetBitmask);
218         cache.hashToHandle[hash] = psoHandle;
219 
220 #if (RENDER_VALIDATION_ENABLED == 1)
221         cache.handleToPipelineLayout[psoHandle] = pipelineLayout;
222 #endif
223 
224         // store needed data for render backend pso creation
225         ShaderSpecializationConstantDataWrapper ssw {
226             vector<ShaderSpecialization::Constant>(
227                 shaderSpecialization.constants.begin(), shaderSpecialization.constants.end()),
228             vector<uint32_t>(shaderSpecialization.data.begin(), shaderSpecialization.data.end()),
229         };
230         cache.psoCreationData.push_back({ shaderHandle, pipelineLayout, move(ssw) });
231     } else {
232         psoHandle = iter->second;
233     }
234 
235     return psoHandle;
236 }
237 
GetComputePsoHandle(const RenderHandle shaderHandle,const RenderHandle pipelineLayoutHandle,const ShaderSpecializationConstantDataView & shaderSpecialization)238 RenderHandle NodeContextPsoManager::GetComputePsoHandle(const RenderHandle shaderHandle,
239     const RenderHandle pipelineLayoutHandle, const ShaderSpecializationConstantDataView& shaderSpecialization)
240 {
241     const PipelineLayout& pl = shaderMgr_.GetPipelineLayoutRef(pipelineLayoutHandle);
242     return GetComputePsoHandle(shaderHandle, pl, shaderSpecialization);
243 }
244 
GetComputePsoHandle(const IShaderManager::ShaderData & shaderData,const ShaderSpecializationConstantDataView & shaderSpecialization)245 RenderHandle NodeContextPsoManager::GetComputePsoHandle(
246     const IShaderManager::ShaderData& shaderData, const ShaderSpecializationConstantDataView& shaderSpecialization)
247 {
248     return GetComputePsoHandle(shaderData.shader, shaderData.pipelineLayoutData, shaderSpecialization);
249 }
250 
GetGraphicsPsoHandleImpl(const RenderHandle shader,const RenderHandle graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const array_view<const DynamicStateEnum> dynamicStates,const GraphicsState * customGraphicsState)251 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandleImpl(const RenderHandle shader,
252     const RenderHandle graphicsState, const PipelineLayout& pipelineLayout,
253     const VertexInputDeclarationView& vertexInputDeclarationView,
254     const ShaderSpecializationConstantDataView& shaderSpecialization,
255     const array_view<const DynamicStateEnum> dynamicStates, const GraphicsState* customGraphicsState)
256 {
257 #if (RENDER_VALIDATION_ENABLED == 1)
258     if (RenderHandleUtil::GetHandleType(shader) != RenderHandleType::SHADER_STATE_OBJECT) {
259         PLUGIN_LOG_E("RENDER_VALIDATION: invalid shader handle given to graphics pso creation");
260     }
261     if (RenderHandleUtil::IsValid(graphicsState) &&
262         RenderHandleUtil::GetHandleType(graphicsState) != RenderHandleType::GRAPHICS_STATE) {
263         PLUGIN_LOG_E("RENDER_VALIDATION: invalid graphics state handle given to graphics pso creation");
264     }
265 #endif
266     if (RenderHandleUtil::GetHandleType(shader) != RenderHandleType::SHADER_STATE_OBJECT) {
267         return {}; // early out
268     }
269     // if not matching pso -> deferred creation in render backend
270     RenderHandle psoHandle;
271 
272     auto& cache = graphicsPipelineStateCache_;
273     uint64_t cGfxHash = 0;
274     if ((!RenderHandleUtil::IsValid(graphicsState)) && customGraphicsState) {
275         cGfxHash = shaderMgr_.HashGraphicsState(*customGraphicsState);
276     }
277     const uint64_t hash = HashGraphicsShader(shader, graphicsState, dynamicStates, shaderSpecialization, cGfxHash);
278     const auto iter = cache.hashToHandle.find(hash);
279     const bool needsNewPso = (iter == cache.hashToHandle.cend());
280     if (needsNewPso) {
281         const auto index = static_cast<uint32_t>(cache.psoCreationData.size());
282         // add pipeline layout descriptor set mask to pso handle for fast evaluation
283         uint32_t descriptorSetBitmask = 0;
284         for (uint32_t idx = 0; idx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++idx) {
285             if (pipelineLayout.descriptorSetLayouts[idx].set != PipelineLayoutConstants::INVALID_INDEX) {
286                 descriptorSetBitmask |= (1 << idx);
287             }
288         }
289         psoHandle = RenderHandleUtil::CreateHandle(RenderHandleType::GRAPHICS_PSO, index, 0, descriptorSetBitmask);
290         cache.hashToHandle[hash] = psoHandle;
291 
292         // store needed data for render backend pso creation
293         ShaderSpecializationConstantDataWrapper ssw {
294             { shaderSpecialization.constants.begin(), shaderSpecialization.constants.end() },
295             { shaderSpecialization.data.begin(), shaderSpecialization.data.end() },
296         };
297         VertexInputDeclarationDataWrapper vidw {
298             { vertexInputDeclarationView.bindingDescriptions.begin(),
299                 vertexInputDeclarationView.bindingDescriptions.end() },
300             { vertexInputDeclarationView.attributeDescriptions.begin(),
301                 vertexInputDeclarationView.attributeDescriptions.end() },
302         };
303 #if (RENDER_VALIDATION_ENABLED == 1)
304         validateSSO(shaderMgr_, shader, vidw);
305         cache.handleToPipelineLayout[psoHandle] = pipelineLayout;
306 #endif
307         // custom graphics state or null
308         unique_ptr<GraphicsState> customGraphicsStatePtr =
309             customGraphicsState ? make_unique<GraphicsState>(*customGraphicsState) : nullptr;
310         GraphicsPipelineStateCreationData psoCreationData;
311         psoCreationData.shaderHandle = shader;
312         psoCreationData.graphicsStateHandle = graphicsState;
313         psoCreationData.pipelineLayout = pipelineLayout;
314         psoCreationData.vertexInputDeclaration = move(vidw);
315         psoCreationData.shaderSpecialization = move(ssw);
316         psoCreationData.customGraphicsState = move(customGraphicsStatePtr);
317         psoCreationData.dynamicStates = { dynamicStates.cbegin(), dynamicStates.cend() };
318         cache.psoCreationData.push_back(move(psoCreationData));
319     } else {
320         psoHandle = iter->second;
321     }
322 
323     return psoHandle;
324 }
325 
GetGraphicsPsoHandle(const RenderHandle shaderHandle,const RenderHandle graphicsState,const RenderHandle pipelineLayoutHandle,const RenderHandle vertexInputDeclarationHandle,const ShaderSpecializationConstantDataView & shaderSpecialization,const array_view<const DynamicStateEnum> dynamicStates)326 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shaderHandle,
327     const RenderHandle graphicsState, const RenderHandle pipelineLayoutHandle,
328     const RenderHandle vertexInputDeclarationHandle, const ShaderSpecializationConstantDataView& shaderSpecialization,
329     const array_view<const DynamicStateEnum> dynamicStates)
330 {
331     RenderHandle psoHandle;
332     const PipelineLayout& pl = shaderMgr_.GetPipelineLayout(pipelineLayoutHandle);
333     VertexInputDeclarationView vidView = shaderMgr_.GetVertexInputDeclarationView(vertexInputDeclarationHandle);
334     const RenderHandle gfxStateHandle =
335         (RenderHandleUtil::GetHandleType(graphicsState) == RenderHandleType::GRAPHICS_STATE)
336             ? graphicsState
337             : shaderMgr_.GetGraphicsStateHandleByShaderHandle(shaderHandle).GetHandle();
338     psoHandle = GetGraphicsPsoHandleImpl(
339         shaderHandle, gfxStateHandle, pl, vidView, shaderSpecialization, dynamicStates, nullptr);
340     return psoHandle;
341 }
342 
GetGraphicsPsoHandle(const RenderHandle shader,const RenderHandle graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const array_view<const DynamicStateEnum> dynamicStates)343 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shader, const RenderHandle graphicsState,
344     const PipelineLayout& pipelineLayout, const VertexInputDeclarationView& vertexInputDeclarationView,
345     const ShaderSpecializationConstantDataView& shaderSpecialization,
346     const array_view<const DynamicStateEnum> dynamicStates)
347 {
348     return GetGraphicsPsoHandleImpl(shader, graphicsState, pipelineLayout, vertexInputDeclarationView,
349         shaderSpecialization, dynamicStates, nullptr);
350 }
351 
GetGraphicsPsoHandle(const RenderHandle shader,const GraphicsState & graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const array_view<const DynamicStateEnum> dynamicStates)352 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shader, const GraphicsState& graphicsState,
353     const PipelineLayout& pipelineLayout, const VertexInputDeclarationView& vertexInputDeclarationView,
354     const ShaderSpecializationConstantDataView& shaderSpecialization,
355     const array_view<const DynamicStateEnum> dynamicStates)
356 {
357     return GetGraphicsPsoHandleImpl(shader, RenderHandle {}, pipelineLayout, vertexInputDeclarationView,
358         shaderSpecialization, dynamicStates, &graphicsState);
359 }
360 
GetGraphicsPsoHandle(const IShaderManager::GraphicsShaderData & shaderData,const ShaderSpecializationConstantDataView & shaderSpecialization,const BASE_NS::array_view<const DynamicStateEnum> dynamicStates)361 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const IShaderManager::GraphicsShaderData& shaderData,
362     const ShaderSpecializationConstantDataView& shaderSpecialization,
363     const BASE_NS::array_view<const DynamicStateEnum> dynamicStates)
364 {
365     const VertexInputDeclarationView vidv = shaderMgr_.GetVertexInputDeclarationView(shaderData.vertexInputDeclaration);
366     return GetGraphicsPsoHandleImpl(shaderData.shader, shaderData.graphicsState, shaderData.pipelineLayoutData, vidv,
367         shaderSpecialization, dynamicStates, nullptr);
368 }
369 
370 #if (RENDER_VALIDATION_ENABLED == 1)
GetComputePsoPipelineLayout(const RenderHandle handle) const371 const PipelineLayout& NodeContextPsoManager::GetComputePsoPipelineLayout(const RenderHandle handle) const
372 {
373     auto& handleToPl = computePipelineStateCache_.handleToPipelineLayout;
374     if (const auto iter = handleToPl.find(handle); iter != handleToPl.cend()) {
375         return iter->second;
376     } else {
377         static PipelineLayout pl;
378         return pl;
379     }
380 }
381 #endif
382 
383 #if (RENDER_VALIDATION_ENABLED == 1)
GetGraphicsPsoPipelineLayout(const RenderHandle handle) const384 const PipelineLayout& NodeContextPsoManager::GetGraphicsPsoPipelineLayout(const RenderHandle handle) const
385 {
386     auto& handleToPl = graphicsPipelineStateCache_.handleToPipelineLayout;
387     if (const auto iter = handleToPl.find(handle); iter != handleToPl.cend()) {
388         return iter->second;
389     } else {
390         static PipelineLayout pl;
391         return pl;
392     }
393 }
394 #endif
395 
GetComputePso(const RenderHandle handle,const LowLevelPipelineLayoutData * pipelineLayoutData)396 const ComputePipelineStateObject* NodeContextPsoManager::GetComputePso(
397     const RenderHandle handle, const LowLevelPipelineLayoutData* pipelineLayoutData)
398 {
399     PLUGIN_ASSERT(RenderHandleUtil::GetHandleType(handle) == RenderHandleType::COMPUTE_PSO);
400     const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
401     PLUGIN_ASSERT_MSG(index < (uint32_t)computePipelineStateCache_.psoCreationData.size(),
402         "Check that IRenderNode::InitNode clears cached handles.");
403 
404     auto& cache = computePipelineStateCache_;
405     if (cache.pipelineStateObjects[index] == nullptr) { // pso needs to be created
406         PLUGIN_ASSERT(index < static_cast<uint32_t>(cache.psoCreationData.size()));
407         const auto& psoDataRef = cache.psoCreationData[index];
408         if (const GpuComputeProgram* gcp = shaderMgr_.GetGpuComputeProgram(psoDataRef.shaderHandle); gcp) {
409             const ShaderSpecializationConstantDataView sscdv {
410                 psoDataRef.shaderSpecialization.constants,
411                 psoDataRef.shaderSpecialization.data,
412             };
413             cache.pipelineStateObjects[index] =
414                 device_.CreateComputePipelineStateObject(*gcp, psoDataRef.pipelineLayout, sscdv, pipelineLayoutData);
415         }
416     }
417     return cache.pipelineStateObjects[index].get();
418 }
419 
GetGraphicsPso(const RenderHandle handle,const RenderPassDesc & renderPassDesc,const array_view<const RenderPassSubpassDesc> renderPassSubpassDescs,const uint32_t subpassIndex,const uint64_t psoStateHash,const LowLevelRenderPassData * renderPassData,const LowLevelPipelineLayoutData * pipelineLayoutData)420 const GraphicsPipelineStateObject* NodeContextPsoManager::GetGraphicsPso(const RenderHandle handle,
421     const RenderPassDesc& renderPassDesc, const array_view<const RenderPassSubpassDesc> renderPassSubpassDescs,
422     const uint32_t subpassIndex, const uint64_t psoStateHash, const LowLevelRenderPassData* renderPassData,
423     const LowLevelPipelineLayoutData* pipelineLayoutData)
424 {
425     PLUGIN_ASSERT(RenderHandleUtil::GetHandleType(handle) == RenderHandleType::GRAPHICS_PSO);
426     const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
427     PLUGIN_ASSERT_MSG(index < (uint32_t)graphicsPipelineStateCache_.psoCreationData.size(),
428         "Check that IRenderNode::InitNode clears cached handles.");
429 
430     auto& cache = graphicsPipelineStateCache_;
431     const uint64_t hash =
432         (device_.GetBackendType() == DeviceBackendType::VULKAN) ? Hash(handle.id, psoStateHash) : handle.id;
433     if (const auto iter = cache.pipelineStateObjects.find(hash); iter != cache.pipelineStateObjects.cend()) {
434         return iter->second.pso.get();
435     } else {
436         PLUGIN_ASSERT(index < static_cast<uint32_t>(cache.psoCreationData.size()));
437         const auto& psoDataRef = cache.psoCreationData[index];
438         if (const GpuShaderProgram* gsp = shaderMgr_.GetGpuShaderProgram(psoDataRef.shaderHandle); gsp) {
439             const GraphicsState* customGraphicsState = psoDataRef.customGraphicsState.get();
440             const GraphicsState& graphicsState = (customGraphicsState)
441                                                      ? *customGraphicsState
442                                                      : shaderMgr_.GetGraphicsStateRef(psoDataRef.graphicsStateHandle);
443 #if (RENDER_VALIDATION_ENABLED == 1)
444             if (subpassIndex >= renderPassSubpassDescs.size()) {
445                 PLUGIN_LOG_ONCE_I("node_context_pso_subpass_index",
446                     "RENDER_VALIDATION: subpassIndex (%u) out-of-bounds (%zu)", subpassIndex,
447                     renderPassSubpassDescs.size());
448             } else if (graphicsState.colorBlendState.colorAttachmentCount !=
449                        renderPassSubpassDescs[subpassIndex].colorAttachmentCount) {
450                 PLUGIN_LOG_ONCE_I("node_context_pso_output_info",
451                     "RENDER_VALIDATION: graphics state color attachment count (%u) does not match "
452                     "render pass subpass color attachment count (%u). (Output not consumed info)",
453                     graphicsState.colorBlendState.colorAttachmentCount,
454                     renderPassSubpassDescs[subpassIndex].colorAttachmentCount);
455             }
456 #endif
457             const auto& vertexInput = psoDataRef.vertexInputDeclaration;
458             const VertexInputDeclarationView vidv { vertexInput.bindingDescriptions,
459                 vertexInput.attributeDescriptions };
460 
461             const auto& shaderSpec = psoDataRef.shaderSpecialization;
462             const ShaderSpecializationConstantDataView sscdv { shaderSpec.constants, shaderSpec.data };
463 
464             auto& newPsoRef = cache.pipelineStateObjects[hash];
465             newPsoRef.shaderHandle = psoDataRef.shaderHandle;
466             newPsoRef.pso = device_.CreateGraphicsPipelineStateObject(*gsp, graphicsState, psoDataRef.pipelineLayout,
467                 vidv, sscdv, psoDataRef.dynamicStates, renderPassDesc, renderPassSubpassDescs, subpassIndex,
468                 renderPassData, pipelineLayoutData);
469             return newPsoRef.pso.get();
470         }
471     }
472     return nullptr;
473 }
474 RENDER_END_NAMESPACE()
475