From 4b54749954651af5d13dc9ea7f6c47a90851adb6 Mon Sep 17 00:00:00 2001 From: Crimson-Hawk Date: Thu, 25 Apr 2024 21:14:30 +0800 Subject: [PATCH] experimental optimization for spirv --- .gitmodules | 3 +++ CMakeLists.txt | 7 +++++ externals/CMakeLists.txt | 5 ++++ src/common/settings.h | 2 ++ src/shader_recompiler/CMakeLists.txt | 8 +++++- .../backend/spirv/emit_spirv.cpp | 26 +++++++++++++++++-- .../backend/spirv/emit_spirv.h | 7 ++--- src/suyu/configuration/shared_translation.cpp | 4 +++ .../renderer_opengl/gl_shader_cache.cpp | 6 +++-- .../renderer_vulkan/vk_pipeline_cache.cpp | 6 +++-- 10 files changed, 64 insertions(+), 10 deletions(-) diff --git a/.gitmodules b/.gitmodules index d9fbaf3971..c3901c0ea5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -67,3 +67,6 @@ [submodule "Vulkan-Utility-Libraries"] path = externals/Vulkan-Utility-Libraries url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git +[submodule "externals/SPIRV-Tools"] + path = externals/SPIRV-Tools + url = https://github.com/KhronosGroup/SPIRV-Tools.git \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cbeb2ee689..d51305fc03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ option(SUYU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}") option(SUYU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) +option(SUYU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ON) + option(SUYU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan-Utility-Libraries from externals" ON) option(SUYU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) @@ -320,6 +322,11 @@ if (NOT SUYU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES) find_package(VulkanUtilityLibraries REQUIRED) endif() +if (NOT SUYU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS) + find_package(PkgConfig REQUIRED) + pkg_check_modules(SPIRV-Tools REQUIRED SPIRV-Tools) +endif() + if (ENABLE_LIBUSB) find_package(libusb 1.0.24 MODULE) endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index c44457f177..210d687c54 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -160,6 +160,11 @@ if (SUYU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES) add_subdirectory(Vulkan-Utility-Libraries) endif() +# SPIRV-Tools +if (YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS) + add_subdirectory(SPIRV-Tools) +endif() + # TZDB (Time Zone Database) add_subdirectory(nx_tzdb) diff --git a/src/common/settings.h b/src/common/settings.h index 4e993ea622..f9c530d577 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -277,6 +277,8 @@ struct Values { SwitchableSetting use_disk_shader_cache{linkage, true, "use_disk_shader_cache", Category::Renderer}; + SwitchableSetting optimize_spirv_output{linkage, false, "optimize_spirv_output", + Category::Renderer}; SwitchableSetting use_asynchronous_gpu_emulation{ linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; SwitchableSetting accelerate_astc{linkage, diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 3252010d9c..1598276542 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt @@ -242,7 +242,13 @@ add_library(shader_recompiler STATIC varying_state.h ) -target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit) +if (SUYU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS) + set(SPIRV_TOOLS_LIBRARY SPIRV-Tools-opt) +else() + set(SPIRV_TOOLS_LIBRARY SPIRV-Tools SPIRV-Tools-opt) +endif() + +target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit ${SPIRV_TOOLS_LIBRARY}) if (MSVC) target_compile_options(shader_recompiler PRIVATE diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 3f9698d6bf..c3d33bb139 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "common/settings.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" @@ -481,7 +482,7 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { } // Anonymous namespace std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, - IR::Program& program, Bindings& bindings) { + IR::Program& program, Bindings& bindings, bool optimize) { EmitContext ctx{profile, runtime_info, program, bindings}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); @@ -493,7 +494,28 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in SetupCapabilities(profile, program.info, ctx); SetupTransformFeedbackCapabilities(ctx, main); PatchPhiNodes(program, ctx); - return ctx.Assemble(); + + if (!optimize) { + return ctx.Assemble(); + } else { + std::vector spirv = ctx.Assemble(); + + 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); + + std::vector result; + 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); + } + return result; + } } Id EmitPhi(EmitContext& ctx, IR::Inst* inst) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 9378814840..3b36211a2d 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h @@ -31,11 +31,12 @@ constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, do constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area); [[nodiscard]] std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, - IR::Program& program, Bindings& bindings); + IR::Program& program, Bindings& bindingss, bool optimize); -[[nodiscard]] inline std::vector EmitSPIRV(const Profile& profile, IR::Program& program) { +[[nodiscard]] inline std::vector EmitSPIRV(const Profile& profile, IR::Program& program, + bool optimize) { Bindings binding; - return EmitSPIRV(profile, {}, program, binding); + return EmitSPIRV(profile, {}, program, binding, optimize); } } // namespace Shader::Backend::SPIRV diff --git a/src/suyu/configuration/shared_translation.cpp b/src/suyu/configuration/shared_translation.cpp index 0e4d13bc64..7d9750e7ff 100644 --- a/src/suyu/configuration/shared_translation.cpp +++ b/src/suyu/configuration/shared_translation.cpp @@ -143,6 +143,10 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { tr("Allows saving shaders to storage for faster loading on following game " "boots.\nDisabling " "it is only intended for debugging.")); + INSERT(Settings, optimize_spirv_output, tr("Optimize SPIRV output shader"), + tr("Runs an additional optimization pass over generated SPIRV shaders.\n" + "Will increase time required for shader compilation.\nMay slightly improve " + "performance.\nThis feature is experimental.")); INSERT( Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), tr("Uses an extra CPU thread for rendering.\nThis option should always remain enabled.")); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 50f86c29eb..f75eedffde 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -537,7 +537,9 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( break; case Settings::ShaderBackend::SpirV: ConvertLegacyToGeneric(program, runtime_info); - sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding); + sources_spirv[stage_index] = + EmitSPIRV(profile, runtime_info, program, binding, + Settings::values.optimize_spirv_output.GetValue()); break; } previous_program = &program; @@ -596,7 +598,7 @@ std::unique_ptr ShaderCache::CreateComputePipeline( code = EmitGLASM(profile, info, program); break; case Settings::ShaderBackend::SpirV: - code_spirv = EmitSPIRV(profile, program); + code_spirv = EmitSPIRV(profile, program, Settings::values.optimize_spirv_output.GetValue()); break; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 46fada689c..971ae0d4d2 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -673,7 +673,8 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)}; ConvertLegacyToGeneric(program, runtime_info); - const std::vector code{EmitSPIRV(profile, runtime_info, program, binding)}; + const std::vector code{EmitSPIRV(profile, runtime_info, program, binding, + Settings::values.optimize_spirv_output.GetValue())}; device.SaveShader(code); modules[stage_index] = BuildShader(device, code); if (device.HasDebuggingToolAttached()) { @@ -767,7 +768,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( } auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; - const std::vector code{EmitSPIRV(profile, program)}; + const std::vector code{ + EmitSPIRV(profile, program, Settings::values.optimize_spirv_output.GetValue())}; device.SaveShader(code); vk::ShaderModule spv_module{BuildShader(device, code)}; if (device.HasDebuggingToolAttached()) {