vk_shader_util.cpp
1 // Copyright 2023 Citra Emulator Project 2 // Licensed under GPLv2 or any later version 3 // Refer to the license.txt file included. 4 5 #include <SPIRV/GlslangToSpv.h> 6 #include <glslang/Include/ResourceLimits.h> 7 #include <glslang/Public/ShaderLang.h> 8 #include "common/assert.h" 9 #include "common/literals.h" 10 #include "common/logging/log.h" 11 #include "video_core/renderer_vulkan/vk_shader_util.h" 12 13 namespace Vulkan { 14 15 using namespace Common::Literals; 16 17 namespace { 18 constexpr TBuiltInResource DefaultTBuiltInResource = { 19 .maxLights = 32, 20 .maxClipPlanes = 6, 21 .maxTextureUnits = 32, 22 .maxTextureCoords = 32, 23 .maxVertexAttribs = 64, 24 .maxVertexUniformComponents = 4096, 25 .maxVaryingFloats = 64, 26 .maxVertexTextureImageUnits = 32, 27 .maxCombinedTextureImageUnits = 80, 28 .maxTextureImageUnits = 32, 29 .maxFragmentUniformComponents = 4096, 30 .maxDrawBuffers = 32, 31 .maxVertexUniformVectors = 128, 32 .maxVaryingVectors = 8, 33 .maxFragmentUniformVectors = 16, 34 .maxVertexOutputVectors = 16, 35 .maxFragmentInputVectors = 15, 36 .minProgramTexelOffset = -8, 37 .maxProgramTexelOffset = 7, 38 .maxClipDistances = 8, 39 .maxComputeWorkGroupCountX = 65535, 40 .maxComputeWorkGroupCountY = 65535, 41 .maxComputeWorkGroupCountZ = 65535, 42 .maxComputeWorkGroupSizeX = 1024, 43 .maxComputeWorkGroupSizeY = 1024, 44 .maxComputeWorkGroupSizeZ = 64, 45 .maxComputeUniformComponents = 1024, 46 .maxComputeTextureImageUnits = 16, 47 .maxComputeImageUniforms = 8, 48 .maxComputeAtomicCounters = 8, 49 .maxComputeAtomicCounterBuffers = 1, 50 .maxVaryingComponents = 60, 51 .maxVertexOutputComponents = 64, 52 .maxGeometryInputComponents = 64, 53 .maxGeometryOutputComponents = 128, 54 .maxFragmentInputComponents = 128, 55 .maxImageUnits = 8, 56 .maxCombinedImageUnitsAndFragmentOutputs = 8, 57 .maxCombinedShaderOutputResources = 8, 58 .maxImageSamples = 0, 59 .maxVertexImageUniforms = 0, 60 .maxTessControlImageUniforms = 0, 61 .maxTessEvaluationImageUniforms = 0, 62 .maxGeometryImageUniforms = 0, 63 .maxFragmentImageUniforms = 8, 64 .maxCombinedImageUniforms = 8, 65 .maxGeometryTextureImageUnits = 16, 66 .maxGeometryOutputVertices = 256, 67 .maxGeometryTotalOutputComponents = 1024, 68 .maxGeometryUniformComponents = 1024, 69 .maxGeometryVaryingComponents = 64, 70 .maxTessControlInputComponents = 128, 71 .maxTessControlOutputComponents = 128, 72 .maxTessControlTextureImageUnits = 16, 73 .maxTessControlUniformComponents = 1024, 74 .maxTessControlTotalOutputComponents = 4096, 75 .maxTessEvaluationInputComponents = 128, 76 .maxTessEvaluationOutputComponents = 128, 77 .maxTessEvaluationTextureImageUnits = 16, 78 .maxTessEvaluationUniformComponents = 1024, 79 .maxTessPatchComponents = 120, 80 .maxPatchVertices = 32, 81 .maxTessGenLevel = 64, 82 .maxViewports = 16, 83 .maxVertexAtomicCounters = 0, 84 .maxTessControlAtomicCounters = 0, 85 .maxTessEvaluationAtomicCounters = 0, 86 .maxGeometryAtomicCounters = 0, 87 .maxFragmentAtomicCounters = 8, 88 .maxCombinedAtomicCounters = 8, 89 .maxAtomicCounterBindings = 1, 90 .maxVertexAtomicCounterBuffers = 0, 91 .maxTessControlAtomicCounterBuffers = 0, 92 .maxTessEvaluationAtomicCounterBuffers = 0, 93 .maxGeometryAtomicCounterBuffers = 0, 94 .maxFragmentAtomicCounterBuffers = 1, 95 .maxCombinedAtomicCounterBuffers = 1, 96 .maxAtomicCounterBufferSize = 16384, 97 .maxTransformFeedbackBuffers = 4, 98 .maxTransformFeedbackInterleavedComponents = 64, 99 .maxCullDistances = 8, 100 .maxCombinedClipAndCullDistances = 8, 101 .maxSamples = 4, 102 .maxMeshOutputVerticesNV = 256, 103 .maxMeshOutputPrimitivesNV = 512, 104 .maxMeshWorkGroupSizeX_NV = 32, 105 .maxMeshWorkGroupSizeY_NV = 1, 106 .maxMeshWorkGroupSizeZ_NV = 1, 107 .maxTaskWorkGroupSizeX_NV = 32, 108 .maxTaskWorkGroupSizeY_NV = 1, 109 .maxTaskWorkGroupSizeZ_NV = 1, 110 .maxMeshViewCountNV = 4, 111 .maxDualSourceDrawBuffersEXT = 1, 112 .limits = 113 TLimits{ 114 .nonInductiveForLoops = 1, 115 .whileLoops = 1, 116 .doWhileLoops = 1, 117 .generalUniformIndexing = 1, 118 .generalAttributeMatrixVectorIndexing = 1, 119 .generalVaryingIndexing = 1, 120 .generalSamplerIndexing = 1, 121 .generalVariableIndexing = 1, 122 .generalConstantMatrixVectorIndexing = 1, 123 }, 124 }; 125 126 EShLanguage ToEshShaderStage(vk::ShaderStageFlagBits stage) { 127 switch (stage) { 128 case vk::ShaderStageFlagBits::eVertex: 129 return EShLanguage::EShLangVertex; 130 case vk::ShaderStageFlagBits::eGeometry: 131 return EShLanguage::EShLangGeometry; 132 case vk::ShaderStageFlagBits::eFragment: 133 return EShLanguage::EShLangFragment; 134 case vk::ShaderStageFlagBits::eCompute: 135 return EShLanguage::EShLangCompute; 136 default: 137 UNREACHABLE_MSG("Unkown shader stage {}", stage); 138 } 139 return EShLanguage::EShLangVertex; 140 } 141 142 bool InitializeCompiler() { 143 static bool glslang_initialized = false; 144 145 if (glslang_initialized) { 146 return true; 147 } 148 149 if (!glslang::InitializeProcess()) { 150 LOG_CRITICAL(Render_Vulkan, "Failed to initialize glslang shader compiler"); 151 return false; 152 } 153 154 std::atexit([]() { glslang::FinalizeProcess(); }); 155 156 glslang_initialized = true; 157 return true; 158 } 159 } // Anonymous namespace 160 161 vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device) { 162 if (!InitializeCompiler()) { 163 return {}; 164 } 165 166 EProfile profile = ECoreProfile; 167 EShMessages messages = 168 static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); 169 EShLanguage lang = ToEshShaderStage(stage); 170 171 const int default_version = 450; 172 const char* pass_source_code = code.data(); 173 int pass_source_code_length = static_cast<int>(code.size()); 174 175 auto shader = std::make_unique<glslang::TShader>(lang); 176 shader->setEnvTarget(glslang::EShTargetSpv, 177 glslang::EShTargetLanguageVersion::EShTargetSpv_1_3); 178 shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1); 179 180 glslang::TShader::ForbidIncluder includer; 181 if (!shader->parse(&DefaultTBuiltInResource, default_version, profile, false, true, messages, 182 includer)) [[unlikely]] { 183 LOG_INFO(Render_Vulkan, "Shader Info Log:\n{}\n{}", shader->getInfoLog(), 184 shader->getInfoDebugLog()); 185 return {}; 186 } 187 188 // Even though there's only a single shader, we still need to link it to generate SPV 189 auto program = std::make_unique<glslang::TProgram>(); 190 program->addShader(shader.get()); 191 if (!program->link(messages)) { 192 LOG_INFO(Render_Vulkan, "Program Info Log:\n{}\n{}", program->getInfoLog(), 193 program->getInfoDebugLog()); 194 return {}; 195 } 196 197 glslang::TIntermediate* intermediate = program->getIntermediate(lang); 198 std::vector<u32> out_code; 199 spv::SpvBuildLogger logger; 200 glslang::SpvOptions options; 201 202 // Enable optimizations on the generated SPIR-V code. 203 options.disableOptimizer = false; 204 options.validate = false; 205 options.optimizeSize = true; 206 207 out_code.reserve(8_KiB); 208 glslang::GlslangToSpv(*intermediate, out_code, &logger, &options); 209 210 const std::string spv_messages = logger.getAllMessages(); 211 if (!spv_messages.empty()) { 212 LOG_INFO(Render_Vulkan, "SPIR-V conversion messages: {}", spv_messages); 213 } 214 215 return CompileSPV(out_code, device); 216 } 217 218 vk::ShaderModule CompileSPV(std::span<const u32> code, vk::Device device) { 219 const vk::ShaderModuleCreateInfo shader_info = { 220 .codeSize = code.size() * sizeof(u32), 221 .pCode = code.data(), 222 }; 223 224 try { 225 return device.createShaderModule(shader_info); 226 } catch (vk::SystemError& err) { 227 UNREACHABLE_MSG("{}", err.what()); 228 } 229 230 return {}; 231 } 232 233 } // namespace Vulkan