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