[VK] Rework SPIRV Shader Optimization (#238)
All checks were successful
eden-build / source (push) Successful in 5m51s
eden-build / linux (push) Successful in 27m16s
eden-build / windows (msvc) (push) Successful in 31m18s
eden-build / android (push) Successful in 31m42s

The actual SPIRV Shader Optimization option doesn't seem to do anything as long as it isn't vinculed, so let's rework it to make it work

Co-authored-by: Gamer64 <76565986+Gamer64ytb@users.noreply.github.com>
Co-authored-by: echosys <echosys@noreply.localhost>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/238
This commit is contained in:
Gamer64 2025-07-03 01:13:33 +00:00
parent c4a26be180
commit a002730d68
No known key found for this signature in database
GPG key ID: BA8734FD0EE46976
30 changed files with 143 additions and 31 deletions

3
.gitmodules vendored
View file

@ -64,3 +64,6 @@
[submodule "externals/sirit/externals/SPIRV-Headers"] [submodule "externals/sirit/externals/SPIRV-Headers"]
path = externals/sirit/externals/SPIRV-Headers path = externals/sirit/externals/SPIRV-Headers
url = https://github.com/KhronosGroup/SPIRV-Headers.git url = https://github.com/KhronosGroup/SPIRV-Headers.git
[submodule "externals/SPIRV-Tools"]
path = externals/SPIRV-Tools
url = https://github.com/KhronosGroup/SPIRV-Tools.git

View file

@ -49,6 +49,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_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_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@ -329,6 +331,11 @@ if (NOT YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
find_package(VulkanUtilityLibraries REQUIRED) find_package(VulkanUtilityLibraries REQUIRED)
endif() 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) if (ENABLE_LIBUSB)
find_package(libusb 1.0.24 MODULE) find_package(libusb 1.0.24 MODULE)
endif() endif()

View file

@ -160,6 +160,12 @@ if (YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
add_subdirectory(Vulkan-Utility-Libraries) add_subdirectory(Vulkan-Utility-Libraries)
endif() endif()
# SPIRV-Tools
if (YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS)
set(SPIRV_SKIP_EXECUTABLES ON)
add_subdirectory(SPIRV-Tools)
endif()
# Boost headers # Boost headers
add_subdirectory(boost-headers) add_subdirectory(boost-headers)

1
externals/SPIRV-Tools vendored Submodule

@ -0,0 +1 @@
Subproject commit 40eb301f320e1d85ce3bc12798022149eae3eee3

@ -1 +1 @@
Subproject commit 409c16be502e39fe70dd6fe2d9ad4842ef2c9a53 Subproject commit 16cedde3564629c43808401ad1eb3ca6ef24709a

@ -1 +1 @@
Subproject commit 4e246c56ec5afb5ad66b9b04374d39ac04675c8e Subproject commit f216bb107bfc6d99a9605572963613e828b10880

@ -1 +1 @@
Subproject commit 539c0a8d8e3733c9f25ea9a184c85c77504f1653 Subproject commit 6ec8481c8a13db586d7b3ba58f4eb9bbf017edf0

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage * Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD

@ -1 +1 @@
Subproject commit 3f17b2af6784bfa2c5aa5dbb8e0e74a607dd8b3b Subproject commit 04b76709bf40a7ce8df3382060ef3620f19de566

View file

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

View file

@ -569,14 +569,14 @@ abstract class SettingsItem(
) )
) )
put( put(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.RENDERER_OPTIMIZE_SPIRV_OUTPUT, IntSetting.RENDERER_OPTIMIZE_SPIRV_OUTPUT,
titleId = R.string.renderer_optimize_spirv_output, titleId = R.string.renderer_optimize_spirv_output,
descriptionId = R.string.renderer_optimize_spirv_output_description, descriptionId = R.string.renderer_optimize_spirv_output_description,
choicesId = R.array.optimizeSpirvOutputEntries, choicesId = R.array.optimizeSpirvOutputEntries,
valuesId = R.array.optimizeSpirvOutputValues valuesId = R.array.optimizeSpirvOutputValues
) )
) )
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS, BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -242,7 +242,13 @@ add_library(shader_recompiler STATIC
varying_state.h 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-opt SPIRV-Tools SPIRV-Tools-link)
endif()
target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit ${SPIRV_TOOLS_LIBRARY})
if (MSVC) if (MSVC)
target_compile_options(shader_recompiler PRIVATE target_compile_options(shader_recompiler PRIVATE

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -6,6 +9,7 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <spirv-tools/optimizer.hpp>
#include "common/settings.h" #include "common/settings.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h" #include "shader_recompiler/backend/spirv/emit_spirv.h"
@ -481,7 +485,7 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
} // Anonymous namespace } // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, std::vector<u32> 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}; EmitContext ctx{profile, runtime_info, program, bindings};
const Id main{DefineMain(ctx, program)}; const Id main{DefineMain(ctx, program)};
DefineEntryPoint(program, ctx, main); DefineEntryPoint(program, ctx, main);
@ -493,7 +497,28 @@ std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in
SetupCapabilities(profile, program.info, ctx); SetupCapabilities(profile, program.info, ctx);
SetupTransformFeedbackCapabilities(ctx, main); SetupTransformFeedbackCapabilities(ctx, main);
PatchPhiNodes(program, ctx); PatchPhiNodes(program, ctx);
return ctx.Assemble();
if (!optimize) {
return ctx.Assemble();
} else {
std::vector<u32> 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<u32> 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) { Id EmitPhi(EmitContext& ctx, IR::Inst* inst) {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -31,11 +34,12 @@ constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, do
constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area); constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area);
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
IR::Program& program, Bindings& bindings); IR::Program& program, Bindings& bindings, bool optimize);
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program,
bool optimize) {
Bindings binding; Bindings binding;
return EmitSPIRV(profile, {}, program, binding); return EmitSPIRV(profile, {}, program, binding, optimize);
} }
} // namespace Shader::Backend::SPIRV } // namespace Shader::Backend::SPIRV

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Ryujinx Team and Contributors // SPDX-FileCopyrightText: Ryujinx Team and Contributors
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -178,6 +181,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
state_tracker{state_tracker_}, shader_notify{shader_notify_}, state_tracker{state_tracker_}, shader_notify{shader_notify_},
use_asynchronous_shaders{device.UseAsynchronousShaders()}, use_asynchronous_shaders{device.UseAsynchronousShaders()},
strict_context_required{device.StrictContextRequired()}, strict_context_required{device.StrictContextRequired()},
optimize_spirv_output{Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Never},
profile{ profile{
.supported_spirv = 0x00010000, .supported_spirv = 0x00010000,
@ -344,6 +348,10 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
if (!use_asynchronous_shaders) { if (!use_asynchronous_shaders) {
workers.reset(); workers.reset();
} }
if (Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Always) {
this->optimize_spirv_output = false;
}
} }
GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() { GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
@ -537,7 +545,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
break; break;
case Settings::ShaderBackend::SpirV: case Settings::ShaderBackend::SpirV:
ConvertLegacyToGeneric(program, runtime_info); ConvertLegacyToGeneric(program, runtime_info);
sources_spirv[stage_index] = EmitSPIRV(profile, runtime_info, program, binding); sources_spirv[stage_index] =
EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output);
break; break;
} }
previous_program = &program; previous_program = &program;
@ -596,7 +605,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
code = EmitGLASM(profile, info, program); code = EmitGLASM(profile, info, program);
break; break;
case Settings::ShaderBackend::SpirV: case Settings::ShaderBackend::SpirV:
code_spirv = EmitSPIRV(profile, program); code_spirv = EmitSPIRV(profile, program, this->optimize_spirv_output);
break; break;
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -311,6 +314,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
texture_cache{texture_cache_}, shader_notify{shader_notify_}, texture_cache{texture_cache_}, shader_notify{shader_notify_},
use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()}, use_asynchronous_shaders{Settings::values.use_asynchronous_shaders.GetValue()},
use_vulkan_pipeline_cache{Settings::values.use_vulkan_driver_pipeline_cache.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(), workers(device.HasBrokenParallelShaderCompiling() ? 1ULL : GetTotalPipelineWorkers(),
"VkPipelineBuilder"), "VkPipelineBuilder"),
serialization_thread(1, "VkPipelineSerialization") { serialization_thread(1, "VkPipelineSerialization") {
@ -579,6 +583,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
if (state.statistics) { if (state.statistics) {
state.statistics->Report(); state.statistics->Report();
} }
if (Settings::values.optimize_spirv_output.GetValue() != Settings::SpirvOptimizeMode::Always) {
this->optimize_spirv_output = false;
}
} }
GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() { GraphicsPipeline* PipelineCache::CurrentGraphicsPipelineSlowPath() {
@ -688,7 +696,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)}; const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info); ConvertLegacyToGeneric(program, runtime_info);
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)}; const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
device.SaveShader(code); device.SaveShader(code);
modules[stage_index] = BuildShader(device, code); modules[stage_index] = BuildShader(device, code);
if (device.HasDebuggingToolAttached()) { if (device.HasDebuggingToolAttached()) {
@ -782,7 +790,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
} }
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
const std::vector<u32> code{EmitSPIRV(profile, program)}; const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
device.SaveShader(code); device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)}; vk::ShaderModule spv_module{BuildShader(device, code)};
if (device.HasDebuggingToolAttached()) { if (device.HasDebuggingToolAttached()) {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -463,6 +463,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent)
PAIR(AppletMode, HLE, tr("Custom frontend")), PAIR(AppletMode, HLE, tr("Custom frontend")),
PAIR(AppletMode, LLE, tr("Real applet")), PAIR(AppletMode, LLE, tr("Real applet")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(), translations->insert({Settings::EnumMetadata<Settings::SpirvOptimizeMode>::Index(),
{ {
PAIR(SpirvOptimizeMode, Never, tr("Never")), PAIR(SpirvOptimizeMode, Never, tr("Never")),