• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 <render/namespace.h>
22 #include <render/nodecontext/intf_node_context_pso_manager.h>
23 
24 #include "device/device.h"
25 #include "device/gpu_program.h"
26 #include "device/gpu_program_util.h"
27 #include "device/gpu_resource_handle_util.h"
28 #include "device/gpu_resource_manager.h"
29 #include "device/pipeline_state_object.h"
30 #include "device/shader_manager.h"
31 #include "util/log.h"
32 
33 template<>
hash(const RENDER_NS::ShaderSpecializationConstantDataView & specialization)34 uint64_t BASE_NS::hash(const RENDER_NS::ShaderSpecializationConstantDataView& specialization)
35 {
36     uint64_t seed = BASE_NS::CompileTime::FNV_OFFSET_BASIS;
37     if ((!specialization.data.empty()) && (!specialization.constants.empty())) {
38         const size_t minSize = BASE_NS::Math::min(specialization.constants.size(), specialization.data.size());
39         for (size_t idx = 0; idx < minSize; ++idx) {
40             const auto& currConstant = specialization.constants[idx];
41             uint64_t v = 0;
42             const auto constantSize = RENDER_NS::GpuProgramUtil::SpecializationByteSize(currConstant.type);
43             if ((currConstant.offset + constantSize) <= specialization.data.size_bytes()) {
44                 uint8_t const* data = (uint8_t const*)specialization.data.data() + currConstant.offset;
45                 size_t const bytes = sizeof(v) < constantSize ? sizeof(v) : constantSize;
46                 seed = BASE_NS::FNV1aHash(data, bytes, seed);
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 DynamicStateFlags dynamicStateFlags,const ShaderSpecializationConstantDataView & shaderSpecialization,const uint64_t customGraphicsStateHash)68 uint64_t HashGraphicsShader(const RenderHandle shaderHandle, const RenderHandle graphicsStateHandle,
69     const DynamicStateFlags dynamicStateFlags, const ShaderSpecializationConstantDataView& shaderSpecialization,
70     const uint64_t customGraphicsStateHash)
71 {
72     return Hash(
73         shaderHandle.id, graphicsStateHandle.id, dynamicStateFlags, shaderSpecialization, customGraphicsStateHash);
74 }
75 
76 #if (RENDER_VALIDATION_ENABLED == 1)
validateSSO(ShaderManager & shaderMgr,const RenderHandle shaderHandle,const VertexInputDeclarationDataWrapper & vidw)77 void validateSSO(
78     ShaderManager& shaderMgr, const RenderHandle shaderHandle, const VertexInputDeclarationDataWrapper& vidw)
79 {
80     const GpuShaderProgram* gsp = shaderMgr.GetGpuShaderProgram(shaderHandle);
81     if (gsp) {
82         const auto& reflection = gsp->GetReflection();
83         const bool hasBindings = (reflection.vertexInputDeclarationView.bindingDescriptions.size() > 0);
84         const bool vidHasBindings = (vidw.bindingDescriptions.size() > 0);
85         if (hasBindings != vidHasBindings) {
86             PLUGIN_LOG_E(
87                 "RENDER_VALIDATION: vertex input declaration pso (bindings: %u) mismatch with shader reflection "
88                 "(bindings: %u)",
89                 static_cast<uint32_t>(vidw.bindingDescriptions.size()),
90                 static_cast<uint32_t>(reflection.vertexInputDeclarationView.bindingDescriptions.size()));
91         }
92     }
93 }
94 #endif
95 } // namespace
96 
NodeContextPsoManager(Device & device,ShaderManager & shaderManager)97 NodeContextPsoManager::NodeContextPsoManager(Device& device, ShaderManager& shaderManager)
98     : device_ { device }, shaderMgr_ { shaderManager }
99 {}
100 
GetComputePsoHandle(const RenderHandle shaderHandle,const PipelineLayout & pipelineLayout,const ShaderSpecializationConstantDataView & shaderSpecialization)101 RenderHandle NodeContextPsoManager::GetComputePsoHandle(const RenderHandle shaderHandle,
102     const PipelineLayout& pipelineLayout, const ShaderSpecializationConstantDataView& shaderSpecialization)
103 {
104     // if not matching pso -> deferred creation in render backend
105     RenderHandle psoHandle;
106 
107     auto& cache = computePipelineStateCache_;
108 
109     const uint64_t hash = HashComputeShader(shaderHandle, shaderSpecialization);
110     const auto iter = cache.hashToHandle.find(hash);
111     const bool needsNewPso = (iter == cache.hashToHandle.cend());
112     if (needsNewPso) {
113         PLUGIN_ASSERT(cache.psoCreationData.size() == cache.pipelineStateObjects.size());
114 
115         // reserve slot for new pso
116         const uint32_t index = static_cast<uint32_t>(cache.psoCreationData.size());
117         cache.pipelineStateObjects.emplace_back(nullptr);
118         // add pipeline layout descriptor set mask to pso handle for fast evaluation
119         uint32_t descriptorSetBitmask = 0;
120         for (uint32_t idx = 0; idx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++idx) {
121             if (pipelineLayout.descriptorSetLayouts[idx].set != PipelineLayoutConstants::INVALID_INDEX) {
122                 descriptorSetBitmask |= (1 << idx);
123             }
124         }
125         psoHandle = RenderHandleUtil::CreateHandle(RenderHandleType::COMPUTE_PSO, index, 0, descriptorSetBitmask);
126         cache.hashToHandle[hash] = psoHandle;
127 
128 #if (RENDER_VALIDATION_ENABLED == 1)
129         cache.handleToPipelineLayout[psoHandle] = pipelineLayout;
130 #endif
131 
132         // store needed data for render backend pso creation
133         ShaderSpecializationConstantDataWrapper ssw {
134             vector<ShaderSpecialization::Constant>(
135                 shaderSpecialization.constants.begin(), shaderSpecialization.constants.end()),
136             vector<uint32_t>(shaderSpecialization.data.begin(), shaderSpecialization.data.end()),
137         };
138         cache.psoCreationData.push_back({ shaderHandle, pipelineLayout, move(ssw) });
139     } else {
140         psoHandle = iter->second;
141     }
142 
143     return psoHandle;
144 }
145 
GetComputePsoHandle(const RenderHandle shaderHandle,const RenderHandle pipelineLayoutHandle,const ShaderSpecializationConstantDataView & shaderSpecialization)146 RenderHandle NodeContextPsoManager::GetComputePsoHandle(const RenderHandle shaderHandle,
147     const RenderHandle pipelineLayoutHandle, const ShaderSpecializationConstantDataView& shaderSpecialization)
148 {
149     RenderHandle psoHandle;
150     if (RenderHandleUtil::GetHandleType(pipelineLayoutHandle) == RenderHandleType::PIPELINE_LAYOUT) {
151         const PipelineLayout& pl = shaderMgr_.GetPipelineLayoutRef(pipelineLayoutHandle);
152         psoHandle = GetComputePsoHandle(shaderHandle, pl, shaderSpecialization);
153     } else {
154         PLUGIN_LOG_E("NodeContextPsoManager: invalid pipeline layout handle given to GetComputePsoHandle()");
155     }
156     return psoHandle;
157 }
158 
GetGraphicsPsoHandleImpl(const RenderHandle shader,const RenderHandle graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const DynamicStateFlags dynamicStateFlags,const GraphicsState * customGraphicsState)159 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandleImpl(const RenderHandle shader,
160     const RenderHandle graphicsState, const PipelineLayout& pipelineLayout,
161     const VertexInputDeclarationView& vertexInputDeclarationView,
162     const ShaderSpecializationConstantDataView& shaderSpecialization, const DynamicStateFlags dynamicStateFlags,
163     const GraphicsState* customGraphicsState)
164 {
165 #if (RENDER_VALIDATION_ENABLED == 1)
166     if (RenderHandleUtil::GetHandleType(shader) != RenderHandleType::SHADER_STATE_OBJECT) {
167         PLUGIN_LOG_E("RENDER_VALIDATION: invalid shader handle given to graphics pso creation");
168     }
169     if (RenderHandleUtil::IsValid(graphicsState) &&
170         RenderHandleUtil::GetHandleType(graphicsState) != RenderHandleType::GRAPHICS_STATE) {
171         PLUGIN_LOG_E("RENDER_VALIDATION: invalid graphics state hadnle given to graphics pso creation");
172     }
173 #endif
174     // if not matching pso -> deferred creation in render backend
175     RenderHandle psoHandle;
176 
177     auto& cache = graphicsPipelineStateCache_;
178     uint64_t cGfxHash = 0;
179     if ((!RenderHandleUtil::IsValid(graphicsState)) && customGraphicsState) {
180         cGfxHash = shaderMgr_.HashGraphicsState(*customGraphicsState);
181     }
182     const uint64_t hash = HashGraphicsShader(shader, graphicsState, dynamicStateFlags, shaderSpecialization, cGfxHash);
183     const auto iter = cache.hashToHandle.find(hash);
184     const bool needsNewPso = (iter == cache.hashToHandle.cend());
185     if (needsNewPso) {
186         const uint32_t index = static_cast<uint32_t>(cache.psoCreationData.size());
187         // add pipeline layout descriptor set mask to pso handle for fast evaluation
188         uint32_t descriptorSetBitmask = 0;
189         for (uint32_t idx = 0; idx < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; ++idx) {
190             if (pipelineLayout.descriptorSetLayouts[idx].set != PipelineLayoutConstants::INVALID_INDEX) {
191                 descriptorSetBitmask |= (1 << idx);
192             }
193         }
194         psoHandle = RenderHandleUtil::CreateHandle(RenderHandleType::GRAPHICS_PSO, index, 0, descriptorSetBitmask);
195         cache.hashToHandle[hash] = psoHandle;
196 
197         // store needed data for render backend pso creation
198         ShaderSpecializationConstantDataWrapper ssw {
199             { shaderSpecialization.constants.begin(), shaderSpecialization.constants.end() },
200             { shaderSpecialization.data.begin(), shaderSpecialization.data.end() },
201         };
202         VertexInputDeclarationDataWrapper vidw {
203             { vertexInputDeclarationView.bindingDescriptions.begin(),
204                 vertexInputDeclarationView.bindingDescriptions.end() },
205             { vertexInputDeclarationView.attributeDescriptions.begin(),
206                 vertexInputDeclarationView.attributeDescriptions.end() },
207         };
208 #if (RENDER_VALIDATION_ENABLED == 1)
209         validateSSO(shaderMgr_, shader, vidw);
210         cache.handleToPipelineLayout[psoHandle] = pipelineLayout;
211 #endif
212         // custom graphics state or null
213         unique_ptr<GraphicsState> customGraphicsStatePtr =
214             customGraphicsState ? make_unique<GraphicsState>(*customGraphicsState) : nullptr;
215         cache.psoCreationData.push_back({ shader, graphicsState, pipelineLayout, dynamicStateFlags, move(vidw),
216             move(ssw), move(customGraphicsStatePtr) });
217     } else {
218         psoHandle = iter->second;
219     }
220 
221     return psoHandle;
222 }
223 
GetGraphicsPsoHandle(const RenderHandle shaderHandle,const RenderHandle graphicsState,const RenderHandle pipelineLayoutHandle,const RenderHandle vertexInputDeclarationHandle,const ShaderSpecializationConstantDataView & shaderSpecialization,const DynamicStateFlags dynamicStateFlags)224 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shaderHandle,
225     const RenderHandle graphicsState, const RenderHandle pipelineLayoutHandle,
226     const RenderHandle vertexInputDeclarationHandle, const ShaderSpecializationConstantDataView& shaderSpecialization,
227     const DynamicStateFlags dynamicStateFlags)
228 {
229     RenderHandle psoHandle;
230     const PipelineLayout& pl = shaderMgr_.GetPipelineLayout(pipelineLayoutHandle);
231     VertexInputDeclarationView vidView = shaderMgr_.GetVertexInputDeclarationView(vertexInputDeclarationHandle);
232     const RenderHandle gfxStateHandle =
233         (RenderHandleUtil::GetHandleType(graphicsState) == RenderHandleType::GRAPHICS_STATE)
234             ? graphicsState
235             : shaderMgr_.GetGraphicsStateHandleByShaderHandle(shaderHandle).GetHandle();
236     psoHandle = GetGraphicsPsoHandleImpl(
237         shaderHandle, gfxStateHandle, pl, vidView, shaderSpecialization, dynamicStateFlags, nullptr);
238     return psoHandle;
239 }
240 
GetGraphicsPsoHandle(const RenderHandle shader,const RenderHandle graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const DynamicStateFlags dynamicStateFlags)241 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shader, const RenderHandle graphicsState,
242     const PipelineLayout& pipelineLayout, const VertexInputDeclarationView& vertexInputDeclarationView,
243     const ShaderSpecializationConstantDataView& shaderSpecialization, const DynamicStateFlags dynamicStateFlags)
244 {
245     return GetGraphicsPsoHandleImpl(shader, graphicsState, pipelineLayout, vertexInputDeclarationView,
246         shaderSpecialization, dynamicStateFlags, nullptr);
247 }
248 
GetGraphicsPsoHandle(const RenderHandle shader,const GraphicsState & graphicsState,const PipelineLayout & pipelineLayout,const VertexInputDeclarationView & vertexInputDeclarationView,const ShaderSpecializationConstantDataView & shaderSpecialization,const DynamicStateFlags dynamicStateFlags)249 RenderHandle NodeContextPsoManager::GetGraphicsPsoHandle(const RenderHandle shader, const GraphicsState& graphicsState,
250     const PipelineLayout& pipelineLayout, const VertexInputDeclarationView& vertexInputDeclarationView,
251     const ShaderSpecializationConstantDataView& shaderSpecialization, const DynamicStateFlags dynamicStateFlags)
252 {
253     return GetGraphicsPsoHandleImpl(shader, RenderHandle {}, pipelineLayout, vertexInputDeclarationView,
254         shaderSpecialization, dynamicStateFlags, &graphicsState);
255 }
256 
257 #if (RENDER_VALIDATION_ENABLED == 1)
GetComputePsoPipelineLayout(const RenderHandle handle) const258 const PipelineLayout& NodeContextPsoManager::GetComputePsoPipelineLayout(const RenderHandle handle) const
259 {
260     auto& handleToPl = computePipelineStateCache_.handleToPipelineLayout;
261     if (const auto iter = handleToPl.find(handle); iter != handleToPl.cend()) {
262         return iter->second;
263     } else {
264         static PipelineLayout pl;
265         return pl;
266     }
267 }
268 #endif
269 
270 #if (RENDER_VALIDATION_ENABLED == 1)
GetGraphicsPsoPipelineLayout(const RenderHandle handle) const271 const PipelineLayout& NodeContextPsoManager::GetGraphicsPsoPipelineLayout(const RenderHandle handle) const
272 {
273     auto& handleToPl = graphicsPipelineStateCache_.handleToPipelineLayout;
274     if (const auto iter = handleToPl.find(handle); iter != handleToPl.cend()) {
275         return iter->second;
276     } else {
277         static PipelineLayout pl;
278         return pl;
279     }
280 }
281 #endif
282 
GetComputePso(const RenderHandle handle,const LowLevelPipelineLayoutData * pipelineLayoutData)283 const ComputePipelineStateObject* NodeContextPsoManager::GetComputePso(
284     const RenderHandle handle, const LowLevelPipelineLayoutData* pipelineLayoutData)
285 {
286     PLUGIN_ASSERT(RenderHandleUtil::GetHandleType(handle) == RenderHandleType::COMPUTE_PSO);
287     const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
288     PLUGIN_ASSERT_MSG(index < (uint32_t)computePipelineStateCache_.psoCreationData.size(),
289         "Check that IRenderNode::InitNode clears cached handles.");
290 
291     auto& cache = computePipelineStateCache_;
292     if (cache.pipelineStateObjects[index] == nullptr) { // pso needs to be created
293         PLUGIN_ASSERT(index < static_cast<uint32_t>(cache.psoCreationData.size()));
294         const auto& psoDataRef = cache.psoCreationData[index];
295         const GpuComputeProgram* gcp = shaderMgr_.GetGpuComputeProgram(psoDataRef.shaderHandle);
296         PLUGIN_ASSERT(gcp);
297         if (gcp) {
298             const ShaderSpecializationConstantDataView sscdv {
299                 psoDataRef.shaderSpecialization.constants,
300                 psoDataRef.shaderSpecialization.data,
301             };
302             cache.pipelineStateObjects[index] =
303                 device_.CreateComputePipelineStateObject(*gcp, psoDataRef.pipelineLayout, sscdv, pipelineLayoutData);
304         }
305     }
306     return cache.pipelineStateObjects[index].get();
307 }
308 
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)309 const GraphicsPipelineStateObject* NodeContextPsoManager::GetGraphicsPso(const RenderHandle handle,
310     const RenderPassDesc& renderPassDesc, const array_view<const RenderPassSubpassDesc> renderPassSubpassDescs,
311     const uint32_t subpassIndex, const uint64_t psoStateHash, const LowLevelRenderPassData* renderPassData,
312     const LowLevelPipelineLayoutData* pipelineLayoutData)
313 {
314     PLUGIN_ASSERT(RenderHandleUtil::GetHandleType(handle) == RenderHandleType::GRAPHICS_PSO);
315     const uint32_t index = RenderHandleUtil::GetIndexPart(handle);
316     PLUGIN_ASSERT_MSG(index < (uint32_t)graphicsPipelineStateCache_.psoCreationData.size(),
317         "Check that IRenderNode::InitNode clears cached handles.");
318 
319     auto& cache = graphicsPipelineStateCache_;
320     const uint64_t hash =
321         (device_.GetBackendType() == DeviceBackendType::VULKAN) ? Hash(handle.id, psoStateHash) : handle.id;
322     if (const auto iter = cache.pipelineStateObjects.find(hash); iter != cache.pipelineStateObjects.cend()) {
323         return iter->second.get();
324     } else {
325         PLUGIN_ASSERT(index < static_cast<uint32_t>(cache.psoCreationData.size()));
326         const auto& psoDataRef = cache.psoCreationData[index];
327         const GpuShaderProgram* gsp = shaderMgr_.GetGpuShaderProgram(psoDataRef.shaderHandle);
328         PLUGIN_ASSERT(gsp);
329         if (gsp) {
330             const GraphicsState* customGraphicsState = psoDataRef.customGraphicsState.get();
331             const GraphicsState& graphicsState = (customGraphicsState)
332                                                      ? *customGraphicsState
333                                                      : shaderMgr_.GetGraphicsStateRef(psoDataRef.graphicsStateHandle);
334 #if (RENDER_VALIDATION_ENABLED == 1)
335             if (graphicsState.colorBlendState.colorAttachmentCount !=
336                 renderPassSubpassDescs[subpassIndex].colorAttachmentCount) {
337                 PLUGIN_LOG_ONCE_I("node_context_pso_output_info",
338                     "RENDER_VALIDATION: graphics state color attachment count (%u) does not match "
339                     "render pass subpass color attachment count (%u). (Output not consumed info)",
340                     graphicsState.colorBlendState.colorAttachmentCount,
341                     renderPassSubpassDescs[subpassIndex].colorAttachmentCount);
342             }
343 #endif
344             const DynamicStateFlags combinedDynamicStateFlags =
345                 (graphicsState.dynamicStateFlags | psoDataRef.dynamicStateFlags);
346 
347             const auto& vertexInput = psoDataRef.vertexInputDeclaration;
348             const VertexInputDeclarationView vidv { vertexInput.bindingDescriptions,
349                 vertexInput.attributeDescriptions };
350 
351             const auto& shaderSpec = psoDataRef.shaderSpecialization;
352             const ShaderSpecializationConstantDataView sscdv { shaderSpec.constants, shaderSpec.data };
353 
354             auto& newPsoRef = cache.pipelineStateObjects[hash];
355             newPsoRef = device_.CreateGraphicsPipelineStateObject(*gsp, graphicsState, psoDataRef.pipelineLayout, vidv,
356                 sscdv, combinedDynamicStateFlags, renderPassDesc, renderPassSubpassDescs, subpassIndex, renderPassData,
357                 pipelineLayoutData);
358             return newPsoRef.get();
359         }
360     }
361     return nullptr;
362 }
363 RENDER_END_NAMESPACE()
364