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