From dda1479ecf127c6729932de58060b5fd7b322d5f Mon Sep 17 00:00:00 2001 From: iwubcode Date: Tue, 24 May 2022 23:12:47 -0500 Subject: [PATCH] VideoBackends / VideoCommon: refactor Vulkan to use new SPIRV functionality --- .../Core/VideoBackends/D3DCommon/Shader.cpp | 6 +- .../Core/VideoBackends/Vulkan/CMakeLists.txt | 9 - .../VideoBackends/Vulkan/ShaderCompiler.cpp | 200 ++---------------- Source/Core/VideoCommon/Spirv.cpp | 32 +-- Source/Core/VideoCommon/Spirv.h | 15 +- 5 files changed, 49 insertions(+), 213 deletions(-) diff --git a/Source/Core/VideoBackends/D3DCommon/Shader.cpp b/Source/Core/VideoBackends/D3DCommon/Shader.cpp index 236efb77e7..1be4b2298e 100644 --- a/Source/Core/VideoBackends/D3DCommon/Shader.cpp +++ b/Source/Core/VideoBackends/D3DCommon/Shader.cpp @@ -114,7 +114,7 @@ std::optional GetSpirv(ShaderStage stage, std::string_view so case ShaderStage::Vertex: { const auto full_source = fmt::format("{}{}", SHADER_HEADER, source); - return SPIRV::CompileVertexShader(full_source); + return SPIRV::CompileVertexShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); } case ShaderStage::Geometry: @@ -126,13 +126,13 @@ std::optional GetSpirv(ShaderStage stage, std::string_view so case ShaderStage::Pixel: { const auto full_source = fmt::format("{}{}", SHADER_HEADER, source); - return SPIRV::CompileFragmentShader(full_source); + return SPIRV::CompileFragmentShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); } case ShaderStage::Compute: { const auto full_source = fmt::format("{}{}", COMPUTE_SHADER_HEADER, source); - return SPIRV::CompileComputeShader(full_source); + return SPIRV::CompileComputeShader(full_source, APIType::D3D, glslang::EShTargetSpv_1_0); } }; diff --git a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt index 110733e613..4489ca9e10 100644 --- a/Source/Core/VideoBackends/Vulkan/CMakeLists.txt +++ b/Source/Core/VideoBackends/Vulkan/CMakeLists.txt @@ -54,15 +54,6 @@ PRIVATE ${CMAKE_SOURCE_DIR}/Externals/Vulkan/Include ) -# Silence warnings on glslang by flagging it as a system include -target_include_directories(videovulkan -SYSTEM PRIVATE - ${CMAKE_SOURCE_DIR}/Externals/glslang/StandAlone - ${CMAKE_SOURCE_DIR}/Externals/glslang/glslang/Public - ${CMAKE_SOURCE_DIR}/Externals/glslang/SPIRV - ${CMAKE_SOURCE_DIR}/Externals/glslang -) - if(MSVC) # Add precompiled header target_link_libraries(videovulkan PRIVATE use_pch) diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp index c9adcc1bf7..fde89ca394 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp @@ -4,36 +4,13 @@ #include "VideoBackends/Vulkan/ShaderCompiler.h" #include -#include -#include -#include -#include #include -// glslang includes -#include "GlslangToSpv.h" -#include "ResourceLimits.h" -#include "ShaderLang.h" -#include "disassemble.h" - -#include "Common/FileUtil.h" -#include "Common/Logging/Log.h" -#include "Common/MsgHandler.h" -#include "Common/StringUtil.h" -#include "Common/Version.h" - #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/VideoBackendBase.h" -#include "VideoCommon/VideoConfig.h" +#include "VideoCommon/Spirv.h" namespace Vulkan::ShaderCompiler { -// Registers itself for cleanup via atexit -bool InitializeGlslang(); - -// Resource limits used when compiling shaders -static const TBuiltInResource* GetCompilerResourceLimits(); - // Regarding the UBO bind points, we subtract one from the binding index because // the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV). // This allows us to share the same shaders but use bind point #0 in the Vulkan @@ -111,25 +88,9 @@ static const char SUBGROUP_HELPER_HEADER[] = R"( #define SUBGROUP_MAX(value) value = subgroupMax(value) )"; -static std::optional CompileShaderToSPV(EShLanguage stage, - const char* stage_filename, - std::string_view source, - std::string_view header) +static std::string GetShaderCode(std::string_view source, std::string_view header) { - if (!InitializeGlslang()) - return std::nullopt; - - std::unique_ptr shader = std::make_unique(stage); - std::unique_ptr program; - glslang::TShader::ForbidIncluder includer; - EProfile profile = ECoreProfile; - EShMessages messages = - static_cast(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules); - int default_version = 450; - std::string full_source_code; - const char* pass_source_code = source.data(); - int pass_source_code_length = static_cast(source.size()); if (!header.empty()) { constexpr size_t subgroup_helper_header_length = std::size(SUBGROUP_HELPER_HEADER) - 1; @@ -138,168 +99,41 @@ static std::optional CompileShaderToSPV(EShLanguage stage, if (g_vulkan_context->SupportsShaderSubgroupOperations()) full_source_code.append(SUBGROUP_HELPER_HEADER, subgroup_helper_header_length); full_source_code.append(source); - pass_source_code = full_source_code.c_str(); - pass_source_code_length = static_cast(full_source_code.length()); } + return full_source_code; +} + +static glslang::EShTargetLanguageVersion GetLanguageVersion() +{ // Sub-group operations require Vulkan 1.1 and SPIR-V 1.3. if (g_vulkan_context->SupportsShaderSubgroupOperations()) - shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_3); + return glslang::EShTargetSpv_1_3; - shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1); - - auto DumpBadShader = [&](const char* msg) { - static int counter = 0; - std::string filename = VideoBackendBase::BadShaderFilename(stage_filename, counter++); - std::ofstream stream; - File::OpenFStream(stream, filename, std::ios_base::out); - if (stream.good()) - { - stream << full_source_code << std::endl; - stream << msg << std::endl; - stream << "Shader Info Log:" << std::endl; - stream << shader->getInfoLog() << std::endl; - stream << shader->getInfoDebugLog() << std::endl; - if (program) - { - stream << "Program Info Log:" << std::endl; - stream << program->getInfoLog() << std::endl; - stream << program->getInfoDebugLog() << std::endl; - } - } - - stream << "\n"; - stream << "Dolphin Version: " + Common::GetScmRevStr() + "\n"; - stream << "Video Backend: " + g_video_backend->GetDisplayName(); - stream.close(); - - PanicAlertFmt("{} (written to {})\nDebug info:\n{}", msg, filename, shader->getInfoLog()); - }; - - if (!shader->parse(GetCompilerResourceLimits(), default_version, profile, false, true, messages, - includer)) - { - DumpBadShader("Failed to parse shader"); - return std::nullopt; - } - - // Even though there's only a single shader, we still need to link it to generate SPV - program = std::make_unique(); - program->addShader(shader.get()); - if (!program->link(messages)) - { - DumpBadShader("Failed to link program"); - return std::nullopt; - } - - glslang::TIntermediate* intermediate = program->getIntermediate(stage); - if (!intermediate) - { - DumpBadShader("Failed to generate SPIR-V"); - return std::nullopt; - } - - SPIRVCodeVector out_code; - spv::SpvBuildLogger logger; - glslang::SpvOptions options; - - if (g_ActiveConfig.bEnableValidationLayer) - { - // Attach the source code to the SPIR-V for tools like RenderDoc. - intermediate->addSourceText(pass_source_code, pass_source_code_length); - - options.generateDebugInfo = true; - options.disableOptimizer = true; - options.optimizeSize = false; - options.disassemble = false; - options.validate = true; - } - - glslang::GlslangToSpv(*intermediate, out_code, &logger, &options); - - // Write out messages - // Temporary: skip if it contains "Warning, version 450 is not yet complete; most version-specific - // features are present, but some are missing." - if (strlen(shader->getInfoLog()) > 108) - WARN_LOG_FMT(VIDEO, "Shader info log: {}", shader->getInfoLog()); - if (strlen(shader->getInfoDebugLog()) > 0) - WARN_LOG_FMT(VIDEO, "Shader debug info log: {}", shader->getInfoDebugLog()); - if (strlen(program->getInfoLog()) > 25) - WARN_LOG_FMT(VIDEO, "Program info log: {}", program->getInfoLog()); - if (strlen(program->getInfoDebugLog()) > 0) - WARN_LOG_FMT(VIDEO, "Program debug info log: {}", program->getInfoDebugLog()); - const std::string spv_messages = logger.getAllMessages(); - if (!spv_messages.empty()) - WARN_LOG_FMT(VIDEO, "SPIR-V conversion messages: {}", spv_messages); - - // Dump source code of shaders out to file if enabled. - if (g_ActiveConfig.iLog & CONF_SAVESHADERS) - { - static int counter = 0; - std::string filename = StringFromFormat("%s%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), - stage_filename, counter++); - - std::ofstream stream; - File::OpenFStream(stream, filename, std::ios_base::out); - if (stream.good()) - { - stream << full_source_code << std::endl; - stream << "Shader Info Log:" << std::endl; - stream << shader->getInfoLog() << std::endl; - stream << shader->getInfoDebugLog() << std::endl; - stream << "Program Info Log:" << std::endl; - stream << program->getInfoLog() << std::endl; - stream << program->getInfoDebugLog() << std::endl; - stream << "SPIR-V conversion messages: " << std::endl; - stream << spv_messages; - stream << "SPIR-V:" << std::endl; - spv::Disassemble(stream, out_code); - } - } - - return out_code; -} - -bool InitializeGlslang() -{ - static bool glslang_initialized = false; - if (glslang_initialized) - return true; - - if (!glslang::InitializeProcess()) - { - PanicAlertFmt("Failed to initialize glslang shader compiler"); - return false; - } - - std::atexit([]() { glslang::FinalizeProcess(); }); - - glslang_initialized = true; - return true; -} - -const TBuiltInResource* GetCompilerResourceLimits() -{ - return &glslang::DefaultTBuiltInResource; + return glslang::EShTargetSpv_1_0; } std::optional CompileVertexShader(std::string_view source_code) { - return CompileShaderToSPV(EShLangVertex, "vs", source_code, SHADER_HEADER); + return SPIRV::CompileVertexShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, + GetLanguageVersion()); } std::optional CompileGeometryShader(std::string_view source_code) { - return CompileShaderToSPV(EShLangGeometry, "gs", source_code, SHADER_HEADER); + return SPIRV::CompileGeometryShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, + GetLanguageVersion()); } std::optional CompileFragmentShader(std::string_view source_code) { - return CompileShaderToSPV(EShLangFragment, "ps", source_code, SHADER_HEADER); + return SPIRV::CompileFragmentShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, + GetLanguageVersion()); } std::optional CompileComputeShader(std::string_view source_code) { - return CompileShaderToSPV(EShLangCompute, "cs", source_code, COMPUTE_SHADER_HEADER); + return SPIRV::CompileComputeShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan, + GetLanguageVersion()); } } // namespace Vulkan::ShaderCompiler diff --git a/Source/Core/VideoCommon/Spirv.cpp b/Source/Core/VideoCommon/Spirv.cpp index 9819811367..773a5463f2 100644 --- a/Source/Core/VideoCommon/Spirv.cpp +++ b/Source/Core/VideoCommon/Spirv.cpp @@ -6,7 +6,6 @@ // glslang includes #include "GlslangToSpv.h" #include "ResourceLimits.h" -#include "ShaderLang.h" #include "disassemble.h" #include "Common/FileUtil.h" @@ -44,9 +43,9 @@ const TBuiltInResource* GetCompilerResourceLimits() } std::optional -CompileShaderToSPV(EShLanguage stage, - std::optional language_version, - const char* stage_filename, std::string_view source) +CompileShaderToSPV(EShLanguage stage, APIType api_type, + glslang::EShTargetLanguageVersion language_version, const char* stage_filename, + std::string_view source) { if (!InitializeGlslang()) return std::nullopt; @@ -56,13 +55,14 @@ CompileShaderToSPV(EShLanguage stage, glslang::TShader::ForbidIncluder includer; EProfile profile = ECoreProfile; EShMessages messages = static_cast(EShMsgDefault | EShMsgSpvRules); + if (api_type == APIType::Vulkan) + messages = static_cast(messages | EShMsgVulkanRules); int default_version = 450; const char* pass_source_code = source.data(); int pass_source_code_length = static_cast(source.size()); - if (language_version) - shader->setEnvTarget(glslang::EShTargetSpv, *language_version); + shader->setEnvTarget(glslang::EShTargetSpv, language_version); shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1); @@ -181,23 +181,27 @@ CompileShaderToSPV(EShLanguage stage, namespace SPIRV { -std::optional CompileVertexShader(std::string_view source_code) +std::optional CompileVertexShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version) { - return CompileShaderToSPV(EShLangVertex, std::nullopt, "vs", source_code); + return CompileShaderToSPV(EShLangVertex, api_type, language_version, "vs", source_code); } -std::optional CompileGeometryShader(std::string_view source_code) +std::optional CompileGeometryShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version) { - return CompileShaderToSPV(EShLangGeometry, std::nullopt, "gs", source_code); + return CompileShaderToSPV(EShLangGeometry, api_type, language_version, "gs", source_code); } -std::optional CompileFragmentShader(std::string_view source_code) +std::optional CompileFragmentShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version) { - return CompileShaderToSPV(EShLangFragment, std::nullopt, "ps", source_code); + return CompileShaderToSPV(EShLangFragment, api_type, language_version, "ps", source_code); } -std::optional CompileComputeShader(std::string_view source_code) +std::optional CompileComputeShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version) { - return CompileShaderToSPV(EShLangCompute, std::nullopt, "cs", source_code); + return CompileShaderToSPV(EShLangCompute, api_type, language_version, "cs", source_code); } } // namespace SPIRV diff --git a/Source/Core/VideoCommon/Spirv.h b/Source/Core/VideoCommon/Spirv.h index 81d1917eeb..fe75ea0f99 100644 --- a/Source/Core/VideoCommon/Spirv.h +++ b/Source/Core/VideoCommon/Spirv.h @@ -8,7 +8,10 @@ #include #include +#include "ShaderLang.h" + #include "Common/CommonTypes.h" +#include "VideoCommon/VideoCommon.h" namespace SPIRV { @@ -17,14 +20,18 @@ using CodeType = u32; using CodeVector = std::vector; // Compile a vertex shader to SPIR-V. -std::optional CompileVertexShader(std::string_view source_code); +std::optional CompileVertexShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version); // Compile a geometry shader to SPIR-V. -std::optional CompileGeometryShader(std::string_view source_code); +std::optional CompileGeometryShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version); // Compile a fragment shader to SPIR-V. -std::optional CompileFragmentShader(std::string_view source_code); +std::optional CompileFragmentShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version); // Compile a compute shader to SPIR-V. -std::optional CompileComputeShader(std::string_view source_code); +std::optional CompileComputeShader(std::string_view source_code, APIType api_type, + glslang::EShTargetLanguageVersion language_version); } // namespace SPIRV