From e6f0b73d23c0b307c531ca7c58d62a347c649479 Mon Sep 17 00:00:00 2001 From: spectranator Date: Thu, 14 Nov 2024 18:03:56 +0100 Subject: [PATCH] video_core: Implement Vulkan SPIR-V Optimization on Desktop OSes --- .gitmodules | 3 + externals/CMakeLists.txt | 4 ++ externals/SPIRV-Tools | 1 + src/common/settings.h | 15 +++++ .../renderer_vulkan/vk_shader_util.cpp | 56 ++++++++++++++++++- .../renderer_vulkan/vk_shader_util.h | 6 ++ .../shader/generator/spv_fs_shader_gen.cpp | 12 +++- .../shader/generator/spv_shader_gen.cpp | 12 +++- 8 files changed, 106 insertions(+), 3 deletions(-) create mode 160000 externals/SPIRV-Tools diff --git a/.gitmodules b/.gitmodules index e762cc163..0e364e91b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -94,3 +94,6 @@ [submodule "externals/oboe"] path = externals/oboe url = https://github.com/google/oboe.git +[submodule "externals/SPIRV-Tools"] + path = externals/SPIRV-Tools + url = https://github.com/KhronosGroup/SPIRV-Tools.git diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 24d1f57bb..5addce320 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -427,6 +427,10 @@ if (ENABLE_VULKAN) target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include) endif() + # SPIRV-Tools + add_library(SPIRV-Tools INTERFACE) + target_include_directories(SPIRV-Tools SYSTEM INTERFACE ./SPIRV-Tools/include) + # adrenotools if (ANDROID AND "arm64" IN_LIST ARCHITECTURE) add_subdirectory(libadrenotools) diff --git a/externals/SPIRV-Tools b/externals/SPIRV-Tools new file mode 160000 index 000000000..35e5f1160 --- /dev/null +++ b/externals/SPIRV-Tools @@ -0,0 +1 @@ +Subproject commit 35e5f1160ecd2b963682e14e869001a60160a0c4 diff --git a/src/common/settings.h b/src/common/settings.h index 199f3282c..a37a16e9a 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -471,6 +471,21 @@ struct Values { Setting renderer_debug{false, "renderer_debug"}; Setting dump_command_buffers{false, "dump_command_buffers"}; SwitchableSetting spirv_shader_gen{true, "spirv_shader_gen"}; + +// SPIR-V Optimization only works on Desktop OSes for now +#ifdef __APPLE__ +#include +#if TARGET_OS_IPHONE + SwitchableSetting optimize_spirv_output{false, "optimize_spirv_output"}; +#elif TARGET_OS_MAC + SwitchableSetting optimize_spirv_output{true, "optimize_spirv_output"}; +#endif +#elif __ANDROID__ + SwitchableSetting optimize_spirv_output{false, "optimize_spirv_output"}; +#else + SwitchableSetting optimize_spirv_output{true, "optimize_spirv_output"}; +#endif + SwitchableSetting async_shader_compilation{false, "async_shader_compilation"}; SwitchableSetting async_presentation{true, "async_presentation"}; SwitchableSetting use_hw_shader{true, "use_hw_shader"}; diff --git a/src/video_core/renderer_vulkan/vk_shader_util.cpp b/src/video_core/renderer_vulkan/vk_shader_util.cpp index c38dc39bd..4a0968801 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_util.cpp @@ -3,12 +3,30 @@ // Refer to the license.txt file included. #include +#include +#include #include #include #include + +// The optimizer included in SPIRV-Tools only works on Desktop OSes +#if defined(_WIN32) || (defined(__linux__) && !defined(__ANDROID__)) +#define OPTIMIZE_SPIRV +#elif defined(__APPLE__) +#include +#if TARGET_OS_OSX +#define OPTIMIZE_SPIRV +#endif +#endif + +#ifdef OPTIMIZE_SPIRV +#include +#endif + #include "common/assert.h" #include "common/literals.h" #include "common/logging/log.h" +#include "common/settings.h" #include "video_core/renderer_vulkan/vk_shader_util.h" namespace Vulkan { @@ -159,6 +177,35 @@ bool InitializeCompiler() { } } // Anonymous namespace +/** + * NOTE: The libraries included in SPIRV-Tools only work on Windows, Linux and MacOS. + * @brief Optimizes SPIR-V code (but only on Desktop) + * @param code The string containing SPIR-V code + */ +std::vector OptimizeSPIRV(std::vector code) { + + std::vector result = code; + +#ifdef OPTIMIZE_SPIRV + std::vector spirv = code; + spvtools::Optimizer spv_opt(SPV_ENV_VULKAN_1_3); + spv_opt.SetMessageConsumer([](spv_message_level_t, const char*, const spv_position_t&, + const char* m) { LOG_ERROR(HW_GPU, "spirv-opt: {}", m); }); + spv_opt.RegisterPerformancePasses(); + + spvtools::OptimizerOptions opt_options; + opt_options.set_run_validator(false); + + if (!spv_opt.Run(spirv.data(), spirv.size(), &result, opt_options)) { + LOG_ERROR(HW_GPU, + "Failed to optimize SPIRV shader output, continuing without optimization"); + result = std::move(spirv); + } +#endif + + return result; +} + /** * @brief Compiles GLSL into SPIRV * @param code The string containing GLSL code. @@ -222,7 +269,14 @@ std::vector CompileGLSLtoSPIRV(std::string_view code, vk::ShaderStageFlagBi LOG_INFO(Render_Vulkan, "SPIR-V conversion messages: {}", spv_messages); } - return out_code; + // Final pass through SPIRV-Optimizer + if (!Settings::values.optimize_spirv_output.GetValue()) { + return out_code; + } else { + std::vector result; + result = OptimizeSPIRV(out_code); + return result; + } } vk::ShaderModule Compile(std::string_view code, vk::ShaderStageFlagBits stage, vk::Device device, diff --git a/src/video_core/renderer_vulkan/vk_shader_util.h b/src/video_core/renderer_vulkan/vk_shader_util.h index 4e6e99434..34b11d627 100644 --- a/src/video_core/renderer_vulkan/vk_shader_util.h +++ b/src/video_core/renderer_vulkan/vk_shader_util.h @@ -10,6 +10,12 @@ namespace Vulkan { +/** + * @brief Optimizes SPIR-V code + * @param code The string containing SPIR-V code + */ +std::vector OptimizeSPIRV(std::vector code); + /** * @brief Compiles GLSL into SPIRV * @param code The string containing GLSL code. diff --git a/src/video_core/shader/generator/spv_fs_shader_gen.cpp b/src/video_core/shader/generator/spv_fs_shader_gen.cpp index 03bf7f49f..6d5a07986 100644 --- a/src/video_core/shader/generator/spv_fs_shader_gen.cpp +++ b/src/video_core/shader/generator/spv_fs_shader_gen.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include +#include "common/settings.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/shader/generator/pica_fs_config.h" #include "video_core/shader/generator/spv_fs_shader_gen.h" @@ -1605,7 +1607,15 @@ void FragmentModule::DefineInterface() { std::vector GenerateFragmentShader(const FSConfig& config, const Profile& profile) { FragmentModule module{config, profile}; module.Generate(); - return module.Assemble(); + + // Run through SPIRV-Optimizer + if (!Settings::values.optimize_spirv_output.GetValue()) { + return module.Assemble(); + } else { + std::vector result; + result = Vulkan::OptimizeSPIRV(module.Assemble()); + return result; + } } } // namespace Pica::Shader::Generator::SPIRV diff --git a/src/video_core/shader/generator/spv_shader_gen.cpp b/src/video_core/shader/generator/spv_shader_gen.cpp index 34e320bd2..f5882876a 100644 --- a/src/video_core/shader/generator/spv_shader_gen.cpp +++ b/src/video_core/shader/generator/spv_shader_gen.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "video_core/pica/regs_rasterizer.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/shader/generator/shader_gen.h" #include "video_core/shader/generator/spv_shader_gen.h" @@ -267,7 +269,15 @@ std::vector GenerateTrivialVertexShader(bool use_clip_planes) { } } }); - return module.Assemble(); + + // Run through SPIR-V Optimizer + if (!Settings::values.optimize_spirv_output.GetValue()) { + return module.Assemble(); + } else { + std::vector result; + result = Vulkan::OptimizeSPIRV(module.Assemble()); + return result; + } } } // namespace Pica::Shader::Generator::SPIRV