Add option to only optimize SPIRV during load (#13)

Adds a new option "On Load" to the "Optimize SPIRV output" option that turns on optimizations during the loading of the shader cache from disk, but turns it off after that.
The previous checkbox states have been named "Never" for unchecked and "Always" for checked.

The idea is that once the shader cache has most of the shaders in a game cached they can be optimized during initial game startup (where a performance hit matters less) and the few shaders that get compiled during runtime are not optimized to reduce performance hits.

Most of the commit is adding the setting to the Android app, the main logic is in the `gl_shader_cache.cpp` and `vk_pipeline_cache.cpp` files.

Reviewed-on: http://vub63vv26q6v27xzv2dtcd25xumubshogm67yrpaz2rculqxs7jlfqad.onion/torzu-emu/torzu/pulls/13
Co-authored-by: echosys <echosys@noreply.localhost>
Co-committed-by: echosys <echosys@noreply.localhost>
This commit is contained in:
echosys 2024-07-19 19:14:19 +00:00 committed by spectranator
parent 224ec03070
commit 7e27e6476d
15 changed files with 72 additions and 9 deletions

View File

@ -18,6 +18,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
RENDERER_ANTI_ALIASING("anti_aliasing"),
RENDERER_SCREEN_LAYOUT("screen_layout"),
RENDERER_ASPECT_RATIO("aspect_ratio"),
RENDERER_OPTIMIZE_SPIRV_OUTPUT("optimize_spirv_output"),
AUDIO_OUTPUT_ENGINE("output_engine"),
MAX_ANISOTROPY("max_anisotropy"),
THEME("theme"),

View File

@ -117,4 +117,15 @@ object Settings {
entries.firstOrNull { it.int == int } ?: Center
}
}
enum class OptimizeSpirvOutput(val int: Int) {
Never(0),
OnLoad(1),
Always(2);
companion object {
fun from(int: Int): OptimizeSpirvOutput =
entries.firstOrNull { it.int == int } ?: OnLoad
}
}
}

View File

@ -291,6 +291,15 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_force_max_clock_description
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_OPTIMIZE_SPIRV_OUTPUT,
titleId = R.string.renderer_optimize_spirv_output,
descriptionId = 0,
choicesId = R.array.optimizeSpirvOutputEntries,
valuesId = R.array.optimizeSpirvOutputValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,

View File

@ -177,6 +177,7 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
add(IntSetting.RENDERER_ASPECT_RATIO.key)
add(IntSetting.VERTICAL_ALIGNMENT.key)
add(IntSetting.RENDERER_OPTIMIZE_SPIRV_OUTPUT.key)
add(BooleanSetting.PICTURE_IN_PICTURE.key)
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)

View File

@ -303,4 +303,15 @@
<item>2</item>
</integer-array>
<string-array name="optimizeSpirvOutputEntries">
<item>@string/never</item>
<item>@string/on_load</item>
<item>@string/always</item>
</string-array>
<integer-array name="optimizeSpirvOutputValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
</resources>

View File

@ -234,6 +234,7 @@
<string name="renderer_anti_aliasing">Anti-aliasing method</string>
<string name="renderer_force_max_clock">Force maximum clocks (Adreno only)</string>
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
<string name="renderer_optimize_spirv_output">Optimize SPIRV output</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, reducing stutter but may introduce glitches.</string>
<string name="renderer_reactive_flushing">Use reactive flushing</string>
@ -662,6 +663,11 @@
<string name="center">Center</string>
<string name="bottom">Bottom</string>
<!-- Optimize SPIRV output -->
<string name="never">Never</string>
<string name="on_load">On Load</string>
<string name="always">Always</string>
<!-- Licenses screen strings -->
<string name="licenses">Licenses</string>
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>

View File

@ -52,6 +52,7 @@ SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true);
SWITCHABLE(ScalingFilter, false);
SWITCHABLE(SpirvOptimizeMode, true);
SWITCHABLE(ShaderBackend, true);
SWITCHABLE(TimeZone, true);
SETTING(VSyncMode, true);

View File

@ -73,6 +73,7 @@ SWITCHABLE(NvdecEmulation, false);
SWITCHABLE(Region, true);
SWITCHABLE(RendererBackend, true);
SWITCHABLE(ScalingFilter, false);
SWITCHABLE(SpirvOptimizeMode, true);
SWITCHABLE(ShaderBackend, true);
SWITCHABLE(TimeZone, true);
SETTING(VSyncMode, true);
@ -278,8 +279,12 @@ struct Values {
SwitchableSetting<bool> use_disk_shader_cache{linkage, true, "use_disk_shader_cache",
Category::Renderer};
SwitchableSetting<bool> optimize_spirv_output{linkage, false, "optimize_spirv_output",
Category::Renderer};
SwitchableSetting<SpirvOptimizeMode, true> optimize_spirv_output{linkage,
SpirvOptimizeMode::OnLoad,
SpirvOptimizeMode::Never,
SpirvOptimizeMode::Always,
"optimize_spirv_output",
Category::Renderer};
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,

View File

@ -155,6 +155,8 @@ ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
ENUM(SpirvOptimizeMode, Never, OnLoad, Always)
template <typename Type>
inline std::string CanonicalizeEnum(Type id) {
const auto group = EnumMetadata<Type>::Canonicalizations();

View File

@ -178,6 +178,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
state_tracker{state_tracker_}, shader_notify{shader_notify_},
use_asynchronous_shaders{device.UseAsynchronousShaders()},
strict_context_required{device.StrictContextRequired()},
optimize_spirv_output{Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Never},
profile{
.supported_spirv = 0x00010000,
@ -344,6 +345,10 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
if (!use_asynchronous_shaders) {
workers.reset();
}
if (Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Always) {
this->optimize_spirv_output = false;
}
}
GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
@ -538,8 +543,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
case Settings::ShaderBackend::SpirV:
ConvertLegacyToGeneric(program, runtime_info);
sources_spirv[stage_index] =
EmitSPIRV(profile, runtime_info, program, binding,
Settings::values.optimize_spirv_output.GetValue());
EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output);
break;
}
previous_program = &program;
@ -598,7 +602,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
code = EmitGLASM(profile, info, program);
break;
case Settings::ShaderBackend::SpirV:
code_spirv = EmitSPIRV(profile, program, Settings::values.optimize_spirv_output.GetValue());
code_spirv = EmitSPIRV(profile, program, this->optimize_spirv_output);
break;
}

View File

@ -73,6 +73,7 @@ private:
VideoCore::ShaderNotify& shader_notify;
const bool use_asynchronous_shaders;
const bool strict_context_required;
bool optimize_spirv_output{};
GraphicsPipelineKey graphics_key{};
GraphicsPipeline* current_pipeline{};

View File

@ -310,6 +310,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
texture_cache{texture_cache_}, shader_notify{shader_notify_},
use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.GetValue()},
optimize_spirv_output{Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Never},
workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(),
"VkPipelineBuilder"),
serialization_thread(1, "VkPipelineSerialization") {
@ -564,6 +565,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (state.statistics) {
state.statistics->Report();
}
if (Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Always) {
this->optimize_spirv_output = false;
}
}
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@ -673,8 +678,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info);
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding,
Settings::values.optimize_spirv_output.GetValue())};
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
if (device.HasDebuggingToolAttached()) {
@ -768,8 +772,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
}
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const std::vector<u32> code{
EmitSPIRV(profile, program, Settings::values.optimize_spirv_output.GetValue())};
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)};
if (device.HasDebuggingToolAttached()) {

View File

@ -150,6 +150,7 @@ private:
VideoCore::ShaderNotify& shader_notify;
bool use_asynchronous_shaders{};
bool use_vulkan_pipeline_cache{};
bool optimize_spirv_output{};
GraphicsPipelineCacheKey graphics_key{};
GraphicsPipeline* current_pipeline{};

View File

@ -319,6 +319,12 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
PAIR(AppletMode, LLE, tr("Real applet")),
}});
translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(),
{
PAIR(SpirvOptimizeMode, Never, tr("Never")),
PAIR(SpirvOptimizeMode, OnLoad, tr("On Load")),
PAIR(SpirvOptimizeMode, Always, tr("Always")),
}});
translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
{
PAIR(AstcDecodeMode, Cpu, tr("CPU")),

View File

@ -277,3 +277,4 @@ Q_DECLARE_METATYPE(Settings::RendererBackend);
Q_DECLARE_METATYPE(Settings::ShaderBackend);
Q_DECLARE_METATYPE(Settings::AstcRecompression);
Q_DECLARE_METATYPE(Settings::AstcDecodeMode);
Q_DECLARE_METATYPE(Settings::SpirvOptimizeMode);