WIP: [vk, SPIR-V] Enable INT64 emulation and disable problematic extensions #2608
39 changed files with 846 additions and 325 deletions
|
@ -10,7 +10,7 @@ if (-not ([bool](net session 2>$null))) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$VSVer = "17"
|
$VSVer = "17"
|
||||||
$ExeFile = "vs_community.exe"
|
$ExeFile = "vs_BuildTools.exe"
|
||||||
$Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile"
|
$Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile"
|
||||||
$Destination = "./$ExeFile"
|
$Destination = "./$ExeFile"
|
||||||
|
|
||||||
|
@ -19,39 +19,21 @@ $WebClient = New-Object System.Net.WebClient
|
||||||
$WebClient.DownloadFile($Uri, $Destination)
|
$WebClient.DownloadFile($Uri, $Destination)
|
||||||
Write-Host "Finished downloading $ExeFile"
|
Write-Host "Finished downloading $ExeFile"
|
||||||
|
|
||||||
|
$VSROOT = "C:/VSBuildTools/$VSVer"
|
||||||
$Arguments = @(
|
$Arguments = @(
|
||||||
"--quiet", # Suppress installer UI
|
"--installPath `"$VSROOT`"", # set custom installation path
|
||||||
"--wait", # Wait for installation to complete
|
"--quiet", # suppress UI
|
||||||
"--norestart", # Prevent automatic restart
|
"--wait", # wait for installation to complete
|
||||||
"--force", # Force installation even if components are already installed
|
"--norestart", # prevent automatic restart
|
||||||
"--add Microsoft.VisualStudio.Workload.NativeDesktop", # Desktop development with C++
|
"--add Microsoft.VisualStudio.Workload.VCTools", # add C++ build tools workload
|
||||||
"--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # Core C++ compiler/tools for x86/x64
|
"--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # add core x86/x64 C++ tools
|
||||||
"--add Microsoft.VisualStudio.Component.Windows11SDK.26100",# Windows 11 SDK (26100)
|
"--add Microsoft.VisualStudio.Component.Windows10SDK.19041" # add specific Windows SDK
|
||||||
"--add Microsoft.VisualStudio.Component.Windows10SDK.19041",# Windows 10 SDK (19041)
|
|
||||||
"--add Microsoft.VisualStudio.Component.VC.Llvm.Clang", # LLVM Clang compiler
|
|
||||||
"--add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", # LLVM Clang integration toolset
|
|
||||||
"--add Microsoft.VisualStudio.Component.Windows11SDK.22621",# Windows 11 SDK (22621)
|
|
||||||
"--add Microsoft.VisualStudio.Component.VC.CMake.Project", # CMake project support
|
|
||||||
"--add Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64", # VC++ 14.2 toolset
|
|
||||||
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang" # LLVM Clang for native desktop
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Write-Host "Installing Visual Studio Build Tools"
|
Write-Host "Installing Visual Studio Build Tools"
|
||||||
$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -ArgumentList $Arguments
|
$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments
|
||||||
|
|
||||||
# Spinner while installing
|
|
||||||
$Spinner = "|/-\"
|
|
||||||
$i = 0
|
|
||||||
while (-not $InstallProcess.HasExited) {
|
|
||||||
Write-Host -NoNewline ("`rInstalling... " + $Spinner[$i % $Spinner.Length])
|
|
||||||
Start-Sleep -Milliseconds 250
|
|
||||||
$i++
|
|
||||||
}
|
|
||||||
|
|
||||||
# Clear spinner line
|
|
||||||
Write-Host "`rSetup completed! "
|
|
||||||
|
|
||||||
$ExitCode = $InstallProcess.ExitCode
|
$ExitCode = $InstallProcess.ExitCode
|
||||||
|
|
||||||
if ($ExitCode -ne 0) {
|
if ($ExitCode -ne 0) {
|
||||||
Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)"
|
Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)"
|
||||||
Exit $ExitCode
|
Exit $ExitCode
|
||||||
|
|
|
@ -147,11 +147,7 @@ if (MSVC OR ANDROID)
|
||||||
set(EXT_DEFAULT ON)
|
set(EXT_DEFAULT ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_SDL2)
|
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" ${EXT_DEFAULT} "ENABLE_SDL2;NOT MSVC" OFF)
|
||||||
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "NOT MSVC" OFF)
|
|
||||||
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
|
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "NOT ANDROID" OFF)
|
||||||
|
|
||||||
|
@ -189,6 +185,8 @@ option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT})
|
||||||
# TODO(crueter): CI this?
|
# TODO(crueter): CI this?
|
||||||
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
|
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
|
||||||
|
|
||||||
|
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||||
|
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "ENABLE_SDL2" OFF)
|
||||||
|
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
|
||||||
|
|
||||||
|
@ -199,14 +197,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_CMD "Compile the eden-cli executable" ON "ENABLE_SDL
|
||||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
||||||
|
|
||||||
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
||||||
if(YUZU_ENABLE_LTO)
|
|
||||||
include(CheckIPOSupported)
|
|
||||||
check_ipo_supported(RESULT COMPILER_SUPPORTS_LTO)
|
|
||||||
if(NOT COMPILER_SUPPORTS_LTO)
|
|
||||||
message(FATAL_ERROR "Your compiler does not support interprocedural optimization (IPO). Re-run CMake with -DYUZU_ENABLE_LTO=OFF.")
|
|
||||||
endif()
|
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${COMPILER_SUPPORTS_LTO})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
|
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
|
||||||
|
|
||||||
|
@ -894,24 +884,19 @@ if (MSVC AND CXX_CLANG)
|
||||||
link_libraries(llvm-mingw-runtime)
|
link_libraries(llvm-mingw-runtime)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (YUZU_USE_FASTER_LD)
|
if (YUZU_USE_FASTER_LD AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
# fallback if everything fails (bfd)
|
# We will assume that if the compiler is GCC, it will attempt to use ld.bfd by default.
|
||||||
set(LINKER bfd)
|
# Try to pick a faster linker.
|
||||||
# clang should always use lld
|
|
||||||
find_program(LLD lld)
|
find_program(LLD lld)
|
||||||
if (LLD)
|
find_program(MOLD mold)
|
||||||
set(LINKER lld)
|
|
||||||
|
if (MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
|
||||||
|
message(NOTICE "Selecting mold as linker")
|
||||||
|
add_link_options("-fuse-ld=mold")
|
||||||
|
elseif (LLD)
|
||||||
|
message(NOTICE "Selecting lld as linker")
|
||||||
|
add_link_options("-fuse-ld=lld")
|
||||||
endif()
|
endif()
|
||||||
# GNU appears to work better with mold
|
|
||||||
# TODO: mold has been slow lately, see if better options exist (search for gold?)
|
|
||||||
if (CXX_GCC)
|
|
||||||
find_program(MOLD mold)
|
|
||||||
if (MOLD AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1")
|
|
||||||
set(LINKER mold)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
message(NOTICE "Selecting ${LINKER} as linker")
|
|
||||||
add_link_options("-fuse-ld=${LINKER}")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set runtime library to MD/MDd for all configurations
|
# Set runtime library to MD/MDd for all configurations
|
||||||
|
|
|
@ -4,8 +4,8 @@ To build Eden, you MUST have a C++ compiler.
|
||||||
* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+
|
* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+
|
||||||
- GCC 12 also requires Clang 14+
|
- GCC 12 also requires Clang 14+
|
||||||
* On Windows, this is either:
|
* On Windows, this is either:
|
||||||
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option),
|
- **[MSVC](https://visualstudio.microsoft.com/downloads/)**,
|
||||||
* *A convenience script to install the Visual Community Studio 2022 with necessary tools is provided in `.ci/windows/install-msvc.ps1`*
|
* *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`*
|
||||||
- clang-cl - can be downloaded from the MSVC installer,
|
- clang-cl - can be downloaded from the MSVC installer,
|
||||||
- or **[MSYS2](https://www.msys2.org)**
|
- or **[MSYS2](https://www.msys2.org)**
|
||||||
* On macOS, this is Apple Clang
|
* On macOS, this is Apple Clang
|
||||||
|
@ -211,4 +211,4 @@ Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/l
|
||||||
|
|
||||||
## All Done
|
## All Done
|
||||||
|
|
||||||
You may now return to the **[root build guide](Build.md)**.
|
You may now return to the **[root build guide](Build.md)**.
|
|
@ -141,7 +141,7 @@ else()
|
||||||
-Wno-missing-field-initializers
|
-Wno-missing-field-initializers
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
|
if (CXX_CLANG OR CXX_ICC) # Clang or AppleClang
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Werror=shadow-uncaptured-local
|
-Werror=shadow-uncaptured-local
|
||||||
|
|
|
@ -175,10 +175,12 @@ android {
|
||||||
"-DYUZU_USE_CPM=ON",
|
"-DYUZU_USE_CPM=ON",
|
||||||
"-DCPMUTIL_FORCE_BUNDLED=ON",
|
"-DCPMUTIL_FORCE_BUNDLED=ON",
|
||||||
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
|
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
|
||||||
|
"-DYUZU_ENABLE_LTO=ON",
|
||||||
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
|
||||||
"-DBUILD_TESTING=OFF",
|
"-DBUILD_TESTING=OFF",
|
||||||
"-DYUZU_TESTS=OFF",
|
"-DYUZU_TESTS=OFF",
|
||||||
"-DDYNARMIC_TESTS=OFF"
|
"-DDYNARMIC_TESTS=OFF",
|
||||||
|
"-DDYNARMIC_ENABLE_LTO=ON"
|
||||||
)
|
)
|
||||||
|
|
||||||
abiFilters("arm64-v8a")
|
abiFilters("arm64-v8a")
|
||||||
|
|
|
@ -178,7 +178,7 @@ struct Values {
|
||||||
SwitchableSetting<std::string> audio_input_device_id{
|
SwitchableSetting<std::string> audio_input_device_id{
|
||||||
linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList};
|
linkage, "auto", "input_device", Category::Audio, Specialization::RuntimeList};
|
||||||
SwitchableSetting<AudioMode, true> sound_index{
|
SwitchableSetting<AudioMode, true> sound_index{
|
||||||
linkage, AudioMode::Stereo,
|
linkage, AudioMode::Stereo, AudioMode::Mono, AudioMode::Surround,
|
||||||
"sound_index", Category::SystemAudio, Specialization::Default, true,
|
"sound_index", Category::SystemAudio, Specialization::Default, true,
|
||||||
true};
|
true};
|
||||||
SwitchableSetting<u8, true> volume{linkage,
|
SwitchableSetting<u8, true> volume{linkage,
|
||||||
|
@ -199,6 +199,8 @@ struct Values {
|
||||||
SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
|
SwitchableSetting<bool> use_multi_core{linkage, true, "use_multi_core", Category::Core};
|
||||||
SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
|
SwitchableSetting<MemoryLayout, true> memory_layout_mode{linkage,
|
||||||
MemoryLayout::Memory_4Gb,
|
MemoryLayout::Memory_4Gb,
|
||||||
|
MemoryLayout::Memory_4Gb,
|
||||||
|
MemoryLayout::Memory_12Gb,
|
||||||
"memory_layout_mode",
|
"memory_layout_mode",
|
||||||
Category::Core,
|
Category::Core,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
|
@ -238,8 +240,9 @@ struct Values {
|
||||||
#endif
|
#endif
|
||||||
"cpu_backend",
|
"cpu_backend",
|
||||||
Category::Cpu};
|
Category::Cpu};
|
||||||
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
|
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
|
||||||
"cpu_accuracy", Category::Cpu};
|
CpuAccuracy::Auto, CpuAccuracy::Paranoid,
|
||||||
|
"cpu_accuracy", Category::Cpu};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_fast_cpu_time{linkage,
|
SwitchableSetting<bool> use_fast_cpu_time{linkage,
|
||||||
false,
|
false,
|
||||||
|
@ -321,10 +324,10 @@ struct Values {
|
||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
SwitchableSetting<RendererBackend, true> renderer_backend{
|
SwitchableSetting<RendererBackend, true> renderer_backend{
|
||||||
linkage, RendererBackend::Vulkan,
|
linkage, RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Null,
|
||||||
"backend", Category::Renderer};
|
"backend", Category::Renderer};
|
||||||
SwitchableSetting<ShaderBackend, true> shader_backend{
|
SwitchableSetting<ShaderBackend, true> shader_backend{
|
||||||
linkage, ShaderBackend::SpirV,
|
linkage, ShaderBackend::SpirV, ShaderBackend::Glsl, ShaderBackend::SpirV,
|
||||||
"shader_backend", Category::Renderer, Specialization::RuntimeList};
|
"shader_backend", Category::Renderer, Specialization::RuntimeList};
|
||||||
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
|
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer,
|
||||||
Specialization::RuntimeList};
|
Specialization::RuntimeList};
|
||||||
|
@ -339,6 +342,8 @@ struct Values {
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
SwitchableSetting<SpirvOptimizeMode, true> optimize_spirv_output{linkage,
|
SwitchableSetting<SpirvOptimizeMode, true> optimize_spirv_output{linkage,
|
||||||
SpirvOptimizeMode::Never,
|
SpirvOptimizeMode::Never,
|
||||||
|
SpirvOptimizeMode::Never,
|
||||||
|
SpirvOptimizeMode::Always,
|
||||||
"optimize_spirv_output",
|
"optimize_spirv_output",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
SwitchableSetting<bool> use_asynchronous_gpu_emulation{
|
||||||
|
@ -349,10 +354,12 @@ struct Values {
|
||||||
#else
|
#else
|
||||||
AstcDecodeMode::Gpu,
|
AstcDecodeMode::Gpu,
|
||||||
#endif
|
#endif
|
||||||
|
AstcDecodeMode::Cpu,
|
||||||
|
AstcDecodeMode::CpuAsynchronous,
|
||||||
"accelerate_astc",
|
"accelerate_astc",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
SwitchableSetting<VSyncMode, true> vsync_mode{
|
SwitchableSetting<VSyncMode, true> vsync_mode{
|
||||||
linkage, VSyncMode::Fifo,
|
linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
|
||||||
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
|
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
|
||||||
true};
|
true};
|
||||||
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
|
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
|
||||||
|
@ -365,6 +372,8 @@ struct Values {
|
||||||
#else
|
#else
|
||||||
FullscreenMode::Exclusive,
|
FullscreenMode::Exclusive,
|
||||||
#endif
|
#endif
|
||||||
|
FullscreenMode::Borderless,
|
||||||
|
FullscreenMode::Exclusive,
|
||||||
"fullscreen_mode",
|
"fullscreen_mode",
|
||||||
Category::Renderer,
|
Category::Renderer,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
|
@ -372,6 +381,8 @@ struct Values {
|
||||||
true};
|
true};
|
||||||
SwitchableSetting<AspectRatio, true> aspect_ratio{linkage,
|
SwitchableSetting<AspectRatio, true> aspect_ratio{linkage,
|
||||||
AspectRatio::R16_9,
|
AspectRatio::R16_9,
|
||||||
|
AspectRatio::R16_9,
|
||||||
|
AspectRatio::Stretch,
|
||||||
"aspect_ratio",
|
"aspect_ratio",
|
||||||
Category::Renderer,
|
Category::Renderer,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
|
@ -419,6 +430,8 @@ struct Values {
|
||||||
#else
|
#else
|
||||||
GpuAccuracy::High,
|
GpuAccuracy::High,
|
||||||
#endif
|
#endif
|
||||||
|
GpuAccuracy::Normal,
|
||||||
|
GpuAccuracy::Extreme,
|
||||||
"gpu_accuracy",
|
"gpu_accuracy",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
|
@ -429,6 +442,8 @@ struct Values {
|
||||||
|
|
||||||
SwitchableSetting<DmaAccuracy, true> dma_accuracy{linkage,
|
SwitchableSetting<DmaAccuracy, true> dma_accuracy{linkage,
|
||||||
DmaAccuracy::Default,
|
DmaAccuracy::Default,
|
||||||
|
DmaAccuracy::Default,
|
||||||
|
DmaAccuracy::Safe,
|
||||||
"dma_accuracy",
|
"dma_accuracy",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
|
@ -441,14 +456,20 @@ struct Values {
|
||||||
#else
|
#else
|
||||||
AnisotropyMode::Automatic,
|
AnisotropyMode::Automatic,
|
||||||
#endif
|
#endif
|
||||||
|
AnisotropyMode::Automatic,
|
||||||
|
AnisotropyMode::X16,
|
||||||
"max_anisotropy",
|
"max_anisotropy",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
|
||||||
AstcRecompression::Uncompressed,
|
AstcRecompression::Uncompressed,
|
||||||
|
AstcRecompression::Uncompressed,
|
||||||
|
AstcRecompression::Bc3,
|
||||||
"astc_recompression",
|
"astc_recompression",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
|
SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
|
||||||
VramUsageMode::Conservative,
|
VramUsageMode::Conservative,
|
||||||
|
VramUsageMode::Conservative,
|
||||||
|
VramUsageMode::Aggressive,
|
||||||
"vram_usage_mode",
|
"vram_usage_mode",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
SwitchableSetting<bool> skip_cpu_inner_invalidation{linkage,
|
SwitchableSetting<bool> skip_cpu_inner_invalidation{linkage,
|
||||||
|
@ -574,10 +595,14 @@ struct Values {
|
||||||
// System
|
// System
|
||||||
SwitchableSetting<Language, true> language_index{linkage,
|
SwitchableSetting<Language, true> language_index{linkage,
|
||||||
Language::EnglishAmerican,
|
Language::EnglishAmerican,
|
||||||
|
Language::Japanese,
|
||||||
|
Language::Serbian,
|
||||||
"language_index",
|
"language_index",
|
||||||
Category::System};
|
Category::System};
|
||||||
SwitchableSetting<Region, true> region_index{linkage, Region::Usa, "region_index", Category::System};
|
SwitchableSetting<Region, true> region_index{linkage, Region::Usa, Region::Japan,
|
||||||
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
|
Region::Taiwan, "region_index", Category::System};
|
||||||
|
SwitchableSetting<TimeZone, true> time_zone_index{linkage, TimeZone::Auto,
|
||||||
|
TimeZone::Auto, TimeZone::Zulu,
|
||||||
"time_zone_index", Category::System};
|
"time_zone_index", Category::System};
|
||||||
// Measured in seconds since epoch
|
// Measured in seconds since epoch
|
||||||
SwitchableSetting<bool> custom_rtc_enabled{
|
SwitchableSetting<bool> custom_rtc_enabled{
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -19,10 +18,8 @@ namespace Settings {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct EnumMetadata {
|
struct EnumMetadata {
|
||||||
static std::vector<std::pair<std::string_view, T>> Canonicalizations();
|
static std::vector<std::pair<std::string, T>> Canonicalizations();
|
||||||
static u32 Index();
|
static u32 Index();
|
||||||
static constexpr T GetFirst();
|
|
||||||
static constexpr T GetLast();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
|
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
|
||||||
|
@ -72,101 +69,138 @@ struct EnumMetadata {
|
||||||
#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
|
#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
|
||||||
#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
|
#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
|
||||||
|
|
||||||
#define PP_HEAD(A, ...) A
|
#define ENUM(NAME, ...) \
|
||||||
|
enum class NAME : u32 { __VA_ARGS__ }; \
|
||||||
#define ENUM(NAME, ...) \
|
template <> \
|
||||||
enum class NAME : u32 { __VA_ARGS__ }; \
|
inline std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
|
||||||
template<> inline std::vector<std::pair<std::string_view, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
|
return {PAIR(NAME, __VA_ARGS__)}; \
|
||||||
return {PAIR(NAME, __VA_ARGS__)}; \
|
} \
|
||||||
} \
|
template <> \
|
||||||
template<> inline u32 EnumMetadata<NAME>::Index() { \
|
inline u32 EnumMetadata<NAME>::Index() { \
|
||||||
return __COUNTER__; \
|
return __COUNTER__; \
|
||||||
} \
|
|
||||||
template<> inline constexpr NAME EnumMetadata<NAME>::GetFirst() { \
|
|
||||||
return NAME::PP_HEAD(__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
template<> inline constexpr NAME EnumMetadata<NAME>::GetLast() { \
|
|
||||||
return (std::vector<std::pair<std::string_view, NAME>>{PAIR(NAME, __VA_ARGS__)}).back().second; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AudioEngine must be specified discretely due to having existing but slightly different
|
// AudioEngine must be specified discretely due to having existing but slightly different
|
||||||
// canonicalizations
|
// canonicalizations
|
||||||
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
||||||
enum class AudioEngine : u32 { Auto, Cubeb, Sdl2, Null, Oboe, };
|
enum class AudioEngine : u32 {
|
||||||
template<>
|
Auto,
|
||||||
inline std::vector<std::pair<std::string_view, AudioEngine>> EnumMetadata<AudioEngine>::Canonicalizations() {
|
Cubeb,
|
||||||
|
Sdl2,
|
||||||
|
Null,
|
||||||
|
Oboe,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::vector<std::pair<std::string, AudioEngine>>
|
||||||
|
EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||||
return {
|
return {
|
||||||
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
|
{"auto", AudioEngine::Auto}, {"cubeb", AudioEngine::Cubeb}, {"sdl2", AudioEngine::Sdl2},
|
||||||
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
{"null", AudioEngine::Null}, {"oboe", AudioEngine::Oboe},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// @brief This is just a sufficiently large number that is more than the number of other enums declared here
|
|
||||||
template<>
|
template <>
|
||||||
inline u32 EnumMetadata<AudioEngine>::Index() {
|
inline u32 EnumMetadata<AudioEngine>::Index() {
|
||||||
|
// This is just a sufficiently large number that is more than the number of other enums declared
|
||||||
|
// here
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
template<>
|
|
||||||
inline constexpr AudioEngine EnumMetadata<AudioEngine>::GetFirst() {
|
|
||||||
return AudioEngine::Auto;
|
|
||||||
}
|
|
||||||
template<>
|
|
||||||
inline constexpr AudioEngine EnumMetadata<AudioEngine>::GetLast() {
|
|
||||||
return AudioEngine::Oboe;
|
|
||||||
}
|
|
||||||
|
|
||||||
ENUM(AudioMode, Mono, Stereo, Surround);
|
ENUM(AudioMode, Mono, Stereo, Surround);
|
||||||
static_assert(EnumMetadata<AudioMode>::GetFirst() == AudioMode::Mono);
|
|
||||||
static_assert(EnumMetadata<AudioMode>::GetLast() == AudioMode::Surround);
|
|
||||||
|
|
||||||
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
|
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
|
||||||
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
|
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
|
||||||
ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian);
|
ChineseSimplified, ChineseTraditional, PortugueseBrazilian, Serbian);
|
||||||
|
|
||||||
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
|
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
|
||||||
|
|
||||||
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
|
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
|
||||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||||
|
|
||||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||||
|
|
||||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||||
|
|
||||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||||
|
|
||||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||||
|
|
||||||
ENUM(VramUsageMode, Conservative, Aggressive);
|
ENUM(VramUsageMode, Conservative, Aggressive);
|
||||||
|
|
||||||
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
||||||
|
|
||||||
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
||||||
|
|
||||||
ENUM(GpuAccuracy, Normal, High, Extreme);
|
ENUM(GpuAccuracy, Normal, High, Extreme);
|
||||||
|
|
||||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||||
|
|
||||||
ENUM(CpuBackend, Dynarmic, Nce);
|
ENUM(CpuBackend, Dynarmic, Nce);
|
||||||
|
|
||||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
||||||
|
|
||||||
ENUM(CpuClock, Boost, Fast)
|
ENUM(CpuClock, Boost, Fast)
|
||||||
|
|
||||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb);
|
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb, Memory_10Gb, Memory_12Gb);
|
||||||
|
|
||||||
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
||||||
|
|
||||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||||
|
|
||||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||||
ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X);
|
|
||||||
|
ENUM(ResolutionSetup,
|
||||||
|
Res1_4X,
|
||||||
|
Res1_2X,
|
||||||
|
Res3_4X,
|
||||||
|
Res1X,
|
||||||
|
Res3_2X,
|
||||||
|
Res2X,
|
||||||
|
Res3X,
|
||||||
|
Res4X,
|
||||||
|
Res5X,
|
||||||
|
Res6X,
|
||||||
|
Res7X,
|
||||||
|
Res8X);
|
||||||
|
|
||||||
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum);
|
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Spline1, Gaussian, Lanczos, ScaleForce, Fsr, Area, MaxEnum);
|
||||||
|
|
||||||
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||||
|
|
||||||
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||||
|
|
||||||
ENUM(ConsoleMode, Handheld, Docked);
|
ENUM(ConsoleMode, Handheld, Docked);
|
||||||
|
|
||||||
ENUM(AppletMode, HLE, LLE);
|
ENUM(AppletMode, HLE, LLE);
|
||||||
|
|
||||||
ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
|
ENUM(SpirvOptimizeMode, Never, OnLoad, Always);
|
||||||
|
|
||||||
ENUM(GpuOverclock, Low, Medium, High)
|
ENUM(GpuOverclock, Low, Medium, High)
|
||||||
|
|
||||||
ENUM(TemperatureUnits, Celsius, Fahrenheit)
|
ENUM(TemperatureUnits, Celsius, Fahrenheit)
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
inline std::string_view CanonicalizeEnum(Type id) {
|
inline std::string CanonicalizeEnum(Type id) {
|
||||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||||
for (auto& [name, value] : group)
|
for (auto& [name, value] : group) {
|
||||||
if (value == id)
|
if (value == id) {
|
||||||
return name;
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
inline Type ToEnum(const std::string& canonicalization) {
|
inline Type ToEnum(const std::string& canonicalization) {
|
||||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||||
for (auto& [name, value] : group)
|
for (auto& [name, value] : group) {
|
||||||
if (name == canonicalization)
|
if (name == canonicalization) {
|
||||||
return value;
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
} // namespace Settings
|
} // namespace Settings
|
||||||
|
|
|
@ -72,17 +72,10 @@ public:
|
||||||
u32 specialization_ = Specialization::Default, bool save_ = true,
|
u32 specialization_ = Specialization::Default, bool save_ = true,
|
||||||
bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
|
bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
|
||||||
requires(ranged)
|
requires(ranged)
|
||||||
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_),
|
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
|
||||||
|
other_setting_),
|
||||||
value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
|
value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
|
||||||
|
|
||||||
explicit Setting(Linkage& linkage, const Type& default_val,
|
|
||||||
const std::string& name, Category category_,
|
|
||||||
u32 specialization_ = Specialization::Default, bool save_ = true,
|
|
||||||
bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
|
|
||||||
requires(ranged && std::is_enum_v<Type>)
|
|
||||||
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_, other_setting_),
|
|
||||||
value{default_val}, default_value{default_val}, maximum{EnumMetadata<Type>::GetLast()}, minimum{EnumMetadata<Type>::GetFirst()} {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the setting's value.
|
* Returns a reference to the setting's value.
|
||||||
*
|
*
|
||||||
|
@ -126,6 +119,9 @@ protected:
|
||||||
return value_.has_value() ? std::to_string(*value_) : "none";
|
return value_.has_value() ? std::to_string(*value_) : "none";
|
||||||
} else if constexpr (std::is_same_v<Type, bool>) {
|
} else if constexpr (std::is_same_v<Type, bool>) {
|
||||||
return value_ ? "true" : "false";
|
return value_ ? "true" : "false";
|
||||||
|
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
|
||||||
|
// Compatibility with old AudioEngine setting being a string
|
||||||
|
return CanonicalizeEnum(value_);
|
||||||
} else if constexpr (std::is_floating_point_v<Type>) {
|
} else if constexpr (std::is_floating_point_v<Type>) {
|
||||||
return fmt::format("{:f}", value_);
|
return fmt::format("{:f}", value_);
|
||||||
} else if constexpr (std::is_enum_v<Type>) {
|
} else if constexpr (std::is_enum_v<Type>) {
|
||||||
|
@ -211,7 +207,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] std::string Canonicalize() const override final {
|
[[nodiscard]] std::string Canonicalize() const override final {
|
||||||
if constexpr (std::is_enum_v<Type>) {
|
if constexpr (std::is_enum_v<Type>) {
|
||||||
return std::string{CanonicalizeEnum(this->GetValue())};
|
return CanonicalizeEnum(this->GetValue());
|
||||||
} else {
|
} else {
|
||||||
return ToString(this->GetValue());
|
return ToString(this->GetValue());
|
||||||
}
|
}
|
||||||
|
@ -292,32 +288,41 @@ public:
|
||||||
* @param other_setting_ A second Setting to associate to this one in metadata
|
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||||
*/
|
*/
|
||||||
template <typename T = BasicSetting>
|
template <typename T = BasicSetting>
|
||||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(!ranged)
|
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
|
||||||
: Setting<Type, false>{ linkage, default_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} {
|
Category category_, u32 specialization_ = Specialization::Default,
|
||||||
|
bool save_ = true, bool runtime_modifiable_ = false,
|
||||||
|
typename std::enable_if<!ranged, T*>::type other_setting_ = nullptr)
|
||||||
|
: Setting<Type, false>{
|
||||||
|
linkage, default_val, name, category_, specialization_,
|
||||||
|
save_, runtime_modifiable_, other_setting_} {
|
||||||
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
||||||
}
|
}
|
||||||
virtual ~SwitchableSetting() = default;
|
virtual ~SwitchableSetting() = default;
|
||||||
|
|
||||||
/// @brief Sets a default value, minimum value, maximum value, and label.
|
/**
|
||||||
/// @param linkage Setting registry
|
* Sets a default value, minimum value, maximum value, and label.
|
||||||
/// @param default_val Initial value of the setting, and default value of the setting
|
*
|
||||||
/// @param min_val Sets the minimum allowed value of the setting
|
* @param linkage Setting registry
|
||||||
/// @param max_val Sets the maximum allowed value of the setting
|
* @param default_val Initial value of the setting, and default value of the setting
|
||||||
/// @param name Label for the setting
|
* @param min_val Sets the minimum allowed value of the setting
|
||||||
/// @param category_ Category of the setting AKA INI group
|
* @param max_val Sets the maximum allowed value of the setting
|
||||||
/// @param specialization_ Suggestion for how frontend implementations represent this in a config
|
* @param name Label for the setting
|
||||||
/// @param save_ Suggests that this should or should not be saved to a frontend config file
|
* @param category_ Category of the setting AKA INI group
|
||||||
/// @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
* @param specialization_ Suggestion for how frontend implementations represent this in a config
|
||||||
/// @param other_setting_ A second Setting to associate to this one in metadata
|
* @param save_ Suggests that this should or should not be saved to a frontend config file
|
||||||
|
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
||||||
|
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||||
|
*/
|
||||||
template <typename T = BasicSetting>
|
template <typename T = BasicSetting>
|
||||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, const Type& max_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged)
|
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
|
||||||
: Setting<Type, true>{linkage, default_val, min_val, max_val, name, category_, specialization_, save_, runtime_modifiable_, other_setting_} {
|
const Type& max_val, const std::string& name, Category category_,
|
||||||
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
u32 specialization_ = Specialization::Default, bool save_ = true,
|
||||||
}
|
bool runtime_modifiable_ = false,
|
||||||
|
typename std::enable_if<ranged, T*>::type other_setting_ = nullptr)
|
||||||
template <typename T = BasicSetting>
|
: Setting<Type, true>{linkage, default_val, min_val,
|
||||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, Category category_, u32 specialization_ = Specialization::Default, bool save_ = true, bool runtime_modifiable_ = false, T* other_setting_ = nullptr) requires(ranged)
|
max_val, name, category_,
|
||||||
: Setting<Type, true>{linkage, default_val, EnumMetadata<Type>::GetFirst(), EnumMetadata<Type>::GetLast(), name, category_, specialization_, save_, runtime_modifiable_, other_setting_} {
|
specialization_, save_, runtime_modifiable_,
|
||||||
|
other_setting_} {
|
||||||
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1274,4 +1274,8 @@ if (YUZU_USE_PRECOMPILED_HEADERS)
|
||||||
target_precompile_headers(core PRIVATE precompiled_headers.h)
|
target_precompile_headers(core PRIVATE precompiled_headers.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (YUZU_ENABLE_LTO)
|
||||||
|
set_property(TARGET core PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
create_target_directory_groups(core)
|
create_target_directory_groups(core)
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
#include "core/hle/service/apm/apm_controller.h"
|
#include "core/hle/service/apm/apm_controller.h"
|
||||||
|
@ -24,7 +23,6 @@
|
||||||
#include "core/hle/service/am/hid_registration.h"
|
#include "core/hle/service/am/hid_registration.h"
|
||||||
#include "core/hle/service/am/lifecycle_manager.h"
|
#include "core/hle/service/am/lifecycle_manager.h"
|
||||||
#include "core/hle/service/am/process_holder.h"
|
#include "core/hle/service/am/process_holder.h"
|
||||||
#include "core/hle/service/am/service/storage.h"
|
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
|
@ -99,9 +97,6 @@ struct Applet {
|
||||||
std::deque<std::vector<u8>> preselected_user_launch_parameter{};
|
std::deque<std::vector<u8>> preselected_user_launch_parameter{};
|
||||||
std::deque<std::vector<u8>> friend_invitation_storage_channel{};
|
std::deque<std::vector<u8>> friend_invitation_storage_channel{};
|
||||||
|
|
||||||
// Context Stack
|
|
||||||
std::stack<SharedPointer<IStorage>> context_stack{};
|
|
||||||
|
|
||||||
// Caller applet
|
// Caller applet
|
||||||
std::weak_ptr<Applet> caller_applet{};
|
std::weak_ptr<Applet> caller_applet{};
|
||||||
std::shared_ptr<AppletDataBroker> caller_applet_broker{};
|
std::shared_ptr<AppletDataBroker> caller_applet_broker{};
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -18,12 +15,12 @@ IProcessWindingController::IProcessWindingController(Core::System& system_,
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, D<&IProcessWindingController::GetLaunchReason>, "GetLaunchReason"},
|
{0, D<&IProcessWindingController::GetLaunchReason>, "GetLaunchReason"},
|
||||||
{11, D<&IProcessWindingController::OpenCallingLibraryApplet>, "OpenCallingLibraryApplet"},
|
{11, D<&IProcessWindingController::OpenCallingLibraryApplet>, "OpenCallingLibraryApplet"},
|
||||||
{21, D<&IProcessWindingController::PushContext>, "PushContext"},
|
{21, nullptr, "PushContext"},
|
||||||
{22, D<&IProcessWindingController::PopContext>, "PopContext"},
|
{22, nullptr, "PopContext"},
|
||||||
{23, D<&IProcessWindingController::CancelWindingReservation>, "CancelWindingReservation"},
|
{23, nullptr, "CancelWindingReservation"},
|
||||||
{30, D<&IProcessWindingController::WindAndDoReserved>, "WindAndDoReserved"},
|
{30, nullptr, "WindAndDoReserved"},
|
||||||
{40, D<&IProcessWindingController::ReserveToStartAndWaitAndUnwindThis>, "ReserveToStartAndWaitAndUnwindThis"},
|
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
|
||||||
{41, D<&IProcessWindingController::ReserveToStartAndWait>, "ReserveToStartAndWait"},
|
{41, nullptr, "ReserveToStartAndWait"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -54,43 +51,4 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IProcessWindingController::PushContext(SharedPointer<IStorage> context) {
|
|
||||||
LOG_INFO(Service_AM, "called");
|
|
||||||
m_applet->context_stack.push(context);
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_context) {
|
|
||||||
LOG_INFO(Service_AM, "called");
|
|
||||||
|
|
||||||
if (m_applet->context_stack.empty()) {
|
|
||||||
LOG_ERROR(Service_AM, "Context stack is empty");
|
|
||||||
R_THROW(ResultUnknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_context = m_applet->context_stack.top();
|
|
||||||
m_applet->context_stack.pop();
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IProcessWindingController::CancelWindingReservation() {
|
|
||||||
LOG_WARNING(Service_AM, "STUBBED");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IProcessWindingController::WindAndDoReserved() {
|
|
||||||
LOG_WARNING(Service_AM, "STUBBED");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
|
|
||||||
LOG_WARNING(Service_AM, "STUBBED");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IProcessWindingController::ReserveToStartAndWait() {
|
|
||||||
LOG_WARNING(Service_AM, "STUBBED");
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
// 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
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/service/am/service/storage.h"
|
|
||||||
#include "core/hle/service/am/am_types.h"
|
#include "core/hle/service/am/am_types.h"
|
||||||
#include "core/hle/service/cmif_types.h"
|
#include "core/hle/service/cmif_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
@ -25,12 +21,6 @@ private:
|
||||||
Result GetLaunchReason(Out<AppletProcessLaunchReason> out_launch_reason);
|
Result GetLaunchReason(Out<AppletProcessLaunchReason> out_launch_reason);
|
||||||
Result OpenCallingLibraryApplet(
|
Result OpenCallingLibraryApplet(
|
||||||
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet);
|
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet);
|
||||||
Result PushContext(SharedPointer<IStorage> in_context);
|
|
||||||
Result PopContext(Out<SharedPointer<IStorage>> out_context);
|
|
||||||
Result CancelWindingReservation();
|
|
||||||
Result WindAndDoReserved();
|
|
||||||
Result ReserveToStartAndWaitAndUnwindThis();
|
|
||||||
Result ReserveToStartAndWait();
|
|
||||||
|
|
||||||
const std::shared_ptr<Applet> m_applet;
|
const std::shared_ptr<Applet> m_applet;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -70,7 +67,6 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
|
||||||
{110, nullptr, "SetApplicationAlbumUserData"},
|
{110, nullptr, "SetApplicationAlbumUserData"},
|
||||||
{120, D<&ISelfController::SaveCurrentScreenshot>, "SaveCurrentScreenshot"},
|
{120, D<&ISelfController::SaveCurrentScreenshot>, "SaveCurrentScreenshot"},
|
||||||
{130, D<&ISelfController::SetRecordVolumeMuted>, "SetRecordVolumeMuted"},
|
{130, D<&ISelfController::SetRecordVolumeMuted>, "SetRecordVolumeMuted"},
|
||||||
{230, D<&ISelfController::Unknown230>, "Unknown230"},
|
|
||||||
{1000, nullptr, "GetDebugStorageChannel"},
|
{1000, nullptr, "GetDebugStorageChannel"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
@ -408,12 +404,4 @@ Result ISelfController::SetRecordVolumeMuted(bool muted) {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ISelfController::Unknown230(u32 in_val, Out<u16> out_val) {
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, in_val={}", in_val);
|
|
||||||
|
|
||||||
*out_val = 0;
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::AM
|
} // namespace Service::AM
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
@ -66,7 +63,6 @@ private:
|
||||||
Result SetAlbumImageTakenNotificationEnabled(bool enabled);
|
Result SetAlbumImageTakenNotificationEnabled(bool enabled);
|
||||||
Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option);
|
Result SaveCurrentScreenshot(Capture::AlbumReportOption album_report_option);
|
||||||
Result SetRecordVolumeMuted(bool muted);
|
Result SetRecordVolumeMuted(bool muted);
|
||||||
Result Unknown230(u32 in_val, Out<u16> out_val);
|
|
||||||
|
|
||||||
Kernel::KProcess* const m_process;
|
Kernel::KProcess* const m_process;
|
||||||
const std::shared_ptr<Applet> m_applet;
|
const std::shared_ptr<Applet> m_applet;
|
||||||
|
|
|
@ -135,8 +135,6 @@ target_include_directories(dynarmic_tests PRIVATE . ../src)
|
||||||
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
|
||||||
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
|
||||||
|
|
||||||
if ("x86_64" IN_LIST ARCHITECTURE)
|
target_compile_options(dynarmic_tests PRIVATE -mavx2)
|
||||||
target_compile_options(dynarmic_tests PRIVATE -mavx2)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_test(dynarmic_tests dynarmic_tests --durations yes)
|
add_test(dynarmic_tests dynarmic_tests --durations yes)
|
||||||
|
|
|
@ -409,7 +409,7 @@ void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
|
void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
|
||||||
if (info.uses_sampled_1d) {
|
if (info.uses_sampled_1d || info.uses_image_1d) {
|
||||||
ctx.AddCapability(spv::Capability::Sampled1D);
|
ctx.AddCapability(spv::Capability::Sampled1D);
|
||||||
}
|
}
|
||||||
if (info.uses_sparse_residency) {
|
if (info.uses_sparse_residency) {
|
||||||
|
|
|
@ -2,14 +2,245 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||||
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
|
||||||
|
|
||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
namespace {
|
namespace {
|
||||||
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0);
|
||||||
|
std::pair<Id, Id> AtomicArgs(EmitContext& ctx);
|
||||||
|
|
||||||
|
enum class PairAtomicOp {
|
||||||
|
Add,
|
||||||
|
SMin,
|
||||||
|
UMin,
|
||||||
|
SMax,
|
||||||
|
UMax,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Xor,
|
||||||
|
Exchange,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PairComponents {
|
||||||
|
Id lo;
|
||||||
|
Id hi;
|
||||||
|
};
|
||||||
|
|
||||||
|
PairComponents ComputePairComponents(EmitContext& ctx, PairAtomicOp op, Id current_lo, Id current_hi,
|
||||||
|
Id value_lo, Id value_hi) {
|
||||||
|
switch (op) {
|
||||||
|
case PairAtomicOp::Add: {
|
||||||
|
const Id sum_lo{ctx.OpIAdd(ctx.U32[1], current_lo, value_lo)};
|
||||||
|
const Id carry_pred{ctx.OpULessThan(ctx.U1, sum_lo, current_lo)};
|
||||||
|
const Id carry{ctx.OpSelect(ctx.U32[1], carry_pred, ctx.Const(1u), ctx.u32_zero_value)};
|
||||||
|
const Id sum_hi_base{ctx.OpIAdd(ctx.U32[1], current_hi, value_hi)};
|
||||||
|
const Id sum_hi{ctx.OpIAdd(ctx.U32[1], sum_hi_base, carry)};
|
||||||
|
return {sum_lo, sum_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::SMin: {
|
||||||
|
const Id current_hi_signed{ctx.OpBitcast(ctx.S32[1], current_hi)};
|
||||||
|
const Id value_hi_signed{ctx.OpBitcast(ctx.S32[1], value_hi)};
|
||||||
|
const Id hi_less{ctx.OpSLessThan(ctx.U1, current_hi_signed, value_hi_signed)};
|
||||||
|
const Id hi_equal{ctx.OpIEqual(ctx.U1, current_hi_signed, value_hi_signed)};
|
||||||
|
const Id lo_less{ctx.OpULessThan(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id lo_equal{ctx.OpIEqual(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id select_current{ctx.OpLogicalOr(ctx.U1, hi_less,
|
||||||
|
ctx.OpLogicalAnd(ctx.U1, hi_equal,
|
||||||
|
ctx.OpLogicalOr(ctx.U1, lo_less, lo_equal)))};
|
||||||
|
const Id new_lo{ctx.OpSelect(ctx.U32[1], select_current, current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpSelect(ctx.U32[1], select_current, current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::UMin: {
|
||||||
|
const Id hi_less{ctx.OpULessThan(ctx.U1, current_hi, value_hi)};
|
||||||
|
const Id hi_equal{ctx.OpIEqual(ctx.U1, current_hi, value_hi)};
|
||||||
|
const Id lo_less{ctx.OpULessThan(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id lo_equal{ctx.OpIEqual(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id select_current{ctx.OpLogicalOr(ctx.U1, hi_less,
|
||||||
|
ctx.OpLogicalAnd(ctx.U1, hi_equal,
|
||||||
|
ctx.OpLogicalOr(ctx.U1, lo_less, lo_equal)))};
|
||||||
|
const Id new_lo{ctx.OpSelect(ctx.U32[1], select_current, current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpSelect(ctx.U32[1], select_current, current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::SMax: {
|
||||||
|
const Id current_hi_signed{ctx.OpBitcast(ctx.S32[1], current_hi)};
|
||||||
|
const Id value_hi_signed{ctx.OpBitcast(ctx.S32[1], value_hi)};
|
||||||
|
const Id hi_greater{ctx.OpSGreaterThan(ctx.U1, current_hi_signed, value_hi_signed)};
|
||||||
|
const Id hi_equal{ctx.OpIEqual(ctx.U1, current_hi_signed, value_hi_signed)};
|
||||||
|
const Id lo_greater{ctx.OpUGreaterThan(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id lo_equal{ctx.OpIEqual(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id select_current{ctx.OpLogicalOr(ctx.U1, hi_greater,
|
||||||
|
ctx.OpLogicalAnd(ctx.U1, hi_equal,
|
||||||
|
ctx.OpLogicalOr(ctx.U1, lo_greater, lo_equal)))};
|
||||||
|
const Id new_lo{ctx.OpSelect(ctx.U32[1], select_current, current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpSelect(ctx.U32[1], select_current, current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::UMax: {
|
||||||
|
const Id hi_greater{ctx.OpUGreaterThan(ctx.U1, current_hi, value_hi)};
|
||||||
|
const Id hi_equal{ctx.OpIEqual(ctx.U1, current_hi, value_hi)};
|
||||||
|
const Id lo_greater{ctx.OpUGreaterThan(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id lo_equal{ctx.OpIEqual(ctx.U1, current_lo, value_lo)};
|
||||||
|
const Id select_current{ctx.OpLogicalOr(ctx.U1, hi_greater,
|
||||||
|
ctx.OpLogicalAnd(ctx.U1, hi_equal,
|
||||||
|
ctx.OpLogicalOr(ctx.U1, lo_greater, lo_equal)))};
|
||||||
|
const Id new_lo{ctx.OpSelect(ctx.U32[1], select_current, current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpSelect(ctx.U32[1], select_current, current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::And: {
|
||||||
|
const Id new_lo{ctx.OpBitwiseAnd(ctx.U32[1], current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpBitwiseAnd(ctx.U32[1], current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::Or: {
|
||||||
|
const Id new_lo{ctx.OpBitwiseOr(ctx.U32[1], current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpBitwiseOr(ctx.U32[1], current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::Xor: {
|
||||||
|
const Id new_lo{ctx.OpBitwiseXor(ctx.U32[1], current_lo, value_lo)};
|
||||||
|
const Id new_hi{ctx.OpBitwiseXor(ctx.U32[1], current_hi, value_hi)};
|
||||||
|
return {new_lo, new_hi};
|
||||||
|
}
|
||||||
|
case PairAtomicOp::Exchange:
|
||||||
|
return {value_lo, value_hi};
|
||||||
|
}
|
||||||
|
ASSERT_MSG(false, "Unhandled pair atomic operation");
|
||||||
|
return {current_lo, current_hi};
|
||||||
|
}
|
||||||
|
|
||||||
|
PairAtomicOp GetPairAtomicOp(Id (Sirit::Module::*func)(Id, Id, Id)) {
|
||||||
|
if (func == &Sirit::Module::OpIAdd) {
|
||||||
|
return PairAtomicOp::Add;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpSMin) {
|
||||||
|
return PairAtomicOp::SMin;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpUMin) {
|
||||||
|
return PairAtomicOp::UMin;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpSMax) {
|
||||||
|
return PairAtomicOp::SMax;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpUMax) {
|
||||||
|
return PairAtomicOp::UMax;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpBitwiseAnd) {
|
||||||
|
return PairAtomicOp::And;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpBitwiseOr) {
|
||||||
|
return PairAtomicOp::Or;
|
||||||
|
}
|
||||||
|
if (func == &Sirit::Module::OpBitwiseXor) {
|
||||||
|
return PairAtomicOp::Xor;
|
||||||
|
}
|
||||||
|
ASSERT_MSG(false, "Unsupported pair atomic opcode");
|
||||||
|
return PairAtomicOp::Exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id EmulateStorageAtomicPair(EmitContext& ctx, PairAtomicOp op, Id pointer, Id value_pair) {
|
||||||
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
|
const Id zero{ctx.u32_zero_value};
|
||||||
|
const Id one{ctx.Const(1u)};
|
||||||
|
const Id low_pointer{ctx.OpAccessChain(ctx.storage_types.U32.element, pointer, zero)};
|
||||||
|
const Id high_pointer{ctx.OpAccessChain(ctx.storage_types.U32.element, pointer, one)};
|
||||||
|
const Id value_lo{ctx.OpCompositeExtract(ctx.U32[1], value_pair, 0U)};
|
||||||
|
const Id value_hi{ctx.OpCompositeExtract(ctx.U32[1], value_pair, 1U)};
|
||||||
|
const Id loop_header{ctx.OpLabel()};
|
||||||
|
const Id loop_body{ctx.OpLabel()};
|
||||||
|
const Id loop_continue{ctx.OpLabel()};
|
||||||
|
const Id loop_merge{ctx.OpLabel()};
|
||||||
|
const Id high_block{ctx.OpLabel()};
|
||||||
|
const Id revert_block{ctx.OpLabel()};
|
||||||
|
|
||||||
|
ctx.OpBranch(loop_header);
|
||||||
|
ctx.AddLabel(loop_header);
|
||||||
|
ctx.OpLoopMerge(loop_merge, loop_continue, spv::LoopControlMask::MaskNone);
|
||||||
|
ctx.OpBranch(loop_body);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_body);
|
||||||
|
const Id current_pair{ctx.OpLoad(ctx.U32[2], pointer)};
|
||||||
|
const Id expected_lo{ctx.OpCompositeExtract(ctx.U32[1], current_pair, 0U)};
|
||||||
|
const Id expected_hi{ctx.OpCompositeExtract(ctx.U32[1], current_pair, 1U)};
|
||||||
|
const PairComponents new_pair{ComputePairComponents(ctx, op, expected_lo, expected_hi, value_lo, value_hi)};
|
||||||
|
const Id low_result{ctx.OpAtomicCompareExchange(ctx.U32[1], low_pointer, scope, semantics, semantics,
|
||||||
|
new_pair.lo, expected_lo)};
|
||||||
|
const Id low_success{ctx.OpIEqual(ctx.U1, low_result, expected_lo)};
|
||||||
|
ctx.OpSelectionMerge(loop_continue, spv::SelectionControlMask::MaskNone);
|
||||||
|
ctx.OpBranchConditional(low_success, high_block, loop_continue);
|
||||||
|
|
||||||
|
ctx.AddLabel(high_block);
|
||||||
|
const Id high_result{ctx.OpAtomicCompareExchange(ctx.U32[1], high_pointer, scope, semantics, semantics,
|
||||||
|
new_pair.hi, expected_hi)};
|
||||||
|
const Id high_success{ctx.OpIEqual(ctx.U1, high_result, expected_hi)};
|
||||||
|
ctx.OpBranchConditional(high_success, loop_merge, revert_block);
|
||||||
|
|
||||||
|
ctx.AddLabel(revert_block);
|
||||||
|
ctx.OpAtomicCompareExchange(ctx.U32[1], low_pointer, scope, semantics, semantics, expected_lo,
|
||||||
|
new_pair.lo);
|
||||||
|
ctx.OpBranch(loop_continue);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_continue);
|
||||||
|
ctx.OpBranch(loop_header);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_merge);
|
||||||
|
return current_pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id EmulateSharedAtomicExchange(EmitContext& ctx, Id offset, Id value_pair) {
|
||||||
|
const Id scope{ctx.Const(static_cast<u32>(spv::Scope::Workgroup))};
|
||||||
|
const Id semantics{ctx.u32_zero_value};
|
||||||
|
const Id value_lo{ctx.OpCompositeExtract(ctx.U32[1], value_pair, 0U)};
|
||||||
|
const Id value_hi{ctx.OpCompositeExtract(ctx.U32[1], value_pair, 1U)};
|
||||||
|
const Id low_pointer{SharedPointer(ctx, offset, 0)};
|
||||||
|
const Id high_pointer{SharedPointer(ctx, offset, 1)};
|
||||||
|
const Id loop_header{ctx.OpLabel()};
|
||||||
|
const Id loop_body{ctx.OpLabel()};
|
||||||
|
const Id loop_continue{ctx.OpLabel()};
|
||||||
|
const Id loop_merge{ctx.OpLabel()};
|
||||||
|
const Id high_block{ctx.OpLabel()};
|
||||||
|
const Id revert_block{ctx.OpLabel()};
|
||||||
|
|
||||||
|
ctx.OpBranch(loop_header);
|
||||||
|
ctx.AddLabel(loop_header);
|
||||||
|
ctx.OpLoopMerge(loop_merge, loop_continue, spv::LoopControlMask::MaskNone);
|
||||||
|
ctx.OpBranch(loop_body);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_body);
|
||||||
|
const Id expected_lo{ctx.OpLoad(ctx.U32[1], low_pointer)};
|
||||||
|
const Id expected_hi{ctx.OpLoad(ctx.U32[1], high_pointer)};
|
||||||
|
const Id current_pair{ctx.OpCompositeConstruct(ctx.U32[2], expected_lo, expected_hi)};
|
||||||
|
const Id low_result{ctx.OpAtomicCompareExchange(ctx.U32[1], low_pointer, scope, semantics, semantics,
|
||||||
|
value_lo, expected_lo)};
|
||||||
|
const Id low_success{ctx.OpIEqual(ctx.U1, low_result, expected_lo)};
|
||||||
|
ctx.OpSelectionMerge(loop_continue, spv::SelectionControlMask::MaskNone);
|
||||||
|
ctx.OpBranchConditional(low_success, high_block, loop_continue);
|
||||||
|
|
||||||
|
ctx.AddLabel(high_block);
|
||||||
|
const Id high_result{ctx.OpAtomicCompareExchange(ctx.U32[1], high_pointer, scope, semantics, semantics,
|
||||||
|
value_hi, expected_hi)};
|
||||||
|
const Id high_success{ctx.OpIEqual(ctx.U1, high_result, expected_hi)};
|
||||||
|
ctx.OpBranchConditional(high_success, loop_merge, revert_block);
|
||||||
|
|
||||||
|
ctx.AddLabel(revert_block);
|
||||||
|
ctx.OpAtomicCompareExchange(ctx.U32[1], low_pointer, scope, semantics, semantics, expected_lo, value_lo);
|
||||||
|
ctx.OpBranch(loop_continue);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_continue);
|
||||||
|
ctx.OpBranch(loop_header);
|
||||||
|
|
||||||
|
ctx.AddLabel(loop_merge);
|
||||||
|
return current_pair;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset) {
|
||||||
const Id shift_id{ctx.Const(2U)};
|
const Id shift_id{ctx.Const(2U)};
|
||||||
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||||
if (index_offset > 0) {
|
if (index_offset > 0) {
|
||||||
|
@ -96,6 +327,12 @@ Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Valu
|
||||||
return ctx.ConstantNull(ctx.U32[2]);
|
return ctx.ConstantNull(ctx.U32[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx.profile.emulate_int64_with_uint2) {
|
||||||
|
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||||
|
binding, offset, sizeof(u32[2]))};
|
||||||
|
return EmulateStorageAtomicPair(ctx, GetPairAtomicOp(non_atomic_func), pointer, value);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||||
binding, offset, sizeof(u32[2]))};
|
binding, offset, sizeof(u32[2]))};
|
||||||
|
@ -175,6 +412,10 @@ Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id offset, Id value) {
|
Id EmitSharedAtomicExchange32x2(EmitContext& ctx, Id offset, Id value) {
|
||||||
|
if (ctx.profile.emulate_int64_with_uint2) {
|
||||||
|
return EmulateSharedAtomicExchange(ctx, offset, value);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||||
const Id pointer_1{SharedPointer(ctx, offset, 0)};
|
const Id pointer_1{SharedPointer(ctx, offset, 0)};
|
||||||
const Id pointer_2{SharedPointer(ctx, offset, 1)};
|
const Id pointer_2{SharedPointer(ctx, offset, 1)};
|
||||||
|
@ -351,6 +592,12 @@ Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR
|
||||||
|
|
||||||
Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
|
Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
|
||||||
const IR::Value& offset, Id value) {
|
const IR::Value& offset, Id value) {
|
||||||
|
if (ctx.profile.emulate_int64_with_uint2) {
|
||||||
|
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||||
|
binding, offset, sizeof(u32[2]))};
|
||||||
|
return EmulateStorageAtomicPair(ctx, PairAtomicOp::Exchange, pointer, value);
|
||||||
|
}
|
||||||
|
|
||||||
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
|
||||||
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
|
||||||
binding, offset, sizeof(u32[2]))};
|
binding, offset, sizeof(u32[2]))};
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||||
|
@ -185,6 +186,84 @@ private:
|
||||||
spv::ImageOperandsMask mask{};
|
spv::ImageOperandsMask mask{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Id SampledVectorType(EmitContext& ctx, TextureComponentType component_type) {
|
||||||
|
switch (component_type) {
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
return ctx.F32[4];
|
||||||
|
case TextureComponentType::Sint:
|
||||||
|
return ctx.S32[4];
|
||||||
|
case TextureComponentType::Uint:
|
||||||
|
return ctx.U32[4];
|
||||||
|
}
|
||||||
|
throw LogicError("Unhandled texture component type {}", static_cast<u32>(component_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectsFloatResult(const IR::Inst* inst) {
|
||||||
|
switch (inst->Type()) {
|
||||||
|
case IR::Type::F32:
|
||||||
|
case IR::Type::F32x2:
|
||||||
|
case IR::Type::F32x3:
|
||||||
|
case IR::Type::F32x4:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Id MakeFloatVector(EmitContext& ctx, float value) {
|
||||||
|
const Id scalar{ctx.Const(value)};
|
||||||
|
return ctx.ConstantComposite(ctx.F32[4], scalar, scalar, scalar, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
Id NormalizeUnsignedSample(EmitContext& ctx, u32 component_bits, Id value) {
|
||||||
|
if (component_bits == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const double max_value = std::exp2(static_cast<double>(component_bits)) - 1.0;
|
||||||
|
if (!(max_value > 0.0)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const float inv_max = static_cast<float>(1.0 / max_value);
|
||||||
|
return ctx.OpFMul(ctx.F32[4], value, MakeFloatVector(ctx, inv_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id NormalizeSignedSample(EmitContext& ctx, u32 component_bits, Id value) {
|
||||||
|
if (component_bits == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const double positive_max = component_bits > 0 ? std::exp2(static_cast<double>(component_bits - 1)) - 1.0 : 0.0;
|
||||||
|
if (!(positive_max > 0.0)) {
|
||||||
|
return ctx.OpFClamp(ctx.F32[4], value, MakeFloatVector(ctx, -1.0f), MakeFloatVector(ctx, 1.0f));
|
||||||
|
}
|
||||||
|
const float inv_pos = static_cast<float>(1.0 / positive_max);
|
||||||
|
const Id scaled{ctx.OpFMul(ctx.F32[4], value, MakeFloatVector(ctx, inv_pos))};
|
||||||
|
return ctx.OpFClamp(ctx.F32[4], scaled, MakeFloatVector(ctx, -1.0f), MakeFloatVector(ctx, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id ConvertSampleToExpectedType(EmitContext& ctx, const IR::Inst* inst,
|
||||||
|
const TextureDefinition* texture_def, Id value) {
|
||||||
|
if (!texture_def || texture_def->component_type == TextureComponentType::Float) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (!ExpectsFloatResult(inst)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
switch (texture_def->component_type) {
|
||||||
|
case TextureComponentType::Sint: {
|
||||||
|
const Id as_float{ctx.OpConvertSToF(ctx.F32[4], value)};
|
||||||
|
return NormalizeSignedSample(ctx, texture_def->component_bit_size, as_float);
|
||||||
|
}
|
||||||
|
case TextureComponentType::Uint: {
|
||||||
|
const Id as_float{ctx.OpConvertUToF(ctx.F32[4], value)};
|
||||||
|
return NormalizeUnsignedSample(ctx, texture_def->component_bit_size, as_float);
|
||||||
|
}
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
||||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||||
if (def.count > 1) {
|
if (def.count > 1) {
|
||||||
|
@ -449,31 +528,39 @@ Id EmitBoundImageWrite(EmitContext&) {
|
||||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id bias_lc, const IR::Value& offset) {
|
Id bias_lc, const IR::Value& offset) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
|
Id sample{};
|
||||||
if (ctx.stage == Stage::Fragment) {
|
if (ctx.stage == Stage::Fragment) {
|
||||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
||||||
bias_lc, offset);
|
bias_lc, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
sample = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||||
} else {
|
} else {
|
||||||
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
|
|
||||||
// if the lod was explicitly zero. This may change on Turing with implicit compute
|
|
||||||
// derivatives
|
|
||||||
const Id lod{ctx.Const(0.0f)};
|
const Id lod{ctx.Const(0.0f)};
|
||||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
}
|
}
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id lod, const IR::Value& offset) {
|
Id lod, const IR::Value& offset) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
const Id sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||||
|
@ -509,13 +596,19 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
|
||||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
const IR::Value& offset, const IR::Value& offset2) {
|
const IR::Value& offset, const IR::Value& offset2) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(ctx, offset, offset2);
|
const ImageOperands operands(ctx, offset, offset2);
|
||||||
if (ctx.profile.need_gather_subpixel_offset) {
|
if (ctx.profile.need_gather_subpixel_offset) {
|
||||||
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
||||||
}
|
}
|
||||||
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
const Id sample = Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||||
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
|
result_type, Texture(ctx, info, index), coords,
|
||||||
operands.MaskOptional(), operands.Span());
|
ctx.Const(info.gather_component), operands.MaskOptional(),
|
||||||
|
operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
|
@ -538,12 +631,17 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
}
|
}
|
||||||
if (Sirit::ValidId(ms)) {
|
if (Sirit::ValidId(ms)) {
|
||||||
// This image is multisampled, lod must be implicit
|
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
}
|
}
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(lod, ms);
|
const ImageOperands operands(lod, ms);
|
||||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
const Id sample = Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
|
||||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
result_type, TextureImage(ctx, info, index), coords,
|
||||||
|
operands.MaskOptional(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||||
|
@ -588,14 +686,19 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
|
||||||
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const auto operands = info.num_derivatives == 3
|
const auto operands = info.num_derivatives == 3
|
||||||
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
ctx.Def(offset), {}, lod_clamp)
|
ctx.Def(offset), {}, lod_clamp)
|
||||||
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
info.num_derivatives, offset, lod_clamp);
|
info.num_derivatives, offset, lod_clamp);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
const Id sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||||
|
|
|
@ -28,9 +28,23 @@ enum class Operation {
|
||||||
FPMax,
|
FPMax,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Id ComponentTypeId(EmitContext& ctx, TextureComponentType component_type) {
|
||||||
|
switch (component_type) {
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
return ctx.F32[1];
|
||||||
|
case TextureComponentType::Sint:
|
||||||
|
return ctx.S32[1];
|
||||||
|
case TextureComponentType::Uint:
|
||||||
|
return ctx.U32[1];
|
||||||
|
}
|
||||||
|
throw LogicError("Unhandled texture component type {}", static_cast<u32>(component_type));
|
||||||
|
}
|
||||||
|
|
||||||
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
||||||
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
||||||
const Id type{ctx.F32[1]};
|
const TextureComponentType component_type = desc.is_depth ? TextureComponentType::Float
|
||||||
|
: desc.component_type;
|
||||||
|
const Id type{ComponentTypeId(ctx, component_type)};
|
||||||
const bool depth{desc.is_depth};
|
const bool depth{desc.is_depth};
|
||||||
const bool ms{desc.is_multisample};
|
const bool ms{desc.is_multisample};
|
||||||
switch (desc.type) {
|
switch (desc.type) {
|
||||||
|
@ -1374,6 +1388,8 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
|
||||||
.image_type = image_type,
|
.image_type = image_type,
|
||||||
.count = desc.count,
|
.count = desc.count,
|
||||||
.is_multisample = desc.is_multisample,
|
.is_multisample = desc.is_multisample,
|
||||||
|
.component_type = desc.component_type,
|
||||||
|
.component_bit_size = desc.component_bit_size,
|
||||||
});
|
});
|
||||||
if (profile.supported_spirv >= 0x00010400) {
|
if (profile.supported_spirv >= 0x00010400) {
|
||||||
interfaces.push_back(id);
|
interfaces.push_back(id);
|
||||||
|
@ -1417,6 +1433,12 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
const Info& info{program.info};
|
const Info& info{program.info};
|
||||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||||
|
|
||||||
|
const auto decorate_flat_if_fragment = [this](Id id) {
|
||||||
|
if (stage == Stage::Fragment) {
|
||||||
|
Decorate(id, spv::Decoration::Flat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (info.uses_workgroup_id) {
|
if (info.uses_workgroup_id) {
|
||||||
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
||||||
}
|
}
|
||||||
|
@ -1432,16 +1454,22 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
}
|
}
|
||||||
if (info.uses_sample_id) {
|
if (info.uses_sample_id) {
|
||||||
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
|
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
|
||||||
|
decorate_flat_if_fragment(sample_id);
|
||||||
}
|
}
|
||||||
if (info.uses_is_helper_invocation) {
|
if (info.uses_is_helper_invocation) {
|
||||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||||
}
|
}
|
||||||
if (info.uses_subgroup_mask) {
|
if (info.uses_subgroup_mask) {
|
||||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_eq);
|
||||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_lt);
|
||||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_le);
|
||||||
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_gt);
|
||||||
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_ge);
|
||||||
}
|
}
|
||||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||||
(profile.warp_size_potentially_larger_than_guest &&
|
(profile.warp_size_potentially_larger_than_guest &&
|
||||||
|
@ -1461,6 +1489,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
}
|
}
|
||||||
if (loads[IR::Attribute::PrimitiveId]) {
|
if (loads[IR::Attribute::PrimitiveId]) {
|
||||||
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
||||||
|
decorate_flat_if_fragment(primitive_id);
|
||||||
}
|
}
|
||||||
if (loads[IR::Attribute::Layer]) {
|
if (loads[IR::Attribute::Layer]) {
|
||||||
AddCapability(spv::Capability::Geometry);
|
AddCapability(spv::Capability::Geometry);
|
||||||
|
|
|
@ -38,6 +38,8 @@ struct TextureDefinition {
|
||||||
Id image_type;
|
Id image_type;
|
||||||
u32 count;
|
u32 count;
|
||||||
bool is_multisample;
|
bool is_multisample;
|
||||||
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u32 component_bit_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBufferDefinition {
|
struct TextureBufferDefinition {
|
||||||
|
|
|
@ -289,7 +289,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||||
if (!host_info.support_float16) {
|
if (!host_info.support_float16) {
|
||||||
Optimization::LowerFp16ToFp32(program);
|
Optimization::LowerFp16ToFp32(program);
|
||||||
}
|
}
|
||||||
if (!host_info.support_int64) {
|
if (!host_info.support_int64 || host_info.emulate_int64_with_uint2) {
|
||||||
Optimization::LowerInt64ToInt32(program);
|
Optimization::LowerInt64ToInt32(program);
|
||||||
}
|
}
|
||||||
if (!host_info.support_conditional_barrier) {
|
if (!host_info.support_conditional_barrier) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct HostTranslateInfo {
|
||||||
bool support_float64{}; ///< True when the device supports 64-bit floats
|
bool support_float64{}; ///< True when the device supports 64-bit floats
|
||||||
bool support_float16{}; ///< True when the device supports 16-bit floats
|
bool support_float16{}; ///< True when the device supports 16-bit floats
|
||||||
bool support_int64{}; ///< True when the device supports 64-bit integers
|
bool support_int64{}; ///< True when the device supports 64-bit integers
|
||||||
|
bool emulate_int64_with_uint2{}; ///< True when 64-bit ops must be lowered to 32-bit pairs
|
||||||
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
|
||||||
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
|
||||||
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr bool IsOneDimensional(TextureType type) {
|
||||||
|
return type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
||||||
|
}
|
||||||
|
|
||||||
void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
|
void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
|
||||||
if (count != 1) {
|
if (count != 1) {
|
||||||
throw NotImplementedException("Constant buffer descriptor indexing");
|
throw NotImplementedException("Constant buffer descriptor indexing");
|
||||||
|
@ -548,7 +552,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageQueryDimensions:
|
case IR::Opcode::ImageQueryDimensions:
|
||||||
case IR::Opcode::ImageGradient: {
|
case IR::Opcode::ImageGradient: {
|
||||||
const TextureType type{inst.Flags<IR::TextureInstInfo>().type};
|
const TextureType type{inst.Flags<IR::TextureInstInfo>().type};
|
||||||
info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
info.uses_sampled_1d |= IsOneDimensional(type);
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
break;
|
break;
|
||||||
|
@ -560,7 +564,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageQueryLod: {
|
case IR::Opcode::ImageQueryLod: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
const TextureType type{flags.type};
|
const TextureType type{flags.type};
|
||||||
info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
info.uses_sampled_1d |= IsOneDimensional(type);
|
||||||
info.uses_shadow_lod |= flags.is_depth != 0;
|
info.uses_shadow_lod |= flags.is_depth != 0;
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
|
@ -569,6 +573,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageRead: {
|
case IR::Opcode::ImageRead: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
|
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
break;
|
break;
|
||||||
|
@ -576,6 +581,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageWrite: {
|
case IR::Opcode::ImageWrite: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
|
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
info.uses_image_buffers |= flags.type == TextureType::Buffer;
|
info.uses_image_buffers |= flags.type == TextureType::Buffer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -761,9 +767,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageAtomicAnd32:
|
case IR::Opcode::ImageAtomicAnd32:
|
||||||
case IR::Opcode::ImageAtomicOr32:
|
case IR::Opcode::ImageAtomicOr32:
|
||||||
case IR::Opcode::ImageAtomicXor32:
|
case IR::Opcode::ImageAtomicXor32:
|
||||||
case IR::Opcode::ImageAtomicExchange32:
|
case IR::Opcode::ImageAtomicExchange32: {
|
||||||
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_atomic_image_u32 = true;
|
info.uses_atomic_image_u32 = true;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "shader_recompiler/host_translate_info.h"
|
#include "shader_recompiler/host_translate_info.h"
|
||||||
#include "shader_recompiler/ir_opt/passes.h"
|
#include "shader_recompiler/ir_opt/passes.h"
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
|
#include "video_core/surface.h"
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -248,11 +249,19 @@ bool IsTextureInstruction(const IR::Inst& inst) {
|
||||||
}
|
}
|
||||||
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
|
const u32 handle = GetTextureHandleCached(env, cbuf);
|
||||||
|
if (handle == 0) {
|
||||||
|
return TexturePixelFormat::A8B8G8R8_UNORM;
|
||||||
|
}
|
||||||
|
return env.ReadTexturePixelFormat(handle);
|
||||||
}
|
}
|
||||||
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
|
const u32 handle = GetTextureHandleCached(env, cbuf);
|
||||||
|
if (handle == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return env.IsTexturePixelFormatInteger(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -524,6 +533,8 @@ public:
|
||||||
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
|
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
|
||||||
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
|
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
|
||||||
desc.has_secondary == existing.has_secondary &&
|
desc.has_secondary == existing.has_secondary &&
|
||||||
|
desc.component_type == existing.component_type &&
|
||||||
|
desc.component_bit_size == existing.component_bit_size &&
|
||||||
desc.cbuf_index == existing.cbuf_index &&
|
desc.cbuf_index == existing.cbuf_index &&
|
||||||
desc.cbuf_offset == existing.cbuf_offset &&
|
desc.cbuf_offset == existing.cbuf_offset &&
|
||||||
desc.shift_left == existing.shift_left &&
|
desc.shift_left == existing.shift_left &&
|
||||||
|
@ -598,6 +609,35 @@ bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureComponentType PixelFormatComponentType(TexturePixelFormat pixel_format, bool is_integer) {
|
||||||
|
if (!is_integer) {
|
||||||
|
return TextureComponentType::Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pixel_format) {
|
||||||
|
case TexturePixelFormat::A8B8G8R8_SINT:
|
||||||
|
case TexturePixelFormat::R8_SINT:
|
||||||
|
case TexturePixelFormat::R16G16B16A16_SINT:
|
||||||
|
case TexturePixelFormat::R32G32B32A32_SINT:
|
||||||
|
case TexturePixelFormat::R32G32_SINT:
|
||||||
|
case TexturePixelFormat::R16_SINT:
|
||||||
|
case TexturePixelFormat::R16G16_SINT:
|
||||||
|
case TexturePixelFormat::R8G8_SINT:
|
||||||
|
case TexturePixelFormat::R32_SINT:
|
||||||
|
return TextureComponentType::Sint;
|
||||||
|
default:
|
||||||
|
return TextureComponentType::Uint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 PixelFormatIntegerComponentBits(TexturePixelFormat pixel_format, bool is_integer) {
|
||||||
|
if (!is_integer) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<u8>(VideoCore::Surface::PixelComponentSizeBitsInteger(
|
||||||
|
static_cast<VideoCore::Surface::PixelFormat>(pixel_format)));
|
||||||
|
}
|
||||||
|
|
||||||
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
|
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
|
||||||
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
|
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
|
||||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||||
|
@ -698,6 +738,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const TexturePixelFormat pixel_format{ReadTexturePixelFormatCached(env, cbuf)};
|
||||||
|
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
||||||
u32 index;
|
u32 index;
|
||||||
switch (inst->GetOpcode()) {
|
switch (inst->GetOpcode()) {
|
||||||
case IR::Opcode::ImageRead:
|
case IR::Opcode::ImageRead:
|
||||||
|
@ -718,7 +760,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
}
|
}
|
||||||
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
||||||
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
||||||
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
|
||||||
if (flags.type == TextureType::Buffer) {
|
if (flags.type == TextureType::Buffer) {
|
||||||
index = descriptors.Add(ImageBufferDescriptor{
|
index = descriptors.Add(ImageBufferDescriptor{
|
||||||
.format = flags.image_format,
|
.format = flags.image_format,
|
||||||
|
@ -764,6 +805,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
.is_depth = flags.is_depth != 0,
|
.is_depth = flags.is_depth != 0,
|
||||||
.is_multisample = is_multisample,
|
.is_multisample = is_multisample,
|
||||||
.has_secondary = cbuf.has_secondary,
|
.has_secondary = cbuf.has_secondary,
|
||||||
|
.component_type = PixelFormatComponentType(pixel_format, is_integer),
|
||||||
|
.component_bit_size = PixelFormatIntegerComponentBits(pixel_format, is_integer),
|
||||||
.cbuf_index = cbuf.index,
|
.cbuf_index = cbuf.index,
|
||||||
.cbuf_offset = cbuf.offset,
|
.cbuf_offset = cbuf.offset,
|
||||||
.shift_left = cbuf.shift_left,
|
.shift_left = cbuf.shift_left,
|
||||||
|
|
|
@ -14,6 +14,7 @@ struct Profile {
|
||||||
bool support_int8{};
|
bool support_int8{};
|
||||||
bool support_int16{};
|
bool support_int16{};
|
||||||
bool support_int64{};
|
bool support_int64{};
|
||||||
|
bool emulate_int64_with_uint2{};
|
||||||
bool support_vertex_instance_id{};
|
bool support_vertex_instance_id{};
|
||||||
bool support_float_controls{};
|
bool support_float_controls{};
|
||||||
bool support_separate_denorm_behavior{};
|
bool support_separate_denorm_behavior{};
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -35,6 +38,12 @@ enum class TextureType : u32 {
|
||||||
};
|
};
|
||||||
constexpr u32 NUM_TEXTURE_TYPES = 9;
|
constexpr u32 NUM_TEXTURE_TYPES = 9;
|
||||||
|
|
||||||
|
enum class TextureComponentType : u32 {
|
||||||
|
Float,
|
||||||
|
Sint,
|
||||||
|
Uint,
|
||||||
|
};
|
||||||
|
|
||||||
enum class TexturePixelFormat {
|
enum class TexturePixelFormat {
|
||||||
A8B8G8R8_UNORM,
|
A8B8G8R8_UNORM,
|
||||||
A8B8G8R8_SNORM,
|
A8B8G8R8_SNORM,
|
||||||
|
@ -174,7 +183,9 @@ struct StorageBufferDescriptor {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBufferDescriptor {
|
struct TextureBufferDescriptor {
|
||||||
bool has_secondary;
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u8 component_bit_size{};
|
||||||
|
bool has_secondary{};
|
||||||
u32 cbuf_index;
|
u32 cbuf_index;
|
||||||
u32 cbuf_offset;
|
u32 cbuf_offset;
|
||||||
u32 shift_left;
|
u32 shift_left;
|
||||||
|
@ -207,6 +218,8 @@ struct TextureDescriptor {
|
||||||
bool is_depth;
|
bool is_depth;
|
||||||
bool is_multisample;
|
bool is_multisample;
|
||||||
bool has_secondary;
|
bool has_secondary;
|
||||||
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u8 component_bit_size{};
|
||||||
u32 cbuf_index;
|
u32 cbuf_index;
|
||||||
u32 cbuf_offset;
|
u32 cbuf_offset;
|
||||||
u32 shift_left;
|
u32 shift_left;
|
||||||
|
|
|
@ -399,6 +399,10 @@ if (YUZU_USE_PRECOMPILED_HEADERS)
|
||||||
target_precompile_headers(video_core PRIVATE precompiled_headers.h)
|
target_precompile_headers(video_core PRIVATE precompiled_headers.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (YUZU_ENABLE_LTO)
|
||||||
|
set_property(TARGET video_core PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ANDROID AND ARCHITECTURE_arm64)
|
if (ANDROID AND ARCHITECTURE_arm64)
|
||||||
target_link_libraries(video_core PRIVATE adrenotools)
|
target_link_libraries(video_core PRIVATE adrenotools)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -785,13 +785,18 @@ void BufferCache<P>::BindHostGraphicsUniformBuffers(size_t stage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index, bool needs_bind) {
|
void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 binding_index,
|
||||||
++channel_state->uniform_cache_shots[0];
|
bool needs_bind) {
|
||||||
const Binding& binding = channel_state->uniform_buffers[stage][index];
|
const Binding& binding = channel_state->uniform_buffers[stage][index];
|
||||||
const DAddr device_addr = binding.device_addr;
|
const DAddr device_addr = binding.device_addr;
|
||||||
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
const u32 size = (std::min)(binding.size, (*channel_state->uniform_buffer_sizes)[stage][index]);
|
||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
|
const bool sync_buffer = SynchronizeBuffer(buffer, device_addr, size);
|
||||||
|
if (sync_buffer) {
|
||||||
|
++channel_state->uniform_cache_hits[0];
|
||||||
|
}
|
||||||
|
++channel_state->uniform_cache_shots[0];
|
||||||
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
|
||||||
size <= channel_state->uniform_buffer_skip_cache_size &&
|
size <= channel_state->uniform_buffer_skip_cache_size &&
|
||||||
!memory_tracker.IsRegionGpuModified(device_addr, size);
|
!memory_tracker.IsRegionGpuModified(device_addr, size);
|
||||||
|
@ -822,10 +827,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
|
||||||
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
device_memory.ReadBlockUnsafe(device_addr, span.data(), size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Classic cached path
|
|
||||||
if (SynchronizeBuffer(buffer, device_addr, size)) {
|
|
||||||
++channel_state->uniform_cache_hits[0];
|
|
||||||
}
|
|
||||||
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
// Skip binding if it's not needed and if the bound buffer is not the fast version
|
||||||
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
// This exists to avoid instances where the fast buffer is bound and a GPU write happens
|
||||||
needs_bind |= HasFastUniformBufferBound(stage, binding_index);
|
needs_bind |= HasFastUniformBufferBound(stage, binding_index);
|
||||||
|
|
|
@ -191,10 +191,19 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
const bool needs_linear_fallback = sampler.RequiresLinearFiltering() &&
|
||||||
!image_view.SupportsAnisotropy()};
|
!image_view.SupportsLinearFiltering();
|
||||||
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
const bool needs_aniso_fallback = sampler.HasAddedAnisotropy() &&
|
||||||
: sampler.Handle()};
|
!image_view.SupportsAnisotropy();
|
||||||
|
if (!image_view.SupportsLinearFiltering()) {
|
||||||
|
ASSERT_MSG(!sampler.RequiresLinearFiltering() || needs_linear_fallback,
|
||||||
|
"Linear filtering sampler bound to unsupported image view");
|
||||||
|
}
|
||||||
|
// Prefer degrading to nearest sampling when the view lacks linear support.
|
||||||
|
const VkSampler vk_sampler = needs_linear_fallback
|
||||||
|
? sampler.HandleWithoutLinearFiltering()
|
||||||
|
: (needs_aniso_fallback ? sampler.HandleWithDefaultAnisotropy()
|
||||||
|
: sampler.Handle());
|
||||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||||
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||||
.support_descriptor_aliasing = device.IsDescriptorAliasingSupported(),
|
.support_descriptor_aliasing = device.IsDescriptorAliasingSupported(),
|
||||||
.support_int8 = device.IsInt8Supported(),
|
.support_int8 = device.IsInt8Supported(),
|
||||||
.support_int16 = device.IsShaderInt16Supported(),
|
.support_int16 = device.IsShaderInt16Supported(),
|
||||||
.support_int64 = device.IsShaderInt64Supported(),
|
.support_int64 = device.IsShaderInt64Supported() && !device.UsesShaderInt64Emulation(),
|
||||||
|
.emulate_int64_with_uint2 = device.UsesShaderInt64Emulation(),
|
||||||
.support_vertex_instance_id = false,
|
.support_vertex_instance_id = false,
|
||||||
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
|
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
|
||||||
.support_separate_denorm_behavior =
|
.support_separate_denorm_behavior =
|
||||||
|
@ -384,7 +385,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||||
host_info = Shader::HostTranslateInfo{
|
host_info = Shader::HostTranslateInfo{
|
||||||
.support_float64 = device.IsFloat64Supported(),
|
.support_float64 = device.IsFloat64Supported(),
|
||||||
.support_float16 = device.IsFloat16Supported(),
|
.support_float16 = device.IsFloat16Supported(),
|
||||||
.support_int64 = device.IsShaderInt64Supported(),
|
.support_int64 = device.IsShaderInt64Supported() && !device.UsesShaderInt64Emulation(),
|
||||||
|
.emulate_int64_with_uint2 = device.UsesShaderInt64Emulation(),
|
||||||
.needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
|
.needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY ||
|
||||||
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
|
driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE ||
|
||||||
driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY,
|
driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY,
|
||||||
|
|
|
@ -1229,6 +1229,18 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case PixelFormat::R16_UNORM:
|
||||||
|
if (src_view.format == PixelFormat::D16_UNORM) {
|
||||||
|
return blit_image_helper.ConvertD16ToR16(dst, src_view);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PixelFormat::D16_UNORM:
|
||||||
|
if (src_view.format == PixelFormat::R16_UNORM) {
|
||||||
|
return blit_image_helper.ConvertR16ToD16(dst, src_view);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case PixelFormat::A8B8G8R8_UNORM:
|
case PixelFormat::A8B8G8R8_UNORM:
|
||||||
case PixelFormat::A8B8G8R8_SNORM:
|
case PixelFormat::A8B8G8R8_SNORM:
|
||||||
case PixelFormat::A8B8G8R8_SINT:
|
case PixelFormat::A8B8G8R8_SINT:
|
||||||
|
@ -1270,7 +1282,6 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||||
case PixelFormat::R32G32_SINT:
|
case PixelFormat::R32G32_SINT:
|
||||||
case PixelFormat::R32_FLOAT:
|
case PixelFormat::R32_FLOAT:
|
||||||
case PixelFormat::R16_FLOAT:
|
case PixelFormat::R16_FLOAT:
|
||||||
case PixelFormat::R16_UNORM:
|
|
||||||
case PixelFormat::R16_SNORM:
|
case PixelFormat::R16_SNORM:
|
||||||
case PixelFormat::R16_UINT:
|
case PixelFormat::R16_UINT:
|
||||||
case PixelFormat::R16_SINT:
|
case PixelFormat::R16_SINT:
|
||||||
|
@ -1325,7 +1336,6 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||||
case PixelFormat::ASTC_2D_6X5_SRGB:
|
case PixelFormat::ASTC_2D_6X5_SRGB:
|
||||||
case PixelFormat::E5B9G9R9_FLOAT:
|
case PixelFormat::E5B9G9R9_FLOAT:
|
||||||
case PixelFormat::D32_FLOAT:
|
case PixelFormat::D32_FLOAT:
|
||||||
case PixelFormat::D16_UNORM:
|
|
||||||
case PixelFormat::X8_D24_UNORM:
|
case PixelFormat::X8_D24_UNORM:
|
||||||
case PixelFormat::S8_UINT:
|
case PixelFormat::S8_UINT:
|
||||||
case PixelFormat::S8_UINT_D24_UNORM:
|
case PixelFormat::S8_UINT_D24_UNORM:
|
||||||
|
@ -2051,6 +2061,8 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||||
},
|
},
|
||||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
||||||
};
|
};
|
||||||
|
supports_linear_filtering = device->IsFormatSupported(
|
||||||
|
create_info.format, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, FormatType::Optimal);
|
||||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||||
VkImageViewCreateInfo ci{create_info};
|
VkImageViewCreateInfo ci{create_info};
|
||||||
ci.viewType = ImageViewType(tex_type);
|
ci.viewType = ImageViewType(tex_type);
|
||||||
|
@ -2101,10 +2113,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info,
|
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info,
|
||||||
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
||||||
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
|
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
|
||||||
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
|
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {
|
||||||
|
supports_linear_filtering = true;
|
||||||
|
}
|
||||||
|
|
||||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
|
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
|
||||||
: VideoCommon::ImageViewBase{params}, device{&runtime.device} {
|
: VideoCommon::ImageViewBase{params}, device{&runtime.device} {
|
||||||
|
supports_linear_filtering = true;
|
||||||
if (device->HasNullDescriptor()) {
|
if (device->HasNullDescriptor()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2165,6 +2180,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||||
if (image_format == Shader::ImageFormat::Typeless) {
|
if (image_format == Shader::ImageFormat::Typeless) {
|
||||||
return Handle(texture_type);
|
return Handle(texture_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_signed{image_format == Shader::ImageFormat::R8_SINT ||
|
const bool is_signed{image_format == Shader::ImageFormat::R8_SINT ||
|
||||||
image_format == Shader::ImageFormat::R16_SINT};
|
image_format == Shader::ImageFormat::R16_SINT};
|
||||||
if (!storage_views) {
|
if (!storage_views) {
|
||||||
|
@ -2234,15 +2250,23 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||||
}
|
}
|
||||||
// Some games have samplers with garbage. Sanitize them here.
|
// Some games have samplers with garbage. Sanitize them here.
|
||||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||||
|
const VkFilter mag_filter = MaxwellToVK::Sampler::Filter(tsc.mag_filter);
|
||||||
|
const VkFilter min_filter = MaxwellToVK::Sampler::Filter(tsc.min_filter);
|
||||||
|
const VkSamplerMipmapMode mipmap_mode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter);
|
||||||
|
const f32 min_lod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod();
|
||||||
|
const f32 max_lod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod();
|
||||||
|
requires_linear_filtering = mag_filter == VK_FILTER_LINEAR || min_filter == VK_FILTER_LINEAR ||
|
||||||
|
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR || max_anisotropy > 1.0f;
|
||||||
|
|
||||||
const auto create_sampler = [&](const f32 anisotropy) {
|
const auto create_sampler = [&](VkFilter mag, VkFilter min, VkSamplerMipmapMode mip,
|
||||||
|
f32 anisotropy) {
|
||||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
.pNext = pnext,
|
.pNext = pnext,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
.magFilter = mag,
|
||||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
.minFilter = min,
|
||||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
.mipmapMode = mip,
|
||||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||||
|
@ -2251,19 +2275,25 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||||
.maxAnisotropy = anisotropy,
|
.maxAnisotropy = anisotropy,
|
||||||
.compareEnable = tsc.depth_compare_enabled,
|
.compareEnable = tsc.depth_compare_enabled,
|
||||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
.minLod = min_lod,
|
||||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
.maxLod = max_lod,
|
||||||
.borderColor =
|
.borderColor =
|
||||||
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
||||||
.unnormalizedCoordinates = VK_FALSE,
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
sampler = create_sampler(max_anisotropy);
|
sampler = create_sampler(mag_filter, min_filter, mipmap_mode, max_anisotropy);
|
||||||
|
|
||||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||||
if (max_anisotropy > max_anisotropy_default) {
|
if (max_anisotropy > max_anisotropy_default) {
|
||||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
|
sampler_default_anisotropy = create_sampler(mag_filter, min_filter, mipmap_mode,
|
||||||
|
max_anisotropy_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requires_linear_filtering) {
|
||||||
|
sampler_no_linear = create_sampler(VK_FILTER_NEAREST, VK_FILTER_NEAREST,
|
||||||
|
VK_SAMPLER_MIPMAP_MODE_NEAREST, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,10 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool IsRescaled() const noexcept;
|
[[nodiscard]] bool IsRescaled() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] bool SupportsLinearFiltering() const noexcept {
|
||||||
|
return supports_linear_filtering;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
||||||
return *image_views[static_cast<size_t>(texture_type)];
|
return *image_views[static_cast<size_t>(texture_type)];
|
||||||
}
|
}
|
||||||
|
@ -278,6 +282,7 @@ private:
|
||||||
vk::ImageView depth_view;
|
vk::ImageView depth_view;
|
||||||
vk::ImageView stencil_view;
|
vk::ImageView stencil_view;
|
||||||
vk::ImageView color_view;
|
vk::ImageView color_view;
|
||||||
|
bool supports_linear_filtering{};
|
||||||
vk::Image null_image;
|
vk::Image null_image;
|
||||||
VkImage image_handle = VK_NULL_HANDLE;
|
VkImage image_handle = VK_NULL_HANDLE;
|
||||||
VkImageView render_target = VK_NULL_HANDLE;
|
VkImageView render_target = VK_NULL_HANDLE;
|
||||||
|
@ -296,16 +301,26 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
||||||
return *sampler_default_anisotropy;
|
return sampler_default_anisotropy ? *sampler_default_anisotropy : *sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkSampler HandleWithoutLinearFiltering() const noexcept {
|
||||||
|
return sampler_no_linear ? *sampler_no_linear : *sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
||||||
return static_cast<bool>(sampler_default_anisotropy);
|
return static_cast<bool>(sampler_default_anisotropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool RequiresLinearFiltering() const noexcept {
|
||||||
|
return requires_linear_filtering;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::Sampler sampler;
|
vk::Sampler sampler;
|
||||||
vk::Sampler sampler_default_anisotropy;
|
vk::Sampler sampler_default_anisotropy;
|
||||||
|
vk::Sampler sampler_no_linear;
|
||||||
|
bool requires_linear_filtering{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Framebuffer {
|
class Framebuffer {
|
||||||
|
|
|
@ -502,6 +502,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_qualcomm) {
|
if (is_qualcomm) {
|
||||||
|
if (extensions.shader_float_controls) {
|
||||||
|
LOG_WARNING(Render_Vulkan,
|
||||||
|
"Qualcomm drivers have broken VK_KHR_shader_float_controls");
|
||||||
|
RemoveExtension(extensions.shader_float_controls,
|
||||||
|
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||||
|
}
|
||||||
LOG_WARNING(Render_Vulkan,
|
LOG_WARNING(Render_Vulkan,
|
||||||
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
|
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
|
||||||
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
||||||
|
@ -974,6 +980,7 @@ bool Device::HasTimelineSemaphore() const {
|
||||||
bool Device::GetSuitability(bool requires_swapchain) {
|
bool Device::GetSuitability(bool requires_swapchain) {
|
||||||
// Assume we will be suitable.
|
// Assume we will be suitable.
|
||||||
bool suitable = true;
|
bool suitable = true;
|
||||||
|
shader_int64_emulation = false;
|
||||||
|
|
||||||
// Configure properties.
|
// Configure properties.
|
||||||
VkPhysicalDeviceVulkan12Features features_1_2{};
|
VkPhysicalDeviceVulkan12Features features_1_2{};
|
||||||
|
@ -985,6 +992,22 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||||
// Set instance version.
|
// Set instance version.
|
||||||
instance_version = properties.properties.apiVersion;
|
instance_version = properties.properties.apiVersion;
|
||||||
|
|
||||||
|
VkPhysicalDeviceDriverProperties driver_probe_props{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
|
||||||
|
};
|
||||||
|
VkPhysicalDeviceProperties2 driver_probe{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||||
|
.pNext = &driver_probe_props,
|
||||||
|
};
|
||||||
|
physical.GetProperties2(driver_probe);
|
||||||
|
const VkDriverId driver_id = driver_probe_props.driverID;
|
||||||
|
const bool is_qualcomm_proprietary = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
|
||||||
|
const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP;
|
||||||
|
|
||||||
|
shader_int64_emulation = is_qualcomm_proprietary;
|
||||||
|
|
||||||
|
const bool disable_shader_int64 = shader_int64_emulation || is_turnip;
|
||||||
|
|
||||||
// Minimum of API version 1.1 is required. (This is well-supported.)
|
// Minimum of API version 1.1 is required. (This is well-supported.)
|
||||||
ASSERT(instance_version >= VK_API_VERSION_1_1);
|
ASSERT(instance_version >= VK_API_VERSION_1_1);
|
||||||
|
|
||||||
|
@ -1095,8 +1118,23 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||||
// Perform the feature test.
|
// Perform the feature test.
|
||||||
physical.GetFeatures2(features2);
|
physical.GetFeatures2(features2);
|
||||||
|
|
||||||
|
if (disable_shader_int64) {
|
||||||
|
features2.features.shaderInt64 = VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
// Base Vulkan 1.0 features are always valid regardless of instance version.
|
// Base Vulkan 1.0 features are always valid regardless of instance version.
|
||||||
features.features = features2.features;
|
features.features = features2.features;
|
||||||
|
if (disable_shader_int64) {
|
||||||
|
features.features.shaderInt64 = VK_FALSE;
|
||||||
|
features.shader_atomic_int64.shaderBufferInt64Atomics = VK_FALSE;
|
||||||
|
features.shader_atomic_int64.shaderSharedInt64Atomics = VK_FALSE;
|
||||||
|
if (shader_int64_emulation) {
|
||||||
|
LOG_WARNING(Render_Vulkan,
|
||||||
|
"Using shaderInt64 emulation on Qualcomm proprietary drivers");
|
||||||
|
} else {
|
||||||
|
LOG_WARNING(Render_Vulkan, "Disabling shaderInt64 support on Turnip drivers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Some features are mandatory. Check those.
|
// Some features are mandatory. Check those.
|
||||||
#define CHECK_FEATURE(feature, name) \
|
#define CHECK_FEATURE(feature, name) \
|
||||||
|
@ -1137,8 +1175,10 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||||
properties.subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
|
properties.subgroup_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
|
||||||
SetNext(next, properties.subgroup_properties);
|
SetNext(next, properties.subgroup_properties);
|
||||||
|
|
||||||
// Retrieve relevant extension properties.
|
// Retrieve relevant extension/core properties.
|
||||||
if (extensions.shader_float_controls) {
|
// Float controls properties are core in Vulkan 1.2; if running on 1.2+ or if the
|
||||||
|
// KHR extension is present, chain the properties struct to query capabilities.
|
||||||
|
if (instance_version >= VK_API_VERSION_1_2 || extensions.shader_float_controls) {
|
||||||
properties.float_controls.sType =
|
properties.float_controls.sType =
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES;
|
||||||
SetNext(next, properties.float_controls);
|
SetNext(next, properties.float_controls);
|
||||||
|
|
|
@ -374,11 +374,18 @@ public:
|
||||||
return features.features.shaderStorageImageReadWithoutFormat;
|
return features.features.shaderStorageImageReadWithoutFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if shader int64 is supported.
|
/// Returns true if shader int64 is supported (natively or via emulation).
|
||||||
bool IsShaderInt64Supported() const {
|
bool IsShaderInt64Supported() const {
|
||||||
|
if (shader_int64_emulation) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return features.features.shaderInt64;
|
return features.features.shaderInt64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when shader int64 operations must be emulated with 32-bit pairs.
|
||||||
|
bool UsesShaderInt64Emulation() const {
|
||||||
|
return shader_int64_emulation;
|
||||||
|
}
|
||||||
/// Returns true if shader int16 is supported.
|
/// Returns true if shader int16 is supported.
|
||||||
bool IsShaderInt16Supported() const {
|
bool IsShaderInt16Supported() const {
|
||||||
return features.features.shaderInt16;
|
return features.features.shaderInt16;
|
||||||
|
@ -441,6 +448,11 @@ public:
|
||||||
|
|
||||||
/// Returns true if VK_KHR_shader_float_controls is enabled.
|
/// Returns true if VK_KHR_shader_float_controls is enabled.
|
||||||
bool IsKhrShaderFloatControlsSupported() const {
|
bool IsKhrShaderFloatControlsSupported() const {
|
||||||
|
const bool is_qualcomm =
|
||||||
|
properties.driver.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
|
||||||
|
if (is_qualcomm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return extensions.shader_float_controls;
|
return extensions.shader_float_controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +597,11 @@ public:
|
||||||
|
|
||||||
/// Returns true if the device supports VK_KHR_shader_atomic_int64.
|
/// Returns true if the device supports VK_KHR_shader_atomic_int64.
|
||||||
bool IsExtShaderAtomicInt64Supported() const {
|
bool IsExtShaderAtomicInt64Supported() const {
|
||||||
|
const auto driver = GetDriverID();
|
||||||
|
if (driver == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||||
|
driver == VK_DRIVER_ID_MESA_TURNIP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return extensions.shader_atomic_int64;
|
return extensions.shader_atomic_int64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -840,6 +857,7 @@ private:
|
||||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||||
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
|
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
|
||||||
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
|
bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format.
|
||||||
|
bool shader_int64_emulation{}; ///< Emulates shader Int64 using 32-bit pairs.
|
||||||
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
|
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
|
||||||
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
|
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
|
||||||
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
|
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
|
||||||
|
|
|
@ -45,31 +45,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<!-- Fixed -->
|
|
||||||
<key>LSApplicationCategoryType</key>
|
|
||||||
<string>public.app-category.games</string>
|
|
||||||
<key>CFBundleDocumentTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeExtensions</key>
|
|
||||||
<array>
|
|
||||||
<string>nsp</string>
|
|
||||||
<string>xci</string>
|
|
||||||
<string>nro</string>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleTypeName</key>
|
|
||||||
<string>Switch File</string>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Viewer</string>
|
|
||||||
<key>LSHandlerRank</key>
|
|
||||||
<string>Default</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
<string>True</string>
|
<string>True</string>
|
||||||
<key>UIDesignRequiresCompatibility</key>
|
|
||||||
<true/> <!-- https://bugreports.qt.io/browse/QTBUG-138942 -->
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -270,8 +270,10 @@ void ConfigureAudio::UpdateAudioDevices(int sink_index) {
|
||||||
void ConfigureAudio::InitializeAudioSinkComboBox() {
|
void ConfigureAudio::InitializeAudioSinkComboBox() {
|
||||||
sink_combo_box->clear();
|
sink_combo_box->clear();
|
||||||
sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
|
sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name));
|
||||||
for (const auto& id : AudioCore::Sink::GetSinkIDs())
|
|
||||||
sink_combo_box->addItem(QString::fromStdString(std::string{Settings::CanonicalizeEnum(id)}));
|
for (const auto& id : AudioCore::Sink::GetSinkIDs()) {
|
||||||
|
sink_combo_box->addItem(QString::fromStdString(Settings::CanonicalizeEnum(id)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureAudio::RetranslateUI() {
|
void ConfigureAudio::RetranslateUI() {
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringLiteral>
|
#include <QStringLiteral>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QObject>
|
|
||||||
#include <qobjectdefs.h>
|
#include <qobjectdefs.h>
|
||||||
#include "qt_common/shared_translation.h"
|
#include "qt_common/shared_translation.h"
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,11 @@ class GameList : public QWidget {
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
COLUMN_NAME,
|
COLUMN_NAME,
|
||||||
|
COLUMN_COMPATIBILITY,
|
||||||
|
COLUMN_ADD_ONS,
|
||||||
COLUMN_FILE_TYPE,
|
COLUMN_FILE_TYPE,
|
||||||
COLUMN_SIZE,
|
COLUMN_SIZE,
|
||||||
COLUMN_PLAY_TIME,
|
COLUMN_PLAY_TIME,
|
||||||
COLUMN_ADD_ONS,
|
|
||||||
COLUMN_COMPATIBILITY,
|
|
||||||
COLUMN_COUNT, // Number of columns
|
COLUMN_COUNT, // Number of columns
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -204,24 +204,36 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path,
|
||||||
const PlayTime::PlayTimeManager& play_time_manager,
|
const PlayTime::PlayTimeManager& play_time_manager,
|
||||||
const FileSys::PatchManager& patch)
|
const FileSys::PatchManager& patch)
|
||||||
{
|
{
|
||||||
auto const it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||||
// The game list uses 99 as compatibility number for untested games
|
|
||||||
QString compatibility = it != compatibility_list.end() ? it->second.first : QStringLiteral("99");
|
|
||||||
|
|
||||||
auto const file_type = loader.GetFileType();
|
// The game list uses this as compatibility number for untested games
|
||||||
auto const file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type));
|
QString compatibility{QStringLiteral("99")};
|
||||||
|
if (it != compatibility_list.end()) {
|
||||||
|
compatibility = it->second.first;
|
||||||
|
}
|
||||||
|
|
||||||
QString patch_versions = GetGameListCachedObject(fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
|
const auto file_type = loader.GetFileType();
|
||||||
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
|
const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type));
|
||||||
});
|
|
||||||
return QList<QStandardItem*>{
|
QList<QStandardItem*> list{
|
||||||
new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), file_type_string, program_id),
|
new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name),
|
||||||
|
file_type_string, program_id),
|
||||||
|
new GameListItemCompat(compatibility),
|
||||||
new GameListItem(file_type_string),
|
new GameListItem(file_type_string),
|
||||||
new GameListItemSize(size),
|
new GameListItemSize(size),
|
||||||
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
|
new GameListItemPlayTime(play_time_manager.GetPlayTime(program_id)),
|
||||||
new GameListItem(patch_versions),
|
|
||||||
new GameListItemCompat(compatibility),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QString patch_versions;
|
||||||
|
|
||||||
|
patch_versions = GetGameListCachedObject(
|
||||||
|
fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] {
|
||||||
|
return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable());
|
||||||
|
});
|
||||||
|
|
||||||
|
list.insert(2, new GameListItem(patch_versions));
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue