From fac1dbc7e6ac61f68f1c9fb3c50952b04c1e9542 Mon Sep 17 00:00:00 2001 From: darktux Date: Wed, 10 Apr 2024 22:04:09 +0000 Subject: [PATCH] Optionally optimize generated SPIRV with spirv-opt (#10) Reviewed-on: http://y2nlvhmmk5jnsvechppxnbyzmmv3vbl7dvzn6ltwcdbpgxixp3clkgqd.onion/darktux/torzu/pulls/10 Co-authored-by: darktux Co-committed-by: darktux --- .gitmodules | 3 +++ CMakeLists.txt | 7 +++++ externals/CMakeLists.txt | 5 ++++ externals/SPIRV-Tools | 1 + src/common/settings.h | 2 ++ src/shader_recompiler/CMakeLists.txt | 8 +++++- .../backend/spirv/emit_spirv.cpp | 26 +++++++++++++++++-- .../backend/spirv/emit_spirv.h | 7 ++--- .../renderer_opengl/gl_shader_cache.cpp | 6 +++-- .../renderer_vulkan/vk_pipeline_cache.cpp | 6 +++-- src/yuzu/configuration/shared_translation.cpp | 4 +++ 11 files changed, 65 insertions(+), 10 deletions(-) create mode 160000 externals/SPIRV-Tools diff --git a/.gitmodules b/.gitmodules index df315f865..1184a1702 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,3 +58,6 @@ [submodule "externals/boost-headers"] path = externals/boost-headers url = https://github.com/boostorg/headers.git +[submodule "externals/SPIRV-Tools"] + path = externals/SPIRV-Tools + url = https://github.com/KhronosGroup/SPIRV-Tools.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 17de9ebac..4d0399022 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,8 @@ option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON) option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan-Utility-Libraries from externals" ON) +option(YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ON) + option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF) option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) @@ -304,6 +306,11 @@ if (NOT YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES) find_package(VulkanUtilityLibraries REQUIRED) endif() +if (NOT YUZU_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 c8ccc1f79..ab637b3f0 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -166,6 +166,11 @@ if (YUZU_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() + # Boost headers add_subdirectory(boost-headers) diff --git a/externals/SPIRV-Tools b/externals/SPIRV-Tools new file mode 160000 index 000000000..3983d15a1 --- /dev/null +++ b/externals/SPIRV-Tools @@ -0,0 +1 @@ +Subproject commit 3983d15a1d34fb95656818af0fc89c6260cbf316 diff --git a/src/common/settings.h b/src/common/settings.h index 2117c1676..b47e1ef88 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -276,6 +276,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 19db17c6d..c809b220a 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 (YUZU_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 3f9698d6b..c3d33bb13 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 937881484..47f88c304 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& bindings, 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/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index c4bad6fca..4801c803e 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 d34b585d6..4fbd321c4 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()) { diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 0549e8ae4..df82f376f 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/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."));