2#include "libmythbase/mythconfig.h"
10#define LOC QString("VulkanShader: ")
14#include <glslang/Public/ShaderLang.h>
15#include <glslang/SPIRV/GlslangToSpv.h>
17static const TBuiltInResource s_TBuiltInResource = {
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,
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,
55 .maxCombinedImageUnitsAndFragmentOutputs = 8,
56 .maxCombinedShaderOutputResources = 8,
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,
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,
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#if HAVE_TBUILTINRESOURCE_EXT_FIELDS
111 .maxMeshOutputVerticesEXT = 0,
112 .maxMeshOutputPrimitivesEXT = 0,
113 .maxMeshWorkGroupSizeX_EXT = 0,
114 .maxMeshWorkGroupSizeY_EXT = 0,
115 .maxMeshWorkGroupSizeZ_EXT = 0,
116 .maxTaskWorkGroupSizeX_EXT = 0,
117 .maxTaskWorkGroupSizeY_EXT = 0,
118 .maxTaskWorkGroupSizeZ_EXT = 0,
119 .maxMeshViewCountEXT = 0,
120 .maxDualSourceDrawBuffersEXT = 0,
124 .nonInductiveForLoops =
true,
126 .doWhileLoops =
true,
127 .generalUniformIndexing =
true,
128 .generalAttributeMatrixVectorIndexing =
true,
129 .generalVaryingIndexing =
true,
130 .generalSamplerIndexing =
true,
131 .generalVariableIndexing =
true,
132 .generalConstantMatrixVectorIndexing =
true,
136static auto GLSLangCompile(EShLanguage Stage,
const QString &Code)
138 std::vector<uint32_t> result;
139 auto *shader =
new glslang::TShader(Stage);
143 QByteArray data = Code.toLocal8Bit();
144 const char *tmp = data.constData();
145 shader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
146 shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3);
147 shader->setStrings(&tmp, 1);
148 if (!shader->parse(&s_TBuiltInResource, glslang::EShTargetVulkan_1_1,
true, EShMsgDefault))
150 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Shader parse error:");
151 LOG(VB_GENERAL, LOG_ERR, shader->getInfoLog());
156 auto *program =
new glslang::TProgram();
163 program->addShader(shader);
164 if (program->link(EShMsgDefault))
166 auto ByteCodeToString = [](std::vector<uint32_t> &OpCodes)
170 for (uint32_t opcode : OpCodes)
172 if (count++ == 0)
string +=
"\n";
173 if (count > 5) count = 0;
174 string +=
"0x" + QString(
"%1, ").arg(opcode, 8, 16, QLatin1Char(
'0')).toUpper();
179 glslang::SpvOptions
options { };
180 options.generateDebugInfo =
false;
183 options.disableOptimizer =
false;
185 GlslangToSpv(*program->getIntermediate(Stage), result, &
options);
186 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Generated SPIR-V: %1bytes").arg(result.size() * sizeof (uint32_t)));
187 LOG(VB_GENERAL, LOG_INFO,
"Source:\n" + Code);
188 LOG(VB_GENERAL, LOG_INFO,
"ByteCode:\n" + ByteCodeToString(result));
192 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Shader link error:");
193 LOG(VB_GENERAL, LOG_ERR, program->getInfoLog());
201bool MythShaderVulkan::InitGLSLang(
bool Release )
203 static QMutex s_glslangLock;
204 static int s_glslangRefcount = 0;
205 static bool s_initSuccess =
false;
206 QMutexLocker locker(&s_glslangLock);
211 if (s_glslangRefcount < 0)
213 LOG(VB_GENERAL, LOG_ERR,
LOC +
"GLSLang ref count error");
217 if (s_glslangRefcount < 1)
219 LOG(VB_GENERAL, LOG_INFO,
LOC +
"GLSLang released");
220 glslang::FinalizeProcess();
225 if (s_glslangRefcount < 1)
227 s_initSuccess = glslang::InitializeProcess();
229 LOG(VB_GENERAL, LOG_INFO,
LOC +
"GLSLang initialised");
232 return s_initSuccess;
235bool MythShaderVulkan::CreateShaderFromGLSL(
const std::vector<MythGLSLStage> &Stages)
240 if (!MythShaderVulkan::InitGLSLang())
243 std::vector<MythSPIRVStage> spirvstages;
245 auto glslangtype = [](VkShaderStageFlags Type)
249 case VK_SHADER_STAGE_VERTEX_BIT:
return EShLangVertex;
250 case VK_SHADER_STAGE_FRAGMENT_BIT:
return EShLangFragment;
251 case VK_SHADER_STAGE_COMPUTE_BIT:
return EShLangCompute;
257 for (
const auto& stage :
Stages)
258 if (glslangtype(stage.first) != EShLangCount)
259 spirvstages.emplace_back(stage.first, GLSLangCompile(glslangtype(stage.first), stage.second));
260 MythShaderVulkan::InitGLSLang(
true);
274 const std::vector<int> &Stages,
279 if (result && !result->IsValidVulkan())
281 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create shader");
289 const std::vector<int> &Stages,
305 for (
const auto & stage :
Stages)
306 if (!Sources->contains(stage) || !Bindings->contains(stage))
310 bool foundvertices =
false;
311 bool pushconstants =
false;
312 VkPushConstantRange ranges = { };
314 std::map<int, std::vector<VkDescriptorSetLayoutBinding>> layoutbindings;
315 std::map<int, std::vector<VkDescriptorPoolSize>> poolsizes;
316 for (
const auto & stage :
Stages)
318 bool isvertex =
false;
323 for (
auto & stagelayout : binding)
325 if (stagelayout.second.stageFlags == VK_SHADER_STAGE_VERTEX_BIT)
328 if (layoutbindings.contains(stagelayout.first))
329 layoutbindings.at(stagelayout.first).emplace_back(stagelayout.second);
331 layoutbindings.insert( { stagelayout.first, { stagelayout.second } } );
333 VkDescriptorPoolSize poolsize = {stagelayout.second.descriptorType, stagelayout.second.descriptorCount};
334 if (poolsizes.contains(stagelayout.first))
335 poolsizes.at(stagelayout.first).emplace_back(poolsize);
337 poolsizes.insert( { stagelayout.first, { poolsize } } );
340 if (isvertex && !foundvertices)
342 foundvertices =
true;
349 VkPushConstantRange range = std::get<4>(desc);
350 if (range.stageFlags)
352 pushconstants =
true;
359 for (
const auto& poolsize : poolsizes)
363 for (
auto & layoutbinding : layoutbindings)
366 VkDescriptorSetLayoutCreateInfo layoutinfo { };
367 layoutinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
368 layoutinfo.bindingCount =
static_cast<uint32_t
>(layoutbinding.second.size());
369 layoutinfo.pBindings = layoutbinding.second.data();
371 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create DescriptorSetLayout");
378 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create all layouts");
382 VkPipelineLayoutCreateInfo pipelinelayout { };
383 pipelinelayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
389 pipelinelayout.pushConstantRangeCount = 1;
390 pipelinelayout.pPushConstantRanges = &ranges;
394 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to create pipeline layout");
400 static bool useglsl = qEnvironmentVariableIsSet(
"MYTHTV_GLSLANG");
403 std::vector<MythGLSLStage> glslstages;
404 std::ranges::transform(
Stages, std::back_inserter(glslstages),
405 [&](
int Stage) {
return MythGLSLStage{Stage & VK_SHADER_STAGE_ALL_GRAPHICS, Sources->at(Stage).first }; });
406 m_vulkanValid = MythShaderVulkan::CreateShaderFromGLSL(glslstages);
411 std::vector<MythSPIRVStage> stages;
412 std::ranges::transform(
Stages, std::back_inserter(stages),
413 [&](
int Stage) {
return MythSPIRVStage{Stage & VK_SHADER_STAGE_ALL_GRAPHICS, Sources->at(Stage).second }; });
436 if (std::ranges::any_of(
Stages,
437 [](
const MythSPIRVStage& Stage) {
return Stage.second.empty(); }))
441 for (
const auto & stage :
Stages)
443 auto size = stage.second.size() *
sizeof (uint32_t);
444 auto *code =
reinterpret_cast<uint32_t*
>(
new uint8_t [size]);
445 memcpy(code, stage.second.data(), size);
447 VkShaderModuleCreateInfo create { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
nullptr, 0, size, code };
449 m_stages.push_back( { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
450 0,
static_cast<VkShaderStageFlagBits
>(stage.first), module,
"main",
nullptr } );
481 static const std::vector<VkDescriptorPoolSize> broken;
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_)
#define MYTH_NULL_DISPATCH
static const MythBindingMap k450ShaderBindings
static const MythShaderMap k450DefaultShaders
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::pair< VkShaderStageFlags, QString > MythGLSLStage
std::vector< MythSetLayout > MythStageLayout
std::map< int, MythBindingDesc > MythBindingMap