MythTV master
mythshadervulkan.cpp
Go to the documentation of this file.
1// MythTV
5
6// Std
7#include <algorithm>
8
9#define LOC QString("VulkanShader: ")
10
11// libglslang
12#if CONFIG_LIBGLSLANG
13#include <glslang/Public/ShaderLang.h>
14#include <glslang/SPIRV/GlslangToSpv.h>
15
16static const TBuiltInResource s_TBuiltInResource = {
17 /* .MaxLights = */ 32,
18 /* .MaxClipPlanes = */ 6,
19 /* .MaxTextureUnits = */ 32,
20 /* .MaxTextureCoords = */ 32,
21 /* .MaxVertexAttribs = */ 64,
22 /* .MaxVertexUniformComponents = */ 4096,
23 /* .MaxVaryingFloats = */ 64,
24 /* .MaxVertexTextureImageUnits = */ 32,
25 /* .MaxCombinedTextureImageUnits = */ 80,
26 /* .MaxTextureImageUnits = */ 32,
27 /* .MaxFragmentUniformComponents = */ 4096,
28 /* .MaxDrawBuffers = */ 32,
29 /* .MaxVertexUniformVectors = */ 128,
30 /* .MaxVaryingVectors = */ 8,
31 /* .MaxFragmentUniformVectors = */ 16,
32 /* .MaxVertexOutputVectors = */ 16,
33 /* .MaxFragmentInputVectors = */ 15,
34 /* .MinProgramTexelOffset = */ -8,
35 /* .MaxProgramTexelOffset = */ 7,
36 /* .MaxClipDistances = */ 8,
37 /* .MaxComputeWorkGroupCountX = */ 65535,
38 /* .MaxComputeWorkGroupCountY = */ 65535,
39 /* .MaxComputeWorkGroupCountZ = */ 65535,
40 /* .MaxComputeWorkGroupSizeX = */ 1024,
41 /* .MaxComputeWorkGroupSizeY = */ 1024,
42 /* .MaxComputeWorkGroupSizeZ = */ 64,
43 /* .MaxComputeUniformComponents = */ 1024,
44 /* .MaxComputeTextureImageUnits = */ 16,
45 /* .MaxComputeImageUniforms = */ 8,
46 /* .MaxComputeAtomicCounters = */ 8,
47 /* .MaxComputeAtomicCounterBuffers = */ 1,
48 /* .MaxVaryingComponents = */ 60,
49 /* .MaxVertexOutputComponents = */ 64,
50 /* .MaxGeometryInputComponents = */ 64,
51 /* .MaxGeometryOutputComponents = */ 128,
52 /* .MaxFragmentInputComponents = */ 128,
53 /* .MaxImageUnits = */ 8,
54 /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
55 /* .MaxCombinedShaderOutputResources = */ 8,
56 /* .MaxImageSamples = */ 0,
57 /* .MaxVertexImageUniforms = */ 0,
58 /* .MaxTessControlImageUniforms = */ 0,
59 /* .MaxTessEvaluationImageUniforms = */ 0,
60 /* .MaxGeometryImageUniforms = */ 0,
61 /* .MaxFragmentImageUniforms = */ 8,
62 /* .MaxCombinedImageUniforms = */ 8,
63 /* .MaxGeometryTextureImageUnits = */ 16,
64 /* .MaxGeometryOutputVertices = */ 256,
65 /* .MaxGeometryTotalOutputComponents = */ 1024,
66 /* .MaxGeometryUniformComponents = */ 1024,
67 /* .MaxGeometryVaryingComponents = */ 64,
68 /* .MaxTessControlInputComponents = */ 128,
69 /* .MaxTessControlOutputComponents = */ 128,
70 /* .MaxTessControlTextureImageUnits = */ 16,
71 /* .MaxTessControlUniformComponents = */ 1024,
72 /* .MaxTessControlTotalOutputComponents = */ 4096,
73 /* .MaxTessEvaluationInputComponents = */ 128,
74 /* .MaxTessEvaluationOutputComponents = */ 128,
75 /* .MaxTessEvaluationTextureImageUnits = */ 16,
76 /* .MaxTessEvaluationUniformComponents = */ 1024,
77 /* .MaxTessPatchComponents = */ 120,
78 /* .MaxPatchVertices = */ 32,
79 /* .MaxTessGenLevel = */ 64,
80 /* .MaxViewports = */ 16,
81 /* .MaxVertexAtomicCounters = */ 0,
82 /* .MaxTessControlAtomicCounters = */ 0,
83 /* .MaxTessEvaluationAtomicCounters = */ 0,
84 /* .MaxGeometryAtomicCounters = */ 0,
85 /* .MaxFragmentAtomicCounters = */ 8,
86 /* .MaxCombinedAtomicCounters = */ 8,
87 /* .MaxAtomicCounterBindings = */ 1,
88 /* .MaxVertexAtomicCounterBuffers = */ 0,
89 /* .MaxTessControlAtomicCounterBuffers = */ 0,
90 /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
91 /* .MaxGeometryAtomicCounterBuffers = */ 0,
92 /* .MaxFragmentAtomicCounterBuffers = */ 1,
93 /* .MaxCombinedAtomicCounterBuffers = */ 1,
94 /* .MaxAtomicCounterBufferSize = */ 16384,
95 /* .MaxTransformFeedbackBuffers = */ 4,
96 /* .MaxTransformFeedbackInterleavedComponents = */ 64,
97 /* .MaxCullDistances = */ 8,
98 /* .MaxCombinedClipAndCullDistances = */ 8,
99 /* .MaxSamples = */ 4,
100 /* .maxMeshOutputVerticesNV = */ 256,
101 /* .maxMeshOutputPrimitivesNV = */ 512,
102 /* .maxMeshWorkGroupSizeX_NV = */ 32,
103 /* .maxMeshWorkGroupSizeY_NV = */ 1,
104 /* .maxMeshWorkGroupSizeZ_NV = */ 1,
105 /* .maxTaskWorkGroupSizeX_NV = */ 32,
106 /* .maxTaskWorkGroupSizeY_NV = */ 1,
107 /* .maxTaskWorkGroupSizeZ_NV = */ 1,
108 /* .maxMeshViewCountNV = */ 4,
109
110 .limits = {
111 /* .nonInductiveForLoops = */ true,
112 /* .whileLoops = */ true,
113 /* .doWhileLoops = */ true,
114 /* .generalUniformIndexing = */ true,
115 /* .generalAttributeMatrixVectorIndexing = */ true,
116 /* .generalVaryingIndexing = */ true,
117 /* .generalSamplerIndexing = */ true,
118 /* .generalVariableIndexing = */ true,
119 /* .generalConstantMatrixVectorIndexing = */ true,
120 }
121};
122
123static auto GLSLangCompile(EShLanguage Stage, const QString &Code)
124{
125 std::vector<uint32_t> result;
126 auto *shader = new glslang::TShader(Stage);
127 if (!shader)
128 return result;
129
130 QByteArray data = Code.toLocal8Bit();
131 const char *tmp = data.constData();
132 shader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
133 shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
134 shader->setStrings(&tmp, 1);
135 if (!shader->parse(&s_TBuiltInResource, glslang::EShTargetVulkan_1_1, true, EShMsgDefault))
136 {
137 LOG(VB_GENERAL, LOG_ERR, LOC + "Shader parse error:");
138 LOG(VB_GENERAL, LOG_ERR, shader->getInfoLog());
139 delete shader;
140 return result;
141 }
142
143 auto *program = new glslang::TProgram();
144 if (!program)
145 {
146 delete shader;
147 return result;
148 }
149
150 program->addShader(shader);
151 if (program->link(EShMsgDefault))
152 {
153 auto ByteCodeToString = [](std::vector<uint32_t> &OpCodes)
154 {
155 QString string;
156 int count = 0;
157 for (uint32_t opcode : OpCodes)
158 {
159 if (count++ == 0) string += "\n";
160 if (count > 5) count = 0;
161 string += "0x" + QString("%1, ").arg(opcode, 8, 16, QLatin1Char('0')).toUpper();
162 }
163 return string;
164 };
165
166 glslang::SpvOptions options { };
167 options.generateDebugInfo = false;
168 options.disassemble = false;
169 options.validate = false;
170 options.disableOptimizer = false;
171 options.optimizeSize = true;
172 GlslangToSpv(*program->getIntermediate(Stage), result, &options);
173 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Generated SPIR-V: %1bytes").arg(result.size() * sizeof (uint32_t)));
174 LOG(VB_GENERAL, LOG_INFO, "Source:\n" + Code);
175 LOG(VB_GENERAL, LOG_INFO, "ByteCode:\n" + ByteCodeToString(result));
176 }
177 else
178 {
179 LOG(VB_GENERAL, LOG_ERR, LOC + "Shader link error:");
180 LOG(VB_GENERAL, LOG_ERR, program->getInfoLog());
181 }
182
183 delete shader;
184 delete program;
185 return result;
186}
187
188bool MythShaderVulkan::InitGLSLang(bool Release /* = false */)
189{
190 static QMutex s_glslangLock;
191 static int s_glslangRefcount = 0;
192 static bool s_initSuccess = false;
193 QMutexLocker locker(&s_glslangLock);
194
195 if (Release)
196 {
197 s_glslangRefcount--;
198 if (s_glslangRefcount < 0)
199 {
200 LOG(VB_GENERAL, LOG_ERR, LOC + "GLSLang ref count error");
201 return false;
202 }
203
204 if (s_glslangRefcount < 1)
205 {
206 LOG(VB_GENERAL, LOG_INFO, LOC + "GLSLang released");
207 glslang::FinalizeProcess();
208 }
209 return true;
210 }
211
212 if (s_glslangRefcount < 1)
213 {
214 s_initSuccess = glslang::InitializeProcess();
215 if (s_initSuccess)
216 LOG(VB_GENERAL, LOG_INFO, LOC + "GLSLang initialised");
217 }
218 s_glslangRefcount++;
219 return s_initSuccess;
220}
221
222bool MythShaderVulkan::CreateShaderFromGLSL(const std::vector<MythGLSLStage> &Stages)
223{
224 if (Stages.empty())
225 return false;
226
227 if (!MythShaderVulkan::InitGLSLang())
228 return false;
229
230 std::vector<MythSPIRVStage> spirvstages;
231
232 auto glslangtype = [](VkShaderStageFlags Type)
233 {
234 switch (Type)
235 {
236 case VK_SHADER_STAGE_VERTEX_BIT: return EShLangVertex;
237 case VK_SHADER_STAGE_FRAGMENT_BIT: return EShLangFragment;
238 case VK_SHADER_STAGE_COMPUTE_BIT: return EShLangCompute;
239 default: break;
240 }
241 return EShLangCount;
242 };
243
244 for (const auto& stage : Stages)
245 if (glslangtype(stage.first) != EShLangCount)
246 spirvstages.emplace_back(stage.first, GLSLangCompile(glslangtype(stage.first), stage.second));
247 MythShaderVulkan::InitGLSLang(true);
248 return CreateShaderFromSPIRV(spirvstages);
249}
250#endif
251
261 const std::vector<int> &Stages,
262 const MythShaderMap *Sources,
263 const MythBindingMap *Bindings)
264{
265 auto * result = new MythShaderVulkan(Vulkan, Stages, Sources, Bindings);
266 if (result && !result->IsValidVulkan())
267 {
268 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create shader");
269 delete result;
270 result = nullptr;
271 }
272 return result;
273}
274
276 const std::vector<int> &Stages,
277 const MythShaderMap *Sources,
278 const MythBindingMap *Bindings)
279 : MythVulkanObject(Vulkan)
280{
281 if (!m_vulkanValid || Stages.empty())
282 return;
283 m_vulkanValid = false;
284
285 if (!Sources)
286 Sources = &k450DefaultShaders;
287
288 if (!Bindings)
289 Bindings = &k450ShaderBindings;
290
291 // ensure we have sources and bindings
292 for (const auto & stage : Stages)
293 if (Sources->find(stage) == Sources->end() || Bindings->find(stage) == Bindings->end())
294 return;
295
296 // build the descriptor set layouts from the shader descriptions
297 bool foundvertices = false;
298 bool pushconstants = false;
299 VkPushConstantRange ranges = { };
300
301 std::map<int, std::vector<VkDescriptorSetLayoutBinding>> layoutbindings;
302 std::map<int, std::vector<VkDescriptorPoolSize>> poolsizes;
303 for (const auto & stage : Stages)
304 {
305 bool isvertex = false;
306 MythBindingDesc desc = Bindings->at(stage);
307 m_topology = std::get<0>(desc);
308 MythStageLayout binding = std::get<1>(desc);
309
310 for (auto & stagelayout : binding)
311 {
312 if (stagelayout.second.stageFlags == VK_SHADER_STAGE_VERTEX_BIT)
313 isvertex = true;
314
315 if (layoutbindings.find(stagelayout.first) != layoutbindings.end())
316 layoutbindings.at(stagelayout.first).emplace_back(stagelayout.second);
317 else
318 layoutbindings.insert( { stagelayout.first, { stagelayout.second } } );
319
320 VkDescriptorPoolSize poolsize = {stagelayout.second.descriptorType, stagelayout.second.descriptorCount};
321 if (poolsizes.find(stagelayout.first) != poolsizes.end())
322 poolsizes.at(stagelayout.first).emplace_back(poolsize);
323 else
324 poolsizes.insert( { stagelayout.first, { poolsize } } );
325 }
326
327 if (isvertex && !foundvertices)
328 {
329 foundvertices = true;
330 m_vertexBindingDesc = std::get<2>(desc);
331 m_vertexAttributes = std::get<3>(desc);
332 }
333
334 if (!pushconstants)
335 {
336 VkPushConstantRange range = std::get<4>(desc);
337 if (range.stageFlags)
338 {
339 pushconstants = true;
340 ranges = range;
341 }
342 }
343 }
344
345 // convert poolsizes to vector
346 for (const auto& poolsize : poolsizes)
347 m_descriptorPoolSizes.emplace_back(poolsize.second);
348
349 // create the desriptor layouts
350 for (auto & layoutbinding : layoutbindings)
351 {
352 VkDescriptorSetLayout layout = MYTH_NULL_DISPATCH;
353 VkDescriptorSetLayoutCreateInfo layoutinfo { };
354 layoutinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
355 layoutinfo.bindingCount = static_cast<uint32_t>(layoutbinding.second.size());
356 layoutinfo.pBindings = layoutbinding.second.data();
357 if (m_vulkanFuncs->vkCreateDescriptorSetLayout(m_vulkanDevice, &layoutinfo, nullptr, &layout) != VK_SUCCESS)
358 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create DescriptorSetLayout");
359 else
360 m_descriptorSetLayouts.push_back(layout);
361 }
362
363 if (m_descriptorSetLayouts.size() != layoutbindings.size())
364 {
365 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create all layouts");
366 return;
367 }
368
369 VkPipelineLayoutCreateInfo pipelinelayout { };
370 pipelinelayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
371 pipelinelayout.setLayoutCount = static_cast<uint32_t>(m_descriptorSetLayouts.size());
372 pipelinelayout.pSetLayouts = m_descriptorSetLayouts.data();
373
374 if (pushconstants)
375 {
376 pipelinelayout.pushConstantRangeCount = 1;
377 pipelinelayout.pPushConstantRanges = &ranges;
378 }
379
380 if (m_vulkanFuncs->vkCreatePipelineLayout(m_vulkanDevice, &pipelinelayout, nullptr, &m_pipelineLayout) != VK_SUCCESS)
381 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create pipeline layout");
382
383 if (!m_pipelineLayout)
384 return;
385
386#if CONFIG_LIBGLSLANG
387 static bool useglsl = qEnvironmentVariableIsSet("MYTHTV_GLSLANG");
388 if (useglsl)
389 {
390 std::vector<MythGLSLStage> glslstages;
391 std::transform(Stages.cbegin(), Stages.cend(), std::back_inserter(glslstages),
392 [&](int Stage) { return MythGLSLStage{Stage & VK_SHADER_STAGE_ALL_GRAPHICS, Sources->at(Stage).first }; });
393 m_vulkanValid = MythShaderVulkan::CreateShaderFromGLSL(glslstages);
394 return;
395 }
396#endif
397
398 std::vector<MythSPIRVStage> stages;
399 std::transform(Stages.cbegin(), Stages.cend(), std::back_inserter(stages),
400 [&](int Stage) { return MythSPIRVStage{Stage & VK_SHADER_STAGE_ALL_GRAPHICS, Sources->at(Stage).second }; });
401 m_vulkanValid = MythShaderVulkan::CreateShaderFromSPIRV(stages);
402}
403
405{
406 if (m_vulkanValid)
407 {
408 m_vulkanFuncs->vkDestroyPipelineLayout(m_vulkanDevice, m_pipelineLayout, nullptr);
409 for (auto & layout : m_descriptorSetLayouts)
410 m_vulkanFuncs->vkDestroyDescriptorSetLayout(m_vulkanDevice, layout, nullptr);
411 for (auto & stage : m_stages)
412 m_vulkanFuncs->vkDestroyShaderModule(m_vulkanDevice, stage.module, nullptr);
413 }
414 for (auto * spirv : m_spirv)
415 delete [] spirv;
416}
417
418bool MythShaderVulkan::CreateShaderFromSPIRV(const std::vector<MythSPIRVStage> &Stages)
419{
420 if (Stages.empty())
421 return false;
422
423 if (std::any_of(Stages.cbegin(), Stages.cend(), [](const MythSPIRVStage& Stage) { return Stage.second.empty(); }))
424 return false;
425
426 bool success = true;
427 for (const auto & stage : Stages)
428 {
429 auto size = stage.second.size() * sizeof (uint32_t);
430 auto *code = reinterpret_cast<uint32_t*>(new uint8_t [size]);
431 memcpy(code, stage.second.data(), size);
432 VkShaderModule module = MYTH_NULL_DISPATCH;
433 VkShaderModuleCreateInfo create { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, nullptr, 0, size, code };
434 success &= (m_vulkanFuncs->vkCreateShaderModule(m_vulkanDevice, &create, nullptr, &module) == VK_SUCCESS);
435 m_stages.push_back( { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr,
436 0, static_cast<VkShaderStageFlagBits>(stage.first), module, "main", nullptr } );
437 m_spirv.push_back(code);
438 }
439 return success;
440}
441
442const std::vector<VkPipelineShaderStageCreateInfo>& MythShaderVulkan::Stages(void) const
443{
444 return m_stages;
445}
446
447
449{
450 return m_vertexAttributes;
451}
452
453const VkVertexInputBindingDescription& MythShaderVulkan::GetVertexBindingDesc(void) const
454{
455 return m_vertexBindingDesc;
456}
457
458VkPipelineLayout MythShaderVulkan::GetPipelineLayout(void) const
459{
460 return m_pipelineLayout;
461}
462
463const std::vector<VkDescriptorPoolSize>& MythShaderVulkan::GetPoolSizes(size_t Set) const
464{
465 if (Set < m_descriptorPoolSizes.size())
466 return m_descriptorPoolSizes.at(Set);
467 static const std::vector<VkDescriptorPoolSize> broken;
468 return broken;
469}
470
471VkDescriptorSetLayout MythShaderVulkan::GetDescSetLayout(size_t Set) const
472{
473 if (Set < m_descriptorSetLayouts.size())
474 return m_descriptorSetLayouts.at(Set);
475 return MYTH_NULL_DISPATCH;
476}
477
478VkPrimitiveTopology MythShaderVulkan::GetTopology() const
479{
480 return m_topology;
481}
Creates shader objects suitable for use with the Vulkan API.
VkPrimitiveTopology GetTopology() const
std::vector< uint32_t * > m_spirv
const std::vector< VkDescriptorPoolSize > & GetPoolSizes(size_t Set) const
bool CreateShaderFromSPIRV(const std::vector< MythSPIRVStage > &Stages)
const std::vector< VkPipelineShaderStageCreateInfo > & Stages(void) const
VkVertexInputBindingDescription m_vertexBindingDesc
VkPipelineLayout m_pipelineLayout
std::vector< std::vector< VkDescriptorPoolSize > > m_descriptorPoolSizes
MythShaderVulkan(MythVulkanObject *Vulkan, const std::vector< int > &Stages, const MythShaderMap *Sources=nullptr, const MythBindingMap *Bindings=nullptr)
VkDescriptorSetLayout GetDescSetLayout(size_t Set) const
static MythShaderVulkan * Create(MythVulkanObject *Vulkan, const std::vector< int > &Stages, const MythShaderMap *Sources=nullptr, const MythBindingMap *Bindings=nullptr)
MythVertexAttrs m_vertexAttributes
VkPrimitiveTopology m_topology
const VkVertexInputBindingDescription & GetVertexBindingDesc(void) const
const MythVertexAttrs & GetVertexAttributes(void) const
std::vector< VkPipelineShaderStageCreateInfo > m_stages
std::vector< VkDescriptorSetLayout > m_descriptorSetLayouts
VkPipelineLayout GetPipelineLayout(void) const
QVulkanDeviceFunctions * m_vulkanFuncs
static guint32 * tmp
Definition: goom_core.cpp:26
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define MYTH_NULL_DISPATCH
static const MythBindingMap k450ShaderBindings
static const MythShaderMap k450DefaultShaders
#define LOC
std::pair< VkShaderStageFlags, const std::vector< uint32_t > > MythSPIRVStage
std::map< int, std::pair< QString, std::vector< uint32_t > > > MythShaderMap
std::vector< VkVertexInputAttributeDescription > MythVertexAttrs
std::tuple< VkPrimitiveTopology, MythStageLayout, VkVertexInputBindingDescription, MythVertexAttrs, VkPushConstantRange > MythBindingDesc
std::vector< MythSetLayout > MythStageLayout
std::map< int, MythBindingDesc > MythBindingMap